Merge "msm: ipa: add static function when IPA v3 is not used"
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
index 83a1601..4afbb24b 100644
--- a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
+++ b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
@@ -20,6 +20,28 @@
"qcom,gcc-gfx-8953"
"qcom,gcc-gfx-sdm450"
"qcom,gcc-gfx-sdm632"
+ "qcom,gcc-8992"
+ "qcom,gcc-8952"
+ "qcom,gcc-8937"
+ "qcom,gcc-8917"
+ "qcom,gcc-8940"
+ "qcom,gcc-8920"
+ "qcom,gcc-spm-8952"
+ "qcom,gcc-spm-8937"
+ "qcom,cc-debug-8952"
+ "qcom,cc-debug-8953"
+ "qcom,cc-debug-8937"
+ "qcom,cc-debug-8917"
+ "qcom,cc-debug-8940"
+ "qcom,cc-debug-8920"
+ "qcom,gcc-mdss-8953"
+ "qcom,gcc-mdss-8952"
+ "qcom,gcc-mdss-8937"
+ "qcom,gcc-mdss-8917"
+ "qcom,gcc-mdss-8940"
+ "qcom,gcc-mdss-8920"
+ "qcom,gcc-gfx-8953"
+ "qcom,gcc-gfx-sdm450"
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt
new file mode 100644
index 0000000..d9e1510
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt
@@ -0,0 +1,78 @@
+Qualcomm Technology MSM8939 CPU clock tree
+
+clock-cpu-8939 is a device that represents the MSM8939 or MSM8952 CPU
+subsystem clock tree. It lists the various power supplies that need to be
+scaled when the clocks are scaled and also other HW specific parameters like
+fmax tables, avs settings table, etc.
+
+Required properties:
+- compatible: Must be one of "qcom,clock-cpu-8939" or
+ "qcom,cpu-clock-8952", "qcom,cpu-clock-8917".
+- reg: Pairs of physical base addresses and region sizes of
+ memory mapped registers.
+- reg-names: Names of the bases for the above registers. Expected
+ bases are:
+ "apcs-c0-rcg-base", "apcs-c1-rcg-base",
+ "apcs-cci-rcg-base", "efuse", "efuse1", "efuse2"
+- vdd-c0-supply: The regulator powering the little cluster
+- vdd-c1-supply: The regulator powering the big cluster
+- vdd-cci-supply: The regulator powering the CCI cluster
+- qcom,speedX-bin-vY-ZZZ:
+ A table of CPU frequency (Hz) to voltage (corner)
+ mapping that represents the max frequency possible
+ for each supported voltage level for a CPU. 'X' is
+ the speed bin into which the device falls into - a
+ bin will have unique frequency-voltage relationships.
+ 'Y' is the characterization version, implying that
+ characterization (deciding what speed bin a device
+ falls into) methods and/or encoding may change. The
+ values 'X' and 'Y' are read from efuse registers, and
+ the right table is picked from multiple possible tables.
+ 'ZZZ' can be c1, c0 or cci depending on whether the table
+ is for the big cluster, little cluster or cci.
+Optional properties:
+- qcom,cpu-pcnoc-vote: Boolean to indicate cpu clocks would need to keep
+ active pcnoc vote.
+- qcom,num-cluster: Boolean to indicate cpu clock code is used for single
+ cluster.
+Example:
+ clock_cpu: qcom,cpu-clock-8939@f9015000 {
+ compatible = "qcom,cpu-clock-8939";
+ reg = <0xf9015000 0x1000>,
+ <0xf9016000 0x1000>,
+ <0xf9011000 0x1000>,
+ <0xf900d000 0x1000>,
+ <0xf900f000 0x1000>,
+ <0xf9112000 0x1000>;
+ reg-names = "apcs-c0-rcg-base", "apcs-c1-rcg-base",
+ "apcs-cci-rcg-base", "efuse", "efuse1",
+ "efuse2";
+ vdd-c0-supply = <&apc_vreg_corner>;
+ vdd-c1-supply = <&apc_vreg_corner>;
+ vdd-cci-supply = <&apc_vreg_corner>;
+ qcom,speed0-bin-v0-c0 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1286400000 3>;
+ qcom,speed0-bin-v0-c1 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1785600000 3>;
+ qcom,speed0-bin-v0-cci =
+ < 0 0>,
+ < 150000000 1>,
+ < 300000000 2>,
+ < 600000000 3>;
+ clocks = <&clock_gcc clk_gpll0_ao>,
+ <&clock_gcc clk_a53ss_c0_pll>,
+ <&clock_gcc clk_gpll0_ao>,
+ <&clock_gcc clk_a53ss_c1_pll>,
+ <&clock_gcc clk_gpll0_ao>,
+ <&clock_gcc clk_a53ss_cci_pll>;
+ clock-names = "clk-c0-4", "clk-c0-5",
+ "clk-c1-4", "clk-c1-5",
+ "clk-cci-4", "clk-cci-5";
+ #clock-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt b/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt
index 36e1a69..a53eba5 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpmh-master-stat.txt
@@ -4,15 +4,28 @@
It tells about the individual masters information at any given
time like "system sleep counts", "system sleep last entered at"
and "system sleep accumulated duration" etc. These stats can be
-show to the user using the debugfs interface of the kernel.
+displayed using the sysfs interface.
To achieve this, device tree node has been added.
+Additionally, RPMH master stats also maintains application processor's
+master stats. It uses profiling units to calculate power down and power
+up stats.
+
The required properties for rpmh-master-stats are:
-- compatible: "qcom,rpmh-master-stats".
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,rpmh-master-stats-v1".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Specifies physical address of start of profiling unit.
Example:
qcom,rpmh-master-stats {
compatible = "qcom,rpmh-master-stats";
+ reg = <0xb221200 0x60>;
};
diff --git a/Documentation/devicetree/bindings/display/bridge/lt9611.txt b/Documentation/devicetree/bindings/display/bridge/lt9611.txt
new file mode 100644
index 0000000..61688b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/lt9611.txt
@@ -0,0 +1,125 @@
+LT9611 DSI to HDMI bridge
+
+
+Required properties:
+ - compatible: Must be "lt,lt9611"
+ - reg: Main I2C slave ID (for I2C host driver)
+ - lt,irq-gpio: Main IRQ gpio mapping
+ - lt,reset-gpio Main reset gpio mapping
+
+
+ Optional properties:
+ - lt,hdmi-ps-gpio: gpio mapping for HDMI PS
+ - lt,hdmi-en-gpio: gpio mapping for HDMI EN
+
+ - lt,supply-entries: A node that lists the elements of the supply used to
+ power the bridge. There can be more than one instance
+ of this binding, in which case the entry would be
+ appended with the supply entry index.
+ e.g. lt,supply-entry@0
+ -- lt,supply-name: name of the supply (vdd/vcc)
+ -- lt,supply-min-voltage: minimum voltage level (uV)
+ -- lt,supply-max-voltage: maximum voltage level (uV)
+ -- lt,supply-enable-load: load drawn (uA) from enabled supply
+ -- lt,supply-disable-load: load drawn (uA) from disabled supply
+ -- lt,supply-ulp-load: load drawn (uA) from supply in ultra-low power mode
+ -- lt,supply-pre-on-sleep: time to sleep (ms) before turning on
+ -- lt,supply-post-on-sleep: time to sleep (ms) after turning on
+ -- lt,supply-pre-off-sleep: time to sleep (ms) before turning off
+ -- lt,supply-post-off-sleep: time to sleep (ms) after turning off
+
+ - lt,non-pluggable: Boolean to indicate if display is non pluggable.
+ - lt,customize-modes: Customized modes when it's non-pluggable display.
+ e.g. lt,customize-mode-id@0
+ -- lt,mode-h-active: Horizontal active pixels for this mode.
+ -- lt,mode-h-front-porch: Horizontal front porch in pixels for this mode.
+ -- lt,mode-h-pulse-width: Horizontal sync width in pixels for this mode.
+ -- lt,mode-h-back-porch: Horizontal back porch in pixels for this mode.
+ -- lt,mode-h-active-high: Boolean to indicate if mode horizontal polarity is active high.
+ -- lt,mode-v-active: Vertical active lines for this mode.
+ -- lt,mode-v-front-porch: Vertical front porch in lines for this mode.
+ -- lt,mode-v-pulse-width: Vertical sync width in lines for this mode.
+ -- lt,mode-v-back-porch: Vertical back porch in lines for this mode.
+ -- lt,mode-v-active-high: Boolean to indicate if mode vertical polarity is active high.
+ -- lt,mode-refersh-rate: Mode refresh rate in hertz.
+ -- lt,mode-clock-in-khz: Mode pclk in KHz.
+
+Required nodes:
+
+The LT9611 has one video port. Its connection is modelled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+Video port 0 is for the DSI input. The remote endpoint phandle should
+be a reference to a valid mipi_dsi_host device node.
+
+
+Example:
+
+&qupv3_se9_i2c {
+ status = "okay";
+ lt9611@3b {
+ compatible = "lt,lt9611";
+ reg = <0x3b>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <125 0>;
+ interrupt-names = "lt_irq";
+ lt,irq-gpio = <&tlmm 125 0x0>;
+ lt,reset-gpio = <&tlmm 134 0x0>;
+ lt,hdmi-ps-gpio = <&tlmm 136 0x0>;
+ lt,hdmi-en-gpio = <&tlmm 137 0x0>;
+
+ vcc-supply = <&pm660l_l6>;
+ vdd-supply = <&pm660_l11>;
+ lt,supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ lt,supply-entry@0 {
+ reg = <0>;
+ lt,supply-name = "vcc";
+ lt,supply-min-voltage = <3300000>;
+ lt,supply-max-voltage = <3300000>;
+ lt,supply-enable-load = <200000>;
+ lt,supply-post-on-sleep = <50>;
+ };
+
+ lt,supply-entry@1 {
+ reg = <1>;
+ lt,supply-name = "vdd";
+ lt,supply-min-voltage = <1800000>;
+ lt,supply-max-voltage = <1800000>;
+ lt,supply-enable-load = <200000>;
+ lt,supply-post-on-sleep = <50>;
+ };
+ };
+
+ lt,customize-modes {
+ lt,customize-mode-id@0 {
+ lt,mode-h-active = <1920>;
+ lt,mode-h-front-porch = <88>;
+ lt,mode-h-pulse-width = <44>;
+ lt,mode-h-back-porch = <148>;
+ lt,mode-h-active-high;
+ lt,mode-v-active = <1080>;
+ lt,mode-v-front-porch = <4>;
+ lt,mode-v-pulse-width = <5>;
+ lt,mode-v-back-porch = <36>;
+ lt,mode-v-active-high;
+ lt,mode-refresh-rate = <60>;
+ lt,mode-clock-in-khz = <148500>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ lt9611_in: endpoint {
+ remote-endpoint = <&ext_dsi_out>;
+ };
+ };
+ };
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
new file mode 100644
index 0000000..12ced5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
@@ -0,0 +1,65 @@
+QTI MPM interrupt controller
+
+MPM (MSM sleep Power Manager) is QTI's platform parent interrupt controller.
+It manages subsystem wakeups and resources during sleep. This driver marks
+the wakeup interrupts in APSS such that it monitors the interrupts when the
+system is asleep, wakes up the APSS when one of these interrupts occur and
+replays it to the subsystem interrupt controller after it becomes operational.
+
+Platform interrupt controller MPM is next in hierarchy, followed by others.
+
+Properties:
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,mpm" for mpm pin data
+ and the respective target compatible flag.
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: should specify the IRQ used by remote processor to wakeup APSS.
+
+- interrupt-parent:
+ Usage: required
+ Value type: <phandle>
+ Definition: Specifies the interrupt parent necessary for hierarchical domain to operate.
+
+- interrupt-controller:
+ Usage: required
+ Value type: <bool>
+ Definition: Identifies the node as an interrupt controller.
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Specifies the base physical address to trigger an interrupt into remote processor.
+
+-reg-names:
+ Usage: required
+ Value type: <string>, <string>
+ Definition: Specifies the address field names.
+
+- qcom,num-mpm-irqs:
+ Usage: optional
+ Value type: <value>
+ Defination: Specifies the number of interrupts supported.
+
+Example:
+
+mpm: mpm@7781b8 {
+ compatible = "qcom,mpm";
+ interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
+ reg = <0x7781b8 0x1000>,
+ <0x17911008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ qcom,num-mpm-irqs = <96>;
+
+ wakegic: wake-gic {
+ compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
index 176f9e1..54ff110 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -119,6 +119,8 @@
2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3
- switchX-supply : phandle of the regulator that needs to be used
as a supply for flash switch_X device.
+- qcom,bst-pwm-ovrhd-uv : Charger flash VPH overhead. Applicable for PMI632 only.
+ Supported values (in mV) are: 300, 400, 500, 600. Default is 300.
Child node: Contains settings for each individual LED. Each LED channel needs a flash node and
torch node for itself, and an individual switch node to serve as an overall switch.
@@ -182,6 +184,10 @@
be edge triggered. Otherwise, it is level triggered.
- qcom,hw-strobe-active-low : Boolean property to select strobe signal polarity. If defined, hw-strobe
signal polarity is set to active-low, else it is active-high.
+- qcom,symmetry-en : Boolean property to specify if the flash LEDs under a
+ switch node are controlled symmetrically. This needs
+ to be specified if a group of flash LED channels are
+ connected to a single LED.
Example:
qcom,leds@d300 {
compatible = "qcom,qpnp-flash-led-v2";
@@ -302,6 +308,7 @@
qcom,led-mask = <3>;
qcom,default-led-trigger =
"switch0_trigger";
+ qcom,symmetry-en;
};
pmi8998_switch1: qcom,led_switch_1 {
diff --git a/Documentation/devicetree/bindings/media/video/laser-sensor.txt b/Documentation/devicetree/bindings/media/video/laser-sensor.txt
new file mode 100644
index 0000000..0003f20
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/laser-sensor.txt
@@ -0,0 +1,29 @@
+Laser Sensor Device Tree Bindings.
+========================================
+
+Boards with the Laser Sensor connected to CCI shall have the following
+properties:
+
+Required node properties:
+ - cell-index: cci hardware core index
+ - compatible:
+ - "st,stmvl53l0" : STMiecroelectronics VL53L0 Laser sensor.
+ - reg : offset and length of the register set for the device
+ - qcom, cci-master: cci master the sensor connected to
+ - cam_cci-supply : cci voltage regulator used
+ - cam_laser-supply: laser sensor voltage regulator
+ - qcom,cam-vreg-name: voltage regulators name
+ - qcom, cam-vreg-min-voltage: specify minimum voltage level for
+ regulators used
+ - qcom, cam-vreg-max-voltage: specify maximum voltage level for
+ regulators used
+ - pinctrl-names : should specify the pin control groups followed by
+ the definition of each group
+ stm,irq-gpio : irq gpio which is to provide interrupts to host.
+ - gpios : should contain phandle to gpio controller node and array of
+ #gpio-cells specifying specific gpio (controller specific)
+ - qcom,gpio-req-tbl-num : contains index to gpios specific to the 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)
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-soc.txt b/Documentation/devicetree/bindings/media/video/msm-cam-soc.txt
new file mode 100644
index 0000000..382395f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-soc.txt
@@ -0,0 +1,41 @@
+* Qualcomm Technologies, Inc. MSM Camera SOC
+
+The below set of properties need to be defined by all the camera
+modules in their respective dtsi to adapt to SOC layer
+
+Required properties:
+ - clock-names: name of the clocks required for the device
+ - qcom,clock-rates: clock rate in Hz
+ - 0 if appropriate clock is required but doesn't have to apply the rate
+ - qcom,vdd-names: names of all the regulators for the device
+ - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+ below optional properties:
+ - qcom,msm-bus,name
+ - qcom,msm-bus,num-cases
+ - qcom,msm-bus,num-paths
+ - qcom,msm-bus,vectors-KBps
+ - qcom,msm-bus-vector-dyn-vote: indicated dynamic or static voting
+ - qcom,clock-cntl-support: indicates if further control supported for clocks
+ - Refer to "Documentation/devicetree/bindings/media/video/msm-ispif.txt" for
+ below optional property:
+ - qcom,clock-control
+
+Example:
+
+ cpp: qcom,cpp@a04000 {
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_cpp>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csiphy_timer_src_clk",
+ "csiphy_timer_clk";
+ qcom,clock-rates = <0 0 200000000 0>;
+ qcom,msm-bus,name = "msm_camera_cpp";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <106 512 0 0>,
+ <106 512 0 0>;
+ qcom,msm-bus-vector-dyn-vote;
+ };
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..371447c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam.txt
@@ -0,0 +1,19 @@
+* Qualcomm Technologies, Inc. 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.
+
+Optional properties:
+- qcom,gpu-limit : valid kgsl frequency.
+
+Example:
+
+ qcom,msm-cam@fd8c0000 {
+ compatible = "qcom,msm-cam";
+ reg = <0xfd8C0000 0x10000>;
+ reg-names = "msm-cam";
+ qcom,gpu-limit = <700000000>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
new file mode 100644
index 0000000..b43b295
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -0,0 +1,398 @@
+* Qualcomm Technologies, Inc. MSM CCI
+
+[First level nodes]
+Required properties:
+- cell-index: cci hardware core index
+- compatible :
+ - "qcom,cci"
+- reg : offset and length of the register set for the device
+ for the cci operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- 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)
+- clock-names: name of the clocks required for the device
+- clock-rates: clock rate in Hz
+
+Optional properties:
+- qcom,cam-vreg-name : name of the voltage regulators required for the device.
+- gdscr-supply : should contain gdsr regulator used for cci clocks.
+- mmagic-supply : should contain mmagic regulator used for mmagic clocks.
+
+- I2c speed settings (*)
+ - i2c_freq_100Khz: qcom,i2c_standard_mode - node should contain clock settings for
+ 100Khz
+ - i2c_freq_400Khz: qcom,i2c_fast_mode - node should contain clock settings for
+ 400Khz
+ - i2c_freq_custom: qcom,i2c_custom_mode - node can contain clock settings for
+ frequencies other than 100Khz and 400Khz which is specific to usecase.
+ Currently it has settings for 375Khz.
+ - i2c_freq_1Mhz: qcom,i2c_fast_plus_mode - node should contain clock
+ settings for 1Mhz
+ * if speed settings is not defined the low level driver can use "i2c_freq_custom"
+ like default
+
+[Second level nodes]
+* Qualcomm Technologies, Inc. CCI clock settings
+
+Optional properties:
+- 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
+
+* Qualcomm Technologies, Inc. MSM Sensor
+
+MSM sensor node contains properties of camera sensor
+
+Required properties:
+- compatible : should be manufacturer name followed by sensor name
+ - "qcom,camera"
+ - "shinetech,gc0310"
+- reg : should contain i2c slave address of the device
+- 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
+- cam_vdig-supply : should contain regulator from which digital voltage is
+ supplied
+- cam_vana-supply : should contain regulator from which analog voltage is
+ supplied
+- cam_vio-supply : should contain regulator from which IO voltage is supplied
+- qcom,cam-vreg-name : should contain names of all regulators needed by this
+ sensor
+ - "cam_vdig", "cam_vana", "cam_vio", "cam_vaf"
+- qcom,cam-vreg-min-voltage : should contain minimum voltage level for
+ regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-max-voltage : should contain maximum voltage level for
+ 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)
+
+Optional properties:
+- qcom,slave-id : should contain i2c slave address, device id address
+ ,expected id read value and device id mask
+- qcom,sensor-name : should contain unique sensor name to differentiate from
+ other sensor
+ - "s5k3l1yx"
+- qcom,sensor-mode : should contain sensor mode supported
+ - 0 -> back camera 2D
+ - 1 -> front camera 2D
+ - 2 -> back camera 3D
+ - 3 -> back camera int 3D
+- qcom,sensor-type : should contain format of data that sensor streams
+ - 0 -> bayer format
+ - 1 -> yuv format
+- qcom,is-vpe : should be enabled if VPE module is required for post processing
+ of this sensor
+ - 1 if required, 0 otherwise
+- qcom,mount-angle : should contain the physical mount angle of the sensor on
+ the target
+ - 0, 90, 180, 360
+- qcom,secure : should be enabled to operate the camera in secure mode
+ - 0, 1
+- qcom,mclk-23880000 : should be enabled if the supported mclk is 23.88Mhz and
+ not 24 Mhz.
+- qcom,gpio-no-mux : should contain field to indicate whether gpio mux table is
+ available
+ - 1 if gpio mux is not available, 0 otherwise
+- 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-reset : should contain index to gpio used by sensors reset_n
+- qcom,gpio-standby : should contain index to gpio used by sensors standby_n
+- qcom,gpio-vio : should contain index to gpio used by sensors io vreg enable
+- qcom,gpio-vana : should contain index to gpio used by sensors analog vreg enable
+- qcom,gpio-vdig : should contain index to gpio used by sensors digital vreg enable
+- qcom,gpio-vaf : should contain index to gpio used by sensors af vreg enable
+- qcom,gpio-af-pwdm : should contain index to gpio used by sensors af pwdm_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)
+- qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,gpio-set-tbl-num : should contain index of gpios that need to be
+ configured by msm
+- qcom,gpio-set-tbl-flags : should contain value to be configured for the gpios
+ present in qcom,gpio-set-tbl-num property (in the same order)
+- qcom,gpio-set-tbl-delay : should contain amount of delay after configuring
+ gpios as specified in gpio_set_tbl_flags property (in the same order)
+- qcom,csi-lane-assign : should contain lane assignment value to map CSIPHY
+ lanes to CSID lanes
+ - 0x4320
+- qcom,csi-lane-mask : should contain lane mask that specifies CSIPHY lanes to
+ be enabled
+- qcom,csi-phy-sel : should contain CSIPHY core instance from which CSID should
+ receive data
+- qcom,actuator-cam-name : should contain actuator cam name associated with
+ this sensor
+ - If actuator does not exist, this property should not be initialized
+ - If actuator exist, this field should indicate the index of actuator to
+ be used
+- qcom,actuator-vcm-pwd : should contain the gpio pin of vcm power to be enabled
+ 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
+- qcom,cci-master : should contain i2c master id to be used for this camera
+ sensor
+ - 0 -> MASTER 0
+ - 1 -> MASTER 1
+- qcom,actuator-src : if auto focus is supported by this sensor, this
+ property should contain phandle of respective actuator node
+- qcom,led-flash-src : if LED flash is supported by this sensor, this
+ property should contain phandle of respective LED flash node
+- qcom,vdd-cx-supply : should contain regulator from which cx voltage is
+ supplied
+- qcom,vdd-cx-name : should contain names of cx regulator
+- qcom,eeprom-src : if eeprom memory is supported by this sensor, this
+ property should contain phandle of respective eeprom nodes
+- qcom,ois-src : if optical image stabilization is supported by this sensor,
+ this property should contain phandle of respective ois node
+- qcom,ir-led-src : if ir led is supported by this sensor, this property
+ should contain phandle of respective ir-led node
+- qcom,ir-cut-src : if ir cut is supported by this sensor, this property
+ should contain phandle of respective ir-cut node
+- qcom,special-support-sensors: if only some special sensors are supported
+ on this board, add sensor name in this property.
+
+* Qualcomm Technologies, Inc. MSM ACTUATOR
+
+Required properties:
+- cell-index : should contain unique identifier to differentiate
+ between multiple actuators
+- reg : should contain i2c slave address of the actuator and length of
+ data field which is 0x0
+- compatible :
+ - "qcom,actuator"
+- qcom,cci-master : should contain i2c master id to be used for this camera
+ sensor
+ - 0 -> MASTER 0
+ - 1 -> MASTER 1
+
+Optional properties:
+- qcom,cam-vreg-name : should contain names of all regulators needed by this
+ actuator
+ - "cam_vaf"
+- qcom,cam-vreg-min-voltage : should contain minimum voltage level in mcrovolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-max-voltage : should contain maximum voltage level in mcrovolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-op-mode : should contain the maximum current in microamps
+ required from the regulators mentioned in the qcom,cam-vreg-name property
+ (in the same order).
+- cam_vaf-supply : should contain regulator from which AF voltage is supplied
+
+* Qualcomm Technologies, Inc. MSM LASER LED
+
+Required properties:
+- cell-index : should contain unique identifier to differentiate
+ between multiple laser led modules
+- reg : should contain i2c slave address of the laser led and length of
+ data field which is 0x0
+- compatible :
+ - "qcom,laser-led"
+- qcom,cci-master : should contain i2c master id to be used for this camera
+ sensor
+ - 0 -> MASTER 0
+ - 1 -> MASTER 1
+
+Optional properties:
+- qcom,cam-vreg-name : should contain names of all regulators needed by this
+ laser led
+- qcom,cam-vreg-min-voltage : should contain minimum voltage level in microvolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-max-voltage : should contain maximum voltage level in microvolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-op-mode : should contain the maximum current in microamps
+ required from the regulators mentioned in the qcom,cam-vreg-name property
+ (in the same order).
+
+* Qualcomm Technologies, Inc. MSM OIS
+
+Required properties:
+- cell-index : should contain unique identifier to differentiate
+ between multiple ois drivers
+- reg : should contain i2c slave address of the ois and length of
+ data field which is 0x0
+- compatible :
+ - "qcom,ois"
+- qcom,cci-master : should contain i2c master id to be used for this camera
+ sensor
+ - 0 -> MASTER 0
+ - 1 -> MASTER 1
+
+Optional properties:
+- qcom,cam-vreg-name : should contain names of all regulators needed by this
+ ois
+ - "cam_vaf"
+- qcom,cam-vreg-min-voltage : should contain minimum voltage level in mcrovolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-max-voltage : should contain maximum voltage level in mcrovolts
+ for regulators mentioned in qcom,cam-vreg-name property (in the same order)
+- qcom,cam-vreg-op-mode : should contain the maximum current in microamps
+ required from the regulators mentioned in the qcom,cam-vreg-name property
+ (in the same order).
+- cam_vaf-supply : should contain regulator from which ois voltage is supplied
+
+Example:
+
+ qcom,cci@0xfda0c000 {
+ cell-index = <0>;
+ compatible = "qcom,cci";
+ reg = <0xfda0c000 0x300>;
+ reg-names = "cci";
+ interrupts = <0 50 0>;
+ interrupt-names = "cci";
+ clock-names = "camss_top_ahb_clk", "vfe_clk_src",
+ "camss_vfe_vfe_clk", "iface_clk", "cpp_core_clk",
+ "cpp_iface_clk", "cpp_bus_clk", "micro_iface_clk";
+ qcom,clock-rates = <0 266670000 0 0 266670000 0 0 0>;
+ 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";
+ i2c_freq_100Khz: qcom,i2c_standard_mode {
+ status = "disabled";
+ };
+ i2c_freq_400Khz: qcom,i2c_fast_mode {
+ status = "disabled";
+ };
+ i2c_freq_custom: qcom,i2c_custom_mode {
+ status = "disabled";
+ };
+
+ actuator0: qcom,actuator@18 {
+ cell-index = <0>;
+ reg = <0x18>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8941_l23>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <3000000>;
+ qcom,cam-vreg-max-voltage = <3000000>;
+ qcom,cam-vreg-op-mode = <100000>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,secure = <1>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,eeprom-src = <&eeprom0>;
+ cam_vdig-supply = <&pm8994_s3>;
+ cam_vio-supply = <&pm8994_lvs1>;
+ cam_vana-supply = <&pm8994_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>,
+ <&tlmm 29 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ };
+
+&i2c_freq_100Khz {
+ 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>;
+ status = "ok";
+};
+
+&i2c_freq_400Khz {
+ qcom,hw-thigh = <20>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <25>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_custom {
+ qcom,hw-thigh = <15>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <25>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_1Mhz {
+ qcom,hw-thigh = <16>;
+ qcom,hw-tlow = <22>;
+ qcom,hw-tsu-sto = <17>;
+ qcom,hw-tsu-sta = <18>;
+ qcom,hw-thd-dat = <16>;
+ qcom,hw-thd-sta = <15>;
+ qcom,hw-tbuf = <19>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <3>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cpp.txt b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
new file mode 100644
index 0000000..450e4d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
@@ -0,0 +1,145 @@
+* Qualcomm Technologies, Inc. MSM CPP
+
+Required properties:
+- cell-index: cpp hardware core index
+- compatible :
+ - "qcom,cpp"
+- reg : offset and length of the register set for the device
+ for the cpp operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+ - cpp - has CPP MICRO register set.
+ - cpp_vbif - has VBIF core register set used by CPP.
+ - cpp_hw - has CPP hardware register set.
+- interrupts : should contain the cpp interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- vdd-supply: phandle to GDSC regulator controlling VFE & CPP core.
+- clocks: list of phandles to the clock controller device and coresponding
+ clock names.
+- clock-names: name of the clocks required for the device used by the consumer.
+- qcom,clock-rates: clock rate in Hz.
+- qcom,min-clock-rate: minimum clock rate in Hz, to be set to CPP hardware in
+ case dynamic clock scaling based on prevalent streams need lower clock rate.
+- qcom,cpp-fw-payload-info: Child node for cpp node having infomration on
+ cpp firmware payload offsets. This is mandatory node.
+- resets: reset specifier pair consists of phandle for the reset controller
+ and reset lines used by this controller.
+- reset-names: reset signal name strings sorted in the same order as the resets
+ property.
+- qcom,src-clock-rates = This is an array which holds clock rates for cpp src
+ clocks. The maximum size for the array is 10.
+
+Required properties of the child node:
+- qcom,stripe-base = Base offset of stripes in cpp payload.
+- qcom,plane-base = Base offset of planes in cpp payload.
+- qcom,stripe-size = size of each stripe in payload.
+- qcom,plane-size = size of each plane in payload.
+- qcom,fe-ptr-off = offset from stripe base to fetch engine address
+ location in payload.
+- qcom,we-ptr-off = offset from stripe base to write engine address
+ location in payload.
+
+Optional properties of the child node:
+- qcom,ref-fe-ptr-off = offset from stripe base to reference fetch engine
+ address location in payload.
+- qcom,ref-we-ptr-off = offset from stripe base to reference write engine
+ address location in payload.
+- qcom,we-meta-ptr-off = offset from stripe base to metadata address
+ location in payload.
+- qcom,fe-mmu-pf-ptr-off = offset from plane base to fetch engine mmu prefetch
+ address min location in payload.
+- qcom,ref-fe-mmu-pf-ptr-off = offset from plane base to reference fetch engine
+ mmu prefetch address min location in payload.
+- qcom,we-mmu-pf-ptr-off = offset from plane base to write engine mmu prefetch
+ address min location in payload.
+- qcom,dup-we-mmu-pf-ptr-off = offset from plane base to duplicate write engine
+ mmu prefetch address min location in payload.
+- qcom,ref-we-mmu-pf-ptr-off = offset from plane base to reference write engine
+ mmu prefetch address min location in payload.
+- qcom,set-group-buffer-len = length/size of set group buffer command used for
+ hfr.
+- qcom,dup-frame-indicator-off = offset for duplicate frame indicator in a
+ batch for frames
+
+Optional properties:
+- mmagic-vdd-supply: phandle to GDSC regulator controlling mmagic.
+- camss-vdd-supply: phandle to GDSC regulator controlling camss.
+- qcom,bus-master: Flag for presence of CPP bus master. It has to be set only for
+ platforms that support such feature.
+- qcom,vbif-setting: The offset and value for vbif core qos registers.
+ The first entry is register offset and second entry is register value.
+- qcom,micro-reset: Boolean flag indicating if micro reset need to be enabled.
+ This needs to present on platforms that support this feature.
+- qcom,cpp-cx-ipeak: To handle Cx peak current limit.
+ <phandle bit>
+ phandle - phandle of cx ipeak device node
+ bit - bit number of client in relevant register
+ This is used to access Cx ipeak HW module to limit the current drawn by
+ various subsystem blocks on Cx power rail. CPP set their bit in tcsr register
+ if it is going to cross its own threshold.
+
+Example:
+
+ qcom,cpp@fda04000 {
+ cell-index = <0>;
+ compatible = "qcom,cpp";
+ reg = <0xfda04000 0x100>,
+ <0xfda80000 0x200>,
+ <0xfda18000 0x008>,
+ <0xfd8c36D4 0x4>;
+ reg-names = "cpp", "cpp_vbif", "cpp_hw", "camss_cpp";
+ interrupts = <0 49 0>;
+ interrupt-names = "cpp";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_cpp>;
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_cpp_clk_src>,
+ <&clock_mmss clk_camss_cpp_ahb_clk>,
+ <&clock_mmss clk_camss_cpp_axi_clk>,
+ <&clock_mmss clk_camss_cpp_clk>,
+ <&clock_mmss clk_camss_micro_ahb_clk>,
+ <&clock_mmss clk_camss_ahb_clk>;
+ <&clock_mmss clk_smmu_cpp_axi_clk>,
+ <&clock_mmss clk_camss_cpp_vbif_ahb_clk>,
+ clock-names = "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk",
+ "mmagic_camss_axi_clk", "camss_top_ahb_clk",
+ "cpp_core_clk", "camss_cpp_ahb_clk",
+ "camss_cpp_axi_clk", "camss_cpp_clk",
+ "micro_iface_clk", "camss_ahb_clk";
+ "smmu_cpp_axi_clk", "cpp_vbif_ahb_clk";
+ qcom,clock-rates = <0 0 0 0 465000000 0 0 465000000 0 0 0 0>;
+ qcom,cpp-cx-ipeak = <&cx_ipeak_lm 2>;
+ qcom,min-clock-rate = <320000000>;
+ qcom,bus-master = <1>;
+ qcom,vbif-qos-setting = <0x20 0x10000000>,
+ <0x24 0x10000000>,
+ <0x28 0x10000000>,
+ <0x2C 0x10000000>;
+ qcom,src-clock-rates = <100000000 200000000 384000000 404000000
+ 480000000 576000000 600000000>;
+ qcom,micro-reset;
+ qcom,cpp-fw-payload-info {
+ qcom,stripe-base = <553>;
+ qcom,plane-base = <481>;
+ qcom,stripe-size = <61>;
+ qcom,plane-size = <24>;
+ qcom,fe-ptr-off = <11>;
+ qcom,we-ptr-off = <23>;
+ qcom,ref-fe-ptr-off = <17>;
+ qcom,ref-we-ptr-off = <36>;
+ qcom,we-meta-ptr-off = <42>;
+ qcom,fe-mmu-pf-ptr-off = <6>;
+ qcom,ref-fe-mmu-pf-ptr-off = <9>;
+ qcom,we-mmu-pf-ptr-off = <12>;
+ qcom,dup-we-mmu-pf-ptr-off = <17>;
+ qcom,ref-we-mmu-pf-ptr-off = <22>;
+ qcom,set-group-buffer-len = <135>;
+ qcom,dup-frame-indicator-off = <70>;
+ resets = <&clock_mmss MMSS_CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt
new file mode 100644
index 0000000..8a6b3c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt
@@ -0,0 +1,40 @@
+* Qualcomm Technologies, Inc. MSM CSI Phy
+
+Required properties:
+- cell-index: csi phy hardware core index
+- compatible :
+ - "qcom,csiphy"
+ - "qcom,csiphy-v2.0"
+ - "qcom,csiphy-v2.2"
+ - "qcom,csiphy-v3.0"
+ - "qcom,csiphy-v3.1"
+ - "qcom,csiphy-v3.1.1"
+ - "qcom,csiphy-v3.2"
+ - "qcom,csiphy-v3.4.2"
+ - "qcom,csiphy-v3.5"
+ - "qcom,csiphy-v5.0"
+ - "qcom,csiphy-v5.01"
+- reg : offset and length of the register set for the device
+ for the csiphy operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should contain the csiphy interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- clock-names: name of the clocks required for the device
+- qcom,clock-rates: clock rate in Hz
+ - 0 if appropriate clock is required but doesn't have to apply the rate
+
+Example:
+
+ qcom,csiphy@fda0ac00 {
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v2.0", "qcom,csiphy";
+ reg = <0xfda0ac00 0x200>;
+ reg-names = "csiphy";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csiphy_timer_src_clk",
+ "csiphy_timer_clk";
+ qcom,clock-rates = <0 0 200000000 0>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-csid.txt b/Documentation/devicetree/bindings/media/video/msm-csid.txt
new file mode 100644
index 0000000..340d986
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-csid.txt
@@ -0,0 +1,52 @@
+* Qualcomm Technologies, Inc. MSM CSID
+
+Required properties:
+- cell-index: csid hardware core index
+- compatible :
+ - "qcom,csid"
+ - "qcom,csid-v2.0"
+ - "qcom,csid-v2.2"
+ - "qcom,csid-v3.0"
+ - "qcom,csid-v3.1"
+ - "qcom,csid-v3.2"
+ - "qcom,csid-v3.5"
+ - "qcom,csid-v4.0"
+ - "qcom,csid-v3.4.2"
+ - "qcom,csid-v3.5.1"
+ - "qcom,csid-v3.4.3"
+ - "qcom,csid-v5.0"
+- reg : offset and length of the register set for the device
+ for the csid operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should contain the csid interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- qcom,csi-vdd-voltage : should specify voltage level
+ for mipi csi in uV.
+- qcom,mipi-csi-vdd-supply : should contain regulator to be used for
+ this csid core
+- clock-names: name of the clocks required for the device
+- qcom,clock-rates: clock rate in Hz
+ - 0 if appropriate clock is required but doesn't have to apply the rate
+
+Optional properties:
+- qcom,cam-vreg-name : name of the voltage regulators required for the device.
+- gdscr-supply : should contain regulator used for csid clocks.
+- mmagic-supply : should contain mmagic regulator used for mmagic clocks.
+
+Example:
+
+ qcom,csid@fda08000 {
+ cell-index = <0>;
+ compatible = "qcom,csid-v2.0", "qcom,csid";
+ reg = <0xfda08000 0x200>;
+ reg-names = "csid";
+ interrupts = <0 51 0>;
+ interrupt-names = "csiphy";
+ qcom,csi-vdd-voltage = <1800000>;
+ qcom,mipi-csi-vdd-supply = <&pm8941_l12>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csi_ahb_clk", "csi_src_clk", "csi_clk",
+ "csi_phy_clk", "csi_pix_clk", "csi_rdi_clk";
+ qcom,clock-rates = <0 0 0 200000000 0 0 0 0>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-eeprom.txt b/Documentation/devicetree/bindings/media/video/msm-eeprom.txt
new file mode 100644
index 0000000..f951b21
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-eeprom.txt
@@ -0,0 +1,199 @@
+* Qualcomm Technologies, Inc. MSM EEPROM
+
+EEPROM is an one time programmed(OTP) device that stores the calibration data
+use for camera sensor. It may either be integrated in the sensor module or in
+the sensor itself. As a result, the power, clock and GPIOs may be the same as
+the camera sensor. The following describes the page block map, power supply,
+clock, GPIO and power on sequence properties of the EEPROM device.
+
+Required properties if probe happens from camera daemon:
+- cell-index: eeprom hardware core index
+- compatible :
+ - "qcom,eeprom"
+- reg : offset of eeprom device registers.
+- qcom,cci-master : should specify the cci core index that eeprom use.
+- cam_vio-supply : should contain regulator to be used for the IO vdd.
+- qcom,cam-vreg-name : should specify the regulator name to be used for
+ this eeprom.
+- qcom,cam-vreg-type : should specify the regulator type to be used for
+ this eeprom.
+- qcom,cam-vreg-min-voltage : should specify minimum voltage level
+ for eeprom in uV.
+- qcom,cam-vreg-max-voltage : should specify maximum voltage level
+ for eeprom in uV.
+- qcom,cam-vreg-op-mode : should specify current level for eeprom in uA.
+- qcom,gpio-no-mux : should specify the gpio mux type.
+- gpios : should specify the gpios to be used for the eeprom.
+- qcom,gpio-reset : should specify the reset gpio index.
+- qcom,gpio-standby : should specify the standby gpio index.
+- qcom,gpio-req-tbl-num : should specify the gpio table index.
+- qcom,gpio-req-tbl-flags : should specify the gpio functions.
+- qcom,gpio-req-tbl-label : should specify the gpio labels.
+- qcom,cam-power-seq-type : should specify the power on sequence types.
+- qcom,cam-power-seq-val : should specify the power on sequence values.
+- qcom,cam-power-seq-cfg-val : should specify the power on sequence config
+ values.
+- qcom,cam-power-seq-delay : should specify the power on sequence delay
+ time in ms.
+
+Optional properties:
+- cam_vdig-supply : should contain regulator to be used for the digital vdd.
+
+
+
+Example:
+
+ eeprom0: qcom,eeprom@60 {
+ cell-index = <0>;
+ reg = <0x60 0x0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ cam_vdig-supply = <&pm8226_l5>;
+ cam_vio-supply = <&pm8226_lvs1>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio";
+ qcom,cam-vreg-type = <0 1>;
+ qcom,cam-vreg-min-voltage = <1200000 0>;
+ qcom,cam-vreg-max-voltage = <1200000 0>;
+ qcom,cam-vreg-op-mode = <200000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 26 0>,
+ <&msmgpio 37 0>,
+ <&msmgpio 36 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
+ qcom,cam-power-seq-type = "sensor_vreg",
+ "sensor_vreg", "sensor_clk",
+ "sensor_gpio", "sensor_gpio";
+ qcom,cam-power-seq-val = "cam_vdig",
+ "cam_vio", "sensor_cam_mclk",
+ "sensor_gpio_reset",
+ "sensor_gpio_standby";
+ qcom,cam-power-seq-cfg-val = <1 1 24000000 1 1>;
+ qcom,cam-power-seq-delay = <1 1 5 5 10>;
+ };
+
+
+
+Required properties if eeprom probe is kernel probe:
+- cell-index: eeprom hardware core index
+- compatible :
+ - "qcom,eeprom"
+- reg : offset of eeprom device registers.
+- qcom,eeprom-name : should specify relevant names of the eeprom module
+ library.
+- qcom,slave-addr : should specify the slave address of the eeprom.
+- qcom,cci-master : should specify the cci core index that eeprom use.
+- qcom,num-blocks : should specify the total block number that eeprom contains,
+ every block should contains page poll and mem.
+- qcom,page%d : number %d page size, start address, address type, data,
+ data type, delay in ms. size 0 stand for non-paged.
+ - address type : 1 byte, 2 word.
+ - data type : 1 byte, 2 word.
+- qcom,poll%d : number %d poll size, poll reg address, address type, data,
+ data type, delay in ms. size 0 stand for not used.
+ - address type : 1 byte, 2 word.
+ - data type : 1 byte, 2 word.
+- qcom,mem%d : number %d memory size, start address, address type, data,
+ data type, delay in ms. size 0 stand for not used.
+ - address type : 1 byte, 2 word.
+ - data type : 1 byte, 2 word.
+- cam_vio-supply : should contain regulator to be used for the IO vdd.
+- qcom,cam-vreg-name : should specify the regulator name to be used for
+ this eeprom.
+- qcom,cam-vreg-type : should specify the regulator type to be used for
+ this eeprom.
+- qcom,cam-vreg-min-voltage : should specify minimum voltage level
+ for eeprom in uV.
+- qcom,cam-vreg-max-voltage : should specify maximum voltage level
+ for eeprom in uV.
+- qcom,cam-vreg-op-mode : should specify current level for eeprom in uA.
+- pinctrl-names : should specify the pin control groups followed by
+ the definition of each group
+- qcom,gpio-no-mux : should specify the gpio mux type.
+- gpios : should specify the gpios to be used for the eeprom.
+- qcom,gpio-reset : should specify the reset gpio index.
+- qcom,gpio-standby : should specify the standby gpio index.
+- qcom,gpio-req-tbl-num : should specify the gpio table index.
+- qcom,gpio-req-tbl-flags : should specify the gpio functions.
+- qcom,gpio-req-tbl-label : should specify the gpio labels.
+- qcom,cam-power-seq-type : should specify the power on sequence types.
+- qcom,cam-power-seq-val : should specify the power on sequence values.
+- qcom,cam-power-seq-cfg-val : should specify the power on sequence config
+ values.
+- qcom,cam-power-seq-delay : should specify the power on sequence delay
+ time in ms.
+
+Optional properties:
+- qcom,pageen%d : number %d page enable reg size, start address, address type,
+ data, data type, delay in ms. size 0 stand for not used.
+- cam_vdig-supply : should contain regulator to be used for the digital vdd.
+- qcom,saddr%d : property should specify the slave address for block (%d).
+- qcom,i2c-freq-mode : property should specify the I2C speed mode.
+
+Optional properties -EEPROM Camera Multimodule
+- qcom,cmm-data-support - Camera MultiModule data capability flag.
+- qcom,cmm-data-compressed - Camera MultiModule data compression flag.
+- qcom,cmm-data-offset - Camera MultiModule data start offset.
+- qcom,cmm-data-size - Camera MultiModule data size.
+
+
+
+Example:
+
+ eeprom0: qcom,eeprom@60 {
+ cell-index = <0>;
+ reg = <0x60 0x0>;
+ qcom,eeprom-name = "msm_eeprom";
+ compatible = "qcom,eeprom";
+ qcom,slave-addr = <0x60>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <2>;
+ qcom,page0 = <1 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0 1 1>;
+ qcom,mem0 = <0 0x0 2 0 1 0>;
+ qcom,page1 = <1 0x0200 2 0x8 1 1>;
+ qcom,pageen1 = <1 0x0202 2 0x01 1 10>;
+ qcom,poll1 = <0 0x0 2 0 1 1>;
+ qcom,mem1 = <32 0x3000 2 0 1 0>;
+ qcom,saddr1 = <0x62>;
+
+ qcom,cmm-data-support;
+ qcom,cmm-data-compressed;
+ qcom,cmm-data-offset = <0>;
+ qcom,cmm-data-size = <0>;
+
+ cam_vdig-supply = <&pm8226_l5>;
+ cam_vio-supply = <&pm8226_lvs1>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio";
+ qcom,cam-vreg-type = <0 1>;
+ qcom,cam-vreg-min-voltage = <1200000 0>;
+ qcom,cam-vreg-max-voltage = <1200000 0>;
+ qcom,cam-vreg-op-mode = <200000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 26 0>,
+ <&msmgpio 37 0>,
+ <&msmgpio 36 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
+ qcom,cam-power-seq-type = "sensor_vreg",
+ "sensor_vreg", "sensor_clk",
+ "sensor_gpio", "sensor_gpio";
+ qcom,cam-power-seq-val = "cam_vdig",
+ "cam_vio", "sensor_cam_mclk",
+ "sensor_gpio_reset",
+ "sensor_gpio_standby";
+ qcom,cam-power-seq-cfg-val = <1 1 24000000 1 1>;
+ qcom,cam-power-seq-delay = <1 1 5 5 10>;
+ };
+
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-fd.txt b/Documentation/devicetree/bindings/media/video/msm-fd.txt
new file mode 100644
index 0000000..0563599
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-fd.txt
@@ -0,0 +1,99 @@
+* Qualcomm Technologies, Inc. MSM FD
+
+Face detection hardware block.
+The Face Detection Hardware Block will offload processing
+on the host and also reduce power consumption.
+Supports:
+Front and back camera face detection concurrently.
+Sizes: QVGA, VGA, WQVGA, WVGA at 20 pix minimum face size.
+
+Required properties:
+
+- compatible:
+ - "qcom,face-detection"
+- reg: offset and length of the register set for the device.
+- reg-names: should specify relevant names to each reg property defined.
+ - "fd_core" - FD CORE hardware register set.
+ - "fd_misc" - FD MISC hardware register set.
+ - "fd_vbif" - FD VBIF hardware register set.
+- interrupts: should contain the fd interrupts. From fd cores with
+ revisions 0x10010000 and higher, power collapse sequence is required.
+ Face detection misc irq is needed to perform power collapse.
+- interrupt-names: should specify relevant names to each interrupts
+ property defined.
+- vdd-supply: phandle to GDSC regulator controlling face detection hw.
+- clocks: list of entries each of which contains:
+ - phandle to the clock controller.
+ - macro containing clock's name in hardware.
+- clock-names: should specify relevant names to each clocks
+ property defined.
+
+Optional properties:
+
+- clock-rates: should specify clock rates in Hz to each clocks
+ property defined.
+ If we want to have different operating clock frequencies we can define
+ rate levels. They should be defined in incremental order.
+- qcom,bus-bandwidth-vectors: Specifies instant and average bus bandwidth
+ vectors per clock rate.
+ Each of entries contains:
+ - ab. Average bus bandwidth (Bps).
+ - ib. Instantaneous bus bandwidth (Bps).
+- mmagic-vdd-supply: phandle to GDSC regulator controlling mmagic.
+- camss-vdd-supply: phandle to GDSC regulator controlling camss.
+- qcom,fd-core-reg-settings: relative address offsets and value pairs for
+ FD CORE registers and bit mask.
+ Format: <reg_addr_offset reg_value reg_mask>
+- qcom,fd-misc-reg-settings: relative address offsets and value pairs for
+ FD MISC registers and bit mask.
+ Format: <reg_addr_offset reg_value reg_mask>
+- qcom,fd-vbif-reg-settings: relative address offsets and value pairs for
+ FD VBIF registers and bit mask.
+ Format: <reg_addr_offset reg_value reg_mask>
+
+Example:
+
+ qcom,fd@fd878000 {
+ compatible = "qcom,face-detection";
+ reg = <0xfd878000 0x800>,
+ <0xfd87c000 0x800>,
+ <0xfd860000 0x1000>;
+ reg-names = "fd_core", "fd_misc", "fd_vbif";
+ interrupts = <0 316 0>;
+ interrupt-names = "fd";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_fd>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_fd_core_clk_src>,
+ <&clock_mmss clk_fd_core_clk>,
+ <&clock_mmss clk_fd_core_uar_clk>,
+ <&clock_mmss clk_fd_ahb_clk>,
+ <&clock_mmss clk_smmu_cpp_axi_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_camss_cpp_axi_clk>,
+ <&clock_mmss clk_camss_cpp_vbif_ahb_clk>,
+ <&clock_mmss clk_smmu_cpp_ahb_clk>;
+ clock-names = "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk" ,
+ "mmagic_camss_axi_clk", "camss_top_ahb_clk",
+ "fd_core_clk_src", "fd_core_clk",
+ "fd_core_uar_clk", "fd_ahb_clk",
+ "smmu_cpp_axi_clk", "camss_ahb_clk",
+ "camss_cpp_axi_clk", "cpp_vbif_ahb_clk",
+ "smmu_cpp_ahb_clk";
+ clock-rates = <0 0 0 0 400000000 400000000 400000000 80000000 0 0 0 0 0>;
+ qcom,bus-bandwidth-vectors = <13000000 13000000>,
+ <45000000 45000000>,
+ <90000000 90000000>;
+ qcom,fd-vbif-reg-settings = <0x20 0x10000000 0x30000000>,
+ <0x24 0x10000000 0x30000000>,
+ <0x28 0x10000000 0x30000000>,
+ <0x2c 0x10000000 0x30000000>;
+ qcom,fd-misc-reg-settings = <0x20 0x2 0x3>,
+ <0x24 0x2 0x3>;
+ qcom,fd-core-reg-settings = <0x8 0x20 0xffffffff>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt
new file mode 100644
index 0000000..96cb28d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt
@@ -0,0 +1,26 @@
+* QTI MSM IR CUT
+
+Required properties:
+- cell-index : ir cut filter hardware core index
+- compatible :
+ - "qcom,ir-cut"
+
+Optional properties:
+- gpios : should specify the gpios to be used for the ir cut filter.
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir cut filter
+- 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)
+- label : should contain unique ir cut filter name
+Example:
+
+qcom,ir-cut@60 {
+ cell-index = <0>;
+ compatible = "qcom,ir-cut";
+ label = "led-ir-label";
+ gpios = <&tlmm 60 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "LED_IR_EN";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-led.txt b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt
new file mode 100644
index 0000000..7e66fa0
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt
@@ -0,0 +1,26 @@
+* QTI MSM IR LED
+
+Required properties:
+- cell-index : ir led hardware core index
+- compatible :
+ - "qcom,ir-led"
+
+Optional properties:
+- gpios : should specify the gpios to be used for the ir led.
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir led
+- 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)
+- label : should contain unique ir led name
+Example:
+
+qcom,ir-led {
+ cell-index = <0>;
+ compatible = "qcom,ir-led";
+ label = "led-ir-label";
+ gpios = <&tlmm 60 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "LED_IR_EN";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-irqrouter.txt b/Documentation/devicetree/bindings/media/video/msm-irqrouter.txt
new file mode 100644
index 0000000..fbcb74d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-irqrouter.txt
@@ -0,0 +1,18 @@
+* Qualcomm Technologies, Inc. MSM IRQ Router
+
+Required properties:
+- cell-index: irq router hardware core index
+- compatible :
+ - "qcom,irqrouter"
+- reg : offset and length of the register set for the device
+ for the irqrouter operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+
+Example:
+
+ qcom,irqrouter@0xfda0c000 {
+ cell-index = <0>;
+ compatible = "qcom,irqrouter";
+ reg = <0xfda00000 0x100>;
+ reg-names = "irqrouter";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-ispif.txt b/Documentation/devicetree/bindings/media/video/msm-ispif.txt
new file mode 100644
index 0000000..d635a4e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ispif.txt
@@ -0,0 +1,159 @@
+* Qualcomm Technologies, Inc. MSM ISPIF
+
+Required properties:
+- cell-index: ispif hardware core index
+- compatible :
+ - "qcom,ispif"
+ - "qcom,ispif-v3.0"
+- reg : offset and length of the register set for the device
+ for the ispif operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should contain the ispif interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- camss-vdd-supply: phandle to GDSC regulator.
+- mmagic-vdd-supply: phandle to mmagic regulator.
+- vfe0-vdd-supply: phandle to vfe0 regulator.
+- vfe1-vdd-supply: phandle to vfe1 regulator.
+- clocks: list of phandles to the clock controller device and coresponding
+ clock names.
+- clock-names: name of the clocks required for the device used by the consumer.
+- qcom,clock-rates: clock rate in Hz.
+- qcom,clock-control: The valid fields are "NO_SET_RATE", "INIT_RATE" and
+ "SET_RATE". "NO_SET_RATE" the corresponding clock is enabled without setting
+ the rate assuming some other driver has already set it to appropriate rate.
+ "INIT_RATE" clock rate is not queried assuming some other driver has set
+ the clock rate and ispif will set the the clock to this rate.
+ "SET_RATE" clock is enabled and the rate is set to the value specified
+ in the property qcom,clock-rates.
+
+Optional properties:
+- qcom,num-isps: The number of ISPs the ISPIF module is connected to. If not set
+ the default value used is 1
+
+Example:
+
+ qcom,ispif@fda0a000 {
+ cell-index = <0>;
+ compatible = "qcom,ispif";
+ reg = <0xfda0a000 0x300>;
+ reg-names = "ispif";
+ interrupts = <0 55 0>;
+ interrupt-names = "ispif";
+ camss-vdd-supply = <&gdsc_camss_top>;
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ vfe1-vdd-supply = <&gdsc_vfe1>;
+ clocks = <&clock_mmss clk_camss_ispif_ahb_clk>,
+ <&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_camss_csi0_clk>,
+ <&clock_mmss clk_camss_csi0rdi_clk>,
+ <&clock_mmss clk_camss_csi0pix_clk>,
+ <&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_camss_csi1_clk>,
+ <&clock_mmss clk_camss_csi1rdi_clk>,
+ <&clock_mmss clk_camss_csi1pix_clk>,
+ <&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_csi2pix_clk>,
+ <&clock_mmss clk_csi3_clk_src>,
+ <&clock_mmss clk_camss_csi3_clk>,
+ <&clock_mmss clk_camss_csi3rdi_clk>,
+ <&clock_mmss clk_camss_csi3pix_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_vfe0_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe1_clk_src>,
+ <&clock_mmss clk_camss_vfe1_clk>,
+ <&clock_mmss clk_camss_csi_vfe1_clk>;
+ clock-names = "ispif_ahb_clk",
+ "csi0_src_clk", "csi0_clk",
+ "csi0_pix_clk", "csi0_rdi_clk",
+ "csi1_src_clk", "csi1_clk",
+ "csi1_pix_clk", "csi1_rdi_clk",
+ "csi2_src_clk", "csi2_clk",
+ "csi2_pix_clk", "csi2_rdi_clk",
+ "csi3_src_clk", "csi3_clk",
+ "csi3_pix_clk", "csi3_rdi_clk",
+ "vfe0_clk_src", "camss_vfe_vfe0_clk", "camss_csi_vfe0_clk",
+ "vfe1_clk_src", "camss_vfe_vfe1_clk", "camss_csi_vfe1_clk";
+ qcom,clock-rates = <0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 0 0 0
+ 0 0 0>;
+ qcom,clock-control = "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE";
+
+ };
+
+or
+
+ qcom,ispif@fda0a000 {
+ cell-index = <0>;
+ compatible = "qcom,ispif-v3.0", "qcom,ispif";
+ reg = <0xfda0a000 0x300>;
+ reg-names = "ispif";
+ interrupts = <0 55 0>;
+ interrupt-names = "ispif";
+ qcom,num-isps = <2>
+ vdd-supply = <&gdsc_camss_top>;
+ clocks = <&clock_mmss clk_camss_ispif_ahb_clk>,
+ <&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_camss_csi0_clk>,
+ <&clock_mmss clk_camss_csi0rdi_clk>,
+ <&clock_mmss clk_camss_csi0pix_clk>,
+ <&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_camss_csi1_clk>,
+ <&clock_mmss clk_camss_csi1rdi_clk>,
+ <&clock_mmss clk_camss_csi1pix_clk>,
+ <&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_csi2pix_clk>,
+ <&clock_mmss clk_csi3_clk_src>,
+ <&clock_mmss clk_camss_csi3_clk>,
+ <&clock_mmss clk_camss_csi3rdi_clk>,
+ <&clock_mmss clk_camss_csi3pix_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_vfe0_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe1_clk_src>,
+ <&clock_mmss clk_camss_vfe1_clk>,
+ <&clock_mmss clk_camss_csi_vfe1_clk>;
+ clock-names = "ispif_ahb_clk",
+ "csi0_src_clk", "csi0_clk",
+ "csi0_pix_clk", "csi0_rdi_clk",
+ "csi1_src_clk", "csi1_clk",
+ "csi1_pix_clk", "csi1_rdi_clk",
+ "csi2_src_clk", "csi2_clk",
+ "csi2_pix_clk", "csi2_rdi_clk",
+ "csi3_src_clk", "csi3_clk",
+ "csi3_pix_clk", "csi3_rdi_clk",
+ "vfe0_clk_src", "camss_vfe_vfe0_clk", "camss_csi_vfe0_clk",
+ "vfe1_clk_src", "camss_vfe_vfe1_clk", "camss_csi_vfe1_clk";
+ qcom,clock-rates = <0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 0 0 0
+ 0 0 0>;
+ qcom,clock-control = "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE";
+
+ };
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-jpeg.txt b/Documentation/devicetree/bindings/media/video/msm-jpeg.txt
new file mode 100644
index 0000000..5aed7c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-jpeg.txt
@@ -0,0 +1,140 @@
+* Qualcomm Technologies, Inc. MSM JPEG
+
+Required properties:
+- cell-index: jpeg hardware core index
+- compatible :
+ - "qcom,jpeg"
+ - "qcom,jpeg_dma"
+- reg : offset and length of the register set of jpeg device and vbif device
+ for the jpeg operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should contain the jpeg interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- clock-names : names of clocks required for the device.
+- clocks : clocks required for the device.
+- qcom, clock-rates: rates of the required clocks.
+- vdd-supply: phandle to GDSC regulator controlling JPEG core.
+- mmagic-vdd-supply: phandle to GDSC regulator controlling mmagic.
+- camss-vdd-supply: phandle to GDSC regulator controlling camss.
+
+Optional properties:
+- qcom,vbif-reg-settings: relative address offsets and value pairs for VBIF registers.
+- qcom,qos-reg-settings: relative address offsets and value pairs for QoS registers.
+- qcom,prefetch-reg-settings: relative address offsets and value pairs for
+ MMU prefetch registers.
+
+Example:
+
+ qcom,jpeg@a1c000 {
+ cell-index = <0>;
+ compatible = "qcom,jpeg";
+ reg = <0xa1c000 0x4000>,
+ <0xa60000 0x3000>;
+ reg-names = "jpeg";
+ interrupts = <0 316 0>;
+ interrupt-names = "jpeg";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk", "smmu_jpeg_axi_clk",
+ "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk",
+ "mmagic_camss_axi_clk";
+ clocks = <&clock_mmss clk_camss_jpeg0_clk>,
+ <&clock_mmss clk_camss_jpeg_ahb_clk>,
+ <&clock_mmss clk_camss_jpeg_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_smmu_jpeg_axi_clk>,
+ <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>;
+ qcom,clock-rates = <320000000 0 0 0 0 0 0 0 0>;
+ qcom,vbif-reg-settings = <0x4 0x1>,
+ <0xb0 0x00100010>,
+ <0xc0 0x10001000>;
+ qcom,qos-reg-settings = <0x28 0x00000008>;
+ qcom,prefetch-reg-settings = <0x30c 0x1111>,
+ <0x318 0x31>,
+ <0x324 0x31>,
+ <0x330 0x31>,
+ <0x33c 0x0>;
+ status = "ok";
+ };
+
+ qcom,jpeg@a24000 {
+ cell-index = <2>;
+ compatible = "qcom,jpeg";
+ reg = <0xa24000 0x4000>,
+ <0xa60000 0x3000>;
+ reg-names = "jpeg";
+ interrupts = <0 318 0>;
+ interrupt-names = "jpeg";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk", "smmu_jpeg_axi_clk",
+ "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk",
+ "mmagic_camss_axi_clk";
+ clocks = <&clock_mmss clk_camss_jpeg2_clk>,
+ <&clock_mmss clk_camss_jpeg_ahb_clk>,
+ <&clock_mmss clk_camss_jpeg_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_smmu_jpeg_axi_clk>,
+ <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>;
+ qcom,clock-rates = <266670000 0 0 0 0 0 0 0 0>;
+ qcom,vbif-reg-settings = <0x4 0x1>,
+ <0xb0 0x00100010>,
+ <0xc0 0x10001000>;
+ qcom,qos-reg-settings = <0x28 0x00000008>;
+ qcom,prefetch-reg-settings = <0x30c 0x1111>,
+ <0x318 0x0>,
+ <0x324 0x31>,
+ <0x330 0x31>,
+ <0x33c 0x31>;
+ status = "ok";
+ };
+
+ qcom,jpeg@aa0000 {
+ cell-index = <3>;
+ compatible = "qcom,jpeg_dma";
+ reg = <0xaa0000 0x4000>,
+ <0xa60000 0x3000>;
+ reg-names = "jpeg";
+ interrupts = <0 304 0>;
+ interrupt-names = "jpeg";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk", "smmu_jpeg_axi_clk",
+ "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk",
+ "mmagic_camss_axi_clk";
+ clocks = <&clock_mmss clk_camss_jpeg_dma_clk>,
+ <&clock_mmss clk_camss_jpeg_ahb_clk>,
+ <&clock_mmss clk_camss_jpeg_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_smmu_jpeg_axi_clk>,
+ <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>;
+ qcom,clock-rates = <266670000 0 0 0 0 0 0 0 0>;
+ qcom,vbif-reg-settings = <0x4 0x1>,
+ <0xb0 0x00100010>,
+ <0xc0 0x10001000>;
+ qcom,qos-reg-settings = <0x28 0x00000008>;
+ qcom,prefetch-reg-settings = <0x18c 0x11>,
+ <0x1a0 0x31>,
+ <0x1b0 0x31>;
+ status = "ok";
+ };
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-jpegdma.txt b/Documentation/devicetree/bindings/media/video/msm-jpegdma.txt
new file mode 100644
index 0000000..a1d6042
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-jpegdma.txt
@@ -0,0 +1,74 @@
+* Qualcomm Technologies, Inc. MSM JPEG DMA
+
+Jpeg dma hardware block.
+
+Jpeg dma can replicate and downscale yuv frames.
+Supported formats: Monochrome, NV12 and NV21.
+
+Required properties:
+- compatible : "qcom,jpegdma".
+- reg : offset and length of the register set of jpeg dma device and
+ vbif device for the jpeg dma operating in compatible mode.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should contain the jpeg interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+- mmagic-vdd-supply: phandle to GDSC regulator controlling mmagic.
+- camss-vdd-supply: phandle to GDSC regulator controlling camss.
+- clock-names : names of clocks required for the device.
+- clocks : clocks required for the device.
+- qcom,clock-rates: should specify clock rates in Hz to each clocks
+ property defined.
+- qcom,max-ds-factor: should specify the max dma downscale factor,
+ supported by HW.
+- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+ below optional properties:
+ - qcom,msm-bus,name
+ - qcom,msm-bus,num-cases
+ - qcom,msm-bus,num-paths
+ - qcom,msm-bus,vectors-KBps
+
+Optional properties:
+- qcom,vbif-reg-settings: relative address offsets and value pairs for VBIF registers.
+- qcom,qos-reg-settings: relative address offsets and value pairs for QoS registers.
+- qcom,prefetch-reg-settings: relative address offsets and value pairs for
+ MMU prefetch registers.
+
+Example:
+ qcom,jpegdma@aa0000 {
+ compatible = "qcom,jpegdma";
+ reg = <0xaa0000 0x4000>,
+ <0xa60000 0x3000>;
+ reg-names = "jpegdma_core", "jpeg_vbif";
+ interrupts = <0 304 0>;
+ interrupt-names = "jpeg";
+ mmagic-vdd-supply = <&gdsc_mmagic_camss>;
+ camss-vdd-supply = <&gdsc_camss_top>;
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "mmagic-vdd", "camss-vdd", "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk", "smmu_jpeg_axi_clk",
+ "mmss_mmagic_ahb_clk", "mmssnoc_axi_clk",
+ "mmagic_camss_axi_clk";
+ clocks = <&clock_mmss clk_camss_jpeg_dma_clk>,
+ <&clock_mmss clk_camss_jpeg_ahb_clk>,
+ <&clock_mmss clk_camss_jpeg_axi_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
+ <&clock_mmss clk_camss_ahb_clk>,
+ <&clock_mmss clk_smmu_jpeg_axi_clk>,
+ <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_gcc clk_mmssnoc_axi_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>;
+ qcom,clock-rates = <266670000 0 0 0 0 0 0 0 0>,
+ qcom,vbif-reg-settings = <0x4 0x1>;
+ qcom,prefetch-reg-settings = <0x18c 0x11>,
+ <0x1a0 0x31>,
+ <0x1b0 0x31>;
+ qcom,msm-bus,name = "msm_camera_jpeg_dma";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <62 512 0 0>,
+ <62 512 666675 666675>;
+ qcom,max-ds-factor = <128>;
+ status = "ok";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
new file mode 100644
index 0000000..aaf1344
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
@@ -0,0 +1,208 @@
+* Qualcomm Technologies, Inc. MSM VFE
+
+Required properties for parent node:
+- compatible :
+ - "qcom,vfe"
+- #address-cells : Number of address related values in the reg field
+- #size-cells : Number of size related values in the reg field
+- ranges: How the register offsets for child translate to parent.
+
+Required properties for child node:
+- cell-index: vfe hardware core index
+- compatible :
+ - "qcom,vfe32"
+ - "qcom,vfe40"
+ - "qcom,vfe44"
+ - "qcom,vfe46"
+ - "qcom,vfe47"
+ - "qcom,vfe48"
+- reg : offset and length of the register set for the device
+ for the vfe operating in compatible mode. For parent node, add union of
+ all registers for both vfe.
+- reg-names : should specify relevant names to each reg property defined.
+ Only needed for child node.
+ - "vfe" - Required.
+ - "vfe_vbif" - Optional for "vfe32". Required for "vfe40".
+ - "vfe_fuse" - Optional.
+- interrupts : should contain the vfe interrupt.
+- interrupt-names : should specify relevant names to each interrupts
+ property defined.
+ - "vfe" - Required.
+- vdd-supply: phandle to GDSC regulator controlling VFE core.
+- qos-entries: number of QoS registers to program
+- qos-regs: relative address offsets of QoS registers
+- qos-settings: QoS values to be written to QoS registers
+- vbif-entries - number of VBIF registers to program (optional)
+- vbif-regs: relative address offsets of VBIF registers (optional)
+- vbif-settings: VBIF values to be written to VBIF registers (optional)
+- ds-entries: number danger/safe registers to program (optional)
+- ds-regs: relative address offsets of danger/safe registers (optional)
+- ds-settings: danger/safe values to be written to registers (optional)
+ NOTE: For all qos*, vbif*, ds* parameters, same SoC can have different
+ hardware versions with different entries/registers/settings. They can be
+ specified by adding version to the string e.g. qos-v2-settings. Also
+ different SoC can have same hardware version and still different QOS, VBIF,
+ and DS parameters. In this case they are exported if separate SoC version
+ specific dts files.
+- max-svs-clk: svs rate of the VFE clock in Hertz.
+- max-nominal-clk: nominal rate of the VFE clock in Hertz.
+- max-turbo-clk: turbo/high rate of the VFE clock in Hertz.
+
+Example:
+
+vfe0: qcom,vfe0@fda10000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe44";
+ reg = <0xfda10000 0x1000>,
+ <0xfda40000 0x200>,
+ <0x7801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
+ interrupts = <0 57 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
+ 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
+ 0xaaaaaaaa 0x0002aaaa>;
+ qos-v2-settings = <0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0x0001aaa9>;
+ qos-v3-settings = <0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0x0001aaa9>;
+ vbif-entries = <17>;
+ vbif-regs = <0x4 0xb0 0xb4 0xb8 0xc0 0xc4 0xc8 0xd0
+ 0xd4 0xd8 0xdc 0xf0 0x178 0x17c 0x124 0x160
+ 0x164>;
+ vbif-settings = <0x1 0x01010101 0x01010101 0x10010110
+ 0x10101010 0x10101010 0x10101010 0x00001010
+ 0x00001010 0x00000707 0x00000707 0x00000030
+ 0x00000fff 0x0fff0fff 0x00000001 0x22222222
+ 0x00002222>;
+ vbif-v2-entries = <16>;
+ vbif-v2-regs = <0x4 0xb0 0xb4 0xb8 0xc0 0xc4 0xc8 0xd0
+ 0xd4 0xd8 0xf0 0x178 0x17c 0x124 0x160
+ 0x164>;
+ vbif-v2-settings = <0x1 0x10101010 0x10101010 0x10101010
+ 0x10101010 0x10101010 0x10101010 0x00000010
+ 0x00000010 0x00000707 0x00000010 0x00000fff
+ 0x0fff0fff 0x00000003 0x22222222 0x00002222>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x00000103>;
+ max-clk-svs = <300000000>;
+ max-clk-nominal = <465000000>;
+ max-clk-turbo = <600000000>;
+};
+
+vfe1: qcom,vfe1@fda14000 {
+ cell-index = <1>;
+ compatible = "qcom,vfe44";
+ reg = <0xfda14000 0x1000>,
+ <0xfda40000 0x200>,
+ <0x7801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
+ interrupts = <0 58 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
+ 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
+ 0xaaaaaaaa 0x0002aaaa>;
+ qos-v2-settings = <0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0x0001aaa9>;
+ qos-v3-settings = <0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0xaaa9aaa9 0xaaa9aaa9
+ 0xaaa9aaa9 0x0001aaa9>;
+ vbif-entries = <17>;
+ vbif-regs = <0x4 0xb0 0xb4 0xb8 0xc0 0xc4 0xc8 0xd0
+ 0xd4 0xd8 0xdc 0xf0 0x178 0x17c 0x124 0x160
+ 0x164>;
+ vbif-settings = <0x1 0x01010101 0x01010101 0x10010110
+ 0x10101010 0x10101010 0x10101010 0x00001010
+ 0x00001010 0x00000707 0x00000707 0x00000030
+ 0x00000fff 0x0fff0fff 0x00000001 0x22222222
+ 0x00002222>;
+ vbif-v2-entries = <16>;
+ vbif-v2-regs = <0x4 0xb0 0xb4 0xb8 0xc0 0xc4 0xc8 0xd0
+ 0xd4 0xd8 0xf0 0x178 0x17c 0x124 0x160
+ 0x164>;
+ vbif-v2-settings = <0x1 0x10101010 0x10101010 0x10101010
+ 0x10101010 0x10101010 0x10101010 0x00000010
+ 0x00000010 0x00000707 0x00000010 0x00000fff
+ 0x0fff0fff 0x00000003 0x22222222 0x00002222>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x44441111 0x44441111
+ 0x44441111 0x00000103>;
+ max-clk-svs = <300000000>;
+ max-clk-nominal = <465000000>;
+ max-clk-turbo = <600000000>;
+};
+
+qcom,vfe {
+ compatible = "qcom,vfe";
+ num_child = <2>;
+};
+
+In version specific file one needs to move only entries that differ between
+SoC versions with same VFE HW version:
+
+ &vfe0 {
+ qos-entries = <8>;
+ qos-regs = <0x378 0x37C 0x380 0x384 0x388 0x38C
+ 0x390 0x394>;
+ qos-settings = <0xAAA9AAA9
+ 0xAAA9AAA9
+ 0xAAA9AAA9
+ 0xAAA9AAA9
+ 0xAAA9AAA9
+ 0xAAA9AAA9
+ 0xAAA9AAA9
+ 0x0001AAA9>;
+ vbif-entries = <1>;
+ vbif-regs = <0x124>;
+ vbif-settings = <0x3>;
+ ds-entries = <17>;
+ ds-regs = <0xBD8 0xBDC 0xBE0 0xBE4 0xBE8
+ 0xBEC 0xBF0 0xBF4 0xBF8 0xBFC 0xC00
+ 0xC04 0xC08 0xC0C 0xC10 0xC14 0xC18>;
+ ds-settings = <0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x44441111
+ 0x00000103>;
+ max-clk-svs = <300000000>;
+ max-clk-nominal = <465000000>;
+ max-clk-turbo = <600000000>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-vpu.txt b/Documentation/devicetree/bindings/media/video/msm-vpu.txt
new file mode 100644
index 0000000..6aea66b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-vpu.txt
@@ -0,0 +1,60 @@
+* Qualcomm Technologies, Inc. MSM VPU
+
+VPU (Video Processing Unit) applies high quality video post-processing
+functions like noise reduction, deinterlacing, scaling, etc in real-time
+on streaming video.
+
+
+Required properties:
+- compatible:
+ - "qcom,vpu"
+- reg: Specify offset and length of the device register sets.
+- reg-names: Names corresponding to the defined register sets.
+ - "vpu_csr": CSR registers
+ - "vpu_smem": Shared memory
+ - "vpu_vbif": VBIF registers (optional)
+- interrupts: Specify the vpu interrupts.
+- interrupt-names: Names corresponding to the defined interrupts list.
+ - "vpu_wdog": Watchdog interrupt
+ - "vpu_hfi": Firmware to Host interrupt
+- clock-names: Array of clocks that the driver requires for the device.
+ The names here correspond to the clock names used in clk_get(<name>).
+- qcom,bus-load-vector-tbl: Vectors of <load, ab, ib>. The (ab,ib) pairs are
+ ddr bus bandwidths to be requested at corresponding video processing load.
+ Vectors should be in ascending order of load, and their number is variable.
+- vdd-supply: regulator that supplies the vpu.
+
+Optional properties:
+- qcom,enabled-iommu-maps: List of IOMMU maps to be enabled, defined by name.
+ If this property is not defined or invalid, then device assumes contiguous
+ buffers. Valid iommu names are:
+ - "vpu_nonsecure": IOMMU for accessing non-secure video buffers.
+ - "vpu_secure": IOMMU for accessing secure video buffers.
+ - "vpu_firmware": IOMMU for loading firmware image.
+- qcom,vbif-reg-presets: List of offset-value pairs for VBIF registers to be
+ programmed. The offsets are from the base register specified in 'vpu_vbif'.
+ This is used to program default register values for QoS settings, etc.
+
+Example:
+ qcom,vpu@fdc00000 {
+ compatible = "qcom,vpu";
+ reg = <0xfdc00000 0xff000>,
+ <0xbfe00000 0x100000>;
+ reg-names = "vpu_csr", "vpu_smem";
+ interrupts = <0 44 0>, <0 45 0>;
+ interrupt-names = "vpu_wdog", "vpu_hfi";
+ clock-names = "core_clk", "bus_clock", "iface_clk";
+ qcom,maple-clk-load-freq-tbl = <100000 50000000>,
+ <500000 400000000>;
+ qcom,vdp-clk-load-freq-tbl = <200000 100000000>,
+ <400000 320000000>;
+ qcom,bus-clk-load-freq-tbl = <100000 40000000>,
+ <200000 80000000>;
+ qcom,bus-load-vector-tbl = <0 0 0>,
+ <489600 536000 1600000>,
+ <979200 2024000 1600000>;
+ qcom,enabled-iommu-maps = "vpu_nonsecure", "vpu_secure";
+ qcom,vbif-reg-presets = <0xb0138 0x43ff>,
+ <0xb0178 0xff12350e>;
+ vdd-supply = <&gdsc_vpu>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/ovti-image-sensor.txt b/Documentation/devicetree/bindings/media/video/ovti-image-sensor.txt
new file mode 100644
index 0000000..cef9cf5
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/ovti-image-sensor.txt
@@ -0,0 +1,9 @@
+OmniVision Image Sensor Device Tree Bindings.
+========================================
+
+Boards with the OmniVision Image Sensor shall have the following properties:
+
+Required root node properties:
+ - compatible:
+ - "ovti,ov8865" : OmniVision OV8865 8 megapixel Image Sensor.
+ - "ovti,ov5648" : OmniVision OV5648 5 megapixel Image Sensor.
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
new file mode 100644
index 0000000..3481b80
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -0,0 +1,201 @@
+Qualcomm Techonologies, Inc. QPNP PMIC QGAUGE (QG) Device
+
+QPNP PMIC QGAUGE device provides the ability to gauge the State-of-Charge
+of the battery. It provides an interface to the clients to read various
+battery related parameters.
+
+=======================
+Required Node Structure
+=======================
+
+Qgauge device must be described in two level of nodes. The first level
+describes the properties of the Qgauge device and the second level
+describes the peripherals managed/used of the module.
+
+====================================
+First Level Node - QGAUGE device
+====================================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,qpnp-qg".
+
+- qcom,pmic-revid
+ Usage: required
+ Value type: <phandle>
+ Definition: Should specify the phandle of PMIC revid module. This is
+ used to identify the PMIC subtype.
+
+- qcom,qg-vadc
+ Usage: required
+ Value type: <phandle>
+ Definition: Phandle for the VADC node, it is used for BATT_ID and
+ BATT_THERM readings.
+
+- qcom,vbatt-empty-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ vbatt-empty interrupt fires. The SOC is forced to 0
+ when this interrupt fires. If not specified, the
+ default value is 3200 mV.
+
+- qcom,vbatt-cutoff-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ the Qgauge algorithm converges to 0 SOC. If not specified
+ the default value is 3400 mV.
+
+- qcom,vbatt-low-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ the VBAT_LOW interrupt fires. Software can take necessary
+ the action when this interrupt fires. If not specified
+ the default value is 3500 mV.
+
+- qcom,qg-iterm-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery current (in mA) at which the the QG algorithm
+ converges the SOC to 100% during charging and can be used to
+ terminate charging. If not specified, the default value is
+ 100mA.
+
+- qcom,delta-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: The SOC percentage increase at which the SOC is
+ periodically reported to the userspace. If not specified,
+ the value defaults to 1%.
+
+- qcom,s2-fifo-length
+ Usage: optional
+ Value type: <u32>
+ Definition: The total number if FIFO samples which need to be filled up
+ in S2 state of QG to fire the FIFO DONE interrupt.
+ Minimum value = 1 Maximum Value = 8. If not specified,
+ the default value is 5.
+
+- qcom,s2-acc-length
+ Usage: optional
+ Value type: <u32>
+ Definition: The number of distinct V & I samples to be accumulated
+ in each FIFO in the S2 state of QG.
+ Minimum Value = 0 Maximum Value = 256. If not specified,
+ the default value is 128.
+
+- qcom,s2-acc-interval-ms
+ Usage: optional
+ Value type: <u32>
+ Definition: The time (in ms) between each of the V & I samples being
+ accumulated in FIFO.
+ Minimum Value = 0 ms Maximum Value = 2550 ms. If not
+ specified the default value is 100 ms.
+
+- qcom,ocv-timer-expiry-min
+ Usage: optional
+ Value type: <u32>
+ Definition: The maximum time (in minutes) for the QG to transition from
+ S3 to S2 state.
+ Minimum Value = 2 min Maximum Value = 30 min. If not
+ specified the hardware default is set to 14 min.
+
+- qcom,ocv-tol-threshold-uv
+ Usage: optional
+ Value type: <u32>
+ Definition: The OCV detection error tolerance (in uV). The maximum
+ voltage allowed between 2 VBATT readings in the S3 state
+ to qualify for a valid OCV.
+ Minimum Value = 0 uV Maximum Value = 12262 uV Step = 195 uV
+
+- qcom,s3-entry-fifo-length
+ Usage: optional
+ Value type: <u32>
+ Definition: The minimum number if FIFO samples which have to qualify the
+ S3 IBAT entry threshold (qcom,s3-entry-ibat-ua) for QG
+ to enter into S3 state.
+ Minimum Value = 1 Maximum Value = 8. The hardware default
+ is configured to 3.
+
+- qcom,s3-entry-ibat-ua
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery current (in uA) for the QG to enter into the S3
+ state. The QG algorithm enters into S3 if the battery
+ current is lower than this threshold consecutive for
+ the FIFO length specified in 'qcom,s3-entry-fifo-length'.
+ Minimum Value = 0 uA Maximum Value = 155550 uA
+ Step = 610 uA.
+
+- qcom,s3-exit-ibat-ua
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery current (in uA) for the QG to exit S3 state.
+ If the battery current is higher than this threshold QG
+ exists S3 state.
+ Minimum Value = 0 uA Maximum Value = 155550 uA
+ Step = 610 uA.
+
+- qcom,rbat-conn-mohm
+ Usage: optional
+ Value type: <u32>
+ Definition: Resistance of the battery connectors in mOhms.
+
+==========================================================
+Second Level Nodes - Peripherals managed by QGAUGE driver
+==========================================================
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Addresses and sizes for the specified peripheral
+
+- interrupts
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Interrupt mapping as per the interrupt encoding
+
+- interrupt-names
+ Usage: optional
+ Value type: <stringlist>
+ Definition: Interrupt names. This list must match up 1-to-1 with the
+ interrupts specified in the 'interrupts' property.
+
+========
+Example
+========
+
+pmi632_qg: qpnp,qg {
+ compatible = "qcom,qpnp-qg";
+ qcom,pmic-revid = <&pmi632_revid>;
+ qcom,qg-vadc = <&pmi632_vadc>;
+ qcom,vbatt-empty-mv = <3200>;
+ qcom,vbatt-low-mv = <3500>;
+ qcom,vbatt-cutoff-mv = <3400>;
+ qcom,qg-iterm-ma = <100>;
+
+ qcom,qgauge@4800 {
+ status = "okay";
+ reg = <0x4800 0x100>;
+ interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x6 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "qg-batt-missing",
+ "qg-vbat-low",
+ "qg-vbat-empty",
+ "qg-fifo-done",
+ "qg-good-ocv",
+ "qg-fsm-state-chg",
+ "qg-event";
+ };
+
+ qcom,qg-sdam@b000 {
+ status = "okay";
+ reg = <0xb000 0x100>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 58c9bf8..fa11e70 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -293,16 +293,17 @@
- compatible : "qcom,msm-pcm-hostless"
-* audio-load-mod
+* msm-audio-apr
Required properties:
- - compatible : "qcom,audio-load-mod"
+ - compatible : "qcom,msm-audio-apr"
+ This device is added to represent APR module.
Optional properties:
- - compatible : "qcom,audio-test-mod"
- Add this compatible as child device to load-module device.
+ - compatible : "qcom,msm-audio-apr-dummy"
+ Add this compatible as child device to msm-audio-apr device.
This child device is added after lpass is up to invoke
deferred probe devices.
@@ -655,10 +656,10 @@
compatible = "qcom,msm-pcm-hostless";
};
- audio_load_mod {
- compatible = "qcom,audio-load-mod";
- audio_test_mod {
- compatible = "qcom,audio-test-mod";
+ qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ msm_audio_apr_dummy {
+ compatible = "qcom,msm-audio-apr-dummy";
};
};
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index 67ffaed..6ff6e9b 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -19,6 +19,7 @@
should be "qcom,sdm630-tsens" for 630 TSENS driver.
should be "qcom,sdm845-tsens" for SDM845 TSENS driver.
should be "qcom,tsens24xx" for 2.4 TSENS controller.
+ should be "qcom,msm8937-tsens" for 8937 TSENS driver.
The compatible property is used to identify the respective controller to use
for the corresponding SoC.
- reg : offset and length of the TSENS registers with associated property in reg-names
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index e13a6a3..5256edd 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -125,10 +125,6 @@
- qcom,hsusb-l1-supported: If present, the device supports l1 (Link power
management).
- qcom,no-selective-suspend: If present selective suspend is disabled on hub ports.
-- qcom,hsusb-otg-mpm-dpsehv-int: If present, indicates mpm interrupt to be
- configured for detection of dp line transition during VDD minimization.
-- qcom,hsusb-otg-mpm-dmsehv-int: If present, indicates mpm interrupt to be
- configured for detection of dm line transition during VDD minimization.
- pinctrl-names : This should be defined if a target uses gpio and pinctrl framework.
See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt.
It should specify the names of the configs that pinctrl can install in driver
@@ -175,8 +171,6 @@
devicetree/bindings/usb/android-dev.txt for details about "streaming-func" property.
- qcom,axi-prefetch-enable: If present, AXI64 interface will be used for transferring data
to/from DDR by controller.
-- qcom,enable-sdp-typec-current-limit: Indicates whether type-c current for SDP CHARGER to
- be limited.
- qcom,enable-phy-id-pullup: If present, PHY can keep D+ pull-up resistor on USB ID line
during cable disconnect.
- qcom,max-svs-sysclk-rate: Indicates system clock frequency voted by driver in
@@ -220,8 +214,6 @@
HSUSB_3p3-supply = <&pm8226_l20>;
qcom,vdd-voltage-level = <1 5 7>;
qcom,dp-manual-pullup;
- qcom,hsusb-otg-mpm-dpsehv-int = <49>;
- qcom,hsusb-otg-mpm-dmsehv-int = <58>;
qcom,max-nominal-sysclk-rate = <133330000>;
qcom,max-svs-sysclk-rate = <100000000>;
qcom,pm-qos-latency = <59>;
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index e996ba5..01acb65 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -161,6 +161,7 @@
linux Linux-specific binding
lltc Linear Technology Corporation
lsi LSI Corp. (LSI Logic)
+lt Lontium Semiconductor Corporation
marvell Marvell Technology Group Ltd.
maxim Maxim Integrated Products
meas Measurement Specialties
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
index e6dc45a..ad1e6ca 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -319,7 +319,7 @@
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_pn2>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_qhm_blsp1: mas-qhm-blsp1 {
@@ -332,7 +332,7 @@
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_pn3>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_qhm_qdss_bam: mas-qhm-qdss-bam {
@@ -342,20 +342,21 @@
qcom,agg-ports = <1>;
qcom,qport = <11>;
qcom,connections = <&slv_qhs_crypto_cfg
- &slv_qhs_pdm &slv_qhs_pcie_parf
- &slv_qhs_tlmm &slv_qhs_spmi_fetcher
- &slv_qhs_prng &slv_qhs_qpic
- &slv_qxs_imem &slv_qhs_snoc_cfg
- &slv_qhs_audio &slv_qhs_sdc1
- &slv_qhs_aoss &slv_qhs_ipa
- &slv_qns_snoc_memnoc &slv_qhs_usb3_phy
- &slv_qhs_aop &slv_qhs_tcsr
- &slv_qhs_blsp1 &slv_xs_sys_tcu_cfg
- &slv_qhs_usb3 &slv_qhs_clk_ctl>;
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
+ &slv_qhs_usb3_phy &slv_qhs_aop
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_sn8>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_qhm_qpic: mas-qhm-qpic {
@@ -393,17 +394,18 @@
qcom,buswidth = <8>;
qcom,agg-ports = <1>;
qcom,connections = <&slv_qhs_crypto_cfg
- &slv_qhs_snoc_cfg &slv_qhs_sdc1
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
&slv_qhs_aoss &slv_qhs_spmi_fetcher
&slv_qhs_pdm &slv_qns_snoc_memnoc
&slv_qhs_tcsr &slv_xs_qdss_stm
&slv_qhs_qpic &slv_qxs_imem
&slv_qhs_ipa &slv_qhs_usb3_phy
&slv_qhs_aop &slv_qhs_blsp1
- &slv_qhs_pcie_parf &slv_qhs_audio
- &slv_qxs_pcie &slv_qhs_tlmm
- &slv_qhs_prng &slv_xs_sys_tcu_cfg
- &slv_qhs_clk_ctl &slv_qhs_usb3>;
+ &slv_qhs_sdc1 &slv_qhs_pcie_parf
+ &slv_qhs_audio &slv_qxs_pcie
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_sn7>;
};
@@ -414,17 +416,17 @@
qcom,buswidth = <8>;
qcom,agg-ports = <1>;
qcom,connections = <&slv_qhs_crypto_cfg
- &slv_qhs_snoc_cfg &slv_qhs_sdc1
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
&slv_qhs_aoss &slv_qhs_spmi_fetcher
&slv_qhs_pdm &slv_qns_snoc_memnoc
&slv_qhs_tcsr &slv_xs_qdss_stm
&slv_qhs_qpic &slv_qxs_imem
&slv_qhs_ipa &slv_qhs_usb3_phy
&slv_qhs_aop &slv_qhs_blsp1
- &slv_qhs_pcie_parf &slv_qhs_audio
- &slv_qhs_tlmm &slv_qhs_prng
- &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
- &slv_qhs_usb3>;
+ &slv_qhs_sdc1 &slv_qhs_pcie_parf
+ &slv_qhs_audio &slv_qhs_tlmm
+ &slv_qhs_prng &slv_xs_sys_tcu_cfg
+ &slv_qhs_clk_ctl &slv_qhs_usb3>;
qcom,bus-dev = <&fab_system_noc>;
};
@@ -434,16 +436,17 @@
qcom,buswidth = <8>;
qcom,agg-ports = <1>;
qcom,connections = <&slv_qhs_crypto_cfg
- &slv_qhs_pdm &slv_qhs_pcie_parf
- &slv_qhs_tlmm &slv_qhs_spmi_fetcher
- &slv_qhs_prng &slv_qhs_qpic
- &slv_qxs_imem &slv_qhs_snoc_cfg
- &slv_qhs_audio &slv_qhs_sdc1
- &slv_qhs_aoss &slv_qhs_ipa
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qhs_tcsr
+ &slv_xs_qdss_stm &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
&slv_qhs_usb3_phy &slv_qhs_aop
- &slv_qhs_tcsr &slv_qhs_blsp1
- &slv_xs_sys_tcu_cfg &slv_qhs_usb3
- &slv_xs_qdss_stm &slv_qhs_clk_ctl>;
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_sn9>;
};
@@ -458,7 +461,7 @@
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_ce>, <&bcm_pn5>;
qcom,ap-owned;
- qcom,prio = <2>;
+ qcom,prio = <0>;
};
mas_qxm_ipa: mas-qxm-ipa {
@@ -471,7 +474,7 @@
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_sn11>;
qcom,ap-owned;
- qcom,prio = <2>;
+ qcom,prio = <0>;
qcom,forwarding;
};
@@ -484,7 +487,7 @@
qcom,connections = <&slv_qxs_pcie>;
qcom,bus-dev = <&fab_system_noc>;
qcom,ap-owned;
- qcom,prio = <2>;
+ qcom,prio = <0>;
};
mas_xm_emac: mas-xm-emac {
@@ -496,7 +499,7 @@
qcom,connections = <&slv_qns_aggre_noc>;
qcom,bus-dev = <&fab_system_noc>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_xm_pcie: mas-xm-pcie {
@@ -508,7 +511,7 @@
qcom,connections = <&slv_qns_aggre_noc>;
qcom,bus-dev = <&fab_system_noc>;
qcom,ap-owned;
- qcom,prio = <2>;
+ qcom,prio = <0>;
qcom,forwarding;
};
@@ -519,20 +522,21 @@
qcom,agg-ports = <1>;
qcom,qport = <3>;
qcom,connections = <&slv_qhs_crypto_cfg
- &slv_qhs_pdm &slv_qhs_pcie_parf
- &slv_qhs_tlmm &slv_qhs_spmi_fetcher
- &slv_qhs_prng &slv_qhs_qpic
- &slv_qxs_imem &slv_qhs_snoc_cfg
- &slv_qhs_audio &slv_qhs_sdc1
- &slv_qhs_aoss &slv_qhs_ipa
- &slv_qns_snoc_memnoc &slv_qhs_usb3_phy
- &slv_qhs_aop &slv_qhs_tcsr
- &slv_qhs_blsp1 &slv_xs_sys_tcu_cfg
- &slv_qhs_usb3 &slv_qhs_clk_ctl>;
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
+ &slv_qhs_usb3_phy &slv_qhs_aop
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_sn8>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_xm_sdc1: mas-xm-sdc1 {
@@ -545,7 +549,7 @@
qcom,bus-dev = <&fab_system_noc>;
qcom,bcms = <&bcm_pn1>;
qcom,ap-owned;
- qcom,prio = <1>;
+ qcom,prio = <0>;
};
mas_xm_usb3: mas-xm-usb3 {
@@ -557,7 +561,7 @@
qcom,connections = <&slv_qns_aggre_noc>;
qcom,bus-dev = <&fab_system_noc>;
qcom,ap-owned;
- qcom,prio = <2>;
+ qcom,prio = <0>;
};
/*Internal nodes*/
@@ -656,6 +660,15 @@
qcom,bcms = <&bcm_pn0>;
};
+ slv_qhs_emac_cfg:slv-qhs-emac-cfg {
+ cell-id = <MSM_BUS_SLAVE_EMAC_CFG>;
+ label = "slv-qhs-emac-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
slv_qhs_ipa:slv-qhs-ipa {
cell-id = <MSM_BUS_SLAVE_IPA_CFG>;
label = "slv-qhs-ipa";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dts
index 8339f9a..e90d97d 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dts
@@ -20,3 +20,11 @@
"qcom,sdxpoorwills", "qcom,cdp";
qcom,board-id = <1 0x1>, <1 0x101>;
};
+
+&pcie_ep {
+ status = "okay";
+};
+
+&pcie0 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dts
index 2240133..86d8636 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dts
@@ -20,3 +20,11 @@
"qcom,sdxpoorwills", "qcom,mtp";
qcom,board-id = <8 0x1>, <8 0x101>;
};
+
+&pcie_ep {
+ status = "okay";
+};
+
+&pcie0 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index d684fb2..c23d48b 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -911,6 +911,11 @@
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
};
+
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,rtb-size = <0x100000>;
+ };
};
#include "pmxpoorwills.dtsi"
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index d1dc88e..cb695ab 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -409,6 +409,7 @@
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
@@ -426,6 +427,7 @@
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -486,6 +488,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 2029932..eaf95af 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -420,6 +420,7 @@
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
@@ -438,6 +439,7 @@
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -503,6 +505,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 29e6335..02b4c58 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -221,6 +221,7 @@
CONFIG_SPI_SPIDEV=m
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PTP_1588_CLOCK=y
CONFIG_PINCTRL_SDXPOORWILLS=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_DEBUG_GPIO=y
@@ -318,6 +319,8 @@
CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_EP_PCIE=y
+CONFIG_EP_PCIE_HW=y
CONFIG_QPNP_REVID=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
@@ -336,6 +339,8 @@
CONFIG_QCOM_SCM=y
CONFIG_MSM_BOOT_STATS=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_BUS_CONFIG_RPMH=y
CONFIG_MSM_SMEM=y
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
@@ -351,6 +356,7 @@
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_MSM_PM=y
+CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 865406f..855f606 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -216,6 +216,7 @@
CONFIG_SLIMBUS=y
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PTP_1588_CLOCK=y
CONFIG_PINCTRL_SDXPOORWILLS=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_POWER_RESET=y
@@ -317,6 +318,8 @@
CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_EP_PCIE=y
+CONFIG_EP_PCIE_HW=y
CONFIG_QPNP_REVID=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
@@ -352,6 +355,7 @@
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_MSM_PM=y
+CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_IIO=y
CONFIG_PWM=y
@@ -387,6 +391,7 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_IPC_LOGGING=y
+CONFIG_QCOM_RTB=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_DEBUG_USER=y
CONFIG_CORESIGHT=y
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 28dcd44..dad9fcb 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/sched_energy.h>
#include <asm/cputype.h>
#include <asm/topology.h>
@@ -505,125 +506,31 @@ void store_cpu_topology(unsigned int cpuid)
update_cpu_capacity(cpuid);
}
-/*
- * ARM TC2 specific energy cost model data. There are no unit requirements for
- * the data. Data can be normalized to any reference point, but the
- * normalization must be consistent. That is, one bogo-joule/watt must be the
- * same quantity for all data, but we don't care what it is.
- */
-static struct idle_state idle_states_cluster_a7[] = {
- { .power = 25 }, /* arch_cpu_idle() (active idle) = WFI */
- { .power = 25 }, /* WFI */
- { .power = 10 }, /* cluster-sleep-l */
- };
-
-static struct idle_state idle_states_cluster_a15[] = {
- { .power = 70 }, /* arch_cpu_idle() (active idle) = WFI */
- { .power = 70 }, /* WFI */
- { .power = 25 }, /* cluster-sleep-b */
- };
-
-static struct capacity_state cap_states_cluster_a7[] = {
- /* Cluster only power */
- { .cap = 150, .power = 2967, }, /* 350 MHz */
- { .cap = 172, .power = 2792, }, /* 400 MHz */
- { .cap = 215, .power = 2810, }, /* 500 MHz */
- { .cap = 258, .power = 2815, }, /* 600 MHz */
- { .cap = 301, .power = 2919, }, /* 700 MHz */
- { .cap = 344, .power = 2847, }, /* 800 MHz */
- { .cap = 387, .power = 3917, }, /* 900 MHz */
- { .cap = 430, .power = 4905, }, /* 1000 MHz */
- };
-
-static struct capacity_state cap_states_cluster_a15[] = {
- /* Cluster only power */
- { .cap = 426, .power = 7920, }, /* 500 MHz */
- { .cap = 512, .power = 8165, }, /* 600 MHz */
- { .cap = 597, .power = 8172, }, /* 700 MHz */
- { .cap = 682, .power = 8195, }, /* 800 MHz */
- { .cap = 768, .power = 8265, }, /* 900 MHz */
- { .cap = 853, .power = 8446, }, /* 1000 MHz */
- { .cap = 938, .power = 11426, }, /* 1100 MHz */
- { .cap = 1024, .power = 15200, }, /* 1200 MHz */
- };
-
-static struct sched_group_energy energy_cluster_a7 = {
- .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a7),
- .idle_states = idle_states_cluster_a7,
- .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a7),
- .cap_states = cap_states_cluster_a7,
-};
-
-static struct sched_group_energy energy_cluster_a15 = {
- .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a15),
- .idle_states = idle_states_cluster_a15,
- .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a15),
- .cap_states = cap_states_cluster_a15,
-};
-
-static struct idle_state idle_states_core_a7[] = {
- { .power = 0 }, /* arch_cpu_idle (active idle) = WFI */
- { .power = 0 }, /* WFI */
- { .power = 0 }, /* cluster-sleep-l */
- };
-
-static struct idle_state idle_states_core_a15[] = {
- { .power = 0 }, /* arch_cpu_idle (active idle) = WFI */
- { .power = 0 }, /* WFI */
- { .power = 0 }, /* cluster-sleep-b */
- };
-
-static struct capacity_state cap_states_core_a7[] = {
- /* Power per cpu */
- { .cap = 150, .power = 187, }, /* 350 MHz */
- { .cap = 172, .power = 275, }, /* 400 MHz */
- { .cap = 215, .power = 334, }, /* 500 MHz */
- { .cap = 258, .power = 407, }, /* 600 MHz */
- { .cap = 301, .power = 447, }, /* 700 MHz */
- { .cap = 344, .power = 549, }, /* 800 MHz */
- { .cap = 387, .power = 761, }, /* 900 MHz */
- { .cap = 430, .power = 1024, }, /* 1000 MHz */
- };
-
-static struct capacity_state cap_states_core_a15[] = {
- /* Power per cpu */
- { .cap = 426, .power = 2021, }, /* 500 MHz */
- { .cap = 512, .power = 2312, }, /* 600 MHz */
- { .cap = 597, .power = 2756, }, /* 700 MHz */
- { .cap = 682, .power = 3125, }, /* 800 MHz */
- { .cap = 768, .power = 3524, }, /* 900 MHz */
- { .cap = 853, .power = 3846, }, /* 1000 MHz */
- { .cap = 938, .power = 5177, }, /* 1100 MHz */
- { .cap = 1024, .power = 6997, }, /* 1200 MHz */
- };
-
-static struct sched_group_energy energy_core_a7 = {
- .nr_idle_states = ARRAY_SIZE(idle_states_core_a7),
- .idle_states = idle_states_core_a7,
- .nr_cap_states = ARRAY_SIZE(cap_states_core_a7),
- .cap_states = cap_states_core_a7,
-};
-
-static struct sched_group_energy energy_core_a15 = {
- .nr_idle_states = ARRAY_SIZE(idle_states_core_a15),
- .idle_states = idle_states_core_a15,
- .nr_cap_states = ARRAY_SIZE(cap_states_core_a15),
- .cap_states = cap_states_core_a15,
-};
-
/* sd energy functions */
static inline
const struct sched_group_energy * const cpu_cluster_energy(int cpu)
{
- return cpu_topology[cpu].socket_id ? &energy_cluster_a7 :
- &energy_cluster_a15;
+ struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL1];
+
+ if (sched_is_energy_aware() && !sge) {
+ pr_warn("Invalid sched_group_energy for Cluster%d\n", cpu);
+ return NULL;
+ }
+
+ return sge;
}
static inline
const struct sched_group_energy * const cpu_core_energy(int cpu)
{
- return cpu_topology[cpu].socket_id ? &energy_core_a7 :
- &energy_core_a15;
+ struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL0];
+
+ if (sched_is_energy_aware() && !sge) {
+ pr_warn("Invalid sched_group_energy for CPU%d\n", cpu);
+ return NULL;
+ }
+
+ return sge;
}
static inline int cpu_corepower_flags(void)
@@ -688,4 +595,5 @@ void __init init_cpu_topology(void)
/* Set scheduler topology descriptor */
set_sched_topology(arm_topology);
+ init_sched_energy_costs();
}
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 7e5cdf2..caed4e1 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -205,7 +205,8 @@
qcs605-360camera.dtb \
qcs605-mtp.dtb \
qcs605-cdp.dtb \
- qcs605-external-codec-mtp.dtb
+ qcs605-external-codec-mtp.dtb \
+ qcs605-lc-mtp.dtb
endif
ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
diff --git a/arch/arm64/boot/dts/qcom/apq8053.dts b/arch/arm64/boot/dts/qcom/apq8053.dts
index bf9e2f2..6bb67c3 100644
--- a/arch/arm64/boot/dts/qcom/apq8053.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "apq8053.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
/ {
model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 SOC";
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
new file mode 100644
index 0000000..77f2a1d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
@@ -0,0 +1,133 @@
+/* Copyright (c) 2018, 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.
+ */
+
+&mdss_mdp {
+ dsi_hx8399c_truly_vid: qcom,mdss_dsi_hx8399c_truly_video{
+ qcom,mdss-dsi-panel-name =
+ "hx8399c video mode dsi truly panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <2160>;
+ qcom,mdss-dsi-h-front-porch = <24>;
+ qcom,mdss-dsi-h-back-porch = <24>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <40>;
+ qcom,mdss-dsi-v-front-porch = <36>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 39 01 00 00 00 00 04
+ b9 ff 83 99
+ 39 01 00 00 00 00 02
+ d2 88
+ 39 01 00 00 00 00 10
+ b1 02 04 74 94 01 32 33
+ 11 11 e6 5d 56 73 02 02
+ 39 01 00 00 00 00 10
+ b2 00 80 80 cc 05 07 5a
+ 11 10 10 00 1e 70 03 D4
+ 39 01 00 00 00 00 2d
+ b4 00 ff 59 59 0c ac 00
+ 00 0c 00 07 0a 00 28 07
+ 08 0c 21 03 00 00 00 ae
+ 87 59 59 0c ac 00 00 0c
+ 00 07 0a 00 28 07 08 0c
+ 01 00 00 ae 01
+ 39 01 00 00 05 00 22
+ d3 00 00 01 01 00 00 10
+ 10 00 00 03 00 03 00 08
+ 78 08 78 00 00 00 00 00
+ 24 02 05 05 03 00 00 00
+ 05 40
+ 39 01 00 00 05 00 21
+ d5 20 20 19 19 18 18 02
+ 03 00 01 24 24 18 18 18
+ 18 24 24 00 00 00 00 00
+ 00 00 00 2f 2f 30 30 31
+ 31
+ 39 01 00 00 05 00 21
+ d6 24 24 18 18 19 19 01
+ 00 03 02 24 24 18 18 18
+ 18 20 20 40 40 40 40 40
+ 40 40 40 2f 2f 30 30 31
+ 31
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 00 00 11
+ d8 aa aa aa aa aa aa aa
+ aa aa ba aa aa aa ba aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 01
+ 39 01 00 00 00 00 11
+ d8 82 ea aa aa 82 ea aa
+ aa 82 ea aa aa 82 ea aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 02
+ 39 01 00 00 00 00 09
+ d8 ff ff c0 3f ff ff c0
+ 3f
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 05 00 37
+ e0 08 2a 39 35 74 7c 87
+ 7f 84 8a 8e 91 93 96 9b
+ 9c 9e a5 a6 ae a1 af b2
+ 5c 58 63 74 08 2a 39 35
+ 74 7c 87 7f 84 8a 8e 91
+ 93 96 9b 9c 9e a5 a6 ae
+ a1 af b2 5c 58 63 74
+ 39 01 00 00 00 00 03
+ b6 7e 7e
+ 39 01 00 00 00 00 02
+ cc 08
+ 39 01 00 00 00 00 06
+ c7 00 08 00 01 08
+ 39 01 00 00 00 00 03
+ c0 25 5a
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 14 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 14 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-t-clk-post = <0x0e>;
+ qcom,mdss-dsi-t-clk-pre = <0x31>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
new file mode 100644
index 0000000..7fdcb2d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
@@ -0,0 +1,52 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "dsi-panel-sim-video.dtsi"
+#include "dsi-panel-sim-cmd.dtsi"
+#include "dsi-panel-truly-1080p-video.dtsi"
+#include "dsi-panel-truly-1080p-cmd.dtsi"
+#include "dsi-panel-r69006-1080p-cmd.dtsi"
+#include "dsi-panel-r69006-1080p-video.dtsi"
+#include "dsi-panel-hx8394f-720p-video.dtsi"
+#include "dsi-adv7533-1080p.dtsi"
+#include "dsi-adv7533-720p.dtsi"
+#include "dsi-panel-truly-720p-video.dtsi"
+#include "dsi-panel-truly-wuxga-video.dtsi"
+#include "dsi-panel-truly-720p-cmd.dtsi"
+#include "dsi-panel-lead-fl10802-fwvga-video.dtsi"
+#include "dsi-panel-icn9706-720-1440p-video.dtsi"
+
+&soc {
+ dsi_panel_pwr_supply: dsi_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <2850000>;
+ qcom,supply-max-voltage = <2850000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mdss-pll.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss-pll.dtsi
new file mode 100644
index 0000000..6987716
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8937-mdss-pll.dtsi
@@ -0,0 +1,103 @@
+/* Copyright (c) 2015, 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.
+ */
+
+&soc {
+ mdss_dsi0_pll: qcom,mdss_dsi_pll@1a94a00 {
+ compatible = "qcom,mdss_dsi_pll_8937";
+ label = "MDSS DSI 0 PLL";
+ cell-index = <0>;
+ #clock-cells = <1>;
+
+ reg = <0x001a94a00 0xd4>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+
+ gdsc-supply = <&gdsc_mdss>;
+ vddio-supply = <&pm8937_l6>;
+
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>;
+ clock-names = "iface_clk";
+ clock-rate = <0>;
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ qcom,ssc-frequency-hz = <30000>;
+ qcom,ssc-ppm = <5000>;
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+
+ qcom,platform-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+ };
+
+ mdss_dsi1_pll: qcom,mdss_dsi_pll@1a96a00 {
+ compatible = "qcom,mdss_dsi_pll_8937";
+ label = "MDSS DSI 1 PLL";
+ cell-index = <1>;
+ #clock-cells = <1>;
+
+ reg = <0x001a96a00 0xd4>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+
+ gdsc-supply = <&gdsc_mdss>;
+ vddio-supply = <&pm8937_l6>;
+
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>;
+ clock-names = "iface_clk";
+ clock-rate = <0>;
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ qcom,ssc-frequency-hz = <30000>;
+ qcom,ssc-ppm = <5000>;
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+
+ qcom,platform-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mdss.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss.dtsi
new file mode 100644
index 0000000..07ff464
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8937-mdss.dtsi
@@ -0,0 +1,410 @@
+/* Copyright (c) 2015-2018, 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.
+ */
+
+&soc {
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ compatible = "qcom,mdss_mdp";
+ reg = <0x01a00000 0x90000>,
+ <0x01ab0000 0x1040>;
+ reg-names = "mdp_phys", "vbif_phys";
+ interrupts = <0 72 0>;
+ vdd-supply = <&gdsc_mdss>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <1 1>; /* 1 time */
+ qcom,mdss-ib-factor = <1 1>; /* 1 time */
+ qcom,mdss-clk-factor = <105 100>; /* 1.05 times */
+
+ qcom,max-mixer-width = <2048>;
+ qcom,max-pipe-width = <2048>;
+
+ /* VBIF QoS remapper settings*/
+ qcom,mdss-vbif-qos-rt-setting = <1 2 2 2>;
+ qcom,mdss-vbif-qos-nrt-setting = <1 1 1 1>;
+
+ qcom,mdss-has-panic-ctrl;
+ qcom,mdss-per-pipe-panic-luts = <0x000f>,
+ <0xffff>,
+ <0xfffc>,
+ <0xff00>;
+
+ qcom,mdss-mdp-reg-offset = <0x00001000>;
+ qcom,max-bandwidth-low-kbps = <3100000>;
+ qcom,max-bandwidth-high-kbps = <3100000>;
+ qcom,max-bandwidth-per-pipe-kbps = <2300000>;
+
+ /* Bandwidth limit settings */
+ qcom,max-bw-settings = <1 3100000>, /* Default */
+ <2 1700000>; /* Camera */
+
+ qcom,max-clk-rate = <320000000>;
+ qcom,mdss-default-ot-rd-limit = <32>;
+ qcom,mdss-default-ot-wr-limit = <16>;
+
+ qcom,mdss-pipe-vig-off = <0x00005000>;
+ qcom,mdss-pipe-rgb-off = <0x00015000 0x00017000>;
+ qcom,mdss-pipe-dma-off = <0x00025000>;
+ qcom,mdss-pipe-cursor-off = <0x00035000>;
+
+ qcom,mdss-pipe-vig-xin-id = <0>;
+ qcom,mdss-pipe-rgb-xin-id = <1 5>;
+ qcom,mdss-pipe-dma-xin-id = <2>;
+ qcom,mdss-pipe-cursor-xin-id = <7>;
+
+ /* Offsets relative to "mdp_phys + mdp-reg-offset" address */
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x2AC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x2AC 4 8>,
+ <0x2B4 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x2AC 8 12>;
+ qcom,mdss-pipe-cursor-clk-ctrl-offsets = <0x3A8 16 15>;
+
+
+ qcom,mdss-ctl-off = <0x00002000 0x00002200 0x00002400>;
+ qcom,mdss-mixer-intf-off = <0x00045000 0x00046000>;
+ qcom,mdss-dspp-off = <0x00055000>;
+ qcom,mdss-wb-off = <0x00065000 0x00066000>;
+ qcom,mdss-intf-off = <0x00000000 0x0006B800 0x0006C000>;
+ qcom,mdss-pingpong-off = <0x00071000 0x00071800>;
+ qcom,mdss-slave-pingpong-off = <0x00073000>;
+ qcom,mdss-cdm-off = <0x0007a200>;
+ qcom,mdss-wfd-mode = "intf";
+ qcom,mdss-highest-bank-bit = <0x1>;
+ qcom,mdss-has-decimation;
+ qcom,mdss-has-non-scalar-rgb;
+ qcom,mdss-has-rotator-downscale;
+ qcom,mdss-rot-downscale-min = <2>;
+ qcom,mdss-rot-downscale-max = <16>;
+ qcom,mdss-idle-power-collapse-enabled;
+ qcom,mdss-rot-block-size = <64>;
+
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc clk_gcc_mdss_axi_clk>,
+ <&clock_gcc clk_mdp_clk_src>,
+ <&clock_gcc_mdss clk_mdss_mdp_vote_clk>,
+ <&clock_gcc clk_gcc_mdss_vsync_clk>;
+ clock-names = "iface_clk", "bus_clk", "core_clk_src",
+ "core_clk", "vsync_clk";
+
+ qcom,mdp-settings = <0x0506c 0x00000000>,
+ <0x1506c 0x00000000>,
+ <0x1706c 0x00000000>,
+ <0x2506c 0x00000000>;
+
+ qcom,vbif-settings = <0x0d0 0x00000010>;
+
+ qcom,regs-dump-mdp = <0x01000 0x01454>,
+ <0x02000 0x02064>,
+ <0x02200 0x02264>,
+ <0x02400 0x02464>,
+ <0x05000 0x05150>,
+ <0x05200 0x05230>,
+ <0x15000 0x15150>,
+ <0x17000 0x17150>,
+ <0x25000 0x25150>,
+ <0x35000 0x35150>,
+ <0x45000 0x452bc>,
+ <0x46000 0x462bc>,
+ <0x55000 0x5522c>,
+ <0x65000 0x652c0>,
+ <0x66000 0x662c0>,
+ <0x6b800 0x6ba68>,
+ <0x6c000 0x6c268>,
+ <0x71000 0x710d4>,
+ <0x71800 0x718d4>;
+
+ qcom,regs-dump-names-mdp = "MDP",
+ "CTL_0", "CTL_1", "CTL_2",
+ "VIG0_SSPP", "VIG0",
+ "RGB0_SSPP", "RGB1_SSPP",
+ "DMA0_SSPP",
+ "CURSOR0_SSPP",
+ "LAYER_0", "LAYER_1",
+ "DSPP_0",
+ "WB_0", "WB_2",
+ "INTF_1", "INTF_2",
+ "PP_0", "PP_1";
+
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <0>;
+ qcom,mdss-prefill-y-buffer-bytes = <0>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <2048>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <4096>;
+
+ qcom,mdss-pp-offsets {
+ qcom,mdss-sspp-mdss-igc-lut-off = <0x2000>;
+ qcom,mdss-sspp-vig-pcc-off = <0x1780>;
+ qcom,mdss-sspp-rgb-pcc-off = <0x380>;
+ qcom,mdss-sspp-dma-pcc-off = <0x380>;
+ qcom,mdss-lm-pgc-off = <0x3C0>;
+ qcom,mdss-dspp-pcc-off = <0x1700>;
+ qcom,mdss-dspp-pgc-off = <0x17C0>;
+ };
+
+ qcom,mdss-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 160000>,
+ <1 590 0 320000>;
+ };
+
+ qcom,mdss-hw-rt-bus {
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_hw_rt";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
+ };
+
+ smmu_mdp_unsec: qcom,smmu_mdp_unsec_cb {
+ compatible = "qcom,smmu_mdp_unsec";
+ iommus = <&apps_iommu 0xC00 0>; /* For NS ctx bank */
+ };
+ smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
+ compatible = "qcom,smmu_mdp_sec";
+ iommus = <&apps_iommu 0xC01 0>; /* For SEC Ctx Bank */
+ };
+
+ mdss_fb0: qcom,mdss_fb_primary {
+ cell-index = <0>;
+ compatible = "qcom,mdss-fb";
+ qcom,cont-splash-memory {
+ linux,contiguous-region = <&cont_splash_mem>;
+ };
+ };
+
+ mdss_fb1: qcom,mdss_fb_wfd {
+ cell-index = <1>;
+ compatible = "qcom,mdss-fb";
+ };
+
+ mdss_fb2: qcom,mdss_fb_secondary {
+ cell-index = <2>;
+ compatible = "qcom,mdss-fb";
+ };
+ };
+
+ mdss_dsi: qcom,mdss_dsi@0 {
+ compatible = "qcom,mdss-dsi";
+ hw-config = "single_dsi";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ gdsc-supply = <&gdsc_mdss>;
+ vdda-supply = <&pm8937_l2>;
+ vddio-supply = <&pm8937_l6>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_dsi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
+
+ ranges = <0x1a94000 0x1a94000 0x300
+ 0x1a94400 0x1a94400 0x280
+ 0x1a94b80 0x1a94b80 0x30
+ 0x193e000 0x193e000 0x30
+ 0x1a96000 0x1a96000 0x300
+ 0x1a96400 0x1a96400 0x280
+ 0x1a96b80 0x1a96b80 0x30
+ 0x193e000 0x193e000 0x30>;
+
+ clocks = <&clock_gcc_mdss clk_mdss_mdp_vote_clk>,
+ <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc clk_gcc_mdss_axi_clk>,
+ <&clock_gcc_mdss clk_ext_byte0_clk_src>,
+ <&clock_gcc_mdss clk_ext_byte1_clk_src>,
+ <&clock_gcc_mdss clk_ext_pclk0_clk_src>,
+ <&clock_gcc_mdss clk_ext_pclk1_clk_src>;
+ clock-names = "mdp_core_clk", "iface_clk", "bus_clk",
+ "ext_byte0_clk", "ext_byte1_clk", "ext_pixel0_clk",
+ "ext_pixel1_clk";
+
+ qcom,mmss-ulp-clamp-ctrl-offset = <0x20>;
+ qcom,mmss-phyreset-ctrl-offset = <0x24>;
+
+ qcom,mdss-fb-map-prim = <&mdss_fb0>;
+ qcom,mdss-fb-map-sec = <&mdss_fb2>;
+ qcom,core-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,core-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda";
+ qcom,supply-min-voltage = <1200000>;
+ qcom,supply-max-voltage = <1200000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <20>;
+ };
+ };
+
+ qcom,phy-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,phy-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+
+ mdss_dsi0: qcom,mdss_dsi_ctrl0@1a94000 {
+ compatible = "qcom,mdss-dsi-ctrl";
+ label = "MDSS DSI CTRL->0";
+ cell-index = <0>;
+ reg = <0x1a94000 0x300>,
+ <0x1a94400 0x280>,
+ <0x1a94b80 0x30>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy",
+ "dsi_phy_regulator", "mmss_misc_phys";
+
+ qcom,timing-db-mode;
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8937_l17>;
+ vddio-supply = <&pm8937_l6>;
+
+ clocks = <&clock_gcc_mdss clk_gcc_mdss_byte0_clk>,
+ <&clock_gcc_mdss clk_gcc_mdss_pclk0_clk>,
+ <&clock_gcc clk_gcc_mdss_esc0_clk>,
+ <&clock_gcc_mdss clk_byte0_clk_src>,
+ <&clock_gcc_mdss clk_pclk0_clk_src>;
+ clock-names = "byte_clk", "pixel_clk", "core_clk",
+ "byte_clk_rcg", "pixel_clk_rcg";
+
+ qcom,platform-strength-ctrl = [ff 06];
+ qcom,platform-bist-ctrl = [00 00 b1 ff 00 00];
+ qcom,platform-regulator-settings = [03 08 07 00
+ 20 07 01];
+ qcom,platform-lane-config = [01 c0 00 00 00 00 00 01 97
+ 01 c0 00 00 05 00 00 01 97
+ 01 c0 00 00 0a 00 00 01 97
+ 01 c0 00 00 0f 00 00 01 97
+ 00 40 00 00 00 00 00 01 ff];
+ };
+
+ mdss_dsi1: qcom,mdss_dsi_ctrl1@1a96000 {
+ compatible = "qcom,mdss-dsi-ctrl";
+ label = "MDSS DSI CTRL->1";
+ cell-index = <1>;
+ reg = <0x1a96000 0x300>,
+ <0x1a96400 0x280>,
+ <0x1a94b80 0x30>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy",
+ "dsi_phy_regulator", "mmss_misc_phys";
+
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8937_l17>;
+ vddio-supply = <&pm8937_l6>;
+
+ clocks = <&clock_gcc_mdss clk_gcc_mdss_byte1_clk>,
+ <&clock_gcc_mdss clk_gcc_mdss_pclk1_clk>,
+ <&clock_gcc clk_gcc_mdss_esc1_clk>,
+ <&clock_gcc_mdss clk_byte1_clk_src>,
+ <&clock_gcc_mdss clk_pclk1_clk_src>;
+ clock-names = "byte_clk", "pixel_clk", "core_clk",
+ "byte_clk_rcg", "pixel_clk_rcg";
+
+ qcom,platform-strength-ctrl = [ff 06];
+ qcom,platform-bist-ctrl = [00 00 b1 ff 00 00];
+ qcom,platform-regulator-settings = [03 08 07 00
+ 20 07 01];
+ qcom,platform-lane-config = [01 c0 00 00 00 00 00 01 97
+ 01 c0 00 00 05 00 00 01 97
+ 01 c0 00 00 0a 00 00 01 97
+ 01 c0 00 00 0f 00 00 01 97
+ 00 40 00 00 00 00 00 01 ff];
+
+ };
+
+ };
+
+ qcom,mdss_wb_panel {
+ compatible = "qcom,mdss_wb";
+ qcom,mdss_pan_res = <640 640>;
+ qcom,mdss_pan_bpp = <24>;
+ qcom,mdss-fb-map = <&mdss_fb1>;
+ };
+
+ mdss_rotator: qcom,mdss_rotator {
+ compatible = "qcom,mdss_rotator";
+ qcom,mdss-wb-count = <1>;
+ qcom,mdss-has-downscale;
+ qcom,mdss-has-ubwc;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rotator";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ rot-vdd-supply = <&gdsc_mdss>;
+ qcom,supply-names = "rot-vdd";
+ qcom,mdss-has-reg-bus;
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc_mdss clk_mdss_rotator_vote_clk>;
+ clock-names = "iface_clk", "rot_core_clk";
+
+ qcom,mdss-rot-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rot_reg";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-pmi8950-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8937-pmi8950-mtp.dtsi
index d0eff96..3da16e4 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-pmi8950-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-pmi8950-mtp.dtsi
@@ -33,11 +33,11 @@
};
};
-&pmi8950_fg {
+&qpnp_fg {
qcom,battery-data = <&mtp_batterydata>;
};
-&pmi8950_charger {
+&qpnp_smbcharger {
qcom,battery-data = <&mtp_batterydata>;
qcom,chg-led-sw-controls;
qcom,chg-led-support;
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index ecab236..14a1e9a 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -15,6 +15,7 @@
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/spmi/spmi.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/msm-clocks-8952.h>
/ {
model = "Qualcomm Technologies, Inc. MSM8937";
@@ -235,6 +236,174 @@
qcom,pipe-attr-ee;
};
+ thermal_zones: thermal-zones {
+ aoss0-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 0>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-core-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 1>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdss-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 2>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ camera-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 3>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpuss-0-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 4>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ apc1_cpu1-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 5>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ apc1_cpu2-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 6>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ apc1_cpu3-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 7>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ apc1_cpu4-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 8>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ apc0_cpu0-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 9>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ gpu0-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 10>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+ };
+
+ tsens0: tsens@4a8000 {
+ compatible = "qcom,msm8937-tsens";
+ reg = <0x4a8000 0x1000>,
+ <0x4a9000 0x1000>,
+ <0xa4000 0x1000>;
+ reg-names = "tsens_srot_physical",
+ "tsens_tm_physical", "tsens_eeprom_physical";
+ interrupts = <0 184 0>;
+ interrupt-names = "tsens-upper-lower";
+ #thermal-sensor-cells = <1>;
+ };
+
slim_msm: slim@c140000{
cell-index = <1>;
compatible = "qcom,slim-ngd";
@@ -271,6 +440,120 @@
qcom,summing-threshold = <10>;
};
+ clock_gcc: qcom,gcc@1800000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,gcc-8937";
+ reg = <0x1800000 0x80000>,
+ <0xb016000 0x00040>,
+ <0xb116000 0x00040>,
+ <0x00a6018 0x00004>;
+ reg-names = "cc_base", "apcs_c1_base",
+ "apcs_c0_base", "efuse";
+ vdd_dig-supply = <&pm8937_s2_level>;
+ vdd_sr2_dig-supply = <&pm8937_s2_level_ao>;
+ vdd_sr2_pll-supply = <&pm8937_l7_ao>;
+ vdd_hf_dig-supply = <&pm8937_s2_level_ao>;
+ vdd_hf_pll-supply = <&pm8937_l7_ao>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ ranges;
+ qcom,spm@0 {
+ compatible = "qcom,gcc-spm-8937";
+ reg = <0x0b111200 0x100>,
+ <0x0b011200 0x100>;
+ reg-names = "spm_c0_base", "spm_c1_base";
+ };
+ };
+
+ clock_debug: qcom,cc-debug@1874000 {
+ compatible = "qcom,cc-debug-8937";
+ reg = <0x1874000 0x4>,
+ <0xb11101c 0x8>;
+ reg-names = "cc_base", "meas";
+ #clock-cells = <1>;
+ };
+
+ clock_cpu: qcom,cpu-clock-8939@b111050 {
+ compatible = "qcom,cpu-clock-8939";
+ reg = <0xb011050 0x8>,
+ <0xb111050 0x8>,
+ <0xb1d1050 0x8>,
+ <0x00a412c 0x8>;
+ reg-names = "apcs-c1-rcg-base", "apcs-c0-rcg-base",
+ "apcs-cci-rcg-base", "efuse";
+ vdd-c0-supply = <&apc_vreg_corner>;
+ vdd-c1-supply = <&apc_vreg_corner>;
+ vdd-cci-supply = <&apc_vreg_corner>;
+ clocks = <&clock_gcc clk_gpll0_ao_clk_src>,
+ <&clock_gcc clk_a53ss_c0_pll>,
+ <&clock_gcc clk_gpll0_ao_clk_src>,
+ <&clock_gcc clk_a53ss_c1_pll>,
+ <&clock_gcc clk_gpll0_ao_clk_src>,
+ <&clock_gcc clk_gpll0_ao_clk_src>;
+ clock-names = "clk-c0-4", "clk-c0-5",
+ "clk-c1-4", "clk-c1-5",
+ "clk-cci-4", "clk-cci-2";
+ qcom,speed0-bin-v0-c0 =
+ < 0 0>,
+ < 768000000 1>,
+ < 902400000 2>,
+ < 998400000 4>,
+ < 1094400000 6>;
+
+ qcom,speed0-bin-v0-c1 =
+ < 0 0>,
+ < 960000000 1>,
+ < 1094400000 2>,
+ < 1248000000 4>,
+ < 1344000000 5>,
+ < 1401000000 6>;
+
+ qcom,speed0-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+
+ qcom,speed1-bin-v0-c0 =
+ < 0 0>,
+ < 768000000 1>,
+ < 902400000 2>,
+ < 998400000 4>,
+ < 1094400000 6>,
+ < 1209600000 7>;
+
+ qcom,speed1-bin-v0-c1 =
+ < 0 0>,
+ < 960000000 1>,
+ < 1094400000 2>,
+ < 1248000000 4>,
+ < 1344000000 5>,
+ < 1401000000 6>,
+ < 1497600000 7>;
+
+ qcom,speed1-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+
+ qcom,speed2-bin-v0-c0 =
+ < 0 0>,
+ < 768000000 1>,
+ < 902400000 2>,
+ < 998400000 3>;
+
+ qcom,speed2-bin-v0-c1 =
+ < 0 0>,
+ < 960000000 1>,
+ < 1094400000 2>,
+ < 1209600000 3>;
+
+ qcom,speed2-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+ #clock-cells = <1>;
+ };
cpubw: qcom,cpubw {
compatible = "qcom,devbw";
@@ -480,6 +763,33 @@
};
};
+ qcom,memshare {
+ compatible = "qcom,memshare";
+
+ qcom,client_1 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x200000>;
+ qcom,client-id = <0>;
+ qcom,allocate-boot-time;
+ label = "modem";
+ };
+
+ qcom,client_2 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x300000>;
+ qcom,client-id = <2>;
+ label = "modem";
+ };
+
+ qcom,client_3 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x500000>;
+ qcom,client-id = <1>;
+ qcom,allocate-boot-time;
+ label = "modem";
+ };
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
@@ -622,3 +932,75 @@
#include "pm8937-rpm-regulator.dtsi"
#include "msm8937-regulator.dtsi"
#include "pm8937.dtsi"
+#include "msm-gdsc-8916.dtsi"
+
+&gdsc_venus {
+ clock-names = "bus_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_venus0_axi_clk>,
+ <&clock_gcc clk_gcc_venus0_vcodec0_clk>;
+ status = "okay";
+};
+
+&gdsc_venus_core0 {
+ qcom,support-hw-trigger;
+ clock-names ="core0_clk";
+ clocks = <&clock_gcc clk_gcc_venus0_core0_vcodec0_clk>;
+ status = "okay";
+};
+
+&gdsc_mdss {
+ clock-names = "core_clk", "bus_clk";
+ clocks = <&clock_gcc clk_gcc_mdss_mdp_clk>,
+ <&clock_gcc clk_gcc_mdss_axi_clk>;
+ qcom,disallow-clear;
+ status = "okay";
+};
+
+&gdsc_jpeg {
+ clock-names = "core_clk", "bus_clk";
+ clocks = <&clock_gcc clk_gcc_camss_jpeg0_clk>,
+ <&clock_gcc clk_gcc_camss_jpeg_axi_clk>;
+ status = "okay";
+};
+
+&gdsc_vfe {
+ clock-names = "core_clk", "bus_clk", "micro_clk",
+ "csi_clk";
+ clocks = <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_axi_clk>,
+ <&clock_gcc clk_gcc_camss_micro_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>;
+ status = "okay";
+};
+
+&gdsc_vfe1 {
+ clock-names = "core_clk", "bus_clk", "micro_clk",
+ "csi_clk";
+ clocks = <&clock_gcc clk_gcc_camss_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_vfe1_axi_clk>,
+ <&clock_gcc clk_gcc_camss_micro_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe1_clk>;
+ status = "okay";
+};
+
+&gdsc_cpp {
+ clock-names = "core_clk", "bus_clk";
+ clocks = <&clock_gcc clk_gcc_camss_cpp_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_axi_clk>;
+ status = "okay";
+};
+
+&gdsc_oxili_gx {
+ clock-names = "core_root_clk";
+ clocks =<&clock_gcc clk_gfx3d_clk_src>;
+ qcom,enable-root-clk;
+ qcom,clk-dis-wait-val = <0x5>;
+ status = "okay";
+};
+
+&gdsc_oxili_cx {
+ reg = <0x1859044 0x4>;
+ clock-names = "core_clk";
+ clocks = <&clock_gcc clk_gcc_oxili_gfx3d_clk>;
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi
new file mode 100644
index 0000000..39d67ab
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&soc {
+ qcom,msm-cam@1b00000 {
+ compatible = "qcom,msm-cam";
+ reg = <0x1b00000 0x40000>;
+ reg-names = "msm-cam";
+ status = "ok";
+ bus-vectors = "suspend", "svs", "nominal", "turbo";
+ qcom,bus-votes = <0 320000000 640000000 640000000>;
+ };
+
+ qcom,csiphy@1b34000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v3.5", "qcom,csiphy";
+ reg = <0x1b34000 0x1000>,
+ <0x1b00030 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi0phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 200000000 0 0 0>;
+ };
+
+ qcom,csiphy@1b35000 {
+ status = "ok";
+ cell-index = <1>;
+ compatible = "qcom,csiphy-v3.5", "qcom,csiphy";
+ reg = <0x1b35000 0x1000>,
+ <0x1b00038 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 79 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi1phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 200000000 0 0 0>;
+ };
+
+ qcom,csiphy@1b36000 {
+ status = "ok";
+ cell-index = <2>;
+ compatible = "qcom,csiphy-v3.5", "qcom,csiphy";
+ reg = <0x1b36000 0x1000>,
+ <0x1b00040 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 315 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi2phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi2phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 200000000 0 0 0>;
+ };
+
+ qcom,csid@1b30000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,csid-v3.5.1", "qcom,csid";
+ reg = <0x1b30000 0x400>;
+ reg-names = "csid";
+ interrupts = <0 51 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1225000>;
+ qcom,mipi-csi-vdd-supply = <&pm8953_s3>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi0_ahb_clk>,
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phy_clk>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi0_phy_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0 0>;
+ };
+
+ qcom,csid@1b30400 {
+ status = "ok";
+ cell-index = <1>;
+ compatible = "qcom,csid-v3.5.1", "qcom,csid";
+ reg = <0x1b30400 0x400>;
+ reg-names = "csid";
+ interrupts = <0 52 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1225000>;
+ qcom,mipi-csi-vdd-supply = <&pm8953_s3>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi1_ahb_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1phy_clk>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi1_phy_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0 0>;
+ };
+
+ qcom,csid@1b30800 {
+ status = "ok";
+ cell-index = <2>;
+ compatible = "qcom,csid-v3.5.1", "qcom,csid";
+ reg = <0x1b30800 0x400>;
+ reg-names = "csid";
+ interrupts = <0 153 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1225000>;
+ qcom,mipi-csi-vdd-supply = <&pm8953_s3>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi2_ahb_clk>,
+ <&clock_gcc clk_csi2_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi2phy_clk>,
+ <&clock_gcc clk_gcc_camss_csi2_clk>,
+ <&clock_gcc clk_gcc_camss_csi2pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi2rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi2_phy_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0 0>;
+ };
+
+ qcom,ispif@1b31000 {
+ cell-index = <0>;
+ compatible = "qcom,ispif-v3.0", "qcom,ispif";
+ reg = <0x1b31000 0x500>,
+ <0x1b00020 0x10>;
+ reg-names = "ispif", "csi_clk_mux";
+ interrupts = <0 55 0>;
+ interrupt-names = "ispif";
+ qcom,num-isps = <0x2>;
+ vfe0-vdd-supply = <&gdsc_vfe>;
+ vfe1-vdd-supply = <&gdsc_vfe1>;
+ qcom,vdd-names = "vfe0-vdd", "vfe1-vdd";
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_csi2_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi2_clk>,
+ <&clock_gcc clk_gcc_camss_csi2rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi2pix_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>,
+ <&clock_gcc clk_vfe1_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe1_clk>;
+ clock-names = "ispif_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_src",
+ "csi0_src_clk", "csi0_clk",
+ "csi0_rdi_clk", "csi0_pix_clk",
+ "csi1_src_clk", "csi1_clk",
+ "csi1_rdi_clk", "csi1_pix_clk",
+ "csi2_src_clk", "csi2_clk",
+ "csi2_rdi_clk", "csi2_pix_clk",
+ "vfe0_clk_src", "camss_vfe_vfe0_clk",
+ "camss_csi_vfe0_clk", "vfe1_clk_src",
+ "camss_vfe_vfe1_clk", "camss_csi_vfe1_clk";
+ qcom,clock-rates = <61540000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 0 0 0
+ 0 0 0>;
+ qcom,clock-cntl-support;
+ qcom,clock-control = "SET_RATE","NO_SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "NO_SET_RATE", "SET_RATE",
+ "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "INIT_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "INIT_RATE", "NO_SET_RATE",
+ "NO_SET_RATE";
+ };
+
+ vfe0: qcom,vfe0@1b10000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe40";
+ reg = <0x1b10000 0x1000>,
+ <0x1b40000 0x200>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 57 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_axi_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "camss_ahb_clk",
+ "vfe_clk_src", "camss_vfe_vfe_clk",
+ "camss_csi_vfe_clk", "iface_clk",
+ "bus_clk", "iface_ahb_clk";
+ qcom,clock-rates = <0 0 266670000 0 0 0 0 0>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55>;
+ vbif-entries = <1>;
+ vbif-regs = <0x124>;
+ vbif-settings = <0x3>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0x00000110>;
+ max-clk-nominal = <465000000>;
+ max-clk-turbo = <465000000>;
+ };
+
+ vfe1: qcom,vfe1@1b14000 {
+ cell-index = <1>;
+ compatible = "qcom,vfe40";
+ reg = <0x1b14000 0x1000>,
+ <0x1ba0000 0x200>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 29 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe1>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_vfe1_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_vfe1_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_vfe1_axi_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>;
+ clock-names = "camss_top_ahb_clk" , "camss_ahb_clk",
+ "vfe_clk_src", "camss_vfe_vfe_clk",
+ "camss_csi_vfe_clk", "iface_clk",
+ "bus_clk", "iface_ahb_clk";
+ qcom,clock-rates = <0 0 266670000 0 0 0 0 0>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55>;
+ vbif-entries = <1>;
+ vbif-regs = <0x124>;
+ vbif-settings = <0x3>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0x00000110>;
+ max-clk-nominal = <465000000>;
+ max-clk-turbo = <465000000>;
+ };
+
+ qcom,vfe {
+ compatible = "qcom,vfe";
+ num_child = <2>;
+ };
+/*
+ * qcom,cam_smmu {
+ * status = "ok";
+ * compatible = "qcom,msm-cam-smmu";
+ * msm_cam_smmu_cb1: msm_cam_smmu_cb1 {
+ * compatible = "qcom,qsmmu-cam-cb";
+ * iommus = <&apps_iommu 0x400>,
+ * <&apps_iommu 0x2800>;
+ * label = "vfe";
+ * qcom,scratch-buf-support;
+ * };
+ *
+ *
+ * msm_cam_smmu_cb3: msm_cam_smmu_cb3 {
+ * compatible = "qcom,qsmmu-cam-cb";
+ * iommus = <&apps_iommu 0x1c00>;
+ * label = "cpp";
+ * };
+ *
+ * msm_cam_smmu_cb4: msm_cam_smmu_cb4 {
+ * compatible = "qcom,qsmmu-cam-cb";
+ * iommus = <&apps_iommu 0x1800>;
+ * label = "jpeg_enc0";
+ * };
+ * };
+ */
+ qcom,jpeg@1b1c000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,jpeg";
+ reg = <0x1b1c000 0x400>,
+ <0x1b60000 0xc30>;
+ reg-names = "jpeg_hw", "jpeg_vbif";
+ interrupts = <0 59 0>;
+ interrupt-names = "jpeg";
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk";
+ clocks = <&clock_gcc clk_gcc_camss_jpeg0_clk>,
+ <&clock_gcc clk_gcc_camss_jpeg_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_jpeg_axi_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ qcom,clock-rates = <266670000 0 0 0 0>;
+ qcom,qos-reg-settings = <0x28 0x0000555e>,
+ <0xc8 0x00005555>;
+ qcom,vbif-reg-settings = <0xc0 0x10101000>,
+ <0xb0 0x10100010>;
+ qcom,msm-bus,name = "msm_camera_jpeg0";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <62 512 0 0>,
+ <62 512 800000 800000>;
+ };
+
+ qcom,irqrouter@1b00000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,irqrouter";
+ reg = <0x1b00000 0x100>;
+ reg-names = "irqrouter";
+ };
+
+ qcom,cpp@1b04000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,cpp";
+ reg = <0x1b04000 0x100>,
+ <0x1b80000 0x200>,
+ <0x1b18000 0x018>,
+ <0x1858078 0x4>;
+ reg-names = "cpp", "cpp_vbif", "cpp_hw", "camss_cpp";
+ interrupts = <0 49 0>;
+ interrupt-names = "cpp";
+ vdd-supply = <&gdsc_cpp>;
+ qcom,vdd-names = "vdd";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_cpp_clk_src>,
+ <&clock_gcc clk_gcc_camss_cpp_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_axi_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_clk>,
+ <&clock_gcc clk_gcc_camss_micro_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "cpp_core_clk",
+ "camss_vfe_cpp_ahb_clk", "camss_vfe_cpp_axi_clk",
+ "camss_vfe_cpp_clk","micro_iface_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 180000000 0 0 180000000 0 0>;
+ qcom,min-clock-rate = <100000000>;
+ qcom,bus-master = <1>;
+ qcom,msm-bus,name = "msm_camera_cpp";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <106 512 0 0>,
+ <106 512 0 0>;
+ qcom,msm-bus-vector-dyn-vote;
+ qcom,cpp-fw-payload-info {
+ qcom,stripe-base = <156>;
+ qcom,plane-base = <141>;
+ qcom,stripe-size = <27>;
+ qcom,plane-size = <5>;
+ qcom,fe-ptr-off = <5>;
+ qcom,we-ptr-off = <11>;
+ };
+ };
+
+ cci: qcom,cci@1b0c000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,cci";
+ reg = <0x1b0c000 0x4000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "cci";
+ interrupts = <0 50 0>;
+ interrupt-names = "cci";
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_cci_clk_src>,
+ <&clock_gcc clk_gcc_camss_cci_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_cci_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>;
+ clock-names = "ispif_ahb_clk", "cci_src_clk",
+ "cci_ahb_clk", "camss_cci_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk";
+ qcom,clock-rates = <61540000 19200000 0 0 0 0>,
+ <61540000 37500000 0 0 0 0>;
+ pinctrl-names = "cci_default", "cci_suspend";
+ pinctrl-0 = <&cci0_active &cci1_active>;
+ pinctrl-1 = <&cci0_suspend &cci1_suspend>;
+ gpios = <&tlmm 29 0>,
+ <&tlmm 30 0>,
+ <&tlmm 31 0>,
+ <&tlmm 32 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";
+ i2c_freq_100Khz: qcom,i2c_standard_mode {
+ status = "disabled";
+ };
+ i2c_freq_400Khz: qcom,i2c_fast_mode {
+ status = "disabled";
+ };
+ i2c_freq_custom: qcom,i2c_custom_mode {
+ status = "disabled";
+ };
+
+ i2c_freq_1Mhz: qcom,i2c_fast_plus_mode {
+ status = "disabled";
+ };
+
+ };
+};
+
+&i2c_freq_100Khz {
+ 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>;
+};
+
+&i2c_freq_400Khz {
+ qcom,hw-thigh = <20>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <32>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_custom {
+ qcom,hw-thigh = <15>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <25>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_1Mhz {
+ qcom,hw-thigh = <16>;
+ qcom,hw-tlow = <22>;
+ qcom,hw-tsu-sto = <17>;
+ qcom,hw-tsu-sta = <18>;
+ qcom,hw-thd-dat = <16>;
+ qcom,hw-thd-sta = <15>;
+ qcom,hw-tbuf = <19>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <3>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index 8212cc8..4c626b0 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -17,6 +17,39 @@
pinctrl-0 = <&uart_console_active>;
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm8953_l8>;
@@ -122,3 +155,71 @@
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 87 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+
+ home {
+ label = "home";
+ gpios = <&tlmm 88 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ debounce-interval = <15>;
+ };
+ };
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active: gpio_key_active {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ };
+ };
+
+ gpio_key_suspend: gpio_key_suspend {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ };
+ };
+ };
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ion.dtsi b/arch/arm64/boot/dts/qcom/msm8953-ion.dtsi
index 34004b0..00203a2 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ion.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-ion.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,5 +32,11 @@
memory-region = <&qseecom_mem>;
qcom,ion-heap-type = "DMA";
};
+
+ qcom,ion-heap@19 { /* QSEECOM TA HEAP */
+ reg = <19>;
+ memory-region = <&qseecom_ta_mem>;
+ qcom,ion-heap-type = "DMA";
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
index cb59ac3..28a6b74 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
@@ -23,6 +23,7 @@
#include "dsi-panel-truly-wuxga-video.dtsi"
#include "dsi-panel-lt8912-480p-video.dtsi"
#include "dsi-panel-lt8912-1080p-video.dtsi"
+#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -86,6 +87,27 @@
24 1b 08 09 05 03 04 a0];
};
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1c 08 09 05 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <55>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
+
&dsi_adv7533_1080p {
qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
24 1f 08 09 05 03 04 a0
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
index 49956df..c6ae512 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
@@ -20,3 +20,21 @@
model = "MTP";
qcom,board-id = <8 0>;
};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
index b53f7b8..97c6db3 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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,16 +33,12 @@
};
};
-&pmi8950_fg {
+&qpnp_fg {
qcom,battery-data = <&mtp_batterydata>;
};
-&pmi8950_charger {
+&qpnp_smbcharger {
qcom,battery-data = <&mtp_batterydata>;
qcom,chg-led-sw-controls;
qcom,chg-led-support;
};
-
-&usb3 {
- extcon = <&pmi8950_charger>;
-};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index 8212cc8..15009b23 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -17,6 +17,39 @@
pinctrl-0 = <&uart_console_active>;
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm8953_l8>;
@@ -122,3 +155,36 @@
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 87 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index eec350d..171b7c4 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -16,6 +16,7 @@
compatible = "qcom,msm8953-pinctrl";
reg = <0x1000000 0x300000>;
interrupts = <0 208 0>;
+ interrupts-extended = <&wakegic GIC_SPI 208 IRQ_TYPE_NONE>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dts
index a9f64a4..5ec92ae 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "msm8953.dtsi"
+#include "pmi8937.dtsi"
+#include "msm8953-pmi8937.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8937 SOC";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
index a208e1a..80050c4 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
@@ -24,10 +24,10 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
- extcon = <&pmi8937_charger>;
+ extcon = <&qpnp_smbcharger>;
};
-&pmi8937_charger {
+&qpnp_smbcharger {
qcom,external-typec;
qcom,typec-psy-name = "typec";
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dts
index e9c80a0d..ba5c3c7 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "msm8953.dtsi"
+#include "pmi8940.dtsi"
+#include "msm8953-pmi8940.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8940 SOC";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
index 28fc0d7..c36dd1b 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
@@ -24,7 +24,7 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
- extcon = <&pmi8940_charger>;
+ extcon = <&qpnp_smbcharger>;
};
&labibb {
@@ -36,7 +36,7 @@
qcom,qpnp-ibb-discharge-resistor = <32>;
};
-&pmi8940_charger {
+&qpnp_smbcharger {
qcom,external-typec;
qcom,typec-psy-name = "typec";
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
index 139ef1e..d81a0a5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
@@ -29,10 +29,10 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
- extcon = <&pmi8950_charger>;
+ extcon = <&qpnp_smbcharger>;
};
-&pmi8950_charger {
+&qpnp_smbcharger {
qcom,external-typec;
qcom,typec-psy-name = "typec";
};
@@ -46,23 +46,3 @@
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
};
-
-&dsi_panel_pwr_supply {
- qcom,panel-supply-entry@2 {
- reg = <2>;
- qcom,supply-name = "lab";
- qcom,supply-min-voltage = <4600000>;
- qcom,supply-max-voltage = <6000000>;
- qcom,supply-enable-load = <100000>;
- qcom,supply-disable-load = <100>;
- };
- qcom,panel-supply-entry@3 {
- reg = <3>;
- qcom,supply-name = "ibb";
- qcom,supply-min-voltage = <4600000>;
- qcom,supply-max-voltage = <6000000>;
- qcom,supply-enable-load = <100000>;
- qcom,supply-disable-load = <100>;
- qcom,supply-post-on-sleep = <10>;
- };
-};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index b29e447..5e8cd99 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -48,6 +48,21 @@
regulator-name = "vdd_vreg";
};
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+ };
};
&blsp1_uart0 {
@@ -56,6 +71,39 @@
pinctrl-0 = <&uart_console_active>;
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm8953_l8>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-regulator.dtsi b/arch/arm64/boot/dts/qcom/msm8953-regulator.dtsi
index 9468181..5a4f024 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-regulator.dtsi
@@ -17,9 +17,11 @@
rpm-regulator-smpa1 {
status = "okay";
pm8953_s1: regulator-s1 {
- regulator-min-microvolt = <870000>;
- regulator-max-microvolt = <1156000>;
- qcom,init-voltage = <1000000>;
+ regulator-min-microvolt =
+ <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
+ regulator-max-microvolt =
+ <RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,use-voltage-level;
status = "okay";
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dts b/arch/arm64/boot/dts/qcom/msm8953.dts
index ddf2218..2f6cbc4 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "msm8953.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 SOC";
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 8f39918..aa96539 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -23,7 +23,7 @@
compatible = "qcom,msm8953";
qcom,msm-id = <293 0x0>;
qcom,msm-name = "MSM8953";
- interrupt-parent = <&intc>;
+ interrupt-parent = <&wakegic>;
chosen {
bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1";
@@ -103,6 +103,14 @@
compatible = "shared-dma-pool";
reusable;
alignment = <0 0x400000>;
+ size = <0 0x0400000>;
+ };
+
+ qseecom_ta_mem: qseecom_ta_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0 0x00000000 0 0xffffffff>;
+ reusable;
+ alignment = <0 0x400000>;
size = <0 0x1000000>;
};
@@ -200,11 +208,28 @@
intc: interrupt-controller@b000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
+ interrupt-parent = <&intc>;
#interrupt-cells = <3>;
reg = <0x0b000000 0x1000>,
<0x0b002000 0x1000>;
};
+ mpm: mpm@601d4 {
+ compatible = "qcom,mpm";
+ interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
+ reg = <0x601d4 0x1000>,
+ <0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ qcom,num-mpm-irqs = <96>;
+
+ wakegic: wake-gic {
+ compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953";
+ interrupt-controller;
+ interrupt-parent = <&intc>;
+ #interrupt-cells = <3>;
+ };
+ };
+
qcom,msm-gladiator@b1c0000 {
compatible = "qcom,msm-gladiator";
reg = <0x0b1c0000 0x4000>;
@@ -1162,6 +1187,11 @@
reg = <0x94c 200>;
};
+
+ diag_dload@c8 {
+ compatible = "qcom,msm-imem-diag-dload";
+ reg = <0xc8 200>;
+ };
};
qcom,memshare {
@@ -1890,6 +1920,7 @@
#include "msm8953-regulator.dtsi"
#include "msm-gdsc-8916.dtsi"
#include "msm8953-thermal.dtsi"
+#include "msm8953-camera.dtsi"
&gdsc_venus {
clock-names = "bus_clk", "core_clk";
diff --git a/arch/arm64/boot/dts/qcom/pm8953.dtsi b/arch/arm64/boot/dts/qcom/pm8953.dtsi
index d77de72..b26f993 100644
--- a/arch/arm64/boot/dts/qcom/pm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8953.dtsi
@@ -85,15 +85,17 @@
reg = <0xc000 0x800>;
interrupts = <0x0 0xc0 0 IRQ_TYPE_NONE>,
+ <0x0 0xc1 0 IRQ_TYPE_NONE>,
<0x0 0xc3 0 IRQ_TYPE_NONE>,
<0x0 0xc6 0 IRQ_TYPE_NONE>,
<0x0 0xc7 0 IRQ_TYPE_NONE>;
- interrupt-names = "pm8953_gpio1", "pm8953_gpio4",
- "pm8953_gpio7", "pm8953_gpio8";
+ interrupt-names = "pm8953_gpio1", "pm8953_gpio2",
+ "pm8953_gpio4", "pm8953_gpio7",
+ "pm8953_gpio8";
gpio-controller;
#gpio-cells = <2>;
- qcom,gpios-disallowed = <2 3 5 6>;
+ qcom,gpios-disallowed = <3 5 6>;
};
pm8953_vadc: vadc@3100 {
@@ -106,6 +108,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ #thermal-sensor-cells = <1>;
chan@5 {
label = "vcoin";
@@ -305,6 +308,36 @@
};
&thermal_zones {
+ xo-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8953_vadc 0x32>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ xo-therm-buf-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8953_vadc 0x3c>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
pm8953_tz {
polling-delay-passive = <0>;
polling-delay = <0>;
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index e5963ef..f3c99c9 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -214,6 +214,185 @@
#gpio-cells = <2>;
qcom,gpios-disallowed = <1>;
};
+
+ pmi632_charger: qcom,qpnp-smb5 {
+ compatible = "qcom,qpnp-smb5";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,pmic-revid = <&pmi632_revid>;
+ dpdm-supply = <&qusb_phy>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts =
+ <0x2 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x4 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x10 0x6 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x10 0x7 IRQ_TYPE_LEVEL_HIGH>;
+
+ interrupt-names = "chgr-error",
+ "chg-state-change",
+ "step-chg-state-change",
+ "step-chg-soc-update-fail",
+ "step-chg-soc-update-req",
+ "fg-fvcal-qualified",
+ "vph-alarm",
+ "vph-drop-prechg";
+ };
+
+ qcom,dcdc@1100 {
+ reg = <0x1100 0x100>;
+ interrupts =
+ <0x2 0x11 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x11 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x11 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x11 0x3 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x11 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x11 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x11 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x11 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "otg-fail",
+ "otg-oc-disable-sw",
+ "otg-oc-hiccup",
+ "bsm-active",
+ "high-duty-cycle",
+ "input-current-limiting",
+ "concurrent-mode-disable",
+ "switcher-power-ok";
+ };
+
+ qcom,batif@1200 {
+ reg = <0x1200 0x100>;
+ interrupts =
+ <0x2 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x12 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x12 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x12 0x6 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x12 0x7 IRQ_TYPE_LEVEL_HIGH>;
+
+ interrupt-names = "bat-temp",
+ "all-chnl-conv-done",
+ "bat-ov",
+ "bat-low",
+ "bat-therm-or-id-missing",
+ "bat-terminal-missing",
+ "buck-oc",
+ "vph-ov";
+ };
+
+ qcom,usb@1300 {
+ reg = <0x1300 0x100>;
+ interrupts =
+ <0x2 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x13 0x4 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x13 0x5 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "usbin-collapse",
+ "usbin-vashdn",
+ "usbin-uv",
+ "usbin-ov",
+ "usbin-plugin",
+ "usbin-revi-change",
+ "usbin-src-change",
+ "usbin-icl-change";
+ };
+
+ qcom,typec@1500 {
+ reg = <0x1500 0x100>;
+ interrupts =
+ <0x2 0x15 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x15 0x1 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x15 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x15 0x3 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x15 0x4 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x15 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x15 0x6 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x15 0x7 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "typec-or-rid-detect-change",
+ "typec-vpd-detect",
+ "typec-cc-state-change",
+ "typec-vconn-oc",
+ "typec-vbus-change",
+ "typec-attach-detach",
+ "typec-legacy-cable-detect",
+ "typec-try-snk-src-detect";
+ };
+
+ qcom,misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts =
+ <0x2 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x16 0x2 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x16 0x3 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x16 0x4 IRQ_TYPE_LEVEL_HIGH>,
+ <0x2 0x16 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+ <0x2 0x16 0x7 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "wdog-snarl",
+ "wdog-bark",
+ "aicl-fail",
+ "aicl-done",
+ "smb-en",
+ "imp-trigger",
+ "temp-change",
+ "temp-change-smb";
+ };
+
+ smb5_vbus: qcom,smb5-vbus {
+ regulator-name = "smb5-vbus";
+ };
+ };
+
+ pmi632_qg: qpnp,qg {
+ compatible = "qcom,qpnp-qg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,vbatt-empty-mv = <3200>;
+ qcom,vbatt-low-mv = <3500>;
+ qcom,vbatt-cutoff-mv = <3400>;
+ qcom,qg-iterm-ma = <100>;
+
+ qcom,qg-vadc = <&pmi632_vadc>;
+ qcom,pmic-revid = <&pmi632_revid>;
+
+ qcom,qgauge@4800 {
+ status = "okay";
+ reg = <0x4800 0x100>;
+ interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "qg-batt-missing",
+ "qg-vbat-low",
+ "qg-vbat-empty",
+ "qg-fifo-done",
+ "qg-good-ocv";
+ };
+
+ qcom,qg-sdam@b100 {
+ status = "okay";
+ reg = <0xb100 0x100>;
+ };
+ };
};
pmi632_3: qcom,pmi632@3 {
@@ -303,5 +482,91 @@
regulator-max-microvolt = <6000000>;
};
};
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led-v2";
+ status = "okay";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "led-fault-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq";
+ qcom,short-circuit-det;
+ qcom,open-circuit-det;
+ qcom,vph-droop-det;
+ qcom,thermal-derate-en;
+ qcom,thermal-derate-current = <200 500 1000>;
+ qcom,isc-delay = <192>;
+ qcom,pmic-revid = <&pmi632_revid>;
+
+ pmi632_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_switch0: qcom,led_switch_0 {
+ label = "switch";
+ qcom,led-name = "led:switch_0";
+ qcom,led-mask = <3>;
+ qcom,default-led-trigger = "switch0_trigger";
+ };
+
+ pmi632_switch1: qcom,led_switch_1 {
+ label = "switch";
+ qcom,led-name = "led:switch_1";
+ qcom,led-mask = <2>;
+ qcom,default-led-trigger = "switch1_trigger";
+ };
+
+ };
+
};
};
diff --git a/arch/arm64/boot/dts/qcom/pmi8937.dtsi b/arch/arm64/boot/dts/qcom/pmi8937.dtsi
index a7aa08a..c72225d 100644
--- a/arch/arm64/boot/dts/qcom/pmi8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8937.dtsi
@@ -153,7 +153,7 @@
#gpio-cells = <2>;
};
- pmi8937_charger: qcom,qpnp-smbcharger {
+ qpnp_smbcharger: qcom,qpnp-smbcharger {
compatible = "qcom,qpnp-smbcharger";
#address-cells = <1>;
#size-cells = <1>;
@@ -268,7 +268,7 @@
};
};
- pmi8937_fg: qcom,fg {
+ qpnp_fg: qcom,fg {
compatible = "qcom,qpnp-fg";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/pmi8940.dtsi b/arch/arm64/boot/dts/qcom/pmi8940.dtsi
index c6d5c87..d83145c 100644
--- a/arch/arm64/boot/dts/qcom/pmi8940.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8940.dtsi
@@ -152,7 +152,7 @@
#gpio-cells = <2>;
};
- pmi8940_charger: qcom,qpnp-smbcharger {
+ qpnp_smbcharger: qcom,qpnp-smbcharger {
compatible = "qcom,qpnp-smbcharger";
#address-cells = <1>;
#size-cells = <1>;
@@ -264,7 +264,7 @@
};
};
- pmi8940_fg: qcom,fg {
+ qpnp_fg: qcom,fg {
compatible = "qcom,qpnp-fg";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
index 4e82cfe..e3388c1 100644
--- a/arch/arm64/boot/dts/qcom/pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -168,7 +168,7 @@
#gpio-cells = <2>;
};
- pmi8950_charger: qcom,qpnp-smbcharger {
+ qpnp_smbcharger: qcom,qpnp-smbcharger {
compatible = "qcom,qpnp-smbcharger";
#address-cells = <1>;
#size-cells = <1>;
@@ -284,7 +284,7 @@
};
};
- pmi8950_fg: qcom,fg {
+ qpnp_fg: qcom,fg {
compatible = "qcom,qpnp-fg";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
index bf2241a..bad4fd7 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
@@ -43,10 +43,94 @@
status = "disabled";
};
-&dsi_dual_nt35597_truly_video {
+&dsi_dual_nt35597_truly_video_display {
status = "disabled";
};
+&qupv3_se9_i2c {
+ status = "okay";
+ lt9611@3b {
+ compatible = "lt,lt9611";
+ reg = <0x3b>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <125 0>;
+ interrupt-names = "lt_irq";
+ lt,irq-gpio = <&tlmm 125 0x0>;
+ lt,reset-gpio = <&tlmm 134 0x0>;
+ lt,hdmi-ps-gpio = <&tlmm 136 0x0>;
+ lt,hdmi-en-gpio = <&tlmm 137 0x0>;
+ lt,non-pluggable;
+
+ vcc-supply = <&pm660l_l6>;
+ vdd-supply = <&pm660_l11>;
+ lt,supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ lt,supply-entry@0 {
+ reg = <0>;
+ lt,supply-name = "vcc";
+ lt,supply-min-voltage = <3300000>;
+ lt,supply-max-voltage = <3300000>;
+ lt,supply-enable-load = <200000>;
+ lt,supply-post-on-sleep = <50>;
+ };
+
+ lt,supply-entry@1 {
+ reg = <1>;
+ lt,supply-name = "vdd";
+ lt,supply-min-voltage = <1800000>;
+ lt,supply-max-voltage = <1800000>;
+ lt,supply-enable-load = <200000>;
+ lt,supply-post-on-sleep = <50>;
+ };
+ };
+
+ lt,customize-modes {
+ lt,customize-mode-id@0 {
+ lt,mode-h-active = <1920>;
+ lt,mode-h-front-porch = <88>;
+ lt,mode-h-pulse-width = <44>;
+ lt,mode-h-back-porch = <148>;
+ lt,mode-h-active-high;
+ lt,mode-v-active = <1080>;
+ lt,mode-v-front-porch = <4>;
+ lt,mode-v-pulse-width = <5>;
+ lt,mode-v-back-porch = <36>;
+ lt,mode-v-active-high;
+ lt,mode-refresh-rate = <60>;
+ lt,mode-clock-in-khz = <148500>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ lt9611_in: endpoint {
+ remote-endpoint = <&ext_dsi_out>;
+ };
+ };
+ };
+ };
+};
+
+&soc {
+ qcom,dsi-display@17 {
+ qcom,dsi-display-active;
+
+ ports {
+ port@0 {
+ endpoint {
+ remote-endpoint = <<9611_in>;
+ };
+ };
+ };
+ };
+};
+
&int_codec {
qcom,model = "sdm670-360cam-snd-card";
qcom,audio-routing =
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dts b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dts
new file mode 100644
index 0000000..3ca53b7
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605-lc.dtsi"
+#include "qcs605-lc-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. QC605 LC Groot + PM8005 MTP";
+ compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+ qcom,board-id = <8 4>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
index d0b5bf3..02b6821 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
@@ -23,3 +23,21 @@
&qupv3_se8_spi {
status = "disabled";
};
+
+&sdhc_1 {
+ vdd-supply = <&pm660_l19>;
+ qcom,vdd-voltage-level = <2960000 2960000>;
+ qcom,vdd-current-level = <0 570000>;
+
+ vdd-io-supply = <&pm660_l8>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <0 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index fcde397..23da195 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -19,27 +19,39 @@
};
&pil_modem_mem {
- reg = <0 0x8b000000 0 0x3c00000>;
+ reg = <0 0x8b000000 0 0x3e00000>;
};
&pil_video_mem {
- reg = <0 0x8ec00000 0 0x500000>;
+ reg = <0 0x8ee00000 0 0x500000>;
};
&wlan_msa_mem {
- reg = <0 0x8f100000 0 0x100000>;
+ reg = <0 0x8f300000 0 0x100000>;
};
&pil_cdsp_mem {
- reg = <0 0x8f200000 0 0x800000>;
+ reg = <0 0x8f400000 0 0x800000>;
};
&pil_mba_mem {
- reg = <0 0x8fa00000 0 0x200000>;
+ reg = <0 0x8fc00000 0 0x200000>;
};
&pil_adsp_mem {
- reg = <0 0x8fc00000 0 0x1e00000>;
+ reg = <0 0x8fe00000 0 0x1e00000>;
+};
+
+&pil_ipa_fw_mem {
+ reg = <0 0x91c00000 0 0x10000>;
+};
+
+&pil_ipa_gsi_mem {
+ reg = <0 0x91c10000 0 0x5000>;
+};
+
+&pil_gpu_mem {
+ reg = <0 0x91c15000 0 0x2000>;
};
&soc {
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
new file mode 100644
index 0000000..4d35628
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
@@ -0,0 +1,1024 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,ascent_3450mah {
+ /* Ascent_wConn_3450mAh_Fresh_averaged_MasterSlave_Feb7th2018 */
+ qcom,max-voltage-uv = <4350000>;
+ qcom,fg-cc-cv-threshold-mv = <4340>;
+ qcom,fastchg-current-ma = <3450>;
+ qcom,batt-id-kohm = <60>;
+ qcom,battery-beta = <3435>;
+ qcom,battery-therm-kohm = <68>;
+ qcom,battery-type =
+ "Ascent_wConn_3450mAh_Fresh_averaged_MasterSlave_Feb7th2018";
+ qcom,qg-batt-profile-ver = <100>;
+
+ qcom,jeita-fcc-ranges = <0 100 1725000
+ 101 400 3450000
+ 401 450 2760000>;
+ qcom,jeita-fv-ranges = <0 100 4250000
+ 101 400 4350000
+ 401 450 4250000>;
+ qcom,step-chg-ranges = <3600000 4200000 3450000
+ 4201000 4300000 2760000
+ 4301000 4350000 2070000>;
+
+ qcom,fcc1-temp-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-data = <3377 3428 3481 3496 3500>;
+ };
+
+ qcom,fcc2-temp-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-data = <3474 3480 3482 3476 3492 3478 3466>;
+ };
+
+ qcom,pc-temp-v1-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <43212 43315 43370 43380 43383>,
+ <42963 43071 43141 43149 43152>,
+ <42723 42832 42902 42916 42922>,
+ <42488 42597 42662 42683 42693>,
+ <42262 42367 42430 42454 42465>,
+ <42043 42143 42202 42225 42238>,
+ <41832 41924 41976 41999 42013>,
+ <41624 41709 41754 41775 41791>,
+ <41419 41497 41536 41556 41571>,
+ <41220 41288 41322 41341 41355>,
+ <41039 41091 41113 41132 41143>,
+ <40866 40911 40916 40928 40936>,
+ <40676 40732 40729 40729 40734>,
+ <40449 40526 40541 40538 40541>,
+ <40230 40302 40340 40351 40355>,
+ <40061 40115 40146 40170 40176>,
+ <39918 39974 39984 40002 40007>,
+ <39787 39846 39843 39845 39846>,
+ <39672 39712 39697 39690 39689>,
+ <39560 39579 39548 39536 39538>,
+ <39426 39419 39388 39376 39384>,
+ <39271 39170 39192 39187 39198>,
+ <39112 38928 38961 38960 38967>,
+ <38934 38809 38788 38776 38775>,
+ <38764 38736 38674 38650 38646>,
+ <38660 38665 38581 38546 38539>,
+ <38589 38587 38491 38448 38439>,
+ <38533 38513 38408 38359 38347>,
+ <38487 38444 38334 38278 38263>,
+ <38448 38381 38265 38204 38187>,
+ <38407 38323 38204 38138 38118>,
+ <38364 38269 38149 38078 38055>,
+ <38322 38219 38099 38026 37999>,
+ <38284 38175 38053 37975 37942>,
+ <38249 38137 38014 37930 37889>,
+ <38211 38098 37974 37884 37834>,
+ <38174 38061 37930 37831 37767>,
+ <38129 38020 37882 37771 37691>,
+ <38055 37954 37816 37698 37610>,
+ <37946 37848 37718 37605 37524>,
+ <37825 37726 37602 37497 37426>,
+ <37689 37595 37474 37372 37301>,
+ <37541 37451 37332 37231 37156>,
+ <37372 37290 37178 37080 36999>,
+ <37271 37162 37061 36964 36886>,
+ <37191 37093 36992 36897 36819>,
+ <37157 37064 36970 36870 36798>,
+ <37128 37039 36949 36844 36770>,
+ <37084 37007 36903 36788 36714>,
+ <36927 36851 36683 36562 36488>,
+ <36555 36481 36290 36180 36108>,
+ <36071 35999 35771 35687 35620>,
+ <35450 35376 35098 35050 34994>,
+ <34604 34523 34174 34184 34150>,
+ <33275 33138 32725 32851 32862>,
+ <30000 30000 30000 30000 30000>;
+ };
+
+ qcom,pc-temp-v2-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <43435 43425 43415 43385 43360 43315 43295>,
+ <43032 43070 43109 43097 43094 43052 43033>,
+ <42666 42748 42821 42826 42838 42799 42781>,
+ <42342 42462 42555 42571 42594 42557 42541>,
+ <42053 42208 42308 42333 42361 42326 42312>,
+ <41788 41968 42072 42101 42133 42099 42086>,
+ <41531 41736 41844 41872 41904 41872 41860>,
+ <41298 41516 41625 41649 41680 41649 41637>,
+ <41128 41303 41405 41431 41460 41430 41419>,
+ <41009 41100 41186 41217 41243 41215 41204>,
+ <40843 40896 40979 41012 41034 41005 40994>,
+ <40462 40694 40796 40826 40840 40802 40789>,
+ <40004 40482 40616 40642 40648 40604 40590>,
+ <39736 40220 40407 40432 40445 40411 40399>,
+ <39556 39903 40175 40199 40233 40222 40214>,
+ <39382 39636 39954 39996 40043 40042 40036>,
+ <39187 39443 39759 39844 39886 39877 39868>,
+ <39001 39272 39570 39700 39736 39717 39706>,
+ <38839 39091 39370 39511 39573 39556 39547>,
+ <38695 38908 39162 39283 39399 39396 39391>,
+ <38574 38742 38973 39072 39208 39217 39217>,
+ <38477 38591 38806 38886 38975 38988 38994>,
+ <38393 38460 38655 38721 38760 38766 38771>,
+ <38314 38358 38519 38585 38613 38613 38614>,
+ <38242 38274 38396 38468 38496 38494 38494>,
+ <38176 38201 38287 38362 38390 38388 38387>,
+ <38117 38138 38189 38265 38294 38291 38288>,
+ <38062 38080 38106 38177 38206 38202 38198>,
+ <38006 38027 38039 38096 38126 38120 38114>,
+ <37953 37980 37983 38021 38051 38044 38037>,
+ <37900 37934 37935 37954 37984 37976 37967>,
+ <37847 37889 37895 37891 37921 37915 37905>,
+ <37795 37843 37856 37838 37862 37856 37846>,
+ <37742 37795 37815 37799 37803 37790 37781>,
+ <37690 37746 37774 37768 37746 37720 37707>,
+ <37635 37692 37727 37728 37685 37643 37621>,
+ <37574 37633 37677 37677 37620 37554 37517>,
+ <37509 37568 37618 37617 37550 37465 37411>,
+ <37441 37491 37546 37548 37475 37387 37327>,
+ <37368 37404 37459 37466 37395 37313 37256>,
+ <37292 37310 37360 37369 37301 37226 37174>,
+ <37209 37206 37243 37246 37187 37117 37069>,
+ <37119 37103 37111 37106 37050 36988 36945>,
+ <37020 37005 36964 36946 36900 36839 36796>,
+ <36901 36909 36860 36837 36818 36761 36712>,
+ <36762 36808 36782 36769 36775 36718 36673>,
+ <36671 36746 36740 36732 36741 36690 36651>,
+ <36552 36670 36688 36684 36692 36649 36612>,
+ <36386 36553 36594 36591 36575 36542 36528>,
+ <36157 36329 36380 36368 36272 36271 36278>,
+ <35824 35961 35991 35957 35796 35833 35867>,
+ <35340 35436 35452 35401 35193 35271 35340>,
+ <34648 34709 34745 34680 34409 34550 34676>,
+ <33623 33672 33759 33677 33330 33585 33791>,
+ <31758 31857 32191 32244 31867 32001 32517>,
+ <27623 28160 28896 29014 27510 28586 29617>;
+ };
+
+ qcom,pc-temp-z1-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <13703 12983 12375 12138 12079>,
+ <13647 12967 12370 12145 12092>,
+ <13621 12953 12363 12143 12093>,
+ <13606 12942 12357 12142 12093>,
+ <13594 12927 12349 12140 12093>,
+ <13585 12916 12342 12137 12093>,
+ <13577 12905 12339 12136 12092>,
+ <13569 12898 12337 12135 12092>,
+ <13560 12896 12337 12135 12091>,
+ <13551 12895 12339 12135 12091>,
+ <13540 12896 12342 12136 12091>,
+ <13527 12899 12342 12136 12091>,
+ <13512 12901 12342 12137 12093>,
+ <13488 12896 12342 12139 12095>,
+ <13469 12887 12343 12141 12097>,
+ <13469 12883 12347 12144 12098>,
+ <13473 12891 12352 12147 12101>,
+ <13478 12904 12360 12151 12103>,
+ <13489 12912 12369 12155 12106>,
+ <13502 12919 12378 12160 12109>,
+ <13514 12927 12387 12165 12113>,
+ <13525 12937 12396 12171 12117>,
+ <13538 12948 12404 12177 12122>,
+ <13555 12957 12412 12184 12127>,
+ <13572 12965 12422 12189 12131>,
+ <13578 12971 12431 12195 12135>,
+ <13580 12978 12440 12200 12139>,
+ <13582 12985 12448 12206 12143>,
+ <13594 12992 12456 12212 12147>,
+ <13613 13004 12464 12218 12151>,
+ <13625 13010 12472 12224 12156>,
+ <13631 13010 12479 12230 12160>,
+ <13632 13010 12486 12237 12165>,
+ <13627 13015 12494 12244 12171>,
+ <13618 13033 12505 12253 12177>,
+ <13619 13047 12517 12262 12183>,
+ <13629 13056 12529 12271 12190>,
+ <13640 13065 12541 12280 12196>,
+ <13647 13068 12553 12290 12203>,
+ <13654 13070 12565 12301 12210>,
+ <13663 13071 12577 12311 12217>,
+ <13678 13085 12588 12322 12224>,
+ <13694 13098 12598 12332 12232>,
+ <13718 13107 12607 12344 12239>,
+ <13717 13110 12621 12356 12248>,
+ <13717 13111 12638 12367 12256>,
+ <13693 13110 12640 12374 12262>,
+ <13712 13131 12642 12382 12269>,
+ <13714 13118 12659 12395 12280>,
+ <13717 13140 12677 12408 12291>,
+ <13717 13160 12678 12422 12303>,
+ <13740 13166 12691 12436 12316>,
+ <13739 13183 12711 12456 12332>,
+ <13754 13201 12735 12479 12355>,
+ <13754 13201 12735 12479 12355>,
+ <13754 13201 12735 12479 12355>;
+ };
+
+ qcom,pc-temp-z2-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <9983 10351 10639 10481 10418>,
+ <10020 10341 10669 10329 10279>,
+ <10051 10333 10528 10319 10282>,
+ <10083 10331 10397 10312 10281>,
+ <10100 10329 10358 10299 10263>,
+ <10094 10325 10328 10285 10234>,
+ <10081 10303 10324 10279 10227>,
+ <10069 10281 10328 10277 10237>,
+ <10058 10278 10330 10276 10251>,
+ <10050 10280 10326 10276 10266>,
+ <10044 10285 10321 10281 10280>,
+ <10039 10298 10323 10288 10285>,
+ <10038 10310 10324 10308 10284>,
+ <10045 10309 10318 10321 10285>,
+ <10054 10304 10304 10319 10295>,
+ <10056 10302 10291 10314 10309>,
+ <10056 10312 10297 10316 10321>,
+ <10058 10329 10320 10326 10335>,
+ <10070 10353 10339 10334 10341>,
+ <10090 10386 10349 10339 10346>,
+ <10111 10400 10358 10343 10354>,
+ <10138 10396 10373 10334 10340>,
+ <10157 10388 10395 10289 10270>,
+ <10169 10381 10400 10243 10212>,
+ <10178 10372 10354 10197 10174>,
+ <10183 10364 10306 10161 10144>,
+ <10190 10352 10304 10160 10141>,
+ <10194 10341 10307 10170 10149>,
+ <10185 10335 10310 10181 10158>,
+ <10169 10332 10311 10196 10169>,
+ <10165 10332 10314 10216 10183>,
+ <10168 10343 10327 10238 10202>,
+ <10171 10357 10356 10267 10226>,
+ <10175 10368 10381 10298 10253>,
+ <10180 10376 10405 10333 10288>,
+ <10183 10384 10422 10367 10326>,
+ <10186 10393 10431 10393 10358>,
+ <10189 10403 10438 10416 10387>,
+ <10187 10410 10442 10437 10405>,
+ <10181 10419 10455 10461 10418>,
+ <10175 10423 10465 10476 10426>,
+ <10164 10407 10466 10476 10430>,
+ <10152 10385 10468 10474 10428>,
+ <10142 10373 10453 10473 10402>,
+ <10064 10371 10455 10479 10389>,
+ <10173 10285 10486 10517 10402>,
+ <10003 10290 10490 10530 10416>,
+ <10151 10295 10531 10566 10477>,
+ <10255 10515 10588 10612 10540>,
+ <10077 10376 10556 10524 10421>,
+ <9948 10240 10480 10454 10314>,
+ <9854 10122 10384 10398 10256>,
+ <9640 9998 10319 10342 10182>,
+ <9440 9830 10255 10261 10065>,
+ <9440 9830 10255 10261 10065>,
+ <9440 9830 10255 10261 10065>;
+ };
+
+ qcom,pc-temp-z3-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <19441 19367 19362 19326 19316>,
+ <19560 19428 19358 19334 19329>,
+ <19615 19467 19373 19341 19335>,
+ <19645 19480 19383 19346 19340>,
+ <19658 19487 19385 19351 19344>,
+ <19657 19488 19387 19354 19347>,
+ <19657 19486 19386 19355 19348>,
+ <19655 19483 19384 19354 19347>,
+ <19651 19481 19382 19352 19346>,
+ <19647 19479 19380 19350 19345>,
+ <19645 19477 19378 19347 19344>,
+ <19642 19470 19377 19345 19342>,
+ <19642 19464 19374 19345 19342>,
+ <19642 19464 19372 19344 19341>,
+ <19643 19467 19372 19343 19338>,
+ <19639 19467 19372 19342 19336>,
+ <19626 19460 19371 19340 19334>,
+ <19615 19450 19369 19338 19332>,
+ <19604 19448 19368 19337 19330>,
+ <19596 19446 19368 19337 19328>,
+ <19597 19447 19369 19337 19326>,
+ <19605 19452 19372 19340 19328>,
+ <19612 19460 19378 19350 19341>,
+ <19619 19462 19386 19357 19350>,
+ <19626 19464 19394 19361 19353>,
+ <19627 19466 19401 19364 19355>,
+ <19626 19472 19400 19364 19355>,
+ <19624 19480 19398 19362 19352>,
+ <19620 19480 19395 19359 19349>,
+ <19615 19476 19391 19356 19346>,
+ <19611 19473 19388 19353 19343>,
+ <19609 19469 19384 19350 19340>,
+ <19606 19466 19381 19347 19336>,
+ <19604 19463 19379 19343 19332>,
+ <19601 19461 19377 19338 19327>,
+ <19597 19458 19375 19335 19324>,
+ <19592 19454 19373 19334 19325>,
+ <19587 19451 19370 19334 19329>,
+ <19584 19448 19368 19334 19330>,
+ <19582 19446 19366 19334 19328>,
+ <19579 19443 19364 19334 19326>,
+ <19575 19441 19362 19334 19326>,
+ <19571 19438 19360 19334 19327>,
+ <19565 19434 19359 19336 19329>,
+ <19361 19423 19353 19335 19328>,
+ <19262 19382 19344 19323 19324>,
+ <19263 19371 19336 19321 19317>,
+ <19261 19361 19332 19317 19313>,
+ <19261 19273 19326 19317 19309>,
+ <19262 19274 19335 19327 19319>,
+ <19263 19303 19337 19330 19324>,
+ <19263 19274 19357 19333 19327>,
+ <19266 19275 19361 19337 19335>,
+ <19272 19280 19368 19349 19345>,
+ <19272 19280 19368 19349 19345>,
+ <19272 19280 19368 19349 19345>;
+ };
+
+ qcom,pc-temp-z4-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <15627 15107 14765 14761 14758>,
+ <15711 15099 14864 14812 14796>,
+ <15693 15080 14843 14798 14788>,
+ <15530 15036 14821 14778 14775>,
+ <15355 14952 14794 14760 14761>,
+ <15246 14887 14764 14740 14745>,
+ <15155 14845 14741 14726 14731>,
+ <15085 14812 14721 14713 14719>,
+ <15031 14784 14710 14705 14711>,
+ <14985 14760 14705 14700 14707>,
+ <14938 14745 14702 14697 14705>,
+ <14895 14735 14696 14693 14702>,
+ <14876 14729 14689 14688 14699>,
+ <14872 14730 14686 14684 14697>,
+ <14869 14735 14686 14681 14694>,
+ <14856 14737 14686 14680 14692>,
+ <14830 14720 14682 14677 14689>,
+ <14808 14702 14673 14672 14684>,
+ <14790 14701 14669 14669 14681>,
+ <14778 14706 14676 14670 14679>,
+ <14782 14717 14687 14675 14677>,
+ <14805 14815 14716 14690 14685>,
+ <14835 14927 14778 14733 14726>,
+ <14898 14927 14803 14755 14750>,
+ <14965 14883 14774 14738 14735>,
+ <14974 14842 14741 14715 14712>,
+ <14963 14813 14728 14705 14703>,
+ <14950 14789 14721 14700 14699>,
+ <14932 14778 14717 14696 14696>,
+ <14910 14771 14715 14693 14692>,
+ <14892 14767 14713 14691 14688>,
+ <14878 14766 14712 14688 14683>,
+ <14867 14765 14712 14684 14677>,
+ <14856 14764 14710 14681 14673>,
+ <14847 14763 14709 14676 14668>,
+ <14838 14761 14706 14673 14666>,
+ <14826 14756 14703 14673 14669>,
+ <14816 14748 14700 14674 14680>,
+ <14809 14744 14697 14674 14683>,
+ <14804 14742 14695 14673 14677>,
+ <14797 14739 14693 14671 14673>,
+ <14787 14730 14687 14667 14673>,
+ <14774 14718 14678 14663 14675>,
+ <14753 14708 14673 14662 14682>,
+ <14897 14683 14660 14653 14674>,
+ <14965 14689 14638 14630 14635>,
+ <14964 14694 14635 14621 14624>,
+ <14958 14699 14630 14614 14617>,
+ <14951 14794 14633 14616 14620>,
+ <14945 14795 14637 14644 14655>,
+ <14957 14769 14642 14654 14664>,
+ <14968 14805 14636 14660 14668>,
+ <14977 14809 14655 14668 14668>,
+ <14993 14817 14684 14674 14665>,
+ <14993 14817 14684 14674 14665>,
+ <14993 14817 14684 14674 14665>;
+ };
+
+ qcom,pc-temp-z5-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <11983 12740 15393 15537 15330>,
+ <13357 14824 16249 17310 17529>,
+ <14286 16482 17811 18377 18523>,
+ <14945 17642 19175 19223 19510>,
+ <15486 18555 20024 20275 20513>,
+ <15957 19327 20723 21346 21540>,
+ <16368 19918 21568 22345 22519>,
+ <16781 20541 22514 23454 23469>,
+ <17187 21683 23120 24074 24236>,
+ <17674 23224 23449 23971 24698>,
+ <18442 24060 23713 23757 25052>,
+ <19483 24549 23914 23816 25554>,
+ <20437 24925 24207 24637 26312>,
+ <21361 25257 24451 25542 26851>,
+ <22105 25909 24697 26092 27021>,
+ <22478 26475 25087 26614 27131>,
+ <22735 27141 26348 27394 27470>,
+ <23076 28081 28954 28566 28331>,
+ <23809 29715 30229 29356 28825>,
+ <24938 32251 30431 29724 28646>,
+ <27176 33544 30446 29885 28135>,
+ <31932 33025 28578 28445 26783>,
+ <35226 31860 23567 23249 22747>,
+ <36819 29994 21621 20580 20432>,
+ <37874 26035 23092 21806 21763>,
+ <37147 24128 25214 23841 23948>,
+ <34993 26078 27156 25488 25430>,
+ <32825 29127 29386 27105 26647>,
+ <30980 31187 31134 28441 27481>,
+ <29376 32887 32400 29515 28206>,
+ <28873 34208 33430 30375 28775>,
+ <28786 35400 34311 31101 29161>,
+ <28801 36250 35051 31824 29873>,
+ <28836 36325 35497 32045 30283>,
+ <28862 36122 35938 31861 30590>,
+ <28907 35854 36221 31646 31106>,
+ <28980 35186 35497 31570 32686>,
+ <29132 34291 33694 31549 35494>,
+ <29670 33796 32315 31543 36261>,
+ <30652 33516 31332 31469 34185>,
+ <30992 33114 30388 31412 32325>,
+ <29938 32130 29262 32046 32870>,
+ <29033 30867 28327 33225 34089>,
+ <28928 29512 27958 34867 35083>,
+ <16971 28826 27177 35407 35456>,
+ <11113 18542 25875 28236 38118>,
+ <11084 16735 22486 31621 32274>,
+ <11037 15601 21798 36765 39261>,
+ <11035 11499 20071 40032 37713>,
+ <11055 11574 22201 35765 26327>,
+ <11017 12415 21829 29484 22484>,
+ <10951 11419 39496 26044 20817>,
+ <10890 11348 40899 24307 20580>,
+ <10822 11392 37952 25288 21070>,
+ <10822 11392 37952 25288 21070>,
+ <10822 11392 37952 25288 21070>;
+ };
+
+ qcom,pc-temp-z6-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <15711 15103 14773 14710 14694>,
+ <15778 15117 14813 14736 14718>,
+ <15776 15120 14809 14733 14717>,
+ <15702 15106 14804 14728 14716>,
+ <15620 15065 14793 14722 14713>,
+ <15561 15031 14779 14715 14707>,
+ <15508 15007 14768 14709 14701>,
+ <15467 14987 14758 14703 14696>,
+ <15435 14971 14751 14698 14692>,
+ <15407 14959 14748 14695 14689>,
+ <15379 14949 14746 14692 14687>,
+ <15353 14940 14743 14689 14685>,
+ <15339 14935 14739 14687 14684>,
+ <15332 14935 14736 14685 14682>,
+ <15326 14938 14737 14684 14680>,
+ <15316 14939 14738 14683 14678>,
+ <15302 14930 14736 14681 14676>,
+ <15293 14921 14733 14679 14673>,
+ <15289 14921 14732 14677 14671>,
+ <15286 14926 14736 14678 14670>,
+ <15291 14936 14744 14682 14669>,
+ <15312 14986 14761 14691 14674>,
+ <15338 15040 14795 14718 14702>,
+ <15373 15042 14808 14732 14717>,
+ <15408 15027 14802 14727 14713>,
+ <15415 15016 14795 14720 14706>,
+ <15414 15011 14791 14717 14702>,
+ <15412 15007 14788 14714 14699>,
+ <15408 15006 14786 14712 14697>,
+ <15402 15004 14785 14710 14694>,
+ <15399 15004 14784 14708 14691>,
+ <15398 15005 14784 14706 14688>,
+ <15397 15006 14784 14704 14684>,
+ <15397 15009 14784 14701 14680>,
+ <15398 15012 14784 14699 14677>,
+ <15400 15015 14785 14696 14674>,
+ <15402 15016 14785 14697 14677>,
+ <15405 15017 14786 14699 14684>,
+ <15410 15020 14787 14700 14687>,
+ <15416 15026 14789 14702 14685>,
+ <15423 15031 14791 14703 14683>,
+ <15430 15034 14791 14703 14684>,
+ <15437 15037 14791 14704 14687>,
+ <15444 15040 14792 14706 14693>,
+ <15425 15039 14789 14704 14690>,
+ <15419 15030 14781 14691 14673>,
+ <15424 15030 14778 14688 14665>,
+ <15425 15032 14777 14685 14661>,
+ <15431 15036 14780 14688 14662>,
+ <15438 15047 14792 14708 14685>,
+ <15458 15060 14801 14718 14695>,
+ <15477 15072 14815 14726 14702>,
+ <15501 15088 14835 14737 14710>,
+ <15534 15114 14862 14753 14720>,
+ <15534 15114 14862 14753 14720>,
+ <15534 15114 14862 14753 14720>;
+ };
+
+ qcom,pc-temp-y1-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <7619 6963 6363 6007 5630 5512 5470>,
+ <7591 6945 6361 6003 5629 5510 5471>,
+ <7573 6934 6357 5999 5628 5508 5471>,
+ <7563 6929 6354 5995 5627 5506 5471>,
+ <7558 6928 6353 5992 5626 5505 5470>,
+ <7556 6928 6357 5988 5625 5503 5470>,
+ <7563 6934 6366 5985 5624 5502 5469>,
+ <7573 6942 6374 5981 5624 5501 5467>,
+ <7583 6939 6374 5982 5623 5500 5467>,
+ <7616 6920 6366 5984 5624 5499 5467>,
+ <7634 6910 6360 5985 5624 5498 5466>,
+ <7600 6919 6357 5980 5623 5498 5466>,
+ <7557 6931 6356 5973 5622 5498 5465>,
+ <7560 6935 6362 5973 5622 5499 5465>,
+ <7590 6938 6380 5978 5625 5500 5466>,
+ <7605 6939 6390 5981 5628 5501 5467>,
+ <7608 6942 6389 5985 5631 5501 5468>,
+ <7612 6946 6387 5990 5634 5502 5469>,
+ <7604 6943 6388 5989 5637 5503 5470>,
+ <7584 6932 6391 5988 5639 5506 5471>,
+ <7570 6929 6394 5987 5643 5508 5472>,
+ <7557 6957 6397 5989 5647 5510 5473>,
+ <7546 6985 6400 5994 5653 5513 5475>,
+ <7543 6976 6405 6001 5658 5515 5478>,
+ <7542 6942 6414 6012 5663 5518 5480>,
+ <7542 6928 6418 6018 5668 5522 5483>,
+ <7552 6934 6419 6019 5674 5525 5486>,
+ <7565 6942 6418 6020 5681 5529 5488>,
+ <7569 6951 6418 6021 5687 5533 5491>,
+ <7568 6972 6418 6023 5692 5537 5493>,
+ <7564 6978 6418 6025 5698 5541 5497>,
+ <7549 6971 6422 6028 5705 5545 5500>,
+ <7534 6961 6425 6032 5712 5550 5504>,
+ <7531 6947 6422 6038 5720 5554 5507>,
+ <7529 6929 6416 6048 5727 5559 5509>,
+ <7527 6925 6416 6055 5734 5563 5512>,
+ <7537 6932 6428 6059 5741 5569 5516>,
+ <7548 6939 6438 6062 5748 5575 5522>,
+ <7546 6939 6433 6063 5757 5580 5526>,
+ <7534 6942 6422 6064 5767 5585 5529>,
+ <7527 6944 6417 6067 5775 5591 5533>,
+ <7526 6946 6416 6077 5783 5596 5537>,
+ <7530 6941 6418 6083 5787 5603 5541>,
+ <7568 6923 6429 6090 5790 5610 5546>,
+ <7559 6953 6428 6086 5808 5622 5555>,
+ <7568 6932 6433 6091 5813 5631 5561>,
+ <7570 6928 6433 6099 5818 5635 5565>,
+ <7608 6933 6442 6113 5834 5640 5567>,
+ <7652 6932 6430 6105 5835 5645 5571>,
+ <7693 6936 6436 6106 5848 5654 5578>,
+ <7737 6965 6440 6099 5858 5666 5585>,
+ <7770 6990 6464 6110 5873 5684 5600>,
+ <7830 7027 6445 6123 5887 5704 5613>,
+ <7848 7064 6471 6136 5914 5726 5633>,
+ <7848 7064 6471 6136 5914 5726 5633>,
+ <7848 7064 6471 6136 5914 5726 5633>;
+ };
+
+ qcom,pc-temp-y2-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <9652 9643 10780 11037 11216 11097 11009>,
+ <9653 9653 10747 10993 11177 11076 11016>,
+ <9654 9867 10710 10945 11129 11053 11018>,
+ <9655 10082 10673 10898 11080 11031 11018>,
+ <9655 10246 10642 10860 11037 11010 11015>,
+ <9655 10308 10624 10837 11007 10992 11010>,
+ <9655 10281 10616 10826 10987 10977 10981>,
+ <9655 10241 10610 10820 10968 10962 10937>,
+ <9655 10275 10600 10816 10953 10942 10920>,
+ <9655 10440 10586 10802 10939 10913 10908>,
+ <9655 10531 10580 10792 10927 10894 10901>,
+ <9654 10522 10610 10786 10915 10881 10897>,
+ <9654 10502 10665 10780 10900 10869 10890>,
+ <9653 10430 10721 10797 10878 10869 10877>,
+ <9653 10226 10789 10873 10846 10876 10849>,
+ <9653 10115 10817 10940 10834 10878 10831>,
+ <9653 9949 10805 10988 10876 10871 10840>,
+ <9653 9749 10791 11021 10939 10863 10860>,
+ <9653 9704 10791 11014 10991 10869 10866>,
+ <9653 9683 10795 10979 11041 10898 10870>,
+ <9653 9677 10801 10965 11063 10936 10877>,
+ <9653 9676 10808 10997 11078 11004 10940>,
+ <9653 9676 10820 11042 11093 11066 11026>,
+ <9652 9674 10836 11062 11099 11074 11055>,
+ <9652 9670 10861 11077 11105 11063 11066>,
+ <9652 9667 10915 11082 11111 11064 11068>,
+ <9652 9665 11341 11085 11126 11092 11072>,
+ <9652 9663 11748 11091 11149 11125 11083>,
+ <9652 9661 11516 11099 11179 11148 11096>,
+ <9652 9659 10816 11110 11217 11174 11118>,
+ <9652 9658 10376 11118 11236 11190 11131>,
+ <9652 9657 10155 11117 11245 11208 11140>,
+ <9652 9656 10000 11109 11252 11224 11152>,
+ <9652 9655 9892 11094 11259 11233 11175>,
+ <9652 9654 9812 11060 11260 11250 11208>,
+ <9652 9654 9762 11020 11250 11255 11218>,
+ <9652 9653 9725 10915 11227 11244 11186>,
+ <9652 9653 9701 10784 11207 11227 11146>,
+ <9652 9653 9688 10695 11192 11211 11135>,
+ <9651 9652 9678 10615 11172 11194 11131>,
+ <9651 9652 9671 10480 11143 11192 11126>,
+ <9651 9652 9665 10201 11106 11216 11112>,
+ <9651 9651 9661 10108 11076 11220 11105>,
+ <9651 9651 9658 10159 11042 11183 11120>,
+ <9650 9651 9656 9965 10929 11157 11073>,
+ <9650 9651 9654 9856 10889 11090 11023>,
+ <9650 9651 9653 9796 10823 11057 10968>,
+ <9649 9650 9653 9741 10794 11051 10955>,
+ <9649 9650 9652 9696 10740 11029 10918>,
+ <9649 9650 9652 9680 10652 10968 10907>,
+ <9648 9650 9651 9670 10540 10916 10868>,
+ <9648 9649 9651 9663 10397 10801 10776>,
+ <9647 9648 9650 9657 10262 10712 10696>,
+ <9647 9648 9650 9654 10448 10584 10596>,
+ <9647 9648 9650 9654 10448 10584 10596>,
+ <9647 9648 9650 9654 10448 10584 10596>;
+ };
+
+ qcom,pc-temp-y3-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <14249 13491 13351 13296 13276 13275 13272>,
+ <14289 13481 13348 13297 13278 13275 13272>,
+ <14293 13476 13346 13298 13280 13275 13273>,
+ <14269 13476 13347 13300 13282 13276 13274>,
+ <14223 13480 13349 13302 13283 13276 13274>,
+ <14165 13486 13352 13303 13283 13276 13275>,
+ <14057 13513 13358 13303 13283 13276 13275>,
+ <13951 13546 13363 13304 13283 13276 13275>,
+ <13946 13543 13365 13306 13283 13277 13275>,
+ <13996 13505 13365 13310 13284 13278 13275>,
+ <14024 13476 13366 13315 13285 13278 13276>,
+ <13979 13465 13371 13319 13285 13278 13276>,
+ <13920 13456 13377 13322 13285 13278 13276>,
+ <13916 13446 13376 13322 13286 13278 13276>,
+ <13935 13434 13369 13321 13288 13279 13277>,
+ <13956 13428 13363 13320 13290 13279 13277>,
+ <13974 13428 13359 13318 13291 13280 13278>,
+ <13993 13428 13354 13316 13292 13282 13278>,
+ <14013 13414 13349 13313 13292 13283 13279>,
+ <14037 13377 13343 13308 13291 13283 13281>,
+ <14068 13356 13338 13304 13290 13284 13281>,
+ <14115 13351 13336 13301 13286 13280 13278>,
+ <14171 13349 13333 13299 13283 13276 13274>,
+ <14221 13354 13323 13298 13282 13275 13273>,
+ <14271 13374 13301 13297 13281 13275 13273>,
+ <14325 13397 13289 13297 13281 13275 13273>,
+ <14385 13424 13273 13297 13280 13275 13273>,
+ <14448 13457 13259 13297 13280 13275 13273>,
+ <14513 13494 13259 13296 13280 13275 13273>,
+ <14578 13537 13259 13296 13280 13275 13273>,
+ <14647 13586 13260 13295 13280 13274 13272>,
+ <14717 13644 13261 13291 13280 13274 13272>,
+ <14792 13708 13262 13288 13279 13274 13272>,
+ <14878 13777 13264 13289 13279 13274 13272>,
+ <14972 13853 13268 13293 13279 13273 13272>,
+ <15069 13939 13274 13295 13279 13273 13272>,
+ <15166 14040 13285 13294 13279 13274 13272>,
+ <15270 14153 13299 13292 13280 13274 13273>,
+ <15386 14276 13317 13292 13279 13274 13273>,
+ <15514 14412 13340 13293 13279 13274 13273>,
+ <15658 14564 13372 13291 13279 13274 13273>,
+ <15825 14738 13423 13271 13280 13275 13273>,
+ <16019 14925 13493 13262 13280 13275 13273>,
+ <16253 15128 13598 13261 13280 13274 13273>,
+ <16581 15354 13729 13263 13279 13275 13274>,
+ <16918 15547 13885 13266 13284 13276 13275>,
+ <17576 15700 13996 13270 13284 13277 13277>,
+ <18634 15883 14139 13280 13288 13279 13277>,
+ <20075 16098 14305 13309 13290 13280 13277>,
+ <22149 16340 14489 13343 13292 13280 13276>,
+ <25271 16656 14726 13389 13294 13281 13277>,
+ <29950 17634 15025 13481 13299 13283 13278>,
+ <37310 19636 15401 13635 13309 13286 13283>,
+ <47876 22946 16005 13899 13335 13293 13287>,
+ <47876 22946 16005 13899 13335 13293 13287>,
+ <47876 22946 16005 13899 13335 13293 13287>;
+ };
+
+ qcom,pc-temp-y4-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <17711 16973 16623 16526 16458 16452 16456>,
+ <17567 16999 16632 16525 16457 16452 16456>,
+ <17504 17030 16641 16525 16457 16452 16456>,
+ <17504 17060 16652 16526 16457 16452 16456>,
+ <17547 17082 16663 16528 16457 16452 16456>,
+ <17613 17089 16674 16531 16457 16452 16456>,
+ <17765 17077 16687 16535 16459 16452 16456>,
+ <17948 17059 16701 16539 16462 16453 16456>,
+ <18024 17098 16711 16545 16465 16453 16456>,
+ <18066 17276 16720 16552 16468 16454 16457>,
+ <18080 17396 16735 16563 16473 16455 16457>,
+ <17811 17426 16824 16584 16480 16458 16458>,
+ <17427 17443 16930 16612 16490 16461 16460>,
+ <17319 17384 16951 16638 16498 16465 16462>,
+ <17285 17155 16958 16663 16504 16470 16465>,
+ <17245 17028 16959 16695 16515 16476 16469>,
+ <17166 17014 16934 16758 16539 16486 16475>,
+ <17085 17005 16887 16816 16571 16502 16486>,
+ <17039 16980 16831 16800 16609 16530 16506>,
+ <17007 16933 16754 16713 16656 16576 16541>,
+ <16985 16886 16692 16635 16664 16593 16558>,
+ <16968 16837 16644 16572 16570 16535 16521>,
+ <16957 16797 16610 16522 16475 16468 16470>,
+ <16954 16783 16607 16507 16460 16455 16458>,
+ <16953 16776 16623 16498 16454 16451 16455>,
+ <16953 16770 16632 16494 16453 16449 16454>,
+ <16951 16765 16645 16493 16454 16449 16453>,
+ <16951 16761 16659 16493 16455 16448 16453>,
+ <16953 16761 16662 16492 16457 16449 16453>,
+ <16959 16761 16663 16493 16461 16452 16454>,
+ <16965 16762 16667 16493 16466 16456 16455>,
+ <16974 16764 16677 16492 16474 16464 16461>,
+ <16982 16767 16687 16492 16479 16471 16470>,
+ <16985 16770 16690 16494 16478 16474 16476>,
+ <16987 16775 16693 16500 16474 16474 16483>,
+ <16990 16778 16693 16505 16467 16471 16482>,
+ <16999 16782 16689 16508 16455 16458 16467>,
+ <17010 16784 16685 16510 16448 16447 16453>,
+ <17021 16783 16685 16511 16449 16450 16455>,
+ <17033 16781 16683 16512 16452 16456 16465>,
+ <17048 16779 16681 16517 16455 16459 16471>,
+ <17066 16770 16670 16558 16460 16458 16470>,
+ <17087 16768 16654 16579 16464 16456 16468>,
+ <17112 16776 16634 16584 16465 16448 16452>,
+ <17124 16801 16629 16593 16474 16448 16444>,
+ <17066 16819 16647 16617 16493 16467 16470>,
+ <17161 16856 16669 16641 16507 16488 16496>,
+ <17324 16905 16699 16672 16536 16520 16521>,
+ <17559 16944 16754 16675 16565 16529 16536>,
+ <17893 16949 16780 16683 16555 16503 16488>,
+ <18450 16941 16767 16681 16554 16495 16479>,
+ <19496 17078 16794 16695 16582 16508 16485>,
+ <21979 17565 16874 16751 16646 16545 16510>,
+ <30091 19162 17124 16888 16859 16671 16581>,
+ <30091 19162 17124 16888 16859 16671 16581>,
+ <30091 19162 17124 16888 16859 16671 16581>;
+ };
+
+ qcom,pc-temp-y5-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <9336 11501 16034 14767 13793 19407 15565>,
+ <10230 11603 15822 15156 15683 19413 16938>,
+ <10807 11875 15497 15333 17017 19416 18058>,
+ <11133 12285 15152 15352 17855 19416 18894>,
+ <11279 12803 14881 15266 18259 19413 19416>,
+ <11310 13403 14778 15128 18284 19405 19590>,
+ <11059 14551 14807 14793 17405 18933 19301>,
+ <10701 15710 14848 14419 16236 18269 18809>,
+ <11143 15425 14688 14471 15943 18195 18561>,
+ <13343 13533 13983 14951 15873 18327 18385>,
+ <14582 12513 13556 15219 15744 18394 18198>,
+ <13448 13803 13493 14892 15050 17832 17726>,
+ <11924 15473 13460 14418 14306 16921 17099>,
+ <11782 15394 13744 14254 14381 16318 16821>,
+ <12065 14258 14851 14181 14869 15815 16673>,
+ <12212 13562 15643 14199 15070 15556 16471>,
+ <12106 13428 15965 14556 14786 15450 15954>,
+ <11900 13274 16199 15160 14490 15387 15465>,
+ <11618 12936 16398 15863 14755 15446 15449>,
+ <11196 12423 16588 16824 15793 15777 15650>,
+ <11019 11952 16732 17460 16919 16267 15980>,
+ <11074 11476 16879 17821 18292 17600 16933>,
+ <11141 11117 16964 18087 19430 19112 18163>,
+ <11133 11033 16245 18242 19821 19838 19000>,
+ <11069 11009 14141 18325 19945 20331 19696>,
+ <11046 11024 12876 18331 19914 20756 20093>,
+ <11109 11056 11798 18517 19842 21457 20297>,
+ <11170 11084 10948 18759 19785 21999 20418>,
+ <11161 11132 10879 18828 19716 21658 20286>,
+ <11138 11232 10862 18868 19575 20509 19563>,
+ <11111 11269 10868 18768 19409 19604 18813>,
+ <11071 11345 11005 17096 19184 18920 18072>,
+ <11039 11421 11165 15478 19049 18499 17524>,
+ <11083 11456 11261 16038 19331 18661 17776>,
+ <11207 11520 11350 17571 20041 19331 19034>,
+ <11252 11543 11387 18037 20606 20274 20444>,
+ <11213 11613 11553 17270 21412 23017 22060>,
+ <11168 11678 11697 16300 21789 25208 23225>,
+ <11151 11640 11640 15861 20914 24442 22731>,
+ <11145 11545 11511 15530 19618 22425 21047>,
+ <11154 11506 11463 14798 19274 21559 20283>,
+ <11189 11457 11412 12697 19217 21506 20308>,
+ <11250 11415 11500 11853 18717 21719 20486>,
+ <11392 11368 11742 11779 17923 22443 22679>,
+ <11471 11364 11818 11639 16687 21754 22971>,
+ <11412 11379 11581 11310 17555 18408 18279>,
+ <11284 11464 11576 11214 16686 17313 17483>,
+ <10955 11729 11969 11315 17033 16467 15409>,
+ <10501 12142 11899 11677 16717 15881 14424>,
+ <10146 11979 11895 11966 17491 17177 15372>,
+ <9743 11649 12007 11983 17478 18213 16253>,
+ <9402 11017 11938 11964 17190 17836 15973>,
+ <9188 10265 11845 11923 16780 17343 17751>,
+ <8988 9626 11312 12185 16916 17400 17911>,
+ <8988 9626 11312 12185 16916 17400 17911>,
+ <8988 9626 11312 12185 16916 17400 17911>;
+ };
+
+ qcom,pc-temp-y6-lut {
+ qcom,lut-col-legend = <(-20) (-10) 0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200 9000 8800>,
+ <8600 8400 8200 8000 7800 7600 7400>,
+ <7200 7000 6800 6600 6400 6200 6000>,
+ <5800 5600 5400 5200 5000 4800 4600>,
+ <4400 4200 4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200 2000 1800>,
+ <1600 1400 1200 1000 900 800 700>,
+ <600 500 400 300 200 100 0>;
+ qcom,lut-data = <7520 6106 5405 5178 5064 5046 5042>,
+ <7480 6097 5401 5177 5064 5046 5042>,
+ <7441 6092 5400 5176 5065 5046 5042>,
+ <7405 6089 5400 5176 5066 5047 5043>,
+ <7374 6088 5401 5176 5067 5047 5044>,
+ <7350 6088 5403 5176 5067 5047 5044>,
+ <7329 6090 5407 5176 5068 5047 5044>,
+ <7314 6094 5413 5177 5068 5047 5044>,
+ <7323 6102 5416 5178 5069 5047 5044>,
+ <7367 6117 5418 5182 5071 5048 5044>,
+ <7391 6129 5422 5187 5073 5049 5045>,
+ <7291 6135 5450 5195 5075 5050 5045>,
+ <7156 6139 5483 5204 5078 5051 5046>,
+ <7138 6123 5488 5211 5081 5052 5047>,
+ <7142 6059 5490 5218 5085 5054 5048>,
+ <7144 6027 5490 5226 5089 5056 5050>,
+ <7141 6030 5484 5243 5097 5060 5052>,
+ <7136 6034 5472 5259 5107 5066 5055>,
+ <7138 6032 5457 5253 5118 5075 5062>,
+ <7150 6027 5435 5225 5131 5088 5073>,
+ <7165 6025 5421 5201 5133 5093 5078>,
+ <7194 6031 5414 5182 5104 5074 5065>,
+ <7230 6042 5409 5169 5075 5052 5048>,
+ <7265 6061 5410 5167 5071 5048 5044>,
+ <7301 6095 5412 5166 5069 5047 5043>,
+ <7340 6132 5415 5166 5069 5046 5042>,
+ <7383 6169 5421 5168 5070 5047 5042>,
+ <7430 6210 5431 5171 5071 5047 5042>,
+ <7476 6254 5444 5175 5073 5048 5043>,
+ <7523 6302 5464 5178 5075 5049 5043>,
+ <7575 6354 5486 5181 5078 5050 5044>,
+ <7633 6411 5511 5183 5081 5052 5046>,
+ <7696 6471 5539 5185 5083 5055 5048>,
+ <7764 6535 5569 5192 5084 5056 5050>,
+ <7836 6602 5603 5205 5084 5056 5053>,
+ <7911 6677 5640 5216 5084 5056 5053>,
+ <7991 6759 5681 5225 5083 5053 5049>,
+ <8076 6849 5727 5234 5083 5051 5046>,
+ <8172 6948 5778 5244 5084 5052 5047>,
+ <8278 7058 5835 5257 5087 5055 5050>,
+ <8396 7178 5899 5273 5091 5057 5052>,
+ <8531 7313 5971 5295 5095 5058 5052>,
+ <8689 7461 6060 5322 5099 5058 5052>,
+ <8881 7625 6170 5355 5104 5056 5049>,
+ <9142 7801 6295 5404 5112 5059 5048>,
+ <9395 7952 6438 5465 5127 5066 5057>,
+ <9966 8083 6542 5513 5135 5074 5067>,
+ <10874 8233 6666 5574 5150 5086 5075>,
+ <12099 8404 6813 5646 5166 5090 5080>,
+ <13825 8597 6972 5725 5172 5084 5066>,
+ <16318 8831 7159 5819 5184 5085 5065>,
+ <20019 9655 7408 5955 5214 5093 5070>,
+ <25895 11356 7725 6149 5262 5110 5083>,
+ <35348 14260 8288 6430 5374 5157 5109>,
+ <35348 14260 8288 6430 5374 5157 5109>,
+ <35348 14260 8288 6430 5374 5157 5109>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
new file mode 100644
index 0000000..f418fa0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
@@ -0,0 +1,1050 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,mlp356477_2800mah {
+ /* mlp356477_2800mah_averaged_MasterSlave_Aug14th2017 */
+ qcom,max-voltage-uv = <4400000>;
+ qcom,fg-cc-cv-threshold-mv = <4390>;
+ qcom,fastchg-current-ma = <4200>;
+ qcom,batt-id-kohm = <82>;
+ qcom,battery-beta = <4250>;
+ qcom,battery-therm-kohm = <32>;
+ qcom,battery-type =
+ "mlp356477_2800mah_averaged_MasterSlave_Aug14th2017";
+ qcom,qg-batt-profile-ver = <100>;
+
+ qcom,jeita-fcc-ranges = <0 150 560000
+ 151 450 4200000
+ 451 550 2380000>;
+ qcom,jeita-fv-ranges = <0 150 4150000
+ 151 450 4400000
+ 451 550 4150000>;
+
+ qcom,fcc1-temp-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-data = <2715 2788 2861 2898 2908>;
+ };
+
+ qcom,fcc2-temp-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-data = <2846 2860 2868 2865 2865>;
+ };
+
+ qcom,pc-temp-v1-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <43494 43682 43812 43865 43879>,
+ <43243 43420 43582 43645 43659>,
+ <42984 43174 43350 43418 43434>,
+ <42737 42940 43115 43191 43208>,
+ <42506 42710 42878 42958 42978>,
+ <42287 42479 42641 42722 42746>,
+ <42087 42250 42407 42489 42514>,
+ <41903 42027 42175 42255 42281>,
+ <41709 41807 41948 42023 42050>,
+ <41489 41592 41723 41794 41822>,
+ <41265 41381 41502 41568 41596>,
+ <41069 41176 41286 41348 41374>,
+ <40898 40982 41074 41131 41155>,
+ <40720 40799 40871 40921 40942>,
+ <40501 40613 40673 40716 40735>,
+ <40269 40405 40482 40518 40534>,
+ <40088 40193 40295 40329 40343>,
+ <39955 40022 40116 40148 40162>,
+ <39834 39894 39952 39975 39988>,
+ <39708 39765 39807 39818 39827>,
+ <39583 39577 39645 39659 39667>,
+ <39447 39345 39420 39461 39475>,
+ <39277 39148 39173 39221 39239>,
+ <39089 38991 38991 39014 39028>,
+ <38930 38860 38851 38860 38868>,
+ <38794 38758 38733 38728 38732>,
+ <38683 38676 38628 38611 38612>,
+ <38607 38602 38535 38507 38505>,
+ <38548 38529 38451 38414 38409>,
+ <38501 38462 38373 38327 38317>,
+ <38466 38407 38305 38251 38234>,
+ <38437 38360 38245 38182 38160>,
+ <38405 38317 38193 38121 38092>,
+ <38366 38277 38147 38067 38030>,
+ <38329 38240 38109 38022 37980>,
+ <38300 38203 38069 37977 37929>,
+ <38273 38170 38027 37921 37863>,
+ <38236 38130 37980 37857 37784>,
+ <38162 38057 37912 37781 37700>,
+ <38062 37946 37816 37690 37609>,
+ <37947 37830 37704 37585 37507>,
+ <37801 37713 37578 37463 37382>,
+ <37644 37575 37436 37322 37239>,
+ <37473 37394 37270 37162 37082>,
+ <37320 37251 37129 37032 36959>,
+ <37220 37161 37058 36960 36899>,
+ <37185 37129 37033 36942 36880>,
+ <37156 37103 37014 36927 36865>,
+ <37120 37070 36988 36902 36835>,
+ <37014 36957 36885 36755 36652>,
+ <36682 36593 36544 36398 36287>,
+ <36204 36109 36077 35921 35807>,
+ <35597 35486 35476 35307 35181>,
+ <34771 34630 34656 34460 34321>,
+ <33460 33262 33379 33128 32955>,
+ <30000 30000 30000 30000 30000>;
+ };
+
+ qcom,pc-temp-v2-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <43850 43830 43805 43740 43725>,
+ <43429 43514 43528 43481 43467>,
+ <43064 43219 43264 43228 43216>,
+ <42767 42948 43012 42983 42973>,
+ <42526 42700 42773 42744 42738>,
+ <42296 42458 42537 42509 42505>,
+ <42059 42213 42303 42277 42272>,
+ <41822 41972 42071 42047 42041>,
+ <41571 41739 41843 41820 41814>,
+ <41298 41515 41619 41595 41589>,
+ <41069 41297 41398 41374 41368>,
+ <40928 41088 41181 41158 41151>,
+ <40808 40884 40967 40946 40938>,
+ <40611 40677 40763 40742 40734>,
+ <40284 40461 40566 40541 40534>,
+ <39989 40259 40373 40348 40342>,
+ <39811 40087 40180 40164 40158>,
+ <39662 39920 39993 39985 39980>,
+ <39459 39722 39821 39814 39808>,
+ <39184 39492 39664 39651 39646>,
+ <38924 39264 39483 39469 39466>,
+ <38721 39048 39237 39230 39230>,
+ <38546 38846 38982 38986 38989>,
+ <38403 38678 38799 38809 38813>,
+ <38282 38535 38654 38669 38674>,
+ <38189 38406 38530 38547 38552>,
+ <38125 38286 38419 38438 38442>,
+ <38075 38179 38320 38341 38343>,
+ <38030 38090 38229 38250 38250>,
+ <37992 38012 38144 38166 38164>,
+ <37955 37949 38066 38089 38086>,
+ <37916 37900 37993 38019 38016>,
+ <37875 37857 37925 37953 37949>,
+ <37832 37817 37860 37884 37875>,
+ <37788 37781 37801 37812 37795>,
+ <37738 37740 37738 37728 37702>,
+ <37680 37688 37671 37625 37587>,
+ <37613 37625 37600 37519 37469>,
+ <37537 37552 37525 37430 37374>,
+ <37448 37465 37446 37352 37292>,
+ <37349 37363 37353 37263 37200>,
+ <37237 37238 37238 37151 37088>,
+ <37114 37101 37106 37022 36960>,
+ <36989 36957 36952 36870 36813>,
+ <36875 36859 36862 36807 36758>,
+ <36792 36792 36828 36785 36734>,
+ <36754 36762 36812 36769 36719>,
+ <36707 36710 36780 36736 36687>,
+ <36633 36613 36721 36656 36581>,
+ <36472 36411 36517 36379 36276>,
+ <36149 36031 36113 35946 35829>,
+ <35650 35485 35584 35386 35258>,
+ <34964 34771 34884 34643 34500>,
+ <34007 33739 33902 33598 33440>,
+ <32474 32144 32393 32025 31829>,
+ <28051 26513 28737 27554 26991>;
+ };
+
+ qcom,pc-temp-z1-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <14442 13243 12339 11979 11839>,
+ <14411 13218 12328 11916 11773>,
+ <14392 13199 12294 11893 11756>,
+ <14384 13180 12273 11879 11745>,
+ <14381 13174 12263 11870 11738>,
+ <14377 13179 12257 11863 11735>,
+ <14369 13182 12253 11858 11731>,
+ <14358 13175 12250 11851 11726>,
+ <14349 13158 12248 11847 11722>,
+ <14339 13146 12242 11845 11720>,
+ <14332 13143 12237 11845 11719>,
+ <14327 13144 12237 11844 11718>,
+ <14321 13142 12238 11840 11716>,
+ <14313 13135 12234 11837 11715>,
+ <14306 13128 12222 11835 11714>,
+ <14299 13120 12215 11834 11714>,
+ <14293 13113 12211 11833 11713>,
+ <14293 13111 12203 11830 11714>,
+ <14297 13119 12202 11828 11715>,
+ <14304 13129 12210 11831 11716>,
+ <14312 13133 12215 11837 11718>,
+ <14318 13130 12221 11841 11721>,
+ <14319 13125 12230 11843 11726>,
+ <14320 13135 12234 11846 11730>,
+ <14324 13151 12236 11852 11734>,
+ <14340 13158 12238 11860 11737>,
+ <14358 13165 12247 11865 11741>,
+ <14373 13167 12258 11870 11747>,
+ <14389 13165 12260 11873 11752>,
+ <14394 13167 12258 11877 11757>,
+ <14373 13169 12256 11880 11760>,
+ <14334 13168 12253 11886 11764>,
+ <14321 13167 12247 11892 11768>,
+ <14348 13170 12248 11897 11772>,
+ <14378 13177 12260 11901 11778>,
+ <14371 13182 12271 11905 11783>,
+ <14343 13188 12277 11910 11788>,
+ <14331 13194 12283 11917 11792>,
+ <14346 13205 12290 11924 11797>,
+ <14369 13219 12300 11931 11803>,
+ <14389 13228 12307 11937 11809>,
+ <14412 13237 12311 11941 11815>,
+ <14410 13245 12315 11945 11820>,
+ <14367 13259 12315 11949 11823>,
+ <14429 13239 12311 11954 11824>,
+ <14440 13243 12333 11959 11830>,
+ <14452 13241 12320 11961 11828>,
+ <14443 13243 12329 11964 11831>,
+ <14484 13241 12332 11968 11836>,
+ <14448 13263 12343 11977 11845>,
+ <14473 13293 12346 11988 11856>,
+ <14501 13300 12357 12000 11864>,
+ <14521 13333 12374 12015 11879>,
+ <14603 13373 12420 12034 11897>,
+ <14603 13373 12420 12034 11897>,
+ <14603 13373 12420 12034 11897>;
+ };
+
+ qcom,pc-temp-z2-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <9070 11213 10264 10349 10299>,
+ <9403 10211 10276 10386 10313>,
+ <9826 10116 10342 10430 10322>,
+ <9983 10114 10362 10447 10350>,
+ <9978 10115 10368 10437 10342>,
+ <9967 10120 10372 10407 10282>,
+ <9846 10126 10371 10393 10237>,
+ <9534 10132 10368 10388 10217>,
+ <9372 10137 10365 10382 10206>,
+ <9574 10141 10365 10378 10208>,
+ <9873 10143 10365 10386 10216>,
+ <9940 10145 10357 10384 10231>,
+ <9908 10147 10346 10370 10273>,
+ <9890 10148 10344 10357 10310>,
+ <9864 10149 10352 10353 10325>,
+ <9749 10147 10354 10353 10336>,
+ <9714 10144 10347 10355 10342>,
+ <10069 10146 10343 10360 10343>,
+ <10530 10156 10344 10366 10344>,
+ <10637 10166 10343 10378 10353>,
+ <10631 10154 10344 10396 10363>,
+ <10605 10119 10374 10405 10360>,
+ <10392 10103 10415 10412 10326>,
+ <10061 10118 10414 10414 10294>,
+ <9958 10135 10371 10402 10266>,
+ <9962 10132 10339 10387 10241>,
+ <9968 10117 10335 10377 10241>,
+ <9971 10107 10337 10360 10253>,
+ <9975 10109 10342 10353 10270>,
+ <9977 10112 10351 10373 10299>,
+ <9855 10116 10359 10397 10330>,
+ <9628 10123 10362 10398 10343>,
+ <9563 10131 10365 10389 10349>,
+ <9597 10143 10370 10392 10366>,
+ <9647 10159 10379 10443 10415>,
+ <9714 10166 10389 10499 10464>,
+ <9809 10169 10397 10520 10486>,
+ <9844 10172 10405 10533 10511>,
+ <9756 10186 10432 10548 10531>,
+ <9631 10212 10505 10586 10563>,
+ <9541 10225 10551 10621 10587>,
+ <9445 10161 10538 10637 10540>,
+ <9378 10144 10527 10646 10468>,
+ <9335 10322 10539 10611 10495>,
+ <9269 13378 10554 10627 10491>,
+ <9218 14361 10475 10629 10534>,
+ <9220 14794 10469 10642 10588>,
+ <9212 15070 10496 10651 10586>,
+ <9188 13785 10469 10739 10647>,
+ <9170 13219 10622 10694 10458>,
+ <9151 12652 10655 10557 10325>,
+ <9135 12236 10610 10495 10272>,
+ <9116 11644 10496 10432 10195>,
+ <9081 11027 10456 10300 10139>,
+ <9081 11027 10456 10300 10139>,
+ <9081 11027 10456 10300 10139>;
+ };
+
+ qcom,pc-temp-z3-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <19308 19258 19367 19369 19345>,
+ <19567 19611 19463 19395 19370>,
+ <19850 19705 19512 19411 19381>,
+ <19967 19740 19520 19416 19385>,
+ <19987 19749 19515 19414 19384>,
+ <19996 19735 19507 19405 19379>,
+ <19860 19713 19499 19400 19373>,
+ <19484 19698 19492 19395 19369>,
+ <19288 19687 19483 19391 19365>,
+ <19508 19679 19473 19388 19362>,
+ <19829 19673 19467 19382 19358>,
+ <19858 19667 19463 19378 19357>,
+ <19678 19660 19461 19372 19355>,
+ <19567 19648 19461 19369 19353>,
+ <19627 19641 19463 19369 19353>,
+ <19647 19646 19465 19368 19355>,
+ <19577 19654 19458 19367 19355>,
+ <19415 19653 19447 19366 19345>,
+ <19268 19631 19443 19365 19337>,
+ <19258 19613 19443 19365 19337>,
+ <19258 19623 19446 19366 19337>,
+ <19260 19652 19459 19374 19342>,
+ <19450 19665 19480 19393 19364>,
+ <19797 19666 19494 19403 19379>,
+ <19919 19667 19506 19402 19379>,
+ <19924 19671 19511 19400 19378>,
+ <19927 19681 19507 19400 19374>,
+ <19922 19686 19499 19400 19366>,
+ <19906 19684 19492 19399 19360>,
+ <19891 19680 19487 19397 19359>,
+ <19798 19673 19482 19393 19359>,
+ <19639 19661 19476 19390 19359>,
+ <19579 19650 19470 19386 19357>,
+ <19566 19644 19465 19382 19355>,
+ <19553 19639 19460 19380 19352>,
+ <19468 19636 19457 19378 19348>,
+ <19316 19630 19455 19375 19346>,
+ <19261 19624 19453 19373 19344>,
+ <19262 19618 19450 19371 19343>,
+ <19263 19609 19443 19369 19345>,
+ <19264 19576 19437 19368 19347>,
+ <19267 19367 19435 19368 19350>,
+ <19269 19261 19432 19367 19353>,
+ <19270 19260 19421 19365 19353>,
+ <19274 19257 19413 19361 19354>,
+ <19278 19257 19368 19354 19332>,
+ <19278 19257 19367 19346 19332>,
+ <19279 19257 19362 19331 19323>,
+ <19282 19257 19368 19326 19327>,
+ <19284 19257 19354 19338 19350>,
+ <19287 19257 19361 19346 19348>,
+ <19290 19257 19368 19356 19347>,
+ <19295 19257 19370 19353 19356>,
+ <19308 19258 19392 19382 19373>,
+ <19308 19258 19392 19382 19373>,
+ <19308 19258 19392 19382 19373>;
+ };
+
+ qcom,pc-temp-z4-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <16598 15992 15337 14972 14879>,
+ <16815 16035 15386 15059 14940>,
+ <16561 15923 15277 14982 14899>,
+ <16283 15657 15173 14901 14842>,
+ <15982 15436 15082 14879 14824>,
+ <15774 15304 15020 14868 14816>,
+ <15800 15207 14988 14856 14809>,
+ <15941 15154 14966 14843 14802>,
+ <16005 15119 14946 14830 14794>,
+ <15750 15094 14928 14818 14785>,
+ <15383 15076 14912 14807 14775>,
+ <15316 15063 14897 14797 14766>,
+ <15433 15048 14884 14788 14757>,
+ <15501 15033 14874 14778 14748>,
+ <15463 15025 14866 14769 14737>,
+ <15509 15037 14860 14760 14725>,
+ <15596 15057 14852 14751 14718>,
+ <15677 15056 14841 14742 14715>,
+ <15729 15002 14835 14738 14712>,
+ <15708 14951 14832 14736 14709>,
+ <15684 15017 14833 14735 14707>,
+ <15650 15207 14923 14756 14718>,
+ <15508 15302 15068 14837 14777>,
+ <15298 15313 15091 14879 14814>,
+ <15235 15319 15040 14870 14809>,
+ <15282 15294 14989 14855 14800>,
+ <15330 15200 14962 14841 14793>,
+ <15320 15131 14943 14826 14787>,
+ <15270 15105 14932 14814 14780>,
+ <15232 15088 14926 14805 14771>,
+ <15276 15076 14923 14798 14761>,
+ <15373 15065 14921 14794 14754>,
+ <15397 15057 14920 14791 14747>,
+ <15385 15051 14919 14789 14742>,
+ <15376 15043 14916 14787 14738>,
+ <15430 15036 14914 14786 14736>,
+ <15544 15016 14915 14794 14745>,
+ <15577 14998 14918 14811 14767>,
+ <15550 14991 14917 14816 14774>,
+ <15520 14987 14909 14811 14768>,
+ <15502 14998 14896 14803 14760>,
+ <15491 15139 14873 14788 14752>,
+ <15475 15211 14850 14774 14747>,
+ <15445 15195 14842 14774 14750>,
+ <15345 15130 14813 14764 14741>,
+ <15318 15079 14802 14736 14721>,
+ <15308 15066 14777 14720 14700>,
+ <15301 15057 14757 14705 14684>,
+ <15285 15045 14739 14698 14668>,
+ <15274 15053 14775 14717 14688>,
+ <15294 15076 14782 14719 14696>,
+ <15311 15080 14778 14712 14701>,
+ <15326 15091 14779 14719 14701>,
+ <15354 15108 14768 14706 14703>,
+ <15354 15108 14768 14706 14703>,
+ <15354 15108 14768 14706 14703>;
+ };
+
+ qcom,pc-temp-z5-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <10764 10957 12110 13794 13670>,
+ <11745 12925 13686 14768 14977>,
+ <12716 14010 14746 16056 16445>,
+ <13404 14718 15521 16744 17361>,
+ <13905 15321 16180 16789 17415>,
+ <14200 15933 16545 16568 17166>,
+ <13611 16455 16653 16420 16940>,
+ <11869 16730 16710 16364 16838>,
+ <10978 16895 16699 16401 16763>,
+ <12521 17062 16594 16402 16659>,
+ <14783 17315 16549 16292 16587>,
+ <15011 17507 16656 16178 16634>,
+ <13819 17490 16845 16003 16770>,
+ <13134 17527 17100 15977 16943>,
+ <13823 17638 17538 16159 17435>,
+ <14192 17626 17786 16425 18421>,
+ <13660 17482 17557 16723 18632>,
+ <12194 17384 17254 17131 17493>,
+ <10877 17447 17366 17305 16542>,
+ <10795 17704 18253 17609 16717>,
+ <10808 18910 18937 18108 17070>,
+ <10857 21136 18167 17900 17037>,
+ <13850 21892 16713 16569 16529>,
+ <19285 19957 16394 15708 16174>,
+ <21102 17736 16736 15560 16006>,
+ <20977 17560 17186 15557 15832>,
+ <20752 18050 17734 15845 15773>,
+ <20272 18693 18431 16567 15708>,
+ <19413 19705 19132 17289 15707>,
+ <18772 20743 19927 17959 16182>,
+ <17386 21034 20581 18611 16967>,
+ <15327 21059 21018 19179 17620>,
+ <14631 21034 21355 19699 18403>,
+ <14512 21133 21507 20256 19056>,
+ <14338 21373 21604 21000 19675>,
+ <13329 21460 21643 21494 20064>,
+ <11563 21322 21601 20994 19488>,
+ <10927 21163 21493 19730 18023>,
+ <10937 21134 21270 19156 17559>,
+ <10948 21040 20651 18991 17865>,
+ <10942 20318 20135 18929 18319>,
+ <10912 14582 19994 18903 18925>,
+ <10885 11608 19761 18788 19444>,
+ <10861 11587 18126 17802 18686>,
+ <10871 11314 17978 17266 18426>,
+ <10857 11130 15054 17271 15914>,
+ <10841 11088 15214 17078 16658>,
+ <10831 11067 15184 16484 17071>,
+ <10841 11046 15966 16365 19357>,
+ <10877 11142 14378 15551 19427>,
+ <10922 11156 14140 15626 17699>,
+ <10887 11109 14272 16679 16814>,
+ <10848 11069 14202 15606 17668>,
+ <10790 10995 15166 19180 19716>,
+ <10790 10995 15166 19180 19716>,
+ <10790 10995 15166 19180 19716>;
+ };
+
+ qcom,pc-temp-z6-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <17029 15901 15123 14807 14723>,
+ <17156 16022 15165 14844 14750>,
+ <17081 15990 15131 14815 14734>,
+ <16977 15851 15090 14780 14711>,
+ <16818 15720 15039 14766 14700>,
+ <16645 15630 15000 14756 14693>,
+ <16486 15560 14978 14746 14686>,
+ <16331 15519 14962 14737 14680>,
+ <16255 15490 14945 14728 14674>,
+ <16244 15468 14929 14720 14668>,
+ <16238 15452 14916 14712 14662>,
+ <16217 15438 14905 14704 14656>,
+ <16172 15422 14897 14697 14651>,
+ <16139 15401 14891 14691 14646>,
+ <16146 15390 14887 14686 14642>,
+ <16166 15395 14883 14681 14638>,
+ <16163 15403 14876 14677 14633>,
+ <16120 15402 14866 14672 14627>,
+ <16076 15372 14862 14670 14622>,
+ <16063 15345 14862 14670 14621>,
+ <16057 15380 14865 14671 14621>,
+ <16055 15480 14915 14684 14629>,
+ <16092 15533 14991 14732 14668>,
+ <16168 15543 15006 14757 14692>,
+ <16218 15549 14992 14753 14691>,
+ <16251 15541 14976 14746 14687>,
+ <16270 15511 14964 14741 14683>,
+ <16266 15488 14954 14735 14676>,
+ <16246 15479 14948 14730 14671>,
+ <16227 15473 14944 14726 14666>,
+ <16204 15467 14942 14722 14662>,
+ <16176 15459 14941 14719 14659>,
+ <16164 15454 14939 14716 14657>,
+ <16159 15453 14938 14715 14654>,
+ <16154 15453 14937 14714 14651>,
+ <16145 15453 14936 14714 14649>,
+ <16133 15450 14939 14717 14652>,
+ <16130 15447 14943 14725 14662>,
+ <16133 15449 14943 14727 14665>,
+ <16139 15454 14940 14725 14664>,
+ <16147 15454 14936 14723 14663>,
+ <16161 15429 14929 14718 14662>,
+ <16171 15417 14922 14712 14662>,
+ <16178 15424 14918 14713 14664>,
+ <16162 15408 14909 14708 14663>,
+ <16165 15399 14883 14694 14643>,
+ <16168 15398 14875 14684 14633>,
+ <16172 15401 14867 14670 14622>,
+ <16179 15405 14866 14665 14618>,
+ <16193 15425 14881 14682 14641>,
+ <16228 15451 14892 14690 14645>,
+ <16262 15468 14902 14695 14650>,
+ <16300 15497 14912 14700 14657>,
+ <16361 15535 14930 14716 14672>,
+ <16361 15535 14930 14716 14672>,
+ <16361 15535 14930 14716 14672>;
+ };
+
+ qcom,pc-temp-y1-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <6704 6050 5566 5322 5235>,
+ <6701 6050 5560 5320 5234>,
+ <6693 6051 5554 5318 5233>,
+ <6684 6051 5548 5316 5231>,
+ <6676 6052 5543 5313 5230>,
+ <6673 6053 5540 5311 5228>,
+ <6667 6054 5539 5308 5225>,
+ <6659 6054 5538 5305 5222>,
+ <6663 6055 5536 5302 5222>,
+ <6693 6056 5529 5299 5222>,
+ <6717 6055 5525 5298 5222>,
+ <6716 6050 5525 5297 5220>,
+ <6707 6044 5526 5296 5217>,
+ <6706 6044 5528 5295 5216>,
+ <6707 6049 5533 5293 5217>,
+ <6709 6052 5536 5291 5217>,
+ <6705 6053 5534 5290 5219>,
+ <6699 6055 5532 5288 5221>,
+ <6700 6057 5532 5289 5221>,
+ <6708 6062 5531 5290 5220>,
+ <6715 6065 5531 5292 5219>,
+ <6712 6067 5532 5294 5220>,
+ <6706 6071 5532 5296 5222>,
+ <6703 6071 5532 5297 5223>,
+ <6702 6070 5532 5298 5225>,
+ <6702 6069 5532 5299 5227>,
+ <6700 6068 5535 5301 5229>,
+ <6699 6065 5539 5305 5232>,
+ <6697 6062 5540 5309 5236>,
+ <6691 6059 5541 5311 5240>,
+ <6688 6059 5542 5314 5243>,
+ <6691 6063 5546 5317 5244>,
+ <6695 6069 5550 5320 5245>,
+ <6693 6072 5553 5324 5247>,
+ <6677 6077 5556 5329 5251>,
+ <6667 6080 5558 5333 5255>,
+ <6679 6081 5563 5336 5258>,
+ <6697 6082 5568 5339 5261>,
+ <6698 6085 5570 5342 5263>,
+ <6684 6094 5571 5348 5265>,
+ <6675 6101 5572 5352 5268>,
+ <6691 6098 5581 5353 5271>,
+ <6707 6096 5586 5356 5275>,
+ <6704 6102 5586 5363 5282>,
+ <6708 6104 5593 5368 5284>,
+ <6725 6098 5595 5370 5288>,
+ <6724 6114 5601 5370 5288>,
+ <6743 6089 5598 5376 5290>,
+ <6750 6086 5605 5374 5291>,
+ <6722 6104 5609 5381 5293>,
+ <6738 6105 5608 5384 5292>,
+ <6759 6112 5624 5392 5304>,
+ <6766 6128 5643 5400 5313>,
+ <6779 6149 5656 5417 5324>,
+ <6779 6149 5656 5417 5324>,
+ <6779 6149 5656 5417 5324>;
+ };
+
+ qcom,pc-temp-y2-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <9643 10639 11070 11121 11086>,
+ <9643 10662 11056 11097 11058>,
+ <9643 10675 11037 11063 11029>,
+ <9871 10680 11014 11027 11001>,
+ <10190 10680 10988 10997 10976>,
+ <10325 10677 10959 10981 10958>,
+ <10308 10664 10918 10973 10946>,
+ <10282 10644 10881 10967 10935>,
+ <10301 10632 10875 10959 10916>,
+ <10440 10623 10874 10933 10882>,
+ <10534 10619 10874 10919 10864>,
+ <10489 10626 10869 10936 10874>,
+ <10417 10643 10861 10961 10888>,
+ <10413 10676 10856 10952 10885>,
+ <10487 10748 10851 10897 10860>,
+ <10553 10801 10851 10866 10840>,
+ <10580 10826 10885 10876 10837>,
+ <10597 10843 10946 10896 10845>,
+ <10521 10842 11018 10943 10884>,
+ <10042 10822 11115 11060 10984>,
+ <9696 10809 11159 11120 11034>,
+ <9682 10815 11129 11098 11013>,
+ <9676 10826 11091 11071 10987>,
+ <9675 10835 11085 11079 11009>,
+ <9676 10846 11088 11121 11106>,
+ <9676 10848 11093 11154 11162>,
+ <9675 10684 11108 11176 11166>,
+ <9671 10460 11130 11199 11168>,
+ <9668 10652 11152 11224 11182>,
+ <9665 11565 11178 11263 11214>,
+ <9662 12069 11192 11286 11238>,
+ <9660 11796 11199 11310 11257>,
+ <9659 11319 11203 11338 11277>,
+ <9657 10963 11202 11344 11307>,
+ <9656 10643 11198 11320 11345>,
+ <9655 10423 11189 11299 11359>,
+ <9654 10140 11148 11294 11325>,
+ <9653 9828 11108 11301 11280>,
+ <9653 9733 11099 11301 11271>,
+ <9653 9707 11096 11290 11278>,
+ <9652 9692 11071 11287 11277>,
+ <9652 9681 10975 11329 11250>,
+ <9652 9674 10939 11356 11231>,
+ <9652 9668 10913 11303 11229>,
+ <9652 9664 10802 11253 11135>,
+ <9652 9663 10839 11180 11044>,
+ <9652 9661 10832 11146 11009>,
+ <9652 9660 10811 11105 10962>,
+ <9651 9659 10794 11061 10965>,
+ <9651 9658 10764 11053 10938>,
+ <9651 9658 10711 10981 10871>,
+ <9651 9656 10651 10927 10829>,
+ <9651 9654 10594 10851 10747>,
+ <9651 9654 10531 10798 10659>,
+ <9651 9654 10531 10798 10659>,
+ <9651 9654 10531 10798 10659>;
+ };
+
+ qcom,pc-temp-y3-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <13677 13390 13309 13301 13279>,
+ <13657 13393 13309 13294 13279>,
+ <13634 13397 13311 13289 13279>,
+ <13612 13400 13312 13285 13279>,
+ <13596 13403 13314 13283 13279>,
+ <13590 13406 13315 13282 13279>,
+ <13594 13408 13315 13283 13280>,
+ <13600 13410 13316 13286 13281>,
+ <13594 13412 13317 13287 13282>,
+ <13548 13416 13319 13287 13282>,
+ <13515 13419 13321 13287 13282>,
+ <13513 13421 13322 13289 13282>,
+ <13512 13423 13323 13291 13282>,
+ <13509 13422 13325 13291 13283>,
+ <13504 13414 13328 13291 13284>,
+ <13497 13407 13329 13291 13286>,
+ <13488 13401 13328 13292 13287>,
+ <13478 13395 13325 13294 13288>,
+ <13470 13387 13324 13297 13290>,
+ <13465 13376 13323 13301 13294>,
+ <13458 13367 13322 13303 13296>,
+ <13428 13360 13315 13297 13290>,
+ <13384 13354 13306 13287 13281>,
+ <13362 13349 13303 13283 13278>,
+ <13349 13346 13300 13281 13277>,
+ <13344 13341 13299 13280 13277>,
+ <13351 13308 13299 13280 13277>,
+ <13369 13266 13299 13282 13277>,
+ <13391 13259 13298 13282 13277>,
+ <13422 13259 13299 13283 13277>,
+ <13460 13258 13299 13283 13277>,
+ <13503 13258 13298 13283 13277>,
+ <13554 13259 13298 13283 13277>,
+ <13615 13263 13298 13283 13277>,
+ <13690 13285 13299 13283 13278>,
+ <13777 13302 13299 13283 13278>,
+ <13879 13294 13296 13281 13277>,
+ <13996 13281 13292 13279 13276>,
+ <14118 13282 13292 13278 13276>,
+ <14247 13299 13292 13277 13277>,
+ <14377 13319 13293 13277 13277>,
+ <14502 13343 13295 13277 13278>,
+ <14624 13368 13298 13277 13278>,
+ <14737 13402 13303 13278 13276>,
+ <14824 13470 13304 13280 13278>,
+ <14801 13476 13307 13284 13280>,
+ <14824 13517 13316 13291 13286>,
+ <14890 13560 13319 13292 13287>,
+ <14961 13607 13332 13295 13287>,
+ <15021 13665 13332 13292 13286>,
+ <15095 13703 13333 13294 13286>,
+ <15215 13779 13341 13296 13289>,
+ <15382 13934 13350 13299 13292>,
+ <15571 14143 13370 13308 13299>,
+ <15571 14143 13370 13308 13299>,
+ <15571 14143 13370 13308 13299>;
+ };
+
+ qcom,pc-temp-y4-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <17305 16798 16610 16483 16470>,
+ <17330 16862 16610 16486 16469>,
+ <17378 16934 16609 16489 16468>,
+ <17433 17001 16608 16491 16467>,
+ <17479 17051 16607 16493 16467>,
+ <17501 17070 16605 16493 16466>,
+ <17503 17059 16603 16493 16466>,
+ <17504 17041 16601 16492 16466>,
+ <17511 17030 16599 16493 16466>,
+ <17550 17023 16599 16497 16470>,
+ <17608 17020 16599 16500 16473>,
+ <17742 17034 16602 16502 16478>,
+ <17902 17062 16609 16506 16483>,
+ <17876 17086 16619 16512 16487>,
+ <17547 17110 16638 16523 16492>,
+ <17320 17134 16659 16535 16498>,
+ <17344 17161 16679 16546 16506>,
+ <17386 17183 16703 16560 16518>,
+ <17370 17170 16748 16586 16537>,
+ <17216 17080 16821 16630 16569>,
+ <17060 16974 16852 16651 16585>,
+ <16974 16865 16754 16602 16552>,
+ <16908 16761 16616 16529 16502>,
+ <16873 16703 16567 16502 16483>,
+ <16851 16662 16543 16489 16473>,
+ <16841 16647 16535 16485 16469>,
+ <16841 16681 16535 16485 16470>,
+ <16842 16727 16535 16487 16471>,
+ <16843 16732 16537 16489 16474>,
+ <16844 16721 16543 16497 16482>,
+ <16845 16716 16549 16507 16491>,
+ <16852 16721 16554 16521 16504>,
+ <16866 16730 16558 16535 16515>,
+ <16879 16732 16556 16540 16518>,
+ <16893 16714 16549 16540 16515>,
+ <16906 16704 16540 16534 16509>,
+ <16920 16735 16528 16505 16492>,
+ <16932 16784 16517 16477 16475>,
+ <16941 16805 16517 16476 16471>,
+ <16947 16815 16522 16479 16469>,
+ <16950 16818 16530 16481 16469>,
+ <16948 16818 16544 16482 16469>,
+ <16945 16817 16555 16481 16467>,
+ <16947 16815 16561 16471 16454>,
+ <16965 16826 16575 16477 16456>,
+ <16961 16849 16584 16493 16477>,
+ <16977 16893 16606 16503 16495>,
+ <17046 16954 16635 16528 16531>,
+ <17107 17025 16679 16557 16532>,
+ <17131 17054 16682 16515 16493>,
+ <17101 17038 16673 16527 16498>,
+ <17060 17051 16712 16548 16514>,
+ <17063 17124 16764 16588 16554>,
+ <17135 17255 16879 16727 16719>,
+ <17135 17255 16879 16727 16719>,
+ <17135 17255 16879 16727 16719>;
+ };
+
+ qcom,pc-temp-y5-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <8712 14223 14774 19943 16308>,
+ <8888 14349 15148 18667 15835>,
+ <10562 14526 15365 17009 15457>,
+ <12204 14713 15466 15353 15176>,
+ <13579 14867 15489 14086 14997>,
+ <14452 14948 15472 13593 14920>,
+ <14948 14973 15335 13800 15003>,
+ <15270 14986 15151 14123 15144>,
+ <15028 14986 15098 14118 15087>,
+ <13370 14973 15056 13826 14716>,
+ <12246 14930 14978 13664 14315>,
+ <13353 14287 14743 13788 13961>,
+ <15163 13386 14398 13870 13634>,
+ <15495 13334 14078 13643 13482>,
+ <15197 13853 13740 13191 13549>,
+ <14993 14346 13497 12887 13638>,
+ <15443 14672 13207 12793 13546>,
+ <16191 15006 12923 12796 13423>,
+ <16138 15417 13004 13001 13548>,
+ <14708 15994 13654 13569 14033>,
+ <13284 16419 14434 14193 14440>,
+ <12599 16670 15491 14905 14826>,
+ <12068 16832 16433 15541 15160>,
+ <11544 16827 16644 15650 15196>,
+ <10962 16682 16591 15306 15346>,
+ <10655 16390 16622 15048 15474>,
+ <10648 14341 16597 15293 15494>,
+ <10645 11728 16540 15744 15472>,
+ <10665 11156 16578 15865 15229>,
+ <10802 11032 16695 15749 14663>,
+ <10946 10959 16756 15607 14352>,
+ <11017 10901 16831 15502 14290>,
+ <11076 10865 16959 15400 14311>,
+ <11177 11054 17363 15705 14665>,
+ <11367 12196 18097 16573 15740>,
+ <11520 13011 18310 16919 16424>,
+ <11584 12496 17269 16620 16376>,
+ <11627 11602 15914 16091 16215>,
+ <11630 11486 15213 15448 16327>,
+ <11595 11681 14700 14692 16543>,
+ <11570 11787 14330 14365 16550>,
+ <11579 11797 14105 14243 16259>,
+ <11596 11778 14219 14209 16346>,
+ <11509 11647 14611 15058 16893>,
+ <11331 11779 14198 14882 16382>,
+ <11388 11508 14156 14882 14589>,
+ <11488 11654 14603 16012 16142>,
+ <11493 11662 14001 15040 14960>,
+ <11613 11939 14918 15399 14828>,
+ <11747 12726 15439 17093 17327>,
+ <11665 12655 15981 17753 17255>,
+ <11585 12280 16288 17881 17960>,
+ <11459 12118 16141 17436 18200>,
+ <11818 11932 16090 17352 17825>,
+ <11818 11932 16090 17352 17825>,
+ <11818 11932 16090 17352 17825>;
+ };
+
+ qcom,pc-temp-y6-lut {
+ qcom,lut-col-legend = <0 10 25 40 50>;
+ qcom,lut-row-legend = <10000 9800 9600 9400 9200>,
+ <9000 8800 8600 8400 8200>,
+ <8000 7800 7600 7400 7200>,
+ <7000 6800 6600 6400 6200>,
+ <6000 5800 5600 5400 5200>,
+ <5000 4800 4600 4400 4200>,
+ <4000 3800 3600 3400 3200>,
+ <3000 2800 2600 2400 2200>,
+ <2000 1800 1600 1400 1200>,
+ <1000 900 800 700 600>,
+ <500 400 300 200 100>,
+ <0>;
+ qcom,lut-data = <6132 5538 5158 5055 5027>,
+ <6176 5550 5158 5053 5026>,
+ <6205 5557 5158 5050 5025>,
+ <6223 5561 5157 5048 5025>,
+ <6232 5562 5156 5046 5025>,
+ <6234 5562 5154 5046 5025>,
+ <6227 5555 5152 5046 5025>,
+ <6211 5540 5150 5047 5026>,
+ <6191 5531 5149 5047 5027>,
+ <6161 5522 5149 5048 5028>,
+ <6144 5519 5149 5049 5029>,
+ <6170 5520 5150 5051 5030>,
+ <6214 5522 5151 5053 5031>,
+ <6207 5523 5154 5055 5033>,
+ <6119 5523 5160 5058 5035>,
+ <6058 5522 5167 5061 5038>,
+ <6060 5524 5172 5065 5041>,
+ <6065 5527 5177 5070 5045>,
+ <6054 5521 5188 5079 5052>,
+ <5986 5487 5208 5095 5064>,
+ <5923 5449 5216 5102 5070>,
+ <5892 5413 5184 5083 5056>,
+ <5871 5381 5138 5056 5035>,
+ <5868 5365 5122 5045 5028>,
+ <5872 5355 5114 5040 5024>,
+ <5878 5349 5111 5038 5023>,
+ <5895 5343 5112 5039 5023>,
+ <5926 5339 5113 5040 5024>,
+ <5960 5339 5115 5042 5026>,
+ <5999 5342 5118 5045 5028>,
+ <6044 5346 5121 5048 5031>,
+ <6096 5354 5124 5053 5034>,
+ <6156 5367 5126 5057 5038>,
+ <6222 5383 5127 5059 5039>,
+ <6297 5405 5127 5060 5039>,
+ <6380 5430 5127 5058 5038>,
+ <6470 5458 5124 5049 5032>,
+ <6568 5491 5120 5041 5027>,
+ <6667 5530 5121 5040 5027>,
+ <6768 5579 5127 5041 5027>,
+ <6867 5632 5133 5042 5028>,
+ <6963 5689 5144 5044 5028>,
+ <7053 5751 5155 5044 5028>,
+ <7135 5817 5167 5043 5024>,
+ <7206 5905 5181 5046 5026>,
+ <7192 5921 5188 5056 5034>,
+ <7214 5976 5205 5063 5044>,
+ <7280 6034 5219 5072 5055>,
+ <7350 6096 5248 5083 5055>,
+ <7405 6155 5252 5069 5043>,
+ <7454 6196 5257 5074 5046>,
+ <7535 6282 5285 5084 5053>,
+ <7666 6442 5320 5100 5069>,
+ <7846 6646 5385 5148 5123>,
+ <7846 6646 5385 5148 5123>,
+ <7846 6646 5385 5148 5123>;
+ };
+
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-mtp.dts b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
index 040b4ba..5744390 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
@@ -24,3 +24,21 @@
qcom,board-id = <8 0>;
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 413612d..c79f7a7 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -26,3 +26,22 @@
qcom,ps-hold-hard-reset-disable;
qcom,ps-hold-shutdown-disable;
};
+
+&usb3 {
+ extcon = <&pmi632_charger>, <&pmi632_charger>, <0>,
+ <&pmi632_charger>, <&pmi632_charger>;
+ vbus_dwc3-supply = <&smb5_vbus>;
+};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "qg-batterydata-ascent-3450mah.dtsi"
+ #include "qg-batterydata-mlp356477-2800mah.dtsi"
+ };
+};
+
+&pmi632_qg {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,rbat-conn-mohm = <20>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi8937.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi8937.dts
index 700e950..eb6a692 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi8937.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi8937.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "sdm450.dtsi"
+#include "pmi8937.dtsi"
+#include "msm8953-pmi8937.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI8937 SOC";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi8940.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi8940.dts
index f50d177..cfdd4e9 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi8940.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi8940.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "sdm450.dtsi"
+#include "pmi8940.dtsi"
+#include "msm8953-pmi8940.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI8940 SOC";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
index 5226e74..9e2981a 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
@@ -12,6 +12,7 @@
*/
#include "msm8953-qrd.dtsi"
+#include "msm8953-mdss-panels.dtsi"
&qusb_phy {
qcom,qusb-phy-init-seq = <0x78 0x80
@@ -31,3 +32,78 @@
status = "disabled";
};
+&tlmm {
+ pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio61";
+ };
+ config {
+ pins = "gpio61";
+ };
+ };
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio61";
+ };
+ config {
+ pins = "gpio61";
+ };
+ };
+ };
+};
+
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <10>;
+ };
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ /delete-property/ vdd-supply;
+
+ qcom,dsi-pref-prim-pan = <&dsi_hx8399c_truly_vid>;
+ qcom,platform-bklight-en-gpio = <&pm8953_gpios 4 0>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+};
+
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450.dts b/arch/arm64/boot/dts/qcom/sdm450.dts
index b829b81..6cdf897 100644
--- a/arch/arm64/boot/dts/qcom/sdm450.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450.dts
@@ -14,6 +14,8 @@
/dts-v1/;
#include "sdm450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 SOC";
diff --git a/arch/arm64/boot/dts/qcom/sdm632.dtsi b/arch/arm64/boot/dts/qcom/sdm632.dtsi
index 5100f28..80e6749 100644
--- a/arch/arm64/boot/dts/qcom/sdm632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi
@@ -21,6 +21,10 @@
qcom,msm-name = "SDM632";
};
+&clock_gcc_mdss {
+ compatible = "qcom,gcc-mdss-sdm632";
+};
+
&clock_gcc {
compatible = "qcom,gcc-sdm632";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index faaf644..73c7be2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
@@ -19,10 +19,10 @@
};
&soc {
- audio_load_mod {
- compatible = "qcom,audio-load-mod";
- audio_test_mod {
- compatible = "qcom,audio-test-mod";
+ qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ msm_audio_apr_dummy {
+ compatible = "qcom,msm-audio-apr-dummy";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
index 5bf8df7..c54b8db 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -186,7 +186,8 @@
reg-names = "phys_addr_base", "offset_addr";
};
- qcom,rpmh-master-stats {
- compatible = "qcom,rpmh-master-stats";
+ qcom,rpmh-master-stats@b221200 {
+ compatible = "qcom,rpmh-master-stats-v1";
+ reg = <0xb221200 0x60>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index a3f7b8e..48deca6 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -489,6 +489,29 @@
ibb-supply = <&lcdb_ncp_vreg>;
};
+ ext_dsi_bridge_display: qcom,dsi-display@17 {
+ compatible = "qcom,dsi-display";
+ label = "ext_dsi_bridge_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0>;
+ qcom,dsi-phy = <&mdss_dsi_phy0>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ ext_dsi_out: endpoint {
+ };
+ };
+ };
+ };
+
sde_wb: qcom,wb-display@0 {
compatible = "qcom,wb-display";
cell-index = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
index 7c4e682..3022998 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
@@ -224,6 +224,7 @@
qcom,sde-dspp-pcc = <0x1700 0x00040000>;
qcom,sde-dspp-gc = <0x17c0 0x00010008>;
qcom,sde-dspp-hist = <0x800 0x00010007>;
+ qcom,sde-dspp-dither = <0x82c 0x00010007>;
};
qcom,platform-supply-entries {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
index f6fa948..9903d19 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -138,7 +138,8 @@
0x254 /* QUSB2PHY_TEST1 */
0x198 /* PLL_BIAS_CONTROL_2 */
0x228 /* QUSB2PHY_SQ_CTRL1 */
- 0x22c>; /* QUSB2PHY_SQ_CTRL2 */
+ 0x22c /* QUSB2PHY_SQ_CTRL2 */
+ 0x27c>; /* QUSB2PHY_DEBUG_CTRL1 */
qcom,qusb-phy-init-seq =
/* <value reg_offset> */
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index ee10cfc..929239a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -140,7 +140,8 @@
reg-names = "phys_addr_base", "offset_addr";
};
- qcom,rpmh-master-stats {
- compatible = "qcom,rpmh-master-stats";
+ qcom,rpmh-master-stats@b221200 {
+ compatible = "qcom,rpmh-master-stats-v1";
+ reg = <0xb221200 0x60>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 4194e67..bfcebf6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -233,6 +233,7 @@
qcom,sde-dspp-pcc = <0x1700 0x00040000>;
qcom,sde-dspp-gc = <0x17c0 0x00010008>;
qcom,sde-dspp-hist = <0x800 0x00010007>;
+ qcom,sde-dspp-dither = <0x82c 0x00010007>;
};
qcom,platform-supply-entries {
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index a682817..3752642 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -324,8 +324,10 @@
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
+CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
CONFIG_QPNP_TYPEC=y
+CONFIG_QPNP_QG=y
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
@@ -358,6 +360,34 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_CAMERA=y
+CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
@@ -435,6 +465,7 @@
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
@@ -448,10 +479,9 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -510,12 +540,16 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 89397d1..63d1671 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -334,8 +334,10 @@
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
+CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
CONFIG_QPNP_TYPEC=y
+CONFIG_QPNP_QG=y
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
@@ -368,6 +370,34 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_CAMERA=y
+CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
@@ -446,6 +476,7 @@
CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
@@ -460,10 +491,9 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -538,6 +568,7 @@
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 8aa1e7d..83b0d66 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -440,7 +440,6 @@
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_PARANOID_SD_INIT=y
-CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_TEST=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 667377f..0e8ef8f 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -384,6 +384,7 @@
CONFIG_DRM=y
CONFIG_DRM_SDE_EVTLOG_DEBUG=y
CONFIG_DRM_SDE_RSC=y
+CONFIG_DRM_LT_LT9611=y
CONFIG_FB_VIRTUAL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
@@ -446,7 +447,6 @@
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
-CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_TEST=y
@@ -524,6 +524,7 @@
CONFIG_MSM_GLADIATOR_ERP=y
CONFIG_QCOM_EUD=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_WDOG_IPI_ENABLE=y
CONFIG_QPNP_PBS=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_QCOM_MINIDUMP=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index ff3bb70..c154576 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -224,6 +224,7 @@
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_SOCKEV_NLMCAST=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 01687f5..0b0d26d 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -229,6 +229,7 @@
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_SOCKEV_NLMCAST=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
@@ -524,6 +525,7 @@
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_EUD=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_WDOG_IPI_ENABLE=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_BUS_CONFIG_RPMH=y
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index e5ca5ab..a6cabf7 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -2391,8 +2391,8 @@ static int fastrpc_internal_munmap_fd(struct fastrpc_file *fl,
if (err)
goto bail;
mutex_lock(&fl->fl_map_mutex);
- if (!fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map)) {
- pr_err("mapping not found to unamp %x va %llx %x\n",
+ if (fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map)) {
+ pr_err("adsprpc: mapping not found to unmap %d va %llx %x\n",
ud->fd, (unsigned long long)ud->va,
(unsigned int)ud->len);
err = -1;
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 23cf293..a469eb9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -1163,18 +1163,31 @@ void extract_dci_events(unsigned char *buf, int len, int data_source,
struct list_head *start, *temp;
struct diag_dci_client_tbl *entry = NULL;
- length = *(uint16_t *)(buf + 1); /* total length of event series */
- if (length == 0) {
- pr_err("diag: Incoming dci event length is invalid\n");
+ if (!buf) {
+ pr_err("diag: In %s buffer is NULL\n", __func__);
return;
}
/*
- * Move directly to the start of the event series. 1 byte for
- * event code and 2 bytes for the length field.
+ * 1 byte for event code and 2 bytes for the length field.
* The length field indicates the total length removing the cmd_code
* and the length field. The event parsing in that case should happen
* till the end.
*/
+ if (len < 3) {
+ pr_err("diag: In %s invalid len: %d\n", __func__, len);
+ return;
+ }
+ length = *(uint16_t *)(buf + 1); /* total length of event series */
+ if ((length == 0) || (len != (length + 3))) {
+ pr_err("diag: Incoming dci event length: %d is invalid\n",
+ length);
+ return;
+ }
+ /*
+ * Move directly to the start of the event series.
+ * The event parsing should happen from start of event
+ * series till the end.
+ */
temp_len = 3;
while (temp_len < length) {
event_id_packet = *(uint16_t *)(buf + temp_len);
@@ -1191,30 +1204,60 @@ void extract_dci_events(unsigned char *buf, int len, int data_source,
* necessary.
*/
timestamp_len = 8;
- memcpy(timestamp, buf + temp_len + 2, timestamp_len);
+ if ((temp_len + timestamp_len + 2) <= len)
+ memcpy(timestamp, buf + temp_len + 2,
+ timestamp_len);
+ else {
+ pr_err("diag: Invalid length in %s, len: %d, temp_len: %d",
+ __func__, len, temp_len);
+ return;
+ }
}
/* 13th and 14th bit represent the payload length */
if (((event_id_packet & 0x6000) >> 13) == 3) {
payload_len_field = 1;
- payload_len = *(uint8_t *)
+ if ((temp_len + timestamp_len + 3) <= len) {
+ payload_len = *(uint8_t *)
(buf + temp_len + 2 + timestamp_len);
- if (payload_len < (MAX_EVENT_SIZE - 13)) {
- /* copy the payload length and the payload */
+ } else {
+ pr_err("diag: Invalid length in %s, len: %d, temp_len: %d",
+ __func__, len, temp_len);
+ return;
+ }
+ if ((payload_len < (MAX_EVENT_SIZE - 13)) &&
+ ((temp_len + timestamp_len + payload_len + 3) <= len)) {
+ /*
+ * Copy the payload length and the payload
+ * after skipping temp_len bytes for already
+ * parsed packet, timestamp_len for timestamp
+ * buffer, 2 bytes for event_id_packet.
+ */
memcpy(event_data + 12, buf + temp_len + 2 +
timestamp_len, 1);
memcpy(event_data + 13, buf + temp_len + 2 +
timestamp_len + 1, payload_len);
} else {
- pr_err("diag: event > %d, payload_len = %d\n",
- (MAX_EVENT_SIZE - 13), payload_len);
+ pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n",
+ (MAX_EVENT_SIZE - 13), payload_len, temp_len);
return;
}
} else {
payload_len_field = 0;
payload_len = (event_id_packet & 0x6000) >> 13;
- /* copy the payload */
- memcpy(event_data + 12, buf + temp_len + 2 +
+ /*
+ * Copy the payload after skipping temp_len bytes
+ * for already parsed packet, timestamp_len for
+ * timestamp buffer, 2 bytes for event_id_packet.
+ */
+ if ((payload_len < (MAX_EVENT_SIZE - 12)) &&
+ ((temp_len + timestamp_len + payload_len + 2) <= len))
+ memcpy(event_data + 12, buf + temp_len + 2 +
timestamp_len, payload_len);
+ else {
+ pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n",
+ (MAX_EVENT_SIZE - 12), payload_len, temp_len);
+ return;
+ }
}
/* Before copying the data to userspace, check if we are still
@@ -1340,19 +1383,19 @@ void extract_dci_log(unsigned char *buf, int len, int data_source, int token,
pr_err("diag: In %s buffer is NULL\n", __func__);
return;
}
-
- /* The first six bytes for the incoming log packet contains
- * Command code (2), the length of the packet (2) and the length
- * of the log (2)
+ /*
+ * The first eight bytes for the incoming log packet contains
+ * Command code (2), the length of the packet (2), the length
+ * of the log (2) and log code (2)
*/
- log_code = *(uint16_t *)(buf + 6);
- read_bytes += sizeof(uint16_t) + 6;
- if (read_bytes > len) {
- pr_err("diag: Invalid length in %s, len: %d, read: %d",
- __func__, len, read_bytes);
+ if (len < 8) {
+ pr_err("diag: In %s invalid len: %d\n", __func__, len);
return;
}
+ log_code = *(uint16_t *)(buf + 6);
+ read_bytes += sizeof(uint16_t) + 6;
+
/* parse through log mask table of each client and check mask */
mutex_lock(&driver->dci_mutex);
list_for_each_safe(start, temp, &driver->dci_client_list) {
@@ -1379,6 +1422,10 @@ void extract_dci_ext_pkt(unsigned char *buf, int len, int data_source,
pr_err("diag: In %s buffer is NULL\n", __func__);
return;
}
+ if (len < (EXT_HDR_LEN + sizeof(uint8_t))) {
+ pr_err("diag: In %s invalid len: %d\n", __func__, len);
+ return;
+ }
version = *(uint8_t *)buf + 1;
if (version < EXT_HDR_VERSION) {
@@ -1390,10 +1437,6 @@ void extract_dci_ext_pkt(unsigned char *buf, int len, int data_source,
pkt = buf + EXT_HDR_LEN;
pkt_cmd_code = *(uint8_t *)pkt;
len -= EXT_HDR_LEN;
- if (len < 0) {
- pr_err("diag: %s, Invalid length len: %d\n", __func__, len);
- return;
- }
switch (pkt_cmd_code) {
case LOG_CMD_CODE:
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index d264e79..74c8055 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -16,6 +16,9 @@
obj-$(CONFIG_ARCH_MSM8953) += clock-gcc-8953.o
obj-$(CONFIG_ARCH_MSM8953) += clock-cpu-8953.o
obj-$(CONFIG_ARCH_MSM8953) += clock-rcgwr.o
+obj-$(CONFIG_ARCH_MSM8937) += clock-gcc-8952.o
+obj-$(CONFIG_ARCH_MSM8937) += clock-cpu-8939.o
+obj-$(CONFIG_ARCH_MSM8937) += clock-rcgwr.o
endif
obj-y += mdss/
diff --git a/drivers/clk/msm/clock-cpu-8939.c b/drivers/clk/msm/clock-cpu-8939.c
new file mode 100644
index 0000000..68ea9a1
--- /dev/null
+++ b/drivers/clk/msm/clock-cpu-8939.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (c) 2014-2016, 2018, 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) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/suspend.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <soc/qcom/clock-local2.h>
+#include <soc/qcom/pm.h>
+
+#include "clock.h"
+#include <dt-bindings/clock/msm-cpu-clocks-8939.h>
+
+DEFINE_VDD_REGS_INIT(vdd_cpu_bc, 1);
+DEFINE_VDD_REGS_INIT(vdd_cpu_lc, 1);
+DEFINE_VDD_REGS_INIT(vdd_cpu_cci, 1);
+
+enum {
+ A53SS_MUX_BC,
+ A53SS_MUX_LC,
+ A53SS_MUX_CCI,
+ A53SS_MUX_NUM,
+};
+
+static const char * const mux_names[] = { "c1", "c0", "cci"};
+
+struct cpu_clk_8939 {
+ u32 cpu_reg_mask;
+ cpumask_t cpumask;
+ bool hw_low_power_ctrl;
+ struct pm_qos_request req;
+ struct clk c;
+ struct latency_level latency_lvl;
+ s32 cpu_latency_no_l2_pc_us;
+};
+
+static struct mux_div_clk a53ssmux_bc = {
+ .ops = &rcg_mux_div_ops,
+ .safe_freq = 400000000,
+ .data = {
+ .max_div = 32,
+ .min_div = 2,
+ .is_half_divider = true,
+ },
+ .c = {
+ .dbg_name = "a53ssmux_bc",
+ .ops = &clk_ops_mux_div_clk,
+ CLK_INIT(a53ssmux_bc.c),
+ },
+ .parents = (struct clk_src[8]) {},
+ .div_mask = BM(4, 0),
+ .src_mask = BM(10, 8) >> 8,
+ .src_shift = 8,
+};
+
+static struct mux_div_clk a53ssmux_lc = {
+ .ops = &rcg_mux_div_ops,
+ .safe_freq = 200000000,
+ .data = {
+ .max_div = 32,
+ .min_div = 2,
+ .is_half_divider = true,
+ },
+ .c = {
+ .dbg_name = "a53ssmux_lc",
+ .ops = &clk_ops_mux_div_clk,
+ CLK_INIT(a53ssmux_lc.c),
+ },
+ .parents = (struct clk_src[8]) {},
+ .div_mask = BM(4, 0),
+ .src_mask = BM(10, 8) >> 8,
+ .src_shift = 8,
+};
+
+static struct mux_div_clk a53ssmux_cci = {
+ .ops = &rcg_mux_div_ops,
+ .safe_freq = 200000000,
+ .data = {
+ .max_div = 32,
+ .min_div = 2,
+ .is_half_divider = true,
+ },
+ .c = {
+ .dbg_name = "a53ssmux_cci",
+ .ops = &clk_ops_mux_div_clk,
+ CLK_INIT(a53ssmux_cci.c),
+ },
+ .parents = (struct clk_src[8]) {},
+ .div_mask = BM(4, 0),
+ .src_mask = BM(10, 8) >> 8,
+ .src_shift = 8,
+};
+
+static void do_nothing(void *unused) { }
+
+static inline struct cpu_clk_8939 *to_cpu_clk_8939(struct clk *c)
+{
+ return container_of(c, struct cpu_clk_8939, c);
+}
+
+static enum handoff cpu_clk_8939_handoff(struct clk *c)
+{
+ c->rate = clk_get_rate(c->parent);
+ return HANDOFF_DISABLED_CLK;
+}
+
+static long cpu_clk_8939_round_rate(struct clk *c, unsigned long rate)
+{
+ return clk_round_rate(c->parent, rate);
+}
+
+static int cpu_clk_8939_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret = 0;
+ struct cpu_clk_8939 *cpuclk = to_cpu_clk_8939(c);
+ bool hw_low_power_ctrl = cpuclk->hw_low_power_ctrl;
+
+ if (hw_low_power_ctrl) {
+ memset(&cpuclk->req, 0, sizeof(cpuclk->req));
+ cpumask_copy(&cpuclk->req.cpus_affine,
+ (const struct cpumask *)&cpuclk->cpumask);
+ cpuclk->req.type = PM_QOS_REQ_AFFINE_CORES;
+ pm_qos_add_request(&cpuclk->req, PM_QOS_CPU_DMA_LATENCY,
+ cpuclk->cpu_latency_no_l2_pc_us - 1);
+ smp_call_function_any(&cpuclk->cpumask, do_nothing,
+ NULL, 1);
+ }
+
+ ret = clk_set_rate(c->parent, rate);
+
+ if (hw_low_power_ctrl)
+ pm_qos_remove_request(&cpuclk->req);
+
+ return ret;
+}
+
+static struct clk_ops clk_ops_cpu = {
+ .set_rate = cpu_clk_8939_set_rate,
+ .round_rate = cpu_clk_8939_round_rate,
+ .handoff = cpu_clk_8939_handoff,
+};
+
+static struct cpu_clk_8939 a53_bc_clk = {
+ .cpu_reg_mask = 0x3,
+ .latency_lvl = {
+ .affinity_level = LPM_AFF_LVL_L2,
+ .reset_level = LPM_RESET_LVL_GDHS,
+ .level_name = "perf",
+ },
+ .cpu_latency_no_l2_pc_us = 300,
+ .c = {
+ .parent = &a53ssmux_bc.c,
+ .ops = &clk_ops_cpu,
+ .vdd_class = &vdd_cpu_bc,
+ .dbg_name = "a53_bc_clk",
+ CLK_INIT(a53_bc_clk.c),
+ },
+};
+
+static struct cpu_clk_8939 a53_lc_clk = {
+ .cpu_reg_mask = 0x103,
+ .latency_lvl = {
+ .affinity_level = LPM_AFF_LVL_L2,
+ .reset_level = LPM_RESET_LVL_GDHS,
+ .level_name = "pwr",
+ },
+ .cpu_latency_no_l2_pc_us = 300,
+ .c = {
+ .parent = &a53ssmux_lc.c,
+ .ops = &clk_ops_cpu,
+ .vdd_class = &vdd_cpu_lc,
+ .dbg_name = "a53_lc_clk",
+ CLK_INIT(a53_lc_clk.c),
+ },
+};
+
+static struct cpu_clk_8939 cci_clk = {
+ .c = {
+ .parent = &a53ssmux_cci.c,
+ .ops = &clk_ops_cpu,
+ .vdd_class = &vdd_cpu_cci,
+ .dbg_name = "cci_clk",
+ CLK_INIT(cci_clk.c),
+ },
+};
+
+static struct clk_lookup cpu_clocks_8939[] = {
+ CLK_LIST(a53ssmux_lc),
+ CLK_LIST(a53ssmux_bc),
+ CLK_LIST(a53ssmux_cci),
+ CLK_LIST(a53_bc_clk),
+ CLK_LIST(a53_lc_clk),
+ CLK_LIST(cci_clk),
+};
+
+static struct clk_lookup cpu_clocks_8939_single_cluster[] = {
+ CLK_LIST(a53ssmux_bc),
+ CLK_LIST(a53_bc_clk),
+};
+
+
+static struct mux_div_clk *a53ssmux[] = {&a53ssmux_bc,
+ &a53ssmux_lc, &a53ssmux_cci};
+
+static struct cpu_clk_8939 *cpuclk[] = { &a53_bc_clk, &a53_lc_clk, &cci_clk};
+
+static struct clk *logical_cpu_to_clk(int cpu)
+{
+ struct device_node *cpu_node = of_get_cpu_node(cpu, NULL);
+ u32 reg;
+
+ /* CPU 0/1/2/3 --> a53_bc_clk and mask = 0x103
+ * CPU 4/5/6/7 --> a53_lc_clk and mask = 0x3
+ */
+ if (cpu_node && !of_property_read_u32(cpu_node, "reg", ®)) {
+ if ((reg | a53_bc_clk.cpu_reg_mask) == a53_bc_clk.cpu_reg_mask)
+ return &a53_lc_clk.c;
+ if ((reg | a53_lc_clk.cpu_reg_mask) == a53_lc_clk.cpu_reg_mask)
+ return &a53_bc_clk.c;
+ }
+
+ return NULL;
+}
+
+static int of_get_fmax_vdd_class(struct platform_device *pdev, struct clk *c,
+ char *prop_name)
+{
+ struct device_node *of = pdev->dev.of_node;
+ int prop_len, i;
+ struct clk_vdd_class *vdd = c->vdd_class;
+ u32 *array;
+
+ if (!of_find_property(of, prop_name, &prop_len)) {
+ dev_err(&pdev->dev, "missing %s\n", prop_name);
+ return -EINVAL;
+ }
+
+ prop_len /= sizeof(u32);
+ if (prop_len % 2) {
+ dev_err(&pdev->dev, "bad length %d\n", prop_len);
+ return -EINVAL;
+ }
+
+ prop_len /= 2;
+ vdd->level_votes = devm_kzalloc(&pdev->dev,
+ prop_len * sizeof(*vdd->level_votes),
+ GFP_KERNEL);
+ if (!vdd->level_votes)
+ return -ENOMEM;
+
+ vdd->vdd_uv = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
+ GFP_KERNEL);
+ if (!vdd->vdd_uv)
+ return -ENOMEM;
+
+ c->fmax = devm_kzalloc(&pdev->dev, prop_len * sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!c->fmax)
+ return -ENOMEM;
+
+ array = devm_kzalloc(&pdev->dev,
+ prop_len * sizeof(u32) * 2, GFP_KERNEL);
+ if (!array)
+ return -ENOMEM;
+
+ of_property_read_u32_array(of, prop_name, array, prop_len * 2);
+ for (i = 0; i < prop_len; i++) {
+ c->fmax[i] = array[2 * i];
+ vdd->vdd_uv[i] = array[2 * i + 1];
+ }
+
+ devm_kfree(&pdev->dev, array);
+ vdd->num_levels = prop_len;
+ vdd->cur_level = prop_len;
+ vdd->use_max_uV = true;
+ c->num_fmax = prop_len;
+ return 0;
+}
+
+static void get_speed_bin(struct platform_device *pdev, int *bin,
+ int *version)
+{
+ struct resource *res;
+ void __iomem *base, *base1, *base2;
+ u32 pte_efuse, pte_efuse1, pte_efuse2;
+
+ *bin = 0;
+ *version = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
+ if (!res) {
+ dev_info(&pdev->dev,
+ "No speed/PVS binning available. Defaulting to 0!\n");
+ return;
+ }
+
+ base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!base) {
+ dev_warn(&pdev->dev,
+ "Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ devm_iounmap(&pdev->dev, base);
+
+ *bin = (pte_efuse >> 2) & 0x7;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse1");
+ if (!res) {
+ dev_info(&pdev->dev,
+ "No PVS version available. Defaulting to 0!\n");
+ goto out;
+ }
+
+ base1 = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!base1) {
+ dev_warn(&pdev->dev,
+ "Unable to read efuse1 data. Defaulting to 0!\n");
+ goto out;
+ }
+
+ pte_efuse1 = readl_relaxed(base1);
+ devm_iounmap(&pdev->dev, base1);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse2");
+ if (!res) {
+ dev_info(&pdev->dev,
+ "No PVS version available. Defaulting to 0!\n");
+ goto out;
+ }
+
+ base2 = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!base2) {
+ dev_warn(&pdev->dev,
+ "Unable to read efuse2 data. Defaulting to 0!\n");
+ goto out;
+ }
+
+ pte_efuse2 = readl_relaxed(base2);
+ devm_iounmap(&pdev->dev, base2);
+
+ *version = ((pte_efuse1 >> 29 & 0x1) | ((pte_efuse2 >> 18 & 0x3) << 1));
+
+out:
+ dev_info(&pdev->dev, "Speed bin: %d PVS Version: %d\n", *bin,
+ *version);
+}
+
+static int of_get_clk_src(struct platform_device *pdev,
+ struct clk_src *parents, int mux_id)
+{
+ struct device_node *of = pdev->dev.of_node;
+ int mux_parents, i, j, index;
+ struct clk *c;
+ char clk_name[] = "clk-xxx-x";
+
+ mux_parents = of_property_count_strings(of, "clock-names");
+ if (mux_parents <= 0) {
+ dev_err(&pdev->dev, "missing clock-names\n");
+ return -EINVAL;
+ }
+ j = 0;
+
+ for (i = 0; i < 8; i++) {
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "clk-%s-%d",
+ mux_names[mux_id], i);
+ index = of_property_match_string(of, "clock-names", clk_name);
+ if (index < 0)
+ continue;
+
+ parents[j].sel = i;
+ parents[j].src = c = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(c)) {
+ if (c != ERR_PTR(-EPROBE_DEFER))
+ dev_err(&pdev->dev, "clk_get: %s\n fail",
+ clk_name);
+ return PTR_ERR(c);
+ }
+ j++;
+ }
+
+ return j;
+}
+
+static int cpu_parse_devicetree(struct platform_device *pdev, int mux_id)
+{
+ struct resource *res;
+ int rc;
+ char rcg_name[] = "apcs-xxx-rcg-base";
+ char vdd_name[] = "vdd-xxx";
+ struct regulator *regulator;
+
+ snprintf(rcg_name, ARRAY_SIZE(rcg_name), "apcs-%s-rcg-base",
+ mux_names[mux_id]);
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, rcg_name);
+ if (!res) {
+ dev_err(&pdev->dev, "missing %s\n", rcg_name);
+ return -EINVAL;
+ }
+ a53ssmux[mux_id]->base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!a53ssmux[mux_id]->base) {
+ dev_err(&pdev->dev, "ioremap failed for %s\n", rcg_name);
+ return -ENOMEM;
+ }
+
+ snprintf(vdd_name, ARRAY_SIZE(vdd_name), "vdd-%s", mux_names[mux_id]);
+ regulator = devm_regulator_get(&pdev->dev, vdd_name);
+ if (IS_ERR(regulator)) {
+ if (PTR_ERR(regulator) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "unable to get regulator\n");
+ return PTR_ERR(regulator);
+ }
+ cpuclk[mux_id]->c.vdd_class->regulator[0] = regulator;
+
+ rc = of_get_clk_src(pdev, a53ssmux[mux_id]->parents, mux_id);
+ if (rc < 0)
+ return rc;
+
+ a53ssmux[mux_id]->num_parents = rc;
+
+ return 0;
+}
+
+static long corner_to_voltage(unsigned long corner, struct device *dev)
+{
+ struct dev_pm_opp *oppl;
+ long uv;
+
+ rcu_read_lock();
+ oppl = dev_pm_opp_find_freq_exact(dev, corner, true);
+ rcu_read_unlock();
+ if (IS_ERR_OR_NULL(oppl))
+ return -EINVAL;
+
+ rcu_read_lock();
+ uv = dev_pm_opp_get_voltage(oppl);
+ rcu_read_unlock();
+
+ return uv;
+}
+
+
+static int add_opp(struct clk *c, struct device *cpudev, struct device *vregdev,
+ unsigned long max_rate)
+{
+ unsigned long rate = 0;
+ int level;
+ long ret, uv, corner;
+ bool use_voltages = false;
+ struct dev_pm_opp *oppl;
+ int j = 1;
+
+ rcu_read_lock();
+ /* Check if the regulator driver has already populated OPP tables */
+ oppl = dev_pm_opp_find_freq_exact(vregdev, 2, true);
+ rcu_read_unlock();
+ if (!IS_ERR_OR_NULL(oppl))
+ use_voltages = true;
+
+ while (1) {
+ rate = c->fmax[j++];
+ level = find_vdd_level(c, rate);
+ if (level <= 0) {
+ pr_warn("clock-cpu: no uv for %lu.\n", rate);
+ return -EINVAL;
+ }
+ uv = corner = c->vdd_class->vdd_uv[level];
+ /*
+ * If corner to voltage mapping is available, populate the OPP
+ * table with the voltages rather than corners.
+ */
+ if (use_voltages) {
+ uv = corner_to_voltage(corner, vregdev);
+ if (uv < 0) {
+ pr_warn("clock-cpu: no uv for corner %lu\n",
+ corner);
+ return uv;
+ }
+ ret = dev_pm_opp_add(cpudev, rate, uv);
+ if (ret) {
+ pr_warn("clock-cpu: couldn't add OPP for %lu\n",
+ rate);
+ return ret;
+ }
+
+ } else {
+ /*
+ * Populate both CPU and regulator devices with the
+ * freq-to-corner OPP table to maintain backward
+ * compatibility.
+ */
+ ret = dev_pm_opp_add(cpudev, rate, corner);
+ if (ret) {
+ pr_warn("clock-cpu: couldn't add OPP for %lu\n",
+ rate);
+ return ret;
+ }
+ ret = dev_pm_opp_add(vregdev, rate, corner);
+ if (ret) {
+ pr_warn("clock-cpu: couldn't add OPP for %lu\n",
+ rate);
+ return ret;
+ }
+ }
+ if (rate >= max_rate)
+ break;
+ }
+
+ return 0;
+}
+
+static void print_opp_table(int a53_c0_cpu, int a53_c1_cpu, bool single_cluster)
+{
+ struct dev_pm_opp *oppfmax, *oppfmin;
+ unsigned long apc0_fmax, apc1_fmax, apc0_fmin, apc1_fmin;
+
+ if (!single_cluster) {
+ apc0_fmax = a53_lc_clk.c.fmax[a53_lc_clk.c.num_fmax - 1];
+ apc0_fmin = a53_lc_clk.c.fmax[1];
+ }
+ apc1_fmax = a53_bc_clk.c.fmax[a53_bc_clk.c.num_fmax - 1];
+ apc1_fmin = a53_bc_clk.c.fmax[1];
+
+ rcu_read_lock();
+ if (!single_cluster) {
+ oppfmax = dev_pm_opp_find_freq_exact(get_cpu_device(a53_c0_cpu),
+ apc0_fmax, true);
+ oppfmin = dev_pm_opp_find_freq_exact(get_cpu_device(a53_c0_cpu),
+ apc0_fmin, true);
+ /*
+ * One time information during boot. Important to know that this
+ * looks sane since it can eventually make its way to the
+ * scheduler.
+ */
+ pr_info("clock_cpu: a53_c0: OPP voltage for %lu: %ld\n",
+ apc0_fmin, dev_pm_opp_get_voltage(oppfmin));
+ pr_info("clock_cpu: a53_c0: OPP voltage for %lu: %ld\n",
+ apc0_fmax, dev_pm_opp_get_voltage(oppfmax));
+ }
+ oppfmax = dev_pm_opp_find_freq_exact(get_cpu_device(a53_c1_cpu),
+ apc1_fmax, true);
+ oppfmin = dev_pm_opp_find_freq_exact(get_cpu_device(a53_c1_cpu),
+ apc1_fmin, true);
+ pr_info("clock_cpu: a53_c1: OPP voltage for %lu: %lu\n", apc1_fmin,
+ dev_pm_opp_get_voltage(oppfmin));
+ pr_info("clock_cpu: a53_c1: OPP voltage for %lu: %lu\n", apc1_fmax,
+ dev_pm_opp_get_voltage(oppfmax));
+ rcu_read_unlock();
+}
+
+static void populate_opp_table(struct platform_device *pdev,
+ bool single_cluster)
+{
+ struct platform_device *apc0_dev = 0, *apc1_dev;
+ struct device_node *apc0_node = NULL, *apc1_node;
+ unsigned long apc0_fmax = 0, apc1_fmax = 0;
+ int cpu, a53_c0_cpu = 0, a53_c1_cpu = 0;
+
+ if (!single_cluster)
+ apc0_node = of_parse_phandle(pdev->dev.of_node,
+ "vdd-c0-supply", 0);
+ apc1_node = of_parse_phandle(pdev->dev.of_node, "vdd-c1-supply", 0);
+ if (!apc0_node && !single_cluster) {
+ pr_err("can't find the apc0 dt node.\n");
+ return;
+ }
+ if (!apc1_node) {
+ pr_err("can't find the apc1 dt node.\n");
+ return;
+ }
+ if (!single_cluster)
+ apc0_dev = of_find_device_by_node(apc0_node);
+
+ apc1_dev = of_find_device_by_node(apc1_node);
+ if (!apc1_dev && !single_cluster) {
+ pr_err("can't find the apc0 device node.\n");
+ return;
+ }
+ if (!apc1_dev) {
+ pr_err("can't find the apc1 device node.\n");
+ return;
+ }
+
+ if (!single_cluster)
+ apc0_fmax = a53_lc_clk.c.fmax[a53_lc_clk.c.num_fmax - 1];
+
+ apc1_fmax = a53_bc_clk.c.fmax[a53_bc_clk.c.num_fmax - 1];
+
+ for_each_possible_cpu(cpu) {
+ pr_debug("the CPU number is : %d\n", cpu);
+ if (cpu/4 == 0) {
+ a53_c1_cpu = cpu;
+ WARN(add_opp(&a53_bc_clk.c, get_cpu_device(cpu),
+ &apc1_dev->dev, apc1_fmax),
+ "Failed to add OPP levels for A53 big cluster\n");
+ } else if (cpu/4 == 1 && !single_cluster) {
+ a53_c0_cpu = cpu;
+ WARN(add_opp(&a53_lc_clk.c, get_cpu_device(cpu),
+ &apc0_dev->dev, apc0_fmax),
+ "Failed to add OPP levels for A53 little cluster\n");
+ }
+ }
+
+ /* One time print during bootup */
+ pr_info("clock-cpu-8939: OPP tables populated (cpu %d and %d)",
+ a53_c0_cpu, a53_c1_cpu);
+
+ print_opp_table(a53_c0_cpu, a53_c1_cpu, single_cluster);
+
+}
+
+static void config_pll(int mux_id)
+{
+ unsigned long rate, aux_rate;
+ struct clk *aux_clk, *main_pll;
+
+ aux_clk = a53ssmux[mux_id]->parents[0].src;
+ main_pll = a53ssmux[mux_id]->parents[1].src;
+
+ aux_rate = clk_get_rate(aux_clk);
+ rate = clk_get_rate(&a53ssmux[mux_id]->c);
+ clk_set_rate(&a53ssmux[mux_id]->c, aux_rate);
+ clk_set_rate(main_pll, clk_round_rate(main_pll, 1));
+ clk_set_rate(&a53ssmux[mux_id]->c, rate);
+
+}
+
+static int clock_8939_pm_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ clk_unprepare(&a53_lc_clk.c);
+ clk_unprepare(&a53_bc_clk.c);
+ clk_unprepare(&cci_clk.c);
+ break;
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ clk_prepare(&a53_lc_clk.c);
+ clk_prepare(&a53_bc_clk.c);
+ clk_prepare(&cci_clk.c);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static int clock_8939_pm_event_single_cluster(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ clk_unprepare(&a53_bc_clk.c);
+ break;
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ clk_prepare(&a53_bc_clk.c);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block clock_8939_pm_notifier = {
+ .notifier_call = clock_8939_pm_event,
+};
+
+static struct notifier_block clock_8939_pm_notifier_single_cluster = {
+ .notifier_call = clock_8939_pm_event_single_cluster,
+};
+
+/**
+ * clock_panic_callback() - panic notification callback function.
+ * This function is invoked when a kernel panic occurs.
+ * @nfb: Notifier block pointer
+ * @event: Value passed unmodified to notifier function
+ * @data: Pointer passed unmodified to notifier function
+ *
+ * Return: NOTIFY_OK
+ */
+static int clock_panic_callback(struct notifier_block *nfb,
+ unsigned long event, void *data)
+{
+ bool single_cluster = 0;
+ unsigned long rate;
+ struct device_node *ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-8939");
+ if (!ofnode)
+ ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-8917");
+ if (ofnode)
+ single_cluster = of_property_read_bool(ofnode,
+ "qcom,num-cluster");
+
+ rate = (a53_bc_clk.c.count) ? a53_bc_clk.c.rate : 0;
+ pr_err("%s frequency: %10lu Hz\n", a53_bc_clk.c.dbg_name, rate);
+
+ if (!single_cluster) {
+ rate = (a53_lc_clk.c.count) ? a53_lc_clk.c.rate : 0;
+ pr_err("%s frequency: %10lu Hz\n", a53_lc_clk.c.dbg_name, rate);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block clock_panic_notifier = {
+ .notifier_call = clock_panic_callback,
+ .priority = 1,
+};
+
+static int clock_a53_probe(struct platform_device *pdev)
+{
+ int speed_bin, version, rc, cpu, mux_id, rate;
+ char prop_name[] = "qcom,speedX-bin-vX-XXX";
+ int mux_num;
+ bool single_cluster;
+
+ single_cluster = of_property_read_bool(pdev->dev.of_node,
+ "qcom,num-cluster");
+
+ get_speed_bin(pdev, &speed_bin, &version);
+
+ mux_num = single_cluster ? A53SS_MUX_LC:A53SS_MUX_NUM;
+
+ for (mux_id = 0; mux_id < mux_num; mux_id++) {
+ rc = cpu_parse_devicetree(pdev, mux_id);
+ if (rc)
+ return rc;
+
+ snprintf(prop_name, ARRAY_SIZE(prop_name),
+ "qcom,speed%d-bin-v%d-%s",
+ speed_bin, version, mux_names[mux_id]);
+
+ rc = of_get_fmax_vdd_class(pdev, &cpuclk[mux_id]->c,
+ prop_name);
+ if (rc) {
+ /* Fall back to most conservative PVS table */
+ dev_err(&pdev->dev, "Unable to load voltage plan %s!\n",
+ prop_name);
+
+ snprintf(prop_name, ARRAY_SIZE(prop_name),
+ "qcom,speed0-bin-v0-%s", mux_names[mux_id]);
+ rc = of_get_fmax_vdd_class(pdev, &cpuclk[mux_id]->c,
+ prop_name);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to load safe voltage plan\n");
+ return rc;
+ }
+ dev_info(&pdev->dev, "Safe voltage plan loaded.\n");
+ }
+ }
+ if (single_cluster)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ cpu_clocks_8939_single_cluster,
+ ARRAY_SIZE(cpu_clocks_8939_single_cluster));
+ else
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ cpu_clocks_8939, ARRAY_SIZE(cpu_clocks_8939));
+
+ if (rc) {
+ dev_err(&pdev->dev, "msm_clock_register failed\n");
+ return rc;
+ }
+
+ if (!single_cluster) {
+ rate = clk_get_rate(&cci_clk.c);
+ clk_set_rate(&cci_clk.c, rate);
+ }
+
+ for (mux_id = 0; mux_id < mux_num; mux_id++) {
+ /* Force a PLL reconfiguration */
+ config_pll(mux_id);
+ }
+
+ /*
+ * We don't want the CPU clocks to be turned off at late init
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
+ * that the clocks have already been prepared and enabled by the time
+ * they take over.
+ */
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ WARN(clk_prepare_enable(&cpuclk[cpu/4]->c),
+ "Unable to turn on CPU clock");
+ if (!single_cluster)
+ clk_prepare_enable(&cci_clk.c);
+ }
+ put_online_cpus();
+
+ for_each_possible_cpu(cpu) {
+ if (logical_cpu_to_clk(cpu) == &a53_bc_clk.c)
+ cpumask_set_cpu(cpu, &a53_bc_clk.cpumask);
+ if (logical_cpu_to_clk(cpu) == &a53_lc_clk.c)
+ cpumask_set_cpu(cpu, &a53_lc_clk.cpumask);
+ }
+
+ a53_lc_clk.hw_low_power_ctrl = true;
+ a53_bc_clk.hw_low_power_ctrl = true;
+
+ if (single_cluster)
+ register_pm_notifier(&clock_8939_pm_notifier_single_cluster);
+ else
+ register_pm_notifier(&clock_8939_pm_notifier);
+
+ populate_opp_table(pdev, single_cluster);
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &clock_panic_notifier);
+
+ return 0;
+}
+
+static const struct of_device_id clock_a53_match_table[] = {
+ {.compatible = "qcom,cpu-clock-8939"},
+ {.compatible = "qcom,cpu-clock-8917"},
+ {}
+};
+
+static struct platform_driver clock_a53_driver = {
+ .probe = clock_a53_probe,
+ .driver = {
+ .name = "cpu-clock-8939",
+ .of_match_table = clock_a53_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init clock_a53_init(void)
+{
+ return platform_driver_register(&clock_a53_driver);
+}
+arch_initcall(clock_a53_init);
+
+static int __init clock_cpu_lpm_get_latency(void)
+{
+ bool single_cluster;
+ int rc = 0;
+ struct device_node *ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-8939");
+
+ if (!ofnode)
+ ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-gold");
+
+ if (!ofnode)
+ return 0;
+
+ single_cluster = of_property_read_bool(ofnode,
+ "qcom,num-cluster");
+
+ rc = lpm_get_latency(&a53_bc_clk.latency_lvl,
+ &a53_bc_clk.cpu_latency_no_l2_pc_us);
+ if (rc < 0)
+ pr_err("Failed to get the L2 PC value for perf\n");
+
+ if (!single_cluster) {
+ rc = lpm_get_latency(&a53_lc_clk.latency_lvl,
+ &a53_lc_clk.cpu_latency_no_l2_pc_us);
+ if (rc < 0)
+ pr_err("Failed to get the L2 PC value for pwr\n");
+
+ pr_debug("Latency for pwr/perf cluster %d : %d\n",
+ a53_lc_clk.cpu_latency_no_l2_pc_us,
+ a53_bc_clk.cpu_latency_no_l2_pc_us);
+ } else {
+ pr_debug("Latency for perf cluster %d\n",
+ a53_bc_clk.cpu_latency_no_l2_pc_us);
+ }
+
+ return rc;
+}
+late_initcall(clock_cpu_lpm_get_latency);
+
+#define APCS_C0_PLL 0xb116000
+#define C0_PLL_MODE 0x0
+#define C0_PLL_L_VAL 0x4
+#define C0_PLL_M_VAL 0x8
+#define C0_PLL_N_VAL 0xC
+#define C0_PLL_USER_CTL 0x10
+#define C0_PLL_CONFIG_CTL 0x14
+
+#define APCS_ALIAS0_CMD_RCGR 0xb111050
+#define APCS_ALIAS0_CFG_OFF 0x4
+#define APCS_ALIAS0_CORE_CBCR_OFF 0x8
+#define SRC_SEL 0x4
+#define SRC_DIV 0x3
+
+static void __init configure_enable_sr2_pll(void __iomem *base)
+{
+ /* Disable Mode */
+ writel_relaxed(0x0, base + C0_PLL_MODE);
+
+ /* Configure L/M/N values */
+ writel_relaxed(0x34, base + C0_PLL_L_VAL);
+ writel_relaxed(0x0, base + C0_PLL_M_VAL);
+ writel_relaxed(0x1, base + C0_PLL_N_VAL);
+
+ /* Configure USER_CTL and CONFIG_CTL value */
+ writel_relaxed(0x0100000f, base + C0_PLL_USER_CTL);
+ writel_relaxed(0x4c015765, base + C0_PLL_CONFIG_CTL);
+
+ /* Enable PLL now */
+ writel_relaxed(0x2, base + C0_PLL_MODE);
+ udelay(2);
+ writel_relaxed(0x6, base + C0_PLL_MODE);
+ udelay(50);
+ writel_relaxed(0x7, base + C0_PLL_MODE);
+ /* Ensure that the writes go through before enabling
+ * PLL
+ */
+ mb();
+}
+
+static int __init cpu_clock_a53_init_little(void)
+{
+ void __iomem *base;
+ int regval = 0, count;
+ struct device_node *ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-8939");
+ if (!ofnode)
+ return 0;
+
+ base = ioremap_nocache(APCS_C0_PLL, SZ_32);
+ configure_enable_sr2_pll(base);
+ iounmap(base);
+
+ base = ioremap_nocache(APCS_ALIAS0_CMD_RCGR, SZ_8);
+ regval = readl_relaxed(base);
+ /* Source GPLL0 and 1/2 the rate of GPLL0 */
+ regval = (SRC_SEL << 8) | SRC_DIV; /* 0x403 */
+ writel_relaxed(regval, base + APCS_ALIAS0_CFG_OFF);
+ /* Make sure src sel and src div is set before update bit */
+ mb();
+
+ /* update bit */
+ regval = readl_relaxed(base);
+ regval |= BIT(0);
+ writel_relaxed(regval, base);
+
+ /* Wait for update to take effect */
+ for (count = 500; count > 0; count--) {
+ if (!(readl_relaxed(base)) & BIT(0))
+ break;
+ udelay(1);
+ }
+
+ /* Enable the branch */
+ regval = readl_relaxed(base + APCS_ALIAS0_CORE_CBCR_OFF);
+ regval |= BIT(0);
+ writel_relaxed(regval, base + APCS_ALIAS0_CORE_CBCR_OFF);
+ /* Branch enable should be complete */
+ mb();
+ iounmap(base);
+
+ pr_info("A53 Power clocks configured\n");
+
+ return 0;
+}
+early_initcall(cpu_clock_a53_init_little);
diff --git a/drivers/clk/msm/clock-gcc-8952.c b/drivers/clk/msm/clock-gcc-8952.c
new file mode 100644
index 0000000..7da217c
--- /dev/null
+++ b/drivers/clk/msm/clock-gcc-8952.c
@@ -0,0 +1,4867 @@
+/*
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <soc/qcom/clock-local2.h>
+#include <soc/qcom/clock-pll.h>
+#include <soc/qcom/clock-alpha-pll.h>
+#include <soc/qcom/clock-voter.h>
+#include <soc/qcom/rpm-smd.h>
+#include <soc/qcom/clock-rpm.h>
+
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+
+#include <dt-bindings/clock/msm-clocks-8952.h>
+#include <dt-bindings/clock/msm-clocks-hwio-8952.h>
+
+#include "clock.h"
+#include "reset.h"
+
+enum {
+ GCC_BASE,
+ APCS_C1_PLL_BASE,
+ APCS_C0_PLL_BASE,
+ APCS_CCI_PLL_BASE,
+ N_BASES,
+};
+
+static void __iomem *virt_bases[N_BASES];
+#define GCC_REG_BASE(x) (void __iomem *)(virt_bases[GCC_BASE] + (x))
+
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
+
+/* SMD clocks */
+DEFINE_CLK_RPM_SMD_BRANCH(xo_clk_src, xo_a_clk_src, RPM_MISC_CLK_TYPE,
+ CXO_CLK_SRC_ID, 19200000);
+DEFINE_CLK_RPM_SMD(pnoc_clk, pnoc_a_clk, RPM_BUS_CLK_TYPE, PNOC_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD(bimc_gpu_clk, bimc_gpu_a_clk, RPM_MEM_CLK_TYPE,
+ BIMC_GPU_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD(sysmmnoc_clk, sysmmnoc_a_clk, RPM_BUS_CLK_TYPE,
+ SYSMMNOC_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD(ipa_clk, ipa_a_clk, RPM_IPA_CLK_TYPE, IPA_CLK_ID, NULL);
+DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_CLK_ID);
+
+/* SMD_XO_BUFFER */
+DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk1, bb_clk1_a, BB_CLK1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk2, bb_clk2_a, BB_CLK2_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(rf_clk2, rf_clk2_a, RF_CLK2_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(div_clk2, div_clk2_a, DIV_CLK2_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk1_pin, bb_clk1_a_pin, BB_CLK1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk2_pin, bb_clk2_a_pin, BB_CLK2_ID);
+
+/* Voter clocks */
+static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(sysmmnoc_msmbus_clk, &sysmmnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(sysmmnoc_msmbus_a_clk, &sysmmnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_usb_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_usb_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_usb_a_clk, &bimc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_usb_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_usb_clk, &snoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_usb_clk, &bimc_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(snoc_wcnss_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_wcnss_a_clk, &bimc_a_clk.c, LONG_MAX);
+
+/* Branch Voter clocks */
+static DEFINE_CLK_BRANCH_VOTER(xo_gcc, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_otg_clk, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_lpm_clk, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_pil_pronto_clk, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_pil_mss_clk, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_wlan_clk, &xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(xo_pil_lpass_clk, &xo_clk_src.c);
+
+DEFINE_CLK_DUMMY(wcnss_m_clk, 0);
+
+enum vdd_sr2_pll_levels {
+ VDD_SR2_PLL_OFF,
+ VDD_SR2_PLL_SVS,
+ VDD_SR2_PLL_NOM,
+ VDD_SR2_PLL_TUR,
+ VDD_SR2_PLL_SUPER_TUR,
+ VDD_SR2_PLL_NUM,
+};
+
+static int vdd_sr2_levels[] = {
+ 0, RPM_REGULATOR_LEVEL_NONE, /* VDD_SR2_PLL_OFF */
+ 1800000, RPM_REGULATOR_LEVEL_SVS, /* VDD_SR2_PLL_SVS */
+ 1800000, RPM_REGULATOR_LEVEL_NOM, /* VDD_SR2_PLL_NOM */
+ 1800000, RPM_REGULATOR_LEVEL_TURBO, /* VDD_SR2_PLL_TUR */
+ 1800000, RPM_REGULATOR_LEVEL_BINNING, /* VDD_SR2_PLL_SUPER_TUR */
+};
+
+static DEFINE_VDD_REGULATORS(vdd_sr2_pll, VDD_SR2_PLL_NUM, 2,
+ vdd_sr2_levels, NULL);
+
+enum vdd_hf_pll_levels {
+ VDD_HF_PLL_OFF,
+ VDD_HF_PLL_SVS,
+ VDD_HF_PLL_NOM,
+ VDD_HF_PLL_TUR,
+ VDD_HF_PLL_SUPER_TUR,
+ VDD_HF_PLL_NUM,
+};
+
+static int vdd_hf_levels[] = {
+ 0, RPM_REGULATOR_LEVEL_NONE, /* VDD_HF_PLL_OFF */
+ 1800000, RPM_REGULATOR_LEVEL_SVS, /* VDD_HF_PLL_SVS */
+ 1800000, RPM_REGULATOR_LEVEL_NOM, /* VDD_HF_PLL_NOM */
+ 1800000, RPM_REGULATOR_LEVEL_TURBO, /* VDD_HF_PLL_TUR */
+ 1800000, RPM_REGULATOR_LEVEL_BINNING, /* VDD_HF_PLL_SUPER_TUR */
+};
+static DEFINE_VDD_REGULATORS(vdd_hf_pll, VDD_HF_PLL_NUM, 2,
+ vdd_hf_levels, NULL);
+
+static struct pll_freq_tbl apcs_cci_pll_freq[] = {
+ F_APCS_PLL(307200000, 16, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(600000000, 31, 0x1, 0x4, 0x0, 0x0, 0x0),
+};
+
+static struct pll_clk a53ss_cci_pll = {
+ .mode_reg = (void __iomem *)APCS_CCI_PLL_MODE,
+ .l_reg = (void __iomem *)APCS_CCI_PLL_L_VAL,
+ .m_reg = (void __iomem *)APCS_CCI_PLL_M_VAL,
+ .n_reg = (void __iomem *)APCS_CCI_PLL_N_VAL,
+ .config_reg = (void __iomem *)APCS_CCI_PLL_USER_CTL,
+ .status_reg = (void __iomem *)APCS_CCI_PLL_STATUS,
+ .freq_tbl = apcs_cci_pll_freq,
+ .masks = {
+ .vco_mask = BM(29, 28),
+ .pre_div_mask = BIT(12),
+ .post_div_mask = BM(9, 8),
+ .mn_en_mask = BIT(24),
+ .main_output_mask = BIT(0),
+ },
+ .base = &virt_bases[APCS_CCI_PLL_BASE],
+ .spm_ctrl = {
+ .offset = 0x40,
+ .event_bit = 0x0,
+ },
+ .c = {
+ .parent = &xo_a_clk_src.c,
+ .dbg_name = "a53ss_cci_pll",
+ .ops = &clk_ops_sr2_pll,
+ .vdd_class = &vdd_sr2_pll,
+ .fmax = (unsigned long [VDD_SR2_PLL_NUM]) {
+ [VDD_SR2_PLL_SVS] = 1000000000,
+ [VDD_SR2_PLL_NOM] = 1900000000,
+ },
+ .num_fmax = VDD_SR2_PLL_NUM,
+ CLK_INIT(a53ss_cci_pll.c),
+ },
+};
+
+static struct pll_freq_tbl apcs_c0_pll_freq[] = {
+ F_APCS_PLL( 249600000, 13, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 307200000, 16, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 345600000, 18, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 384000000, 20, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 460800000, 24, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 499200000, 26, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 518400000, 27, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 768000000, 40, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 806400000, 42, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 844800000, 44, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 883200000, 46, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 902400000, 47, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 921600000, 48, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 998400000, 52, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1094400000, 57, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1209600000, 63, 0x0, 0x1, 0x0, 0x0, 0x0),
+};
+
+static struct pll_clk a53ss_c0_pll = {
+ .mode_reg = (void __iomem *)APCS_C0_PLL_MODE,
+ .l_reg = (void __iomem *)APCS_C0_PLL_L_VAL,
+ .m_reg = (void __iomem *)APCS_C0_PLL_M_VAL,
+ .n_reg = (void __iomem *)APCS_C0_PLL_N_VAL,
+ .config_reg = (void __iomem *)APCS_C0_PLL_USER_CTL,
+ .status_reg = (void __iomem *)APCS_C0_PLL_STATUS,
+ .freq_tbl = apcs_c0_pll_freq,
+ .masks = {
+ .vco_mask = BM(29, 28),
+ .pre_div_mask = BIT(12),
+ .post_div_mask = BM(9, 8),
+ .mn_en_mask = BIT(24),
+ .main_output_mask = BIT(0),
+ },
+ .base = &virt_bases[APCS_C0_PLL_BASE],
+ .spm_ctrl = {
+ .offset = 0x50,
+ .event_bit = 0x4,
+ },
+ .c = {
+ .parent = &xo_a_clk_src.c,
+ .dbg_name = "a53ss_c0_pll",
+ .ops = &clk_ops_sr2_pll,
+ .vdd_class = &vdd_sr2_pll,
+ .fmax = (unsigned long [VDD_SR2_PLL_NUM]) {
+ [VDD_SR2_PLL_SVS] = 1000000000,
+ [VDD_SR2_PLL_NOM] = 1900000000,
+ },
+ .num_fmax = VDD_SR2_PLL_NUM,
+ CLK_INIT(a53ss_c0_pll.c),
+ },
+};
+
+static struct pll_freq_tbl apcs_c1_pll_freq[] = {
+ F_APCS_PLL( 345600000, 18, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 422400000, 22, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 499200000, 26, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 652800000, 34, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 729600000, 38, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 806400000, 42, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 844800000, 44, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 883200000, 46, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 960000000, 50, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL( 998400000, 52, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1036800000, 54, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1094400000, 57, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1113600000, 58, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1190400000, 62, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1209600000, 63, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1248000000, 65, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1267200000, 66, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1344000000, 70, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1401000000, 73, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1420800000, 74, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1440000000, 75, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1459200000, 76, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1497600000, 78, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1516800000, 79, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1536000000, 80, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1651200000, 86, 0x0, 0x1, 0x0, 0x0, 0x0),
+};
+
+static struct pll_clk a53ss_c1_pll = {
+ .mode_reg = (void __iomem *)APCS_C1_PLL_MODE,
+ .l_reg = (void __iomem *)APCS_C1_PLL_L_VAL,
+ .m_reg = (void __iomem *)APCS_C1_PLL_M_VAL,
+ .n_reg = (void __iomem *)APCS_C1_PLL_N_VAL,
+ .config_reg = (void __iomem *)APCS_C1_PLL_USER_CTL,
+ .status_reg = (void __iomem *)APCS_C1_PLL_STATUS,
+ .freq_tbl = apcs_c1_pll_freq,
+ .masks = {
+ .vco_mask = BM(29, 28),
+ .pre_div_mask = BIT(12),
+ .post_div_mask = BM(9, 8),
+ .mn_en_mask = BIT(24),
+ .main_output_mask = BIT(0),
+ },
+ .base = &virt_bases[APCS_C1_PLL_BASE],
+ .spm_ctrl = {
+ .offset = 0x50,
+ .event_bit = 0x4,
+ },
+ .c = {
+ .parent = &xo_a_clk_src.c,
+ .dbg_name = "a53ss_c1_pll",
+ .ops = &clk_ops_sr2_pll,
+ .vdd_class = &vdd_hf_pll,
+ .fmax = (unsigned long [VDD_HF_PLL_NUM]) {
+ [VDD_HF_PLL_SVS] = 1000000000,
+ [VDD_HF_PLL_NOM] = 2000000000,
+ },
+ .num_fmax = VDD_HF_PLL_NUM,
+ CLK_INIT(a53ss_c1_pll.c),
+ },
+};
+
+static struct pll_vote_clk gpll0_sleep_clk_src = {
+ .en_reg = (void __iomem *)APCS_CLOCK_SLEEP_ENA_VOTE,
+ .en_mask = BIT(23),
+ .status_reg = (void __iomem *)GPLL0_MODE,
+ .status_mask = BIT(30),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &xo_clk_src.c,
+ .rate = 800000000,
+ .dbg_name = "gpll0_sleep_clk_src",
+ .ops = &clk_ops_pll_sleep_vote,
+ CLK_INIT(gpll0_sleep_clk_src.c),
+ },
+};
+
+static unsigned int soft_vote_gpll0;
+
+/* PLL_ACTIVE_FLAG bit of GCC_GPLL0_MODE register
+ * gets set from PLL voting FSM.It indicates when
+ * FSM has enabled the PLL and PLL should be locked.
+ */
+static struct pll_vote_clk gpll0_clk_src_8952 = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)GPLL0_STATUS,
+ .status_mask = BIT(17),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &xo_clk_src.c,
+ .rate = 800000000,
+ .dbg_name = "gpll0_clk_src_8952",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_clk_src_8952.c),
+ },
+};
+
+static struct pll_vote_clk gpll0_clk_src_8937 = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)GPLL0_MODE,
+ .status_mask = BIT(30),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &gpll0_sleep_clk_src.c,
+ .rate = 800000000,
+ .dbg_name = "gpll0_clk_src_8937",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_clk_src_8937.c),
+ },
+};
+
+DEFINE_EXT_CLK(gpll0_clk_src, NULL);
+DEFINE_EXT_CLK(gpll0_ao_clk_src, NULL);
+DEFINE_EXT_CLK(gpll0_out_aux_clk_src, &gpll0_clk_src.c);
+DEFINE_EXT_CLK(gpll0_out_main_clk_src, &gpll0_clk_src.c);
+DEFINE_EXT_CLK(ext_pclk0_clk_src, NULL);
+DEFINE_EXT_CLK(ext_byte0_clk_src, NULL);
+DEFINE_EXT_CLK(ext_pclk1_clk_src, NULL);
+DEFINE_EXT_CLK(ext_byte1_clk_src, NULL);
+
+/* Don't vote for xo if using this clock to allow xo shutdown */
+static struct pll_vote_clk gpll0_ao_clk_src_8952 = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)GPLL0_STATUS,
+ .status_mask = BIT(17),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_ACPU,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &xo_a_clk_src.c,
+ .rate = 800000000,
+ .dbg_name = "gpll0_ao_clk_src_8952",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_ao_clk_src_8952.c),
+ },
+};
+
+static struct pll_vote_clk gpll0_ao_clk_src_8937 = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(0),
+ .status_reg = (void __iomem *)GPLL0_MODE,
+ .status_mask = BIT(30),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_ACPU,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &xo_a_clk_src.c,
+ .rate = 800000000,
+ .dbg_name = "gpll0_ao_clk_src_8937",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_ao_clk_src_8937.c),
+ },
+};
+
+static struct pll_vote_clk gpll6_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(7),
+ .status_reg = (void __iomem *)GPLL6_STATUS,
+ .status_mask = BIT(17),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .parent = &xo_clk_src.c,
+ .rate = 1080000000,
+ .dbg_name = "gpll6_clk_src",
+ .ops = &clk_ops_pll_vote,
+ CLK_INIT(gpll6_clk_src.c),
+ },
+};
+
+DEFINE_EXT_CLK(gpll6_aux_clk_src, &gpll6_clk_src.c);
+DEFINE_EXT_CLK(gpll6_out_main_clk_src, &gpll6_clk_src.c);
+
+static struct alpha_pll_masks pll_masks_p = {
+ .lock_mask = BIT(31),
+ .active_mask = BIT(30),
+ .vco_mask = BM(21, 20) >> 20,
+ .vco_shift = 20,
+ .alpha_en_mask = BIT(24),
+ .output_mask = 0xf,
+ .update_mask = BIT(22),
+ .post_div_mask = BM(11, 8),
+ .test_ctl_lo_mask = BM(31, 0),
+ .test_ctl_hi_mask = BM(31, 0),
+};
+
+/* Slewing plls won't allow to change vco_sel.
+ * Hence will have only one vco table entry
+ */
+static struct alpha_pll_vco_tbl p_vco[] = {
+ VCO(0, 700000000, 1400000000),
+};
+
+/* Slewing plls won't allow to change vco_sel.
+ * Hence will have only one vco table entry
+ */
+static struct alpha_pll_vco_tbl p_vco_8937[] = {
+ VCO(1, 525000000, 1066000000),
+};
+
+static struct alpha_pll_clk gpll3_clk_src = {
+ .masks = &pll_masks_p,
+ .base = &virt_bases[GCC_BASE],
+ .offset = GPLL3_MODE,
+ .vco_tbl = p_vco,
+ .num_vco = ARRAY_SIZE(p_vco),
+ .enable_config = 1,
+ /*
+ * gpll3 is dedicated to oxili and has a fuse implementation for
+ * post divider to limit frequency. HW with fuse blown has a divider
+ * value set to 2. So lets stick to divide by 2 in software to avoid
+ * conflicts.
+ */
+ .post_div_config = 1 << 8,
+ .slew = true,
+ .config_ctl_val = 0x4001055b,
+ .test_ctl_hi_val = 0x40000600,
+ .c = {
+ .rate = 1050000000,
+ .parent = &xo_clk_src.c,
+ .dbg_name = "gpll3_clk_src",
+ .ops = &clk_ops_dyna_alpha_pll,
+ VDD_DIG_FMAX_MAP1(NOMINAL, 1400000000),
+ CLK_INIT(gpll3_clk_src.c),
+ },
+};
+
+static struct pll_vote_clk gpll4_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE,
+ .en_mask = BIT(5),
+ .status_reg = (void __iomem *)GPLL4_MODE,
+ .status_mask = BIT(30),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 1152000000,
+ .parent = &xo_clk_src.c,
+ .dbg_name = "gpll4_clk_src",
+ .ops = &clk_ops_pll_vote,
+ VDD_DIG_FMAX_MAP1(NOMINAL, 1400000000),
+ CLK_INIT(gpll4_clk_src.c),
+ },
+};
+DEFINE_EXT_CLK(gpll4_out_clk_src, &gpll4_clk_src.c);
+
+static struct clk_freq_tbl ftbl_gcc_camss_top_ahb_clk[] = {
+ F( 40000000, gpll0, 10, 1, 2),
+ F( 61540000, gpll0, 13, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F_END
+};
+
+static struct rcg_clk camss_top_ahb_clk_src = {
+ .cmd_rcgr_reg = CAMSS_TOP_AHB_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_top_ahb_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "camss_top_ahb_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP3(LOWER, 40000000, LOW, 61540000,
+ NOMINAL, 80000000),
+ CLK_INIT(camss_top_ahb_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_apss_ahb_clk[] = {
+ F( 19200000, xo_a, 1, 0, 0),
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133330000, gpll0, 6, 0, 0),
+ F_END
+};
+
+static struct rcg_clk apss_ahb_clk_src = {
+ .cmd_rcgr_reg = APSS_AHB_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_apss_ahb_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "apss_ahb_clk_src",
+ .ops = &clk_ops_rcg,
+ CLK_INIT(apss_ahb_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_csi0_2_clk[] = {
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_csi0_2_clk_8937[] = {
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F_END
+};
+
+
+static struct rcg_clk csi0_clk_src = {
+ .cmd_rcgr_reg = CSI0_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_csi0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "csi0_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(csi0_clk_src.c),
+ },
+};
+
+static struct rcg_clk csi1_clk_src = {
+ .cmd_rcgr_reg = CSI1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_csi0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "csi1_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(csi1_clk_src.c),
+ },
+};
+
+static struct rcg_clk csi2_clk_src = {
+ .cmd_rcgr_reg = CSI2_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_csi0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "csi2_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(csi2_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = {
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 180000000, gpll6, 6, 0, 0),
+ F( 228570000, gpll0, 3.5, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_venus0_vcodec0_clk_8937[] = {
+ F( 166150000, gpll6, 6.5, 0, 0),
+ F( 240000000, gpll6, 4.5, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_venus0_vcodec0_clk_8917[] = {
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 270000000, gpll6, 4, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 329140000, gpll4_out, 3.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct rcg_clk vcodec0_clk_src = {
+ .cmd_rcgr_reg = VCODEC0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_venus0_vcodec0_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "vcodec0_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP5(LOWER, 133330000, LOW, 180000000,
+ NOMINAL, 228570000, NOM_PLUS, 266670000,
+ HIGH, 308570000),
+ CLK_INIT(vcodec0_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_vfe0_1_clk[] = {
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_vfe0_1_clk_8937[] = {
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133333333, gpll0, 6, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F( 400000000, gpll0, 2, 0, 0),
+ F( 432000000, gpll6, 2.5, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_vfe0_1_clk_8917[] = {
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133333333, gpll0, 6, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 329140000, gpll4_out, 3.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct rcg_clk vfe0_clk_src = {
+ .cmd_rcgr_reg = VFE0_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_vfe0_1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "vfe0_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP5(LOWER, 133330000, LOW, 266670000,
+ NOMINAL, 308570000, NOM_PLUS, 320000000,
+ HIGH, 360000000),
+ CLK_INIT(vfe0_clk_src.c),
+ },
+};
+
+static struct rcg_clk vfe1_clk_src = {
+ .cmd_rcgr_reg = VFE1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_vfe0_1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "vfe1_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP5(LOWER, 133330000, LOW, 266670000,
+ NOMINAL, 308570000, NOM_PLUS, 320000000,
+ HIGH, 360000000),
+ CLK_INIT(vfe1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 465000000, 930000000, gpll3, 1, 0, 0),
+ F_SLEW( 500000000, 1000000000, gpll3, 1, 0, 0),
+ F_SLEW( 550000000, 1100000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8937[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 216000000, FIXED_CLK_SRC, gpll6_aux, 5, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 300000000, 600000000, gpll3, 1, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 375000000, 750000000, gpll3, 1, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 450000000, 900000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8937_475MHz[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 216000000, FIXED_CLK_SRC, gpll6_aux, 5, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 300000000, 600000000, gpll3, 1, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 375000000, 750000000, gpll3, 1, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 450000000, 900000000, gpll3, 1, 0, 0),
+ F_SLEW( 475000000, 950000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8940_500MHz[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 216000000, FIXED_CLK_SRC, gpll6_aux, 5, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 300000000, 600000000, gpll3, 1, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 375000000, 750000000, gpll3, 1, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 450000000, 900000000, gpll3, 1, 0, 0),
+ F_SLEW( 475000000, 950000000, gpll3, 1, 0, 0),
+ F_SLEW( 500000000, 1000000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8917[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 270000000, FIXED_CLK_SRC, gpll6_aux, 4, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 484800000, 969600000, gpll3, 1, 0, 0),
+ F_SLEW( 523200000, 1046400000, gpll3, 1, 0, 0),
+ F_SLEW( 550000000, 1100000000, gpll3, 1, 0, 0),
+ F_SLEW( 598000000, 1196000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8917_650MHz[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 270000000, FIXED_CLK_SRC, gpll6_aux, 4, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 484800000, 969600000, gpll3, 1, 0, 0),
+ F_SLEW( 523200000, 1046400000, gpll3, 1, 0, 0),
+ F_SLEW( 550000000, 1100000000, gpll3, 1, 0, 0),
+ F_SLEW( 598000000, 1196000000, gpll3, 1, 0, 0),
+ F_SLEW( 650000000, 1300000000, gpll3, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk gfx3d_clk_src = {
+ .cmd_rcgr_reg = GFX3D_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_oxili_gfx3d_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gfx3d_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP5(LOWER, 240000000, LOW, 400000000,
+ NOMINAL, 465000000, NOM_PLUS, 500000000,
+ HIGH, 550000000),
+ CLK_INIT(gfx3d_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F( 50000000, gpll0, 16, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_qup1_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP1_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup1_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp1_qup1_i2c_apps_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk[] = {
+ F( 960000, xo, 10, 1, 2),
+ F( 4800000, xo, 4, 0, 0),
+ F( 9600000, xo, 2, 0, 0),
+ F( 16000000, gpll0, 10, 1, 5),
+ F( 19200000, xo, 1, 0, 0),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 50000000, gpll0, 16, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917[] = {
+ F( 960000, xo, 10, 1, 2),
+ F( 4800000, xo, 4, 0, 0),
+ F( 9600000, xo, 2, 0, 0),
+ F( 16000000, gpll0, 10, 1, 5),
+ F( 19200000, xo, 1, 0, 0),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 40000000, gpll0, 10, 1, 2),
+ F( 50000000, gpll0, 16, 0, 0),
+ F_END
+};
+
+static struct rcg_clk blsp1_qup1_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP1_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup1_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup1_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup2_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP2_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup2_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp1_qup2_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup2_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP2_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup2_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup2_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup3_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP3_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup3_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp1_qup3_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup3_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP3_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup3_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup3_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup4_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP4_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup4_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp1_qup4_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_qup4_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_QUP4_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_qup4_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp1_qup4_spi_apps_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_2_uart1_2_apps_clk[] = {
+ F( 3686400, gpll0, 1, 72, 15625),
+ F( 7372800, gpll0, 1, 144, 15625),
+ F( 14745600, gpll0, 1, 288, 15625),
+ F( 16000000, gpll0, 10, 1, 5),
+ F( 19200000, xo, 1, 0, 0),
+ F( 24000000, gpll0, 1, 3, 100),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 32000000, gpll0, 1, 1, 25),
+ F( 40000000, gpll0, 1, 1, 20),
+ F( 46400000, gpll0, 1, 29, 500),
+ F( 48000000, gpll0, 1, 3, 50),
+ F( 51200000, gpll0, 1, 8, 125),
+ F( 56000000, gpll0, 1, 7, 100),
+ F( 58982400, gpll0, 1, 1152, 15625),
+ F( 60000000, gpll0, 1, 3, 40),
+ F( 64000000, gpll0, 1, 2, 25),
+ F_END
+};
+
+static struct rcg_clk blsp1_uart1_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART1_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_uart1_2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart1_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 32000000, NOMINAL, 64000000),
+ CLK_INIT(blsp1_uart1_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp1_uart2_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP1_UART2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_uart1_2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp1_uart2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 32000000, NOMINAL, 64000000),
+ CLK_INIT(blsp1_uart2_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup1_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP1_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup1_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp2_qup1_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup1_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP1_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup1_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp2_qup1_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup2_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP2_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup2_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp2_qup2_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup2_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP2_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup2_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp2_qup2_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup3_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP3_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup3_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp2_qup3_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup3_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP3_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup3_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp2_qup3_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup4_i2c_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP4_I2C_APPS_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup4_i2c_apps_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 50000000),
+ CLK_INIT(blsp2_qup4_i2c_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_qup4_spi_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_QUP4_SPI_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_qup4_spi_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 25000000, NOMINAL, 50000000),
+ CLK_INIT(blsp2_qup4_spi_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_uart1_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_UART1_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_uart1_2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_uart1_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 32000000, NOMINAL, 64000000),
+ CLK_INIT(blsp2_uart1_apps_clk_src.c),
+ },
+};
+
+static struct rcg_clk blsp2_uart2_apps_clk_src = {
+ .cmd_rcgr_reg = BLSP2_UART2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_blsp1_2_uart1_2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "blsp2_uart2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 32000000, NOMINAL, 64000000),
+ CLK_INIT(blsp2_uart2_apps_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_cci_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F( 37500000, gpll0_out_aux, 1, 3, 64),
+ F_END
+};
+
+static struct rcg_clk cci_clk_src = {
+ .cmd_rcgr_reg = CCI_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_cci_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "cci_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 19200000, LOW, 37500000),
+ CLK_INIT(cci_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_cpp_clk[] = {
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 180000000, gpll6, 6, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_cpp_clk_8937[] = {
+ F( 133333333, gpll0, 6, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266666667, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_cpp_clk_8917[] = {
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F( 360000000, gpll6, 3, 0, 0),
+ F_END
+};
+
+static struct rcg_clk cpp_clk_src = {
+ .cmd_rcgr_reg = CPP_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_cpp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "cpp_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP5(LOWER, 133330000, LOW, 180000000,
+ NOMINAL, 266670000, NOM_PLUS, 308570000,
+ HIGH, 320000000),
+ CLK_INIT(cpp_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_gp0_1_clk[] = {
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F_END
+};
+
+static struct rcg_clk camss_gp0_clk_src = {
+ .cmd_rcgr_reg = MM_GP0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_gp0_1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "camss_gp0_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(camss_gp0_clk_src.c),
+ },
+};
+
+static struct rcg_clk camss_gp1_clk_src = {
+ .cmd_rcgr_reg = MM_GP1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_gp0_1_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "camss_gp1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(camss_gp1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_jpeg0_clk[] = {
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_jpeg0_clk_8937[] = {
+ F( 133333333, gpll0, 6, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266666667, gpll0, 3, 0, 0),
+ F( 308570000, gpll6, 3.5, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk jpeg0_clk_src = {
+ .cmd_rcgr_reg = JPEG0_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_jpeg0_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "jpeg0_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 133330000, NOMINAL, 266670000,
+ HIGH, 320000000),
+ CLK_INIT(jpeg0_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_mclk0_2_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F( 24000000, gpll6, 1, 1, 45),
+ F( 66670000, gpll0, 12, 0, 0),
+ F_END
+};
+
+static struct rcg_clk mclk0_clk_src = {
+ .cmd_rcgr_reg = MCLK0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_mclk0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "mclk0_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 66670000),
+ CLK_INIT(mclk0_clk_src.c),
+ },
+};
+
+static struct rcg_clk mclk1_clk_src = {
+ .cmd_rcgr_reg = MCLK1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_mclk0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "mclk1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 66670000),
+ CLK_INIT(mclk1_clk_src.c),
+ },
+};
+
+static struct rcg_clk mclk2_clk_src = {
+ .cmd_rcgr_reg = MCLK2_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_camss_mclk0_2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "mclk2_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 66670000),
+ CLK_INIT(mclk2_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_csi0_1phytimer_clk[] = {
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_camss_csi0_1phytimer_clk_8917[] = {
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F_END
+};
+
+static struct rcg_clk csi0phytimer_clk_src = {
+ .cmd_rcgr_reg = CSI0PHYTIMER_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_csi0_1phytimer_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "csi0phytimer_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(csi0phytimer_clk_src.c),
+ },
+};
+
+static struct rcg_clk csi1phytimer_clk_src = {
+ .cmd_rcgr_reg = CSI1PHYTIMER_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_camss_csi0_1phytimer_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "csi1phytimer_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 100000000, LOW, 160000000,
+ NOMINAL, 200000000),
+ CLK_INIT(csi1phytimer_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_crypto_clk[] = {
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk crypto_clk_src = {
+ .cmd_rcgr_reg = CRYPTO_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_crypto_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "crypto_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP2(LOWER, 80000000, NOMINAL, 160000000),
+ CLK_INIT(crypto_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_gp1_3_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk gp1_clk_src = {
+ .cmd_rcgr_reg = GP1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp1_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 19200000),
+ CLK_INIT(gp1_clk_src.c),
+ },
+};
+
+static struct rcg_clk gp2_clk_src = {
+ .cmd_rcgr_reg = GP2_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp2_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOW, 19200000),
+ CLK_INIT(gp2_clk_src.c),
+ },
+};
+
+static struct rcg_clk gp3_clk_src = {
+ .cmd_rcgr_reg = GP3_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gp3_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOW, 19200000),
+ CLK_INIT(gp3_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_byte0_clk[] = {
+ {
+ .div_src_val = BVAL(10, 8, dsi0_phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_byte0_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_byte0_clk_8937[] = {
+ {
+ .div_src_val = BVAL(10, 8, xo_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &xo_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi0_0phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_byte0_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi1_0phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_byte1_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct rcg_clk byte0_clk_src = {
+ .cmd_rcgr_reg = BYTE0_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .current_freq = ftbl_gcc_mdss_byte0_clk,
+ .freq_tbl = ftbl_gcc_mdss_byte0_clk,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "byte0_clk_src",
+ .ops = &clk_ops_byte_multiparent,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ VDD_DIG_FMAX_MAP2(LOWER, 120000000, NOMINAL, 187500000),
+ CLK_INIT(byte0_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_byte1_clk[] = {
+ {
+ .div_src_val = BVAL(10, 8, xo_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &xo_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi1_1phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_byte1_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi0_1phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_byte0_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct rcg_clk byte1_clk_src = {
+ .cmd_rcgr_reg = BYTE1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .current_freq = ftbl_gcc_mdss_byte1_clk,
+ .freq_tbl = ftbl_gcc_mdss_byte1_clk,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "byte1_clk_src",
+ .ops = &clk_ops_byte_multiparent,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ VDD_DIG_FMAX_MAP2(LOWER, 125000000, NOMINAL, 187500000),
+ CLK_INIT(byte1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_esc0_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk esc0_clk_src = {
+ .cmd_rcgr_reg = ESC0_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_mdss_esc0_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "esc0_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 19200000),
+ CLK_INIT(esc0_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_esc1_clk_src[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk esc1_clk_src = {
+ .cmd_rcgr_reg = ESC1_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_esc1_clk_src,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "esc1_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 19200000),
+ CLK_INIT(esc1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_mdp_clk[] = {
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 145450000, gpll0, 5.5, 0, 0),
+ F( 160000000, gpll0, 5, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 266670000, gpll0, 3, 0, 0),
+ F( 320000000, gpll0, 2.5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk mdp_clk_src = {
+ .cmd_rcgr_reg = MDP_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_mdss_mdp_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "mdp_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 160000000, NOMINAL, 266670000,
+ HIGH, 320000000),
+ CLK_INIT(mdp_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_pclk0_clk[] = {
+ {
+ .div_src_val = BVAL(10, 8, dsi0_phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_pclk0_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_pclk0_clk_8937[] = {
+ {
+ .div_src_val = BVAL(10, 8, xo_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &xo_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi0_0phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_pclk0_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi1_0phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_pclk1_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct rcg_clk pclk0_clk_src = {
+ .cmd_rcgr_reg = PCLK0_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .current_freq = ftbl_gcc_mdss_pclk0_clk,
+ .freq_tbl = ftbl_gcc_mdss_pclk0_clk,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "pclk0_clk_src",
+ .ops = &clk_ops_pixel_multiparent,
+ VDD_DIG_FMAX_MAP2(LOWER, 160000000, NOMINAL, 250000000),
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(pclk0_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_pclk1_clk[] = {
+ {
+ .div_src_val = BVAL(10, 8, xo_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &xo_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi1_1phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_pclk1_clk_src.c,
+ .freq_hz = 0,
+ },
+ {
+ .div_src_val = BVAL(10, 8, dsi0_1phypll_source_val)
+ | BVAL(4, 0, 0),
+ .src_clk = &ext_pclk0_clk_src.c,
+ .freq_hz = 0,
+ },
+ F_END
+};
+
+static struct rcg_clk pclk1_clk_src = {
+ .cmd_rcgr_reg = PCLK1_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .current_freq = ftbl_gcc_mdss_pclk1_clk,
+ .freq_tbl = ftbl_gcc_mdss_pclk1_clk,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "pclk1_clk_src",
+ .ops = &clk_ops_pixel_multiparent,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ VDD_DIG_FMAX_MAP2(LOWER, 166670000, NOMINAL, 250000000),
+ CLK_INIT(pclk1_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_mdss_vsync_clk[] = {
+ F( 19200000, xo, 1, 0, 0),
+ F_END
+};
+
+static struct rcg_clk vsync_clk_src = {
+ .cmd_rcgr_reg = VSYNC_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_mdss_vsync_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "vsync_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 19200000),
+ CLK_INIT(vsync_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_pdm2_clk[] = {
+ F( 64000000, gpll0, 12.5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk pdm2_clk_src = {
+ .cmd_rcgr_reg = PDM2_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_pdm2_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "pdm2_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP1(LOWER, 64000000),
+ CLK_INIT(pdm2_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc1_apps_clk[] = {
+ F( 144000, xo, 16, 3, 25),
+ F( 400000, xo, 12, 1, 4),
+ F( 20000000, gpll0, 10, 1, 4),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 177770000, gpll0, 4.5, 0, 0),
+ F( 192000000, gpll4, 6, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F( 384000000, gpll4, 3, 0, 0),
+ F_END
+};
+
+static struct rcg_clk sdcc1_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC1_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc1_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc1_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 50000000, NOMINAL, 384000000),
+ CLK_INIT(sdcc1_apps_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc1_ice_core_clk[] = {
+ F( 100000000, gpll0_out_main, 8, 0, 0),
+ F( 200000000, gpll0_out_main, 4, 0, 0),
+ F_END
+};
+
+static struct rcg_clk sdcc1_ice_core_clk_src = {
+ .cmd_rcgr_reg = SDCC1_ICE_CORE_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc1_ice_core_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 100000000, NOMINAL, 200000000),
+ CLK_INIT(sdcc1_ice_core_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc2_apps_clk[] = {
+ F( 144000, xo, 16, 3, 25),
+ F( 400000, xo, 12, 1, 4),
+ F( 20000000, gpll0, 10, 1, 4),
+ F( 25000000, gpll0, 16, 1, 2),
+ F( 50000000, gpll0, 16, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 177770000, gpll0, 4.5, 0, 0),
+ F( 200000000, gpll0, 4, 0, 0),
+ F_END
+};
+
+static struct rcg_clk sdcc2_apps_clk_src = {
+ .cmd_rcgr_reg = SDCC2_APPS_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_sdcc2_apps_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "sdcc2_apps_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP2(LOWER, 100000000, NOMINAL, 200000000),
+ CLK_INIT(sdcc2_apps_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_fs_ic_clk[] = {
+ F( 60000000, gpll6_out_main, 9, 1, 2),
+ F_END
+};
+
+static struct rcg_clk usb_fs_ic_clk_src = {
+ .cmd_rcgr_reg = USB_FS_IC_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_usb_fs_ic_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_fs_ic_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 60000000),
+ CLK_INIT(usb_fs_ic_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_fs_system_clk[] = {
+ F( 64000000, gpll0_out_aux, 12.5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_fs_system_clk_src = {
+ .cmd_rcgr_reg = USB_FS_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_mnd,
+ .freq_tbl = ftbl_gcc_usb_fs_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_fs_system_clk_src",
+ .ops = &clk_ops_rcg_mnd,
+ VDD_DIG_FMAX_MAP1(LOWER, 64000000),
+ CLK_INIT(usb_fs_system_clk_src.c),
+ },
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
+ F( 57140000, gpll0, 14, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk_8917[] = {
+ F( 80000000, gpll0, 10, 0, 0),
+ F( 100000000, gpll0, 8, 0, 0),
+ F( 133330000, gpll0, 6, 0, 0),
+ F( 177780000, gpll0, 4.5, 0, 0),
+ F_END
+};
+
+static struct rcg_clk usb_hs_system_clk_src = {
+ .cmd_rcgr_reg = USB_HS_SYSTEM_CMD_RCGR,
+ .set_rate = set_rate_hid,
+ .freq_tbl = ftbl_gcc_usb_hs_system_clk,
+ .current_freq = &rcg_dummy_freq,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "usb_hs_system_clk_src",
+ .ops = &clk_ops_rcg,
+ VDD_DIG_FMAX_MAP3(LOWER, 57140000, NOMINAL, 133330000,
+ HIGH, 177780000),
+ CLK_INIT(usb_hs_system_clk_src.c),
+ },
+};
+
+static struct branch_clk gcc_bimc_gpu_clk = {
+ .cbcr_reg = BIMC_GPU_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_bimc_gpu_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_bimc_gpu_clk.c),
+ },
+};
+
+static struct branch_clk gcc_dcc_clk = {
+ .cbcr_reg = DCC_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_dcc_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_dcc_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_blsp1_ahb_clk = {
+ .cbcr_reg = BLSP1_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(10),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_blsp1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
+ .parent = &blsp1_qup1_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
+ .parent = &blsp1_qup1_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
+ .parent = &blsp1_qup2_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
+ .parent = &blsp1_qup2_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
+ .parent = &blsp1_qup3_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
+ .parent = &blsp1_qup3_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
+ .parent = &blsp1_qup4_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
+ .cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
+ .parent = &blsp1_qup4_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart1_apps_clk = {
+ .cbcr_reg = BLSP1_UART1_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart1_apps_clk",
+ .parent = &blsp1_uart1_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp1_uart2_apps_clk = {
+ .cbcr_reg = BLSP1_UART2_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp1_uart2_apps_clk",
+ .parent = &blsp1_uart2_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_blsp2_ahb_clk = {
+ .cbcr_reg = BLSP2_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(20),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_blsp2_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup1_i2c_apps_clk = {
+ .cbcr_reg = BLSP2_QUP1_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup1_i2c_apps_clk",
+ .parent = &blsp2_qup1_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup1_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup1_spi_apps_clk = {
+ .cbcr_reg = BLSP2_QUP1_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup1_spi_apps_clk",
+ .parent = &blsp2_qup1_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup1_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup2_i2c_apps_clk = {
+ .cbcr_reg = BLSP2_QUP2_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup2_i2c_apps_clk",
+ .parent = &blsp2_qup2_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup2_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup2_spi_apps_clk = {
+ .cbcr_reg = BLSP2_QUP2_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup2_spi_apps_clk",
+ .parent = &blsp2_qup2_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup2_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup3_i2c_apps_clk = {
+ .cbcr_reg = BLSP2_QUP3_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup3_i2c_apps_clk",
+ .parent = &blsp2_qup3_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup3_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup3_spi_apps_clk = {
+ .cbcr_reg = BLSP2_QUP3_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup3_spi_apps_clk",
+ .parent = &blsp2_qup3_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup3_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup4_i2c_apps_clk = {
+ .cbcr_reg = BLSP2_QUP4_I2C_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup4_i2c_apps_clk",
+ .parent = &blsp2_qup4_i2c_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup4_i2c_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_qup4_spi_apps_clk = {
+ .cbcr_reg = BLSP2_QUP4_SPI_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_qup4_spi_apps_clk",
+ .parent = &blsp2_qup4_spi_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_qup4_spi_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_uart1_apps_clk = {
+ .cbcr_reg = BLSP2_UART1_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_uart1_apps_clk",
+ .parent = &blsp2_uart1_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_uart1_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_blsp2_uart2_apps_clk = {
+ .cbcr_reg = BLSP2_UART2_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_blsp2_uart2_apps_clk",
+ .parent = &blsp2_uart2_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_blsp2_uart2_apps_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_boot_rom_ahb_clk = {
+ .cbcr_reg = BOOT_ROM_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(7),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_boot_rom_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_boot_rom_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_cci_ahb_clk = {
+ .cbcr_reg = CAMSS_CCI_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_cci_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_cci_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_cci_clk = {
+ .cbcr_reg = CAMSS_CCI_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_cci_clk",
+ .parent = &cci_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_cci_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_cpp_ahb_clk = {
+ .cbcr_reg = CAMSS_CPP_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_cpp_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_cpp_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_cpp_axi_clk = {
+ .cbcr_reg = CAMSS_CPP_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_cpp_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_cpp_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_cpp_clk = {
+ .cbcr_reg = CAMSS_CPP_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_cpp_clk",
+ .parent = &cpp_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_cpp_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0_ahb_clk = {
+ .cbcr_reg = CAMSS_CSI0_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0_clk = {
+ .cbcr_reg = CAMSS_CSI0_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0_clk",
+ .parent = &csi0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0phy_clk = {
+ .cbcr_reg = CAMSS_CSI0PHY_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0phy_clk",
+ .parent = &csi0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0phy_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0pix_clk = {
+ .cbcr_reg = CAMSS_CSI0PIX_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0pix_clk",
+ .parent = &csi0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0pix_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0rdi_clk = {
+ .cbcr_reg = CAMSS_CSI0RDI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0rdi_clk",
+ .parent = &csi0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0rdi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1_ahb_clk = {
+ .cbcr_reg = CAMSS_CSI1_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1_clk = {
+ .cbcr_reg = CAMSS_CSI1_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1_clk",
+ .parent = &csi1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1phy_clk = {
+ .cbcr_reg = CAMSS_CSI1PHY_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1phy_clk",
+ .parent = &csi1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1phy_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1pix_clk = {
+ .cbcr_reg = CAMSS_CSI1PIX_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1pix_clk",
+ .parent = &csi1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1pix_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1rdi_clk = {
+ .cbcr_reg = CAMSS_CSI1RDI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1rdi_clk",
+ .parent = &csi1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1rdi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi2_ahb_clk = {
+ .cbcr_reg = CAMSS_CSI2_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi2_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi2_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi2_clk = {
+ .cbcr_reg = CAMSS_CSI2_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi2_clk",
+ .parent = &csi2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi2phy_clk = {
+ .cbcr_reg = CAMSS_CSI2PHY_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi2phy_clk",
+ .parent = &csi2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi2phy_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi2pix_clk = {
+ .cbcr_reg = CAMSS_CSI2PIX_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi2pix_clk",
+ .parent = &csi2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi2pix_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi2rdi_clk = {
+ .cbcr_reg = CAMSS_CSI2RDI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi2rdi_clk",
+ .parent = &csi2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi2rdi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi_vfe0_clk = {
+ .cbcr_reg = CAMSS_CSI_VFE0_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi_vfe0_clk",
+ .parent = &vfe0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi_vfe0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi_vfe1_clk = {
+ .cbcr_reg = CAMSS_CSI_VFE1_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi_vfe1_clk",
+ .parent = &vfe1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi_vfe1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_gp0_clk = {
+ .cbcr_reg = CAMSS_GP0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_gp0_clk",
+ .parent = &camss_gp0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_gp0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_gp1_clk = {
+ .cbcr_reg = CAMSS_GP1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_gp1_clk",
+ .parent = &camss_gp1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_gp1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_ispif_ahb_clk = {
+ .cbcr_reg = CAMSS_ISPIF_AHB_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_ispif_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_ispif_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_jpeg0_clk = {
+ .cbcr_reg = CAMSS_JPEG0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_jpeg0_clk",
+ .parent = &jpeg0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_jpeg0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_jpeg_ahb_clk = {
+ .cbcr_reg = CAMSS_JPEG_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_jpeg_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_jpeg_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_jpeg_axi_clk = {
+ .cbcr_reg = CAMSS_JPEG_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_jpeg_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_jpeg_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_mclk0_clk = {
+ .cbcr_reg = CAMSS_MCLK0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_mclk0_clk",
+ .parent = &mclk0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_mclk0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_mclk1_clk = {
+ .cbcr_reg = CAMSS_MCLK1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_mclk1_clk",
+ .parent = &mclk1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_mclk1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_mclk2_clk = {
+ .cbcr_reg = CAMSS_MCLK2_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_mclk2_clk",
+ .parent = &mclk2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_mclk2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_micro_ahb_clk = {
+ .cbcr_reg = CAMSS_MICRO_AHB_CBCR,
+ .bcr_reg = CAMSS_MICRO_BCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_micro_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_micro_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi0phytimer_clk = {
+ .cbcr_reg = CAMSS_CSI0PHYTIMER_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi0phytimer_clk",
+ .parent = &csi0phytimer_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi0phytimer_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_csi1phytimer_clk = {
+ .cbcr_reg = CAMSS_CSI1PHYTIMER_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_csi1phytimer_clk",
+ .parent = &csi1phytimer_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_csi1phytimer_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_ahb_clk = {
+ .cbcr_reg = CAMSS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_top_ahb_clk = {
+ .cbcr_reg = CAMSS_TOP_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_top_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_top_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe0_clk = {
+ .cbcr_reg = CAMSS_VFE0_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe0_clk",
+ .parent = &vfe0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe_ahb_clk = {
+ .cbcr_reg = CAMSS_VFE_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe_axi_clk = {
+ .cbcr_reg = CAMSS_VFE_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe1_ahb_clk = {
+ .cbcr_reg = CAMSS_VFE1_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe1_ahb_clk",
+ .parent = &camss_top_ahb_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe1_axi_clk = {
+ .cbcr_reg = CAMSS_VFE1_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe1_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe1_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_camss_vfe1_clk = {
+ .cbcr_reg = CAMSS_VFE1_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_camss_vfe1_clk",
+ .parent = &vfe1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_camss_vfe1_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_crypto_ahb_clk = {
+ .cbcr_reg = CRYPTO_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(0),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_crypto_ahb_clk",
+ .parent = &crypto_clk_src.c,
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_crypto_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_crypto_axi_clk = {
+ .cbcr_reg = CRYPTO_AXI_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(1),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_crypto_axi_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_crypto_axi_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_crypto_clk = {
+ .cbcr_reg = CRYPTO_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(2),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_crypto_clk",
+ .parent = &crypto_clk_src.c,
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_crypto_clk.c),
+ },
+};
+
+static struct gate_clk gcc_oxili_gmem_clk = {
+ .en_reg = OXILI_GMEM_CBCR,
+ .en_mask = BIT(0),
+ .delay_us = 50,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_oxili_gmem_clk",
+ .parent = &gfx3d_clk_src.c,
+ .ops = &clk_ops_gate,
+ CLK_INIT(gcc_oxili_gmem_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp1_clk = {
+ .cbcr_reg = GP1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp1_clk",
+ .parent = &gp1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp2_clk = {
+ .cbcr_reg = GP2_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp2_clk",
+ .parent = &gp2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_gp3_clk = {
+ .cbcr_reg = GP3_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gp3_clk",
+ .parent = &gp3_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_gp3_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_ahb_clk = {
+ .cbcr_reg = MDSS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_axi_clk = {
+ .cbcr_reg = MDSS_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_byte0_clk = {
+ .cbcr_reg = MDSS_BYTE0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_byte0_clk",
+ .parent = &byte0_clk_src.c,
+ .ops = &clk_ops_branch,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(gcc_mdss_byte0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_byte1_clk = {
+ .cbcr_reg = MDSS_BYTE1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_byte1_clk",
+ .parent = &byte1_clk_src.c,
+ .ops = &clk_ops_branch,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(gcc_mdss_byte1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_esc0_clk = {
+ .cbcr_reg = MDSS_ESC0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_esc0_clk",
+ .parent = &esc0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_esc0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_esc1_clk = {
+ .cbcr_reg = MDSS_ESC1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_esc1_clk",
+ .parent = &esc1_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_esc1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_mdp_clk = {
+ .cbcr_reg = MDSS_MDP_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_mdp_clk",
+ .parent = &mdp_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_mdp_clk.c),
+ },
+};
+
+static DEFINE_CLK_VOTER(mdss_mdp_vote_clk, &gcc_mdss_mdp_clk.c, 0);
+static DEFINE_CLK_VOTER(mdss_rotator_vote_clk, &gcc_mdss_mdp_clk.c, 0);
+
+static struct branch_clk gcc_mdss_pclk0_clk = {
+ .cbcr_reg = MDSS_PCLK0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_pclk0_clk",
+ .parent = &pclk0_clk_src.c,
+ .ops = &clk_ops_branch,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(gcc_mdss_pclk0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_pclk1_clk = {
+ .cbcr_reg = MDSS_PCLK1_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_pclk1_clk",
+ .parent = &pclk1_clk_src.c,
+ .ops = &clk_ops_branch,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(gcc_mdss_pclk1_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mdss_vsync_clk = {
+ .cbcr_reg = MDSS_VSYNC_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdss_vsync_clk",
+ .parent = &vsync_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mdss_vsync_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mss_cfg_ahb_clk = {
+ .cbcr_reg = MSS_CFG_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mss_cfg_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mss_cfg_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_mss_q6_bimc_axi_clk = {
+ .cbcr_reg = MSS_Q6_BIMC_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mss_q6_bimc_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_mss_q6_bimc_axi_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_apss_tcu_clk;
+static struct branch_clk gcc_bimc_gfx_clk = {
+ .cbcr_reg = BIMC_GFX_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_bimc_gfx_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_bimc_gfx_clk.c),
+ .depends = &gcc_apss_tcu_clk.c,
+ },
+};
+
+static struct branch_clk gcc_oxili_ahb_clk = {
+ .cbcr_reg = OXILI_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_oxili_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_oxili_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_oxili_gfx3d_clk = {
+ .cbcr_reg = OXILI_GFX3D_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_oxili_gfx3d_clk",
+ .parent = &gfx3d_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_oxili_gfx3d_clk.c),
+ },
+};
+
+static struct branch_clk gcc_oxili_timer_clk = {
+ .cbcr_reg = OXILI_TIMER_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_oxili_timer_clk",
+ .parent = &xo_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_oxili_timer_clk.c),
+ },
+};
+
+static struct branch_clk gcc_oxili_aon_clk = {
+ .cbcr_reg = OXILI_AON_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_oxili_aon_clk",
+ .parent = &gfx3d_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_oxili_aon_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm2_clk = {
+ .cbcr_reg = PDM2_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm2_clk",
+ .parent = &pdm2_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm2_clk.c),
+ },
+};
+
+static struct branch_clk gcc_pdm_ahb_clk = {
+ .cbcr_reg = PDM_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_pdm_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_pdm_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_prng_ahb_clk = {
+ .cbcr_reg = PRNG_AHB_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(8),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_prng_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_prng_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_qdss_dap_clk = {
+ .cbcr_reg = QDSS_DAP_CBCR,
+ .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(21),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qdss_dap_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_qdss_dap_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc1_ahb_clk = {
+ .cbcr_reg = SDCC1_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc1_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc1_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc1_apps_clk = {
+ .cbcr_reg = SDCC1_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc1_apps_clk",
+ .parent = &sdcc1_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc1_apps_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc1_ice_core_clk = {
+ .cbcr_reg = SDCC1_ICE_CORE_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc1_ice_core_clk",
+ .parent = &sdcc1_ice_core_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc1_ice_core_clk.c),
+ }
+};
+
+static struct branch_clk gcc_sdcc2_ahb_clk = {
+ .cbcr_reg = SDCC2_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_sdcc2_apps_clk = {
+ .cbcr_reg = SDCC2_APPS_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_sdcc2_apps_clk",
+ .parent = &sdcc2_apps_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_sdcc2_apps_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_cpp_tbu_clk = {
+ .cbcr_reg = CPP_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(14),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_cpp_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_cpp_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_gfx_tbu_clk = {
+ .cbcr_reg = GFX_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(3),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gfx_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_gfx_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_gfx_tcu_clk = {
+ .cbcr_reg = GFX_TCU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(2),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gfx_tcu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_gfx_tcu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_apss_tcu_clk = {
+ .cbcr_reg = APSS_TCU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(1),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_apss_tcu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_apss_tcu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_gtcu_ahb_clk = {
+ .cbcr_reg = GTCU_AHB_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(13),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_gtcu_ahb_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_gtcu_ahb_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_ipa_tbu_clk = {
+ .cbcr_reg = IPA_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(16),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_ipa_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_ipa_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_jpeg_tbu_clk = {
+ .cbcr_reg = JPEG_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(10),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_jpeg_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_jpeg_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_mdp_tbu_clk = {
+ .cbcr_reg = MDP_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(4),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_mdp_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_mdp_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_smmu_cfg_clk = {
+ .cbcr_reg = SMMU_CFG_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(12),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_smmu_cfg_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_smmu_cfg_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_venus_tbu_clk = {
+ .cbcr_reg = VENUS_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(5),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_venus_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_vfe1_tbu_clk = {
+ .cbcr_reg = VFE1_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(17),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_vfe1_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_vfe1_tbu_clk.c),
+ },
+};
+
+static struct local_vote_clk gcc_vfe_tbu_clk = {
+ .cbcr_reg = VFE_TBU_CBCR,
+ .vote_reg = APCS_SMMU_CLOCK_BRANCH_ENA_VOTE,
+ .en_mask = BIT(9),
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_vfe_tbu_clk",
+ .ops = &clk_ops_vote,
+ CLK_INIT(gcc_vfe_tbu_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb2a_phy_sleep_clk = {
+ .cbcr_reg = USB2A_PHY_SLEEP_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb2a_phy_sleep_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb2a_phy_sleep_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_phy_cfg_ahb_clk = {
+ .cbcr_reg = USB_HS_PHY_CFG_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_phy_cfg_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_phy_cfg_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_fs_ahb_clk = {
+ .cbcr_reg = USB_FS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_fs_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_fs_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_fs_ic_clk = {
+ .cbcr_reg = USB_FS_IC_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_fs_ic_clk",
+ .parent = &usb_fs_ic_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_fs_ic_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_fs_system_clk = {
+ .cbcr_reg = USB_FS_SYSTEM_CBCR,
+ .bcr_reg = USB_FS_BCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_fs_system_clk",
+ .parent = &usb_fs_system_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_fs_system_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_ahb_clk = {
+ .cbcr_reg = USB_HS_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_usb_hs_system_clk = {
+ .cbcr_reg = USB_HS_SYSTEM_CBCR,
+ .bcr_reg = USB_HS_BCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb_hs_system_clk",
+ .parent = &usb_hs_system_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_usb_hs_system_clk.c),
+ },
+};
+
+static struct reset_clk gcc_usb2_hs_phy_only_clk = {
+ .reset_reg = USB2_HS_PHY_ONLY_BCR,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_usb2_hs_phy_only_clk",
+ .ops = &clk_ops_rst,
+ CLK_INIT(gcc_usb2_hs_phy_only_clk.c),
+ },
+};
+
+static struct reset_clk gcc_qusb2_phy_clk = {
+ .reset_reg = QUSB2_PHY_BCR,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_qusb2_phy_clk",
+ .ops = &clk_ops_rst,
+ CLK_INIT(gcc_qusb2_phy_clk.c),
+ },
+};
+
+static struct branch_clk gcc_venus0_ahb_clk = {
+ .cbcr_reg = VENUS0_AHB_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus0_ahb_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_venus0_ahb_clk.c),
+ },
+};
+
+static struct branch_clk gcc_venus0_axi_clk = {
+ .cbcr_reg = VENUS0_AXI_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus0_axi_clk",
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_venus0_axi_clk.c),
+ },
+};
+
+static struct branch_clk gcc_venus0_core0_vcodec0_clk = {
+ .cbcr_reg = VENUS0_CORE0_VCODEC0_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus0_core0_vcodec0_clk",
+ .parent = &vcodec0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_venus0_core0_vcodec0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_venus0_core1_vcodec0_clk = {
+ .cbcr_reg = VENUS0_CORE1_VCODEC0_CBCR,
+ .has_sibling = 1,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus0_core1_vcodec0_clk",
+ .parent = &vcodec0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_venus0_core1_vcodec0_clk.c),
+ },
+};
+
+static struct branch_clk gcc_venus0_vcodec0_clk = {
+ .cbcr_reg = VENUS0_VCODEC0_CBCR,
+ .has_sibling = 0,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .dbg_name = "gcc_venus0_vcodec0_clk",
+ .parent = &vcodec0_clk_src.c,
+ .ops = &clk_ops_branch,
+ CLK_INIT(gcc_venus0_vcodec0_clk.c),
+ },
+};
+
+static struct clk_ops clk_ops_debug_mux;
+
+static void __iomem *meas_base;
+
+static struct measure_clk apc0_m_clk = {
+ .c = {
+ .ops = &clk_ops_empty,
+ .dbg_name = "apc0_m_clk",
+ CLK_INIT(apc0_m_clk.c),
+ },
+};
+
+static struct measure_clk apc1_m_clk = {
+ .c = {
+ .ops = &clk_ops_empty,
+ .dbg_name = "apc1_m_clk",
+ CLK_INIT(apc1_m_clk.c),
+ },
+};
+
+static struct measure_clk cci_m_clk = {
+ .c = {
+ .ops = &clk_ops_empty,
+ .dbg_name = "cci_m_clk",
+ CLK_INIT(cci_m_clk.c),
+ },
+};
+
+static struct mux_clk apss_debug_ter_mux = {
+ .ops = &mux_reg_ops,
+ .mask = 0x3,
+ .shift = 8,
+ MUX_SRC_LIST(
+ {&apc0_m_clk.c, 0},
+ {&apc1_m_clk.c, 1},
+ {&cci_m_clk.c, 2},
+ ),
+ .base = &meas_base,
+ .c = {
+ .dbg_name = "apss_debug_ter_mux",
+ .ops = &clk_ops_gen_mux,
+ CLK_INIT(apss_debug_ter_mux.c),
+ },
+};
+
+static struct mux_clk apss_debug_sec_mux = {
+ .ops = &mux_reg_ops,
+ .mask = 0x7,
+ .shift = 12,
+ MUX_SRC_LIST(
+ {&apss_debug_ter_mux.c, 0},
+ ),
+ MUX_REC_SRC_LIST(
+ &apss_debug_ter_mux.c,
+ ),
+ .base = &meas_base,
+ .c = {
+ .dbg_name = "apss_debug_sec_mux",
+ .ops = &clk_ops_gen_mux,
+ CLK_INIT(apss_debug_sec_mux.c),
+ },
+};
+
+static struct mux_clk apss_debug_pri_mux = {
+ .ops = &mux_reg_ops,
+ .mask = 0x3,
+ .shift = 16,
+ MUX_SRC_LIST(
+ {&apss_debug_sec_mux.c, 0},
+ ),
+ MUX_REC_SRC_LIST(
+ &apss_debug_sec_mux.c,
+ ),
+ .base = &meas_base,
+ .c = {
+ .dbg_name = "apss_debug_pri_mux",
+ .ops = &clk_ops_gen_mux,
+ CLK_INIT(apss_debug_pri_mux.c),
+ },
+};
+
+static struct measure_clk_data debug_mux_priv = {
+ .cxo = &xo_clk_src.c,
+ .plltest_reg = GCC_PLLTEST_PAD_CFG,
+ .plltest_val = 0x51A00,
+ .xo_div4_cbcr = GCC_XO_DIV4_CBCR,
+ .ctl_reg = CLOCK_FRQ_MEASURE_CTL,
+ .status_reg = CLOCK_FRQ_MEASURE_STATUS,
+ .base = &virt_bases[GCC_BASE],
+};
+
+static struct mux_clk gcc_debug_mux = {
+ .priv = &debug_mux_priv,
+ .ops = &mux_reg_ops,
+ .offset = GCC_DEBUG_CLK_CTL,
+ .mask = 0x1FF,
+ .en_offset = GCC_DEBUG_CLK_CTL,
+ .en_mask = BIT(16),
+ .base = &virt_bases[GCC_BASE],
+ MUX_REC_SRC_LIST(
+ &apss_debug_pri_mux.c,
+ ),
+ MUX_SRC_LIST(
+ { &apss_debug_pri_mux.c, 0x016A},
+ { &snoc_clk.c, 0x0000 },
+ { &sysmmnoc_clk.c, 0x0001 },
+ { &pnoc_clk.c, 0x0008 },
+ { &bimc_clk.c, 0x0154 },
+ { &gcc_gp1_clk.c, 0x0010 },
+ { &gcc_gp2_clk.c, 0x0011 },
+ { &gcc_gp3_clk.c, 0x0012 },
+ { &gcc_bimc_gfx_clk.c, 0x002d },
+ { &gcc_mss_cfg_ahb_clk.c, 0x0030 },
+ { &gcc_mss_q6_bimc_axi_clk.c, 0x0031 },
+ { &gcc_apss_tcu_clk.c, 0x0050 },
+ { &gcc_mdp_tbu_clk.c, 0x0051 },
+ { &gcc_gfx_tbu_clk.c, 0x0052 },
+ { &gcc_gfx_tcu_clk.c, 0x0053 },
+ { &gcc_venus_tbu_clk.c, 0x0054 },
+ { &gcc_gtcu_ahb_clk.c, 0x0058 },
+ { &gcc_vfe_tbu_clk.c, 0x005a },
+ { &gcc_smmu_cfg_clk.c, 0x005b },
+ { &gcc_jpeg_tbu_clk.c, 0x005c },
+ { &gcc_usb_hs_system_clk.c, 0x0060 },
+ { &gcc_usb_hs_ahb_clk.c, 0x0061 },
+ { &gcc_usb2a_phy_sleep_clk.c, 0x0063 },
+ { &gcc_usb_hs_phy_cfg_ahb_clk.c, 0x0064 },
+ { &gcc_sdcc1_apps_clk.c, 0x0068 },
+ { &gcc_sdcc1_ahb_clk.c, 0x0069 },
+ { &gcc_sdcc1_ice_core_clk.c, 0x006a },
+ { &gcc_sdcc2_apps_clk.c, 0x0070 },
+ { &gcc_sdcc2_ahb_clk.c, 0x0071 },
+ { &gcc_blsp1_ahb_clk.c, 0x0088 },
+ { &gcc_blsp1_qup1_spi_apps_clk.c, 0x008a },
+ { &gcc_blsp1_qup1_i2c_apps_clk.c, 0x008b },
+ { &gcc_blsp1_uart1_apps_clk.c, 0x008c },
+ { &gcc_blsp1_qup2_spi_apps_clk.c, 0x008e },
+ { &gcc_blsp1_qup2_i2c_apps_clk.c, 0x0090 },
+ { &gcc_blsp1_uart2_apps_clk.c, 0x0091 },
+ { &gcc_blsp1_qup3_spi_apps_clk.c, 0x0093 },
+ { &gcc_blsp1_qup3_i2c_apps_clk.c, 0x0094 },
+ { &gcc_blsp1_qup4_spi_apps_clk.c, 0x0095 },
+ { &gcc_blsp1_qup4_i2c_apps_clk.c, 0x0096 },
+ { &gcc_blsp2_ahb_clk.c, 0x0098 },
+ { &gcc_blsp2_qup1_spi_apps_clk.c, 0x009a },
+ { &gcc_blsp2_qup1_i2c_apps_clk.c, 0x009b },
+ { &gcc_blsp2_uart1_apps_clk.c, 0x009c },
+ { &gcc_blsp2_qup2_spi_apps_clk.c, 0x009e },
+ { &gcc_blsp2_qup2_i2c_apps_clk.c, 0x00a0 },
+ { &gcc_blsp2_uart2_apps_clk.c, 0x00a1 },
+ { &gcc_blsp2_qup3_spi_apps_clk.c, 0x00a3 },
+ { &gcc_blsp2_qup3_i2c_apps_clk.c, 0x00a4 },
+ { &gcc_blsp2_qup4_spi_apps_clk.c, 0x00a5 },
+ { &gcc_blsp2_qup4_i2c_apps_clk.c, 0x00a6 },
+ { &gcc_camss_ahb_clk.c, 0x00a8 },
+ { &gcc_camss_top_ahb_clk.c, 0x00a9 },
+ { &gcc_camss_micro_ahb_clk.c, 0x00aa },
+ { &gcc_camss_gp0_clk.c, 0x00ab },
+ { &gcc_camss_gp1_clk.c, 0x00ac },
+ { &gcc_camss_mclk0_clk.c, 0x00ad },
+ { &gcc_camss_mclk1_clk.c, 0x00ae },
+ { &gcc_camss_cci_clk.c, 0x00af },
+ { &gcc_camss_cci_ahb_clk.c, 0x00b0 },
+ { &gcc_camss_csi0phytimer_clk.c, 0x00b1 },
+ { &gcc_camss_csi1phytimer_clk.c, 0x00b2 },
+ { &gcc_camss_jpeg0_clk.c, 0x00b3 },
+ { &gcc_camss_jpeg_ahb_clk.c, 0x00b4 },
+ { &gcc_camss_jpeg_axi_clk.c, 0x00b5 },
+ { &gcc_camss_vfe0_clk.c, 0x00b8 },
+ { &gcc_camss_cpp_clk.c, 0x00b9 },
+ { &gcc_camss_cpp_ahb_clk.c, 0x00ba },
+ { &gcc_camss_vfe_ahb_clk.c, 0x00bb },
+ { &gcc_camss_vfe_axi_clk.c, 0x00bc },
+ { &gcc_camss_csi_vfe0_clk.c, 0x00bf },
+ { &gcc_camss_csi0_clk.c, 0x00c0 },
+ { &gcc_camss_csi0_ahb_clk.c, 0x00c1 },
+ { &gcc_camss_csi0phy_clk.c, 0x00c2 },
+ { &gcc_camss_csi0rdi_clk.c, 0x00c3 },
+ { &gcc_camss_csi0pix_clk.c, 0x00c4 },
+ { &gcc_camss_csi1_clk.c, 0x00c5 },
+ { &gcc_camss_csi1_ahb_clk.c, 0x00c6 },
+ { &gcc_camss_csi1phy_clk.c, 0x00c7 },
+ { &gcc_pdm_ahb_clk.c, 0x00d0 },
+ { &gcc_pdm2_clk.c, 0x00d2 },
+ { &gcc_prng_ahb_clk.c, 0x00d8 },
+ { &gcc_camss_csi1rdi_clk.c, 0x00e0 },
+ { &gcc_camss_csi1pix_clk.c, 0x00e1 },
+ { &gcc_camss_ispif_ahb_clk.c, 0x00e2 },
+ { &gcc_camss_csi2_clk.c, 0x00e3 },
+ { &gcc_camss_csi2_ahb_clk.c, 0x00e4 },
+ { &gcc_camss_csi2phy_clk.c, 0x00e5 },
+ { &gcc_camss_csi2rdi_clk.c, 0x00e6 },
+ { &gcc_camss_csi2pix_clk.c, 0x00e7 },
+ { &gcc_cpp_tbu_clk.c, 0x00e9 },
+ { &gcc_boot_rom_ahb_clk.c, 0x00f8 },
+ { &gcc_usb_fs_ahb_clk.c, 0x00f1 },
+ { &gcc_usb_fs_ic_clk.c, 0x00f4 },
+ { &gcc_crypto_clk.c, 0x0138 },
+ { &gcc_crypto_axi_clk.c, 0x0139 },
+ { &gcc_crypto_ahb_clk.c, 0x013a },
+ { &gcc_bimc_gpu_clk.c, 0x0157 },
+ { &gcc_ipa_tbu_clk.c, 0x0198 },
+ { &gcc_vfe1_tbu_clk.c, 0x0199 },
+ { &gcc_camss_csi_vfe1_clk.c, 0x01a0 },
+ { &gcc_camss_vfe1_clk.c, 0x01a1 },
+ { &gcc_camss_vfe1_ahb_clk.c, 0x01a2 },
+ { &gcc_camss_vfe1_axi_clk.c, 0x01a3 },
+ { &gcc_venus0_core0_vcodec0_clk.c, 0x01b8 },
+ { &gcc_venus0_core1_vcodec0_clk.c, 0x01b9 },
+ { &gcc_camss_mclk2_clk.c, 0x01bd },
+ { &gcc_oxili_timer_clk.c, 0x01e9 },
+ { &gcc_oxili_gfx3d_clk.c, 0x01ea },
+ { &gcc_oxili_ahb_clk.c, 0x01eb },
+ { &gcc_oxili_gmem_clk.c, 0x01f0 },
+ { &gcc_venus0_vcodec0_clk.c, 0x01f1 },
+ { &gcc_venus0_axi_clk.c, 0x01f2 },
+ { &gcc_venus0_ahb_clk.c, 0x01f3 },
+ { &gcc_mdss_ahb_clk.c, 0x01f6 },
+ { &gcc_mdss_axi_clk.c, 0x01f7 },
+ { &gcc_mdss_pclk0_clk.c, 0x01f8 },
+ { &gcc_mdss_mdp_clk.c, 0x01f9 },
+ { &gcc_mdss_vsync_clk.c, 0x01fb },
+ { &gcc_mdss_byte0_clk.c, 0x01fc },
+ { &gcc_mdss_esc0_clk.c, 0x01fd },
+ { &wcnss_m_clk.c, 0x0ec },
+ ),
+ .c = {
+ .dbg_name = "gcc_debug_mux",
+ .ops = &clk_ops_debug_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE | CLKFLAG_MEASURE,
+ CLK_INIT(gcc_debug_mux.c),
+ },
+};
+
+static struct mux_clk gcc_debug_mux_8937 = {
+ .priv = &debug_mux_priv,
+ .ops = &mux_reg_ops,
+ .offset = GCC_DEBUG_CLK_CTL,
+ .mask = 0x1FF,
+ .en_offset = GCC_DEBUG_CLK_CTL,
+ .en_mask = BIT(16),
+ .base = &virt_bases[GCC_BASE],
+ MUX_REC_SRC_LIST(
+ &apss_debug_pri_mux.c,
+ ),
+ MUX_SRC_LIST(
+ { &apss_debug_pri_mux.c, 0x016A},
+ { &snoc_clk.c, 0x0000 },
+ { &sysmmnoc_clk.c, 0x0001 },
+ { &pnoc_clk.c, 0x0008 },
+ { &bimc_clk.c, 0x015A },
+ { &ipa_clk.c, 0x1B0 },
+ { &gcc_gp1_clk.c, 0x0010 },
+ { &gcc_gp2_clk.c, 0x0011 },
+ { &gcc_gp3_clk.c, 0x0012 },
+ { &gcc_bimc_gfx_clk.c, 0x002d },
+ { &gcc_mss_cfg_ahb_clk.c, 0x0030 },
+ { &gcc_mss_q6_bimc_axi_clk.c, 0x0031 },
+ { &gcc_qdss_dap_clk.c, 0x0049 },
+ { &gcc_apss_tcu_clk.c, 0x0050 },
+ { &gcc_mdp_tbu_clk.c, 0x0051 },
+ { &gcc_gfx_tbu_clk.c, 0x0052 },
+ { &gcc_gfx_tcu_clk.c, 0x0053 },
+ { &gcc_venus_tbu_clk.c, 0x0054 },
+ { &gcc_gtcu_ahb_clk.c, 0x0058 },
+ { &gcc_vfe_tbu_clk.c, 0x005a },
+ { &gcc_smmu_cfg_clk.c, 0x005b },
+ { &gcc_jpeg_tbu_clk.c, 0x005c },
+ { &gcc_usb_hs_system_clk.c, 0x0060 },
+ { &gcc_usb_hs_ahb_clk.c, 0x0061 },
+ { &gcc_usb2a_phy_sleep_clk.c, 0x0063 },
+ { &gcc_usb_hs_phy_cfg_ahb_clk.c, 0x0064 },
+ { &gcc_sdcc1_apps_clk.c, 0x0068 },
+ { &gcc_sdcc1_ahb_clk.c, 0x0069 },
+ { &gcc_sdcc1_ice_core_clk.c, 0x006a },
+ { &gcc_sdcc2_apps_clk.c, 0x0070 },
+ { &gcc_sdcc2_ahb_clk.c, 0x0071 },
+ { &gcc_blsp1_ahb_clk.c, 0x0088 },
+ { &gcc_blsp1_qup1_spi_apps_clk.c, 0x008a },
+ { &gcc_blsp1_qup1_i2c_apps_clk.c, 0x008b },
+ { &gcc_blsp1_uart1_apps_clk.c, 0x008c },
+ { &gcc_blsp1_qup2_spi_apps_clk.c, 0x008e },
+ { &gcc_blsp1_qup2_i2c_apps_clk.c, 0x0090 },
+ { &gcc_blsp1_uart2_apps_clk.c, 0x0091 },
+ { &gcc_blsp1_qup3_spi_apps_clk.c, 0x0093 },
+ { &gcc_blsp1_qup3_i2c_apps_clk.c, 0x0094 },
+ { &gcc_blsp1_qup4_spi_apps_clk.c, 0x0095 },
+ { &gcc_blsp1_qup4_i2c_apps_clk.c, 0x0096 },
+ { &gcc_blsp2_ahb_clk.c, 0x0098 },
+ { &gcc_blsp2_qup1_spi_apps_clk.c, 0x009a },
+ { &gcc_blsp2_qup1_i2c_apps_clk.c, 0x009b },
+ { &gcc_blsp2_uart1_apps_clk.c, 0x009c },
+ { &gcc_blsp2_qup2_spi_apps_clk.c, 0x009e },
+ { &gcc_blsp2_qup2_i2c_apps_clk.c, 0x00a0 },
+ { &gcc_blsp2_uart2_apps_clk.c, 0x00a1 },
+ { &gcc_blsp2_qup3_spi_apps_clk.c, 0x00a3 },
+ { &gcc_blsp2_qup3_i2c_apps_clk.c, 0x00a4 },
+ { &gcc_blsp2_qup4_spi_apps_clk.c, 0x00a5 },
+ { &gcc_blsp2_qup4_i2c_apps_clk.c, 0x00a6 },
+ { &gcc_camss_ahb_clk.c, 0x00a8 },
+ { &gcc_camss_top_ahb_clk.c, 0x00a9 },
+ { &gcc_camss_micro_ahb_clk.c, 0x00aa },
+ { &gcc_camss_gp0_clk.c, 0x00ab },
+ { &gcc_camss_gp1_clk.c, 0x00ac },
+ { &gcc_camss_mclk0_clk.c, 0x00ad },
+ { &gcc_camss_mclk1_clk.c, 0x00ae },
+ { &gcc_camss_cci_clk.c, 0x00af },
+ { &gcc_camss_cci_ahb_clk.c, 0x00b0 },
+ { &gcc_camss_csi0phytimer_clk.c, 0x00b1 },
+ { &gcc_camss_csi1phytimer_clk.c, 0x00b2 },
+ { &gcc_camss_jpeg0_clk.c, 0x00b3 },
+ { &gcc_camss_jpeg_ahb_clk.c, 0x00b4 },
+ { &gcc_camss_jpeg_axi_clk.c, 0x00b5 },
+ { &gcc_camss_vfe0_clk.c, 0x00b8 },
+ { &gcc_camss_cpp_clk.c, 0x00b9 },
+ { &gcc_camss_cpp_ahb_clk.c, 0x00ba },
+ { &gcc_camss_vfe_ahb_clk.c, 0x00bb },
+ { &gcc_camss_vfe_axi_clk.c, 0x00bc },
+ { &gcc_camss_csi_vfe0_clk.c, 0x00bf },
+ { &gcc_camss_csi0_clk.c, 0x00c0 },
+ { &gcc_camss_csi0_ahb_clk.c, 0x00c1 },
+ { &gcc_camss_csi0phy_clk.c, 0x00c2 },
+ { &gcc_camss_csi0rdi_clk.c, 0x00c3 },
+ { &gcc_camss_csi0pix_clk.c, 0x00c4 },
+ { &gcc_camss_csi1_clk.c, 0x00c5 },
+ { &gcc_camss_csi1_ahb_clk.c, 0x00c6 },
+ { &gcc_camss_csi1phy_clk.c, 0x00c7 },
+ { &gcc_pdm_ahb_clk.c, 0x00d0 },
+ { &gcc_pdm2_clk.c, 0x00d2 },
+ { &gcc_prng_ahb_clk.c, 0x00d8 },
+ { &gcc_camss_csi1rdi_clk.c, 0x00e0 },
+ { &gcc_camss_csi1pix_clk.c, 0x00e1 },
+ { &gcc_camss_ispif_ahb_clk.c, 0x00e2 },
+ { &gcc_camss_csi2_clk.c, 0x00e3 },
+ { &gcc_camss_csi2_ahb_clk.c, 0x00e4 },
+ { &gcc_camss_csi2phy_clk.c, 0x00e5 },
+ { &gcc_camss_csi2rdi_clk.c, 0x00e6 },
+ { &gcc_camss_csi2pix_clk.c, 0x00e7 },
+ { &gcc_cpp_tbu_clk.c, 0x00e9 },
+ { &gcc_boot_rom_ahb_clk.c, 0x00f8 },
+ { &gcc_crypto_clk.c, 0x0138 },
+ { &gcc_crypto_axi_clk.c, 0x0139 },
+ { &gcc_crypto_ahb_clk.c, 0x013a },
+ { &gcc_bimc_gpu_clk.c, 0x0157 },
+ { &gcc_ipa_tbu_clk.c, 0x0198 },
+ { &gcc_vfe1_tbu_clk.c, 0x0199 },
+ { &gcc_camss_csi_vfe1_clk.c, 0x01a0 },
+ { &gcc_camss_vfe1_clk.c, 0x01a1 },
+ { &gcc_camss_vfe1_ahb_clk.c, 0x01a2 },
+ { &gcc_camss_vfe1_axi_clk.c, 0x01a3 },
+ { &gcc_camss_cpp_axi_clk.c, 0x01a4 },
+ { &gcc_venus0_core0_vcodec0_clk.c, 0x01b8 },
+ { &gcc_dcc_clk.c, 0x01b9 },
+ { &gcc_camss_mclk2_clk.c, 0x01bd },
+ { &gcc_oxili_timer_clk.c, 0x01e9 },
+ { &gcc_oxili_gfx3d_clk.c, 0x01ea },
+ { &gcc_oxili_aon_clk.c, 0x00ee },
+ { &gcc_oxili_ahb_clk.c, 0x01eb },
+ { &gcc_venus0_vcodec0_clk.c, 0x01f1 },
+ { &gcc_venus0_axi_clk.c, 0x01f2 },
+ { &gcc_venus0_ahb_clk.c, 0x01f3 },
+ { &gcc_mdss_ahb_clk.c, 0x01f6 },
+ { &gcc_mdss_axi_clk.c, 0x01f7 },
+ { &gcc_mdss_pclk0_clk.c, 0x01f8 },
+ { &gcc_mdss_pclk1_clk.c, 0x01e3 },
+ { &gcc_mdss_mdp_clk.c, 0x01f9 },
+ { &gcc_mdss_vsync_clk.c, 0x01fb },
+ { &gcc_mdss_byte0_clk.c, 0x01fc },
+ { &gcc_mdss_byte1_clk.c, 0x01e4 },
+ { &gcc_mdss_esc0_clk.c, 0x01fd },
+ { &gcc_mdss_esc1_clk.c, 0x01e5 },
+ { &wcnss_m_clk.c, 0x0ec },
+ ),
+ .c = {
+ .dbg_name = "gcc_debug_mux_8937",
+ .ops = &clk_ops_debug_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE | CLKFLAG_MEASURE,
+ CLK_INIT(gcc_debug_mux_8937.c),
+ },
+};
+
+/* Clock lookup */
+static struct clk_lookup msm_clocks_lookup_common[] = {
+ /* RPM clocks */
+ CLK_LIST(xo_clk_src),
+ CLK_LIST(xo_a_clk_src),
+ CLK_LIST(xo_otg_clk),
+ CLK_LIST(xo_lpm_clk),
+ CLK_LIST(xo_pil_mss_clk),
+ CLK_LIST(xo_pil_pronto_clk),
+ CLK_LIST(xo_wlan_clk),
+ CLK_LIST(xo_pil_lpass_clk),
+
+ CLK_LIST(sysmmnoc_msmbus_clk),
+ CLK_LIST(snoc_msmbus_clk),
+ CLK_LIST(snoc_msmbus_a_clk),
+ CLK_LIST(sysmmnoc_msmbus_a_clk),
+ CLK_LIST(pnoc_msmbus_clk),
+ CLK_LIST(pnoc_msmbus_a_clk),
+ CLK_LIST(bimc_msmbus_clk),
+ CLK_LIST(bimc_msmbus_a_clk),
+ CLK_LIST(pnoc_keepalive_a_clk),
+
+ CLK_LIST(pnoc_usb_a_clk),
+ CLK_LIST(snoc_usb_a_clk),
+ CLK_LIST(bimc_usb_a_clk),
+ CLK_LIST(pnoc_usb_clk),
+ CLK_LIST(snoc_usb_clk),
+ CLK_LIST(bimc_usb_clk),
+
+ CLK_LIST(snoc_wcnss_a_clk),
+ CLK_LIST(bimc_wcnss_a_clk),
+
+ CLK_LIST(qdss_clk),
+ CLK_LIST(qdss_a_clk),
+
+ CLK_LIST(snoc_clk),
+ CLK_LIST(sysmmnoc_clk),
+ CLK_LIST(pnoc_clk),
+ CLK_LIST(bimc_clk),
+ CLK_LIST(snoc_a_clk),
+ CLK_LIST(sysmmnoc_a_clk),
+ CLK_LIST(pnoc_a_clk),
+ CLK_LIST(bimc_a_clk),
+
+ CLK_LIST(bb_clk1),
+ CLK_LIST(bb_clk1_a),
+ CLK_LIST(bb_clk2),
+ CLK_LIST(bb_clk2_a),
+ CLK_LIST(rf_clk2),
+ CLK_LIST(rf_clk2_a),
+ CLK_LIST(div_clk2),
+ CLK_LIST(div_clk2_a),
+
+ CLK_LIST(bb_clk1_pin),
+ CLK_LIST(bb_clk1_a_pin),
+ CLK_LIST(bb_clk2_pin),
+ CLK_LIST(bb_clk2_a_pin),
+
+ CLK_LIST(gpll0_clk_src),
+ CLK_LIST(gpll0_ao_clk_src),
+ CLK_LIST(gpll6_clk_src),
+ CLK_LIST(gpll4_clk_src),
+ CLK_LIST(gpll3_clk_src),
+ CLK_LIST(a53ss_c1_pll),
+ CLK_LIST(gcc_blsp1_ahb_clk),
+ CLK_LIST(gcc_blsp2_ahb_clk),
+ CLK_LIST(gcc_boot_rom_ahb_clk),
+ CLK_LIST(gcc_crypto_ahb_clk),
+ CLK_LIST(gcc_crypto_axi_clk),
+ CLK_LIST(gcc_crypto_clk),
+ CLK_LIST(gcc_prng_ahb_clk),
+ CLK_LIST(gcc_cpp_tbu_clk),
+ CLK_LIST(gcc_apss_tcu_clk),
+ CLK_LIST(gcc_jpeg_tbu_clk),
+ CLK_LIST(gcc_mdp_tbu_clk),
+ CLK_LIST(gcc_smmu_cfg_clk),
+ CLK_LIST(gcc_venus_tbu_clk),
+ CLK_LIST(gcc_vfe1_tbu_clk),
+ CLK_LIST(gcc_vfe_tbu_clk),
+ CLK_LIST(camss_top_ahb_clk_src),
+ CLK_LIST(apss_ahb_clk_src),
+ CLK_LIST(csi0_clk_src),
+ CLK_LIST(csi1_clk_src),
+ CLK_LIST(csi2_clk_src),
+ CLK_LIST(vcodec0_clk_src),
+ CLK_LIST(vfe0_clk_src),
+ CLK_LIST(vfe1_clk_src),
+ CLK_LIST(gfx3d_clk_src),
+ CLK_LIST(blsp1_qup2_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup2_spi_apps_clk_src),
+ CLK_LIST(blsp1_qup3_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup3_spi_apps_clk_src),
+ CLK_LIST(blsp1_qup4_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup4_spi_apps_clk_src),
+ CLK_LIST(blsp1_uart1_apps_clk_src),
+ CLK_LIST(blsp1_uart2_apps_clk_src),
+ CLK_LIST(blsp2_qup1_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup1_spi_apps_clk_src),
+ CLK_LIST(blsp2_qup2_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup2_spi_apps_clk_src),
+ CLK_LIST(blsp2_qup3_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup3_spi_apps_clk_src),
+ CLK_LIST(blsp2_uart1_apps_clk_src),
+ CLK_LIST(blsp2_uart2_apps_clk_src),
+ CLK_LIST(cci_clk_src),
+ CLK_LIST(cpp_clk_src),
+ CLK_LIST(camss_gp0_clk_src),
+ CLK_LIST(camss_gp1_clk_src),
+ CLK_LIST(jpeg0_clk_src),
+ CLK_LIST(mclk0_clk_src),
+ CLK_LIST(mclk1_clk_src),
+ CLK_LIST(mclk2_clk_src),
+ CLK_LIST(csi0phytimer_clk_src),
+ CLK_LIST(csi1phytimer_clk_src),
+ CLK_LIST(crypto_clk_src),
+ CLK_LIST(gp1_clk_src),
+ CLK_LIST(gp2_clk_src),
+ CLK_LIST(gp3_clk_src),
+ CLK_LIST(esc0_clk_src),
+ CLK_LIST(mdp_clk_src),
+ CLK_LIST(vsync_clk_src),
+ CLK_LIST(pdm2_clk_src),
+ CLK_LIST(sdcc1_apps_clk_src),
+ CLK_LIST(sdcc1_ice_core_clk_src),
+ CLK_LIST(sdcc2_apps_clk_src),
+ CLK_LIST(usb_hs_system_clk_src),
+ CLK_LIST(gcc_bimc_gpu_clk),
+ CLK_LIST(gcc_blsp1_qup2_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup2_spi_apps_clk),
+ CLK_LIST(gcc_blsp1_qup3_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup3_spi_apps_clk),
+ CLK_LIST(gcc_blsp1_qup4_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup4_spi_apps_clk),
+ CLK_LIST(gcc_blsp1_uart1_apps_clk),
+ CLK_LIST(gcc_blsp1_uart2_apps_clk),
+ CLK_LIST(gcc_blsp2_qup1_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup1_spi_apps_clk),
+ CLK_LIST(gcc_blsp2_qup2_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup2_spi_apps_clk),
+ CLK_LIST(gcc_blsp2_qup3_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup3_spi_apps_clk),
+ CLK_LIST(gcc_blsp2_uart1_apps_clk),
+ CLK_LIST(gcc_blsp2_uart2_apps_clk),
+ CLK_LIST(gcc_camss_cci_ahb_clk),
+ CLK_LIST(gcc_camss_cci_clk),
+ CLK_LIST(gcc_camss_cpp_ahb_clk),
+ CLK_LIST(gcc_camss_cpp_axi_clk),
+ CLK_LIST(gcc_camss_cpp_clk),
+ CLK_LIST(gcc_camss_csi0_ahb_clk),
+ CLK_LIST(gcc_camss_csi0_clk),
+ CLK_LIST(gcc_camss_csi0phy_clk),
+ CLK_LIST(gcc_camss_csi0pix_clk),
+ CLK_LIST(gcc_camss_csi0rdi_clk),
+ CLK_LIST(gcc_camss_csi1_ahb_clk),
+ CLK_LIST(gcc_camss_csi1_clk),
+ CLK_LIST(gcc_camss_csi1phy_clk),
+ CLK_LIST(gcc_camss_csi1pix_clk),
+ CLK_LIST(gcc_camss_csi1rdi_clk),
+ CLK_LIST(gcc_camss_csi2_ahb_clk),
+ CLK_LIST(gcc_camss_csi2_clk),
+ CLK_LIST(gcc_camss_csi2phy_clk),
+ CLK_LIST(gcc_camss_csi2pix_clk),
+ CLK_LIST(gcc_camss_csi2rdi_clk),
+ CLK_LIST(gcc_camss_csi_vfe0_clk),
+ CLK_LIST(gcc_camss_csi_vfe1_clk),
+ CLK_LIST(gcc_camss_gp0_clk),
+ CLK_LIST(gcc_camss_gp1_clk),
+ CLK_LIST(gcc_camss_ispif_ahb_clk),
+ CLK_LIST(gcc_camss_jpeg0_clk),
+ CLK_LIST(gcc_camss_jpeg_ahb_clk),
+ CLK_LIST(gcc_camss_jpeg_axi_clk),
+ CLK_LIST(gcc_camss_mclk0_clk),
+ CLK_LIST(gcc_camss_mclk1_clk),
+ CLK_LIST(gcc_camss_mclk2_clk),
+ CLK_LIST(gcc_camss_micro_ahb_clk),
+ CLK_LIST(gcc_camss_csi0phytimer_clk),
+ CLK_LIST(gcc_camss_csi1phytimer_clk),
+ CLK_LIST(gcc_camss_ahb_clk),
+ CLK_LIST(gcc_camss_top_ahb_clk),
+ CLK_LIST(gcc_camss_vfe0_clk),
+ CLK_LIST(gcc_camss_vfe_ahb_clk),
+ CLK_LIST(gcc_camss_vfe_axi_clk),
+ CLK_LIST(gcc_camss_vfe1_ahb_clk),
+ CLK_LIST(gcc_camss_vfe1_axi_clk),
+ CLK_LIST(gcc_camss_vfe1_clk),
+ CLK_LIST(gcc_gp1_clk),
+ CLK_LIST(gcc_gp2_clk),
+ CLK_LIST(gcc_gp3_clk),
+ CLK_LIST(gcc_mdss_ahb_clk),
+ CLK_LIST(gcc_mdss_axi_clk),
+ CLK_LIST(gcc_mdss_esc0_clk),
+ CLK_LIST(gcc_mdss_mdp_clk),
+ CLK_LIST(gcc_mdss_vsync_clk),
+ CLK_LIST(gcc_mss_cfg_ahb_clk),
+ CLK_LIST(gcc_mss_q6_bimc_axi_clk),
+ CLK_LIST(gcc_bimc_gfx_clk),
+ CLK_LIST(gcc_oxili_ahb_clk),
+ CLK_LIST(gcc_oxili_gfx3d_clk),
+ CLK_LIST(gcc_pdm2_clk),
+ CLK_LIST(gcc_pdm_ahb_clk),
+ CLK_LIST(gcc_sdcc1_ahb_clk),
+ CLK_LIST(gcc_sdcc1_apps_clk),
+ CLK_LIST(gcc_sdcc1_ice_core_clk),
+ CLK_LIST(gcc_sdcc2_ahb_clk),
+ CLK_LIST(gcc_sdcc2_apps_clk),
+ CLK_LIST(gcc_usb2a_phy_sleep_clk),
+ CLK_LIST(gcc_usb_hs_phy_cfg_ahb_clk),
+ CLK_LIST(gcc_usb_hs_ahb_clk),
+ CLK_LIST(gcc_usb_hs_system_clk),
+ CLK_LIST(gcc_venus0_ahb_clk),
+ CLK_LIST(gcc_venus0_axi_clk),
+ CLK_LIST(gcc_venus0_core0_vcodec0_clk),
+ CLK_LIST(gcc_venus0_vcodec0_clk),
+
+ /* Reset clks */
+ CLK_LIST(gcc_usb2_hs_phy_only_clk),
+ CLK_LIST(gcc_qusb2_phy_clk),
+
+ /* WCNSS Debug */
+ CLK_LIST(wcnss_m_clk),
+};
+
+static struct clk_lookup msm_clocks_lookup_8952[] = {
+ CLK_LIST(a53ss_c0_pll),
+ CLK_LIST(a53ss_cci_pll),
+ CLK_LIST(ipa_clk),
+ CLK_LIST(ipa_a_clk),
+ CLK_LIST(gpll0_clk_src_8952),
+ CLK_LIST(gpll0_ao_clk_src_8952),
+ CLK_LIST(gcc_oxili_gmem_clk),
+ CLK_LIST(usb_fs_ic_clk_src),
+ CLK_LIST(gcc_usb_fs_ic_clk),
+ CLK_LIST(usb_fs_system_clk_src),
+ CLK_LIST(gcc_usb_fs_system_clk),
+ CLK_LIST(gcc_gfx_tcu_clk),
+ CLK_LIST(gcc_gfx_tbu_clk),
+ CLK_LIST(gcc_gtcu_ahb_clk),
+ CLK_LIST(gcc_ipa_tbu_clk),
+ CLK_LIST(gcc_usb_fs_ahb_clk),
+ CLK_LIST(gcc_venus0_core1_vcodec0_clk),
+ CLK_LIST(blsp1_qup1_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup1_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp1_qup1_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup1_spi_apps_clk),
+ CLK_LIST(blsp2_qup4_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup4_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp2_qup4_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup4_spi_apps_clk),
+ CLK_LIST(gcc_oxili_timer_clk),
+};
+
+static struct clk_lookup msm_clocks_lookup_8937[] = {
+ CLK_LIST(gpll0_clk_src_8937),
+ CLK_LIST(gpll0_ao_clk_src_8937),
+ CLK_LIST(gpll0_sleep_clk_src),
+ CLK_LIST(a53ss_c0_pll),
+ CLK_LIST(esc1_clk_src),
+ CLK_LIST(gcc_mdss_esc1_clk),
+ CLK_LIST(gcc_dcc_clk),
+ CLK_LIST(gcc_oxili_aon_clk),
+ CLK_LIST(gcc_qdss_dap_clk),
+ CLK_LIST(blsp1_qup1_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup1_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp1_qup1_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup1_spi_apps_clk),
+ CLK_LIST(blsp2_qup4_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup4_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp2_qup4_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup4_spi_apps_clk),
+ CLK_LIST(gcc_oxili_timer_clk),
+};
+
+static struct clk_lookup msm_clocks_lookup_8917[] = {
+ CLK_LIST(gpll0_clk_src_8937),
+ CLK_LIST(gpll0_ao_clk_src_8937),
+ CLK_LIST(gpll0_sleep_clk_src),
+ CLK_LIST(bimc_gpu_clk),
+ CLK_LIST(bimc_gpu_a_clk),
+ CLK_LIST(gcc_dcc_clk),
+ CLK_LIST(gcc_qdss_dap_clk),
+ CLK_LIST(gcc_gfx_tcu_clk),
+ CLK_LIST(gcc_gfx_tbu_clk),
+ CLK_LIST(gcc_gtcu_ahb_clk),
+};
+
+static struct clk_lookup msm_clocks_lookup_8920[] = {
+ CLK_LIST(gpll0_clk_src_8937),
+ CLK_LIST(gpll0_ao_clk_src_8937),
+ CLK_LIST(gpll0_sleep_clk_src),
+ CLK_LIST(bimc_gpu_clk),
+ CLK_LIST(bimc_gpu_a_clk),
+ CLK_LIST(gcc_dcc_clk),
+ CLK_LIST(gcc_qdss_dap_clk),
+ CLK_LIST(gcc_gfx_tcu_clk),
+ CLK_LIST(gcc_gfx_tbu_clk),
+ CLK_LIST(gcc_gtcu_ahb_clk),
+ CLK_LIST(ipa_clk),
+ CLK_LIST(ipa_a_clk),
+ CLK_LIST(gcc_ipa_tbu_clk),
+};
+
+static struct clk_lookup msm_clocks_lookup_8940[] = {
+ CLK_LIST(gpll0_clk_src_8937),
+ CLK_LIST(gpll0_ao_clk_src_8937),
+ CLK_LIST(gpll0_sleep_clk_src),
+ CLK_LIST(a53ss_c0_pll),
+ CLK_LIST(esc1_clk_src),
+ CLK_LIST(gcc_mdss_esc1_clk),
+ CLK_LIST(gcc_dcc_clk),
+ CLK_LIST(gcc_oxili_aon_clk),
+ CLK_LIST(gcc_qdss_dap_clk),
+ CLK_LIST(blsp1_qup1_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup1_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp1_qup1_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup1_spi_apps_clk),
+ CLK_LIST(blsp2_qup4_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup4_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp2_qup4_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup4_spi_apps_clk),
+ CLK_LIST(gcc_oxili_timer_clk),
+ CLK_LIST(ipa_clk),
+ CLK_LIST(ipa_a_clk),
+ CLK_LIST(gcc_ipa_tbu_clk),
+};
+
+/* Please note that the order of reg-names is important */
+static int get_mmio_addr(struct platform_device *pdev, u32 nbases)
+{
+ int i, count;
+ const char *str;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+
+ count = of_property_count_strings(dev->of_node, "reg-names");
+ if (count < nbases) {
+ dev_err(dev, "missing reg-names property, expected %d strings\n",
+ nbases);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nbases; i++) {
+ of_property_read_string_index(dev->of_node, "reg-names", i,
+ &str);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, str);
+ if (!res) {
+ dev_err(dev, "Unable to retrieve register base.\n");
+ return -ENOMEM;
+ }
+
+ virt_bases[i] = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (!virt_bases[i]) {
+ dev_err(dev, "Failed to map in CC registers.\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static const struct msm_reset_map gcc_8952_resets[] = {
+ [GCC_CAMSS_MICRO_BCR] = {0x56008},
+ [GCC_USB_FS_BCR] = {0x3F000},
+ [GCC_USB_HS_BCR] = {0x41000},
+ [GCC_USB2_HS_PHY_ONLY_BCR] = {0x41034},
+ [GCC_QUSB2_PHY_BCR] = {0x4103C},
+};
+
+static void override_for_8917(int speed_bin)
+{
+ gpll3_clk_src.c.rate = 930000000;
+
+ OVERRIDE_FMAX3(csi0,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(csi0, ftbl_gcc_camss_csi0_2_clk, 8937);
+ OVERRIDE_FMAX3(csi1,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(csi1, ftbl_gcc_camss_csi0_2_clk, 8937);
+ OVERRIDE_FMAX3(csi2,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(csi2, ftbl_gcc_camss_csi0_2_clk, 8937);
+
+ OVERRIDE_FMAX5(vfe0,
+ LOWER, 160000000, LOW, 266670000, NOMINAL, 320000000,
+ NOM_PLUS, 329140000, HIGH, 360000000);
+ OVERRIDE_FTABLE(vfe0, ftbl_gcc_camss_vfe0_1_clk, 8917);
+ OVERRIDE_FMAX5(vfe1,
+ LOWER, 160000000, LOW, 266670000, NOMINAL, 320000000,
+ NOM_PLUS, 329140000, HIGH, 360000000);
+ OVERRIDE_FTABLE(vfe1, ftbl_gcc_camss_vfe0_1_clk, 8917);
+ OVERRIDE_FTABLE(vcodec0, ftbl_gcc_venus0_vcodec0_clk, 8917);
+ OVERRIDE_FMAX5(vcodec0,
+ LOWER, 200000000, LOW, 270000000, NOMINAL, 308570000,
+ NOM_PLUS, 329140000, HIGH, 360000000);
+ OVERRIDE_FMAX4(cpp,
+ LOWER, 160000000, LOW, 266670000, NOMINAL, 320000000,
+ NOM_PLUS, 360000000);
+ OVERRIDE_FTABLE(cpp, ftbl_gcc_camss_cpp_clk, 8917);
+ OVERRIDE_FMAX5(jpeg0,
+ LOWER, 133330000, LOW, 200000000, NOMINAL, 266670000,
+ NOM_PLUS, 308570000, HIGH, 320000000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(jpeg0, ftbl_gcc_camss_jpeg0_clk, 8937);
+
+ if (speed_bin) {
+ OVERRIDE_FMAX5(gfx3d,
+ LOWER, 270000000, LOW, 400000000, NOMINAL, 484800000,
+ NOM_PLUS, 523200000, HIGH, 650000000);
+ OVERRIDE_FTABLE(gfx3d, ftbl_gcc_oxili_gfx3d_clk, 8917_650MHz);
+ } else {
+ OVERRIDE_FMAX5(gfx3d,
+ LOWER, 270000000, LOW, 400000000, NOMINAL, 484800000,
+ NOM_PLUS, 523200000, HIGH, 598000000);
+ OVERRIDE_FTABLE(gfx3d, ftbl_gcc_oxili_gfx3d_clk, 8917);
+ }
+
+ OVERRIDE_FMAX1(cci, LOWER, 37500000);
+
+ OVERRIDE_FTABLE(csi0phytimer, ftbl_gcc_camss_csi0_1phytimer_clk, 8917);
+ OVERRIDE_FMAX3(csi0phytimer, LOWER, 100000000, LOW, 200000000,
+ NOMINAL, 266670000);
+ OVERRIDE_FTABLE(csi1phytimer, ftbl_gcc_camss_csi0_1phytimer_clk, 8917);
+ OVERRIDE_FMAX3(csi1phytimer, LOWER, 100000000, LOW, 200000000,
+ NOMINAL, 266670000);
+ OVERRIDE_FMAX2(gp1, LOWER, 100000000, NOMINAL, 200000000);
+ OVERRIDE_FMAX2(gp2, LOWER, 100000000, NOMINAL, 200000000);
+ OVERRIDE_FMAX2(gp3, LOWER, 100000000, NOMINAL, 200000000);
+
+ OVERRIDE_FMAX2(byte0, LOWER, 125000000, NOMINAL, 187500000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(byte0, ftbl_gcc_mdss_byte0_clk, 8937);
+ byte0_clk_src.current_freq = ftbl_gcc_mdss_pclk0_clk_8937;
+
+ OVERRIDE_FMAX2(pclk0, LOWER, 166670000, NOMINAL, 250000000);
+ /* Frequency Table same as 8937 */
+ OVERRIDE_FTABLE(pclk0, ftbl_gcc_mdss_pclk0_clk, 8937);
+ pclk0_clk_src.current_freq = ftbl_gcc_mdss_pclk0_clk_8937;
+
+ OVERRIDE_FMAX2(sdcc1_apps, LOWER, 200000000, NOMINAL, 400000000);
+ OVERRIDE_FTABLE(usb_hs_system, ftbl_gcc_usb_hs_system_clk, 8917);
+ OVERRIDE_FMAX3(usb_hs_system, LOWER, 800000000, NOMINAL, 133333000,
+ HIGH, 177780000);
+}
+
+static void override_for_8937(int speed_bin)
+{
+ gpll3_clk_src.c.rate = 900000000;
+ gpll3_clk_src.vco_tbl = p_vco_8937;
+ gpll3_clk_src.num_vco = ARRAY_SIZE(p_vco_8937);
+ OVERRIDE_FMAX2(gpll3, LOW, 800000000, NOMINAL, 1066000000);
+
+ OVERRIDE_FMAX1(cci, LOWER, 37500000);
+ OVERRIDE_FMAX3(csi0,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ OVERRIDE_FTABLE(csi0, ftbl_gcc_camss_csi0_2_clk, 8937);
+ OVERRIDE_FMAX3(csi1,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ OVERRIDE_FTABLE(csi1, ftbl_gcc_camss_csi0_2_clk, 8937);
+ OVERRIDE_FMAX3(csi2,
+ LOWER, 100000000, LOW, 200000000, NOMINAL, 266670000);
+ OVERRIDE_FTABLE(csi2, ftbl_gcc_camss_csi0_2_clk, 8937);
+ OVERRIDE_FMAX4(vfe0,
+ LOWER, 160000000, LOW, 308570000, NOMINAL, 400000000,
+ NOM_PLUS, 432000000);
+ OVERRIDE_FTABLE(vfe0, ftbl_gcc_camss_vfe0_1_clk, 8937);
+ OVERRIDE_FMAX4(vfe1,
+ LOWER, 160000000, LOW, 308570000, NOMINAL, 400000000,
+ NOM_PLUS, 432000000);
+ OVERRIDE_FTABLE(vfe1, ftbl_gcc_camss_vfe0_1_clk, 8937);
+
+ if (speed_bin) {
+ OVERRIDE_FMAX6(gfx3d,
+ LOWER, 216000000, LOW, 300000000,
+ NOMINAL, 375000000, NOM_PLUS, 400000000,
+ HIGH, 450000000, SUPER_TUR, 475000000);
+ OVERRIDE_FTABLE(gfx3d, ftbl_gcc_oxili_gfx3d_clk, 8937_475MHz);
+ } else {
+ OVERRIDE_FMAX5(gfx3d,
+ LOWER, 216000000, LOW, 300000000,
+ NOMINAL, 375000000, NOM_PLUS, 400000000,
+ HIGH, 450000000);
+ OVERRIDE_FTABLE(gfx3d, ftbl_gcc_oxili_gfx3d_clk, 8937);
+ }
+
+ OVERRIDE_FMAX5(cpp,
+ LOWER, 160000000, LOW, 266670000, NOMINAL, 320000000,
+ NOM_PLUS, 342860000, HIGH, 360000000);
+ OVERRIDE_FTABLE(cpp, ftbl_gcc_camss_cpp_clk, 8937);
+ OVERRIDE_FMAX5(jpeg0,
+ LOWER, 133330000, LOW, 200000000, NOMINAL, 266670000,
+ NOM_PLUS, 308570000, HIGH, 320000000);
+ OVERRIDE_FTABLE(jpeg0, ftbl_gcc_camss_jpeg0_clk, 8937);
+ OVERRIDE_FMAX2(csi0phytimer, LOWER, 100000000, LOW, 200000000);
+ OVERRIDE_FMAX2(csi1phytimer, LOWER, 100000000, LOW, 200000000);
+ OVERRIDE_FMAX2(gp1, LOWER, 100000000, NOMINAL, 200000000);
+ OVERRIDE_FMAX2(gp2, LOWER, 100000000, NOMINAL, 200000000);
+ OVERRIDE_FMAX2(gp3, LOWER, 100000000, NOMINAL, 200000000);
+ OVERRIDE_FMAX2(byte0, LOWER, 125000000, NOMINAL, 187500000);
+ OVERRIDE_FTABLE(byte0, ftbl_gcc_mdss_byte0_clk, 8937);
+ OVERRIDE_FMAX2(byte1, LOWER, 125000000, NOMINAL, 187500000);
+ byte0_clk_src.current_freq = ftbl_gcc_mdss_byte0_clk_8937;
+ OVERRIDE_FMAX2(pclk0, LOWER, 166670000, NOMINAL, 250000000);
+ OVERRIDE_FTABLE(pclk0, ftbl_gcc_mdss_pclk0_clk, 8937);
+ OVERRIDE_FMAX2(pclk1, LOWER, 166670000, NOMINAL, 250000000);
+ pclk0_clk_src.current_freq = ftbl_gcc_mdss_pclk0_clk_8937;
+ OVERRIDE_FTABLE(vcodec0, ftbl_gcc_venus0_vcodec0_clk, 8937);
+ OVERRIDE_FMAX5(vcodec0,
+ LOWER, 166150000, LOW, 240000000, NOMINAL, 308570000,
+ NOM_PLUS, 320000000, HIGH, 360000000);
+ OVERRIDE_FMAX2(sdcc1_apps, LOWER, 100000000,
+ NOMINAL, 400000000);
+}
+
+static void get_speed_bin(struct platform_device *pdev, int *bin)
+{
+ struct resource *res;
+ void __iomem *base;
+ u32 config_efuse;
+
+ *bin = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
+ if (!res) {
+ dev_info(&pdev->dev,
+ "No GPU speed binning available. Defaulting to 0.\n");
+ return;
+ }
+
+ base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!base) {
+ dev_warn(&pdev->dev,
+ "Unable to ioremap efuse reg address. Defaulting to 0.\n");
+ return;
+ }
+
+ config_efuse = readl_relaxed(base);
+ devm_iounmap(&pdev->dev, base);
+ *bin = (config_efuse >> 31) & 0x1;
+
+ dev_info(&pdev->dev, "GPU speed bin: %d\n", *bin);
+}
+
+static int msm_gcc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+ int speed_bin;
+ u32 regval, nbases = N_BASES;
+ bool compat_bin = false;
+ bool compat_bin2 = false;
+ bool compat_bin3 = false;
+ bool compat_bin4 = false;
+
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-8937");
+
+ compat_bin2 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-8917");
+
+ compat_bin3 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-8940");
+
+ compat_bin4 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-8920");
+
+ ret = vote_bimc(&bimc_clk, INT_MAX);
+ if (ret < 0)
+ return ret;
+
+ if (compat_bin2 || compat_bin4)
+ nbases = APCS_C0_PLL_BASE;
+
+ ret = get_mmio_addr(pdev, nbases);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
+ if (!res) {
+ dev_err(&pdev->dev, "Register base not defined\n");
+ return -ENOMEM;
+ }
+
+ virt_bases[GCC_BASE] = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!virt_bases[GCC_BASE]) {
+ dev_err(&pdev->dev, "Failed to ioremap CC registers\n");
+ return -ENOMEM;
+ }
+
+ vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig");
+ if (IS_ERR(vdd_dig.regulator[0])) {
+ if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_dig regulator!!!\n");
+ return PTR_ERR(vdd_dig.regulator[0]);
+ }
+
+ if (!compat_bin2 && !compat_bin4) {
+ vdd_sr2_pll.regulator[0] = devm_regulator_get(&pdev->dev,
+ "vdd_sr2_pll");
+ if (IS_ERR(vdd_sr2_pll.regulator[0])) {
+ if (PTR_ERR(vdd_sr2_pll.regulator[0]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Unable to get vdd_sr2_pll regulator!!!\n");
+ return PTR_ERR(vdd_sr2_pll.regulator[0]);
+ }
+
+ vdd_sr2_pll.regulator[1] = devm_regulator_get(&pdev->dev,
+ "vdd_sr2_dig");
+ if (IS_ERR(vdd_sr2_pll.regulator[1])) {
+ if (PTR_ERR(vdd_sr2_pll.regulator[1]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Unable to get vdd_sr2_dig regulator!!!\n");
+ return PTR_ERR(vdd_sr2_pll.regulator[1]);
+ }
+ }
+
+ vdd_hf_pll.regulator[0] = devm_regulator_get(&pdev->dev,
+ "vdd_hf_pll");
+ if (IS_ERR(vdd_hf_pll.regulator[0])) {
+ if (PTR_ERR(vdd_hf_pll.regulator[0]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Unable to get vdd_sr2_pll regulator!!!\n");
+ return PTR_ERR(vdd_hf_pll.regulator[0]);
+ }
+
+ vdd_hf_pll.regulator[1] = devm_regulator_get(&pdev->dev,
+ "vdd_hf_dig");
+ if (IS_ERR(vdd_hf_pll.regulator[1])) {
+ if (PTR_ERR(vdd_hf_pll.regulator[1]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Unable to get vdd_hf_dig regulator!!!\n");
+ return PTR_ERR(vdd_hf_pll.regulator[1]);
+ }
+
+ /* Vote for GPLL0 to turn on. Needed by acpuclock. */
+ regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE));
+ regval |= BIT(0);
+ writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE));
+
+ if (compat_bin || compat_bin3) {
+ gpll0_clk_src.c.parent = &gpll0_clk_src_8937.c;
+ gpll0_ao_clk_src.c.parent = &gpll0_ao_clk_src_8937.c;
+ /* Oxili Ocmem in GX rail: OXILI_GMEM_CLAMP_IO */
+ regval = readl_relaxed(GCC_REG_BASE(GX_DOMAIN_MISC));
+ regval &= ~BIT(0);
+ writel_relaxed(regval, GCC_REG_BASE(GX_DOMAIN_MISC));
+ get_speed_bin(pdev, &speed_bin);
+ override_for_8937(speed_bin);
+
+ if (compat_bin3) {
+ if (speed_bin) {
+ gfx3d_clk_src.freq_tbl =
+ ftbl_gcc_oxili_gfx3d_clk_8940_500MHz;
+ gfx3d_clk_src.c.fmax[VDD_DIG_SUPER_TUR] =
+ 500000000;
+ } else {
+ gfx3d_clk_src.freq_tbl =
+ ftbl_gcc_oxili_gfx3d_clk_8937_475MHz;
+ gfx3d_clk_src.c.fmax[VDD_DIG_SUPER_TUR] =
+ 475000000;
+ }
+ }
+ } else if (compat_bin2 || compat_bin4) {
+ gpll0_clk_src.c.parent = &gpll0_clk_src_8937.c;
+ gpll0_ao_clk_src.c.parent = &gpll0_ao_clk_src_8937.c;
+ vdd_dig.num_levels = VDD_DIG_NUM_8917;
+ vdd_dig.cur_level = VDD_DIG_NUM_8917;
+ vdd_hf_pll.num_levels = VDD_HF_PLL_NUM_8917;
+ vdd_hf_pll.cur_level = VDD_HF_PLL_NUM_8917;
+ get_speed_bin(pdev, &speed_bin);
+ override_for_8917(speed_bin);
+
+ if (compat_bin2) {
+ blsp1_qup2_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ blsp1_qup3_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ blsp1_qup4_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ blsp2_qup1_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ blsp2_qup2_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ blsp2_qup3_spi_apps_clk_src.freq_tbl =
+ ftbl_gcc_blsp1_2_qup1_4_spi_apps_clk_8917;
+ }
+ } else {
+ gpll0_clk_src.c.parent = &gpll0_clk_src_8952.c;
+ gpll0_ao_clk_src.c.parent = &gpll0_ao_clk_src_8952.c;
+ }
+
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_common,
+ ARRAY_SIZE(msm_clocks_lookup_common));
+ if (ret)
+ return ret;
+
+ if (compat_bin)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_8937,
+ ARRAY_SIZE(msm_clocks_lookup_8937));
+ else if (compat_bin2)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_8917,
+ ARRAY_SIZE(msm_clocks_lookup_8917));
+ else if (compat_bin3)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_8940,
+ ARRAY_SIZE(msm_clocks_lookup_8940));
+ else if (compat_bin4)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_8920,
+ ARRAY_SIZE(msm_clocks_lookup_8920));
+ else
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_8952,
+ ARRAY_SIZE(msm_clocks_lookup_8952));
+ if (ret)
+ return ret;
+
+ ret = enable_rpm_scaling();
+ if (ret)
+ return ret;
+
+ clk_set_rate(&apss_ahb_clk_src.c, 19200000);
+ clk_prepare_enable(&apss_ahb_clk_src.c);
+
+ /*
+ * Hold an active set vote for PCNOC AHB source. Sleep set vote is 0.
+ */
+ clk_set_rate(&pnoc_keepalive_a_clk.c, 19200000);
+ clk_prepare_enable(&pnoc_keepalive_a_clk.c);
+
+ clk_prepare_enable(&xo_a_clk_src.c);
+ clk_prepare_enable(&gcc_blsp1_ahb_clk.c);
+ clk_prepare_enable(&gcc_blsp2_ahb_clk.c);
+ clk_prepare_enable(&gcc_blsp1_uart2_apps_clk.c);
+ clk_prepare_enable(&gcc_blsp1_uart1_apps_clk.c);
+ clk_prepare_enable(&gcc_bimc_gpu_clk.c);
+ clk_prepare_enable(&sysmmnoc_msmbus_a_clk.c);
+ clk_prepare_enable(&sysmmnoc_a_clk.c);
+
+ if (!compat_bin && !compat_bin3) {
+ /* Configure Sleep and Wakeup cycles for GMEM clock */
+ regval = readl_relaxed(GCC_REG_BASE(OXILI_GMEM_CBCR));
+ regval ^= 0xFF0;
+ regval |= CLKFLAG_WAKEUP_CYCLES << 8;
+ regval |= CLKFLAG_SLEEP_CYCLES << 4;
+ writel_relaxed(regval, GCC_REG_BASE(OXILI_GMEM_CBCR));
+ } else {
+ /* Configure Sleep and Wakeup cycles for OXILI clock */
+ regval = readl_relaxed(GCC_REG_BASE(OXILI_GFX3D_CBCR));
+ regval &= ~0xF0;
+ regval |= CLKFLAG_SLEEP_CYCLES << 4;
+ writel_relaxed(regval, GCC_REG_BASE(OXILI_GFX3D_CBCR));
+ }
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ return ret;
+
+ msm_reset_controller_register(pdev, gcc_8952_resets,
+ ARRAY_SIZE(gcc_8952_resets), virt_bases[GCC_BASE]);
+
+ dev_info(&pdev->dev, "Registered GCC clocks\n");
+
+ return 0;
+}
+
+static const struct of_device_id msm_clock_gcc_match_table[] = {
+ { .compatible = "qcom,gcc-8952" },
+ { .compatible = "qcom,gcc-8937" },
+ { .compatible = "qcom,gcc-8917" },
+ { .compatible = "qcom,gcc-8940" },
+ { .compatible = "qcom,gcc-8920" },
+ {}
+};
+
+static struct platform_driver msm_clock_gcc_driver = {
+ .probe = msm_gcc_probe,
+ .driver = {
+ .name = "qcom,gcc-8952",
+ .of_match_table = msm_clock_gcc_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int msm_gcc_spm_probe(struct platform_device *pdev)
+{
+ struct resource *res = NULL;
+ bool compat_bin = false;
+
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,spm-8952");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spm_c0_base");
+ if (!res) {
+ dev_err(&pdev->dev, "SPM register base not defined for c0\n");
+ return -ENOMEM;
+ }
+
+ a53ss_c0_pll.spm_ctrl.spm_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!a53ss_c0_pll.spm_ctrl.spm_base) {
+ dev_err(&pdev->dev, "Failed to ioremap c0 spm registers\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spm_c1_base");
+ if (!res) {
+ dev_err(&pdev->dev, "SPM register base not defined for c1\n");
+ return -ENOMEM;
+ }
+
+ a53ss_c1_pll.spm_ctrl.spm_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!a53ss_c1_pll.spm_ctrl.spm_base) {
+ dev_err(&pdev->dev, "Failed to ioremap c1 spm registers\n");
+ return -ENOMEM;
+ }
+
+ if (compat_bin) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "spm_cci_base");
+ if (!res) {
+ dev_err(&pdev->dev, "SPM register base not defined for cci\n");
+ return -ENOMEM;
+ }
+
+ a53ss_cci_pll.spm_ctrl.spm_base = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
+ if (!a53ss_cci_pll.spm_ctrl.spm_base) {
+ dev_err(&pdev->dev, "Failed to ioremap cci spm registers\n");
+ return -ENOMEM;
+ }
+ }
+
+ dev_info(&pdev->dev, "Registered GCC SPM clocks\n");
+
+ return 0;
+}
+
+static const struct of_device_id msm_clock_spm_match_table[] = {
+ { .compatible = "qcom,gcc-spm-8952" },
+ { .compatible = "qcom,gcc-spm-8937" },
+ {}
+};
+
+static struct platform_driver msm_clock_spm_driver = {
+ .probe = msm_gcc_spm_probe,
+ .driver = {
+ .name = "qcom,gcc-spm-8952",
+ .of_match_table = msm_clock_spm_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_gcc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&msm_clock_gcc_driver);
+ if (!ret)
+ ret = platform_driver_register(&msm_clock_spm_driver);
+
+ return ret;
+}
+
+static struct clk_lookup msm_clocks_measure[] = {
+ CLK_LOOKUP_OF("measure", gcc_debug_mux, "debug"),
+ CLK_LIST(apss_debug_pri_mux),
+ CLK_LIST(apc0_m_clk),
+ CLK_LIST(apc1_m_clk),
+ CLK_LIST(cci_m_clk),
+};
+
+static struct clk_lookup msm_clocks_measure_8937[] = {
+ CLK_LOOKUP_OF("measure", gcc_debug_mux_8937, "debug"),
+ CLK_LIST(apss_debug_pri_mux),
+ CLK_LIST(apc0_m_clk),
+ CLK_LIST(apc1_m_clk),
+ CLK_LIST(cci_m_clk),
+};
+
+static int msm_clock_debug_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *res;
+ bool compat_bin = false, compat_bin2 = false;
+ bool compat_bin3 = false;
+ bool compat_bin4 = false;
+
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cc-debug-8937");
+
+ compat_bin2 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cc-debug-8917");
+
+ compat_bin3 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cc-debug-8940");
+
+ compat_bin4 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cc-debug-8920");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "meas");
+ if (!res) {
+ dev_err(&pdev->dev, "GLB clock diag base not defined.\n");
+ return -EINVAL;
+ }
+
+ meas_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!meas_base) {
+ dev_err(&pdev->dev, "Unable to map GLB clock diag base.\n");
+ return -ENOMEM;
+ }
+
+ clk_ops_debug_mux = clk_ops_gen_mux;
+ clk_ops_debug_mux.get_rate = measure_get_rate;
+
+ if (compat_bin2)
+ gcc_debug_mux_8937.post_div = 0x3;
+
+ if (!compat_bin && !compat_bin2 && !compat_bin3 && !compat_bin4)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_measure, ARRAY_SIZE(msm_clocks_measure));
+ else
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_measure_8937,
+ ARRAY_SIZE(msm_clocks_measure_8937));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register debug Mux\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "Registered Debug Mux successfully\n");
+ return ret;
+}
+
+static const struct of_device_id msm_clock_debug_match_table[] = {
+ { .compatible = "qcom,cc-debug-8952" },
+ { .compatible = "qcom,cc-debug-8937" },
+ { .compatible = "qcom,cc-debug-8917" },
+ { .compatible = "qcom,cc-debug-8940" },
+ { .compatible = "qcom,cc-debug-8920" },
+ {}
+};
+
+static struct platform_driver msm_clock_debug_driver = {
+ .probe = msm_clock_debug_probe,
+ .driver = {
+ .name = "qcom,cc-debug-8952",
+ .of_match_table = msm_clock_debug_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_clock_debug_init(void)
+{
+ return platform_driver_register(&msm_clock_debug_driver);
+}
+
+/* MDSS DSI_PHY_PLL */
+static struct clk_lookup msm_clocks_gcc_mdss_common[] = {
+ CLK_LIST(ext_pclk0_clk_src),
+ CLK_LIST(ext_byte0_clk_src),
+ CLK_LIST(byte0_clk_src),
+ CLK_LIST(pclk0_clk_src),
+ CLK_LIST(gcc_mdss_pclk0_clk),
+ CLK_LIST(gcc_mdss_byte0_clk),
+ CLK_LIST(mdss_mdp_vote_clk),
+ CLK_LIST(mdss_rotator_vote_clk),
+};
+
+static struct clk_lookup msm_clocks_gcc_mdss_8937[] = {
+ CLK_LIST(ext_pclk1_clk_src),
+ CLK_LIST(ext_byte1_clk_src),
+ CLK_LIST(byte1_clk_src),
+ CLK_LIST(pclk1_clk_src),
+ CLK_LIST(gcc_mdss_pclk1_clk),
+ CLK_LIST(gcc_mdss_byte1_clk),
+};
+
+static int msm_gcc_mdss_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct clk *curr_p;
+ bool compat_bin = false;
+
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-mdss-8937");
+ if (!compat_bin)
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-mdss-8940");
+
+ curr_p = ext_pclk0_clk_src.c.parent = devm_clk_get(&pdev->dev,
+ "pclk0_src");
+ if (IS_ERR(curr_p)) {
+ dev_err(&pdev->dev, "Failed to get pclk0 source.\n");
+ return PTR_ERR(curr_p);
+ }
+
+ curr_p = ext_byte0_clk_src.c.parent = devm_clk_get(&pdev->dev,
+ "byte0_src");
+ if (IS_ERR(curr_p)) {
+ dev_err(&pdev->dev, "Failed to get byte source.\n");
+ ret = PTR_ERR(curr_p);
+ goto byte0_fail;
+ }
+
+ ext_pclk0_clk_src.c.flags = CLKFLAG_NO_RATE_CACHE;
+ ext_byte0_clk_src.c.flags = CLKFLAG_NO_RATE_CACHE;
+
+ if (compat_bin) {
+ curr_p = ext_pclk1_clk_src.c.parent = devm_clk_get(&pdev->dev,
+ "pclk1_src");
+ if (IS_ERR(curr_p)) {
+ dev_err(&pdev->dev, "Failed to get pclk1 source.\n");
+ ret = PTR_ERR(curr_p);
+ goto fail;
+ }
+
+ curr_p = ext_byte1_clk_src.c.parent = devm_clk_get(&pdev->dev,
+ "byte1_src");
+ if (IS_ERR(curr_p)) {
+ dev_err(&pdev->dev, "Failed to get byte1 source.\n");
+ ret = PTR_ERR(curr_p);
+ goto byte1_fail;
+ }
+
+ ext_pclk1_clk_src.c.flags = CLKFLAG_NO_RATE_CACHE;
+ ext_byte1_clk_src.c.flags = CLKFLAG_NO_RATE_CACHE;
+ }
+
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_gcc_mdss_common,
+ ARRAY_SIZE(msm_clocks_gcc_mdss_common));
+ if (ret)
+ goto fail;
+
+ if (compat_bin) {
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_gcc_mdss_8937,
+ ARRAY_SIZE(msm_clocks_gcc_mdss_8937));
+ if (ret)
+ goto fail_8937;
+ }
+
+ dev_info(&pdev->dev, "Registered GCC MDSS clocks.\n");
+
+ return ret;
+fail_8937:
+ devm_clk_put(&pdev->dev, ext_byte1_clk_src.c.parent);
+byte1_fail:
+ devm_clk_put(&pdev->dev, ext_pclk1_clk_src.c.parent);
+fail:
+ devm_clk_put(&pdev->dev, ext_byte0_clk_src.c.parent);
+byte0_fail:
+ devm_clk_put(&pdev->dev, ext_pclk0_clk_src.c.parent);
+ return ret;
+
+}
+
+static const struct of_device_id msm_clock_mdss_match_table[] = {
+ { .compatible = "qcom,gcc-mdss-8952" },
+ { .compatible = "qcom,gcc-mdss-8937" },
+ { .compatible = "qcom,gcc-mdss-8917" },
+ { .compatible = "qcom,gcc-mdss-8940" },
+ { .compatible = "qcom,gcc-mdss-8920" },
+ {}
+};
+
+static struct platform_driver msm_clock_gcc_mdss_driver = {
+ .probe = msm_gcc_mdss_probe,
+ .driver = {
+ .name = "gcc-mdss-8952",
+ .of_match_table = msm_clock_mdss_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_gcc_mdss_init(void)
+{
+ return platform_driver_register(&msm_clock_gcc_mdss_driver);
+}
+arch_initcall(msm_gcc_init);
+fs_initcall_sync(msm_gcc_mdss_init);
+late_initcall(msm_clock_debug_init);
diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c
index fd80b56..57adebf 100644
--- a/drivers/clk/msm/clock-gcc-8953.c
+++ b/drivers/clk/msm/clock-gcc-8953.c
@@ -3797,13 +3797,6 @@ static int msm_gcc_probe(struct platform_device *pdev)
clk_set_rate(&apss_ahb_clk_src.c, 19200000);
clk_prepare_enable(&apss_ahb_clk_src.c);
- clk_prepare_enable(&gcc_blsp1_ahb_clk.c);
- clk_prepare_enable(&gcc_usb30_master_clk.c);
- clk_prepare_enable(&gcc_usb30_mock_utmi_clk.c);
- clk_prepare_enable(&gcc_blsp1_uart1_apps_clk.c);
- clk_prepare_enable(&gcc_apss_ahb_clk.c);
- clk_prepare_enable(&gcc_crypto_ahb_clk.c);
- clk_prepare_enable(&gcc_crypto_axi_clk.c);
/*
* Hold an active set vote for PCNOC AHB source. Sleep set
* vote is 0.
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 6c66b7f..4615122 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -37,8 +37,8 @@
#include <linux/cpuhotplug.h>
#include <soc/qcom/pm.h>
#include <soc/qcom/event_timer.h>
+#include <soc/qcom/lpm_levels.h>
#include <soc/qcom/lpm-stats.h>
-#include <soc/qcom/system_pm.h>
#include <soc/qcom/minidump.h>
#include <asm/arch_timer.h>
#include <asm/suspend.h>
@@ -82,6 +82,9 @@ struct lpm_debug {
uint32_t arg4;
};
+static struct system_pm_ops *sys_pm_ops;
+
+
struct lpm_cluster *lpm_root_node;
#define MAXSAMPLES 5
@@ -126,11 +129,6 @@ static void cluster_prepare(struct lpm_cluster *cluster,
const struct cpumask *cpu, int child_idx, bool from_idle,
int64_t time);
-static int msm_pm_sleep_time_override;
-module_param_named(sleep_time_override,
- msm_pm_sleep_time_override, int, 0664);
-static uint64_t suspend_wake_time;
-
static bool print_parsed_dt;
module_param_named(print_parsed_dt, print_parsed_dt, bool, 0664);
@@ -148,20 +146,15 @@ s32 msm_cpuidle_get_deep_idle_latency(void)
}
EXPORT_SYMBOL(msm_cpuidle_get_deep_idle_latency);
-void lpm_suspend_wake_time(uint64_t wakeup_time)
+uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops)
{
- if (wakeup_time <= 0) {
- suspend_wake_time = msm_pm_sleep_time_override;
- return;
- }
+ if (sys_pm_ops)
+ return -EUSERS;
- if (msm_pm_sleep_time_override &&
- (msm_pm_sleep_time_override < wakeup_time))
- suspend_wake_time = msm_pm_sleep_time_override;
- else
- suspend_wake_time = wakeup_time;
+ sys_pm_ops = pm_ops;
+
+ return 0;
}
-EXPORT_SYMBOL(lpm_suspend_wake_time);
static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
struct latency_level *lat_level)
@@ -706,28 +699,40 @@ static int cpu_power_select(struct cpuidle_device *dev,
return best_level;
}
+static unsigned int get_next_online_cpu(bool from_idle)
+{
+ unsigned int cpu;
+ ktime_t next_event;
+ unsigned int next_cpu = raw_smp_processor_id();
+
+ if (!from_idle)
+ return next_cpu;
+ next_event.tv64 = KTIME_MAX;
+ for_each_online_cpu(cpu) {
+ ktime_t *next_event_c;
+
+ next_event_c = get_next_event_cpu(cpu);
+ if (next_event_c->tv64 < next_event.tv64) {
+ next_event.tv64 = next_event_c->tv64;
+ next_cpu = cpu;
+ }
+ }
+ return next_cpu;
+}
+
static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
- struct cpumask *mask, bool from_idle, uint32_t *pred_time)
+ bool from_idle, uint32_t *pred_time)
{
int cpu;
- int next_cpu = raw_smp_processor_id();
ktime_t next_event;
struct cpumask online_cpus_in_cluster;
struct lpm_history *history;
int64_t prediction = LONG_MAX;
- next_event.tv64 = KTIME_MAX;
- if (!suspend_wake_time)
- suspend_wake_time = msm_pm_sleep_time_override;
- if (!from_idle) {
- if (mask)
- cpumask_copy(mask, cpumask_of(raw_smp_processor_id()));
- if (!suspend_wake_time)
- return ~0ULL;
- else
- return USEC_PER_SEC * suspend_wake_time;
- }
+ if (!from_idle)
+ return ~0ULL;
+ next_event.tv64 = KTIME_MAX;
cpumask_and(&online_cpus_in_cluster,
&cluster->num_children_in_sync, cpu_online_mask);
@@ -737,7 +742,6 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
next_event_c = get_next_event_cpu(cpu);
if (next_event_c->tv64 < next_event.tv64) {
next_event.tv64 = next_event_c->tv64;
- next_cpu = cpu;
}
if (from_idle && lpm_prediction) {
@@ -747,9 +751,6 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
}
}
- if (mask)
- cpumask_copy(mask, cpumask_of(next_cpu));
-
if (from_idle && lpm_prediction) {
if (prediction > ktime_to_us(ktime_get()))
*pred_time = prediction - ktime_to_us(ktime_get());
@@ -932,7 +933,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
if (!cluster)
return -EINVAL;
- sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL,
+ sleep_us = (uint32_t)get_cluster_sleep_time(cluster,
from_idle, &cpupred_us);
if (from_idle) {
@@ -983,8 +984,12 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
if (suspend_in_progress && from_idle && level->notify_rpm)
continue;
- if (level->notify_rpm && !system_sleep_allowed())
- continue;
+ if (level->notify_rpm) {
+ if (!(sys_pm_ops && sys_pm_ops->sleep_allowed))
+ continue;
+ if (!sys_pm_ops->sleep_allowed())
+ continue;
+ }
best_level = i;
@@ -1018,7 +1023,8 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
bool from_idle, int predicted)
{
struct lpm_cluster_level *level = &cluster->levels[idx];
- struct cpumask online_cpus;
+ struct cpumask online_cpus, cpumask;
+ unsigned int cpu;
cpumask_and(&online_cpus, &cluster->num_children_in_sync,
cpu_online_mask);
@@ -1043,10 +1049,13 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
}
if (level->notify_rpm) {
+ cpu = get_next_online_cpu(from_idle);
+ cpumask_copy(&cpumask, cpumask_of(cpu));
clear_predict_history();
clear_cl_predict_history();
- if (system_sleep_enter())
- return -EBUSY;
+ if (sys_pm_ops && sys_pm_ops->enter)
+ if ((sys_pm_ops->enter(&cpumask)))
+ return -EBUSY;
}
/* Notify cluster enter event after successfully config completion */
cluster_notify(cluster, level, true);
@@ -1177,7 +1186,8 @@ static void cluster_unprepare(struct lpm_cluster *cluster,
level = &cluster->levels[cluster->last_level];
if (level->notify_rpm)
- system_sleep_exit();
+ if (sys_pm_ops && sys_pm_ops->exit)
+ sys_pm_ops->exit();
update_debug_pc_event(CLUSTER_EXIT, cluster->last_level,
cluster->num_children_in_sync.bits[0],
@@ -1230,7 +1240,8 @@ static inline void cpu_unprepare(struct lpm_cpu *cpu, int cpu_index,
cpu_pm_exit();
}
-int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
+static int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl,
+ bool from_idle)
{
int state_id = 0;
@@ -1243,7 +1254,7 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
&cluster->child_cpus))
goto unlock_and_return;
- state_id |= get_cluster_id(cluster->parent, aff_lvl);
+ state_id |= get_cluster_id(cluster->parent, aff_lvl, from_idle);
if (cluster->last_level != cluster->default_level) {
struct lpm_cluster_level *level
@@ -1257,7 +1268,8 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
* the wakeup value by reading the bc timer directly.
*/
if (level->notify_rpm)
- system_sleep_update_wakeup();
+ if (sys_pm_ops && sys_pm_ops->update_wakeup)
+ sys_pm_ops->update_wakeup(from_idle);
if (level->psci_id)
(*aff_lvl)++;
}
@@ -1286,7 +1298,7 @@ static bool psci_enter_sleep(struct lpm_cpu *cpu, int idx, bool from_idle)
return success;
}
- state_id = get_cluster_id(cpu->parent, &affinity_level);
+ state_id = get_cluster_id(cpu->parent, &affinity_level, from_idle);
power_state = PSCI_POWER_STATE(cpu->levels[idx].is_reset);
affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
state_id |= power_state | affinity_level | cpu->levels[idx].psci_id;
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index 2f7a55d..1ff69c8 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -109,8 +109,6 @@ struct lpm_cluster {
struct hrtimer histtimer;
};
-void lpm_suspend_wake_time(uint64_t wakeup_time);
-
struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
void free_cluster_node(struct lpm_cluster *cluster);
void cluster_dt_walkthrough(struct lpm_cluster *cluster);
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 10e12e7..688c776 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -74,6 +74,15 @@
---help---
Toshiba TC358767 eDP bridge chip driver.
+config DRM_LT_LT9611
+ bool "LT LT9611 DSI/HDMI Bridge"
+ depends on OF
+ select DRM_KMS_HELPER
+ select REGMAP_I2C
+ select DRM_MIPI_DSI
+ help
+ Support for the LT Devices LT9611 DSI to HDMI encoder.
+
source "drivers/gpu/drm/bridge/analogix/Kconfig"
source "drivers/gpu/drm/bridge/adv7511/Kconfig"
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index cdf3a3c..68cf605 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -10,3 +10,4 @@
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
+obj-$(CONFIG_DRM_LT_LT9611) += lt9611.o
diff --git a/drivers/gpu/drm/bridge/lt9611.c b/drivers/gpu/drm/bridge/lt9611.c
new file mode 100644
index 0000000..4327e93
--- /dev/null
+++ b/drivers/gpu/drm/bridge/lt9611.c
@@ -0,0 +1,2180 @@
+/* Copyright (c) 2018, 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) "%s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/hdmi.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_mipi_dsi.h>
+
+
+#define CFG_HPD_INTERRUPTS BIT(0)
+#define CFG_EDID_INTERRUPTS BIT(1)
+#define CFG_CEC_INTERRUPTS BIT(2)
+#define CFG_VID_CHK_INTERRUPTS BIT(3)
+
+#define EDID_SEG_SIZE 256
+
+struct lt9611_reg_cfg {
+ u8 reg;
+ u8 val;
+ int sleep_in_ms;
+};
+
+struct lt9611_vreg {
+ struct regulator *vreg; /* vreg handle */
+ char vreg_name[32];
+ int min_voltage;
+ int max_voltage;
+ int enable_load;
+ int disable_load;
+ int pre_on_sleep;
+ int post_on_sleep;
+ int pre_off_sleep;
+ int post_off_sleep;
+};
+
+struct lt9611_video_cfg {
+ u32 h_active;
+ u32 h_front_porch;
+ u32 h_pulse_width;
+ u32 h_back_porch;
+ bool h_polarity;
+ u32 v_active;
+ u32 v_front_porch;
+ u32 v_pulse_width;
+ u32 v_back_porch;
+ bool v_polarity;
+ u32 pclk_khz;
+ bool interlaced;
+ u32 vic;
+ enum hdmi_picture_aspect ar;
+ u32 num_of_lanes;
+ u32 num_of_intfs;
+ u8 scaninfo;
+};
+
+struct lt9611 {
+ struct device *dev;
+ struct drm_bridge bridge;
+
+ struct device_node *host_node;
+ struct mipi_dsi_device *dsi;
+
+ u8 i2c_addr;
+ int irq;
+ bool ac_mode;
+
+ u32 irq_gpio;
+ u32 reset_gpio;
+ u32 hdmi_ps_gpio;
+ u32 hdmi_en_gpio;
+
+ unsigned int num_vreg;
+ struct lt9611_vreg *vreg_config;
+
+ struct i2c_client *i2c_client;
+
+ enum drm_connector_status status;
+ bool power_on;
+
+ /* get display modes from device tree */
+ bool non_pluggable;
+ u32 num_of_modes;
+ struct list_head mode_list;
+
+ struct drm_display_mode curr_mode;
+ struct lt9611_video_cfg video_cfg;
+
+ u8 edid_buf[EDID_SEG_SIZE];
+ bool hdmi_mode;
+};
+
+static struct lt9611_reg_cfg lt9611_init_setup[] = {
+ /* LT9611_System_Init */
+ {0xFF, 0x81, 0},
+ {0x01, 0x18, 0}, /* sel xtal clock */
+
+ /* timer for frequency meter */
+ {0xff, 0x82, 0},
+ {0x1b, 0x69, 0}, /*timer 2*/
+ {0x1c, 0x78, 0},
+ {0xcb, 0x69, 0}, /*timer 1 */
+ {0xcc, 0x78, 0},
+
+ /* irq init */
+ {0xff, 0x82, 0},
+ {0x51, 0x01, 0},
+ {0x58, 0x0a, 0}, /* hpd irq */
+ {0x59, 0x80, 0}, /* hpd debounce width */
+ {0x9e, 0xf7, 0}, /* video check irq */
+
+ /* power consumption for work */
+ {0xff, 0x80, 0},
+ {0x04, 0xf0, 0},
+ {0x06, 0xf0, 0},
+ {0x0a, 0x80, 0},
+ {0x0b, 0x40, 0},
+ {0x0d, 0xef, 0},
+ {0x11, 0xfa, 0},
+};
+
+struct lt9611_timing_info {
+ u16 xres;
+ u16 yres;
+ u8 bpp;
+ u8 fps;
+ u8 lanes;
+ u8 intfs;
+};
+
+static struct lt9611_timing_info lt9611_supp_timing_cfg[] = {
+ {3840, 2160, 24, 30, 4, 2}, /* 3840x2160 24bit 30Hz 4Lane 2ports */
+ {1920, 1080, 24, 60, 4, 1}, /* 1080P 24bit 60Hz 4lane 1port */
+ {1920, 1080, 24, 30, 3, 1}, /* 1080P 24bit 30Hz 3lane 1port */
+ {1920, 1080, 24, 24, 3, 1},
+ {720, 480, 24, 60, 2, 1},
+ {720, 576, 24, 50, 2, 1},
+ {640, 480, 24, 60, 2, 1},
+ {0xffff, 0xffff, 0xff, 0xff, 0xff},
+};
+
+static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct lt9611, bridge);
+}
+
+static struct lt9611 *connector_to_lt9611(struct drm_connector *connector)
+{
+ WARN_ON(!connector->private);
+
+ return bridge_to_lt9611(connector->private);
+}
+
+static int lt9611_write(struct lt9611 *pdata, u8 reg, u8 val)
+{
+ struct i2c_client *client = pdata->i2c_client;
+ u8 buf[2] = {reg, val};
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ };
+
+ if (i2c_transfer(client->adapter, &msg, 1) < 1) {
+ pr_err("i2c write failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int lt9611_read(struct lt9611 *pdata, u8 reg, char *buf, u32 size)
+{
+ struct i2c_client *client = pdata->i2c_client;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = ®,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = size,
+ .buf = buf,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ pr_err("i2c read failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int lt9611_write_array(struct lt9611 *pdata,
+ struct lt9611_reg_cfg *cfg, int size)
+{
+ int ret = 0;
+ int i;
+
+ size = size / sizeof(struct lt9611_reg_cfg);
+ for (i = 0; i < size; i++) {
+ ret = lt9611_write(pdata, cfg[i].reg, cfg[i].val);
+
+ if (ret != 0) {
+ pr_err("reg writes failed. Last write %02X to %02X\n",
+ cfg[i].val, cfg[i].reg);
+ goto w_regs_fail;
+ }
+
+ if (cfg[i].sleep_in_ms)
+ msleep(cfg[i].sleep_in_ms);
+ }
+
+w_regs_fail:
+ if (ret != 0)
+ pr_err("exiting with ret = %d after %d writes\n", ret, i);
+
+ return ret;
+}
+
+static int lt9611_parse_dt_modes(struct device_node *np,
+ struct list_head *head,
+ u32 *num_of_modes)
+{
+ int rc = 0;
+ struct drm_display_mode *mode;
+ u32 mode_count = 0;
+ struct device_node *node = NULL;
+ struct device_node *root_node = NULL;
+ u32 h_front_porch, h_pulse_width, h_back_porch;
+ u32 v_front_porch, v_pulse_width, v_back_porch;
+ bool h_active_high, v_active_high;
+ u32 flags = 0;
+
+ root_node = of_get_child_by_name(np, "lt,customize-modes");
+ if (!root_node) {
+ root_node = of_parse_phandle(np, "lt,customize-modes", 0);
+ if (!root_node) {
+ pr_info("No entry present for lt,customize-modes");
+ goto end;
+ }
+ }
+
+ for_each_child_of_node(root_node, node) {
+ rc = 0;
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode) {
+ pr_err("Out of memory\n");
+ rc = -ENOMEM;
+ continue;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-h-active",
+ &mode->hdisplay);
+ if (rc) {
+ pr_err("failed to read h-active, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-h-front-porch",
+ &h_front_porch);
+ if (rc) {
+ pr_err("failed to read h-front-porch, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-h-pulse-width",
+ &h_pulse_width);
+ if (rc) {
+ pr_err("failed to read h-pulse-width, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-h-back-porch",
+ &h_back_porch);
+ if (rc) {
+ pr_err("failed to read h-back-porch, rc=%d\n", rc);
+ goto fail;
+ }
+
+ h_active_high = of_property_read_bool(node,
+ "lt,mode-h-active-high");
+
+ rc = of_property_read_u32(node, "lt,mode-v-active",
+ &mode->vdisplay);
+ if (rc) {
+ pr_err("failed to read v-active, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-v-front-porch",
+ &v_front_porch);
+ if (rc) {
+ pr_err("failed to read v-front-porch, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-v-pulse-width",
+ &v_pulse_width);
+ if (rc) {
+ pr_err("failed to read v-pulse-width, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-v-back-porch",
+ &v_back_porch);
+ if (rc) {
+ pr_err("failed to read v-back-porch, rc=%d\n", rc);
+ goto fail;
+ }
+
+ v_active_high = of_property_read_bool(node,
+ "lt,mode-v-active-high");
+
+ rc = of_property_read_u32(node, "lt,mode-refresh-rate",
+ &mode->vrefresh);
+ if (rc) {
+ pr_err("failed to read refresh-rate, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(node, "lt,mode-clock-in-khz",
+ &mode->clock);
+ if (rc) {
+ pr_err("failed to read clock, rc=%d\n", rc);
+ goto fail;
+ }
+
+ mode->hsync_start = mode->hdisplay + h_front_porch;
+ mode->hsync_end = mode->hsync_start + h_pulse_width;
+ mode->htotal = mode->hsync_end + h_back_porch;
+ mode->vsync_start = mode->vdisplay + v_front_porch;
+ mode->vsync_end = mode->vsync_start + v_pulse_width;
+ mode->vtotal = mode->vsync_end + v_back_porch;
+ if (h_active_high)
+ flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NHSYNC;
+ if (v_active_high)
+ flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NVSYNC;
+ mode->flags = flags;
+
+ if (!rc) {
+ mode_count++;
+ list_add_tail(&mode->head, head);
+ }
+
+ drm_mode_set_name(mode);
+
+ pr_debug("mode[%s] h[%d,%d,%d,%d] v[%d,%d,%d,%d] %d %x %dkHZ\n",
+ mode->name, mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal, mode->vdisplay,
+ mode->vsync_start, mode->vsync_end, mode->vtotal,
+ mode->vrefresh, mode->flags, mode->clock);
+fail:
+ if (rc) {
+ kfree(mode);
+ continue;
+ }
+ }
+
+ if (num_of_modes)
+ *num_of_modes = mode_count;
+
+end:
+ return rc;
+}
+
+
+static int lt9611_parse_dt(struct device *dev,
+ struct lt9611 *pdata)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *end_node;
+ int ret = 0;
+
+ end_node = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
+ if (!end_node) {
+ pr_err("remote endpoint not found\n");
+ return -ENODEV;
+ }
+
+ pdata->host_node = of_graph_get_remote_port_parent(end_node);
+ of_node_put(end_node);
+ if (!pdata->host_node) {
+ pr_err("remote node not found\n");
+ return -ENODEV;
+ }
+ of_node_put(pdata->host_node);
+
+ pdata->irq_gpio =
+ of_get_named_gpio(np, "lt,irq-gpio", 0);
+ if (!gpio_is_valid(pdata->irq_gpio)) {
+ pr_err("irq gpio not specified\n");
+ ret = -EINVAL;
+ }
+ pr_debug("irq_gpio=%d\n", pdata->irq_gpio);
+
+ pdata->reset_gpio =
+ of_get_named_gpio(np, "lt,reset-gpio", 0);
+ if (!gpio_is_valid(pdata->reset_gpio)) {
+ pr_err("reset gpio not specified\n");
+ ret = -EINVAL;
+ }
+ pr_debug("reset_gpio=%d\n", pdata->reset_gpio);
+
+ pdata->hdmi_ps_gpio =
+ of_get_named_gpio(np, "lt,hdmi-ps-gpio", 0);
+ if (!gpio_is_valid(pdata->hdmi_ps_gpio))
+ pr_debug("hdmi ps gpio not specified\n");
+ else
+ pr_debug("hdmi_ps_gpio=%d\n", pdata->hdmi_ps_gpio);
+
+ pdata->hdmi_en_gpio =
+ of_get_named_gpio(np, "lt,hdmi-en-gpio", 0);
+ if (!gpio_is_valid(pdata->hdmi_en_gpio))
+ pr_debug("hdmi en gpio not specified\n");
+ else
+ pr_debug("hdmi_en_gpio=%d\n", pdata->hdmi_en_gpio);
+
+ pdata->ac_mode = of_property_read_bool(np, "lt,ac-mode");
+ pr_debug("ac_mode=%d\n", pdata->ac_mode);
+
+ pdata->non_pluggable = of_property_read_bool(np, "lt,non-pluggable");
+ pr_debug("non_pluggable = %d\n", pdata->non_pluggable);
+ if (pdata->non_pluggable) {
+ INIT_LIST_HEAD(&pdata->mode_list);
+ ret = lt9611_parse_dt_modes(np,
+ &pdata->mode_list, &pdata->num_of_modes);
+ }
+
+ return ret;
+}
+
+static int lt9611_gpio_configure(struct lt9611 *pdata, bool on)
+{
+ int ret = 0;
+
+ if (on) {
+ ret = gpio_request(pdata->reset_gpio,
+ "lt9611-reset-gpio");
+ if (ret) {
+ pr_err("lt9611 reset gpio request failed\n");
+ goto error;
+ }
+
+ ret = gpio_direction_output(pdata->reset_gpio, 0);
+ if (ret) {
+ pr_err("lt9611 reset gpio direction failed\n");
+ goto reset_error;
+ }
+
+ if (gpio_is_valid(pdata->hdmi_en_gpio)) {
+ ret = gpio_request(pdata->hdmi_en_gpio,
+ "lt9611-hdmi-en-gpio");
+ if (ret) {
+ pr_err("lt9611 hdmi en gpio request failed\n");
+ goto reset_error;
+ }
+
+ ret = gpio_direction_output(pdata->hdmi_en_gpio, 1);
+ if (ret) {
+ pr_err("lt9611 hdmi en gpio direction failed\n");
+ goto hdmi_en_error;
+ }
+ }
+
+ if (gpio_is_valid(pdata->hdmi_ps_gpio)) {
+ ret = gpio_request(pdata->hdmi_ps_gpio,
+ "lt9611-hdmi-ps-gpio");
+ if (ret) {
+ pr_err("lt9611 hdmi ps gpio request failed\n");
+ goto hdmi_en_error;
+ }
+
+ ret = gpio_direction_input(pdata->hdmi_ps_gpio);
+ if (ret) {
+ pr_err("lt9611 hdmi ps gpio direction failed\n");
+ goto hdmi_ps_error;
+ }
+ }
+
+ ret = gpio_request(pdata->irq_gpio, "lt9611-irq-gpio");
+ if (ret) {
+ pr_err("lt9611 irq gpio request failed\n");
+ goto hdmi_ps_error;
+ }
+
+ ret = gpio_direction_input(pdata->irq_gpio);
+ if (ret) {
+ pr_err("lt9611 irq gpio direction failed\n");
+ goto irq_error;
+ }
+ } else {
+ gpio_free(pdata->irq_gpio);
+ if (gpio_is_valid(pdata->hdmi_ps_gpio))
+ gpio_free(pdata->hdmi_ps_gpio);
+ if (gpio_is_valid(pdata->hdmi_en_gpio))
+ gpio_free(pdata->hdmi_en_gpio);
+ gpio_free(pdata->reset_gpio);
+ }
+
+ return ret;
+
+
+irq_error:
+ gpio_free(pdata->irq_gpio);
+hdmi_ps_error:
+ if (gpio_is_valid(pdata->hdmi_ps_gpio))
+ gpio_free(pdata->hdmi_ps_gpio);
+hdmi_en_error:
+ if (gpio_is_valid(pdata->hdmi_en_gpio))
+ gpio_free(pdata->hdmi_en_gpio);
+reset_error:
+ gpio_free(pdata->reset_gpio);
+error:
+ return ret;
+}
+
+static int lt9611_read_device_rev(struct lt9611 *pdata)
+{
+ u8 rev = 0;
+ int ret = 0;
+
+ lt9611_write(pdata, 0xff, 0x80);
+ lt9611_write(pdata, 0xee, 0x01);
+
+ ret = lt9611_read(pdata, 0x02, &rev, 1);
+
+ if (ret == 0)
+ pr_info("LT9611 revsion: 0x%x\n", rev);
+
+ return ret;
+}
+
+static int lt9611_mipi_input_analog(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ struct lt9611_reg_cfg reg_cfg[] = {
+ {0xff, 0x81, 0},
+ {0x06, 0x40, 0}, /*port A rx current*/
+ {0x0a, 0xfe, 0}, /*port A ldo voltage set*/
+ {0x0b, 0xbf, 0}, /*enable port A lprx*/
+ {0x11, 0x40, 0}, /*port B rx current*/
+ {0x15, 0xfe, 0}, /*port B ldo voltage set*/
+ {0x16, 0xbf, 0}, /*enable port B lprx*/
+
+ {0x1c, 0x03, 0}, /*PortA clk lane no-LP mode*/
+ {0x20, 0x03, 0}, /*PortB clk lane with-LP mode*/
+ };
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ lt9611_write_array(pdata, reg_cfg, sizeof(reg_cfg));
+
+ return 0;
+}
+
+static int lt9611_mipi_input_digital(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ u8 lanes = 0;
+ u8 ports = 0;
+ struct lt9611_reg_cfg reg_cfg[] = {
+ {0xff, 0x82, 0},
+ {0x4f, 0x80, 0},
+ {0x50, 0x10, 0},
+ {0xff, 0x83, 0},
+
+ {0x02, 0x0a, 0},
+ {0x06, 0x0a, 0},
+ };
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ lanes = cfg->num_of_lanes;
+ ports = cfg->num_of_intfs;
+
+ lt9611_write(pdata, 0xff, 0x83);
+ if (lanes == 4)
+ lt9611_write(pdata, 0x00, 0x00);
+ else if (lanes < 4)
+ lt9611_write(pdata, 0x00, lanes);
+ else {
+ pr_err("invalid lane count\n");
+ return -EINVAL;
+ }
+
+ if (ports == 1)
+ lt9611_write(pdata, 0x0a, 0x00);
+ else if (ports == 2)
+ lt9611_write(pdata, 0x0a, 0x03);
+ else {
+ pr_err("invalid port count\n");
+ return -EINVAL;
+ }
+
+ lt9611_write_array(pdata, reg_cfg, sizeof(reg_cfg));
+
+ return 0;
+}
+
+static void lt9611_mipi_video_setup(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ u32 h_total, h_act, hpw, hfp, hss;
+ u32 v_total, v_act, vpw, vfp, vss;
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ h_total = cfg->h_active + cfg->h_front_porch +
+ cfg->h_pulse_width + cfg->h_back_porch;
+ v_total = cfg->v_active + cfg->v_front_porch +
+ cfg->v_pulse_width + cfg->v_back_porch;
+
+ h_act = cfg->h_active;
+ hpw = cfg->h_pulse_width;
+ hfp = cfg->h_front_porch;
+ hss = cfg->h_pulse_width + cfg->h_back_porch;
+
+ v_act = cfg->v_active;
+ vpw = cfg->v_pulse_width;
+ vfp = cfg->v_front_porch;
+ vss = cfg->v_pulse_width + cfg->v_back_porch;
+
+ pr_debug("h_total=%d, h_active=%d, hfp=%d, hpw=%d, hbp=%d\n",
+ h_total, cfg->h_active, cfg->h_front_porch,
+ cfg->h_pulse_width, cfg->h_back_porch);
+
+ pr_debug("v_total=%d, v_active=%d, vfp=%d, vpw=%d, vbp=%d\n",
+ v_total, cfg->v_active, cfg->v_front_porch,
+ cfg->v_pulse_width, cfg->v_back_porch);
+
+ lt9611_write(pdata, 0xff, 0x83);
+
+ lt9611_write(pdata, 0x0d, (u8)(v_total / 256));
+ lt9611_write(pdata, 0x0e, (u8)(v_total % 256));
+
+ lt9611_write(pdata, 0x0f, (u8)(v_act / 256));
+ lt9611_write(pdata, 0x10, (u8)(v_act % 256));
+
+ lt9611_write(pdata, 0x11, (u8)(h_total / 256));
+ lt9611_write(pdata, 0x12, (u8)(h_total % 256));
+
+ lt9611_write(pdata, 0x13, (u8)(h_act / 256));
+ lt9611_write(pdata, 0x14, (u8)(h_act % 256));
+
+ lt9611_write(pdata, 0x15, (u8)(vpw % 256));
+ lt9611_write(pdata, 0x16, (u8)(hpw % 256));
+
+ lt9611_write(pdata, 0x17, (u8)(vfp % 256));
+
+ lt9611_write(pdata, 0x18, (u8)(vss % 256));
+
+ lt9611_write(pdata, 0x19, (u8)(hfp % 256));
+
+ lt9611_write(pdata, 0x1a, (u8)(hss / 256));
+ lt9611_write(pdata, 0x1b, (u8)(hss % 256));
+}
+
+static int lt9611_pcr_setup(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ u32 h_act = 0;
+ struct lt9611_reg_cfg reg_cfg[] = {
+ {0xff, 0x83, 0},
+
+ {0x2d, 0x38, 0}, /* up_lmt */
+ {0x2e, 0x00, 0},
+ {0x31, 0x10, 0}, /* low lmt */
+ {0x32, 0x00, 0},
+
+ {0xff, 0x80, 0},
+ {0x11, 0x5a, 0}, /* pcr reset */
+ {0x11, 0xfa, 0},
+ };
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ h_act = cfg->h_active;
+
+ if (h_act == 1920) {
+ lt9611_write(pdata, 0xff, 0x83);
+ lt9611_write(pdata, 0x48, 0x00);
+ lt9611_write(pdata, 0x49, 0x81);
+
+ lt9611_write(pdata, 0x4a, 0x10);
+ lt9611_write(pdata, 0x1d, 0x10);
+ lt9611_write(pdata, 0x21, 0x41);
+ lt9611_write(pdata, 0x22, 0x40);
+ lt9611_write(pdata, 0x24, 0x11);
+ lt9611_write(pdata, 0x25, 0x30);
+ lt9611_write(pdata, 0x26, 0x37);
+ lt9611_write(pdata, 0x2a, 0x02);
+ } else if (h_act == 3840) {
+ lt9611_write(pdata, 0xff, 0x83);
+ lt9611_write(pdata, 0x0b, 0x03);
+ lt9611_write(pdata, 0x0c, 0xd0);
+ lt9611_write(pdata, 0x48, 0x03);
+ lt9611_write(pdata, 0x49, 0xd0);
+
+ lt9611_write(pdata, 0x4a, 0x01);
+ lt9611_write(pdata, 0x1d, 0x10);
+ lt9611_write(pdata, 0x21, 0x41);
+ lt9611_write(pdata, 0x22, 0x40);
+ lt9611_write(pdata, 0x24, 0x70);
+ lt9611_write(pdata, 0x25, 0x50);
+ lt9611_write(pdata, 0x26, 0x37);
+ lt9611_write(pdata, 0x2a, 0x03);
+ } else if (h_act == 640) {
+ lt9611_write(pdata, 0xff, 0x83);
+ lt9611_write(pdata, 0x0b, 0x01);
+ lt9611_write(pdata, 0x0c, 0x20);
+ lt9611_write(pdata, 0x48, 0x00);
+ lt9611_write(pdata, 0x49, 0x81);
+
+ lt9611_write(pdata, 0x4a, 0x10);
+ lt9611_write(pdata, 0x1d, 0x10);
+ lt9611_write(pdata, 0x21, 0x41);
+ lt9611_write(pdata, 0x22, 0x40);
+ lt9611_write(pdata, 0x24, 0x11);
+ lt9611_write(pdata, 0x25, 0x30);
+ lt9611_write(pdata, 0x26, 0x13);
+ lt9611_write(pdata, 0x2a, 0x03);
+ }
+
+ lt9611_write_array(pdata, reg_cfg, sizeof(reg_cfg));
+
+ return 0;
+}
+
+static int lt9611_pll_setup(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ u32 pclk = 0;
+ struct lt9611_reg_cfg reg_cfg[] = {
+ /* txpll init */
+ {0xff, 0x81, 0},
+ {0x23, 0x40, 0},
+ {0x24, 0x64, 0},
+ {0x25, 0x80, 0},
+ {0x26, 0x55, 0},
+ {0x2c, 0x37, 0},
+ {0x2f, 0x01, 0},
+ {0x26, 0x55, 0},
+ {0x27, 0x66, 0},
+ {0x28, 0x88, 0},
+ };
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ pclk = cfg->pclk_khz;
+
+ lt9611_write_array(pdata, reg_cfg, sizeof(reg_cfg));
+
+ if (pclk > 150000)
+ lt9611_write(pdata, 0x2d, 0x88);
+ else if (pclk > 70000)
+ lt9611_write(pdata, 0x2d, 0x99);
+ else
+ lt9611_write(pdata, 0x2d, 0xaa);
+
+ lt9611_write(pdata, 0xff, 0x82);
+ pclk = pclk / 2;
+ lt9611_write(pdata, 0xe3, pclk/65536); /* pclk[19:16] */
+ pclk = pclk % 65536;
+ lt9611_write(pdata, 0xe4, pclk/256); /* pclk[15:8] */
+ lt9611_write(pdata, 0xe5, pclk%256); /* pclk[7:0] */
+
+ lt9611_write(pdata, 0xde, 0x20);
+ lt9611_write(pdata, 0xde, 0xe0);
+
+ lt9611_write(pdata, 0xff, 0x80);
+ lt9611_write(pdata, 0x16, 0xf1);
+ lt9611_write(pdata, 0x16, 0xf3);
+
+ return 0;
+}
+
+static int lt9611_video_check(struct lt9611 *pdata)
+{
+ int ret = 0;
+ u32 v_total, v_act, h_act_a, h_act_b, h_total_sysclk;
+ u8 temp = 0;
+
+ if (!pdata) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ /* top module video check */
+ lt9611_write(pdata, 0xff, 0x82);
+
+ /* v_act */
+ ret = lt9611_read(pdata, 0x82, &temp, 1);
+ if (ret)
+ goto end;
+
+ v_act = temp << 8;
+ ret = lt9611_read(pdata, 0x83, &temp, 1);
+ if (ret)
+ goto end;
+ v_act = v_act + temp;
+
+ /* v_total */
+ ret = lt9611_read(pdata, 0x6c, &temp, 1);
+ if (ret)
+ goto end;
+ v_total = temp << 8;
+ ret = lt9611_read(pdata, 0x6d, &temp, 1);
+ if (ret)
+ goto end;
+ v_total = v_total + temp;
+
+ /* h_total_sysclk */
+ ret = lt9611_read(pdata, 0x86, &temp, 1);
+ if (ret)
+ goto end;
+ h_total_sysclk = temp << 8;
+ ret = lt9611_read(pdata, 0x87, &temp, 1);
+ if (ret)
+ goto end;
+ h_total_sysclk = h_total_sysclk + temp;
+
+ /* h_act_a */
+ lt9611_write(pdata, 0xff, 0x83);
+ ret = lt9611_read(pdata, 0x82, &temp, 1);
+ if (ret)
+ goto end;
+ h_act_a = temp << 8;
+ ret = lt9611_read(pdata, 0x83, &temp, 1);
+ if (ret)
+ goto end;
+ h_act_a = (h_act_a + temp)/3;
+
+ /* h_act_b */
+ lt9611_write(pdata, 0xff, 0x83);
+ ret = lt9611_read(pdata, 0x86, &temp, 1);
+ if (ret)
+ goto end;
+ h_act_b = temp << 8;
+ ret = lt9611_read(pdata, 0x87, &temp, 1);
+ if (ret)
+ goto end;
+ h_act_b = (h_act_b + temp)/3;
+
+ pr_info("video check: h_act_a=%d, h_act_b=%d, v_act=%d, v_total=%d, h_total_sysclk=%d\n",
+ h_act_a, h_act_b, v_act, v_total, h_total_sysclk);
+
+ return 0;
+
+end:
+ pr_err("read video check error\n");
+ return ret;
+}
+
+static int lt9611_hdmi_tx_digital(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ int ret = -EINVAL;
+ u32 checksum, vic;
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return ret;
+ }
+
+ vic = cfg->vic;
+ checksum = 0x46 - vic;
+
+ lt9611_write(pdata, 0xff, 0x84);
+ lt9611_write(pdata, 0x43, checksum);
+ lt9611_write(pdata, 0x44, 0x84);
+ lt9611_write(pdata, 0x47, vic);
+
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_write(pdata, 0xd6, 0x8c);
+ lt9611_write(pdata, 0xd7, 0x04);
+
+ return ret;
+}
+
+static int lt9611_hdmi_tx_phy(struct lt9611 *pdata,
+ struct lt9611_video_cfg *cfg)
+{
+ int ret = -EINVAL;
+ struct lt9611_reg_cfg reg_cfg[] = {
+ {0xff, 0x81, 0},
+ {0x30, 0x6a, 0},
+ {0x31, 0x44, 0}, /* HDMI DC mode */
+ {0x32, 0x4a, 0},
+ {0x33, 0x0b, 0},
+ {0x34, 0x00, 0},
+ {0x35, 0x00, 0},
+ {0x36, 0x00, 0},
+ {0x37, 0x44, 0},
+ {0x3f, 0x0f, 0},
+ {0x40, 0xa0, 0},
+ {0x41, 0xa0, 0},
+ {0x42, 0xa0, 0},
+ {0x43, 0xa0, 0},
+ {0x44, 0x0a, 0},
+ };
+
+ if (!pdata || !cfg) {
+ pr_err("invalid input\n");
+ return ret;
+ }
+
+ /* HDMI AC mode */
+ if (pdata->ac_mode)
+ reg_cfg[2].val = 0x73;
+
+ lt9611_write_array(pdata, reg_cfg, sizeof(reg_cfg));
+
+ return ret;
+}
+
+static void lt9611_hdmi_output_enable(struct lt9611 *pdata)
+{
+ lt9611_write(pdata, 0xff, 0x81);
+ lt9611_write(pdata, 0x30, 0xea);
+}
+
+static void lt9611_hdmi_output_disable(struct lt9611 *pdata)
+{
+ lt9611_write(pdata, 0xff, 0x81);
+ lt9611_write(pdata, 0x30, 0x6a);
+}
+
+static irqreturn_t lt9611_irq_thread_handler(int irq, void *dev_id)
+{
+ struct lt9611 *pdata = dev_id;
+ u8 irq_flag0 = 0;
+ u8 irq_flag3 = 0;
+
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_read(pdata, 0x0f, &irq_flag3, 1);
+ lt9611_read(pdata, 0x0c, &irq_flag0, 1);
+
+ /* hpd changed low */
+ if (irq_flag3 & 0x80) {
+ pr_info("hdmi cable disconnected\n");
+
+ lt9611_write(pdata, 0xff, 0x82); /* irq 3 clear flag */
+ lt9611_write(pdata, 0x07, 0xbf);
+ lt9611_write(pdata, 0x07, 0x3f);
+ }
+ /* hpd changed high */
+ if (irq_flag3 & 0x40) {
+ pr_info("hdmi cable connected\n");
+
+ lt9611_write(pdata, 0xff, 0x82); /* irq 3 clear flag */
+ lt9611_write(pdata, 0x07, 0x7f);
+ lt9611_write(pdata, 0x07, 0x3f);
+ }
+
+ /* video input changed */
+ if (irq_flag0 & 0x01) {
+ pr_info("video input changed\n");
+ lt9611_write(pdata, 0xff, 0x82); /* irq 0 clear flag */
+ lt9611_write(pdata, 0x9e, 0xff);
+ lt9611_write(pdata, 0x9e, 0xf7);
+ lt9611_write(pdata, 0x04, 0xff);
+ lt9611_write(pdata, 0x04, 0xfe);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int lt9611_enable_interrupts(struct lt9611 *pdata, int interrupts)
+{
+ int ret = 0;
+ u8 reg_val = 0;
+ u8 init_reg_val;
+
+ if (!pdata) {
+ pr_err("invalid input\n");
+ goto end;
+ }
+
+ if (interrupts & CFG_VID_CHK_INTERRUPTS) {
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_read(pdata, 0x00, ®_val, 1);
+
+ if (reg_val & 0x01) {
+ init_reg_val = reg_val & 0xfe;
+ pr_debug("enabling video check interrupts\n");
+ lt9611_write(pdata, 0x00, init_reg_val);
+ }
+ lt9611_write(pdata, 0x04, 0xff); /* clear */
+ lt9611_write(pdata, 0x04, 0xfe);
+ }
+
+ if (interrupts & CFG_HPD_INTERRUPTS) {
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_read(pdata, 0x03, ®_val, 1);
+
+ if (reg_val & 0xc0) { //reg_val | 0xc0???
+ init_reg_val = reg_val & 0x3f;
+ pr_debug("enabling hpd interrupts\n");
+ lt9611_write(pdata, 0x03, init_reg_val);
+ }
+
+ lt9611_write(pdata, 0x07, 0xff); //clear
+ lt9611_write(pdata, 0x07, 0x3f);
+ }
+
+end:
+ return ret;
+}
+
+static void lt9611_pcr_mk_debug(struct lt9611 *pdata)
+{
+ u8 m = 0, k1 = 0, k2 = 0, k3 = 0;
+
+ lt9611_write(pdata, 0xff, 0x83);
+ lt9611_read(pdata, 0xb4, &m, 1);
+ lt9611_read(pdata, 0xb5, &k1, 1);
+ lt9611_read(pdata, 0xb6, &k2, 1);
+ lt9611_read(pdata, 0xb7, &k3, 1);
+
+ pr_info("pcr mk:0x%x 0x%x 0x%x 0x%x\n",
+ m, k1, k2, k3);
+}
+
+static void lt9611_sleep_setup(struct lt9611 *pdata)
+{
+ struct lt9611_reg_cfg sleep_setup[] = {
+ {0xff, 0x80, 0}, //register I2C addr
+ {0x24, 0x76, 0},
+ {0x23, 0x01, 0},
+ {0xff, 0x81, 0}, //set addr pin as output
+ {0x57, 0x03, 0},
+ {0x49, 0x0b, 0},
+ {0xff, 0x81, 0}, //anlog power down
+ {0x51, 0x30, 0}, //disable IRQ
+ {0x02, 0x48, 0}, //MIPI Rx power down
+ {0x23, 0x80, 0},
+ {0x30, 0x00, 0},
+ {0x00, 0x01, 0}, //bandgap power down
+ {0x01, 0x00, 0}, //system clk power down
+ };
+
+ pr_err("sleep\n");
+
+ lt9611_write_array(pdata, sleep_setup, sizeof(sleep_setup));
+}
+
+static int lt9611_power_on(struct lt9611 *pdata, bool on)
+{
+ int ret = 0;
+
+ pr_debug("power_on: on=%d\n", on);
+
+ if (on && !pdata->power_on) {
+ lt9611_write_array(pdata, lt9611_init_setup,
+ sizeof(lt9611_init_setup));
+
+ ret = lt9611_enable_interrupts(pdata, CFG_HPD_INTERRUPTS);
+ if (ret) {
+ pr_err("Failed to enable HPD intr %d\n", ret);
+ return ret;
+ }
+ pdata->power_on = true;
+ } else if (!on) {
+ lt9611_write(pdata, 0xff, 0x81);
+ lt9611_write(pdata, 0x30, 0x6a);
+
+ pdata->power_on = false;
+ }
+
+ return ret;
+}
+
+static int lt9611_video_on(struct lt9611 *pdata, bool on)
+{
+ int ret = 0;
+ struct lt9611_video_cfg *cfg = &pdata->video_cfg;
+
+ pr_debug("on=%d\n", on);
+
+ if (on) {
+ lt9611_mipi_input_analog(pdata, cfg);
+ lt9611_mipi_input_digital(pdata, cfg);
+ lt9611_pll_setup(pdata, cfg);
+ lt9611_mipi_video_setup(pdata, cfg);
+ lt9611_pcr_setup(pdata, cfg);
+ lt9611_hdmi_tx_digital(pdata, cfg);
+ lt9611_hdmi_tx_phy(pdata, cfg);
+
+ msleep(500);
+
+ lt9611_video_check(pdata);
+ lt9611_hdmi_output_enable(pdata);
+ } else {
+ lt9611_hdmi_output_disable(pdata);
+ }
+
+ return ret;
+}
+
+static void lt9611_mipi_byte_clk_debug(struct lt9611 *pdata)
+{
+ u8 reg_val = 0;
+ u32 byte_clk;
+
+ /* port A byte clk meter */
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_write(pdata, 0xc7, 0x03); /* port A */
+ msleep(50);
+ lt9611_read(pdata, 0xcd, ®_val, 1);
+
+ if ((reg_val & 0x60) == 0x60) {
+ byte_clk = (reg_val & 0x0f) * 65536;
+ lt9611_read(pdata, 0xce, ®_val, 1);
+ byte_clk = byte_clk + reg_val * 256;
+ lt9611_read(pdata, 0xcf, ®_val, 1);
+ byte_clk = byte_clk + reg_val;
+
+ pr_info("port A byte clk = %d khz,\n", byte_clk);
+ } else
+ pr_info("port A byte clk unstable\n");
+
+ /* port B byte clk meter */
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_write(pdata, 0xc7, 0x04); /* port B */
+ msleep(50);
+ lt9611_read(pdata, 0xcd, ®_val, 1);
+
+ if ((reg_val & 0x60) == 0x60) {
+ byte_clk = (reg_val & 0x0f) * 65536;
+ lt9611_read(pdata, 0xce, ®_val, 1);
+ byte_clk = byte_clk + reg_val * 256;
+ lt9611_read(pdata, 0xcf, ®_val, 1);
+ byte_clk = byte_clk + reg_val;
+
+ pr_info("port B byte clk = %d khz,\n", byte_clk);
+ } else
+ pr_info("port B byte clk unstable\n");
+}
+
+static void lt9611_reset(struct lt9611 *pdata)
+{
+ gpio_set_value(pdata->reset_gpio, 1);
+ msleep(20);
+ gpio_set_value(pdata->reset_gpio, 0);
+ msleep(20);
+ gpio_set_value(pdata->reset_gpio, 1);
+ msleep(20);
+}
+
+static void lt9611_assert_5v(struct lt9611 *pdata)
+{
+ if (gpio_is_valid(pdata->hdmi_en_gpio)) {
+ gpio_set_value(pdata->hdmi_en_gpio, 1);
+ msleep(20);
+ }
+}
+
+static int lt9611_config_vreg(struct device *dev,
+ struct lt9611_vreg *in_vreg, int num_vreg, bool config)
+{
+ int i = 0, rc = 0;
+ struct lt9611_vreg *curr_vreg = NULL;
+
+ if (!in_vreg || !num_vreg)
+ return rc;
+
+ if (config) {
+ for (i = 0; i < num_vreg; i++) {
+ curr_vreg = &in_vreg[i];
+ curr_vreg->vreg = regulator_get(dev,
+ curr_vreg->vreg_name);
+ rc = PTR_RET(curr_vreg->vreg);
+ if (rc) {
+ pr_err("%s get failed. rc=%d\n",
+ curr_vreg->vreg_name, rc);
+ curr_vreg->vreg = NULL;
+ goto vreg_get_fail;
+ }
+
+ rc = regulator_set_voltage(
+ curr_vreg->vreg,
+ curr_vreg->min_voltage,
+ curr_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s set vltg fail\n",
+ curr_vreg->vreg_name);
+ goto vreg_set_voltage_fail;
+ }
+ }
+ } else {
+ for (i = num_vreg-1; i >= 0; i--) {
+ curr_vreg = &in_vreg[i];
+ if (curr_vreg->vreg) {
+ regulator_set_voltage(curr_vreg->vreg,
+ 0, curr_vreg->max_voltage);
+
+ regulator_put(curr_vreg->vreg);
+ curr_vreg->vreg = NULL;
+ }
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+ regulator_set_load(curr_vreg->vreg, 0);
+
+vreg_set_voltage_fail:
+ regulator_put(curr_vreg->vreg);
+ curr_vreg->vreg = NULL;
+
+vreg_get_fail:
+ for (i--; i >= 0; i--) {
+ curr_vreg = &in_vreg[i];
+ goto vreg_unconfig;
+ }
+ return rc;
+}
+
+static int lt9611_get_dt_supply(struct device *dev,
+ struct lt9611 *pdata)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_root_node = NULL;
+ struct device_node *supply_node = NULL;
+
+ if (!dev || !pdata) {
+ pr_err("invalid input param dev:%pK pdata:%pK\n", dev, pdata);
+ return -EINVAL;
+ }
+
+ of_node = dev->of_node;
+
+ pdata->num_vreg = 0;
+ supply_root_node = of_get_child_by_name(of_node,
+ "lt,supply-entries");
+ if (!supply_root_node) {
+ pr_info("no supply entry present\n");
+ return 0;
+ }
+
+ pdata->num_vreg = of_get_available_child_count(supply_root_node);
+ if (pdata->num_vreg == 0) {
+ pr_info("no vreg present\n");
+ return 0;
+ }
+
+ pr_debug("vreg found. count=%d\n", pdata->num_vreg);
+ pdata->vreg_config = devm_kzalloc(dev, sizeof(struct lt9611_vreg) *
+ pdata->num_vreg, GFP_KERNEL);
+ if (!pdata->vreg_config)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(supply_root_node, supply_node) {
+ const char *st = NULL;
+
+ rc = of_property_read_string(supply_node,
+ "lt,supply-name", &st);
+ if (rc) {
+ pr_err("error reading name. rc=%d\n", rc);
+ goto error;
+ }
+
+ strlcpy(pdata->vreg_config[i].vreg_name, st,
+ sizeof(pdata->vreg_config[i].vreg_name));
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading min volt. rc=%d\n", rc);
+ goto error;
+ }
+ pdata->vreg_config[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading max volt. rc=%d\n", rc);
+ goto error;
+ }
+ pdata->vreg_config[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-enable-load", &tmp);
+ if (rc)
+ pr_debug("no supply enable load value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].enable_load = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-disable-load", &tmp);
+ if (rc)
+ pr_debug("no supply disable load value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].disable_load = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-pre-on-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply pre on sleep value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-pre-off-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply pre off sleep value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-post-on-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply post on sleep value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "lt,supply-post-off-sleep", &tmp);
+ if (rc)
+ pr_debug("no supply post off sleep value. rc=%d\n", rc);
+
+ pdata->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
+ pdata->vreg_config[i].vreg_name,
+ pdata->vreg_config[i].min_voltage,
+ pdata->vreg_config[i].max_voltage,
+ pdata->vreg_config[i].enable_load,
+ pdata->vreg_config[i].disable_load,
+ pdata->vreg_config[i].pre_on_sleep,
+ pdata->vreg_config[i].post_on_sleep,
+ pdata->vreg_config[i].pre_off_sleep,
+ pdata->vreg_config[i].post_off_sleep);
+ ++i;
+
+ rc = 0;
+ }
+
+ rc = lt9611_config_vreg(dev,
+ pdata->vreg_config, pdata->num_vreg, true);
+ if (rc)
+ goto error;
+
+ return rc;
+
+error:
+ if (pdata->vreg_config) {
+ devm_kfree(dev, pdata->vreg_config);
+ pdata->vreg_config = NULL;
+ pdata->num_vreg = 0;
+ }
+
+ return rc;
+}
+
+static void lt9611_put_dt_supply(struct device *dev,
+ struct lt9611 *pdata)
+{
+ if (!dev || !pdata) {
+ pr_err("invalid input param dev:%pK pdata:%pK\n", dev, pdata);
+ return;
+ }
+
+ lt9611_config_vreg(dev,
+ pdata->vreg_config, pdata->num_vreg, false);
+
+ if (pdata->vreg_config) {
+ devm_kfree(dev, pdata->vreg_config);
+ pdata->vreg_config = NULL;
+ }
+ pdata->num_vreg = 0;
+}
+
+static int lt9611_enable_vreg(struct lt9611 *pdata, int enable)
+{
+ int i = 0, rc = 0;
+ bool need_sleep;
+ struct lt9611_vreg *in_vreg = pdata->vreg_config;
+ int num_vreg = pdata->num_vreg;
+
+ if (enable) {
+ for (i = 0; i < num_vreg; i++) {
+ rc = PTR_RET(in_vreg[i].vreg);
+ if (rc) {
+ pr_err("%s regulator error. rc=%d\n",
+ in_vreg[i].vreg_name, rc);
+ goto vreg_set_opt_mode_fail;
+ }
+ need_sleep = !regulator_is_enabled(in_vreg[i].vreg);
+ if (in_vreg[i].pre_on_sleep && need_sleep)
+ usleep_range(in_vreg[i].pre_on_sleep * 1000,
+ in_vreg[i].pre_on_sleep * 1000);
+ rc = regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].enable_load);
+ if (rc < 0) {
+ pr_err("%s set opt m fail\n",
+ in_vreg[i].vreg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ rc = regulator_enable(in_vreg[i].vreg);
+ if (in_vreg[i].post_on_sleep && need_sleep)
+ usleep_range(in_vreg[i].post_on_sleep * 1000,
+ in_vreg[i].post_on_sleep * 1000);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ in_vreg[i].vreg_name);
+ goto disable_vreg;
+ }
+ }
+ } else {
+ for (i = num_vreg-1; i >= 0; i--) {
+ if (in_vreg[i].pre_off_sleep)
+ usleep_range(in_vreg[i].pre_off_sleep * 1000,
+ in_vreg[i].pre_off_sleep * 1000);
+ regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
+ regulator_disable(in_vreg[i].vreg);
+ if (in_vreg[i].post_off_sleep)
+ usleep_range(in_vreg[i].post_off_sleep * 1000,
+ in_vreg[i].post_off_sleep * 1000);
+ }
+ }
+ return rc;
+
+disable_vreg:
+ regulator_set_load(in_vreg[i].vreg, in_vreg[i].disable_load);
+
+vreg_set_opt_mode_fail:
+ for (i--; i >= 0; i--) {
+ if (in_vreg[i].pre_off_sleep)
+ usleep_range(in_vreg[i].pre_off_sleep * 1000,
+ in_vreg[i].pre_off_sleep * 1000);
+ regulator_set_load(in_vreg[i].vreg,
+ in_vreg[i].disable_load);
+ regulator_disable(in_vreg[i].vreg);
+ if (in_vreg[i].post_off_sleep)
+ usleep_range(in_vreg[i].post_off_sleep * 1000,
+ in_vreg[i].post_off_sleep * 1000);
+ }
+
+ return rc;
+}
+
+static struct lt9611_timing_info *lt9611_get_supported_timing(
+ struct drm_display_mode *mode) {
+ int i = 0;
+
+ while (lt9611_supp_timing_cfg[i].xres != 0xffff) {
+ if (lt9611_supp_timing_cfg[i].xres == mode->hdisplay &&
+ lt9611_supp_timing_cfg[i].yres == mode->vdisplay &&
+ lt9611_supp_timing_cfg[i].fps ==
+ drm_mode_vrefresh(mode)) {
+ return <9611_supp_timing_cfg[i];
+ }
+ i++;
+ }
+
+ return NULL;
+}
+
+/* TODO: intf/lane number needs info from both DSI host and client */
+static int lt9611_get_intf_num(struct lt9611 *pdata,
+ struct drm_display_mode *mode)
+{
+ int num_of_intfs = 0;
+ struct lt9611_timing_info *timing =
+ lt9611_get_supported_timing(mode);
+
+ if (timing)
+ num_of_intfs = timing->intfs;
+ else {
+ pr_err("interface number not defined by bridge chip\n");
+ num_of_intfs = 0;
+ }
+
+ return num_of_intfs;
+}
+
+static int lt9611_get_lane_num(struct lt9611 *pdata,
+ struct drm_display_mode *mode)
+{
+ int num_of_lanes = 0;
+ struct lt9611_timing_info *timing =
+ lt9611_get_supported_timing(mode);
+
+ if (timing)
+ num_of_lanes = timing->lanes;
+ else {
+ pr_err("lane number not defined by bridge chip\n");
+ num_of_lanes = 0;
+ }
+
+ return num_of_lanes;
+}
+
+static void lt9611_get_video_cfg(struct lt9611 *pdata,
+ struct drm_display_mode *mode,
+ struct lt9611_video_cfg *video_cfg)
+{
+ int rc = 0;
+ struct hdmi_avi_infoframe avi_frame;
+
+ memset(&avi_frame, 0, sizeof(avi_frame));
+
+ video_cfg->h_active = mode->hdisplay;
+ video_cfg->v_active = mode->vdisplay;
+ video_cfg->h_front_porch = mode->hsync_start - mode->hdisplay;
+ video_cfg->v_front_porch = mode->vsync_start - mode->vdisplay;
+ video_cfg->h_back_porch = mode->htotal - mode->hsync_end;
+ video_cfg->v_back_porch = mode->vtotal - mode->vsync_end;
+ video_cfg->h_pulse_width = mode->hsync_end - mode->hsync_start;
+ video_cfg->v_pulse_width = mode->vsync_end - mode->vsync_start;
+ video_cfg->pclk_khz = mode->clock;
+
+ video_cfg->h_polarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC);
+ video_cfg->v_polarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC);
+
+ video_cfg->num_of_lanes = lt9611_get_lane_num(pdata, mode);
+ video_cfg->num_of_intfs = lt9611_get_intf_num(pdata, mode);
+
+ pr_debug("video=h[%d,%d,%d,%d] v[%d,%d,%d,%d] pclk=%d lane=%d intf=%d\n",
+ video_cfg->h_active, video_cfg->h_front_porch,
+ video_cfg->h_pulse_width, video_cfg->h_back_porch,
+ video_cfg->v_active, video_cfg->v_front_porch,
+ video_cfg->v_pulse_width, video_cfg->v_back_porch,
+ video_cfg->pclk_khz, video_cfg->num_of_lanes,
+ video_cfg->num_of_intfs);
+
+ rc = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, mode);
+ if (rc) {
+ pr_err("get avi frame failed ret=%d\n", rc);
+ } else {
+ video_cfg->scaninfo = avi_frame.scan_mode;
+ video_cfg->ar = avi_frame.picture_aspect;
+ video_cfg->vic = avi_frame.video_code;
+ pr_debug("scaninfo=%d ar=%d vic=%d\n",
+ video_cfg->scaninfo, video_cfg->ar, video_cfg->vic);
+ }
+}
+
+/* connector funcs */
+static enum drm_connector_status
+lt9611_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct lt9611 *pdata = connector_to_lt9611(connector);
+
+ if (!pdata->non_pluggable) {
+ u8 reg_val = 0;
+ int connected = 0;
+
+ lt9611_write(pdata, 0xff, 0x82);
+ lt9611_read(pdata, 0x5e, ®_val, 1);
+ connected = (reg_val & BIT(2));
+ pr_debug("connected = %x\n", connected);
+
+ pdata->status = connected ? connector_status_connected :
+ connector_status_disconnected;
+ } else
+ pdata->status = connector_status_connected;
+
+ return pdata->status;
+}
+
+static int lt9611_read_edid(struct lt9611 *pdata)
+{
+ int ret = 0;
+ u8 i, j;
+ u8 temp = 0;
+
+ if (!pdata) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ memset(pdata->edid_buf, 0, EDID_SEG_SIZE);
+
+ lt9611_write(pdata, 0xff, 0x85);
+ lt9611_write(pdata, 0x03, 0xc9);
+ lt9611_write(pdata, 0x04, 0xa0); /* 0xA0 is EDID device address */
+ lt9611_write(pdata, 0x05, 0x00); /* 0x00 is EDID offset address */
+ lt9611_write(pdata, 0x06, 0x20); /* length for read */
+ lt9611_write(pdata, 0x14, 0x7f);
+
+ for (i = 0 ; i < 8 ; i++) {
+ lt9611_write(pdata, 0x05, i * 32); /* offset address */
+ lt9611_write(pdata, 0x07, 0x36);
+ lt9611_write(pdata, 0x07, 0x31);
+ lt9611_write(pdata, 0x07, 0x37);
+ usleep_range(5000, 10000);
+
+ lt9611_read(pdata, 0x40, &temp, 1);
+
+ if (temp & 0x02) { /*KEY_DDC_ACCS_DONE=1*/
+ for (j = 0; j < 32; j++) {
+ lt9611_read(pdata, 0x83,
+ &(pdata->edid_buf[i*32+j]), 1);
+ }
+ } else if (temp & 0x50) { /* DDC No Ack or Abitration lost */
+ pr_err("read edid failed: no ack\n");
+ ret = -EIO;
+ goto end;
+ } else {
+ pr_err("read edid failed: access not done\n");
+ ret = -EIO;
+ goto end;
+ }
+ }
+
+ pr_debug("read edid succeeded, checksum = 0x%x\n",
+ pdata->edid_buf[255]);
+
+end:
+ lt9611_write(pdata, 0x07, 0x1f);
+ return ret;
+}
+
+/* TODO: add support for more extenstion blocks */
+static int lt9611_get_edid_block(void *data, u8 *buf, unsigned int block,
+ size_t len)
+{
+ struct lt9611 *pdata = data;
+ int ret = 0;
+
+ pr_debug("get edid block: block=%d, len=%d\n", block, (int)len);
+
+ if (len > 128)
+ return -EINVAL;
+
+ /* support up to 1 extension block */
+ if (block > 1)
+ return -EINVAL;
+
+ if (block == 0) {
+ /* always read 2 edid blocks once */
+ ret = lt9611_read_edid(pdata);
+ if (ret) {
+ pr_err("edid read failed\n");
+ return ret;
+ }
+ }
+
+ if (block % 2 == 0)
+ memcpy(buf, pdata->edid_buf, len);
+ else
+ memcpy(buf, pdata->edid_buf + 128, len);
+
+ return 0;
+}
+
+static int lt9611_connector_get_modes(struct drm_connector *connector)
+{
+ struct lt9611 *pdata = connector_to_lt9611(connector);
+ struct drm_display_mode *mode, *m;
+ unsigned int count = 0;
+
+ pr_debug("get modes\n");
+
+ if (pdata->non_pluggable) {
+ list_for_each_entry(mode, &pdata->mode_list, head) {
+ m = drm_mode_duplicate(connector->dev, mode);
+ if (!m) {
+ pr_err("failed to add hdmi mode %dx%d\n",
+ mode->hdisplay, mode->vdisplay);
+ break;
+ }
+ drm_mode_probed_add(connector, m);
+ }
+ count = pdata->num_of_modes;
+ } else {
+ struct edid *edid;
+
+ if (!pdata->power_on)
+ lt9611_power_on(pdata, true);
+ edid = drm_do_get_edid(connector, lt9611_get_edid_block, pdata);
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ count = drm_add_edid_modes(connector, edid);
+
+ pdata->hdmi_mode = drm_detect_hdmi_monitor(edid);
+ pr_debug("hdmi_mode = %d\n", pdata->hdmi_mode);
+
+ /* TODO: this should not be hard coded */
+ drm_set_preferred_mode(connector, 1920, 1080);
+
+ kfree(edid);
+ }
+
+ return count;
+}
+
+static enum drm_mode_status lt9611_connector_mode_valid(
+ struct drm_connector *connector, struct drm_display_mode *mode)
+{
+ struct lt9611_timing_info *timing =
+ lt9611_get_supported_timing(mode);
+
+ return timing ? MODE_OK : MODE_BAD;
+}
+
+/* bridge funcs */
+static void lt9611_bridge_enable(struct drm_bridge *bridge)
+{
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+
+ pr_debug("bridge enable\n");
+
+ if (lt9611_power_on(pdata, true)) {
+ pr_err("power on failed\n");
+ return;
+ }
+
+ if (lt9611_video_on(pdata, true)) {
+ pr_err("video on failed\n");
+ return;
+ }
+}
+
+static void lt9611_bridge_disable(struct drm_bridge *bridge)
+{
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+
+ pr_debug("bridge disable\n");
+
+ if (lt9611_video_on(pdata, false)) {
+ pr_err("video on failed\n");
+ return;
+ }
+
+ if (lt9611_power_on(pdata, false)) {
+ pr_err("power on failed\n");
+ return;
+ }
+}
+
+static void lt9611_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+ struct lt9611_video_cfg *video_cfg = &pdata->video_cfg;
+ int ret = 0;
+
+ pr_debug("bridge mode_set: hdisplay=%d, vdisplay=%d, vrefresh=%d, clock=%d\n",
+ adj_mode->hdisplay, adj_mode->vdisplay,
+ adj_mode->vrefresh, adj_mode->clock);
+
+ drm_mode_copy(&pdata->curr_mode, adj_mode);
+
+ memset(video_cfg, 0, sizeof(struct lt9611_video_cfg));
+ lt9611_get_video_cfg(pdata, adj_mode, video_cfg);
+
+ /* TODO: update intf number of host */
+ if (video_cfg->num_of_lanes != pdata->dsi->lanes) {
+ mipi_dsi_detach(pdata->dsi);
+ pdata->dsi->lanes = video_cfg->num_of_lanes;
+ ret = mipi_dsi_attach(pdata->dsi);
+ if (ret)
+ pr_err("failed to change host lanes\n");
+ }
+}
+
+static int lt9611_bridge_attach(struct drm_bridge *bridge)
+{
+ struct mipi_dsi_host *host;
+ struct mipi_dsi_device *dsi;
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+ int ret;
+ const struct mipi_dsi_device_info info = { .type = "lt9611",
+ .channel = 0,
+ .node = NULL,
+ };
+
+ pr_debug("bridge attach\n");
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ host = of_find_mipi_dsi_host_by_node(pdata->host_node);
+ if (!host) {
+ pr_err("failed to find dsi host\n");
+ return -EPROBE_DEFER;
+ }
+
+ dsi = mipi_dsi_device_register_full(host, &info);
+ if (IS_ERR(dsi)) {
+ pr_err("failed to create dsi device\n");
+ ret = PTR_ERR(dsi);
+ goto err_dsi_device;
+ }
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO_BLLP |
+ MIPI_DSI_MODE_VIDEO_EOF_BLLP;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ pr_err("failed to attach dsi to host\n");
+ goto err_dsi_attach;
+ }
+
+ pdata->dsi = dsi;
+
+ return 0;
+
+err_dsi_attach:
+ mipi_dsi_device_unregister(dsi);
+err_dsi_device:
+ return ret;
+}
+
+static void lt9611_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+
+ pr_debug("bridge pre_enable\n");
+
+ lt9611_reset(pdata);
+
+ lt9611_write(pdata, 0xff, 0x80);
+ lt9611_write(pdata, 0xee, 0x01);
+}
+
+static bool lt9611_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ pr_debug("bridge mode_fixup\n");
+
+ return true;
+}
+
+static void lt9611_bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct lt9611 *pdata = bridge_to_lt9611(bridge);
+
+ pr_debug("bridge post_disable\n");
+
+ lt9611_sleep_setup(pdata);
+}
+
+static struct drm_connector_funcs override_funcs;
+static struct drm_connector_helper_funcs override_helper_private;
+
+static int lt9611_bridge_connector_init(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ pr_debug("bridge connector_init\n");
+
+ if (connector->encoder != bridge->encoder) {
+ pr_err("bridge and connector need attach to the same encoder\n");
+ return -EINVAL;
+ }
+
+ connector->private = bridge;
+
+ /*
+ * Make a copy of drm_connector_funcs and drm_connector_helper_funcs. To
+ * make sure other KMS components won't be broken. For example, if other
+ * connectors share the implementation for ->funs, overwriting this will
+ * break other connectors.
+ */
+ override_funcs = *connector->funcs;
+ override_funcs.detect = lt9611_connector_detect;
+ connector->funcs = &override_funcs;
+
+ override_helper_private = *connector->helper_private;
+ override_helper_private.get_modes = lt9611_connector_get_modes;
+ override_helper_private.mode_valid = lt9611_connector_mode_valid;
+ connector->helper_private = &override_helper_private;
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs lt9611_bridge_funcs = {
+ .attach = lt9611_bridge_attach,
+ .mode_fixup = lt9611_bridge_mode_fixup,
+ .pre_enable = lt9611_bridge_pre_enable,
+ .enable = lt9611_bridge_enable,
+ .disable = lt9611_bridge_disable,
+ .post_disable = lt9611_bridge_post_disable,
+ .mode_set = lt9611_bridge_mode_set,
+ .connector_init = lt9611_bridge_connector_init,
+};
+
+/* sysfs */
+static int lt9611_dump_debug_info(struct lt9611 *pdata)
+{
+ if (!pdata->power_on) {
+ pr_err("device is not power on\n");
+ return -EINVAL;
+ }
+
+ lt9611_video_check(pdata);
+
+ lt9611_pcr_mk_debug(pdata);
+
+ lt9611_mipi_byte_clk_debug(pdata);
+
+ return 0;
+}
+
+static ssize_t lt9611_dump_info_wta_attr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct lt9611 *pdata = dev_get_drvdata(dev);
+
+ if (!pdata) {
+ pr_err("pdata is NULL\n");
+ return -EINVAL;
+ }
+
+ lt9611_dump_debug_info(pdata);
+
+ return count;
+}
+
+static DEVICE_ATTR(dump_info, 0200, NULL, lt9611_dump_info_wta_attr);
+
+static struct attribute *lt9611_sysfs_attrs[] = {
+ &dev_attr_dump_info.attr,
+ NULL,
+};
+
+static struct attribute_group lt9611_sysfs_attr_grp = {
+ .attrs = lt9611_sysfs_attrs,
+};
+
+static int lt9611_sysfs_init(struct device *dev)
+{
+ int rc = 0;
+
+ if (!dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = sysfs_create_group(&dev->kobj, <9611_sysfs_attr_grp);
+ if (rc)
+ pr_err("%s: sysfs group creation failed %d\n", __func__, rc);
+
+ return rc;
+}
+
+static void lt9611_sysfs_remove(struct device *dev)
+{
+ if (!dev) {
+ pr_err("%s: Invalid params\n", __func__);
+ return;
+ }
+
+ sysfs_remove_group(&dev->kobj, <9611_sysfs_attr_grp);
+}
+
+static int lt9611_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lt9611 *pdata;
+ int ret = 0;
+
+ if (!client || !client->dev.of_node) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("device doesn't support I2C\n");
+ return -ENODEV;
+ }
+
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct lt9611), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = lt9611_parse_dt(&client->dev, pdata);
+ if (ret) {
+ pr_err("failed to parse device tree\n");
+ goto err_dt_parse;
+ }
+
+ ret = lt9611_get_dt_supply(&client->dev, pdata);
+ if (ret) {
+ pr_err("failed to get dt supply\n");
+ goto err_dt_parse;
+ }
+
+ pdata->dev = &client->dev;
+ pdata->i2c_client = client;
+ pr_debug("I2C address is %x\n", client->addr);
+
+ ret = lt9611_gpio_configure(pdata, true);
+ if (ret) {
+ pr_err("failed to configure GPIOs\n");
+ goto err_dt_supply;
+ }
+
+ lt9611_assert_5v(pdata);
+
+ ret = lt9611_enable_vreg(pdata, true);
+ if (ret) {
+ pr_err("failed to enable vreg\n");
+ goto err_dt_supply;
+ }
+
+ lt9611_reset(pdata);
+
+ ret = lt9611_read_device_rev(pdata);
+ if (ret) {
+ pr_err("failed to read chip rev\n");
+ goto err_i2c_prog;
+ }
+
+ pdata->irq = gpio_to_irq(pdata->irq_gpio);
+ ret = request_threaded_irq(pdata->irq, NULL, lt9611_irq_thread_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lt9611", pdata);
+ if (ret) {
+ pr_err("failed to request irq\n");
+ goto err_i2c_prog;
+ }
+
+ i2c_set_clientdata(client, pdata);
+ dev_set_drvdata(&client->dev, pdata);
+
+ ret = lt9611_sysfs_init(&client->dev);
+ if (ret) {
+ pr_err("sysfs init failed\n");
+ goto err_sysfs_init;
+ }
+
+ pdata->bridge.funcs = <9611_bridge_funcs;
+ pdata->bridge.of_node = client->dev.of_node;
+
+ drm_bridge_add(&pdata->bridge);
+
+ return ret;
+
+err_sysfs_init:
+ disable_irq(pdata->irq);
+ free_irq(pdata->irq, pdata);
+err_i2c_prog:
+ lt9611_gpio_configure(pdata, false);
+err_dt_supply:
+ lt9611_put_dt_supply(&client->dev, pdata);
+err_dt_parse:
+ devm_kfree(&client->dev, pdata);
+ return ret;
+}
+
+static int lt9611_remove(struct i2c_client *client)
+{
+ int ret = -EINVAL;
+ struct lt9611 *pdata = i2c_get_clientdata(client);
+ struct drm_display_mode *mode, *n;
+
+ if (!pdata)
+ goto end;
+
+ mipi_dsi_detach(pdata->dsi);
+ mipi_dsi_device_unregister(pdata->dsi);
+
+ drm_bridge_remove(&pdata->bridge);
+
+ lt9611_sysfs_remove(&client->dev);
+
+ disable_irq(pdata->irq);
+ free_irq(pdata->irq, pdata);
+
+ ret = lt9611_gpio_configure(pdata, false);
+
+ lt9611_put_dt_supply(&client->dev, pdata);
+
+ if (pdata->non_pluggable) {
+ list_for_each_entry_safe(mode, n, &pdata->mode_list, head) {
+ list_del(&mode->head);
+ kfree(mode);
+ }
+ }
+
+ devm_kfree(&client->dev, pdata);
+
+end:
+ return ret;
+}
+
+
+static struct i2c_device_id lt9611_id[] = {
+ { "lt,lt9611", 0},
+ {}
+};
+
+static const struct of_device_id lt9611_match_table[] = {
+ {.compatible = "lt,lt9611"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, lt9611_match_table);
+
+static struct i2c_driver lt9611_driver = {
+ .driver = {
+ .name = "lt9611",
+ .owner = THIS_MODULE,
+ .of_match_table = lt9611_match_table,
+ },
+ .probe = lt9611_probe,
+ .remove = lt9611_remove,
+ .id_table = lt9611_id,
+};
+
+static int __init lt9611_init(void)
+{
+ return i2c_add_driver(<9611_driver);
+}
+
+static void __exit lt9611_exit(void)
+{
+ i2c_del_driver(<9611_driver);
+}
+
+module_init(lt9611_init);
+module_exit(lt9611_exit);
+
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index c1a670d..d5437d0 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -668,6 +668,27 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
}
}
+ /**
+ * Since pp interrupt is heavy weight, try to queue the work
+ * into a dedicated worker thread, so that they dont interrupt
+ * other important events.
+ */
+ kthread_init_worker(&priv->pp_event_worker);
+ priv->pp_event_thread = kthread_run(kthread_worker_fn,
+ &priv->pp_event_worker, "pp_event");
+
+ ret = sched_setscheduler(priv->pp_event_thread,
+ SCHED_FIFO, ¶m);
+ if (ret)
+ pr_warn("pp_event thread priority update failed: %d\n",
+ ret);
+
+ if (IS_ERR(priv->pp_event_thread)) {
+ dev_err(dev, "failed to create pp_event kthread\n");
+ priv->pp_event_thread = NULL;
+ goto fail;
+ }
+
ret = drm_vblank_init(ddev, priv->num_crtcs);
if (ret < 0) {
dev_err(dev, "failed to initialize vblank\n");
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index cc09256..17a41d5 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -578,6 +578,9 @@ struct msm_drm_private {
struct msm_drm_thread disp_thread[MAX_CRTCS];
struct msm_drm_thread event_thread[MAX_CRTCS];
+ struct task_struct *pp_event_thread;
+ struct kthread_worker pp_event_worker;
+
unsigned int num_encoders;
struct drm_encoder *encoders[MAX_ENCODERS];
diff --git a/drivers/gpu/drm/msm/sde/sde_ad4.h b/drivers/gpu/drm/msm/sde/sde_ad4.h
index 06f004c..bf08360 100644
--- a/drivers/gpu/drm/msm/sde/sde_ad4.h
+++ b/drivers/gpu/drm/msm/sde/sde_ad4.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -48,6 +48,7 @@ enum ad_property {
AD_SUSPEND,
AD_ASSERTIVE,
AD_BACKLIGHT,
+ AD_STRENGTH,
AD_IPC_SUSPEND,
AD_IPC_RESUME,
AD_IPC_RESET,
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 3e084d5..3e3533e 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -69,6 +69,8 @@ static void dspp_igc_install_property(struct drm_crtc *crtc);
static void dspp_hist_install_property(struct drm_crtc *crtc);
+static void dspp_dither_install_property(struct drm_crtc *crtc);
+
typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);
static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -98,6 +100,7 @@ do { \
func[SDE_DSPP_GC] = dspp_gc_install_property; \
func[SDE_DSPP_IGC] = dspp_igc_install_property; \
func[SDE_DSPP_HIST] = dspp_hist_install_property; \
+ func[SDE_DSPP_DITHER] = dspp_dither_install_property; \
} while (0)
typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -133,6 +136,7 @@ enum {
SDE_CP_CRTC_DSPP_AD_INPUT,
SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS,
SDE_CP_CRTC_DSPP_AD_BACKLIGHT,
+ SDE_CP_CRTC_DSPP_AD_STRENGTH,
SDE_CP_CRTC_DSPP_MAX,
/* DSPP features end */
@@ -736,6 +740,13 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
}
hw_lm->ops.setup_gc(hw_lm, &hw_cfg);
break;
+ case SDE_CP_CRTC_DSPP_DITHER:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_dither) {
+ ret = -EINVAL;
+ continue;
+ }
+ hw_dspp->ops.setup_pa_dither(hw_dspp, &hw_cfg);
+ break;
case SDE_CP_CRTC_DSPP_HIST_CTRL:
if (!hw_dspp || !hw_dspp->ops.setup_histogram) {
ret = -EINVAL;
@@ -805,6 +816,15 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
break;
+ case SDE_CP_CRTC_DSPP_AD_STRENGTH:
+ if (!hw_dspp || !hw_dspp->ops.setup_ad) {
+ ret = -EINVAL;
+ continue;
+ }
+ ad_cfg.prop = AD_STRENGTH;
+ ad_cfg.hw_cfg = &hw_cfg;
+ hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ break;
default:
ret = -EINVAL;
break;
@@ -1365,6 +1385,9 @@ static void dspp_ad_install_property(struct drm_crtc *crtc)
sde_cp_crtc_install_range_property(crtc,
"SDE_DSPP_AD_V4_ASSERTIVENESS",
SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS, 0, (BIT(8) - 1), 0);
+ sde_cp_crtc_install_range_property(crtc,
+ "SDE_DSPP_AD_V4_STRENGTH",
+ SDE_CP_CRTC_DSPP_AD_STRENGTH, 0, (BIT(10) - 1), 0);
sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_AD_V4_INPUT",
SDE_CP_CRTC_DSPP_AD_INPUT, 0, U16_MAX, 0);
sde_cp_crtc_install_range_property(crtc,
@@ -1498,6 +1521,31 @@ static void dspp_hist_install_property(struct drm_crtc *crtc)
}
}
+static void dspp_dither_install_property(struct drm_crtc *crtc)
+{
+ char feature_name[256];
+ struct sde_kms *kms = NULL;
+ struct sde_mdss_cfg *catalog = NULL;
+ u32 version;
+
+ kms = get_kms(crtc);
+ catalog = kms->catalog;
+
+ version = catalog->dspp[0].sblk->dither.version >> 16;
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_DITHER_V", version);
+ switch (version) {
+ case 1:
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_DITHER,
+ sizeof(struct drm_msm_pa_dither));
+ break;
+ default:
+ DRM_ERROR("version %d not supported\n", version);
+ break;
+ }
+}
+
static void sde_cp_update_list(struct sde_cp_node *prop_node,
struct sde_crtc *crtc, bool dirty_list)
{
@@ -1508,6 +1556,7 @@ static void sde_cp_update_list(struct sde_cp_node *prop_node,
case SDE_CP_CRTC_DSPP_AD_INPUT:
case SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS:
case SDE_CP_CRTC_DSPP_AD_BACKLIGHT:
+ case SDE_CP_CRTC_DSPP_AD_STRENGTH:
if (dirty_list)
list_add_tail(&prop_node->dirty_list, &crtc->ad_dirty);
else
@@ -1556,6 +1605,9 @@ static int sde_cp_ad_validate_prop(struct sde_cp_node *prop_node,
case SDE_CP_CRTC_DSPP_AD_BACKLIGHT:
ad_prop = AD_BACKLIGHT;
break;
+ case SDE_CP_CRTC_DSPP_AD_STRENGTH:
+ ad_prop = AD_STRENGTH;
+ break;
default:
/* Not an AD property */
return 0;
@@ -1573,7 +1625,8 @@ static void sde_cp_ad_interrupt_cb(void *arg, int irq_idx)
{
struct sde_crtc *crtc = arg;
- sde_crtc_event_queue(&crtc->base, sde_cp_notify_ad_event, NULL);
+ sde_crtc_event_queue(&crtc->base, sde_cp_notify_ad_event,
+ NULL, true);
}
static void sde_cp_notify_ad_event(struct drm_crtc *crtc_drm, void *arg)
@@ -1834,7 +1887,8 @@ static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx)
}
/* notify histogram event */
- sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL);
+ sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event,
+ NULL, true);
}
static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg)
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 8d2f115..8c1599e 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -5927,7 +5927,8 @@ static void _sde_crtc_event_cb(struct kthread_work *work)
}
int sde_crtc_event_queue(struct drm_crtc *crtc,
- void (*func)(struct drm_crtc *crtc, void *usr), void *usr)
+ void (*func)(struct drm_crtc *crtc, void *usr),
+ void *usr, bool color_processing_event)
{
unsigned long irq_flags;
struct sde_crtc *sde_crtc;
@@ -5966,7 +5967,11 @@ int sde_crtc_event_queue(struct drm_crtc *crtc,
/* queue new event request */
kthread_init_work(&event->kt_work, _sde_crtc_event_cb);
- kthread_queue_work(&priv->event_thread[crtc_id].worker,
+ if (color_processing_event)
+ kthread_queue_work(&priv->pp_event_worker,
+ &event->kt_work);
+ else
+ kthread_queue_work(&priv->event_thread[crtc_id].worker,
&event->kt_work);
return 0;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 78f15ec..21ce3db 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -664,10 +664,12 @@ static inline bool sde_crtc_is_reset_required(struct drm_crtc *crtc)
* @crtc: Pointer to drm crtc structure
* @func: Pointer to callback function
* @usr: Pointer to user data to be passed to callback
+ * @color_processing_event: True if color processing event
* Returns: Zero on success
*/
int sde_crtc_event_queue(struct drm_crtc *crtc,
- void (*func)(struct drm_crtc *crtc, void *usr), void *usr);
+ void (*func)(struct drm_crtc *crtc, void *usr),
+ void *usr, bool color_processing_event);
/**
* sde_crtc_res_add - add given resource to resource pool in crtc state
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
index 593e972..66445da 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
@@ -16,7 +16,12 @@
#include "sde_hw_lm.h"
#include "sde_ad4.h"
-#define AD_STATE_READY(x) ((x) == (ad4_init | ad4_cfg | ad4_mode | ad4_input))
+#define AD_STATE_READY(x) \
+ (((x) & ad4_init) && \
+ ((x) & ad4_cfg) && \
+ ((x) & ad4_mode) && \
+ (((x) & ad4_input) | ((x) & ad4_strength)))
+
#define MERGE_WIDTH_RIGHT 6
#define MERGE_WIDTH_LEFT 5
#define AD_IPC_FRAME_COUNT 2
@@ -26,6 +31,7 @@ enum ad4_ops_bitmask {
ad4_cfg = BIT(AD_CFG),
ad4_mode = BIT(AD_MODE),
ad4_input = BIT(AD_INPUT),
+ ad4_strength = BIT(AD_STRENGTH),
ad4_ops_max = BIT(31),
};
@@ -37,6 +43,8 @@ enum ad4_state {
ad4_state_ipcs,
/* idle power collapse resume state */
ad4_state_ipcr,
+ /* manual mode state */
+ ad4_state_manual,
ad4_state_max,
};
@@ -48,6 +56,8 @@ static int ad4_params_check(struct sde_hw_dspp *dspp,
static int ad4_no_op_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_setup_debug(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
+static int ad4_setup_debug_manual(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg);
static int ad4_mode_setup(struct sde_hw_dspp *dspp, enum ad4_modes mode);
static int ad4_mode_setup_common(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
@@ -81,6 +91,10 @@ static int ad4_backlight_setup(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
static int ad4_backlight_setup_ipcr(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
+static int ad4_strength_setup(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg);
+static int ad4_strength_setup_idle(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg);
static int ad4_ipc_suspend_setup_run(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
@@ -103,6 +117,7 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_idle][AD_SUSPEND] = ad4_suspend_setup,
[ad4_state_idle][AD_ASSERTIVE] = ad4_assertive_setup,
[ad4_state_idle][AD_BACKLIGHT] = ad4_backlight_setup,
+ [ad4_state_idle][AD_STRENGTH] = ad4_strength_setup_idle,
[ad4_state_idle][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESET] = ad4_no_op_setup,
@@ -115,6 +130,7 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_startup][AD_ASSERTIVE] = ad4_assertive_setup,
[ad4_state_startup][AD_BACKLIGHT] = ad4_backlight_setup,
[ad4_state_startup][AD_IPC_SUSPEND] = ad4_no_op_setup,
+ [ad4_state_startup][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESET] = ad4_ipc_reset_setup_startup,
@@ -125,6 +141,7 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_run][AD_SUSPEND] = ad4_suspend_setup,
[ad4_state_run][AD_ASSERTIVE] = ad4_assertive_setup,
[ad4_state_run][AD_BACKLIGHT] = ad4_backlight_setup,
+ [ad4_state_run][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_run][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_run,
[ad4_state_run][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_run][AD_IPC_RESET] = ad4_setup_debug,
@@ -136,6 +153,7 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_ipcs][AD_SUSPEND] = ad4_no_op_setup,
[ad4_state_ipcs][AD_ASSERTIVE] = ad4_no_op_setup,
[ad4_state_ipcs][AD_BACKLIGHT] = ad4_no_op_setup,
+ [ad4_state_ipcs][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_ipcs][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_ipcs][AD_IPC_RESUME] = ad4_ipc_resume_setup_ipcs,
[ad4_state_ipcs][AD_IPC_RESET] = ad4_no_op_setup,
@@ -147,9 +165,22 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_ipcr][AD_SUSPEND] = ad4_suspend_setup,
[ad4_state_ipcr][AD_ASSERTIVE] = ad4_assertive_setup_ipcr,
[ad4_state_ipcr][AD_BACKLIGHT] = ad4_backlight_setup_ipcr,
+ [ad4_state_ipcr][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_ipcr][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_ipcr,
[ad4_state_ipcr][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_ipcr][AD_IPC_RESET] = ad4_ipc_reset_setup_ipcr,
+
+ [ad4_state_manual][AD_MODE] = ad4_mode_setup_common,
+ [ad4_state_manual][AD_INIT] = ad4_init_setup,
+ [ad4_state_manual][AD_CFG] = ad4_cfg_setup,
+ [ad4_state_manual][AD_INPUT] = ad4_no_op_setup,
+ [ad4_state_manual][AD_SUSPEND] = ad4_no_op_setup,
+ [ad4_state_manual][AD_ASSERTIVE] = ad4_no_op_setup,
+ [ad4_state_manual][AD_BACKLIGHT] = ad4_no_op_setup,
+ [ad4_state_manual][AD_STRENGTH] = ad4_strength_setup,
+ [ad4_state_manual][AD_IPC_SUSPEND] = ad4_no_op_setup,
+ [ad4_state_manual][AD_IPC_RESUME] = ad4_no_op_setup,
+ [ad4_state_manual][AD_IPC_RESET] = ad4_setup_debug_manual,
};
struct ad4_info {
@@ -288,23 +319,33 @@ static int ad4_no_op_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
static int ad4_setup_debug(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
{
- u32 strength = 0, i = 0;
+ u32 strength = 0;
struct sde_hw_mixer *hw_lm;
hw_lm = cfg->hw_cfg->mixer_info;
- if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer) {
+ if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer)
/* this AD core is the salve core */
- for (i = DSPP_0; i < DSPP_MAX; i++) {
- if (info[i].is_master) {
- strength = info[i].last_str;
- break;
- }
- }
- } else {
- strength = SDE_REG_READ(&dspp->hw,
- dspp->cap->sblk->ad.base + 0x4c);
- pr_debug("%s(): AD strength = %d\n", __func__, strength);
- }
+ return 0;
+
+ strength = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x4c);
+ pr_debug("%s(): AD strength = %d\n", __func__, strength);
+
+ return 0;
+}
+
+static int ad4_setup_debug_manual(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg)
+{
+ u32 strength = 0;
+ struct sde_hw_mixer *hw_lm;
+
+ hw_lm = cfg->hw_cfg->mixer_info;
+ if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer)
+ /* this AD core is the salve core */
+ return 0;
+
+ strength = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x15c);
+ pr_debug("%s(): AD strength = %d in manual mode\n", __func__, strength);
return 0;
}
@@ -313,8 +354,8 @@ static int ad4_mode_setup(struct sde_hw_dspp *dspp, enum ad4_modes mode)
{
u32 blk_offset;
- blk_offset = 0x04;
if (mode == AD4_OFF) {
+ blk_offset = 0x04;
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
0x101);
info[dspp->idx].state = ad4_state_idle;
@@ -328,11 +369,30 @@ static int ad4_mode_setup(struct sde_hw_dspp *dspp, enum ad4_modes mode)
info[dspp->idx].last_als = 0x0;
info[dspp->idx].cached_als = U64_MAX;
} else {
- if (info[dspp->idx].state == ad4_state_idle) {
- info[dspp->idx].frame_count = 0;
- info[dspp->idx].state = ad4_state_startup;
- pr_debug("%s(): AD state move to startup\n", __func__);
+ if (mode == AD4_MANUAL) {
+ /*vc_control_0 */
+ blk_offset = 0x138;
+ SDE_REG_WRITE(&dspp->hw,
+ dspp->cap->sblk->ad.base + blk_offset, 0);
+ /* irdx_control_0 */
+ blk_offset = 0x13c;
+ SDE_REG_WRITE(&dspp->hw,
+ dspp->cap->sblk->ad.base + blk_offset,
+ info[dspp->idx].irdx_control_0);
}
+ if (info[dspp->idx].state == ad4_state_idle) {
+ if (mode == AD4_MANUAL) {
+ info[dspp->idx].state = ad4_state_manual;
+ pr_debug("%s(): AD state move to manual\n",
+ __func__);
+ } else {
+ info[dspp->idx].frame_count = 0;
+ info[dspp->idx].state = ad4_state_startup;
+ pr_debug("%s(): AD state move to startup\n",
+ __func__);
+ }
+ }
+ blk_offset = 0x04;
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
0x100);
}
@@ -831,7 +891,7 @@ static int ad4_cfg_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
info[dspp->idx].vc_control_0 = (ad_cfg->cfg_param_041 & (BIT(7) - 1));
- blk_offset += 160;
+ blk_offset = 0x160;
val = (ad_cfg->cfg_param_043 & (BIT(10) - 1));
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
@@ -1264,6 +1324,8 @@ void sde_read_intr_resp_ad4(struct sde_hw_dspp *dspp, u32 event,
dspp->cap->sblk->ad.base + 0x2c);
*resp_out = SDE_REG_READ(&dspp->hw,
dspp->cap->sblk->ad.base + 0x48);
+ pr_debug("%s(): AD4 input BL %u, output BL %u\n", __func__,
+ (*resp_in), (*resp_out));
break;
default:
break;
@@ -1446,3 +1508,41 @@ static int ad4_ipc_reset_setup_startup(struct sde_hw_dspp *dspp,
return 0;
}
+
+static int ad4_strength_setup(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg)
+{
+ u64 strength = 0, val;
+ u32 blk_offset = 0x15c;
+
+ if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
+ DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
+ sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
+ return -EINVAL;
+ }
+
+ if (cfg->hw_cfg->payload)
+ strength = *((u64 *)cfg->hw_cfg->payload);
+ else
+ strength = 0;
+
+ /* set manual strength */
+ info[dspp->idx].completed_ops_mask |= ad4_strength;
+ val = (strength & (BIT(10) - 1));
+ SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
+ return 0;
+}
+
+static int ad4_strength_setup_idle(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg)
+{
+ int ret;
+
+ ret = ad4_strength_setup(dspp, cfg);
+ if (ret)
+ return ret;
+
+ if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
+ ad4_mode_setup(dspp, info[dspp->idx].mode);
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index d32459a..9e64d78 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -35,6 +35,8 @@
#define PA_LUTV_DSPP_CTRL_OFF 0x4c
#define PA_LUTV_DSPP_SWAP_OFF 0x18
+#define PA_DITH_DSPP_MATRIX_OFF 0x4
+
#define PA_HUE_MASK 0xFFF
#define PA_SAT_MASK 0xFFFF
#define PA_VAL_MASK 0xFF
@@ -971,3 +973,45 @@ void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
/* lock hist buffer */
SDE_REG_WRITE(&ctx->hw, offset_ctl, 1);
}
+
+void sde_setup_dspp_dither_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_pa_dither *dither;
+ u32 ctrl_off, matrix_off;
+ u32 opmode, data, i;
+
+ if (!hw_cfg || (hw_cfg->len != sizeof(struct drm_msm_pa_dither) &&
+ hw_cfg->payload)) {
+ DRM_ERROR("hw %pK payload %pK size %d expected sz %zd\n",
+ hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL),
+ ((hw_cfg) ? hw_cfg->len : 0),
+ sizeof(struct drm_msm_pa_dither));
+ return;
+ }
+
+ ctrl_off = ctx->cap->sblk->dither.base;
+ matrix_off = ctrl_off + PA_DITH_DSPP_MATRIX_OFF;
+
+ /* Turn off feature */
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("Disable DSPP dither feature\n");
+ SDE_REG_WRITE(&ctx->hw, ctrl_off, 0);
+ return;
+ }
+ DRM_DEBUG_DRIVER("Enable DSPP Dither feature\n");
+ dither = hw_cfg->payload;
+
+ for (i = 0; i < DITHER_MATRIX_SZ; i += 4) {
+ data = (dither->matrix[i] & REG_MASK(4)) |
+ ((dither->matrix[i + 1] & REG_MASK(4)) << 4) |
+ ((dither->matrix[i + 2] & REG_MASK(4)) << 8) |
+ ((dither->matrix[i + 3] & REG_MASK(4)) << 12);
+ SDE_REG_WRITE(&ctx->hw, matrix_off + i, data);
+ }
+
+ opmode = BIT(0);
+ opmode |= (dither->offset_en) ? BIT(1) : 0;
+ opmode |= ((dither->strength) & REG_MASK(4)) << 4;
+ SDE_REG_WRITE(&ctx->hw, ctrl_off, opmode);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 3c783ee..95ce7fe 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -143,4 +143,11 @@ void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
* @ctx: Pointer to DSPP context
*/
void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_dither_v1_7 - setup DSPP dither feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to dither data
+ */
+void sde_setup_dspp_dither_v1_7(struct sde_hw_dspp *ctx, void *cfg);
#endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index b268e8f..4451301 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -94,6 +94,12 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
c->ops.setup_sixzone =
sde_setup_dspp_sixzone_v17;
break;
+ case SDE_DSPP_DITHER:
+ if (c->cap->sblk->dither.version ==
+ SDE_COLOR_PROCESS_VER(0x1, 0x7))
+ c->ops.setup_pa_dither =
+ sde_setup_dspp_dither_v1_7;
+ break;
case SDE_DSPP_VLUT:
if (c->cap->sblk->vlut.version ==
(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 2d2ac5b..7fe3baa 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -115,11 +115,11 @@ struct sde_hw_dspp_ops {
void (*setup_danger_safe)(struct sde_hw_dspp *ctx, void *cfg);
/**
- * setup_dither - setup dspp dither
+ * setup_pa_dither - setup dspp PA dither
* @ctx: Pointer to dspp context
* @cfg: Pointer to configuration
*/
- void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg);
+ void (*setup_pa_dither)(struct sde_hw_dspp *ctx, void *cfg);
/**
* setup_vlut - setup dspp PA VLUT
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 9a878fb..86b3986 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -565,6 +565,8 @@ static int sendcmd(struct adreno_device *adreno_dev,
return -EBUSY;
}
+ memset(&time, 0x0, sizeof(time));
+
dispatcher->inflight++;
dispatch_q->inflight++;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 5168d9e..1faad93 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2018, 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
@@ -120,13 +120,60 @@ static void adreno_ringbuffer_wptr(struct adreno_device *adreno_dev,
}
+static void adreno_profile_submit_time(struct adreno_submit_time *time)
+{
+ struct kgsl_drawobj *drawobj;
+ struct kgsl_drawobj_cmd *cmdobj;
+ struct kgsl_mem_entry *entry;
+
+ if (time == NULL)
+ return;
+
+ drawobj = time->drawobj;
+
+ if (drawobj == NULL)
+ return;
+
+ cmdobj = CMDOBJ(drawobj);
+ entry = cmdobj->profiling_buf_entry;
+
+ if (entry) {
+ struct kgsl_drawobj_profiling_buffer *profile_buffer;
+
+ profile_buffer = kgsl_gpuaddr_to_vaddr(&entry->memdesc,
+ cmdobj->profiling_buffer_gpuaddr);
+
+ if (profile_buffer == NULL)
+ return;
+
+ /* Return kernel clock time to the the client if requested */
+ if (drawobj->flags & KGSL_DRAWOBJ_PROFILING_KTIME) {
+ uint64_t secs = time->ktime;
+
+ profile_buffer->wall_clock_ns =
+ do_div(secs, NSEC_PER_SEC);
+ profile_buffer->wall_clock_s = secs;
+ } else {
+ profile_buffer->wall_clock_s = time->utime.tv_sec;
+ profile_buffer->wall_clock_ns = time->utime.tv_nsec;
+ }
+
+ profile_buffer->gpu_ticks_queued = time->ticks;
+
+ kgsl_memdesc_unmap(&entry->memdesc);
+ }
+}
+
void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb,
struct adreno_submit_time *time)
{
struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb);
- if (time != NULL)
+ if (time != NULL) {
adreno_get_submit_time(adreno_dev, rb, time);
+ /* Put the timevalues in the profiling buffer */
+ adreno_profile_submit_time(time);
+ }
adreno_ringbuffer_wptr(adreno_dev, rb);
}
@@ -261,7 +308,8 @@ int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- int i, status;
+ int i;
+ int status = -ENOMEM;
if (!adreno_is_a3xx(adreno_dev)) {
status = kgsl_allocate_global(device, &device->scratch,
@@ -774,16 +822,12 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
int flags = KGSL_CMD_FLAGS_NONE;
int ret;
struct adreno_ringbuffer *rb;
- struct kgsl_drawobj_profiling_buffer *profile_buffer = NULL;
unsigned int dwords = 0;
struct adreno_submit_time local;
- struct kgsl_mem_entry *entry = cmdobj->profiling_buf_entry;
struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
bool set_ib1list_marker = false;
- if (entry)
- profile_buffer = kgsl_gpuaddr_to_vaddr(&entry->memdesc,
- cmdobj->profiling_buffer_gpuaddr);
+ memset(&local, 0x0, sizeof(local));
context = drawobj->context;
drawctxt = ADRENO_CONTEXT(context);
@@ -855,7 +899,8 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
dwords += (numibs * 30);
if (drawobj->flags & KGSL_DRAWOBJ_PROFILING &&
- !adreno_is_a3xx(adreno_dev) && profile_buffer) {
+ !adreno_is_a3xx(adreno_dev) &&
+ (cmdobj->profiling_buf_entry != NULL)) {
user_profiling = true;
dwords += 6;
@@ -875,6 +920,8 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
if (time == NULL)
time = &local;
+
+ time->drawobj = drawobj;
}
if (test_bit(CMDOBJ_PROFILE, &cmdobj->priv)) {
@@ -1027,35 +1074,9 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
if (!ret) {
set_bit(KGSL_CONTEXT_PRIV_SUBMITTED, &context->priv);
cmdobj->global_ts = drawctxt->internal_timestamp;
-
- /* Put the timevalues in the profiling buffer */
- if (user_profiling) {
- /*
- * Return kernel clock time to the the client
- * if requested
- */
- if (drawobj->flags & KGSL_DRAWOBJ_PROFILING_KTIME) {
- uint64_t secs = time->ktime;
-
- profile_buffer->wall_clock_ns =
- do_div(secs, NSEC_PER_SEC);
- profile_buffer->wall_clock_s = secs;
- } else {
- profile_buffer->wall_clock_s =
- time->utime.tv_sec;
- profile_buffer->wall_clock_ns =
- time->utime.tv_nsec;
- }
- profile_buffer->gpu_ticks_queued = time->ticks;
- }
}
done:
- /* Corresponding unmap to the memdesc map of profile_buffer */
- if (entry)
- kgsl_memdesc_unmap(&entry->memdesc);
-
-
trace_kgsl_issueibcmds(device, context->id, numibs, drawobj->timestamp,
drawobj->flags, ret, drawctxt->type);
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index 1dfdb5b..a4dc901 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2018, 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
@@ -43,11 +43,13 @@ struct kgsl_device_private;
* @ticks: GPU ticks at submit time (from the 19.2Mhz timer)
* @ktime: local clock time (in nanoseconds)
* @utime: Wall clock time
+ * @drawobj: the object that we want to profile
*/
struct adreno_submit_time {
uint64_t ticks;
u64 ktime;
struct timespec utime;
+ struct kgsl_drawobj *drawobj;
};
/**
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5039a06..49514e3 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -3418,7 +3418,13 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv,
if (entry == NULL)
return -EINVAL;
+ if (!kgsl_mem_entry_set_pend(entry)) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
if (entry->memdesc.cur_bindings != 0) {
+ kgsl_mem_entry_unset_pend(entry);
kgsl_mem_entry_put(entry);
return -EINVAL;
}
@@ -3487,7 +3493,13 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv,
if (entry == NULL)
return -EINVAL;
+ if (!kgsl_mem_entry_set_pend(entry)) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
if (entry->bind_tree.rb_node != NULL) {
+ kgsl_mem_entry_unset_pend(entry);
kgsl_mem_entry_put(entry);
return -EINVAL;
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 2cd132e..1baf20e 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2748,6 +2748,7 @@ _aware(struct kgsl_device *device)
{
int status = 0;
struct gmu_device *gmu = &device->gmu;
+ unsigned int state = device->state;
switch (device->state) {
case KGSL_STATE_RESET:
@@ -2794,13 +2795,15 @@ _aware(struct kgsl_device *device)
WARN_ONCE(1, "Failed to recover GMU\n");
if (device->snapshot)
device->snapshot->recovered = false;
+ kgsl_pwrctrl_set_state(device, state);
} else {
if (device->snapshot)
device->snapshot->recovered = true;
+ kgsl_pwrctrl_set_state(device,
+ KGSL_STATE_AWARE);
}
clear_bit(GMU_FAULT, &gmu->flags);
- kgsl_pwrctrl_set_state(device, KGSL_STATE_AWARE);
return status;
}
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 450059c..07e01c4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -75,4 +75,4 @@
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
-obj-$(CONFIG_QTI_PDC) += qcom/
+obj-$(CONFIG_ARCH_QCOM) += qcom/
diff --git a/drivers/irqchip/qcom/Kconfig b/drivers/irqchip/qcom/Kconfig
index 0038047..5d2b451 100644
--- a/drivers/irqchip/qcom/Kconfig
+++ b/drivers/irqchip/qcom/Kconfig
@@ -27,3 +27,12 @@
default y if ARCH_SDXPOORWILLS
help
QTI Power Domain Controller for SDxPoorwills
+
+config QTI_MPM
+ bool "QTI MPM"
+ depends on ARCH_QCOM
+ select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ QTI MSM Power Manager driver to manage and configure wakeup
+ IRQs.
diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile
index c4ff9ef..1a8ee65 100644
--- a/drivers/irqchip/qcom/Makefile
+++ b/drivers/irqchip/qcom/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_QTI_PDC_SDM845) += pdc-sdm845.o
obj-$(CONFIG_QTI_PDC_SDM670) += pdc-sdm670.o
obj-$(CONFIG_QTI_PDC_SDXPOORWILLS) += pdc-sdxpoorwills.o
+obj-$(CONFIG_QTI_MPM) += mpm.o mpm-8953.o
diff --git a/drivers/irqchip/qcom/mpm-8953.c b/drivers/irqchip/qcom/mpm-8953.c
new file mode 100644
index 0000000..c9b15af
--- /dev/null
+++ b/drivers/irqchip/qcom/mpm-8953.c
@@ -0,0 +1,24 @@
+/* Copyright (c) 2018, 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 "mpm.h"
+
+const struct mpm_pin mpm_msm8953_gic_chip_data[] = {
+ {2, 216}, /* tsens_upper_lower_int */
+ {37, 252}, /* qmp_usb3_lfps_rxterm_irq -> ss_phy_irq */
+ {49, 168}, /* qusb2phy_dpse_hv -> hs_phy_irq*/
+ {53, 104}, /* mdss_irq */
+ {58, 168}, /* qusb2phy_dmse_hv -> hs_phy_irq*/
+ {88, 222}, /* ee0_krait_hlos_spmi_periph_irq */
+ {-1},
+};
diff --git a/drivers/irqchip/qcom/mpm.c b/drivers/irqchip/qcom/mpm.c
new file mode 100644
index 0000000..ba4cfa5
--- /dev/null
+++ b/drivers/irqchip/qcom/mpm.c
@@ -0,0 +1,532 @@
+/* Copyright (c) 2010-2018, 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/tick.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include<linux/ktime.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/of_irq.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <asm/arch_timer.h>
+#include <soc/qcom/rpm-notifier.h>
+#include <soc/qcom/lpm_levels.h>
+#include "mpm.h"
+#define CREATE_TRACE_POINTS
+#include "trace/events/mpm.h"
+
+#define ARCH_TIMER_HZ (19200000)
+#define MAX_MPM_PIN_PER_IRQ 2
+#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
+#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
+#define CLEAR_TYPE(reg, type) (reg & ~(1 << type))
+#define ENABLE_TYPE(reg, type) (reg | (1 << type))
+#define MPM_REG_ENABLE 0
+#define MPM_REG_FALLING_EDGE 1
+#define MPM_REG_RISING_EDGE 2
+#define MPM_REG_POLARITY 3
+#define MPM_REG_STATUS 4
+
+#define QCOM_MPM_REG_WIDTH DIV_ROUND_UP(num_mpm_irqs, 32)
+#define MPM_REGISTER(reg, index) ((reg * QCOM_MPM_REG_WIDTH + index + 2) * (4))
+
+struct msm_mpm_device_data {
+ struct device *dev;
+ void __iomem *mpm_request_reg_base;
+ void __iomem *mpm_ipc_reg;
+ irq_hw_number_t ipc_irq;
+ struct irq_domain *gic_chip_domain;
+};
+
+static int msm_pm_sleep_time_override;
+static int num_mpm_irqs = 64;
+module_param_named(sleep_time_override,
+ msm_pm_sleep_time_override, int, 0664);
+static struct msm_mpm_device_data msm_mpm_dev_data;
+static unsigned int *mpm_to_irq;
+static DEFINE_SPINLOCK(mpm_lock);
+
+static int msm_get_irq_pin(int mpm_pin, struct mpm_pin *mpm_data)
+{
+ int i = 0;
+
+ if (!mpm_data)
+ return -ENODEV;
+
+ for (i = 0; mpm_data[i].pin >= 0; i++) {
+ if (mpm_data[i].pin == mpm_pin)
+ return mpm_to_irq[mpm_data[i].pin];
+ }
+
+ return -EINVAL;
+}
+
+static void msm_get_mpm_pin(struct irq_data *d, int *mpm_pin)
+{
+ struct mpm_pin *mpm_data = NULL;
+ int i = 0, j = 0;
+
+ if (!d || !d->domain->host_data)
+ return;
+
+ mpm_data = d->domain->host_data;
+
+ for (i = 0; (mpm_data[i].pin >= 0) && (j < MAX_MPM_PIN_PER_IRQ); i++) {
+ if (mpm_data[i].hwirq == d->hwirq) {
+ mpm_pin[j] = mpm_data[i].pin;
+ mpm_to_irq[mpm_data[i].pin] = d->irq;
+ j++;
+ }
+ }
+}
+
+static inline uint32_t msm_mpm_read(unsigned int reg, unsigned int subreg_index)
+{
+ unsigned int offset = MPM_REGISTER(reg, subreg_index);
+
+ return readl_relaxed(msm_mpm_dev_data.mpm_request_reg_base + offset);
+}
+
+static inline void msm_mpm_write(unsigned int reg,
+ unsigned int subreg_index,
+ uint32_t value)
+{
+ void __iomem *mpm_reg_base = msm_mpm_dev_data.mpm_request_reg_base;
+ /*
+ * Add 2 to offset to account for the 64 bit timer in the vMPM
+ * mapping
+ */
+ unsigned int offset = MPM_REGISTER(reg, subreg_index);
+ u32 r_value;
+
+ writel_relaxed(value, mpm_reg_base + offset);
+
+ do {
+ r_value = readl_relaxed(mpm_reg_base + offset);
+ udelay(5);
+ } while (r_value != value);
+}
+
+static inline void msm_mpm_enable_irq(struct irq_data *d, bool on)
+{
+ int mpm_pin[MAX_MPM_PIN_PER_IRQ] = {-1, -1};
+ unsigned long flags;
+ int i = 0;
+ u32 enable;
+ unsigned int index, mask;
+ unsigned int reg;
+
+ reg = MPM_REG_ENABLE;
+ msm_get_mpm_pin(d, mpm_pin);
+ for (i = 0; i < MAX_MPM_PIN_PER_IRQ; i++) {
+ if (mpm_pin[i] < 0)
+ return;
+
+ index = mpm_pin[i]/32;
+ mask = mpm_pin[i]%32;
+ spin_lock_irqsave(&mpm_lock, flags);
+ enable = msm_mpm_read(reg, index);
+
+ if (on)
+ enable = ENABLE_INTR(enable, mask);
+ else
+ enable = CLEAR_INTR(enable, mask);
+
+ msm_mpm_write(reg, index, enable);
+ spin_unlock_irqrestore(&mpm_lock, flags);
+ }
+}
+
+static inline void msm_mpm_set_type(struct irq_data *d,
+ unsigned int flowtype)
+{
+ int mpm_pin[MAX_MPM_PIN_PER_IRQ] = {-1, -1};
+ unsigned long flags;
+ int i = 0;
+ u32 type;
+ unsigned int index, mask;
+ unsigned int reg = 0;
+
+ msm_get_mpm_pin(d, mpm_pin);
+ for (i = 0; i < MAX_MPM_PIN_PER_IRQ; i++) {
+ if (mpm_pin[i] < 0)
+ return;
+
+ index = mpm_pin[i]/32;
+ mask = mpm_pin[i]%32;
+
+ if (flowtype & IRQ_TYPE_LEVEL_HIGH)
+ reg = MPM_REG_FALLING_EDGE;
+
+ if (flowtype & IRQ_TYPE_EDGE_RISING)
+ reg = MPM_REG_RISING_EDGE;
+
+ if (flowtype & IRQ_TYPE_EDGE_FALLING)
+ reg = MPM_REG_POLARITY;
+
+ spin_lock_irqsave(&mpm_lock, flags);
+ type = msm_mpm_read(reg, index);
+
+ if (flowtype)
+ type = ENABLE_TYPE(type, mask);
+ else
+ type = CLEAR_TYPE(type, mask);
+
+ msm_mpm_write(reg, index, type);
+ spin_unlock_irqrestore(&mpm_lock, flags);
+ }
+}
+
+static void msm_mpm_gic_chip_mask(struct irq_data *d)
+{
+ msm_mpm_enable_irq(d, false);
+ irq_chip_mask_parent(d);
+}
+
+static void msm_mpm_gic_chip_unmask(struct irq_data *d)
+{
+ msm_mpm_enable_irq(d, true);
+ irq_chip_unmask_parent(d);
+}
+
+static int msm_mpm_gic_chip_set_type(struct irq_data *d, unsigned int type)
+{
+ msm_mpm_set_type(d, type);
+ return irq_chip_set_type_parent(d, type);
+}
+
+static struct irq_chip msm_mpm_gic_chip = {
+ .name = "mpm-gic",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = msm_mpm_gic_chip_mask,
+ .irq_disable = msm_mpm_gic_chip_mask,
+ .irq_unmask = msm_mpm_gic_chip_unmask,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = msm_mpm_gic_chip_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+#endif
+};
+
+static int msm_mpm_gic_chip_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count < 3)
+ return -EINVAL;
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int msm_mpm_gic_chip_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs,
+ void *data)
+{
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int ret;
+
+ ret = msm_mpm_gic_chip_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &msm_mpm_gic_chip, NULL);
+
+ parent_fwspec = *fwspec;
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops msm_mpm_gic_chip_domain_ops = {
+ .translate = msm_mpm_gic_chip_translate,
+ .alloc = msm_mpm_gic_chip_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static inline void msm_mpm_send_interrupt(void)
+{
+ writel_relaxed(2, msm_mpm_dev_data.mpm_ipc_reg);
+ /* Ensure the write is complete before returning. */
+ wmb();
+}
+
+static inline void msm_mpm_timer_write(uint32_t *expiry)
+{
+ writel_relaxed(expiry[0], msm_mpm_dev_data.mpm_request_reg_base);
+ writel_relaxed(expiry[1], msm_mpm_dev_data.mpm_request_reg_base + 0x4);
+}
+
+static void msm_mpm_enter_sleep(struct cpumask *cpumask)
+{
+ msm_mpm_send_interrupt();
+ irq_set_affinity(msm_mpm_dev_data.ipc_irq, cpumask);
+}
+
+static int msm_get_mpm_pin_map(unsigned int mpm_irq)
+{
+ struct mpm_pin *mpm_gic_pin_map = NULL;
+ int apps_irq;
+
+ mpm_gic_pin_map = (struct mpm_pin *)
+ msm_mpm_dev_data.gic_chip_domain->host_data;
+ apps_irq = msm_get_irq_pin(mpm_irq, mpm_gic_pin_map);
+ return apps_irq;
+
+}
+
+static void system_pm_exit_sleep(void)
+{
+ msm_rpm_exit_sleep();
+}
+
+static cycle_t us_to_ticks(uint64_t sleep_val)
+{
+ uint64_t sec, nsec;
+ cycle_t wakeup;
+
+ sec = sleep_val;
+ do_div(sec, USEC_PER_SEC);
+ nsec = sleep_val - sec * USEC_PER_SEC;
+
+ if (nsec > 0) {
+ nsec = nsec * NSEC_PER_USEC;
+ do_div(nsec, NSEC_PER_SEC);
+ }
+
+ sleep_val = sec + nsec;
+
+ wakeup = (u64)sleep_val * ARCH_TIMER_HZ;
+
+ if (sleep_val)
+ wakeup += arch_counter_get_cntvct();
+ else
+ wakeup = (~0ULL);
+
+ return wakeup;
+}
+
+static int system_pm_update_wakeup(bool from_idle)
+{
+ uint64_t wake_time;
+ uint32_t lo = ~0U, hi = ~0U;
+ cycle_t wakeup;
+
+ if (unlikely(!from_idle && msm_pm_sleep_time_override)) {
+ wake_time = msm_pm_sleep_time_override * USEC_PER_SEC;
+ wakeup = us_to_ticks(wake_time);
+ } else {
+ /* Read the hardware to get the most accurate value */
+
+ arch_timer_mem_get_cval(&lo, &hi);
+ wakeup = lo;
+ wakeup |= ((uint64_t)(hi) << 32);
+ }
+
+ msm_mpm_timer_write((uint32_t *)&wakeup);
+ return 0;
+}
+
+static int system_pm_enter_sleep(struct cpumask *mask)
+{
+ int ret = 0;
+
+ ret = msm_rpm_enter_sleep(0, mask);
+ if (ret)
+ return ret;
+ msm_mpm_enter_sleep(mask);
+ return ret;
+}
+
+static bool system_pm_sleep_allowed(void)
+{
+ return !msm_rpm_waiting_for_ack();
+}
+
+static struct system_pm_ops pm_ops = {
+ .enter = system_pm_enter_sleep,
+ .exit = system_pm_exit_sleep,
+ .update_wakeup = system_pm_update_wakeup,
+ .sleep_allowed = system_pm_sleep_allowed,
+};
+
+/*
+ * Triggered by RPM when system resumes from deep sleep
+ */
+static irqreturn_t msm_mpm_irq(int irq, void *dev_id)
+{
+ unsigned long pending;
+ uint32_t value[3];
+ int i, k, apps_irq;
+ unsigned int mpm_irq;
+ struct irq_desc *desc = NULL;
+ unsigned int reg = MPM_REG_ENABLE;
+
+ for (i = 0; i < QCOM_MPM_REG_WIDTH; i++) {
+ value[i] = msm_mpm_read(reg, i);
+ trace_mpm_wakeup_enable_irqs(i, value[i]);
+ }
+
+ for (i = 0; i < QCOM_MPM_REG_WIDTH; i++) {
+ pending = msm_mpm_read(MPM_REG_STATUS, i);
+ pending &= (unsigned long)value[i];
+
+ trace_mpm_wakeup_pending_irqs(i, pending);
+ for_each_set_bit(k, &pending, 32) {
+ mpm_irq = 32 * i + k;
+ apps_irq = msm_get_mpm_pin_map(mpm_irq);
+ desc = apps_irq ?
+ irq_to_desc(apps_irq) : NULL;
+
+ if (desc && !irqd_is_level_type(&desc->irq_data))
+ irq_set_irqchip_state(apps_irq,
+ IRQCHIP_STATE_PENDING, true);
+
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int msm_mpm_probe(struct device_node *node)
+{
+ struct msm_mpm_device_data *dev = &msm_mpm_dev_data;
+ int ret = 0;
+ int irq;
+
+ dev->mpm_request_reg_base = of_iomap_by_name(node, "vmpm");
+ if (!dev->mpm_request_reg_base) {
+ pr_err("Unable to iomap\n");
+ ret = -EADDRNOTAVAIL;
+ goto reg_base_err;
+ }
+
+ dev->mpm_ipc_reg = of_iomap_by_name(node, "ipc");
+ if (!dev->mpm_ipc_reg) {
+ pr_err("Unable to iomap IPC register\n");
+ ret = -EADDRNOTAVAIL;
+ goto ipc_reg_err;
+ }
+
+ irq = of_irq_get(node, 0);
+ if (irq <= 0) {
+ pr_err("no IRQ resource info\n");
+ ret = irq;
+ goto ipc_irq_err;
+ }
+ dev->ipc_irq = irq;
+
+ ret = request_irq(dev->ipc_irq, msm_mpm_irq,
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, "mpm",
+ msm_mpm_irq);
+ if (ret) {
+ pr_err("request_irq failed errno: %d\n", ret);
+ goto ipc_irq_err;
+ }
+
+ ret = irq_set_irq_wake(dev->ipc_irq, 1);
+ if (ret) {
+ pr_err("failed to set wakeup irq %lu: %d\n",
+ dev->ipc_irq, ret);
+ goto set_wake_irq_err;
+ }
+
+ return register_system_pm_ops(&pm_ops);
+
+set_wake_irq_err:
+ free_irq(dev->ipc_irq, msm_mpm_irq);
+ipc_irq_err:
+ iounmap(dev->mpm_ipc_reg);
+ipc_reg_err:
+ iounmap(dev->mpm_request_reg_base);
+reg_base_err:
+ return ret;
+}
+
+static const struct of_device_id mpm_gic_chip_data_table[] = {
+ {
+ .compatible = "qcom,mpm-gic-msm8953",
+ .data = mpm_msm8953_gic_chip_data,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, mpm_gic_chip_data_table);
+
+static int __init mpm_gic_chip_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *parent_domain;
+ const struct of_device_id *id;
+ struct device_node *parent_node;
+
+ if (!parent) {
+ pr_err("%s(): no parent for mpm-gic\n", node->full_name);
+ return -ENXIO;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("unable to obtain gic parent domain\n");
+ return -ENXIO;
+ }
+
+ of_property_read_u32(node, "qcom,num-mpm-irqs", &num_mpm_irqs);
+
+ mpm_to_irq = kcalloc(num_mpm_irqs, sizeof(*mpm_to_irq), GFP_KERNEL);
+ if (!mpm_to_irq)
+ return -ENOMEM;
+
+ id = of_match_node(mpm_gic_chip_data_table, node);
+ if (!id) {
+ pr_err("can not find mpm_gic_data_table of_node\n");
+ return -ENODEV;
+ }
+
+ msm_mpm_dev_data.gic_chip_domain = irq_domain_add_hierarchy(
+ parent_domain, 0, num_mpm_irqs, node,
+ &msm_mpm_gic_chip_domain_ops, (void *)id->data);
+ if (!msm_mpm_dev_data.gic_chip_domain) {
+ pr_err("gic domain add failed\n");
+ return -ENOMEM;
+ }
+
+ msm_mpm_dev_data.gic_chip_domain->name = "qcom,mpm-gic";
+
+ parent_node = of_get_parent(node);
+ return msm_mpm_probe(parent_node);
+}
+
+IRQCHIP_DECLARE(mpm_gic_chip, "qcom,mpm-gic", mpm_gic_chip_init);
diff --git a/drivers/irqchip/qcom/mpm.h b/drivers/irqchip/qcom/mpm.h
new file mode 100644
index 0000000..72185fc
--- /dev/null
+++ b/drivers/irqchip/qcom/mpm.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2018, 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 __QCOM_MPM_H__
+#define __QCOM_MPM_H__
+
+#include <linux/irq.h>
+#include <linux/device.h>
+
+struct mpm_pin {
+ int pin;
+ irq_hw_number_t hwirq;
+};
+
+extern const struct mpm_pin mpm_msm8953_gic_chip_data[];
+
+#endif /* __QCOM_MPM_H__ */
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index f3f9a1a..d06ee3b 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -34,132 +34,161 @@
#include "leds.h"
#define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08)
+
#define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09)
+#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
+
#define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10)
+
#define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40)
+#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
+
#define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43)
+
#define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46)
+#define FLASH_LED_MOD_CTRL_MASK BIT(7)
+#define FLASH_LED_MOD_ENABLE BIT(7)
+
#define FLASH_LED_REG_IRES(base) (base + 0x47)
+
#define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48)
+#define FLASH_LED_STROBE_MASK GENMASK(1, 0)
+
#define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49)
+#define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2)
+#define FLASH_HW_STROBE_MASK GENMASK(2, 0)
+
#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C)
+#define FLASH_LED_ENABLE BIT(0)
+
#define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D)
+#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
+#define FLASH_LED_HDRM_VOL_SHIFT 4
+
#define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50)
#define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51)
-#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
-#define FLASH_LED_REG_THERMAL_RMP_DN_RATE(base) (base + 0x55)
-#define FLASH_LED_REG_THERMAL_THRSH1(base) (base + 0x56)
-#define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57)
-#define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58)
-#define FLASH_LED_REG_THERMAL_HYSTERESIS(base) (base + 0x59)
-#define FLASH_LED_REG_THERMAL_DEBOUNCE(base) (base + 0x5A)
-#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
-#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
-#define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67)
-#define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68)
-#define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69)
-#define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A)
-#define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B)
-#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E)
-#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F)
-#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70)
-#define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71)
-#define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72)
-#define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76)
-#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
-#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
-#define FLASH_LED_STROBE_MASK GENMASK(1, 0)
-#define FLASH_HW_STROBE_MASK GENMASK(2, 0)
+#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0)
-#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0)
-#define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0)
-#define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4)
-#define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0)
-#define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0)
-#define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0)
-#define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4)
-#define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0)
-#define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0)
-#define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0)
-#define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0)
-#define FLASH_LED_MOD_CTRL_MASK BIT(7)
-#define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2)
-#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
-#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0)
-#define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4)
+#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
+
+#define FLASH_LED_REG_THERMAL_RMP_DN_RATE(base) (base + 0x55)
#define THERMAL_OTST1_RAMP_CTRL_MASK BIT(7)
#define THERMAL_OTST1_RAMP_CTRL_SHIFT 7
#define THERMAL_DERATE_SLOW_SHIFT 4
#define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4)
#define THERMAL_DERATE_FAST_MASK GENMASK(2, 0)
-#define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0)
+
+#define FLASH_LED_REG_THERMAL_THRSH1(base) (base + 0x56)
+#define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0)
+
+#define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57)
+#define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58)
+
+#define FLASH_LED_REG_THERMAL_HYSTERESIS(base) (base + 0x59)
+#define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0)
+
+#define FLASH_LED_REG_THERMAL_DEBOUNCE(base) (base + 0x5A)
+#define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0)
+
+#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
+#define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4)
+#define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0)
+#define FLASH_LED_VPH_DROOP_HYST_SHIFT 4
+
+#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
+#define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0)
+
+#define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67)
+#define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0)
+
+#define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68)
+#define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69)
+#define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A)
+
+#define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B)
+#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
+
+#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E)
+#define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4)
+#define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0)
+
+#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F)
+#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0)
+#define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4)
+#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4)
+
+#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70)
+#define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0)
+
+#define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71)
#define LED3_FLASH_ONCE_ONLY_BIT BIT(1)
+
+#define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72)
#define LPG_INPUT_SEL_BIT BIT(0)
+#define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76)
+#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0)
+
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
#define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor)
-#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
-#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
-#define FLASH_LED_ISC_DELAY_DEFAULT 3
-#define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2
-#define FLASH_LED_VPH_DROOP_HYST_SHIFT 4
-#define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2
-#define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5
-#define FLASH_LED_DEBOUNCE_MAX 3
-#define FLASH_LED_HYSTERESIS_MAX 3
-#define FLASH_LED_VPH_DROOP_THRESH_MAX 7
-#define THERMAL_DERATE_SLOW_MAX 314592
-#define THERMAL_DERATE_FAST_MAX 512
-#define THERMAL_DEBOUNCE_TIME_MAX 64
-#define THERMAL_DERATE_HYSTERESIS_MAX 3
-#define FLASH_LED_THERMAL_THRSH_MIN 3
-#define FLASH_LED_THERMAL_THRSH_MAX 7
-#define FLASH_LED_THERMAL_OTST_LEVELS 3
-#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000
-#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000
-#define FLASH_LED_RPARA_DEFAULT_UOHM 0
-#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
-#define FLASH_LED_LMH_LEVEL_DEFAULT 0
-#define FLASH_LED_LMH_MITIGATION_ENABLE 1
-#define FLASH_LED_LMH_MITIGATION_DISABLE 0
-#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4)
-#define FLASH_LED_CHGR_MITIGATION_DISABLE 0
-#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2
-#define FLASH_LED_MITIGATION_SEL_MAX 2
-#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4
-#define FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT 0xA
-#define FLASH_LED_CHGR_MITIGATION_THRSH_MAX 0x1F
-#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
-#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
-#define FLASH_LED_IRES_BASE 3
-#define FLASH_LED_IRES_DIVISOR 2500
-#define FLASH_LED_IRES_MIN_UA 5000
-#define FLASH_LED_IRES_DEFAULT_UA 12500
-#define FLASH_LED_IRES_DEFAULT_VAL 0x00
-#define FLASH_LED_HDRM_VOL_SHIFT 4
-#define FLASH_LED_HDRM_VOL_DEFAULT_MV 0x80
-#define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04
-#define FLASH_LED_HDRM_VOL_BASE_MV 125
-#define FLASH_LED_HDRM_VOL_STEP_MV 25
-#define FLASH_LED_STROBE_CFG_DEFAULT 0x00
-#define FLASH_LED_HW_STROBE_OPTION_1 0x00
-#define FLASH_LED_HW_STROBE_OPTION_2 0x01
-#define FLASH_LED_HW_STROBE_OPTION_3 0x02
-#define FLASH_LED_ENABLE BIT(0)
-#define FLASH_LED_MOD_ENABLE BIT(7)
-#define FLASH_LED_DISABLE 0x00
-#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
-#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750
-#define FLASH_LED_IRES5P0_MAX_CURR_MA 640
-#define FLASH_LED_IRES7P5_MAX_CURR_MA 960
-#define FLASH_LED_IRES10P0_MAX_CURR_MA 1280
-#define FLASH_LED_IRES12P5_MAX_CURR_MA 1600
-#define MAX_IRES_LEVELS 4
+#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
+#define FLASH_LED_ISC_DELAY_DEFAULT 3
+#define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2
+#define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2
+#define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5
+#define BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT 7
+#define FLASH_LED_DEBOUNCE_MAX 3
+#define FLASH_LED_HYSTERESIS_MAX 3
+#define FLASH_LED_VPH_DROOP_THRESH_MAX 7
+#define THERMAL_DERATE_SLOW_MAX 314592
+#define THERMAL_DERATE_FAST_MAX 512
+#define THERMAL_DEBOUNCE_TIME_MAX 64
+#define THERMAL_DERATE_HYSTERESIS_MAX 3
+#define FLASH_LED_THERMAL_THRSH_MIN 3
+#define FLASH_LED_THERMAL_THRSH_MAX 7
+#define FLASH_LED_THERMAL_OTST_LEVELS 3
+#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000
+#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000
+#define FLASH_LED_RPARA_DEFAULT_UOHM 0
+#define FLASH_LED_LMH_LEVEL_DEFAULT 0
+#define FLASH_LED_LMH_MITIGATION_ENABLE 1
+#define FLASH_LED_LMH_MITIGATION_DISABLE 0
+#define FLASH_LED_CHGR_MITIGATION_DISABLE 0
+#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2
+#define FLASH_LED_MITIGATION_SEL_MAX 2
+#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4
+#define FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT 0xA
+#define FLASH_LED_CHGR_MITIGATION_THRSH_MAX 0x1F
+#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
+#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
+#define FLASH_LED_IRES_BASE 3
+#define FLASH_LED_IRES_DIVISOR 2500
+#define FLASH_LED_IRES_MIN_UA 5000
+#define FLASH_LED_IRES_DEFAULT_UA 12500
+#define FLASH_LED_IRES_DEFAULT_VAL 0x00
+#define FLASH_LED_HDRM_VOL_DEFAULT_MV 0x80
+#define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04
+#define FLASH_LED_HDRM_VOL_BASE_MV 125
+#define FLASH_LED_HDRM_VOL_STEP_MV 25
+#define FLASH_LED_STROBE_CFG_DEFAULT 0x00
+#define FLASH_LED_HW_STROBE_OPTION_1 0x00
+#define FLASH_LED_HW_STROBE_OPTION_2 0x01
+#define FLASH_LED_HW_STROBE_OPTION_3 0x02
+#define FLASH_LED_DISABLE 0x00
+#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
+#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750
+#define FLASH_LED_IRES5P0_MAX_CURR_MA 640
+#define FLASH_LED_IRES7P5_MAX_CURR_MA 960
+#define FLASH_LED_IRES10P0_MAX_CURR_MA 1280
+#define FLASH_LED_IRES12P5_MAX_CURR_MA 1600
+#define MAX_IRES_LEVELS 4
+#define FLASH_BST_PWM_OVRHD_MIN_UV 300000
+#define FLASH_BST_PWM_OVRHD_MAX_UV 600000
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
@@ -171,6 +200,7 @@ enum flash_charger_mitigation {
};
enum flash_led_type {
+ FLASH_LED_TYPE_UNKNOWN,
FLASH_LED_TYPE_FLASH,
FLASH_LED_TYPE_TORCH,
};
@@ -204,13 +234,13 @@ struct flash_node_data {
int prev_current_ma;
u8 duration;
u8 id;
- u8 type;
u8 ires_idx;
u8 default_ires_idx;
u8 hdrm_val;
u8 current_reg_val;
u8 strobe_ctrl;
u8 strobe_sel;
+ enum flash_led_type type;
bool led_on;
};
@@ -225,6 +255,7 @@ struct flash_switch_data {
int led_mask;
bool regulator_on;
bool enabled;
+ bool symmetry_en;
};
/*
@@ -253,6 +284,7 @@ struct flash_led_platform_data {
u32 led1n2_iclamp_mid_ma;
u32 led3_iclamp_low_ma;
u32 led3_iclamp_mid_ma;
+ u32 bst_pwm_ovrhd_uv;
u8 isc_delay;
u8 warmup_delay;
u8 current_derate_en_cfg;
@@ -278,6 +310,8 @@ struct qpnp_flash_led {
struct flash_node_data *fnode;
struct flash_switch_data *snode;
struct power_supply *bms_psy;
+ struct power_supply *main_psy;
+ struct power_supply *usb_psy;
struct notifier_block nb;
spinlock_t lock;
int num_fnodes;
@@ -693,10 +727,13 @@ static int get_property_from_fg(struct qpnp_flash_led *led,
return rc;
}
-#define VOLTAGE_HDRM_DEFAULT_MV 350
+#define VOLTAGE_HDRM_DEFAULT_MV 350
+#define BHARGER_VOLTAGE_HDRM_DEFAULT_MV 400
+#define BHARGER_HEADROOM_OFFSET_MV 50
static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led)
{
int i, voltage_hdrm_mv = 0, voltage_hdrm_max = 0;
+ u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
for (i = 0; i < led->num_fnodes; i++) {
if (led->fnode[i].led_on) {
@@ -720,13 +757,18 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led)
voltage_hdrm_mv = 350;
}
+ if (pmic_subtype == PMI632_SUBTYPE)
+ voltage_hdrm_mv += BHARGER_HEADROOM_OFFSET_MV;
+
voltage_hdrm_max = max(voltage_hdrm_max,
voltage_hdrm_mv);
}
}
if (!voltage_hdrm_max)
- return VOLTAGE_HDRM_DEFAULT_MV;
+ return (pmic_subtype == PMI632_SUBTYPE) ?
+ BHARGER_VOLTAGE_HDRM_DEFAULT_MV :
+ VOLTAGE_HDRM_DEFAULT_MV;
return voltage_hdrm_max;
}
@@ -737,7 +779,7 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led)
#define BOB_EFFICIENCY 900LL
#define VIN_FLASH_MIN_UV 3300000LL
static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led,
- int *max_current)
+ int *max_current)
{
int ocv_uv, ibat_now, voltage_hdrm_mv, rc;
int rbatt_uohm = 0;
@@ -838,8 +880,181 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led,
return 0;
}
+static int is_main_psy_available(struct qpnp_flash_led *led)
+{
+ if (!led->main_psy) {
+ led->main_psy = power_supply_get_by_name("main");
+ if (!led->main_psy) {
+ pr_err_ratelimited("Couldn't get main_psy\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int is_usb_psy_available(struct qpnp_flash_led *led)
+{
+ if (!led->usb_psy) {
+ led->usb_psy = power_supply_get_by_name("usb");
+ if (!led->usb_psy) {
+ pr_err_ratelimited("Couldn't get usb_psy\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+#define CHGBST_EFFICIENCY 800LL
+#define CHGBST_FLASH_VDIP_MARGIN 10000
+#define VIN_FLASH_UV 5000000
+#define BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA 1500
+#define BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA 1100
+static int qpnp_flash_led_calc_bharger_max_current(struct qpnp_flash_led *led,
+ int *max_current)
+{
+ union power_supply_propval pval = {0, };
+ int ocv_uv, ibat_now, voltage_hdrm_mv, flash_led_max_total_curr_ma, rc;
+ int rbatt_uohm = 0, usb_present, otg_enable;
+ int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
+ int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip;
+ int64_t bst_pwm_ovrhd_uv;
+
+ rc = is_usb_psy_available(led);
+ if (rc < 0)
+ return rc;
+
+ rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_SCOPE,
+ &pval);
+ if (rc < 0) {
+ pr_err("usb psy does not support usb present, rc=%d\n", rc);
+ return rc;
+ }
+ otg_enable = pval.intval;
+
+ /* RESISTANCE = esr_uohm + rslow_uohm */
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE,
+ &rbatt_uohm);
+ if (rc < 0) {
+ pr_err("bms psy does not support resistance, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* If no battery is connected, return max possible flash current */
+ if (!rbatt_uohm) {
+ *max_current = (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM) ?
+ BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA :
+ BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA;
+ return 0;
+ }
+
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
+ if (rc < 0) {
+ pr_err("bms psy does not support OCV, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW,
+ &ibat_now);
+ if (rc < 0) {
+ pr_err("bms psy does not support current, rc=%d\n", rc);
+ return rc;
+ }
+
+ bst_pwm_ovrhd_uv = led->pdata->bst_pwm_ovrhd_uv;
+
+ rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+ if (rc < 0) {
+ pr_err("usb psy does not support usb present, rc=%d\n", rc);
+ return rc;
+ }
+ usb_present = pval.intval;
+
+ rbatt_uohm += led->pdata->rpara_uohm;
+ voltage_hdrm_mv = qpnp_flash_led_get_voltage_headroom(led);
+ vph_flash_vdip =
+ VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold)
+ + CHGBST_FLASH_VDIP_MARGIN;
+
+ /* Check if LMH_MITIGATION needs to be triggered */
+ if (!led->trigger_lmh && (ocv_uv < led->pdata->lmh_ocv_threshold_uv ||
+ rbatt_uohm > led->pdata->lmh_rbatt_threshold_uohm)) {
+ led->trigger_lmh = true;
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MITIGATION_SW(led->base),
+ FLASH_LED_LMH_MITIGATION_EN_MASK,
+ FLASH_LED_LMH_MITIGATION_ENABLE);
+ if (rc < 0) {
+ pr_err("trigger lmh mitigation failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Wait for LMH mitigation to take effect */
+ udelay(100);
+
+ return qpnp_flash_led_calc_bharger_max_current(led,
+ max_current);
+ }
+
+ /*
+ * Calculate the maximum current that can pulled out of the battery
+ * before the battery voltage dips below a safe threshold.
+ */
+ ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV,
+ rbatt_uohm);
+
+ if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) {
+ /*
+ * If the calculated current is below the OCP threshold, then
+ * use it as the possible flash current.
+ */
+ ibat_flash_ua = ibat_safe_ua - ibat_now;
+ vph_flash_uv = vph_flash_vdip;
+ } else {
+ /*
+ * If the calculated current is above the OCP threshold, then
+ * use the ocp threshold instead.
+ *
+ * Any higher current will be tripping the battery OCP.
+ */
+ ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now;
+ vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
+ * led->pdata->ibatt_ocp_threshold_ua, UCONV);
+ }
+
+ /* when USB is present or OTG is enabled, VIN_FLASH is always at 5V */
+ if (usb_present || (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM))
+ vin_flash_uv = VIN_FLASH_UV;
+ else
+ /* Calculate the input voltage of the flash module. */
+ vin_flash_uv = max((led->pdata->vled_max_uv +
+ (voltage_hdrm_mv * MCONV)),
+ vph_flash_uv + bst_pwm_ovrhd_uv);
+
+ /* Calculate the available power for the flash module. */
+ avail_flash_power_fw = CHGBST_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
+ /*
+ * Calculate the available amount of current the flash module can draw
+ * before collapsing the battery. (available power/ flash input voltage)
+ */
+ avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
+ flash_led_max_total_curr_ma = otg_enable ?
+ BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA :
+ BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA;
+ *max_current = min(flash_led_max_total_curr_ma,
+ (int)(div64_s64(avail_flash_ua, MCONV)));
+
+ pr_debug("avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d max_current=%lld usb_present=%d otg_enable=%d\n",
+ avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm, led->trigger_lmh,
+ (*max_current * MCONV), usb_present, otg_enable);
+ return 0;
+}
+
+
static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led,
- int *thermal_current_lim)
+ int *thermal_current_lim)
{
int rc;
u8 thermal_thrsh1, thermal_thrsh2, thermal_thrsh3, otst_status;
@@ -929,9 +1144,16 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led,
int *max_avail_current)
{
int thermal_current_lim = 0, rc;
+ u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
led->trigger_lmh = false;
- rc = qpnp_flash_led_calc_max_current(led, max_avail_current);
+
+ if (pmic_subtype == PMI632_SUBTYPE)
+ rc = qpnp_flash_led_calc_bharger_max_current(led,
+ max_avail_current);
+ else
+ rc = qpnp_flash_led_calc_max_current(led, max_avail_current);
+
if (rc < 0) {
pr_err("Couldn't calculate max_avail_current, rc=%d\n", rc);
return rc;
@@ -973,6 +1195,7 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
int prgm_current_ma = value;
int min_ma = fnode->ires_ua / 1000;
struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
+ u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
if (value <= 0)
prgm_current_ma = 0;
@@ -1001,7 +1224,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
- if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) {
+ if (pmic_subtype != PMI632_SUBTYPE &&
+ led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) {
qpnp_flash_led_aggregate_max_current(fnode);
led->trigger_chgr = false;
if (led->total_current_ma >= 1000)
@@ -1031,7 +1255,7 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
}
}
- if (!led->trigger_chgr) {
+ if (led->pdata->chgr_mitigation_sel && !led->trigger_chgr) {
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SW(led->base),
FLASH_LED_CHGR_MITIGATION_EN_MASK,
@@ -1091,9 +1315,116 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
return 0;
}
+static int qpnp_flash_led_symmetry_config(struct flash_switch_data *snode)
+{
+ struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+ int i, total_curr_ma = 0, num_leds = 0, prgm_current_ma;
+ enum flash_led_type type = FLASH_LED_TYPE_UNKNOWN;
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if (snode->led_mask & BIT(led->fnode[i].id)) {
+ if (led->fnode[i].type == FLASH_LED_TYPE_FLASH &&
+ led->fnode[i].led_on)
+ type = FLASH_LED_TYPE_FLASH;
+
+ if (led->fnode[i].type == FLASH_LED_TYPE_TORCH &&
+ led->fnode[i].led_on)
+ type = FLASH_LED_TYPE_TORCH;
+ }
+ }
+
+ if (type == FLASH_LED_TYPE_UNKNOWN) {
+ pr_err("Incorrect type possibly because of no active LEDs\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if ((snode->led_mask & BIT(led->fnode[i].id)) &&
+ (led->fnode[i].type == type)) {
+ total_curr_ma += led->fnode[i].current_ma;
+ num_leds++;
+ }
+ }
+
+ if (num_leds > 0 && total_curr_ma > 0) {
+ prgm_current_ma = total_curr_ma / num_leds;
+ } else {
+ pr_err("Incorrect configuration, num_leds: %d total_curr_ma: %d\n",
+ num_leds, total_curr_ma);
+ return -EINVAL;
+ }
+
+ if (prgm_current_ma == 0) {
+ pr_warn("prgm_curr_ma cannot be 0\n");
+ return 0;
+ }
+
+ pr_debug("num_leds: %d total: %d prgm_curr_ma: %d\n", num_leds,
+ total_curr_ma, prgm_current_ma);
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if (snode->led_mask & BIT(led->fnode[i].id) &&
+ led->fnode[i].current_ma != prgm_current_ma &&
+ led->fnode[i].type == type) {
+ qpnp_flash_led_node_set(&led->fnode[i],
+ prgm_current_ma);
+ pr_debug("%s LED %d current: %d code: %d ires_ua: %d\n",
+ (type == FLASH_LED_TYPE_FLASH) ?
+ "flash" : "torch",
+ led->fnode[i].id, prgm_current_ma,
+ led->fnode[i].current_reg_val,
+ led->fnode[i].ires_ua);
+ }
+ }
+
+ return 0;
+}
+
+#define FLASH_LED_MODULE_EN_TIME_MS 300
+static int qpnp_flash_poll_vreg_ok(struct qpnp_flash_led *led)
+{
+ int rc, i;
+ union power_supply_propval pval = {0, };
+
+ rc = is_main_psy_available(led);
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < 60; i++) {
+ /* wait for the flash vreg_ok to be set */
+ mdelay(5);
+
+ rc = power_supply_get_property(led->main_psy,
+ POWER_SUPPLY_PROP_FLASH_TRIGGER, &pval);
+ if (rc < 0) {
+ pr_err("main psy doesn't support reading prop %d rc = %d\n",
+ POWER_SUPPLY_PROP_FLASH_TRIGGER, rc);
+ return rc;
+ }
+
+ if (pval.intval > 0) {
+ pr_debug("Flash trigger set\n");
+ break;
+ }
+
+ if (pval.intval < 0) {
+ pr_err("Error during flash trigger %d\n", pval.intval);
+ return pval.intval;
+ }
+ }
+
+ if (!pval.intval) {
+ pr_err("Failed to enable the module\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
{
struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+ u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
int rc, i, addr_offset;
u8 val, mask;
@@ -1109,6 +1440,15 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
}
/* Iterate over all active leds for this switch node */
+ if (snode->symmetry_en) {
+ rc = qpnp_flash_led_symmetry_config(snode);
+ if (rc < 0) {
+ pr_err("Failed to configure current symmetrically, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
val = 0;
for (i = 0; i < led->num_fnodes; i++)
if (led->fnode[i].led_on &&
@@ -1179,6 +1519,19 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE);
if (rc < 0)
return rc;
+
+ if (pmic_subtype == PMI632_SUBTYPE) {
+ rc = qpnp_flash_poll_vreg_ok(led);
+ if (rc < 0) {
+ /* Disable the module */
+ qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MOD_CTRL(led->base),
+ FLASH_LED_MOD_CTRL_MASK,
+ FLASH_LED_DISABLE);
+
+ return rc;
+ }
+ }
}
led->enable++;
@@ -1195,7 +1548,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
udelay(500);
}
- if (led->trigger_chgr) {
+ if (led->pdata->chgr_mitigation_sel && led->trigger_chgr) {
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SW(led->base),
FLASH_LED_CHGR_MITIGATION_EN_MASK,
@@ -1216,12 +1569,91 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
return 0;
}
+static int qpnp_flash_led_regulator_control(struct led_classdev *led_cdev,
+ int options, int *max_current)
+{
+ int rc;
+ u8 pmic_subtype;
+ struct flash_switch_data *snode;
+ struct qpnp_flash_led *led;
+ union power_supply_propval ret = {0, };
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+ led = dev_get_drvdata(&snode->pdev->dev);
+ pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
+
+ if (pmic_subtype == PMI632_SUBTYPE) {
+ rc = is_main_psy_available(led);
+ if (rc < 0)
+ return rc;
+
+ rc = is_usb_psy_available(led);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
+ pr_err("Invalid options %d\n", options);
+ return -EINVAL;
+ }
+
+ if (options & ENABLE_REGULATOR) {
+ if (pmic_subtype == PMI632_SUBTYPE) {
+ ret.intval = 1;
+ rc = power_supply_set_property(led->main_psy,
+ POWER_SUPPLY_PROP_FLASH_ACTIVE,
+ &ret);
+ if (rc < 0) {
+ pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n",
+ rc);
+ return rc;
+ }
+ pr_debug("FLASH_ACTIVE = 1\n");
+ } else {
+ rc = qpnp_flash_led_regulator_enable(led, snode, true);
+ if (rc < 0) {
+ pr_err("enable regulator failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ if (options & DISABLE_REGULATOR) {
+ if (pmic_subtype == PMI632_SUBTYPE) {
+ ret.intval = 0;
+ rc = power_supply_set_property(led->main_psy,
+ POWER_SUPPLY_PROP_FLASH_ACTIVE,
+ &ret);
+ if (rc < 0) {
+ pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n",
+ rc);
+ return rc;
+ }
+ pr_debug("FLASH_ACTIVE = 0\n");
+ } else {
+ rc = qpnp_flash_led_regulator_enable(led, snode, false);
+ if (rc < 0) {
+ pr_err("disable regulator failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ if (options & QUERY_MAX_CURRENT) {
+ rc = qpnp_flash_led_get_max_avail_current(led, max_current);
+ if (rc < 0) {
+ pr_err("query max current failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
static int qpnp_flash_led_prepare_v2(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev;
- struct flash_switch_data *snode;
- struct qpnp_flash_led *led;
int rc;
if (!trig) {
@@ -1235,39 +1667,9 @@ static int qpnp_flash_led_prepare_v2(struct led_trigger *trig, int options,
return -EINVAL;
}
- snode = container_of(led_cdev, struct flash_switch_data, cdev);
- led = dev_get_drvdata(&snode->pdev->dev);
+ rc = qpnp_flash_led_regulator_control(led_cdev, options, max_current);
- if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
- pr_err("Invalid options %d\n", options);
- return -EINVAL;
- }
-
- if (options & ENABLE_REGULATOR) {
- rc = qpnp_flash_led_regulator_enable(led, snode, true);
- if (rc < 0) {
- pr_err("enable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & DISABLE_REGULATOR) {
- rc = qpnp_flash_led_regulator_enable(led, snode, false);
- if (rc < 0) {
- pr_err("disable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & QUERY_MAX_CURRENT) {
- rc = qpnp_flash_led_get_max_avail_current(led, max_current);
- if (rc < 0) {
- pr_err("query max current failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- return 0;
+ return rc;
}
static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
@@ -1309,6 +1711,29 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
spin_unlock(&led->lock);
}
+static ssize_t qpnp_flash_led_prepare_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc, options, max_current;
+ u32 val;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ rc = kstrtouint(buf, 0, &val);
+ if (rc < 0)
+ return rc;
+
+ if (val != 0 && val != 1)
+ return count;
+
+ options = val ? ENABLE_REGULATOR : DISABLE_REGULATOR;
+
+ rc = qpnp_flash_led_regulator_control(led_cdev, options, &max_current);
+ if (rc < 0)
+ return rc;
+
+ return count;
+}
+
/* sysfs show function for flash_max_current */
static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1331,6 +1756,7 @@ static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
/* sysfs attributes exported by flash_led */
static struct device_attribute qpnp_flash_led_attrs[] = {
__ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL),
+ __ATTR(enable, 0664, NULL, qpnp_flash_led_prepare_store),
};
static int flash_led_psy_notifier_call(struct notifier_block *nb,
@@ -1459,6 +1885,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
int rc, min_ma;
u32 val;
bool hw_strobe = 0, edge_trigger = 0, active_high = 0;
+ u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
fnode->pdev = led->pdev;
fnode->cdev.brightness_set = qpnp_flash_led_brightness_set;
@@ -1488,6 +1915,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
rc = of_property_read_u32(node, "qcom,id", &val);
if (!rc) {
fnode->id = (u8)val;
+
+ if (pmic_subtype == PMI632_SUBTYPE && fnode->id > 1) {
+ pr_err("Flash node id = %d not supported\n", fnode->id);
+ return -EINVAL;
+ }
} else {
pr_err("Unable to read flash LED ID\n");
return rc;
@@ -1707,6 +2139,8 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
return rc;
}
+ snode->symmetry_en = of_property_read_bool(node, "qcom,symmetry-en");
+
if (snode->led_mask < 1 || snode->led_mask > 7) {
pr_err("Invalid value for led-mask\n");
return -EINVAL;
@@ -1792,6 +2226,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
struct device_node *revid_node;
int rc;
u32 val;
+ u8 pmic_subtype;
bool short_circuit_det, open_circuit_det, vph_droop_det;
revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
@@ -1812,10 +2247,10 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EPROBE_DEFER;
}
+ pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype;
pr_debug("PMIC subtype %d Digital major %d\n",
led->pdata->pmic_rev_id->pmic_subtype,
led->pdata->pmic_rev_id->rev4);
-
led->pdata->hdrm_auto_mode_en = of_property_read_bool(node,
"qcom,hdrm-auto-mode");
@@ -1923,7 +2358,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
led->pdata->thermal_hysteresis = -EINVAL;
rc = of_property_read_u32(node, "qcom,thermal-hysteresis", &val);
if (!rc) {
- if (led->pdata->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ if (pmic_subtype == PM660L_SUBTYPE)
val = THERMAL_HYST_TEMP_TO_VAL(val, 20);
else
val = THERMAL_HYST_TEMP_TO_VAL(val, 15);
@@ -1987,7 +2422,13 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT;
+ if (pmic_subtype == PMI632_SUBTYPE)
+ led->pdata->vph_droop_threshold =
+ BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT;
+ else
+ led->pdata->vph_droop_threshold =
+ FLASH_LED_VPH_DROOP_THRESH_DEFAULT;
+
rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
if (!rc) {
led->pdata->vph_droop_threshold =
@@ -2132,7 +2573,12 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION;
+ if (pmic_subtype == PMI632_SUBTYPE)
+ led->pdata->chgr_mitigation_sel =
+ FLASH_DISABLE_CHARGER_MITIGATION;
+ else
+ led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION;
+
rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val);
if (!rc) {
led->pdata->chgr_mitigation_sel = val;
@@ -2160,6 +2606,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
+ led->pdata->bst_pwm_ovrhd_uv = FLASH_BST_PWM_OVRHD_MIN_UV;
+ rc = of_property_read_u32(node, "qcom,bst-pwm-ovrhd-uv", &val);
+ if (!rc) {
+ if (val >= FLASH_BST_PWM_OVRHD_MIN_UV &&
+ val <= FLASH_BST_PWM_OVRHD_MAX_UV)
+ led->pdata->bst_pwm_ovrhd_uv = val;
+ }
+
led->pdata->all_ramp_up_done_irq =
of_irq_get_byname(node, "all-ramp-up-done-irq");
if (led->pdata->all_ramp_up_done_irq < 0)
diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig
index 4367048..9e4ea8d 100644
--- a/drivers/media/platform/msm/Kconfig
+++ b/drivers/media/platform/msm/Kconfig
@@ -13,8 +13,44 @@
Enabling this adds support for the camera driver stack including sensor,
IFE and postprocessing drivers.
+menuconfig MSM_CAMERA
+ bool "QTI MSM camera and video capture support"
+ depends on ARCH_QCOM && VIDEO_V4L2 && I2C
+ ---help---
+ Say Y here to enable selecting the video adapters for
+ QTI msm camera and video capture drivers. enabling this
+ adds support for the camera driver stack including sensor, isp
+ and postprocessing drivers for legacy chipsets.
+
+config MSM_CAMERA_DEBUG
+ bool "QTI MSM camera debugging with printk"
+ depends on MSM_CAMERA
+ default n
+ ---help---
+ Enable printk() debug for msm camera
+
+menuconfig MSMB_CAMERA
+ bool "QTI MSM camera and video capture 2.0 support"
+ depends on ARCH_QCOM && VIDEO_V4L2 && I2C
+ ---help---
+ Say Y here to enable selecting the video adapters for
+ QTI 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 "QTI 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/platform/msm/camera_v2/Kconfig"
+endif # MSMB_CAMERA
+
source "drivers/media/platform/msm/vidc_3x/Kconfig"
source "drivers/media/platform/msm/vidc/Kconfig"
source "drivers/media/platform/msm/sde/Kconfig"
source "drivers/media/platform/msm/dvb/Kconfig"
source "drivers/media/platform/msm/broadcast/Kconfig"
+
diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile
index fea242a..24c27cd 100644
--- a/drivers/media/platform/msm/Makefile
+++ b/drivers/media/platform/msm/Makefile
@@ -6,5 +6,6 @@
obj-$(CONFIG_MSM_VIDC_3X_V4L2) += vidc_3x/
obj-y += sde/
obj-$(CONFIG_SPECTRA_CAMERA) += camera/
+obj-$(CONFIG_MSMB_CAMERA) += camera_v2/
obj-y += broadcast/
obj-$(CONFIG_DVB_MPQ) += dvb/
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
new file mode 100644
index 0000000..cabc612
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -0,0 +1,261 @@
+config MSM_CAMERA_SENSOR
+ bool "QTI MSM camera sensor support"
+ depends on MSMB_CAMERA
+ select NEW_LEDS
+ select LEDS_CLASS
+ ---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 "QTI 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 "QTI 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 "QTI 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_CSI22_HEADER
+ bool "QTI MSM CSI 2.2 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 2.2
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required targets based on 8610
+ platform.
+
+config MSM_CSI30_HEADER
+ bool "QTI 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_CSI31_HEADER
+ bool "QTI MSM CSI 3.1 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
+ APQ8084 platform.
+
+config MSM_CSIPHY
+ bool "QTI 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 "QTI 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_EEPROM
+ bool "QTI MSM Camera ROM Interface for Calibration support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for ROM Interface for Calibration
+ Provides interface for reading the Claibration data.
+ and also provides support for writing data in case of FLASH ROM.
+ Currently supports I2C, CCI and SPI protocol
+
+config MSM_ISPIF
+ bool "QTI 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 pix or raw
+ data interface in VFE.
+
+config MSM_ISPIF_V1
+ bool "QTI 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 MSM_CSI22_HEADER can be routed to of pix
+ or raw data interface in VFE.
+
+config MSM_ISPIF_V2
+ bool "QTI 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 pix
+ or raw data interface in VFE.
+
+config IMX134
+ bool "Sensor IMX134 (BAYER 8M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Sony 8 MP Bayer Sensor with auto focus, uses
+ 4 mipi lanes full resolution @30fps and
+ HFR @60fps and @120fps,
+ Video HDR support.
+
+config IMX132
+ bool "Sensor IMX132 (BAYER 2M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Sony 2 MP Bayer Sensor with auto focus, uses
+ 2 mipi lanes, preview config = 1920 x 1080 at 30 fps,
+ snapshot config = 1920 x 1080 at 30 fps,
+ Video HDR support.
+
+config OV9724
+ bool "Sensor OV9724 (BAYER 2M)"
+ depends on MSMB_CAMERA
+ ---help---
+ OmniVision 2 MP Bayer Sensor, supports 2 mipi lanes,
+ preview and snapshot config at 1280*720 at 30 fps,
+ hfr video at 60, 90 and 120 fps. This sensor driver does
+ not support auto focus.
+
+config OV5648
+ bool "Sensor OV5648 (BAYER 5M)"
+ depends on MSMB_CAMERA
+ ---help---
+ OmniVision 5 MP Bayer Sensor, only use 1 mipi lane,
+ preview set to 1296*972 at 30 fps,
+ snapshot set to 2592*1944 at 12 fps,
+ This sensor driver does not support auto focus.
+
+config GC0339
+ bool "Sensor GC0339 (BAYER .3M)"
+ depends on MSMB_CAMERA
+ ---help---
+ gc0339 is a Galaxycore .3 MP Bayer Sensor.
+ It supports 1 or 2 mipi lanes.
+ Preview and snapshot resolution shall be 640*480 at 30 fps,
+ It does not support auto focus.
+
+config OV8825
+ bool "OmniVision OV8825 (BAYER 8MP)"
+ depends on MSMB_CAMERA
+ ---help---
+ OmniVision 8 MP Bayer Sensor with auto focus.uses
+ 2 mipi lanes, preview config = 1632*1224 30 fps,
+ snapshot config = 3264 * 2448 at 18 fps.
+ 2 lanes max fps is 18, 4 lanes max fps is 24.
+
+config OV8865
+ bool "OmniVision OV8865 (BAYER 8MP)"
+ depends on MSMB_CAMERA
+ ---help---
+ OmniVision 8 MP Bayer Sensor with auto focus.uses
+ 4 mipi lanes, preview config = 1632*1224 30 fps,
+ snapshot config = 3264 * 2448 at 30 fps.
+ Max fps is 30fps at 3264 * 2448, 60fps at 1632 * 1224
+
+config s5k4e1
+ bool "Sensor s5k4e1 (BAYER 5MP)"
+ depends on MSMB_CAMERA
+ ---help---
+ Samsung 5 MP Bayer Sensor. It uses 2 mipi lanes,
+ supports 720P preview at 30 fps
+ and QSXGA snapshot at 15 fps.
+ This sensor driver does not support auto focus.
+
+config OV12830
+ bool "OmniVision OV12830 (BAYER 12MP)"
+ depends on MSMB_CAMERA
+ ---help---
+ OmniVision 12.8 MP Bayer Sensor with auto focus.uses
+ 4 mipi lanes, preview config = 2112 * 1500 at 30 fps,
+ snapshot config = 4224 * 3000 at 15 fps.
+ 2 lanes max fps is 18, 4 lanes max fps is 24.
+
+config MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ tristate "QTI 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
+
+config MSMB_JPEG
+ tristate "QTI MSM Jpeg Encoder Engine support"
+ depends on MSMB_CAMERA && (ARCH_MSM8974 || ARCH_MSM8226 || ARCH_APQ8084 || ARCH_MSM8916 || ARCH_QCOM)
+ ---help---
+ Enable support for Jpeg Encoder/Decoder
+ Engine for 8974.
+ This module serves as the common driver
+ for the JPEG 1.0 encoder and decoder.
+
+config MSM_GEMINI
+ tristate "QTI MSM Gemini JPEG engine support"
+ depends on MSMB_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
+ ---help---
+ Enables support for the Gemini JPEG encoder
+ Engine for 8x60, 7x30 and 8960.
+ This module serves as the driver
+ for JPEG encoding functionality.
+
+config MSM_FD
+ tristate "QTI MSM FD face detection engine support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enables support for the MSM FD face detection engine
+ Engine for 8996, 8998.
+ This module serves as the driver for
+ detection of face ROI in a luma frame.
+
+config MSM_JPEGDMA
+ tristate "QTI MSM Jpeg dma"
+ depends on MSMB_CAMERA
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ Enable support for Jpeg dma engine
+ Engine for 8996, 8998.
+ This module serves as the driver for
+ downscale and data transfer functionality.
+
+config MSM_SEC_CCI_TA_NAME
+ string "Name of TA to handle Secure CCI transactions"
+ depends on MSM_CCI
+ default "seccamdemo64"
+
+config MSM_SEC_CCI_DEBUG
+ bool "QTI MSM Secure CCI Relay Debug"
+ depends on MSM_CCI
+ ---help---
+ Enables simulation of secure camera for Secure CCI Realy
+ debugging.
diff --git a/drivers/media/platform/msm/camera_v2/Makefile b/drivers/media/platform/msm/camera_v2/Makefile
new file mode 100644
index 0000000..cdb0468
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/Makefile
@@ -0,0 +1,24 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/codecs
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/isps
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_10
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_dma
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/fd
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+
+obj-$(CONFIG_MSMB_CAMERA) += common/
+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) += pproc/
+obj-$(CONFIG_MSMB_CAMERA) += isp/
+obj-$(CONFIG_MSMB_CAMERA) += ispif/
+obj-$(CONFIG_MSMB_JPEG) += jpeg_10/
+obj-$(CONFIG_MSM_JPEGDMA) += jpeg_dma/
+obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr/
+obj-$(CONFIG_MSM_FD) += fd/
diff --git a/drivers/media/platform/msm/camera_v2/camera/Makefile b/drivers/media/platform/msm/camera_v2/camera/Makefile
new file mode 100644
index 0000000..bd70750
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/camera/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += camera.o
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
new file mode 100644
index 0000000..0efbd1b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -0,0 +1,972 @@
+/* Copyright (c) 2012-2018, 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 <media/videobuf2-v4l2.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;
+ unsigned int is_vb2_valid; /*0 if no vb2 buffers on stream, else 1*/
+ struct vb2_queue vb2_q;
+ bool stream_created;
+ struct mutex lock;
+};
+
+static void camera_pack_event(struct file *filep, int evt_id,
+ int command, int value, 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;
+ event_data->arg_value = value;
+}
+
+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) {
+ pr_err("%s : event_data status out of bounds\n",
+ __func__);
+ pr_err("%s : Line %d event_data->status 0X%x\n",
+ __func__, __LINE__, event_data->status);
+
+ switch (event_data->status) {
+ case MSM_CAMERA_ERR_CMD_FAIL:
+ case MSM_CAMERA_ERR_MAPPING:
+ return -EFAULT;
+ case MSM_CAMERA_ERR_DEVICE_BUSY:
+ return -EBUSY;
+ default:
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int camera_v4l2_querycap(struct file *filep, void *fh,
+ struct v4l2_capability *cap)
+{
+ int rc;
+ struct v4l2_event event;
+
+ if (msm_is_daemon_present() == false)
+ return 0;
+
+ /* can use cap->driver to make differentiation */
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_QUERY_CAP, -1, &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,
+ const struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (msm_is_daemon_present() == false)
+ return 0;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_CROP, -1, &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 (msm_is_daemon_present() == false)
+ return 0;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_G_CROP, -1, &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 (msm_is_daemon_present() == false)
+ return 0;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ ctrl->id, -1, &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;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ unsigned int session_id = pvdev->vdev->num;
+
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ if (ctrl->id == MSM_CAMERA_PRIV_G_SESSION_ID) {
+ ctrl->value = session_id;
+ } else {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ ctrl->id, -1, &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;
+ struct msm_v4l2_event_data *event_data;
+
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM, ctrl->id,
+ ctrl->value, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+ event_data = (struct msm_v4l2_event_data *)event.u.data;
+ ctrl->value = event_data->ret_value;
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_reqbufs(struct file *filep, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ int ret;
+ struct msm_session *session;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ unsigned int session_id = pvdev->vdev->num;
+
+ session = msm_session_find(session_id);
+ if (WARN_ON(!session))
+ return -EIO;
+ mutex_lock(&sp->lock);
+ ret = vb2_reqbufs(&sp->vb2_q, req);
+ mutex_unlock(&sp->lock);
+ return ret;
+}
+
+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)
+{
+ int ret;
+ struct msm_session *session;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ unsigned int session_id = pvdev->vdev->num;
+
+ session = msm_session_find(session_id);
+ if (WARN_ON(!session))
+ return -EIO;
+ mutex_lock(&sp->lock);
+ ret = vb2_qbuf(&sp->vb2_q, pb);
+ mutex_unlock(&sp->lock);
+ return ret;
+}
+
+static int camera_v4l2_dqbuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ int ret;
+ struct msm_session *session;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ unsigned int session_id = pvdev->vdev->num;
+
+ session = msm_session_find(session_id);
+ if (WARN_ON(!session))
+ return -EIO;
+ mutex_lock(&sp->lock);
+ ret = vb2_dqbuf(&sp->vb2_q, pb, filep->f_flags & O_NONBLOCK);
+ mutex_unlock(&sp->lock);
+ return ret;
+}
+
+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);
+
+ mutex_lock(&sp->lock);
+ rc = vb2_streamon(&sp->vb2_q, buf_type);
+ mutex_unlock(&sp->lock);
+
+ if (msm_is_daemon_present() == false)
+ return 0;
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_ON, -1, &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 = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_OFF, -1, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+ rc = camera_check_event_status(&event);
+ }
+ mutex_lock(&sp->lock);
+ vb2_streamoff(&sp->vb2_q, buf_type);
+ mutex_unlock(&sp->lock);
+ return rc;
+}
+
+static int camera_v4l2_g_fmt_vid_cap_mplane(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ int rc = -EINVAL;
+
+ if (msm_is_daemon_present() == false)
+ return 0;
+
+ 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, -1, &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_vid_cap_mplane(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_VIDEO_CAPTURE_MPLANE) {
+
+ 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);
+ /* num_planes need to bound checked, otherwise for loop
+ * can execute forever
+ */
+ if (WARN_ON(user_fmt->num_planes > VIDEO_MAX_PLANES))
+ return -EINVAL;
+ for (i = 0; i < user_fmt->num_planes; i++)
+ pr_debug("%s: plane size[%d]\n", __func__,
+ user_fmt->plane_sizes[i]);
+
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_FMT, -1, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ return rc;
+ }
+ sp->is_vb2_valid = 1;
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_try_fmt_vid_cap_mplane(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ 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, -1, &event);
+
+ rc = msm_create_stream(event_data->session_id,
+ event_data->stream_id, &sp->vb2_q);
+ if (rc < 0)
+ return rc;
+
+ if (msm_is_daemon_present() != false) {
+ 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;
+ sp->stream_created = true;
+
+ 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,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ mutex_lock(&sp->lock);
+ rc = v4l2_event_subscribe(&sp->fh, sub, 5, NULL);
+ mutex_unlock(&sp->lock);
+
+ return rc;
+}
+
+static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ mutex_lock(&sp->lock);
+ rc = v4l2_event_unsubscribe(&sp->fh, sub);
+ mutex_unlock(&sp->lock);
+
+ return rc;
+}
+
+static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh,
+ bool valid_prio, unsigned int cmd, void *arg)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct msm_camera_private_ioctl_arg *k_ioctl = arg;
+ long rc = -EINVAL;
+
+ if (WARN_ON(!k_ioctl || !pvdev))
+ return -EIO;
+
+ if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD)
+ return -EINVAL;
+
+ switch (k_ioctl->id) {
+ case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: {
+ struct msm_camera_return_buf ptr;
+ struct msm_camera_return_buf __user *tmp = NULL;
+
+ MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr,
+ sizeof(tmp));
+ if (copy_from_user(&ptr, tmp,
+ sizeof(struct msm_camera_return_buf))) {
+ return -EFAULT;
+ }
+ rc = msm_vb2_return_buf_by_idx(pvdev->vdev->num, sp->stream_id,
+ ptr.index);
+ }
+ break;
+ default:
+ pr_debug("unimplemented id %d", k_ioctl->id);
+ return -EINVAL;
+ }
+ 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_vid_cap_mplane = camera_v4l2_g_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = camera_v4l2_s_fmt_vid_cap_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = camera_v4l2_try_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,
+ .vidioc_default = camera_v4l2_vidioc_private_ioctl,
+};
+
+static int camera_v4l2_fh_open(struct file *filep)
+{
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp;
+ unsigned long stream_id;
+
+ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return -ENOMEM;
+
+ filep->private_data = &sp->fh;
+
+ /* stream_id = open id */
+ stream_id = atomic_read(&pvdev->opened);
+ sp->stream_id = find_first_zero_bit(
+ (const unsigned long *)&stream_id, MSM_CAMERA_STREAM_CNT_BITS);
+ pr_debug("%s: Found stream_id=%d\n", __func__, sp->stream_id);
+
+ mutex_init(&sp->lock);
+
+ 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);
+ mutex_destroy(&sp->lock);
+ kzfree(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) {
+ pr_err("%s : memory not available\n", __func__);
+ 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->buf_struct_size = sizeof(struct msm_vb2_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ return vb2_queue_init(q);
+}
+
+static void camera_v4l2_vb2_q_release(struct file *filep)
+{
+ struct camera_v4l2_private *sp = filep->private_data;
+
+ kzfree(sp->vb2_q.drv_priv);
+ mutex_lock(&sp->lock);
+ vb2_queue_release(&sp->vb2_q);
+ mutex_unlock(&sp->lock);
+}
+
+static int camera_v4l2_open(struct file *filep)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ unsigned long opn_idx, idx;
+
+ if (WARN_ON(!pvdev))
+ return -EIO;
+
+ mutex_lock(&pvdev->video_drvdata_mutex);
+ rc = camera_v4l2_fh_open(filep);
+ if (rc < 0) {
+ pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
+ goto fh_open_fail;
+ }
+
+ opn_idx = atomic_read(&pvdev->opened);
+ idx = opn_idx;
+ /* every stream has a vb2 queue */
+ rc = camera_v4l2_vb2_q_init(filep);
+ if (rc < 0) {
+ pr_err("%s : vb2 queue init fails Line %d rc %d\n",
+ __func__, __LINE__, rc);
+ goto vb2_q_fail;
+ }
+
+ if (!atomic_read(&pvdev->opened)) {
+ pm_stay_awake(&pvdev->vdev->dev);
+
+ /* Disable power collapse latency */
+ msm_pm_qos_update_request(CAMERA_DISABLE_PC_LATENCY);
+
+ /* create a new session when first opened */
+ rc = msm_create_session(pvdev->vdev->num, pvdev->vdev);
+ if (rc < 0) {
+ pr_err("%s : session creation failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
+ goto session_fail;
+ }
+
+ rc = msm_create_command_ack_q(pvdev->vdev->num,
+ find_first_zero_bit((const unsigned long *)&opn_idx,
+ MSM_CAMERA_STREAM_CNT_BITS));
+ if (rc < 0) {
+ pr_err("%s : creation of command_ack queue failed\n",
+ __func__);
+ pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc);
+ goto command_ack_q_fail;
+ }
+
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_NEW_SESSION,
+ 0, -1, &event);
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0) {
+ pr_err("%s : NEW_SESSION event failed,rc %d\n",
+ __func__, rc);
+ goto post_fail;
+ }
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto post_fail;
+ }
+ /* Enable power collapse latency */
+ msm_pm_qos_update_request(CAMERA_ENABLE_PC_LATENCY);
+ } else {
+ rc = msm_create_command_ack_q(pvdev->vdev->num,
+ find_first_zero_bit((const unsigned long *)&opn_idx,
+ MSM_CAMERA_STREAM_CNT_BITS));
+ if (rc < 0) {
+ pr_err("%s : creation of command_ack queue failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
+ goto stream_fail;
+ }
+ }
+ idx |= (1UL <<
+ find_first_zero_bit((const unsigned long *)&opn_idx,
+ MSM_CAMERA_STREAM_CNT_BITS));
+ atomic_cmpxchg(&pvdev->opened, opn_idx, idx);
+ mutex_unlock(&pvdev->video_drvdata_mutex);
+
+ 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:
+ pm_relax(&pvdev->vdev->dev);
+stream_fail:
+ camera_v4l2_vb2_q_release(filep);
+vb2_q_fail:
+ camera_v4l2_fh_release(filep);
+fh_open_fail:
+ mutex_unlock(&pvdev->video_drvdata_mutex);
+ 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);
+
+ if (sp->is_vb2_valid == 1)
+ 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)
+{
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+ unsigned int opn_idx, mask;
+ struct msm_session *session;
+
+ if (WARN_ON(!pvdev))
+ return -EIO;
+
+ session = msm_session_find(pvdev->vdev->num);
+ if (WARN_ON(!session))
+ return -EIO;
+
+ mutex_lock(&pvdev->video_drvdata_mutex);
+ mutex_lock(&session->close_lock);
+ opn_idx = atomic_read(&pvdev->opened);
+ mask = (1 << sp->stream_id);
+ opn_idx &= ~mask;
+ atomic_set(&pvdev->opened, opn_idx);
+
+ if (msm_is_daemon_present() != false && sp->stream_created == true) {
+ pr_debug("%s: close stream_id=%d\n", __func__, sp->stream_id);
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_DEL_STREAM, -1, &event);
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ }
+
+ if (sp->stream_created == true)
+ sp->stream_created = false;
+
+ if (atomic_read(&pvdev->opened) == 0) {
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_DEL_SESSION,
+ 0, -1, &event);
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ }
+ msm_delete_command_ack_q(pvdev->vdev->num, 0);
+ msm_delete_stream(pvdev->vdev->num, sp->stream_id);
+ mutex_unlock(&session->close_lock);
+ /* This should take care of both normal close
+ * and application crashes
+ */
+ camera_v4l2_vb2_q_release(filep);
+ msm_destroy_session(pvdev->vdev->num);
+
+ pm_relax(&pvdev->vdev->dev);
+ } else {
+ msm_delete_command_ack_q(pvdev->vdev->num,
+ sp->stream_id);
+
+ camera_v4l2_vb2_q_release(filep);
+ msm_delete_stream(pvdev->vdev->num, sp->stream_id);
+ mutex_unlock(&session->close_lock);
+ }
+
+ camera_v4l2_fh_release(filep);
+ mutex_unlock(&pvdev->video_drvdata_mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long camera_handle_internal_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long rc = 0;
+ struct msm_camera_private_ioctl_arg k_ioctl;
+ void __user *tmp_compat_ioctl_ptr = NULL;
+
+ rc = msm_copy_camera_private_ioctl_args(arg,
+ &k_ioctl, &tmp_compat_ioctl_ptr);
+ if (rc < 0) {
+ pr_err("Subdev cmd %d failed\n", cmd);
+ return rc;
+ }
+ switch (k_ioctl.id) {
+ case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: {
+ if (k_ioctl.size != sizeof(struct msm_camera_return_buf)) {
+ pr_debug("Invalid size for id %d with size %d",
+ k_ioctl.id, k_ioctl.size);
+ return -EINVAL;
+ }
+ k_ioctl.ioctl_ptr = (__force __u64 __user)tmp_compat_ioctl_ptr;
+ if (!k_ioctl.ioctl_ptr) {
+ pr_debug("Invalid ptr for id %d", k_ioctl.id);
+ return -EINVAL;
+ }
+ rc = camera_v4l2_vidioc_private_ioctl(file, file->private_data,
+ 0, cmd, (void *)&k_ioctl);
+ }
+ break;
+ default:
+ pr_debug("unimplemented id %d", k_ioctl.id);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+
+ switch (cmd) {
+ case VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD: {
+ ret = camera_handle_internal_compat_ioctl(file, cmd, arg);
+ if (ret < 0) {
+ pr_debug("Subdev cmd %d fail\n", cmd);
+ return ret;
+ }
+ }
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+
+ }
+ return ret;
+}
+#endif
+static struct v4l2_file_operations camera_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = camera_v4l2_open,
+ .poll = camera_v4l2_poll,
+ .release = camera_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = camera_v4l2_compat_ioctl,
+#endif
+};
+
+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;
+ }
+ media_device_init(v4l2_dev->mdev);
+ 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_pads_init(&pvdev->vdev->entity, 0, NULL);
+ if (WARN_ON(rc < 0))
+ goto entity_fail;
+ pvdev->vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ //TODO: Use entity.name in from userspcae to find device.
+ //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);
+ mutex_init(&pvdev->video_drvdata_mutex);
+ video_set_drvdata(pvdev->vdev, pvdev);
+ device_init_wakeup(&pvdev->vdev->dev, 1);
+ 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:
+ kzfree(v4l2_dev->mdev);
+mdev_fail:
+#endif
+ kzfree(v4l2_dev);
+v4l2_fail:
+ video_device_release(pvdev->vdev);
+video_fail:
+ kzfree(pvdev);
+init_end:
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.h b/drivers/media/platform/msm/camera_v2/camera/camera.h
new file mode 100644
index 0000000..06a9c82
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2018, 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/platform/msm/camera_v2/common/Makefile b/drivers/media/platform/msm/camera_v2/common/Makefile
new file mode 100644
index 0000000..74fe58f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/
+ccflags-y += -Idrivers/misc/
+obj-$(CONFIG_MSMB_CAMERA) += msm_camera_io_util.o cam_smmu_api.o cam_hw_ops.o cam_soc_api.o msm_camera_tz_util.o
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.c b/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.c
new file mode 100644
index 0000000..a3b41d0
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-AHB %s:%d " fmt, __func__, __LINE__
+#define TRUE 1
+#include <linux/module.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include "cam_hw_ops.h"
+
+#ifdef CONFIG_CAM_AHB_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+struct cam_ahb_client {
+ enum cam_ahb_clk_vote vote;
+};
+
+struct cam_bus_vector {
+ const char *name;
+};
+
+struct cam_ahb_client_data {
+ struct msm_bus_scale_pdata *pbus_data;
+ u32 ahb_client;
+ u32 ahb_clk_state;
+ struct msm_bus_vectors *paths;
+ struct msm_bus_paths *usecases;
+ struct cam_bus_vector *vectors;
+ u32 *votes;
+ u32 cnt;
+ u32 probe_done;
+ struct cam_ahb_client clients[CAM_AHB_CLIENT_MAX];
+ struct mutex lock;
+};
+
+static struct cam_ahb_client_data data;
+
+static int get_vector_index(char *name)
+{
+ int i = 0, rc = -1;
+
+ for (i = 0; i < data.cnt; i++) {
+ if (strcmp(name, data.vectors[i].name) == 0)
+ return i;
+ }
+
+ return rc;
+}
+
+int cam_ahb_clk_init(struct platform_device *pdev)
+{
+ int i = 0, cnt = 0, rc = 0, index = 0;
+ struct device_node *of_node;
+
+ if (!pdev) {
+ pr_err("invalid pdev argument\n");
+ return -EINVAL;
+ }
+
+ of_node = pdev->dev.of_node;
+ data.cnt = of_property_count_strings(of_node, "bus-vectors");
+ if (data.cnt == 0) {
+ pr_err("no vectors strings found in device tree, count=%d",
+ data.cnt);
+ return 0;
+ }
+
+ cnt = of_property_count_u32_elems(of_node, "qcom,bus-votes");
+ if (cnt == 0) {
+ pr_err("no vector values found in device tree, count=%d", cnt);
+ return 0;
+ }
+
+ if (data.cnt != cnt) {
+ pr_err("vector mismatch num of strings=%u, num of values %d\n",
+ data.cnt, cnt);
+ return -EINVAL;
+ }
+
+ CDBG("number of bus vectors: %d\n", data.cnt);
+
+ data.vectors = devm_kzalloc(&pdev->dev,
+ sizeof(struct cam_bus_vector) * cnt,
+ GFP_KERNEL);
+ if (!data.vectors)
+ return -ENOMEM;
+
+ for (i = 0; i < data.cnt; i++) {
+ rc = of_property_read_string_index(of_node, "bus-vectors",
+ i, &(data.vectors[i].name));
+ CDBG("dbg: names[%d] = %s\n", i, data.vectors[i].name);
+ if (rc < 0) {
+ pr_err("failed\n");
+ rc = -EINVAL;
+ goto err1;
+ }
+ }
+
+ data.paths = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_bus_vectors) * cnt,
+ GFP_KERNEL);
+ if (!data.paths) {
+ rc = -ENOMEM;
+ goto err1;
+ }
+
+ data.usecases = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_bus_paths) * cnt,
+ GFP_KERNEL);
+ if (!data.usecases) {
+ rc = -ENOMEM;
+ goto err2;
+ }
+
+ data.pbus_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_bus_scale_pdata),
+ GFP_KERNEL);
+ if (!data.pbus_data) {
+ rc = -ENOMEM;
+ goto err3;
+ }
+
+ data.votes = devm_kzalloc(&pdev->dev, sizeof(u32) * cnt,
+ GFP_KERNEL);
+ if (!data.votes) {
+ rc = -ENOMEM;
+ goto err4;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,bus-votes",
+ data.votes, cnt);
+
+ for (i = 0; i < data.cnt; i++) {
+ data.paths[i] = (struct msm_bus_vectors) {
+ MSM_BUS_MASTER_AMPSS_M0,
+ MSM_BUS_SLAVE_CAMERA_CFG,
+ 0,
+ data.votes[i]
+ };
+ data.usecases[i] = (struct msm_bus_paths) {
+ .num_paths = 1,
+ .vectors = &data.paths[i],
+ };
+ CDBG("dbg: votes[%d] = %u\n", i, data.votes[i]);
+ }
+
+ *data.pbus_data = (struct msm_bus_scale_pdata) {
+ .name = "msm_camera_ahb",
+ .num_usecases = data.cnt,
+ .usecase = data.usecases,
+ };
+
+ data.ahb_client =
+ msm_bus_scale_register_client(data.pbus_data);
+ if (!data.ahb_client) {
+ pr_err("ahb vote registering failed\n");
+ rc = -EINVAL;
+ goto err5;
+ }
+
+ index = get_vector_index("suspend");
+ if (index < 0) {
+ pr_err("svs vector not supported\n");
+ rc = -EINVAL;
+ goto err6;
+ }
+
+ /* request for svs in init */
+ msm_bus_scale_client_update_request(data.ahb_client,
+ index);
+ data.ahb_clk_state = CAM_AHB_SUSPEND_VOTE;
+ data.probe_done = TRUE;
+ mutex_init(&data.lock);
+
+ CDBG("dbg, done registering ahb votes\n");
+ CDBG("dbg, clk state :%u, probe :%d\n",
+ data.ahb_clk_state, data.probe_done);
+ return rc;
+
+err6:
+ msm_bus_scale_unregister_client(data.ahb_client);
+err5:
+ devm_kfree(&pdev->dev, data.votes);
+ data.votes = NULL;
+err4:
+ devm_kfree(&pdev->dev, data.pbus_data);
+ data.pbus_data = NULL;
+err3:
+ devm_kfree(&pdev->dev, data.usecases);
+ data.usecases = NULL;
+err2:
+ devm_kfree(&pdev->dev, data.paths);
+ data.paths = NULL;
+err1:
+ devm_kfree(&pdev->dev, data.vectors);
+ data.vectors = NULL;
+ return rc;
+}
+EXPORT_SYMBOL(cam_ahb_clk_init);
+
+static int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id,
+ enum cam_ahb_clk_vote vote)
+{
+ int i = 0;
+ u32 max = 0;
+
+ CDBG("dbg: id :%u, vote : 0x%x\n", id, vote);
+ mutex_lock(&data.lock);
+ data.clients[id].vote = vote;
+
+ if (vote == data.ahb_clk_state) {
+ CDBG("dbg: already at desired vote\n");
+ mutex_unlock(&data.lock);
+ return 0;
+ }
+
+ for (i = 0; i < CAM_AHB_CLIENT_MAX; i++) {
+ if (data.clients[i].vote > max)
+ max = data.clients[i].vote;
+ }
+
+ CDBG("dbg: max vote : %u\n", max);
+ if (max != data.ahb_clk_state) {
+ msm_bus_scale_client_update_request(data.ahb_client,
+ max);
+ data.ahb_clk_state = max;
+ CDBG("dbg: state : %u, vector : %d\n",
+ data.ahb_clk_state, max);
+ }
+ mutex_unlock(&data.lock);
+ return 0;
+}
+
+static int cam_ahb_get_voltage_level(unsigned int corner)
+{
+ switch (corner) {
+ case RPM_REGULATOR_CORNER_NONE:
+ return CAM_AHB_SUSPEND_VOTE;
+
+ case RPM_REGULATOR_CORNER_SVS_KRAIT:
+ case RPM_REGULATOR_CORNER_SVS_SOC:
+ return CAM_AHB_SVS_VOTE;
+
+ case RPM_REGULATOR_CORNER_NORMAL:
+ return CAM_AHB_NOMINAL_VOTE;
+
+ case RPM_REGULATOR_CORNER_SUPER_TURBO:
+ return CAM_AHB_TURBO_VOTE;
+
+ case RPM_REGULATOR_CORNER_TURBO:
+ case RPM_REGULATOR_CORNER_RETENTION:
+ default:
+ return -EINVAL;
+ }
+}
+
+int cam_config_ahb_clk(struct device *dev, unsigned long freq,
+ enum cam_ahb_clk_client id, enum cam_ahb_clk_vote vote)
+{
+ struct dev_pm_opp *opp;
+ unsigned int corner;
+ enum cam_ahb_clk_vote dyn_vote = vote;
+ int rc = -EINVAL;
+
+ if (id >= CAM_AHB_CLIENT_MAX) {
+ pr_err("err: invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (data.probe_done != TRUE) {
+ pr_err("ahb init is not done yet\n");
+ return -EINVAL;
+ }
+
+ CDBG("dbg: id :%u, vote : 0x%x\n", id, vote);
+ switch (dyn_vote) {
+ case CAM_AHB_SUSPEND_VOTE:
+ case CAM_AHB_SVS_VOTE:
+ case CAM_AHB_NOMINAL_VOTE:
+ case CAM_AHB_TURBO_VOTE:
+ break;
+ case CAM_AHB_DYNAMIC_VOTE:
+ if (!dev) {
+ pr_err("device is NULL\n");
+ return -EINVAL;
+ }
+ opp = dev_pm_opp_find_freq_exact(dev, freq, true);
+ if (IS_ERR(opp)) {
+ pr_err("Error on OPP freq :%ld\n", freq);
+ return -EINVAL;
+ }
+ corner = dev_pm_opp_get_voltage(opp);
+ if (corner == 0) {
+ pr_err("Bad voltage corner for OPP freq :%ld\n", freq);
+ return -EINVAL;
+ }
+ dyn_vote = cam_ahb_get_voltage_level(corner);
+ if (dyn_vote < 0) {
+ pr_err("Bad vote requested\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("err: invalid vote argument\n");
+ return -EINVAL;
+ }
+
+ rc = cam_consolidate_ahb_vote(id, dyn_vote);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+EXPORT_SYMBOL(cam_config_ahb_clk);
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.h b/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.h
new file mode 100644
index 0000000..bb87ad1
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_hw_ops.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_HW_OPS_H_
+#define _CAM_HW_OPS_H_
+
+enum cam_ahb_clk_vote {
+ /* need to update the voting requests
+ * according to dtsi entries.
+ */
+ CAM_AHB_SUSPEND_VOTE = 0x0,
+ CAM_AHB_SVS_VOTE = 0x01,
+ CAM_AHB_NOMINAL_VOTE = 0x02,
+ CAM_AHB_TURBO_VOTE = 0x03,
+ CAM_AHB_DYNAMIC_VOTE = 0xFF,
+};
+
+enum cam_ahb_clk_client {
+ CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_CLIENT_CSID,
+ CAM_AHB_CLIENT_CCI,
+ CAM_AHB_CLIENT_ISPIF,
+ CAM_AHB_CLIENT_VFE0,
+ CAM_AHB_CLIENT_VFE1,
+ CAM_AHB_CLIENT_CPP,
+ CAM_AHB_CLIENT_FD,
+ CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_CLIENT_MAX
+};
+
+int cam_config_ahb_clk(struct device *dev, unsigned long freq,
+ enum cam_ahb_clk_client id, enum cam_ahb_clk_vote vote);
+int cam_ahb_clk_init(struct platform_device *pdev);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
new file mode 100644
index 0000000..36e3752
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -0,0 +1,2328 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "CAM-SMMU %s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/dma-buf.h>
+#include <asm/dma-iommu.h>
+#include <asm/cacheflush.h>
+#include <linux/dma-direction.h>
+#include <linux/of_platform.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/workqueue.h>
+#include <linux/sizes.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/secure_buffer.h>
+#include <msm_camera_tz_util.h>
+#include "cam_smmu_api.h"
+
+#define SCRATCH_ALLOC_START SZ_128K
+#define SCRATCH_ALLOC_END SZ_256M
+#define VA_SPACE_END SZ_2G
+#define IOMMU_INVALID_DIR -1
+#define BYTE_SIZE 8
+#define COOKIE_NUM_BYTE 2
+#define COOKIE_SIZE (BYTE_SIZE*COOKIE_NUM_BYTE)
+#define COOKIE_MASK ((1<<COOKIE_SIZE)-1)
+#define HANDLE_INIT (-1)
+#define CAM_SMMU_CB_MAX 2
+#define CAM_SMMU_SID_MAX 4
+
+
+#define GET_SMMU_HDL(x, y) (((x) << COOKIE_SIZE) | ((y) & COOKIE_MASK))
+#define GET_SMMU_TABLE_IDX(x) (((x) >> COOKIE_SIZE) & COOKIE_MASK)
+
+#define CAMERA_DEVICE_ID 0x16
+#define SECURE_SYSCALL_ID 0x18
+
+#ifdef CONFIG_CAM_SMMU_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+struct cam_smmu_work_payload {
+ int idx;
+ struct iommu_domain *domain;
+ struct device *dev;
+ unsigned long iova;
+ int flags;
+ void *token;
+ struct list_head list;
+};
+
+enum cam_protection_type {
+ CAM_PROT_INVALID,
+ CAM_NON_SECURE,
+ CAM_SECURE,
+ CAM_PROT_MAX,
+};
+
+enum cam_iommu_type {
+ CAM_SMMU_INVALID,
+ CAM_QSMMU,
+ CAM_ARM_SMMU,
+ CAM_SMMU_MAX,
+};
+
+enum cam_smmu_buf_state {
+ CAM_SMMU_BUFF_EXIST,
+ CAM_SMMU_BUFF_NOT_EXIST
+};
+
+enum cam_smmu_init_dir {
+ CAM_SMMU_TABLE_INIT,
+ CAM_SMMU_TABLE_DEINIT,
+};
+
+struct scratch_mapping {
+ void *bitmap;
+ size_t bits;
+ unsigned int order;
+ dma_addr_t base;
+};
+
+struct cam_context_bank_info {
+ struct device *dev;
+ struct dma_iommu_mapping *mapping;
+ enum iommu_attr attr;
+ dma_addr_t va_start;
+ size_t va_len;
+ const char *name;
+ bool is_secure;
+ uint8_t scratch_buf_support;
+ struct scratch_mapping scratch_map;
+ struct list_head smmu_buf_list;
+ struct mutex lock;
+ int handle;
+ enum cam_smmu_ops_param state;
+ client_handler handler[CAM_SMMU_CB_MAX];
+ client_reset_handler hw_reset_handler[CAM_SMMU_CB_MAX];
+ void *token[CAM_SMMU_CB_MAX];
+ int cb_count;
+ int ref_cnt;
+ int sids[CAM_SMMU_SID_MAX];
+};
+
+struct cam_iommu_cb_set {
+ struct cam_context_bank_info *cb_info;
+ u32 cb_num;
+ u32 cb_init_count;
+ struct work_struct smmu_work;
+ struct mutex payload_list_lock;
+ struct list_head payload_list;
+};
+
+static const struct of_device_id msm_cam_smmu_dt_match[] = {
+ { .compatible = "qcom,msm-cam-smmu", },
+ { .compatible = "qcom,msm-cam-smmu-cb", },
+ {}
+};
+
+struct cam_dma_buff_info {
+ struct dma_buf *buf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *table;
+ enum dma_data_direction dir;
+ int iommu_dir;
+ int ref_count;
+ dma_addr_t paddr;
+ struct list_head list;
+ int ion_fd;
+ size_t len;
+ size_t phys_len;
+};
+
+struct cam_sec_buff_info {
+ struct ion_handle *i_hdl;
+ struct ion_client *i_client;
+ enum dma_data_direction dir;
+ int ref_count;
+ dma_addr_t paddr;
+ struct list_head list;
+ int ion_fd;
+ size_t len;
+};
+
+static struct cam_iommu_cb_set iommu_cb_set;
+
+static enum dma_data_direction cam_smmu_translate_dir(
+ enum cam_smmu_map_dir dir);
+
+static int cam_smmu_check_handle_unique(int hdl);
+
+static int cam_smmu_create_iommu_handle(int idx);
+
+static int cam_smmu_create_add_handle_in_table(char *name,
+ int *hdl);
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx,
+ int ion_fd);
+
+static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx,
+ int ion_fd);
+
+static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map,
+ dma_addr_t base, size_t size,
+ int order);
+
+static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping,
+ size_t size,
+ dma_addr_t *iova);
+
+static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping,
+ dma_addr_t addr, size_t size);
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx,
+ dma_addr_t virt_addr);
+
+static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
+ enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr);
+
+static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx,
+ size_t virt_len,
+ size_t phys_len,
+ unsigned int iommu_dir,
+ dma_addr_t *virt_addr);
+static int cam_smmu_unmap_buf_and_remove_from_list(
+ struct cam_dma_buff_info *mapping_info, int idx);
+
+static int cam_smmu_free_scratch_buffer_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx);
+
+static void cam_smmu_clean_buffer_list(int idx);
+
+static void cam_smmu_print_list(int idx);
+
+static void cam_smmu_print_table(void);
+
+static int cam_smmu_probe(struct platform_device *pdev);
+
+static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr);
+
+static void cam_smmu_page_fault_work(struct work_struct *work)
+{
+ int j;
+ int idx;
+ struct cam_smmu_work_payload *payload;
+
+ mutex_lock(&iommu_cb_set.payload_list_lock);
+ payload = list_first_entry(&iommu_cb_set.payload_list,
+ struct cam_smmu_work_payload,
+ list);
+ list_del(&payload->list);
+ mutex_unlock(&iommu_cb_set.payload_list_lock);
+
+ /* Dereference the payload to call the handler */
+ idx = payload->idx;
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ cam_smmu_check_vaddr_in_range(idx, (void *)payload->iova);
+ for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+ if ((iommu_cb_set.cb_info[idx].handler[j])) {
+ iommu_cb_set.cb_info[idx].handler[j](
+ payload->domain,
+ payload->dev,
+ payload->iova,
+ payload->flags,
+ iommu_cb_set.cb_info[idx].token[j]);
+ }
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ kfree(payload);
+}
+
+static void cam_smmu_print_list(int idx)
+{
+ struct cam_dma_buff_info *mapping;
+
+ pr_err("index = %d ", idx);
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ pr_err("ion_fd = %d, paddr= 0x%pK, len = %u\n",
+ mapping->ion_fd, (void *)mapping->paddr,
+ (unsigned int)mapping->len);
+ }
+}
+
+static void cam_smmu_print_table(void)
+{
+ int i;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ pr_err("i= %d, handle= %d, name_addr=%pK\n", i,
+ (int)iommu_cb_set.cb_info[i].handle,
+ (void *)iommu_cb_set.cb_info[i].name);
+ pr_err("dev = %pK ", iommu_cb_set.cb_info[i].dev);
+ }
+}
+
+
+static int cam_smmu_query_vaddr_in_range(int handle,
+ unsigned long fault_addr, unsigned long *start_addr,
+ unsigned long *end_addr, int *fd)
+{
+ int idx, rc = -EINVAL;
+ struct cam_dma_buff_info *mapping;
+ unsigned long sa, ea;
+
+ if (!start_addr || !end_addr || !fd) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ sa = (unsigned long)mapping->paddr;
+ ea = (unsigned long)mapping->paddr + mapping->len;
+
+ if (sa <= fault_addr && fault_addr < ea) {
+ *start_addr = sa;
+ *end_addr = ea;
+ *fd = mapping->ion_fd;
+ rc = 0;
+ break;
+ }
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_query_vaddr_in_range);
+
+static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
+{
+ struct cam_dma_buff_info *mapping;
+ unsigned long start_addr, end_addr, current_addr;
+
+ current_addr = (unsigned long)vaddr;
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ start_addr = (unsigned long)mapping->paddr;
+ end_addr = (unsigned long)mapping->paddr + mapping->len;
+
+ if (start_addr <= current_addr && current_addr < end_addr) {
+ pr_err("Error: va %pK is valid: range:%pK-%pK, fd = %d cb: %s\n",
+ vaddr, (void *)start_addr, (void *)end_addr,
+ mapping->ion_fd,
+ iommu_cb_set.cb_info[idx].name);
+ return;
+ }
+ CDBG("va %pK is not in this range: %pK-%pK, fd = %d\n",
+ vaddr, (void *)start_addr, (void *)end_addr,
+ mapping->ion_fd);
+ }
+ if (!strcmp(iommu_cb_set.cb_info[idx].name, "vfe"))
+ pr_err_ratelimited("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
+ else
+ pr_err("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
+}
+
+void cam_smmu_reg_client_page_fault_handler(int handle,
+ client_handler page_fault_handler,
+ client_reset_handler hw_reset_handler,
+ void *token)
+{
+ int idx, i = 0;
+
+ if (!token) {
+ pr_err("Error: token is NULL\n");
+ return;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return;
+ }
+
+ if (page_fault_handler) {
+ if (iommu_cb_set.cb_info[idx].cb_count == CAM_SMMU_CB_MAX) {
+ pr_err("%s Should not regiester more handlers\n",
+ iommu_cb_set.cb_info[idx].name);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return;
+ }
+ iommu_cb_set.cb_info[idx].cb_count++;
+ for (i = 0; i < iommu_cb_set.cb_info[idx].cb_count; i++) {
+ if (iommu_cb_set.cb_info[idx].token[i] == NULL) {
+ iommu_cb_set.cb_info[idx].token[i] = token;
+ iommu_cb_set.cb_info[idx].handler[i] =
+ page_fault_handler;
+ iommu_cb_set.cb_info[idx].hw_reset_handler[i] =
+ hw_reset_handler;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < CAM_SMMU_CB_MAX; i++) {
+ if (iommu_cb_set.cb_info[idx].token[i] == token) {
+ iommu_cb_set.cb_info[idx].token[i] = NULL;
+ iommu_cb_set.cb_info[idx].handler[i] =
+ NULL;
+ iommu_cb_set.cb_info[idx].cb_count--;
+ break;
+ }
+ }
+ if (i == CAM_SMMU_CB_MAX)
+ pr_err("Error: hdl %x no matching tokens: %s\n",
+ handle, iommu_cb_set.cb_info[idx].name);
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+}
+
+static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova,
+ int flags, void *token)
+{
+ char *cb_name;
+ int idx;
+ int j;
+ struct cam_smmu_work_payload *payload;
+
+ if (!token) {
+ pr_err("Error: token is NULL\n");
+ pr_err("Error: domain = %pK, device = %pK\n", domain, dev);
+ pr_err("iova = %lX, flags = %d\n", iova, flags);
+ return 0;
+ }
+
+ cb_name = (char *)token;
+ /* check whether it is in the table */
+ for (idx = 0; idx < iommu_cb_set.cb_num; idx++) {
+ if (!strcmp(iommu_cb_set.cb_info[idx].name, cb_name))
+ break;
+ }
+
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: index is not valid, index = %d, token = %s\n",
+ idx, cb_name);
+ return 0;
+ }
+
+ payload = kzalloc(sizeof(struct cam_smmu_work_payload), GFP_ATOMIC);
+ if (!payload)
+ return 0;
+
+ payload->domain = domain;
+ payload->dev = dev;
+ payload->iova = iova;
+ payload->flags = flags;
+ payload->token = token;
+ payload->idx = idx;
+
+ /* trigger hw reset handler */
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+ if ((iommu_cb_set.cb_info[idx].hw_reset_handler[j])) {
+ iommu_cb_set.cb_info[idx].hw_reset_handler[j](
+ payload->domain,
+ payload->dev,
+ iommu_cb_set.cb_info[idx].token[j]);
+ }
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+
+ mutex_lock(&iommu_cb_set.payload_list_lock);
+ list_add_tail(&payload->list, &iommu_cb_set.payload_list);
+ mutex_unlock(&iommu_cb_set.payload_list_lock);
+
+ schedule_work(&iommu_cb_set.smmu_work);
+
+ return 0;
+}
+
+static int cam_smmu_translate_dir_to_iommu_dir(
+ enum cam_smmu_map_dir dir)
+{
+ switch (dir) {
+ case CAM_SMMU_MAP_READ:
+ return IOMMU_READ;
+ case CAM_SMMU_MAP_WRITE:
+ return IOMMU_WRITE;
+ case CAM_SMMU_MAP_RW:
+ return IOMMU_READ|IOMMU_WRITE;
+ case CAM_SMMU_MAP_INVALID:
+ default:
+ pr_err("Error: Direction is invalid. dir = %d\n", dir);
+ break;
+ };
+ return IOMMU_INVALID_DIR;
+}
+
+static enum dma_data_direction cam_smmu_translate_dir(
+ enum cam_smmu_map_dir dir)
+{
+ switch (dir) {
+ case CAM_SMMU_MAP_READ:
+ return DMA_FROM_DEVICE;
+ case CAM_SMMU_MAP_WRITE:
+ return DMA_TO_DEVICE;
+ case CAM_SMMU_MAP_RW:
+ return DMA_BIDIRECTIONAL;
+ case CAM_SMMU_MAP_INVALID:
+ default:
+ pr_err("Error: Direction is invalid. dir = %d\n", (int)dir);
+ break;
+ }
+ return DMA_NONE;
+}
+
+static void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops)
+{
+ unsigned int i;
+ int j = 0;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ iommu_cb_set.cb_info[i].handle = HANDLE_INIT;
+ INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_list);
+ iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH;
+ iommu_cb_set.cb_info[i].dev = NULL;
+ iommu_cb_set.cb_info[i].cb_count = 0;
+ iommu_cb_set.cb_info[i].ref_cnt = 0;
+ for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+ iommu_cb_set.cb_info[i].token[j] = NULL;
+ iommu_cb_set.cb_info[i].handler[j] = NULL;
+ }
+ for (j = 0; j < CAM_SMMU_SID_MAX; j++)
+ iommu_cb_set.cb_info[i].sids[j] = -1;
+
+ if (ops == CAM_SMMU_TABLE_INIT)
+ mutex_init(&iommu_cb_set.cb_info[i].lock);
+ else
+ mutex_destroy(&iommu_cb_set.cb_info[i].lock);
+ }
+}
+
+static int cam_smmu_check_handle_unique(int hdl)
+{
+ int i;
+
+ if (hdl == HANDLE_INIT) {
+ CDBG("iommu handle is init number. Need to try again\n");
+ return 1;
+ }
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ if (iommu_cb_set.cb_info[i].handle == HANDLE_INIT)
+ continue;
+
+ if (iommu_cb_set.cb_info[i].handle == hdl) {
+ CDBG("iommu handle %d conflicts\n", (int)hdl);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * use low 2 bytes for handle cookie
+ */
+static int cam_smmu_create_iommu_handle(int idx)
+{
+ int rand, hdl = 0;
+
+ get_random_bytes(&rand, COOKIE_NUM_BYTE);
+ hdl = GET_SMMU_HDL(idx, rand);
+ CDBG("create handle value = %x\n", (int)hdl);
+ return hdl;
+}
+
+static int cam_smmu_attach_device(int idx)
+{
+ int rc;
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+
+ /* attach the mapping to device */
+ rc = arm_iommu_attach_device(cb->dev, cb->mapping);
+ if (rc < 0) {
+ pr_err("Error: ARM IOMMU attach failed. ret = %d\n", rc);
+ return -ENODEV;
+ }
+ return rc;
+}
+
+static int cam_smmu_create_add_handle_in_table(char *name,
+ int *hdl)
+{
+ int i;
+ int handle;
+
+ /* create handle and add in the iommu hardware table */
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ if (!strcmp(iommu_cb_set.cb_info[i].name, name)) {
+ mutex_lock(&iommu_cb_set.cb_info[i].lock);
+ if (iommu_cb_set.cb_info[i].handle != HANDLE_INIT) {
+ pr_err("Error: %s already got handle 0x%x\n",
+ name,
+ iommu_cb_set.cb_info[i].handle);
+ *hdl = iommu_cb_set.cb_info[i].handle;
+ iommu_cb_set.cb_info[i].ref_cnt++;
+ mutex_unlock(&iommu_cb_set.cb_info[i].lock);
+ return -EINVAL;
+ }
+
+ /* make sure handle is unique */
+ do {
+ handle = cam_smmu_create_iommu_handle(i);
+ } while (cam_smmu_check_handle_unique(handle));
+
+ /* put handle in the table */
+ iommu_cb_set.cb_info[i].handle = handle;
+ iommu_cb_set.cb_info[i].cb_count = 0;
+ iommu_cb_set.cb_info[i].ref_cnt++;
+ *hdl = handle;
+ CDBG("%s creates handle 0x%x\n", name, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[i].lock);
+ return 0;
+ }
+ }
+
+ /* if i == iommu_cb_set.cb_num */
+ pr_err("Error: Cannot find name %s or all handle exist!\n",
+ name);
+ cam_smmu_print_table();
+ return -EINVAL;
+}
+
+static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map,
+ dma_addr_t base, size_t size,
+ int order)
+{
+ unsigned int count = size >> (PAGE_SHIFT + order);
+ unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long);
+ int err = 0;
+
+ if (!count) {
+ err = -EINVAL;
+ pr_err("Error: wrong size passed, page count can't be zero");
+ goto bail;
+ }
+
+ scratch_map->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!scratch_map->bitmap) {
+ err = -ENOMEM;
+ goto bail;
+ }
+
+ scratch_map->base = base;
+ scratch_map->bits = BITS_PER_BYTE * bitmap_size;
+ scratch_map->order = order;
+
+bail:
+ return err;
+}
+
+static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping,
+ size_t size,
+ dma_addr_t *iova)
+{
+ int rc = 0;
+ unsigned int order = get_order(size);
+ unsigned int align = 0;
+ unsigned int count, start;
+
+ count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) +
+ (1 << mapping->order) - 1) >> mapping->order;
+
+ /* Transparently, add a guard page to the total count of pages
+ * to be allocated
+ */
+ count++;
+
+ if (order > mapping->order)
+ align = (1 << (order - mapping->order)) - 1;
+
+ start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits, 0,
+ count, align);
+
+ if (start > mapping->bits)
+ rc = -ENOMEM;
+
+ bitmap_set(mapping->bitmap, start, count);
+
+ *iova = mapping->base + (start << (mapping->order + PAGE_SHIFT));
+ return rc;
+}
+
+static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping,
+ dma_addr_t addr, size_t size)
+{
+ unsigned int start = (addr - mapping->base) >>
+ (mapping->order + PAGE_SHIFT);
+ unsigned int count = ((size >> PAGE_SHIFT) +
+ (1 << mapping->order) - 1) >> mapping->order;
+
+ if (!addr) {
+ pr_err("Error: Invalid address\n");
+ return -EINVAL;
+ }
+
+ if (start + count > mapping->bits) {
+ pr_err("Error: Invalid page bits in scratch map\n");
+ return -EINVAL;
+ }
+
+ /* Transparently, add a guard page to the total count of pages
+ * to be freed
+ */
+ count++;
+
+ bitmap_clear(mapping->bitmap, start, count);
+
+ return 0;
+}
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx,
+ dma_addr_t virt_addr)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->paddr == virt_addr) {
+ CDBG("Found virtual address %lx\n",
+ (unsigned long)virt_addr);
+ return mapping;
+ }
+ }
+
+ pr_err("Error: Cannot find virtual address %lx by index %d\n",
+ (unsigned long)virt_addr, idx);
+ return NULL;
+}
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx,
+ int ion_fd)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->ion_fd == ion_fd) {
+ CDBG(" find ion_fd %d\n", ion_fd);
+ return mapping;
+ }
+ }
+
+ pr_err("Error: Cannot find fd %d by index %d\n",
+ ion_fd, idx);
+ return NULL;
+}
+
+static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx,
+ int ion_fd)
+{
+ struct cam_sec_buff_info *mapping;
+
+ list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->ion_fd == ion_fd) {
+ CDBG("[sec_cam] find ion_fd %d\n", ion_fd);
+ return mapping;
+ }
+ }
+ pr_err("Error: Cannot find fd %d by index %d\n",
+ ion_fd, idx);
+ return NULL;
+}
+
+static void cam_smmu_clean_buffer_list(int idx)
+{
+ int ret;
+ struct cam_dma_buff_info *mapping_info, *temp;
+
+ list_for_each_entry_safe(mapping_info, temp,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ CDBG("Free mapping address %pK, i = %d, fd = %d\n",
+ (void *)mapping_info->paddr, idx,
+ mapping_info->ion_fd);
+
+ if (mapping_info->ion_fd == 0xDEADBEEF)
+ /* Clean up scratch buffers */
+ ret = cam_smmu_free_scratch_buffer_remove_from_list(
+ mapping_info, idx);
+ else
+ /* Clean up regular mapped buffers */
+ ret = cam_smmu_unmap_buf_and_remove_from_list(
+ mapping_info,
+ idx);
+
+ if (ret < 0) {
+ pr_err("Buffer delete failed: idx = %d\n", idx);
+ pr_err("Buffer delete failed: addr = %lx, fd = %d\n",
+ (unsigned long)mapping_info->paddr,
+ mapping_info->ion_fd);
+ /*
+ * Ignore this error and continue to delete other
+ * buffers in the list
+ */
+ continue;
+ }
+ }
+}
+
+static int cam_smmu_attach(int idx)
+{
+ int ret;
+
+ if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_ATTACH) {
+ ret = 0;
+ } else if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) {
+ ret = cam_smmu_attach_device(idx);
+ if (ret < 0) {
+ pr_err("Error: ATTACH fail\n");
+ return -ENODEV;
+ }
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH;
+ ret = 0;
+ } else {
+ pr_err("Error: Not detach/attach\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cam_smmu_send_syscall_cpp_intf(int vmid, int idx)
+{
+ int rc = 0;
+ struct scm_desc desc = {0};
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+ uint32_t sid_info;
+
+
+ sid_info = cb->sids[0]; /* CPP SID */
+
+ desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
+ desc.args[0] = CAMERA_DEVICE_ID;
+ desc.args[1] = SCM_BUFFER_PHYS(&sid_info);
+ desc.args[2] = sizeof(uint32_t);
+ desc.args[3] = vmid;
+ /*
+ * Syscall to hypervisor to switch CPP SID's
+ * between secure and non-secure contexts
+ */
+ dmac_flush_range(&sid_info, &sid_info + 1);
+ if (scm_call2(SCM_SIP_FNID(SCM_SVC_MP, SECURE_SYSCALL_ID),
+ &desc)){
+ pr_err("call to hypervisor failed\n");
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int cam_smmu_send_syscall_pix_intf(int vmid, int idx)
+{
+ int rc = 0;
+ struct scm_desc desc = {0};
+ uint32_t *sid_info = NULL;
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+
+ sid_info = kzalloc(sizeof(uint32_t) * 2, GFP_KERNEL);
+ if (!sid_info)
+ return -ENOMEM;
+
+ sid_info[0] = cb->sids[0]; /* VFE 0 Image SID */
+ sid_info[1] = cb->sids[2]; /* VFE 1 Image SID */
+
+ desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
+ desc.args[0] = CAMERA_DEVICE_ID;
+ desc.args[1] = SCM_BUFFER_PHYS(sid_info);
+ desc.args[2] = sizeof(uint32_t) * 2;
+ desc.args[3] = vmid;
+ /*
+ * Syscall to hypervisor to switch VFE SID's
+ * between secure and non-secure contexts
+ */
+ dmac_flush_range(sid_info, sid_info + 2);
+ if (scm_call2(SCM_SIP_FNID(SCM_SVC_MP, SECURE_SYSCALL_ID),
+ &desc)){
+ pr_err("call to hypervisor failed\n");
+ kfree(sid_info);
+ return -EINVAL;
+ }
+
+ kfree(sid_info);
+ return rc;
+}
+
+static int cam_smmu_detach_device(int idx)
+{
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+
+ if (!list_empty_careful(&iommu_cb_set.cb_info[idx].smmu_buf_list)) {
+ pr_err("Client %s buffer list is not clean!\n",
+ iommu_cb_set.cb_info[idx].name);
+ cam_smmu_print_list(idx);
+ cam_smmu_clean_buffer_list(idx);
+ }
+
+ /* detach the mapping to device */
+ arm_iommu_detach_device(cb->dev);
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH;
+ return 0;
+}
+
+static int cam_smmu_attach_sec_cpp(int idx)
+{
+ int32_t rc = 0;
+
+ /*
+ * When switching to secure, detach CPP NS, do scm call
+ * with CPP SID and no need of attach again, because
+ * all cpp sids are shared in SCM call. so no need of
+ * attach again.
+ */
+ if (cam_smmu_send_syscall_cpp_intf(VMID_CP_CAMERA, idx)) {
+ pr_err("error: syscall failed\n");
+ return -EINVAL;
+ }
+
+ rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
+ MSM_CAMERA_TZ_HW_BLOCK_CPP);
+ if (rc != 0) {
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point cpp had already
+ * entered the secure mode.
+ */
+ }
+
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH;
+
+ return 0;
+}
+
+static int cam_smmu_detach_sec_cpp(int idx)
+{
+ int32_t rc = 0;
+
+ rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
+ MSM_CAMERA_TZ_HW_BLOCK_CPP);
+ if (rc != 0) {
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point cpp is in secure
+ * mode and should be switched to non-secure regardless
+ */
+ }
+
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH;
+
+ /*
+ * When exiting secure, do scm call to attach
+ * with CPP SID in NS mode.
+ */
+ if (cam_smmu_send_syscall_cpp_intf(VMID_HLOS, idx)) {
+ pr_err("error: syscall failed\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cam_smmu_attach_sec_vfe_ns_stats(int idx)
+{
+ int32_t rc = 0;
+
+ /*
+ *When switching to secure, for secure pix and non-secure stats
+ *localizing scm/attach of non-secure SID's in attach secure
+ */
+ if (cam_smmu_send_syscall_pix_intf(VMID_CP_CAMERA, idx)) {
+ pr_err("error: syscall failed\n");
+ return -EINVAL;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ if (cam_smmu_attach(idx)) {
+ pr_err("error: failed to attach\n");
+ return -EINVAL;
+ }
+ }
+
+ rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
+ MSM_CAMERA_TZ_HW_BLOCK_ISP);
+ if (rc != 0) {
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point vfe had already
+ * entered the secure mode
+ */
+ }
+
+ return 0;
+}
+
+static int cam_smmu_detach_sec_vfe_ns_stats(int idx)
+{
+ int32_t rc = 0;
+
+ rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
+ MSM_CAMERA_TZ_HW_BLOCK_ISP);
+ if (rc != 0) {
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point vfe is in secure
+ * mode and should be switched to non-secure regardless
+ */
+ }
+
+ /*
+ *While exiting from secure mode for secure pix and non-secure stats,
+ *localizing detach/scm of non-secure SID's to detach secure
+ */
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_DETACH) {
+ if (cam_smmu_detach_device(idx) < 0) {
+ pr_err("Error: ARM IOMMU detach failed\n");
+ return -ENODEV;
+ }
+ }
+
+ if (cam_smmu_send_syscall_pix_intf(VMID_HLOS, idx)) {
+ pr_err("error: syscall failed\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
+ enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ int rc = -1;
+ struct cam_dma_buff_info *mapping_info;
+ struct dma_buf *buf = NULL;
+ struct dma_buf_attachment *attach = NULL;
+ struct sg_table *table = NULL;
+
+ if (!paddr_ptr) {
+ rc = -EINVAL;
+ goto err_out;
+ }
+
+ /* allocate memory for each buffer information */
+ buf = dma_buf_get(ion_fd);
+ if (IS_ERR_OR_NULL(buf)) {
+ rc = PTR_ERR(buf);
+ pr_err("Error: dma get buf failed. fd = %d rc = %d\n",
+ ion_fd, rc);
+ goto err_out;
+ }
+
+ attach = dma_buf_attach(buf, iommu_cb_set.cb_info[idx].dev);
+ if (IS_ERR_OR_NULL(attach)) {
+ rc = PTR_ERR(attach);
+ pr_err("Error: dma buf attach failed\n");
+ goto err_put;
+ }
+
+ table = dma_buf_map_attachment(attach, dma_dir);
+ if (IS_ERR_OR_NULL(table)) {
+ rc = PTR_ERR(table);
+ pr_err("Error: dma buf map attachment failed\n");
+ goto err_detach;
+ }
+
+ rc = msm_dma_map_sg_lazy(iommu_cb_set.cb_info[idx].dev, table->sgl,
+ table->nents, dma_dir, buf);
+ if (rc != table->nents) {
+ pr_err("Error: msm_dma_map_sg_lazy failed\n");
+ rc = -ENOMEM;
+ goto err_unmap_sg;
+ }
+
+ if (table->sgl) {
+ CDBG("DMA buf: %pK, device: %pK, attach: %pK, table: %pK\n",
+ (void *)buf,
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)attach, (void *)table);
+ CDBG("table sgl: %pK, rc: %d, dma_address: 0x%x\n",
+ (void *)table->sgl, rc,
+ (unsigned int)table->sgl->dma_address);
+ } else {
+ rc = -EINVAL;
+ pr_err("Error: table sgl is null\n");
+ goto err_map_addr;
+ }
+
+ /* fill up mapping_info */
+ mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
+ if (!mapping_info) {
+ rc = -ENOSPC;
+ goto err_map_addr;
+ }
+ mapping_info->ion_fd = ion_fd;
+ mapping_info->buf = buf;
+ mapping_info->attach = attach;
+ mapping_info->table = table;
+ mapping_info->paddr = sg_dma_address(table->sgl);
+ mapping_info->len = (size_t)sg_dma_len(table->sgl);
+ mapping_info->dir = dma_dir;
+ mapping_info->ref_count = 1;
+
+ /* return paddr and len to client */
+ *paddr_ptr = sg_dma_address(table->sgl);
+ *len_ptr = (size_t)sg_dma_len(table->sgl);
+
+ if (!*paddr_ptr || !*len_ptr) {
+ pr_err("Error: Space Allocation failed!\n");
+ rc = -ENOSPC;
+ goto err_mapping_info;
+ }
+ CDBG("name %s ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n",
+ iommu_cb_set.cb_info[idx].name,
+ ion_fd,
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)*paddr_ptr, (unsigned int)*len_ptr);
+
+ /* add to the list */
+ list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+ return 0;
+
+err_mapping_info:
+ kzfree(mapping_info);
+err_map_addr:
+ msm_dma_unmap_sg(iommu_cb_set.cb_info[idx].dev,
+ table->sgl, table->nents,
+ dma_dir, buf);
+err_unmap_sg:
+ dma_buf_unmap_attachment(attach, table, dma_dir);
+err_detach:
+ dma_buf_detach(buf, attach);
+err_put:
+ dma_buf_put(buf);
+err_out:
+ return rc;
+}
+
+static int cam_smmu_unmap_buf_and_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx)
+{
+ if ((!mapping_info->buf) || (!mapping_info->table) ||
+ (!mapping_info->attach)) {
+ pr_err("Error: Invalid params dev = %pK, table = %pK",
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)mapping_info->table);
+ pr_err("Error:dma_buf = %pK, attach = %pK\n",
+ (void *)mapping_info->buf,
+ (void *)mapping_info->attach);
+ return -EINVAL;
+ }
+
+ /* iommu buffer clean up */
+ msm_dma_unmap_sg(iommu_cb_set.cb_info[idx].dev,
+ mapping_info->table->sgl, mapping_info->table->nents,
+ mapping_info->dir, mapping_info->buf);
+ dma_buf_unmap_attachment(mapping_info->attach,
+ mapping_info->table, mapping_info->dir);
+ dma_buf_detach(mapping_info->buf, mapping_info->attach);
+ dma_buf_put(mapping_info->buf);
+ mapping_info->buf = NULL;
+
+ list_del_init(&mapping_info->list);
+
+ /* free one buffer */
+ kfree(mapping_info);
+ return 0;
+}
+
+static enum cam_smmu_buf_state cam_smmu_check_fd_in_list(int idx,
+ int ion_fd, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->ion_fd == ion_fd) {
+ mapping->ref_count++;
+ *paddr_ptr = mapping->paddr;
+ *len_ptr = mapping->len;
+ return CAM_SMMU_BUFF_EXIST;
+ }
+ }
+ return CAM_SMMU_BUFF_NOT_EXIST;
+}
+
+static enum cam_smmu_buf_state cam_smmu_check_secure_fd_in_list(int idx,
+ int ion_fd, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ struct cam_sec_buff_info *mapping;
+
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->ion_fd == ion_fd) {
+ mapping->ref_count++;
+ *paddr_ptr = mapping->paddr;
+ *len_ptr = mapping->len;
+ return CAM_SMMU_BUFF_EXIST;
+ }
+ }
+ return CAM_SMMU_BUFF_NOT_EXIST;
+}
+
+int cam_smmu_get_handle(char *identifier, int *handle_ptr)
+{
+ int ret = 0;
+
+ if (!identifier) {
+ pr_err("Error: iommu hardware name is NULL\n");
+ return -EFAULT;
+ }
+
+ if (!handle_ptr) {
+ pr_err("Error: handle pointer is NULL\n");
+ return -EFAULT;
+ }
+
+ /* create and put handle in the table */
+ ret = cam_smmu_create_add_handle_in_table(identifier, handle_ptr);
+ if (ret < 0) {
+ pr_err("Error: %s get handle fail\n", identifier);
+ return ret;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(cam_smmu_get_handle);
+
+
+int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data)
+{
+ int ret = 0, idx;
+ struct cam_context_bank_info *cb = NULL;
+ struct iommu_domain *domain = NULL;
+
+ CDBG("E: set_attr\n");
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) {
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+ cb = &iommu_cb_set.cb_info[idx];
+ cb->attr |= flags;
+ /* set attributes */
+ ret = iommu_domain_set_attr(domain, cb->attr, (void *)data);
+ if (ret < 0) {
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ pr_err("Error: set attr\n");
+ return -ENODEV;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return ret;
+}
+EXPORT_SYMBOL(cam_smmu_set_attr);
+
+
+int cam_smmu_ops(int handle, enum cam_smmu_ops_param ops)
+{
+ int ret = 0, idx;
+
+ CDBG("E: ops = %d\n", ops);
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ switch (ops) {
+ case CAM_SMMU_ATTACH: {
+ ret = cam_smmu_attach(idx);
+ break;
+ }
+ case CAM_SMMU_DETACH: {
+ ret = cam_smmu_detach_device(idx);
+ break;
+ }
+ case CAM_SMMU_ATTACH_SEC_VFE_NS_STATS: {
+ ret = cam_smmu_attach_sec_vfe_ns_stats(idx);
+ break;
+ }
+ case CAM_SMMU_DETACH_SEC_VFE_NS_STATS: {
+ ret = cam_smmu_detach_sec_vfe_ns_stats(idx);
+ break;
+ }
+ case CAM_SMMU_ATTACH_SEC_CPP: {
+ ret = cam_smmu_attach_sec_cpp(idx);
+ break;
+ }
+ case CAM_SMMU_DETACH_SEC_CPP: {
+ ret = cam_smmu_detach_sec_cpp(idx);
+ break;
+ }
+ case CAM_SMMU_VOTE:
+ case CAM_SMMU_DEVOTE:
+ default:
+ pr_err("Error: idx = %d, ops = %d\n", idx, ops);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return ret;
+}
+EXPORT_SYMBOL(cam_smmu_ops);
+
+static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx,
+ size_t virt_len,
+ size_t phys_len,
+ unsigned int iommu_dir,
+ dma_addr_t *virt_addr)
+{
+ unsigned long nents = virt_len / phys_len;
+ struct cam_dma_buff_info *mapping_info = NULL;
+ size_t unmapped;
+ dma_addr_t iova = 0;
+ struct scatterlist *sg;
+ int i = 0;
+ int rc;
+ struct iommu_domain *domain = NULL;
+ struct page *page;
+ struct sg_table *table = NULL;
+
+ CDBG("%s: nents = %lu, idx = %d, virt_len = %zx\n",
+ __func__, nents, idx, virt_len);
+ CDBG("%s: phys_len = %zx, iommu_dir = %d, virt_addr = %pK\n",
+ __func__, phys_len, iommu_dir, virt_addr);
+
+ /* This table will go inside the 'mapping' structure
+ * where it will be held until put_scratch_buffer is called
+ */
+ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table) {
+ rc = -ENOMEM;
+ goto err_table_alloc;
+ }
+
+ rc = sg_alloc_table(table, nents, GFP_KERNEL);
+ if (rc < 0) {
+ rc = -EINVAL;
+ goto err_sg_alloc;
+ }
+
+ page = alloc_pages(GFP_KERNEL, get_order(phys_len));
+ if (!page) {
+ rc = -ENOMEM;
+ goto err_page_alloc;
+ }
+
+ /* Now we create the sg list */
+ for_each_sg(table->sgl, sg, table->nents, i)
+ sg_set_page(sg, page, phys_len, 0);
+
+
+ /* Get the domain from within our cb_set struct and map it*/
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+
+ rc = cam_smmu_alloc_scratch_va(&iommu_cb_set.cb_info[idx].scratch_map,
+ virt_len, &iova);
+
+ if (rc < 0) {
+ pr_err("Could not find valid iova for scratch buffer");
+ goto err_iommu_map;
+ }
+
+ if (iommu_map_sg(domain,
+ iova,
+ table->sgl,
+ table->nents,
+ iommu_dir) != virt_len) {
+ pr_err("iommu_map_sg() failed");
+ goto err_iommu_map;
+ }
+
+ /* Now update our mapping information within the cb_set struct */
+ mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
+ if (!mapping_info) {
+ rc = -ENOMEM;
+ goto err_mapping_info;
+ }
+
+ mapping_info->ion_fd = 0xDEADBEEF;
+ mapping_info->buf = NULL;
+ mapping_info->attach = NULL;
+ mapping_info->table = table;
+ mapping_info->paddr = iova;
+ mapping_info->len = virt_len;
+ mapping_info->iommu_dir = iommu_dir;
+ mapping_info->ref_count = 1;
+ mapping_info->phys_len = phys_len;
+
+ CDBG("%s: paddr = %pK, len = %zx, phys_len = %zx",
+ __func__, (void *)mapping_info->paddr,
+ mapping_info->len, mapping_info->phys_len);
+
+ list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+
+ *virt_addr = (dma_addr_t)iova;
+
+ CDBG("%s: mapped virtual address = %lx\n", __func__,
+ (unsigned long)*virt_addr);
+ return 0;
+
+err_mapping_info:
+ unmapped = iommu_unmap(domain, iova, virt_len);
+ if (unmapped != virt_len)
+ pr_err("Unmapped only %zx instead of %zx", unmapped, virt_len);
+err_iommu_map:
+ __free_pages(sg_page(table->sgl), get_order(phys_len));
+err_page_alloc:
+ sg_free_table(table);
+err_sg_alloc:
+ kfree(table);
+err_table_alloc:
+ return rc;
+}
+
+static int cam_smmu_free_scratch_buffer_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx)
+{
+ int rc = 0;
+ size_t unmapped;
+ struct iommu_domain *domain =
+ iommu_cb_set.cb_info[idx].mapping->domain;
+ struct scratch_mapping *scratch_map =
+ &iommu_cb_set.cb_info[idx].scratch_map;
+
+ if (!mapping_info->table) {
+ pr_err("Error: Invalid params: dev = %pK, table = %pK, ",
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)mapping_info->table);
+ return -EINVAL;
+ }
+
+ /* Clean up the mapping_info struct from the list */
+ unmapped = iommu_unmap(domain, mapping_info->paddr, mapping_info->len);
+ if (unmapped != mapping_info->len)
+ pr_err("Unmapped only %zx instead of %zx",
+ unmapped, mapping_info->len);
+
+ rc = cam_smmu_free_scratch_va(scratch_map,
+ mapping_info->paddr,
+ mapping_info->len);
+ if (rc < 0) {
+ pr_err("Error: Invalid iova while freeing scratch buffer\n");
+ rc = -EINVAL;
+ }
+
+ __free_pages(sg_page(mapping_info->table->sgl),
+ get_order(mapping_info->phys_len));
+ sg_free_table(mapping_info->table);
+ kfree(mapping_info->table);
+ list_del_init(&mapping_info->list);
+
+ kfree(mapping_info);
+ mapping_info = NULL;
+
+ return rc;
+}
+
+int cam_smmu_get_phy_addr_scratch(int handle,
+ enum cam_smmu_map_dir dir,
+ dma_addr_t *paddr_ptr,
+ size_t virt_len,
+ size_t phys_len)
+{
+ int idx, rc = 0;
+ unsigned int iommu_dir;
+
+ if (!paddr_ptr || !virt_len || !phys_len) {
+ pr_err("Error: Input pointer or lengths invalid\n");
+ return -EINVAL;
+ }
+
+ if (virt_len < phys_len) {
+ pr_err("Error: virt_len > phys_len");
+ return -EINVAL;
+ }
+
+ iommu_dir = cam_smmu_translate_dir_to_iommu_dir(dir);
+ if (iommu_dir == IOMMU_INVALID_DIR) {
+ pr_err("Error: translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].scratch_buf_support) {
+ pr_err("Error: Context bank does not support scratch bufs\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ CDBG("%s: smmu handle = %x, idx = %d, dir = %d\n",
+ __func__, handle, idx, dir);
+ CDBG("%s: virt_len = %zx, phys_len = %zx\n",
+ __func__, phys_len, virt_len);
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Error: Device %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!IS_ALIGNED(virt_len, PAGE_SIZE)) {
+ pr_err("Requested scratch buffer length not page aligned");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!IS_ALIGNED(virt_len, phys_len)) {
+ pr_err("Requested virtual length not aligned with physical length");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rc = cam_smmu_alloc_scratch_buffer_add_to_list(idx,
+ virt_len,
+ phys_len,
+ iommu_dir,
+ paddr_ptr);
+ if (rc < 0) {
+ pr_err("Error: mapping or add list fail\n");
+ goto error;
+ }
+
+error:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+
+int cam_smmu_put_phy_addr_scratch(int handle,
+ dma_addr_t paddr)
+{
+ int idx;
+ int rc = -1;
+ struct cam_dma_buff_info *mapping_info;
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto handle_err;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].scratch_buf_support) {
+ pr_err("Error: Context bank does not support scratch buffers");
+ rc = -EINVAL;
+ goto handle_err;
+ }
+
+ /* Based on virtual address and index, we can find mapping info
+ * of the scratch buffer
+ */
+ mapping_info = cam_smmu_find_mapping_by_virt_address(idx, paddr);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params\n");
+ rc = -EINVAL;
+ goto handle_err;
+ }
+
+ /* unmapping one buffer from device */
+ rc = cam_smmu_free_scratch_buffer_remove_from_list(mapping_info, idx);
+ if (rc < 0) {
+ pr_err("Error: unmap or remove list fail\n");
+ goto handle_err;
+ }
+handle_err:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+
+int cam_smmu_alloc_get_stage2_scratch_mem(int handle,
+ enum cam_smmu_map_dir dir, struct ion_client *client,
+ struct ion_handle **sc_handle, ion_phys_addr_t *addr,
+ size_t *len_ptr)
+{
+ int idx, rc = 0;
+ enum dma_data_direction dma_dir;
+
+ dma_dir = cam_smmu_translate_dir(dir);
+ if (dma_dir == DMA_NONE) {
+ pr_err("Error: translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ return -EINVAL;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Error: Device %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ return -EINVAL;
+ }
+ *sc_handle = ion_alloc(client, SZ_2M, SZ_2M,
+ ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID),
+ ION_FLAG_SECURE | ION_FLAG_CP_CAMERA);
+ if (IS_ERR_OR_NULL((void *) (*sc_handle))) {
+ rc = -ENOMEM;
+ goto err_ion_handle;
+ }
+
+ /* return addr and len to client */
+ rc = ion_phys(client, *sc_handle, addr, len_ptr);
+ if (rc) {
+ pr_err("%s: ION Get Physical failed, rc = %d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto err_ion_phys;
+ }
+
+ CDBG("dev = %pK, paddr= %pK, len = %u\n",
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)*addr, (unsigned int)*len_ptr);
+ return rc;
+
+err_ion_phys:
+ ion_free(client, *sc_handle);
+
+err_ion_handle:
+ *sc_handle = NULL;
+ return rc;
+}
+
+int cam_smmu_free_stage2_scratch_mem(int handle,
+ struct ion_client *client, struct ion_handle *sc_handle)
+{
+ int idx = 0;
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+ ion_free(client, sc_handle);
+ return 0;
+}
+
+static int cam_smmu_secure_unmap_buf_and_remove_from_list(
+ struct cam_sec_buff_info *mapping_info,
+ int idx)
+{
+ if (!mapping_info) {
+ pr_err("Error: List doesn't exist\n");
+ return -EINVAL;
+ }
+ ion_free(mapping_info->i_client, mapping_info->i_hdl);
+ list_del_init(&mapping_info->list);
+
+ /* free one buffer */
+ kfree(mapping_info);
+ return 0;
+}
+
+int cam_smmu_put_stage2_phy_addr(int handle, int ion_fd)
+{
+ int idx, rc;
+ struct cam_sec_buff_info *mapping_info;
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ /* based on ion fd and index, we can find mapping info of buffer */
+ mapping_info = cam_smmu_find_mapping_by_sec_buf_idx(idx, ion_fd);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params! idx = %d, fd = %d\n",
+ idx, ion_fd);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ mapping_info->ref_count--;
+ if (mapping_info->ref_count > 0) {
+ CDBG("There are still %u buffer(s) with same fd %d",
+ mapping_info->ref_count, mapping_info->ion_fd);
+ rc = 0;
+ goto put_addr_end;
+ }
+
+ /* unmapping one buffer from device */
+ rc = cam_smmu_secure_unmap_buf_and_remove_from_list(mapping_info, idx);
+ if (rc < 0) {
+ pr_err("Error: unmap or remove list fail\n");
+ goto put_addr_end;
+ }
+
+put_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_put_stage2_phy_addr);
+
+static int cam_smmu_map_stage2_buffer_and_add_to_list(int idx, int ion_fd,
+ enum dma_data_direction dma_dir, struct ion_client *client,
+ dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ int rc = 0;
+ struct ion_handle *i_handle = NULL;
+ struct cam_sec_buff_info *mapping_info;
+
+
+ /* clean the content from clients */
+ *paddr_ptr = (dma_addr_t)NULL;
+ *len_ptr = (size_t)0;
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Error: Device %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ return -EINVAL;
+ }
+
+ i_handle = ion_import_dma_buf_fd(client, ion_fd);
+ if (IS_ERR_OR_NULL((void *)(i_handle))) {
+ pr_err("%s: ion import dma buffer failed\n", __func__);
+ return -EINVAL;
+ }
+
+ /* return addr and len to client */
+ rc = ion_phys(client, i_handle, paddr_ptr, len_ptr);
+ if (rc) {
+ pr_err("%s: ION Get Physical failed, rc = %d\n",
+ __func__, rc);
+ return -EINVAL;
+ }
+
+ /* fill up mapping_info */
+ mapping_info = kzalloc(sizeof(struct cam_sec_buff_info), GFP_KERNEL);
+ if (!mapping_info)
+ return -ENOSPC;
+
+ mapping_info->ion_fd = ion_fd;
+ mapping_info->paddr = *paddr_ptr;
+ mapping_info->len = *len_ptr;
+ mapping_info->dir = dma_dir;
+ mapping_info->ref_count = 1;
+ mapping_info->i_hdl = i_handle;
+ mapping_info->i_client = client;
+
+ CDBG("ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n", ion_fd,
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)*paddr_ptr, (unsigned int)*len_ptr);
+
+ /* add to the list */
+ list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+
+ return rc;
+}
+
+int cam_smmu_get_stage2_phy_addr(int handle,
+ int ion_fd, enum cam_smmu_map_dir dir,
+ struct ion_client *client, ion_phys_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ int idx, rc;
+ enum dma_data_direction dma_dir;
+ enum cam_smmu_buf_state buf_state;
+
+ if (!paddr_ptr || !len_ptr) {
+ pr_err("Error: Input pointers are invalid\n");
+ return -EINVAL;
+ }
+ /* clean the content from clients */
+ *paddr_ptr = (dma_addr_t)NULL;
+ *len_ptr = (size_t)0;
+
+ dma_dir = cam_smmu_translate_dir(dir);
+ if (dma_dir == DMA_NONE) {
+ pr_err("Error: translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Error: Device %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ buf_state = cam_smmu_check_secure_fd_in_list(idx, ion_fd, paddr_ptr,
+ len_ptr);
+ if (buf_state == CAM_SMMU_BUFF_EXIST) {
+ CDBG("ion_fd:%d already in the list, give same addr back",
+ ion_fd);
+ rc = 0;
+ goto get_addr_end;
+ }
+ rc = cam_smmu_map_stage2_buffer_and_add_to_list(idx, ion_fd, dma_dir,
+ client, paddr_ptr, len_ptr);
+ if (rc < 0) {
+ pr_err("Error: mapping or add list fail\n");
+ goto get_addr_end;
+ }
+
+get_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+
+
+int cam_smmu_get_phy_addr(int handle, int ion_fd,
+ enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ int idx, rc;
+ enum dma_data_direction dma_dir;
+ enum cam_smmu_buf_state buf_state;
+
+ if (!paddr_ptr || !len_ptr) {
+ pr_err("Error: Input pointers are invalid\n");
+ return -EINVAL;
+ }
+ /* clean the content from clients */
+ *paddr_ptr = (dma_addr_t)NULL;
+ *len_ptr = (size_t)0;
+
+ dma_dir = cam_smmu_translate_dir(dir);
+ if (dma_dir == DMA_NONE) {
+ pr_err("Error: translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Error: Device %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr, len_ptr);
+ if (buf_state == CAM_SMMU_BUFF_EXIST) {
+ CDBG("ion_fd:%d already in the list, give same addr back",
+ ion_fd);
+ rc = 0;
+ goto get_addr_end;
+ }
+ rc = cam_smmu_map_buffer_and_add_to_list(idx, ion_fd, dma_dir,
+ paddr_ptr, len_ptr);
+ if (rc < 0) {
+ pr_err("Error: mapping or add list fail\n");
+ goto get_addr_end;
+ }
+
+get_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_get_phy_addr);
+
+int cam_smmu_put_phy_addr(int handle, int ion_fd)
+{
+ int idx, rc;
+ struct cam_dma_buff_info *mapping_info;
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ /* based on ion fd and index, we can find mapping info of buffer */
+ mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params! idx = %d, fd = %d\n",
+ idx, ion_fd);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ mapping_info->ref_count--;
+ if (mapping_info->ref_count > 0) {
+ CDBG("There are still %u buffer(s) with same fd %d",
+ mapping_info->ref_count, mapping_info->ion_fd);
+ rc = 0;
+ goto put_addr_end;
+ }
+
+ /* unmapping one buffer from device */
+ rc = cam_smmu_unmap_buf_and_remove_from_list(mapping_info, idx);
+ if (rc < 0) {
+ pr_err("Error: unmap or remove list fail\n");
+ goto put_addr_end;
+ }
+
+put_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_put_phy_addr);
+
+int cam_smmu_destroy_handle(int handle)
+{
+ int idx;
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (--iommu_cb_set.cb_info[idx].ref_cnt != 0) {
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return 0;
+ }
+
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ if (!list_empty_careful(&iommu_cb_set.cb_info[idx].smmu_buf_list)) {
+ pr_err("Client %s buffer list is not clean!\n",
+ iommu_cb_set.cb_info[idx].name);
+ cam_smmu_print_list(idx);
+ cam_smmu_clean_buffer_list(idx);
+ }
+
+ iommu_cb_set.cb_info[idx].cb_count = 0;
+ iommu_cb_set.cb_info[idx].handle = HANDLE_INIT;
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return 0;
+}
+EXPORT_SYMBOL(cam_smmu_destroy_handle);
+
+/*This function can only be called after smmu driver probe*/
+int cam_smmu_get_num_of_clients(void)
+{
+ return iommu_cb_set.cb_num;
+}
+
+static void cam_smmu_release_cb(struct platform_device *pdev)
+{
+ int i = 0;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ arm_iommu_detach_device(iommu_cb_set.cb_info[i].dev);
+ arm_iommu_release_mapping(iommu_cb_set.cb_info[i].mapping);
+ }
+
+ devm_kfree(&pdev->dev, iommu_cb_set.cb_info);
+ iommu_cb_set.cb_num = 0;
+}
+
+static int cam_smmu_setup_cb(struct cam_context_bank_info *cb,
+ struct device *dev)
+{
+ int rc = 0;
+
+ if (!cb || !dev) {
+ pr_err("Error: invalid input params\n");
+ return -EINVAL;
+ }
+
+ cb->dev = dev;
+ /* Reserve 256M if scratch buffer support is desired
+ * and initialize the scratch mapping structure
+ */
+ if (cb->scratch_buf_support) {
+ cb->va_start = SCRATCH_ALLOC_END;
+ cb->va_len = VA_SPACE_END - SCRATCH_ALLOC_END;
+
+ rc = cam_smmu_init_scratch_map(&cb->scratch_map,
+ SCRATCH_ALLOC_START,
+ SCRATCH_ALLOC_END - SCRATCH_ALLOC_START,
+ 0);
+ if (rc < 0) {
+ pr_err("Error: failed to create scratch map\n");
+ rc = -ENODEV;
+ goto end;
+ }
+ } else {
+ cb->va_start = SZ_128K;
+ cb->va_len = VA_SPACE_END - SZ_128M;
+ }
+
+ /* create a virtual mapping */
+ cb->mapping = arm_iommu_create_mapping(&platform_bus_type,
+ cb->va_start, cb->va_len);
+ if (IS_ERR(cb->mapping)) {
+ pr_err("Error: create mapping Failed\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ return 0;
+end:
+ return rc;
+}
+
+static int cam_smmu_populate_sids(struct device *dev,
+ struct cam_context_bank_info *cb)
+{
+ int i, j, rc = 0;
+ unsigned int cnt = 0;
+ const void *property;
+
+ /* set the name of the context bank */
+ property = of_get_property(dev->of_node, "iommus", &cnt);
+ cnt /= 4;
+ for (i = 0, j = 0; i < cnt; i = i + 2, j++) {
+ rc = of_property_read_u32_index(dev->of_node,
+ "iommus", i + 1, &cb->sids[j]);
+ if (rc < 0)
+ pr_err("misconfiguration, can't fetch SID\n");
+
+ pr_err("__debug cnt = %d, cb->name: :%s sid [%d] = %d\n,",
+ cnt, cb->name, j, cb->sids[j]);
+ }
+ return rc;
+}
+
+static int cam_alloc_smmu_context_banks(struct device *dev)
+{
+ struct device_node *domains_child_node = NULL;
+
+ if (!dev) {
+ pr_err("Error: Invalid device\n");
+ return -ENODEV;
+ }
+
+ iommu_cb_set.cb_num = 0;
+
+ /* traverse thru all the child nodes and increment the cb count */
+ for_each_child_of_node(dev->of_node, domains_child_node) {
+ if (of_device_is_compatible(domains_child_node,
+ "qcom,msm-cam-smmu-cb"))
+ iommu_cb_set.cb_num++;
+
+ if (of_device_is_compatible(domains_child_node,
+ "qcom,qsmmu-cam-cb"))
+ iommu_cb_set.cb_num++;
+ }
+
+ if (iommu_cb_set.cb_num == 0) {
+ pr_err("Error: no context banks present\n");
+ return -ENOENT;
+ }
+
+ /* allocate memory for the context banks */
+ iommu_cb_set.cb_info = devm_kzalloc(dev,
+ iommu_cb_set.cb_num * sizeof(struct cam_context_bank_info),
+ GFP_KERNEL);
+
+ if (!iommu_cb_set.cb_info) {
+ pr_err("Error: cannot allocate context banks\n");
+ return -ENOMEM;
+ }
+
+ cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_INIT);
+ iommu_cb_set.cb_init_count = 0;
+
+ CDBG("no of context banks :%d\n", iommu_cb_set.cb_num);
+ return 0;
+}
+
+static int cam_populate_smmu_context_banks(struct device *dev,
+ enum cam_iommu_type type)
+{
+ int rc = 0;
+ struct cam_context_bank_info *cb;
+ struct device *ctx;
+
+ if (!dev) {
+ pr_err("Error: Invalid device\n");
+ return -ENODEV;
+ }
+
+ /* check the bounds */
+ if (iommu_cb_set.cb_init_count >= iommu_cb_set.cb_num) {
+ pr_err("Error: populate more than allocated cb\n");
+ rc = -EBADHANDLE;
+ goto cb_init_fail;
+ }
+
+ /* read the context bank from cb set */
+ cb = &iommu_cb_set.cb_info[iommu_cb_set.cb_init_count];
+
+ /* set the name of the context bank */
+ rc = of_property_read_string(dev->of_node, "label", &cb->name);
+ if (rc) {
+ pr_err("Error: failed to read label from sub device\n");
+ goto cb_init_fail;
+ }
+
+ /* populate SID's for each cb */
+ rc = cam_smmu_populate_sids(dev, cb);
+ if (rc < 0)
+ pr_err("Error: failed to populate sids : %s\n", cb->name);
+
+ /* Check if context bank supports scratch buffers */
+ if (of_property_read_bool(dev->of_node, "qcom,scratch-buf-support"))
+ cb->scratch_buf_support = 1;
+ else
+ cb->scratch_buf_support = 0;
+
+ /* set the secure/non secure domain type */
+ if (of_property_read_bool(dev->of_node, "qcom,secure-context"))
+ cb->is_secure = true;
+ else
+ cb->is_secure = false;
+
+ CDBG("cb->name :%s, cb->is_secure :%d, cb->scratch_support :%d\n",
+ cb->name, cb->is_secure, cb->scratch_buf_support);
+
+ /* set up the iommu mapping for the context bank */
+ if (type == CAM_QSMMU) {
+ pr_err("Error: QSMMU ctx not supported for: %s\n", cb->name);
+ return -EINVAL;
+ }
+ ctx = dev;
+ CDBG("getting Arm SMMU ctx : %s\n", cb->name);
+
+ rc = cam_smmu_setup_cb(cb, ctx);
+ if (rc < 0)
+ pr_err("Error: failed to setup cb : %s\n", cb->name);
+
+ iommu_set_fault_handler(cb->mapping->domain,
+ cam_smmu_iommu_fault_handler,
+ (void *)cb->name);
+
+ /* increment count to next bank */
+ iommu_cb_set.cb_init_count++;
+
+ CDBG("X: cb init count :%d\n", iommu_cb_set.cb_init_count);
+ return rc;
+
+cb_init_fail:
+ iommu_cb_set.cb_info = NULL;
+ return rc;
+}
+
+static int cam_smmu_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct device *dev = &pdev->dev;
+
+ if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu")) {
+ rc = cam_alloc_smmu_context_banks(dev);
+ if (rc < 0) {
+ pr_err("Error: allocating context banks\n");
+ return -ENOMEM;
+ }
+ }
+ if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu-cb")) {
+ rc = cam_populate_smmu_context_banks(dev, CAM_ARM_SMMU);
+ if (rc < 0) {
+ pr_err("Error: populating context banks\n");
+ return -ENOMEM;
+ }
+ return rc;
+ }
+ if (of_device_is_compatible(dev->of_node, "qcom,qsmmu-cam-cb")) {
+ rc = cam_populate_smmu_context_banks(dev, CAM_QSMMU);
+ if (rc < 0) {
+ pr_err("Error: populating context banks\n");
+ return -ENOMEM;
+ }
+ return rc;
+ }
+
+ /* probe thru all the subdevices */
+ rc = of_platform_populate(pdev->dev.of_node, msm_cam_smmu_dt_match,
+ NULL, &pdev->dev);
+ if (rc < 0)
+ pr_err("Error: populating devices\n");
+
+ INIT_WORK(&iommu_cb_set.smmu_work, cam_smmu_page_fault_work);
+ mutex_init(&iommu_cb_set.payload_list_lock);
+ INIT_LIST_HEAD(&iommu_cb_set.payload_list);
+
+ return rc;
+}
+
+static int cam_smmu_remove(struct platform_device *pdev)
+{
+ /* release all the context banks and memory allocated */
+ cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_DEINIT);
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-cam-smmu"))
+ cam_smmu_release_cb(pdev);
+ return 0;
+}
+
+static struct platform_driver cam_smmu_driver = {
+ .probe = cam_smmu_probe,
+ .remove = cam_smmu_remove,
+ .driver = {
+ .name = "msm_cam_smmu",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cam_smmu_dt_match,
+ },
+};
+
+static int __init cam_smmu_init_module(void)
+{
+ return platform_driver_register(&cam_smmu_driver);
+}
+
+static void __exit cam_smmu_exit_module(void)
+{
+ platform_driver_unregister(&cam_smmu_driver);
+}
+
+module_init(cam_smmu_init_module);
+module_exit(cam_smmu_exit_module);
+MODULE_DESCRIPTION("MSM Camera SMMU driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h
new file mode 100644
index 0000000..ec8476d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h
@@ -0,0 +1,242 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_SMMU_API_H_
+#define _CAM_SMMU_API_H_
+
+#include <linux/dma-direction.h>
+#include <linux/module.h>
+#include <linux/dma-buf.h>
+#include <asm/dma-iommu.h>
+#include <linux/dma-direction.h>
+#include <linux/of_platform.h>
+#include <linux/iommu.h>
+#include <linux/random.h>
+#include <linux/spinlock_types.h>
+#include <linux/mutex.h>
+#include <linux/msm_ion.h>
+
+
+/*
+ * Enum for possible CAM SMMU operations
+ */
+
+enum cam_smmu_ops_param {
+ CAM_SMMU_ATTACH,
+ CAM_SMMU_DETACH,
+ CAM_SMMU_ATTACH_SEC_VFE_NS_STATS,
+ CAM_SMMU_DETACH_SEC_VFE_NS_STATS,
+ CAM_SMMU_ATTACH_SEC_CPP,
+ CAM_SMMU_DETACH_SEC_CPP,
+ CAM_SMMU_VOTE,
+ CAM_SMMU_DEVOTE,
+ CAM_SMMU_OPS_INVALID
+};
+
+enum cam_smmu_map_dir {
+ CAM_SMMU_MAP_READ,
+ CAM_SMMU_MAP_WRITE,
+ CAM_SMMU_MAP_RW,
+ CAM_SMMU_MAP_INVALID
+};
+
+typedef void (*client_handler)(struct iommu_domain *,
+ struct device *, unsigned long,
+ int, void*);
+
+typedef void (*client_reset_handler)(struct iommu_domain *,
+ struct device *, void*);
+
+/**
+ * @param identifier: Unique identifier to be used by clients which they
+ * should get from device tree. CAM SMMU driver will
+ * not enforce how this string is obtained and will
+ * only validate this against the list of permitted
+ * identifiers
+ * @param handle_ptr: Based on the indentifier, CAM SMMU drivier will
+ * fill the handle pointed by handle_ptr
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_handle(char *identifier, int *handle_ptr);
+
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param flags : SMMU attribute type
+ * @data : Value of attribute
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data);
+
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param op : Operation to be performed. Can be either CAM_SMMU_ATTACH
+ * or CAM_SMMU_DETACH
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_ops(int handle, enum cam_smmu_ops_param op);
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param ion_fd: ION handle identifying the memory buffer.
+ * @phys_addr : Pointer to physical address where mapped address will be
+ * returned.
+ * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL,
+ * DMA_TO_DEVICE or DMA_FROM_DEVICE
+ * @len : Length of buffer mapped returned by CAM SMMU driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_phy_addr(int handle,
+ int ion_fd, enum cam_smmu_map_dir dir,
+ dma_addr_t *dma_addr, size_t *len_ptr);
+
+/**
+ * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param ion_fd: ION handle identifying the memory buffer.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_put_phy_addr(int handle, int ion_fd);
+
+/**
+ * @param handle: Client has to pass back the smmu handle provided.
+ * @param ion_fd: ION handle identifying the memory buffer.
+ * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL,
+ * DMA_TO_DEVICE or DMA_FROM_DEVICE
+ * @client : Client has to pass the ion_client pointer created by the client.
+ * @phys_addr : Pointer to physical address where mapped address will be
+ * returned.
+ * @len : Length of buffer mapped returned by CAM SMMU driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_stage2_phy_addr(int handle,
+ int ion_fd, enum cam_smmu_map_dir dir,
+ struct ion_client *client, ion_phys_addr_t *addr,
+ size_t *len_ptr);
+
+/**
+ * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param ion_fd: ION handle identifying the memory buffer.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_put_stage2_phy_addr(int handle, int ion_fd);
+
+/**
+ * @param handle: Client has to pass back the smmu handle provided.
+ * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL,
+ * DMA_TO_DEVICE or DMA_FROM_DEVICE, client has to pass.
+ * @client : Client has to pass the ion_client pointer created by the client.
+ * @ion_handle : handle to the buffer returned by CAM SMMU driver.
+ * @phys_addr : Pointer to physical address where mapped address will be
+ * returned.
+ * @len : Length of buffer mapped returned by CAM SMMU driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_alloc_get_stage2_scratch_mem(int handle,
+ enum cam_smmu_map_dir dir, struct ion_client *client,
+ struct ion_handle **sc_handle, ion_phys_addr_t *addr,
+ size_t *len_ptr);
+
+
+/**
+ * @param handle: Client has to pass back the smmu handle provided.
+ * @client : Client has to pass the ion_client pointer provided by SMMU
+ * driver.
+ * @ion_handle : Client has to pass the ion_handle provided by SMMU
+ * driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int cam_smmu_free_stage2_scratch_mem(int handle,
+ struct ion_client *client, struct ion_handle *sc_handle);
+
+/**
+ * @brief : Allocates a scratch buffer
+ *
+ * This function allocates a scratch virtual buffer of length virt_len in the
+ * device virtual address space mapped to phys_len physically contiguous bytes
+ * in that device's SMMU.
+ *
+ * virt_len and phys_len are expected to be aligned to PAGE_SIZE and with each
+ * other, otherwise -EINVAL is returned.
+ *
+ * -EINVAL will be returned if virt_len is less than phys_len.
+ *
+ * Passing a too large phys_len might also cause failure if that much size is
+ * not available for allocation in a physically contiguous way.
+ *
+ * @param handle : Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param dir : Direction of mapping which will translate to IOMMU_READ
+ * IOMMU_WRITE or a bit mask of both.
+ * @param paddr_ptr: Device virtual address that the client device will be
+ * able to read from/write to
+ * @param virt_len : Virtual length of the scratch buffer
+ * @param phys_len : Physical length of the scratch buffer
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int cam_smmu_get_phy_addr_scratch(int handle,
+ enum cam_smmu_map_dir dir,
+ dma_addr_t *paddr_ptr,
+ size_t virt_len,
+ size_t phys_len);
+
+/**
+ * @brief : Frees a scratch buffer
+ *
+ * This function frees a scratch buffer and releases the corresponding SMMU
+ * mappings.
+ *
+ * @param handle : Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * IOMMU_WRITE or a bit mask of both.
+ * @param paddr_ptr: Device virtual address of client's scratch buffer that
+ * will be freed.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int cam_smmu_put_phy_addr_scratch(int handle,
+ dma_addr_t paddr);
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_destroy_handle(int handle);
+
+/**
+ * @return numger of client. Zero in case of error.
+ */
+int cam_smmu_get_num_of_clients(void);
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @return Index of SMMU client. Nagative in case of error.
+ */
+int cam_smmu_find_index_by_handle(int hdl);
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param client_page_fault_handler: It is triggered in IOMMU page fault
+ * @param client_hw_reset_handler: It is triggered in IOMMU page fault
+ * @param token: It is input param when trigger page fault handler
+ */
+void cam_smmu_reg_client_page_fault_handler(int handle,
+ client_handler page_fault_handler,
+ client_reset_handler hw_reset_handler,
+ void *token);
+
+#endif /* _CAM_SMMU_API_H_ */
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
new file mode 100644
index 0000000..3c38f12
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
@@ -0,0 +1,1100 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-SOC %s:%d " fmt, __func__, __LINE__
+#define NO_SET_RATE -1
+#define INIT_RATE -2
+
+#ifdef CONFIG_CAM_SOC_API_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/msm-bus.h>
+#include <linux/clk/msm-clk.h>
+#include "cam_soc_api.h"
+
+struct msm_cam_bus_pscale_data {
+ struct msm_bus_scale_pdata *pdata;
+ uint32_t bus_client;
+ uint32_t num_usecases;
+ uint32_t num_paths;
+ unsigned int vector_index;
+ bool dyn_vote;
+ struct mutex lock;
+};
+
+static struct msm_cam_bus_pscale_data g_cv[CAM_BUS_CLIENT_MAX];
+
+
+/* Get all clocks from DT */
+static int msm_camera_get_clk_info_internal(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+ size_t cnt, tmp;
+ uint32_t *rates, i = 0;
+ const char *clk_ctl = NULL;
+ bool clock_cntl_support = false;
+ struct device_node *of_node;
+
+ of_node = dev->of_node;
+
+ cnt = of_property_count_strings(of_node, "clock-names");
+ if (cnt <= 0) {
+ pr_err("err: No clocks found in DT=%zu\n", cnt);
+ return -EINVAL;
+ }
+
+ tmp = of_property_count_u32_elems(of_node, "qcom,clock-rates");
+ if (tmp <= 0) {
+ pr_err("err: No clk rates device tree, count=%zu", tmp);
+ return -EINVAL;
+ }
+
+ if (cnt != tmp) {
+ pr_err("err: clk name/rates mismatch, strings=%zu, rates=%zu\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,clock-cntl-support")) {
+ tmp = of_property_count_strings(of_node,
+ "qcom,clock-control");
+ if (tmp <= 0) {
+ pr_err("err: control strings not found in DT count=%zu",
+ tmp);
+ return -EINVAL;
+ }
+ if (cnt != tmp) {
+ pr_err("err: controls mismatch, strings=%zu, ctl=%zu\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+ clock_cntl_support = true;
+ }
+
+ *num_clk = cnt;
+
+ *clk_info = devm_kcalloc(dev, cnt,
+ sizeof(struct msm_cam_clk_info), GFP_KERNEL);
+ if (!*clk_info)
+ return -ENOMEM;
+
+ *clk_ptr = devm_kcalloc(dev, cnt, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!*clk_ptr) {
+ rc = -ENOMEM;
+ goto err1;
+ }
+
+ rates = devm_kcalloc(dev, cnt, sizeof(long), GFP_KERNEL);
+ if (!rates) {
+ rc = -ENOMEM;
+ goto err2;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,clock-rates",
+ rates, cnt);
+ if (rc < 0) {
+ pr_err("err: failed reading clock rates\n");
+ rc = -EINVAL;
+ goto err3;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &((*clk_info)[i].clk_name));
+ if (rc < 0) {
+ pr_err("%s reading clock-name failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto err3;
+ }
+
+ CDBG("dbg: clk-name[%d] = %s\n", i, (*clk_info)[i].clk_name);
+ if (clock_cntl_support) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,clock-control", i, &clk_ctl);
+ if (rc < 0) {
+ pr_err("%s reading clock-control failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto err3;
+ }
+
+ if (!strcmp(clk_ctl, "NO_SET_RATE"))
+ (*clk_info)[i].clk_rate = NO_SET_RATE;
+ else if (!strcmp(clk_ctl, "INIT_RATE"))
+ (*clk_info)[i].clk_rate = INIT_RATE;
+ else if (!strcmp(clk_ctl, "SET_RATE"))
+ (*clk_info)[i].clk_rate = rates[i];
+ else {
+ pr_err("%s: error: clock control has invalid value\n",
+ __func__);
+ rc = -EBUSY;
+ goto err3;
+ }
+ } else
+ (*clk_info)[i].clk_rate =
+ (rates[i] == 0) ? (long)-1 : rates[i];
+
+ CDBG("dbg: clk-rate[%d] = rate: %ld\n",
+ i, (*clk_info)[i].clk_rate);
+
+ (*clk_ptr)[i] =
+ devm_clk_get(dev, (*clk_info)[i].clk_name);
+ if (IS_ERR((*clk_ptr)[i])) {
+ rc = PTR_ERR((*clk_ptr)[i]);
+ goto err4;
+ }
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+
+ devm_kfree(dev, rates);
+ return rc;
+
+err4:
+ if (i > 0) {
+ do {
+ devm_clk_put(dev, (*clk_ptr)[--i]);
+ } while (i);
+ }
+err3:
+ devm_kfree(dev, rates);
+err2:
+ devm_kfree(dev, *clk_ptr);
+err1:
+ devm_kfree(dev, *clk_info);
+ return rc;
+}
+
+/* Get all clocks from DT for I2C devices */
+int msm_camera_i2c_dev_get_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+
+ if (!dev || !clk_info || !clk_ptr || !num_clk)
+ return -EINVAL;
+
+ rc = msm_camera_get_clk_info_internal(dev, clk_info, clk_ptr, num_clk);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_i2c_dev_get_clk_info);
+
+/* Get all clocks from DT for platform devices */
+int msm_camera_get_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk)
+{
+ int rc = 0;
+
+ if (!pdev || !&pdev->dev || !clk_info || !clk_ptr || !num_clk)
+ return -EINVAL;
+
+ rc = msm_camera_get_clk_info_internal(&pdev->dev,
+ clk_info, clk_ptr, num_clk);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_clk_info);
+
+/* Get all clocks and multiple rates from DT */
+int msm_camera_get_clk_info_and_rates(
+ struct platform_device *pdev,
+ struct msm_cam_clk_info **pclk_info,
+ struct clk ***pclks,
+ uint32_t ***pclk_rates,
+ size_t *num_set,
+ size_t *num_clk)
+{
+ int rc = 0, tmp_var, cnt, tmp;
+ int32_t i = 0, j = 0;
+ struct device_node *of_node;
+ uint32_t **rates;
+ struct clk **clks;
+ struct msm_cam_clk_info *clk_info;
+
+ if (!pdev || !pclk_info || !num_clk
+ || !pclk_rates || !pclks || !num_set)
+ return -EINVAL;
+
+ of_node = pdev->dev.of_node;
+
+ cnt = of_property_count_strings(of_node, "clock-names");
+ if (cnt <= 0) {
+ pr_err("err: No clocks found in DT=%d\n", cnt);
+ return -EINVAL;
+ }
+
+ tmp = of_property_count_u32_elems(of_node, "qcom,clock-rates");
+ if (tmp <= 0) {
+ pr_err("err: No clk rates device tree, count=%d\n", tmp);
+ return -EINVAL;
+ }
+
+ if ((tmp % cnt) != 0) {
+ pr_err("err: clk name/rates mismatch, strings=%d, rates=%d\n",
+ cnt, tmp);
+ return -EINVAL;
+ }
+
+ *num_clk = cnt;
+ *num_set = (tmp / cnt);
+
+ clk_info = devm_kcalloc(&pdev->dev, cnt,
+ sizeof(struct msm_cam_clk_info), GFP_KERNEL);
+ if (!clk_info)
+ return -ENOMEM;
+
+ clks = devm_kcalloc(&pdev->dev, cnt, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!clks) {
+ rc = -ENOMEM;
+ goto err1;
+ }
+
+ rates = devm_kcalloc(&pdev->dev, *num_set,
+ sizeof(uint32_t *), GFP_KERNEL);
+ if (!rates) {
+ rc = -ENOMEM;
+ goto err2;
+ }
+
+ for (i = 0; i < *num_set; i++) {
+ rates[i] = devm_kcalloc(&pdev->dev, *num_clk,
+ sizeof(uint32_t), GFP_KERNEL);
+ if (!rates[i] && (i > 0)) {
+ rc = -ENOMEM;
+ for (j = i - 1; j >= 0; j--)
+ devm_kfree(&pdev->dev, rates[j]);
+ goto err3;
+ }
+ }
+
+ tmp_var = 0;
+ for (i = 0; i < *num_set; i++) {
+ for (j = 0; j < *num_clk; j++) {
+ rc = of_property_read_u32_index(of_node,
+ "qcom,clock-rates", tmp_var++, &rates[i][j]);
+ if (rc < 0) {
+ pr_err("err: failed reading clock rates\n");
+ rc = -EINVAL;
+ goto err4;
+ }
+ CDBG("Clock rate idx %d idx %d value %d\n",
+ i, j, rates[i][j]);
+ }
+ }
+ for (i = 0; i < *num_clk; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &clk_info[i].clk_name);
+ if (rc < 0) {
+ pr_err("%s reading clock-name failed index %d\n",
+ __func__, i);
+ rc = -EINVAL;
+ goto err4;
+ }
+
+ CDBG("dbg: clk-name[%d] = %s\n", i, clk_info[i].clk_name);
+
+ clks[i] =
+ devm_clk_get(&pdev->dev, clk_info[i].clk_name);
+ if (IS_ERR(clks[i])) {
+ rc = PTR_ERR(clks[i]);
+ goto err5;
+ }
+ CDBG("clk ptr[%d] :%pK\n", i, clks[i]);
+ }
+ *pclk_info = clk_info;
+ *pclks = clks;
+ *pclk_rates = rates;
+
+ return rc;
+
+err5:
+ if (i > 0) {
+ do {
+ devm_clk_put(&pdev->dev, clks[--i]);
+ } while (i);
+ }
+err4:
+ for (i = 0; i < *num_set; i++)
+ devm_kfree(&pdev->dev, rates[i]);
+err3:
+ devm_kfree(&pdev->dev, rates);
+err2:
+ devm_kfree(&pdev->dev, clks);
+err1:
+ devm_kfree(&pdev->dev, clk_info);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_clk_info_and_rates);
+
+/* Enable/Disable all clocks */
+int msm_camera_clk_enable(struct device *dev,
+ struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ int i;
+ int rc = 0;
+ long clk_rate;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ CDBG("enable %s\n", clk_info[i].clk_name);
+ if (clk_info[i].clk_rate > 0) {
+ clk_rate = clk_round_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (clk_rate < 0) {
+ pr_err("%s round failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+
+ } else if (clk_info[i].clk_rate == INIT_RATE) {
+ clk_rate = clk_get_rate(clk_ptr[i]);
+ if (clk_rate == 0) {
+ clk_rate =
+ clk_round_rate(clk_ptr[i], 0);
+ if (clk_rate <= 0) {
+ pr_err("%s round rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ rc = clk_set_rate(clk_ptr[i], clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ rc = clk_prepare_enable(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_enable_err;
+ }
+ if (clk_info[i].delay > 20) {
+ msleep(clk_info[i].delay);
+ } else if (clk_info[i].delay) {
+ usleep_range(clk_info[i].delay * 1000,
+ (clk_info[i].delay * 1000) + 1000);
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ CDBG("%s disable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_disable_unprepare(clk_ptr[i]);
+ }
+ }
+ }
+ return rc;
+
+cam_clk_enable_err:
+cam_clk_set_err:
+ for (i--; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ clk_disable_unprepare(clk_ptr[i]);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_clk_enable);
+
+/* Set rate on a specific clock */
+long msm_camera_clk_set_rate(struct device *dev,
+ struct clk *clk,
+ long clk_rate)
+{
+ int rc = 0;
+ long rate = 0;
+
+ if (!dev || !clk || (clk_rate < 0))
+ return -EINVAL;
+
+ CDBG("clk : %pK, enable : %ld\n", clk, clk_rate);
+
+ if (clk_rate > 0) {
+ rate = clk_round_rate(clk, clk_rate);
+ if (rate < 0) {
+ pr_err("round rate failed\n");
+ return -EINVAL;
+ }
+
+ rc = clk_set_rate(clk, rate);
+ if (rc < 0) {
+ pr_err("set rate failed\n");
+ return -EINVAL;
+ }
+ }
+
+ return rate;
+}
+EXPORT_SYMBOL(msm_camera_clk_set_rate);
+
+int msm_camera_set_clk_flags(struct clk *clk, unsigned long flags)
+{
+ if (!clk)
+ return -EINVAL;
+
+ CDBG("clk : %pK, flags : %ld\n", clk, flags);
+
+ return clk_set_flags(clk, flags);
+}
+EXPORT_SYMBOL(msm_camera_set_clk_flags);
+
+/* release memory allocated for clocks */
+static int msm_camera_put_clk_info_internal(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int i;
+
+ for (i = cnt - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ devm_clk_put(dev, (*clk_ptr)[i]);
+
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+ devm_kfree(dev, *clk_info);
+ devm_kfree(dev, *clk_ptr);
+ *clk_info = NULL;
+ *clk_ptr = NULL;
+ return 0;
+}
+
+/* release memory allocated for clocks for i2c devices */
+int msm_camera_i2c_dev_put_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int rc = 0;
+
+ if (!dev || !clk_info || !clk_ptr)
+ return -EINVAL;
+
+ rc = msm_camera_put_clk_info_internal(dev, clk_info, clk_ptr, cnt);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_i2c_dev_put_clk_info);
+
+/* release memory allocated for clocks for platform devices */
+int msm_camera_put_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt)
+{
+ int rc = 0;
+
+ if (!pdev || !&pdev->dev || !clk_info || !clk_ptr)
+ return -EINVAL;
+
+ rc = msm_camera_put_clk_info_internal(&pdev->dev,
+ clk_info, clk_ptr, cnt);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_put_clk_info);
+
+int msm_camera_put_clk_info_and_rates(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, uint32_t ***clk_rates,
+ size_t set, size_t cnt)
+{
+ int i;
+
+ for (i = set - 1; i >= 0; i--)
+ devm_kfree(&pdev->dev, (*clk_rates)[i]);
+
+ devm_kfree(&pdev->dev, *clk_rates);
+ for (i = cnt - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL)
+ devm_clk_put(&pdev->dev, (*clk_ptr)[i]);
+ CDBG("clk ptr[%d] :%pK\n", i, (*clk_ptr)[i]);
+ }
+ devm_kfree(&pdev->dev, *clk_info);
+ devm_kfree(&pdev->dev, *clk_ptr);
+ *clk_info = NULL;
+ *clk_ptr = NULL;
+ *clk_rates = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_put_clk_info_and_rates);
+
+/* Get reset info from DT */
+int msm_camera_get_reset_info(struct platform_device *pdev,
+ struct reset_control **micro_iface_reset)
+{
+ if (!pdev || !micro_iface_reset)
+ return -EINVAL;
+
+ if (of_property_match_string(pdev->dev.of_node, "reset-names",
+ "micro_iface_reset")) {
+ pr_err("err: Reset property not found\n");
+ return -EINVAL;
+ }
+
+ *micro_iface_reset = devm_reset_control_get
+ (&pdev->dev, "micro_iface_reset");
+ if (IS_ERR(*micro_iface_reset))
+ return PTR_ERR(*micro_iface_reset);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_get_reset_info);
+
+/* Get regulators from DT */
+int msm_camera_get_regulator_info(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info,
+ int *num_reg)
+{
+ uint32_t cnt;
+ int i, rc;
+ struct device_node *of_node;
+ char prop_name[32];
+ struct msm_cam_regulator *tmp_reg;
+
+ if (!pdev || !vdd_info || !num_reg)
+ return -EINVAL;
+
+ of_node = pdev->dev.of_node;
+
+ if (!of_get_property(of_node, "qcom,vdd-names", NULL)) {
+ pr_err("err: Regulators property not found\n");
+ return -EINVAL;
+ }
+
+ cnt = of_property_count_strings(of_node, "qcom,vdd-names");
+ if (cnt <= 0) {
+ pr_err("err: no regulators found in device tree, count=%d",
+ cnt);
+ return -EINVAL;
+ }
+
+ tmp_reg = devm_kcalloc(&pdev->dev, cnt,
+ sizeof(struct msm_cam_regulator), GFP_KERNEL);
+ if (!tmp_reg)
+ return -ENOMEM;
+
+ for (i = 0; i < cnt; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,vdd-names", i, &tmp_reg[i].name);
+ if (rc < 0) {
+ pr_err("Fail to fetch regulators: %d\n", i);
+ rc = -EINVAL;
+ goto err1;
+ }
+
+ CDBG("regulator-names[%d] = %s\n", i, tmp_reg[i].name);
+
+ snprintf(prop_name, 32, "%s-supply", tmp_reg[i].name);
+
+ if (of_get_property(of_node, prop_name, NULL)) {
+ tmp_reg[i].vdd =
+ devm_regulator_get(&pdev->dev, tmp_reg[i].name);
+ if (IS_ERR(tmp_reg[i].vdd)) {
+ rc = -EINVAL;
+ pr_err("Fail to get regulator :%d\n", i);
+ goto err1;
+ }
+ } else {
+ pr_err("Regulator phandle not found :%s\n",
+ tmp_reg[i].name);
+ rc = -EINVAL;
+ goto err1;
+ }
+ CDBG("vdd ptr[%d] :%pK\n", i, tmp_reg[i].vdd);
+ }
+
+ *num_reg = cnt;
+ *vdd_info = tmp_reg;
+
+ return 0;
+
+err1:
+ for (--i; i >= 0; i--)
+ devm_regulator_put(tmp_reg[i].vdd);
+ devm_kfree(&pdev->dev, tmp_reg);
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_get_regulator_info);
+
+
+/* Enable/Disable regulators */
+int msm_camera_regulator_enable(struct msm_cam_regulator *vdd_info,
+ int cnt, int enable)
+{
+ int i;
+ int rc;
+ struct msm_cam_regulator *tmp = vdd_info;
+
+ if (!tmp) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+ CDBG("cnt : %d\n", cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (tmp && !IS_ERR_OR_NULL(tmp->vdd)) {
+ CDBG("name : %s, enable : %d\n", tmp->name, enable);
+ if (enable) {
+ rc = regulator_enable(tmp->vdd);
+ if (rc < 0) {
+ pr_err("regulator enable failed %d\n",
+ i);
+ goto error;
+ }
+ } else {
+ rc = regulator_disable(tmp->vdd);
+ if (rc < 0)
+ pr_err("regulator disable failed %d\n",
+ i);
+ }
+ }
+ tmp++;
+ }
+
+ return 0;
+error:
+ for (--i; i > 0; i--) {
+ --tmp;
+ if (!IS_ERR_OR_NULL(tmp->vdd))
+ regulator_disable(tmp->vdd);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_regulator_enable);
+
+/* set regulator mode */
+int msm_camera_regulator_set_mode(struct msm_cam_regulator *vdd_info,
+ int cnt, bool mode)
+{
+ int i;
+ int rc;
+ struct msm_cam_regulator *tmp = vdd_info;
+
+ if (!tmp) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+ CDBG("cnt : %d\n", cnt);
+
+ for (i = 0; i < cnt; i++) {
+ if (tmp && !IS_ERR_OR_NULL(tmp->vdd)) {
+ CDBG("name : %s, enable : %d\n", tmp->name, mode);
+ if (mode) {
+ rc = regulator_set_mode(tmp->vdd,
+ REGULATOR_MODE_FAST);
+ if (rc < 0) {
+ pr_err("regulator enable failed %d\n",
+ i);
+ goto error;
+ }
+ } else {
+ rc = regulator_set_mode(tmp->vdd,
+ REGULATOR_MODE_NORMAL);
+ if (rc < 0)
+ pr_err("regulator disable failed %d\n",
+ i);
+ goto error;
+ }
+ }
+ tmp++;
+ }
+
+ return 0;
+error:
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_regulator_set_mode);
+
+
+/* Put regulators regulators */
+void msm_camera_put_regulators(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int cnt)
+{
+ int i;
+
+ if (!vdd_info || !*vdd_info) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ for (i = cnt - 1; i >= 0; i--) {
+ if (vdd_info[i] && !IS_ERR_OR_NULL(vdd_info[i]->vdd))
+ devm_regulator_put(vdd_info[i]->vdd);
+ CDBG("vdd ptr[%d] :%pK\n", i, vdd_info[i]->vdd);
+ }
+
+ devm_kfree(&pdev->dev, *vdd_info);
+ *vdd_info = NULL;
+}
+EXPORT_SYMBOL(msm_camera_put_regulators);
+
+struct resource *msm_camera_get_irq(struct platform_device *pdev,
+ char *irq_name)
+{
+ if (!pdev || !irq_name) {
+ pr_err("Invalid params\n");
+ return NULL;
+ }
+
+ CDBG("Get irq for %s\n", irq_name);
+ return platform_get_resource_byname(pdev, IORESOURCE_IRQ, irq_name);
+}
+EXPORT_SYMBOL(msm_camera_get_irq);
+
+int msm_camera_register_irq(struct platform_device *pdev,
+ struct resource *irq, irq_handler_t handler,
+ unsigned long irqflags, char *irq_name, void *dev_id)
+{
+ int rc = 0;
+
+ if (!pdev || !irq || !handler || !irq_name || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ rc = devm_request_irq(&pdev->dev, irq->start, handler,
+ irqflags, irq_name, dev_id);
+ if (rc < 0) {
+ pr_err("irq request fail\n");
+ rc = -EINVAL;
+ }
+
+ CDBG("Registered irq for %s[resource - %pK]\n", irq_name, irq);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_register_irq);
+
+int msm_camera_register_threaded_irq(struct platform_device *pdev,
+ struct resource *irq, irq_handler_t handler_fn,
+ irq_handler_t thread_fn, unsigned long irqflags,
+ const char *irq_name, void *dev_id)
+{
+ int rc = 0;
+
+ if (!pdev || !irq || !irq_name || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev, irq->start, handler_fn,
+ thread_fn, irqflags, irq_name, dev_id);
+ if (rc < 0) {
+ pr_err("irq request fail\n");
+ rc = -EINVAL;
+ }
+
+ CDBG("Registered irq for %s[resource - %pK]\n", irq_name, irq);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_camera_register_threaded_irq);
+
+int msm_camera_enable_irq(struct resource *irq, int enable)
+{
+ if (!irq) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("irq Enable %d\n", enable);
+ if (enable)
+ enable_irq(irq->start);
+ else
+ disable_irq(irq->start);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_enable_irq);
+
+int msm_camera_unregister_irq(struct platform_device *pdev,
+ struct resource *irq, void *dev_id)
+{
+
+ if (!pdev || !irq || !dev_id) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("Un Registering irq for [resource - %pK]\n", irq);
+ devm_free_irq(&pdev->dev, irq->start, dev_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_unregister_irq);
+
+void __iomem *msm_camera_get_reg_base(struct platform_device *pdev,
+ char *device_name, int reserve_mem)
+{
+ struct resource *mem;
+ void __iomem *base;
+
+ if (!pdev || !device_name) {
+ pr_err("Invalid params\n");
+ return NULL;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return NULL;
+ }
+
+ if (reserve_mem) {
+ CDBG("device:%pK, mem : %pK, size : %d\n",
+ &pdev->dev, mem, (int)resource_size(mem));
+ if (!devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ device_name)) {
+ pr_err("err: no valid mem region for device:%s\n",
+ device_name);
+ return NULL;
+ }
+ }
+
+ base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base) {
+ devm_release_mem_region(&pdev->dev, mem->start,
+ resource_size(mem));
+ pr_err("err: ioremap failed: %s\n", device_name);
+ return NULL;
+ }
+
+ CDBG("base : %pK\n", base);
+ return base;
+}
+EXPORT_SYMBOL(msm_camera_get_reg_base);
+
+uint32_t msm_camera_get_res_size(struct platform_device *pdev,
+ char *device_name)
+{
+ struct resource *mem;
+
+ if (!pdev || !device_name) {
+ pr_err("Invalid params\n");
+ return 0;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return 0;
+ }
+ return resource_size(mem);
+}
+EXPORT_SYMBOL(msm_camera_get_res_size);
+
+
+int msm_camera_put_reg_base(struct platform_device *pdev,
+ void __iomem *base, char *device_name, int reserve_mem)
+{
+ struct resource *mem;
+
+ if (!pdev || !base || !device_name) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ CDBG("device name :%s\n", device_name);
+ mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, device_name);
+ if (!mem) {
+ pr_err("err: mem resource %s not found\n", device_name);
+ return -EINVAL;
+ }
+ CDBG("mem : %pK, size : %d\n", mem, (int)resource_size(mem));
+
+ devm_iounmap(&pdev->dev, base);
+ if (reserve_mem)
+ devm_release_mem_region(&pdev->dev,
+ mem->start, resource_size(mem));
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_put_reg_base);
+
+/* Register the bus client */
+uint32_t msm_camera_register_bus_client(struct platform_device *pdev,
+ enum cam_bus_client id)
+{
+ int rc = 0;
+ uint32_t bus_client, num_usecases, num_paths;
+ struct msm_bus_scale_pdata *pdata;
+ struct device_node *of_node;
+
+ CDBG("Register client ID: %d\n", id);
+
+ if (id >= CAM_BUS_CLIENT_MAX || !pdev) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+
+ of_node = pdev->dev.of_node;
+
+ if (!g_cv[id].pdata) {
+ rc = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
+ &num_usecases);
+ if (rc) {
+ pr_err("num-usecases not found\n");
+ return -EINVAL;
+ }
+ rc = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
+ &num_paths);
+ if (rc) {
+ pr_err("num-usecases not found\n");
+ return -EINVAL;
+ }
+
+ if (num_paths != 1) {
+ pr_err("Exceeds number of paths\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(of_node,
+ "qcom,msm-bus-vector-dyn-vote")) {
+ if (num_usecases != 2) {
+ pr_err("Excess or less vectors\n");
+ return -EINVAL;
+ }
+ g_cv[id].dyn_vote = true;
+ }
+
+ pdata = msm_bus_cl_get_pdata(pdev);
+ if (!pdata) {
+ pr_err("failed get_pdata client_id :%d\n", id);
+ return -EINVAL;
+ }
+ bus_client = msm_bus_scale_register_client(pdata);
+ if (!bus_client) {
+ pr_err("Unable to register bus client :%d\n", id);
+ return -EINVAL;
+ }
+ } else {
+ pr_err("vector already setup client_id : %d\n", id);
+ return -EINVAL;
+ }
+
+ g_cv[id].pdata = pdata;
+ g_cv[id].bus_client = bus_client;
+ g_cv[id].vector_index = 0;
+ g_cv[id].num_usecases = num_usecases;
+ g_cv[id].num_paths = num_paths;
+ mutex_init(&g_cv[id].lock);
+ CDBG("Exit Client ID: %d\n", id);
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_register_bus_client);
+
+/* Update the bus bandwidth */
+uint32_t msm_camera_update_bus_bw(int id, uint64_t ab, uint64_t ib)
+{
+ struct msm_bus_paths *path;
+ struct msm_bus_scale_pdata *pdata;
+ int idx = 0;
+
+ if (id >= CAM_BUS_CLIENT_MAX) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+ if (g_cv[id].num_usecases != 2 ||
+ g_cv[id].num_paths != 1 ||
+ g_cv[id].dyn_vote != true) {
+ pr_err("dynamic update not allowed\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&g_cv[id].lock);
+ idx = g_cv[id].vector_index;
+ idx = 1 - idx;
+ g_cv[id].vector_index = idx;
+ mutex_unlock(&g_cv[id].lock);
+
+ pdata = g_cv[id].pdata;
+ path = &(pdata->usecase[idx]);
+ path->vectors[0].ab = ab;
+ path->vectors[0].ib = ib;
+
+ CDBG("Register client ID : %d [ab : %llx, ib : %llx], update :%d\n",
+ id, ab, ib, idx);
+ msm_bus_scale_client_update_request(g_cv[id].bus_client, idx);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_update_bus_bw);
+
+/* Update the bus vector */
+uint32_t msm_camera_update_bus_vector(enum cam_bus_client id,
+ int vector_index)
+{
+ if (id >= CAM_BUS_CLIENT_MAX || g_cv[id].dyn_vote == true) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+
+ if (vector_index < 0 || vector_index > g_cv[id].num_usecases) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+
+ CDBG("Register client ID : %d vector idx: %d,\n", id, vector_index);
+ msm_bus_scale_client_update_request(g_cv[id].bus_client,
+ vector_index);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_update_bus_vector);
+
+/* Unregister the bus client */
+uint32_t msm_camera_unregister_bus_client(enum cam_bus_client id)
+{
+ if (id >= CAM_BUS_CLIENT_MAX) {
+ pr_err("Invalid params");
+ return -EINVAL;
+ }
+
+ CDBG("UnRegister client ID: %d\n", id);
+
+ mutex_destroy(&g_cv[id].lock);
+ msm_bus_scale_unregister_client(g_cv[id].bus_client);
+ g_cv[id].bus_client = 0;
+ g_cv[id].num_usecases = 0;
+ g_cv[id].num_paths = 0;
+ g_cv[id].vector_index = 0;
+ g_cv[id].dyn_vote = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_unregister_bus_client);
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h
new file mode 100644
index 0000000..0e0736a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h
@@ -0,0 +1,471 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_SOC_API_H_
+#define _CAM_SOC_API_H_
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock_types.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/reset.h>
+#include <soc/qcom/camera2.h>
+
+enum cam_bus_client {
+ CAM_BUS_CLIENT_VFE,
+ CAM_BUS_CLIENT_CPP,
+ CAM_BUS_CLIENT_FD,
+ CAM_BUS_CLIENT_JPEG_ENC0,
+ CAM_BUS_CLIENT_JPEG_ENC1,
+ CAM_BUS_CLIENT_JPEG_DEC,
+ CAM_BUS_CLIENT_JPEG_DMA,
+ CAM_BUS_CLIENT_MAX
+};
+
+struct msm_cam_regulator {
+ const char *name;
+ struct regulator *vdd;
+};
+
+/**
+ * @brief : Gets clock information from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_get_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk);
+
+/**
+ * @brief : Gets clock information from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * i2c device
+ *
+ * @param dev : i2c device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_i2c_dev_get_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ size_t *num_clk);
+
+/**
+ * @brief : Gets clock information and rates from dtsi
+ *
+ * This function extracts the clocks information for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get clocks information
+ * @param clk_info : Pointer to populate clock information array
+ * @param clk_ptr : Pointer to populate clock resource pointers
+ * @param clk_rates : Pointer to populate clock rates
+ * @param num_set: Pointer to populate the number of sets of rates
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_get_clk_info_and_rates(
+ struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr,
+ uint32_t ***clk_rates,
+ size_t *num_set,
+ size_t *num_clk);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param pdev : Pointer to platform device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_put_clk_info(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param dev : Pointer to i2c device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_i2c_dev_put_clk_info(struct device *dev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, int cnt);
+
+/**
+ * @brief : Puts clock information
+ *
+ * This function releases the memory allocated for the clocks
+ *
+ * @param pdev : Pointer to platform device
+ * @param clk_info : Pointer to release the allocated memory
+ * @param clk_ptr : Pointer to release the clock resources
+ * @param clk_ptr : Pointer to release the clock rates
+ * @param set : Number of sets of clock rates
+ * @param cnt : Number of clk resources
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int msm_camera_put_clk_info_and_rates(struct platform_device *pdev,
+ struct msm_cam_clk_info **clk_info,
+ struct clk ***clk_ptr, uint32_t ***clk_rates,
+ size_t set, size_t cnt);
+/**
+ * @brief : Enable clocks
+ *
+ * This function enables the clocks for a specified device
+ *
+ * @param dev : Device to get clocks information
+ * @param clk_info : Pointer to populate clock information
+ * @param clk_ptr : Pointer to populate clock information
+ * @param num_clk: Pointer to populate the number of clocks
+ * extracted from dtsi
+ * @param enable : Flag to specify enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_clk_enable(struct device *dev,
+ struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr,
+ int num_clk,
+ int enable);
+/**
+ * @brief : Set clock rate
+ *
+ * This function sets the rate for a specified clock and
+ * returns the rounded value
+ *
+ * @param dev : Device to get clocks information
+ * @param clk : Pointer to clock to set rate
+ * @param clk_rate : Rate to be set
+ *
+ * @return Status of operation. Negative in case of error. clk rate otherwise.
+ */
+
+long msm_camera_clk_set_rate(struct device *dev,
+ struct clk *clk,
+ long clk_rate);
+
+/**
+ * @brief : Gets reset info
+ *
+ * This function extracts the reset information for a specific
+ * platform device
+ *
+ * @param pdev : platform device to get reset information
+ * @param micro_iface_reset : Pointer to populate the reset names
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_get_reset_info(struct platform_device *pdev,
+ struct reset_control **micro_iface_reset);
+/**
+ * @brief : Sets flags of a clock
+ *
+ * This function will set the flags for a specified clock
+ *
+ * @param clk : Pointer to clock to set flags for
+ * @param flags : The flags to set
+ *
+ * @return Status of operation.
+ */
+
+int msm_camera_set_clk_flags(struct clk *clk, unsigned long flags);
+/**
+ * @brief : Gets regulator info
+ *
+ * This function extracts the regulator information for a specific
+ * platform device
+ *
+ * @param pdev : platform device to get regulator information
+ * @param vdd_info: Pointer to populate the regulator names
+ * @param num_reg: Pointer to populate the number of regulators
+ * extracted from dtsi
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_get_regulator_info(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int *num_reg);
+/**
+ * @brief : Enable/Disable the regultors
+ *
+ * This function enables/disables the regulators for a specific
+ * platform device
+ *
+ * @param vdd_info: Pointer to list of regulators
+ * @param cnt: Number of regulators to enable/disable
+ * @param enable: Flags specifies either enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_regulator_enable(struct msm_cam_regulator *vdd_info,
+ int cnt, int enable);
+
+/**
+ * @brief : set the regultors mode
+ *
+ * This function sets the regulators for a specific
+ * mode. say:REGULATOR_MODE_FAST/REGULATOR_MODE_NORMAL
+ *
+ * @param vdd_info: Pointer to list of regulators
+ * @param cnt: Number of regulators to enable/disable
+ * @param mode: Flags specifies either enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_regulator_set_mode(struct msm_cam_regulator *vdd_info,
+ int cnt, bool mode);
+
+
+/**
+ * @brief : Release the regulators
+ *
+ * This function releases the regulator resources.
+ *
+ * @param pdev: Pointer to platform device
+ * @param vdd_info: Pointer to list of regulators
+ * @param cnt: Number of regulators to release
+ */
+
+void msm_camera_put_regulators(struct platform_device *pdev,
+ struct msm_cam_regulator **vdd_info, int cnt);
+/**
+ * @brief : Get the IRQ resource
+ *
+ * This function gets the irq resource from dtsi for a specific
+ * platform device
+ *
+ * @param pdev : Platform device to get IRQ
+ * @param irq_name: Name of the IRQ resource to get from DTSI
+ *
+ * @return Pointer to resource if success else null
+ */
+
+struct resource *msm_camera_get_irq(struct platform_device *pdev,
+ char *irq_name);
+/**
+ * @brief : Register the IRQ
+ *
+ * This function registers the irq resource for specified hardware
+ *
+ * @param pdev : Platform device to register IRQ resource
+ * @param irq : IRQ resource
+ * @param handler : IRQ handler
+ * @param irqflags : IRQ flags
+ * @param irq_name: Name of the IRQ
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_register_irq(struct platform_device *pdev,
+ struct resource *irq,
+ irq_handler_t handler,
+ unsigned long irqflags,
+ char *irq_name,
+ void *dev);
+
+/**
+ * @brief : Register the threaded IRQ
+ *
+ * This function registers the irq resource for specified hardware
+ *
+ * @param pdev : Platform device to register IRQ resource
+ * @param irq : IRQ resource
+ * @param handler_fn : IRQ handler function
+ * @param thread_fn : thread handler function
+ * @param irqflags : IRQ flags
+ * @param irq_name: Name of the IRQ
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_register_threaded_irq(struct platform_device *pdev,
+ struct resource *irq,
+ irq_handler_t handler_fn,
+ irq_handler_t thread_fn,
+ unsigned long irqflags,
+ const char *irq_name,
+ void *dev);
+
+/**
+ * @brief : Enable/Disable the IRQ
+ *
+ * This function enables or disables a specific IRQ
+ *
+ * @param irq : IRQ resource
+ * @param flag : flag to enable/disable
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_enable_irq(struct resource *irq, int flag);
+
+/**
+ * @brief : UnRegister the IRQ
+ *
+ * This function Unregisters/Frees the irq resource
+ *
+ * @param pdev : Pointer to platform device
+ * @param irq : IRQ resource
+ * @param dev : Token of the device
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_unregister_irq(struct platform_device *pdev,
+ struct resource *irq, void *dev_id);
+
+/**
+ * @brief : Gets device register base
+ *
+ * This function extracts the device's register base from the dtsi
+ * for the specified platform device
+ *
+ * @param pdev : Platform device to get regulator infor
+ * @param device_name : Name of the device to fetch the register base
+ * @param reserve_mem : Flag to decide whether to reserve memory
+ * region or not.
+ *
+ * @return Pointer to resource if success else null
+ */
+
+void __iomem *msm_camera_get_reg_base(struct platform_device *pdev,
+ char *device_name, int reserve_mem);
+
+/**
+ * @brief : Puts device register base
+ *
+ * This function releases the memory region for the specified
+ * resource
+ *
+ * @param pdev : Pointer to platform device
+ * @param base : Pointer to base to unmap
+ * @param device_name : Device name
+ * @param reserve_mem : Flag to decide whether to release memory
+ * region or not.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_put_reg_base(struct platform_device *pdev, void __iomem *base,
+ char *device_name, int reserve_mem);
+
+/**
+ * @brief : Register the bus client
+ *
+ * This function registers the bus client
+ *
+ * @param pdev : Pointer to platform device
+ * @param id : client identifier
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+uint32_t msm_camera_register_bus_client(struct platform_device *pdev,
+ enum cam_bus_client id);
+
+/**
+ * @brief : Update bus vector
+ *
+ * This function votes for the specified vector to the bus
+ *
+ * @param id : client identifier
+ * @param vector_index : vector index to register
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+uint32_t msm_camera_update_bus_vector(enum cam_bus_client id,
+ int vector_index);
+
+/**
+ * @brief : Update the bus bandwidth
+ *
+ * This function updates the bandwidth for the specific client
+ *
+ * @param client_id : client identifier
+ * @param ab : Asolute bandwidth
+ * @param ib : Instantaneous bandwidth
+ *
+ * @return non-zero as client id if success else fail
+ */
+
+uint32_t msm_camera_update_bus_bw(int id, uint64_t ab, uint64_t ib);
+
+/**
+ * @brief : UnRegister the bus client
+ *
+ * This function unregisters the bus client
+ *
+ * @param id : client identifier
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+uint32_t msm_camera_unregister_bus_client(enum cam_bus_client id);
+
+/**
+ * @brief : Gets resource size
+ *
+ * This function returns the size of the resource for the
+ * specified platform device
+ *
+ * @param pdev : Platform device to get regulator infor
+ * @param device_name : Name of the device to fetch the register base
+ *
+ * @return size of the resource
+ */
+
+uint32_t msm_camera_get_res_size(struct platform_device *pdev,
+ char *device_name);
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
new file mode 100644
index 0000000..009b4cd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
@@ -0,0 +1,866 @@
+/* Copyright (c) 2011-2014, 2017-2018, 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/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <soc/qcom/camera2.h>
+#include <linux/msm-bus.h>
+#include "msm_camera_io_util.h"
+
+#define BUFF_SIZE_128 128
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+void msm_camera_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+/* This API is to write a block of data
+ * to same address
+ */
+int32_t msm_camera_io_w_block(const u32 *addr, void __iomem *base,
+ u32 len)
+{
+ int i;
+
+ if (!addr || !len || !base)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ CDBG("%s: len =%d val=%x base =%pK\n", __func__,
+ len, addr[i], base);
+ writel_relaxed(addr[i], base);
+ }
+ return 0;
+}
+
+/* This API is to write a block of registers
+ * which is like a 2 dimensional array table with
+ * register offset and data
+ */
+int32_t msm_camera_io_w_reg_block(const u32 *addr, void __iomem *base,
+ u32 len)
+{
+ int i;
+
+ if (!addr || !len || !base)
+ return -EINVAL;
+
+ for (i = 0; i < len; i = i + 2) {
+ CDBG("%s: len =%d val=%x base =%pK reg=%x\n", __func__,
+ len, addr[i + 1], base, addr[i]);
+ writel_relaxed(addr[i + 1], base + addr[i]);
+ }
+ return 0;
+}
+
+void msm_camera_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data));
+ /* ensure write is done */
+ wmb();
+ writel_relaxed((data), (addr));
+ /* ensure write is done */
+ wmb();
+}
+
+int32_t msm_camera_io_w_mb_block(const u32 *addr, void __iomem *base, u32 len)
+{
+ int i;
+
+ if (!addr || !len || !base)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ /* ensure write is done */
+ wmb();
+ CDBG("%s: len =%d val=%x base =%pK\n", __func__,
+ len, addr[i], base);
+ writel_relaxed(addr[i], base);
+ }
+ /* ensure last write is done */
+ wmb();
+ return 0;
+}
+
+u32 msm_camera_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+
+ CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data));
+ return data;
+}
+
+u32 msm_camera_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ /* ensure read is done */
+ rmb();
+ data = readl_relaxed(addr);
+ /* ensure read is done */
+ rmb();
+ CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data));
+ return data;
+}
+
+static void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
+ void *src_addr, u32 len)
+{
+ int i;
+ u32 __iomem *d = (u32 __iomem *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+int32_t msm_camera_io_poll_value(void __iomem *addr, u32 wait_data, u32 retry,
+ unsigned long min_usecs, unsigned long max_usecs)
+{
+ uint32_t tmp, cnt = 0;
+ int32_t rc = 0;
+
+ if (!addr)
+ return -EINVAL;
+
+ tmp = msm_camera_io_r(addr);
+ while ((tmp != wait_data) && (cnt++ < retry)) {
+ if (min_usecs > 0 && max_usecs > 0)
+ usleep_range(min_usecs, max_usecs);
+ tmp = msm_camera_io_r(addr);
+ }
+ if (cnt > retry) {
+ pr_debug("Poll failed by value\n");
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int32_t msm_camera_io_poll_value_wmask(void __iomem *addr, u32 wait_data,
+ u32 bmask, u32 retry, unsigned long min_usecs, unsigned long max_usecs)
+{
+ uint32_t tmp, cnt = 0;
+ int32_t rc = 0;
+
+ if (!addr)
+ return -EINVAL;
+
+ tmp = msm_camera_io_r(addr);
+ while (((tmp & bmask) != wait_data) && (cnt++ < retry)) {
+ if (min_usecs > 0 && max_usecs > 0)
+ usleep_range(min_usecs, max_usecs);
+ tmp = msm_camera_io_r(addr);
+ }
+ if (cnt > retry) {
+ pr_debug("Poll failed with mask\n");
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+void msm_camera_io_dump(void __iomem *addr, int size, int enable)
+{
+ char line_str[128];
+ int i;
+ ptrdiff_t p = 0;
+ size_t offset = 0, used = 0;
+ u32 data;
+
+ CDBG("%s: addr=%pK size=%d\n", __func__, addr, size);
+
+ if (!addr || (size <= 0) || !enable)
+ return;
+
+ line_str[0] = '\0';
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ used = snprintf(line_str + offset,
+ sizeof(line_str) - offset, "0x%04tX: ", p);
+ if (offset + used >= sizeof(line_str)) {
+ pr_err("%s\n", line_str);
+ offset = 0;
+ line_str[0] = '\0';
+ } else {
+ offset += used;
+ }
+ }
+ data = readl_relaxed(addr + p);
+ p = p + 4;
+ used = snprintf(line_str + offset,
+ sizeof(line_str) - offset, "%08x ", data);
+ if (offset + used >= sizeof(line_str)) {
+ pr_err("%s\n", line_str);
+ offset = 0;
+ line_str[0] = '\0';
+ } else {
+ offset += used;
+ }
+ if ((i + 1) % 4 == 0) {
+ pr_err("%s\n", line_str);
+ line_str[0] = '\0';
+ offset = 0;
+ }
+ }
+ if (line_str[0] != '\0')
+ pr_err("%s\n", line_str);
+}
+
+void msm_camera_io_dump_wstring_base(void __iomem *addr,
+ struct msm_cam_dump_string_info *dump_data,
+ int size)
+{
+ int i, u = sizeof(struct msm_cam_dump_string_info);
+
+ pr_debug("%s: addr=%pK data=%pK size=%d u=%d, cnt=%d\n", __func__,
+ addr, dump_data, size, u,
+ (size/u));
+
+ if (!addr || (size <= 0) || !dump_data) {
+ pr_err("%s: addr=%pK data=%pK size=%d\n", __func__,
+ addr, dump_data, size);
+ return;
+ }
+ for (i = 0; i < (size / u); i++)
+ pr_debug("%s 0x%x\n", (dump_data + i)->print,
+ readl_relaxed((dump_data + i)->offset + addr));
+}
+
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void *src_addr, u32 len)
+{
+ CDBG("%s: %pK %pK %d\n", __func__, dest_addr, src_addr, len);
+ msm_camera_io_memcpy_toio(dest_addr, src_addr, len / 4);
+}
+
+void msm_camera_io_memcpy_mb(void __iomem *dest_addr,
+ void *src_addr, u32 len)
+{
+ int i;
+ u32 __iomem *d = (u32 __iomem *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+ /* This is generic function called who needs to register
+ * writes with memory barrier
+ */
+ wmb();
+ for (i = 0; i < (len / 4); i++) {
+ msm_camera_io_w(*s++, d++);
+ /* ensure write is done after every iteration */
+ wmb();
+ }
+}
+
+int msm_cam_clk_sel_src(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct msm_cam_clk_info *clk_src_info, int num_clk)
+{
+ int i;
+ int rc = 0;
+ struct clk *mux_clk = NULL;
+ struct clk *src_clk = NULL;
+
+ for (i = 0; i < num_clk; i++) {
+ if (clk_src_info[i].clk_name) {
+ mux_clk = clk_get(dev, clk_info[i].clk_name);
+ if (IS_ERR(mux_clk)) {
+ pr_err("%s get failed\n",
+ clk_info[i].clk_name);
+ continue;
+ }
+ src_clk = clk_get(dev, clk_src_info[i].clk_name);
+ if (IS_ERR(src_clk)) {
+ pr_err("%s get failed\n",
+ clk_src_info[i].clk_name);
+ continue;
+ }
+ clk_set_parent(mux_clk, src_clk);
+ }
+ }
+ return rc;
+}
+
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ int i;
+ int rc = 0;
+ long clk_rate;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ CDBG("%s enable %s\n", __func__, clk_info[i].clk_name);
+ clk_ptr[i] = clk_get(dev, clk_info[i].clk_name);
+ if (IS_ERR(clk_ptr[i])) {
+ pr_err("%s get failed\n", clk_info[i].clk_name);
+ rc = PTR_ERR(clk_ptr[i]);
+ goto cam_clk_get_err;
+ }
+ if (clk_info[i].clk_rate > 0) {
+ clk_rate = clk_round_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (clk_rate < 0) {
+ pr_err("%s round failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+
+ } else if (clk_info[i].clk_rate == INIT_RATE) {
+ clk_rate = clk_get_rate(clk_ptr[i]);
+ if (clk_rate == 0) {
+ clk_rate =
+ clk_round_rate(clk_ptr[i], 0);
+ if (clk_rate < 0) {
+ pr_err("%s round rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ rc = clk_set_rate(clk_ptr[i],
+ clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ }
+ rc = clk_prepare(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s prepare failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_prepare_err;
+ }
+
+ rc = clk_enable(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_enable_err;
+ }
+ if (clk_info[i].delay > 20) {
+ msleep(clk_info[i].delay);
+ } else if (clk_info[i].delay) {
+ usleep_range(clk_info[i].delay * 1000,
+ (clk_info[i].delay * 1000) + 1000);
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ CDBG("%s disable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+ }
+ return rc;
+
+
+cam_clk_enable_err:
+ clk_unprepare(clk_ptr[i]);
+cam_clk_prepare_err:
+cam_clk_set_err:
+ clk_put(clk_ptr[i]);
+cam_clk_get_err:
+ for (i--; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+ return rc;
+}
+
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int config)
+{
+ int i = 0, j = 0;
+ int rc = 0;
+ struct camera_vreg_t *curr_vreg;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (cam_vreg == NULL) {
+ pr_err("%s:%d cam_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_OR_NULL(reg_ptr[j])) {
+ pr_err("%s: %s get failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ reg_ptr[j] = NULL;
+ goto vreg_get_fail;
+ }
+ if (regulator_count_voltages(reg_ptr[j]) > 0) {
+ rc = regulator_set_voltage(
+ reg_ptr[j],
+ curr_vreg->min_voltage,
+ curr_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (curr_vreg->op_mode >= 0) {
+ rc = regulator_set_load(
+ reg_ptr[j],
+ curr_vreg->op_mode);
+ rc = 0;
+ if (rc < 0) {
+ pr_err(
+ "%s:%s set optimum mode fail\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ }
+ } else {
+ for (i = num_vreg_seq-1; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ curr_vreg = &cam_vreg[j];
+ if (reg_ptr[j]) {
+ if (regulator_count_voltages(reg_ptr[j]) > 0) {
+ if (curr_vreg->op_mode >= 0) {
+ regulator_set_load(
+ reg_ptr[j], 0);
+ }
+ regulator_set_voltage(
+ reg_ptr[j], 0, curr_vreg->
+ max_voltage);
+ }
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+ }
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (regulator_count_voltages(reg_ptr[j]) > 0)
+ regulator_set_load(reg_ptr[j], 0);
+
+vreg_set_opt_mode_fail:
+if (regulator_count_voltages(reg_ptr[j]) > 0)
+ regulator_set_voltage(reg_ptr[j], 0,
+ curr_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+
+vreg_get_fail:
+ for (i--; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ curr_vreg = &cam_vreg[j];
+ goto vreg_unconfig;
+ }
+ return -ENODEV;
+}
+
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int enable)
+{
+ int i = 0, j = 0, rc = 0;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!num_vreg_seq)
+ num_vreg_seq = num_vreg;
+
+ if (enable) {
+ for (i = 0; i < num_vreg_seq; i++) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ if (IS_ERR_OR_NULL(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;
+ if (reg_ptr[j]) {
+ 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_err("%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;
+ const char *vreg_name = NULL;
+
+ if (!dev || !cam_vreg || !reg_ptr) {
+ pr_err("%s: get failed NULL parameter\n", __func__);
+ goto vreg_get_fail;
+ }
+ if (cam_vreg->type == VREG_TYPE_CUSTOM) {
+ if (cam_vreg->custom_vreg_name == NULL) {
+ pr_err("%s : can't find sub reg name",
+ __func__);
+ goto vreg_get_fail;
+ }
+ vreg_name = cam_vreg->custom_vreg_name;
+ } else {
+ if (cam_vreg->reg_name == NULL) {
+ pr_err("%s : can't find reg name", __func__);
+ goto vreg_get_fail;
+ }
+ vreg_name = cam_vreg->reg_name;
+ }
+
+ if (config) {
+ CDBG("%s enable %s\n", __func__, vreg_name);
+ *reg_ptr = regulator_get(dev, vreg_name);
+ if (IS_ERR(*reg_ptr)) {
+ pr_err("%s: %s get failed\n", __func__, vreg_name);
+ *reg_ptr = NULL;
+ goto vreg_get_fail;
+ }
+ if (regulator_count_voltages(*reg_ptr) > 0) {
+ CDBG("%s: voltage min=%d, max=%d\n",
+ __func__, cam_vreg->min_voltage,
+ cam_vreg->max_voltage);
+ rc = regulator_set_voltage(
+ *reg_ptr, cam_vreg->min_voltage,
+ cam_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__, vreg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (cam_vreg->op_mode >= 0) {
+ rc = regulator_set_load(*reg_ptr,
+ cam_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s: %s set optimum mode failed\n",
+ __func__, vreg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ rc = regulator_enable(*reg_ptr);
+ if (rc < 0) {
+ pr_err("%s: %s regulator_enable failed\n", __func__,
+ vreg_name);
+ goto vreg_unconfig;
+ }
+ } else {
+ CDBG("%s disable %s\n", __func__, vreg_name);
+ if (*reg_ptr) {
+ CDBG("%s disable %s\n", __func__, vreg_name);
+ regulator_disable(*reg_ptr);
+ if (regulator_count_voltages(*reg_ptr) > 0) {
+ if (cam_vreg->op_mode >= 0)
+ regulator_set_load(*reg_ptr, 0);
+ regulator_set_voltage(
+ *reg_ptr, 0, cam_vreg->max_voltage);
+ }
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+ } else {
+ pr_err("%s can't disable %s\n", __func__, vreg_name);
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (regulator_count_voltages(*reg_ptr) > 0)
+ regulator_set_load(*reg_ptr, 0);
+
+vreg_set_opt_mode_fail:
+if (regulator_count_voltages(*reg_ptr) > 0)
+ regulator_set_voltage(*reg_ptr, 0, cam_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+
+vreg_get_fail:
+ return -ENODEV;
+}
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en)
+{
+ int rc = 0, i = 0, err = 0;
+
+ if (!gpio_tbl || !size) {
+ pr_err("%s:%d invalid gpio_tbl %pK / size %d\n", __func__,
+ __LINE__, gpio_tbl, size);
+ return -EINVAL;
+ }
+ for (i = 0; i < size; i++) {
+ CDBG("%s:%d i %d, gpio %d dir %ld\n", __func__, __LINE__, i,
+ gpio_tbl[i].gpio, gpio_tbl[i].flags);
+ }
+ if (gpio_en) {
+ for (i = 0; i < size; i++) {
+ err = gpio_request_one(gpio_tbl[i].gpio,
+ gpio_tbl[i].flags, gpio_tbl[i].label);
+ if (err) {
+ /*
+ * After GPIO request fails, contine to
+ * apply new gpios, outout a error message
+ * for driver bringup debug
+ */
+ pr_err("%s:%d gpio %d:%s request fails\n",
+ __func__, __LINE__,
+ gpio_tbl[i].gpio, gpio_tbl[i].label);
+ }
+ }
+ } else {
+ gpio_free_array(gpio_tbl, size);
+ }
+ return rc;
+}
+
+/*
+ * msm_camera_get_dt_reg_settings - Get dt reg settings from device-tree.
+ * @of_node: Pointer to device of_node from dev.
+ * @dt_prop_name: String of the property to search in of_node from dev.
+ * @reg_s: Double pointer will be allocated by this function and filled.
+ * @size: Pointer to fill the length of the available entries.
+ */
+int msm_camera_get_dt_reg_settings(struct device_node *of_node,
+ const char *dt_prop_name, uint32_t **reg_s,
+ unsigned int *size)
+{
+ int ret;
+ unsigned int cnt;
+
+ if (!of_node || !dt_prop_name || !size || !reg_s) {
+ pr_err("%s: Error invalid args %pK:%pK:%pK:%pK\n",
+ __func__, size, reg_s, of_node, dt_prop_name);
+ return -EINVAL;
+ }
+ if (!of_get_property(of_node, dt_prop_name, &cnt)) {
+ pr_debug("Missing dt reg settings for %s\n", dt_prop_name);
+ return -ENOENT;
+ }
+
+ if (!cnt || (cnt % 8)) {
+ pr_err("%s: Error invalid number of entries cnt=%d\n",
+ __func__, cnt);
+ return -EINVAL;
+ }
+ cnt /= 4;
+ if (cnt != 0) {
+ *reg_s = kcalloc(cnt, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!*reg_s)
+ return -ENOMEM;
+ ret = of_property_read_u32_array(of_node,
+ dt_prop_name,
+ *reg_s,
+ cnt);
+ if (ret < 0) {
+ pr_err("%s: No dt reg info read for %s ret=%d\n",
+ __func__, dt_prop_name, ret);
+ kfree(*reg_s);
+ return -ENOENT;
+ }
+ *size = cnt;
+ } else {
+ pr_err("%s: Error invalid entries\n", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * msm_camera_get_dt_reg_settings - Free dt reg settings memory.
+ * @reg_s: Double pointer will be allocated by this function and filled.
+ * @size: Pointer to set the length as invalid.
+ */
+void msm_camera_put_dt_reg_settings(uint32_t **reg_s,
+ unsigned int *size)
+{
+ kfree(*reg_s);
+ *reg_s = NULL;
+ *size = 0;
+}
+
+int msm_camera_hw_write_dt_reg_settings(void __iomem *base,
+ uint32_t *reg_s,
+ unsigned int size)
+{
+ int32_t rc = 0;
+
+ if (!reg_s || !base || !size) {
+ pr_err("%s: Error invalid args\n", __func__);
+ return -EINVAL;
+ }
+ rc = msm_camera_io_w_reg_block((const u32 *) reg_s,
+ base, size);
+ if (rc < 0)
+ pr_err("%s: Failed dt reg setting write\n", __func__);
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h
new file mode 100644
index 0000000..77809d2
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h
@@ -0,0 +1,93 @@
+/* Copyright (c) 2011-2014, 2018, 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_IO_UTIL_H
+#define __MSM_CAMERA_IO_UTIL_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <soc/qcom/camera2.h>
+#include <media/msm_cam_sensor.h>
+#include <media/v4l2-ioctl.h>
+
+#define NO_SET_RATE -1
+#define INIT_RATE -2
+
+struct msm_gpio_set_tbl {
+ unsigned int gpio;
+ unsigned long flags;
+ uint32_t delay;
+};
+
+struct msm_cam_dump_string_info {
+ const char *print;
+ uint32_t offset;
+};
+
+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, int enable);
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void *src_addr, u32 len);
+void msm_camera_io_memcpy_mb(void __iomem *dest_addr,
+ void *src_addr, u32 len);
+int msm_cam_clk_sel_src(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct msm_cam_clk_info *clk_src_info, int num_clk);
+int 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);
+void msm_camera_io_dump_wstring_base(void __iomem *addr,
+ struct msm_cam_dump_string_info *dump_data,
+ int size);
+int32_t msm_camera_io_poll_value_wmask(void __iomem *addr, u32 wait_data,
+ u32 bmask, u32 retry, unsigned long min_usecs,
+ unsigned long max_usecs);
+int32_t msm_camera_io_poll_value(void __iomem *addr, u32 wait_data, u32 retry,
+ unsigned long min_usecs, unsigned long max_usecs);
+int32_t msm_camera_io_w_block(const u32 *addr, void __iomem *base, u32 len);
+int32_t msm_camera_io_w_reg_block(const u32 *addr, void __iomem *base, u32 len);
+int32_t msm_camera_io_w_mb_block(const u32 *addr, void __iomem *base, u32 len);
+int msm_camera_get_dt_reg_settings(struct device_node *of_node,
+ const char *dt_prop_name, uint32_t **reg_s,
+ unsigned int *size);
+void msm_camera_put_dt_reg_settings(uint32_t **reg_s,
+ unsigned int *size);
+int msm_camera_hw_write_dt_reg_settings(void __iomem *base,
+ uint32_t *reg_s,
+ unsigned int size);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c
new file mode 100644
index 0000000..22f4891
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c
@@ -0,0 +1,354 @@
+/* Copyright (c) 2016-2018, 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/ktime.h>
+#include <linux/mutex.h>
+#include <soc/qcom/camera2.h>
+#include "qseecom_kernel.h"
+#include "msm_camera_tz_util.h"
+
+#define EMPTY_QSEECOM_HANDLE NULL
+#define QSEECOM_SBUFF_SIZE SZ_128K
+
+#define MSM_CAMERA_TZ_UTIL_VERBOSE
+
+#define MSM_CAMERA_TZ_BOOT_PROTECTED (false)
+
+/* Update version major number in case the HLOS-TA interface is changed*/
+#define TA_IF_VERSION_MAJ 1
+#define TA_IF_VERSION_MIN 2
+
+#undef CDBG
+#ifdef MSM_CAMERA_TZ_UTIL_VERBOSE
+ #define CDBG(fmt, args...) \
+ pr_info(CONFIG_MSM_SEC_CCI_TA_NAME "::%s:%d - " fmt,\
+ __func__, __LINE__, ##args)
+#else /* MSM_CAMERA_TZ_UTIL_VERBOSE */
+ #define CDBG(fmt, args...) \
+ pr_debug("%s:%d - " fmt, __func__, __LINE__, ##args)
+#endif /* MSM_CAMERA_TZ_UTIL_VERBOSE */
+
+#pragma pack(push, msm_camera_tz_util, 1)
+
+/* MSM_CAMERA_TZ_CMD_GET_IF_VERSION */
+#define msm_camera_tz_i2c_get_if_version_req_t msm_camera_tz_generic_req_t
+
+struct msm_camera_tz_i2c_get_if_version_rsp_t {
+ enum msm_camera_tz_status_t rc;
+ uint32_t if_version_maj;
+ uint32_t if_version_min;
+};
+
+/* MSM_CAMERA_TZ_CMD_SET_MODE */
+struct msm_camera_tz_set_mode_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ uint32_t mode;
+ uint32_t hw_block;
+};
+
+#define msm_camera_tz_set_mode_rsp_t msm_camera_tz_generic_rsp_t
+
+#pragma pack(pop, msm_camera_tz_util)
+
+/* TA communication control structure */
+struct msm_camera_tz_ctrl_t {
+ uint32_t ta_enabled;
+ struct qseecom_handle *ta_qseecom_handle;
+ const char *ta_name;
+ uint32_t secure_hw_blocks;
+};
+
+static struct msm_camera_tz_ctrl_t msm_camera_tz_ctrl = {
+ 0, NULL, CONFIG_MSM_CAMERA_TZ_TA_NAME, 0
+};
+
+static DEFINE_MUTEX(msm_camera_tz_util_lock);
+
+struct qseecom_handle *msm_camera_tz_get_ta_handle(void)
+{
+ return msm_camera_tz_ctrl.ta_qseecom_handle;
+}
+
+void msm_camera_tz_lock(void)
+{
+ mutex_lock(&msm_camera_tz_util_lock);
+}
+
+void msm_camera_tz_unlock(void)
+{
+ mutex_unlock(&msm_camera_tz_util_lock);
+}
+
+int32_t get_cmd_rsp_buffers(
+ struct qseecom_handle *ta_qseecom_handle,
+ void **cmd, int *cmd_len,
+ void **rsp, int *rsp_len)
+{
+ if ((ta_qseecom_handle == NULL) ||
+ (cmd == NULL) || (cmd_len == NULL) ||
+ (rsp == NULL) || (rsp_len == NULL)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (*cmd_len & QSEECOM_ALIGN_MASK)
+ *cmd_len = QSEECOM_ALIGN(*cmd_len);
+
+ if (*rsp_len & QSEECOM_ALIGN_MASK)
+ *rsp_len = QSEECOM_ALIGN(*rsp_len);
+
+ if ((*rsp_len + *cmd_len) > QSEECOM_SBUFF_SIZE) {
+ pr_err("%s:%d - Shared buffer too small to hold cmd=%d and rsp=%d\n",
+ __func__, __LINE__,
+ *cmd_len, *rsp_len);
+ return -ENOMEM;
+ }
+
+ *cmd = ta_qseecom_handle->sbuf;
+ *rsp = ta_qseecom_handle->sbuf + *cmd_len;
+ return 0;
+}
+
+static int32_t msm_camera_tz_i2c_ta_get_if_version(
+ struct qseecom_handle *ta_qseecom_handle,
+ uint32_t *if_version_maj,
+ uint32_t *if_version_min)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_get_if_version_req_t *cmd;
+ struct msm_camera_tz_i2c_get_if_version_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ if ((ta_qseecom_handle == NULL) ||
+ (if_version_maj == NULL) || (if_version_min == NULL)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_get_if_version_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_get_if_version_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_GET_IF_VERSION;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Unable to get if version info, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+
+ if (rsp->rc < 0) {
+ CDBG("TZ App error, rc=%d\n", rsp->rc);
+ rc = -EFAULT;
+ } else {
+ *if_version_maj = rsp->if_version_maj;
+ *if_version_min = rsp->if_version_min;
+ CDBG("TZ If version %d.%d\n", *if_version_maj,
+ *if_version_min);
+ }
+ }
+ return rc;
+}
+
+int32_t msm_camera_tz_load_ta(void)
+{
+ int32_t rc = 0;
+
+ if (MSM_CAMERA_TZ_BOOT_PROTECTED &&
+ msm_camera_tz_ctrl.ta_enabled > 0) {
+ CDBG("TA loaded from boot(TA %s - %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+ return 0;
+ }
+
+ CDBG("Enter (TA name = %s, %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+
+ msm_camera_tz_lock();
+ if (msm_camera_tz_ctrl.ta_enabled == 0) {
+ ktime_t startTime = ktime_get();
+
+ /* Start the TA */
+ if ((msm_camera_tz_ctrl.ta_qseecom_handle == NULL) &&
+ (msm_camera_tz_ctrl.ta_name != NULL) &&
+ ('\0' != msm_camera_tz_ctrl.ta_name[0])) {
+ uint32_t if_version_maj = 0;
+ uint32_t if_version_min = 0;
+
+ rc = qseecom_start_app(
+ &msm_camera_tz_ctrl.ta_qseecom_handle,
+ (char *)msm_camera_tz_ctrl.ta_name,
+ QSEECOM_SBUFF_SIZE);
+ if (!rc)
+ rc = msm_camera_tz_i2c_ta_get_if_version(
+ msm_camera_tz_ctrl.ta_qseecom_handle,
+ &if_version_maj, &if_version_min);
+
+ if (!rc) {
+ if (if_version_maj != TA_IF_VERSION_MAJ) {
+ CDBG("TA ver mismatch %d.%d != %d.%d\n",
+ if_version_maj, if_version_min,
+ TA_IF_VERSION_MAJ,
+ TA_IF_VERSION_MIN);
+ rc = qseecom_shutdown_app(
+ &msm_camera_tz_ctrl.
+ ta_qseecom_handle);
+ msm_camera_tz_ctrl.ta_qseecom_handle
+ = EMPTY_QSEECOM_HANDLE;
+ rc = -EFAULT;
+ } else {
+ msm_camera_tz_ctrl.ta_enabled = 1;
+ }
+ }
+ }
+ CDBG("Load TA %s - %s(%d) - %lluus\n",
+ msm_camera_tz_ctrl.ta_name,
+ (msm_camera_tz_ctrl.ta_enabled)?"Ok" :
+ "Failed", rc,
+ ktime_us_delta(ktime_get(), startTime));
+ } else {
+ msm_camera_tz_ctrl.ta_enabled++;
+ CDBG("TA already loaded (TA %s - %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+ }
+ msm_camera_tz_unlock();
+ return rc;
+}
+
+int32_t msm_camera_tz_unload_ta(void)
+{
+ int32_t rc = -EFAULT;
+
+ if (MSM_CAMERA_TZ_BOOT_PROTECTED) {
+ CDBG("TA loaded from boot(TA %s - %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+ return 0;
+ }
+
+ CDBG("Enter (TA name = %s, %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+
+ msm_camera_tz_lock();
+ if (msm_camera_tz_ctrl.ta_enabled == 1) {
+ ktime_t startTime = ktime_get();
+
+ rc = qseecom_shutdown_app(&msm_camera_tz_ctrl.
+ ta_qseecom_handle);
+ msm_camera_tz_ctrl.ta_qseecom_handle
+ = EMPTY_QSEECOM_HANDLE;
+ msm_camera_tz_ctrl.ta_enabled = 0;
+ CDBG("Unload TA %s - %s(%d) - %lluus\n",
+ msm_camera_tz_ctrl.ta_name,
+ (!rc)?"Ok":"Failed", rc,
+ ktime_us_delta(ktime_get(), startTime));
+ } else {
+ msm_camera_tz_ctrl.ta_enabled--;
+ CDBG("TA still loaded (TA %s - %d)\n",
+ msm_camera_tz_ctrl.ta_name,
+ msm_camera_tz_ctrl.ta_enabled);
+ }
+ msm_camera_tz_unlock();
+ return rc;
+}
+
+static int32_t msm_camera_tz_ta_set_mode(uint32_t mode,
+ uint32_t hw_block)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_set_mode_req_t *cmd;
+ struct msm_camera_tz_set_mode_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle =
+ msm_camera_tz_get_ta_handle();
+ ktime_t startTime = ktime_get();
+
+ if (ta_qseecom_handle == NULL) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_set_mode_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_set_mode_rsp_t);
+
+ msm_camera_tz_lock();
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_SET_MODE;
+ cmd->mode = mode;
+ cmd->hw_block = hw_block;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ msm_camera_tz_unlock();
+ return rc;
+ }
+ rc = rsp->rc;
+ }
+ msm_camera_tz_unlock();
+ CDBG("Done: rc=%d, Mode=0x%08X - %lluus\n",
+ rc, mode,
+ ktime_us_delta(ktime_get(), startTime));
+ return rc;
+}
+
+uint32_t msm_camera_tz_set_mode(uint32_t mode,
+ uint32_t hw_block)
+{
+ uint32_t rc = 0;
+
+ switch (mode) {
+ case MSM_CAMERA_TZ_MODE_SECURE:
+ rc = msm_camera_tz_load_ta();
+ if (!rc) {
+ rc = msm_camera_tz_ta_set_mode(mode, hw_block);
+ if (rc)
+ msm_camera_tz_ctrl.secure_hw_blocks |= hw_block;
+ }
+ break;
+ case MSM_CAMERA_TZ_MODE_NON_SECURE:
+ msm_camera_tz_ta_set_mode(mode, hw_block);
+ if (rc)
+ msm_camera_tz_ctrl.secure_hw_blocks &= ~hw_block;
+ rc = msm_camera_tz_unload_ta();
+ break;
+ default:
+ pr_err("%s:%d - Incorrect mode: %d (hw: 0x%08X)\n",
+ __func__, __LINE__,
+ mode, hw_block);
+ return -EINVAL;
+ }
+ CDBG("Set Mode - rc=%d, Mode: 0x%08X\n",
+ rc, mode);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h
new file mode 100644
index 0000000..8a64af9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h
@@ -0,0 +1,91 @@
+/* Copyright (c) 2016, 2018, 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_TZ_UTIL_H
+#define __MSM_CAMERA_TZ_UTIL_H
+
+#include <soc/qcom/camera2.h>
+
+#ifndef CONFIG_MSM_CAMERA_TZ_TA_NAME
+#define CONFIG_MSM_CAMERA_TZ_TA_NAME "seccamdemo64"
+#endif /* CONFIG_MSM_CAMERA_TZ_TA_NAME */
+
+#define MSM_CAMERA_TZ_MODE_NON_SECURE 0x0000000000
+#define MSM_CAMERA_TZ_MODE_SECURE 0x0000000001
+
+#define MSM_CAMERA_TZ_HW_BLOCK_CSIDCORE 0x0000000001
+#define MSM_CAMERA_TZ_HW_BLOCK_ISPIF 0x0000000002
+#define MSM_CAMERA_TZ_HW_BLOCK_CCI 0x0000000004
+#define MSM_CAMERA_TZ_HW_BLOCK_ISP 0x0000000008
+#define MSM_CAMERA_TZ_HW_BLOCK_CPP 0x0000000010
+
+enum msm_camera_tz_cmd_id_t {
+ MSM_CAMERA_TZ_CMD_NONE,
+ MSM_CAMERA_TZ_CMD_GET_IF_VERSION,
+ MSM_CAMERA_TZ_CMD_POWER_UP,
+ MSM_CAMERA_TZ_CMD_POWER_DOWN,
+ MSM_CAMERA_TZ_CMD_CCI_GENERIC,
+ MSM_CAMERA_TZ_CMD_CCI_READ,
+ MSM_CAMERA_TZ_CMD_CCI_READ_SEQ,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_SEQ,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_ASYNC,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_SYNC,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_SYNC_BLOCK,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_SEQ_TABLE,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_W_MICRODELAY,
+ MSM_CAMERA_TZ_CMD_CCI_POLL,
+ MSM_CAMERA_TZ_CMD_CCI_WRITE_CONF_TBL,
+ MSM_CAMERA_TZ_CMD_CCI_UTIL,
+ MSM_CAMERA_TZ_CMD_SET_MODE,
+ MSM_CAMERA_TZ_CMD_FRAME_NOTIFICATION,
+};
+
+enum msm_camera_tz_status_t {
+ MSM_CAMERA_TZ_STATUS_SUCCESS = 0,
+ MSM_CAMERA_TZ_STATUS_GENERAL_FAILURE = -1,
+ MSM_CAMERA_TZ_STATUS_INVALID_INPUT_PARAMS = -2,
+ MSM_CAMERA_TZ_STATUS_INVALID_SENSOR_ID = -3,
+ MSM_CAMERA_TZ_STATUS_BYPASS = -4,
+ MSM_CAMERA_TZ_STATUS_TIMEOUT = -5,
+
+ MSM_CAMERA_TZ_STATUS_RESET_DONE = 1,
+ MSM_CAMERA_TZ_STATUS_ERR_SIZE = 0x7FFFFFFF
+};
+
+#pragma pack(push, msm_camera_tz, 1)
+
+struct msm_camera_tz_generic_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+};
+
+struct msm_camera_tz_generic_rsp_t {
+ enum msm_camera_tz_status_t rc;
+};
+
+#pragma pack(pop, msm_camera_tz)
+
+uint32_t msm_camera_tz_set_mode(
+ uint32_t mode, uint32_t hw_block);
+
+struct qseecom_handle *msm_camera_tz_get_ta_handle(void);
+int32_t get_cmd_rsp_buffers(
+ struct qseecom_handle *ta_qseecom_handle,
+ void **cmd, int *cmd_len,
+ void **rsp, int *rsp_len);
+int32_t msm_camera_tz_load_ta(void);
+int32_t msm_camera_tz_unload_ta(void);
+void msm_camera_tz_lock(void);
+void msm_camera_tz_unlock(void);
+
+#endif /* __MSM_CAMERA_TZ_UTIL_H */
diff --git a/drivers/media/platform/msm/camera_v2/fd/Makefile b/drivers/media/platform/msm/camera_v2/fd/Makefile
new file mode 100644
index 0000000..8d01d3a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/Makefile
@@ -0,0 +1,8 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc/cpp
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/
+
+obj-$(CONFIG_MSM_FD) += msm_fd_dev.o msm_fd_hw.o
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
new file mode 100644
index 0000000..eb5bdee
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
@@ -0,0 +1,1492 @@
+/* Copyright (c) 2014-2018, 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/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ion.h>
+#include <linux/msm_ion.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <linux/clk/msm-clk.h>
+
+#include "msm_fd_dev.h"
+#include "msm_fd_hw.h"
+#include "msm_fd_regs.h"
+
+#define MSM_FD_DRV_NAME "msm_fd"
+
+#define MSM_FD_WORD_SIZE_BYTES 4
+
+/* Face detection thresholds definitions */
+#define MSM_FD_DEF_THRESHOLD 5
+#define MSM_FD_MAX_THRESHOLD_VALUE 9
+
+/* Face angle lookup table */
+#define MSM_FD_DEF_ANGLE_IDX 2
+static int msm_fd_angle[] = {45, 135, 359};
+
+/* Face direction lookup table */
+#define MSM_FD_DEF_DIR_IDX 0
+static int msm_fd_dir[] = {0, 90, 270, 180};
+
+/* Minimum face size lookup table */
+#define MSM_FD_DEF_MIN_SIZE_IDX 0
+static int msm_fd_min_size[] = {20, 25, 32, 40};
+
+/* Face detection size lookup table */
+static struct msm_fd_size fd_size[] = {
+ {
+ .width = 320,
+ .height = 240,
+ .reg_val = MSM_FD_IMAGE_SIZE_QVGA,
+ .work_size = (13120 * MSM_FD_WORD_SIZE_BYTES),
+ },
+ {
+ .width = 427,
+ .height = 240,
+ .reg_val = MSM_FD_IMAGE_SIZE_WQVGA,
+ .work_size = (17744 * MSM_FD_WORD_SIZE_BYTES),
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .reg_val = MSM_FD_IMAGE_SIZE_VGA,
+ .work_size = (52624 * MSM_FD_WORD_SIZE_BYTES),
+ },
+ {
+ .width = 854,
+ .height = 480,
+ .reg_val = MSM_FD_IMAGE_SIZE_WVGA,
+ .work_size = (70560 * MSM_FD_WORD_SIZE_BYTES),
+ },
+};
+
+/*
+ * msm_fd_ctx_from_fh - Get fd context from v4l2 fh.
+ * @fh: Pointer to v4l2 fh.
+ */
+static inline struct fd_ctx *msm_fd_ctx_from_fh(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct fd_ctx, fh);
+}
+
+/*
+ * msm_fd_get_format_index - Get format index from v4l2 format.
+ * @f: Pointer to v4l2 format struct.
+ */
+static int msm_fd_get_format_index(struct v4l2_format *f)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(fd_size); index++) {
+ if (f->fmt.pix.width <= fd_size[index].width &&
+ f->fmt.pix.height <= fd_size[index].height)
+ return index;
+ }
+ return index - 1;
+}
+
+/*
+ * msm_fd_get_idx_from_value - Get array index from value.
+ * @value: Value for which index is needed.
+ * @array: Array in which index is searched for.
+ * @array_size: Array size.
+ */
+static int msm_fd_get_idx_from_value(int value, int *array, int array_size)
+{
+ int index;
+ int i;
+
+ index = 0;
+ for (i = 1; i < array_size; i++) {
+ if (value == array[i]) {
+ index = i;
+ break;
+ }
+ if (abs(value - array[i]) < abs(value - array[index]))
+ index = i;
+ }
+ return index;
+}
+
+/*
+ * msm_fd_fill_format_from_index - Fill v4l2 format struct from size index.
+ * @f: Pointer of v4l2 struct which will be filled.
+ * @index: Size index (Format will be filled based on this index).
+ */
+static int msm_fd_fill_format_from_index(struct v4l2_format *f, int index)
+{
+ f->fmt.pix.width = fd_size[index].width;
+ f->fmt.pix.height = fd_size[index].height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
+ if (f->fmt.pix.bytesperline < f->fmt.pix.width)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+
+ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, 16);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/*
+ * msm_fd_fill_format_from_ctx - Fill v4l2 format struct from fd context.
+ * @f: Pointer of v4l2 struct which will be filled.
+ * @c: Pointer to fd context.
+ */
+static int msm_fd_fill_format_from_ctx(struct v4l2_format *f, struct fd_ctx *c)
+{
+ if (c->format.size == NULL)
+ return -EINVAL;
+
+ f->fmt.pix.width = c->format.size->width;
+ f->fmt.pix.height = c->format.size->height;
+ f->fmt.pix.pixelformat = c->format.pixelformat;
+ f->fmt.pix.bytesperline = c->format.bytesperline;
+ f->fmt.pix.sizeimage = c->format.sizeimage;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/*
+ * msm_fd_queue_setup - vb2_ops queue_setup callback.
+ * @q: Pointer to vb2 queue struct.
+ * @fmt: Pointer to v4l2 format struct (NULL is valid argument).
+ * @num_buffers: Pointer of number of buffers requested.
+ * @num_planes: Pointer to number of planes requested.
+ * @sizes: Array containing sizes of planes.
+ * @alloc_ctxs: Array of allocated contexts for each plane.
+ */
+static int msm_fd_queue_setup(struct vb2_queue *q,
+// const void *parg,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_ctxs[])
+{
+ struct fd_ctx *ctx = vb2_get_drv_priv(q);
+ //const struct v4l2_format *fmt = parg;
+ const struct v4l2_format *fmt = NULL;
+
+ *num_planes = 1;
+
+ if (fmt == NULL)
+ sizes[0] = ctx->format.sizeimage;
+ else
+ sizes[0] = fmt->fmt.pix.sizeimage;
+
+ alloc_ctxs[0] = (struct device *)&ctx->mem_pool;
+
+ return 0;
+}
+
+/*
+ * msm_fd_buf_init - vb2_ops buf_init callback.
+ * @vb: Pointer to vb2 buffer struct.
+ */
+static int msm_fd_buf_init(struct vb2_buffer *vb)
+{
+ struct msm_fd_buffer *fd_buffer =
+ (struct msm_fd_buffer *)vb;
+
+ INIT_LIST_HEAD(&fd_buffer->list);
+ atomic_set(&fd_buffer->active, 0);
+
+ return 0;
+}
+
+/*
+ * msm_fd_buf_queue - vb2_ops buf_queue callback.
+ * @vb: Pointer to vb2 buffer struct.
+ */
+static void msm_fd_buf_queue(struct vb2_buffer *vb)
+{
+ struct fd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct msm_fd_buffer *fd_buffer =
+ (struct msm_fd_buffer *)vb;
+
+ fd_buffer->format = ctx->format;
+ fd_buffer->settings = ctx->settings;
+ fd_buffer->work_addr = ctx->work_buf.addr;
+ msm_fd_hw_add_buffer(ctx->fd_device, fd_buffer);
+
+ if (vb->vb2_queue->streaming)
+ msm_fd_hw_schedule_and_start(ctx->fd_device);
+
+}
+
+/*
+ * msm_fd_start_streaming - vb2_ops start_streaming callback.
+ * @q: Pointer to vb2 queue struct.
+ * @count: Number of buffer queued before stream on call.
+ */
+static int msm_fd_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fd_ctx *ctx = vb2_get_drv_priv(q);
+ int ret;
+
+ if (ctx->work_buf.fd == -1) {
+ dev_err(ctx->fd_device->dev, "Missing working buffer\n");
+ return -EINVAL;
+ }
+
+ ret = msm_fd_hw_get(ctx->fd_device, ctx->settings.speed);
+ if (ret < 0) {
+ dev_err(ctx->fd_device->dev, "Can not acquire fd hw\n");
+ goto out;
+ }
+
+ ret = msm_fd_hw_schedule_and_start(ctx->fd_device);
+ if (ret < 0)
+ dev_err(ctx->fd_device->dev, "Can not start fd hw\n");
+
+out:
+ return ret;
+}
+
+/*
+ * msm_fd_stop_streaming - vb2_ops stop_streaming callback.
+ * @q: Pointer to vb2 queue struct.
+ */
+static void msm_fd_stop_streaming(struct vb2_queue *q)
+{
+ struct fd_ctx *ctx = vb2_get_drv_priv(q);
+
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ msm_fd_hw_remove_buffers_from_queue(ctx->fd_device, q);
+ msm_fd_hw_put(ctx->fd_device);
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+}
+
+/* Videobuf2 queue callbacks. */
+static struct vb2_ops msm_fd_vb2_q_ops = {
+ .queue_setup = msm_fd_queue_setup,
+ .buf_init = msm_fd_buf_init,
+ .buf_queue = msm_fd_buf_queue,
+ .start_streaming = msm_fd_start_streaming,
+ .stop_streaming = msm_fd_stop_streaming,
+};
+
+/*
+ * msm_fd_get_userptr - Map and get buffer handler for user pointer buffer.
+ * @alloc_ctx: Contexts allocated in buf_setup.
+ * @vaddr: Virtual addr passed from userpsace (in our case ion fd)
+ * @size: Size of the buffer
+ * @write: True if buffer will be used for writing the data.
+ */
+static void *msm_fd_get_userptr(struct device *alloc_ctx,
+ unsigned long vaddr, unsigned long size,
+ enum dma_data_direction dma_dir)
+{
+ struct msm_fd_mem_pool *pool = (void *)alloc_ctx;
+ struct msm_fd_buf_handle *buf;
+ int ret;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ ret = msm_fd_hw_map_buffer(pool, vaddr, buf);
+ if (ret < 0 || buf->size < size)
+ goto error;
+
+ return buf;
+error:
+ kzfree(buf);
+ return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * msm_fd_put_userptr - Unmap and free buffer handler.
+ * @buf_priv: Buffer handler allocated get_userptr callback.
+ */
+static void msm_fd_put_userptr(void *buf_priv)
+{
+ if (IS_ERR_OR_NULL(buf_priv))
+ return;
+
+ msm_fd_hw_unmap_buffer(buf_priv);
+
+ kzfree(buf_priv);
+}
+
+/* Videobuf2 memory callbacks. */
+static struct vb2_mem_ops msm_fd_vb2_mem_ops = {
+ .get_userptr = msm_fd_get_userptr,
+ .put_userptr = msm_fd_put_userptr,
+};
+
+/*
+ * msm_fd_vbif_error_handler - FD VBIF Error handler
+ * @handle: FD Device handle
+ * @error: CPP-VBIF Error code
+ */
+static int msm_fd_vbif_error_handler(void *handle, uint32_t error)
+{
+ struct fd_ctx *ctx;
+ struct msm_fd_device *fd;
+ struct msm_fd_buffer *active_buf;
+ int ret;
+
+ if (handle == NULL)
+ return 0;
+
+ ctx = (struct fd_ctx *)handle;
+ fd = (struct msm_fd_device *)ctx->fd_device;
+
+ if (error == CPP_VBIF_ERROR_HANG) {
+ mutex_lock(&fd->recovery_lock);
+ dev_err(fd->dev, "Handling FD VBIF Hang\n");
+ if (fd->state != MSM_FD_DEVICE_RUNNING) {
+ dev_err(fd->dev, "FD is not FD_DEVICE_RUNNING, %d\n",
+ fd->state);
+ mutex_unlock(&fd->recovery_lock);
+ return 0;
+ }
+ fd->recovery_mode = 1;
+
+ /* Halt and reset */
+ msm_fd_hw_put(fd);
+ msm_fd_hw_get(fd, ctx->settings.speed);
+
+ /* Get active buffer */
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+ active_buf = msm_fd_hw_get_active_buffer(fd, 1);
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+
+ if (active_buf == NULL) {
+ dev_dbg(fd->dev, "no active buffer, return\n");
+ fd->recovery_mode = 0;
+ mutex_unlock(&fd->recovery_lock);
+ return 0;
+ }
+
+ dev_dbg(fd->dev, "Active Buffer present.. Start re-schedule\n");
+
+ /* Queue the buffer again */
+ msm_fd_hw_add_buffer(fd, active_buf);
+
+ /* Schedule and restart */
+ ret = msm_fd_hw_schedule_next_buffer(fd, 1);
+ if (ret) {
+ dev_err(fd->dev, "Cannot reschedule buffer, recovery failed\n");
+ fd->recovery_mode = 0;
+ mutex_unlock(&fd->recovery_lock);
+ return ret;
+ }
+ dev_dbg(fd->dev, "Restarted FD after VBIF HAng\n");
+ mutex_unlock(&fd->recovery_lock);
+ }
+ return 0;
+}
+
+/*
+ * msm_fd_open - Fd device open method.
+ * @file: Pointer to file struct.
+ */
+static int msm_fd_open(struct file *file)
+{
+ struct msm_fd_device *device = video_drvdata(file);
+ struct video_device *video = video_devdata(file);
+ struct fd_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->fd_device = device;
+
+ /* Initialize work buffer handler */
+ ctx->work_buf.pool = NULL;
+ ctx->work_buf.fd = -1;
+
+ /* Set ctx defaults */
+ ctx->settings.speed = ctx->fd_device->clk_rates_num - 1;
+ ctx->settings.angle_index = MSM_FD_DEF_ANGLE_IDX;
+ ctx->settings.direction_index = MSM_FD_DEF_DIR_IDX;
+ ctx->settings.min_size_index = MSM_FD_DEF_MIN_SIZE_IDX;
+ ctx->settings.threshold = MSM_FD_DEF_THRESHOLD;
+
+ atomic_set(&ctx->subscribed_for_event, 0);
+
+ v4l2_fh_init(&ctx->fh, video);
+
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->vb2_q.drv_priv = ctx;
+ ctx->vb2_q.mem_ops = &msm_fd_vb2_mem_ops;
+ ctx->vb2_q.ops = &msm_fd_vb2_q_ops;
+ ctx->vb2_q.buf_struct_size = sizeof(struct msm_fd_buffer);
+ ctx->vb2_q.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ctx->vb2_q.io_modes = VB2_USERPTR;
+ ctx->vb2_q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ mutex_init(&ctx->lock);
+ ret = vb2_queue_init(&ctx->vb2_q);
+ if (ret < 0) {
+ dev_err(device->dev, "Error queue init\n");
+ goto error_vb2_queue_init;
+ }
+
+ ctx->mem_pool.fd_device = ctx->fd_device;
+ ctx->stats = vmalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS);
+ if (!ctx->stats) {
+ dev_err(device->dev, "No memory for face statistics\n");
+ ret = -ENOMEM;
+ goto error_stats_vmalloc;
+ }
+
+ ret = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_FD,
+ CAM_AHB_SVS_VOTE);
+ if (ret < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ goto error_ahb_config;
+ }
+
+ /* Register with CPP VBIF error handler */
+ msm_cpp_vbif_register_error_handler((void *)ctx,
+ VBIF_CLIENT_FD, msm_fd_vbif_error_handler);
+
+ return 0;
+
+error_ahb_config:
+ vfree(ctx->stats);
+error_stats_vmalloc:
+ vb2_queue_release(&ctx->vb2_q);
+error_vb2_queue_init:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+/*
+ * msm_fd_release - Fd device release method.
+ * @file: Pointer to file struct.
+ */
+static int msm_fd_release(struct file *file)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data);
+
+ /* Un-register with CPP VBIF error handler */
+ msm_cpp_vbif_register_error_handler((void *)ctx,
+ VBIF_CLIENT_FD, NULL);
+
+ mutex_lock(&ctx->lock);
+ vb2_queue_release(&ctx->vb2_q);
+ mutex_unlock(&ctx->lock);
+
+ vfree(ctx->stats);
+
+ if (ctx->work_buf.fd != -1)
+ msm_fd_hw_unmap_buffer(&ctx->work_buf);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ kfree(ctx);
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_FD,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+
+ return 0;
+}
+
+/*
+ * msm_fd_poll - Fd device pool method.
+ * @file: Pointer to file struct.
+ * @wait: Pointer to pool table struct.
+ */
+static unsigned int msm_fd_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(file->private_data);
+ unsigned int ret;
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_poll(&ctx->vb2_q, file, wait);
+ mutex_unlock(&ctx->lock);
+
+ if (atomic_read(&ctx->subscribed_for_event)) {
+ poll_wait(file, &ctx->fh.wait, wait);
+ if (v4l2_event_pending(&ctx->fh))
+ ret |= POLLPRI;
+ }
+
+ return ret;
+}
+
+/*
+ * msm_fd_private_ioctl - V4l2 private ioctl handler.
+ * @file: Pointer to file struct.
+ * @fd: V4l2 device file handle.
+ * @valid_prio: Priority ioctl valid flag.
+ * @cmd: Ioctl command.
+ * @arg: Ioctl argument.
+ */
+static long msm_fd_private_ioctl(struct file *file, void *fh,
+ bool valid_prio, unsigned int cmd, void *arg)
+{
+ struct msm_fd_result *req_result = arg;
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ struct msm_fd_stats *stats;
+ int stats_idx;
+ int ret = 0;
+ int i;
+
+ switch (cmd) {
+ case VIDIOC_MSM_FD_GET_RESULT:
+ if (req_result->frame_id == 0) {
+ dev_err(ctx->fd_device->dev, "Invalid frame id\n");
+ return -EINVAL;
+ }
+
+ stats_idx = req_result->frame_id % MSM_FD_MAX_RESULT_BUFS;
+ stats = &ctx->stats[stats_idx];
+ if (req_result->frame_id != atomic_read(&stats->frame_id)) {
+ dev_err(ctx->fd_device->dev, "Stats not available\n");
+ return -EINVAL;
+ }
+
+ if (req_result->face_cnt > stats->face_cnt)
+ req_result->face_cnt = stats->face_cnt;
+
+ for (i = 0; i < req_result->face_cnt; i++) {
+ ret = copy_to_user((void __user *)
+ &req_result->face_data[i],
+ &stats->face_data[i],
+ sizeof(struct msm_fd_face_data));
+ if (ret) {
+ dev_err(ctx->fd_device->dev, "Copy to user\n");
+ return -EFAULT;
+ }
+ }
+
+ if (req_result->frame_id != atomic_read(&stats->frame_id)) {
+ dev_err(ctx->fd_device->dev, "Erroneous buffer\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(ctx->fd_device->dev, "Wrong ioctl type %x\n", cmd);
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+/*
+ * msm_fd_compat_ioctl32 - Compat ioctl handler function.
+ * @file: Pointer to file struct.
+ * @cmd: Ioctl command.
+ * @arg: Ioctl argument.
+ */
+static long msm_fd_compat_ioctl32(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ switch (cmd) {
+ case VIDIOC_MSM_FD_GET_RESULT32:
+ {
+ struct msm_fd_result32 result32;
+ struct msm_fd_result result;
+
+ if (copy_from_user(&result32, (void __user *)arg,
+ sizeof(result32)))
+ return -EFAULT;
+
+ result.frame_id = result32.frame_id;
+ result.face_cnt = result32.face_cnt;
+ result.face_data = compat_ptr(result32.face_data);
+
+ ret = msm_fd_private_ioctl(file, file->private_data,
+ 0, VIDIOC_MSM_FD_GET_RESULT, (void *)&result);
+
+ result32.frame_id = result.frame_id;
+ result32.face_cnt = result.face_cnt;
+
+ if (copy_to_user((void __user *)arg, &result32,
+ sizeof(result32)))
+ return -EFAULT;
+
+ break;
+ }
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+
+ }
+
+ return ret;
+}
+#endif
+
+/* Fd device file operations callbacks */
+static const struct v4l2_file_operations fd_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_fd_open,
+ .release = msm_fd_release,
+ .poll = msm_fd_poll,
+ .unlocked_ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = msm_fd_compat_ioctl32,
+#endif
+};
+
+/*
+ * msm_fd_querycap - V4l2 ioctl query capability handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @cap: Pointer to v4l2_capability struct need to be filled.
+ */
+static int msm_fd_querycap(struct file *file,
+ void *fh, struct v4l2_capability *cap)
+{
+ cap->bus_info[0] = 0;
+ strlcpy(cap->driver, MSM_FD_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MSM_FD_DRV_NAME, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT;
+
+ return 0;
+}
+
+/*
+ * msm_fd_enum_fmt_vid_out - V4l2 ioctl enumerate format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_fmtdesc struct need to be filled.
+ */
+static int msm_fd_enum_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index > 0)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strlcpy(f->description, "8 Greyscale",
+ sizeof(f->description));
+
+ return 0;
+}
+
+/*
+ * msm_fd_g_fmt - V4l2 ioctl get format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct need to be filled.
+ */
+static int msm_fd_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ return msm_fd_fill_format_from_ctx(f, ctx);
+}
+
+/*
+ * msm_fd_try_fmt_vid_out - V4l2 ioctl try format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_fd_try_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ int index;
+
+ index = msm_fd_get_format_index(f);
+
+ return msm_fd_fill_format_from_index(f, index);
+}
+
+/*
+ * msm_fd_s_fmt_vid_out - V4l2 ioctl set format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_fd_s_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int index;
+
+ index = msm_fd_get_format_index(f);
+
+ msm_fd_fill_format_from_index(f, index);
+
+ ctx->format.size = &fd_size[index];
+ ctx->format.pixelformat = f->fmt.pix.pixelformat;
+ ctx->format.bytesperline = f->fmt.pix.bytesperline;
+ ctx->format.sizeimage = f->fmt.pix.sizeimage;
+
+ /* Initialize crop */
+ ctx->format.crop.top = 0;
+ ctx->format.crop.left = 0;
+ ctx->format.crop.width = fd_size[index].width;
+ ctx->format.crop.height = fd_size[index].height;
+
+ return 0;
+}
+
+/*
+ * msm_fd_reqbufs - V4l2 ioctl request buffers handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @req: Pointer to v4l2_requestbuffer struct.
+ */
+static int msm_fd_reqbufs(struct file *file,
+ void *fh, struct v4l2_requestbuffers *req)
+{
+ int ret;
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_reqbufs(&ctx->vb2_q, req);
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+/*
+ * msm_fd_qbuf - V4l2 ioctl queue buffer handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @pb: Pointer to v4l2_buffer struct.
+ */
+static int msm_fd_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *pb)
+{
+ int ret;
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_qbuf(&ctx->vb2_q, pb);
+ mutex_unlock(&ctx->lock);
+ return ret;
+
+}
+
+/*
+ * msm_fd_dqbuf - V4l2 ioctl dequeue buffer handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @pb: Pointer to v4l2_buffer struct.
+ */
+static int msm_fd_dqbuf(struct file *file,
+ void *fh, struct v4l2_buffer *pb)
+{
+ int ret;
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+/*
+ * msm_fd_streamon - V4l2 ioctl stream on handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf_type: V4l2 buffer type.
+ */
+static int msm_fd_streamon(struct file *file,
+ void *fh, enum v4l2_buf_type buf_type)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int ret;
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_streamon(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
+ if (ret < 0)
+ dev_err(ctx->fd_device->dev, "Stream on fails\n");
+
+ return ret;
+}
+
+/*
+ * msm_fd_streamoff - V4l2 ioctl stream off handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf_type: V4l2 buffer type.
+ */
+static int msm_fd_streamoff(struct file *file,
+ void *fh, enum v4l2_buf_type buf_type)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int ret;
+
+ mutex_lock(&ctx->lock);
+ ret = vb2_streamoff(&ctx->vb2_q, buf_type);
+ mutex_unlock(&ctx->lock);
+ if (ret < 0)
+ dev_err(ctx->fd_device->dev, "Stream off fails\n");
+
+ return ret;
+}
+
+/*
+ * msm_fd_subscribe_event - V4l2 ioctl subscribe for event handler.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_event_subscription containing event information.
+ */
+static int msm_fd_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int ret;
+
+ if (sub->type != MSM_EVENT_FD)
+ return -EINVAL;
+
+ ret = v4l2_event_subscribe(fh, sub, MSM_FD_MAX_RESULT_BUFS, NULL);
+ if (!ret)
+ atomic_set(&ctx->subscribed_for_event, 1);
+
+ return ret;
+}
+
+/*
+ * msm_fd_unsubscribe_event - V4l2 ioctl unsubscribe from event handler.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_event_subscription containing event information.
+ */
+static int msm_fd_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int ret;
+
+ ret = v4l2_event_unsubscribe(fh, sub);
+ if (!ret)
+ atomic_set(&ctx->subscribed_for_event, 0);
+
+ return ret;
+}
+
+/*
+ * msm_fd_guery_ctrl - V4l2 ioctl query control.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_queryctrl struct info need to be filled based on id.
+ */
+static int msm_fd_guery_ctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ switch (a->id) {
+ case V4L2_CID_FD_SPEED:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = ctx->fd_device->clk_rates_num;
+ a->minimum = 0;
+ a->maximum = ctx->fd_device->clk_rates_num;
+ a->step = 1;
+ strlcpy(a->name, "msm fd face speed idx",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_FACE_ANGLE:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = msm_fd_angle[MSM_FD_DEF_ANGLE_IDX];
+ a->minimum = msm_fd_angle[0];
+ a->maximum = msm_fd_angle[ARRAY_SIZE(msm_fd_angle) - 1];
+ a->step = 1;
+ strlcpy(a->name, "msm fd face angle ctrl",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_FACE_DIRECTION:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = msm_fd_dir[MSM_FD_DEF_DIR_IDX];
+ a->minimum = msm_fd_dir[0];
+ a->maximum = msm_fd_dir[ARRAY_SIZE(msm_fd_dir) - 1];
+ a->step = 1;
+ strlcpy(a->name, "msm fd face direction ctrl",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_MIN_FACE_SIZE:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = msm_fd_min_size[MSM_FD_DEF_MIN_SIZE_IDX];
+ a->minimum = msm_fd_min_size[0];
+ a->maximum = msm_fd_min_size[ARRAY_SIZE(msm_fd_min_size) - 1];
+ a->step = 1;
+ strlcpy(a->name, "msm fd minimum face size (pixels)",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_DETECTION_THRESHOLD:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = MSM_FD_DEF_THRESHOLD;
+ a->minimum = 0;
+ a->maximum = MSM_FD_MAX_THRESHOLD_VALUE;
+ a->step = 1;
+ strlcpy(a->name, "msm fd detection threshold",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_SIZE:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = fd_size[0].work_size;
+ a->minimum = fd_size[(ARRAY_SIZE(fd_size) - 1)].work_size;
+ a->maximum = fd_size[0].work_size;
+ a->step = 1;
+ strlcpy(a->name, "msm fd working memory size",
+ sizeof(a->name));
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_FD:
+ a->type = V4L2_CTRL_TYPE_INTEGER;
+ a->default_value = -1;
+ a->minimum = 0;
+ a->maximum = INT_MAX;
+ a->step = 1;
+ strlcpy(a->name, "msm fd ion fd of working memory",
+ sizeof(a->name));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_fd_g_ctrl - V4l2 ioctl get control.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_queryctrl struct need to be filled.
+ */
+static int msm_fd_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ switch (a->id) {
+ case V4L2_CID_FD_SPEED:
+ a->value = ctx->settings.speed;
+ break;
+ case V4L2_CID_FD_FACE_ANGLE:
+ a->value = msm_fd_angle[ctx->settings.angle_index];
+ break;
+ case V4L2_CID_FD_FACE_DIRECTION:
+ a->value = msm_fd_dir[ctx->settings.direction_index];
+ break;
+ case V4L2_CID_FD_MIN_FACE_SIZE:
+ a->value = msm_fd_min_size[ctx->settings.min_size_index];
+ break;
+ case V4L2_CID_FD_DETECTION_THRESHOLD:
+ a->value = ctx->settings.threshold;
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_SIZE:
+ if (!ctx->format.size)
+ return -EINVAL;
+
+ a->value = ctx->format.size->work_size;
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_FD:
+ if (ctx->work_buf.fd == -1)
+ return -EINVAL;
+
+ a->value = ctx->work_buf.fd;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_fd_s_ctrl - V4l2 ioctl set control.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_queryctrl struct need to be set.
+ */
+static int msm_fd_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int idx;
+ int ret;
+
+ switch (a->id) {
+ case V4L2_CID_FD_SPEED:
+ if (a->value > ctx->fd_device->clk_rates_num - 1)
+ a->value = ctx->fd_device->clk_rates_num - 1;
+ else if (a->value < 0)
+ a->value = 0;
+
+ ctx->settings.speed = a->value;
+ break;
+ case V4L2_CID_FD_FACE_ANGLE:
+ idx = msm_fd_get_idx_from_value(a->value, msm_fd_angle,
+ ARRAY_SIZE(msm_fd_angle));
+
+ ctx->settings.angle_index = idx;
+ a->value = msm_fd_angle[ctx->settings.angle_index];
+ break;
+ case V4L2_CID_FD_FACE_DIRECTION:
+ idx = msm_fd_get_idx_from_value(a->value, msm_fd_dir,
+ ARRAY_SIZE(msm_fd_dir));
+
+ ctx->settings.direction_index = idx;
+ a->value = msm_fd_dir[ctx->settings.direction_index];
+ break;
+ case V4L2_CID_FD_MIN_FACE_SIZE:
+ idx = msm_fd_get_idx_from_value(a->value, msm_fd_min_size,
+ ARRAY_SIZE(msm_fd_min_size));
+
+ ctx->settings.min_size_index = idx;
+ a->value = msm_fd_min_size[ctx->settings.min_size_index];
+ break;
+ case V4L2_CID_FD_DETECTION_THRESHOLD:
+ if (a->value > MSM_FD_MAX_THRESHOLD_VALUE)
+ a->value = MSM_FD_MAX_THRESHOLD_VALUE;
+ else if (a->value < 0)
+ a->value = 0;
+
+ ctx->settings.threshold = a->value;
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_SIZE:
+ if (!ctx->format.size)
+ return -EINVAL;
+
+ if (a->value < ctx->format.size->work_size)
+ a->value = ctx->format.size->work_size;
+ break;
+ case V4L2_CID_FD_WORK_MEMORY_FD:
+ mutex_lock(&ctx->fd_device->recovery_lock);
+ if (ctx->work_buf.fd != -1)
+ msm_fd_hw_unmap_buffer(&ctx->work_buf);
+ if (a->value >= 0) {
+ ret = msm_fd_hw_map_buffer(&ctx->mem_pool,
+ a->value, &ctx->work_buf);
+ if (ret < 0) {
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&ctx->fd_device->recovery_lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_fd_cropcap - V4l2 ioctl crop capabilities.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_cropcap struct need to be set.
+ */
+static int msm_fd_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ if (!ctx->format.size) {
+ dev_err(ctx->fd_device->dev, "Cropcap fails format missing\n");
+ return -EINVAL;
+ }
+
+ a->bounds.top = 0;
+ a->bounds.left = 0;
+ a->bounds.width = ctx->format.size->width;
+ a->bounds.height = ctx->format.size->height;
+
+ a->defrect = ctx->format.crop;
+
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+/*
+ * msm_fd_g_crop - V4l2 ioctl get crop.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_crop struct need to be set.
+ */
+static int msm_fd_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+
+ if (!ctx->format.size) {
+ dev_err(ctx->fd_device->dev, "Get crop, format missing!\n");
+ return -EINVAL;
+ }
+
+ crop->c = ctx->format.crop;
+
+ return 0;
+}
+
+/*
+ * msm_fd_s_crop - V4l2 ioctl set crop.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_crop struct need to be set.
+ */
+static int msm_fd_s_crop(struct file *file, void *fh,
+ const struct v4l2_crop *crop)
+{
+ struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh);
+ int min_face_size;
+
+ if (!ctx->format.size) {
+ dev_err(ctx->fd_device->dev, "Get crop, format missing!\n");
+ return -EINVAL;
+ }
+
+ /* First check that crop is valid */
+ min_face_size = msm_fd_min_size[ctx->settings.min_size_index];
+
+ if (crop->c.width < min_face_size || crop->c.height < min_face_size)
+ return -EINVAL;
+
+ if (crop->c.width + crop->c.left > ctx->format.size->width)
+ return -EINVAL;
+
+ if (crop->c.height + crop->c.top > ctx->format.size->height)
+ return -EINVAL;
+
+ ctx->format.crop = crop->c;
+
+ return 0;
+}
+
+/* V4l2 ioctl handlers */
+static const struct v4l2_ioctl_ops fd_ioctl_ops = {
+ .vidioc_querycap = msm_fd_querycap,
+ .vidioc_enum_fmt_vid_out = msm_fd_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = msm_fd_g_fmt,
+ .vidioc_try_fmt_vid_out = msm_fd_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = msm_fd_s_fmt_vid_out,
+ .vidioc_reqbufs = msm_fd_reqbufs,
+ .vidioc_qbuf = msm_fd_qbuf,
+ .vidioc_dqbuf = msm_fd_dqbuf,
+ .vidioc_streamon = msm_fd_streamon,
+ .vidioc_streamoff = msm_fd_streamoff,
+ .vidioc_queryctrl = msm_fd_guery_ctrl,
+ .vidioc_s_ctrl = msm_fd_s_ctrl,
+ .vidioc_g_ctrl = msm_fd_g_ctrl,
+ .vidioc_cropcap = msm_fd_cropcap,
+ .vidioc_g_crop = msm_fd_g_crop,
+ .vidioc_s_crop = msm_fd_s_crop,
+ .vidioc_subscribe_event = msm_fd_subscribe_event,
+ .vidioc_unsubscribe_event = msm_fd_unsubscribe_event,
+ .vidioc_default = msm_fd_private_ioctl,
+};
+
+/*
+ * msm_fd_fill_results - Read and fill face detection result.
+ * @fd: Pointer to fd device.
+ * @face: Pointer of face data which information need to be stored.
+ * @idx: Face number index need to be filled.
+ */
+static void msm_fd_fill_results(struct msm_fd_device *fd,
+ struct msm_fd_face_data *face, int idx)
+{
+ int half_face_size;
+
+ msm_fd_hw_get_result_angle_pose(fd, idx, &face->angle, &face->pose);
+
+ msm_fd_hw_get_result_conf_size(fd, idx, &face->confidence,
+ &face->face.width);
+ face->face.height = face->face.width;
+
+ face->face.left = msm_fd_hw_get_result_x(fd, idx);
+ face->face.top = msm_fd_hw_get_result_y(fd, idx);
+
+ half_face_size = (face->face.width >> 1);
+ if (face->face.left > half_face_size)
+ face->face.left -= half_face_size;
+ else
+ face->face.left = 0;
+
+ half_face_size = (face->face.height >> 1);
+ if (face->face.top > half_face_size)
+ face->face.top -= half_face_size;
+ else
+ face->face.top = 0;
+}
+
+/*
+ * msm_fd_wq_handler - Fd device workqueue handler.
+ * @work: Pointer to work struct.
+ *
+ * This function is bottom half of fd irq what it does:
+ *
+ * - Stop the fd engine.
+ * - Getter fd result and store in stats buffer.
+ * - If available schedule next buffer for processing.
+ * - Sent event to v4l2.
+ * - Release buffer from v4l2 queue.
+ */
+static void msm_fd_wq_handler(struct work_struct *work)
+{
+ struct msm_fd_buffer *active_buf;
+ struct msm_fd_stats *stats;
+ struct msm_fd_event *fd_event;
+ struct msm_fd_device *fd;
+ struct fd_ctx *ctx;
+ struct v4l2_event event;
+ int i;
+
+ fd = container_of(work, struct msm_fd_device, work);
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+ active_buf = msm_fd_hw_get_active_buffer(fd, 0);
+ if (!active_buf) {
+ /* This should never happen, something completely wrong */
+ dev_err(fd->dev, "Oops no active buffer empty queue\n");
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+ return;
+ }
+ ctx = vb2_get_drv_priv(active_buf->vb_v4l2_buf.vb2_buf.vb2_queue);
+
+ /* Increment sequence number, 0 means sequence is not valid */
+ ctx->sequence++;
+ if (unlikely(!ctx->sequence))
+ ctx->sequence = 1;
+
+ /* Fill face detection statistics */
+ stats = &ctx->stats[ctx->sequence % MSM_FD_MAX_RESULT_BUFS];
+
+ /* First mark stats as invalid */
+ atomic_set(&stats->frame_id, 0);
+
+ stats->face_cnt = msm_fd_hw_get_face_count(fd);
+ for (i = 0; i < stats->face_cnt; i++)
+ msm_fd_fill_results(fd, &stats->face_data[i], i);
+
+ /* Stats are ready, set correct frame id */
+ atomic_set(&stats->frame_id, ctx->sequence);
+
+ /* If Recovery mode is on, we got IRQ after recovery, reset it */
+ if (fd->recovery_mode) {
+ fd->recovery_mode = 0;
+ dev_dbg(fd->dev, "Got IRQ after Recovery\n");
+ }
+
+ if (fd->state == MSM_FD_DEVICE_RUNNING) {
+ /* We have the data from fd hw, we can start next processing */
+ msm_fd_hw_schedule_next_buffer(fd, 0);
+ }
+
+ /* Return buffer to vb queue */
+ active_buf->vb_v4l2_buf.sequence = ctx->fh.sequence;
+ vb2_buffer_done(&active_buf->vb_v4l2_buf.vb2_buf, VB2_BUF_STATE_DONE);
+
+ /* Sent event */
+ memset(&event, 0x00, sizeof(event));
+ event.type = MSM_EVENT_FD;
+ fd_event = (struct msm_fd_event *)event.u.data;
+ fd_event->face_cnt = stats->face_cnt;
+ fd_event->buf_index = active_buf->vb_v4l2_buf.vb2_buf.index;
+ fd_event->frame_id = ctx->sequence;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+
+ /* Release buffer from the device */
+ msm_fd_hw_buffer_done(fd, active_buf, 0);
+
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+}
+
+/*
+ * fd_probe - Fd device probe method.
+ * @pdev: Pointer fd platform device.
+ */
+static int fd_probe(struct platform_device *pdev)
+{
+ struct msm_fd_device *fd;
+ int ret;
+ int i;
+
+ /* Face detection device struct */
+ fd = kzalloc(sizeof(struct msm_fd_device), GFP_KERNEL);
+ if (!fd)
+ return -ENOMEM;
+
+ mutex_init(&fd->lock);
+ spin_lock_init(&fd->slock);
+ mutex_init(&fd->recovery_lock);
+ init_completion(&fd->hw_halt_completion);
+ INIT_LIST_HEAD(&fd->buf_queue);
+ fd->pdev = pdev;
+ fd->dev = &pdev->dev;
+
+ /* Get resources */
+ ret = msm_fd_hw_get_mem_resources(pdev, fd);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail get resources\n");
+ ret = -ENODEV;
+ goto error_mem_resources;
+ }
+
+ ret = msm_camera_get_regulator_info(pdev, &fd->vdd_info,
+ &fd->num_reg);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail to get regulators\n");
+ goto error_get_regulator;
+ }
+ ret = msm_camera_get_clk_info_and_rates(pdev, &fd->clk_info,
+ &fd->clk, &fd->clk_rates, &fd->clk_rates_num, &fd->clk_num);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail to get clocks\n");
+ goto error_get_clocks;
+ }
+
+ /*set memcore and mem periphery logic flags to 0*/
+ for (i = 0; i < fd->clk_num; i++) {
+ if ((strcmp(fd->clk_info[i].clk_name,
+ "mmss_fd_core_clk") == 0) ||
+ (strcmp(fd->clk_info[i].clk_name,
+ "mmss_fd_core_uar_clk") == 0)) {
+ msm_camera_set_clk_flags(fd->clk[i],
+ CLKFLAG_NORETAIN_MEM);
+ msm_camera_set_clk_flags(fd->clk[i],
+ CLKFLAG_NORETAIN_PERIPH);
+ }
+ }
+
+ ret = msm_camera_register_bus_client(pdev, CAM_BUS_CLIENT_FD);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail to get bus\n");
+ goto error_get_bus;
+ }
+
+ /* Get face detect hw before read engine revision */
+ ret = msm_fd_hw_get(fd, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail to get hw\n");
+ goto error_hw_get_request_irq;
+ }
+ fd->hw_revision = msm_fd_hw_get_revision(fd);
+
+ msm_fd_hw_put(fd);
+
+ ret = msm_fd_hw_request_irq(pdev, fd, msm_fd_wq_handler);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Fail request irq\n");
+ goto error_hw_get_request_irq;
+ }
+
+ /* v4l2 device */
+ ret = v4l2_device_register(&pdev->dev, &fd->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ ret = -ENOENT;
+ goto error_v4l2_register;
+ }
+
+ fd->video.fops = &fd_fops;
+ fd->video.ioctl_ops = &fd_ioctl_ops;
+ fd->video.minor = -1;
+ fd->video.release = video_device_release;
+ fd->video.v4l2_dev = &fd->v4l2_dev;
+ fd->video.vfl_dir = VFL_DIR_TX;
+ fd->video.vfl_type = VFL_TYPE_GRABBER;
+ strlcpy(fd->video.name, MSM_FD_DRV_NAME, sizeof(fd->video.name));
+
+ ret = video_register_device(&fd->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ v4l2_err(&fd->v4l2_dev, "Failed to register video device\n");
+ goto error_video_register;
+ }
+
+ video_set_drvdata(&fd->video, fd);
+
+ platform_set_drvdata(pdev, fd);
+
+ return 0;
+
+error_video_register:
+ v4l2_device_unregister(&fd->v4l2_dev);
+error_v4l2_register:
+ msm_fd_hw_release_irq(fd);
+error_hw_get_request_irq:
+ msm_camera_unregister_bus_client(CAM_BUS_CLIENT_FD);
+error_get_bus:
+ msm_camera_put_clk_info_and_rates(pdev, &fd->clk_info,
+ &fd->clk, &fd->clk_rates, fd->clk_rates_num, fd->clk_num);
+error_get_clocks:
+ msm_camera_put_regulators(pdev, &fd->vdd_info, fd->num_reg);
+error_get_regulator:
+ msm_fd_hw_release_mem_resources(fd);
+error_mem_resources:
+ kfree(fd);
+ return ret;
+}
+
+/*
+ * fd_device_remove - Fd device remove method.
+ * @pdev: Pointer fd platform device.
+ */
+static int fd_device_remove(struct platform_device *pdev)
+{
+ struct msm_fd_device *fd;
+
+ fd = platform_get_drvdata(pdev);
+ if (fd == NULL) {
+ dev_err(&pdev->dev, "Can not get fd drvdata\n");
+ return 0;
+ }
+ video_unregister_device(&fd->video);
+ v4l2_device_unregister(&fd->v4l2_dev);
+ msm_fd_hw_release_irq(fd);
+ msm_camera_unregister_bus_client(CAM_BUS_CLIENT_FD);
+ msm_camera_put_clk_info_and_rates(pdev, &fd->clk_info,
+ &fd->clk, &fd->clk_rates, fd->clk_rates_num, fd->clk_num);
+ msm_camera_put_regulators(pdev, &fd->vdd_info, fd->num_reg);
+ msm_fd_hw_release_mem_resources(fd);
+ kfree(fd);
+
+ return 0;
+}
+
+/* Device tree match struct */
+static const struct of_device_id msm_fd_dt_match[] = {
+ {.compatible = "qcom,face-detection"},
+ {}
+};
+
+/* Fd platform driver definition */
+static struct platform_driver fd_driver = {
+ .probe = fd_probe,
+ .remove = fd_device_remove,
+ .driver = {
+ .name = MSM_FD_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_fd_dt_match,
+ },
+};
+
+static int __init msm_fd_init_module(void)
+{
+ return platform_driver_register(&fd_driver);
+}
+
+static void __exit msm_fd_exit_module(void)
+{
+ platform_driver_unregister(&fd_driver);
+}
+
+module_init(msm_fd_init_module);
+module_exit(msm_fd_exit_module);
+MODULE_DESCRIPTION("MSM FD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h
new file mode 100644
index 0000000..c0030f9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.h
@@ -0,0 +1,272 @@
+/* Copyright (c) 2014-2018, 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_FD_DEV_H__
+#define __MSM_FD_DEV_H__
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-v4l2.h>
+#include <linux/msm-bus.h>
+#include <media/msm_fd.h>
+#include <linux/dma-buf.h>
+#include <linux/msm_ion.h>
+#include "cam_soc_api.h"
+#include "cam_hw_ops.h"
+#include "msm_cpp.h"
+
+/* Maximum number of result buffers */
+#define MSM_FD_MAX_RESULT_BUFS 5
+/* Max number of clocks defined in device tree */
+#define MSM_FD_MAX_CLK_NUM 15
+/* Max number of clock rates defined in device tree */
+#define MSM_FD_MAX_CLK_RATES 5
+/* Max number of faces which can be detected in one hw processing */
+#define MSM_FD_MAX_FACES_DETECTED 32
+/* Max number of regulators defined in device tree */
+#define MSM_FD_MAX_REGULATOR_NUM 3
+
+/* Conditional spin lock macro */
+#define MSM_FD_SPIN_LOCK(l, f) ({\
+ if (f) \
+ spin_lock(&l); \
+})
+
+/* Conditional spin unlock macro */
+#define MSM_FD_SPIN_UNLOCK(l, f) ({ \
+ if (f) \
+ spin_unlock(&l); \
+})
+
+/*
+ * struct msm_fd_size - Structure contain FD size related values.
+ * @width: Image width.
+ * @height: Image height.
+ * @reg_val: Register value for this size.
+ * @work_size: Working buffer size in bytes for this size.
+ */
+struct msm_fd_size {
+ int width;
+ int height;
+ u32 reg_val;
+ int work_size;
+};
+
+/*
+ * struct msm_fd_setings - Structure contain FD settings values.
+ * @min_size_index: Minimum face size array index.
+ * @angle_index: Face detection angle array index.
+ * @direction_index: Face detection direction array index.
+ * @threshold: Face detection threshold value.
+ * @speed: Face detection speed value (it should match with clock rate index).
+ */
+struct msm_fd_setings {
+ unsigned int min_size_index;
+ unsigned int angle_index;
+ unsigned int direction_index;
+ unsigned int threshold;
+ unsigned int speed;
+};
+
+/*
+ * struct msm_fd_format - Structure contain FD format settings.
+ * @size: Pointer to fd size struct used for this format.
+ * @crop: V4l2 crop structure.
+ * @bytesperline: Bytes per line of input image buffer.
+ * @sizeimage: Size of input image buffer.
+ * @pixeformat: Pix format of input image buffer.
+ */
+struct msm_fd_format {
+ struct msm_fd_size *size;
+ struct v4l2_rect crop;
+ int bytesperline;
+ int sizeimage;
+ u32 pixelformat;
+};
+
+/*
+ * struct msm_fd_mem_pool - Structure contain FD memory pool information.
+ * @fd_device: Pointer to fd device.
+ * @client: Pointer to ion client.
+ * @domain_num: Domain number associated with FD hw.
+ */
+struct msm_fd_mem_pool {
+ struct msm_fd_device *fd_device;
+};
+
+/*
+ * struct msm_fd_buf_handle - Structure contain FD buffer handle information.
+ * @fd: ion FD from which this buffer is imported.
+ * @pool: Pointer to FD memory pool struct.
+ * @handle: Pointer to ion handle.
+ * @size: Size of the buffer.
+ * @addr: Adders of FD mmu mapped buffer. This address should be set to FD hw.
+ */
+struct msm_fd_buf_handle {
+ int fd;
+ struct msm_fd_mem_pool *pool;
+ size_t size;
+ ion_phys_addr_t addr;
+};
+
+/*
+ * struct msm_fd_buffer - Vb2 buffer wrapper structure.
+ * @vb: Videobuf 2 buffer structure.
+ * @active: Flag indicating if buffer currently used by FD hw.
+ * @completion: Completion need to wait on, if buffer is used by FD hw.
+ * @format: Format information of this buffer.
+ * @settings: Settings value of this buffer.
+ * @work_addr: Working buffer address need to be used when for this buffer.
+ * @list: Buffer is part of FD device processing queue
+ */
+struct msm_fd_buffer {
+ struct vb2_v4l2_buffer vb_v4l2_buf;
+ atomic_t active;
+ struct completion completion;
+ struct msm_fd_format format;
+ struct msm_fd_setings settings;
+ ion_phys_addr_t work_addr;
+ struct list_head list;
+};
+
+/*
+ * struct msm_fd_stats - Structure contains FD result statistic information.
+ * @frame_id: Frame id for which statistic corresponds to.
+ * @face_cnt: Number of faces detected and included in face data.
+ * @face_data: Structure containing detected face data information.
+ */
+struct msm_fd_stats {
+ atomic_t frame_id;
+ u32 face_cnt;
+ struct msm_fd_face_data face_data[MSM_FD_MAX_FACES_DETECTED];
+};
+
+/*
+ * struct fd_ctx - Structure contains per open file handle context.
+ * @fd_device: Pointer to fd device.
+ * @fh: V4l2 file handle.
+ * @vb2_q: Videobuf 2 queue.
+ * @sequence: Sequence number for this statistic.
+ * @format: Current format.
+ * @settings: Current settings.
+ * @mem_pool: FD hw memory pool.
+ * @stats: Pointer to statistic buffers.
+ * @work_buf: Working memory buffer handle.
+ */
+struct fd_ctx {
+ struct msm_fd_device *fd_device;
+ struct v4l2_fh fh;
+ struct vb2_queue vb2_q;
+ unsigned int sequence;
+ atomic_t subscribed_for_event;
+ struct msm_fd_format format;
+ struct msm_fd_setings settings;
+ struct msm_fd_mem_pool mem_pool;
+ struct msm_fd_stats *stats;
+ struct msm_fd_buf_handle work_buf;
+ struct mutex lock;
+};
+
+/*
+ * enum msm_fd_device_state - FD device state.
+ * @MSM_FD_DEVICE_IDLE: Device is idle, we can start with processing.
+ * @MSM_FD_DEVICE_RUNNING: Device is running, next processing will be
+ * scheduled from fd irq.
+ */
+enum msm_fd_device_state {
+ MSM_FD_DEVICE_IDLE,
+ MSM_FD_DEVICE_RUNNING,
+};
+
+/*
+ * enum msm_fd_mem_resources - FD device iomem resources.
+ * @MSM_FD_IOMEM_CORE: Index of fd core registers.
+ * @MSM_FD_IOMEM_MISC: Index of fd misc registers.
+ * @MSM_FD_IOMEM_VBIF: Index of fd vbif registers.
+ * @MSM_FD_IOMEM_LAST: Not valid.
+ */
+enum msm_fd_mem_resources {
+ MSM_FD_IOMEM_CORE,
+ MSM_FD_IOMEM_MISC,
+ MSM_FD_IOMEM_VBIF,
+ MSM_FD_IOMEM_LAST
+};
+
+/*
+ * struct msm_fd_device - FD device structure.
+ * @hw_revision: Face detection hw revision.
+ * @lock: Lock used for reference count.
+ * @slock: Spinlock used to protect FD device struct.
+ * @irq_num: Face detection irq number.
+ * @ref_count: Device reference count.
+ * @res_mem: Array of memory resources used by FD device.
+ * @iomem_base: Array of register mappings used by FD device.
+ * @vdd: Pointer to vdd regulator.
+ * @clk_num: Number of clocks attached to the device.
+ * @clk: Array of clock resources used by fd device.
+ * @clk_rates: Array of clock rates set.
+ * @bus_vectors: Pointer to bus vectors array.
+ * @bus_paths: Pointer to bus paths array.
+ * @bus_scale_data: Memory access bus scale data.
+ * @bus_client: Memory access bus client.
+ * @iommu_attached_cnt: Iommu attached devices reference count.
+ * @iommu_hdl: reference for iommu context.
+ * @dev: Pointer to device struct.
+ * @v4l2_dev: V4l2 device.
+ * @video: Video device.
+ * @state: FD device state.
+ * @buf_queue: FD device processing queue.
+ * @work_queue: Pointer to FD device IRQ bottom half workqueue.
+ * @work: IRQ bottom half work struct.
+ * @hw_halt_completion: Completes when face detection hw halt completes.
+ * @recovery_mode: Indicates if FD is in recovery mode
+ */
+struct msm_fd_device {
+ u32 hw_revision;
+
+ struct mutex lock;
+ spinlock_t slock;
+ struct mutex recovery_lock;
+ int ref_count;
+
+ int irq_num;
+ void __iomem *iomem_base[MSM_FD_IOMEM_LAST];
+ struct msm_cam_clk_info *clk_info;
+ struct msm_cam_regulator *vdd_info;
+ int num_reg;
+ struct resource *irq;
+
+ size_t clk_num;
+ size_t clk_rates_num;
+ struct clk **clk;
+ uint32_t **clk_rates;
+ uint32_t bus_client;
+
+ unsigned int iommu_attached_cnt;
+
+ int iommu_hdl;
+ struct device *dev;
+ struct platform_device *pdev;
+ struct v4l2_device v4l2_dev;
+ struct video_device video;
+
+ enum msm_fd_device_state state;
+ struct list_head buf_queue;
+ struct workqueue_struct *work_queue;
+ struct work_struct work;
+ struct completion hw_halt_completion;
+ int recovery_mode;
+ uint32_t clk_rate_idx;
+};
+
+#endif /* __MSM_FD_DEV_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.c
new file mode 100644
index 0000000..2bc453f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.c
@@ -0,0 +1,1342 @@
+/* Copyright (c) 2014-2016, 2018, 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/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/msm_ion.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <media/videobuf2-core.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include "msm_fd_dev.h"
+#include "msm_fd_hw.h"
+#include "msm_fd_regs.h"
+#include "cam_smmu_api.h"
+#include "msm_camera_io_util.h"
+
+/* After which revision misc irq for engine is needed */
+#define MSM_FD_MISC_IRQ_FROM_REV 0x10010000
+/* Face detection workqueue name */
+#define MSM_FD_WORQUEUE_NAME "face-detection"
+/* Face detection bus client name */
+#define MSM_FD_BUS_CLIENT_NAME "msm_face_detect"
+/* Face detection processing timeout in ms */
+#define MSM_FD_PROCESSING_TIMEOUT_MS 150
+/* Face detection halt timeout in ms */
+#define MSM_FD_HALT_TIMEOUT_MS 100
+/* Smmu callback name */
+#define MSM_FD_SMMU_CB_NAME "camera_fd"
+/*
+ * enum msm_fd_reg_setting_entries - FD register setting entries in DT.
+ * @MSM_FD_REG_ADDR_OFFSET_IDX: Register address offset index.
+ * @MSM_FD_REG_VALUE_IDX: Register value index.
+ * @MSM_FD_REG_MASK_IDX: Regester mask index.
+ * @MSM_FD_REG_LAST_IDX: Index count.
+ */
+enum msm_fd_dt_reg_setting_index {
+ MSM_FD_REG_ADDR_OFFSET_IDX,
+ MSM_FD_REG_VALUE_IDX,
+ MSM_FD_REG_MASK_IDX,
+ MSM_FD_REG_LAST_IDX
+};
+
+/*
+ * msm_fd_hw_read_reg - Fd read from register.
+ * @fd: Pointer to fd device.
+ * @base_idx: Fd memory resource index.
+ * @reg: Register addr need to be read from.
+ */
+static inline u32 msm_fd_hw_read_reg(struct msm_fd_device *fd,
+ enum msm_fd_mem_resources base_idx, u32 reg)
+{
+ return msm_camera_io_r(fd->iomem_base[base_idx] + reg);
+}
+
+/*
+ * msm_fd_hw_read_reg - Fd write to register.
+ * @fd: Pointer to fd device.
+ * @base_idx: Fd memory resource index.
+ * @reg: Register addr need to be read from.
+ e @value: Value to be written.
+ */
+static inline void msm_fd_hw_write_reg(struct msm_fd_device *fd,
+ enum msm_fd_mem_resources base_idx, u32 reg, u32 value)
+{
+ msm_camera_io_w(value, fd->iomem_base[base_idx] + reg);
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd clear register bits.
+ * @fd: Pointer to fd device.
+ * @base_idx: Fd memory resource index.
+ * @reg: Register addr need to be read from.
+ * @clr_bits: Bits need to be clear from register.
+ */
+static inline void msm_fd_hw_reg_clr(struct msm_fd_device *fd,
+ enum msm_fd_mem_resources mmio_range, u32 reg, u32 clr_bits)
+{
+ u32 bits = msm_fd_hw_read_reg(fd, mmio_range, reg);
+
+ msm_fd_hw_write_reg(fd, mmio_range, reg, (bits & ~clr_bits));
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd set register bits.
+ * @fd: Pointer to fd device.
+ * @base_idx: Fd memory resource index.
+ * @reg: Register addr need to be read from.
+ * @set_bits: Bits need to be set to register.
+ */
+static inline void msm_fd_hw_reg_set(struct msm_fd_device *fd,
+ enum msm_fd_mem_resources mmio_range, u32 reg, u32 set_bits)
+{
+ u32 bits = msm_fd_hw_read_reg(fd, mmio_range, reg);
+
+ msm_fd_hw_write_reg(fd, mmio_range, reg, (bits | set_bits));
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd set size mode register.
+ * @fd: Pointer to fd device.
+ * @mode: Size mode to be set.
+ */
+static inline void msm_fd_hw_set_size_mode(struct msm_fd_device *fd, u32 mode)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_IMAGE_SIZE, mode);
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd set crop registers.
+ * @fd: Pointer to fd device.
+ * @crop: Pointer to v4l2 crop struct containing the crop information
+ */
+static inline void msm_fd_hw_set_crop(struct msm_fd_device *fd,
+ struct v4l2_rect *crop)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_START_X,
+ (crop->top & MSM_FD_START_X_MASK));
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_START_Y,
+ (crop->left & MSM_FD_START_Y_MASK));
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_SIZE_X,
+ (crop->width & MSM_FD_SIZE_X_MASK));
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_SIZE_Y,
+ (crop->height & MSM_FD_SIZE_Y_MASK));
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd set bytes per line register.
+ * @fd: Pointer to fd device.
+ * @b: Bytes per line need to be set.
+ */
+static inline void msm_fd_hw_set_bytesperline(struct msm_fd_device *fd, u32 b)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_LINE_BYTES,
+ (b & MSM_FD_LINE_BYTES_MASK));
+}
+
+/*
+ * msm_fd_hw_reg_clr - Fd set image address.
+ * @fd: Pointer to fd device.
+ * @addr: Input image address need to be set.
+ */
+static inline void msm_fd_hw_set_image_addr(struct msm_fd_device *fd, u32 addr)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_IMAGE_ADDR, addr);
+}
+
+/*
+ * msm_fd_hw_set_work_addr - Fd set working buffer address.
+ * @fd: Pointer to fd device.
+ * @addr: Working buffer address need to be set.
+ */
+static inline void msm_fd_hw_set_work_addr(struct msm_fd_device *fd, u32 addr)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_WORK_ADDR, addr);
+}
+
+/*
+ * msm_fd_hw_set_direction_angle - Fd set face direction and face angle.
+ * @fd: Pointer to fd device.
+ * @direction: Face direction need to be set.
+ * @angle: Face angle need to be set.
+ */
+static inline void msm_fd_hw_set_direction_angle(struct msm_fd_device *fd,
+ u32 direction, u32 angle)
+{
+ u32 reg;
+ u32 value;
+
+ value = direction | (angle ? 1 << (angle + 1) : 0);
+ if (value > MSM_FD_CONDT_DIR_MAX)
+ value = MSM_FD_CONDT_DIR_MAX;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONDT);
+
+ reg &= ~MSM_FD_CONDT_DIR_MASK;
+ reg |= (value << MSM_FD_CONDT_DIR_SHIFT);
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONDT, reg);
+}
+
+/*
+ * msm_fd_hw_set_min_face - Fd set minimum face size register.
+ * @fd: Pointer to fd device.
+ * @size: Minimum face size need to be set.
+ */
+static inline void msm_fd_hw_set_min_face(struct msm_fd_device *fd, u32 size)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONDT);
+
+ reg &= ~MSM_FD_CONDT_MIN_MASK;
+ reg |= (size << MSM_FD_CONDT_MIN_SHIFT);
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONDT, reg);
+}
+
+/*
+ * msm_fd_hw_set_threshold - Fd set detection threshold register.
+ * @fd: Pointer to fd device.
+ * @c: Maximum face count need to be set.
+ */
+static inline void msm_fd_hw_set_threshold(struct msm_fd_device *fd, u32 thr)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_DHINT,
+ (thr & MSM_FD_DHINT_MASK));
+}
+
+/*
+ * msm_fd_hw_srst - Sw reset control registers.
+ * @fd: Pointer to fd device.
+ *
+ * Before every processing we need to toggle this bit,
+ * This functions set sw reset control bit to 1/0.
+ */
+static inline void msm_fd_hw_srst(struct msm_fd_device *fd)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL,
+ MSM_FD_CONTROL_SRST);
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL, 0);
+}
+
+/*
+ * msm_fd_hw_get_face_count - Fd read face count register.
+ * @fd: Pointer to fd device.
+ */
+int msm_fd_hw_get_face_count(struct msm_fd_device *fd)
+{
+ u32 reg;
+ u32 value;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_RESULT_CNT);
+
+ value = reg & MSM_FD_RESULT_CNT_MASK;
+ if (value > MSM_FD_MAX_FACES_DETECTED) {
+ dev_warn(fd->dev, "Face count %d out of limit\n", value);
+ value = MSM_FD_MAX_FACES_DETECTED;
+ }
+
+ return value;
+}
+
+/*
+ * msm_fd_hw_run - Starts face detection engine.
+ * @fd: Pointer to fd device.
+ *
+ * Before call this function make sure that control sw reset is perfomed
+ * (see function msm_fd_hw_srst).
+ * NOTE: Engine need to be reset before started again.
+ */
+static inline void msm_fd_hw_run(struct msm_fd_device *fd)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL,
+ MSM_FD_CONTROL_RUN);
+}
+
+/*
+ * msm_fd_hw_is_finished - Check if fd hw engine is done with processing.
+ * @fd: Pointer to fd device.
+ *
+ * NOTE: If finish bit is not set, we should not read the result.
+ */
+static int msm_fd_hw_is_finished(struct msm_fd_device *fd)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL);
+
+ return reg & MSM_FD_CONTROL_FINISH;
+}
+
+/*
+ * msm_fd_hw_is_runnig - Check if fd hw engine is busy.
+ * @fd: Pointer to fd device.
+ */
+static int msm_fd_hw_is_runnig(struct msm_fd_device *fd)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL);
+
+ return reg & MSM_FD_CONTROL_RUN;
+}
+
+/*
+ * msm_fd_hw_misc_irq_is_core - Check if fd received misc core irq.
+ * @fd: Pointer to fd device.
+ */
+static int msm_fd_hw_misc_irq_is_core(struct msm_fd_device *fd)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_MISC,
+ MSM_FD_MISC_IRQ_STATUS);
+
+ return reg & MSM_FD_MISC_IRQ_STATUS_CORE_IRQ;
+}
+
+/*
+ * msm_fd_hw_misc_irq_is_halt - Check if fd received misc halt irq.
+ * @fd: Pointer to fd device.
+ */
+static int msm_fd_hw_misc_irq_is_halt(struct msm_fd_device *fd)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_MISC,
+ MSM_FD_MISC_IRQ_STATUS);
+
+ return reg & MSM_FD_MISC_IRQ_STATUS_HALT_REQ;
+}
+
+/*
+ * msm_fd_hw_misc_clear_all_irq - Clear all misc irq statuses.
+ * @fd: Pointer to fd device.
+ */
+static void msm_fd_hw_misc_clear_all_irq(struct msm_fd_device *fd)
+{
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC, MSM_FD_MISC_IRQ_CLEAR,
+ MSM_FD_MISC_IRQ_CLEAR_HALT | MSM_FD_MISC_IRQ_CLEAR_CORE);
+}
+
+/*
+ * msm_fd_hw_misc_irq_enable - Enable fd misc core and halt irq.
+ * @fd: Pointer to fd device.
+ */
+static void msm_fd_hw_misc_irq_enable(struct msm_fd_device *fd)
+{
+ msm_fd_hw_reg_set(fd, MSM_FD_IOMEM_MISC, MSM_FD_MISC_IRQ_MASK,
+ MSM_FD_MISC_IRQ_CLEAR_HALT | MSM_FD_MISC_IRQ_CLEAR_CORE);
+}
+
+/*
+ * msm_fd_hw_misc_irq_disable - Disable fd misc core and halt irq.
+ * @fd: Pointer to fd device.
+ */
+static void msm_fd_hw_misc_irq_disable(struct msm_fd_device *fd)
+{
+ msm_fd_hw_reg_clr(fd, MSM_FD_IOMEM_MISC, MSM_FD_MISC_IRQ_MASK,
+ MSM_FD_MISC_IRQ_CLEAR_HALT | MSM_FD_MISC_IRQ_CLEAR_CORE);
+}
+
+/*
+ * msm_fd_hw_get_revision - Get hw revision and store in to device.
+ * @fd: Pointer to fd device.
+ */
+int msm_fd_hw_get_revision(struct msm_fd_device *fd)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_MISC,
+ MSM_FD_MISC_HW_VERSION);
+
+ dev_dbg(fd->dev, "Face detection hw revision 0x%x\n", reg);
+
+ return reg;
+}
+
+/*
+ * msm_fd_hw_get_result_x - Get fd result center x coordinate.
+ * @fd: Pointer to fd device.
+ * @idx: Result face index
+ */
+int msm_fd_hw_get_result_x(struct msm_fd_device *fd, int idx)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE,
+ MSM_FD_RESULT_CENTER_X(idx));
+
+ return reg;
+}
+
+/*
+ * msm_fd_hw_get_result_y - Get fd result center y coordinate.
+ * @fd: Pointer to fd device.
+ * @idx: Result face index
+ */
+int msm_fd_hw_get_result_y(struct msm_fd_device *fd, int idx)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE,
+ MSM_FD_RESULT_CENTER_Y(idx));
+
+ return reg;
+}
+
+/*
+ * msm_fd_hw_get_result_conf_size - Get fd result confident level and size.
+ * @fd: Pointer to fd device.
+ * @idx: Result face index.
+ * @conf: Pointer to confident value need to be filled.
+ * @size: Pointer to size value need to be filled.
+ */
+void msm_fd_hw_get_result_conf_size(struct msm_fd_device *fd,
+ int idx, u32 *conf, u32 *size)
+{
+ u32 reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE,
+ MSM_FD_RESULT_CONF_SIZE(idx));
+
+ *conf = (reg >> MSM_FD_RESULT_CONF_SHIFT) & MSM_FD_RESULT_CONF_MASK;
+ *size = (reg >> MSM_FD_RESULT_SIZE_SHIFT) & MSM_FD_RESULT_SIZE_MASK;
+}
+
+/*
+ * msm_fd_hw_get_result_angle_pose - Get fd result angle and pose.
+ * @fd: Pointer to fd device.
+ * @idx: Result face index.
+ * @angle: Pointer to angle value need to be filled.
+ * @pose: Pointer to pose value need to be filled.
+ */
+void msm_fd_hw_get_result_angle_pose(struct msm_fd_device *fd, int idx,
+ u32 *angle, u32 *pose)
+{
+ u32 reg;
+ u32 pose_reg;
+
+ reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_CORE,
+ MSM_FD_RESULT_ANGLE_POSE(idx));
+ *angle = (reg >> MSM_FD_RESULT_ANGLE_SHIFT) & MSM_FD_RESULT_ANGLE_MASK;
+ pose_reg = (reg >> MSM_FD_RESULT_POSE_SHIFT) & MSM_FD_RESULT_POSE_MASK;
+
+ switch (pose_reg) {
+ case MSM_FD_RESULT_POSE_FRONT:
+ *pose = MSM_FD_POSE_FRONT;
+ break;
+ case MSM_FD_RESULT_POSE_RIGHT_DIAGONAL:
+ *pose = MSM_FD_POSE_RIGHT_DIAGONAL;
+ break;
+ case MSM_FD_RESULT_POSE_RIGHT:
+ *pose = MSM_FD_POSE_RIGHT;
+ break;
+ case MSM_FD_RESULT_POSE_LEFT_DIAGONAL:
+ *pose = MSM_FD_POSE_LEFT_DIAGONAL;
+ break;
+ case MSM_FD_RESULT_POSE_LEFT:
+ *pose = MSM_FD_POSE_LEFT;
+ break;
+ default:
+ dev_err(fd->dev, "Invalid pose from the engine\n");
+ *pose = MSM_FD_POSE_FRONT;
+ break;
+ }
+}
+
+/*
+ * msm_fd_hw_misc_irq_supported - Check if misc irq is supported.
+ * @fd: Pointer to fd device.
+ */
+static int msm_fd_hw_misc_irq_supported(struct msm_fd_device *fd)
+{
+ return fd->hw_revision >= MSM_FD_MISC_IRQ_FROM_REV;
+}
+
+/*
+ * msm_fd_hw_halt - Halt fd core.
+ * @fd: Pointer to fd device.
+ */
+static void msm_fd_hw_halt(struct msm_fd_device *fd)
+{
+ unsigned long time;
+
+ if (msm_fd_hw_misc_irq_supported(fd)) {
+ init_completion(&fd->hw_halt_completion);
+
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC, MSM_FD_HW_STOP, 1);
+
+ time = wait_for_completion_timeout(&fd->hw_halt_completion,
+ msecs_to_jiffies(MSM_FD_HALT_TIMEOUT_MS));
+ if (!time)
+ dev_err(fd->dev, "Face detection halt timeout\n");
+
+ /* Reset sequence after halt */
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC, MSM_FD_MISC_SW_RESET,
+ MSM_FD_MISC_SW_RESET_SET);
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL,
+ MSM_FD_CONTROL_SRST);
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC,
+ MSM_FD_MISC_SW_RESET, 0);
+ msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_CORE, MSM_FD_CONTROL, 0);
+ }
+}
+
+/*
+ * msm_fd_core_irq - Face detection core irq handler.
+ * @irq: Irq number.
+ * @dev_id: Pointer to fd device.
+ */
+static irqreturn_t msm_fd_hw_core_irq(int irq, void *dev_id)
+{
+ struct msm_fd_device *fd = dev_id;
+
+ if (msm_fd_hw_is_finished(fd))
+ queue_work(fd->work_queue, &fd->work);
+ else
+ dev_err(fd->dev, "Something wrong! FD still running\n");
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * msm_fd_hw_misc_irq - Face detection misc irq handler.
+ * @irq: Irq number.
+ * @dev_id: Pointer to fd device.
+ */
+static irqreturn_t msm_fd_hw_misc_irq(int irq, void *dev_id)
+{
+ struct msm_fd_device *fd = dev_id;
+
+ if (msm_fd_hw_misc_irq_is_core(fd))
+ msm_fd_hw_core_irq(irq, dev_id);
+
+ if (msm_fd_hw_misc_irq_is_halt(fd))
+ complete_all(&fd->hw_halt_completion);
+
+ msm_fd_hw_misc_clear_all_irq(fd);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * msm_fd_hw_request_irq - Configure and enable vbif interface.
+ * @pdev: Pointer to platform device.
+ * @fd: Pointer to fd device.
+ * @work_func: Pointer to work func used for irq bottom half.
+ */
+int msm_fd_hw_request_irq(struct platform_device *pdev,
+ struct msm_fd_device *fd, work_func_t work_func)
+{
+ int ret;
+
+ fd->irq = msm_camera_get_irq(pdev, "fd");
+ if (fd->irq_num < 0) {
+ dev_err(fd->dev, "Can not get fd core irq resource\n");
+ ret = -ENODEV;
+ goto error_irq;
+ }
+
+ /* If vbif is shared we will need wrapper irq for releasing vbif */
+ if (msm_fd_hw_misc_irq_supported(fd)) {
+ ret = msm_camera_register_irq(pdev,
+ fd->irq, msm_fd_hw_misc_irq,
+ IRQF_TRIGGER_RISING, "fd", fd);
+ if (ret) {
+ dev_err(fd->dev, "Can not claim wrapper IRQ\n");
+ goto error_irq;
+ }
+ } else {
+ ret = msm_camera_register_irq(pdev,
+ fd->irq, msm_fd_hw_core_irq,
+ IRQF_TRIGGER_RISING, "fd", fd);
+ if (ret) {
+ dev_err(&pdev->dev, "Can not claim core IRQ\n");
+ goto error_irq;
+ }
+
+ }
+
+ fd->work_queue = alloc_workqueue(MSM_FD_WORQUEUE_NAME,
+ WQ_HIGHPRI | WQ_UNBOUND, 0);
+ if (!fd->work_queue) {
+ dev_err(fd->dev, "Can not register workqueue\n");
+ ret = -ENOMEM;
+ goto error_alloc_workqueue;
+ }
+ INIT_WORK(&fd->work, work_func);
+
+ return 0;
+
+error_alloc_workqueue:
+ msm_camera_unregister_irq(pdev, fd->irq, fd);
+error_irq:
+ return ret;
+}
+
+/*
+ * msm_fd_hw_release_irq - Free core and wrap irq.
+ * @fd: Pointer to fd device.
+ */
+void msm_fd_hw_release_irq(struct msm_fd_device *fd)
+{
+ if (fd->irq)
+ msm_camera_unregister_irq(fd->pdev, fd->irq, fd);
+
+ if (fd->work_queue) {
+ destroy_workqueue(fd->work_queue);
+ fd->work_queue = NULL;
+ }
+}
+
+/*
+ * msm_fd_hw_set_dt_parms_by_name() - read DT params and write to registers.
+ * @fd: Pointer to fd device.
+ * @dt_prop_name: Name of the device tree property to read.
+ * @base_idx: Fd memory resource index.
+ *
+ * This function reads register offset and value pairs from dtsi based on
+ * device tree property name and writes to FD registers.
+ *
+ * Return: 0 on success and negative error on failure.
+ */
+static int32_t msm_fd_hw_set_dt_parms_by_name(struct msm_fd_device *fd,
+ const char *dt_prop_name,
+ enum msm_fd_mem_resources base_idx)
+{
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *dt_reg_settings = NULL;
+ uint32_t dt_count = 0;
+
+ of_node = fd->dev->of_node;
+ pr_debug("%s:%d E\n", __func__, __LINE__);
+
+ if (!of_get_property(of_node, dt_prop_name, &dt_count)) {
+ pr_err("%s: Error property does not exist\n", __func__);
+ return -ENOENT;
+ }
+ if (dt_count % (sizeof(int32_t) * MSM_FD_REG_LAST_IDX)) {
+ pr_err("%s: Error invalid entries\n", __func__);
+ return -EINVAL;
+ }
+ dt_count /= sizeof(int32_t);
+ if (dt_count != 0) {
+ dt_reg_settings = kcalloc(dt_count,
+ sizeof(uint32_t),
+ GFP_KERNEL);
+
+ if (!dt_reg_settings)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(of_node,
+ dt_prop_name,
+ dt_reg_settings,
+ dt_count);
+ if (rc < 0) {
+ pr_err("%s: No reg info\n", __func__);
+ kfree(dt_reg_settings);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < dt_count; i = i + MSM_FD_REG_LAST_IDX) {
+ msm_fd_hw_reg_clr(fd, base_idx,
+ dt_reg_settings[i + MSM_FD_REG_ADDR_OFFSET_IDX],
+ dt_reg_settings[i + MSM_FD_REG_MASK_IDX]);
+ msm_fd_hw_reg_set(fd, base_idx,
+ dt_reg_settings[i + MSM_FD_REG_ADDR_OFFSET_IDX],
+ dt_reg_settings[i + MSM_FD_REG_VALUE_IDX] &
+ dt_reg_settings[i + MSM_FD_REG_MASK_IDX]);
+ pr_debug("%s:%d] %pK %08x\n", __func__, __LINE__,
+ fd->iomem_base[base_idx] +
+ dt_reg_settings[i + MSM_FD_REG_ADDR_OFFSET_IDX],
+ dt_reg_settings[i + MSM_FD_REG_VALUE_IDX] &
+ dt_reg_settings[i + MSM_FD_REG_MASK_IDX]);
+ }
+ kfree(dt_reg_settings);
+ }
+ return 0;
+}
+
+/*
+ * msm_fd_hw_set_dt_parms() - set FD device tree configuration.
+ * @fd: Pointer to fd device.
+ *
+ * This function holds an array of device tree property names and calls
+ * msm_fd_hw_set_dt_parms_by_name() for each property.
+ *
+ * Return: 0 on success and negative error on failure.
+ */
+static int msm_fd_hw_set_dt_parms(struct msm_fd_device *fd)
+{
+ int rc = 0;
+ uint8_t dt_prop_cnt = MSM_FD_IOMEM_LAST;
+ char *dt_prop_name[MSM_FD_IOMEM_LAST] = {"qcom,fd-core-reg-settings",
+ "qcom,fd-misc-reg-settings", "qcom,fd-vbif-reg-settings"};
+
+ while (dt_prop_cnt) {
+ dt_prop_cnt--;
+ rc = msm_fd_hw_set_dt_parms_by_name(fd,
+ dt_prop_name[dt_prop_cnt],
+ dt_prop_cnt);
+ if (rc == -ENOENT) {
+ pr_debug("%s: No %s property\n", __func__,
+ dt_prop_name[dt_prop_cnt]);
+ rc = 0;
+ } else if (rc < 0) {
+ pr_err("%s: %s params set fail\n", __func__,
+ dt_prop_name[dt_prop_cnt]);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+/*
+ * msm_fd_hw_release_mem_resources - Releases memory resources.
+ * @fd: Pointer to fd device.
+ */
+void msm_fd_hw_release_mem_resources(struct msm_fd_device *fd)
+{
+ msm_camera_put_reg_base(fd->pdev,
+ fd->iomem_base[MSM_FD_IOMEM_MISC], "fd_misc", true);
+ msm_camera_put_reg_base(fd->pdev,
+ fd->iomem_base[MSM_FD_IOMEM_CORE], "fd_core", true);
+ msm_camera_put_reg_base(fd->pdev,
+ fd->iomem_base[MSM_FD_IOMEM_VBIF], "fd_vbif", false);
+}
+
+/*
+ * msm_fd_hw_get_mem_resources - Get memory resources.
+ * @pdev: Pointer to fd platform device.
+ * @fd: Pointer to fd device.
+ *
+ * Get and ioremap platform memory resources.
+ */
+int msm_fd_hw_get_mem_resources(struct platform_device *pdev,
+ struct msm_fd_device *fd)
+{
+ int ret = 0;
+
+ /* Prepare memory resources */
+ fd->iomem_base[MSM_FD_IOMEM_CORE] =
+ msm_camera_get_reg_base(pdev, "fd_core", true);
+ if (!fd->iomem_base[MSM_FD_IOMEM_CORE]) {
+ dev_err(fd->dev, "%s can not map fd_core region\n", __func__);
+ ret = -ENODEV;
+ goto fd_core_base_failed;
+ }
+
+ fd->iomem_base[MSM_FD_IOMEM_MISC] =
+ msm_camera_get_reg_base(pdev, "fd_misc", true);
+ if (!fd->iomem_base[MSM_FD_IOMEM_MISC]) {
+ dev_err(fd->dev, "%s can not map fd_misc region\n", __func__);
+ ret = -ENODEV;
+ goto fd_misc_base_failed;
+ }
+
+ fd->iomem_base[MSM_FD_IOMEM_VBIF] =
+ msm_camera_get_reg_base(pdev, "fd_vbif", false);
+ if (!fd->iomem_base[MSM_FD_IOMEM_VBIF]) {
+ dev_err(fd->dev, "%s can not map fd_vbif region\n", __func__);
+ ret = -ENODEV;
+ goto fd_vbif_base_failed;
+ }
+
+ return ret;
+fd_vbif_base_failed:
+ msm_camera_put_reg_base(pdev,
+ fd->iomem_base[MSM_FD_IOMEM_MISC], "fd_misc", true);
+fd_misc_base_failed:
+ msm_camera_put_reg_base(pdev,
+ fd->iomem_base[MSM_FD_IOMEM_CORE], "fd_core", true);
+fd_core_base_failed:
+ return ret;
+}
+
+/*
+ * msm_fd_hw_bus_request - Request bus for memory access.
+ * @fd: Pointer to fd device.
+ * @idx: Bus bandwidth array index described in device tree.
+ */
+static int msm_fd_hw_bus_request(struct msm_fd_device *fd, unsigned int idx)
+{
+ int ret;
+
+ ret = msm_camera_update_bus_vector(CAM_BUS_CLIENT_FD, idx);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail bus scale update %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_fd_hw_set_clock_rate_idx - Set clock rate based on the index.
+ * @fd: Pointer to fd device.
+ * @idx: Clock Array index described in device tree.
+ */
+static int msm_fd_hw_set_clock_rate_idx(struct msm_fd_device *fd,
+ unsigned int idx)
+{
+ int ret;
+ int i;
+
+ if (idx >= fd->clk_rates_num) {
+ dev_err(fd->dev, "Invalid clock index %u\n", idx);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < fd->clk_num; i++) {
+ ret = msm_camera_clk_set_rate(&fd->pdev->dev,
+ fd->clk[i], fd->clk_rates[idx][i]);
+ if (ret < 0) {
+ dev_err(fd->dev, "fail set rate on idx[%u][%u]\n",
+ idx, i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * msm_fd_hw_update_settings() - API to set clock rate and bus settings
+ * @fd: Pointer to fd device.
+ * @buf: fd buffer
+ */
+static int msm_fd_hw_update_settings(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buf)
+{
+ int ret = 0;
+ uint32_t clk_rate_idx;
+
+ if (!buf)
+ return 0;
+
+ clk_rate_idx = buf->settings.speed;
+ if (fd->clk_rate_idx == clk_rate_idx)
+ return 0;
+
+ if (fd->bus_client) {
+ ret = msm_fd_hw_bus_request(fd, clk_rate_idx);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail bus scale update %d\n", ret);
+ return -EINVAL;
+ }
+ }
+
+ ret = msm_fd_hw_set_clock_rate_idx(fd, clk_rate_idx);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail to set clock rate idx\n");
+ goto end;
+ }
+ dev_dbg(fd->dev, "set clk %d %d", fd->clk_rate_idx, clk_rate_idx);
+ fd->clk_rate_idx = clk_rate_idx;
+
+end:
+ return ret;
+}
+
+/*
+ * msm_fd_hw_get - Get fd hw for performing any hw operation.
+ * @fd: Pointer to fd device.
+ * @clock_rate_idx: Clock rate index.
+ *
+ * Prepare fd hw for operation. Have reference count protected by
+ * fd device mutex.
+ */
+int msm_fd_hw_get(struct msm_fd_device *fd, unsigned int clock_rate_idx)
+{
+ int ret;
+
+ mutex_lock(&fd->lock);
+
+ if (fd->ref_count == 0) {
+ ret =
+ msm_camera_regulator_enable(fd->vdd_info,
+ fd->num_reg, true);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail to enable vdd\n");
+ goto error;
+ }
+
+ ret = msm_fd_hw_bus_request(fd, clock_rate_idx);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail bus request\n");
+ goto error_bus_request;
+ }
+ ret = msm_fd_hw_set_clock_rate_idx(fd, clock_rate_idx);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail to set clock rate idx\n");
+ goto error_clocks;
+ }
+ ret = msm_camera_clk_enable(&fd->pdev->dev, fd->clk_info,
+ fd->clk, fd->clk_num, true);
+ if (ret < 0) {
+ dev_err(fd->dev, "Fail clk enable request\n");
+ goto error_clocks;
+ }
+
+ if (msm_fd_hw_misc_irq_supported(fd))
+ msm_fd_hw_misc_irq_enable(fd);
+
+ ret = msm_fd_hw_set_dt_parms(fd);
+ if (ret < 0)
+ goto error_set_dt;
+
+ fd->clk_rate_idx = clock_rate_idx;
+ }
+
+ fd->ref_count++;
+ mutex_unlock(&fd->lock);
+
+ return 0;
+
+error_set_dt:
+ if (msm_fd_hw_misc_irq_supported(fd))
+ msm_fd_hw_misc_irq_disable(fd);
+ msm_camera_clk_enable(&fd->pdev->dev, fd->clk_info,
+ fd->clk, fd->clk_num, false);
+error_clocks:
+error_bus_request:
+ msm_camera_regulator_enable(fd->vdd_info, fd->num_reg, false);
+error:
+ mutex_unlock(&fd->lock);
+ return ret;
+}
+
+/*
+ * msm_fd_hw_get - Put fd hw.
+ * @fd: Pointer to fd device.
+ *
+ * Release fd hw. Have reference count protected by
+ * fd device mutex.
+ */
+void msm_fd_hw_put(struct msm_fd_device *fd)
+{
+ mutex_lock(&fd->lock);
+ if (WARN_ON(fd->ref_count == 0))
+ goto err;
+
+ if (--fd->ref_count == 0) {
+ msm_fd_hw_halt(fd);
+
+ if (msm_fd_hw_misc_irq_supported(fd))
+ msm_fd_hw_misc_irq_disable(fd);
+
+ /* vector index 0 is 0 ab and 0 ib */
+ msm_fd_hw_bus_request(fd, 0);
+ msm_camera_clk_enable(&fd->pdev->dev, fd->clk_info,
+ fd->clk, fd->clk_num, false);
+ msm_camera_regulator_enable(fd->vdd_info, fd->num_reg, false);
+ }
+err:
+ mutex_unlock(&fd->lock);
+}
+
+/*
+ * msm_fd_hw_attach_iommu - Attach iommu to face detection engine.
+ * @fd: Pointer to fd device.
+ *
+ * Iommu attach have reference count protected by
+ * fd device mutex.
+ */
+static int msm_fd_hw_attach_iommu(struct msm_fd_device *fd)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&fd->lock);
+
+ if (fd->iommu_attached_cnt == UINT_MAX) {
+ dev_err(fd->dev, "Max count reached! can not attach iommu\n");
+ goto error;
+ }
+
+ if (fd->iommu_attached_cnt == 0) {
+ ret = cam_smmu_get_handle(MSM_FD_SMMU_CB_NAME, &fd->iommu_hdl);
+ if (ret < 0) {
+ dev_err(fd->dev, "get handle failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+ ret = cam_smmu_ops(fd->iommu_hdl, CAM_SMMU_ATTACH);
+ if (ret < 0) {
+ dev_err(fd->dev, "Can not attach iommu domain.\n");
+ goto error_attach;
+ }
+ }
+ fd->iommu_attached_cnt++;
+ mutex_unlock(&fd->lock);
+
+ return 0;
+
+error_attach:
+ cam_smmu_destroy_handle(fd->iommu_hdl);
+error:
+ mutex_unlock(&fd->lock);
+ return ret;
+}
+
+/*
+ * msm_fd_hw_detach_iommu - Detach iommu from face detection engine.
+ * @fd: Pointer to fd device.
+ *
+ * Iommu detach have reference count protected by
+ * fd device mutex.
+ */
+static void msm_fd_hw_detach_iommu(struct msm_fd_device *fd)
+{
+ mutex_lock(&fd->lock);
+ if (fd->iommu_attached_cnt == 0) {
+ dev_err(fd->dev, "There is no attached device\n");
+ mutex_unlock(&fd->lock);
+ return;
+ }
+ if (--fd->iommu_attached_cnt == 0) {
+ cam_smmu_ops(fd->iommu_hdl, CAM_SMMU_DETACH);
+ cam_smmu_destroy_handle(fd->iommu_hdl);
+ }
+ mutex_unlock(&fd->lock);
+}
+
+/*
+ * msm_fd_hw_map_buffer - Map buffer to fd hw mmu.
+ * @pool: Pointer to fd memory pool.
+ * @fd: Ion fd.
+ * @buf: Fd buffer handle, for storing mapped buffer information.
+ *
+ * It will map ion fd to fd hw mmu.
+ */
+int msm_fd_hw_map_buffer(struct msm_fd_mem_pool *pool, int fd,
+ struct msm_fd_buf_handle *buf)
+{
+ int ret;
+
+ if (!pool || fd < 0)
+ return -EINVAL;
+
+ ret = msm_fd_hw_attach_iommu(pool->fd_device);
+ if (ret < 0)
+ return -ENOMEM;
+
+ buf->pool = pool;
+ buf->fd = fd;
+ ret = cam_smmu_get_phy_addr(pool->fd_device->iommu_hdl,
+ buf->fd, CAM_SMMU_MAP_RW,
+ &buf->addr, &buf->size);
+ if (ret < 0) {
+ pr_err("Error: cannot get phy addr\n");
+ return -ENOMEM;
+ }
+ return buf->size;
+}
+
+/*
+ * msm_fd_hw_unmap_buffer - Unmap buffer from fd hw mmu.
+ * @buf: Fd buffer handle, for storing mapped buffer information.
+ */
+void msm_fd_hw_unmap_buffer(struct msm_fd_buf_handle *buf)
+{
+ if (buf->size) {
+ cam_smmu_put_phy_addr(buf->pool->fd_device->iommu_hdl,
+ buf->fd);
+ msm_fd_hw_detach_iommu(buf->pool->fd_device);
+ }
+
+ buf->fd = -1;
+ buf->pool = NULL;
+}
+
+/*
+ * msm_fd_hw_enable - Configure and enable fd hw.
+ * @fd: Fd device.
+ * @buffer: Buffer need to be processed.
+ *
+ * Configure and starts fd processing with given buffer.
+ * NOTE: Fd will not be enabled if engine is in running state.
+ */
+static int msm_fd_hw_enable(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer)
+{
+ struct msm_fd_buf_handle *buf_handle =
+ buffer->vb_v4l2_buf.vb2_buf.planes[0].mem_priv;
+
+ if (msm_fd_hw_is_runnig(fd)) {
+ dev_err(fd->dev, "Device is busy we can not enable\n");
+ return 0;
+ }
+
+ msm_fd_hw_srst(fd);
+ msm_fd_hw_set_size_mode(fd, buffer->format.size->reg_val);
+ msm_fd_hw_set_crop(fd, &buffer->format.crop);
+ msm_fd_hw_set_bytesperline(fd, buffer->format.bytesperline);
+ msm_fd_hw_set_image_addr(fd, buf_handle->addr);
+ msm_fd_hw_set_work_addr(fd, buffer->work_addr);
+ msm_fd_hw_set_min_face(fd, buffer->settings.min_size_index);
+ msm_fd_hw_set_threshold(fd, buffer->settings.threshold);
+ msm_fd_hw_set_direction_angle(fd, buffer->settings.direction_index,
+ buffer->settings.angle_index);
+ msm_fd_hw_run(fd);
+ if (fd->recovery_mode)
+ dev_err(fd->dev, "Scheduled buffer in recovery mode\n");
+ return 1;
+}
+
+/*
+ * msm_fd_hw_try_enable - Try to enable fd hw.
+ * @fd: Fd device.
+ * @buffer: Buffer need to be processed.
+ * @state: Enable on device state
+ *
+ * It will enable fd hw if actual device state is equal with state argument.
+ */
+static int msm_fd_hw_try_enable(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer, enum msm_fd_device_state state)
+{
+ int enabled = 0;
+
+ if (state == fd->state) {
+ fd->state = MSM_FD_DEVICE_RUNNING;
+ atomic_set(&buffer->active, 1);
+
+ msm_fd_hw_enable(fd, buffer);
+ enabled = 1;
+ }
+ return enabled;
+}
+
+/*
+ * msm_fd_hw_next_buffer - Get next buffer from fd device processing queue.
+ * @fd: Fd device.
+ */
+struct msm_fd_buffer *msm_fd_hw_get_next_buffer(struct msm_fd_device *fd)
+{
+ struct msm_fd_buffer *buffer = NULL;
+
+ if (!list_empty(&fd->buf_queue))
+ buffer = list_first_entry(&fd->buf_queue,
+ struct msm_fd_buffer, list);
+
+ return buffer;
+}
+
+/*
+ * msm_fd_hw_add_buffer - Add buffer to fd device processing queue.
+ * @fd: Fd device.
+ */
+void msm_fd_hw_add_buffer(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer)
+{
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+
+ atomic_set(&buffer->active, 0);
+ init_completion(&buffer->completion);
+
+ INIT_LIST_HEAD(&buffer->list);
+ list_add_tail(&buffer->list, &fd->buf_queue);
+
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+}
+
+/*
+ * msm_fd_hw_remove_buffers_from_queue - Removes buffer from
+ * fd device processing queue.
+ * @fd: Fd device.
+ */
+void msm_fd_hw_remove_buffers_from_queue(struct msm_fd_device *fd,
+ struct vb2_queue *vb2_q)
+{
+ struct msm_fd_buffer *curr_buff;
+ struct msm_fd_buffer *temp;
+ struct msm_fd_buffer *active_buffer;
+ unsigned long time;
+
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+ active_buffer = NULL;
+ list_for_each_entry_safe(curr_buff, temp, &fd->buf_queue, list) {
+ if (curr_buff->vb_v4l2_buf.vb2_buf.vb2_queue == vb2_q) {
+
+ if (atomic_read(&curr_buff->active))
+ active_buffer = curr_buff;
+ else {
+ /* Do a Buffer done on all the other buffers */
+ vb2_buffer_done(&curr_buff->vb_v4l2_buf.vb2_buf,
+ VB2_BUF_STATE_DONE);
+ list_del(&curr_buff->list);
+ }
+ }
+ }
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+
+ /* We need to wait active buffer to finish */
+ if (active_buffer) {
+ time = wait_for_completion_timeout(&active_buffer->completion,
+ msecs_to_jiffies(MSM_FD_PROCESSING_TIMEOUT_MS));
+
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+ if (!time) {
+ if (atomic_read(&active_buffer->active)) {
+ atomic_set(&active_buffer->active, 0);
+ /* Do a vb2 buffer done since it timed out */
+ vb2_buffer_done(
+ &active_buffer->vb_v4l2_buf.vb2_buf,
+ VB2_BUF_STATE_DONE);
+ /* Remove active buffer */
+ msm_fd_hw_get_active_buffer(fd, 0);
+ /* Schedule if other buffers are present */
+ msm_fd_hw_schedule_next_buffer(fd, 0);
+ } else {
+ dev_err(fd->dev, "activ buf no longer active\n");
+ }
+ }
+ fd->state = MSM_FD_DEVICE_IDLE;
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+ }
+}
+
+/*
+ * msm_fd_hw_buffer_done - Mark as done and removes from processing queue.
+ * @fd: Fd device.
+ * @buffer: Fd buffer.
+ */
+int msm_fd_hw_buffer_done(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer, u8 lock_flag)
+{
+ int ret = 0;
+
+ if (atomic_read(&buffer->active)) {
+ atomic_set(&buffer->active, 0);
+ complete_all(&buffer->completion);
+ } else {
+ ret = -1;
+ }
+ return ret;
+}
+
+/*
+ * msm_fd_hw_get_active_buffer - Get active buffer from fd processing queue.
+ * @fd: Fd device.
+ */
+struct msm_fd_buffer *msm_fd_hw_get_active_buffer(struct msm_fd_device *fd,
+ u8 lock_flag)
+{
+ struct msm_fd_buffer *buffer = NULL;
+
+ if (!list_empty(&fd->buf_queue)) {
+ buffer = list_first_entry(&fd->buf_queue,
+ struct msm_fd_buffer, list);
+ list_del(&buffer->list);
+ }
+
+ return buffer;
+}
+
+/*
+ * msm_fd_hw_schedule_and_start - Schedule active buffer and start processing.
+ * @fd: Fd device.
+ *
+ * This can be executed only when device is in idle state.
+ */
+int msm_fd_hw_schedule_and_start(struct msm_fd_device *fd)
+{
+ struct msm_fd_buffer *buf;
+
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+
+ buf = msm_fd_hw_get_next_buffer(fd);
+ if (buf)
+ msm_fd_hw_try_enable(fd, buf, MSM_FD_DEVICE_IDLE);
+
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+
+ msm_fd_hw_update_settings(fd, buf);
+
+ return 0;
+}
+
+/*
+ * msm_fd_hw_schedule_next_buffer - Schedule next buffer and start processing.
+ * @fd: Fd device.
+ *
+ * NOTE: This can be executed only when device is in running state.
+ */
+int msm_fd_hw_schedule_next_buffer(struct msm_fd_device *fd, u8 lock_flag)
+{
+ struct msm_fd_buffer *buf;
+ int ret;
+
+ if (lock_flag) {
+ MSM_FD_SPIN_LOCK(fd->slock, 1);
+
+ /* We can schedule next buffer only in running state */
+ if (fd->state != MSM_FD_DEVICE_RUNNING) {
+ dev_err(fd->dev, "Can not schedule next buffer\n");
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+ return -EBUSY;
+ }
+
+ buf = msm_fd_hw_get_next_buffer(fd);
+ if (buf) {
+ ret = msm_fd_hw_try_enable(fd, buf,
+ MSM_FD_DEVICE_RUNNING);
+ if (ret == 0) {
+ dev_err(fd->dev, "Can not process next buffer\n");
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+ return -EBUSY;
+ }
+ } else {
+ fd->state = MSM_FD_DEVICE_IDLE;
+ if (fd->recovery_mode)
+ dev_err(fd->dev, "No Buffer in recovery mode.Device Idle\n");
+ }
+ MSM_FD_SPIN_UNLOCK(fd->slock, 1);
+ } else {
+ /* We can schedule next buffer only in running state */
+ if (fd->state != MSM_FD_DEVICE_RUNNING) {
+ dev_err(fd->dev, "Can not schedule next buffer\n");
+ return -EBUSY;
+ }
+
+ buf = msm_fd_hw_get_next_buffer(fd);
+ if (buf) {
+ ret = msm_fd_hw_try_enable(fd, buf,
+ MSM_FD_DEVICE_RUNNING);
+ if (ret == 0) {
+ dev_err(fd->dev, "Can not process next buffer\n");
+ return -EBUSY;
+ }
+ } else {
+ fd->state = MSM_FD_DEVICE_IDLE;
+ if (fd->recovery_mode)
+ dev_err(fd->dev, "No Buffer in recovery mode.Device Idle\n");
+ }
+ }
+
+ msm_fd_hw_update_settings(fd, buf);
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.h b/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.h
new file mode 100644
index 0000000..84b10f3
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_hw.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2014-2016, 2018, 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_FD_HW_H__
+#define __MSM_FD_HW_H__
+
+#include "msm_fd_dev.h"
+
+int msm_fd_hw_get_face_count(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_result_x(struct msm_fd_device *fd, int idx);
+
+int msm_fd_hw_get_result_y(struct msm_fd_device *fd, int idx);
+
+void msm_fd_hw_get_result_conf_size(struct msm_fd_device *fd,
+ int idx, u32 *conf, u32 *size);
+
+void msm_fd_hw_get_result_angle_pose(struct msm_fd_device *fd, int idx,
+ u32 *angle, u32 *pose);
+
+int msm_fd_hw_request_irq(struct platform_device *pdev,
+ struct msm_fd_device *fd, work_func_t work_func);
+
+void msm_fd_hw_release_irq(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_revision(struct msm_fd_device *fd);
+
+void msm_fd_hw_release_mem_resources(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_mem_resources(struct platform_device *pdev,
+ struct msm_fd_device *fd);
+
+int msm_fd_hw_get_iommu(struct msm_fd_device *fd);
+
+void msm_fd_hw_put_iommu(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_regulators(struct msm_fd_device *fd);
+
+int msm_fd_hw_put_regulators(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_clocks(struct msm_fd_device *fd);
+
+int msm_fd_hw_put_clocks(struct msm_fd_device *fd);
+
+int msm_fd_hw_get_bus(struct msm_fd_device *fd);
+
+void msm_fd_hw_put_bus(struct msm_fd_device *fd);
+
+int msm_fd_hw_get(struct msm_fd_device *fd, unsigned int clock_rate_idx);
+
+void msm_fd_hw_put(struct msm_fd_device *fd);
+
+int msm_fd_hw_map_buffer(struct msm_fd_mem_pool *pool, int fd,
+ struct msm_fd_buf_handle *buf);
+
+void msm_fd_hw_unmap_buffer(struct msm_fd_buf_handle *buf);
+
+void msm_fd_hw_add_buffer(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer);
+
+void msm_fd_hw_remove_buffers_from_queue(struct msm_fd_device *fd,
+ struct vb2_queue *vb2_q);
+
+int msm_fd_hw_buffer_done(struct msm_fd_device *fd,
+ struct msm_fd_buffer *buffer, u8 lock_flag);
+
+struct msm_fd_buffer *msm_fd_hw_get_active_buffer(struct msm_fd_device *fd,
+ u8 lock_flag);
+
+struct msm_fd_buffer *msm_fd_hw_get_next_buffer(struct msm_fd_device *fd);
+
+int msm_fd_hw_schedule_and_start(struct msm_fd_device *fd);
+
+int msm_fd_hw_schedule_next_buffer(struct msm_fd_device *fd, u8 lock_flag);
+
+#endif /* __MSM_FD_HW_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_regs.h b/drivers/media/platform/msm/camera_v2/fd/msm_fd_regs.h
new file mode 100644
index 0000000..beb192b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_regs.h
@@ -0,0 +1,169 @@
+/* Copyright (c) 2014-2015, 2018, 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_FD_REGS_H__
+#define __MSM_FD_REGS_H__
+
+/* FD core registers */
+#define MSM_FD_CONTROL (0x00)
+#define MSM_FD_CONTROL_SRST (1 << 0)
+#define MSM_FD_CONTROL_RUN (1 << 1)
+#define MSM_FD_CONTROL_FINISH (1 << 2)
+
+#define MSM_FD_RESULT_CNT (0x04)
+#define MSM_FD_RESULT_CNT_MASK (0x3F)
+
+#define MSM_FD_CONDT (0x08)
+#define MSM_FD_CONDT_MIN_MASK (0x03)
+#define MSM_FD_CONDT_MIN_SHIFT (0x00)
+#define MSM_FD_CONDT_DIR_MAX (0x08)
+#define MSM_FD_CONDT_DIR_MASK (0x3C)
+#define MSM_FD_CONDT_DIR_SHIFT (0x02)
+
+#define MSM_FD_START_X (0x0C)
+#define MSM_FD_START_X_MASK (0x3FF)
+
+#define MSM_FD_START_Y (0x10)
+#define MSM_FD_START_Y_MASK (0x1FF)
+
+#define MSM_FD_SIZE_X (0x14)
+#define MSM_FD_SIZE_X_MASK (0x3FF)
+
+#define MSM_FD_SIZE_Y (0x18)
+#define MSM_FD_SIZE_Y_MASK (0x1FF)
+
+#define MSM_FD_DHINT (0x1C)
+#define MSM_FD_DHINT_MASK (0xF)
+
+#define MSM_FD_IMAGE_ADDR (0x24)
+#define MSM_FD_IMAGE_ADDR_ALIGN (0x8)
+
+#define MSM_FD_WORK_ADDR (0x28)
+#define MSM_FD_WORK_ADDR_ALIGN (0x8)
+
+#define MSM_FD_IMAGE_SIZE (0x2C)
+#define MSM_FD_IMAGE_SIZE_QVGA (0x0)
+#define MSM_FD_IMAGE_SIZE_VGA (0x1)
+#define MSM_FD_IMAGE_SIZE_WQVGA (0x2)
+#define MSM_FD_IMAGE_SIZE_WVGA (0x3)
+
+#define MSM_FD_LINE_BYTES (0x30)
+#define MSM_FD_LINE_BYTES_MASK (0x1FFF)
+#define MSM_FD_LINE_BYTES_ALIGN (0x8)
+
+#define MSM_FD_RESULT_CENTER_X(x) (0x400 + (0x10 * (x)))
+
+#define MSM_FD_RESULT_CENTER_Y(x) (0x404 + (0x10 * (x)))
+
+#define MSM_FD_RESULT_CONF_SIZE(x) (0x408 + (0x10 * (x)))
+#define MSM_FD_RESULT_SIZE_MASK (0x1FF)
+#define MSM_FD_RESULT_SIZE_SHIFT (0x000)
+#define MSM_FD_RESULT_CONF_MASK (0xF)
+#define MSM_FD_RESULT_CONF_SHIFT (0x9)
+
+#define MSM_FD_RESULT_ANGLE_POSE(x) (0x40C + (0x10 * (x)))
+#define MSM_FD_RESULT_ANGLE_MASK (0x1FF)
+#define MSM_FD_RESULT_ANGLE_SHIFT (0x000)
+#define MSM_FD_RESULT_POSE_MASK (0x7)
+#define MSM_FD_RESULT_POSE_SHIFT (0x9)
+#define MSM_FD_RESULT_POSE_FRONT (0x1)
+#define MSM_FD_RESULT_POSE_RIGHT_DIAGONAL (0x2)
+#define MSM_FD_RESULT_POSE_RIGHT (0x3)
+#define MSM_FD_RESULT_POSE_LEFT_DIAGONAL (0x4)
+#define MSM_FD_RESULT_POSE_LEFT (0x5)
+
+/* FD misc registers */
+#define MSM_FD_MISC_HW_VERSION (0x00)
+#define MSM_FD_MISC_CGC_DISABLE (0x04)
+#define MSM_FD_HW_STOP (0x08)
+
+#define MSM_FD_MISC_SW_RESET (0x10)
+#define MSM_FD_MISC_SW_RESET_SET (1 << 0)
+
+#define MSM_FD_MISC_FIFO_STATUS (0x14)
+#define MSM_FD_MISC_FIFO_STATUS_RFIFO_DCNT_MAST (0x1F)
+#define MSM_FD_MISC_FIFO_STATUS_RFIFO_DCNT_SHIFT (0)
+#define MSM_FD_MISC_FIFO_STATUS_RFIFO_FULL (1 << 13)
+#define MSM_FD_MISC_FIFO_STATUS_RFIFO_EMPTY (1 << 14)
+#define MSM_FD_MISC_FIFO_STATUS_WFIFO_DCNT_MAST (0x1F)
+#define MSM_FD_MISC_FIFO_STATUS_WFIFO_DCNT_SHIFT (16)
+#define MSM_FD_MISC_FIFO_STATUS_WFIFO_EMPTY (1 << 29)
+#define MSM_FD_MISC_FIFO_STATUS_WFIFO_FULL (1 << 30)
+
+#define MSM_FD_MISC_DATA_ENDIAN (0x18)
+#define MSM_FD_MISC_DATA_ENDIAN_BYTE_SWAP_SET (1 << 0)
+
+#define MSM_FD_MISC_VBIF_REQ_PRIO (0x20)
+#define MSM_FD_MISC_VBIF_REQ_PRIO_MASK (0x3)
+
+#define MSM_FD_MISC_VBIF_PRIO_LEVEL (0x24)
+#define MSM_FD_MISC_VBIF_PRIO_LEVEL_MASK (0x3)
+
+#define MSM_FD_MISC_VBIF_MMU_PDIRECT (0x28)
+#define MSM_FD_MISC_VBIF_MMU_PDIRECT_INCREMENT (1 << 0)
+
+#define MSM_FD_MISC_VBIF_IRQ_CLR (0x30)
+#define MSM_FD_MISC_VBIF_IRQ_CLR_ALL (1 << 0)
+
+#define MSM_FD_MISC_VBIF_DONE_STATUS (0x34)
+#define MSM_FD_MISC_VBIF_DONE_STATUS_WRITE (1 << 0)
+#define MSM_FD_MISC_VBIF_DONE_STATUS_READ (1 << 1)
+
+#define MSM_FD_MISC_IRQ_MASK (0x50)
+#define MSM_FD_MISC_IRQ_MASK_HALT_REQ (1 << 1)
+#define MSM_FD_MISC_IRQ_MASK_CORE_IRQ (1 << 0)
+
+#define MSM_FD_MISC_IRQ_STATUS (0x54)
+#define MSM_FD_MISC_IRQ_STATUS_HALT_REQ (1 << 1)
+#define MSM_FD_MISC_IRQ_STATUS_CORE_IRQ (1 << 0)
+
+#define MSM_FD_MISC_IRQ_CLEAR (0x58)
+#define MSM_FD_MISC_IRQ_CLEAR_HALT (1 << 1)
+#define MSM_FD_MISC_IRQ_CLEAR_CORE (1 << 0)
+
+#define MSM_FD_MISC_TEST_BUS_SEL (0x40)
+#define MSM_FD_MISC_TEST_BUS_SEL_TEST_MODE_MASK (0xF)
+#define MSM_FD_MISC_TEST_BUS_SEL_TEST_MODE_SHIFT (0)
+#define MSM_FD_MISC_TEST_BUS_SEL_7_0_MASK (0x3)
+#define MSM_FD_MISC_TEST_BUS_SEL_7_0_SHIFT (16)
+#define MSM_FD_MISC_TEST_BUS_SEL_15_8_MASK (0x3)
+#define MSM_FD_MISC_TEST_BUS_SEL_15_8_SHIFT (18)
+#define MSM_FD_MISC_TEST_BUS_SEL_23_16_MASK (0x3)
+#define MSM_FD_MISC_TEST_BUS_SEL_23_16_SHIFT (20)
+#define MSM_FD_MISC_TEST_BUS_SEL_31_24_MASK (0x3)
+#define MSM_FD_MISC_TEST_BUS_SEL_31_24_SHIFT (22)
+
+#define MSM_FD_MISC_AHB_TEST_EN (0x44)
+#define MSM_FD_MISC_AHB_TEST_EN_MASK (0x3)
+
+#define MSM_FD_MISC_FD2VBIF_INT_TEST_SEL (0x48)
+#define MSM_FD_MISC_FD2VBIF_INT_TEST_MASK (0xF)
+
+#define MSM_FD_MISC_TEST_BUS (0x4C)
+
+/* FD vbif registers */
+#define MSM_FD_VBIF_CLKON (0x04)
+#define MSM_FD_VBIF_QOS_OVERRIDE_EN (0x10)
+#define MSM_FD_VBIF_QOS_OVERRIDE_REQPRI (0x18)
+#define MSM_FD_VBIF_QOS_OVERRIDE_PRILVL (0x1C)
+#define MSM_FD_VBIF_IN_RD_LIM_CONF0 (0xB0)
+#define MSM_FD_VBIF_IN_WR_LIM_CONF0 (0xC0)
+#define MSM_FD_VBIF_OUT_RD_LIM_CONF0 (0xD0)
+#define MSM_FD_VBIF_OUT_WR_LIM_CONF0 (0xD4)
+#define MSM_FD_VBIF_DDR_OUT_MAX_BURST (0xD8)
+#define MSM_FD_VBIF_ARB_CTL (0xF0)
+#define MSM_FD_VBIF_OUT_AXI_AMEMTYPE_CONF0 (0x160)
+#define MSM_FD_VBIF_OUT_AXI_AOOO_EN (0x178)
+#define MSM_FD_VBIF_OUT_AXI_AOOO (0x17c)
+#define MSM_FD_VBIF_ROUND_ROBIN_QOS_ARB (0x124)
+
+#endif /* __MSM_FD_REGS_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/Makefile b/drivers/media/platform/msm/camera_v2/isp/Makefile
new file mode 100644
index 0000000..621d81d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common/
+obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp48.o msm_isp47.o msm_isp46.o msm_isp44.o msm_isp40.o msm_isp.o
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
new file mode 100644
index 0000000..fda45cb
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -0,0 +1,1514 @@
+/* Copyright (c) 2013-2018, 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 <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+#include "msm.h"
+#include "msm_buf_mgr.h"
+#include "cam_smmu_api.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define BUF_DEBUG_FULL 0
+#define MAX_LIST_COUNT 100
+
+static int msm_buf_check_head_sanity(struct msm_isp_bufq *bufq)
+{
+ int rc = 0;
+ struct list_head *prev = NULL;
+ struct list_head *next = NULL;
+
+ if (!bufq) {
+ pr_err("%s: Error! Invalid bufq\n", __func__);
+ return -EINVAL;
+ }
+
+ prev = bufq->head.prev;
+ next = bufq->head.next;
+
+ if (!prev) {
+ pr_err("%s: Error! bufq->head.prev is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!next) {
+ pr_err("%s: Error! bufq->head.next is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (prev->next != &bufq->head) {
+ pr_err("%s: Error! head prev->next is %pK should be %pK\n",
+ __func__, prev->next, &bufq->head);
+ return -EINVAL;
+ }
+
+ if (next->prev != &bufq->head) {
+ pr_err("%s: Error! head next->prev is %pK should be %pK\n",
+ __func__, next->prev, &bufq->head);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+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;
+
+ /* bufq_handle cannot be 0 */
+ if ((bufq_handle == 0) ||
+ bufq_index >= BUF_MGR_NUM_BUF_Q ||
+ (bufq_index >= buf_mgr->num_buf_q))
+ return NULL;
+
+ 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,
+ uint32_t session_id, uint32_t stream_id)
+{
+ int i;
+ uint32_t embedded_stream_id = 0;
+
+ 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 0;
+ }
+
+ /* put stream id in handle, if its stats, use FFFF */
+ if (stream_id & (1 << 31))
+ embedded_stream_id = 0xFFFF;
+ else
+ embedded_stream_id = stream_id;
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].bufq_handle == 0) {
+ buf_mgr->bufq[i].bufq_handle =
+ embedded_stream_id << 8 | i;
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_free_bufq_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;
+
+ /* Set everything except lock to 0 */
+ bufq->bufq_handle = 0;
+ bufq->bufs = NULL;
+ bufq->session_id = 0;
+ bufq->stream_id = 0;
+ bufq->num_bufs = 0;
+ bufq->buf_type = 0;
+ INIT_LIST_HEAD(&bufq->head);
+
+ return 0;
+}
+
+static void msm_isp_copy_planes_from_v4l2_buffer(
+ struct msm_isp_qbuf_buffer *qbuf_buf,
+ const struct vb2_buffer *vb2_buf)
+{
+ int i;
+
+ qbuf_buf->num_planes = vb2_buf->num_planes;
+ for (i = 0; i < qbuf_buf->num_planes; i++) {
+ qbuf_buf->planes[i].addr = vb2_buf->planes[i].m.userptr;
+ qbuf_buf->planes[i].offset = vb2_buf->planes[i].data_offset;
+ qbuf_buf->planes[i].length = vb2_buf->planes[i].length;
+ }
+}
+
+static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ struct msm_isp_qbuf_buffer *qbuf_buf,
+ uint32_t stream_id)
+{
+ int i, rc = -1;
+ int ret;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ uint32_t accu_length = 0;
+ struct msm_isp_bufq *bufq = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq, stream id %x\n",
+ __func__, stream_id);
+ return -EINVAL;
+ }
+
+ if (qbuf_buf->num_planes > MAX_PLANES_PER_STREAM) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, qbuf_buf->num_planes, stream_id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < qbuf_buf->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ mapped_info->buf_fd = qbuf_buf->planes[i].addr;
+
+ if (bufq->security_mode == SECURE_MODE)
+ ret = cam_smmu_get_stage2_phy_addr(buf_mgr->iommu_hdl,
+ mapped_info->buf_fd,
+ CAM_SMMU_MAP_RW,
+ buf_mgr->client,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+ else
+ ret = cam_smmu_get_phy_addr(buf_mgr->iommu_hdl,
+ mapped_info->buf_fd,
+ CAM_SMMU_MAP_RW,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+ if (ret) {
+ rc = -EINVAL;
+ pr_err_ratelimited("%s: cannot map address", __func__);
+ goto get_phy_err;
+ }
+
+ mapped_info->paddr += accu_length;
+ accu_length += qbuf_buf->planes[i].length;
+
+ CDBG("%s: plane: %d addr:%pK\n",
+ __func__, i, (void *)mapped_info->paddr);
+
+ }
+ buf_info->num_planes = qbuf_buf->num_planes;
+ return 0;
+get_phy_err:
+ i--;
+
+ return rc;
+}
+
+static void msm_isp_unprepare_v4l2_buf(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ uint32_t stream_id)
+{
+ int i;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ struct msm_isp_bufq *bufq = NULL;
+
+ if (!buf_mgr || !buf_info) {
+ pr_err("%s: NULL ptr %pK %pK\n", __func__,
+ buf_mgr, buf_info);
+ return;
+ }
+
+ if (buf_info->num_planes > VIDEO_MAX_PLANES) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, buf_info->num_planes, stream_id);
+ return;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq, stream id %x\n",
+ __func__, stream_id);
+ return;
+ }
+
+ for (i = 0; i < buf_info->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ /* SEC_CAM: check any change is needed for secure_mode */
+ if (bufq->security_mode == SECURE_MODE)
+ cam_smmu_put_stage2_phy_addr(buf_mgr->iommu_hdl,
+ mapped_info->buf_fd);
+ else
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl,
+ mapped_info->buf_fd);
+ }
+}
+
+static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer_mapped_info *mapped_info, uint32_t fd)
+{
+ int rc = 0;
+ int ret;
+
+ if (!buf_mgr || !mapped_info) {
+ pr_err_ratelimited("%s: %d] NULL ptr buf_mgr %pK mapped_info %pK\n",
+ __func__, __LINE__, buf_mgr, mapped_info);
+ return -EINVAL;
+ }
+ if (buf_mgr->secure_enable == SECURE_MODE)
+ ret = cam_smmu_get_stage2_phy_addr(buf_mgr->iommu_hdl,
+ fd,
+ CAM_SMMU_MAP_RW,
+ buf_mgr->client,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+ else
+ ret = cam_smmu_get_phy_addr(buf_mgr->iommu_hdl,
+ fd,
+ CAM_SMMU_MAP_RW,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+
+ if (ret) {
+ rc = -EINVAL;
+ pr_err_ratelimited("%s: cannot map address", __func__);
+ goto smmu_map_error;
+ }
+ CDBG("%s: addr:%pK\n",
+ __func__, (void *)mapped_info->paddr);
+
+ return rc;
+smmu_map_error:
+ if (buf_mgr->secure_enable == SECURE_MODE)
+ cam_smmu_put_stage2_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+ else
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+ return rc;
+}
+
+static int msm_isp_unmap_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t fd)
+{
+ if (!buf_mgr) {
+ pr_err_ratelimited("%s: %d] NULL ptr buf_mgr\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ /* SEC_CAMERA: recheck Put part for stats */
+ if (buf_mgr->secure_enable == SECURE_MODE)
+ cam_smmu_put_stage2_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+ else
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+
+ return 0;
+}
+
+static int msm_isp_buf_prepare(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info, struct vb2_v4l2_buffer *vb2_v4l2_buf)
+{
+ int rc = -1;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ struct msm_isp_qbuf_buffer buf;
+
+ 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;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n",
+ __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ rc = buf_info->state;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ if (buf_info->state != MSM_ISP_BUFFER_STATE_INITIALIZED) {
+ pr_err("%s: Invalid buffer state: %d bufq %x buf-id %d\n",
+ __func__, buf_info->state, bufq->bufq_handle,
+ buf_info->buf_idx);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ if (vb2_v4l2_buf) {
+ msm_isp_copy_planes_from_v4l2_buffer(&buf,
+ &vb2_v4l2_buf->vb2_buf);
+ buf_info->vb2_v4l2_buf = vb2_v4l2_buf;
+ } else {
+ buf = info->buffer;
+ }
+
+ rc = msm_isp_prepare_v4l2_buf(buf_mgr, buf_info, &buf, bufq->stream_id);
+ if (rc < 0) {
+ pr_err_ratelimited("%s: Prepare buffer error\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare_all(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) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state ==
+ MSM_ISP_BUFFER_STATE_INITIALIZED)
+ continue;
+
+ if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_HAL) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED)
+ buf_mgr->vb2_ops->put_buf(
+ buf_info->vb2_v4l2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info, bufq->stream_id);
+ }
+ return 0;
+}
+
+static int msm_isp_get_buf_by_index(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *temp_buf_info;
+ uint32_t i = 0;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (buf_index >= bufq->num_bufs) {
+ pr_err("%s: Invalid buf index: %d max: %d\n", __func__,
+ buf_index, bufq->num_bufs);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ *buf_info = NULL;
+ for (i = 0; bufq->num_bufs; i++) {
+ temp_buf_info = &bufq->bufs[i];
+ if (temp_buf_info && temp_buf_info->buf_idx == buf_index) {
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+
+ if (*buf_info) {
+ pr_debug("Found buf in isp buf mgr");
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t buf_handle, int32_t buf_idx)
+{
+ 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 -EINVAL;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, buf_handle, buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_INITIALIZED)
+ return 0;
+
+ if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_HAL) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED)
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_v4l2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info, bufq->stream_id);
+
+ return 0;
+}
+
+
+static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info)
+{
+ int rc = -1;
+ unsigned long flags;
+ struct msm_isp_buffer *temp_buf_info = NULL;
+ struct msm_isp_bufq *bufq = NULL;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+
+ if (buf_mgr->open_count == 0) {
+ pr_err_ratelimited("%s: bug mgr open cnt = 0\n",
+ __func__);
+ return 0;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err_ratelimited("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (!bufq->bufq_handle) {
+ pr_err_ratelimited("%s: Invalid bufq handle\n", __func__);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ *buf_info = NULL;
+
+ switch (BUF_SRC(bufq->stream_id)) {
+ case MSM_ISP_BUFFER_SRC_NATIVE:
+ list_for_each_entry(temp_buf_info, &bufq->head, list) {
+ if (temp_buf_info->state ==
+ MSM_ISP_BUFFER_STATE_QUEUED) {
+ list_del_init(&temp_buf_info->list);
+ if (msm_buf_check_head_sanity(bufq) < 0) {
+ spin_unlock_irqrestore(
+ &bufq->bufq_lock, flags);
+ WARN(1, "%s buf_handle 0x%x buf_idx %d\n",
+ __func__,
+ bufq->bufq_handle,
+ temp_buf_info->buf_idx);
+ return -EFAULT;
+ }
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+ break;
+ case MSM_ISP_BUFFER_SRC_HAL:
+ if (buf_index == MSM_ISP_INVALID_BUF_INDEX)
+ vb2_v4l2_buf = buf_mgr->vb2_ops->get_buf(
+ bufq->session_id, bufq->stream_id);
+ else
+ vb2_v4l2_buf = buf_mgr->vb2_ops->get_buf_by_idx(
+ bufq->session_id, bufq->stream_id, buf_index);
+ if (vb2_v4l2_buf) {
+ if (vb2_v4l2_buf->vb2_buf.index < bufq->num_bufs) {
+ *buf_info = &bufq->bufs[vb2_v4l2_buf
+ ->vb2_buf.index];
+ (*buf_info)->vb2_v4l2_buf = vb2_v4l2_buf;
+ } else {
+ pr_err("%s: Incorrect buf index %d\n",
+ __func__, vb2_v4l2_buf->vb2_buf.index);
+ rc = -EINVAL;
+ }
+ if ((*buf_info) == NULL) {
+ buf_mgr->vb2_ops->put_buf(vb2_v4l2_buf,
+ bufq->session_id, bufq->stream_id);
+ pr_err("%s: buf index %d not found!\n",
+ __func__, vb2_v4l2_buf->vb2_buf.index);
+ rc = -EINVAL;
+
+ }
+ } else {
+ CDBG("%s: No HAL Buffer session_id: %d stream_id: %d\n",
+ __func__, bufq->session_id, bufq->stream_id);
+ rc = -EINVAL;
+ }
+ break;
+ case MSM_ISP_BUFFER_SRC_SCRATCH:
+ /* In scratch buf case we have only on buffer in queue.
+ * We return every time same buffer.
+ */
+ *buf_info = list_entry(bufq->head.next, typeof(**buf_info),
+ list);
+ break;
+ default:
+ pr_err("%s: Incorrect buf source.\n", __func__);
+ rc = -EINVAL;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ if (!(*buf_info)) {
+ rc = -ENOMEM;
+ } else {
+ (*buf_info)->state = MSM_ISP_BUFFER_STATE_DEQUEUED;
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_put_buf_unsafe(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:
+ if (BUF_SRC(bufq->stream_id)) {
+ if (!list_empty(&buf_info->list)) {
+ WARN(1, "%s: buf %x/%x double add\n",
+ __func__, bufq_handle, buf_index);
+ return -EFAULT;
+ }
+ list_add_tail(&buf_info->list, &bufq->head);
+ if (msm_buf_check_head_sanity(bufq) < 0) {
+ WARN(1, "%s buf_handle 0x%x buf_idx %d\n",
+ __func__,
+ bufq->bufq_handle,
+ buf_info->buf_idx);
+ return -EFAULT;
+ }
+ } else {
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_v4l2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ case MSM_ISP_BUFFER_STATE_DISPATCHED:
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ case MSM_ISP_BUFFER_STATE_QUEUED:
+ case MSM_ISP_BUFFER_STATE_DIVERTED:
+ default:
+ WARN(1, "%s: bufq 0x%x, buf idx 0x%x, incorrect state = %d",
+ __func__, bufq_handle, buf_index, buf_info->state);
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ int rc = -1;
+ unsigned long flags;
+ 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;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+
+ rc = msm_isp_put_buf_unsafe(buf_mgr, bufq_handle, buf_index);
+
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ return rc;
+}
+
+static int msm_isp_buf_divert(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id)
+{
+ unsigned long flags;
+ 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 -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+
+ buf_info->frame_id = frame_id;
+ if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_NATIVE) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
+ buf_info->tv = tv;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return 0;
+}
+
+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, uint32_t output_format)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ enum msm_isp_buffer_state state;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return -EINVAL;
+ }
+
+ 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 -EINVAL;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ state = buf_info->state;
+
+ if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_HAL) {
+ if (state == MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ buf_mgr->vb2_ops->buf_done(buf_info->vb2_v4l2_buf,
+ bufq->session_id, bufq->stream_id,
+ frame_id, tv, output_format);
+ } else {
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+ goto done;
+ }
+
+ /*
+ * For native buffer put the diverted buffer back to queue since caller
+ * is not going to send it to CPP, this is error case like
+ * drop_frame/empty_buffer
+ */
+ if (state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ rc = msm_isp_put_buf_unsafe(buf_mgr, buf_info->bufq_handle,
+ buf_info->buf_idx);
+ if (rc < 0)
+ pr_err("%s: Buf put failed\n", __func__);
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+done:
+ return rc;
+}
+
+static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, enum msm_isp_buffer_flush_t flush_type,
+ struct timeval *tv, uint32_t frame_id)
+{
+ int i;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ unsigned long flags;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ for (i = 0; i < bufq->num_bufs; i++) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, i);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ continue;
+ }
+ switch (flush_type) {
+ case MSM_ISP_BUFFER_FLUSH_DIVERTED:
+ if (buf_info->state !=
+ MSM_ISP_BUFFER_STATE_DIVERTED)
+ continue;
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ msm_isp_put_buf_unsafe(buf_mgr,
+ bufq_handle, buf_info->buf_idx);
+ break;
+ case MSM_ISP_BUFFER_FLUSH_ALL:
+ if (buf_info->state ==
+ MSM_ISP_BUFFER_STATE_DIVERTED)
+ continue;
+ if (buf_info->state !=
+ MSM_ISP_BUFFER_STATE_DEQUEUED)
+ continue;
+ msm_isp_put_buf_unsafe(buf_mgr,
+ bufq_handle, buf_info->buf_idx);
+ break;
+ default:
+ WARN(1, "Invalid flush type %d\n", flush_type);
+ }
+ }
+
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return 0;
+}
+
+static int msm_isp_buf_enqueue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ int rc = 0, buf_state;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, info->handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq, handle 0x%x, stream id %x num_plane %d\n"
+ , __func__, info->handle, (info->handle >> 8),
+ info->buffer.num_planes);
+ return -EINVAL;
+ }
+
+ buf_state = msm_isp_buf_prepare(buf_mgr, info, NULL);
+ if (buf_state < 0) {
+ pr_err_ratelimited("%s: Buf prepare failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (buf_state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+ if (info->dirty_buf) {
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last]
+ = MSM_ISP_BUFFER_STATE_PUT_BUF;
+ buf_info->buf_debug.put_state_last ^= 1;
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ rc = msm_isp_put_buf(buf_mgr,
+ info->handle, info->buf_idx);
+ } else {
+ if (BUF_SRC(bufq->stream_id))
+ pr_err("%s: Invalid native buffer state\n",
+ __func__);
+ else {
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_PUT_BUF;
+ buf_info->buf_debug.put_state_last ^= 1;
+ rc = msm_isp_buf_done(buf_mgr,
+ info->handle, info->buf_idx,
+ buf_info->tv, buf_info->frame_id, 0);
+ }
+ }
+ } else {
+ if (BUF_SRC(bufq->stream_id) != MSM_ISP_BUFFER_SRC_HAL) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ buf_info->buf_debug.put_state_last ^= 1;
+ rc = msm_isp_put_buf(buf_mgr,
+ info->handle, info->buf_idx);
+ if (rc < 0) {
+ pr_err("%s: Buf put failed stream %x\n",
+ __func__, bufq->stream_id);
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_buf_dequeue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ struct msm_isp_buffer *buf_info = NULL;
+ int rc = 0;
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("Invalid buffer dequeue\n");
+ return -EINVAL;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ pr_err("%s: Invalid state %d\n", __func__, buf_info->state);
+ return -EINVAL;
+ }
+ msm_isp_buf_unprepare(buf_mgr, info->handle, info->buf_idx);
+
+ buf_info->state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+
+ 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;
+ }
+ }
+ pr_err("%s: No match found 0x%x 0x%x\n", __func__,
+ session_id, stream_id);
+ return 0;
+}
+
+static int msm_isp_get_buf_src(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t *buf_src)
+{
+ struct msm_isp_bufq *bufq = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n",
+ __func__);
+ return -EINVAL;
+ }
+ *buf_src = BUF_SRC(bufq->stream_id);
+
+ return 0;
+}
+
+static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request_ver2 *buf_request)
+{
+ int i;
+ struct msm_isp_bufq *bufq = NULL;
+
+ CDBG("%s: E\n", __func__);
+
+ if (!buf_request->num_buf || buf_request->num_buf > VB2_MAX_FRAME) {
+ pr_err("Invalid buffer request\n");
+ return -EINVAL;
+ }
+
+ buf_request->handle = msm_isp_get_buf_handle(buf_mgr,
+ buf_request->session_id, buf_request->stream_id);
+ if (!buf_request->handle) {
+ pr_err("Invalid buffer handle\n");
+ return -EINVAL;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_request->handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq stream_id %x\n",
+ __func__, buf_request->stream_id);
+
+ return -EINVAL;
+ }
+
+ bufq->bufs = kzalloc(sizeof(struct msm_isp_buffer) *
+ buf_request->num_buf, GFP_KERNEL);
+ if (!bufq->bufs) {
+ msm_isp_free_bufq_handle(buf_mgr, buf_request->handle);
+ return -ENOMEM;
+ }
+ spin_lock_init(&bufq->bufq_lock);
+ 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;
+ bufq->buf_type = buf_request->buf_type;
+ INIT_LIST_HEAD(&bufq->head);
+ bufq->security_mode = buf_request->security_mode;
+
+ for (i = 0; i < buf_request->num_buf; i++) {
+ bufq->bufs[i].state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+ bufq->bufs[i].buf_debug.put_state[0] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ bufq->bufs[i].buf_debug.put_state[1] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ bufq->bufs[i].buf_debug.put_state_last = 0;
+ bufq->bufs[i].bufq_handle = bufq->bufq_handle;
+ bufq->bufs[i].buf_idx = i;
+ INIT_LIST_HEAD(&bufq->bufs[i].list);
+ }
+
+ 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;
+ unsigned long flags;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq release\n");
+ return -EINVAL;
+ }
+
+ msm_isp_buf_unprepare_all(buf_mgr, bufq_handle);
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ kfree(bufq->bufs);
+ msm_isp_free_bufq_handle(buf_mgr, bufq_handle);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ return 0;
+}
+
+static void msm_isp_release_all_bufq(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ unsigned long flags;
+ 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_all(buf_mgr, bufq->bufq_handle);
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ kfree(bufq->bufs);
+ msm_isp_free_bufq_handle(buf_mgr, bufq->bufq_handle);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+}
+
+
+/**
+ * msm_isp_buf_put_scratch() - Release scratch buffers
+ * @buf_mgr: The buffer structure for h/w
+ *
+ * Returns 0 on success else error code
+ */
+static int msm_isp_buf_put_scratch(struct msm_isp_buf_mgr *buf_mgr)
+{
+ int rc;
+
+ if (!buf_mgr->scratch_buf_addr)
+ return 0;
+
+ if (buf_mgr->secure_enable == SECURE_MODE) {
+ rc = cam_smmu_free_stage2_scratch_mem(buf_mgr->iommu_hdl,
+ buf_mgr->client, buf_mgr->sc_handle);
+ if (buf_mgr->scratch_buf_stats_addr)
+ rc = cam_smmu_put_phy_addr_scratch(buf_mgr->iommu_hdl,
+ buf_mgr->scratch_buf_stats_addr);
+ } else {
+ rc = cam_smmu_put_phy_addr_scratch(buf_mgr->iommu_hdl,
+ buf_mgr->scratch_buf_addr);
+ }
+ if (rc)
+ pr_err("%s: failed to put scratch buffer to img iommu: %d\n",
+ __func__, rc);
+
+
+ if (!rc) {
+ buf_mgr->scratch_buf_addr = 0;
+ buf_mgr->scratch_buf_stats_addr = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * msm_isp_buf_get_scratch() - Create scratch buffers
+ * @buf_mgr: The buffer structure for h/w
+ *
+ * Create and map scratch buffers for all IOMMU's under the buffer
+ * manager.
+ *
+ * Returns 0 on success else error code
+ */
+static int msm_isp_buf_get_scratch(struct msm_isp_buf_mgr *buf_mgr)
+{
+ int rc;
+ size_t range = buf_mgr->scratch_buf_range;
+
+ if (buf_mgr->scratch_buf_addr || !buf_mgr->scratch_buf_range)
+ /* already mapped or not supported */
+ return 0;
+
+ if (buf_mgr->secure_enable == SECURE_MODE) {
+ rc = cam_smmu_alloc_get_stage2_scratch_mem(buf_mgr->iommu_hdl,
+ CAM_SMMU_MAP_RW,
+ buf_mgr->client,
+ &buf_mgr->sc_handle,
+ &buf_mgr->scratch_buf_addr,
+ &range);
+ if (rc)
+ goto done;
+
+ rc = cam_smmu_get_phy_addr_scratch(
+ buf_mgr->iommu_hdl,
+ CAM_SMMU_MAP_RW,
+ &buf_mgr->scratch_buf_stats_addr,
+ buf_mgr->scratch_buf_range,
+ SZ_4K);
+ if (rc)
+ msm_isp_buf_put_scratch(buf_mgr);
+ } else {
+ rc = cam_smmu_get_phy_addr_scratch(
+ buf_mgr->iommu_hdl,
+ CAM_SMMU_MAP_RW,
+ &buf_mgr->scratch_buf_addr,
+ buf_mgr->scratch_buf_range,
+ SZ_4K);
+ buf_mgr->scratch_buf_stats_addr = buf_mgr->scratch_buf_addr;
+ }
+done:
+ if (rc) {
+ pr_err("%s: failed to map scratch buffer to img iommu: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr,
+ void *arg)
+{
+ struct msm_vfe_smmu_attach_cmd *cmd = arg;
+ int rc = 0;
+ int32_t stall_disable = 1;
+
+ pr_debug("%s: cmd->security_mode : %d\n", __func__, cmd->security_mode);
+
+ mutex_lock(&buf_mgr->lock);
+ if (cmd->iommu_attach_mode == IOMMU_ATTACH) {
+ /* disable smmu stall on fault */
+ cam_smmu_set_attr(buf_mgr->iommu_hdl,
+ DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable);
+ /*
+ * Call hypervisor thru scm call to notify secure or
+ * non-secure mode
+ */
+ if (buf_mgr->attach_ref_cnt == 0) {
+ if (cmd->security_mode == SECURE_MODE)
+ rc = cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_ATTACH_SEC_VFE_NS_STATS);
+ else
+ rc = cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_ATTACH);
+ if (rc < 0) {
+ pr_err("%s: img smmu attach error, rc :%d\n",
+ __func__, rc);
+ goto err1;
+ }
+ buf_mgr->secure_enable = cmd->security_mode;
+ }
+ buf_mgr->attach_ref_cnt++;
+ rc = msm_isp_buf_get_scratch(buf_mgr);
+ if (rc)
+ goto err2;
+ }
+
+ mutex_unlock(&buf_mgr->lock);
+ return rc;
+
+err2:
+ if (buf_mgr->secure_enable == SECURE_MODE)
+ cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_DETACH_SEC_VFE_NS_STATS);
+ else
+ cam_smmu_ops(buf_mgr->iommu_hdl, CAM_SMMU_DETACH);
+err1:
+ mutex_unlock(&buf_mgr->lock);
+ return rc;
+}
+
+
+static int msm_isp_init_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name)
+{
+ int rc = -1;
+ int i = 0;
+
+ mutex_lock(&buf_mgr->lock);
+ if (buf_mgr->open_count++) {
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+ }
+
+ CDBG("%s: E\n", __func__);
+ buf_mgr->attach_ref_cnt = 0;
+
+ buf_mgr->num_buf_q = BUF_MGR_NUM_BUF_Q;
+ memset(buf_mgr->bufq, 0, sizeof(buf_mgr->bufq));
+
+ rc = cam_smmu_get_handle("vfe", &buf_mgr->iommu_hdl);
+ if (rc < 0) {
+ pr_err("vfe get handle failed\n");
+ goto get_handle_error;
+ }
+
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++)
+ spin_lock_init(&buf_mgr->bufq[i].bufq_lock);
+
+ buf_mgr->pagefault_debug_disable = 0;
+ buf_mgr->frameId_mismatch_recovery = 0;
+ /* create ION client */
+ buf_mgr->client = msm_ion_client_create("vfe");
+get_handle_error:
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+}
+
+static int msm_isp_deinit_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ mutex_lock(&buf_mgr->lock);
+ if (buf_mgr->open_count > 0)
+ buf_mgr->open_count--;
+
+ if (buf_mgr->open_count) {
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+ }
+ msm_isp_release_all_bufq(buf_mgr);
+ buf_mgr->num_buf_q = 0;
+ buf_mgr->pagefault_debug_disable = 0;
+
+ msm_isp_buf_put_scratch(buf_mgr);
+ if (buf_mgr->attach_ref_cnt != 0) {
+ if (buf_mgr->secure_enable == SECURE_MODE)
+ cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_DETACH_SEC_VFE_NS_STATS);
+ else
+ cam_smmu_ops(buf_mgr->iommu_hdl, CAM_SMMU_DETACH);
+ }
+ cam_smmu_destroy_handle(buf_mgr->iommu_hdl);
+ buf_mgr->attach_ref_cnt = 0;
+ buf_mgr->secure_enable = 0;
+ buf_mgr->attach_ref_cnt = 0;
+ if (buf_mgr->client) {
+ ion_client_destroy(buf_mgr->client);
+ buf_mgr->client = NULL;
+ }
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+}
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg)
+{
+ int rc = -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_MSM_ISP_REQUEST_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+ struct msm_isp_buf_request_ver2 buf_req_ver2;
+
+ memcpy(&buf_req_ver2, buf_req,
+ sizeof(struct msm_isp_buf_request));
+ buf_req_ver2.security_mode = NON_SECURE_MODE;
+ rc = buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2);
+ memcpy(buf_req, &buf_req_ver2,
+ sizeof(struct msm_isp_buf_request));
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_BUF_VER2: {
+ struct msm_isp_buf_request_ver2 *buf_req_ver2 = arg;
+
+ rc = buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2);
+ break;
+ }
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+
+ rc = buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_DEQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+
+ rc = buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+
+ rc = buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ break;
+ }
+ case VIDIOC_MSM_ISP_UNMAP_BUF: {
+ struct msm_isp_unmap_buf_req *unmap_req = arg;
+
+ rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
+ break;
+ }
+ }
+ return rc;
+}
+
+static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned long fault_addr)
+{
+ struct msm_isp_buffer *bufs = NULL;
+ uint32_t i = 0, j = 0, k = 0, rc = 0;
+ char *print_buf = NULL, temp_buf[100];
+ uint32_t print_buf_size = 2000;
+ unsigned long start_addr = 0, end_addr = 0;
+ int buf_addr_delta = -1;
+ int temp_delta = 0;
+ uint32_t debug_stream_id = 0;
+ uint32_t debug_buf_idx = 0;
+ uint32_t debug_buf_plane = 0;
+ unsigned long debug_start_addr = 0;
+ unsigned long debug_end_addr = 0;
+ uint32_t debug_frame_id = 0;
+ enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+
+ if (!buf_mgr) {
+ pr_err_ratelimited("%s: %d] NULL buf_mgr\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) {
+ bufq = &buf_mgr->bufq[i];
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (!bufq->bufq_handle) {
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ continue;
+ }
+
+ for (j = 0; j < bufq->num_bufs; j++) {
+ bufs = &bufq->bufs[j];
+ if (!bufs)
+ continue;
+
+ for (k = 0; k < bufs->num_planes; k++) {
+ start_addr = bufs->
+ mapped_info[k].paddr;
+ end_addr = bufs->mapped_info[k].paddr +
+ bufs->mapped_info[k].len - 1;
+ temp_delta = fault_addr - start_addr;
+ if (temp_delta < 0)
+ continue;
+
+ if (buf_addr_delta == -1 ||
+ temp_delta < buf_addr_delta) {
+ buf_addr_delta = temp_delta;
+ debug_stream_id = bufq->stream_id;
+ debug_buf_idx = j;
+ debug_buf_plane = k;
+ debug_start_addr = start_addr;
+ debug_end_addr = end_addr;
+ debug_frame_id = bufs->frame_id;
+ debug_state = bufs->state;
+ }
+ }
+ }
+ start_addr = 0;
+ end_addr = 0;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+
+ pr_err("%s: ==== SMMU page fault addr %lx ====\n", __func__,
+ fault_addr);
+ pr_err("%s: nearby stream id %x, frame_id %d\n", __func__,
+ debug_stream_id, debug_frame_id);
+ pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__,
+ debug_buf_idx, debug_buf_plane, debug_state);
+ pr_err("%s: buf address %pK -- %pK\n", __func__,
+ (void *)debug_start_addr, (void *)debug_end_addr);
+
+ if (BUF_DEBUG_FULL) {
+ print_buf = kzalloc(print_buf_size, GFP_ATOMIC);
+ if (!print_buf) {
+ pr_err("%s failed: No memory", __func__);
+ return -ENOMEM;
+ }
+ snprintf(print_buf, print_buf_size, "%s\n", __func__);
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) {
+ if (i % 2 == 0 && i > 0) {
+ pr_err("%s\n", print_buf);
+ print_buf[0] = 0;
+ }
+ if (buf_mgr->bufq[i].bufq_handle != 0) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "handle %x stream %x num_bufs %d\n",
+ buf_mgr->bufq[i].bufq_handle,
+ buf_mgr->bufq[i].stream_id,
+ buf_mgr->bufq[i].num_bufs);
+ strlcat(print_buf, temp_buf, print_buf_size);
+ for (j = 0; j < buf_mgr->bufq[i].num_bufs;
+ j++) {
+ bufs = &buf_mgr->bufq[i].bufs[j];
+ if (!bufs)
+ break;
+
+ for (k = 0; k < bufs->num_planes; k++) {
+ start_addr = bufs->
+ mapped_info[k].paddr;
+ end_addr = bufs->mapped_info[k].
+ paddr + bufs->
+ mapped_info[k].len;
+ snprintf(temp_buf,
+ sizeof(temp_buf),
+ " buf %d plane %d start_addr %pK end_addr %pK\n",
+ j, k,
+ (void *)start_addr,
+ (void *)end_addr);
+ strlcat(print_buf, temp_buf,
+ print_buf_size);
+ }
+ }
+ start_addr = 0;
+ end_addr = 0;
+ }
+ }
+ pr_err("%s\n", print_buf);
+ kfree(print_buf);
+ }
+ return rc;
+}
+
+static struct msm_isp_buf_ops isp_buf_ops = {
+ .request_buf = msm_isp_request_bufq,
+ .enqueue_buf = msm_isp_buf_enqueue,
+ .dequeue_buf = msm_isp_buf_dequeue,
+ .release_buf = msm_isp_release_bufq,
+ .get_bufq_handle = msm_isp_get_bufq_handle,
+ .get_buf_src = msm_isp_get_buf_src,
+ .get_buf = msm_isp_get_buf,
+ .get_buf_by_index = msm_isp_get_buf_by_index,
+ .map_buf = msm_isp_map_buf,
+ .unmap_buf = msm_isp_unmap_buf,
+ .put_buf = msm_isp_put_buf,
+ .flush_buf = msm_isp_flush_buf,
+ .buf_done = msm_isp_buf_done,
+ .buf_mgr_init = msm_isp_init_isp_buf_mgr,
+ .buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
+ .buf_mgr_debug = msm_isp_buf_mgr_debug,
+ .get_bufq = msm_isp_get_bufq,
+ .buf_divert = msm_isp_buf_divert,
+};
+
+int msm_isp_create_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops,
+ struct device *dev,
+ uint32_t scratch_buf_range)
+{
+ int rc = 0;
+
+ if (buf_mgr->init_done)
+ return rc;
+
+ buf_mgr->ops = &isp_buf_ops;
+ buf_mgr->vb2_ops = vb2_ops;
+ buf_mgr->open_count = 0;
+ buf_mgr->pagefault_debug_disable = 0;
+ buf_mgr->secure_enable = NON_SECURE_MODE;
+ buf_mgr->scratch_buf_range = scratch_buf_range;
+ mutex_init(&buf_mgr->lock);
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
new file mode 100644
index 0000000..4ea6dd6
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
@@ -0,0 +1,224 @@
+/* Copyright (c) 2013-2018, 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 "msm_sd.h"
+
+/* Buffer type could be userspace / HAL.
+ * Userspase could provide native or scratch buffer.
+ */
+#define BUF_SRC(id) ( \
+ (id & ISP_SCRATCH_BUF_BIT) ? MSM_ISP_BUFFER_SRC_SCRATCH : \
+ (id & ISP_NATIVE_BUF_BIT) ? MSM_ISP_BUFFER_SRC_NATIVE : \
+ MSM_ISP_BUFFER_SRC_HAL)
+
+/*
+ * This mask can be set dynamically if there are more than 2 VFE
+ * and 2 of those are used
+ */
+#define ISP_SHARE_BUF_MASK 0x3
+#define ISP_NUM_BUF_MASK 2
+#define BUF_MGR_NUM_BUF_Q 28
+#define MAX_IOMMU_CTX 2
+
+#define MSM_ISP_INVALID_BUF_INDEX 0xFFFFFFFF
+
+struct msm_isp_buf_mgr;
+
+enum msm_isp_buffer_src_t {
+ MSM_ISP_BUFFER_SRC_HAL,
+ MSM_ISP_BUFFER_SRC_NATIVE,
+ MSM_ISP_BUFFER_SRC_SCRATCH,
+ MSM_ISP_BUFFER_SRC_MAX,
+};
+
+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_DIVERTED, /* Sent to other hardware*/
+ MSM_ISP_BUFFER_STATE_DISPATCHED, /* Sent to HAL*/
+};
+
+enum msm_isp_buffer_put_state {
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED, /* on init */
+ MSM_ISP_BUFFER_STATE_PUT_BUF, /* on rotation */
+ MSM_ISP_BUFFER_STATE_FLUSH, /* on recovery */
+ MSM_ISP_BUFFER_STATE_DROP_REG, /* on drop frame for reg_update */
+ MSM_ISP_BUFFER_STATE_DROP_SKIP, /* on drop frame for sw skip */
+ MSM_ISP_BUFFER_STATE_RETURN_EMPTY, /* for return empty */
+};
+
+enum msm_isp_buffer_flush_t {
+ MSM_ISP_BUFFER_FLUSH_DIVERTED,
+ MSM_ISP_BUFFER_FLUSH_ALL,
+};
+
+enum msm_isp_buf_mgr_state {
+ MSM_ISP_BUF_MGR_ATTACH,
+ MSM_ISP_BUF_MGR_DETACH,
+};
+
+struct msm_isp_buffer_mapped_info {
+ size_t len;
+ dma_addr_t paddr;
+ int buf_fd;
+};
+
+struct buffer_cmd {
+ struct list_head list;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+};
+
+struct msm_isp_buffer_debug_t {
+ enum msm_isp_buffer_put_state put_state[2];
+ uint8_t put_state_last;
+};
+
+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;
+ uint32_t frame_id;
+ struct timeval *tv;
+ /* Indicates whether buffer is used as ping ot pong buffer */
+ uint32_t pingpong_bit;
+ /* Indicates buffer is reconfig due to drop frame */
+ uint32_t is_drop_reconfig;
+
+ /*Native buffer*/
+ struct list_head list;
+ enum msm_isp_buffer_state state;
+
+ struct msm_isp_buffer_debug_t buf_debug;
+
+ /*Vb2 buffer data*/
+ struct vb2_v4l2_buffer *vb2_v4l2_buf;
+};
+
+struct msm_isp_bufq {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t num_bufs;
+ uint32_t bufq_handle;
+ enum msm_isp_buf_type buf_type;
+ struct msm_isp_buffer *bufs;
+ spinlock_t bufq_lock;
+ /*Native buffer queue*/
+ struct list_head head;
+ enum smmu_attach_mode security_mode;
+};
+
+struct msm_isp_buf_ops {
+ int (*request_buf)(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request_ver2 *buf_request);
+
+ int (*enqueue_buf)(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info);
+
+ int (*dequeue_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_src)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t *buf_src);
+
+ int (*get_buf)(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info);
+
+ int (*get_buf_by_index)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info);
+
+ int (*map_buf)(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer_mapped_info *mapped_info, uint32_t fd);
+
+ int (*unmap_buf)(struct msm_isp_buf_mgr *buf_mgr, uint32_t fd);
+
+ int (*put_buf)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index);
+
+ int (*flush_buf)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, enum msm_isp_buffer_flush_t flush_type,
+ struct timeval *tv, uint32_t frame_id);
+
+ 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, uint32_t output_format);
+ void (*register_ctx)(struct msm_isp_buf_mgr *buf_mgr,
+ struct device **iommu_ctx1, struct device **iommu_ctx2,
+ int num_iommu_ctx1, int num_iommu_ctx2);
+ int (*buf_mgr_init)(struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name);
+ int (*buf_mgr_deinit)(struct msm_isp_buf_mgr *buf_mgr);
+ int (*buf_mgr_debug)(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned long fault_addr);
+ struct msm_isp_bufq * (*get_bufq)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle);
+ int (*buf_divert)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id);
+};
+
+struct msm_isp_buf_mgr {
+ int init_done;
+ uint32_t open_count;
+ uint32_t pagefault_debug_disable;
+ uint32_t frameId_mismatch_recovery;
+ uint16_t num_buf_q;
+ struct msm_isp_bufq bufq[BUF_MGR_NUM_BUF_Q];
+
+ struct ion_client *client;
+ struct msm_isp_buf_ops *ops;
+
+ struct msm_sd_req_vb2_q *vb2_ops;
+
+
+ /*Add secure mode*/
+ int secure_enable;
+
+ int attach_ref_cnt;
+ enum msm_isp_buf_mgr_state attach_state;
+ struct device *isp_dev;
+ struct mutex lock;
+ /* Scratch buffer */
+ dma_addr_t scratch_buf_addr;
+ dma_addr_t scratch_buf_stats_addr;
+ uint32_t scratch_buf_range;
+ int iommu_hdl;
+ struct ion_handle *sc_handle;
+};
+
+int msm_isp_create_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops, struct device *dev,
+ uint32_t scratch_addr_range);
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg);
+
+int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr,
+ void *arg);
+
+#endif /* _MSM_ISP_BUF_H_ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
new file mode 100644
index 0000000..7af9a3e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -0,0 +1,771 @@
+/* Copyright (c) 2013-2018, 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 <linux/of_device.h>
+#include <linux/sched_clock.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.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_isp48.h"
+#include "msm_isp47.h"
+#include "msm_isp46.h"
+#include "msm_isp44.h"
+#include "msm_isp40.h"
+#include "msm_isp32.h"
+
+static struct msm_sd_req_vb2_q vfe_vb2_ops;
+static struct msm_isp_buf_mgr vfe_buf_mgr;
+static struct msm_vfe_common_dev_data vfe_common_data;
+static struct dual_vfe_resource dualvfe;
+
+static const struct of_device_id msm_vfe_dt_match[] = {
+ {
+ .compatible = "qcom,vfe",
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
+
+#define MAX_OVERFLOW_COUNTERS 29
+#define OVERFLOW_LENGTH 1024
+#define OVERFLOW_BUFFER_LENGTH 64
+static char stat_line[OVERFLOW_LENGTH];
+
+static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev,
+ struct msm_isp_bw_req_info *isp_req_hist);
+
+static char *stats_str[MAX_OVERFLOW_COUNTERS] = {
+ "imgmaster0_overflow_cnt",
+ "imgmaster1_overflow_cnt",
+ "imgmaster2_overflow_cnt",
+ "imgmaster3_overflow_cnt",
+ "imgmaster4_overflow_cnt",
+ "imgmaster5_overflow_cnt",
+ "imgmaster6_overflow_cnt",
+ "be_overflow_cnt",
+ "bg_overflow_cnt",
+ "bf_overflow_cnt",
+ "awb_overflow_cnt",
+ "rs_overflow_cnt",
+ "cs_overflow_cnt",
+ "ihist_overflow_cnt",
+ "skinbhist_overflow_cnt",
+ "bfscale_overflow_cnt",
+ "ISP_VFE0_client_info.active",
+ "ISP_VFE0_client_info.ab",
+ "ISP_VFE0_client_info.ib",
+ "ISP_VFE1_client_info.active",
+ "ISP_VFE1_client_info.ab",
+ "ISP_VFE1_client_info.ib",
+ "ISP_CPP_client_info.active",
+ "ISP_CPP_client_info.ab",
+ "ISP_CPP_client_info.ib",
+ "ISP_last_overflow.ab",
+ "ISP_last_overflow.ib",
+ "ISP_VFE_CLK_RATE",
+ "ISP_CPP_CLK_RATE",
+};
+
+#define MAX_DEPTH_BW_REQ_HISTORY 25
+#define MAX_BW_HISTORY_BUFF_LEN 6144
+#define MAX_BW_HISTORY_LINE_BUFF_LEN 512
+
+#define MAX_UB_INFO_BUFF_LEN 1024
+#define MAX_UB_INFO_LINE_BUFF_LEN 256
+
+static struct msm_isp_bw_req_info
+ msm_isp_bw_request_history[MAX_DEPTH_BW_REQ_HISTORY];
+static int msm_isp_bw_request_history_idx;
+static char bw_request_history_buff[MAX_BW_HISTORY_BUFF_LEN];
+static char ub_info_buffer[MAX_UB_INFO_BUFF_LEN];
+static spinlock_t req_history_lock;
+
+static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t vfe_debugfs_statistics_read(struct file *t_file,
+ char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ uint64_t *ptr;
+ char buffer[OVERFLOW_BUFFER_LENGTH] = {0};
+ struct vfe_device *vfe_dev = (struct vfe_device *)
+ t_file->private_data;
+ struct msm_isp_statistics *stats = vfe_dev->stats;
+
+ memset(stat_line, 0, sizeof(stat_line));
+ msm_isp_util_get_bandwidth_stats(vfe_dev, stats);
+ ptr = (uint64_t *)(stats);
+ for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) {
+ strlcat(stat_line, stats_str[i], sizeof(stat_line));
+ strlcat(stat_line, " ", sizeof(stat_line));
+ snprintf(buffer, sizeof(buffer), "%llu", ptr[i]);
+ strlcat(stat_line, buffer, sizeof(stat_line));
+ strlcat(stat_line, "\r\n", sizeof(stat_line));
+ }
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, stat_line, strlen(stat_line));
+}
+
+static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct vfe_device *vfe_dev = (struct vfe_device *)
+ t_file->private_data;
+ struct msm_isp_statistics *stats = vfe_dev->stats;
+
+ memset(stats, 0, sizeof(struct msm_isp_statistics));
+
+ return sizeof(struct msm_isp_statistics);
+}
+
+static int bw_history_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t bw_history_read(struct file *t_file, char __user *t_char,
+ size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ char *out_buffer = bw_request_history_buff;
+ char line_buffer[MAX_BW_HISTORY_LINE_BUFF_LEN] = {0};
+ struct msm_isp_bw_req_info *isp_req_hist =
+ (struct msm_isp_bw_req_info *) t_file->private_data;
+
+ memset(out_buffer, 0, MAX_BW_HISTORY_BUFF_LEN);
+
+ snprintf(line_buffer, sizeof(line_buffer),
+ "Bus bandwidth request history in chronological order:\n");
+ strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff));
+
+ snprintf(line_buffer, sizeof(line_buffer),
+ "MSM_ISP_MIN_AB = %u, MSM_ISP_MIN_IB = %u\n\n",
+ MSM_ISP_MIN_AB, MSM_ISP_MIN_IB);
+ strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff));
+
+ for (i = 0; i < MAX_DEPTH_BW_REQ_HISTORY; i++) {
+ snprintf(line_buffer, sizeof(line_buffer),
+ "idx = %d, client = %u, timestamp = %llu, ab = %llu, ib = %llu\n"
+ "ISP0.active = %x, ISP0.ab = %llu, ISP0.ib = %llu\n"
+ "ISP1.active = %x, ISP1.ab = %llu, ISP1.ib = %llu\n"
+ "CPP.active = %x, CPP.ab = %llu, CPP.ib = %llu\n\n",
+ i, isp_req_hist[i].client, isp_req_hist[i].timestamp,
+ isp_req_hist[i].total_ab, isp_req_hist[i].total_ib,
+ isp_req_hist[i].client_info[0].active,
+ isp_req_hist[i].client_info[0].ab,
+ isp_req_hist[i].client_info[0].ib,
+ isp_req_hist[i].client_info[1].active,
+ isp_req_hist[i].client_info[1].ab,
+ isp_req_hist[i].client_info[1].ib,
+ isp_req_hist[i].client_info[2].active,
+ isp_req_hist[i].client_info[2].ab,
+ isp_req_hist[i].client_info[2].ib);
+ strlcat(out_buffer, line_buffer,
+ sizeof(bw_request_history_buff));
+ }
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, out_buffer, strlen(out_buffer));
+}
+
+static ssize_t bw_history_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct msm_isp_bw_req_info *isp_req_hist =
+ (struct msm_isp_bw_req_info *) t_file->private_data;
+
+ memset(isp_req_hist, 0, sizeof(msm_isp_bw_request_history));
+ msm_isp_bw_request_history_idx = 0;
+ return sizeof(msm_isp_bw_request_history);
+}
+
+static int ub_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t ub_info_read(struct file *t_file, char __user *t_char,
+ size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ char *out_buffer = ub_info_buffer;
+ char line_buffer[MAX_UB_INFO_LINE_BUFF_LEN] = {0};
+ struct vfe_device *vfe_dev =
+ (struct vfe_device *) t_file->private_data;
+ struct msm_isp_ub_info *ub_info = vfe_dev->ub_info;
+
+ memset(out_buffer, 0, MAX_UB_INFO_LINE_BUFF_LEN);
+ snprintf(line_buffer, sizeof(line_buffer),
+ "wm_ub_policy_type = %d\n"
+ "num_wm = %d\n"
+ "wm_ub = %d\n",
+ ub_info->policy, ub_info->num_wm, ub_info->wm_ub);
+ strlcat(out_buffer, line_buffer,
+ sizeof(ub_info_buffer));
+ for (i = 0; i < ub_info->num_wm; i++) {
+ snprintf(line_buffer, sizeof(line_buffer),
+ "data[%d] = 0x%x, addr[%d] = 0x%llx\n",
+ i, ub_info->data[i], i, ub_info->addr[i]);
+ strlcat(out_buffer, line_buffer,
+ sizeof(ub_info_buffer));
+ }
+
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, out_buffer, strlen(out_buffer));
+}
+
+static ssize_t ub_info_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct vfe_device *vfe_dev =
+ (struct vfe_device *) t_file->private_data;
+ struct msm_isp_ub_info *ub_info = vfe_dev->ub_info;
+
+ memset(ub_info, 0, sizeof(struct msm_isp_ub_info));
+
+ return sizeof(struct msm_isp_ub_info);
+}
+
+static const struct file_operations vfe_debugfs_error = {
+ .open = vfe_debugfs_statistics_open,
+ .read = vfe_debugfs_statistics_read,
+ .write = vfe_debugfs_statistics_write,
+};
+
+static const struct file_operations bw_history_ops = {
+ .open = bw_history_open,
+ .read = bw_history_read,
+ .write = bw_history_write,
+};
+
+static const struct file_operations ub_info_ops = {
+ .open = ub_info_open,
+ .read = ub_info_read,
+ .write = ub_info_write,
+};
+
+static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev,
+ struct msm_isp_bw_req_info *isp_req_hist)
+{
+ struct dentry *debugfs_base;
+ char dirname[32] = {0};
+
+ snprintf(dirname, sizeof(dirname), "msm_isp%d", vfe_dev->pdev->id);
+ debugfs_base = debugfs_create_dir(dirname, NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+ if (!debugfs_create_file("stats", 0644, debugfs_base,
+ vfe_dev, &vfe_debugfs_error))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("bw_req_history", 0644,
+ debugfs_base, isp_req_hist, &bw_history_ops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("ub_info", 0644,
+ debugfs_base, vfe_dev, &ub_info_ops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void msm_isp_update_req_history(uint32_t client, uint64_t ab,
+ uint64_t ib,
+ struct msm_isp_bandwidth_info *client_info,
+ unsigned long long ts)
+{
+ int i;
+
+ spin_lock(&req_history_lock);
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].client =
+ client;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].timestamp =
+ ts;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ab =
+ ab;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ib =
+ ib;
+
+ for (i = 0; i < MAX_ISP_CLIENT; i++) {
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].active = client_info[i].active;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].ab = client_info[i].ab;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].ib = client_info[i].ib;
+ }
+
+ msm_isp_bw_request_history_idx = (msm_isp_bw_request_history_idx + 1)
+ % MAX_DEPTH_BW_REQ_HISTORY;
+ spin_unlock(&req_history_lock);
+}
+
+void msm_isp_update_last_overflow_ab_ib(struct vfe_device *vfe_dev)
+{
+ spin_lock(&req_history_lock);
+ vfe_dev->msm_isp_last_overflow_ab =
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ab;
+ vfe_dev->msm_isp_last_overflow_ib =
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ib;
+ spin_unlock(&req_history_lock);
+}
+
+
+#ifdef CONFIG_COMPAT
+static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg)
+{
+ long rc;
+
+ if (is_compat_task()) {
+ struct msm_isp_event_data32 *event_data32;
+ struct msm_isp_event_data *event_data;
+ struct v4l2_event isp_event;
+ struct v4l2_event *isp_event_user;
+
+ memset(&isp_event, 0, sizeof(isp_event));
+ rc = v4l2_event_dequeue(vfh, &isp_event,
+ file->f_flags & O_NONBLOCK);
+ if (rc)
+ return rc;
+ event_data = (struct msm_isp_event_data *)
+ isp_event.u.data;
+ isp_event_user = (struct v4l2_event *)arg;
+ memcpy(isp_event_user, &isp_event,
+ sizeof(*isp_event_user));
+ event_data32 = (struct msm_isp_event_data32 *)
+ isp_event_user->u.data;
+ memset(event_data32, 0,
+ sizeof(struct msm_isp_event_data32));
+ event_data32->timestamp.tv_sec =
+ event_data->timestamp.tv_sec;
+ event_data32->timestamp.tv_usec =
+ event_data->timestamp.tv_usec;
+ event_data32->mono_timestamp.tv_sec =
+ event_data->mono_timestamp.tv_sec;
+ event_data32->mono_timestamp.tv_usec =
+ event_data->mono_timestamp.tv_usec;
+ event_data32->frame_id = event_data->frame_id;
+ memcpy(&(event_data32->u), &(event_data->u),
+ sizeof(event_data32->u));
+ } else {
+ rc = v4l2_event_dequeue(vfh, arg,
+ file->f_flags & O_NONBLOCK);
+ }
+ return rc;
+}
+#else
+static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg)
+{
+ return v4l2_event_dequeue(vfh, arg,
+ file->f_flags & O_NONBLOCK);
+}
+#endif
+
+static long msm_isp_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+
+ switch (cmd) {
+ case VIDIOC_DQEVENT: {
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+ return msm_isp_dqevent(file, vfh, arg);
+ }
+ break;
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+}
+
+static struct v4l2_subdev_core_ops msm_vfe_v4l2_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_vfe_v4l2_subdev_ops = {
+ .core = &msm_vfe_v4l2_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe_subdev_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
+};
+
+static long msm_isp_v4l2_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_isp_subdev_do_ioctl);
+}
+
+static void isp_vma_open(struct vm_area_struct *vma)
+{
+ pr_debug("%s: open called\n", __func__);
+}
+
+static void isp_vma_close(struct vm_area_struct *vma)
+{
+ pr_debug("%s: close called\n", __func__);
+}
+
+static int isp_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page;
+ struct vfe_device *vfe_dev = vma->vm_private_data;
+ struct isp_proc *isp_page = NULL;
+
+ isp_page = vfe_dev->isp_page;
+
+ pr_debug("%s: vfeid:%d u_virt_addr:0x%lx k_virt_addr:%pK\n",
+ __func__, vfe_dev->pdev->id, vma->vm_start,
+ (void *)isp_page);
+ if (isp_page != NULL) {
+ page = virt_to_page(isp_page);
+ get_page(page);
+ vmf->page = page;
+ isp_page->kernel_sofid =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ isp_page->vfeid = vfe_dev->pdev->id;
+ }
+ return 0;
+}
+
+static const struct vm_operations_struct isp_vm_ops = {
+ .open = isp_vma_open,
+ .close = isp_vma_close,
+ .fault = isp_vma_fault,
+};
+
+static int msm_isp_v4l2_fops_mmap(struct file *filep,
+ struct vm_area_struct *vma)
+{
+ int ret = -EINVAL;
+ struct video_device *vdev = video_devdata(filep);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ vma->vm_ops = &isp_vm_ops;
+ vma->vm_flags |=
+ (unsigned long)(VM_DONTEXPAND | VM_DONTDUMP);
+ vma->vm_private_data = vfe_dev;
+ isp_vma_open(vma);
+ ret = 0;
+ pr_debug("%s: isp mmap is called vm_start: 0x%lx\n",
+ __func__, vma->vm_start);
+ return ret;
+}
+
+static struct v4l2_file_operations msm_isp_v4l2_fops = {
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = msm_isp_v4l2_fops_ioctl,
+#endif
+ .unlocked_ioctl = msm_isp_v4l2_fops_ioctl,
+ .mmap = msm_isp_v4l2_fops_mmap
+};
+
+static int vfe_set_common_data(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct vfe_device *vfe_dev = NULL;
+
+ sd = (struct v4l2_subdev *)platform_get_drvdata(pdev);
+ if (!sd) {
+ pr_err("%s: Error! Cannot find subdev\n", __func__);
+ return -EPERM;
+ }
+ vfe_dev = (struct vfe_device *)v4l2_get_subdevdata(sd);
+ if (!vfe_dev) {
+ pr_err("%s: Error! Cannot find vfe_dev\n", __func__);
+ return -EPERM;
+ }
+
+ vfe_dev->common_data = (struct msm_vfe_common_dev_data *)
+ pdev->dev.platform_data;
+
+ vfe_dev->common_data->dual_vfe_res = &dualvfe;
+ vfe_dev->common_data->dual_vfe_res->axi_data[vfe_dev->pdev->id] =
+ &vfe_dev->axi_data;
+ vfe_dev->common_data->dual_vfe_res->stats_data[vfe_dev->pdev->id] =
+ &vfe_dev->stats_data;
+ vfe_dev->common_data->dual_vfe_res->vfe_dev[vfe_dev->pdev->id] =
+ vfe_dev;
+ return 0;
+}
+
+static int vfe_probe(struct platform_device *pdev)
+{
+ struct vfe_parent_device *vfe_parent_dev;
+ int rc = 0;
+ struct device_node *node;
+ struct platform_device *new_dev = NULL;
+ uint32_t i = 0;
+ char name[10] = "\0";
+
+ vfe_parent_dev = kzalloc(sizeof(struct vfe_parent_device),
+ GFP_KERNEL);
+ if (!vfe_parent_dev) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ vfe_parent_dev->common_sd = kzalloc(
+ sizeof(struct msm_vfe_common_subdev), GFP_KERNEL);
+ if (!vfe_parent_dev->common_sd) {
+ rc = -ENOMEM;
+ goto probe_fail1;
+ }
+
+ vfe_parent_dev->common_sd->common_data = &vfe_common_data;
+ mutex_init(&vfe_common_data.vfe_common_mutex);
+ spin_lock_init(&vfe_common_data.common_dev_data_lock);
+ spin_lock_init(&vfe_common_data.vfe_irq_dump.
+ common_dev_irq_dump_lock);
+ spin_lock_init(&vfe_common_data.vfe_irq_dump.
+ common_dev_tasklet_dump_lock);
+ for (i = 0; i < (VFE_AXI_SRC_MAX * MAX_VFE); i++)
+ spin_lock_init(&(vfe_common_data.streams[i].lock));
+ for (i = 0; i < (MSM_ISP_STATS_MAX * MAX_VFE); i++)
+ spin_lock_init(&(vfe_common_data.stats_streams[i].lock));
+
+ for (i = 0; i <= MAX_VFE; i++) {
+ INIT_LIST_HEAD(&vfe_common_data.tasklets[i].tasklet_q);
+ tasklet_init(&vfe_common_data.tasklets[i].tasklet,
+ msm_isp_do_tasklet,
+ (unsigned long)(&vfe_common_data.tasklets[i]));
+ spin_lock_init(&vfe_common_data.tasklets[i].tasklet_lock);
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "num_child", &vfe_parent_dev->num_hw_sd);
+
+ for (i = 0; i < vfe_parent_dev->num_hw_sd; i++) {
+ node = NULL;
+ snprintf(name, sizeof(name), "qcom,vfe%d", i);
+ node = of_find_node_by_name(NULL, name);
+ if (!node) {
+ pr_err("%s: Error! Cannot find node in dtsi %s\n",
+ __func__, name);
+ goto probe_fail2;
+ }
+ new_dev = of_find_device_by_node(node);
+ if (!new_dev) {
+ pr_err("%s: Failed to find device on bus %s\n",
+ __func__, node->name);
+ goto probe_fail2;
+ }
+ vfe_parent_dev->child_list[i] = new_dev;
+ new_dev->dev.platform_data =
+ (void *)vfe_parent_dev->common_sd->common_data;
+ rc = vfe_set_common_data(new_dev);
+ if (rc < 0)
+ goto probe_fail2;
+ }
+
+ vfe_parent_dev->num_sd = vfe_parent_dev->num_hw_sd;
+ vfe_parent_dev->pdev = pdev;
+
+ return rc;
+
+probe_fail2:
+ kfree(vfe_parent_dev->common_sd);
+probe_fail1:
+ kfree(vfe_parent_dev);
+end:
+ return rc;
+}
+
+int vfe_hw_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;
+
+ vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL);
+ if (!vfe_dev) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ vfe_dev->stats = kzalloc(sizeof(struct msm_isp_statistics), GFP_KERNEL);
+ if (!vfe_dev->stats) {
+ rc = -ENOMEM;
+ goto probe_fail1;
+ }
+
+ vfe_dev->ub_info = kzalloc(sizeof(struct msm_isp_ub_info), GFP_KERNEL);
+ if (!vfe_dev->ub_info) {
+ rc = -ENOMEM;
+ goto probe_fail2;
+ }
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &pdev->id);
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ vfe_dev->hw_info =
+ (struct msm_vfe_hardware_info *) match_dev->data;
+ /* Cx ipeak support */
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,vfe-cx-ipeak", NULL)) {
+ vfe_dev->vfe_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,vfe-cx-ipeak");
+ }
+ } else {
+ vfe_dev->hw_info = (struct msm_vfe_hardware_info *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+
+ if (!vfe_dev->hw_info) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ ISP_DBG("%s: device id = %d\n", __func__, pdev->id);
+
+ vfe_dev->pdev = pdev;
+
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_platform_data(vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to get platform resources\n", __func__);
+ rc = -ENOMEM;
+ goto probe_fail3;
+ }
+
+ v4l2_subdev_init(&vfe_dev->subdev.sd, &msm_vfe_v4l2_subdev_ops);
+ vfe_dev->subdev.sd.internal_ops =
+ &msm_vfe_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->realtime_mutex);
+ mutex_init(&vfe_dev->core_mutex);
+ spin_lock_init(&vfe_dev->shared_data_lock);
+ spin_lock_init(&vfe_dev->reg_update_lock);
+ spin_lock_init(&req_history_lock);
+ spin_lock_init(&vfe_dev->completion_lock);
+ media_entity_pads_init(&vfe_dev->subdev.sd.entity, 0, NULL);
+ vfe_dev->subdev.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ //vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE;
+ vfe_dev->subdev.sd.entity.name = pdev->name;
+ vfe_dev->subdev.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x2;
+ rc = msm_sd_register(&vfe_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto probe_fail3;
+ }
+ msm_cam_copy_v4l2_subdev_fops(&msm_isp_v4l2_fops);
+ msm_isp_v4l2_fops.unlocked_ioctl = msm_isp_v4l2_fops_ioctl;
+#ifdef CONFIG_COMPAT
+ msm_isp_v4l2_fops.compat_ioctl32 =
+ msm_isp_v4l2_fops_ioctl;
+#endif
+ msm_isp_v4l2_fops.mmap = msm_isp_v4l2_fops_mmap;
+
+ vfe_dev->subdev.sd.devnode->fops = &msm_isp_v4l2_fops;
+
+ 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, &pdev->dev,
+ vfe_dev->hw_info->axi_hw_info->scratch_buf_range);
+ if (rc < 0) {
+ pr_err("%s: Unable to create buffer manager\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ msm_isp_enable_debugfs(vfe_dev, msm_isp_bw_request_history);
+ vfe_dev->buf_mgr->init_done = 1;
+ vfe_dev->vfe_open_cnt = 0;
+ /*Allocate a page in kernel and map it to camera user process*/
+ vfe_dev->isp_page = (struct isp_proc *)get_zeroed_page(GFP_KERNEL);
+ if (vfe_dev->isp_page == NULL) {
+ pr_err("%s: no enough memory\n", __func__);
+ rc = -ENOMEM;
+ goto probe_fail3;
+ }
+ vfe_dev->isp_page->vfeid = vfe_dev->pdev->id;
+ return rc;
+
+probe_fail3:
+ kfree(vfe_dev->ub_info);
+probe_fail2:
+ kfree(vfe_dev->stats);
+probe_fail1:
+ kfree(vfe_dev);
+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,
+ },
+};
+
+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);
+}
+
+late_initcall(msm_vfe_init_module);
+module_exit(msm_vfe_exit_module);
+MODULE_DESCRIPTION("MSM VFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
new file mode 100644
index 0000000..cc1ef7a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -0,0 +1,865 @@
+/* Copyright (c) 2013-2018, 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>
+#ifdef CONFIG_MSM_AVTIMER
+#include <linux/avtimer_kernel.h>
+#endif
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+
+#include "msm_buf_mgr.h"
+#include "cam_hw_ops.h"
+#include <soc/qcom/cx_ipeak.h>
+
+#define VFE40_8974V1_VERSION 0x10000018
+#define VFE40_8974V2_VERSION 0x1001001A
+#define VFE40_8974V3_VERSION 0x1001001B
+#define VFE40_8x26_VERSION 0x20000013
+#define VFE40_8x26V2_VERSION 0x20010014
+#define VFE40_8916_VERSION 0x10030000
+#define VFE40_8939_VERSION 0x10040000
+#define VFE40_8952_VERSION 0x10060000
+#define VFE40_8976_VERSION 0x10050000
+#define VFE40_8937_VERSION 0x10080000
+#define VFE40_8917_VERSION 0x10080001
+#define VFE40_8953_VERSION 0x10090000
+#define VFE32_8909_VERSION 0x30600
+
+#define MAX_IOMMU_CTX 2
+#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 MAX_REG_UPDATE_THRESHOLD 10
+#define ISP_Q2 (1 << 2)
+
+#define VFE_PING_FLAG 0xFFFFFFFF
+#define VFE_PONG_FLAG 0x0
+
+#define VFE_MAX_CFG_TIMEOUT 3000
+#define VFE_CLK_INFO_MAX 16
+#define STATS_COMP_BIT_MASK 0x1FF
+
+#define MSM_ISP_MIN_AB 100000000
+#define MSM_ISP_MIN_IB 100000000
+#define MAX_BUFFERS_IN_HW 2
+
+#define MAX_VFE 2
+#define MAX_VFE_IRQ_DEBUG_DUMP_SIZE 10
+#define MAX_RECOVERY_THRESHOLD 5
+
+struct vfe_device;
+struct msm_vfe_axi_stream;
+struct msm_vfe_stats_stream;
+
+#define VFE_SD_HW_MAX VFE_SD_COMMON
+
+/* Irq operations to perform on the irq mask register */
+enum msm_isp_irq_operation {
+ /* enable the irq bits in given parameters */
+ MSM_ISP_IRQ_ENABLE = 1,
+ /* disable the irq bits in the given parameters */
+ MSM_ISP_IRQ_DISABLE = 2,
+ /* set the irq bits to the given parameters */
+ MSM_ISP_IRQ_SET = 3,
+};
+
+/* This struct is used to save/track SOF info for some INTF.
+ * e.g. used in Master-Slave mode
+ */
+struct msm_vfe_sof_info {
+ uint32_t timestamp_ms;
+ uint32_t mono_timestamp_ms;
+ uint32_t frame_id;
+};
+
+/* Each INTF in Master-Slave mode uses this struct. */
+struct msm_vfe_dual_hw_ms_info {
+ /* type is Master/Slave */
+ enum msm_vfe_dual_hw_ms_type dual_hw_ms_type;
+ enum msm_vfe_dual_cam_sync_mode sync_state;
+ struct msm_vfe_sof_info sof_info;
+ int index;
+};
+
+struct vfe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum msm_isp_pack_fmt {
+ QCOM,
+ MIPI,
+ DPCM6,
+ DPCM8,
+ PLAIN8,
+ PLAIN16,
+ DPCM10,
+ MAX_ISP_PACK_FMT,
+};
+
+enum msm_isp_camif_update_state {
+ NO_UPDATE,
+ ENABLE_CAMIF,
+ DISABLE_CAMIF,
+ DISABLE_CAMIF_IMMEDIATELY
+};
+
+struct msm_isp_timestamp {
+ /*Monotonic clock for v4l2 buffer*/
+ struct timeval buf_time;
+ /*Monotonic clock for VT */
+ struct timeval vt_time;
+ /*Wall clock for userspace event*/
+ struct timeval event_time;
+};
+
+struct msm_vfe_irq_ops {
+ void (*read_and_clear_irq_status)(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+ 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,
+ struct msm_isp_timestamp *ts);
+ void (*process_epoch_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ 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,
+ struct msm_isp_timestamp *ts);
+ void (*process_axi_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*process_stats_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*config_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ enum msm_isp_irq_operation);
+ void (*preprocess_camif_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
+};
+
+struct msm_vfe_axi_ops {
+ void (*reload_wm)(struct vfe_device *vfe_dev, void __iomem *vfe_base,
+ uint32_t reload_mask);
+ void (*enable_wm)(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable);
+ int32_t (*cfg_io_format)(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src,
+ uint32_t io_format);
+ void (*cfg_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t framedrop_pattern, uint32_t framedrop_period);
+ 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 *stream_info,
+ 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 *stream_info,
+ 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_ub)(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+ void (*read_wm_ping_pong_addr)(struct vfe_device *vfe_dev);
+ void (*update_ping_pong_addr)(void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size);
+ 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);
+ int (*halt)(struct vfe_device *vfe_dev, uint32_t blocking);
+ void (*restart)(struct vfe_device *vfe_dev, uint32_t blocking,
+ uint32_t enable_camif);
+ void (*update_cgc_override)(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t cgc_override);
+ uint32_t (*ub_reg_offset)(struct vfe_device *vfe_dev, int idx);
+ uint32_t (*get_ub_size)(struct vfe_device *vfe_dev);
+};
+
+struct msm_vfe_core_ops {
+ void (*reg_update)(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+ long (*reset_hw)(struct vfe_device *vfe_dev, uint32_t first_start,
+ uint32_t blocking_call);
+ int (*init_hw)(struct vfe_device *vfe_dev);
+ void (*init_hw_reg)(struct vfe_device *vfe_dev);
+ void (*clear_status_reg)(struct vfe_device *vfe_dev);
+ void (*release_hw)(struct vfe_device *vfe_dev);
+ void (*cfg_input_mux)(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+ int (*start_fetch_eng)(struct vfe_device *vfe_dev,
+ void *arg);
+ void (*update_camif_state)(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state);
+ void (*cfg_rdi_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src);
+ void (*get_error_mask)(uint32_t *error_mask0, uint32_t *error_mask1);
+ void (*process_error_status)(struct vfe_device *vfe_dev);
+ void (*get_overflow_mask)(uint32_t *overflow_mask);
+ void (*get_irq_mask)(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask);
+ void (*get_halt_restart_mask)(uint32_t *irq0_mask,
+ uint32_t *irq1_mask);
+ void (*get_rdi_wm_mask)(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask);
+ bool (*is_module_cfg_lock_needed)(uint32_t reg_offset);
+ int (*ahb_clk_cfg)(struct vfe_device *vfe_dev,
+ struct msm_isp_ahb_clk_cfg *ahb_cfg);
+ int (*start_fetch_eng_multi_pass)(struct vfe_device *vfe_dev,
+ void *arg);
+ void (*set_halt_restart_mask)(struct vfe_device *vfe_dev);
+ void (*set_bus_err_ign_mask)(struct vfe_device *vfe_dev,
+ int wm, int enable);
+ void (*get_bus_err_mask)(struct vfe_device *vfe_dev,
+ uint32_t *bus_err, uint32_t *irq_status1);
+};
+
+struct msm_vfe_stats_ops {
+ int (*get_stats_idx)(enum msm_isp_stats_type stats_type);
+ int (*check_streams)(struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t framedrop_pattern, uint32_t framedrop_period);
+ void (*clear_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_comp_mask)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t comp_index,
+ uint8_t enable);
+ 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 *stream_info);
+ 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 (*enable_module)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+
+ void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
+
+ 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);
+ uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev);
+
+ void (*update_cgc_override)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+ void (*enable_stats_wm)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+};
+
+enum msm_isp_hw_client {
+ ISP_VFE0,
+ ISP_VFE1,
+ ISP_CPP,
+ MAX_ISP_CLIENT,
+};
+
+struct msm_isp_bandwidth_info {
+ uint32_t active;
+ uint64_t ab;
+ uint64_t ib;
+};
+
+struct msm_isp_bandwidth_mgr {
+ uint32_t bus_client;
+ uint32_t bus_vector_active_idx;
+ uint32_t use_count;
+ struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT];
+ int (*update_bw)(struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+ void (*deinit_bw_mgr)(struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+};
+
+struct msm_vfe_platform_ops {
+ int (*get_platform_data)(struct vfe_device *vfe_dev);
+ int (*enable_clks)(struct vfe_device *vfe_dev, int enable);
+ int (*get_clks)(struct vfe_device *vfe_dev);
+ void (*put_clks)(struct vfe_device *vfe_dev);
+ int (*get_clk_rates)(struct vfe_device *vfe_dev,
+ struct msm_isp_clk_rates *rates);
+ int (*get_max_clk_rate)(struct vfe_device *vfe_dev, long *rate);
+ int (*set_clk_rate)(struct vfe_device *vfe_dev, long *rate);
+ int (*enable_regulators)(struct vfe_device *vfe_dev, int enable);
+ int (*get_regulators)(struct vfe_device *vfe_dev);
+ void (*put_regulators)(struct vfe_device *vfe_dev);
+ int (*init_bw_mgr)(struct vfe_device *vfe_dev,
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+ int (*update_bw)(struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+ void (*deinit_bw_mgr)(struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+};
+
+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_platform_ops platform_ops;
+};
+
+struct msm_vfe_hardware_info {
+ int num_iommu_ctx;
+ /* secure iommu ctx nums */
+ int num_iommu_secure_ctx;
+ int vfe_clk_idx;
+ int runtime_axi_update;
+ struct msm_vfe_ops vfe_ops;
+ struct msm_vfe_axi_hardware_info *axi_hw_info;
+ struct msm_vfe_stats_hardware_info *stats_hw_info;
+ uint32_t dmi_reg_offset;
+ uint32_t min_ab;
+ uint32_t min_ib;
+ const char *regulator_names[];
+};
+
+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;
+ uint32_t scratch_buf_range;
+};
+
+enum msm_vfe_axi_state {
+ AVAILABLE,
+ INACTIVE,
+ ACTIVE,
+ PAUSED,
+ START_PENDING,
+ STOP_PENDING,
+ PAUSE_PENDING,
+ RESUME_PENDING,
+ STARTING,
+ STOPPING,
+ PAUSING,
+ RESUMING,
+ UPDATING,
+};
+
+#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_frame_request_queue {
+ struct list_head list;
+ enum msm_vfe_buff_queue_id buff_queue_id;
+ uint32_t buf_index;
+ uint8_t cmd_used;
+};
+
+enum msm_isp_comp_irq_types {
+ MSM_ISP_COMP_IRQ_REG_UPD = 0,
+ MSM_ISP_COMP_IRQ_EPOCH = 1,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE = 2,
+ MSM_ISP_COMP_IRQ_PONG_BUFDONE = 3,
+ MSM_ISP_COMP_IRQ_MAX = 4
+};
+
+#define MSM_VFE_REQUESTQ_SIZE 8
+
+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_VFE][MAX_PLANES_PER_STREAM];
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_VFE][MAX_PLANES_PER_STREAM];
+ uint8_t comp_mask_index[MAX_VFE];
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle[VFE_BUF_QUEUE_MAX];
+ uint8_t controllable_output;
+ uint8_t undelivered_request_cnt;
+ uint8_t request_q_idx;
+ uint32_t request_q_cnt;
+ struct list_head request_q;
+ struct msm_vfe_frame_request_queue
+ request_queue_cmd[MSM_VFE_REQUESTQ_SIZE];
+ uint32_t stream_handle[MAX_VFE];
+ uint8_t buf_divert;
+ enum msm_vfe_axi_stream_type stream_type;
+ uint32_t frame_based;
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern;
+ uint32_t current_framedrop_period; /* user requested period*/
+ uint32_t requested_framedrop_period; /* requested period*/
+ uint32_t activated_framedrop_period; /* active hw period */
+ uint32_t num_burst_capture;/*number of frame to capture*/
+ uint32_t init_frame_drop;
+ spinlock_t lock;
+
+ /*Bandwidth calculation info*/
+ uint32_t max_width[MAX_VFE];
+ /*Based on format plane size in Q2. e.g NV12 = 1.5*/
+ uint32_t format_factor;
+ uint32_t bandwidth[MAX_VFE];
+
+ uint32_t runtime_num_burst_capture;
+ uint32_t runtime_output_format;
+ enum msm_stream_rdi_input_type rdi_input_type;
+ struct msm_isp_sw_framskip sw_skip;
+ int8_t sw_ping_pong_bit;
+
+ struct vfe_device *vfe_dev[MAX_VFE];
+ int num_isp;
+ struct completion active_comp;
+ struct completion inactive_comp;
+ uint32_t update_vfe_mask;
+ /*
+ * bits in this mask are set that correspond to vfe_id of
+ * the vfe on which this stream operates
+ */
+ uint32_t vfe_mask;
+ uint32_t composite_irq[MSM_ISP_COMP_IRQ_MAX];
+ int lpm_mode;
+};
+
+struct msm_vfe_axi_composite_info {
+ uint32_t stream_handle;
+ uint32_t stream_composite_mask;
+};
+
+enum msm_vfe_camif_state {
+ CAMIF_ENABLE,
+ CAMIF_DISABLE,
+};
+
+struct msm_vfe_src_info {
+ uint32_t frame_id;
+ uint32_t reg_update_frame_id;
+ uint8_t active;
+ uint8_t stream_count;
+ uint8_t raw_stream_count;
+ enum msm_vfe_inputmux input_mux;
+ uint32_t width;
+ long pixel_clock;
+ uint32_t input_format;/*V4L2 pix format with bayer pattern*/
+ uint32_t last_updt_frm_id;
+ uint32_t sof_counter_step;
+ struct timeval time_stamp;
+ enum msm_vfe_dual_hw_type dual_hw_type;
+ struct msm_vfe_dual_hw_ms_info dual_hw_ms_info;
+ bool accept_frame;
+ uint32_t lpm;
+};
+
+struct msm_vfe_fetch_engine_info {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t buf_idx;
+ uint8_t is_busy;
+ uint8_t offline_mode;
+ uint32_t fd;
+};
+
+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;
+ 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 num_active_stream;
+ uint8_t num_rdi_stream;
+ uint8_t num_pix_stream;
+ uint32_t rdi_wm_mask;
+ struct msm_vfe_axi_composite_info
+ composite_info[MAX_NUM_COMPOSITE_MASK];
+ uint8_t num_used_composite_mask;
+ atomic_t axi_cfg_update[VFE_SRC_MAX];
+ struct msm_vfe_src_info src_info[VFE_SRC_MAX];
+ uint16_t stream_handle_cnt;
+ uint32_t event_mask;
+ uint8_t enable_frameid_recovery;
+ uint8_t recovery_count;
+};
+
+struct msm_vfe_stats_hardware_info {
+ uint32_t stats_capability_mask;
+ uint8_t *stats_ping_pong_offset;
+ uint8_t *stats_wm_index;
+ uint8_t num_stats_type;
+ uint8_t num_stats_comp_mask;
+};
+
+enum msm_vfe_stats_state {
+ STATS_AVAILABLE,
+ STATS_INACTIVE,
+ STATS_ACTIVE,
+ STATS_START_PENDING,
+ STATS_STOP_PENDING,
+ STATS_STARTING,
+ STATS_STOPPING,
+};
+
+struct msm_vfe_stats_stream {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t stream_handle[MAX_VFE];
+ uint32_t composite_flag;
+ enum msm_isp_stats_type stats_type;
+ enum msm_vfe_stats_state state;
+ uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
+ uint32_t irq_subsample_pattern;
+ uint32_t init_stats_frame_drop;
+ struct msm_isp_sw_framskip sw_skip;
+
+ uint32_t buffer_offset[MAX_VFE];
+ struct msm_isp_buffer *buf[2];
+ uint32_t bufq_handle;
+
+ spinlock_t lock;
+ struct vfe_device *vfe_dev[MAX_VFE];
+ int num_isp;
+ struct completion active_comp;
+ struct completion inactive_comp;
+ /*
+ * bits in this mask are set that correspond to vfe_id of
+ * the vfe on which this stream operates
+ */
+ uint32_t vfe_mask;
+ uint32_t composite_irq[MSM_ISP_COMP_IRQ_MAX];
+};
+
+struct msm_vfe_stats_shared_data {
+ uint8_t num_active_stream;
+ atomic_t stats_comp_mask[MAX_NUM_STATS_COMP_MASK];
+ uint16_t stream_handle_cnt;
+};
+
+struct msm_vfe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct msm_isp_timestamp ts;
+ uint8_t cmd_used;
+ struct vfe_device *vfe_dev;
+};
+
+#define MSM_VFE_TASKLETQ_SIZE 400
+
+enum msm_vfe_overflow_state {
+ NO_OVERFLOW,
+ OVERFLOW_DETECTED,
+ HALT_ENFORCED,
+};
+
+struct msm_vfe_error_info {
+ atomic_t overflow_state;
+ uint32_t error_mask0;
+ uint32_t error_mask1;
+ uint32_t violation_status;
+ uint32_t camif_status;
+ uint8_t stream_framedrop_count[BUF_MGR_NUM_BUF_Q];
+ uint8_t stats_framedrop_count[MSM_ISP_STATS_MAX];
+ uint32_t info_dump_frame_count;
+ uint32_t error_count;
+ uint32_t framedrop_flag;
+};
+
+struct msm_isp_statistics {
+ int64_t imagemaster0_overflow;
+ int64_t imagemaster1_overflow;
+ int64_t imagemaster2_overflow;
+ int64_t imagemaster3_overflow;
+ int64_t imagemaster4_overflow;
+ int64_t imagemaster5_overflow;
+ int64_t imagemaster6_overflow;
+ int64_t be_overflow;
+ int64_t bg_overflow;
+ int64_t bf_overflow;
+ int64_t awb_overflow;
+ int64_t rs_overflow;
+ int64_t cs_overflow;
+ int64_t ihist_overflow;
+ int64_t skinbhist_overflow;
+ int64_t bfscale_overflow;
+
+ int64_t isp_vfe0_active;
+ int64_t isp_vfe0_ab;
+ int64_t isp_vfe0_ib;
+
+ int64_t isp_vfe1_active;
+ int64_t isp_vfe1_ab;
+ int64_t isp_vfe1_ib;
+
+ int64_t isp_cpp_active;
+ int64_t isp_cpp_ab;
+ int64_t isp_cpp_ib;
+
+ int64_t last_overflow_ab;
+ int64_t last_overflow_ib;
+
+ int64_t vfe_clk_rate;
+ int64_t cpp_clk_rate;
+};
+
+struct msm_isp_bw_req_info {
+ uint32_t client;
+ unsigned long long timestamp;
+ uint64_t total_ab;
+ uint64_t total_ib;
+ struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT];
+};
+
+#define MSM_ISP_MAX_WM 7
+struct msm_isp_ub_info {
+ enum msm_wm_ub_cfg_type policy;
+ uint8_t num_wm;
+ uint32_t wm_ub;
+ uint32_t data[MSM_ISP_MAX_WM];
+ uint64_t addr[MSM_ISP_MAX_WM];
+};
+
+struct msm_vfe_hw_init_parms {
+ const char *entries;
+ const char *regs;
+ const char *settings;
+};
+
+struct dual_vfe_resource {
+ struct vfe_device *vfe_dev[MAX_VFE];
+ void __iomem *vfe_base[MAX_VFE];
+ uint32_t reg_update_mask[MAX_VFE];
+ struct msm_vfe_stats_shared_data *stats_data[MAX_VFE];
+ struct msm_vfe_axi_shared_data *axi_data[MAX_VFE];
+ uint32_t wm_reload_mask[MAX_VFE];
+};
+
+struct master_slave_resource_info {
+ enum msm_vfe_dual_hw_type dual_hw_type;
+ uint32_t sof_delta_threshold; /* Updated by Master */
+ uint32_t active_src_mask;
+ uint32_t src_sof_mask;
+ int master_index;
+ int primary_slv_idx;
+ struct msm_vfe_src_info *src_info[MAX_VFE * VFE_SRC_MAX];
+ uint32_t num_src;
+ enum msm_vfe_dual_cam_sync_mode dual_sync_mode;
+};
+
+struct msm_vfe_irq_debug_info {
+ uint32_t vfe_id;
+ struct msm_isp_timestamp ts;
+ uint32_t core_id;
+ uint32_t irq_status0[MAX_VFE];
+ uint32_t irq_status1[MAX_VFE];
+ uint32_t ping_pong_status[MAX_VFE];
+};
+
+struct msm_vfe_irq_dump {
+ spinlock_t common_dev_irq_dump_lock;
+ spinlock_t common_dev_tasklet_dump_lock;
+ uint8_t current_irq_index;
+ uint8_t current_tasklet_index;
+ struct msm_vfe_irq_debug_info
+ irq_debug[MAX_VFE_IRQ_DEBUG_DUMP_SIZE];
+ struct msm_vfe_irq_debug_info
+ tasklet_debug[MAX_VFE_IRQ_DEBUG_DUMP_SIZE];
+};
+
+struct msm_vfe_tasklet {
+ spinlock_t tasklet_lock;
+ uint8_t taskletq_idx;
+ struct list_head tasklet_q;
+ struct tasklet_struct tasklet;
+ struct msm_vfe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
+};
+
+struct msm_vfe_common_dev_data {
+ spinlock_t common_dev_data_lock;
+ struct dual_vfe_resource *dual_vfe_res;
+ struct master_slave_resource_info ms_resource;
+ struct msm_vfe_axi_stream streams[VFE_AXI_SRC_MAX * MAX_VFE];
+ struct msm_vfe_stats_stream stats_streams[MSM_ISP_STATS_MAX * MAX_VFE];
+ struct mutex vfe_common_mutex;
+ uint8_t pd_buf_idx;
+ /* Irq debug Info */
+ struct msm_vfe_irq_dump vfe_irq_dump;
+ struct msm_vfe_tasklet tasklets[MAX_VFE + 1];
+};
+
+struct msm_vfe_common_subdev {
+ /* parent reference */
+ struct vfe_parent_device *parent;
+
+ /* Media Subdevice */
+ struct msm_sd_subdev *subdev;
+
+ /* Buf Mgr */
+ struct msm_isp_buf_mgr *buf_mgr;
+
+ /* Common Data */
+ struct msm_vfe_common_dev_data *common_data;
+};
+
+struct isp_proc {
+ uint32_t kernel_sofid;
+ uint32_t vfeid;
+};
+
+struct vfe_device {
+ /* Driver private data */
+ struct platform_device *pdev;
+ struct msm_vfe_common_dev_data *common_data;
+ struct msm_sd_subdev subdev;
+ struct msm_isp_buf_mgr *buf_mgr;
+
+ /* Resource info */
+ struct resource *vfe_irq;
+ void __iomem *vfe_base;
+ uint32_t vfe_base_size;
+ void __iomem *vfe_vbif_base;
+ uint32_t vfe_vbif_base_size;
+ struct device *iommu_ctx[MAX_IOMMU_CTX];
+ struct msm_cam_regulator *regulator_info;
+ uint32_t vfe_num_regulators;
+ struct clk **vfe_clk;
+ struct msm_cam_clk_info *vfe_clk_info;
+ uint32_t **vfe_clk_rates;
+ size_t num_clk;
+ size_t num_rates;
+ struct clk **hvx_clk;
+ struct msm_cam_clk_info *hvx_clk_info;
+ size_t num_hvx_clk;
+ size_t num_norm_clk;
+ bool hvx_clk_state;
+ enum cam_ahb_clk_vote ahb_vote;
+ enum cam_ahb_clk_vote user_requested_ahb_vote;
+ struct cx_ipeak_client *vfe_cx_ipeak;
+
+ /* Sync variables*/
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct mutex realtime_mutex;
+ struct mutex core_mutex;
+ spinlock_t shared_data_lock;
+ spinlock_t reg_update_lock;
+ spinlock_t completion_lock;
+
+ /* Tasklet info */
+ atomic_t irq_cnt;
+
+ /* Data structures */
+ 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_vfe_error_info error_info;
+ struct msm_vfe_fetch_engine_info fetch_engine_info;
+ enum msm_vfe_hvx_streaming_cmd hvx_cmd;
+ uint8_t cur_hvx_state;
+
+ /* State variables */
+ uint32_t vfe_hw_version;
+ uint32_t vfe_open_cnt;
+ uint8_t vt_enable;
+ uint32_t vfe_ub_policy;
+ uint8_t reset_pending;
+ uint8_t reg_update_requested;
+ uint8_t reg_updated;
+ uint32_t is_split;
+ uint32_t dual_vfe_enable;
+ unsigned long page_fault_addr;
+ uint32_t vfe_hw_limit;
+
+ /* Debug variables */
+ int dump_reg;
+ struct msm_isp_statistics *stats;
+ uint64_t msm_isp_last_overflow_ab;
+ uint64_t msm_isp_last_overflow_ib;
+ struct msm_isp_ub_info *ub_info;
+ int32_t isp_sof_debug;
+ int32_t isp_raw0_debug;
+ int32_t isp_raw1_debug;
+ int32_t isp_raw2_debug;
+
+ /* irq info */
+ uint32_t irq0_mask;
+ uint32_t irq1_mask;
+ uint32_t bus_err_ign_mask;
+ uint32_t recovery_irq0_mask;
+ uint32_t recovery_irq1_mask;
+ /* total bandwidth per vfe */
+ uint64_t total_bandwidth;
+ struct isp_proc *isp_page;
+};
+
+struct vfe_parent_device {
+ struct platform_device *pdev;
+ uint32_t num_sd;
+ uint32_t num_hw_sd;
+ struct platform_device *child_list[VFE_SD_HW_MAX];
+ struct msm_vfe_common_subdev *common_sd;
+};
+
+int vfe_hw_probe(struct platform_device *pdev);
+void msm_isp_update_last_overflow_ab_ib(struct vfe_device *vfe_dev);
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
new file mode 100644
index 0000000..6da1360
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -0,0 +1,1539 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "msm_isp32.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"
+
+static const struct platform_device_id msm_vfe32_dev_id[] = {
+ {"msm_vfe32", (kernel_ulong_t) &vfe32_hw_info},
+ {}
+};
+
+#define VFE32_BURST_LEN 2
+#define VFE32_UB_SIZE 1024
+#define VFE32_UB_SIZE_32KB 2048
+#define VFE32_EQUAL_SLICE_UB 194
+#define VFE32_AXI_SLICE_UB 792
+#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
+#define VFE32_RDI_BASE(idx) (idx ? 0x734 + 0x4 * (idx - 1) : 0x06FC)
+#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) & 0x1)))
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 7, 8, 9, 10, 11, 12, 13};
+
+#define VFE32_NUM_STATS_TYPE 7
+#define VFE32_STATS_BASE(idx) (0xF4 + 0xC * idx)
+#define VFE32_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE32_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (stats_pingpong_offset_map[idx])) & 0x1))
+
+#define VFE32_CLK_IDX 1
+#define MSM_ISP32_TOTAL_WM_UB 792
+/*792 double word*/
+
+static struct msm_cam_clk_info msm_vfe32_1_clk_info[VFE_CLK_INFO_MAX];
+
+static struct msm_cam_clk_info msm_vfe32_2_clk_info[] = {
+ /*vfe32 clock info for A-family: 8960 */
+ {"vfe_clk", 266667000},
+ {"vfe_pclk", -1},
+ {"csi_vfe_clk", -1},
+};
+
+static uint32_t msm_vfe32_ub_reg_offset(struct vfe_device *vfe_dev, int idx)
+{
+ return (VFE32_WM_BASE(idx) + 0x10);
+}
+
+static uint32_t msm_vfe32_get_ub_size(struct vfe_device *vfe_dev)
+{
+ return MSM_ISP32_TOTAL_WM_UB;
+}
+
+static int32_t msm_vfe32_init_qos_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *qos_parms,
+ struct msm_vfe_hw_init_parms *ds_parms)
+{
+ void __iomem *vfebase = vfe_dev->vfe_base;
+ struct device_node *of_node;
+ uint32_t *ds_settings = NULL, *ds_regs = NULL, ds_entries = 0;
+ int32_t i = 0, rc = 0;
+ uint32_t *qos_settings = NULL, *qos_regs = NULL, qos_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, qos_parms->entries,
+ &qos_entries);
+ if (rc < 0 || !qos_entries) {
+ pr_err("%s: NO QOS entries found\n", __func__);
+ } else {
+ qos_settings = kcalloc(qos_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!qos_settings)
+ return -ENOMEM;
+ qos_regs = kcalloc(qos_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!qos_regs) {
+ kfree(qos_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, qos_parms->regs,
+ qos_regs, qos_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS BUS BDG info\n", __func__);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ } else {
+ if (qos_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ qos_parms->settings,
+ qos_settings, qos_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS settings\n",
+ __func__);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ } else {
+ for (i = 0; i < qos_entries; i++)
+ msm_camera_io_w(qos_settings[i],
+ vfebase + qos_regs[i]);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ }
+ } else {
+ kfree(qos_settings);
+ kfree(qos_regs);
+ }
+ }
+ }
+ rc = of_property_read_u32(of_node, ds_parms->entries,
+ &ds_entries);
+ if (rc < 0 || !ds_entries) {
+ pr_err("%s: NO D/S entries found\n", __func__);
+ } else {
+ ds_settings = kcalloc(ds_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!ds_settings)
+ return -ENOMEM;
+ ds_regs = kcalloc(ds_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!ds_regs) {
+ kfree(ds_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, ds_parms->regs,
+ ds_regs, ds_entries);
+ if (rc < 0) {
+ pr_err("%s: NO D/S register info\n", __func__);
+ kfree(ds_settings);
+ kfree(ds_regs);
+ } else {
+ if (ds_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ ds_parms->settings, ds_settings,
+ ds_entries);
+ if (rc < 0) {
+ pr_err("%s: NO D/S settings\n",
+ __func__);
+ kfree(ds_settings);
+ kfree(ds_regs);
+ } else {
+ for (i = 0; i < ds_entries; i++)
+ msm_camera_io_w(ds_settings[i],
+ vfebase + ds_regs[i]);
+ kfree(ds_regs);
+ kfree(ds_settings);
+ }
+ } else {
+ kfree(ds_regs);
+ kfree(ds_settings);
+ }
+ }
+ }
+ return 0;
+}
+
+static int32_t msm_vfe32_init_vbif_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *vbif_parms)
+{
+ void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *vbif_settings = NULL, *vbif_regs = NULL, vbif_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, vbif_parms->entries,
+ &vbif_entries);
+ if (rc < 0 || !vbif_entries) {
+ pr_err("%s: NO VBIF entries found\n", __func__);
+ } else {
+ vbif_settings = kcalloc(vbif_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!vbif_settings)
+ return -ENOMEM;
+ vbif_regs = kcalloc(vbif_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!vbif_regs) {
+ kfree(vbif_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, vbif_parms->regs,
+ vbif_regs, vbif_entries);
+ if (rc < 0) {
+ pr_err("%s: NO VBIF info\n", __func__);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ } else {
+ rc = of_property_read_u32_array(of_node,
+ vbif_parms->settings,
+ vbif_settings, vbif_entries);
+ if (rc < 0) {
+ pr_err("%s: NO VBIF settings\n",
+ __func__);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ } else {
+ for (i = 0; i < vbif_entries; i++)
+ msm_camera_io_w(
+ vbif_settings[i],
+ vfe_vbif_base + vbif_regs[i]);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ }
+ }
+ }
+ return 0;
+}
+
+static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+
+ vfe_dev->vfe_clk_idx = 0;
+ rc = msm_isp_init_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
+ if (rc < 0) {
+ pr_err("%s: Bandwidth registration Failed!\n", __func__);
+ goto bus_scale_register_failed;
+ }
+
+ 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_isp_get_clk_info(vfe_dev, vfe_dev->pdev,
+ &msm_vfe32_1_clk_info[0]);
+ if (rc < 0) {
+ pr_err("msm_isp_get_clk_info() failed\n");
+ goto fs_failed;
+ }
+
+ if (vfe_dev->num_clk <= 0) {
+ pr_err("%s: Invalid num of clock\n", __func__);
+ goto fs_failed;
+ } else {
+ vfe_dev->vfe_clk =
+ kzalloc(sizeof(struct clk *) * vfe_dev->num_clk,
+ GFP_KERNEL);
+ if (!vfe_dev->vfe_clk) {
+ pr_err("%s:%d No memory\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_1_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_1_clk_info), 1);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+ else
+ vfe_dev->vfe_clk_idx = 2;
+ } else
+ vfe_dev->vfe_clk_idx = 1;
+
+ 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->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] =
+ vfe_dev->vfe_base;
+
+ 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;
+ }
+
+ return rc;
+irq_req_failed:
+ iounmap(vfe_dev->vfe_vbif_base);
+ vfe_dev->vfe_vbif_base = NULL;
+vbif_remap_failed:
+ iounmap(vfe_dev->vfe_base);
+ vfe_dev->vfe_base = NULL;
+vfe_remap_failed:
+ if (vfe_dev->vfe_clk_idx == 1)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_1_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_1_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 2)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 0);
+clk_enable_failed:
+ if (vfe_dev->fs_vfe)
+ regulator_disable(vfe_dev->fs_vfe);
+ kfree(vfe_dev->vfe_clk);
+fs_failed:
+ msm_isp_deinit_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
+bus_scale_register_failed:
+ return rc;
+}
+
+static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev)
+{
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x20);
+ disable_irq(vfe_dev->vfe_irq->start);
+ free_irq(vfe_dev->vfe_irq->start, vfe_dev);
+ tasklet_kill(&vfe_dev->vfe_tasklet);
+ msm_isp_flush_tasklet(vfe_dev);
+ iounmap(vfe_dev->vfe_vbif_base);
+ vfe_dev->vfe_vbif_base = NULL;
+ if (vfe_dev->vfe_clk_idx == 1)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_1_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_1_clk_info), 0);
+ if (vfe_dev->vfe_clk_idx == 2)
+ msm_cam_clk_enable(&vfe_dev->pdev->dev,
+ msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
+ ARRAY_SIZE(msm_vfe32_2_clk_info), 0);
+ vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL;
+ iounmap(vfe_dev->vfe_base);
+ vfe_dev->vfe_base = NULL;
+ kfree(vfe_dev->vfe_clk);
+ regulator_disable(vfe_dev->fs_vfe);
+ msm_isp_deinit_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
+}
+
+static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_hw_init_parms qos_parms;
+ struct msm_vfe_hw_init_parms vbif_parms;
+ struct msm_vfe_hw_init_parms ds_parms;
+
+ qos_parms.entries = "qos-entries";
+ qos_parms.regs = "qos-regs";
+ qos_parms.settings = "qos-settings";
+ vbif_parms.entries = "vbif-entries";
+ vbif_parms.regs = "vbif-regs";
+ vbif_parms.settings = "vbif-settings";
+ ds_parms.entries = "ds-entries";
+ ds_parms.regs = "ds-regs";
+ ds_parms.settings = "ds-settings";
+
+ msm_vfe32_init_qos_parms(vfe_dev, &qos_parms, &ds_parms);
+ msm_vfe32_init_vbif_parms(vfe_dev, &vbif_parms);
+
+ /* CGC_OVERRIDE */
+ msm_camera_io_w(0x07FFFFFF, vfe_dev->vfe_base + 0xC);
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000009, vfe_dev->vfe_base + 0x3C);
+ msm_camera_io_w(0x01000021, vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w_mb(0x1CFFFFFF, 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_clear_status_reg(struct vfe_device *vfe_dev)
+{
+ msm_camera_io_w((1 << 23), vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x20);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18);
+}
+
+static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & BIT(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)
+{
+}
+
+static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x1F))
+ return;
+
+ if (irq_status0 & BIT(0)) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0, ts);
+ msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static void msm_vfe32_process_violation_status(struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status = vfe_dev->error_info.violation_status;
+
+ if (!violation_status)
+ return;
+
+ if (violation_status & BIT(0))
+ pr_err("%s: black violation\n", __func__);
+ if (violation_status & BIT(1))
+ pr_err("%s: rolloff violation\n", __func__);
+ if (violation_status & BIT(2))
+ pr_err("%s: demux violation\n", __func__);
+ if (violation_status & BIT(3))
+ pr_err("%s: demosaic violation\n", __func__);
+ if (violation_status & BIT(4))
+ pr_err("%s: crop violation\n", __func__);
+ if (violation_status & BIT(5))
+ pr_err("%s: scale violation\n", __func__);
+ if (violation_status & BIT(6))
+ pr_err("%s: wb violation\n", __func__);
+ if (violation_status & BIT(7))
+ pr_err("%s: clf violation\n", __func__);
+ if (violation_status & BIT(8))
+ pr_err("%s: matrix violation\n", __func__);
+ if (violation_status & BIT(9))
+ pr_err("%s: rgb lut violation\n", __func__);
+ if (violation_status & BIT(10))
+ pr_err("%s: la violation\n", __func__);
+ if (violation_status & BIT(11))
+ pr_err("%s: chroma enhance violation\n", __func__);
+ if (violation_status & BIT(12))
+ pr_err("%s: chroma suppress mce violation\n", __func__);
+ if (violation_status & BIT(13))
+ pr_err("%s: skin enhance violation\n", __func__);
+ if (violation_status & BIT(14))
+ pr_err("%s: asf violation\n", __func__);
+ if (violation_status & BIT(15))
+ pr_err("%s: scale y violation\n", __func__);
+ if (violation_status & BIT(16))
+ pr_err("%s: scale cbcr violation\n", __func__);
+ if (violation_status & BIT(17))
+ pr_err("%s: chroma subsample violation\n", __func__);
+ if (violation_status & BIT(18))
+ pr_err("%s: framedrop enc y violation\n", __func__);
+ if (violation_status & BIT(19))
+ pr_err("%s: framedrop enc cbcr violation\n", __func__);
+ if (violation_status & BIT(20))
+ pr_err("%s: framedrop view y violation\n", __func__);
+ if (violation_status & BIT(21))
+ pr_err("%s: framedrop view cbcr violation\n", __func__);
+ if (violation_status & BIT(22))
+ pr_err("%s: realign buf y violation\n", __func__);
+ if (violation_status & BIT(23))
+ pr_err("%s: realign buf cb violation\n", __func__);
+ if (violation_status & BIT(24))
+ pr_err("%s: realign buf cr violation\n", __func__);
+}
+
+static void msm_vfe32_get_overflow_mask(uint32_t *overflow_mask)
+{
+ *overflow_mask = 0x0;
+}
+
+static void msm_vfe32_process_error_status(struct vfe_device *vfe_dev)
+{
+ uint32_t error_status1 = vfe_dev->error_info.error_mask1;
+
+ if (error_status1 & BIT(0))
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, vfe_dev->error_info.camif_status);
+ if (error_status1 & BIT(1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (error_status1 & BIT(2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (error_status1 & BIT(3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (error_status1 & BIT(4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (error_status1 & BIT(5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (error_status1 & BIT(6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (error_status1 & BIT(7)) {
+ pr_err("%s: violation\n", __func__);
+ msm_vfe32_process_violation_status(vfe_dev);
+ }
+ if (error_status1 & BIT(8)) {
+ vfe_dev->stats->imagemaster0_overflow++;
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(9)) {
+ vfe_dev->stats->imagemaster1_overflow++;
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(10)) {
+ vfe_dev->stats->imagemaster2_overflow++;
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(11)) {
+ vfe_dev->stats->imagemaster3_overflow++;
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(12)) {
+ vfe_dev->stats->imagemaster4_overflow++;
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(13)) {
+ vfe_dev->stats->imagemaster5_overflow++;
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(14)) {
+ vfe_dev->stats->imagemaster6_overflow++;
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(15)) {
+ vfe_dev->stats->bg_overflow++;
+ pr_err("%s: status ae/bg bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(16)) {
+ vfe_dev->stats->bf_overflow++;
+ pr_err("%s: status af/bf bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(17)) {
+ vfe_dev->stats->awb_overflow++;
+ pr_err("%s: status awb bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(18)) {
+ vfe_dev->stats->rs_overflow++;
+ pr_err("%s: status rs bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(19)) {
+ vfe_dev->stats->cs_overflow++;
+ pr_err("%s: status cs bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(20)) {
+ vfe_dev->stats->ihist_overflow++;
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(21)) {
+ vfe_dev->stats->skinbhist_overflow++;
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ }
+ if (error_status1 & BIT(22))
+ pr_err("%s: axi error\n", __func__);
+}
+
+static void msm_vfe32_read_and_clear_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_mb(*irq_status1, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x18);
+
+ if (*irq_status1 & BIT(0))
+ vfe_dev->error_info.camif_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x204);
+
+ if (*irq_status1 & BIT(7))
+ vfe_dev->error_info.violation_status |=
+ msm_camera_io_r(vfe_dev->vfe_base + 0x7B4);
+}
+
+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);
+}
+
+static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ uint32_t rdi_status;
+ enum msm_vfe_input_src i;
+
+ if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000))
+ return;
+
+ if (irq_status0 & BIT(5)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
+ VFE_PIX_0);
+ if (vfe_dev->axi_data.stream_update[VFE_PIX_0]) {
+ rdi_status = msm_camera_io_r(vfe_dev->vfe_base +
+ VFE32_XBAR_BASE(0));
+ rdi_status |= msm_camera_io_r(vfe_dev->vfe_base +
+ VFE32_XBAR_BASE(4));
+
+ if ((rdi_status & BIT(7)) && (!(irq_status0 & 0x20)))
+ return;
+ }
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ }
+
+ for (i = VFE_RAW_0; i <= VFE_RAW_2; i++) {
+ if (irq_status1 & BIT(26 + (i - VFE_RAW_0))) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
+ msm_isp_axi_stream_update(vfe_dev, i, ts);
+ msm_isp_update_framedrop_reg(vfe_dev, i);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
+ i);
+ }
+ }
+
+ msm_isp_update_error_frame_count(vfe_dev);
+
+}
+
+static void msm_vfe32_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ /* Not supported */
+}
+
+static void msm_vfe32_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) {
+ msm_camera_io_w_mb(0xF,
+ vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
+ + 0x260);
+ msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260);
+ } else if (!vfe_dev->is_split) {
+ msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260);
+ }
+}
+
+static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking)
+{
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
+ return wait_for_completion_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+}
+
+static void msm_vfe32_axi_reload_wm(
+ struct vfe_device *vfe_dev, void __iomem *vfe_base,
+ uint32_t reload_mask)
+{
+ if (!vfe_dev->pdev->dev.of_node) {
+ /*vfe32 A-family: 8960*/
+ msm_camera_io_w_mb(reload_mask, vfe_base + 0x38);
+ } else {
+ /*vfe32 B-family: 8610*/
+ msm_camera_io_w(0x0, vfe_base + 0x24);
+ msm_camera_io_w(0x0, vfe_base + 0x28);
+ msm_camera_io_w(0x0, vfe_base + 0x20);
+ msm_camera_io_w_mb(0x1, vfe_base + 0x18);
+ msm_camera_io_w(0x9AAAAAAA, vfe_base + 0x600);
+ msm_camera_io_w(reload_mask, vfe_base + 0x38);
+ }
+}
+
+static void msm_vfe32_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val = msm_camera_io_r(
+ vfe_base + VFE32_WM_BASE(wm_idx));
+ if (enable)
+ val |= 0x1;
+ else
+ val &= ~0x1;
+ msm_camera_io_w_mb(val,
+ 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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index[vfe_idx];
+ 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 |= BIT(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)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index[vfe_idx];
+ 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 &= ~BIT(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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= BIT(stream_info->wm[vfe_idx][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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~BIT(stream_info->wm[vfe_idx][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,
+ uint32_t framedrop_period)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+
+ if (stream_info->stream_src == PIX_ENCODER) {
+ msm_camera_io_w(framedrop_period - 1, vfe_base + 0x504);
+ msm_camera_io_w(framedrop_period - 1, vfe_base + 0x508);
+ msm_camera_io_w(framedrop_pattern, vfe_base + 0x50C);
+ msm_camera_io_w(framedrop_pattern, vfe_base + 0x510);
+ } else if (stream_info->stream_src == PIX_VIEWFINDER) {
+ msm_camera_io_w(framedrop_period - 1, vfe_base + 0x514);
+ msm_camera_io_w(framedrop_period - 1, vfe_base + 0x518);
+ msm_camera_io_w(framedrop_pattern, vfe_base + 0x51C);
+ msm_camera_io_w(framedrop_pattern, vfe_base + 0x520);
+ }
+ msm_camera_io_w_mb(0x1, 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 int32_t msm_vfe32_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format)
+{
+ int bpp, bpp_reg = 0, pack_fmt = 0, pack_reg = 0;
+ uint32_t io_format_reg;
+
+ bpp = msm_isp_get_bit_per_pixel(io_format);
+ if (bpp < 0) {
+ pr_err("%s:%d invalid io_format %d bpp %d", __func__, __LINE__,
+ io_format, bpp);
+ return -EINVAL;
+ }
+
+ switch (bpp) {
+ case 8:
+ bpp_reg = 0;
+ break;
+ case 10:
+ bpp_reg = 1 << 0;
+ break;
+ case 12:
+ bpp_reg = 1 << 1;
+ break;
+ default:
+ pr_err("%s:%d invalid bpp %d", __func__, __LINE__, bpp);
+ return -EINVAL;
+ }
+
+ if (stream_src == IDEAL_RAW) {
+ pack_fmt = msm_isp_get_pack_format(io_format);
+ switch (pack_fmt) {
+ case QCOM:
+ pack_reg = 0x0;
+ break;
+ case MIPI:
+ pack_reg = 0x1;
+ break;
+ case DPCM6:
+ pack_reg = 0x2;
+ break;
+ case DPCM8:
+ pack_reg = 0x3;
+ break;
+ case PLAIN8:
+ pack_reg = 0x4;
+ break;
+ case PLAIN16:
+ pack_reg = 0x5;
+ break;
+ default:
+ pr_err("%s: invalid pack fmt!\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x6F8);
+ switch (stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4 | pack_reg;
+ break;
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x6F8);
+ return 0;
+}
+
+static int msm_vfe32_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ return 0;
+}
+
+static void msm_vfe32_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ pr_err("%s: Fetch engine not supported\n", __func__);
+}
+
+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->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 + 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(first_pixel << 16 | last_pixel,
+ vfe_dev->vfe_base + 0x1F0);
+
+ msm_camera_io_w(first_line << 16 | 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_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ msm_vfe32_cfg_camif(vfe_dev, pix_cfg);
+ break;
+ case EXTERNAL_READ:
+ msm_vfe32_cfg_fetch_engine(vfe_dev, pix_cfg);
+ break;
+ default:
+ pr_err("%s: Unsupported input mux %d\n",
+ __func__, pix_cfg->input_mux);
+ }
+}
+
+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;
+
+ if (update_state == ENABLE_CAMIF) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ val |= 0x1;
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x1C);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4);
+ 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].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;
+ } else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ msm_camera_io_w_mb(0x6, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ }
+}
+
+static void msm_vfe32_cfg_rdi_reg(struct vfe_device *vfe_dev,
+ struct msm_vfe_rdi_cfg *rdi_cfg, enum msm_vfe_input_src input_src)
+{
+ uint8_t rdi = input_src - VFE_RAW_0;
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE32_RDI_BASE(0));
+ rdi_reg_cfg &= ~(BIT(16 + rdi));
+ rdi_reg_cfg |= rdi_cfg->frame_based << (16 + rdi);
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base + VFE32_RDI_BASE(0));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+ rdi_reg_cfg &= 0x70003;
+ rdi_reg_cfg |= (rdi * 3) << 28 | rdi_cfg->cid << 4 | 0x4;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+
+}
+
+static void msm_vfe32_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+
+ if (!stream_info->frame_based) {
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width)+1)/2 - 1) << 16 |
+ (stream_info->plane_cfg[vfe_idx][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_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_stride) << 16 |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) << 4 | VFE32_BURST_LEN;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ } else {
+ msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
+ val =
+ msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width) << 16 |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) << 4 | VFE32_BURST_LEN;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ }
+}
+
+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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][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);
+}
+
+static void msm_vfe32_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_info->plane_cfg[vfe_idx][plane_idx];
+ uint8_t wm = stream_info->wm[vfe_idx][plane_idx];
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ switch (stream_info->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 5;
+ } else {
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV16:
+ xbar_cfg |= 0x3 << 3; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= BIT(1); /*PAIR_STREAM_EN*/
+ }
+ if (stream_info->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_INTF_0:
+ xbar_cfg = 0xA0;
+ break;
+ case RDI_INTF_1:
+ xbar_cfg = 0xC0;
+ break;
+ case RDI_INTF_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));
+}
+
+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)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm = stream_info->wm[vfe_idx][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_update_ping_pong_addr(void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size)
+{
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE32_PING_PONG_BASE(wm_idx, pingpong_bit));
+}
+
+static int msm_vfe32_axi_halt(struct vfe_device *vfe_dev, uint32_t blocking)
+{
+ uint32_t halt_mask;
+ uint32_t axi_busy_flag = true;
+
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);
+ while (axi_busy_flag) {
+ if (msm_camera_io_r(
+ vfe_dev->vfe_base + 0x1DC) & 0x1)
+ axi_busy_flag = false;
+ }
+ msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x1D8);
+ halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
+ halt_mask &= 0xFEFFFFFF;
+ /* Disable AXI IRQ */
+ msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20);
+ return 0;
+}
+
+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_stats_idx(enum msm_isp_stats_type stats_type)
+{
+ switch (stats_type) {
+ case MSM_ISP_STATS_AEC:
+ case MSM_ISP_STATS_BG:
+ return 0;
+ case MSM_ISP_STATS_AF:
+ case MSM_ISP_STATS_BF:
+ return 1;
+ case MSM_ISP_STATS_AWB:
+ return 2;
+ case MSM_ISP_STATS_RS:
+ return 3;
+ case MSM_ISP_STATS_CS:
+ return 4;
+ case MSM_ISP_STATS_IHIST:
+ return 5;
+ case MSM_ISP_STATS_SKIN:
+ case MSM_ISP_STATS_BHIST:
+ return 6;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static int msm_vfe32_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ return 0;
+}
+
+static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t comp_idx, uint8_t enable)
+{
+}
+
+static void msm_vfe32_stats_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t irq_mask;
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= BIT(STATS_IDX(stream_info->stream_handle[vfe_idx]) + 13);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_stats_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ uint32_t irq_mask;
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(BIT(STATS_IDX(stream_info->stream_handle) + 13));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_stats_cfg_wm_reg(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ /*Nothing to configure for VFE3.x*/
+}
+
+static void msm_vfe32_stats_clear_wm_reg(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ /*Nothing to configure for VFE3.x*/
+}
+
+static void msm_vfe32_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = VFE32_UB_SIZE;
+ uint32_t ub_size[VFE32_NUM_STATS_TYPE] = {
+ 107, /*MSM_ISP_STATS_BG*/
+ 92, /*MSM_ISP_STATS_BF*/
+ 2, /*MSM_ISP_STATS_AWB*/
+ 7, /*MSM_ISP_STATS_RS*/
+ 16, /*MSM_ISP_STATS_CS*/
+ 2, /*MSM_ISP_STATS_IHIST*/
+ 7, /*MSM_ISP_STATS_BHIST*/
+ };
+
+ if (vfe_dev->vfe_hw_version == VFE32_8909_VERSION)
+ ub_offset = VFE32_UB_SIZE_32KB;
+
+ for (i = 0; i < VFE32_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE32_STATS_BASE(i) + 0x8);
+ }
+}
+
+static bool msm_vfe32_is_module_cfg_lock_needed(
+ uint32_t reg_offset)
+{
+ return false;
+}
+
+static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+
+ for (i = 0; i < VFE32_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ module_cfg_mask |= 1 << (5 + i);
+ break;
+ case 5:
+ module_cfg_mask |= 1 << 16;
+ break;
+ case 6:
+ module_cfg_mask |= 1 << 19;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x10);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x10);
+}
+
+static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
+ dma_addr_t paddr, uint32_t buf_sz)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ int stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE32_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+}
+
+static uint32_t msm_vfe32_stats_get_wm_mask(uint32_t irq_status0,
+ uint32_t irq_status1)
+{
+ return (irq_status0 >> 13) & 0x7F;
+}
+
+static uint32_t msm_vfe32_stats_get_comp_mask(uint32_t irq_status0,
+ uint32_t irq_status1)
+{
+ return (irq_status0 >> 24) & 0x1;
+}
+
+static uint32_t msm_vfe32_stats_get_frame_id(struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+
+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_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->dev.of_node)
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
+ else
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe");
+
+ if (!vfe_dev->iommu_ctx[0]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ if (!vfe_dev->pdev->dev.of_node)
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
+ else
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe");
+
+ if (!vfe_dev->iommu_ctx[1]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+vfe_no_resource:
+ return rc;
+}
+
+static void msm_vfe32_get_error_mask(uint32_t *error_mask0,
+ uint32_t *error_mask1)
+{
+ *error_mask0 = 0x00000000;
+ *error_mask1 = 0x007FFFFF;
+}
+
+struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
+ .num_wm = 5,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 64,
+ .scratch_buf_range = SZ_32M,
+};
+
+static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_AEC | 1 << MSM_ISP_STATS_BG |
+ 1 << MSM_ISP_STATS_AF | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_AWB | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_SKIN | 1 << MSM_ISP_STATS_BHIST,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .num_stats_type = VFE32_NUM_STATS_TYPE,
+ .num_stats_comp_mask = 0,
+};
+
+struct msm_vfe_hardware_info vfe32_hw_info = {
+ .num_iommu_ctx = 2,
+ .num_iommu_secure_ctx = 0,
+ .vfe_clk_idx = VFE32_CLK_IDX,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe32_read_and_clear_irq_status,
+ .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_reg_update = msm_vfe32_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ .process_epoch_irq = msm_vfe32_process_epoch_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe32_axi_reload_wm,
+ .enable_wm = msm_vfe32_axi_enable_wm,
+ .cfg_io_format = msm_vfe32_cfg_io_format,
+ .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_ub = msm_vfe47_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,
+ .ub_reg_offset = msm_vfe40_ub_reg_offset,
+ .get_ub_size = msm_vfe40_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe32_reg_update,
+ .cfg_input_mux = msm_vfe32_cfg_input_mux,
+ .update_camif_state = msm_vfe32_update_camif_state,
+ .start_fetch_eng = msm_vfe32_start_fetch_engine,
+ .cfg_rdi_reg = msm_vfe32_cfg_rdi_reg,
+ .reset_hw = msm_vfe32_reset_hardware,
+ .init_hw = msm_vfe32_init_hardware,
+ .init_hw_reg = msm_vfe32_init_hardware_reg,
+ .clear_status_reg = msm_vfe32_clear_status_reg,
+ .release_hw = msm_vfe32_release_hardware,
+ .get_platform_data = msm_vfe32_get_platform_data,
+ .get_error_mask = msm_vfe32_get_error_mask,
+ .process_error_status = msm_vfe32_process_error_status,
+ .get_overflow_mask = msm_vfe32_get_overflow_mask,
+ .is_module_cfg_lock_needed =
+ msm_vfe32_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = NULL,
+ .set_bus_err_ign_mask = NULL,
+ .get_bus_err_mask = NULL,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe32_get_stats_idx,
+ .check_streams = msm_vfe32_stats_check_streams,
+ .cfg_comp_mask = msm_vfe32_stats_cfg_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe32_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe32_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe32_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe32_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe32_stats_cfg_ub,
+ .enable_module = msm_vfe32_stats_enable_module,
+ .update_ping_pong_addr =
+ msm_vfe32_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe32_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe32_stats_get_wm_mask,
+ .get_frame_id = msm_vfe32_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe32_get_pingpong_status,
+ .enable_stats_wm = NULL,
+ },
+ },
+ .dmi_reg_offset = 0x5A0,
+ .axi_hw_info = &msm_vfe32_axi_hw_info,
+ .stats_hw_info = &msm_vfe32_stats_hw_info,
+};
+EXPORT_SYMBOL(vfe32_hw_info);
+
+static const struct of_device_id msm_vfe32_dt_match[] = {
+ {
+ .compatible = "qcom,vfe32",
+ .data = &vfe32_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe32_dt_match);
+
+static struct platform_driver vfe32_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe32",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe32_dt_match,
+ },
+ .id_table = msm_vfe32_dev_id,
+};
+
+static int __init msm_vfe32_init_module(void)
+{
+ return platform_driver_register(&vfe32_driver);
+}
+
+static void __exit msm_vfe32_exit_module(void)
+{
+ platform_driver_unregister(&vfe32_driver);
+}
+
+module_init(msm_vfe32_init_module);
+module_exit(msm_vfe32_exit_module);
+MODULE_DESCRIPTION("MSM VFE32 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.h
new file mode 100644
index 0000000..3669b97
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013-2014, 2018, 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/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
new file mode 100644
index 0000000..de7d9ed
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -0,0 +1,2371 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <asm/div64.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"
+#include "msm_isp47.h"
+#include "linux/iopoll.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define VFE40_BURST_LEN 1
+#define VFE40_BURST_LEN_8916_VERSION 2
+#define VFE40_BURST_LEN_8952_VERSION 3
+#define VFE40_WM_BIT_SHIFT 4
+#define VFE40_WM_BIT_SHIFT_8976_VERSION 3
+#define VFE40_STATS_BURST_LEN 1
+#define VFE40_STATS_BURST_LEN_8916_VERSION 2
+#define VFE40_FETCH_BURST_LEN 3
+#define VFE40_UB_SIZE 1536 /* 1536 * 128 bits = 24KB */
+#define VFE40_UB_SIZE_8952 2048 /* 2048 * 128 bits = 32KB */
+#define VFE40_UB_SIZE_8916 3072 /* 3072 * 128 bits = 48KB */
+#define VFE40_EQUAL_SLICE_UB 190 /* (UB_SIZE - STATS SIZE)/6 */
+#define VFE40_EQUAL_SLICE_UB_8916 236
+#define VFE40_TOTAL_WM_UB 1144 /* UB_SIZE - STATS SIZE */
+#define VFE40_TOTAL_WM_UB_8916 1656
+#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
+#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
+#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) & 0x1)))
+
+#define VFE40_BUS_RD_CGC_OVERRIDE_BIT 16
+
+#define STATS_IDX_BE 0
+#define STATS_IDX_BG 1
+#define STATS_IDX_BF 2
+#define STATS_IDX_AWB 3
+#define STATS_IDX_RS 4
+#define STATS_IDX_CS 5
+#define STATS_IDX_IHIST 6
+#define STATS_IDX_BHIST 7
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 8, 9, 10, 11, 12, 13, 14, 15};
+
+#define VFE40_NUM_STATS_TYPE 8
+#define VFE40_STATS_BASE(idx) (0x168 + 0x18 * idx)
+#define VFE40_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE40_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (stats_pingpong_offset_map[idx])) & 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_OCMEM_OUT_MAX_BURST 0xDC
+#define VFE40_VBIF_ARB_CTL 0xF0
+#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB 0x124
+#define VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0 0x160
+#define VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1 0x164
+#define VFE40_VBIF_OUT_AXI_AOOO_EN 0x178
+#define VFE40_VBIF_OUT_AXI_AOOO 0x17C
+
+#define VFE40_BUS_BDG_QOS_CFG_0 0x000002C4
+#define VFE40_BUS_BDG_QOS_CFG_1 0x000002C8
+#define VFE40_BUS_BDG_QOS_CFG_2 0x000002CC
+#define VFE40_BUS_BDG_QOS_CFG_3 0x000002D0
+#define VFE40_BUS_BDG_QOS_CFG_4 0x000002D4
+#define VFE40_BUS_BDG_QOS_CFG_5 0x000002D8
+#define VFE40_BUS_BDG_QOS_CFG_6 0x000002DC
+#define VFE40_BUS_BDG_QOS_CFG_7 0x000002E0
+
+#define VFE40_CLK_IDX 2
+
+static uint32_t msm_vfe40_ub_reg_offset(struct vfe_device *vfe_dev, int idx)
+{
+ return (VFE40_WM_BASE(idx) + 0x10);
+}
+
+static uint32_t msm_vfe40_get_ub_size(struct vfe_device *vfe_dev)
+{
+ if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION) {
+ vfe_dev->ub_info->wm_ub = VFE40_TOTAL_WM_UB_8916;
+ return VFE40_TOTAL_WM_UB_8916;
+ }
+ return VFE40_TOTAL_WM_UB;
+}
+
+static void msm_vfe40_config_irq(struct vfe_device *vfe_dev,
+ uint32_t irq0_mask, uint32_t irq1_mask,
+ enum msm_isp_irq_operation oper)
+{
+ switch (oper) {
+ case MSM_ISP_IRQ_ENABLE:
+ vfe_dev->irq0_mask |= irq0_mask;
+ vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ break;
+ case MSM_ISP_IRQ_DISABLE:
+ vfe_dev->irq0_mask &= ~irq0_mask;
+ vfe_dev->irq1_mask &= ~irq1_mask;
+ break;
+ case MSM_ISP_IRQ_SET:
+ vfe_dev->irq0_mask = irq0_mask;
+ vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ }
+ msm_camera_io_w_mb(vfe_dev->irq0_mask, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(vfe_dev->irq1_mask, vfe_dev->vfe_base + 0x2C);
+}
+
+static int32_t msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *qos_parms,
+ struct msm_vfe_hw_init_parms *ds_parms)
+{
+ void __iomem *vfebase = vfe_dev->vfe_base;
+ struct device_node *of_node;
+ uint32_t *ds_settings = NULL, *ds_regs = NULL, ds_entries = 0;
+ int32_t i = 0, rc = 0;
+ uint32_t *qos_settings = NULL, *qos_regs = NULL, qos_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, qos_parms->entries,
+ &qos_entries);
+ if (rc < 0 || !qos_entries) {
+ pr_err("%s: NO QOS entries found\n", __func__);
+ } else {
+ qos_settings = kcalloc(qos_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!qos_settings)
+ return -ENOMEM;
+ qos_regs = kcalloc(qos_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!qos_regs) {
+ kfree(qos_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, qos_parms->regs,
+ qos_regs, qos_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS BUS BDG info\n", __func__);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ } else {
+ if (qos_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ qos_parms->settings,
+ qos_settings, qos_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS settings\n",
+ __func__);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ } else {
+ for (i = 0; i < qos_entries; i++)
+ msm_camera_io_w(qos_settings[i],
+ vfebase + qos_regs[i]);
+ kfree(qos_settings);
+ kfree(qos_regs);
+ }
+ } else {
+ kfree(qos_settings);
+ kfree(qos_regs);
+ }
+ }
+ }
+ rc = of_property_read_u32(of_node, ds_parms->entries,
+ &ds_entries);
+ if (rc < 0 || !ds_entries) {
+ pr_err("%s: NO D/S entries found\n", __func__);
+ } else {
+ ds_settings = kcalloc(qos_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!ds_settings)
+ return -ENOMEM;
+ ds_regs = kcalloc(ds_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!ds_regs) {
+ kfree(ds_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, ds_parms->regs,
+ ds_regs, ds_entries);
+ if (rc < 0) {
+ pr_err("%s: NO D/S register info\n", __func__);
+ kfree(ds_settings);
+ kfree(ds_regs);
+ } else {
+ if (ds_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ ds_parms->settings, ds_settings,
+ ds_entries);
+ if (rc < 0) {
+ pr_err("%s: NO D/S settings\n",
+ __func__);
+ kfree(ds_settings);
+ kfree(ds_regs);
+ } else {
+ for (i = 0; i < ds_entries; i++)
+ msm_camera_io_w(ds_settings[i],
+ vfebase + ds_regs[i]);
+ kfree(ds_regs);
+ kfree(ds_settings);
+ }
+ } else {
+ kfree(ds_regs);
+ kfree(ds_settings);
+ }
+ }
+ }
+ return 0;
+}
+
+static int32_t msm_vfe40_init_vbif_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *vbif_parms)
+{
+ void __iomem *vfe_vbif_base = vfe_dev->vfe_vbif_base;
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *vbif_settings = NULL, *vbif_regs = NULL, vbif_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, vbif_parms->entries,
+ &vbif_entries);
+ if (rc < 0 || !vbif_entries) {
+ pr_err("%s: NO VBIF entries found\n", __func__);
+ } else {
+ vbif_settings = kcalloc(vbif_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!vbif_settings)
+ return -ENOMEM;
+ vbif_regs = kcalloc(vbif_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!vbif_regs) {
+ kfree(vbif_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, vbif_parms->regs,
+ vbif_regs, vbif_entries);
+ if (rc < 0) {
+ pr_err("%s: NO VBIF info\n", __func__);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ } else {
+ rc = of_property_read_u32_array(of_node,
+ vbif_parms->settings,
+ vbif_settings, vbif_entries);
+ if (rc < 0) {
+ pr_err("%s: NO VBIF settings\n",
+ __func__);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ } else {
+ for (i = 0; i < vbif_entries; i++)
+ msm_camera_io_w(
+ vbif_settings[i],
+ vfe_vbif_base + vbif_regs[i]);
+ kfree(vbif_settings);
+ kfree(vbif_regs);
+ }
+ }
+ }
+ return 0;
+}
+
+static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_hw_init_parms qos_parms;
+ struct msm_vfe_hw_init_parms vbif_parms;
+ struct msm_vfe_hw_init_parms ds_parms;
+
+ qos_parms.entries = "qos-entries";
+ qos_parms.regs = "qos-regs";
+ qos_parms.settings = "qos-settings";
+ vbif_parms.entries = "vbif-entries";
+ vbif_parms.regs = "vbif-regs";
+ vbif_parms.settings = "vbif-settings";
+ ds_parms.entries = "ds-entries";
+ ds_parms.regs = "ds-regs";
+ ds_parms.settings = "ds-settings";
+
+ switch (vfe_dev->vfe_hw_version) {
+ case VFE40_8974V1_VERSION:
+ case VFE40_8x26_VERSION:
+ case VFE40_8916_VERSION:
+ case VFE40_8939_VERSION:
+ break;
+ case VFE40_8x26V2_VERSION:
+ qos_parms.settings = "qos-v2-settings";
+ break;
+ case VFE40_8974V2_VERSION:
+ case VFE40_8974V3_VERSION:
+ if (vfe_dev->vfe_hw_version == VFE40_8974V2_VERSION)
+ qos_parms.settings = "qos-v2-settings";
+ else
+ qos_parms.settings = "qos-v3-settings";
+ vbif_parms.entries = "vbif-v2-entries";
+ vbif_parms.regs = "vbif-v2-regs";
+ vbif_parms.settings = "vbif-v2-settings";
+ break;
+ case VFE40_8937_VERSION:
+ case VFE40_8953_VERSION:
+ case VFE40_8917_VERSION:
+ default:
+ ISP_DBG("%s: No special QOS\n", __func__);
+ }
+
+ msm_vfe40_init_qos_parms(vfe_dev, &qos_parms, &ds_parms);
+ msm_vfe40_init_vbif_parms(vfe_dev, &vbif_parms);
+ /* BUS_CFG */
+ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50);
+ msm_vfe40_config_irq(vfe_dev, 0x800000E0, 0xFEFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe40_clear_status_reg(struct vfe_device *vfe_dev)
+{
+ vfe_dev->irq0_mask = (1 << 31);
+ vfe_dev->irq1_mask = 0;
+ msm_vfe40_config_irq(vfe_dev, (1 << 31), 0,
+ MSM_ISP_IRQ_SET);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24);
+}
+
+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);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);
+ }
+}
+
+static void msm_vfe40_process_input_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x1000003))
+ return;
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_increment_frame_id(vfe_dev, VFE_PIX_0, ts);
+ }
+
+ if (irq_status0 & (1 << 24)) {
+ ISP_DBG("%s: Fetch Engine Read IRQ\n", __func__);
+ msm_isp_fetch_engine_done_notify(vfe_dev,
+ &vfe_dev->fetch_engine_info);
+ }
+
+ if (irq_status0 & (1 << 1))
+ ISP_DBG("%s: EOF IRQ\n", __func__);
+}
+
+static void msm_vfe40_process_violation_status(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status = vfe_dev->error_info.violation_status;
+
+ if (!violation_status)
+ return;
+
+ if (violation_status & (1 << 0))
+ pr_err("%s: vfe %d camif violation\n", __func__,
+ vfe_dev->pdev->id);
+ if (violation_status & (1 << 1))
+ pr_err("%s: vfe %d black violation\n", __func__,
+ vfe_dev->pdev->id);
+ if (violation_status & (1 << 2))
+ pr_err("%s: vfe %d rolloff violation\n", __func__,
+ vfe_dev->pdev->id);
+ 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 suppress 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_status(struct vfe_device *vfe_dev)
+{
+ uint32_t error_status1 = vfe_dev->error_info.error_mask1;
+
+ if (error_status1 & (1 << 0)) {
+ pr_err_ratelimited("%s: vfe %d camif error status: 0x%x\n",
+ __func__, vfe_dev->pdev->id,
+ vfe_dev->error_info.camif_status);
+ msm_camera_io_dump(vfe_dev->vfe_base + 0x2F4, 0x30, 1);
+ }
+ if (error_status1 & (1 << 1))
+ pr_err_ratelimited("%s: stats bhist overwrite\n", __func__);
+ if (error_status1 & (1 << 2))
+ pr_err_ratelimited("%s: stats cs overwrite\n", __func__);
+ if (error_status1 & (1 << 3))
+ pr_err_ratelimited("%s: stats ihist overwrite\n", __func__);
+ if (error_status1 & (1 << 4))
+ pr_err_ratelimited("%s: realign buf y overflow\n", __func__);
+ if (error_status1 & (1 << 5))
+ pr_err_ratelimited("%s: realign buf cb overflow\n", __func__);
+ if (error_status1 & (1 << 6))
+ pr_err_ratelimited("%s: realign buf cr overflow\n", __func__);
+ if (error_status1 & (1 << 7))
+ msm_vfe40_process_violation_status(vfe_dev);
+ if (error_status1 & (1 << 9)) {
+ vfe_dev->stats->imagemaster0_overflow++;
+ pr_err_ratelimited("%s: image master 0 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 10)) {
+ vfe_dev->stats->imagemaster1_overflow++;
+ pr_err_ratelimited("%s: image master 1 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 11)) {
+ vfe_dev->stats->imagemaster2_overflow++;
+ pr_err_ratelimited("%s: image master 2 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 12)) {
+ vfe_dev->stats->imagemaster3_overflow++;
+ pr_err_ratelimited("%s: image master 3 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 13)) {
+ vfe_dev->stats->imagemaster4_overflow++;
+ pr_err_ratelimited("%s: image master 4 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 14)) {
+ vfe_dev->stats->imagemaster5_overflow++;
+ pr_err_ratelimited("%s: image master 5 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 15)) {
+ vfe_dev->stats->imagemaster6_overflow++;
+ pr_err_ratelimited("%s: image master 6 bus overflow\n",
+ __func__);
+ }
+ if (error_status1 & (1 << 16)) {
+ vfe_dev->stats->be_overflow++;
+ pr_err_ratelimited("%s: status be bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 17)) {
+ vfe_dev->stats->bg_overflow++;
+ pr_err_ratelimited("%s: status bg bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 18)) {
+ vfe_dev->stats->bf_overflow++;
+ pr_err_ratelimited("%s: status bf bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 19)) {
+ vfe_dev->stats->awb_overflow++;
+ pr_err_ratelimited("%s: status awb bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 20)) {
+ vfe_dev->stats->rs_overflow++;
+ pr_err_ratelimited("%s: status rs bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 21)) {
+ vfe_dev->stats->cs_overflow++;
+ pr_err_ratelimited("%s: status cs bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 22)) {
+ vfe_dev->stats->ihist_overflow++;
+ pr_err_ratelimited("%s: status ihist bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 23)) {
+ vfe_dev->stats->skinbhist_overflow++;
+ pr_err_ratelimited("%s: status skin bhist bus overflow\n",
+ __func__);
+ }
+
+ /* Update ab/ib values for any overflow that may have occurred*/
+ if ((error_status1 >> 9) & 0x7FFF)
+ msm_isp_update_last_overflow_ab_ib(vfe_dev);
+}
+
+static void msm_vfe40_read_and_clear_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);
+ /*
+ * Ignore composite 2/3 irq which is used for dual VFE only
+ */
+ if (*irq_status0 & 0x6000000)
+ *irq_status0 &= ~(0x18000000);
+ 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);
+ if (*irq_status0 & 0x18000000) {
+ pr_err_ratelimited("%s: Protection triggered\n", __func__);
+ *irq_status0 &= ~(0x18000000);
+ }
+
+ *irq_status0 &= vfe_dev->irq0_mask;
+ *irq_status1 &= vfe_dev->irq1_mask;
+
+ if (*irq_status1 & (1 << 0)) {
+ vfe_dev->error_info.camif_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x31C);
+ msm_vfe40_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE);
+ }
+
+ if (*irq_status1 & (1 << 7))
+ vfe_dev->error_info.violation_status |=
+ msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+
+}
+
+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);
+}
+
+static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ enum msm_vfe_input_src i;
+ uint32_t shift_irq;
+ uint8_t reg_updated = 0;
+ unsigned long flags;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+ /* Shift status bits so that PIX REG UPDATE is 1st bit */
+ shift_irq = ((irq_status0 & 0xF0) >> 4);
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ if (shift_irq & BIT(i)) {
+ reg_updated |= BIT(i);
+ ISP_DBG("%s REG_UPDATE IRQ %x\n", __func__,
+ (uint32_t)BIT(i));
+ switch (i) {
+ case VFE_PIX_0:
+ msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
+ VFE_PIX_0, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].
+ raw_stream_count == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, i);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ msm_isp_increment_frame_id(vfe_dev, i, ts);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /*
+ * Reg Update is pseudo SOF for RDI,
+ * so request every frame
+ */
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, i);
+ /* reg upd is also epoch for RDI */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ break;
+ default:
+ pr_err("%s: Error case\n", __func__);
+ return;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ if (reg_updated & BIT(VFE_PIX_0))
+ vfe_dev->reg_updated = 1;
+
+ vfe_dev->reg_update_requested &= ~reg_updated;
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static void msm_vfe40_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ uint32_t update_mask = 0;
+ unsigned long flags;
+
+ /* This HW supports upto VFE_RAW_2 */
+ if (frame_src > VFE_RAW_2 && frame_src != VFE_SRC_MAX) {
+ pr_err("%s Error case\n", __func__);
+ return;
+ }
+
+ /*
+ * If frame_src == VFE_SRC_MAX request reg_update on all
+ * supported INTF
+ */
+ if (frame_src == VFE_SRC_MAX)
+ update_mask = 0xF;
+ else
+ update_mask = BIT((uint32_t)frame_src);
+ ISP_DBG("%s update_mask %x\n", __func__, update_mask);
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].reg_update_frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ vfe_dev->reg_update_requested |= update_mask;
+ vfe_dev->common_data->dual_vfe_res->reg_update_mask[vfe_dev->pdev->id] =
+ vfe_dev->reg_update_requested;
+ if ((vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) &&
+ ((frame_src == VFE_PIX_0) || (frame_src == VFE_SRC_MAX))) {
+ if (!vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]) {
+ pr_err("%s vfe_base for ISP_VFE0 is NULL\n", __func__);
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock,
+ flags);
+ return;
+ }
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
+ + 0x378);
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x378);
+ } else if (!vfe_dev->is_split ||
+ ((frame_src == VFE_PIX_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
+ (frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x378);
+ }
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static void msm_vfe40_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0xc))
+ return;
+
+ if (irq_status0 & BIT(2)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
+ msm_isp_update_error_frame_count(vfe_dev);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking_call)
+{
+ long rc = 0;
+
+ init_completion(&vfe_dev->reset_complete);
+
+ if (first_start) {
+ msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC);
+ } else {
+ msm_camera_io_w_mb(0x1EF, vfe_dev->vfe_base + 0xC);
+ msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, vfe_dev->vfe_base, 0x0003FFFF);
+ }
+
+
+ if (blocking_call) {
+ rc = wait_for_completion_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+ }
+ return rc;
+}
+
+static void msm_vfe40_axi_reload_wm(struct vfe_device *vfe_dev,
+ void __iomem *vfe_base, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_base + 0x4C);
+}
+
+static void msm_vfe40_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x974);
+ if (enable)
+ val |= (1 << wm_idx);
+ else
+ val &= ~(1 << wm_idx);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x974);
+}
+
+static void msm_vfe40_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ val = msm_camera_io_r(vfe_base + VFE40_WM_BASE(wm_idx));
+ if (enable)
+ val |= 0x1;
+ else
+ val &= ~0x1;
+ msm_camera_io_w_mb(val,
+ 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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+
+ 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);
+ msm_vfe40_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+ vfe_dev->irq0_mask &= ~BIT(27);
+
+ 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);
+ msm_vfe40_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe40_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[vfe_idx][0] + 8));
+ msm_vfe40_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe40_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
+ uint32_t framedrop_period)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ msm_camera_io_w(framedrop_pattern, vfe_base +
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+ temp = msm_camera_io_r(vfe_base +
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ temp &= 0xFFFFFF83;
+ msm_camera_io_w(temp | (framedrop_period - 1) << 2,
+ vfe_base + VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ }
+
+ msm_camera_io_w_mb(0x1, vfe_base + 0x378);
+}
+
+static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+}
+
+static int32_t msm_vfe40_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
+{
+ int rc = 0;
+
+ switch (bpp) {
+ case 8:
+ *bpp_reg = 0;
+ break;
+ case 10:
+ *bpp_reg = 1 << 0;
+ break;
+ case 12:
+ *bpp_reg = 1 << 1;
+ break;
+ default:
+ pr_err("%s:%d invalid bpp %d", __func__, __LINE__, bpp);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int32_t msm_vfe40_convert_io_fmt_to_reg(
+ enum msm_isp_pack_fmt pack_format, uint32_t *pack_reg)
+{
+ int rc = 0;
+
+ switch (pack_format) {
+ case QCOM:
+ *pack_reg = 0x0;
+ break;
+ case MIPI:
+ *pack_reg = 0x1;
+ break;
+ case DPCM6:
+ *pack_reg = 0x2;
+ break;
+ case DPCM8:
+ *pack_reg = 0x3;
+ break;
+ case PLAIN8:
+ *pack_reg = 0x4;
+ break;
+ case PLAIN16:
+ *pack_reg = 0x5;
+ break;
+ default:
+ pr_err("%s: invalid pack fmt %d!\n", __func__, pack_format);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int32_t msm_vfe40_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format)
+{
+ int rc = 0;
+ int bpp = 0, read_bpp = 0;
+ enum msm_isp_pack_fmt pack_fmt = 0, read_pack_fmt = 0;
+ uint32_t bpp_reg = 0, pack_reg = 0;
+ uint32_t read_bpp_reg = 0, read_pack_reg = 0;
+ uint32_t io_format_reg = 0; /*io format register bit*/
+
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x54);
+ if ((stream_src < RDI_INTF_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux ==
+ EXTERNAL_READ)) {
+ read_bpp = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe40_convert_bpp_to_reg(read_bpp, &read_bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! in_bpp %d rc %d\n",
+ __func__, read_bpp, rc);
+ return rc;
+ }
+ read_pack_fmt = msm_isp_get_pack_format(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe40_convert_io_fmt_to_reg(
+ read_pack_fmt, &read_pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /*use input format(v4l2_pix_fmt) to get pack format*/
+ io_format_reg &= 0xFFC8FFFF;
+ io_format_reg |= (read_bpp_reg << 20 | read_pack_reg << 16);
+ }
+
+ bpp = msm_isp_get_bit_per_pixel(io_format);
+ rc = msm_vfe40_convert_bpp_to_reg(bpp, &bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! bpp %d rc = %d\n",
+ __func__, bpp, rc);
+ return rc;
+ }
+
+ switch (stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ /*use output format(v4l2_pix_fmt) to get pack format*/
+ pack_fmt = msm_isp_get_pack_format(io_format);
+ rc = msm_vfe40_convert_io_fmt_to_reg(pack_fmt, &pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4 | pack_reg;
+ break;
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x54);
+ return 0;
+}
+
+static int msm_vfe40_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+ /* There is other option of passing buffer address from user,
+ *in such case, driver needs to map the buffer and use it
+ */
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr, vfe_dev->vfe_base + 0x228);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x378);
+
+ msm_camera_io_w_mb(0x10000, vfe_dev->vfe_base + 0x4C);
+ msm_camera_io_w_mb(0x20000, vfe_dev->vfe_base + 0x4C);
+
+ ISP_DBG("%s:VFE%d Fetch Engine ready\n", __func__, vfe_dev->pdev->id);
+ return 0;
+}
+
+static int msm_vfe40_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_multi_pass_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+ /* There is other option of passing buffer address from user,
+ * in such case, driver needs to map the buffer and use it
+ */
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr + fe_cfg->input_buf_offset,
+ vfe_dev->vfe_base + 0x228);
+
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x378);
+
+ msm_camera_io_w_mb(0x10000, vfe_dev->vfe_base + 0x4C);
+ msm_camera_io_w_mb(0x20000, vfe_dev->vfe_base + 0x4C);
+
+ ISP_DBG("%s:VFE%d Fetch Engine ready\n", __func__, vfe_dev->pdev->id);
+ return 0;
+}
+
+static void msm_vfe40_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t x_size_word;
+ uint32_t temp = 0;
+ uint32_t main_unpack_pattern = 0;
+ struct msm_vfe_fetch_engine_cfg *fe_cfg = NULL;
+
+ if (pix_cfg->input_mux != EXTERNAL_READ) {
+ pr_err("%s: Invalid mux configuration - mux: %d",
+ __func__, pix_cfg->input_mux);
+ return;
+ }
+
+ fe_cfg = &pix_cfg->fetch_engine_cfg;
+ pr_debug("%s: fetch_dbg wd x ht buf = %d x %d, fe = %d x %d\n",
+ __func__, fe_cfg->buf_width, fe_cfg->buf_height,
+ fe_cfg->fetch_width, fe_cfg->fetch_height);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_cgc_override(vfe_dev,
+ VFE40_BUS_RD_CGC_OVERRIDE_BIT, 1);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp &= 0xFFFFFFFD;
+ temp |= (1 << 1);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ msm_vfe40_config_irq(vfe_dev, (1 << 24), 0,
+ MSM_ISP_IRQ_ENABLE);
+
+ msm_camera_io_w((fe_cfg->fetch_height - 1),
+ vfe_dev->vfe_base + 0x238);
+
+ /* need to update to use formulae to calculate X_SIZE_WORD*/
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->buf_width);
+
+ msm_camera_io_w((x_size_word - 1) << 16, vfe_dev->vfe_base + 0x23C);
+
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->fetch_width);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ temp |= 2 << 16 | pix_cfg->pixel_pattern;
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x1C);
+
+ if (vfe_dev->vfe_hw_version == VFE40_8953_VERSION) {
+ msm_camera_io_w(x_size_word << 17 |
+ (fe_cfg->buf_height-1) << 4 |
+ VFE40_FETCH_BURST_LEN,
+ vfe_dev->vfe_base + 0x240);
+ msm_camera_io_w(0 << 29 | 2 << 26 |
+ (fe_cfg->buf_width - 1) << 13 |
+ (fe_cfg->buf_height - 1),
+ vfe_dev->vfe_base + 0x244);
+ } else {
+ msm_camera_io_w(x_size_word << 16 |
+ (fe_cfg->buf_height-1) << 4 |
+ VFE40_FETCH_BURST_LEN,
+ vfe_dev->vfe_base + 0x240);
+ msm_camera_io_w(0 << 28 | 2 << 25 |
+ (fe_cfg->buf_width - 1) << 12 |
+ (fe_cfg->buf_height - 1),
+ vfe_dev->vfe_base + 0x244);
+ }
+
+ /* need to use formulae to calculate MAIN_UNPACK_PATTERN*/
+ switch (vfe_dev->axi_data.src_info[VFE_PIX_0].input_format) {
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ main_unpack_pattern = 0xB210;
+ break;
+ default:
+ main_unpack_pattern = 0xF6543210;
+ break;
+ }
+ msm_camera_io_w(main_unpack_pattern,
+ vfe_dev->vfe_base + 0x248);
+ msm_camera_io_w(0xF, vfe_dev->vfe_base + 0x264);
+
+}
+
+static void msm_vfe40_cfg_testgen(struct vfe_device *vfe_dev,
+ struct msm_vfe_testgen_cfg *testgen_cfg)
+{
+ uint32_t bit_per_pixel = 0;
+ uint32_t bpp_reg = 0;
+ uint32_t bayer_pix_pattern_reg = 0;
+ uint32_t unicolorbar_reg = 0;
+ uint32_t unicolor_enb = 0;
+
+ bit_per_pixel = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+
+ switch (bit_per_pixel) {
+ case 8:
+ bpp_reg = 0x0;
+ break;
+ case 10:
+ bpp_reg = 0x1;
+ break;
+ case 12:
+ bpp_reg = 0x10;
+ break;
+ case 14:
+ bpp_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid bpp %d\n", __func__, bit_per_pixel);
+ break;
+ }
+
+ msm_camera_io_w(bpp_reg << 16 | testgen_cfg->burst_num_frame,
+ vfe_dev->vfe_base + 0x940);
+
+ msm_camera_io_w(((testgen_cfg->lines_per_frame - 1) << 16) |
+ (testgen_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0x944);
+
+ msm_camera_io_w(testgen_cfg->h_blank, vfe_dev->vfe_base + 0x958);
+
+ msm_camera_io_w((1 << 16) | testgen_cfg->v_blank,
+ vfe_dev->vfe_base + 0x95C);
+
+ switch (testgen_cfg->pixel_bayer_pattern) {
+ case ISP_BAYER_RGRGRG:
+ bayer_pix_pattern_reg = 0x0;
+ break;
+ case ISP_BAYER_GRGRGR:
+ bayer_pix_pattern_reg = 0x1;
+ break;
+ case ISP_BAYER_BGBGBG:
+ bayer_pix_pattern_reg = 0x10;
+ break;
+ case ISP_BAYER_GBGBGB:
+ bayer_pix_pattern_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid pix pattern %d\n",
+ __func__, bit_per_pixel);
+ break;
+ }
+
+ if (testgen_cfg->color_bar_pattern == COLOR_BAR_8_COLOR) {
+ unicolor_enb = 0x0;
+ } else {
+ unicolor_enb = 0x1;
+ switch (testgen_cfg->color_bar_pattern) {
+ case UNICOLOR_WHITE:
+ unicolorbar_reg = 0x0;
+ break;
+ case UNICOLOR_YELLOW:
+ unicolorbar_reg = 0x1;
+ break;
+ case UNICOLOR_CYAN:
+ unicolorbar_reg = 0x10;
+ break;
+ case UNICOLOR_GREEN:
+ unicolorbar_reg = 0x11;
+ break;
+ case UNICOLOR_MAGENTA:
+ unicolorbar_reg = 0x100;
+ break;
+ case UNICOLOR_RED:
+ unicolorbar_reg = 0x101;
+ break;
+ case UNICOLOR_BLUE:
+ unicolorbar_reg = 0x110;
+ break;
+ case UNICOLOR_BLACK:
+ unicolorbar_reg = 0x111;
+ break;
+ default:
+ pr_err("%s: invalid colorbar %d\n",
+ __func__, testgen_cfg->color_bar_pattern);
+ break;
+ }
+ }
+ msm_camera_io_w((testgen_cfg->rotate_period << 8) |
+ (bayer_pix_pattern_reg << 6) | (unicolor_enb << 4) |
+ (unicolorbar_reg), vfe_dev->vfe_base + 0x968);
+}
+
+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;
+ uint16_t epoch_line1;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ uint32_t val, subsample_period, subsample_pattern;
+ struct msm_vfe_camif_subsample_cfg *subsample_cfg =
+ &pix_cfg->camif_cfg.subsample_cfg;
+ uint16_t bus_sub_en = 0;
+
+ vfe_dev->dual_vfe_enable = camif_cfg->is_split;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x1C);
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+ epoch_line1 = camif_cfg->epoch_line1;
+
+ if ((epoch_line1 <= 0) || (epoch_line1 > last_line))
+ epoch_line1 = last_line - 50;
+
+ if ((last_line - epoch_line1) > 100)
+ epoch_line1 = last_line - 100;
+
+ subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
+ subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
+
+ 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);
+
+ /* configure EPOCH0: 20 lines, and
+ * configure EPOCH1: epoch_line1 before EOF
+ */
+ msm_camera_io_w_mb(0x140000 | epoch_line1,
+ vfe_dev->vfe_base + 0x318);
+ pr_debug("%s:%d: epoch_line1: %d\n",
+ __func__, __LINE__, epoch_line1);
+ if (subsample_period && subsample_pattern) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ val &= 0xFFE0FFFF;
+ val = (subsample_period - 1) << 16;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ ISP_DBG("%s:camif PERIOD %x PATTERN %x\n",
+ __func__, subsample_period, subsample_pattern);
+
+ val = subsample_pattern;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x314);
+ } else {
+ 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);
+
+ if (subsample_cfg->pixel_skip || subsample_cfg->line_skip) {
+ bus_sub_en = 1;
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ val &= 0xFFFFFFDF;
+ val = val | bus_sub_en << 5;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ subsample_cfg->pixel_skip &= 0x0000FFFF;
+ subsample_cfg->line_skip &= 0x0000FFFF;
+ msm_camera_io_w((subsample_cfg->line_skip << 16) |
+ subsample_cfg->pixel_skip,
+ vfe_dev->vfe_base + 0x30C);
+ if (subsample_cfg->first_pixel ||
+ subsample_cfg->last_pixel ||
+ subsample_cfg->first_line ||
+ subsample_cfg->last_line) {
+ msm_camera_io_w(
+ subsample_cfg->first_pixel << 16 |
+ subsample_cfg->last_pixel,
+ vfe_dev->vfe_base + 0x8A4);
+ msm_camera_io_w(
+ subsample_cfg->first_line << 16 |
+ subsample_cfg->last_line,
+ vfe_dev->vfe_base + 0x8A8);
+ val = msm_camera_io_r(
+ vfe_dev->vfe_base + 0x2F8);
+ val |= 1 << 22;
+ msm_camera_io_w(val,
+ vfe_dev->vfe_base + 0x2F8);
+ }
+
+ ISP_DBG("%s:camif raw op fmt %d\n",
+ __func__, subsample_cfg->output_format);
+ /* Pdaf output will be sent in PLAIN16 format*/
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x54);
+ switch (subsample_cfg->output_format) {
+ case CAMIF_PLAIN_8:
+ val |= 4 << 9;
+ break;
+ case CAMIF_PLAIN_16:
+ val |= 5 << 9;
+ break;
+ case CAMIF_MIPI_RAW:
+ val |= 1 << 9;
+ break;
+ case CAMIF_QCOM_RAW:
+ default:
+ break;
+ }
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x54);
+ }
+}
+
+static void msm_vfe40_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t core_cfg = 0;
+ uint32_t val = 0;
+
+ core_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ core_cfg &= 0xFFFCFFFF;
+
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ core_cfg |= 0x0 << 16;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x1C);
+ msm_vfe40_cfg_camif(vfe_dev, pix_cfg);
+ break;
+ case TESTGEN:
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x974);
+ val |= (1 << 31);
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x974);
+
+ /* CAMIF and TESTGEN will both go thorugh CAMIF*/
+ core_cfg |= 0x1 << 16;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x1C);
+ msm_vfe40_cfg_camif(vfe_dev, pix_cfg);
+ msm_vfe40_cfg_testgen(vfe_dev, &pix_cfg->testgen_cfg);
+ break;
+ case EXTERNAL_READ:
+ core_cfg |= 0x2 << 16;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x1C);
+ msm_vfe40_cfg_fetch_engine(vfe_dev, pix_cfg);
+ break;
+ default:
+ pr_err("%s: Unsupported 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;
+
+ if (update_state == ENABLE_CAMIF) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24);
+ msm_vfe40_config_irq(vfe_dev, 0xFF, 0x81,
+ MSM_ISP_IRQ_ENABLE);
+
+ 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].stream_count > 0) ? 1 : 0);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ /* testgen GO*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1, vfe_dev->vfe_base + 0x93C);
+ msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x2F4);
+ 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 ||
+ update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ uint32_t poll_val;
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ update_state = DISABLE_CAMIF;
+ msm_vfe40_config_irq(vfe_dev, 0, 0x81,
+ MSM_ISP_IRQ_DISABLE);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x464);
+ /* disable danger signal */
+ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464);
+ msm_camera_io_w_mb((update_state == DISABLE_CAMIF ? 0x0 : 0x6),
+ vfe_dev->vfe_base + 0x2F4);
+ if (readl_poll_timeout_atomic(vfe_dev->vfe_base + 0x31C,
+ poll_val, poll_val & 0x80000000, 1000, 2000000))
+ pr_err("%s: camif disable failed %x\n",
+ __func__, poll_val);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ /* testgen OFF*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0x93C);
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w((1 << 0), vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+ msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask,
+ vfe_dev->irq1_mask,
+ MSM_ISP_IRQ_SET);
+ }
+}
+
+static void msm_vfe40_cfg_rdi_reg(
+ struct vfe_device *vfe_dev, struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src)
+{
+ uint8_t rdi = input_src - VFE_RAW_0;
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_BASE(0));
+ rdi_reg_cfg &= ~(BIT(16 + rdi));
+ rdi_reg_cfg |= rdi_cfg->frame_based << (16 + rdi);
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base + VFE40_RDI_BASE(0));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+ rdi_reg_cfg &= 0x70003;
+ rdi_reg_cfg |= (rdi * 3) << 28 | rdi_cfg->cid << 4 | 0x4;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+}
+
+static void msm_vfe40_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ uint32_t burst_len, wm_bit_shift = VFE40_WM_BIT_SHIFT_8976_VERSION;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE40_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+
+ if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8939_VERSION) {
+ burst_len = VFE40_BURST_LEN_8916_VERSION;
+ wm_bit_shift = VFE40_WM_BIT_SHIFT;
+ } else if (vfe_dev->vfe_hw_version == VFE40_8952_VERSION) {
+ burst_len = VFE40_BURST_LEN_8952_VERSION;
+ wm_bit_shift = VFE40_WM_BIT_SHIFT;
+ } else if (vfe_dev->vfe_hw_version == VFE40_8976_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8937_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8917_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8953_VERSION) {
+ burst_len = VFE40_BURST_LEN_8952_VERSION;
+ wm_bit_shift = VFE40_WM_BIT_SHIFT_8976_VERSION;
+ } else {
+ burst_len = VFE40_BURST_LEN;
+ }
+
+ if (!stream_info->frame_based) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + wm_base);
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width)+1)/2 - 1) << 16 |
+ (stream_info->plane_cfg[vfe_idx][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_info->output_format,
+ stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_stride) << 16 |
+ (stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_height - 1) << wm_bit_shift |
+ burst_len;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+ } else {
+ msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
+ val =
+ msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_width) << 16 |
+ (stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_height - 1) << 4 |
+ burst_len;
+ 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 */
+}
+
+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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE40_WM_BASE(stream_info->wm[vfe_idx][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);
+}
+
+static void msm_vfe40_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ switch (stream_info->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_info->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_INTF_0:
+ xbar_cfg = 0x500;
+ break;
+ case RDI_INTF_1:
+ xbar_cfg = 0x600;
+ break;
+ case RDI_INTF_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));
+}
+
+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)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
+ uint32_t xbar_reg_cfg = 0;
+
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ 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));
+}
+
+static void msm_vfe40_read_wm_ping_pong_addr(
+ struct vfe_device *vfe_dev)
+{
+ msm_camera_io_dump(vfe_dev->vfe_base +
+ (VFE40_WM_BASE(0) & 0xFFFFFFF0), 0x200, 1);
+}
+
+static void msm_vfe40_update_ping_pong_addr(
+ void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size)
+{
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE40_PING_PONG_BASE(wm_idx, pingpong_bit));
+}
+
+static void msm_vfe40_set_halt_restart_mask(struct vfe_device *vfe_dev)
+{
+ msm_vfe40_config_irq(vfe_dev, BIT(31), BIT(8), MSM_ISP_IRQ_SET);
+}
+
+static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
+ uint32_t blocking)
+{
+ int rc = 0;
+ enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
+
+ /* Keep only halt and restart mask */
+ msm_vfe40_config_irq(vfe_dev, (1 << 31), (1 << 8),
+ MSM_ISP_IRQ_SET);
+
+ msm_isp_get_timestamp(&ts, vfe_dev);
+ /* if any stream is waiting for update, signal complete */
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ }
+
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
+
+ if (blocking) {
+ init_completion(&vfe_dev->halt_complete);
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+ if (rc <= 0)
+ pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
+ vfe_dev->pdev->id, rc);
+ } else {
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ }
+
+ return rc;
+}
+
+static void msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif)
+{
+ msm_vfe40_config_irq(vfe_dev, vfe_dev->recovery_irq0_mask,
+ vfe_dev->recovery_irq1_mask,
+ MSM_ISP_IRQ_ENABLE);
+ msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318);
+
+ /* Start AXI */
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, VFE_SRC_MAX);
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+ if (enable_camif)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+}
+
+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 int msm_vfe40_get_stats_idx(enum msm_isp_stats_type stats_type)
+{
+ switch (stats_type) {
+ case MSM_ISP_STATS_BE:
+ return 0;
+ case MSM_ISP_STATS_BG:
+ return 1;
+ case MSM_ISP_STATS_BF:
+ return 2;
+ case MSM_ISP_STATS_AWB:
+ return 3;
+ case MSM_ISP_STATS_RS:
+ return 4;
+ case MSM_ISP_STATS_CS:
+ return 5;
+ case MSM_ISP_STATS_IHIST:
+ return 6;
+ case MSM_ISP_STATS_BHIST:
+ return 7;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static int msm_vfe40_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ return 0;
+}
+
+static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t request_comp_index, uint8_t enable)
+{
+ uint32_t comp_mask_reg, mask_bf_scale;
+ atomic_t *stats_comp_mask;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask < 1)
+ return;
+
+ if (request_comp_index >= MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__, request_comp_index,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask >
+ MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ stats_mask = stats_mask & 0xFF;
+ mask_bf_scale = stats_mask;
+
+ stats_comp_mask = &stats_data->stats_comp_mask[request_comp_index];
+ comp_mask_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x44);
+
+ if (enable) {
+ comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8);
+ atomic_set(stats_comp_mask, stats_mask |
+ atomic_read(stats_comp_mask));
+ msm_vfe40_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_ENABLE);
+ } else {
+ if (!(atomic_read(stats_comp_mask) & stats_mask))
+ return;
+ atomic_set(stats_comp_mask,
+ ~stats_mask & atomic_read(stats_comp_mask));
+ comp_mask_reg &= ~(mask_bf_scale <<
+ (16 + request_comp_index * 8));
+ msm_vfe40_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_DISABLE);
+ }
+ msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44);
+
+ ISP_DBG("%s: comp_mask_reg: %x comp mask0 %x mask1: %x\n",
+ __func__, comp_mask_reg,
+ atomic_read(&stats_data->stats_comp_mask[0]),
+ atomic_read(&stats_data->stats_comp_mask[1]));
+
+}
+
+static void msm_vfe40_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe40_config_irq(vfe_dev,
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 16), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe40_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe40_config_irq(vfe_dev,
+ (1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 16)), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe40_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE40_STATS_BASE(stats_idx);
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
+ vfe_dev->vfe_base + stats_base + 0x8);
+ /*WR_IRQ_FRAMEDROP_PATTERN*/
+ msm_camera_io_w(stream_info->framedrop_pattern,
+ vfe_dev->vfe_base + stats_base + 0x10);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe40_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t val = 0;
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE40_STATS_BASE(stats_idx);
+
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x8);
+ /*WR_IRQ_FRAMEDROP_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset;
+ uint32_t stats_burst_len;
+ uint32_t ub_size[VFE40_NUM_STATS_TYPE] = {
+ 64, /*MSM_ISP_STATS_BE*/
+ 128, /*MSM_ISP_STATS_BG*/
+ 128, /*MSM_ISP_STATS_BF*/
+ 16, /*MSM_ISP_STATS_AWB*/
+ 8, /*MSM_ISP_STATS_RS*/
+ 16, /*MSM_ISP_STATS_CS*/
+ 16, /*MSM_ISP_STATS_IHIST*/
+ 16, /*MSM_ISP_STATS_BHIST*/
+ };
+
+ if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8939_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8937_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8917_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8953_VERSION) {
+ stats_burst_len = VFE40_STATS_BURST_LEN_8916_VERSION;
+ ub_offset = VFE40_UB_SIZE_8916;
+ } else if (vfe_dev->vfe_hw_version == VFE40_8952_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8976_VERSION) {
+ stats_burst_len = VFE40_STATS_BURST_LEN_8916_VERSION;
+ ub_offset = VFE40_UB_SIZE_8952;
+ } else {
+ stats_burst_len = VFE40_STATS_BURST_LEN;
+ ub_offset = VFE40_UB_SIZE;
+ }
+
+ for (i = 0; i < VFE40_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(stats_burst_len << 30 |
+ ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE40_STATS_BASE(i) + 0xC);
+ }
+}
+
+static void msm_vfe40_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, cgc_mask = 0;
+
+ for (i = 0; i < VFE40_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_IDX_BE:
+ cgc_mask |= (1 << 8);
+ break;
+ case STATS_IDX_BF:
+ cgc_mask |= (1 << 10);
+ break;
+ case STATS_IDX_BG:
+ cgc_mask |= (1 << 9);
+ break;
+ case STATS_IDX_BHIST:
+ cgc_mask |= (1 << 15);
+ break;
+ case STATS_IDX_AWB:
+ cgc_mask |= (1 << 11);
+ break;
+ case STATS_IDX_RS:
+ cgc_mask |= (1 << 12);
+ break;
+ case STATS_IDX_CS:
+ cgc_mask |= (1 << 13);
+ break;
+ case STATS_IDX_IHIST:
+ cgc_mask |= (1 << 14);
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /* CGC override */
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x974);
+ if (enable)
+ module_cfg |= cgc_mask;
+ else
+ module_cfg &= ~cgc_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x974);
+}
+
+static bool msm_vfe40_is_module_cfg_lock_needed(
+ uint32_t reg_offset)
+{
+ if (reg_offset == 0x18)
+ return true;
+ else
+ return false;
+}
+
+static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+ unsigned long flags;
+
+ for (i = 0; i < VFE40_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ module_cfg_mask |= 1 << (5 + i);
+ break;
+ case 6:
+ module_cfg_mask |= 1 << 15;
+ break;
+ case 7:
+ module_cfg_mask |= 1 << 18;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /*
+ * For vfe40 stats and other modules share module_cfg register.
+ * Hence need to Grab lock.
+ */
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x18);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x18);
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
+}
+
+static void msm_vfe40_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE40_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+}
+
+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) & 0x3;
+}
+
+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 void msm_vfe40_get_error_mask(
+ uint32_t *error_mask0, uint32_t *error_mask1)
+{
+ *error_mask0 = 0x00000000;
+ *error_mask1 = 0x00FFFEFF;
+}
+
+static void msm_vfe40_get_overflow_mask(uint32_t *overflow_mask)
+{
+ *overflow_mask = 0x00FFFE7E;
+}
+
+static void msm_vfe40_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask)
+{
+ *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
+}
+
+static void msm_vfe40_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask)
+{
+ *irq0_mask = vfe_dev->irq0_mask;
+ *irq1_mask = vfe_dev->irq1_mask;
+}
+
+static void msm_vfe40_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask)
+{
+ *irq0_mask = BIT(31);
+ *irq1_mask = BIT(8);
+}
+
+static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 64,
+ .scratch_buf_range = SZ_32M + SZ_4M,
+};
+
+static struct msm_vfe_stats_hardware_info msm_vfe40_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_BE | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST |
+ 1 << MSM_ISP_STATS_AWB | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .num_stats_type = VFE40_NUM_STATS_TYPE,
+ .num_stats_comp_mask = 2,
+};
+
+struct msm_vfe_hardware_info vfe40_hw_info = {
+ .num_iommu_ctx = 1,
+ .num_iommu_secure_ctx = 1,
+ .vfe_clk_idx = VFE40_CLK_IDX,
+ .runtime_axi_update = 0,
+ .min_ab = 12000000,
+ .min_ib = 12000000,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe40_read_and_clear_irq_status,
+ .read_irq_status = msm_vfe40_read_irq_status,
+ .process_camif_irq = msm_vfe40_process_input_irq,
+ .process_reset_irq = msm_vfe40_process_reset_irq,
+ .process_halt_irq = msm_vfe40_process_halt_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,
+ .process_epoch_irq = msm_vfe40_process_epoch_irq,
+ .config_irq = msm_vfe40_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe40_axi_reload_wm,
+ .enable_wm = msm_vfe40_axi_enable_wm,
+ .cfg_io_format = msm_vfe40_cfg_io_format,
+ .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_ub = msm_vfe47_cfg_axi_ub,
+ .read_wm_ping_pong_addr =
+ msm_vfe40_read_wm_ping_pong_addr,
+ .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,
+ .restart = msm_vfe40_axi_restart,
+ .update_cgc_override =
+ msm_vfe40_axi_update_cgc_override,
+ .ub_reg_offset = msm_vfe40_ub_reg_offset,
+ .get_ub_size = msm_vfe40_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe40_reg_update,
+ .cfg_input_mux = msm_vfe40_cfg_input_mux,
+ .update_camif_state = msm_vfe40_update_camif_state,
+ .start_fetch_eng = msm_vfe40_start_fetch_engine,
+ .cfg_rdi_reg = msm_vfe40_cfg_rdi_reg,
+ .reset_hw = msm_vfe40_reset_hardware,
+ .init_hw = msm_vfe47_init_hardware,
+ .init_hw_reg = msm_vfe40_init_hardware_reg,
+ .clear_status_reg = msm_vfe40_clear_status_reg,
+ .release_hw = msm_vfe47_release_hardware,
+ .get_error_mask = msm_vfe40_get_error_mask,
+ .get_overflow_mask = msm_vfe40_get_overflow_mask,
+ .get_rdi_wm_mask = msm_vfe40_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe40_get_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe40_get_halt_restart_mask,
+ .process_error_status = msm_vfe40_process_error_status,
+ .is_module_cfg_lock_needed =
+ msm_vfe40_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = NULL,
+ .start_fetch_eng_multi_pass =
+ msm_vfe40_start_fetch_engine_multi_pass,
+ .set_halt_restart_mask =
+ msm_vfe40_set_halt_restart_mask,
+ .set_bus_err_ign_mask = NULL,
+ .get_bus_err_mask = NULL,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe40_get_stats_idx,
+ .check_streams = msm_vfe40_stats_check_streams,
+ .cfg_comp_mask = msm_vfe40_stats_cfg_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,
+ .enable_module = msm_vfe40_stats_enable_module,
+ .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,
+ .get_pingpong_status = msm_vfe40_get_pingpong_status,
+ .update_cgc_override =
+ msm_vfe40_stats_update_cgc_override,
+ .enable_stats_wm = NULL,
+ },
+ .platform_ops = {
+ .get_platform_data = msm_vfe47_get_platform_data,
+ .enable_regulators = msm_vfe47_enable_regulators,
+ .get_regulators = msm_vfe47_get_regulators,
+ .put_regulators = msm_vfe47_put_regulators,
+ .enable_clks = msm_vfe47_enable_clks,
+ .get_clks = msm_vfe47_get_clks,
+ .put_clks = msm_vfe47_put_clks,
+ .get_clk_rates = msm_vfe47_get_clk_rates,
+ .get_max_clk_rate = msm_vfe47_get_max_clk_rate,
+ .set_clk_rate = msm_vfe47_set_clk_rate,
+ .init_bw_mgr = msm_vfe47_init_bandwidth_mgr,
+ .deinit_bw_mgr = msm_vfe47_deinit_bandwidth_mgr,
+ .update_bw = msm_vfe47_update_bandwidth,
+ }
+ },
+ .dmi_reg_offset = 0x918,
+ .axi_hw_info = &msm_vfe40_axi_hw_info,
+ .stats_hw_info = &msm_vfe40_stats_hw_info,
+ .regulator_names = {"vdd"},
+};
+EXPORT_SYMBOL(vfe40_hw_info);
+
+static const struct of_device_id msm_vfe40_dt_match[] = {
+ {
+ .compatible = "qcom,vfe40",
+ .data = &vfe40_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe40_dt_match);
+
+static struct platform_driver vfe40_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe40",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe40_dt_match,
+ },
+};
+
+static int __init msm_vfe40_init_module(void)
+{
+ return platform_driver_register(&vfe40_driver);
+}
+
+static void __exit msm_vfe40_exit_module(void)
+{
+ platform_driver_unregister(&vfe40_driver);
+}
+
+module_init(msm_vfe40_init_module);
+module_exit(msm_vfe40_exit_module);
+MODULE_DESCRIPTION("MSM VFE40 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.h
new file mode 100644
index 0000000..98c7174
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013-2014, 2018, 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/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
new file mode 100644
index 0000000..bc0a31f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
@@ -0,0 +1,1967 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+
+#include "msm_isp44.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"
+#include "msm_isp47.h"
+#include "linux/iopoll.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define STATS_IDX_BF_SCALE 0
+#define STATS_IDX_BE 1
+#define STATS_IDX_BG 2
+#define STATS_IDX_BF 3
+#define STATS_IDX_AWB 4
+#define STATS_IDX_RS 5
+#define STATS_IDX_CS 6
+#define STATS_IDX_IHIST 7
+#define STATS_IDX_BHIST 8
+
+#define VFE44_8084V1_VERSION 0x4000000A
+
+#define VFE44_BURST_LEN 3
+#define VFE44_FETCH_BURST_LEN 3
+#define VFE44_STATS_BURST_LEN 2
+#define VFE44_UB_SIZE 2048
+#define MSM_ISP44_TOTAL_IMAGE_UB 1528
+#define VFE44_WM_BASE(idx) (0x6C + 0x24 * idx)
+#define VFE44_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
+#define VFE44_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2))
+#define VFE44_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0)
+#define VFE44_PING_PONG_BASE(wm, ping_pong) \
+ (VFE44_WM_BASE(wm) + 0x4 * (1 + ((~ping_pong) & 0x1)))
+
+#define VFE44_BUS_RD_CGC_OVERRIDE_BIT 16
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+#define SHIFT_BF_SCALE_BIT 1
+#define VFE44_NUM_STATS_COMP 2
+#define VFE44_NUM_STATS_TYPE 9
+#define VFE44_STATS_BASE(idx) \
+ ((idx) == STATS_IDX_BF_SCALE ? 0xA0C : (0x168 + 0x18 * (idx-1)))
+#define VFE44_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE44_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (stats_pingpong_offset_map[idx])) & 0x1))
+
+#define VFE44_CLK_IDX 2
+
+static uint32_t msm_vfe44_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx)
+{
+ return (VFE44_WM_BASE(wm_idx) + 0x10);
+}
+
+static uint32_t msm_vfe44_get_ub_size(struct vfe_device *vfe_dev)
+{
+ return MSM_ISP44_TOTAL_IMAGE_UB;
+}
+
+static void msm_vfe44_config_irq(struct vfe_device *vfe_dev,
+ uint32_t irq0_mask, uint32_t irq1_mask,
+ enum msm_isp_irq_operation oper)
+{
+ switch (oper) {
+ case MSM_ISP_IRQ_ENABLE:
+ vfe_dev->irq0_mask |= irq0_mask;
+ vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ break;
+ case MSM_ISP_IRQ_DISABLE:
+ vfe_dev->irq0_mask &= ~irq0_mask;
+ vfe_dev->irq1_mask &= ~irq1_mask;
+ break;
+ case MSM_ISP_IRQ_SET:
+ vfe_dev->irq0_mask = irq0_mask;
+ vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ break;
+ }
+ msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C);
+}
+
+static int32_t msm_vfe44_init_dt_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *dt_parms)
+{
+ void __iomem *vfebase = vfe_dev->vfe_base;
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *dt_settings = NULL, *dt_regs = NULL, dt_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, dt_parms->entries,
+ &dt_entries);
+ if (rc < 0 || !dt_entries) {
+ pr_err("%s: NO QOS entries found\n", __func__);
+ return -EINVAL;
+ }
+ dt_settings = kcalloc(dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_settings)
+ return -ENOMEM;
+ dt_regs = kcalloc(dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_regs) {
+ kfree(dt_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, dt_parms->regs,
+ dt_regs, dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS BUS BDG info\n", __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ return -EINVAL;
+ }
+ if (dt_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ dt_parms->settings,
+ dt_settings, dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS settings\n",
+ __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ } else {
+ for (i = 0; i < dt_entries; i++) {
+ msm_camera_io_w(dt_settings[i],
+ vfebase + dt_regs[i]);
+ }
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ } else {
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ return 0;
+}
+
+static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_hw_init_parms qos_parms;
+ struct msm_vfe_hw_init_parms vbif_parms;
+ struct msm_vfe_hw_init_parms ds_parms;
+
+ qos_parms.entries = "qos-entries";
+ qos_parms.regs = "qos-regs";
+ qos_parms.settings = "qos-settings";
+ vbif_parms.entries = "vbif-entries";
+ vbif_parms.regs = "vbif-regs";
+ vbif_parms.settings = "vbif-settings";
+ ds_parms.entries = "ds-entries";
+ ds_parms.regs = "ds-regs";
+ ds_parms.settings = "ds-settings";
+
+ msm_vfe44_init_dt_parms(vfe_dev, &qos_parms);
+ msm_vfe44_init_dt_parms(vfe_dev, &ds_parms);
+ msm_vfe44_init_dt_parms(vfe_dev, &vbif_parms);
+
+ /* BUS_CFG */
+ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50);
+ msm_vfe44_config_irq(vfe_dev, 0x800000E0, 0xFFFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
+
+}
+
+static void msm_vfe44_clear_status_reg(struct vfe_device *vfe_dev)
+{
+ msm_vfe44_config_irq(vfe_dev, 0x80000000, 0,
+ MSM_ISP_IRQ_SET);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24);
+}
+
+static void msm_vfe44_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);
+ vfe_dev->reset_pending = 0;
+ }
+}
+
+static void msm_vfe44_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);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);
+ }
+}
+
+static void msm_vfe44_process_input_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x1000003))
+ return;
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_increment_frame_id(vfe_dev, VFE_PIX_0, ts);
+ }
+
+ if (irq_status0 & (1 << 24)) {
+ ISP_DBG("%s: Fetch Engine Read IRQ\n", __func__);
+ msm_isp_fetch_engine_done_notify(vfe_dev,
+ &vfe_dev->fetch_engine_info);
+ }
+
+ if (irq_status0 & (1 << 1))
+ ISP_DBG("%s: EOF IRQ\n", __func__);
+}
+
+static void msm_vfe44_process_violation_status(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status = vfe_dev->error_info.violation_status;
+
+ 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 suppress 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 << 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__);
+ if (violation_status & (1 << 28))
+ pr_err("%s: ltm violation\n", __func__);
+ if (violation_status & (1 << 29))
+ pr_err("%s: ltm cov violation\n", __func__);
+ if (violation_status & (1 << 30))
+ pr_err("%s: abf violation\n", __func__);
+ if (violation_status & (1 << 31))
+ pr_err("%s: bpc violation\n", __func__);
+}
+
+static void msm_vfe44_process_error_status(struct vfe_device *vfe_dev)
+{
+ uint32_t error_status1 = vfe_dev->error_info.error_mask1;
+
+ if (error_status1 & (1 << 0)) {
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, vfe_dev->error_info.camif_status);
+ msm_camera_io_dump(vfe_dev->vfe_base + 0x2f4, 0x30, 1);
+ }
+ if (error_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (error_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (error_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (error_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (error_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (error_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (error_status1 & (1 << 7))
+ msm_vfe44_process_violation_status(vfe_dev);
+ if (error_status1 & (1 << 9)) {
+ vfe_dev->stats->imagemaster0_overflow++;
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 10)) {
+ vfe_dev->stats->imagemaster1_overflow++;
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 11)) {
+ vfe_dev->stats->imagemaster2_overflow++;
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 12)) {
+ vfe_dev->stats->imagemaster3_overflow++;
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 13)) {
+ vfe_dev->stats->imagemaster4_overflow++;
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 14)) {
+ vfe_dev->stats->imagemaster5_overflow++;
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 15)) {
+ vfe_dev->stats->imagemaster6_overflow++;
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 16)) {
+ vfe_dev->stats->be_overflow++;
+ pr_err("%s: status be bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 17)) {
+ vfe_dev->stats->bg_overflow++;
+ pr_err("%s: status bg bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 18)) {
+ vfe_dev->stats->bf_overflow++;
+ pr_err("%s: status bf bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 19)) {
+ vfe_dev->stats->awb_overflow++;
+ pr_err("%s: status awb bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 20)) {
+ vfe_dev->stats->rs_overflow++;
+ pr_err("%s: status rs bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 21)) {
+ vfe_dev->stats->cs_overflow++;
+ pr_err("%s: status cs bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 22)) {
+ vfe_dev->stats->ihist_overflow++;
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 23)) {
+ vfe_dev->stats->skinbhist_overflow++;
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ }
+ if (error_status1 & (1 << 24)) {
+ vfe_dev->stats->bfscale_overflow++;
+ pr_err("%s: status bf scale bus overflow\n", __func__);
+ }
+}
+
+static void msm_vfe44_read_and_clear_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);
+ *irq_status0 &= vfe_dev->irq0_mask;
+ *irq_status1 &= vfe_dev->irq1_mask;
+ if (*irq_status0 & 0x10000000) {
+ pr_err_ratelimited("%s: Protection triggered\n", __func__);
+ *irq_status0 &= ~(0x10000000);
+ }
+
+ if (*irq_status1 & (1 << 0)) {
+ vfe_dev->error_info.camif_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x31C);
+ msm_vfe44_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE);
+ }
+
+ if (*irq_status1 & (1 << 7))
+ vfe_dev->error_info.violation_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+
+}
+
+static void msm_vfe44_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);
+}
+
+static void msm_vfe44_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ enum msm_vfe_input_src i;
+ uint32_t shift_irq;
+ uint8_t reg_updated = 0;
+ unsigned long flags;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+ /* Shift status bits so that PIX SOF is 1st bit */
+ shift_irq = ((irq_status0 & 0xF0) >> 4);
+
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ if (shift_irq & BIT(i)) {
+ reg_updated |= BIT(i);
+ ISP_DBG("%s REG_UPDATE IRQ %x\n", __func__,
+ (uint32_t)BIT(i));
+ switch (i) {
+ case VFE_PIX_0:
+ msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
+ VFE_PIX_0, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].
+ raw_stream_count == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, i);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ msm_isp_increment_frame_id(vfe_dev, i, ts);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /*
+ * Reg Update is pseudo SOF for RDI,
+ * so request every frame
+ */
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, i);
+ /* reg upd is epoch for rdi */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ break;
+ default:
+ pr_err("%s: Error case\n", __func__);
+ return;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ if (reg_updated & BIT(VFE_PIX_0))
+ vfe_dev->reg_updated = 1;
+
+ vfe_dev->reg_update_requested &= ~reg_updated;
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static void msm_vfe44_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0xc))
+ return;
+
+ if (irq_status0 & BIT(2)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
+ msm_isp_update_error_frame_count(vfe_dev);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static void msm_vfe44_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ uint32_t update_mask = 0;
+ unsigned long flags;
+
+ /* This HW supports upto VFE_RAW_2 */
+ if (frame_src > VFE_RAW_2 && frame_src != VFE_SRC_MAX) {
+ pr_err("%s Error case\n", __func__);
+ return;
+ }
+
+ /*
+ * If frame_src == VFE_SRC_MAX request reg_update on
+ * all supported INTF
+ */
+ if (frame_src == VFE_SRC_MAX)
+ update_mask = 0xF;
+ else
+ update_mask = BIT((uint32_t)frame_src);
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].reg_update_frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ vfe_dev->reg_update_requested |= update_mask;
+ vfe_dev->common_data->dual_vfe_res->reg_update_mask[vfe_dev->pdev->id] =
+ vfe_dev->reg_update_requested;
+ if ((vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) &&
+ ((frame_src == VFE_PIX_0) || (frame_src == VFE_SRC_MAX))) {
+ if (!vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]) {
+ pr_err("%s vfe_base for ISP_VFE0 is NULL\n", __func__);
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock,
+ flags);
+ return;
+ }
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
+ + 0x378);
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x378);
+ } else if (!vfe_dev->is_split ||
+ ((frame_src == VFE_PIX_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
+ (frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x378);
+ }
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static long msm_vfe44_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking_call)
+{
+ long rc = 0;
+
+ init_completion(&vfe_dev->reset_complete);
+
+ if (blocking_call)
+ vfe_dev->reset_pending = 1;
+
+ if (first_start) {
+ msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC);
+ } else {
+ msm_camera_io_w_mb(0x1EF, vfe_dev->vfe_base + 0xC);
+ msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, vfe_dev->vfe_base, 0x0031FFFF);
+ }
+
+ if (blocking_call) {
+ rc = wait_for_completion_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+ if (rc <= 0) {
+ pr_err("%s:%d failed: reset timeout\n", __func__,
+ __LINE__);
+ vfe_dev->reset_pending = 0;
+ }
+ }
+
+ return rc;
+}
+
+static void msm_vfe44_axi_reload_wm(struct vfe_device *vfe_dev,
+ void __iomem *vfe_base, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_base + 0x4C);
+}
+
+static void msm_vfe44_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ val = msm_camera_io_r(vfe_base + VFE44_WM_BASE(wm_idx));
+ if (enable)
+ val |= 0x1;
+ else
+ val &= ~0x1;
+ msm_camera_io_w_mb(val,
+ vfe_base + VFE44_WM_BASE(wm_idx));
+}
+
+static void msm_vfe44_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t cgc_override)
+{
+ uint32_t val = 0;
+
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x974);
+ if (cgc_override)
+ val |= (1 << wm_idx);
+ else
+ val &= ~(1 << wm_idx);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x974);
+}
+
+static void msm_vfe44_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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+
+ 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);
+
+ msm_vfe44_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+
+ 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);
+
+ msm_vfe44_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe44_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe44_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe44_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe44_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe44_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
+ uint32_t framedrop_period)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ msm_camera_io_w(framedrop_pattern, vfe_base +
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+ temp = msm_camera_io_r(vfe_base +
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ temp &= 0xFFFFFF83;
+ msm_camera_io_w(temp | (framedrop_period - 1) << 2,
+ vfe_base +
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ }
+}
+
+static void msm_vfe44_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+}
+
+static int32_t msm_vfe44_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
+{
+ int rc = 0;
+
+ switch (bpp) {
+ case 8:
+ *bpp_reg = 0;
+ break;
+ case 10:
+ *bpp_reg = 1 << 0;
+ break;
+ case 12:
+ *bpp_reg = 1 << 1;
+ break;
+ default:
+ pr_err("%s:%d invalid bpp %d", __func__, __LINE__, bpp);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_vfe44_convert_io_fmt_to_reg(
+ enum msm_isp_pack_fmt pack_format, uint32_t *pack_reg)
+{
+ int rc = 0;
+
+ switch (pack_format) {
+ case QCOM:
+ *pack_reg = 0x0;
+ break;
+ case MIPI:
+ *pack_reg = 0x1;
+ break;
+ case DPCM6:
+ *pack_reg = 0x2;
+ break;
+ case DPCM8:
+ *pack_reg = 0x3;
+ break;
+ case PLAIN8:
+ *pack_reg = 0x4;
+ break;
+ case PLAIN16:
+ *pack_reg = 0x5;
+ break;
+ default:
+ pr_err("%s: invalid pack fmt %d!\n", __func__, pack_format);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_vfe44_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format)
+{
+ int rc = 0;
+ int bpp = 0, read_bpp = 0;
+ enum msm_isp_pack_fmt pack_fmt = 0, read_pack_fmt = 0;
+ uint32_t bpp_reg = 0, pack_reg = 0;
+ uint32_t read_bpp_reg = 0, read_pack_reg = 0;
+ uint32_t io_format_reg = 0; /*io format register bit*/
+
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x54);
+
+ /*input config*/
+ if ((stream_src < RDI_INTF_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux ==
+ EXTERNAL_READ)) {
+ read_bpp = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe44_convert_bpp_to_reg(read_bpp, &read_bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! in_bpp %d rc %d\n",
+ __func__, read_bpp, rc);
+ return rc;
+ }
+
+ read_pack_fmt = msm_isp_get_pack_format(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe44_convert_io_fmt_to_reg(
+ read_pack_fmt, &read_pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /*use input format(v4l2_pix_fmt) to get pack format*/
+ io_format_reg &= 0xFFC8FFFF;
+ io_format_reg |= (read_bpp_reg << 20 | read_pack_reg << 16);
+ }
+
+ bpp = msm_isp_get_bit_per_pixel(io_format);
+ rc = msm_vfe44_convert_bpp_to_reg(bpp, &bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! bpp %d rc = %d\n",
+ __func__, bpp, rc);
+ return rc;
+ }
+
+ switch (stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ /*use output format(v4l2_pix_fmt) to get pack format*/
+ pack_fmt = msm_isp_get_pack_format(io_format);
+ rc = msm_vfe44_convert_io_fmt_to_reg(pack_fmt, &pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4 | pack_reg;
+ break;
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return -EINVAL;
+ }
+
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x54);
+ return 0;
+}
+
+static int msm_vfe44_fetch_engine_start(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+ /* There is other option of passing buffer address from user,
+ * in such case, driver needs to map the buffer and use it
+ */
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0) {
+ pr_err("%s: No fetch buffer\n", __func__);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr, vfe_dev->vfe_base + 0x228);
+
+ msm_camera_io_w_mb(0x10000, vfe_dev->vfe_base + 0x4C);
+ msm_camera_io_w_mb(0x20000, vfe_dev->vfe_base + 0x4C);
+
+ ISP_DBG("%s: Fetch Engine ready\n", __func__);
+ return 0;
+}
+
+static void msm_vfe44_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t x_size_word;
+ struct msm_vfe_fetch_engine_cfg *fe_cfg = NULL;
+ uint32_t temp = 0;
+
+ if (pix_cfg->input_mux == EXTERNAL_READ) {
+ fe_cfg = &pix_cfg->fetch_engine_cfg;
+ pr_debug("%s: fetch_dbg wd x ht buf = %d x %d, fe = %d x %d\n",
+ __func__, fe_cfg->buf_width, fe_cfg->buf_height,
+ fe_cfg->fetch_width, fe_cfg->fetch_height);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_cgc_override(vfe_dev,
+ VFE44_BUS_RD_CGC_OVERRIDE_BIT, 1);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp &= 0xFFFFFFFD;
+ temp |= (1 << 1);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ msm_vfe44_config_irq(vfe_dev, (1 << 24), 0,
+ MSM_ISP_IRQ_SET);
+ msm_camera_io_w((fe_cfg->fetch_height - 1) & 0xFFF,
+ vfe_dev->vfe_base + 0x238);
+
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->fetch_width);
+ msm_camera_io_w((x_size_word - 1) << 16,
+ vfe_dev->vfe_base + 0x23C);
+
+ msm_camera_io_w(x_size_word << 16 |
+ (fe_cfg->buf_height - 1) << 4 | VFE44_FETCH_BURST_LEN,
+ vfe_dev->vfe_base + 0x240);
+
+ msm_camera_io_w(0 << 28 | 2 << 25 |
+ ((fe_cfg->buf_width - 1) & 0x1FFF) << 12 |
+ ((fe_cfg->buf_height - 1) & 0xFFF), vfe_dev->vfe_base + 0x244);
+
+ /* need to use formulae to calculate MAIN_UNPACK_PATTERN*/
+ msm_camera_io_w(0xF6543210, vfe_dev->vfe_base + 0x248);
+ msm_camera_io_w(0xF, vfe_dev->vfe_base + 0x264);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ temp |= 2 << 16 | pix_cfg->pixel_pattern;
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x1C);
+
+ } else {
+ pr_err("%s: Invalid mux configuration - mux: %d", __func__,
+ pix_cfg->input_mux);
+ return;
+ }
+
+}
+
+static void msm_vfe44_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, subsample_period, subsample_pattern;
+ struct msm_vfe_camif_subsample_cfg *subsample_cfg =
+ &pix_cfg->camif_cfg.subsample_cfg;
+ uint16_t bus_sub_en = 0;
+
+ vfe_dev->dual_vfe_enable = camif_cfg->is_split;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x1C);
+
+ if (subsample_cfg->pixel_skip || subsample_cfg->line_skip) {
+ bus_sub_en = 1;
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ val &= 0xFFFFFFDF;
+ val = val | bus_sub_en << 5;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ subsample_cfg->pixel_skip &= 0x0000FFFF;
+ subsample_cfg->line_skip &= 0x0000FFFF;
+ msm_camera_io_w((subsample_cfg->line_skip << 16) |
+ subsample_cfg->pixel_skip,
+ vfe_dev->vfe_base + 0x30C);
+ }
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+ subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
+ subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
+
+ 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);
+ if (subsample_period && subsample_pattern) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ val &= 0xFFE0FFFF;
+ val = (subsample_period - 1) << 16;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ ISP_DBG("%s:camif PERIOD %x PATTERN %x\n",
+ __func__, subsample_period, subsample_pattern);
+
+ val = subsample_pattern;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x314);
+ } else {
+ 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);
+
+}
+
+static void msm_vfe44_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ msm_vfe44_cfg_camif(vfe_dev, pix_cfg);
+ break;
+ case EXTERNAL_READ:
+ msm_vfe44_cfg_fetch_engine(vfe_dev, pix_cfg);
+ break;
+ default:
+ pr_err("%s: Unsupported input mux %d\n",
+ __func__, pix_cfg->input_mux);
+ }
+}
+
+static void msm_vfe44_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;
+
+ if (update_state == ENABLE_CAMIF) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24);
+
+ msm_vfe44_config_irq(vfe_dev, 0xF7, 0x81,
+ MSM_ISP_IRQ_ENABLE);
+ msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318);
+
+ 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].stream_count > 0) ? 1 : 0);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ 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(0x4, vfe_dev->vfe_base + 0x2F4);
+ 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 ||
+ update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ uint32_t poll_val;
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ update_state = DISABLE_CAMIF;
+ msm_vfe44_config_irq(vfe_dev, 0,
+ 0x81, MSM_ISP_IRQ_DISABLE);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18);
+ /* disable danger signal */
+ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0xC18);
+ msm_camera_io_w_mb((update_state == DISABLE_CAMIF ? 0x0 : 0x6),
+ vfe_dev->vfe_base + 0x2F4);
+ if (readl_poll_timeout_atomic(vfe_dev->vfe_base + 0x31C,
+ poll_val, poll_val & 0x80000000, 1000, 2000000))
+ pr_err("%s: camif disable failed %x\n",
+ __func__, poll_val);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w((1 << 0), vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+ msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask,
+ vfe_dev->irq1_mask, MSM_ISP_IRQ_SET);
+ }
+}
+
+static void msm_vfe44_cfg_rdi_reg(
+ struct vfe_device *vfe_dev, struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src)
+{
+ uint8_t rdi = input_src - VFE_RAW_0;
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE44_RDI_BASE(0));
+ rdi_reg_cfg &= ~(BIT(16 + rdi));
+ rdi_reg_cfg |= rdi_cfg->frame_based << (16 + rdi);
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base + VFE44_RDI_BASE(0));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE44_RDI_BASE(rdi));
+ rdi_reg_cfg &= 0x70003;
+ rdi_reg_cfg |= (rdi * 3) << 28 | rdi_cfg->cid << 4 | 0x4;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE44_RDI_BASE(rdi));
+}
+
+static void msm_vfe44_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE44_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+
+ if (!stream_info->frame_based) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + wm_base);
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width)+1)/2 - 1) << 16 |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+
+ /*WR_BUFFER_CFG*/
+ val = (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
+ val = (((val & 0xfff) << 2) | ((val >> 12) & 0x3));
+ val = val << 2 |
+ msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_stride) << 16 |
+ VFE44_BURST_LEN;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+ } else {
+ msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
+ val = (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
+ val = (((val & 0xfff) << 2) | ((val >> 12) & 0x3));
+ val = val << 2 |
+ msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][
+ plane_idx].output_width) << 16 |
+ VFE44_BURST_LEN;
+ 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 */
+}
+
+static void msm_vfe44_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE44_WM_BASE(stream_info->wm[vfe_idx][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);
+}
+
+static void msm_vfe44_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ switch (stream_info->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_info->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_INTF_0:
+ xbar_cfg = 0x500;
+ break;
+ case RDI_INTF_1:
+ xbar_cfg = 0x600;
+ break;
+ case RDI_INTF_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 + VFE44_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE44_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE44_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE44_XBAR_BASE(wm));
+}
+
+static void msm_vfe44_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
+ uint32_t xbar_reg_cfg = 0;
+
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE44_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE44_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE44_XBAR_BASE(wm));
+}
+
+static void msm_vfe44_read_wm_ping_pong_addr(
+ struct vfe_device *vfe_dev)
+{
+ msm_camera_io_dump(vfe_dev->vfe_base +
+ (VFE44_WM_BASE(0) & 0xFFFFFFF0), 0x200, 1);
+}
+
+static void msm_vfe44_update_ping_pong_addr(
+ void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size)
+{
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE44_PING_PONG_BASE(wm_idx, pingpong_bit));
+}
+
+static void msm_vfe44_set_halt_restart_mask(struct vfe_device *vfe_dev)
+{
+ msm_vfe44_config_irq(vfe_dev, BIT(31), BIT(8), MSM_ISP_IRQ_SET);
+}
+
+
+static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev,
+ uint32_t blocking)
+{
+ int rc = 0;
+ enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
+
+ /* Keep only halt and restart mask */
+ msm_vfe44_config_irq(vfe_dev, (1 << 31), (1 << 8),
+ MSM_ISP_IRQ_SET);
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ == OVERFLOW_DETECTED)
+ pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
+ __func__, vfe_dev->pdev->id, blocking);
+
+ if (blocking) {
+ init_completion(&vfe_dev->halt_complete);
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ rc = wait_for_completion_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+ if (rc <= 0)
+ pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
+ vfe_dev->pdev->id, rc);
+ } else {
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ }
+
+ msm_isp_get_timestamp(&ts, vfe_dev);
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ /* if any stream is waiting for update, signal complete */
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ }
+
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
+
+ return rc;
+}
+
+static void msm_vfe44_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif)
+{
+ msm_vfe44_config_irq(vfe_dev, vfe_dev->recovery_irq0_mask,
+ vfe_dev->recovery_irq1_mask, MSM_ISP_IRQ_ENABLE);
+ msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318);
+
+ /* Start AXI */
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, VFE_SRC_MAX);
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+ if (enable_camif)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+}
+
+static uint32_t msm_vfe44_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 8) & 0x7F;
+}
+
+static uint32_t msm_vfe44_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 25) & 0xF;
+}
+
+static uint32_t msm_vfe44_get_pingpong_status(
+ struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x268);
+}
+
+static int msm_vfe44_get_stats_idx(enum msm_isp_stats_type stats_type)
+{
+ switch (stats_type) {
+ case MSM_ISP_STATS_BE:
+ return STATS_IDX_BE;
+ case MSM_ISP_STATS_BG:
+ return STATS_IDX_BG;
+ case MSM_ISP_STATS_BF:
+ return STATS_IDX_BF;
+ case MSM_ISP_STATS_AWB:
+ return STATS_IDX_AWB;
+ case MSM_ISP_STATS_RS:
+ return STATS_IDX_RS;
+ case MSM_ISP_STATS_CS:
+ return STATS_IDX_CS;
+ case MSM_ISP_STATS_IHIST:
+ return STATS_IDX_IHIST;
+ case MSM_ISP_STATS_BHIST:
+ return STATS_IDX_BHIST;
+ case MSM_ISP_STATS_BF_SCALE:
+ return STATS_IDX_BF_SCALE;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static int msm_vfe44_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ if (stream_info[STATS_IDX_BF].state ==
+ STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state !=
+ STATS_AVAILABLE) {
+ pr_err("%s: does not support BF_SCALE while BF is disabled\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (stream_info[STATS_IDX_BF].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF].composite_flag !=
+ stream_info[STATS_IDX_BF_SCALE].composite_flag) {
+ pr_err("%s: Different composite flag for BF and BF_SCALE\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_vfe44_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t request_comp_index, uint8_t enable)
+{
+ uint32_t comp_mask_reg, mask_bf_scale;
+ atomic_t *stats_comp_mask;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask < 1)
+ return;
+
+ if (request_comp_index >= MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__, request_comp_index,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask >
+ MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ /* BF scale is controlled by BF also so ignore bit 0 of BF scale */
+ stats_mask = stats_mask & 0x1FF;
+ mask_bf_scale = stats_mask >> SHIFT_BF_SCALE_BIT;
+
+ stats_comp_mask = &stats_data->stats_comp_mask[request_comp_index];
+ comp_mask_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x44);
+
+ if (enable) {
+ comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8);
+ atomic_set(stats_comp_mask, stats_mask |
+ atomic_read(stats_comp_mask));
+ msm_vfe44_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_ENABLE);
+ } else {
+ if (!(atomic_read(stats_comp_mask) & stats_mask))
+ return;
+ if (stats_mask & (1 << STATS_IDX_BF_SCALE) &&
+ atomic_read(stats_comp_mask) &
+ (1 << STATS_IDX_BF_SCALE))
+ atomic_set(stats_comp_mask,
+ ~(1 << STATS_IDX_BF_SCALE) &
+ atomic_read(stats_comp_mask));
+
+ atomic_set(stats_comp_mask,
+ ~stats_mask & atomic_read(stats_comp_mask));
+ comp_mask_reg &= ~(mask_bf_scale <<
+ (16 + request_comp_index * 8));
+ msm_vfe44_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_DISABLE);
+ }
+ msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44);
+
+ ISP_DBG("%s: comp_mask_reg: %x comp mask0 %x mask1: %x\n",
+ __func__, comp_mask_reg,
+ atomic_read(&stats_data->stats_comp_mask[0]),
+ atomic_read(&stats_data->stats_comp_mask[1]));
+
+}
+
+static void msm_vfe44_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe44_config_irq(vfe_dev,
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe44_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe44_config_irq(vfe_dev,
+ (1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15)), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe44_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE44_STATS_BASE(stats_idx);
+ /* BF_SCALE does not have its own WR_ADDR_CFG,
+ * IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
+ * it's using the same from BF
+ */
+ if (stats_idx == STATS_IDX_BF_SCALE)
+ return;
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
+ vfe_dev->vfe_base + stats_base + 0x8);
+ /*WR_IRQ_FRAMEDROP_PATTERN*/
+ msm_camera_io_w(stream_info->framedrop_pattern,
+ vfe_dev->vfe_base + stats_base + 0x10);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe44_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t val = 0;
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE44_STATS_BASE(stats_idx);
+ /* BF_SCALE does not have its own WR_ADDR_CFG,
+ * IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
+ * it's using the same from BF
+ */
+ if (stats_idx == STATS_IDX_BF_SCALE)
+ return;
+
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x8);
+ /*WR_IRQ_FRAMEDROP_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe44_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = VFE44_UB_SIZE;
+ uint32_t ub_size[VFE44_NUM_STATS_TYPE] = {
+ 128, /*MSM_ISP_STATS_BF_SCALE*/
+ 64, /*MSM_ISP_STATS_BE*/
+ 128, /*MSM_ISP_STATS_BG*/
+ 128, /*MSM_ISP_STATS_BF*/
+ 16, /*MSM_ISP_STATS_AWB*/
+ 8, /*MSM_ISP_STATS_RS*/
+ 16, /*MSM_ISP_STATS_CS*/
+ 16, /*MSM_ISP_STATS_IHIST*/
+ 16, /*MSM_ISP_STATS_BHIST*/
+ };
+
+ for (i = 0; i < VFE44_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(VFE44_STATS_BURST_LEN << 30 |
+ ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE44_STATS_BASE(i) +
+ ((i == STATS_IDX_BF_SCALE) ? 0x8 : 0xC));
+ }
+}
+
+static bool msm_vfe44_is_module_cfg_lock_needed(
+ uint32_t reg_offset)
+{
+ if (reg_offset == 0x18)
+ return true;
+ else
+ return false;
+}
+
+static void msm_vfe44_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+ uint32_t stats_cfg, stats_cfg_mask = 0;
+ unsigned long flags;
+
+ for (i = 0; i < VFE44_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_IDX_BE:
+ case STATS_IDX_BG:
+ case STATS_IDX_BF:
+ case STATS_IDX_AWB:
+ case STATS_IDX_RS:
+ case STATS_IDX_CS:
+ module_cfg_mask |= 1 << (4 + i);
+ break;
+ case STATS_IDX_IHIST:
+ module_cfg_mask |= 1 << 15;
+ break;
+ case STATS_IDX_BHIST:
+ module_cfg_mask |= 1 << 18;
+ break;
+ case STATS_IDX_BF_SCALE:
+ stats_cfg_mask |= 1 << 2;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /*
+ * For vfe44 stats and other modules share module_cfg register.
+ * Hence need to Grab lock.
+ */
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x18);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x18);
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
+
+ stats_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x888);
+ if (enable)
+ stats_cfg |= stats_cfg_mask;
+ else
+ stats_cfg &= ~stats_cfg_mask;
+ msm_camera_io_w(stats_cfg, vfe_dev->vfe_base + 0x888);
+}
+
+static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t cgc_override)
+{
+ int i;
+ uint32_t val = 0, cgc_mask = 0;
+
+ for (i = 0; i < VFE44_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_IDX_BE:
+ cgc_mask |= (1 << 8);
+ break;
+ case STATS_IDX_BG:
+ cgc_mask |= (1 << 9);
+ break;
+ case STATS_IDX_BF:
+ cgc_mask |= (1 << 10);
+ break;
+ case STATS_IDX_AWB:
+ cgc_mask |= (1 << 11);
+ break;
+ case STATS_IDX_RS:
+ cgc_mask |= (1 << 12);
+ break;
+ case STATS_IDX_CS:
+ cgc_mask |= (1 << 13);
+ break;
+ case STATS_IDX_IHIST:
+ cgc_mask |= (1 << 14);
+ break;
+ case STATS_IDX_BHIST:
+ cgc_mask |= (1 << 15);
+ break;
+ case STATS_IDX_BF_SCALE:
+ cgc_mask |= (1 << 10);
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /* CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x974);
+ if (cgc_override)
+ val |= cgc_mask;
+ else
+ val &= ~cgc_mask;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x974);
+}
+
+static void msm_vfe44_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE44_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+}
+
+static uint32_t msm_vfe44_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 15) & 0x1FF;
+}
+
+static uint32_t msm_vfe44_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 29) & 0x3;
+}
+
+static uint32_t msm_vfe44_stats_get_frame_id(
+ struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+
+static void msm_vfe44_get_error_mask(
+ uint32_t *error_mask0, uint32_t *error_mask1)
+{
+ *error_mask0 = 0x00000000;
+ *error_mask1 = 0x01FFFEFF;
+}
+
+static void msm_vfe44_get_overflow_mask(uint32_t *overflow_mask)
+{
+ *overflow_mask = 0x00FFFE7E;
+}
+
+static void msm_vfe44_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask)
+{
+ *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
+}
+
+static void msm_vfe44_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask)
+{
+ *irq0_mask = vfe_dev->irq0_mask;
+ *irq1_mask = vfe_dev->irq1_mask;
+}
+
+static void msm_vfe44_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask)
+{
+ *irq0_mask = BIT(31);
+ *irq1_mask = BIT(8);
+}
+
+static struct msm_vfe_axi_hardware_info msm_vfe44_axi_hw_info = {
+ .num_wm = 6,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 96,
+ .scratch_buf_range = SZ_32M + SZ_4M,
+};
+
+static struct msm_vfe_stats_hardware_info msm_vfe44_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_BE | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST |
+ 1 << MSM_ISP_STATS_AWB | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_BF_SCALE,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .num_stats_type = VFE44_NUM_STATS_TYPE,
+ .num_stats_comp_mask = VFE44_NUM_STATS_COMP,
+};
+
+struct msm_vfe_hardware_info vfe44_hw_info = {
+ .num_iommu_ctx = 1,
+ .num_iommu_secure_ctx = 1,
+ .vfe_clk_idx = VFE44_CLK_IDX,
+ .runtime_axi_update = 0,
+ .min_ab = 100000000,
+ .min_ib = 100000000,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe44_read_and_clear_irq_status,
+ .read_irq_status = msm_vfe44_read_irq_status,
+ .process_camif_irq = msm_vfe44_process_input_irq,
+ .process_reset_irq = msm_vfe44_process_reset_irq,
+ .process_halt_irq = msm_vfe44_process_halt_irq,
+ .process_reg_update = msm_vfe44_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ .process_epoch_irq = msm_vfe44_process_epoch_irq,
+ .config_irq = msm_vfe44_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe44_axi_reload_wm,
+ .enable_wm = msm_vfe44_axi_enable_wm,
+ .cfg_io_format = msm_vfe44_cfg_io_format,
+ .cfg_comp_mask = msm_vfe44_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe44_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe44_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe44_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe44_cfg_framedrop,
+ .clear_framedrop = msm_vfe44_clear_framedrop,
+ .cfg_wm_reg = msm_vfe44_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe44_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe44_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe44_axi_clear_wm_xbar_reg,
+ .cfg_ub = msm_vfe47_cfg_axi_ub,
+ .read_wm_ping_pong_addr =
+ msm_vfe44_read_wm_ping_pong_addr,
+ .update_ping_pong_addr =
+ msm_vfe44_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe44_get_comp_mask,
+ .get_wm_mask = msm_vfe44_get_wm_mask,
+ .get_pingpong_status = msm_vfe44_get_pingpong_status,
+ .halt = msm_vfe44_axi_halt,
+ .restart = msm_vfe44_axi_restart,
+ .update_cgc_override =
+ msm_vfe44_axi_update_cgc_override,
+ .ub_reg_offset = msm_vfe44_ub_reg_offset,
+ .get_ub_size = msm_vfe44_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe44_reg_update,
+ .cfg_input_mux = msm_vfe44_cfg_input_mux,
+ .update_camif_state = msm_vfe44_update_camif_state,
+ .start_fetch_eng = msm_vfe44_fetch_engine_start,
+ .cfg_rdi_reg = msm_vfe44_cfg_rdi_reg,
+ .reset_hw = msm_vfe44_reset_hardware,
+ .init_hw = msm_vfe47_init_hardware,
+ .init_hw_reg = msm_vfe44_init_hardware_reg,
+ .clear_status_reg = msm_vfe44_clear_status_reg,
+ .release_hw = msm_vfe47_release_hardware,
+ .get_error_mask = msm_vfe44_get_error_mask,
+ .get_overflow_mask = msm_vfe44_get_overflow_mask,
+ .get_rdi_wm_mask = msm_vfe44_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe44_get_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe44_get_halt_restart_mask,
+ .process_error_status = msm_vfe44_process_error_status,
+ .is_module_cfg_lock_needed =
+ msm_vfe44_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = NULL,
+ .set_halt_restart_mask =
+ msm_vfe44_set_halt_restart_mask,
+ .set_bus_err_ign_mask = NULL,
+ .get_bus_err_mask = NULL,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe44_get_stats_idx,
+ .check_streams = msm_vfe44_stats_check_streams,
+ .cfg_comp_mask = msm_vfe44_stats_cfg_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe44_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe44_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe44_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe44_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe44_stats_cfg_ub,
+ .enable_module = msm_vfe44_stats_enable_module,
+ .update_ping_pong_addr =
+ msm_vfe44_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe44_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe44_stats_get_wm_mask,
+ .get_frame_id = msm_vfe44_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe44_get_pingpong_status,
+ .update_cgc_override =
+ msm_vfe44_stats_update_cgc_override,
+ .enable_stats_wm = NULL,
+ },
+ .platform_ops = {
+ .get_platform_data = msm_vfe47_get_platform_data,
+ .enable_regulators = msm_vfe47_enable_regulators,
+ .get_regulators = msm_vfe47_get_regulators,
+ .put_regulators = msm_vfe47_put_regulators,
+ .enable_clks = msm_vfe47_enable_clks,
+ .get_clks = msm_vfe47_get_clks,
+ .put_clks = msm_vfe47_put_clks,
+ .get_clk_rates = msm_vfe47_get_clk_rates,
+ .get_max_clk_rate = msm_vfe47_get_max_clk_rate,
+ .set_clk_rate = msm_vfe47_set_clk_rate,
+ .init_bw_mgr = msm_vfe47_init_bandwidth_mgr,
+ .deinit_bw_mgr = msm_vfe47_deinit_bandwidth_mgr,
+ .update_bw = msm_vfe47_update_bandwidth,
+ }
+ },
+ .dmi_reg_offset = 0x918,
+ .axi_hw_info = &msm_vfe44_axi_hw_info,
+ .stats_hw_info = &msm_vfe44_stats_hw_info,
+ .regulator_names = {"vdd"},
+};
+EXPORT_SYMBOL(vfe44_hw_info);
+
+static const struct of_device_id msm_vfe44_dt_match[] = {
+ {
+ .compatible = "qcom,vfe44",
+ .data = &vfe44_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe44_dt_match);
+
+static struct platform_driver vfe44_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe44",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe44_dt_match,
+ },
+};
+
+static int __init msm_vfe44_init_module(void)
+{
+ return platform_driver_register(&vfe44_driver);
+}
+
+static void __exit msm_vfe44_exit_module(void)
+{
+ platform_driver_unregister(&vfe44_driver);
+}
+
+module_init(msm_vfe44_init_module);
+module_exit(msm_vfe44_exit_module);
+MODULE_DESCRIPTION("MSM VFE44 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.h
new file mode 100644
index 0000000..274674b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013-2014, 2018, 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_ISP44_H__
+#define __MSM_ISP44_H__
+
+extern struct msm_vfe_hardware_info vfe44_hw_info;
+#endif /* __MSM_ISP44_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
new file mode 100644
index 0000000..b319738
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
@@ -0,0 +1,2037 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+
+#include "msm_isp46.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"
+#include "msm_isp47.h"
+#include "linux/iopoll.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define STATS_IDX_BF_SCALE 0
+#define STATS_IDX_HDR_BE 1
+#define STATS_IDX_BG 2
+#define STATS_IDX_BF 3
+#define STATS_IDX_HDR_BHIST 4
+#define STATS_IDX_RS 5
+#define STATS_IDX_CS 6
+#define STATS_IDX_IHIST 7
+#define STATS_IDX_BHIST 8
+
+#define VFE46_8994V1_VERSION 0x60000000
+
+#define VFE46_BURST_LEN 3
+#define VFE46_FETCH_BURST_LEN 3
+#define VFE46_STATS_BURST_LEN 3
+#define VFE46_UB_SIZE_VFE0 2048
+#define VFE46_UB_SIZE_VFE1 1536
+#define VFE46_UB_STATS_SIZE 144
+#define MSM_ISP46_TOTAL_IMAGE_UB_VFE0 (VFE46_UB_SIZE_VFE0 - VFE46_UB_STATS_SIZE)
+#define MSM_ISP46_TOTAL_IMAGE_UB_VFE1 (VFE46_UB_SIZE_VFE1 - VFE46_UB_STATS_SIZE)
+#define VFE46_WM_BASE(idx) (0xA0 + 0x24 * idx)
+#define VFE46_RDI_BASE(idx) (0x39C + 0x4 * idx)
+#define VFE46_XBAR_BASE(idx) (0x90 + 0x4 * (idx / 2))
+#define VFE46_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0)
+#define VFE46_PING_PONG_BASE(wm, ping_pong) \
+ (VFE46_WM_BASE(wm) + 0x4 * (1 + ((~ping_pong) & 0x1)))
+#define SHIFT_BF_SCALE_BIT 1
+#define VFE46_NUM_STATS_COMP 2
+#define VFE46_BUS_RD_CGC_OVERRIDE_BIT 16
+
+static uint32_t stats_base_addr[] = {
+ 0x1E4, /* BF_SCALE */
+ 0x19C, /* HDR_BE */
+ 0x1F0, /* BG */
+ 0x1CC, /* BF */
+ 0x1B4, /* HDR_BHIST */
+ 0x220, /* RS */
+ 0x238, /* CS */
+ 0x250, /* IHIST */
+ 0x208, /* BHIST (SKIN_BHIST) */
+};
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 11, /* BF_SCALE */
+ 8, /* HDR_BE */
+ 12, /* BG */
+ 10, /* BF */
+ 9, /* HDR_BHIST */
+ 14, /* RS */
+ 15, /* CS */
+ 16, /* IHIST */
+ 13, /* BHIST (SKIN_BHIST) */
+};
+
+#define VFE46_NUM_STATS_TYPE 9
+#define VFE46_STATS_BASE(idx) (stats_base_addr[idx])
+#define VFE46_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE46_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (stats_pingpong_offset_map[idx])) & 0x1))
+
+#define VFE46_CLK_IDX 2
+
+static uint32_t msm_vfe46_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx)
+{
+ return (VFE46_WM_BASE(wm_idx) + 0x10);
+}
+
+static uint32_t msm_vfe46_get_ub_size(struct vfe_device *vfe_dev)
+{
+ if (vfe_dev->pdev->id == ISP_VFE0)
+ return MSM_ISP46_TOTAL_IMAGE_UB_VFE0;
+ return MSM_ISP46_TOTAL_IMAGE_UB_VFE1;
+}
+
+static void msm_vfe46_config_irq(struct vfe_device *vfe_dev,
+ uint32_t irq0_mask, uint32_t irq1_mask,
+ enum msm_isp_irq_operation oper)
+{
+ switch (oper) {
+ case MSM_ISP_IRQ_ENABLE:
+ vfe_dev->irq0_mask |= irq0_mask;
+ vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+ break;
+ case MSM_ISP_IRQ_DISABLE:
+ vfe_dev->irq0_mask &= ~irq0_mask;
+ vfe_dev->irq1_mask &= ~irq1_mask;
+ break;
+ case MSM_ISP_IRQ_SET:
+ vfe_dev->irq0_mask = irq0_mask;
+ vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+ break;
+ }
+ msm_camera_io_w_mb(vfe_dev->irq0_mask,
+ vfe_dev->vfe_base + 0x5C);
+ msm_camera_io_w_mb(vfe_dev->irq1_mask,
+ vfe_dev->vfe_base + 0x60);
+}
+
+static int32_t msm_vfe46_init_dt_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *dt_parms, void __iomem *dev_mem_base)
+{
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *dt_settings = NULL, *dt_regs = NULL, num_dt_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, dt_parms->entries,
+ &num_dt_entries);
+ if (rc < 0 || !num_dt_entries) {
+ pr_err("%s: NO QOS entries found\n", __func__);
+ return -EINVAL;
+ }
+ dt_settings = kcalloc(num_dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_settings)
+ return -ENOMEM;
+ dt_regs = kcalloc(num_dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_regs) {
+ kfree(dt_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, dt_parms->regs,
+ dt_regs, num_dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS BUS BDG info\n", __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ return -EINVAL;
+ }
+ if (dt_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ dt_parms->settings,
+ dt_settings, num_dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS settings\n",
+ __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ } else {
+ for (i = 0; i < num_dt_entries; i++) {
+ msm_camera_io_w(dt_settings[i],
+ dev_mem_base +
+ dt_regs[i]);
+ }
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ } else {
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ return 0;
+}
+
+static void msm_vfe46_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_hw_init_parms qos_parms;
+ struct msm_vfe_hw_init_parms vbif_parms;
+ struct msm_vfe_hw_init_parms ds_parms;
+
+ memset(&qos_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+ memset(&vbif_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+ memset(&ds_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+
+ qos_parms.entries = "qos-entries";
+ qos_parms.regs = "qos-regs";
+ qos_parms.settings = "qos-settings";
+ vbif_parms.entries = "vbif-entries";
+ vbif_parms.regs = "vbif-regs";
+ vbif_parms.settings = "vbif-settings";
+ ds_parms.entries = "ds-entries";
+ ds_parms.regs = "ds-regs";
+ ds_parms.settings = "ds-settings";
+
+ msm_vfe46_init_dt_parms(vfe_dev, &qos_parms, vfe_dev->vfe_base);
+ msm_vfe46_init_dt_parms(vfe_dev, &ds_parms, vfe_dev->vfe_base);
+ msm_vfe46_init_dt_parms(vfe_dev, &vbif_parms, vfe_dev->vfe_vbif_base);
+
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x84);
+ /* IRQ_MASK/CLEAR */
+ msm_vfe46_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe46_clear_status_reg(struct vfe_device *vfe_dev)
+{
+ msm_vfe46_config_irq(vfe_dev, 0x80000000, 0, MSM_ISP_IRQ_SET);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+}
+
+static void msm_vfe46_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);
+ vfe_dev->reset_pending = 0;
+ }
+}
+
+static void msm_vfe46_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);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x374);
+ }
+}
+
+static void msm_vfe46_process_input_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x1000003))
+ return;
+
+ if (irq_status0 & (1 << 24)) {
+ ISP_DBG("%s: Fetch Engine Read IRQ\n", __func__);
+ msm_isp_fetch_engine_done_notify(vfe_dev,
+ &vfe_dev->fetch_engine_info);
+ }
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_increment_frame_id(vfe_dev, VFE_PIX_0, ts);
+ }
+
+ if (irq_status0 & (1 << 1))
+ ISP_DBG("%s: EOF IRQ\n", __func__);
+}
+
+static void msm_vfe46_process_violation_status(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status = vfe_dev->error_info.violation_status;
+
+ if (violation_status > 39) {
+ pr_err("%s: invalid violation status %d\n",
+ __func__, violation_status);
+ return;
+ }
+
+ pr_err("%s: VFE pipeline violation status %d\n", __func__,
+ violation_status);
+}
+
+static void msm_vfe46_process_error_status(struct vfe_device *vfe_dev)
+{
+ uint32_t error_status1 = vfe_dev->error_info.error_mask1;
+
+ if (error_status1 & (1 << 0)) {
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, vfe_dev->error_info.camif_status);
+ msm_camera_io_dump(vfe_dev->vfe_base + 0x3A8, 0x30, 1);
+ }
+ if (error_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (error_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (error_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (error_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (error_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (error_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (error_status1 & (1 << 7))
+ msm_vfe46_process_violation_status(vfe_dev);
+ if (error_status1 & (1 << 9))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (error_status1 & (1 << 10))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (error_status1 & (1 << 11))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (error_status1 & (1 << 12))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (error_status1 & (1 << 13))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (error_status1 & (1 << 14))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (error_status1 & (1 << 15))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (error_status1 & (1 << 16))
+ pr_err("%s: status hdr be bus overflow\n", __func__);
+ if (error_status1 & (1 << 17))
+ pr_err("%s: status bg bus overflow\n", __func__);
+ if (error_status1 & (1 << 18))
+ pr_err("%s: status bf bus overflow\n", __func__);
+ if (error_status1 & (1 << 19))
+ pr_err("%s: status hdr bhist bus overflow\n", __func__);
+ if (error_status1 & (1 << 20))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (error_status1 & (1 << 21))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (error_status1 & (1 << 22))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (error_status1 & (1 << 23))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ if (error_status1 & (1 << 24))
+ pr_err("%s: status bf scale bus overflow\n", __func__);
+}
+
+static void msm_vfe46_read_and_clear_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 + 0x6C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70);
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58);
+
+ *irq_status0 &= vfe_dev->irq0_mask;
+ *irq_status1 &= vfe_dev->irq1_mask;
+
+ if (*irq_status1 & (1 << 0)) {
+ vfe_dev->error_info.camif_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x3D0);
+ msm_vfe46_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE);
+ }
+
+ if (*irq_status1 & (1 << 7))
+ vfe_dev->error_info.violation_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x7C);
+
+}
+
+static void msm_vfe46_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 + 0x6C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70);
+}
+
+static void msm_vfe46_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ enum msm_vfe_input_src i;
+ uint32_t shift_irq;
+ uint8_t reg_updated = 0;
+ unsigned long flags;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+ /* Shift status bits so that PIX REG UPDATE is 1st bit */
+ shift_irq = ((irq_status0 & 0xF0) >> 4);
+
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ if (shift_irq & BIT(i)) {
+ reg_updated |= BIT(i);
+ ISP_DBG("%s Reg Update IRQ %x\n", __func__,
+ (uint32_t)BIT(i));
+
+ switch (i) {
+ case VFE_PIX_0:
+ msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
+ VFE_PIX_0, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ msm_isp_stats_stream_update(vfe_dev);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, i);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ msm_isp_increment_frame_id(vfe_dev, i, ts);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /*
+ * Reg Update is pseudo SOF for RDI,
+ * so request every frame
+ */
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, i);
+ /* reg upd is also epoch for rdi */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ break;
+ default:
+ pr_err("%s: Error case\n", __func__);
+ return;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ if (reg_updated & BIT(VFE_PIX_0))
+ vfe_dev->reg_updated = 1;
+
+ vfe_dev->reg_update_requested &= ~reg_updated;
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static void msm_vfe46_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0xc))
+ return;
+
+ if (irq_status0 & BIT(2)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
+ msm_isp_update_error_frame_count(vfe_dev);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static void msm_vfe46_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ uint32_t update_mask = 0;
+ unsigned long flags;
+
+ /* This HW supports upto VFE_RAW_2 */
+ if (frame_src > VFE_RAW_2 && frame_src != VFE_SRC_MAX) {
+ pr_err("%s Error case\n", __func__);
+ return;
+ }
+
+ /*
+ * If frame_src == VFE_SRC_MAX request reg_update on all
+ * supported INTF
+ */
+ if (frame_src == VFE_SRC_MAX)
+ update_mask = 0xF;
+ else
+ update_mask = BIT((uint32_t)frame_src);
+ ISP_DBG("%s update_mask %x\n", __func__, update_mask);
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].reg_update_frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ vfe_dev->reg_update_requested |= update_mask;
+ vfe_dev->common_data->dual_vfe_res->reg_update_mask[vfe_dev->pdev->id] =
+ vfe_dev->reg_update_requested;
+ if ((vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) &&
+ ((frame_src == VFE_PIX_0) || (frame_src == VFE_SRC_MAX))) {
+ if (!vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]) {
+ pr_err("%s vfe_base for ISP_VFE0 is NULL\n", __func__);
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock,
+ flags);
+ return;
+ }
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
+ + 0x3D8);
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x3D8);
+ } else if (!vfe_dev->is_split ||
+ ((frame_src == VFE_PIX_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
+ (frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x3D8);
+ }
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+static long msm_vfe46_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking_call)
+{
+ long rc = 0;
+
+ init_completion(&vfe_dev->reset_complete);
+
+ if (blocking_call)
+ vfe_dev->reset_pending = 1;
+
+ if (first_start) {
+ msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0x18);
+ } else {
+ msm_camera_io_w_mb(0x1EF, vfe_dev->vfe_base + 0x18);
+ msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, vfe_dev->vfe_base, 0x0031FFFF);
+ }
+
+ if (blocking_call) {
+ rc = wait_for_completion_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+ if (rc <= 0) {
+ pr_err("%s:%d failed: reset timeout\n", __func__,
+ __LINE__);
+ vfe_dev->reset_pending = 0;
+ }
+ }
+
+ return rc;
+}
+
+static void msm_vfe46_axi_reload_wm(struct vfe_device *vfe_dev,
+ void __iomem *vfe_base, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_base + 0x80);
+}
+
+static void msm_vfe46_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ if (enable)
+ val |= (1 << wm_idx);
+ else
+ val &= ~(1 << wm_idx);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x3C);
+}
+
+static void msm_vfe46_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ val = msm_camera_io_r(vfe_base + VFE46_WM_BASE(wm_idx));
+ if (enable)
+ val |= 0x1;
+ else
+ val &= ~0x1;
+ msm_camera_io_w_mb(val,
+ vfe_base + VFE46_WM_BASE(wm_idx));
+}
+
+static void msm_vfe46_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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
+ 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 + 0x74);
+
+ msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
+
+ msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe46_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe46_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe46_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe46_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe46_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
+ uint32_t framedrop_period)
+{
+ uint32_t i, temp;
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ msm_camera_io_w(framedrop_pattern, vfe_base +
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+ temp = msm_camera_io_r(vfe_base +
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ temp &= 0xFFFFFF83;
+ msm_camera_io_w(temp | (framedrop_period - 1) << 2,
+ vfe_base + VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
+ }
+}
+
+static void msm_vfe46_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
+}
+
+static int32_t msm_vfe46_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
+{
+ int rc = 0;
+
+ switch (bpp) {
+ case 8:
+ *bpp_reg = 0;
+ break;
+ case 10:
+ *bpp_reg = 0x1;
+ break;
+ case 12:
+ *bpp_reg = 0x2;
+ break;
+ case 14:
+ *bpp_reg = 0x3;
+ break;
+ default:
+ pr_err("%s:%d invalid bpp %d\n", __func__, __LINE__, bpp);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_vfe46_convert_io_fmt_to_reg(
+ enum msm_isp_pack_fmt pack_format, uint32_t *pack_reg)
+{
+ int rc = 0;
+
+ switch (pack_format) {
+ case QCOM:
+ *pack_reg = 0x0;
+ break;
+ case MIPI:
+ *pack_reg = 0x1;
+ break;
+ case DPCM6:
+ *pack_reg = 0x2;
+ break;
+ case DPCM8:
+ *pack_reg = 0x3;
+ break;
+ case PLAIN8:
+ *pack_reg = 0x4;
+ break;
+ case PLAIN16:
+ *pack_reg = 0x5;
+ break;
+ default:
+ pr_err("%s: invalid pack fmt %d!\n", __func__, pack_format);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+static int32_t msm_vfe46_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format)
+{
+ int rc = 0;
+ int bpp = 0, read_bpp = 0;
+ enum msm_isp_pack_fmt pack_fmt = 0, read_pack_fmt = 0;
+ uint32_t bpp_reg = 0, pack_reg = 0;
+ uint32_t read_bpp_reg = 0, read_pack_reg = 0;
+ uint32_t io_format_reg = 0; /*io format register bit*/
+
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x88);
+
+ /*input config*/
+ if ((stream_src < RDI_INTF_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux ==
+ EXTERNAL_READ)) {
+ read_bpp = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe46_convert_bpp_to_reg(read_bpp, &read_bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! in_bpp %d rc %d\n",
+ __func__, read_bpp, rc);
+ return rc;
+ }
+
+ read_pack_fmt = msm_isp_get_pack_format(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe46_convert_io_fmt_to_reg(
+ read_pack_fmt, &read_pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /*use input format(v4l2_pix_fmt) to get pack format*/
+ io_format_reg &= 0xFFC8FFFF;
+ io_format_reg |= (read_bpp_reg << 20 | read_pack_reg << 16);
+ }
+
+ bpp = msm_isp_get_bit_per_pixel(io_format);
+ rc = msm_vfe46_convert_bpp_to_reg(bpp, &bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! bpp %d rc = %d\n",
+ __func__, bpp, rc);
+ return rc;
+ }
+
+ switch (stream_src) {
+ case PIX_VIDEO:
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ /*use output format(v4l2_pix_fmt) to get pack format*/
+ pack_fmt = msm_isp_get_pack_format(io_format);
+ rc = msm_vfe46_convert_io_fmt_to_reg(pack_fmt, &pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4 | pack_reg;
+ break;
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x88);
+ return 0;
+}
+
+static int msm_vfe46_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+ /* There is other option of passing buffer address from user,
+ * in such case, driver needs to map the buffer and use it
+ */
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr, vfe_dev->vfe_base + 0x268);
+
+ msm_camera_io_w_mb(0x100000, vfe_dev->vfe_base + 0x80);
+ msm_camera_io_w_mb(0x200000, vfe_dev->vfe_base + 0x80);
+
+ ISP_DBG("%s:VFE%d Fetch Engine ready\n", __func__, vfe_dev->pdev->id);
+ return 0;
+}
+
+static void msm_vfe46_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t x_size_word, temp;
+ struct msm_vfe_fetch_engine_cfg *fe_cfg = NULL;
+
+ if (pix_cfg->input_mux == EXTERNAL_READ) {
+ fe_cfg = &pix_cfg->fetch_engine_cfg;
+ pr_debug("%s:VFE%d wd x ht buf = %d x %d, fe = %d x %d\n",
+ __func__, vfe_dev->pdev->id, fe_cfg->buf_width,
+ fe_cfg->buf_height,
+ fe_cfg->fetch_width, fe_cfg->fetch_height);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x84);
+ temp &= 0xFFFFFFFD;
+ temp |= (1 << 1);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84);
+
+ msm_vfe46_config_irq(vfe_dev, 1 << 24, 0,
+ MSM_ISP_IRQ_ENABLE);
+
+ temp = fe_cfg->fetch_height - 1;
+ msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x278);
+
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->fetch_width);
+ msm_camera_io_w((x_size_word - 1) << 16,
+ vfe_dev->vfe_base + 0x27C);
+
+ msm_camera_io_w(x_size_word << 16 |
+ (temp & 0x3FFF) << 2 | VFE46_FETCH_BURST_LEN,
+ vfe_dev->vfe_base + 0x280);
+
+ temp = ((fe_cfg->buf_width - 1) & 0x3FFF) << 16 |
+ ((fe_cfg->buf_height - 1) & 0x3FFF);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x284);
+
+ /* need to use formulae to calculate MAIN_UNPACK_PATTERN*/
+ msm_camera_io_w(0xF6543210, vfe_dev->vfe_base + 0x288);
+ msm_camera_io_w(0xF, vfe_dev->vfe_base + 0x2A4);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_cgc_override(vfe_dev,
+ VFE46_BUS_RD_CGC_OVERRIDE_BIT, 1);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp |= 2 << 5;
+ temp |= 128 << 8;
+ temp |= pix_cfg->pixel_pattern;
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ } else {
+ pr_err("%s: Invalid mux configuration - mux: %d", __func__,
+ pix_cfg->input_mux);
+ }
+}
+
+static void msm_vfe46_cfg_testgen(struct vfe_device *vfe_dev,
+ struct msm_vfe_testgen_cfg *testgen_cfg)
+{
+ uint32_t temp;
+ uint32_t bit_per_pixel = 0;
+ uint32_t bpp_reg = 0;
+ uint32_t bayer_pix_pattern_reg = 0;
+ uint32_t unicolorbar_reg = 0;
+ uint32_t unicolor_enb = 0;
+
+ bit_per_pixel = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+
+ switch (bit_per_pixel) {
+ case 8:
+ bpp_reg = 0x0;
+ break;
+ case 10:
+ bpp_reg = 0x1;
+ break;
+ case 12:
+ bpp_reg = 0x10;
+ break;
+ case 14:
+ bpp_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid bpp %d\n", __func__, bit_per_pixel);
+ break;
+ }
+
+ msm_camera_io_w(bpp_reg << 16 | testgen_cfg->burst_num_frame,
+ vfe_dev->vfe_base + 0xAF8);
+
+ msm_camera_io_w(((testgen_cfg->lines_per_frame - 1) << 16) |
+ (testgen_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0xAFC);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp |= (((testgen_cfg->h_blank) & 0x3FFF) << 8);
+ temp |= (1 << 24);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ msm_camera_io_w((1 << 16) | testgen_cfg->v_blank,
+ vfe_dev->vfe_base + 0xB0C);
+
+ switch (testgen_cfg->pixel_bayer_pattern) {
+ case ISP_BAYER_RGRGRG:
+ bayer_pix_pattern_reg = 0x0;
+ break;
+ case ISP_BAYER_GRGRGR:
+ bayer_pix_pattern_reg = 0x1;
+ break;
+ case ISP_BAYER_BGBGBG:
+ bayer_pix_pattern_reg = 0x10;
+ break;
+ case ISP_BAYER_GBGBGB:
+ bayer_pix_pattern_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid pix pattern %d\n",
+ __func__, bit_per_pixel);
+ break;
+ }
+
+ if (testgen_cfg->color_bar_pattern == COLOR_BAR_8_COLOR) {
+ unicolor_enb = 0x0;
+ } else {
+ unicolor_enb = 0x1;
+ switch (testgen_cfg->color_bar_pattern) {
+ case UNICOLOR_WHITE:
+ unicolorbar_reg = 0x0;
+ break;
+ case UNICOLOR_YELLOW:
+ unicolorbar_reg = 0x1;
+ break;
+ case UNICOLOR_CYAN:
+ unicolorbar_reg = 0x10;
+ break;
+ case UNICOLOR_GREEN:
+ unicolorbar_reg = 0x11;
+ break;
+ case UNICOLOR_MAGENTA:
+ unicolorbar_reg = 0x100;
+ break;
+ case UNICOLOR_RED:
+ unicolorbar_reg = 0x101;
+ break;
+ case UNICOLOR_BLUE:
+ unicolorbar_reg = 0x110;
+ break;
+ case UNICOLOR_BLACK:
+ unicolorbar_reg = 0x111;
+ break;
+ default:
+ pr_err("%s: invalid colorbar %d\n",
+ __func__, testgen_cfg->color_bar_pattern);
+ break;
+ }
+ }
+
+ msm_camera_io_w((testgen_cfg->rotate_period << 8) |
+ (bayer_pix_pattern_reg << 6) | (unicolor_enb << 4) |
+ (unicolorbar_reg), vfe_dev->vfe_base + 0xB14);
+}
+
+static void msm_vfe46_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, subsample_period, subsample_pattern;
+ struct msm_vfe_camif_subsample_cfg *subsample_cfg =
+ &pix_cfg->camif_cfg.subsample_cfg;
+ uint16_t bus_sub_en = 0;
+
+ if (subsample_cfg->pixel_skip || subsample_cfg->line_skip)
+ bus_sub_en = 1;
+ else
+ bus_sub_en = 0;
+
+ vfe_dev->dual_vfe_enable = camif_cfg->is_split;
+
+ msm_camera_io_w(pix_cfg->input_mux << 5 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x50);
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+ subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
+ subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
+
+ if (bus_sub_en) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3AC);
+ val &= 0xFFFFFFDF;
+ val = val | bus_sub_en << 5;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3AC);
+ subsample_cfg->pixel_skip &= 0x0000FFFF;
+ subsample_cfg->line_skip &= 0x0000FFFF;
+ msm_camera_io_w((subsample_cfg->line_skip << 16) |
+ subsample_cfg->pixel_skip, vfe_dev->vfe_base + 0x3C0);
+ }
+
+ msm_camera_io_w(camif_cfg->lines_per_frame << 16 |
+ camif_cfg->pixels_per_line, vfe_dev->vfe_base + 0x3B4);
+
+ msm_camera_io_w(first_pixel << 16 | last_pixel,
+ vfe_dev->vfe_base + 0x3B8);
+
+ msm_camera_io_w(first_line << 16 | last_line,
+ vfe_dev->vfe_base + 0x3BC);
+
+ if (subsample_period && subsample_pattern) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3AC);
+ val &= 0xFFE0FFFF;
+ val = (subsample_period - 1) << 16;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3AC);
+ ISP_DBG("%s:camif PERIOD %x PATTERN %x\n",
+ __func__, subsample_period, subsample_pattern);
+
+ val = subsample_pattern;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3C8);
+ } else {
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x3C8);
+ }
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x39C);
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x39C);
+}
+
+static void msm_vfe46_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t core_cfg = 0;
+ uint32_t val = 0;
+
+ core_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ core_cfg &= 0xFFFFFF9F;
+
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ core_cfg |= 0x0 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe46_cfg_camif(vfe_dev, pix_cfg);
+ break;
+ case TESTGEN:
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ val |= (1 << 31);
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3C);
+
+ /* CAMIF and TESTGEN will both go thorugh CAMIF*/
+ core_cfg |= 0x1 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe46_cfg_camif(vfe_dev, pix_cfg);
+ msm_vfe46_cfg_testgen(vfe_dev, &pix_cfg->testgen_cfg);
+ break;
+ case EXTERNAL_READ:
+ core_cfg |= 0x2 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe46_cfg_fetch_engine(vfe_dev, pix_cfg);
+ break;
+ default:
+ pr_err("%s: Unsupported input mux %d\n",
+ __func__, pix_cfg->input_mux);
+ break;
+ }
+}
+
+static void msm_vfe46_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;
+
+ if (update_state == ENABLE_CAMIF) {
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
+ msm_vfe46_config_irq(vfe_dev, 0x15, 0x81,
+ MSM_ISP_IRQ_ENABLE);
+
+ 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].stream_count > 0) ? 1 : 0);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3AC);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3AC);
+ msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x3A8);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x3A8);
+ /* configure EPOCH0 for 20 lines */
+ msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x3CC);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
+ /* testgen GO*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1, vfe_dev->vfe_base + 0xAF4);
+ } else if (update_state == DISABLE_CAMIF ||
+ update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ uint32_t poll_val;
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ update_state = DISABLE_CAMIF;
+ msm_vfe46_config_irq(vfe_dev, 0, 0x81,
+ MSM_ISP_IRQ_DISABLE);
+ /* disable danger signal */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18);
+ val &= ~(1 << 8);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0xC18);
+ msm_camera_io_w_mb((update_state == DISABLE_CAMIF ? 0x0 : 0x6),
+ vfe_dev->vfe_base + 0x3A8);
+ if (readl_poll_timeout_atomic(vfe_dev->vfe_base + 0x3D0,
+ poll_val, poll_val & 0x80000000, 1000, 2000000))
+ pr_err("%s: camif disable failed %x\n",
+ __func__, poll_val);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ /* testgen OFF*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0xAF4);
+ }
+}
+
+static void msm_vfe46_cfg_rdi_reg(
+ struct vfe_device *vfe_dev, struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src)
+{
+ uint8_t rdi = input_src - VFE_RAW_0;
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE46_RDI_BASE(rdi));
+ rdi_reg_cfg &= 0x03;
+ rdi_reg_cfg |= (rdi * 3) << 28 | rdi_cfg->cid << 4 | 0x4;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE46_RDI_BASE(rdi));
+}
+
+static void msm_vfe46_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE46_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + wm_base + 0xC);
+ val &= ~0x2;
+ if (stream_info->frame_based)
+ val |= 0x2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
+ if (!stream_info->frame_based) {
+ /* WR_IMAGE_SIZE */
+ val =
+ ((msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width)+3)/4 - 1) << 16 |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ /* WR_BUFFER_CFG */
+ val = VFE46_BURST_LEN |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) <<
+ 2 |
+ ((msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_stride)+1)/2) << 16;
+ 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 */
+}
+
+static void msm_vfe46_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE46_WM_BASE(stream_info->wm[vfe_idx][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);
+}
+
+static void msm_vfe46_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ switch (stream_info->stream_src) {
+ case PIX_VIDEO:
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /* SINGLE_STREAM_SEL */
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ /* PAIR_STREAM_SWAP_CTRL */
+ xbar_cfg |= 0x3 << 4;
+ break;
+ }
+ xbar_cfg |= 0x1 << 2; /* PAIR_STREAM_EN */
+ }
+ if (stream_info->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /* VIEW_STREAM_EN */
+ else if (stream_info->stream_src == PIX_VIDEO)
+ xbar_cfg |= 0x2;
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x300;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x400;
+ break;
+ case RDI_INTF_0:
+ xbar_cfg = 0xC00;
+ break;
+ case RDI_INTF_1:
+ xbar_cfg = 0xD00;
+ break;
+ case RDI_INTF_2:
+ xbar_cfg = 0xE00;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ break;
+ }
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE46_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE46_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE46_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE46_XBAR_BASE(wm));
+}
+
+static void msm_vfe46_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
+ uint32_t xbar_reg_cfg = 0;
+
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE46_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE46_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE46_XBAR_BASE(wm));
+}
+
+static void msm_vfe46_read_wm_ping_pong_addr(
+ struct vfe_device *vfe_dev)
+{
+ msm_camera_io_dump(vfe_dev->vfe_base +
+ (VFE46_WM_BASE(0) & 0xFFFFFFF0), 0x200, 1);
+}
+
+static void msm_vfe46_update_ping_pong_addr(
+ void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size)
+{
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE46_PING_PONG_BASE(wm_idx, pingpong_bit));
+}
+
+static void msm_vfe46_set_halt_restart_mask(struct vfe_device *vfe_dev)
+{
+ msm_vfe46_config_irq(vfe_dev, BIT(31), BIT(8), MSM_ISP_IRQ_SET);
+}
+
+static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev,
+ uint32_t blocking)
+{
+ int rc = 0;
+ enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
+
+ /* Keep only halt and restart mask */
+ msm_vfe46_config_irq(vfe_dev, (1 << 31), (1 << 8),
+ MSM_ISP_IRQ_SET);
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ == OVERFLOW_DETECTED)
+ pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
+ __func__, vfe_dev->pdev->id, blocking);
+
+ if (blocking) {
+ init_completion(&vfe_dev->halt_complete);
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x374);
+ rc = wait_for_completion_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+ if (rc <= 0)
+ pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
+ vfe_dev->pdev->id, rc);
+ } else {
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x374);
+ }
+
+ msm_isp_get_timestamp(&ts, vfe_dev);
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ }
+
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
+
+ return rc;
+}
+
+static void msm_vfe46_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif)
+{
+ msm_vfe46_config_irq(vfe_dev, vfe_dev->recovery_irq0_mask,
+ vfe_dev->recovery_irq1_mask,
+ MSM_ISP_IRQ_ENABLE);
+ msm_camera_io_w_mb(0x20000, vfe_dev->vfe_base + 0x3CC);
+
+ /* Start AXI */
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x374);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, VFE_SRC_MAX);
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+ if (enable_camif)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+}
+
+static uint32_t msm_vfe46_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 8) & 0x7F;
+}
+
+static uint32_t msm_vfe46_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 25) & 0xF;
+}
+
+static uint32_t msm_vfe46_get_pingpong_status(
+ struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x2A8);
+}
+
+static int msm_vfe46_get_stats_idx(enum msm_isp_stats_type stats_type)
+{
+ switch (stats_type) {
+ case MSM_ISP_STATS_HDR_BE:
+ return STATS_IDX_HDR_BE;
+ case MSM_ISP_STATS_BG:
+ return STATS_IDX_BG;
+ case MSM_ISP_STATS_BF:
+ return STATS_IDX_BF;
+ case MSM_ISP_STATS_HDR_BHIST:
+ return STATS_IDX_HDR_BHIST;
+ case MSM_ISP_STATS_RS:
+ return STATS_IDX_RS;
+ case MSM_ISP_STATS_CS:
+ return STATS_IDX_CS;
+ case MSM_ISP_STATS_IHIST:
+ return STATS_IDX_IHIST;
+ case MSM_ISP_STATS_BHIST:
+ return STATS_IDX_BHIST;
+ case MSM_ISP_STATS_BF_SCALE:
+ return STATS_IDX_BF_SCALE;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
+}
+
+static int msm_vfe46_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ if (stream_info[STATS_IDX_BF].state ==
+ STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state !=
+ STATS_AVAILABLE) {
+ pr_err("%s: does not support BF_SCALE while BF is disabled\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (stream_info[STATS_IDX_BF].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF].composite_flag !=
+ stream_info[STATS_IDX_BF_SCALE].composite_flag) {
+ pr_err("%s: Different composite flag for BF and BF_SCALE\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_vfe46_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t request_comp_index, uint8_t enable)
+{
+ uint32_t comp_mask_reg, mask_bf_scale;
+ atomic_t *stats_comp_mask;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask < 1)
+ return;
+
+ if (request_comp_index >= MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__, request_comp_index,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask >
+ MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ /* BF scale is controlled by BF also so ignore bit 0 of BF scale */
+ stats_mask = stats_mask & 0x1FF;
+ mask_bf_scale = stats_mask >> SHIFT_BF_SCALE_BIT;
+
+ stats_comp_mask = &stats_data->stats_comp_mask[request_comp_index];
+ comp_mask_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x78);
+
+ if (enable) {
+ comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8);
+ atomic_set(stats_comp_mask, stats_mask |
+ atomic_read(stats_comp_mask));
+ msm_vfe46_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_ENABLE);
+ } else {
+ if (!(atomic_read(stats_comp_mask) & stats_mask))
+ return;
+ if (stats_mask & (1 << STATS_IDX_BF_SCALE) &&
+ atomic_read(stats_comp_mask) &
+ (1 << STATS_IDX_BF_SCALE))
+ atomic_set(stats_comp_mask,
+ ~(1 << STATS_IDX_BF_SCALE) &
+ atomic_read(stats_comp_mask));
+
+ atomic_set(stats_comp_mask,
+ ~stats_mask & atomic_read(stats_comp_mask));
+ comp_mask_reg &= ~(mask_bf_scale <<
+ (16 + request_comp_index * 8));
+ msm_vfe46_config_irq(vfe_dev,
+ 1 << (request_comp_index + 29), 0,
+ MSM_ISP_IRQ_DISABLE);
+ }
+ msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78);
+
+ ISP_DBG("%s: comp_mask_reg: %x comp mask0 %x mask1: %x\n",
+ __func__, comp_mask_reg,
+ atomic_read(&stats_data->stats_comp_mask[0]),
+ atomic_read(&stats_data->stats_comp_mask[1]));
+
+}
+
+static void msm_vfe46_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe46_config_irq(vfe_dev,
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+static void msm_vfe46_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ msm_vfe46_config_irq(vfe_dev,
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+static void msm_vfe46_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE46_STATS_BASE(stats_idx);
+
+ /*
+ * BF_SCALE does not have its own WR_ADDR_CFG,
+ * IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
+ * it's using the same from BF.
+ */
+ if (stats_idx == STATS_IDX_BF_SCALE)
+ return;
+
+ /* WR_ADDR_CFG */
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
+ vfe_dev->vfe_base + stats_base + 0x8);
+ /* WR_IRQ_FRAMEDROP_PATTERN */
+ msm_camera_io_w(stream_info->framedrop_pattern,
+ vfe_dev->vfe_base + stats_base + 0x10);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe46_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t val = 0;
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE46_STATS_BASE(stats_idx);
+ /*
+ * BF_SCALE does not have its own WR_ADDR_CFG,
+ * IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
+ * it's using the same from BF.
+ */
+ if (stats_idx == STATS_IDX_BF_SCALE)
+ return;
+
+ /* WR_ADDR_CFG */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x8);
+ /* WR_IRQ_FRAMEDROP_PATTERN */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x14);
+}
+
+static void msm_vfe46_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ uint32_t ub_size[VFE46_NUM_STATS_TYPE] = {
+ 16, /* MSM_ISP_STATS_BF_SCALE */
+ 16, /* MSM_ISP_STATS_HDR_BE */
+ 16, /* MSM_ISP_STATS_BG */
+ 16, /* MSM_ISP_STATS_BF */
+ 16, /* MSM_ISP_STATS_HDR_BHIST */
+ 16, /* MSM_ISP_STATS_RS */
+ 16, /* MSM_ISP_STATS_CS */
+ 16, /* MSM_ISP_STATS_IHIST */
+ 16, /* MSM_ISP_STATS_BHIST */
+ };
+ if (vfe_dev->pdev->id == ISP_VFE1)
+ ub_offset = VFE46_UB_SIZE_VFE1;
+ else if (vfe_dev->pdev->id == ISP_VFE0)
+ ub_offset = VFE46_UB_SIZE_VFE0;
+ else
+ pr_err("%s: incorrect VFE device\n", __func__);
+
+ for (i = 0; i < VFE46_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(VFE46_STATS_BURST_LEN << 30 |
+ ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE46_STATS_BASE(i) +
+ ((i == STATS_IDX_BF_SCALE) ? 0x8 : 0xC));
+ }
+}
+
+static void msm_vfe46_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, cgc_mask = 0;
+
+ for (i = 0; i < VFE46_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_IDX_HDR_BE:
+ cgc_mask |= 1;
+ break;
+ case STATS_IDX_HDR_BHIST:
+ cgc_mask |= (1 << 1);
+ break;
+ case STATS_IDX_BF:
+ cgc_mask |= (1 << 2);
+ break;
+ case STATS_IDX_BG:
+ cgc_mask |= (1 << 3);
+ break;
+ case STATS_IDX_BHIST:
+ cgc_mask |= (1 << 4);
+ break;
+ case STATS_IDX_RS:
+ cgc_mask |= (1 << 5);
+ break;
+ case STATS_IDX_CS:
+ cgc_mask |= (1 << 6);
+ break;
+ case STATS_IDX_IHIST:
+ cgc_mask |= (1 << 7);
+ break;
+ case STATS_IDX_BF_SCALE:
+ cgc_mask |= (1 << 2);
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /* CGC override */
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
+ if (enable)
+ module_cfg |= cgc_mask;
+ else
+ module_cfg &= ~cgc_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x30);
+}
+
+static bool msm_vfe46_is_module_cfg_lock_needed(
+ uint32_t reg_offset)
+{
+ return false;
+}
+
+static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+ uint32_t stats_cfg, stats_cfg_mask = 0;
+
+ for (i = 0; i < VFE46_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_IDX_HDR_BE:
+ module_cfg_mask |= 1;
+ break;
+ case STATS_IDX_HDR_BHIST:
+ module_cfg_mask |= 1 << 1;
+ break;
+ case STATS_IDX_BF:
+ module_cfg_mask |= 1 << 2;
+ break;
+ case STATS_IDX_BG:
+ module_cfg_mask |= 1 << 3;
+ break;
+ case STATS_IDX_BHIST:
+ module_cfg_mask |= 1 << 4;
+ break;
+ case STATS_IDX_RS:
+ module_cfg_mask |= 1 << 5;
+ break;
+ case STATS_IDX_CS:
+ module_cfg_mask |= 1 << 6;
+ break;
+ case STATS_IDX_IHIST:
+ module_cfg_mask |= 1 << 7;
+ break;
+ case STATS_IDX_BF_SCALE:
+ stats_cfg_mask |= 1 << 5;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x44);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x44);
+
+ stats_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x9B8);
+ if (enable)
+ stats_cfg |= stats_cfg_mask;
+ else
+ stats_cfg &= ~stats_cfg_mask;
+ msm_camera_io_w(stats_cfg, vfe_dev->vfe_base + 0x9B8);
+}
+
+static void msm_vfe46_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE46_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+}
+
+static uint32_t msm_vfe46_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 15) & 0x1FF;
+}
+
+static uint32_t msm_vfe46_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 29) & 0x3;
+}
+
+static uint32_t msm_vfe46_stats_get_frame_id(
+ struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+
+static void msm_vfe46_get_error_mask(
+ uint32_t *error_mask0, uint32_t *error_mask1)
+{
+ *error_mask0 = 0x00000000;
+ *error_mask1 = 0x01FFFEFF;
+}
+
+static void msm_vfe46_get_overflow_mask(uint32_t *overflow_mask)
+{
+ *overflow_mask = 0x01FFFE7E;
+}
+
+static void msm_vfe46_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask)
+{
+ *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
+}
+
+static void msm_vfe46_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask)
+{
+ *irq0_mask = vfe_dev->irq0_mask;
+ *irq1_mask = vfe_dev->irq1_mask;
+}
+
+static void msm_vfe46_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask)
+{
+ *irq0_mask = BIT(31);
+ *irq1_mask = BIT(8);
+}
+static struct msm_vfe_axi_hardware_info msm_vfe46_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 96,
+ .scratch_buf_range = SZ_32M + SZ_4M,
+};
+
+static struct msm_vfe_stats_hardware_info msm_vfe46_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_HDR_BE | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST |
+ 1 << MSM_ISP_STATS_HDR_BHIST | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_BF_SCALE,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .num_stats_type = VFE46_NUM_STATS_TYPE,
+ .num_stats_comp_mask = VFE46_NUM_STATS_COMP,
+};
+
+struct msm_vfe_hardware_info vfe46_hw_info = {
+ .num_iommu_ctx = 1,
+ .num_iommu_secure_ctx = 1,
+ .vfe_clk_idx = VFE46_CLK_IDX,
+ .runtime_axi_update = 0,
+ .min_ab = 100000000,
+ .min_ib = 100000000,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe46_read_and_clear_irq_status,
+ .read_irq_status = msm_vfe46_read_irq_status,
+ .process_camif_irq = msm_vfe46_process_input_irq,
+ .process_reset_irq = msm_vfe46_process_reset_irq,
+ .process_halt_irq = msm_vfe46_process_halt_irq,
+ .process_reg_update = msm_vfe46_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ .process_epoch_irq = msm_vfe46_process_epoch_irq,
+ .config_irq = msm_vfe46_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe46_axi_reload_wm,
+ .enable_wm = msm_vfe46_axi_enable_wm,
+ .cfg_io_format = msm_vfe46_cfg_io_format,
+ .cfg_comp_mask = msm_vfe46_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe46_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe46_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe46_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe46_cfg_framedrop,
+ .clear_framedrop = msm_vfe46_clear_framedrop,
+ .cfg_wm_reg = msm_vfe46_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe46_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe46_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe46_axi_clear_wm_xbar_reg,
+ .cfg_ub = msm_vfe47_cfg_axi_ub,
+ .read_wm_ping_pong_addr =
+ msm_vfe46_read_wm_ping_pong_addr,
+ .update_ping_pong_addr =
+ msm_vfe46_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe46_get_comp_mask,
+ .get_wm_mask = msm_vfe46_get_wm_mask,
+ .get_pingpong_status = msm_vfe46_get_pingpong_status,
+ .halt = msm_vfe46_axi_halt,
+ .restart = msm_vfe46_axi_restart,
+ .update_cgc_override =
+ msm_vfe46_axi_update_cgc_override,
+ .ub_reg_offset = msm_vfe46_ub_reg_offset,
+ .get_ub_size = msm_vfe46_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe46_reg_update,
+ .cfg_input_mux = msm_vfe46_cfg_input_mux,
+ .update_camif_state = msm_vfe46_update_camif_state,
+ .start_fetch_eng = msm_vfe46_start_fetch_engine,
+ .cfg_rdi_reg = msm_vfe46_cfg_rdi_reg,
+ .reset_hw = msm_vfe46_reset_hardware,
+ .init_hw = msm_vfe47_init_hardware,
+ .init_hw_reg = msm_vfe46_init_hardware_reg,
+ .clear_status_reg = msm_vfe46_clear_status_reg,
+ .release_hw = msm_vfe47_release_hardware,
+ .get_error_mask = msm_vfe46_get_error_mask,
+ .get_overflow_mask = msm_vfe46_get_overflow_mask,
+ .get_rdi_wm_mask = msm_vfe46_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe46_get_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe46_get_halt_restart_mask,
+ .process_error_status = msm_vfe46_process_error_status,
+ .is_module_cfg_lock_needed =
+ msm_vfe46_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = NULL,
+ .set_halt_restart_mask =
+ msm_vfe46_set_halt_restart_mask,
+ .set_bus_err_ign_mask = NULL,
+ .get_bus_err_mask = NULL,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe46_get_stats_idx,
+ .check_streams = msm_vfe46_stats_check_streams,
+ .cfg_comp_mask = msm_vfe46_stats_cfg_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe46_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe46_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe46_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe46_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe46_stats_cfg_ub,
+ .enable_module = msm_vfe46_stats_enable_module,
+ .update_ping_pong_addr =
+ msm_vfe46_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe46_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe46_stats_get_wm_mask,
+ .get_frame_id = msm_vfe46_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe46_get_pingpong_status,
+ .update_cgc_override =
+ msm_vfe46_stats_update_cgc_override,
+ .enable_stats_wm = NULL,
+ },
+ .platform_ops = {
+ .get_platform_data = msm_vfe47_get_platform_data,
+ .enable_regulators = msm_vfe47_enable_regulators,
+ .get_regulators = msm_vfe47_get_regulators,
+ .put_regulators = msm_vfe47_put_regulators,
+ .enable_clks = msm_vfe47_enable_clks,
+ .get_clks = msm_vfe47_get_clks,
+ .put_clks = msm_vfe47_put_clks,
+ .get_clk_rates = msm_vfe47_get_clk_rates,
+ .get_max_clk_rate = msm_vfe47_get_max_clk_rate,
+ .set_clk_rate = msm_vfe47_set_clk_rate,
+ .init_bw_mgr = msm_vfe47_init_bandwidth_mgr,
+ .deinit_bw_mgr = msm_vfe47_deinit_bandwidth_mgr,
+ .update_bw = msm_vfe47_update_bandwidth,
+ }
+ },
+ .dmi_reg_offset = 0xACC,
+ .axi_hw_info = &msm_vfe46_axi_hw_info,
+ .stats_hw_info = &msm_vfe46_stats_hw_info,
+ .regulator_names = {"vdd"},
+};
+EXPORT_SYMBOL(vfe46_hw_info);
+
+static const struct of_device_id msm_vfe46_dt_match[] = {
+ {
+ .compatible = "qcom,vfe46",
+ .data = &vfe46_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe46_dt_match);
+
+static struct platform_driver vfe46_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe46",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe46_dt_match,
+ },
+};
+
+static int __init msm_vfe46_init_module(void)
+{
+ return platform_driver_register(&vfe46_driver);
+}
+
+static void __exit msm_vfe46_exit_module(void)
+{
+ platform_driver_unregister(&vfe46_driver);
+}
+
+module_init(msm_vfe46_init_module);
+module_exit(msm_vfe46_exit_module);
+MODULE_DESCRIPTION("MSM VFE46 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.h
new file mode 100644
index 0000000..68444a0
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2013-2014, 2018, 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_ISP46_H__
+#define __MSM_ISP46_H__
+
+extern struct msm_vfe_hardware_info vfe46_hw_info;
+#endif /* __MSM_ISP46_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
new file mode 100644
index 0000000..e03f76f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -0,0 +1,3130 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+
+#include "msm.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_isp.h"
+#include "msm_camera_io_util.h"
+#include "cam_hw_ops.h"
+#include "msm_isp47.h"
+#include "cam_soc_api.h"
+#include "msm_isp48.h"
+#include "linux/iopoll.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define VFE47_8996V1_VERSION 0x70000000
+
+#define VFE47_BURST_LEN 3
+#define VFE47_FETCH_BURST_LEN 3
+#define VFE47_STATS_BURST_LEN 3
+#define VFE47_UB_SIZE_VFE0 2048
+#define VFE47_UB_SIZE_VFE1 1536
+#define VFE47_UB_STATS_SIZE 144
+#define MSM_ISP47_TOTAL_IMAGE_UB_VFE0 (VFE47_UB_SIZE_VFE0 - VFE47_UB_STATS_SIZE)
+#define MSM_ISP47_TOTAL_IMAGE_UB_VFE1 (VFE47_UB_SIZE_VFE1 - VFE47_UB_STATS_SIZE)
+#define VFE47_WM_BASE(idx) (0xA0 + 0x2C * idx)
+#define VFE47_RDI_BASE(idx) (0x46C + 0x4 * idx)
+#define VFE47_XBAR_BASE(idx) (0x90 + 0x4 * (idx / 2))
+#define VFE47_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0)
+/*add ping MAX and Pong MAX*/
+#define VFE47_PING_PONG_BASE(wm, ping_pong) \
+ (VFE47_WM_BASE(wm) + 0x4 * (1 + (((~ping_pong) & 0x1) * 2)))
+#define SHIFT_BF_SCALE_BIT 1
+
+#define VFE47_BUS_RD_CGC_OVERRIDE_BIT 16
+
+#define VFE47_VBIF_CLK_OFFSET 0x4
+
+static uint32_t stats_base_addr[] = {
+ 0x1D4, /* HDR_BE */
+ 0x254, /* BG(AWB_BG) */
+ 0x214, /* BF */
+ 0x1F4, /* HDR_BHIST */
+ 0x294, /* RS */
+ 0x2B4, /* CS */
+ 0x2D4, /* IHIST */
+ 0x274, /* BHIST (SKIN_BHIST) */
+ 0x234, /* AEC_BG */
+};
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 8, /* HDR_BE */
+ 12, /* BG(AWB_BG) */
+ 10, /* BF */
+ 9, /* HDR_BHIST */
+ 14, /* RS */
+ 15, /* CS */
+ 16, /* IHIST */
+ 13, /* BHIST (SKIN_BHIST) */
+ 11, /* AEC_BG */
+};
+
+static uint8_t stats_irq_map_comp_mask[] = {
+ 16, /* HDR_BE */
+ 17, /* BG(AWB_BG) */
+ 18, /* BF EARLY DONE/ BF */
+ 19, /* HDR_BHIST */
+ 20, /* RS */
+ 21, /* CS */
+ 22, /* IHIST */
+ 23, /* BHIST (SKIN_BHIST) */
+ 15, /* AEC_BG */
+};
+
+#define VFE47_STATS_BASE(idx) (stats_base_addr[idx])
+#define VFE47_STATS_PING_PONG_BASE(idx, ping_pong) \
+ (VFE47_STATS_BASE(idx) + 0x4 * \
+ (~(ping_pong >> (stats_pingpong_offset_map[idx])) & 0x1) * 2)
+
+#define VFE47_SRC_CLK_DTSI_IDX 5
+
+static struct msm_bus_vectors msm_isp_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+/* During open node request min ab/ib bus bandwidth which
+ * is needed to successfully enable bus clocks
+ */
+static struct msm_bus_vectors msm_isp_ping_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = MSM_ISP_MIN_AB,
+ .ib = MSM_ISP_MIN_IB,
+ },
+};
+
+static struct msm_bus_vectors msm_isp_pong_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_paths msm_isp_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_isp_init_vectors),
+ msm_isp_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_isp_ping_vectors),
+ msm_isp_ping_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_isp_pong_vectors),
+ msm_isp_pong_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_isp_bus_client_pdata = {
+ msm_isp_bus_client_config,
+ NULL,
+ ARRAY_SIZE(msm_isp_bus_client_config),
+ .name = "msm_camera_isp",
+ 0
+};
+
+uint32_t msm_vfe47_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx)
+{
+ return (VFE47_WM_BASE(wm_idx) + 0x18);
+}
+
+uint32_t msm_vfe47_get_ub_size(struct vfe_device *vfe_dev)
+{
+ if (vfe_dev->pdev->id == ISP_VFE0)
+ return MSM_ISP47_TOTAL_IMAGE_UB_VFE0;
+ return MSM_ISP47_TOTAL_IMAGE_UB_VFE1;
+}
+
+void msm_vfe47_config_irq(struct vfe_device *vfe_dev,
+ uint32_t irq0_mask, uint32_t irq1_mask,
+ enum msm_isp_irq_operation oper)
+{
+ switch (oper) {
+ case MSM_ISP_IRQ_ENABLE:
+ vfe_dev->irq0_mask |= irq0_mask;
+ vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+ break;
+ case MSM_ISP_IRQ_DISABLE:
+ vfe_dev->irq0_mask &= ~irq0_mask;
+ vfe_dev->irq1_mask &= ~irq1_mask;
+ break;
+ case MSM_ISP_IRQ_SET:
+ vfe_dev->irq0_mask = irq0_mask;
+ vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+ break;
+ }
+ msm_camera_io_w_mb(vfe_dev->irq0_mask,
+ vfe_dev->vfe_base + 0x5C);
+ msm_camera_io_w_mb(vfe_dev->irq1_mask,
+ vfe_dev->vfe_base + 0x60);
+}
+
+static int32_t msm_vfe47_init_dt_parms(struct vfe_device *vfe_dev,
+ struct msm_vfe_hw_init_parms *dt_parms, void __iomem *dev_mem_base)
+{
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *dt_settings = NULL, *dt_regs = NULL, num_dt_entries = 0;
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, dt_parms->entries,
+ &num_dt_entries);
+ if (rc < 0 || !num_dt_entries) {
+ pr_err("%s: NO QOS entries found\n", __func__);
+ return -EINVAL;
+ }
+ dt_settings = kcalloc(num_dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_settings)
+ return -ENOMEM;
+ dt_regs = kcalloc(num_dt_entries, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_regs) {
+ kfree(dt_settings);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node, dt_parms->regs,
+ dt_regs, num_dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS BUS BDG info\n", __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ return -EINVAL;
+ }
+ if (dt_parms->settings) {
+ rc = of_property_read_u32_array(of_node,
+ dt_parms->settings,
+ dt_settings, num_dt_entries);
+ if (rc < 0) {
+ pr_err("%s: NO QOS settings\n",
+ __func__);
+ kfree(dt_settings);
+ kfree(dt_regs);
+ } else {
+ for (i = 0; i < num_dt_entries; i++) {
+ msm_camera_io_w(dt_settings[i],
+ dev_mem_base +
+ dt_regs[i]);
+ }
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ } else {
+ kfree(dt_settings);
+ kfree(dt_regs);
+ }
+ return 0;
+}
+
+static enum cam_ahb_clk_vote msm_isp47_get_cam_clk_vote(
+ enum msm_vfe_ahb_clk_vote vote)
+{
+ switch (vote) {
+ case MSM_ISP_CAMERA_AHB_SVS_VOTE:
+ return CAM_AHB_SVS_VOTE;
+ case MSM_ISP_CAMERA_AHB_TURBO_VOTE:
+ return CAM_AHB_TURBO_VOTE;
+ case MSM_ISP_CAMERA_AHB_NOMINAL_VOTE:
+ return CAM_AHB_NOMINAL_VOTE;
+ case MSM_ISP_CAMERA_AHB_SUSPEND_VOTE:
+ return CAM_AHB_SUSPEND_VOTE;
+ }
+ return 0;
+}
+
+int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
+ struct msm_isp_ahb_clk_cfg *ahb_cfg)
+{
+ int rc = 0;
+ enum cam_ahb_clk_vote vote;
+ enum cam_ahb_clk_vote src_clk_vote;
+ struct msm_isp_clk_rates clk_rates;
+
+ if (ahb_cfg) {
+ vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote);
+ vfe_dev->user_requested_ahb_vote = vote;
+ } else {
+ vote = vfe_dev->user_requested_ahb_vote;
+ }
+
+ vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev,
+ &clk_rates);
+ if (vfe_dev->vfe_clk_info[vfe_dev->hw_info->vfe_clk_idx].clk_rate <=
+ clk_rates.svs_rate)
+ src_clk_vote = CAM_AHB_SVS_VOTE;
+ else if (vfe_dev->vfe_clk_info[vfe_dev->hw_info->vfe_clk_idx].clk_rate
+ <= clk_rates.nominal_rate)
+ src_clk_vote = CAM_AHB_NOMINAL_VOTE;
+ else
+ src_clk_vote = CAM_AHB_TURBO_VOTE;
+
+ /* vote for higher of the user requested or src clock matched vote */
+ if (vote < src_clk_vote)
+ vote = src_clk_vote;
+
+ if (vote && vfe_dev->ahb_vote != vote) {
+ rc = cam_config_ahb_clk(NULL, 0,
+ (vfe_dev->pdev->id == ISP_VFE0 ?
+ CAM_AHB_CLIENT_VFE0 : CAM_AHB_CLIENT_VFE1), vote);
+ if (rc)
+ pr_err("%s: failed to set ahb vote to %x\n",
+ __func__, vote);
+ else
+ vfe_dev->ahb_vote = vote;
+ }
+ return rc;
+}
+
+int msm_vfe47_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+ enum cam_ahb_clk_client id;
+
+ if (vfe_dev->pdev->id == 0)
+ id = CAM_AHB_CLIENT_VFE0;
+ else
+ id = CAM_AHB_CLIENT_VFE1;
+
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(
+ vfe_dev, 1);
+ if (rc)
+ goto enable_regulators_failed;
+
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(
+ vfe_dev, 1);
+ if (rc)
+ goto clk_enable_failed;
+
+ vfe_dev->user_requested_ahb_vote = CAM_AHB_SVS_VOTE;
+ rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ goto ahb_vote_fail;
+ }
+ vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE;
+
+ vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] =
+ vfe_dev->vfe_base;
+
+ rc = msm_camera_enable_irq(vfe_dev->vfe_irq, 1);
+ if (rc < 0)
+ goto irq_enable_fail;
+
+ return rc;
+irq_enable_fail:
+ vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL;
+ if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE;
+ahb_vote_fail:
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 0);
+clk_enable_failed:
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0);
+enable_regulators_failed:
+ return rc;
+}
+
+void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
+{
+ enum cam_ahb_clk_client id;
+ unsigned long rate = 0;
+
+ /* when closing node, disable all irq */
+ vfe_dev->irq0_mask = 0;
+ vfe_dev->irq1_mask = 0;
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ vfe_dev->irq0_mask, vfe_dev->irq1_mask,
+ MSM_ISP_IRQ_SET);
+ msm_camera_enable_irq(vfe_dev->vfe_irq, 0);
+ tasklet_kill(&(vfe_dev->common_data->tasklets[vfe_dev->pdev->id].
+ tasklet));
+ msm_isp_flush_tasklet(vfe_dev);
+
+ vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL;
+
+ msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, 0, 0);
+
+ if (vfe_dev->pdev->id == 0)
+ id = CAM_AHB_CLIENT_VFE0;
+ else
+ id = CAM_AHB_CLIENT_VFE1;
+
+ vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate);
+
+ if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to vote for AHB\n", __func__);
+
+ vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE;
+
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(
+ vfe_dev, 0);
+ msm_vfe47_configure_hvx(vfe_dev, 0);
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0);
+}
+
+void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_hw_init_parms qos_parms;
+ struct msm_vfe_hw_init_parms vbif_parms;
+ struct msm_vfe_hw_init_parms ds_parms;
+
+ memset(&qos_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+ memset(&vbif_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+ memset(&ds_parms, 0, sizeof(struct msm_vfe_hw_init_parms));
+
+ qos_parms.entries = "qos-entries";
+ qos_parms.regs = "qos-regs";
+ qos_parms.settings = "qos-settings";
+ vbif_parms.entries = "vbif-entries";
+ vbif_parms.regs = "vbif-regs";
+ vbif_parms.settings = "vbif-settings";
+ ds_parms.entries = "ds-entries";
+ ds_parms.regs = "ds-regs";
+ ds_parms.settings = "ds-settings";
+
+ msm_vfe47_init_dt_parms(vfe_dev, &qos_parms, vfe_dev->vfe_base);
+ msm_vfe47_init_dt_parms(vfe_dev, &ds_parms, vfe_dev->vfe_base);
+ msm_vfe47_init_dt_parms(vfe_dev, &vbif_parms, vfe_dev->vfe_vbif_base);
+
+
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84);
+ /* IRQ_MASK/CLEAR */
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE);
+}
+
+void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev)
+{
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x80000000, 0x0, MSM_ISP_IRQ_SET);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
+}
+
+void msm_vfe47_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ unsigned long flags;
+
+ if (irq_status0 & (1 << 31)) {
+ spin_lock_irqsave(&vfe_dev->completion_lock, flags);
+ complete(&vfe_dev->reset_complete);
+ vfe_dev->reset_pending = 0;
+ spin_unlock_irqrestore(&vfe_dev->completion_lock, flags);
+ }
+}
+
+void msm_vfe47_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t val = 0;
+
+ if (irq_status1 & (1 << 8)) {
+ complete(&vfe_dev->halt_complete);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x400);
+ }
+
+ val = msm_camera_io_r(vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
+ val &= ~(0x1);
+ msm_camera_io_w(val, vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
+}
+
+void msm_vfe47_process_input_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x1000003))
+ return;
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF IRQ\n", __func__);
+ msm_isp_increment_frame_id(vfe_dev, VFE_PIX_0, ts);
+ }
+
+ if (irq_status0 & (1 << 24)) {
+ ISP_DBG("%s: Fetch Engine Read IRQ\n", __func__);
+ msm_isp_fetch_engine_done_notify(vfe_dev,
+ &vfe_dev->fetch_engine_info);
+ }
+
+
+ if (irq_status0 & (1 << 1))
+ ISP_DBG("%s: EOF IRQ\n", __func__);
+}
+
+void msm_vfe47_process_violation_status(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status = vfe_dev->error_info.violation_status;
+
+ if (violation_status > 39) {
+ pr_err("%s: invalid violation status %d\n",
+ __func__, violation_status);
+ return;
+ }
+
+ pr_err_ratelimited("%s: VFE pipeline violation status %d\n", __func__,
+ violation_status);
+}
+
+void msm_vfe47_process_error_status(struct vfe_device *vfe_dev)
+{
+ uint32_t error_status1 = vfe_dev->error_info.error_mask1;
+
+ if (error_status1 & (1 << 0)) {
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, vfe_dev->error_info.camif_status);
+ /* dump camif registers on camif error */
+ msm_camera_io_dump(vfe_dev->vfe_base + 0x478, 0x3C, 1);
+ /* testgen */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_dump(vfe_dev->vfe_base + 0xC58, 0x28, 1);
+ }
+ if (error_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (error_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (error_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (error_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (error_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (error_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (error_status1 & (1 << 7))
+ msm_vfe47_process_violation_status(vfe_dev);
+ if (error_status1 & (1 << 9))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (error_status1 & (1 << 10))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (error_status1 & (1 << 11))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (error_status1 & (1 << 12))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (error_status1 & (1 << 13))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (error_status1 & (1 << 14))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (error_status1 & (1 << 15))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (error_status1 & (1 << 16))
+ pr_err("%s: status hdr be bus overflow\n", __func__);
+ if (error_status1 & (1 << 17))
+ pr_err("%s: status bg bus overflow\n", __func__);
+ if (error_status1 & (1 << 18))
+ pr_err("%s: status bf bus overflow\n", __func__);
+ if (error_status1 & (1 << 19))
+ pr_err("%s: status hdr bhist bus overflow\n", __func__);
+ if (error_status1 & (1 << 20))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (error_status1 & (1 << 21))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (error_status1 & (1 << 22))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (error_status1 & (1 << 23))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ if (error_status1 & (1 << 24))
+ pr_err("%s: status aec bg bus overflow\n", __func__);
+ if (error_status1 & (1 << 25))
+ pr_err("%s: status dsp error\n", __func__);
+}
+
+void msm_vfe47_read_and_clear_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 + 0x6C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70);
+ /* Mask off bits that are not enabled */
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58);
+ *irq_status0 &= vfe_dev->irq0_mask;
+ *irq_status1 &= vfe_dev->irq1_mask;
+
+ if (*irq_status1 & (1 << 0)) {
+ vfe_dev->error_info.camif_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x4A4);
+ /* mask off camif error after first occurrance */
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0,
+ (1 << 0), MSM_ISP_IRQ_DISABLE);
+ }
+
+ if (*irq_status1 & (1 << 7))
+ vfe_dev->error_info.violation_status =
+ msm_camera_io_r(vfe_dev->vfe_base + 0x7C);
+
+}
+
+void msm_vfe47_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 + 0x6C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70);
+}
+
+void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ enum msm_vfe_input_src i;
+ uint32_t shift_irq;
+ uint8_t reg_updated = 0;
+ unsigned long flags;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+ /* Shift status bits so that PIX SOF is 1st bit */
+ shift_irq = ((irq_status0 & 0xF0) >> 4);
+
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ if (shift_irq & BIT(i)) {
+ reg_updated |= BIT(i);
+ ISP_DBG("%s REG_UPDATE IRQ %x vfe %d\n", __func__,
+ (uint32_t)BIT(i), vfe_dev->pdev->id);
+ switch (i) {
+ case VFE_PIX_0:
+ msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
+ VFE_PIX_0, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /*
+ * if only camif raw streams active then force
+ * reg update
+ */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count > 0 &&
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, i);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ msm_isp_increment_frame_id(vfe_dev, i, ts);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /*
+ * Reg Update is pseudo SOF for RDI,
+ * so request every frame
+ */
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, i);
+ /* reg upd is also epoch for RDI */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ break;
+ default:
+ pr_err("%s: Error case\n", __func__);
+ return;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ if (reg_updated & BIT(VFE_PIX_0))
+ vfe_dev->reg_updated = 1;
+
+ vfe_dev->reg_update_requested &= ~reg_updated;
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0xc))
+ return;
+
+ if (irq_status0 & BIT(2)) {
+ ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
+ msm_isp_update_error_frame_count(vfe_dev);
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0)
+{
+ if (irq_status0 & BIT(3))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
+ if (irq_status0 & BIT(0))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true;
+}
+
+void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ uint32_t update_mask = 0;
+ unsigned long flags;
+
+ /* This HW supports upto VFE_RAW_2 */
+ if (frame_src > VFE_RAW_2 && frame_src != VFE_SRC_MAX) {
+ pr_err("%s Error case\n", __func__);
+ return;
+ }
+
+ /*
+ * If frame_src == VFE_SRC_MAX request reg_update on all
+ * supported INTF
+ */
+ if (frame_src == VFE_SRC_MAX)
+ update_mask = 0xF;
+ else
+ update_mask = BIT((uint32_t)frame_src);
+ ISP_DBG("%s update_mask %x\n", __func__, update_mask);
+
+ spin_lock_irqsave(&vfe_dev->reg_update_lock, flags);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].reg_update_frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ vfe_dev->reg_update_requested |= update_mask;
+ vfe_dev->common_data->dual_vfe_res->reg_update_mask[vfe_dev->pdev->id] =
+ vfe_dev->reg_update_requested;
+ if ((vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) &&
+ ((frame_src == VFE_PIX_0) || (frame_src == VFE_SRC_MAX))) {
+ if (!vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]) {
+ pr_err("%s vfe_base for ISP_VFE0 is NULL\n", __func__);
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock,
+ flags);
+ return;
+ }
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
+ + 0x4AC);
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x4AC);
+ } else if (!vfe_dev->is_split ||
+ ((frame_src == VFE_PIX_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
+ (frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
+ msm_camera_io_w_mb(update_mask,
+ vfe_dev->vfe_base + 0x4AC);
+ }
+ spin_unlock_irqrestore(&vfe_dev->reg_update_lock, flags);
+}
+
+long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking_call)
+{
+ long rc = 0;
+ uint32_t reset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe_dev->completion_lock, flags);
+ init_completion(&vfe_dev->reset_complete);
+ spin_unlock_irqrestore(&vfe_dev->completion_lock, flags);
+
+ if (blocking_call)
+ vfe_dev->reset_pending = 1;
+
+ if (first_start) {
+ if (msm_vfe_is_vfe48(vfe_dev))
+ reset = 0x3F7;
+ else
+ reset = 0x3FF;
+ msm_camera_io_w_mb(reset, vfe_dev->vfe_base + 0x18);
+ } else {
+ if (msm_vfe_is_vfe48(vfe_dev))
+ reset = 0x3E7;
+ else
+ reset = 0x3EF;
+ msm_camera_io_w_mb(reset, vfe_dev->vfe_base + 0x18);
+ msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, vfe_dev->vfe_base, 0x0011FFFF);
+ }
+
+ if (blocking_call) {
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(100));
+ if (rc <= 0) {
+ pr_err("%s:%d failed: reset timeout\n", __func__,
+ __LINE__);
+ vfe_dev->reset_pending = 0;
+ }
+ }
+
+ return rc;
+}
+
+void msm_vfe47_axi_reload_wm(struct vfe_device *vfe_dev,
+ void __iomem *vfe_base, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_base + 0x80);
+}
+
+void msm_vfe47_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ if (enable)
+ val |= (1 << wm_idx);
+ else
+ val &= ~(1 << wm_idx);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x3C);
+}
+
+static void msm_vfe47_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ val = msm_camera_io_r(vfe_base + VFE47_WM_BASE(wm_idx));
+ if (enable)
+ val |= 0x1;
+ else
+ val &= ~0x1;
+ msm_camera_io_w_mb(val,
+ vfe_base + VFE47_WM_BASE(wm_idx));
+}
+
+void msm_vfe47_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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+ int i;
+ uint32_t overflow_mask = 0;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
+ 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 + 0x74);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ overflow_mask |= (1 << (stream_info->wm[vfe_idx][i] + 9));
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (comp_mask_index + 25), overflow_mask,
+ MSM_ISP_IRQ_ENABLE);
+}
+
+void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << (comp_mask_index + 25)), 0,
+ MSM_ISP_IRQ_DISABLE);
+}
+
+void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (stream_info->wm[vfe_idx][0] + 8),
+ 1 << (stream_info->wm[vfe_idx][0] + 9),
+ MSM_ISP_IRQ_ENABLE);
+}
+
+void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
+}
+
+void msm_vfe47_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
+ uint32_t framedrop_period)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ msm_camera_io_w(framedrop_pattern, vfe_base +
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x24);
+ temp = msm_camera_io_r(vfe_base +
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x14);
+ temp &= 0xFFFFFF83;
+ msm_camera_io_w(temp | (framedrop_period - 1) << 2,
+ vfe_base + VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x14);
+ }
+}
+
+void msm_vfe47_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x24);
+}
+
+static int32_t msm_vfe47_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
+{
+ int rc = 0;
+
+ switch (bpp) {
+ case 8:
+ *bpp_reg = 0;
+ break;
+ case 10:
+ *bpp_reg = 1;
+ break;
+ case 12:
+ *bpp_reg = 2;
+ break;
+ case 14:
+ *bpp_reg = 3;
+ break;
+ default:
+ pr_err("%s:%d invalid bpp %d", __func__, __LINE__, bpp);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_vfe47_convert_io_fmt_to_reg(
+ enum msm_isp_pack_fmt pack_format, uint32_t *pack_reg)
+{
+ int rc = 0;
+
+ switch (pack_format) {
+ case QCOM:
+ *pack_reg = 0x0;
+ break;
+ case MIPI:
+ *pack_reg = 0x1;
+ break;
+ case DPCM6:
+ *pack_reg = 0x2;
+ break;
+ case DPCM8:
+ *pack_reg = 0x3;
+ break;
+ case PLAIN8:
+ *pack_reg = 0x4;
+ break;
+ case PLAIN16:
+ *pack_reg = 0x5;
+ break;
+ case DPCM10:
+ *pack_reg = 0x6;
+ break;
+ default:
+ pr_err("%s: invalid pack fmt %d!\n", __func__, pack_format);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+int32_t msm_vfe47_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format)
+{
+ int rc = 0;
+ int bpp = 0, read_bpp = 0;
+ enum msm_isp_pack_fmt pack_fmt = 0, read_pack_fmt = 0;
+ uint32_t bpp_reg = 0, pack_reg = 0;
+ uint32_t read_bpp_reg = 0, read_pack_reg = 0;
+ uint32_t io_format_reg = 0; /*io format register bit*/
+
+ io_format_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x88);
+
+ /*input config*/
+ if ((stream_src < RDI_INTF_0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux ==
+ EXTERNAL_READ)) {
+ read_bpp = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe47_convert_bpp_to_reg(read_bpp, &read_bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! in_bpp %d rc %d\n",
+ __func__, read_bpp, rc);
+ return rc;
+ }
+
+ read_pack_fmt = msm_isp_get_pack_format(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+ rc = msm_vfe47_convert_io_fmt_to_reg(
+ read_pack_fmt, &read_pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /*use input format(v4l2_pix_fmt) to get pack format*/
+ io_format_reg &= 0xFFC8FFFF;
+ io_format_reg |= (read_bpp_reg << 20 | read_pack_reg << 16);
+ }
+
+ bpp = msm_isp_get_bit_per_pixel(io_format);
+ rc = msm_vfe47_convert_bpp_to_reg(bpp, &bpp_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_bpp_to_reg err! bpp %d rc = %d\n",
+ __func__, bpp, rc);
+ return rc;
+ }
+
+ switch (stream_src) {
+ case PIX_VIDEO:
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case CAMIF_RAW:
+ io_format_reg &= 0xFFFFCFFF;
+ io_format_reg |= bpp_reg << 12;
+ break;
+ case IDEAL_RAW:
+ /*use output format(v4l2_pix_fmt) to get pack format*/
+ pack_fmt = msm_isp_get_pack_format(io_format);
+ rc = msm_vfe47_convert_io_fmt_to_reg(pack_fmt, &pack_reg);
+ if (rc < 0) {
+ pr_err("%s: convert_io_fmt_to_reg err! rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ io_format_reg &= 0xFFFFFFC8;
+ io_format_reg |= bpp_reg << 4 | pack_reg;
+ break;
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ default:
+ pr_err("%s: Invalid stream source\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(io_format_reg, vfe_dev->vfe_base + 0x88);
+ return 0;
+}
+
+int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+
+ /* There is other option of passing buffer address from user,
+ * in such case, driver needs to map the buffer and use it
+ */
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr, vfe_dev->vfe_base + 0x2F4);
+
+ msm_camera_io_w_mb(0x100000, vfe_dev->vfe_base + 0x80);
+ msm_camera_io_w_mb(0x200000, vfe_dev->vfe_base + 0x80);
+
+ ISP_DBG("%s:VFE%d Fetch Engine ready\n", __func__, vfe_dev->pdev->id);
+
+ return 0;
+}
+
+int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_fetch_eng_multi_pass_start *fe_cfg = arg;
+ struct msm_isp_buffer_mapped_info mapped_info;
+
+ if (vfe_dev->fetch_engine_info.is_busy == 1) {
+ pr_err("%s: fetch engine busy\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&mapped_info, 0, sizeof(struct msm_isp_buffer_mapped_info));
+
+ vfe_dev->fetch_engine_info.session_id = fe_cfg->session_id;
+ vfe_dev->fetch_engine_info.stream_id = fe_cfg->stream_id;
+ vfe_dev->fetch_engine_info.offline_mode = fe_cfg->offline_mode;
+ vfe_dev->fetch_engine_info.fd = fe_cfg->fd;
+
+ if (!fe_cfg->offline_mode) {
+ bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, fe_cfg->session_id,
+ fe_cfg->stream_id);
+ vfe_dev->fetch_engine_info.bufq_handle = bufq_handle;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+ mapped_info = buf->mapped_info[0];
+ buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ } else {
+ rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr,
+ &mapped_info, fe_cfg->fd);
+ if (rc < 0) {
+ pr_err("%s: can not map buffer\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ vfe_dev->fetch_engine_info.buf_idx = fe_cfg->buf_idx;
+ vfe_dev->fetch_engine_info.is_busy = 1;
+
+ msm_camera_io_w(mapped_info.paddr + fe_cfg->input_buf_offset,
+ vfe_dev->vfe_base + 0x2F4);
+ msm_camera_io_w_mb(0x100000, vfe_dev->vfe_base + 0x80);
+ msm_camera_io_w_mb(0x200000, vfe_dev->vfe_base + 0x80);
+
+ ISP_DBG("%s:VFE%d Fetch Engine ready\n", __func__, vfe_dev->pdev->id);
+
+ return 0;
+}
+
+void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t x_size_word, temp;
+ struct msm_vfe_fetch_engine_cfg *fe_cfg = NULL;
+ uint32_t main_unpack_pattern = 0;
+
+ if (pix_cfg->input_mux == EXTERNAL_READ) {
+ fe_cfg = &pix_cfg->fetch_engine_cfg;
+ pr_debug("%s:VFE%d wd x ht buf = %d x %d, fe = %d x %d\n",
+ __func__, vfe_dev->pdev->id, fe_cfg->buf_width,
+ fe_cfg->buf_height,
+ fe_cfg->fetch_width, fe_cfg->fetch_height);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_cgc_override(vfe_dev,
+ VFE47_BUS_RD_CGC_OVERRIDE_BIT, 1);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x84);
+ temp &= 0xFFFFFFFD;
+ temp |= (1 << 1);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84);
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << 24), 0,
+ MSM_ISP_IRQ_ENABLE);
+
+ temp = fe_cfg->fetch_height - 1;
+ msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x308);
+
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->buf_width);
+ msm_camera_io_w((x_size_word - 1) << 16,
+ vfe_dev->vfe_base + 0x30c);
+
+ x_size_word = msm_isp_cal_word_per_line(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format,
+ fe_cfg->fetch_width);
+ msm_camera_io_w(x_size_word << 16 |
+ (temp & 0x3FFF) << 2 | VFE47_FETCH_BURST_LEN,
+ vfe_dev->vfe_base + 0x310);
+
+ temp = ((fe_cfg->buf_width - 1) & 0x3FFF) << 16 |
+ ((fe_cfg->buf_height - 1) & 0x3FFF);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x314);
+
+ /* need to use formulae to calculate MAIN_UNPACK_PATTERN*/
+ switch (vfe_dev->axi_data.src_info[VFE_PIX_0].input_format) {
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ main_unpack_pattern = 0xB210;
+ break;
+ default:
+ main_unpack_pattern = 0xF6543210;
+ break;
+ }
+ msm_camera_io_w(main_unpack_pattern,
+ vfe_dev->vfe_base + 0x318);
+ msm_camera_io_w(0xF, vfe_dev->vfe_base + 0x334);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp |= 2 << 5;
+ temp |= 128 << 8;
+ temp |= (pix_cfg->pixel_pattern & 0x3);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ } else {
+ pr_err("%s: Invalid mux configuration - mux: %d", __func__,
+ pix_cfg->input_mux);
+ }
+}
+
+void msm_vfe47_cfg_testgen(struct vfe_device *vfe_dev,
+ struct msm_vfe_testgen_cfg *testgen_cfg)
+{
+ uint32_t temp;
+ uint32_t bit_per_pixel = 0;
+ uint32_t bpp_reg = 0;
+ uint32_t bayer_pix_pattern_reg = 0;
+ uint32_t unicolorbar_reg = 0;
+ uint32_t unicolor_enb = 0;
+
+ bit_per_pixel = msm_isp_get_bit_per_pixel(
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format);
+
+ switch (bit_per_pixel) {
+ case 8:
+ bpp_reg = 0x0;
+ break;
+ case 10:
+ bpp_reg = 0x1;
+ break;
+ case 12:
+ bpp_reg = 0x10;
+ break;
+ case 14:
+ bpp_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid bpp %d\n", __func__, bit_per_pixel);
+ break;
+ }
+
+ msm_camera_io_w(bpp_reg << 16 | testgen_cfg->burst_num_frame,
+ vfe_dev->vfe_base + 0xC5C);
+
+ msm_camera_io_w(((testgen_cfg->lines_per_frame - 1) << 16) |
+ (testgen_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0xC60);
+
+ temp = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ temp |= (((testgen_cfg->h_blank) & 0x3FFF) << 8);
+ temp |= (1 << 22);
+ msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50);
+
+ msm_camera_io_w((1 << 16) | testgen_cfg->v_blank,
+ vfe_dev->vfe_base + 0xC70);
+
+ switch (testgen_cfg->pixel_bayer_pattern) {
+ case ISP_BAYER_RGRGRG:
+ bayer_pix_pattern_reg = 0x0;
+ break;
+ case ISP_BAYER_GRGRGR:
+ bayer_pix_pattern_reg = 0x1;
+ break;
+ case ISP_BAYER_BGBGBG:
+ bayer_pix_pattern_reg = 0x10;
+ break;
+ case ISP_BAYER_GBGBGB:
+ bayer_pix_pattern_reg = 0x11;
+ break;
+ default:
+ pr_err("%s: invalid pix pattern %d\n",
+ __func__, bit_per_pixel);
+ break;
+ }
+
+ if (testgen_cfg->color_bar_pattern == COLOR_BAR_8_COLOR) {
+ unicolor_enb = 0x0;
+ } else {
+ unicolor_enb = 0x1;
+ switch (testgen_cfg->color_bar_pattern) {
+ case UNICOLOR_WHITE:
+ unicolorbar_reg = 0x0;
+ break;
+ case UNICOLOR_YELLOW:
+ unicolorbar_reg = 0x1;
+ break;
+ case UNICOLOR_CYAN:
+ unicolorbar_reg = 0x10;
+ break;
+ case UNICOLOR_GREEN:
+ unicolorbar_reg = 0x11;
+ break;
+ case UNICOLOR_MAGENTA:
+ unicolorbar_reg = 0x100;
+ break;
+ case UNICOLOR_RED:
+ unicolorbar_reg = 0x101;
+ break;
+ case UNICOLOR_BLUE:
+ unicolorbar_reg = 0x110;
+ break;
+ case UNICOLOR_BLACK:
+ unicolorbar_reg = 0x111;
+ break;
+ default:
+ pr_err("%s: invalid colorbar %d\n",
+ __func__, testgen_cfg->color_bar_pattern);
+ break;
+ }
+ }
+
+ msm_camera_io_w((testgen_cfg->rotate_period << 8) |
+ (bayer_pix_pattern_reg << 6) | (unicolor_enb << 4) |
+ (unicolorbar_reg), vfe_dev->vfe_base + 0xC78);
+}
+
+void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint16_t first_pixel, last_pixel, first_line, last_line;
+ uint16_t epoch_line1;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ struct msm_vfe_testgen_cfg *testgen_cfg = &pix_cfg->testgen_cfg;
+ uint32_t val, subsample_period, subsample_pattern;
+ uint32_t irq_sub_period = 32;
+ uint32_t frame_sub_period = 32;
+ struct msm_vfe_camif_subsample_cfg *subsample_cfg =
+ &pix_cfg->camif_cfg.subsample_cfg;
+ uint16_t bus_sub_en = 0;
+
+ if (subsample_cfg->pixel_skip || subsample_cfg->line_skip)
+ bus_sub_en = 1;
+ else
+ bus_sub_en = 0;
+
+ vfe_dev->dual_vfe_enable = camif_cfg->is_split;
+
+ msm_camera_io_w(pix_cfg->input_mux << 5 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x50);
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+ epoch_line1 = camif_cfg->epoch_line1;
+
+ if ((epoch_line1 <= 0) || (epoch_line1 > last_line))
+ epoch_line1 = last_line - 50;
+
+ if ((last_line - epoch_line1) > 100)
+ epoch_line1 = last_line - 100;
+
+ subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
+ subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
+
+ if (pix_cfg->input_mux == TESTGEN)
+ msm_camera_io_w((testgen_cfg->lines_per_frame - 1) << 16 |
+ (testgen_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+ else
+ msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
+ (camif_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+
+ if (bus_sub_en) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
+ val &= 0xFFFFFFDF;
+ val = val | bus_sub_en << 5;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x47C);
+ subsample_cfg->pixel_skip &= 0x0000FFFF;
+ subsample_cfg->line_skip &= 0x0000FFFF;
+ msm_camera_io_w((subsample_cfg->line_skip << 16) |
+ subsample_cfg->pixel_skip, vfe_dev->vfe_base + 0x490);
+ }
+
+
+ msm_camera_io_w(first_pixel << 16 | last_pixel,
+ vfe_dev->vfe_base + 0x488);
+
+ msm_camera_io_w(first_line << 16 | last_line,
+ vfe_dev->vfe_base + 0x48C);
+
+ /* configure EPOCH0: 20 lines, and
+ * configure EPOCH1: epoch_line1 before EOF
+ */
+ msm_camera_io_w_mb(0x140000 | epoch_line1,
+ vfe_dev->vfe_base + 0x4A0);
+ pr_debug("%s:%d: epoch_line1: %d\n",
+ __func__, __LINE__, epoch_line1);
+ msm_camera_io_w(((irq_sub_period - 1) << 8) | 0 << 5 |
+ (frame_sub_period - 1), vfe_dev->vfe_base + 0x494);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x498);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x49C);
+ if (subsample_period && subsample_pattern) {
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x494);
+ val &= 0xFFFFE0FF;
+ val |= (subsample_period - 1) << 8;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x494);
+ ISP_DBG("%s:camif PERIOD %x PATTERN %x\n",
+ __func__, subsample_period, subsample_pattern);
+
+ val = subsample_pattern;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x49C);
+ } else {
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x49C);
+ }
+
+ if (subsample_cfg->first_pixel ||
+ subsample_cfg->last_pixel ||
+ subsample_cfg->first_line ||
+ subsample_cfg->last_line) {
+ msm_camera_io_w(
+ subsample_cfg->first_pixel << 16 |
+ subsample_cfg->last_pixel,
+ vfe_dev->vfe_base + 0xCE4);
+ msm_camera_io_w(
+ subsample_cfg->first_line << 16 |
+ subsample_cfg->last_line,
+ vfe_dev->vfe_base + 0xCE8);
+ val = msm_camera_io_r(
+ vfe_dev->vfe_base + 0x47C);
+ ISP_DBG("%s: camif raw crop enabled\n", __func__);
+ val |= 1 << 22;
+ msm_camera_io_w(val,
+ vfe_dev->vfe_base + 0x47C);
+ }
+
+ ISP_DBG("%s: camif raw op fmt %d\n",
+ __func__, subsample_cfg->output_format);
+ /* Pdaf output can be sent in below formats */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x88);
+ switch (subsample_cfg->output_format) {
+ case CAMIF_PLAIN_8:
+ val |= PLAIN8 << 9;
+ break;
+ case CAMIF_PLAIN_16:
+ val |= PLAIN16 << 9;
+ break;
+ case CAMIF_MIPI_RAW:
+ val |= MIPI << 9;
+ break;
+ case CAMIF_QCOM_RAW:
+ val |= QCOM << 9;
+ break;
+ default:
+ break;
+ }
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x88);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x46C);
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x46C);
+}
+
+void msm_vfe47_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint32_t core_cfg = 0;
+ uint32_t val = 0;
+
+ core_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ core_cfg &= 0xFFFFFF9F;
+
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ core_cfg |= 0x0 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe47_cfg_camif(vfe_dev, pix_cfg);
+ break;
+ case TESTGEN:
+ /* Change CGC override */
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ val |= (1 << 31);
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x3C);
+
+ /* CAMIF and TESTGEN will both go thorugh CAMIF*/
+ core_cfg |= 0x1 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe47_cfg_camif(vfe_dev, pix_cfg);
+ msm_vfe47_cfg_testgen(vfe_dev, &pix_cfg->testgen_cfg);
+ break;
+ case EXTERNAL_READ:
+ core_cfg |= 0x2 << 5;
+ msm_camera_io_w_mb(core_cfg, vfe_dev->vfe_base + 0x50);
+ msm_vfe47_cfg_fetch_engine(vfe_dev, pix_cfg);
+ break;
+ default:
+ pr_err("%s: Unsupported input mux %d\n",
+ __func__, pix_cfg->input_mux);
+ break;
+ }
+}
+
+void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
+ uint8_t is_stream_on)
+{
+ uint32_t val;
+ int rc = 0;
+
+ if (is_stream_on == vfe_dev->cur_hvx_state) {
+ ISP_DBG("already in same hvx state\n");
+ return;
+ }
+ if (vfe_dev->buf_mgr->secure_enable == SECURE_MODE) {
+ pr_err("%s: Cannot configure hvx, secure_mode: %d\n",
+ __func__,
+ vfe_dev->buf_mgr->secure_enable);
+ return;
+ }
+ if (!vfe_dev->hvx_clk) {
+ pr_err("%s: no stream_clk\n", __func__);
+ return;
+ }
+ if (is_stream_on) {
+ /* Enable HVX */
+ if (!vfe_dev->hvx_clk_state) {
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk enable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = true;
+ }
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ val |= (1 << 3);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
+ val &= 0xFF7FFFFF;
+ if (vfe_dev->hvx_cmd == HVX_ROUND_TRIP)
+ val |= (1 << 23);
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
+ } else {
+ /* Disable HVX */
+ if (!vfe_dev->hvx_clk_state)
+ return;
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk disable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = false;
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
+ val &= 0xFFFFFFF7;
+ msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
+ }
+ vfe_dev->cur_hvx_state = is_stream_on;
+}
+
+void msm_vfe47_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 + 0x47C);
+ if (update_state == ENABLE_CAMIF) {
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x1F, 0x91,
+ MSM_ISP_IRQ_ENABLE);
+
+ if ((vfe_dev->hvx_cmd > HVX_DISABLE) &&
+ (vfe_dev->hvx_cmd <= HVX_ROUND_TRIP))
+ msm_vfe47_configure_hvx(vfe_dev, 1);
+ else
+ msm_vfe47_configure_hvx(vfe_dev, 0);
+
+ 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].stream_count > 0) ? 1 : 0);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x47C);
+ msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x478);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x478);
+ /* testgen GO*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1, vfe_dev->vfe_base + 0xC58);
+ } else if (update_state == DISABLE_CAMIF ||
+ update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ uint32_t poll_val;
+
+ /* For testgen always halt on camif boundary */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ update_state = DISABLE_CAMIF;
+ /* turn off camif, violation and write master overwrite irq */
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x91,
+ MSM_ISP_IRQ_DISABLE);
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x464);
+ /* disable danger signal */
+ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464);
+ msm_camera_io_w_mb((update_state == DISABLE_CAMIF ? 0x0 : 0x6),
+ vfe_dev->vfe_base + 0x478);
+ if (readl_poll_timeout_atomic(vfe_dev->vfe_base + 0x4A4,
+ poll_val, poll_val & 0x80000000, 1000, 2000000))
+ pr_err("%s: camif disable failed %x\n",
+ __func__, poll_val);
+ /* testgen OFF*/
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
+ msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0xC58);
+
+ if ((vfe_dev->hvx_cmd > HVX_DISABLE) &&
+ (vfe_dev->hvx_cmd <= HVX_ROUND_TRIP))
+ msm_vfe47_configure_hvx(vfe_dev, 0);
+
+ }
+}
+
+void msm_vfe47_cfg_rdi_reg(
+ struct vfe_device *vfe_dev, struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src)
+{
+ uint8_t rdi = input_src - VFE_RAW_0;
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE47_RDI_BASE(rdi));
+ rdi_reg_cfg &= 0x3;
+ rdi_reg_cfg |= (rdi * 3) << 28 | rdi_cfg->cid << 4 | 1 << 2;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE47_RDI_BASE(rdi));
+}
+
+void msm_vfe47_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE47_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+ val = msm_camera_io_r(vfe_dev->vfe_base + wm_base + 0x14);
+ val &= ~0x2;
+ if (stream_info->frame_based)
+ val |= 0x2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ if (!stream_info->frame_based) {
+ /* WR_IMAGE_SIZE */
+ val = ((msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_width)+3)/4 - 1) << 16 |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x1C);
+ /* WR_BUFFER_CFG */
+ val = VFE47_BURST_LEN |
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) <<
+ 2 |
+ ((msm_isp_cal_word_per_line(stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_stride)+1)/2) << 16;
+ }
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + wm_base + 0x28);
+}
+
+void msm_vfe47_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE47_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+ /* WR_ADDR_CFG */
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ /* WR_IMAGE_SIZE */
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x1C);
+ /* WR_BUFFER_CFG */
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x28);
+}
+
+void msm_vfe47_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+ switch (stream_info->stream_src) {
+ case PIX_VIDEO:
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /* SINGLE_STREAM_SEL */
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ /* PAIR_STREAM_SWAP_CTRL */
+ xbar_cfg |= 0x3 << 4;
+ break;
+ }
+ xbar_cfg |= 0x1 << 2; /* PAIR_STREAM_EN */
+ }
+ if (stream_info->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /* VIEW_STREAM_EN */
+ else if (stream_info->stream_src == PIX_VIDEO)
+ xbar_cfg |= 0x2;
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x300;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x400;
+ break;
+ case RDI_INTF_0:
+ xbar_cfg = 0xC00;
+ break;
+ case RDI_INTF_1:
+ xbar_cfg = 0xD00;
+ break;
+ case RDI_INTF_2:
+ xbar_cfg = 0xE00;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ break;
+ }
+
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE47_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE47_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE47_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE47_XBAR_BASE(wm));
+}
+
+void msm_vfe47_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
+ uint32_t xbar_reg_cfg = 0;
+
+ wm = stream_info->wm[vfe_idx][plane_idx];
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE47_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE47_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE47_XBAR_BASE(wm));
+}
+
+
+void msm_vfe47_cfg_axi_ub_equal_default(
+ struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ 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;
+ uint64_t delta;
+
+ if (frame_src == VFE_PIX_0) {
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i] &&
+ SRC_TO_INTF(
+ HANDLE_TO_IDX(axi_data->free_wm[i])) ==
+ VFE_PIX_0) {
+ num_used_wms++;
+ total_image_size +=
+ axi_data->wm_image_size[i];
+ }
+ }
+ ub_offset = (axi_data->hw_info->num_rdi * 2) *
+ axi_data->hw_info->min_wm_ub;
+ prop_size = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_ub_size(vfe_dev) -
+ axi_data->hw_info->min_wm_ub * (num_used_wms +
+ axi_data->hw_info->num_rdi * 2);
+ }
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (!axi_data->free_wm[i]) {
+ msm_camera_io_w(0,
+ vfe_dev->vfe_base +
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ ub_reg_offset(vfe_dev, i));
+ continue;
+ }
+
+ if (frame_src != SRC_TO_INTF(
+ HANDLE_TO_IDX(axi_data->free_wm[i])))
+ continue;
+
+ if (frame_src == VFE_PIX_0) {
+ if (total_image_size) {
+ delta = (uint64_t)axi_data->wm_image_size[i] *
+ (uint64_t)prop_size;
+ do_div(delta, total_image_size);
+ wm_ub_size = axi_data->hw_info->min_wm_ub +
+ (uint32_t)delta;
+ msm_camera_io_w(ub_offset << 16 |
+ (wm_ub_size - 1),
+ vfe_dev->vfe_base +
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ ub_reg_offset(vfe_dev, i));
+ ub_offset += wm_ub_size;
+ } else {
+ pr_err("%s: image size is zero\n", __func__);
+ }
+ } else {
+ uint32_t rdi_ub_offset;
+ int plane;
+ int vfe_idx;
+ struct msm_vfe_axi_stream *stream_info;
+
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(axi_data->free_wm[i]));
+ if (!stream_info) {
+ pr_err("%s: stream_info is NULL!", __func__);
+ return;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ for (plane = 0; plane < stream_info->num_planes;
+ plane++)
+ if (stream_info->wm[vfe_idx][plane] ==
+ axi_data->free_wm[i])
+ break;
+
+ rdi_ub_offset = (SRC_TO_INTF(
+ HANDLE_TO_IDX(axi_data->free_wm[i])) -
+ VFE_RAW_0) *
+ axi_data->hw_info->min_wm_ub * 2;
+ wm_ub_size = axi_data->hw_info->min_wm_ub * 2;
+ msm_camera_io_w(rdi_ub_offset << 16 | (wm_ub_size - 1),
+ vfe_dev->vfe_base +
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ ub_reg_offset(vfe_dev, i));
+ }
+ }
+}
+
+void msm_vfe47_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;
+ uint32_t ub_equal_slice = 0;
+
+ ub_equal_slice = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_ub_size(vfe_dev) /
+ axi_data->hw_info->num_wm;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ msm_camera_io_w(ub_offset << 16 | (ub_equal_slice - 1),
+ vfe_dev->vfe_base +
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ ub_reg_offset(vfe_dev, i));
+ ub_offset += ub_equal_slice;
+ }
+}
+
+void msm_vfe47_cfg_axi_ub(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT;
+ if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
+ msm_vfe47_cfg_axi_ub_equal_slicing(vfe_dev);
+ else
+ msm_vfe47_cfg_axi_ub_equal_default(vfe_dev, frame_src);
+}
+
+void msm_vfe47_read_wm_ping_pong_addr(
+ struct vfe_device *vfe_dev)
+{
+ msm_camera_io_dump(vfe_dev->vfe_base +
+ (VFE47_WM_BASE(0) & 0xFFFFFFF0), 0x200, 1);
+}
+
+void msm_vfe47_update_ping_pong_addr(
+ void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size)
+{
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ uint32_t paddr32_max = 0;
+
+ if (buf_size < 0)
+ buf_size = 0;
+
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE47_PING_PONG_BASE(wm_idx, pingpong_bit));
+ msm_camera_io_w(paddr32_max, vfe_base +
+ VFE47_PING_PONG_BASE(wm_idx, pingpong_bit) + 0x4);
+
+}
+
+void msm_vfe47_set_halt_restart_mask(struct vfe_device *vfe_dev)
+{
+ msm_vfe47_config_irq(vfe_dev, BIT(31), BIT(8), MSM_ISP_IRQ_SET);
+}
+
+int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
+ uint32_t blocking)
+{
+ int rc = 0;
+ enum msm_vfe_input_src i;
+ uint32_t val = 0;
+ struct msm_isp_timestamp ts;
+
+ val = msm_camera_io_r(vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
+ val |= 0x1;
+ msm_camera_io_w(val, vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
+
+ /* Keep only halt and reset mask */
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << 31), (1 << 8),
+ MSM_ISP_IRQ_SET);
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ == OVERFLOW_DETECTED)
+ pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
+ __func__, vfe_dev->pdev->id, blocking);
+
+ if (blocking) {
+ init_completion(&vfe_dev->halt_complete);
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400);
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+ if (rc <= 0)
+ pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
+ vfe_dev->pdev->id, rc);
+
+ } else {
+ /* Halt AXI Bus Bridge */
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400);
+ }
+
+ msm_isp_get_timestamp(&ts, vfe_dev);
+ for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
+ /* if any stream is waiting for update, signal fake completes */
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ }
+
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
+
+ return rc;
+}
+
+void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif)
+{
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ vfe_dev->recovery_irq0_mask,
+ vfe_dev->recovery_irq1_mask,
+ MSM_ISP_IRQ_ENABLE);
+ /* Start AXI */
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x400);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, VFE_SRC_MAX);
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+ if (enable_camif)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+}
+
+uint32_t msm_vfe47_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 8) & 0x7F;
+}
+
+uint32_t msm_vfe47_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 25) & 0xF;
+}
+
+uint32_t msm_vfe47_get_pingpong_status(
+ struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x338);
+}
+
+int msm_vfe47_get_stats_idx(enum msm_isp_stats_type stats_type)
+{
+ /*idx use for composite, need to map to irq status*/
+ switch (stats_type) {
+ case MSM_ISP_STATS_HDR_BE:
+ return STATS_COMP_IDX_HDR_BE;
+ case MSM_ISP_STATS_BG:
+ return STATS_COMP_IDX_BG;
+ case MSM_ISP_STATS_BF:
+ return STATS_COMP_IDX_BF;
+ case MSM_ISP_STATS_HDR_BHIST:
+ return STATS_COMP_IDX_HDR_BHIST;
+ case MSM_ISP_STATS_RS:
+ return STATS_COMP_IDX_RS;
+ case MSM_ISP_STATS_CS:
+ return STATS_COMP_IDX_CS;
+ case MSM_ISP_STATS_IHIST:
+ return STATS_COMP_IDX_IHIST;
+ case MSM_ISP_STATS_BHIST:
+ return STATS_COMP_IDX_BHIST;
+ case MSM_ISP_STATS_AEC_BG:
+ return STATS_COMP_IDX_AEC_BG;
+ default:
+ pr_err("%s: Invalid stats type\n", __func__);
+ return -EINVAL;
+ }
+}
+
+int msm_vfe47_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ return 0;
+}
+
+void msm_vfe47_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev, uint32_t stats_mask,
+ uint8_t request_comp_index, uint8_t enable)
+{
+ uint32_t comp_mask_reg;
+ atomic_t *stats_comp_mask;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask < 1)
+ return;
+
+ if (request_comp_index >= MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__, request_comp_index,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ if (vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask >
+ MAX_NUM_STATS_COMP_MASK) {
+ pr_err("%s: num of comp masks %d exceed max %d\n",
+ __func__,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask,
+ MAX_NUM_STATS_COMP_MASK);
+ return;
+ }
+
+ stats_mask = stats_mask & 0x1FF;
+
+ stats_comp_mask = &stats_data->stats_comp_mask[request_comp_index];
+ comp_mask_reg = msm_camera_io_r(vfe_dev->vfe_base + 0x78);
+
+ if (enable) {
+ comp_mask_reg |= stats_mask << (request_comp_index * 16);
+ atomic_set(stats_comp_mask, stats_mask |
+ atomic_read(stats_comp_mask));
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (29 + request_comp_index),
+ 0, MSM_ISP_IRQ_ENABLE);
+ } else {
+ if (!(atomic_read(stats_comp_mask) & stats_mask))
+ return;
+
+ atomic_set(stats_comp_mask,
+ ~stats_mask & atomic_read(stats_comp_mask));
+ comp_mask_reg &= ~(stats_mask << (request_comp_index * 16));
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (29 + request_comp_index),
+ 0, MSM_ISP_IRQ_DISABLE);
+ }
+
+ msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78);
+
+ ISP_DBG("%s: comp_mask_reg: %x comp mask0 %x mask1: %x\n",
+ __func__, comp_mask_reg,
+ atomic_read(&stats_data->stats_comp_mask[0]),
+ atomic_read(&stats_data->stats_comp_mask[1]));
+
+}
+
+void msm_vfe47_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) {
+ case STATS_COMP_IDX_AEC_BG:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 15, 1 << 24, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_HDR_BE:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 16, 1 << 16, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_BG:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 17, 1 << 17, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_BF:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 18, 1 << 26 | 1 << 18,
+ MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_HDR_BHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 19, 1 << 19, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_RS:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 20, 1 << 20, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_CS:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 21, 1 << 21, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_IHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 22, 1 << 22, MSM_ISP_IRQ_ENABLE);
+ break;
+ case STATS_COMP_IDX_BHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 23, 1 << 23, MSM_ISP_IRQ_ENABLE);
+ break;
+ default:
+ pr_err("%s: Invalid stats idx %d\n", __func__,
+ STATS_IDX(stream_info->stream_handle[vfe_idx]));
+ }
+}
+
+void msm_vfe47_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) {
+ case STATS_COMP_IDX_AEC_BG:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 15, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_HDR_BE:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 16, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_BG:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 17, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_BF:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 18, 1 << 26,
+ MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_HDR_BHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 19, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_RS:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 20, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_CS:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 21, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_IHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 22, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ case STATS_COMP_IDX_BHIST:
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 23, 0, MSM_ISP_IRQ_DISABLE);
+ break;
+ default:
+ pr_err("%s: Invalid stats idx %d\n", __func__,
+ STATS_IDX(stream_info->stream_handle[vfe_idx]));
+ }
+}
+
+void msm_vfe47_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE47_STATS_BASE(stats_idx);
+
+ /* WR_ADDR_CFG */
+ msm_camera_io_w((stream_info->framedrop_period - 1) << 2,
+ vfe_dev->vfe_base + stats_base + 0x10);
+ /* WR_IRQ_FRAMEDROP_PATTERN */
+ msm_camera_io_w(stream_info->framedrop_pattern,
+ vfe_dev->vfe_base + stats_base + 0x18);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + stats_base + 0x1C);
+}
+
+void msm_vfe47_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t val = 0;
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE47_STATS_BASE(stats_idx);
+
+ /* WR_ADDR_CFG */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10);
+ /* WR_IRQ_FRAMEDROP_PATTERN */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x18);
+ /* WR_IRQ_SUBSAMPLE_PATTERN */
+ msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x1C);
+}
+
+void msm_vfe47_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ uint32_t ub_size[VFE47_NUM_STATS_TYPE] = {
+ 16, /* MSM_ISP_STATS_HDR_BE */
+ 16, /* MSM_ISP_STATS_BG */
+ 16, /* MSM_ISP_STATS_BF */
+ 16, /* MSM_ISP_STATS_HDR_BHIST */
+ 16, /* MSM_ISP_STATS_RS */
+ 16, /* MSM_ISP_STATS_CS */
+ 16, /* MSM_ISP_STATS_IHIST */
+ 16, /* MSM_ISP_STATS_BHIST */
+ 16, /* MSM_ISP_STATS_AEC_BG */
+ };
+ if (vfe_dev->pdev->id == ISP_VFE1)
+ ub_offset = VFE47_UB_SIZE_VFE1;
+ else if (vfe_dev->pdev->id == ISP_VFE0)
+ ub_offset = VFE47_UB_SIZE_VFE0;
+ else
+ pr_err("%s: incorrect VFE device\n", __func__);
+
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(VFE47_STATS_BURST_LEN << 30 |
+ ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE47_STATS_BASE(i) + 0x14);
+ }
+}
+
+void msm_vfe47_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, cgc_mask = 0;
+
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_COMP_IDX_HDR_BE:
+ cgc_mask |= 1;
+ break;
+ case STATS_COMP_IDX_BG:
+ cgc_mask |= (1 << 3);
+ break;
+ case STATS_COMP_IDX_BHIST:
+ cgc_mask |= (1 << 4);
+ break;
+ case STATS_COMP_IDX_RS:
+ cgc_mask |= (1 << 5);
+ break;
+ case STATS_COMP_IDX_CS:
+ cgc_mask |= (1 << 6);
+ break;
+ case STATS_COMP_IDX_IHIST:
+ cgc_mask |= (1 << 7);
+ break;
+ case STATS_COMP_IDX_AEC_BG:
+ cgc_mask |= (1 << 8);
+ break;
+ case STATS_COMP_IDX_BF:
+ cgc_mask |= (1 << 2);
+ break;
+ case STATS_COMP_IDX_HDR_BHIST:
+ cgc_mask |= (1 << 1);
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ /* CGC override: enforce BAF for DMI */
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
+ if (enable)
+ module_cfg |= cgc_mask;
+ else
+ module_cfg &= ~cgc_mask;
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x30);
+}
+
+bool msm_vfe47_is_module_cfg_lock_needed(
+ uint32_t reg_offset)
+{
+ return false;
+}
+
+void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+ uint32_t module_cfg, module_cfg_mask = 0;
+
+ /* BF stats involve DMI cfg, ignore*/
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) {
+ if ((stats_mask >> i) & 0x1) {
+ switch (i) {
+ case STATS_COMP_IDX_HDR_BE:
+ module_cfg_mask |= 1;
+ break;
+ case STATS_COMP_IDX_HDR_BHIST:
+ module_cfg_mask |= 1 << 1;
+ break;
+ case STATS_COMP_IDX_BF:
+ module_cfg_mask |= 1 << 2;
+ break;
+ case STATS_COMP_IDX_BG:
+ module_cfg_mask |= 1 << 3;
+ break;
+ case STATS_COMP_IDX_BHIST:
+ module_cfg_mask |= 1 << 4;
+ break;
+ case STATS_COMP_IDX_RS:
+ module_cfg_mask |= 1 << 5;
+ break;
+ case STATS_COMP_IDX_CS:
+ module_cfg_mask |= 1 << 6;
+ break;
+ case STATS_COMP_IDX_IHIST:
+ module_cfg_mask |= 1 << 7;
+ break;
+ case STATS_COMP_IDX_AEC_BG:
+ module_cfg_mask |= 1 << 8;
+ break;
+ default:
+ pr_err("%s: Invalid stats mask\n", __func__);
+ return;
+ }
+ }
+ }
+
+ module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x44);
+ if (enable)
+ module_cfg |= module_cfg_mask;
+ else
+ module_cfg &= ~module_cfg_mask;
+
+ msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x44);
+ /* enable wm if needed */
+ if (vfe_dev->hw_info->vfe_ops.stats_ops.enable_stats_wm)
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_stats_wm(vfe_dev,
+ stats_mask, enable);
+}
+
+void msm_vfe47_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size)
+{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ uint32_t paddr32_max;
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+
+ msm_camera_io_w(paddr32, vfe_base +
+ VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
+ msm_camera_io_w(paddr32_max, vfe_base +
+ VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status) + 0x4);
+}
+
+uint32_t msm_vfe47_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ /* TODO: define bf early done irq in status_0 and
+ * bf pingpong done in status_1
+ */
+ uint32_t comp_mapped_irq_mask = 0;
+ int i = 0;
+
+ /* remove early done and handle separately,
+ * add bf idx on status 1
+ */
+ irq_status0 &= ~(1 << 18);
+
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++)
+ if ((irq_status0 >> stats_irq_map_comp_mask[i]) & 0x1)
+ comp_mapped_irq_mask |= (1 << i);
+ if ((irq_status1 >> 26) & 0x1)
+ comp_mapped_irq_mask |= (1 << STATS_COMP_IDX_BF);
+
+ return comp_mapped_irq_mask;
+}
+
+uint32_t msm_vfe47_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 29) & 0x3;
+}
+
+uint32_t msm_vfe47_stats_get_frame_id(
+ struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+
+void msm_vfe47_deinit_bandwidth_mgr(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ msm_bus_scale_client_update_request(
+ isp_bandwidth_mgr->bus_client, 0);
+ msm_bus_scale_unregister_client(isp_bandwidth_mgr->bus_client);
+ isp_bandwidth_mgr->bus_client = 0;
+}
+
+int msm_vfe47_init_bandwidth_mgr(struct vfe_device *vfe_dev,
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ int rc = 0;
+
+ isp_bandwidth_mgr->bus_client =
+ msm_bus_scale_register_client(&msm_isp_bus_client_pdata);
+ if (!isp_bandwidth_mgr->bus_client) {
+ pr_err("%s: client register failed\n", __func__);
+ return -EINVAL;
+ }
+ isp_bandwidth_mgr->bus_vector_active_idx = 1;
+ rc = msm_bus_scale_client_update_request(
+ isp_bandwidth_mgr->bus_client,
+ isp_bandwidth_mgr->bus_vector_active_idx);
+ if (rc)
+ msm_vfe47_deinit_bandwidth_mgr(isp_bandwidth_mgr);
+ return rc;
+}
+
+int msm_vfe47_update_bandwidth(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ int i;
+ uint64_t ab = 0;
+ uint64_t ib = 0;
+ struct msm_bus_paths *path = NULL;
+
+ ALT_VECTOR_IDX(isp_bandwidth_mgr->bus_vector_active_idx);
+ path = &(msm_isp_bus_client_pdata.usecase[
+ isp_bandwidth_mgr->bus_vector_active_idx]);
+ path->vectors[0].ab = 0;
+ path->vectors[0].ib = 0;
+ for (i = 0; i < MAX_ISP_CLIENT; i++) {
+ if (isp_bandwidth_mgr->client_info[i].active) {
+ path->vectors[0].ab +=
+ isp_bandwidth_mgr->client_info[i].ab;
+ path->vectors[0].ib +=
+ isp_bandwidth_mgr->client_info[i].ib;
+ ab += isp_bandwidth_mgr->client_info[i].ab;
+ ib += isp_bandwidth_mgr->client_info[i].ib;
+ }
+ }
+ msm_bus_scale_client_update_request(isp_bandwidth_mgr->bus_client,
+ isp_bandwidth_mgr->bus_vector_active_idx);
+ /* Insert into circular buffer */
+ msm_isp_update_req_history(isp_bandwidth_mgr->bus_client,
+ ab, ib,
+ isp_bandwidth_mgr->client_info,
+ sched_clock());
+ return 0;
+}
+
+int msm_vfe47_get_clks(struct vfe_device *vfe_dev)
+{
+ int i, rc;
+ struct clk *stream_clk;
+ struct msm_cam_clk_info clk_info;
+
+ rc = msm_camera_get_clk_info(vfe_dev->pdev, &vfe_dev->vfe_clk_info,
+ &vfe_dev->vfe_clk, &vfe_dev->num_clk);
+ if (rc)
+ return rc;
+
+ vfe_dev->num_norm_clk = vfe_dev->num_clk;
+ for (i = 0; i < vfe_dev->num_clk; i++) {
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "camss_vfe_stream_clk") == 0) {
+ stream_clk = vfe_dev->vfe_clk[i];
+ clk_info = vfe_dev->vfe_clk_info[i];
+ vfe_dev->num_hvx_clk = 1;
+ vfe_dev->num_norm_clk = vfe_dev->num_clk - 1;
+ break;
+ }
+ }
+ if (i >= vfe_dev->num_clk)
+ pr_err("%s: cannot find camss_vfe_stream_clk\n", __func__);
+ else {
+ /* Switch stream_clk to the last element*/
+ for (; i < vfe_dev->num_clk - 1; i++) {
+ vfe_dev->vfe_clk[i] = vfe_dev->vfe_clk[i+1];
+ vfe_dev->vfe_clk_info[i] = vfe_dev->vfe_clk_info[i+1];
+ }
+ vfe_dev->vfe_clk_info[vfe_dev->num_clk-1] = clk_info;
+ vfe_dev->vfe_clk[vfe_dev->num_clk-1] = stream_clk;
+ vfe_dev->hvx_clk_info =
+ &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
+ }
+
+ for (i = 0; i < vfe_dev->num_clk; i++) {
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "vfe_clk_src") == 0)
+ vfe_dev->hw_info->vfe_clk_idx = i;
+ }
+ return 0;
+}
+
+void msm_vfe47_put_clks(struct vfe_device *vfe_dev)
+{
+ msm_camera_put_clk_info(vfe_dev->pdev, &vfe_dev->vfe_clk_info,
+ &vfe_dev->vfe_clk, vfe_dev->num_clk);
+
+ vfe_dev->num_clk = 0;
+ vfe_dev->hvx_clk = NULL;
+ vfe_dev->hvx_clk_info = NULL;
+ vfe_dev->num_hvx_clk = 0;
+ vfe_dev->num_norm_clk = 0;
+}
+
+int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable)
+{
+ return msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->vfe_clk_info,
+ vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable);
+}
+
+int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
+{
+ int rc = 0;
+ int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
+ int ret;
+ long clk_rate, prev_clk_rate;
+
+ clk_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
+ if (vfe_dev->vfe_clk_info[clk_idx].clk_rate == clk_rate)
+ return rc;
+
+ prev_clk_rate =
+ vfe_dev->vfe_clk_info[clk_idx].clk_rate;
+ vfe_dev->vfe_clk_info[clk_idx].clk_rate =
+ clk_rate;
+ /*
+ * if cx_ipeak is supported vote first so that dsp throttling is
+ * reduced before we go to turbo
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->vfe_clk_info[clk_idx].clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ /*set vfe clock*/
+ rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev,
+ vfe_dev->vfe_clk[clk_idx], *rate);
+ if (rc < 0)
+ return rc;
+ /*
+ * if cx_ipeak is supported remove the vote for non-turbo clock and
+ * if voting done earlier
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->vfe_clk_info[clk_idx].clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
+ vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL);
+ return 0;
+}
+
+int msm_vfe47_get_max_clk_rate(struct vfe_device *vfe_dev, long *rate)
+{
+ int clk_idx = 0;
+ unsigned long max_value = ~0;
+ long round_rate = 0;
+
+ if (!vfe_dev || !rate) {
+ pr_err("%s:%d failed: vfe_dev %pK rate %pK\n", __func__,
+ __LINE__, vfe_dev, rate);
+ return -EINVAL;
+ }
+
+ *rate = 0;
+ if (!vfe_dev->hw_info) {
+ pr_err("%s:%d failed: vfe_dev->hw_info %pK\n", __func__,
+ __LINE__, vfe_dev->hw_info);
+ return -EINVAL;
+ }
+
+ clk_idx = vfe_dev->hw_info->vfe_clk_idx;
+ if (clk_idx >= vfe_dev->num_clk) {
+ pr_err("%s:%d failed: clk_idx %d max array size %zd\n",
+ __func__, __LINE__, clk_idx,
+ vfe_dev->num_clk);
+ return -EINVAL;
+ }
+
+ round_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], max_value);
+ if (round_rate < 0) {
+ pr_err("%s: Invalid vfe clock rate\n", __func__);
+ return -EINVAL;
+ }
+
+ *rate = round_rate;
+ return 0;
+}
+
+int msm_vfe47_get_clk_rates(struct vfe_device *vfe_dev,
+ struct msm_isp_clk_rates *rates)
+{
+ struct device_node *of_node;
+ int32_t rc = 0;
+ uint32_t svs = 0, nominal = 0, turbo = 0;
+
+ if (!vfe_dev || !rates) {
+ pr_err("%s:%d failed: vfe_dev %pK rates %pK\n", __func__,
+ __LINE__, vfe_dev, rates);
+ return -EINVAL;
+ }
+
+ if (!vfe_dev->pdev) {
+ pr_err("%s:%d failed: vfe_dev->pdev %pK\n", __func__,
+ __LINE__, vfe_dev->pdev);
+ return -EINVAL;
+ }
+
+ of_node = vfe_dev->pdev->dev.of_node;
+
+ if (!of_node) {
+ pr_err("%s %d failed: of_node = %pK\n", __func__,
+ __LINE__, of_node);
+ return -EINVAL;
+ }
+
+ /*
+ * Many older targets dont define svs.
+ * return svs=0 for older targets.
+ */
+ rc = of_property_read_u32(of_node, "max-clk-svs",
+ &svs);
+ if (rc < 0)
+ svs = 0;
+
+ rc = of_property_read_u32(of_node, "max-clk-nominal",
+ &nominal);
+ if (rc < 0 || !nominal) {
+ pr_err("%s: nominal rate error\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(of_node, "max-clk-turbo",
+ &turbo);
+ if (rc < 0 || !turbo) {
+ pr_err("%s: turbo rate error\n", __func__);
+ return -EINVAL;
+ }
+ rates->svs_rate = svs;
+ rates->nominal_rate = nominal;
+ rates->high_rate = turbo;
+ return 0;
+}
+
+void msm_vfe47_put_regulators(struct vfe_device *vfe_dev)
+{
+ int i;
+
+ for (i = 0; i < vfe_dev->vfe_num_regulators; i++)
+ regulator_put(vfe_dev->regulator_info[i].vdd);
+
+ vfe_dev->vfe_num_regulators = 0;
+ kfree(vfe_dev->regulator_info);
+ vfe_dev->regulator_info = NULL;
+}
+
+int msm_vfe47_get_regulators(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ int i;
+
+ vfe_dev->vfe_num_regulators =
+ sizeof(*vfe_dev->hw_info->regulator_names) / sizeof(char *);
+
+ vfe_dev->regulator_info = kzalloc(sizeof(struct msm_cam_regulator) *
+ vfe_dev->vfe_num_regulators, GFP_KERNEL);
+ if (!vfe_dev->regulator_info)
+ return -ENOMEM;
+
+ for (i = 0; i < vfe_dev->vfe_num_regulators; i++) {
+ vfe_dev->regulator_info[i].vdd = regulator_get(
+ &vfe_dev->pdev->dev,
+ vfe_dev->hw_info->regulator_names[i]);
+ if (IS_ERR(vfe_dev->regulator_info[i].vdd)) {
+ pr_err("%s: Regulator vfe get failed %ld\n", __func__,
+ PTR_ERR(vfe_dev->regulator_info[i].vdd));
+ rc = -ENODEV;
+ goto reg_get_fail;
+ }
+ }
+ return 0;
+
+reg_get_fail:
+ for (i--; i >= 0; i--)
+ regulator_put(vfe_dev->regulator_info[i].vdd);
+ kfree(vfe_dev->regulator_info);
+ vfe_dev->regulator_info = NULL;
+ return rc;
+}
+
+int msm_vfe47_enable_regulators(struct vfe_device *vfe_dev, int enable)
+{
+ return msm_camera_regulator_enable(vfe_dev->regulator_info,
+ vfe_dev->vfe_num_regulators, enable);
+}
+
+int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ void __iomem *vfe_fuse_base;
+ uint32_t vfe_fuse_base_size;
+
+ vfe_dev->vfe_base = msm_camera_get_reg_base(vfe_dev->pdev, "vfe", 0);
+ if (!vfe_dev->vfe_base)
+ return -ENOMEM;
+ vfe_dev->vfe_vbif_base = msm_camera_get_reg_base(vfe_dev->pdev,
+ "vfe_vbif", 0);
+ if (!vfe_dev->vfe_vbif_base) {
+ rc = -ENOMEM;
+ goto vbif_base_fail;
+ }
+
+ vfe_dev->vfe_irq = msm_camera_get_irq(vfe_dev->pdev, "vfe");
+ if (!vfe_dev->vfe_irq) {
+ rc = -ENODEV;
+ goto vfe_irq_fail;
+ }
+
+ vfe_dev->vfe_base_size = msm_camera_get_res_size(vfe_dev->pdev, "vfe");
+ vfe_dev->vfe_vbif_base_size = msm_camera_get_res_size(vfe_dev->pdev,
+ "vfe_vbif");
+ if (!vfe_dev->vfe_base_size || !vfe_dev->vfe_vbif_base_size) {
+ rc = -ENOMEM;
+ goto get_res_fail;
+ }
+ vfe_dev->vfe_hw_limit = 0;
+ vfe_fuse_base = msm_camera_get_reg_base(vfe_dev->pdev,
+ "vfe_fuse", 0);
+ vfe_fuse_base_size = msm_camera_get_res_size(vfe_dev->pdev,
+ "vfe_fuse");
+ if (vfe_fuse_base) {
+ if (vfe_fuse_base_size)
+ vfe_dev->vfe_hw_limit =
+ (msm_camera_io_r(vfe_fuse_base) >> 5) & 0x1;
+ msm_camera_put_reg_base(vfe_dev->pdev, vfe_fuse_base,
+ "vfe_fuse", 0);
+ }
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_regulators(vfe_dev);
+ if (rc)
+ goto get_regulator_fail;
+
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_clks(vfe_dev);
+ if (rc)
+ goto get_clkcs_fail;
+
+ rc = msm_camera_register_irq(vfe_dev->pdev, vfe_dev->vfe_irq,
+ msm_isp_process_irq,
+ IRQF_TRIGGER_RISING, "vfe", vfe_dev);
+ if (rc < 0)
+ goto irq_register_fail;
+
+ msm_camera_enable_irq(vfe_dev->vfe_irq, 0);
+
+ rc = msm_isp_init_bandwidth_mgr(vfe_dev, ISP_VFE0 + vfe_dev->pdev->id);
+ if (rc)
+ goto init_bw_fail;
+
+ return 0;
+
+init_bw_fail:
+ msm_camera_unregister_irq(vfe_dev->pdev, vfe_dev->vfe_irq, "vfe");
+irq_register_fail:
+ vfe_dev->hw_info->vfe_ops.platform_ops.put_clks(vfe_dev);
+get_clkcs_fail:
+ vfe_dev->hw_info->vfe_ops.platform_ops.put_regulators(vfe_dev);
+get_regulator_fail:
+get_res_fail:
+ vfe_dev->vfe_vbif_base_size = 0;
+ vfe_dev->vfe_base_size = 0;
+vfe_irq_fail:
+ msm_camera_put_reg_base(vfe_dev->pdev, vfe_dev->vfe_base,
+ "vfe_vbif", 0);
+vbif_base_fail:
+ msm_camera_put_reg_base(vfe_dev->pdev, vfe_dev->vfe_base, "vfe", 0);
+ return rc;
+}
+
+void msm_vfe47_get_error_mask(
+ uint32_t *error_mask0, uint32_t *error_mask1)
+{
+ *error_mask0 = 0x00000000;
+ *error_mask1 = 0x0BFFFEFF;
+}
+
+void msm_vfe47_get_overflow_mask(uint32_t *overflow_mask)
+{
+ *overflow_mask = 0x09FFFE7E;
+}
+
+void msm_vfe47_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask)
+{
+ *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
+}
+
+void msm_vfe47_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask)
+{
+ *irq0_mask = vfe_dev->irq0_mask;
+ *irq1_mask = vfe_dev->irq1_mask;
+}
+
+void msm_vfe47_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask)
+{
+ *irq0_mask = BIT(31);
+ *irq1_mask = BIT(8);
+}
+
+static struct msm_vfe_axi_hardware_info msm_vfe47_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 96,
+ .scratch_buf_range = SZ_32M + SZ_4M,
+};
+
+static struct msm_vfe_stats_hardware_info msm_vfe47_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_HDR_BE | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST |
+ 1 << MSM_ISP_STATS_HDR_BHIST | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_AEC_BG,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .num_stats_type = VFE47_NUM_STATS_TYPE,
+ .num_stats_comp_mask = VFE47_NUM_STATS_COMP,
+};
+
+struct msm_vfe_hardware_info vfe47_hw_info = {
+ .num_iommu_ctx = 1,
+ .num_iommu_secure_ctx = 0,
+ .vfe_clk_idx = VFE47_SRC_CLK_DTSI_IDX,
+ .runtime_axi_update = 1,
+ .min_ib = 100000000,
+ .min_ab = 100000000,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe47_read_and_clear_irq_status,
+ .process_camif_irq = msm_vfe47_process_input_irq,
+ .process_reset_irq = msm_vfe47_process_reset_irq,
+ .process_halt_irq = msm_vfe47_process_halt_irq,
+ .process_reg_update = msm_vfe47_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ .process_epoch_irq = msm_vfe47_process_epoch_irq,
+ .config_irq = msm_vfe47_config_irq,
+ .read_irq_status = msm_vfe47_read_irq_status,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe47_axi_reload_wm,
+ .enable_wm = msm_vfe47_axi_enable_wm,
+ .cfg_io_format = msm_vfe47_cfg_io_format,
+ .cfg_comp_mask = msm_vfe47_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe47_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe47_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe47_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe47_cfg_framedrop,
+ .clear_framedrop = msm_vfe47_clear_framedrop,
+ .cfg_wm_reg = msm_vfe47_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe47_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe47_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe47_axi_clear_wm_xbar_reg,
+ .cfg_ub = msm_vfe47_cfg_axi_ub,
+ .read_wm_ping_pong_addr =
+ msm_vfe47_read_wm_ping_pong_addr,
+ .update_ping_pong_addr =
+ msm_vfe47_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe47_get_comp_mask,
+ .get_wm_mask = msm_vfe47_get_wm_mask,
+ .get_pingpong_status = msm_vfe47_get_pingpong_status,
+ .halt = msm_vfe47_axi_halt,
+ .restart = msm_vfe47_axi_restart,
+ .update_cgc_override =
+ msm_vfe47_axi_update_cgc_override,
+ .ub_reg_offset = msm_vfe47_ub_reg_offset,
+ .get_ub_size = msm_vfe47_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe47_reg_update,
+ .cfg_input_mux = msm_vfe47_cfg_input_mux,
+ .update_camif_state = msm_vfe47_update_camif_state,
+ .start_fetch_eng = msm_vfe47_start_fetch_engine,
+ .cfg_rdi_reg = msm_vfe47_cfg_rdi_reg,
+ .reset_hw = msm_vfe47_reset_hardware,
+ .init_hw = msm_vfe47_init_hardware,
+ .init_hw_reg = msm_vfe47_init_hardware_reg,
+ .clear_status_reg = msm_vfe47_clear_status_reg,
+ .release_hw = msm_vfe47_release_hardware,
+ .get_error_mask = msm_vfe47_get_error_mask,
+ .get_overflow_mask = msm_vfe47_get_overflow_mask,
+ .get_rdi_wm_mask = msm_vfe47_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe47_get_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe47_get_halt_restart_mask,
+ .process_error_status = msm_vfe47_process_error_status,
+ .is_module_cfg_lock_needed =
+ msm_vfe47_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = msm_isp47_ahb_clk_cfg,
+ .start_fetch_eng_multi_pass =
+ msm_vfe47_start_fetch_engine_multi_pass,
+ .set_halt_restart_mask =
+ msm_vfe47_set_halt_restart_mask,
+ .set_bus_err_ign_mask = NULL,
+ .get_bus_err_mask = NULL,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe47_get_stats_idx,
+ .check_streams = msm_vfe47_stats_check_streams,
+ .cfg_comp_mask = msm_vfe47_stats_cfg_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe47_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe47_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe47_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe47_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe47_stats_cfg_ub,
+ .enable_module = msm_vfe47_stats_enable_module,
+ .update_ping_pong_addr =
+ msm_vfe47_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe47_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe47_stats_get_wm_mask,
+ .get_frame_id = msm_vfe47_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe47_get_pingpong_status,
+ .update_cgc_override =
+ msm_vfe47_stats_update_cgc_override,
+ .enable_stats_wm = NULL,
+ },
+ .platform_ops = {
+ .get_platform_data = msm_vfe47_get_platform_data,
+ .enable_regulators = msm_vfe47_enable_regulators,
+ .get_regulators = msm_vfe47_get_regulators,
+ .put_regulators = msm_vfe47_put_regulators,
+ .enable_clks = msm_vfe47_enable_clks,
+ .get_clks = msm_vfe47_get_clks,
+ .put_clks = msm_vfe47_put_clks,
+ .get_clk_rates = msm_vfe47_get_clk_rates,
+ .get_max_clk_rate = msm_vfe47_get_max_clk_rate,
+ .set_clk_rate = msm_vfe47_set_clk_rate,
+ .init_bw_mgr = msm_vfe47_init_bandwidth_mgr,
+ .deinit_bw_mgr = msm_vfe47_deinit_bandwidth_mgr,
+ .update_bw = msm_vfe47_update_bandwidth,
+ }
+ },
+ .dmi_reg_offset = 0xC2C,
+ .axi_hw_info = &msm_vfe47_axi_hw_info,
+ .stats_hw_info = &msm_vfe47_stats_hw_info,
+ .regulator_names = {"vdd", "camss-vdd", "mmagic-vdd"},
+};
+EXPORT_SYMBOL(vfe47_hw_info);
+
+static const struct of_device_id msm_vfe47_dt_match[] = {
+ {
+ .compatible = "qcom,vfe47",
+ .data = &vfe47_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe47_dt_match);
+
+static struct platform_driver vfe47_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe47",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe47_dt_match,
+ },
+};
+
+static int __init msm_vfe47_init_module(void)
+{
+ return platform_driver_register(&vfe47_driver);
+}
+
+static void __exit msm_vfe47_exit_module(void)
+{
+ platform_driver_unregister(&vfe47_driver);
+}
+
+module_init(msm_vfe47_init_module);
+module_exit(msm_vfe47_exit_module);
+MODULE_DESCRIPTION("MSM VFE47 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
new file mode 100644
index 0000000..d08ee27
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
@@ -0,0 +1,209 @@
+/* Copyright (c) 2013-2014,2016-2018, 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_ISP47_H__
+#define __MSM_ISP47_H__
+
+#define VFE47_NUM_STATS_COMP 2
+#define VFE47_NUM_STATS_TYPE 9
+/*composite mask order*/
+enum msm_vfe47_stats_comp_idx {
+ STATS_COMP_IDX_HDR_BE = 0,
+ STATS_COMP_IDX_BG,
+ STATS_COMP_IDX_BF,
+ STATS_COMP_IDX_HDR_BHIST,
+ STATS_COMP_IDX_RS,
+ STATS_COMP_IDX_CS,
+ STATS_COMP_IDX_IHIST,
+ STATS_COMP_IDX_BHIST,
+ STATS_COMP_IDX_AEC_BG,
+};
+
+extern struct msm_vfe_hardware_info vfe47_hw_info;
+
+void msm_vfe47_read_and_clear_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+void msm_vfe47_enable_camif_error(struct vfe_device *vfe_dev,
+ int enable);
+void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
+void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
+ uint32_t first_start, uint32_t blocking_call);
+void msm_vfe47_axi_reload_wm(struct vfe_device *vfe_dev,
+ void __iomem *vfe_base, uint32_t reload_mask);
+void msm_vfe47_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable);
+void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+void msm_vfe47_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
+ uint32_t framedrop_period);
+void msm_vfe47_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+int32_t msm_vfe47_cfg_io_format(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src, uint32_t io_format);
+int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg);
+int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev,
+ void *arg);
+void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+void msm_vfe47_cfg_testgen(struct vfe_device *vfe_dev,
+ struct msm_vfe_testgen_cfg *testgen_cfg);
+void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+void msm_vfe47_cfg_input_mux(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
+ uint8_t is_stream_on);
+void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state);
+void msm_vfe47_cfg_rdi_reg(
+ struct vfe_device *vfe_dev, struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src);
+void msm_vfe47_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx);
+void msm_vfe47_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+void msm_vfe47_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx);
+void msm_vfe47_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+void msm_vfe47_cfg_axi_ub_equal_default(
+ struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+void msm_vfe47_cfg_axi_ub_equal_slicing(
+ struct vfe_device *vfe_dev);
+void msm_vfe47_cfg_axi_ub(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+void msm_vfe47_read_wm_ping_pong_addr(
+ struct vfe_device *vfe_dev);
+void msm_vfe47_update_ping_pong_addr(
+ void __iomem *vfe_base,
+ uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
+ int32_t buf_size);
+int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
+ uint32_t blocking);
+void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif);
+uint32_t msm_vfe47_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1);
+uint32_t msm_vfe47_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1);
+uint32_t msm_vfe47_get_pingpong_status(
+ struct vfe_device *vfe_dev);
+int msm_vfe47_get_stats_idx(enum msm_isp_stats_type stats_type);
+int msm_vfe47_stats_check_streams(
+ struct msm_vfe_stats_stream *stream_info);
+void msm_vfe47_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev, uint32_t stats_mask,
+ uint8_t request_comp_index, uint8_t enable);
+void msm_vfe47_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+void msm_vfe47_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+void msm_vfe47_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+void msm_vfe47_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+void msm_vfe47_stats_cfg_ub(struct vfe_device *vfe_dev);
+void msm_vfe47_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+bool msm_vfe47_is_module_cfg_lock_needed(
+ uint32_t reg_offset);
+void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+void msm_vfe47_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
+uint32_t msm_vfe47_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1);
+uint32_t msm_vfe47_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1);
+uint32_t msm_vfe47_stats_get_frame_id(
+ struct vfe_device *vfe_dev);
+void msm_vfe47_get_error_mask(
+ uint32_t *error_mask0, uint32_t *error_mask1);
+void msm_vfe47_get_overflow_mask(uint32_t *overflow_mask);
+void msm_vfe47_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask);
+void msm_vfe47_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask);
+void msm_vfe47_restore_irq_mask(struct vfe_device *vfe_dev);
+void msm_vfe47_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask);
+int msm_vfe47_init_hardware(struct vfe_device *vfe_dev);
+void msm_vfe47_release_hardware(struct vfe_device *vfe_dev);
+void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev);
+void msm_vfe47_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+void msm_vfe47_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+void msm_vfe47_process_input_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+void msm_vfe47_process_violation_status(
+ struct vfe_device *vfe_dev);
+void msm_vfe47_process_error_status(struct vfe_device *vfe_dev);
+void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev);
+int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev);
+int msm_vfe47_enable_regulators(struct vfe_device *vfe_dev, int enable);
+int msm_vfe47_get_regulators(struct vfe_device *vfe_dev);
+void msm_vfe47_put_regulators(struct vfe_device *vfe_dev);
+int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable);
+int msm_vfe47_get_clks(struct vfe_device *vfe_dev);
+void msm_vfe47_put_clks(struct vfe_device *vfe_dev);
+int msm_vfe47_get_clk_rates(struct vfe_device *vfe_dev,
+ struct msm_isp_clk_rates *rates);
+int msm_vfe47_get_max_clk_rate(struct vfe_device *vfe_dev, long *rate);
+int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate);
+int msm_vfe47_init_bandwidth_mgr(struct vfe_device *vfe_dev,
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+void msm_vfe47_deinit_bandwidth_mgr(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+int msm_vfe47_update_bandwidth(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr);
+void msm_vfe47_config_irq(struct vfe_device *vfe_dev,
+ uint32_t irq0_mask, uint32_t irq1_mask,
+ enum msm_isp_irq_operation oper);
+int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
+ struct msm_isp_ahb_clk_cfg *ahb_cfg);
+void msm_vfe47_set_halt_restart_mask(struct vfe_device *vfe_dev);
+uint32_t msm_vfe47_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx);
+uint32_t msm_vfe47_get_ub_size(struct vfe_device *vfe_dev);
+#endif /* __MSM_ISP47_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
new file mode 100644
index 0000000..9bece18
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
@@ -0,0 +1,505 @@
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/clk/msm-clk.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"
+#include "cam_hw_ops.h"
+#include "msm_isp47.h"
+#include "msm_isp48.h"
+#include "cam_soc_api.h"
+
+#define MSM_VFE48_BUS_CLIENT_INIT 0xABAB
+#define VFE48_STATS_BURST_LEN 3
+#define VFE48_UB_SIZE_VFE 2048 /* 2048 * 256 bits = 64KB */
+#define VFE48_UB_STATS_SIZE 144
+#define MSM_ISP48_TOTAL_IMAGE_UB_VFE (VFE48_UB_SIZE_VFE - VFE48_UB_STATS_SIZE)
+
+
+static uint32_t stats_base_addr[] = {
+ 0x1D4, /* HDR_BE */
+ 0x254, /* BG(AWB_BG) */
+ 0x214, /* BF */
+ 0x1F4, /* HDR_BHIST */
+ 0x294, /* RS */
+ 0x2B4, /* CS */
+ 0x2D4, /* IHIST */
+ 0x274, /* BHIST (SKIN_BHIST) */
+ 0x234, /* AEC_BG */
+};
+
+#define VFE48_STATS_BASE(idx) (stats_base_addr[idx])
+
+static struct msm_vfe_axi_hardware_info msm_vfe48_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 96,
+ .scratch_buf_range = SZ_32M,
+};
+
+static uint8_t stats_pingpong_offset_map[] = {
+ 8, /* HDR_BE */
+ 12, /* BG(AWB_BG) */
+ 10, /* BF */
+ 9, /* HDR_BHIST */
+ 14, /* RS */
+ 15, /* CS */
+ 16, /* IHIST */
+ 13, /* BHIST (SKIN_BHIST) */
+ 11, /* AEC_BG */
+};
+
+static uint8_t stats_wm_index[] = {
+ 7, /* HDR_BE */
+ 11, /* BG(AWB_BG) */
+ 9, /* BF */
+ 8, /* HDR_BHIST */
+ 13, /* RS */
+ 14, /* CS */
+ 15, /* IHIST */
+ 12, /* BHIST (SKIN_BHIST) */
+ 10, /* AEC_BG */
+};
+
+#define VFE48_SRC_CLK_DTSI_IDX 3
+
+static struct msm_vfe_stats_hardware_info msm_vfe48_stats_hw_info = {
+ .stats_capability_mask =
+ 1 << MSM_ISP_STATS_HDR_BE | 1 << MSM_ISP_STATS_BF |
+ 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST |
+ 1 << MSM_ISP_STATS_HDR_BHIST | 1 << MSM_ISP_STATS_IHIST |
+ 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS |
+ 1 << MSM_ISP_STATS_AEC_BG,
+ .stats_ping_pong_offset = stats_pingpong_offset_map,
+ .stats_wm_index = stats_wm_index,
+ .num_stats_type = VFE47_NUM_STATS_TYPE,
+ .num_stats_comp_mask = VFE47_NUM_STATS_COMP,
+};
+
+static void msm_vfe48_axi_enable_wm(void __iomem *vfe_base,
+ uint8_t wm_idx, uint8_t enable)
+{
+ uint32_t val;
+
+ if (enable)
+ val = (0x2 << (2 * wm_idx));
+ else
+ val = (0x1 << (2 * wm_idx));
+
+ msm_camera_io_w_mb(val, vfe_base + 0xCEC);
+}
+
+static void msm_vfe48_enable_stats_wm(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+ int i;
+
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) {
+ if (!(stats_mask & 0x1)) {
+ stats_mask >>= 1;
+ continue;
+ }
+ stats_mask >>= 1;
+ msm_vfe48_axi_enable_wm(vfe_dev->vfe_base,
+ vfe_dev->hw_info->stats_hw_info->stats_wm_index[i],
+ enable);
+ }
+}
+
+static void msm_vfe48_deinit_bandwidth_mgr(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ msm_camera_unregister_bus_client(CAM_BUS_CLIENT_VFE);
+ isp_bandwidth_mgr->bus_client = 0;
+}
+
+static int msm_vfe48_init_bandwidth_mgr(struct vfe_device *vfe_dev,
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ int rc = 0;
+
+ rc = msm_camera_register_bus_client(vfe_dev->pdev, CAM_BUS_CLIENT_VFE);
+ if (rc) {
+ /*
+ * Fallback to the older method of registration. While testing
+ * this the bus apis that the soc api uses were not
+ * enabled and hence we need to use the old api for now
+ */
+ vfe_dev->hw_info->vfe_ops.platform_ops.init_bw_mgr =
+ msm_vfe47_init_bandwidth_mgr;
+ vfe_dev->hw_info->vfe_ops.platform_ops.deinit_bw_mgr =
+ msm_vfe47_deinit_bandwidth_mgr;
+ vfe_dev->hw_info->vfe_ops.platform_ops.update_bw =
+ msm_vfe47_update_bandwidth;
+ return vfe_dev->hw_info->vfe_ops.platform_ops.init_bw_mgr(
+ vfe_dev, isp_bandwidth_mgr);
+ }
+ isp_bandwidth_mgr->bus_client = MSM_VFE48_BUS_CLIENT_INIT;
+ return rc;
+}
+
+static int msm_vfe48_update_bandwidth(
+ struct msm_isp_bandwidth_mgr *isp_bandwidth_mgr)
+{
+ int i;
+ uint64_t ab = 0;
+ uint64_t ib = 0;
+ int rc = 0;
+
+ for (i = 0; i < MAX_ISP_CLIENT; i++) {
+ if (isp_bandwidth_mgr->client_info[i].active) {
+ ab += isp_bandwidth_mgr->client_info[i].ab;
+ ib += isp_bandwidth_mgr->client_info[i].ib;
+ }
+ }
+ rc = msm_camera_update_bus_bw(CAM_BUS_CLIENT_VFE, ab, ib);
+ /* Insert into circular buffer */
+ if (!rc)
+ msm_isp_update_req_history(isp_bandwidth_mgr->bus_client,
+ ab, ib,
+ isp_bandwidth_mgr->client_info,
+ sched_clock());
+ return rc;
+}
+
+static void msm_vfe48_put_clks(struct vfe_device *vfe_dev)
+{
+ msm_camera_put_clk_info_and_rates(vfe_dev->pdev, &vfe_dev->vfe_clk_info,
+ &vfe_dev->vfe_clk, &vfe_dev->vfe_clk_rates,
+ vfe_dev->num_rates,
+ vfe_dev->num_clk);
+
+ vfe_dev->num_clk = 0;
+ vfe_dev->num_rates = 0;
+ vfe_dev->hvx_clk = NULL;
+ vfe_dev->hvx_clk_info = NULL;
+ vfe_dev->num_hvx_clk = 0;
+ vfe_dev->num_norm_clk = 0;
+}
+
+static int msm_vfe48_get_clks(struct vfe_device *vfe_dev)
+{
+ int rc;
+ int i, j;
+ struct clk *stream_clk;
+ struct msm_cam_clk_info clk_info;
+
+ rc = msm_camera_get_clk_info_and_rates(vfe_dev->pdev,
+ &vfe_dev->vfe_clk_info, &vfe_dev->vfe_clk,
+ &vfe_dev->vfe_clk_rates,
+ &vfe_dev->num_rates,
+ &vfe_dev->num_clk);
+
+ if (rc)
+ return rc;
+ vfe_dev->num_norm_clk = vfe_dev->num_clk;
+ for (i = 0; i < vfe_dev->num_clk; i++) {
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "camss_vfe_stream_clk") == 0) {
+ stream_clk = vfe_dev->vfe_clk[i];
+ clk_info = vfe_dev->vfe_clk_info[i];
+ vfe_dev->num_hvx_clk = 1;
+ vfe_dev->num_norm_clk = vfe_dev->num_clk - 1;
+ break;
+ }
+ }
+ if (i >= vfe_dev->num_clk)
+ pr_err("%s: cannot find camss_vfe_stream_clk\n", __func__);
+ else {
+ /* Switch stream_clk to the last element*/
+ for (; i < vfe_dev->num_clk - 1; i++) {
+ vfe_dev->vfe_clk[i] = vfe_dev->vfe_clk[i+1];
+ vfe_dev->vfe_clk_info[i] = vfe_dev->vfe_clk_info[i+1];
+ for (j = 0; j < MSM_VFE_MAX_CLK_RATES; j++)
+ vfe_dev->vfe_clk_rates[j][i] =
+ vfe_dev->vfe_clk_rates[j][i+1];
+ }
+ vfe_dev->vfe_clk_info[vfe_dev->num_clk-1] = clk_info;
+ vfe_dev->vfe_clk[vfe_dev->num_clk-1] = stream_clk;
+ vfe_dev->hvx_clk_info =
+ &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
+ }
+
+ for (i = 0; i < vfe_dev->num_clk; i++) {
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "vfe_clk_src") == 0) {
+ vfe_dev->hw_info->vfe_clk_idx = i;
+ /* set initial clk rate to svs */
+ msm_camera_clk_set_rate(&vfe_dev->pdev->dev,
+ vfe_dev->vfe_clk[i],
+ vfe_dev->vfe_clk_rates
+ [MSM_VFE_CLK_RATE_SVS][i]);
+ }
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "mnoc_maxi_clk") == 0)
+ vfe_dev->vfe_clk_info[i].clk_rate = INIT_RATE;
+ /* set no memory retention */
+ if (strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "camss_vfe_clk") == 0 ||
+ strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "camss_csi_vfe_clk") == 0 ||
+ strcmp(vfe_dev->vfe_clk_info[i].clk_name,
+ "camss_vfe_vbif_axi_clk") == 0) {
+ msm_camera_set_clk_flags(vfe_dev->vfe_clk[i],
+ CLKFLAG_NORETAIN_MEM);
+ msm_camera_set_clk_flags(vfe_dev->vfe_clk[i],
+ CLKFLAG_NORETAIN_PERIPH);
+ }
+ }
+ return 0;
+}
+
+static int msm_vfe48_get_clk_rates(struct vfe_device *vfe_dev,
+ struct msm_isp_clk_rates *rates)
+{
+ rates->svs_rate = vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_SVS]
+ [vfe_dev->hw_info->vfe_clk_idx];
+ rates->nominal_rate = vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx];
+ rates->high_rate = vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO]
+ [vfe_dev->hw_info->vfe_clk_idx];
+
+ return 0;
+}
+
+static int msm_vfe48_get_regulators(struct vfe_device *vfe_dev)
+{
+ return msm_camera_get_regulator_info(vfe_dev->pdev,
+ &vfe_dev->regulator_info, &vfe_dev->vfe_num_regulators);
+}
+
+static void msm_vfe48_put_regulators(struct vfe_device *vfe_dev)
+{
+ msm_camera_put_regulators(vfe_dev->pdev,
+ &vfe_dev->regulator_info, vfe_dev->vfe_num_regulators);
+ vfe_dev->vfe_num_regulators = 0;
+}
+
+static void msm_vfe48_get_bus_err_mask(struct vfe_device *vfe_dev,
+ uint32_t *bus_err, uint32_t *irq_status1)
+{
+ *bus_err = msm_camera_io_r(vfe_dev->vfe_base + 0xC94);
+
+ *bus_err &= ~vfe_dev->bus_err_ign_mask;
+ if (*bus_err == 0)
+ *irq_status1 &= ~(1 << 4);
+}
+
+static void msm_vfe48_set_bus_err_ign_mask(struct vfe_device *vfe_dev,
+ int wm, int enable)
+{
+ if (enable)
+ vfe_dev->bus_err_ign_mask |= (1 << wm);
+ else
+ vfe_dev->bus_err_ign_mask &= ~(1 << wm);
+}
+
+void msm_vfe48_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0, stats_burst_len;
+ uint32_t ub_size[VFE47_NUM_STATS_TYPE] = {
+ 16, /* MSM_ISP_STATS_HDR_BE */
+ 16, /* MSM_ISP_STATS_BG */
+ 16, /* MSM_ISP_STATS_BF */
+ 16, /* MSM_ISP_STATS_HDR_BHIST */
+ 16, /* MSM_ISP_STATS_RS */
+ 16, /* MSM_ISP_STATS_CS */
+ 16, /* MSM_ISP_STATS_IHIST */
+ 16, /* MSM_ISP_STATS_BHIST */
+ 16, /* MSM_ISP_STATS_AEC_BG */
+ };
+
+ stats_burst_len = VFE48_STATS_BURST_LEN;
+ ub_offset = VFE48_UB_SIZE_VFE;
+
+ for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) {
+ ub_offset -= ub_size[i];
+ msm_camera_io_w(stats_burst_len << 30 |
+ ub_offset << 16 | (ub_size[i] - 1),
+ vfe_dev->vfe_base + VFE48_STATS_BASE(i) + 0x14);
+ }
+}
+
+uint32_t msm_vfe48_get_ub_size(struct vfe_device *vfe_dev)
+{
+ return MSM_ISP48_TOTAL_IMAGE_UB_VFE;
+}
+
+
+
+struct msm_vfe_hardware_info vfe48_hw_info = {
+ .num_iommu_ctx = 1,
+ .num_iommu_secure_ctx = 0,
+ .vfe_clk_idx = VFE48_SRC_CLK_DTSI_IDX,
+ .runtime_axi_update = 1,
+ .min_ib = 100000000,
+ .min_ab = 100000000,
+ .vfe_ops = {
+ .irq_ops = {
+ .read_and_clear_irq_status =
+ msm_vfe47_read_and_clear_irq_status,
+ .read_irq_status = msm_vfe47_read_irq_status,
+ .process_camif_irq = msm_vfe47_process_input_irq,
+ .process_reset_irq = msm_vfe47_process_reset_irq,
+ .process_halt_irq = msm_vfe47_process_halt_irq,
+ .process_reg_update = msm_vfe47_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ .process_epoch_irq = msm_vfe47_process_epoch_irq,
+ .config_irq = msm_vfe47_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe47_axi_reload_wm,
+ .enable_wm = msm_vfe48_axi_enable_wm,
+ .cfg_io_format = msm_vfe47_cfg_io_format,
+ .cfg_comp_mask = msm_vfe47_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe47_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe47_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe47_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe47_cfg_framedrop,
+ .clear_framedrop = msm_vfe47_clear_framedrop,
+ .cfg_wm_reg = msm_vfe47_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe47_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe47_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe47_axi_clear_wm_xbar_reg,
+ .cfg_ub = msm_vfe47_cfg_axi_ub,
+ .read_wm_ping_pong_addr =
+ msm_vfe47_read_wm_ping_pong_addr,
+ .update_ping_pong_addr =
+ msm_vfe47_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe47_get_comp_mask,
+ .get_wm_mask = msm_vfe47_get_wm_mask,
+ .get_pingpong_status = msm_vfe47_get_pingpong_status,
+ .halt = msm_vfe47_axi_halt,
+ .restart = msm_vfe47_axi_restart,
+ .update_cgc_override =
+ msm_vfe47_axi_update_cgc_override,
+ .ub_reg_offset = msm_vfe47_ub_reg_offset,
+ .get_ub_size = msm_vfe48_get_ub_size,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe47_reg_update,
+ .cfg_input_mux = msm_vfe47_cfg_input_mux,
+ .update_camif_state = msm_vfe47_update_camif_state,
+ .start_fetch_eng = msm_vfe47_start_fetch_engine,
+ .cfg_rdi_reg = msm_vfe47_cfg_rdi_reg,
+ .reset_hw = msm_vfe47_reset_hardware,
+ .init_hw = msm_vfe47_init_hardware,
+ .init_hw_reg = msm_vfe47_init_hardware_reg,
+ .clear_status_reg = msm_vfe47_clear_status_reg,
+ .release_hw = msm_vfe47_release_hardware,
+ .get_error_mask = msm_vfe47_get_error_mask,
+ .get_overflow_mask = msm_vfe47_get_overflow_mask,
+ .get_rdi_wm_mask = msm_vfe47_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe47_get_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe47_get_halt_restart_mask,
+ .process_error_status = msm_vfe47_process_error_status,
+ .is_module_cfg_lock_needed =
+ msm_vfe47_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = msm_isp47_ahb_clk_cfg,
+ .start_fetch_eng_multi_pass =
+ msm_vfe47_start_fetch_engine_multi_pass,
+ .set_halt_restart_mask =
+ msm_vfe47_set_halt_restart_mask,
+ .set_bus_err_ign_mask = msm_vfe48_set_bus_err_ign_mask,
+ .get_bus_err_mask = msm_vfe48_get_bus_err_mask,
+ },
+ .stats_ops = {
+ .get_stats_idx = msm_vfe47_get_stats_idx,
+ .check_streams = msm_vfe47_stats_check_streams,
+ .cfg_comp_mask = msm_vfe47_stats_cfg_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe47_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe47_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe47_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe47_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe48_stats_cfg_ub,
+ .enable_module = msm_vfe47_stats_enable_module,
+ .update_ping_pong_addr =
+ msm_vfe47_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe47_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe47_stats_get_wm_mask,
+ .get_frame_id = msm_vfe47_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe47_get_pingpong_status,
+ .update_cgc_override =
+ msm_vfe47_stats_update_cgc_override,
+ .enable_stats_wm = msm_vfe48_enable_stats_wm,
+ },
+ .platform_ops = {
+ .get_platform_data = msm_vfe47_get_platform_data,
+ .enable_regulators = msm_vfe47_enable_regulators,
+ .get_regulators = msm_vfe48_get_regulators,
+ .put_regulators = msm_vfe48_put_regulators,
+ .enable_clks = msm_vfe47_enable_clks,
+ .update_bw = msm_vfe48_update_bandwidth,
+ .init_bw_mgr = msm_vfe48_init_bandwidth_mgr,
+ .deinit_bw_mgr = msm_vfe48_deinit_bandwidth_mgr,
+ .get_clks = msm_vfe48_get_clks,
+ .put_clks = msm_vfe48_put_clks,
+ .set_clk_rate = msm_vfe47_set_clk_rate,
+ .get_max_clk_rate = msm_vfe47_get_max_clk_rate,
+ .get_clk_rates = msm_vfe48_get_clk_rates,
+ },
+ },
+ .dmi_reg_offset = 0xC2C,
+ .axi_hw_info = &msm_vfe48_axi_hw_info,
+ .stats_hw_info = &msm_vfe48_stats_hw_info,
+};
+EXPORT_SYMBOL(vfe48_hw_info);
+
+static const struct of_device_id msm_vfe48_dt_match[] = {
+ {
+ .compatible = "qcom,vfe48",
+ .data = &vfe48_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe48_dt_match);
+
+static struct platform_driver vfe48_driver = {
+ .probe = vfe_hw_probe,
+ .driver = {
+ .name = "msm_vfe48",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe48_dt_match,
+ },
+};
+
+static int __init msm_vfe47_init_module(void)
+{
+ return platform_driver_register(&vfe48_driver);
+}
+
+static void __exit msm_vfe47_exit_module(void)
+{
+ platform_driver_unregister(&vfe48_driver);
+}
+
+module_init(msm_vfe47_init_module);
+module_exit(msm_vfe47_exit_module);
+MODULE_DESCRIPTION("MSM VFE48 driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.h
new file mode 100644
index 0000000..cc052bc
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2016, 2018, 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_ISP48_H__
+#define __MSM_ISP48_H__
+
+extern struct msm_vfe_hardware_info vfe48_hw_info;
+
+enum msm_vfe_clk_rates {
+ MSM_VFE_CLK_RATE_SVS = 0,
+ MSM_VFE_CLK_RATE_NOMINAL = 1,
+ MSM_VFE_CLK_RATE_TURBO = 2,
+ MSM_VFE_MAX_CLK_RATES = 3,
+};
+
+#define MSM_VFE48_HW_VERSION 0x8
+#define MSM_VFE48_HW_VERSION_SHIFT 28
+#define MSM_VFE48_HW_VERSION_MASK 0xF
+
+static inline int msm_vfe_is_vfe48(struct vfe_device *vfe_dev)
+{
+ return (((vfe_dev->vfe_hw_version >> MSM_VFE48_HW_VERSION_SHIFT) &
+ MSM_VFE48_HW_VERSION_MASK) == MSM_VFE48_HW_VERSION);
+}
+
+void msm_vfe48_stats_cfg_ub(struct vfe_device *vfe_dev);
+uint32_t msm_vfe48_get_ub_size(struct vfe_device *vfe_dev);
+
+
+#endif /* __MSM_ISP48_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
new file mode 100644
index 0000000..0b7bb8d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -0,0 +1,4360 @@
+/* Copyright (c) 2013-2018, 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 <asm/div64.h>
+#include "msm_isp_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp48.h"
+
+#define HANDLE_TO_IDX(handle) (handle & 0xFF)
+#define ISP_SOF_DEBUG_COUNT 0
+
+static void msm_isp_reload_ping_pong_offset(
+ struct msm_vfe_axi_stream *stream_info);
+
+static void __msm_isp_axi_stream_update(
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_isp_timestamp *ts);
+
+static int msm_isp_update_stream_bandwidth(
+ struct msm_vfe_axi_stream *stream_info, int enable);
+
+#define DUAL_VFE_AND_VFE1(s, v) ((s->stream_src < RDI_INTF_0) && \
+ v->is_split && vfe_dev->pdev->id == ISP_VFE1)
+
+#define RDI_OR_NOT_DUAL_VFE(v, s) (!v->is_split || \
+ ((s->stream_src >= RDI_INTF_0) && \
+ (stream_info->stream_src <= RDI_INTF_2)))
+
+static int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
+ 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)
+{
+ uint32_t i = 0;
+ int rc = 0;
+
+ if (stream_info->state != AVAILABLE) {
+ pr_err("%s:%d invalid state %d expected %d\n",
+ __func__, __LINE__, stream_info->state,
+ AVAILABLE);
+ return -EINVAL;
+ }
+
+ if (stream_info->num_isp == 0) {
+ stream_info->session_id = stream_cfg_cmd->session_id;
+ stream_info->stream_id = stream_cfg_cmd->stream_id;
+ stream_info->buf_divert = stream_cfg_cmd->buf_divert;
+ stream_info->stream_src = stream_cfg_cmd->stream_src;
+ stream_info->controllable_output =
+ stream_cfg_cmd->controllable_output;
+ stream_info->activated_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ if (stream_cfg_cmd->controllable_output)
+ stream_cfg_cmd->frame_skip_pattern = SKIP_ALL;
+ INIT_LIST_HEAD(&stream_info->request_q);
+ } else {
+ /* check if the stream has been added for the vfe-device */
+ if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: stream %pK/%x is already added for vfe dev %d vfe_mask %x\n",
+ __func__, stream_info, stream_info->stream_id,
+ vfe_dev->pdev->id, stream_info->vfe_mask);
+ return -EINVAL;
+ }
+ if (stream_info->session_id != stream_cfg_cmd->session_id) {
+ pr_err("%s: dual stream session id mismatch %d/%d\n",
+ __func__, stream_info->session_id,
+ stream_cfg_cmd->session_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_id != stream_cfg_cmd->stream_id) {
+ pr_err("%s: dual stream stream id mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_cfg_cmd->stream_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->controllable_output !=
+ stream_cfg_cmd->controllable_output) {
+ pr_err("%s: dual stream controllable_op mismatch %d/%d\n",
+ __func__, stream_info->controllable_output,
+ stream_cfg_cmd->controllable_output);
+ rc = -EINVAL;
+ }
+ if (stream_info->buf_divert != stream_cfg_cmd->buf_divert) {
+ pr_err("%s: dual stream buf_divert mismatch %d/%d\n",
+ __func__, stream_info->buf_divert,
+ stream_cfg_cmd->buf_divert);
+ rc = -EINVAL;
+ }
+ if (rc)
+ return rc;
+ }
+ stream_info->vfe_dev[stream_info->num_isp] = vfe_dev;
+ stream_info->num_isp++;
+
+ 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 | stream_info->stream_src;
+
+ ISP_DBG("%s: vfe %d handle %x\n", __func__, vfe_dev->pdev->id,
+ stream_cfg_cmd->axi_stream_handle);
+
+ stream_info->stream_handle[stream_info->num_isp - 1] =
+ stream_cfg_cmd->axi_stream_handle;
+ stream_info->vfe_mask |= (1 << vfe_dev->pdev->id);
+
+ if (!vfe_dev->is_split || stream_cfg_cmd->stream_src >= RDI_INTF_0 ||
+ stream_info->num_isp == MAX_VFE) {
+ stream_info->state = INACTIVE;
+
+ for (i = 0; i < MSM_ISP_COMP_IRQ_MAX; i++)
+ stream_info->composite_irq[i] = 0;
+ }
+ return 0;
+}
+
+static void msm_isp_axi_destroy_stream(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info)
+{
+ int k;
+ int j;
+ int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ /*
+ * For the index being removed, shift everything to it's right by 1
+ * so that the index being removed becomes the last index
+ */
+ for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) {
+ stream_info->vfe_dev[i] = stream_info->vfe_dev[k];
+ stream_info->stream_handle[i] = stream_info->stream_handle[k];
+ stream_info->bandwidth[i] = stream_info->bandwidth[k];
+ stream_info->max_width[i] = stream_info->max_width[k];
+ stream_info->comp_mask_index[i] =
+ stream_info->comp_mask_index[k];
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->plane_cfg[i][j] =
+ stream_info->plane_cfg[k][j];
+ stream_info->wm[i][j] = stream_info->wm[k][j];
+ }
+ }
+
+ stream_info->num_isp--;
+ stream_info->vfe_dev[stream_info->num_isp] = NULL;
+ stream_info->stream_handle[stream_info->num_isp] = 0;
+ stream_info->bandwidth[stream_info->num_isp] = 0;
+ stream_info->max_width[stream_info->num_isp] = 0;
+ stream_info->comp_mask_index[stream_info->num_isp] = -1;
+ stream_info->vfe_mask &= ~(1 << vfe_dev->pdev->id);
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->wm[stream_info->num_isp][j] = -1;
+ memset(&stream_info->plane_cfg[stream_info->num_isp][j],
+ 0, sizeof(
+ stream_info->plane_cfg[stream_info->num_isp][j]));
+ }
+
+ if (stream_info->num_isp == 0) {
+ /* release the bufq */
+ for (k = 0; k < VFE_BUF_QUEUE_MAX; k++)
+ stream_info->bufq_handle[k] = 0;
+ stream_info->vfe_mask = 0;
+ stream_info->state = AVAILABLE;
+ }
+}
+
+static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int rc = -1, i;
+ int vfe_idx;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ 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_SBGGR10DPCM6:
+ case V4L2_PIX_FMT_SGBRG10DPCM6:
+ case V4L2_PIX_FMT_SGRBG10DPCM6:
+ case V4L2_PIX_FMT_SRGGB10DPCM6:
+ case V4L2_PIX_FMT_SBGGR10DPCM8:
+ case V4L2_PIX_FMT_SGBRG10DPCM8:
+ case V4L2_PIX_FMT_SGRBG10DPCM8:
+ case V4L2_PIX_FMT_SRGGB10DPCM8:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ case V4L2_PIX_FMT_META10:
+ case V4L2_PIX_FMT_GREY:
+ stream_info->num_planes = 1;
+ stream_info->format_factor = ISP_Q2;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ stream_info->num_planes = 2;
+ stream_info->format_factor = 1.5 * ISP_Q2;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ stream_info->num_planes = 2;
+ stream_info->format_factor = 2 * ISP_Q2;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ stream_info->num_planes = 2;
+ stream_info->format_factor = 3 * ISP_Q2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__,
+ stream_cfg_cmd->output_format);
+ 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->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;
+ }
+
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ stream_info->plane_cfg[vfe_idx][i] =
+ stream_cfg_cmd->plane_cfg[i];
+ stream_info->max_width[vfe_idx] =
+ max(stream_info->max_width[vfe_idx],
+ stream_cfg_cmd->plane_cfg[i].output_width);
+ }
+
+ stream_info->output_format = stream_cfg_cmd->output_format;
+ stream_info->runtime_output_format = stream_info->output_format;
+ stream_info->stream_src = stream_cfg_cmd->stream_src;
+ stream_info->frame_based = stream_cfg_cmd->frame_base;
+ return 0;
+}
+
+static uint32_t msm_isp_axi_get_plane_size(
+ struct msm_vfe_axi_stream *stream_info, int vfe_idx, int plane_idx)
+{
+ uint32_t size = 0;
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ stream_info->plane_cfg[vfe_idx];
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ case V4L2_PIX_FMT_GREY:
+ 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:
+ case V4L2_PIX_FMT_SBGGR10DPCM6:
+ case V4L2_PIX_FMT_SGBRG10DPCM6:
+ case V4L2_PIX_FMT_SGRBG10DPCM6:
+ case V4L2_PIX_FMT_SRGGB10DPCM6:
+ case V4L2_PIX_FMT_SBGGR10DPCM8:
+ case V4L2_PIX_FMT_SGBRG10DPCM8:
+ case V4L2_PIX_FMT_SGRBG10DPCM8:
+ case V4L2_PIX_FMT_SRGGB10DPCM8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_META10:
+ /* 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:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ 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;
+ break;
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ 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;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__,
+ stream_info->output_format);
+ break;
+ }
+ return size;
+}
+
+static void msm_isp_axi_reserve_wm(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int i, j;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ 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_info->stream_handle[vfe_idx];
+ axi_data->wm_image_size[j] =
+ msm_isp_axi_get_plane_size(
+ stream_info, vfe_idx, i);
+ axi_data->num_used_wm++;
+ break;
+ }
+ }
+ ISP_DBG("%s vfe %d stream_handle %x wm %d\n", __func__,
+ vfe_dev->pdev->id,
+ stream_info->stream_handle[vfe_idx], j);
+ stream_info->wm[vfe_idx][i] = j;
+ /* setup var to ignore bus error from RDI wm */
+ if (stream_info->stream_src >= RDI_INTF_0) {
+ if (vfe_dev->hw_info->vfe_ops.core_ops.
+ set_bus_err_ign_mask)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ set_bus_err_ign_mask(vfe_dev, j, 1);
+ }
+ }
+}
+
+void msm_isp_axi_free_wm(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ axi_data->free_wm[stream_info->wm[vfe_idx][i]] = 0;
+ axi_data->num_used_wm--;
+ if (stream_info->stream_src >= RDI_INTF_0) {
+ if (vfe_dev->hw_info->vfe_ops.core_ops.
+ set_bus_err_ign_mask)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ set_bus_err_ign_mask(vfe_dev,
+ stream_info->wm[vfe_idx][i], 0);
+ }
+ }
+ if (stream_info->stream_src <= IDEAL_RAW)
+ axi_data->num_pix_stream++;
+ else if (stream_info->stream_src < VFE_AXI_SRC_MAX)
+ axi_data->num_rdi_stream++;
+}
+
+static void msm_isp_axi_reserve_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ uint8_t comp_mask = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ comp_mask |= 1 << stream_info->wm[vfe_idx][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_info->stream_handle[vfe_idx];
+ axi_data->composite_info[i].
+ stream_composite_mask = comp_mask;
+ axi_data->num_used_composite_mask++;
+ break;
+ }
+ }
+ stream_info->comp_mask_index[vfe_idx] = i;
+}
+
+static void msm_isp_axi_free_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;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]].
+ stream_composite_mask = 0;
+ axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]].
+ stream_handle = 0;
+ axi_data->num_used_composite_mask--;
+}
+
+/**
+ * msm_isp_cfg_framedrop_reg() - Program the period and pattern
+ * @stream_info: The stream for which programming is done
+ *
+ * This function calculates the period and pattern to be configured
+ * for the stream based on the current frame id of the stream's input
+ * source and the initial framedrops.
+ *
+ * Returns void.
+ */
+static void msm_isp_cfg_framedrop_reg(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
+ uint32_t runtime_init_frame_drop;
+ uint32_t framedrop_pattern = 0;
+ uint32_t framedrop_period = MSM_VFE_STREAM_STOP_PERIOD;
+ enum msm_vfe_input_src frame_src = SRC_TO_INTF(stream_info->stream_src);
+ int i;
+
+ if (vfe_dev->axi_data.src_info[frame_src].frame_id >=
+ stream_info->init_frame_drop)
+ runtime_init_frame_drop = 0;
+ else
+ runtime_init_frame_drop = stream_info->init_frame_drop -
+ vfe_dev->axi_data.src_info[frame_src].frame_id;
+
+ if (!runtime_init_frame_drop)
+ framedrop_period = stream_info->current_framedrop_period;
+
+ if (framedrop_period != MSM_VFE_STREAM_STOP_PERIOD)
+ framedrop_pattern = 0x1;
+
+ if (WARN_ON(framedrop_period == 0))
+ pr_err("%s framedrop_period is 0", __func__);
+
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
+ vfe_dev, stream_info, framedrop_pattern,
+ framedrop_period);
+ }
+
+ ISP_DBG("%s: stream %x src %x framedrop pattern %x period %u\n",
+ __func__,
+ stream_info->stream_handle[0], stream_info->stream_src,
+ framedrop_pattern, framedrop_period);
+
+ stream_info->requested_framedrop_period = framedrop_period;
+}
+
+static int msm_isp_composite_irq(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ enum msm_isp_comp_irq_types irq)
+{
+ /* interrupt recv on same vfe w/o recv on other vfe */
+ if (stream_info->composite_irq[irq] & (1 << vfe_dev->pdev->id)) {
+ msm_isp_dump_ping_pong_mismatch(vfe_dev);
+ pr_err("%s: irq %d out of sync for dual vfe on vfe %d\n",
+ __func__, irq, vfe_dev->pdev->id);
+ return -EINVAL;
+ }
+
+ stream_info->composite_irq[irq] |= (1 << vfe_dev->pdev->id);
+ if (stream_info->composite_irq[irq] != stream_info->vfe_mask)
+ return 1;
+
+ stream_info->composite_irq[irq] = 0;
+
+ return 0;
+}
+
+/**
+ * msm_isp_update_framedrop_reg() - Update frame period pattern on h/w
+ * @stream_info: Stream for which update is to be performed
+ *
+ * If the period and pattern needs to be updated for a stream then it is
+ * updated here. Updates happen if initial frame drop reaches 0 or burst
+ * streams have been provided new skip pattern from user space.
+ *
+ * Returns void
+ */
+static void msm_isp_update_framedrop_reg(struct msm_vfe_axi_stream *stream_info)
+{
+ if (stream_info->stream_type == BURST_STREAM) {
+ if (stream_info->runtime_num_burst_capture == 0 ||
+ (stream_info->runtime_num_burst_capture == 1 &&
+ stream_info->activated_framedrop_period == 1))
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ }
+
+ if (stream_info->undelivered_request_cnt > 0)
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+
+ /*
+ * re-configure the period pattern, only if it's not already
+ * set to what we want
+ */
+ if (stream_info->current_framedrop_period !=
+ stream_info->requested_framedrop_period) {
+ msm_isp_cfg_framedrop_reg(stream_info);
+ }
+}
+
+void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ enum msm_isp_comp_irq_types irq,
+ struct msm_isp_timestamp *ts)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ unsigned long flags;
+ int ret;
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ if (SRC_TO_INTF(stream_info->stream_src) !=
+ frame_src) {
+ continue;
+ }
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
+ continue;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ ret = msm_isp_composite_irq(vfe_dev, stream_info, irq);
+ if (ret) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (ret < 0) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return;
+ }
+ continue;
+ }
+
+ switch (irq) {
+ case MSM_ISP_COMP_IRQ_REG_UPD:
+ stream_info->activated_framedrop_period =
+ stream_info->requested_framedrop_period;
+ __msm_isp_axi_stream_update(stream_info, ts);
+ break;
+ case MSM_ISP_COMP_IRQ_EPOCH:
+ if (stream_info->state == ACTIVE)
+ msm_isp_update_framedrop_reg(stream_info);
+ break;
+ default:
+ WARN(1, "Invalid irq %d\n", irq);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+}
+
+/**
+ * msm_isp_reset_framedrop() - Compute the framedrop period pattern
+ * @vfe_dev: Device for which the period and pattern is computed
+ * @stream_info: The stream for the which period and pattern is generated
+ *
+ * This function is called when stream starts or is reset. It's main
+ * purpose is to setup the runtime parameters of framedrop required
+ * for the stream.
+ *
+ * Returms void
+ */
+void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t framedrop_period = 0;
+
+ stream_info->runtime_num_burst_capture = stream_info->num_burst_capture;
+
+ /**
+ * only reset none controllable output stream, since the
+ * controllable stream framedrop period will be controlled
+ * by the request frame api
+ */
+ if (!stream_info->controllable_output) {
+ framedrop_period =
+ msm_isp_get_framedrop_period(
+ stream_info->frame_skip_pattern);
+ if (stream_info->frame_skip_pattern == SKIP_ALL)
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ else
+ stream_info->current_framedrop_period =
+ framedrop_period;
+ }
+
+ msm_isp_cfg_framedrop_reg(stream_info);
+ ISP_DBG("%s: init frame drop: %d\n", __func__,
+ stream_info->init_frame_drop);
+ ISP_DBG("%s: num_burst_capture: %d\n", __func__,
+ stream_info->runtime_num_burst_capture);
+}
+
+void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *ts, struct msm_isp_sof_info *sof_info)
+{
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data;
+ int i;
+ uint32_t stream_idx;
+
+ if (!vfe_dev || !sof_info) {
+ pr_err("%s %d failed: vfe_dev %pK sof_info %pK\n", __func__,
+ __LINE__, vfe_dev, sof_info);
+ return;
+ }
+ sof_info->regs_not_updated = 0;
+ sof_info->reg_update_fail_mask = 0;
+ sof_info->stream_get_buf_fail_mask = 0;
+
+ axi_data = &vfe_dev->axi_data;
+
+ for (i = 0; i < RDI_INTF_0; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ i);
+ stream_idx = HANDLE_TO_IDX(stream_info->stream_handle[0]);
+
+ /*
+ * Process drop only if controllable ACTIVE PIX stream &&
+ * reg_not_updated
+ * OR stream is in RESUMING state.
+ * Other cases there is no drop to report, so continue.
+ */
+ if (!((stream_info->state == ACTIVE &&
+ stream_info->controllable_output &&
+ (SRC_TO_INTF(stream_info->stream_src) ==
+ VFE_PIX_0)) ||
+ stream_info->state == RESUMING))
+ continue;
+
+ if (stream_info->controllable_output &&
+ !vfe_dev->reg_updated) {
+ if (stream_info->undelivered_request_cnt) {
+ /* report that registers are not updated
+ * and return empty buffer for controllable
+ * outputs
+ */
+ sof_info->regs_not_updated =
+ !vfe_dev->reg_updated;
+ pr_err("Drop frame no reg update\n");
+ if (msm_isp_drop_frame(vfe_dev, stream_info, ts,
+ sof_info)) {
+ pr_err("drop frame failed\n");
+ }
+ }
+ }
+
+ if (stream_info->state == RESUMING &&
+ !stream_info->controllable_output) {
+ ISP_DBG("%s: axi_updating_mask strm_id %x frm_id %d\n",
+ __func__, stream_idx, vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(stream_info->stream_src)]
+ .frame_id);
+ sof_info->axi_updating_mask |=
+ 1 << stream_idx;
+ }
+ }
+
+ vfe_dev->reg_updated = 0;
+
+ /* report frame drop per stream */
+ if (vfe_dev->error_info.framedrop_flag) {
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) {
+ if (vfe_dev->error_info.stream_framedrop_count[i]) {
+ ISP_DBG("%s: get buf failed i %d\n", __func__,
+ i);
+ sof_info->stream_get_buf_fail_mask |= (1 << i);
+ vfe_dev->error_info.
+ stream_framedrop_count[i] = 0;
+ }
+ }
+ vfe_dev->error_info.framedrop_flag = 0;
+ }
+}
+
+static void msm_isp_sync_dual_cam_frame_id(
+ struct vfe_device *vfe_dev,
+ struct master_slave_resource_info *ms_res,
+ enum msm_vfe_input_src frame_src,
+ struct msm_isp_timestamp *ts)
+{
+ struct msm_vfe_src_info *src_info =
+ &vfe_dev->axi_data.src_info[frame_src];
+ int i;
+ uint32_t frame_id = src_info->frame_id;
+ uint32_t master_time = 0, current_time;
+
+ if (src_info->dual_hw_ms_info.sync_state ==
+ ms_res->dual_sync_mode) {
+ (frame_src == VFE_PIX_0) ? src_info->frame_id +=
+ vfe_dev->axi_data.src_info[frame_src].
+ sof_counter_step :
+ src_info->frame_id++;
+ return;
+ }
+
+ /* find highest frame id */
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (src_info == ms_res->src_info[i] ||
+ ms_res->src_info[i]->active == 0)
+ continue;
+ if (frame_id >= ms_res->src_info[i]->frame_id)
+ continue;
+ frame_id = ms_res->src_info[i]->frame_id;
+ master_time = ms_res->src_info[i]->
+ dual_hw_ms_info.sof_info.mono_timestamp_ms;
+ }
+ /* copy highest frame id to the intf based on sof delta */
+ current_time = ts->buf_time.tv_sec * 1000 +
+ ts->buf_time.tv_usec / 1000;
+
+ if (current_time > master_time &&
+ (current_time - master_time) > ms_res->sof_delta_threshold) {
+ if (frame_src == VFE_PIX_0)
+ frame_id += vfe_dev->axi_data.src_info[frame_src].
+ sof_counter_step;
+ else
+ frame_id += 1;
+ } else {
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (src_info == ms_res->src_info[i] ||
+ ((1 << ms_res->src_info[i]->
+ dual_hw_ms_info.index) &
+ ms_res->active_src_mask) == 0)
+ continue;
+ if (ms_res->src_info[i]->frame_id == frame_id)
+ ms_res->src_sof_mask |= (1 <<
+ ms_res->src_info[i]->dual_hw_ms_info.index);
+ }
+ }
+ ms_res->active_src_mask |= (1 << src_info->dual_hw_ms_info.index);
+ src_info->frame_id = frame_id;
+ src_info->dual_hw_ms_info.sync_state = MSM_ISP_DUAL_CAM_SYNC;
+}
+
+void msm_isp_increment_frame_id(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts)
+{
+ struct msm_vfe_src_info *src_info = NULL;
+ struct msm_vfe_sof_info *sof_info = NULL;
+ enum msm_vfe_dual_hw_type dual_hw_type;
+ enum msm_vfe_dual_hw_ms_type ms_type;
+ unsigned long flags;
+ int i;
+ struct master_slave_resource_info *ms_res =
+ &vfe_dev->common_data->ms_resource;
+
+ spin_lock_irqsave(&vfe_dev->common_data->common_dev_data_lock, flags);
+ dual_hw_type =
+ vfe_dev->axi_data.src_info[frame_src].dual_hw_type;
+ ms_type =
+ vfe_dev->axi_data.src_info[frame_src].
+ dual_hw_ms_info.dual_hw_ms_type;
+
+ src_info = &vfe_dev->axi_data.src_info[frame_src];
+ if (dual_hw_type == DUAL_HW_MASTER_SLAVE) {
+ msm_isp_sync_dual_cam_frame_id(vfe_dev, ms_res, frame_src, ts);
+ if (src_info->dual_hw_ms_info.sync_state ==
+ MSM_ISP_DUAL_CAM_SYNC) {
+ /*
+ * for dual hw check that we recv sof from all
+ * linked intf
+ */
+ if (ms_res->src_sof_mask & (1 <<
+ src_info->dual_hw_ms_info.index)) {
+ pr_err_ratelimited("Frame out of sync on vfe %d\n",
+ vfe_dev->pdev->id);
+ /*
+ * set this isp as async mode to force
+ *it sync again at the next sof
+ */
+ src_info->dual_hw_ms_info.sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ /*
+ * set the other isp as async mode to force
+ * it sync again at the next sof
+ */
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (src_info == ms_res->src_info[i] ||
+ ms_res->src_info[i]->
+ active == 0)
+ continue;
+ ms_res->src_info[i]->dual_hw_ms_info.
+ sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ }
+ }
+ ms_res->src_sof_mask |= (1 <<
+ src_info->dual_hw_ms_info.index);
+ if (ms_res->active_src_mask == ms_res->src_sof_mask)
+ ms_res->src_sof_mask = 0;
+ }
+ sof_info = &vfe_dev->axi_data.src_info[frame_src].
+ dual_hw_ms_info.sof_info;
+ sof_info->frame_id = vfe_dev->axi_data.src_info[frame_src].
+ frame_id;
+ sof_info->timestamp_ms = ts->event_time.tv_sec * 1000 +
+ ts->event_time.tv_usec / 1000;
+ sof_info->mono_timestamp_ms = ts->buf_time.tv_sec * 1000 +
+ ts->buf_time.tv_usec / 1000;
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ if (frame_src == VFE_PIX_0) {
+ vfe_dev->axi_data.src_info[frame_src].frame_id +=
+ vfe_dev->axi_data.src_info[frame_src].
+ sof_counter_step;
+ ISP_DBG("%s: vfe %d sof_step %d\n", __func__,
+ vfe_dev->pdev->id,
+ vfe_dev->axi_data.src_info[frame_src].
+ sof_counter_step);
+ } else {
+ vfe_dev->axi_data.src_info[frame_src].frame_id++;
+ }
+ }
+
+ if (frame_src == VFE_PIX_0) {
+ vfe_dev->isp_page->kernel_sofid =
+ vfe_dev->axi_data.src_info[frame_src].frame_id;
+ if (!src_info->frame_id &&
+ !src_info->reg_update_frame_id &&
+ ((src_info->frame_id -
+ src_info->reg_update_frame_id) >
+ (MAX_REG_UPDATE_THRESHOLD *
+ src_info->sof_counter_step))) {
+ pr_err("%s:%d reg_update not received for %d frames\n",
+ __func__, __LINE__,
+ src_info->frame_id -
+ src_info->reg_update_frame_id);
+
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_REG_UPDATE_MISSING);
+ }
+ }
+}
+
+static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src)
+{
+ struct msm_vfe_axi_stream *pd_stream_info = NULL;
+ uint32_t pingpong_status = 0, pingpong_bit = 0;
+ struct msm_isp_buffer *done_buf = NULL;
+ int vfe_idx = -1;
+ unsigned long flags;
+
+ if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2)
+ return;
+
+ pd_stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ RDI_INTF_0 + frame_src - VFE_RAW_0);
+
+ if (pd_stream_info && (pd_stream_info->state == ACTIVE) &&
+ (pd_stream_info->rdi_input_type ==
+ MSM_CAMERA_RDI_PDAF)) {
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ vfe_dev, pd_stream_info);
+ pingpong_status = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_pingpong_status(vfe_dev);
+ pingpong_bit = ((pingpong_status >>
+ pd_stream_info->wm[vfe_idx][0]) & 0x1);
+ done_buf = pd_stream_info->buf[pingpong_bit];
+ spin_lock_irqsave(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ if (done_buf)
+ vfe_dev->common_data->pd_buf_idx = done_buf->buf_idx;
+ else
+ vfe_dev->common_data->pd_buf_idx = 0xF;
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ }
+}
+
+void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts)
+{
+ struct msm_isp_event_data event_data;
+ struct msm_vfe_sof_info *sof_info = NULL, *self_sof = NULL;
+ enum msm_vfe_dual_hw_ms_type ms_type;
+ unsigned long flags;
+
+ memset(&event_data, 0, sizeof(event_data));
+
+ switch (event_type) {
+ case ISP_EVENT_SOF:
+ if (frame_src == VFE_PIX_0) {
+ if (vfe_dev->isp_sof_debug < ISP_SOF_DEBUG_COUNT)
+ pr_err("%s: PIX0 frame id: %u\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ vfe_dev->isp_sof_debug++;
+ } else if (frame_src == VFE_RAW_0) {
+ if (vfe_dev->isp_raw0_debug < ISP_SOF_DEBUG_COUNT)
+ pr_err("%s: RAW_0 frame id: %u\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_RAW_0].frame_id);
+ vfe_dev->isp_raw0_debug++;
+ } else if (frame_src == VFE_RAW_1) {
+ if (vfe_dev->isp_raw1_debug < ISP_SOF_DEBUG_COUNT)
+ pr_err("%s: RAW_1 frame id: %u\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_RAW_1].frame_id);
+ vfe_dev->isp_raw1_debug++;
+ } else if (frame_src == VFE_RAW_2) {
+ if (vfe_dev->isp_raw2_debug < ISP_SOF_DEBUG_COUNT)
+ pr_err("%s: RAW_2 frame id: %u\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_RAW_2].frame_id);
+ vfe_dev->isp_raw2_debug++;
+ }
+
+ ISP_DBG("%s: vfe %d frame_src %d\n", __func__,
+ vfe_dev->pdev->id, frame_src);
+
+ /*
+ * Cannot support dual_cam and framedrop same time in union.
+ * If need to support framedrop as well, move delta calculation
+ * to userspace
+ */
+ spin_lock_irqsave(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ if (vfe_dev->common_data->ms_resource.dual_sync_mode ==
+ MSM_ISP_DUAL_CAM_SYNC &&
+ vfe_dev->axi_data.src_info[frame_src].dual_hw_type ==
+ DUAL_HW_MASTER_SLAVE) {
+ struct master_slave_resource_info *ms_res =
+ &vfe_dev->common_data->ms_resource;
+ self_sof = &vfe_dev->axi_data.src_info[frame_src].
+ dual_hw_ms_info.sof_info;
+ ms_type = vfe_dev->axi_data.src_info[frame_src].
+ dual_hw_ms_info.dual_hw_ms_type;
+ /* only send back time delta for primatry intf */
+ if (ms_res->primary_slv_idx > 0 &&
+ ms_type == MS_TYPE_MASTER)
+ sof_info = &ms_res->src_info[
+ ms_res->primary_slv_idx]->
+ dual_hw_ms_info.sof_info;
+ if (ms_type != MS_TYPE_MASTER &&
+ ms_res->master_index > 0)
+ sof_info = &ms_res->src_info[
+ ms_res->master_index]->
+ dual_hw_ms_info.sof_info;
+ if (sof_info) {
+ event_data.u.sof_info.ms_delta_info.
+ delta[0] =
+ self_sof->mono_timestamp_ms -
+ sof_info->mono_timestamp_ms;
+ event_data.u.sof_info.ms_delta_info.
+ num_delta_info = 1;
+ }
+ }
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ if (frame_src == VFE_PIX_0)
+ msm_isp_check_for_output_error(vfe_dev, ts,
+ &event_data.u.sof_info);
+ /*
+ * Get and store the buf idx for PD stats
+ * this is to send the PD stats buffer address
+ * in BF stats done.
+ */
+ msm_isp_update_pd_stats_idx(vfe_dev, frame_src);
+ break;
+
+ default:
+ break;
+ }
+
+ event_data.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id;
+ event_data.timestamp = ts->event_time;
+ event_data.mono_timestamp = ts->buf_time;
+ msm_isp_send_event(vfe_dev, event_type | frame_src, &event_data);
+}
+
+/**
+ * msm_isp_calculate_framedrop() - Setup frame period and pattern
+ * @vfe_dev: vfe device.
+ * @stream_cfg_cmd: User space input parameter for perion/pattern.
+ *
+ * Initialize the h/w stream framedrop period and pattern sent
+ * by user space.
+ *
+ * Returns 0 on success else error code.
+ */
+static int msm_isp_calculate_framedrop(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ uint32_t framedrop_period = 0;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+
+ if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)
+ < VFE_AXI_SRC_MAX) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
+ } else {
+ pr_err("%s: Invalid stream handle", __func__);
+ return -EINVAL;
+ }
+ if (!stream_info) {
+ pr_err("%s: Stream info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_cfg_cmd->frame_skip_pattern);
+ stream_info->frame_skip_pattern =
+ stream_cfg_cmd->frame_skip_pattern;
+ if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL)
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ else
+ stream_info->current_framedrop_period = framedrop_period;
+
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+
+ if (stream_cfg_cmd->burst_count > 0) {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->num_burst_capture =
+ stream_cfg_cmd->burst_count;
+ } else {
+ stream_info->stream_type = CONTINUOUS_STREAM;
+ }
+ return 0;
+}
+
+static void msm_isp_calculate_bandwidth(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int bpp = 0;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_axi_shared_data *axi_data;
+ int i;
+
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ axi_data = &vfe_dev->axi_data;
+ if (stream_info->stream_src < RDI_INTF_0) {
+ stream_info->bandwidth[i] =
+ (vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate /
+ axi_data->src_info[VFE_PIX_0].width) *
+ stream_info->max_width[i];
+ stream_info->bandwidth[i] =
+ (unsigned long)stream_info->bandwidth[i] *
+ stream_info->format_factor / ISP_Q2;
+ } else {
+ int rdi = SRC_TO_INTF(stream_info->stream_src);
+
+ bpp = msm_isp_get_bit_per_pixel(
+ stream_info->output_format);
+ if (rdi < VFE_SRC_MAX) {
+ stream_info->bandwidth[i] =
+ (vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate /
+ 8) * bpp;
+ } else {
+ pr_err("%s: Invalid rdi interface\n", __func__);
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_MSM_AVTIMER
+void msm_isp_start_avtimer(void)
+{
+ avcs_core_open();
+ avcs_core_disable_power_collapse(1);
+}
+
+void msm_isp_get_avtimer_ts(
+ struct msm_isp_timestamp *time_stamp)
+{
+ int rc = 0;
+ uint32_t avtimer_usec = 0;
+ uint64_t avtimer_tick = 0;
+
+ rc = avcs_core_query_timer(&avtimer_tick);
+ if (rc < 0) {
+ pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n",
+ __func__, rc);
+ /* In case of error return zero AVTimer Tick Value */
+ time_stamp->vt_time.tv_sec = 0;
+ time_stamp->vt_time.tv_usec = 0;
+ } else {
+ avtimer_usec = do_div(avtimer_tick, USEC_PER_SEC);
+ time_stamp->vt_time.tv_sec = (uint32_t)(avtimer_tick);
+ time_stamp->vt_time.tv_usec = avtimer_usec;
+ pr_debug("%s: AVTimer TS = %u:%u\n", __func__,
+ (uint32_t)(avtimer_tick), avtimer_usec);
+ }
+}
+#else
+void msm_isp_start_avtimer(void)
+{
+ pr_err("AV Timer is not supported\n");
+}
+
+void msm_isp_get_avtimer_ts(
+ struct msm_isp_timestamp *time_stamp)
+{
+ pr_err_ratelimited("%s: Error: AVTimer driver not available\n",
+ __func__);
+ time_stamp->vt_time.tv_sec = 0;
+ time_stamp->vt_time.tv_usec = 0;
+}
+#endif
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i = 0;
+ uint32_t io_format = 0;
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_axi_stream *stream_info;
+
+ if (stream_cfg_cmd->stream_src >= VFE_AXI_SRC_MAX) {
+ pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__,
+ stream_cfg_cmd->stream_src);
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ stream_cfg_cmd->stream_src);
+
+ rc = msm_isp_axi_create_stream(vfe_dev,
+ &vfe_dev->axi_data, stream_cfg_cmd, stream_info);
+ if (rc) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ rc = msm_isp_validate_axi_request(
+ vfe_dev, stream_info, stream_cfg_cmd);
+ if (rc) {
+ msm_isp_axi_destroy_stream(vfe_dev, stream_info);
+ pr_err("%s: Request validation failed\n", __func__);
+ return rc;
+ }
+
+ stream_info->rdi_input_type = stream_cfg_cmd->rdi_input_type;
+ vfe_dev->reg_update_requested &=
+ ~(BIT(SRC_TO_INTF(stream_info->stream_src)));
+
+ msm_isp_axi_reserve_wm(vfe_dev, stream_info);
+
+ if (stream_info->stream_src < RDI_INTF_0) {
+ io_format = vfe_dev->axi_data.src_info[VFE_PIX_0].input_format;
+ if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ if (stream_info->stream_src == CAMIF_RAW &&
+ io_format != stream_info->output_format)
+ pr_debug("%s: Overriding input format\n",
+ __func__);
+
+ io_format = stream_info->output_format;
+ }
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.cfg_io_format(
+ vfe_dev, stream_info->stream_src, io_format);
+ if (rc) {
+ pr_err("%s: cfg io format failed\n", __func__);
+ goto done;
+ }
+ }
+
+ if (!stream_info->controllable_output) {
+ /*
+ * check that the parameters passed from second vfe is same
+ * as first vfe, do this only for non controllable stream
+ * right now because user driver has bug where it sends
+ * mismatch info for controllable streams
+ */
+ if (stream_info->num_isp > 1) {
+ if (stream_cfg_cmd->init_frame_drop !=
+ stream_info->init_frame_drop) {
+ pr_err("%s: stream %d init drop mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->init_frame_drop,
+ stream_cfg_cmd->init_frame_drop);
+ rc = -EINVAL;
+ }
+ if (stream_cfg_cmd->frame_skip_pattern !=
+ stream_info->frame_skip_pattern) {
+ pr_err("%s: stream %d skip pattern mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->frame_skip_pattern,
+ stream_cfg_cmd->frame_skip_pattern);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_type == CONTINUOUS_STREAM &&
+ stream_cfg_cmd->burst_count > 0) {
+ pr_err("%s: stream %d stream type mismatch\n",
+ __func__, stream_info->stream_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->num_burst_capture !=
+ stream_cfg_cmd->burst_count) {
+ pr_err("%s: stream %d stream burst count mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->num_burst_capture,
+ stream_cfg_cmd->burst_count);
+ rc = -EINVAL;
+ }
+ } else {
+ rc = msm_isp_calculate_framedrop(vfe_dev,
+ stream_cfg_cmd);
+ }
+ if (rc)
+ goto done;
+ } else {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->num_burst_capture = 0;
+ stream_info->frame_skip_pattern = NO_SKIP;
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ }
+ if (stream_cfg_cmd->vt_enable && !vfe_dev->vt_enable) {
+ vfe_dev->vt_enable = stream_cfg_cmd->vt_enable;
+ msm_isp_start_avtimer();
+ }
+
+ if (stream_info->num_planes > 1)
+ msm_isp_axi_reserve_comp_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_info, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_xbar_reg(vfe_dev, stream_info, i);
+ }
+ if (stream_info->state == INACTIVE) {
+ /* initialize the WM ping pong with scratch buffer */
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
+ }
+done:
+ if (rc) {
+ msm_isp_axi_free_wm(vfe_dev, stream_info);
+ msm_isp_axi_destroy_stream(vfe_dev, stream_info);
+ }
+ return rc;
+}
+
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i = 0;
+ struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
+ int vfe_idx;
+
+ if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >=
+ VFE_AXI_SRC_MAX) {
+ pr_err("%s: Invalid stream handle\n", __func__);
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_release_cmd->stream_handle));
+
+ vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info);
+ if (vfe_idx == -ENOTTY ||
+ stream_release_cmd->stream_handle !=
+ stream_info->stream_handle[vfe_idx]) {
+ pr_err("%s: Invalid stream %pK handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n",
+ __func__, stream_info,
+ stream_release_cmd->stream_handle,
+ vfe_idx != -ENOTTY ?
+ stream_info->stream_handle[vfe_idx] : 0, vfe_idx,
+ vfe_dev->pdev->id, stream_info->num_isp);
+ return -EINVAL;
+ }
+
+ if (stream_info->state != INACTIVE && stream_info->state != AVAILABLE) {
+ 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, stream_info);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
+ msm_isp_axi_free_wm(vfe_dev, stream_info);
+
+ msm_isp_axi_destroy_stream(vfe_dev, stream_info);
+
+ return rc;
+}
+
+void msm_isp_release_all_axi_stream(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_stream_release_cmd
+ stream_release_cmd[VFE_AXI_SRC_MAX];
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_axi_stream *stream_info;
+ int i;
+ int vfe_idx;
+ int num_stream = 0;
+ unsigned long flags;
+
+ stream_cfg_cmd.cmd = STOP_STREAM;
+ stream_cfg_cmd.num_streams = 0;
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream_user(
+ vfe_dev, stream_info);
+ if (-ENOTTY == vfe_idx) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_release_cmd[num_stream++].stream_handle =
+ stream_info->stream_handle[vfe_idx];
+ if (stream_info->state == INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_cfg_cmd.stream_handle[
+ stream_cfg_cmd.num_streams] =
+ stream_info->stream_handle[vfe_idx];
+ stream_cfg_cmd.num_streams++;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ if (stream_cfg_cmd.num_streams)
+ msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg_cmd);
+
+ for (i = 0; i < num_stream; i++)
+ msm_isp_release_axi_stream(vfe_dev, &stream_release_cmd[i]);
+}
+
+static void msm_isp_axi_stream_enable_cfg(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int enable_wm = 0;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_axi_shared_data *axi_data;
+ uint32_t stream_idx = stream_info->stream_src;
+ int k;
+ int i;
+
+ WARN_ON(stream_idx >= VFE_AXI_SRC_MAX);
+
+ WARN_ON(stream_info->state != START_PENDING &&
+ stream_info->state != RESUME_PENDING &&
+ stream_info->state != STOP_PENDING &&
+ stream_info->state != PAUSE_PENDING);
+
+ if (stream_info->state == START_PENDING ||
+ stream_info->state == RESUME_PENDING) {
+ enable_wm = 1;
+ } else {
+ enable_wm = 0;
+ }
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.enable_wm(
+ vfe_dev->vfe_base,
+ stream_info->wm[k][i], enable_wm);
+ if (enable_wm)
+ continue;
+ /*
+ * Issue a reg update for Raw Snapshot Case
+ * since we dont have reg update ack
+ */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count > 0
+ && vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_count == 0) {
+ if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev,
+ VFE_PIX_0);
+ }
+ }
+ }
+ if (stream_info->state == START_PENDING)
+ axi_data->num_active_stream++;
+ else if (stream_info->state == STOP_PENDING)
+ axi_data->num_active_stream--;
+ }
+}
+
+static void __msm_isp_axi_stream_update(
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_isp_timestamp *ts)
+{
+ int j;
+ int intf = SRC_TO_INTF(stream_info->stream_src);
+ struct vfe_device *vfe_dev;
+ int k;
+
+ switch (stream_info->state) {
+ case UPDATING:
+ stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
+ break;
+ case STOP_PENDING:
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = STOPPING;
+ break;
+ case START_PENDING:
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = STARTING;
+ break;
+ case STOPPING:
+ stream_info->state = INACTIVE;
+ for (k = 0; k < MSM_ISP_COMP_IRQ_MAX; k++)
+ stream_info->composite_irq[k] = 0;
+ complete_all(&stream_info->inactive_comp);
+ break;
+ case STARTING:
+ stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
+ break;
+ case PAUSING:
+ stream_info->state = PAUSED;
+ msm_isp_reload_ping_pong_offset(stream_info);
+ for (j = 0; j < stream_info->num_planes; j++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ }
+ }
+ stream_info->state = RESUME_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = RESUMING;
+ break;
+ case RESUMING:
+ stream_info->runtime_output_format = stream_info->output_format;
+ stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
+ for (j = 0; j < stream_info->num_isp; j++) {
+ /* notify that all streams have been updated */
+ msm_isp_notify(stream_info->vfe_dev[j],
+ ISP_EVENT_STREAM_UPDATE_DONE, intf, ts);
+ atomic_set(&stream_info->vfe_dev[j]->
+ axi_data.axi_cfg_update[intf], 0);
+ }
+ stream_info->update_vfe_mask = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ struct msm_isp_timestamp *ts)
+{
+ int i;
+ unsigned long flags;
+ struct msm_vfe_axi_stream *stream_info;
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ if (SRC_TO_INTF(stream_info->stream_src) !=
+ frame_src) {
+ ISP_DBG("%s stream_src %d frame_src %d\n", __func__,
+ SRC_TO_INTF(
+ stream_info->stream_src),
+ frame_src);
+ continue;
+ }
+ if (stream_info->state == AVAILABLE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_axi_stream_update(stream_info, ts);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+}
+
+static void msm_isp_reload_ping_pong_offset(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i, j;
+ uint32_t bit;
+ struct msm_isp_buffer *buf;
+ int32_t buf_size_byte = 0;
+ int32_t word_per_line = 0;
+ int k;
+ struct vfe_device *vfe_dev;
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ if (!buf)
+ continue;
+
+ bit = i ? 0 : 1;
+
+ for (j = 0; j < stream_info->num_planes; j++) {
+ word_per_line = msm_isp_cal_word_per_line(
+ stream_info->output_format, stream_info->
+ plane_cfg[k][j].output_stride);
+ if (word_per_line < 0) {
+ /* 0 means no prefetch*/
+ word_per_line = 0;
+ buf_size_byte = 0;
+ } else {
+ buf_size_byte = (word_per_line * 8 *
+ stream_info->plane_cfg[k][j].
+ output_scan_lines) - stream_info->
+ plane_cfg[k][j].plane_addr_offset;
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_ping_pong_addr(
+ vfe_dev->vfe_base,
+ stream_info->wm[k][j],
+ bit,
+ buf->mapped_info[j].paddr +
+ stream_info->plane_cfg[k][j].
+ plane_addr_offset,
+ buf_size_byte);
+ }
+ }
+ }
+}
+
+static int msm_isp_update_deliver_count(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_bit,
+ struct msm_isp_buffer *done_buf)
+{
+ int rc = 0;
+
+ if (!stream_info->controllable_output)
+ goto done;
+
+ if (!stream_info->undelivered_request_cnt ||
+ (done_buf == NULL)) {
+ pr_err_ratelimited("%s:%d error undelivered_request_cnt 0\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ goto done;
+ } else {
+ if ((done_buf->is_drop_reconfig == 1) &&
+ (stream_info->sw_ping_pong_bit == -1)) {
+ goto done;
+ }
+ /*After wm reload, we get bufdone for ping buffer*/
+ if (stream_info->sw_ping_pong_bit == -1)
+ stream_info->sw_ping_pong_bit = 0;
+ if (done_buf->is_drop_reconfig != 1)
+ stream_info->undelivered_request_cnt--;
+ if (pingpong_bit != stream_info->sw_ping_pong_bit) {
+ pr_err("%s:%d ping pong bit actual %d sw %d\n",
+ __func__, __LINE__, pingpong_bit,
+ stream_info->sw_ping_pong_bit);
+ rc = -EINVAL;
+ goto done;
+ }
+ stream_info->sw_ping_pong_bit ^= 1;
+ }
+done:
+ return rc;
+}
+
+void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event)
+{
+ struct msm_isp_event_data error_event;
+ struct msm_vfe_axi_halt_cmd halt_cmd;
+ struct vfe_device *temp_dev = NULL;
+ uint32_t irq_status0 = 0, irq_status1 = 0;
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state) !=
+ NO_OVERFLOW)
+ /* Recovery is already in Progress */
+ return;
+
+ if (event == ISP_EVENT_PING_PONG_MISMATCH &&
+ vfe_dev->axi_data.recovery_count < MAX_RECOVERY_THRESHOLD) {
+ pr_err("%s: ping pong mismatch on vfe%d recovery count %d\n",
+ __func__, vfe_dev->pdev->id,
+ vfe_dev->axi_data.recovery_count);
+ msm_isp_process_overflow_irq(vfe_dev,
+ &irq_status0, &irq_status1, 1);
+ vfe_dev->axi_data.recovery_count++;
+ return;
+ }
+ memset(&halt_cmd, 0, sizeof(struct msm_vfe_axi_halt_cmd));
+ memset(&error_event, 0, sizeof(struct msm_isp_event_data));
+ halt_cmd.stop_camif = 1;
+ halt_cmd.overflow_detected = 0;
+ halt_cmd.blocking_halt = 0;
+
+ pr_err("%s: vfe%d fatal error!\n", __func__, vfe_dev->pdev->id);
+
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ HALT_ENFORCED);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.set_halt_restart_mask(vfe_dev);
+ if (vfe_dev->is_split) {
+ int other_vfe_id = (vfe_dev->pdev->id == ISP_VFE0 ?
+ ISP_VFE1 : ISP_VFE0);
+ temp_dev = vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[other_vfe_id];
+ atomic_set(&temp_dev->error_info.overflow_state,
+ HALT_ENFORCED);
+ temp_dev->hw_info->vfe_ops.core_ops.
+ set_halt_restart_mask(temp_dev);
+ }
+ error_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+
+ msm_isp_send_event(vfe_dev, event, &error_event);
+}
+
+int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
+ unsigned long fault_addr)
+{
+ int i, j;
+ struct msm_isp_buffer *buf = NULL;
+ uint32_t pingpong_bit;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+ int k;
+
+ for (j = 0; j < VFE_AXI_SRC_MAX; j++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, j);
+ if (stream_info->state == INACTIVE ||
+ stream_info->state == AVAILABLE)
+ continue;
+
+ for (pingpong_bit = 0; pingpong_bit < 2; pingpong_bit++) {
+ dma_addr_t temp;
+
+ buf = stream_info->buf[pingpong_bit];
+ if (buf == NULL) {
+ pr_err("%s: buf NULL for stream %x num_isp %d\n",
+ __func__,
+ stream_info->stream_src,
+ stream_info->num_isp);
+ continue;
+ }
+ temp = buf->mapped_info[0].paddr +
+ buf->mapped_info[0].len;
+ pr_err("%s: stream %x ping bit %d uses buffer %pK-%pK, num_isp %d\n",
+ __func__, stream_info->stream_src,
+ pingpong_bit,
+ &buf->mapped_info[0].paddr, &temp,
+ stream_info->num_isp);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ pr_debug(
+ "%s: stream_id %x ping-pong %d plane %d start_addr %pK addr_offset %x len %zx stride %d scanline %d\n"
+ , __func__, stream_info->stream_id,
+ pingpong_bit, i,
+ (void *)buf->mapped_info[i].paddr,
+ stream_info->
+ plane_cfg[k][i].plane_addr_offset,
+ buf->mapped_info[i].len,
+ stream_info->
+ plane_cfg[k][i].output_stride,
+ stream_info->
+ plane_cfg[k][i].output_scan_lines
+ );
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct msm_isp_buffer *msm_isp_get_stream_buffer(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int rc = 0;
+ uint32_t bufq_handle = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_frame_request_queue *queue_req;
+ uint32_t buf_index = MSM_ISP_INVALID_BUF_INDEX;
+
+ if (!stream_info->controllable_output) {
+ bufq_handle = stream_info->bufq_handle
+ [VFE_BUF_QUEUE_DEFAULT];
+ } else {
+ queue_req = list_first_entry_or_null(
+ &stream_info->request_q,
+ struct msm_vfe_frame_request_queue, list);
+ if (!queue_req)
+ return buf;
+
+ bufq_handle = stream_info->
+ bufq_handle[queue_req->buff_queue_id];
+
+ if (!bufq_handle ||
+ stream_info->request_q_cnt <= 0) {
+ pr_err_ratelimited("%s: Drop request. Shared stream is stopped.\n",
+ __func__);
+ return buf;
+ }
+ buf_index = queue_req->buf_index;
+ queue_req->cmd_used = 0;
+ list_del(&queue_req->list);
+ stream_info->request_q_cnt--;
+ }
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle, buf_index, &buf);
+
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return buf;
+ }
+ if (rc < 0)
+ return buf;
+
+ if (buf->num_planes != stream_info->num_planes) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ bufq_handle, buf->buf_idx);
+ buf = NULL;
+ }
+ return buf;
+}
+
+int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ uint32_t buf_idx)
+{
+ int i, rc = 0;
+ struct msm_isp_buffer *buf = NULL;
+ uint32_t pingpong_bit;
+ uint32_t buffer_size_byte = 0;
+ int32_t word_per_line = 0;
+ dma_addr_t paddr;
+ uint32_t bufq_handle = 0;
+ int vfe_idx;
+
+ bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT];
+
+ if (!vfe_dev->is_split) {
+ rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
+ vfe_dev->buf_mgr, bufq_handle, buf_idx, &buf);
+ if (rc < 0 || !buf) {
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
+ __func__, rc, buf);
+ return -EINVAL;
+ }
+
+ if (buf->num_planes != stream_info->num_planes) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ bufq_handle, buf->buf_idx);
+ return -EINVAL;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ word_per_line = msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[vfe_idx][i].
+ output_stride);
+ if (word_per_line < 0) {
+ /* 0 means no prefetch*/
+ word_per_line = 0;
+ buffer_size_byte = 0;
+ } else {
+ buffer_size_byte = (word_per_line * 8 *
+ stream_info->plane_cfg[vfe_idx][i].
+ output_scan_lines) -
+ stream_info->
+ plane_cfg[vfe_idx][i].plane_addr_offset;
+ }
+ paddr = buf->mapped_info[i].paddr;
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_ping_pong_addr(
+ vfe_dev->vfe_base, stream_info->wm[vfe_idx][i],
+ pingpong_bit, paddr +
+ stream_info->
+ plane_cfg[vfe_idx][i].plane_addr_offset,
+ buffer_size_byte);
+ stream_info->buf[!pingpong_bit] = buf;
+ buf->pingpong_bit = !pingpong_bit;
+ }
+ buf->state = MSM_ISP_BUFFER_STATE_DEQUEUED;
+ stream_info->buf[!pingpong_bit] = buf;
+ buf->pingpong_bit = !pingpong_bit;
+ }
+ return rc;
+
+}
+
+static int msm_isp_cfg_ping_pong_address(
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct msm_isp_buffer *buf)
+{
+ int i;
+ int j;
+ uint32_t pingpong_bit;
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
+ uint32_t buffer_size_byte = 0;
+ int32_t word_per_line = 0;
+ dma_addr_t paddr;
+
+
+ /* Isolate pingpong_bit from pingpong_status */
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[0][0]) & 0x1);
+
+ /* return if buffer already present */
+ if (stream_info->buf[!pingpong_bit]) {
+ pr_err("stream %x buffer already set for pingpong %d\n",
+ stream_info->stream_src, !pingpong_bit);
+ return 1;
+ }
+
+ if (buf == NULL)
+ buf = msm_isp_get_stream_buffer(vfe_dev, stream_info);
+
+ if (!buf) {
+ msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
+ return 0;
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ paddr = buf->mapped_info[i].paddr;
+ ISP_DBG(
+ "%s: vfe %d config buf %d to pingpong %d stream %x\n",
+ __func__, vfe_dev->pdev->id,
+ buf->buf_idx, !pingpong_bit,
+ stream_info->stream_id);
+ for (j = 0; j < stream_info->num_isp; j++) {
+ vfe_dev = stream_info->vfe_dev[j];
+ word_per_line =
+ msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[j][i].output_stride);
+ if (word_per_line < 0) {
+ /* 0 means no prefetch*/
+ word_per_line = 0;
+ buffer_size_byte = 0;
+ } else {
+ buffer_size_byte =
+ (word_per_line * 8 *
+ stream_info->plane_cfg[j][i].
+ output_scan_lines) -
+ stream_info->plane_cfg[j][i].
+ plane_addr_offset;
+ }
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_ping_pong_addr(
+ vfe_dev->vfe_base,
+ stream_info->wm[j][i],
+ pingpong_bit, paddr +
+ stream_info->plane_cfg[j][i].
+ plane_addr_offset,
+ buffer_size_byte);
+ }
+ }
+ stream_info->buf[!pingpong_bit] = buf;
+ buf->pingpong_bit = !pingpong_bit;
+ return 0;
+}
+
+static void msm_isp_handle_done_buf_frame_id_mismatch(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info,
+ struct msm_isp_buffer *buf, struct timeval *time_stamp,
+ uint32_t frame_id)
+{
+ struct msm_isp_event_data error_event;
+ int ret = 0;
+
+ memset(&error_event, 0, sizeof(error_event));
+ error_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ error_event.u.error_info.err_type =
+ ISP_ERROR_FRAME_ID_MISMATCH;
+ if (stream_info->buf_divert)
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ else
+ ret = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx, time_stamp,
+ frame_id,
+ stream_info->runtime_output_format);
+ if (ret == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ return;
+ }
+ msm_isp_send_event(vfe_dev, ISP_EVENT_ERROR,
+ &error_event);
+ pr_err("%s: Error! frame id mismatch!! 1st buf frame %d,curr frame %d\n",
+ __func__, buf->frame_id, frame_id);
+ vfe_dev->buf_mgr->frameId_mismatch_recovery = 1;
+}
+
+static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf,
+ struct timeval *time_stamp, uint32_t frame_id)
+{
+ int rc;
+ unsigned long flags;
+ struct msm_isp_event_data buf_event;
+ uint32_t stream_idx = stream_info->stream_src;
+ uint32_t buf_src;
+ uint8_t drop_frame = 0;
+ struct msm_isp_bufq *bufq = NULL;
+
+ memset(&buf_event, 0, sizeof(buf_event));
+
+ if (stream_idx >= VFE_AXI_SRC_MAX) {
+ pr_err_ratelimited("%s: Invalid stream_idx", __func__);
+ return -EINVAL;
+ }
+
+ if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX) {
+ pr_err_ratelimited("%s: Invalid stream index, put buf back to vb2 queue\n",
+ __func__);
+ rc = vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return -EINVAL;
+ }
+
+ if (stream_info->stream_type != BURST_STREAM &&
+ (stream_info->sw_skip.stream_src_mask &
+ (1 << stream_info->stream_src))) {
+ /* Hw stream output of this src is requested for drop */
+ if (stream_info->sw_skip.skip_mode == SKIP_ALL) {
+ /* drop all buffers */
+ drop_frame = 1;
+ } else if (stream_info->sw_skip.skip_mode == SKIP_RANGE &&
+ (stream_info->sw_skip.min_frame_id <= frame_id &&
+ stream_info->sw_skip.max_frame_id >= frame_id)) {
+ drop_frame = 1;
+ } else if (frame_id > stream_info->sw_skip.max_frame_id) {
+ spin_lock_irqsave(&stream_info->lock, flags);
+ memset(&stream_info->sw_skip, 0,
+ sizeof(struct msm_isp_sw_framskip));
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ }
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_src(vfe_dev->buf_mgr,
+ buf->bufq_handle, &buf_src);
+ if (rc != 0) {
+ pr_err_ratelimited("%s: Error getting buf_src\n", __func__);
+ return -EINVAL;
+ }
+
+ if (drop_frame) {
+ buf->buf_debug.put_state[
+ buf->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_DROP_SKIP;
+ buf->buf_debug.put_state_last ^= 1;
+ if (stream_info->buf_divert)
+ vfe_dev->buf_mgr->ops->put_buf(
+ vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ else
+ rc = vfe_dev->buf_mgr->ops->buf_done(
+ vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx,
+ time_stamp, frame_id,
+ stream_info->runtime_output_format);
+
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ if (!rc) {
+ ISP_DBG("%s:%d vfe_id %d Buffer dropped %d\n",
+ __func__, __LINE__, vfe_dev->pdev->id,
+ frame_id);
+ /*
+ * Return rc which is 0 at this point so that
+ * we can cfg ping pong and we can continue
+ * streaming
+ */
+ return rc;
+ }
+ }
+
+ buf_event.frame_id = frame_id;
+ buf_event.timestamp = *time_stamp;
+ 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 = buf->bufq_handle;
+ buf_event.u.buf_done.buf_idx = buf->buf_idx;
+ buf_event.u.buf_done.output_format =
+ stream_info->runtime_output_format;
+ if (vfe_dev->fetch_engine_info.is_busy &&
+ SRC_TO_INTF(stream_info->stream_src) == VFE_PIX_0) {
+ vfe_dev->fetch_engine_info.is_busy = 0;
+ }
+
+ if (stream_info->buf_divert &&
+ buf_src != MSM_ISP_BUFFER_SRC_SCRATCH) {
+
+ bufq = vfe_dev->buf_mgr->ops->get_bufq(vfe_dev->buf_mgr,
+ buf->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq buf_handle %x\n",
+ __func__, buf->bufq_handle);
+ return -EINVAL;
+ }
+
+ /* divert native buffers */
+ vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx, time_stamp,
+ frame_id);
+
+ if ((bufq != NULL) && bufq->buf_type == ISP_SHARE_BUF)
+ msm_isp_send_event(vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[ISP_VFE1],
+ ISP_EVENT_BUF_DIVERT, &buf_event);
+ else
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_BUF_DIVERT, &buf_event);
+ } else {
+ ISP_DBG("%s: vfe_id %d send buf done buf-id %d bufq %x\n",
+ __func__, vfe_dev->pdev->id, buf->buf_idx,
+ buf->bufq_handle);
+ msm_isp_send_event(vfe_dev, ISP_EVENT_BUF_DONE,
+ &buf_event);
+ buf->buf_debug.put_state[
+ buf->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_PUT_BUF;
+ buf->buf_debug.put_state_last ^= 1;
+ rc = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx, time_stamp,
+ frame_id, stream_info->runtime_output_format);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int msm_isp_drop_frame(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, struct msm_isp_timestamp *ts,
+ struct msm_isp_sof_info *sof_info)
+{
+ struct msm_isp_buffer *done_buf = NULL;
+ uint32_t pingpong_status;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ uint32_t pingpong_bit;
+ int vfe_idx;
+ int rc = -1;
+
+ if (!vfe_dev || !stream_info || !ts || !sof_info) {
+ pr_err("%s %d vfe_dev %pK stream_info %pK ts %pK op_info %pK\n",
+ __func__, __LINE__, vfe_dev, stream_info, ts,
+ sof_info);
+ return -EINVAL;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ pingpong_status =
+ ~vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+ pingpong_bit =
+ (~(pingpong_status >> stream_info->wm[vfe_idx][0]) & 0x1);
+ done_buf = stream_info->buf[pingpong_bit];
+ if (done_buf &&
+ (stream_info->composite_irq[MSM_ISP_COMP_IRQ_EPOCH] == 0)) {
+ if ((stream_info->sw_ping_pong_bit != -1) &&
+ !vfe_dev->reg_updated) {
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, ~pingpong_status, done_buf);
+ if (rc < 0) {
+ ISP_DBG("%s: Error configuring ping_pong\n",
+ __func__);
+ bufq = vfe_dev->buf_mgr->ops->get_bufq(
+ vfe_dev->buf_mgr,
+ done_buf->bufq_handle);
+ if (!bufq) {
+ spin_unlock_irqrestore(
+ &stream_info->lock,
+ flags);
+ pr_err("%s: Invalid bufq buf_handle %x\n",
+ __func__,
+ done_buf->bufq_handle);
+ return -EINVAL;
+ }
+ sof_info->reg_update_fail_mask_ext |=
+ (bufq->bufq_handle & 0xFF);
+ }
+ }
+ /*Avoid Drop Frame and re-issue pingpong cfg*/
+ /*this notify is per ping and pong buffer*/
+ done_buf->is_drop_reconfig = 1;
+ stream_info->current_framedrop_period = 1;
+ /*Avoid Multiple request frames for single SOF*/
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
+
+ if (stream_info->current_framedrop_period !=
+ stream_info->requested_framedrop_period) {
+ msm_isp_cfg_framedrop_reg(stream_info);
+ }
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ /* if buf done will not come, we need to process it ourself */
+ if (stream_info->activated_framedrop_period ==
+ MSM_VFE_STREAM_STOP_PERIOD) {
+ /* no buf done come */
+ msm_isp_process_axi_irq_stream(vfe_dev, stream_info,
+ pingpong_status, ts);
+ if (done_buf)
+ done_buf->is_drop_reconfig = 0;
+ }
+ return 0;
+}
+
+/**
+ * msm_isp_input_disable() - Disable the input for given vfe
+ * @vfe_dev: The vfe device whose input is to be disabled
+ *
+ * Returns - void
+ *
+ * If stream count on an input line is 0 then disable the input
+ */
+static void msm_isp_input_disable(struct vfe_device *vfe_dev, int cmd_type)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int stream_count;
+ int total_stream_count = 0;
+ int i;
+ struct msm_vfe_src_info *src_info;
+ int ext_read =
+ (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
+
+ for (i = 0; i < VFE_SRC_MAX; i++)
+ total_stream_count += axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+
+ for (i = 0; i < VFE_SRC_MAX; i++) {
+ stream_count = axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+ if (stream_count)
+ continue;
+ if (axi_data->src_info[i].active == 0)
+ continue;
+ /* deactivate the input line */
+ axi_data->src_info[i].active = 0;
+ src_info = &axi_data->src_info[i];
+
+ if (src_info->dual_hw_type == DUAL_HW_MASTER_SLAVE) {
+ struct master_slave_resource_info *ms_res =
+ &vfe_dev->common_data->ms_resource;
+ unsigned long flags;
+
+ spin_lock_irqsave(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ if (src_info->dual_hw_ms_info.index ==
+ ms_res->master_index)
+ ms_res->master_index = -1;
+ if (src_info->dual_hw_ms_info.index ==
+ ms_res->primary_slv_idx)
+ ms_res->primary_slv_idx = -1;
+ ms_res->active_src_mask &= ~(1 <<
+ src_info->dual_hw_ms_info.index);
+ ms_res->src_sof_mask &= ~(1 <<
+ src_info->dual_hw_ms_info.index);
+ ms_res->src_info[src_info->dual_hw_ms_info.index] =
+ NULL;
+ ms_res->num_src--;
+ if (ms_res->num_src == 0)
+ ms_res->dual_sync_mode = MSM_ISP_DUAL_CAM_ASYNC;
+ src_info->dual_hw_ms_info.sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ src_info->dual_hw_type = DUAL_NONE;
+ src_info->dual_hw_ms_info.index = -1;
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ }
+ if (i != VFE_PIX_0 || ext_read)
+ continue;
+ if (total_stream_count == 0 || cmd_type == STOP_IMMEDIATELY)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF_IMMEDIATELY);
+ else
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF);
+ }
+ /*
+ * halt and reset hardware if all streams are disabled, in this case
+ * ispif is halted immediately as well
+ */
+ if (total_stream_count == 0) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ msm_isp_flush_tasklet(vfe_dev);
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1);
+ if (msm_vfe_is_vfe48(vfe_dev))
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, 1);
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+ }
+
+}
+
+/**
+ * msm_isp_input_enable() - Enable the input for given vfe
+ * @vfe_dev: The vfe device whose input is to be enabled
+ *
+ * Returns - void
+ *
+ * Enable inout line if it is not enabled
+ */
+static void msm_isp_input_enable(struct vfe_device *vfe_dev,
+ int sync_frame_id_src)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int ext_read =
+ (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
+ int stream_count;
+ int i;
+
+ for (i = 0; i < VFE_SRC_MAX; i++) {
+ stream_count = axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+ if (stream_count == 0)
+ continue;
+ if (axi_data->src_info[i].active)
+ continue;
+ /* activate the input since it is deactivated */
+ axi_data->src_info[i].frame_id = 0;
+ if (axi_data->src_info[i].input_mux != EXTERNAL_READ)
+ axi_data->src_info[i].active = 1;
+ if (i >= VFE_RAW_0 && sync_frame_id_src) {
+ /*
+ * Incase PIX and RDI streams are part
+ * of same session, this will ensure
+ * RDI stream will have same frame id
+ * as of PIX stream
+ */
+ axi_data->src_info[i].frame_id =
+ axi_data->src_info[VFE_PIX_0].frame_id;
+ }
+ /* when start reset overflow state and cfg ub for this intf */
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev, i);
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ NO_OVERFLOW);
+ if (i != VFE_PIX_0 || ext_read)
+ continue;
+ /* for camif input the camif needs enabling */
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+ }
+}
+
+/**
+ * msm_isp_update_intf_stream_cnt() - Update the stream count in axi interface
+ * @stream_info: The stream that is either being enabled/disabled
+ * @enable: 0 means stream is being disabled, else enabled
+ *
+ * Returns - void
+ */
+static void msm_isp_update_intf_stream_cnt(
+ struct msm_vfe_axi_stream *stream_info,
+ int enable)
+{
+ int i;
+
+ switch (stream_info->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case PIX_VIDEO:
+ case IDEAL_RAW:
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ for (i = 0; i < stream_info->num_isp; i++) {
+ if (enable)
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ stream_count++;
+ else
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ stream_count--;
+ }
+ break;
+ case CAMIF_RAW:
+ for (i = 0; i < stream_info->num_isp; i++) {
+ if (enable)
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ raw_stream_count++;
+ else
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ raw_stream_count--;
+ }
+ break;
+ default:
+ WARN(1, "Invalid steam src %d\n", stream_info->stream_src);
+ }
+}
+
+/*Factor in Q2 format*/
+#define ISP_DEFAULT_FORMAT_FACTOR 6
+#define ISP_BUS_UTILIZATION_FACTOR 6
+static int msm_isp_update_stream_bandwidth(
+ struct msm_vfe_axi_stream *stream_info, int enable)
+{
+ int i, rc = 0;
+ uint64_t total_bandwidth = 0;
+ int vfe_idx;
+ struct vfe_device *vfe_dev;
+
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ if (enable) {
+ total_bandwidth =
+ vfe_dev->total_bandwidth +
+ stream_info->bandwidth[vfe_idx];
+ } else {
+ total_bandwidth = vfe_dev->total_bandwidth -
+ stream_info->bandwidth[vfe_idx];
+ }
+ vfe_dev->total_bandwidth = total_bandwidth;
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (total_bandwidth + vfe_dev->hw_info->min_ab),
+ (total_bandwidth + vfe_dev->hw_info->min_ib));
+
+ if (rc < 0)
+ pr_err("%s: update failed rc %d stream src %d vfe dev %d\n",
+ __func__, rc, stream_info->stream_src,
+ vfe_dev->pdev->id);
+ }
+ return rc;
+}
+
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
+{
+ int i, rc = 0;
+ uint32_t intf;
+ unsigned long flags;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL;
+
+ ab_ib_vote = (struct msm_vfe_dual_lpm_mode *)arg;
+ if (!ab_ib_vote) {
+ pr_err("%s: ab_ib_vote is NULL !!!\n", __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) {
+ pr_err("%s: ab_ib_vote num_src is exceeding limit\n",
+ __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) {
+ pr_err("%s: ab_ib_vote num_src is exceeding limit\n",
+ __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ab_ib_vote->lpm_mode) {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ if (stream_info == NULL)
+ continue;
+ /* loop all stream on current session */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ vfe_dev->axi_data.src_info[intf].lpm =
+ ab_ib_vote->lpm_mode;
+ if (stream_info->lpm_mode ||
+ stream_info->state == INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ continue;
+ }
+ stream_info->lpm_mode = ab_ib_vote->lpm_mode;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_update_stream_bandwidth(stream_info, 0);
+ }
+ } else {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ if (stream_info == NULL)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ vfe_dev->axi_data.src_info[intf].lpm =
+ ab_ib_vote->lpm_mode;
+ if (stream_info->lpm_mode == 0 ||
+ stream_info->state == INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ continue;
+ }
+ stream_info->lpm_mode = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_update_stream_bandwidth(stream_info, 1);
+ }
+ }
+ return rc;
+}
+
+static int msm_isp_init_stream_ping_pong_reg(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int rc = 0;
+
+ /* Set address for both PING & PO NG register */
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, VFE_PING_FLAG, NULL);
+ /* No buffer available on start is not error */
+ if (rc == -ENOMEM && stream_info->stream_type != BURST_STREAM)
+ return 0;
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n",
+ __func__);
+ return rc;
+ }
+ if (stream_info->stream_type != BURST_STREAM ||
+ stream_info->runtime_num_burst_capture > 1) {
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, VFE_PONG_FLAG, NULL);
+ /* No buffer available on start is not error */
+ if (rc == -ENOMEM)
+ return 0;
+ }
+
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n",
+ __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void msm_isp_get_stream_wm_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask)
+{
+ int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ *wm_reload_mask |= (1 << stream_info->wm[vfe_idx][i]);
+}
+
+int msm_isp_axi_halt(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_halt_cmd *halt_cmd)
+{
+ int rc = 0;
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state) ==
+ OVERFLOW_DETECTED)
+ pr_err("%s: VFE%d Bus overflow detected: start recovery!\n",
+ __func__, vfe_dev->pdev->id);
+
+ /* take care of pending items in tasklet before halt */
+ msm_isp_flush_tasklet(vfe_dev);
+
+ if (halt_cmd->stop_camif) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF_IMMEDIATELY);
+ }
+ rc |= vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev,
+ halt_cmd->blocking_halt);
+
+ return rc;
+}
+
+int msm_isp_axi_reset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_reset_cmd *reset_cmd)
+{
+ int rc = 0, i, k;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t bufq_handle = 0, bufq_id = 0;
+ struct msm_isp_timestamp timestamp;
+ struct msm_vfe_frame_request_queue *queue_req;
+ unsigned long flags;
+ int vfe_idx;
+
+ if (!reset_cmd) {
+ pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd);
+ rc = -1;
+ return rc;
+ }
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
+ rc = -1;
+ pr_err("%s invalid stream src = %d\n",
+ __func__,
+ stream_info->stream_src);
+ break;
+ }
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
+ continue;
+
+ /* handle dual stream on ISP_VFE1 turn */
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+
+ /* set ping pong to scratch before flush */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ stream_info->undelivered_request_cnt = 0;
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ while (!list_empty(&stream_info->request_q)) {
+ queue_req = list_first_entry_or_null(
+ &stream_info->request_q,
+ struct msm_vfe_frame_request_queue, list);
+ if (queue_req) {
+ queue_req->cmd_used = 0;
+ list_del(&queue_req->list);
+ }
+ }
+ for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX;
+ bufq_id++) {
+ bufq_handle = stream_info->bufq_handle[bufq_id];
+ if (!bufq_handle)
+ continue;
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
+ ×tamp.buf_time,
+ reset_cmd->frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ struct vfe_device *temp_vfe_dev =
+ stream_info->vfe_dev[k];
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ temp_vfe_dev, stream_info);
+ if (stream_info->num_planes > 1) {
+ temp_vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_comp_mask(temp_vfe_dev,
+ stream_info);
+ } else {
+ temp_vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_irq_mask(temp_vfe_dev,
+ stream_info);
+ }
+ axi_data = &temp_vfe_dev->axi_data;
+ axi_data->src_info[SRC_TO_INTF(stream_info->
+ stream_src)].frame_id =
+ reset_cmd->frame_id;
+ }
+ msm_isp_reset_burst_count_and_frame_drop(
+ vfe_dev, stream_info);
+ }
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, reset_cmd->blocking);
+ /*
+ * call reset a second time for vfe48, calling
+ * only once causes bus error on camif enable
+ */
+ if (msm_vfe_is_vfe48(vfe_dev))
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, reset_cmd->blocking);
+
+ if (rc < 0)
+ pr_err("%s Error! reset hw Timed out\n", __func__);
+
+ return 0;
+}
+
+int msm_isp_axi_restart(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_restart_cmd *restart_cmd)
+{
+ int rc = 0, i, k, j;
+ struct msm_vfe_axi_stream *stream_info;
+ uint32_t wm_reload_mask[MAX_VFE] = {0, 0};
+ unsigned long flags;
+ int vfe_idx;
+
+ vfe_dev->buf_mgr->frameId_mismatch_recovery = 0;
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
+ continue;
+ /* handle dual stream on ISP_VFE1 turn */
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ for (j = 0; j < MSM_ISP_COMP_IRQ_MAX; j++)
+ stream_info->composite_irq[j] = 0;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ struct vfe_device *temp_vfe_dev =
+ stream_info->vfe_dev[k];
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ temp_vfe_dev, stream_info);
+ for (j = 0; j < stream_info->num_planes; j++)
+ temp_vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(
+ temp_vfe_dev->vfe_base,
+ stream_info->wm[vfe_idx][j], 1);
+ msm_isp_get_stream_wm_mask(temp_vfe_dev, stream_info,
+ &wm_reload_mask[temp_vfe_dev->pdev->id]);
+ }
+ msm_isp_init_stream_ping_pong_reg(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ struct vfe_device *temp_vfe_dev =
+ vfe_dev->common_data->dual_vfe_res->vfe_dev[k];
+ if (wm_reload_mask[k])
+ temp_vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(
+ temp_vfe_dev,
+ temp_vfe_dev->vfe_base, wm_reload_mask[k]);
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0,
+ restart_cmd->enable_camif);
+
+ return rc;
+}
+
+static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
+ uint8_t cgc_override)
+{
+ int i = 0, j = 0;
+ struct msm_vfe_axi_stream *stream_info;
+ int k;
+ struct vfe_device *vfe_dev;
+ int vfe_idx;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ VFE_AXI_SRC_MAX) {
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
+ if (!stream_info) {
+ pr_err("%s: stream_info is NULL", __func__);
+ return -EINVAL;
+ }
+ for (j = 0; j < stream_info->num_planes; j++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (!vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_cgc_override)
+ continue;
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ vfe_dev, stream_info);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_cgc_override(vfe_dev,
+ stream_info->wm[vfe_idx][j],
+ cgc_override);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * msm_isp_axi_wait_for_stream_cfg_done() - Wait for a stream completion
+ * @stream_info: The stream to wait on
+ * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE
+ *
+ * Returns - 0 on success else error code
+ */
+static int msm_isp_axi_wait_for_stream_cfg_done(
+ struct msm_vfe_axi_stream *stream_info, int active)
+{
+ int rc = -1;
+ unsigned long flags;
+
+ /* No need to wait if stream is already in required state */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (active && ACTIVE == stream_info->state)
+ rc = 0;
+ if (!active && INACTIVE == stream_info->state)
+ rc = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc == 0)
+ return rc;
+
+ rc = wait_for_completion_timeout(
+ active ? &stream_info->active_comp :
+ &stream_info->inactive_comp,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+
+ if (rc <= 0) {
+ rc = rc ? rc : -ETIMEDOUT;
+ pr_err("%s: wait for stream %x/%x state %d config failed %d\n",
+ __func__,
+ stream_info->stream_id,
+ stream_info->stream_src,
+ stream_info->state,
+ rc);
+ rc = -EINVAL;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+/**
+ * msm_isp_axi_wait_for_streams() - Wait for completion of a number of streams
+ * @streams: The streams to wait on
+ * @num_stream: Number of streams to wait on
+ * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE
+ *
+ * Returns - 0 on success else error code
+ */
+static int msm_isp_axi_wait_for_streams(struct msm_vfe_axi_stream **streams,
+ int num_stream, int active)
+{
+ int i;
+ int rc = 0;
+ struct msm_vfe_axi_stream *stream_info;
+
+ for (i = 0; i < num_stream; i++) {
+ stream_info = streams[i];
+ rc |= msm_isp_axi_wait_for_stream_cfg_done(stream_info, active);
+ }
+ return rc;
+}
+
+static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info,
+ int cmd)
+{
+ switch (stream_info->state) {
+ case AVAILABLE:
+ return -EINVAL;
+ case PAUSING:
+ case RESUMING:
+ case RESUME_PENDING:
+ case ACTIVE:
+ case PAUSED:
+ if (cmd != 0)
+ return -EALREADY;
+ break;
+ case INACTIVE:
+ if (cmd == 0)
+ return -EALREADY;
+ break;
+ /*
+ * stream cannot be in following states since we always
+ * wait in ioctl for stream to be active or inactive
+ */
+ case UPDATING:
+ case START_PENDING:
+ case STARTING:
+ case STOPPING:
+ case STOP_PENDING:
+ case PAUSE_PENDING:
+ default:
+ WARN(1, "Invalid state %d\n", stream_info->state);
+ }
+ return 0;
+}
+
+
+static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream **streams, int num_streams, int cmd_type)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data;
+ struct msm_isp_timestamp timestamp;
+ uint32_t bufq_id = 0, bufq_handle = 0;
+ struct msm_vfe_axi_stream *stream_info;
+ unsigned long flags;
+ uint32_t intf;
+ int rc;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ int k;
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ msm_isp_update_intf_stream_cnt(stream_info, 0);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ }
+ }
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ msm_isp_input_disable(update_vfes[k], cmd_type);
+ }
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ /*
+ * since we can get here from start axi stream error path due
+ * to which the stream may be intermittent state like
+ * STARTING/START_PENDING, force the stream to move out of
+ * intermittent state so it can be made INACTIVE. The
+ * intermittent states update variables so better to go through
+ * those state transitions instead of directly forcing stream to
+ * be INACTIVE
+ */
+ memset(&stream_info->sw_skip, 0,
+ sizeof(struct msm_isp_sw_framskip));
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ if (stream_info->lpm_mode == 0 &&
+ stream_info->state != PAUSED) {
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(stream_info,
+ ×tamp);
+ }
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
+ stream_info->undelivered_request_cnt = 0;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (stream_info->num_planes > 1)
+ 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);
+ }
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STOP_PENDING;
+ if (stream_info->lpm_mode ||
+ stream_info->state == PAUSED) {
+ /* don't wait for reg update */
+ while (stream_info->state != INACTIVE)
+ __msm_isp_axi_stream_update(stream_info,
+ ×tamp);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ /* make sure all stats are stopped if camif is stopped */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active == 0)
+ msm_isp_stop_all_stats_stream(vfe_dev);
+ }
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ if (((stream_info->stream_type == BURST_STREAM) &&
+ stream_info->runtime_num_burst_capture == 0) ||
+ (stream_info->vfe_dev[0]->axi_data.src_info[intf].
+ active == 0)) {
+ while (stream_info->state != INACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, ×tamp);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0);
+ if (rc) {
+ pr_err("%s: wait for stream comp failed, retry...\n", __func__);
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_axi_stream_update(stream_info,
+ ×tamp);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0);
+ if (rc) {
+ pr_err("%s: wait for stream comp failed, force streams to inactive\n",
+ __func__);
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ while (stream_info->state != INACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, ×tamp);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ }
+ }
+ }
+ /* clear buffers that are dequeued */
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->lpm_mode == 0)
+ msm_isp_update_stream_bandwidth(stream_info, 0);
+ for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) {
+ bufq_handle = stream_info->bufq_handle[bufq_id];
+ if (!bufq_handle)
+ continue;
+ vfe_dev = stream_info->vfe_dev[0];
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
+ ×tamp.buf_time, 0);
+ if (rc == -EFAULT)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ }
+ }
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ axi_data = &vfe_dev->axi_data;
+ if (axi_data->src_info[intf].stream_count == 0)
+ vfe_dev->reg_update_requested &=
+ ~(BIT(intf));
+ }
+ }
+}
+
+static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint8_t src_state;
+ uint32_t wm_reload_mask[MAX_VFE] = {0, 0};
+ struct msm_vfe_axi_stream *stream_info;
+ uint32_t src_mask = 0;
+ unsigned long flags;
+ struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM];
+ int num_streams = 0;
+ struct msm_isp_timestamp timestamp;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ int k;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev_ioctl->axi_data;
+ uint32_t intf;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ msm_isp_get_timestamp(×tamp, vfe_dev_ioctl);
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
+ if (!stream_info) {
+ pr_err("%s: stream_info is NULL", __func__);
+ return -EINVAL;
+ }
+ if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
+ src_state = axi_data->src_info[
+ SRC_TO_INTF(stream_info->stream_src)].active;
+
+ else {
+ ISP_DBG("%s: invalid src info index\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = __msm_isp_check_stream_state(stream_info, 1);
+ if (-EALREADY == rc) {
+ rc = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ goto error;
+ }
+
+ msm_isp_calculate_bandwidth(stream_info);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
+ stream_info, &wm_reload_mask[
+ stream_info->vfe_dev[k]->pdev->id]);
+ src_state = stream_info->vfe_dev[k]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].active;
+ if (update_vfes[stream_info->vfe_dev[k]->pdev->id])
+ continue;
+ update_vfes[stream_info->vfe_dev[k]->pdev->id] =
+ stream_info->vfe_dev[k];
+ }
+ msm_isp_reset_framedrop(vfe_dev_ioctl, stream_info);
+ rc = msm_isp_init_stream_ping_pong_reg(stream_info);
+ if (rc < 0) {
+ pr_err("%s: No buffer for stream%d\n", __func__,
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->stream_handle[i]));
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ goto error;
+ }
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (stream_info->num_planes > 1) {
+ 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);
+ }
+ }
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ stream_info->lpm_mode = vfe_dev_ioctl->
+ axi_data.src_info[intf].lpm;
+ if (stream_info->lpm_mode == 0) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_update_stream_bandwidth(stream_info, 1);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ }
+ init_completion(&stream_info->active_comp);
+ stream_info->state = START_PENDING;
+ msm_isp_update_intf_stream_cnt(stream_info, 1);
+
+ ISP_DBG("%s, Stream 0x%x src_state %d on vfe %d\n", __func__,
+ stream_info->stream_src, src_state,
+ vfe_dev_ioctl->pdev->id);
+ if (src_state) {
+ src_mask |= (1 << SRC_TO_INTF(stream_info->stream_src));
+ if (stream_info->lpm_mode) {
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, ×tamp);
+ }
+ } else {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+
+ if (vfe_dev->dump_reg)
+ msm_camera_io_dump(vfe_dev->vfe_base,
+ 0x1000, 1);
+ }
+
+ /* Configure AXI start bits to start immediately */
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, ×tamp);
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev,
+ SRC_TO_INTF(stream_info->stream_src));
+ }
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ streams[num_streams++] = stream_info;
+ }
+
+ for (i = 0; i < MAX_VFE; i++) {
+ vfe_dev = update_vfes[i];
+ if (!vfe_dev)
+ continue;
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
+ vfe_dev->vfe_base, wm_reload_mask[i]);
+
+ msm_isp_input_enable(vfe_dev,
+ stream_cfg_cmd->sync_frame_id_src);
+ }
+
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 1);
+ if (rc < 0) {
+ pr_err("%s: wait for config done failed\n", __func__);
+ goto error;
+ }
+
+ return 0;
+error:
+ __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams,
+ STOP_STREAM);
+
+ return rc;
+}
+
+static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+ struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM];
+ int num_streams = 0;
+ unsigned long flags;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM ||
+ stream_cfg_cmd->num_streams == 0)
+ return -EINVAL;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
+ if (!stream_info) {
+ pr_err("%s: stream_info is NULL", __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = __msm_isp_check_stream_state(stream_info, 0);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc) {
+ /*
+ * continue stopping other streams as error here means
+ * stream is already not active
+ */
+ rc = 0;
+ continue;
+ }
+ streams[num_streams++] = stream_info;
+ }
+ __msm_isp_stop_axi_streams(vfe_dev_ioctl, streams, num_streams,
+ stream_cfg_cmd->cmd);
+
+ return rc;
+}
+
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
+ uint32_t stream_idx[MAX_NUM_STREAM];
+ int i;
+ int vfe_idx;
+ struct msm_vfe_axi_stream *stream_info;
+
+ memset(stream_idx, 0, sizeof(stream_idx));
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ VFE_AXI_SRC_MAX)
+ return -EINVAL;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
+ if (!stream_info) {
+ pr_err("%s: stream_info is NULL", __func__);
+ return -EINVAL;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle %x vfe_idx %d expected %x\n",
+ __func__, stream_cfg_cmd->stream_handle[i],
+ vfe_idx,
+ (vfe_idx != -ENOTTY) ?
+ stream_info->stream_handle[vfe_idx] : 0);
+ return -EINVAL;
+ }
+ /* check for duplicate stream handle */
+ if (stream_idx[stream_info->stream_src] ==
+ stream_cfg_cmd->stream_handle[i])
+ stream_cfg_cmd->stream_handle[i] = 0;
+ else
+ stream_idx[stream_info->stream_src] =
+ stream_cfg_cmd->stream_handle[i];
+ }
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1);
+
+ rc = msm_isp_start_axi_stream(
+ vfe_dev, stream_cfg_cmd);
+ } else {
+ rc = msm_isp_stop_axi_stream(
+ vfe_dev, stream_cfg_cmd);
+
+ msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 0);
+
+ /*
+ * Use different ret value to not overwrite the error from
+ * msm_isp_stop_axi_stream
+ */
+ if (vfe_dev->axi_data.num_active_stream == 0)
+ vfe_dev->hvx_cmd = HVX_DISABLE;
+ if (vfe_dev->is_split) {
+ struct vfe_device *vfe_temp =
+ vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[ISP_VFE0];
+ if (vfe_temp->axi_data.num_active_stream == 0)
+ vfe_temp->hvx_cmd = HVX_DISABLE;
+ }
+ }
+
+ if (rc < 0)
+ pr_err("%s: start/stop %d stream failed\n", __func__,
+ stream_cfg_cmd->cmd);
+ return rc;
+}
+
+static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id,
+ uint32_t frame_id, uint32_t buf_index,
+ enum msm_vfe_input_src frame_src)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf = NULL;
+ uint32_t bufq_handle = 0;
+ uint32_t stream_idx;
+ struct msm_isp_event_data error_event;
+ struct msm_isp_timestamp timestamp;
+
+ if (!vfe_dev || !stream_info) {
+ pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__,
+ __LINE__, vfe_dev, stream_info);
+ return -EINVAL;
+ }
+
+ stream_idx = stream_info->stream_src;
+ if (!stream_info->controllable_output)
+ return -EINVAL;
+
+ if (frame_src >= VFE_SRC_MAX) {
+ pr_err("%s: Invalid frame_src %d", __func__, frame_src);
+ return -EINVAL;
+ }
+
+ if (stream_idx >= VFE_AXI_SRC_MAX) {
+ pr_err("%s: Invalid stream_idx", __func__);
+ return rc;
+ }
+
+ if (user_stream_id == stream_info->stream_id)
+ bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT];
+ else
+ bufq_handle = stream_info->bufq_handle[VFE_BUF_QUEUE_SHARED];
+
+
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle, buf_index, &buf);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+
+ if (rc < 0 || buf == NULL) {
+ pr_err("Skip framedrop report due to no buffer\n");
+ return rc;
+ }
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+ buf->buf_debug.put_state[buf->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_DROP_REG;
+ buf->buf_debug.put_state_last ^= 1;
+ rc = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx,
+ ×tamp.buf_time, frame_id,
+ stream_info->runtime_output_format);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+
+ memset(&error_event, 0, sizeof(error_event));
+ error_event.frame_id = frame_id;
+ error_event.u.error_info.err_type = ISP_ERROR_RETURN_EMPTY_BUFFER;
+ error_event.u.error_info.session_id = stream_info->session_id;
+ error_event.u.error_info.stream_id_mask =
+ 1 << (bufq_handle & 0xFF);
+ msm_isp_send_event(vfe_dev, ISP_EVENT_ERROR, &error_event);
+
+ return 0;
+}
+
+static int msm_isp_request_frame(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id,
+ uint32_t frame_id, uint32_t buf_index)
+{
+ struct msm_vfe_axi_stream_request_cmd stream_cfg_cmd;
+ struct msm_vfe_frame_request_queue *queue_req;
+ uint32_t pingpong_status;
+ unsigned long flags;
+ int rc = 0;
+ enum msm_vfe_input_src frame_src = 0;
+ int k;
+ uint32_t wm_mask = 0;
+ int vfe_idx;
+ uint32_t pingpong_bit = 0;
+
+ if (!vfe_dev || !stream_info) {
+ pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__,
+ __LINE__, vfe_dev, stream_info);
+ return -EINVAL;
+ }
+
+ /* return early for dual vfe0 */
+ if (stream_info->num_isp > 1 && vfe_dev->pdev->id == ISP_VFE0)
+ return 0;
+
+ if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
+ pr_err("%s:%d invalid stream src %d\n", __func__, __LINE__,
+ stream_info->stream_src);
+ return -EINVAL;
+ }
+
+ frame_src = SRC_TO_INTF(stream_info->stream_src);
+ pingpong_status = vfe_dev->hw_info->
+ vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+ /*
+ * If PIX stream is active then RDI path uses SOF frame ID of PIX
+ * In case of standalone RDI streaming, SOF are used from
+ * individual intf.
+ */
+ /*
+ * If frame_id = 1 then no eof check is needed
+ */
+ if (vfe_dev->axi_data.src_info[frame_src].active &&
+ frame_src == VFE_PIX_0 &&
+ vfe_dev->axi_data.src_info[frame_src].accept_frame == false) {
+ pr_debug("%s:%d invalid time to request frame %d\n",
+ __func__, __LINE__, frame_id);
+ goto error;
+ }
+ if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id !=
+ vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev->
+ axi_data.src_info[frame_src].sof_counter_step)) ||
+ ((!vfe_dev->axi_data.src_info[frame_src].active))) {
+ pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n",
+ __func__, __LINE__, frame_id,
+ vfe_dev->axi_data.src_info[frame_src].frame_id,
+ vfe_dev->axi_data.src_info[frame_src].active);
+ goto error;
+ }
+ if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
+ pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n",
+ __func__, __LINE__,
+ stream_info->undelivered_request_cnt,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ goto error;
+ }
+ if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt &&
+ MSM_VFE_STREAM_STOP_PERIOD !=
+ stream_info->activated_framedrop_period) {
+ pr_debug("%s:%d vfe %d frame_id %d prev_pattern %x stream_id %x\n",
+ __func__, __LINE__, vfe_dev->pdev->id, frame_id,
+ stream_info->activated_framedrop_period,
+ stream_info->stream_id);
+
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index, frame_src);
+ if (rc < 0)
+ pr_err("%s:%d failed: return_empty_buffer src %d\n",
+ __func__, __LINE__, frame_src);
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ msm_isp_cfg_framedrop_reg(stream_info);
+ return 0;
+ }
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ /* When wm reloaded, pingpong status register would be stale, pingpong
+ * status would be updated only after AXI_DONE interrupt processed.
+ * So, we should avoid reading value from pingpong status register
+ * until buf_done happens for ping buffer.
+ */
+ if ((stream_info->undelivered_request_cnt == 1) &&
+ (stream_info->sw_ping_pong_bit != -1)) {
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
+ vfe_dev);
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
+ if (stream_info->sw_ping_pong_bit == !pingpong_bit) {
+ ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n",
+ __func__, stream_info->stream_id);
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index,
+ frame_src);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ return 0;
+ }
+ }
+
+ queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx];
+ if (queue_req->cmd_used) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err_ratelimited("%s: Request queue overflow.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (user_stream_id == stream_info->stream_id)
+ queue_req->buff_queue_id = VFE_BUF_QUEUE_DEFAULT;
+ else
+ queue_req->buff_queue_id = VFE_BUF_QUEUE_SHARED;
+
+ if (!stream_info->bufq_handle[queue_req->buff_queue_id]) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s:%d request frame failed on hw stream 0x%x, request stream %d due to no bufq idx: %d\n",
+ __func__, __LINE__,
+ stream_info->stream_handle[0],
+ user_stream_id, queue_req->buff_queue_id);
+ return 0;
+ }
+ queue_req->buf_index = buf_index;
+ queue_req->cmd_used = 1;
+
+ stream_info->request_q_idx =
+ (stream_info->request_q_idx + 1) % MSM_VFE_REQUESTQ_SIZE;
+ list_add_tail(&queue_req->list, &stream_info->request_q);
+ stream_info->request_q_cnt++;
+
+ stream_info->undelivered_request_cnt++;
+ stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx];
+ stream_cfg_cmd.frame_skip_pattern = NO_SKIP;
+ stream_cfg_cmd.init_frame_drop = 0;
+ stream_cfg_cmd.burst_count = stream_info->request_q_cnt;
+
+ if (stream_info->undelivered_request_cnt == 1) {
+ rc = msm_isp_cfg_ping_pong_address(stream_info,
+ VFE_PING_FLAG, NULL);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ stream_info->undelivered_request_cnt--;
+ pr_err_ratelimited("%s:%d fail to cfg HAL buffer\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ wm_mask = 0;
+ msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
+ stream_info, &wm_mask);
+ stream_info->vfe_dev[k]->
+ hw_info->vfe_ops.axi_ops.reload_wm(
+ stream_info->vfe_dev[k],
+ stream_info->vfe_dev[k]->vfe_base, wm_mask);
+
+ }
+ /* sw_ping_pong_bit is updated only when AXI_DONE.
+ * so now reset this bit to -1.
+ */
+ stream_info->sw_ping_pong_bit = -1;
+ } else if (stream_info->undelivered_request_cnt == 2) {
+ if (stream_info->sw_ping_pong_bit == -1) {
+ /* This means wm is reloaded & ping buffer is
+ * already configured. And AXI_DONE for ping
+ * is still pending. So, config pong buffer
+ * now.
+ */
+ rc = msm_isp_cfg_ping_pong_address(stream_info,
+ VFE_PONG_FLAG, NULL);
+ } else {
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, pingpong_status, NULL);
+ }
+ if (rc) {
+ stream_info->undelivered_request_cnt--;
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ pr_err_ratelimited("%s:%d fail to cfg HAL buffer\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ } else {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ stream_info->undelivered_request_cnt--;
+ pr_err_ratelimited("%s: Invalid undeliver frame count %d\n",
+ __func__, stream_info->undelivered_request_cnt);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_calculate_framedrop(vfe_dev, &stream_cfg_cmd);
+ if (rc == 0)
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+
+ /*Avoid Multiple request frames for single SOF*/
+ vfe_dev->axi_data.src_info[frame_src].accept_frame = false;
+
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ return rc;
+error:
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index, frame_src);
+ if (rc < 0)
+ pr_err("%s:%d failed: return_empty_buffer src %d\n",
+ __func__, __LINE__, frame_src);
+ return 0;
+
+}
+
+static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t stream_id)
+{
+ int rc = 0;
+ uint32_t bufq_id = 0;
+ unsigned long flags;
+
+ if (stream_id == stream_info->stream_id)
+ bufq_id = VFE_BUF_QUEUE_DEFAULT;
+ else
+ bufq_id = VFE_BUF_QUEUE_SHARED;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ if (stream_info->bufq_handle[bufq_id] == 0) {
+ stream_info->bufq_handle[bufq_id] =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(vfe_dev->buf_mgr,
+ stream_info->session_id, stream_id);
+ if (stream_info->bufq_handle[bufq_id] == 0) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: failed: No valid buffer queue for stream: 0x%x\n",
+ __func__, stream_id);
+ return -EINVAL;
+ }
+ } else {
+ uint32_t bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr,
+ stream_info->session_id,
+ stream_id);
+ if (bufq_handle != stream_info->bufq_handle[bufq_id]) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: Stream %x already has buffer q %x cannot add handle %x\n",
+ __func__, stream_id,
+ stream_info->bufq_handle[bufq_id], bufq_handle);
+ return -EINVAL;
+ }
+ }
+
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ ISP_DBG("%d: Add bufq handle:0x%x, idx:%d, for stream %d on VFE %d\n",
+ __LINE__, stream_info->bufq_handle[bufq_id],
+ bufq_id, stream_info->stream_handle[0],
+ vfe_dev->pdev->id);
+
+ return rc;
+}
+
+static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t stream_id)
+{
+ uint32_t bufq_id = 0;
+ unsigned long flags;
+
+ if (stream_id == stream_info->stream_id)
+ bufq_id = VFE_BUF_QUEUE_DEFAULT;
+ else
+ bufq_id = VFE_BUF_QUEUE_SHARED;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ if (stream_info->bufq_handle[bufq_id]) {
+ stream_info->bufq_handle[bufq_id] = 0;
+ if (stream_info->state == ACTIVE) {
+ init_completion(&stream_info->active_comp);
+ stream_info->state = UPDATING;
+ }
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (stream_info->state == UPDATING)
+ msm_isp_axi_wait_for_stream_cfg_done(stream_info, 1);
+
+}
+
+/**
+ * msm_isp_stream_axi_cfg_update() - Apply axi config update to a stream
+ * @vfe_dev: The vfe device on which the update is to be applied
+ * @stream_info: Stream for which update is to be applied
+ * @update_info: Parameters of the update
+ *
+ * Returns - 0 on success else error code
+ *
+ * For dual vfe stream apply the update once update for both vfe is
+ * received.
+ */
+static int msm_isp_stream_axi_cfg_update(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_vfe_axi_stream_cfg_update_info *update_info)
+{
+ int j;
+ int k;
+ unsigned long flags;
+ int vfe_idx;
+
+ if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[
+ SRC_TO_INTF(stream_info->stream_src)])) {
+ pr_err("%s: Update in progress for vfe %d intf %d\n",
+ __func__, vfe_dev->pdev->id,
+ SRC_TO_INTF(stream_info->stream_src));
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state != ACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("Invalid stream state for axi update %d\n",
+ stream_info->state);
+ return -EINVAL;
+ }
+ if (stream_info->update_vfe_mask) {
+ if (stream_info->update_vfe_mask & (1 << vfe_dev->pdev->id)) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: Stream %pK/%x Update already in progress for vfe %d\n",
+ __func__, stream_info, stream_info->stream_src,
+ vfe_dev->pdev->id);
+ return -EINVAL;
+ }
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (j = 0; j < stream_info->num_planes; j++)
+ stream_info->plane_cfg[vfe_idx][j] = update_info->plane_cfg[j];
+
+ stream_info->update_vfe_mask |= (1 << vfe_dev->pdev->id);
+ /* wait for update from all vfe's under stream before applying */
+ if (stream_info->update_vfe_mask != stream_info->vfe_mask) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return 0;
+ }
+
+ atomic_set(&vfe_dev->axi_data.axi_cfg_update[
+ SRC_TO_INTF(stream_info->stream_src)], 1);
+ stream_info->output_format = update_info->output_format;
+ init_completion(&stream_info->active_comp);
+ if (((vfe_dev->hw_info->runtime_axi_update == 0) ||
+ (vfe_dev->dual_vfe_enable == 1))) {
+ stream_info->state = PAUSE_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = PAUSING;
+ } else {
+ for (j = 0; j < stream_info->num_planes; j++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ }
+ }
+ stream_info->state = RESUMING;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return 0;
+}
+
+int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
+ struct msm_isp_sw_framskip *sw_skip_info = NULL;
+ unsigned long flags;
+ struct msm_isp_timestamp timestamp;
+ uint32_t frame_id;
+ int vfe_idx;
+
+ /*num_stream is uint32 and update_info[] bound by MAX_NUM_STREAM*/
+ if (update_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ /*check array reference bounds*/
+ if (HANDLE_TO_IDX(update_info->stream_handle) >=
+ VFE_AXI_SRC_MAX) {
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX)
+ continue;
+ if (stream_info->state != ACTIVE &&
+ stream_info->state != INACTIVE &&
+ update_cmd->update_type !=
+ UPDATE_STREAM_REQUEST_FRAMES &&
+ update_cmd->update_type !=
+ UPDATE_STREAM_REMOVE_BUFQ &&
+ update_cmd->update_type !=
+ UPDATE_STREAM_SW_FRAME_DROP) {
+ pr_err("%s: Invalid stream state %d, update cmd %d\n",
+ __func__, stream_info->state,
+ stream_info->stream_id);
+ return -EINVAL;
+ }
+ if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG &&
+ stream_info->state != ACTIVE) {
+ pr_err("%s: AXI stream config updating\n", __func__);
+ return -EBUSY;
+ }
+ }
+
+ switch (update_cmd->update_type) {
+ case ENABLE_STREAM_BUF_DIVERT:
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ stream_info->buf_divert = 1;
+ }
+ break;
+ case DISABLE_STREAM_BUF_DIVERT:
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ stream_info->buf_divert = 0;
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+ frame_id = vfe_dev->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].frame_id;
+ /* set ping pong address to scratch before flush */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ stream_info->bufq_handle
+ [VFE_BUF_QUEUE_DEFAULT],
+ MSM_ISP_BUFFER_FLUSH_DIVERTED,
+ ×tamp.buf_time, frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+ break;
+ case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&stream_info->lock, flags);
+ /* no change then break early */
+ if (stream_info->current_framedrop_period ==
+ framedrop_period) {
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ break;
+ }
+ if (stream_info->controllable_output) {
+ pr_err("Controllable output streams does not support custom frame skip pattern\n");
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ return -EINVAL;
+ }
+ if (update_info->skip_pattern == SKIP_ALL)
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ else
+ stream_info->current_framedrop_period =
+ framedrop_period;
+ if (stream_info->stream_type != BURST_STREAM)
+ msm_isp_cfg_framedrop_reg(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ break;
+ }
+ case UPDATE_STREAM_SW_FRAME_DROP: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ sw_skip_info = &update_info->sw_skip_info;
+ if (sw_skip_info &&
+ sw_skip_info->stream_src_mask != 0) {
+ /* SW image buffer drop */
+ pr_debug("%x sw skip type %x mode %d min %d max %d\n",
+ stream_info->stream_id,
+ sw_skip_info->stats_type_mask,
+ sw_skip_info->skip_mode,
+ sw_skip_info->min_frame_id,
+ sw_skip_info->max_frame_id);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ stream_info->sw_skip = *sw_skip_info;
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ }
+ }
+ break;
+ }
+ case UPDATE_STREAM_AXI_CONFIG: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ rc = msm_isp_stream_axi_cfg_update(vfe_dev, stream_info,
+ update_info);
+ if (rc)
+ return rc;
+ }
+ break;
+ }
+ case UPDATE_STREAM_REQUEST_FRAMES: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ rc = msm_isp_request_frame(vfe_dev, stream_info,
+ update_info->user_stream_id,
+ update_info->frame_id,
+ MSM_ISP_INVALID_BUF_INDEX);
+ if (rc)
+ pr_err("%s failed to request frame!\n",
+ __func__);
+ }
+ break;
+ }
+ case UPDATE_STREAM_ADD_BUFQ: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ rc = msm_isp_add_buf_queue(vfe_dev, stream_info,
+ update_info->user_stream_id);
+ if (rc)
+ pr_err("%s failed to add bufq!\n", __func__);
+ }
+ break;
+ }
+ case UPDATE_STREAM_REMOVE_BUFQ: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ msm_isp_remove_buf_queue(vfe_dev, stream_info,
+ update_info->user_stream_id);
+ pr_debug("%s, Remove bufq for Stream 0x%x\n",
+ __func__, stream_info->stream_id);
+ }
+ break;
+ }
+ case UPDATE_STREAM_REQUEST_FRAMES_VER2: {
+ struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm =
+ &update_cmd->req_frm_ver2;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(req_frm->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ rc = msm_isp_request_frame(vfe_dev, stream_info,
+ req_frm->user_stream_id,
+ req_frm->frame_id,
+ req_frm->buf_index);
+ if (rc)
+ pr_err("%s failed to request frame!\n",
+ __func__);
+ break;
+ }
+ case UPDATE_STREAM_OFFLINE_AXI_CONFIG: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ if (!stream_info) {
+ pr_err("%s: stream_info is null", __func__);
+ return -EINVAL;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ vfe_dev, stream_info);
+ msm_isp_stream_axi_cfg_update(vfe_dev, stream_info,
+ update_info);
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Invalid update type %d\n", __func__,
+ update_cmd->update_type);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t pingpong_status,
+ struct msm_isp_timestamp *ts)
+{
+ int rc = -1;
+ uint32_t pingpong_bit = 0, i;
+ struct msm_isp_buffer *done_buf = NULL;
+ unsigned long flags;
+ struct timeval *time_stamp;
+ uint32_t frame_id, buf_index = -1;
+ int vfe_idx;
+
+ if (!ts) {
+ pr_err("%s: Error! Invalid argument\n", __func__);
+ return;
+ }
+
+ if (vfe_dev->vt_enable) {
+ msm_isp_get_avtimer_ts(ts);
+ time_stamp = &ts->vt_time;
+ } else {
+ time_stamp = &ts->buf_time;
+ }
+
+ frame_id = vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ pingpong_bit = (~(pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
+ for (i = 0; i < stream_info->num_planes; i++) {
+ if (pingpong_bit !=
+ (~(pingpong_status >>
+ stream_info->wm[vfe_idx][i]) & 0x1)) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_dump_ping_pong_mismatch(vfe_dev);
+ pr_err("%s: Write master ping pong mismatch. Status: 0x%x %x\n",
+ __func__, pingpong_status,
+ stream_info->stream_src);
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return;
+ }
+ }
+ if (stream_info->state == INACTIVE) {
+ WARN_ON(stream_info->buf[pingpong_bit] != NULL);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return;
+ }
+
+ /* composite the irq for dual vfe */
+ rc = msm_isp_composite_irq(vfe_dev, stream_info,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE + pingpong_bit);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc < 0)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return;
+ }
+
+ done_buf = stream_info->buf[pingpong_bit];
+
+ if (vfe_dev->buf_mgr->frameId_mismatch_recovery == 1) {
+ if (done_buf) {
+ if (done_buf->is_drop_reconfig == 1)
+ done_buf->is_drop_reconfig = 0;
+ }
+ pr_err_ratelimited("%s: Mismatch Recovery in progress, drop frame!\n",
+ __func__);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return;
+ }
+
+ if (done_buf)
+ buf_index = done_buf->buf_idx;
+
+ ISP_DBG("%s: vfe %d: stream 0x%x, frame id %d, pingpong bit %d\n",
+ __func__,
+ vfe_dev->pdev->id,
+ stream_info->stream_id,
+ frame_id,
+ pingpong_bit);
+
+ stream_info->frame_id++;
+ stream_info->buf[pingpong_bit] = NULL;
+
+ if (stream_info->controllable_output &&
+ (done_buf != NULL) &&
+ (stream_info->sw_ping_pong_bit == -1) &&
+ (done_buf->is_drop_reconfig == 1)) {
+ /* When wm reloaded and corresponding reg_update fail
+ * then buffer is reconfig as PING buffer. so, avoid
+ * NULL assignment to PING buffer and eventually
+ * next AXI_DONE or buf_done can be successful
+ */
+ stream_info->buf[pingpong_bit] = done_buf;
+ }
+
+ if (stream_info->stream_type == CONTINUOUS_STREAM ||
+ stream_info->runtime_num_burst_capture > 1) {
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, pingpong_status, NULL);
+ if (rc < 0)
+ ISP_DBG("%s: Error configuring ping_pong\n",
+ __func__);
+ } else if (done_buf && (done_buf->is_drop_reconfig != 1)) {
+ msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
+ }
+
+ if (!done_buf) {
+ if (stream_info->buf_divert) {
+ vfe_dev->error_info.stream_framedrop_count[
+ stream_info->bufq_handle[
+ VFE_BUF_QUEUE_DEFAULT] & 0xFF]++;
+ vfe_dev->error_info.framedrop_flag = 1;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return;
+ }
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_num_burst_capture) {
+ ISP_DBG("%s: burst_frame_count: %d\n",
+ __func__,
+ stream_info->runtime_num_burst_capture);
+ stream_info->runtime_num_burst_capture--;
+ }
+
+ rc = msm_isp_update_deliver_count(vfe_dev, stream_info,
+ pingpong_bit, done_buf);
+ if (rc) {
+ if (done_buf->is_drop_reconfig == 1)
+ done_buf->is_drop_reconfig = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err_ratelimited("%s:VFE%d get done buf fail\n",
+ __func__, vfe_dev->pdev->id);
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return;
+ }
+
+
+ if ((done_buf->frame_id != frame_id) &&
+ vfe_dev->axi_data.enable_frameid_recovery) {
+ if (done_buf->is_drop_reconfig == 1)
+ done_buf->is_drop_reconfig = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_handle_done_buf_frame_id_mismatch(vfe_dev,
+ stream_info, done_buf, time_stamp, frame_id);
+ return;
+ }
+
+ if (done_buf->is_drop_reconfig == 1) {
+ /* When ping/pong buf is already reconfigured
+ * then dont issue buf-done for current buffer
+ */
+ done_buf->is_drop_reconfig = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_process_done_buf(vfe_dev, stream_info,
+ done_buf, time_stamp, frame_id);
+ }
+}
+
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ int i, rc = 0;
+ 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;
+ int wm;
+
+ 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;
+
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ 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++) {
+ rc = 0;
+ comp_info = &axi_data->composite_info[i];
+ wm_mask &= ~(comp_info->stream_composite_mask);
+ if (comp_mask & (1 << i)) {
+ stream_idx = HANDLE_TO_IDX(comp_info->stream_handle);
+ if ((!comp_info->stream_handle) ||
+ (stream_idx >= VFE_AXI_SRC_MAX)) {
+ pr_err_ratelimited("%s: Invalid handle for composite irq\n",
+ __func__);
+ for (wm = 0; wm < axi_data->hw_info->num_wm;
+ wm++)
+ if (comp_info->stream_composite_mask &
+ (1 << wm))
+ msm_isp_cfg_wm_scratch(vfe_dev,
+ wm, pingpong_status);
+ continue;
+ }
+ stream_idx = HANDLE_TO_IDX(comp_info->stream_handle);
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ stream_idx);
+
+ msm_isp_process_axi_irq_stream(vfe_dev, stream_info,
+ pingpong_status, ts);
+
+ }
+ }
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (wm_mask & (1 << i)) {
+ stream_idx = HANDLE_TO_IDX(axi_data->free_wm[i]);
+ if ((!axi_data->free_wm[i]) ||
+ (stream_idx >= VFE_AXI_SRC_MAX)) {
+ pr_err("%s: Invalid handle for wm irq\n",
+ __func__);
+ msm_isp_cfg_wm_scratch(vfe_dev, i,
+ pingpong_status);
+ continue;
+ }
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ stream_idx);
+
+ msm_isp_process_axi_irq_stream(vfe_dev, stream_info,
+ pingpong_status, ts);
+ }
+ }
+}
+
+void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int i, j;
+ int vfe_idx;
+
+ if (!vfe_dev || !axi_data) {
+ pr_err("%s: error %pK %pK\n", __func__, vfe_dev, axi_data);
+ return;
+ }
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+
+ if (stream_info->state != ACTIVE)
+ continue;
+
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.enable_wm(
+ vfe_dev->vfe_base,
+ stream_info->wm[vfe_idx][j], 0);
+ }
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
new file mode 100644
index 0000000..c0ba7ab
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -0,0 +1,175 @@
+/* Copyright (c) 2013-2018, 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"
+
+#define HANDLE_TO_IDX(handle) (handle & 0xFF)
+#define SRC_TO_INTF(src) \
+ ((src < RDI_INTF_0 || src == VFE_AXI_SRC_MAX) ? VFE_PIX_0 : \
+ (VFE_RAW_0 + src - RDI_INTF_0))
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
+
+void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_start_avtimer(void);
+void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp);
+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);
+int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+int msm_isp_axi_halt(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_halt_cmd *halt_cmd);
+int msm_isp_axi_reset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_reset_cmd *reset_cmd);
+int msm_isp_axi_restart(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_restart_cmd *restart_cmd);
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ struct msm_isp_timestamp *ts);
+
+void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ enum msm_isp_comp_irq_types irq,
+ struct msm_isp_timestamp *ts);
+
+void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts);
+
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+
+void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev);
+
+int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
+ unsigned long fault_addr);
+
+void msm_isp_increment_frame_id(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts);
+
+int msm_isp_drop_frame(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, struct msm_isp_timestamp *ts,
+ struct msm_isp_sof_info *sof_info);
+
+void msm_isp_halt(struct vfe_device *vfe_dev);
+void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event);
+
+void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t pingpong_status,
+ struct msm_isp_timestamp *ts);
+
+void msm_isp_release_all_axi_stream(struct vfe_device *vfe_dev);
+void msm_isp_axi_free_wm(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *ts, struct msm_isp_sof_info *sof_info);
+
+static inline int msm_isp_get_vfe_idx_for_stream_user(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx;
+
+ for (vfe_idx = 0; vfe_idx < stream_info->num_isp; vfe_idx++) {
+ if (stream_info->vfe_dev[vfe_idx] == vfe_dev)
+ return vfe_idx;
+ }
+ return -ENOTTY;
+}
+
+static inline int msm_isp_get_vfe_idx_for_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info);
+
+ if (vfe_idx < 0) {
+ WARN(1, "%s vfe index misssing for stream %d, vfe %d\n",
+ __func__, stream_info->stream_src, vfe_dev->pdev->id);
+ vfe_idx = 0;
+ }
+ return vfe_idx;
+}
+
+static inline void msm_isp_cfg_wm_scratch(struct vfe_device *vfe_dev,
+ int wm,
+ uint32_t pingpong_bit)
+{
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev->vfe_base, wm,
+ pingpong_bit, vfe_dev->buf_mgr->scratch_buf_addr, 0);
+}
+
+static inline void msm_isp_cfg_stream_scratch(
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t pingpong_status)
+{
+ int i;
+ int j;
+ uint32_t pingpong_bit;
+ int vfe_idx;
+
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0][0]) & 0x1);
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (j = 0; j < stream_info->num_isp; j++) {
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ stream_info->vfe_dev[j], stream_info);
+ msm_isp_cfg_wm_scratch(stream_info->vfe_dev[j],
+ stream_info->wm[vfe_idx][i],
+ ~pingpong_bit);
+ }
+ }
+ stream_info->buf[pingpong_bit] = NULL;
+}
+
+static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data(
+ struct vfe_device *vfe_dev, uint32_t stream_idx)
+{
+ struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data;
+ struct msm_vfe_axi_stream *stream_info;
+
+ if (stream_idx >= VFE_AXI_SRC_MAX) {
+ pr_err("invalid stream_idx %d\n", stream_idx);
+ return NULL;
+ }
+
+ if (vfe_dev->is_split && stream_idx < RDI_INTF_0)
+ stream_info = &common_data->streams[stream_idx];
+ else
+ stream_info = &common_data->streams[VFE_AXI_SRC_MAX *
+ vfe_dev->pdev->id + stream_idx];
+ return stream_info;
+}
+
+static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream(
+ struct dual_vfe_resource *dual_vfe_res,
+ int vfe_id, uint32_t index)
+{
+ return msm_isp_get_stream_common_data(dual_vfe_res->vfe_dev[vfe_id],
+ index);
+}
+
+int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ uint32_t buf_idx);
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev,
+ void *arg);
+#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
new file mode 100644
index 0000000..98be3dd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -0,0 +1,1356 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+
+static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status)
+{
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev, stream_info,
+ pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr,
+ SZ_32M);
+}
+
+static inline void msm_isp_stats_cfg_stream_scratch(
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status)
+{
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle[0]);
+ uint32_t pingpong_bit;
+ uint32_t stats_pingpong_offset;
+ struct vfe_device *vfe_dev;
+ int i;
+
+ stats_pingpong_offset = stream_info->vfe_dev[0]->hw_info->
+ stats_hw_info->stats_ping_pong_offset[stats_idx];
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ msm_isp_stats_cfg_wm_scratch(vfe_dev, stream_info,
+ pingpong_status);
+ }
+
+ stream_info->buf[pingpong_bit] = NULL;
+}
+
+static int msm_isp_composite_stats_irq(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ enum msm_isp_comp_irq_types irq)
+{
+ /* interrupt recv on same vfe w/o recv on other vfe */
+ if (stream_info->composite_irq[irq] & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: irq %d out of sync for dual vfe on vfe %d\n",
+ __func__, irq, vfe_dev->pdev->id);
+ return -EFAULT;
+ }
+
+ stream_info->composite_irq[irq] |= (1 << vfe_dev->pdev->id);
+ if (stream_info->composite_irq[irq] != stream_info->vfe_mask)
+ return 1;
+
+ stream_info->composite_irq[irq] = 0;
+
+ return 0;
+}
+
+static int msm_isp_stats_cfg_ping_pong_address(
+ struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf = NULL;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle[0]);
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
+ uint32_t stats_pingpong_offset;
+ uint32_t pingpong_bit;
+ int k;
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type ||
+ stats_idx >= MSM_ISP_STATS_MAX) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+ stats_pingpong_offset = vfe_dev->hw_info->stats_hw_info->
+ stats_ping_pong_offset[stats_idx];
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+ /* if buffer already exists then no need to replace */
+ if (stream_info->buf[pingpong_bit])
+ return 0;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle,
+ MSM_ISP_INVALID_BUF_INDEX, &buf);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ if (rc < 0 || NULL == buf) {
+ for (k = 0; k < stream_info->num_isp; k++)
+ stream_info->vfe_dev[k]->error_info.
+ stats_framedrop_count[stats_idx]++;
+ }
+
+ if (buf && buf->num_planes != 1) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+
+ if (!buf) {
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ pingpong_status);
+ return 0;
+ }
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev, stream_info, pingpong_status,
+ buf->mapped_info[0].paddr +
+ stream_info->buffer_offset[k],
+ buf->mapped_info[0].len);
+ }
+ stream_info->buf[pingpong_bit] = buf;
+ buf->pingpong_bit = pingpong_bit;
+
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *ts,
+ struct msm_isp_event_data *buf_event,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t *comp_stats_type_mask, uint32_t pingpong_status)
+{
+ int32_t rc = 0, frame_id = 0, drop_buffer = 0;
+ struct msm_isp_stats_event *stats_event = NULL;
+ struct msm_isp_sw_framskip *sw_skip = NULL;
+ int32_t buf_index = -1;
+ uint32_t pingpong_bit;
+ struct msm_isp_buffer *done_buf;
+ uint32_t stats_pingpong_offset;
+ uint32_t stats_idx;
+ int vfe_idx;
+ unsigned long flags;
+
+ if (!vfe_dev || !ts || !buf_event || !stream_info) {
+ pr_err("%s:%d failed: invalid params %pK %pK %pK %pK\n",
+ __func__, __LINE__, vfe_dev, ts, buf_event,
+ stream_info);
+ return -EINVAL;
+ }
+ frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ sw_skip = &stream_info->sw_skip;
+ stats_event = &buf_event->u.stats;
+
+ if (sw_skip->stats_type_mask &
+ (1 << stream_info->stats_type)) {
+ /* Hw stream output of this src is requested
+ * for drop
+ */
+ if (sw_skip->skip_mode == SKIP_ALL) {
+ /* drop all buffers */
+ drop_buffer = 1;
+ } else if (sw_skip->skip_mode == SKIP_RANGE &&
+ (sw_skip->min_frame_id <= frame_id &&
+ sw_skip->max_frame_id >= frame_id)) {
+ drop_buffer = 1;
+ } else if (frame_id > sw_skip->max_frame_id) {
+ memset(sw_skip, 0, sizeof
+ (struct msm_isp_sw_framskip));
+ }
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, stream_info);
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+
+ stats_pingpong_offset =
+ vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
+ stats_idx];
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+
+ rc = msm_isp_composite_stats_irq(vfe_dev, stream_info,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE + pingpong_bit);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc < 0)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return rc;
+ }
+
+ done_buf = stream_info->buf[pingpong_bit];
+ /* Program next buffer */
+ stream_info->buf[pingpong_bit] = NULL;
+ rc = msm_isp_stats_cfg_ping_pong_address(stream_info,
+ pingpong_status);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ if (!done_buf)
+ return rc;
+
+ buf_index = done_buf->buf_idx;
+ if (drop_buffer) {
+ vfe_dev->buf_mgr->ops->put_buf(
+ vfe_dev->buf_mgr,
+ done_buf->bufq_handle,
+ done_buf->buf_idx);
+ } else {
+ /* divert native buffers */
+ vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
+ done_buf->bufq_handle, done_buf->buf_idx,
+ &ts->buf_time, frame_id);
+ }
+ stats_event->stats_buf_idxs
+ [stream_info->stats_type] =
+ done_buf->buf_idx;
+
+ stats_event->pd_stats_idx = 0xF;
+ if (stream_info->stats_type == MSM_ISP_STATS_BF) {
+ spin_lock_irqsave(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ stats_event->pd_stats_idx = vfe_dev->common_data->pd_buf_idx;
+ vfe_dev->common_data->pd_buf_idx = 0xF;
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ }
+ if (comp_stats_type_mask == NULL) {
+ stats_event->stats_mask =
+ 1 << stream_info->stats_type;
+ ISP_DBG("%s: stats frameid: 0x%x %d bufq %x\n",
+ __func__, buf_event->frame_id,
+ stream_info->stats_type, done_buf->bufq_handle);
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_STATS_NOTIFY +
+ stream_info->stats_type,
+ buf_event);
+ } else {
+ *comp_stats_type_mask |=
+ 1 << stream_info->stats_type;
+ }
+
+ return rc;
+}
+
+static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
+ uint32_t stats_irq_mask, struct msm_isp_timestamp *ts,
+ bool is_composite)
+{
+ int i, rc = 0;
+ struct msm_isp_event_data buf_event;
+ struct msm_isp_stats_event *stats_event = &buf_event.u.stats;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ uint32_t pingpong_status;
+ uint32_t comp_stats_type_mask = 0;
+ int result = 0;
+
+ memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
+ buf_event.timestamp = ts->event_time;
+ buf_event.mono_timestamp = ts->buf_time;
+
+ buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ pingpong_status = vfe_dev->hw_info->
+ vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ if (!(stats_irq_mask & (1 << i)))
+ continue;
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_INACTIVE ||
+ stream_info->state == STATS_STOPPING) {
+ pr_debug("%s: Warning! Stream already inactive. Drop irq handling\n",
+ __func__);
+ continue;
+ }
+
+ rc = msm_isp_stats_buf_divert(vfe_dev, ts,
+ &buf_event, stream_info,
+ is_composite ? &comp_stats_type_mask : NULL,
+ pingpong_status);
+ if (rc < 0) {
+ pr_err("%s:%d failed: stats buf divert rc %d\n",
+ __func__, __LINE__, rc);
+ result = rc;
+ }
+ }
+ if (is_composite && comp_stats_type_mask) {
+ ISP_DBG("%s:vfe_id %d comp_stats frameid %x,comp_mask %x\n",
+ __func__, vfe_dev->pdev->id, buf_event.frame_id,
+ comp_stats_type_mask);
+ stats_event->stats_mask = comp_stats_type_mask;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_COMP_STATS_NOTIFY, &buf_event);
+ comp_stats_type_mask = 0;
+ }
+ return result;
+}
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ int j, rc;
+ uint32_t atomic_stats_mask = 0;
+ uint32_t stats_comp_mask = 0, stats_irq_mask = 0;
+ bool comp_flag = false;
+ uint32_t num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
+ stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ stats_irq_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(stats_comp_mask || stats_irq_mask))
+ return;
+
+ ISP_DBG("%s: vfe %d status: 0x%x\n", __func__, vfe_dev->pdev->id,
+ irq_status0);
+
+ /* Clear composite mask irq bits, they will be restored by comp mask */
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ stats_irq_mask &= ~atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+ }
+
+ /* Process non-composite irq */
+ if (stats_irq_mask) {
+ rc = msm_isp_stats_configure(vfe_dev, stats_irq_mask, ts,
+ comp_flag);
+ }
+
+ /* Process composite irq */
+ if (stats_comp_mask) {
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ if (!(stats_comp_mask & (1 << j)))
+ continue;
+
+ atomic_stats_mask = atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+
+ rc = msm_isp_stats_configure(vfe_dev, atomic_stats_mask,
+ ts, !comp_flag);
+ }
+ }
+}
+
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int rc = 0;
+ uint32_t stats_idx;
+ uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
+ int i;
+
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
+
+ if (!(vfe_dev->hw_info->stats_hw_info->stats_capability_mask &
+ (1 << stream_req_cmd->stats_type))) {
+ pr_err("%s: Stats type not supported\n", __func__);
+ return rc;
+ }
+
+ if (stream_info->state != STATS_AVAILABLE) {
+ pr_err("%s: Stats already requested\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid framedrop pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid irq subsample pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->composite_flag >
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_req_cmd->composite_flag,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask);
+ return -EINVAL;
+ }
+
+ if (stream_info->num_isp == 0) {
+ stream_info->session_id = stream_req_cmd->session_id;
+ stream_info->stream_id = stream_req_cmd->stream_id;
+ stream_info->composite_flag = stream_req_cmd->composite_flag;
+ stream_info->stats_type = stream_req_cmd->stats_type;
+ framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ if (framedrop_pattern == SKIP_ALL)
+ framedrop_pattern = 0;
+ else
+ framedrop_pattern = 1;
+ stream_info->framedrop_pattern = framedrop_pattern;
+ stream_info->init_stats_frame_drop =
+ stream_req_cmd->init_frame_drop;
+ stream_info->irq_subsample_pattern =
+ stream_req_cmd->irq_subsample_pattern;
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+ stream_info->framedrop_period = framedrop_period;
+ } else {
+ if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: stats %d already requested for vfe %d\n",
+ __func__, stats_idx, vfe_dev->pdev->id);
+ return -EINVAL;
+ }
+ if (stream_info->session_id != stream_req_cmd->session_id)
+ rc = -EINVAL;
+ if (stream_info->session_id != stream_req_cmd->session_id)
+ rc = -EINVAL;
+ if (stream_info->composite_flag !=
+ stream_req_cmd->composite_flag)
+ rc = -EINVAL;
+ if (stream_info->stats_type != stream_req_cmd->stats_type)
+ rc = -EINVAL;
+ framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ if (framedrop_pattern == SKIP_ALL)
+ framedrop_pattern = 0;
+ else
+ framedrop_pattern = 1;
+ if (stream_info->framedrop_pattern != framedrop_pattern)
+ rc = -EINVAL;
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+ if (stream_info->framedrop_period != framedrop_period)
+ rc = -EINVAL;
+ if (rc) {
+ pr_err("%s: Stats stream param mismatch between vfe\n",
+ __func__);
+ return rc;
+ }
+ }
+ stream_info->buffer_offset[stream_info->num_isp] =
+ stream_req_cmd->buffer_offset;
+ stream_info->vfe_dev[stream_info->num_isp] = vfe_dev;
+ stream_info->vfe_mask |= (1 << vfe_dev->pdev->id);
+ stream_info->num_isp++;
+ if (!vfe_dev->is_split || stream_info->num_isp == MAX_VFE) {
+ stream_info->state = STATS_INACTIVE;
+ for (i = 0; i < MSM_ISP_COMP_IRQ_MAX; i++)
+ stream_info->composite_irq[i] = 0;
+ }
+
+ if ((vfe_dev->stats_data.stream_handle_cnt << 8) == 0)
+ vfe_dev->stats_data.stream_handle_cnt++;
+
+ stream_req_cmd->stream_handle =
+ (++vfe_dev->stats_data.stream_handle_cnt) << 8 | stats_idx;
+
+ stream_info->stream_handle[stream_info->num_isp - 1] =
+ stream_req_cmd->stream_handle;
+ return 0;
+}
+
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ uint32_t stats_idx;
+
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx);
+
+ rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd, stream_info);
+ if (rc < 0) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ if (stream_info->init_stats_frame_drop == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
+ stream_info);
+
+ if (stream_info->state == STATS_INACTIVE) {
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ }
+ return rc;
+}
+
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
+ int stats_idx = STATS_IDX(stream_release_cmd->stream_handle);
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ int vfe_idx;
+ int i;
+ int k;
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx);
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(
+ vfe_dev, stream_info);
+ if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
+ stream_release_cmd->stream_handle) {
+ pr_err("%s: Invalid stream handle %x, expected %x\n",
+ __func__, stream_release_cmd->stream_handle,
+ vfe_idx != -ENOTTY ?
+ stream_info->stream_handle[vfe_idx] : 0);
+ return -EINVAL;
+ }
+ if (stream_info->state == STATS_AVAILABLE) {
+ pr_err("%s: stream already release\n", __func__);
+ return rc;
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info);
+
+ if (stream_info->state != STATS_INACTIVE) {
+ stream_cfg_cmd.enable = 0;
+ stream_cfg_cmd.num_streams = 1;
+ stream_cfg_cmd.stream_handle[0] =
+ stream_release_cmd->stream_handle;
+ msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+ }
+
+ for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) {
+ stream_info->vfe_dev[i] = stream_info->vfe_dev[k];
+ stream_info->stream_handle[i] = stream_info->stream_handle[k];
+ stream_info->buffer_offset[i] = stream_info->buffer_offset[k];
+ }
+
+ stream_info->vfe_dev[stream_info->num_isp] = NULL;
+ stream_info->stream_handle[stream_info->num_isp] = 0;
+ stream_info->buffer_offset[stream_info->num_isp] = 0;
+ stream_info->num_isp--;
+ stream_info->vfe_mask &= ~(1 << vfe_dev->pdev->id);
+ if (stream_info->num_isp == 0)
+ stream_info->state = STATS_AVAILABLE;
+
+ return 0;
+}
+
+void msm_isp_stop_all_stats_stream(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_stats_stream *stream_info;
+ int i;
+ int vfe_idx;
+ unsigned long flags;
+
+ stream_cfg_cmd.enable = 0;
+ stream_cfg_cmd.num_streams = 0;
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_cfg_cmd.stream_handle[
+ stream_cfg_cmd.num_streams] =
+ stream_info->stream_handle[vfe_idx];
+ stream_cfg_cmd.num_streams++;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ if (stream_cfg_cmd.num_streams)
+ msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+}
+
+void msm_isp_release_all_stats_stream(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_stats_stream_release_cmd
+ stream_release_cmd[MSM_ISP_STATS_MAX];
+ struct msm_vfe_stats_stream *stream_info;
+ int i;
+ int vfe_idx;
+ int num_stream = 0;
+ unsigned long flags;
+
+ msm_isp_stop_all_stats_stream(vfe_dev);
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == STATS_AVAILABLE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_release_cmd[num_stream++].stream_handle =
+ stream_info->stream_handle[vfe_idx];
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ for (i = 0; i < num_stream; i++)
+ msm_isp_release_stats_stream(vfe_dev, &stream_release_cmd[i]);
+}
+
+static int msm_isp_init_stats_ping_pong_reg(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int rc = 0;
+
+ stream_info->bufq_handle =
+ stream_info->vfe_dev[0]->buf_mgr->ops->get_bufq_handle(
+ stream_info->vfe_dev[0]->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: no buf configured for stream: 0x%x\n",
+ __func__, stream_info->stream_handle[0]);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_stats_cfg_ping_pong_address(
+ stream_info, VFE_PING_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n", __func__);
+ return rc;
+ }
+ rc = msm_isp_stats_cfg_ping_pong_address(
+ stream_info, VFE_PONG_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n", __func__);
+ return rc;
+ }
+ return rc;
+}
+
+static void __msm_isp_update_stats_framedrop_reg(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int k;
+ struct vfe_device *vfe_dev;
+
+ if (!stream_info->init_stats_frame_drop)
+ return;
+ stream_info->init_stats_frame_drop--;
+ if (stream_info->init_stats_frame_drop)
+ return;
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
+ stream_info);
+
+ }
+}
+
+static void __msm_isp_stats_stream_update(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ uint32_t enable = 0;
+ uint8_t comp_flag = 0;
+ int k;
+ struct vfe_device *vfe_dev;
+ int index = STATS_IDX(stream_info->stream_handle[0]);
+
+ switch (stream_info->state) {
+ case STATS_INACTIVE:
+ case STATS_ACTIVE:
+ case STATS_AVAILABLE:
+ break;
+ case STATS_START_PENDING:
+ enable = 1;
+ case STATS_STOP_PENDING:
+ stream_info->state =
+ (stream_info->state == STATS_START_PENDING ?
+ STATS_STARTING : STATS_STOPPING);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(index), enable);
+ comp_flag = stream_info->composite_flag;
+ if (comp_flag) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_comp_mask(vfe_dev, BIT(index),
+ (comp_flag - 1), enable);
+ } else {
+ if (enable)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(vfe_dev,
+ stream_info);
+ else
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_irq_mask(vfe_dev,
+ stream_info);
+ }
+ }
+ break;
+ case STATS_STARTING:
+ stream_info->state = STATS_ACTIVE;
+ complete_all(&stream_info->active_comp);
+ break;
+ case STATS_STOPPING:
+ stream_info->state = STATS_INACTIVE;
+ complete_all(&stream_info->inactive_comp);
+ break;
+ }
+}
+
+
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+ unsigned long flags;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_stats_stream_update(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+}
+
+void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_isp_comp_irq_types irq)
+{
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+ unsigned long flags;
+ int rc;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ rc = msm_isp_composite_stats_irq(vfe_dev, stream_info, irq);
+
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (-EFAULT == rc) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_PING_PONG_MISMATCH);
+ return;
+ }
+ continue;
+ }
+
+ if (irq == MSM_ISP_COMP_IRQ_REG_UPD)
+ __msm_isp_stats_stream_update(stream_info);
+ else if (irq == MSM_ISP_COMP_IRQ_EPOCH &&
+ stream_info->state == STATS_ACTIVE)
+ __msm_isp_update_stats_framedrop_reg(stream_info);
+
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+}
+
+static int msm_isp_stats_wait_for_stream_cfg_done(
+ struct msm_vfe_stats_stream *stream_info,
+ int active)
+{
+ int rc = -1;
+
+ if (active && stream_info->state == STATS_ACTIVE)
+ rc = 0;
+ if (!active && stream_info->state == STATS_INACTIVE)
+ rc = 0;
+ if (rc == 0)
+ return rc;
+
+ rc = wait_for_completion_timeout(active ? &stream_info->active_comp :
+ &stream_info->inactive_comp,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+ if (rc <= 0) {
+ rc = rc ? rc : -ETIMEDOUT;
+ pr_err("%s: wait for stats stream %x idx %d state %d active %d config failed %d\n",
+ __func__, stream_info->stream_id,
+ STATS_IDX(stream_info->stream_handle[0]),
+ stream_info->state, active, rc);
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int msm_isp_stats_wait_for_streams(
+ struct msm_vfe_stats_stream **streams,
+ int num_stream, int active)
+{
+ int rc = 0;
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+
+ for (i = 0; i < num_stream; i++) {
+ stream_info = streams[i];
+ rc |= msm_isp_stats_wait_for_stream_cfg_done(stream_info,
+ active);
+ }
+ return rc;
+}
+
+static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ uint32_t stats_mask = 0, idx;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ struct msm_vfe_stats_stream *stream_info;
+ int k;
+
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev,
+ idx);
+ if (stream_info->state == STATS_AVAILABLE)
+ continue;
+
+ /*
+ * we update cgc after making streams inactive or before
+ * starting streams, so stream should be in inactive state
+ */
+ if (stream_info->state == STATS_INACTIVE)
+ stats_mask |= 1 << idx;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ if (update_vfes[stream_info->vfe_dev[k]->pdev->id])
+ continue;
+ update_vfes[stream_info->vfe_dev[k]->pdev->id] =
+ stream_info->vfe_dev[k];
+ }
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ }
+ }
+ return 0;
+}
+
+int msm_isp_stats_reset(struct vfe_device *vfe_dev)
+{
+ int i = 0, rc = 0;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_isp_timestamp timestamp;
+ unsigned long flags;
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, ×tamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int msm_isp_stats_restart(struct vfe_device *vfe_dev)
+{
+ int i = 0;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ unsigned long flags;
+ int j;
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ for (j = 0; j < MSM_ISP_COMP_IRQ_MAX; j++)
+ stream_info->composite_irq[j] = 0;
+ msm_isp_init_stats_ping_pong_reg(
+ stream_info);
+ for (j = 0; j < stream_info->num_isp; j++) {
+ struct vfe_device *temp_vfe_dev =
+ stream_info->vfe_dev[j];
+ uint8_t comp_flag = stream_info->composite_flag;
+
+ temp_vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ temp_vfe_dev, BIT(i), 1);
+ if (comp_flag)
+ temp_vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_comp_mask(temp_vfe_dev, BIT(i),
+ (comp_flag - 1), 1);
+ else
+ temp_vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(
+ temp_vfe_dev, stream_info);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ return 0;
+}
+
+static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+ uint32_t idx;
+ int vfe_idx;
+ uint32_t stats_idx[MSM_ISP_STATS_MAX];
+
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+ memset(stats_idx, 0, sizeof(stats_idx));
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, idx);
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received expected %x\n",
+ __func__, stream_cfg_cmd->stream_handle[i],
+ vfe_idx == -ENOTTY ? 0 :
+ stream_info->stream_handle[vfe_idx]);
+ return -EINVAL;
+ }
+ /* remove duplicate handles */
+ if (stats_idx[idx] == stream_cfg_cmd->stream_handle[i])
+ stream_cfg_cmd->stream_handle[i] = 0;
+ else
+ stats_idx[idx] = stream_cfg_cmd->stream_handle[i];
+ }
+ return 0;
+}
+
+static void __msm_isp_stop_stats_streams(
+ struct msm_vfe_stats_stream **streams,
+ int num_streams,
+ struct msm_isp_timestamp timestamp)
+{
+ int i;
+ int k;
+ struct msm_vfe_stats_stream *stream_info;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_stats_shared_data *stats_data;
+ unsigned long flags;
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STATS_STOP_PENDING;
+ if (stream_info->vfe_dev[0]->
+ axi_data.src_info[VFE_PIX_0].active == 0) {
+ while (stream_info->state != STATS_INACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
+ }
+ for (k = 0; k < stream_info->num_isp; k++) {
+ stats_data = &stream_info->vfe_dev[k]->stats_data;
+ stats_data->num_active_stream--;
+ }
+
+ msm_isp_stats_cfg_stream_scratch(
+ stream_info, VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(
+ stream_info, VFE_PONG_FLAG);
+ vfe_dev = stream_info->vfe_dev[0];
+ if (vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, ×tamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id ==
+ -EFAULT))
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ if (msm_isp_stats_wait_for_streams(streams, num_streams, 0)) {
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == STATS_INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ while (stream_info->state != STATS_INACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ }
+}
+
+static int msm_isp_check_stats_stream_state(
+ struct msm_vfe_stats_stream *stream_info,
+ int cmd)
+{
+ switch (stream_info->state) {
+ case STATS_AVAILABLE:
+ return -EINVAL;
+ case STATS_INACTIVE:
+ if (cmd == 0)
+ return -EALREADY;
+ break;
+ case STATS_ACTIVE:
+ if (cmd)
+ return -EALREADY;
+ break;
+ default:
+ WARN(1, "Invalid stats state %d\n", stream_info->state);
+ }
+ return 0;
+}
+
+static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, idx;
+ uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = NULL;
+ int num_stream = 0;
+ struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
+ struct msm_isp_timestamp timestamp;
+ unsigned long flags;
+ int k;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ uint32_t num_active_streams[MAX_VFE] = {0, 0};
+ struct vfe_device *vfe_dev;
+
+ msm_isp_get_timestamp(×tamp, vfe_dev_ioctl);
+
+ num_stats_comp_mask =
+ vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask;
+ if (stream_cfg_cmd->num_streams >= MSM_ISP_STATS_MAX)
+ stream_cfg_cmd->num_streams = MSM_ISP_STATS_MAX;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev_ioctl, idx);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = msm_isp_check_stats_stream_state(stream_info, 1);
+ if (rc == -EALREADY) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = 0;
+ continue;
+ }
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ goto error;
+ }
+ rc = msm_isp_init_stats_ping_pong_reg(
+ stream_info);
+ if (rc < 0) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: No buffer for stream%d\n", __func__, idx);
+ return rc;
+ }
+ init_completion(&stream_info->active_comp);
+ stream_info->state = STATS_START_PENDING;
+ if (vfe_dev_ioctl->axi_data.src_info[VFE_PIX_0].active == 0) {
+ while (stream_info->state != STATS_ACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ stats_mask |= 1 << idx;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ stats_data = &vfe_dev->stats_data;
+ if (update_vfes[vfe_dev->pdev->id] == NULL) {
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ num_active_streams[vfe_dev->pdev->id] =
+ stats_data->num_active_stream;
+ }
+ stats_data->num_active_stream++;
+ }
+
+ if (stream_info->composite_flag)
+ comp_stats_mask[stream_info->composite_flag-1] |=
+ 1 << idx;
+
+ ISP_DBG("%s: stats_mask %x %x\n",
+ __func__, comp_stats_mask[0],
+ comp_stats_mask[1]);
+ if (stats_data)
+ ISP_DBG("%s: active_streams = %d\n", __func__,
+ stats_data->num_active_stream);
+ streams[num_stream++] = stream_info;
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k] || num_active_streams[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+ }
+
+ rc = msm_isp_stats_wait_for_streams(streams, num_stream, 1);
+ if (rc)
+ goto error;
+ return 0;
+error:
+ __msm_isp_stop_stats_streams(streams, num_stream, timestamp);
+ return rc;
+}
+
+static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t idx;
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_isp_timestamp timestamp;
+ int num_stream = 0;
+ struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
+ unsigned long flags;
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, idx);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = msm_isp_check_stats_stream_state(stream_info, 0);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = 0;
+ continue;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ streams[num_stream++] = stream_info;
+ }
+
+ __msm_isp_stop_stats_streams(streams, num_stream, timestamp);
+
+ return rc;
+}
+
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+
+ rc = msm_isp_check_stream_cfg_cmd(vfe_dev, stream_cfg_cmd);
+ if (rc)
+ return rc;
+
+ if (stream_cfg_cmd->enable) {
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+
+ rc = msm_isp_start_stats_stream(vfe_dev, stream_cfg_cmd);
+ } else {
+ rc = msm_isp_stop_stats_stream(vfe_dev, stream_cfg_cmd);
+
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+ }
+
+ return rc;
+}
+
+int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
+ struct msm_isp_sw_framskip *sw_skip_info = NULL;
+ int vfe_idx;
+ int k;
+
+ if (update_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s: Invalid num_streams %d\n",
+ __func__, update_cmd->num_streams);
+ return -EINVAL;
+ }
+
+ /*validate request*/
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ /*check array reference bounds*/
+ if (STATS_IDX(update_info->stream_handle)
+ >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s: stats idx %d out of bound!", __func__,
+ STATS_IDX(update_info->stream_handle));
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev,
+ STATS_IDX(update_info->stream_handle));
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
+ update_info->stream_handle) {
+ pr_err("%s: stats stream handle %x %x mismatch!\n",
+ __func__, vfe_idx != -ENOTTY ?
+ stream_info->stream_handle[vfe_idx] : 0,
+ update_info->stream_handle);
+ continue;
+ }
+
+ switch (update_cmd->update_type) {
+ case UPDATE_STREAM_STATS_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ if (update_info->skip_pattern ==
+ SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period;
+ if (stream_info->init_stats_frame_drop == 0)
+ for (k = 0; k < stream_info->num_isp; k++)
+ stream_info->vfe_dev[k]->hw_info->
+ vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
+ break;
+ }
+ case UPDATE_STREAM_SW_FRAME_DROP: {
+ sw_skip_info =
+ &update_info->sw_skip_info;
+ if (!stream_info->sw_skip.stream_src_mask)
+ stream_info->sw_skip = *sw_skip_info;
+
+ if (sw_skip_info->stats_type_mask != 0) {
+ /* No image buffer skip, only stats skip */
+ pr_debug("%s:%x skip type %x mode %d min %d max %d\n",
+ __func__, stream_info->stream_id,
+ sw_skip_info->stats_type_mask,
+ sw_skip_info->skip_mode,
+ sw_skip_info->min_frame_id,
+ sw_skip_info->max_frame_id);
+ stream_info->sw_skip.stats_type_mask =
+ sw_skip_info->stats_type_mask;
+ }
+ break;
+ }
+
+ default:
+ pr_err("%s: Invalid update type\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_stats_disable(struct vfe_device *vfe_dev)
+{
+ int i;
+ unsigned int mask = 0;
+
+ if (!vfe_dev) {
+ pr_err("%s: error NULL ptr\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++)
+ mask |= 1 << i;
+
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(vfe_dev, mask, 0);
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
new file mode 100644
index 0000000..9d5ae8c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2013-2016, 2018, 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"
+#define STATS_IDX(idx) (idx & 0xFF)
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd,
+ struct msm_vfe_stats_stream *stream_info);
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev);
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_update_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);
+void msm_isp_stats_disable(struct vfe_device *vfe_dev);
+int msm_isp_stats_reset(struct vfe_device *vfe_dev);
+int msm_isp_stats_restart(struct vfe_device *vfe_dev);
+void msm_isp_release_all_stats_stream(struct vfe_device *vfe_dev);
+void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_isp_comp_irq_types irq);
+void msm_isp_stop_all_stats_stream(struct vfe_device *vfe_dev);
+
+static inline int msm_isp_get_vfe_idx_for_stats_stream_user(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx;
+
+ for (vfe_idx = 0; vfe_idx < stream_info->num_isp; vfe_idx++)
+ if (stream_info->vfe_dev[vfe_idx] == vfe_dev)
+ return vfe_idx;
+ return -ENOTTY;
+}
+
+static inline int msm_isp_get_vfe_idx_for_stats_stream(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+
+ if (vfe_idx < 0) {
+ WARN(1, "%s vfe index missing for stream %d vfe %d\n",
+ __func__, stream_info->stats_type, vfe_dev->pdev->id);
+ vfe_idx = 0;
+ }
+ return vfe_idx;
+}
+
+static inline struct msm_vfe_stats_stream *
+ msm_isp_get_stats_stream_common_data(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type idx)
+{
+ if (vfe_dev->is_split)
+ return &vfe_dev->common_data->stats_streams[idx];
+ else
+ return &vfe_dev->common_data->stats_streams[idx +
+ MSM_ISP_STATS_MAX * vfe_dev->pdev->id];
+}
+
+static inline struct msm_vfe_stats_stream *
+ msm_isp_get_stats_stream(struct dual_vfe_resource *dual_vfe_res,
+ int vfe_id,
+ enum msm_isp_stats_type idx)
+{
+ return msm_isp_get_stats_stream_common_data(
+ dual_vfe_res->vfe_dev[vfe_id], idx);
+}
+#endif /* __MSM_ISP_STATS_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
new file mode 100644
index 0000000..9959f70
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -0,0 +1,2501 @@
+/* Copyright (c) 2013-2018, 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/io.h>
+#include <media/v4l2-subdev.h>
+#include <linux/ratelimit.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"
+#include "cam_smmu_api.h"
+#include "msm_isp48.h"
+#define CREATE_TRACE_POINTS
+#include "trace/events/msm_cam.h"
+
+
+#define MAX_ISP_V4l2_EVENTS 100
+#define MAX_ISP_REG_LIST 100
+static DEFINE_MUTEX(bandwidth_mgr_mutex);
+static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
+
+#define MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev) { \
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) { \
+ struct vfe_device *vfe1_dev = vfe_dev->common_data-> \
+ dual_vfe_res->vfe_dev[ISP_VFE1]; \
+ mutex_lock(&vfe1_dev->core_mutex); \
+ } \
+}
+
+#define MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev) { \
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) { \
+ struct vfe_device *vfe1_dev = vfe_dev->common_data-> \
+ dual_vfe_res->vfe_dev[ISP_VFE1]; \
+ mutex_unlock(&vfe1_dev->core_mutex); \
+ } \
+}
+
+static uint64_t msm_isp_cpp_clk_rate;
+
+#define VFE40_8974V2_VERSION 0x1001001A
+
+void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format)
+{
+ int i;
+ char text[5];
+
+ text[4] = '\0';
+ for (i = 0; i < 4; i++) {
+ text[i] = (char)(((fourcc_format) >> (i * 8)) & 0xFF);
+ if ((text[i] < '0') || (text[i] > 'z')) {
+ pr_err("%s: Invalid output format %d (unprintable)\n",
+ origin, fourcc_format);
+ return;
+ }
+ }
+ pr_err("%s: Invalid output format %s\n",
+ origin, text);
+}
+
+int msm_isp_init_bandwidth_mgr(struct vfe_device *vfe_dev,
+ enum msm_isp_hw_client client)
+{
+ int rc = 0;
+
+ mutex_lock(&bandwidth_mgr_mutex);
+ if (isp_bandwidth_mgr.client_info[client].active) {
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return rc;
+ }
+ isp_bandwidth_mgr.client_info[client].active = 1;
+ isp_bandwidth_mgr.use_count++;
+ if (vfe_dev && !isp_bandwidth_mgr.bus_client) {
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.init_bw_mgr(vfe_dev,
+ &isp_bandwidth_mgr);
+ if (!rc) {
+ isp_bandwidth_mgr.update_bw =
+ vfe_dev->hw_info->vfe_ops.platform_ops.update_bw;
+ isp_bandwidth_mgr.deinit_bw_mgr =
+ vfe_dev->hw_info->vfe_ops.platform_ops.deinit_bw_mgr;
+ }
+ }
+ if (rc) {
+ isp_bandwidth_mgr.use_count--;
+ isp_bandwidth_mgr.client_info[client].active = 0;
+ }
+
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return rc;
+}
+
+int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
+ uint64_t ab, uint64_t ib)
+{
+ int rc;
+
+ mutex_lock(&bandwidth_mgr_mutex);
+ if (!isp_bandwidth_mgr.use_count ||
+ !isp_bandwidth_mgr.bus_client) {
+ pr_err("%s:error bandwidth manager inactive use_cnt:%d bus_clnt:%d\n",
+ __func__, isp_bandwidth_mgr.use_count,
+ isp_bandwidth_mgr.bus_client);
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return -EINVAL;
+ }
+
+ isp_bandwidth_mgr.client_info[client].ab = ab;
+ isp_bandwidth_mgr.client_info[client].ib = ib;
+ rc = isp_bandwidth_mgr.update_bw(&isp_bandwidth_mgr);
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return 0;
+}
+
+void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client)
+{
+ if (client >= MAX_ISP_CLIENT) {
+ pr_err("invalid Client id %d", client);
+ return;
+ }
+ mutex_lock(&bandwidth_mgr_mutex);
+ memset(&isp_bandwidth_mgr.client_info[client], 0,
+ sizeof(struct msm_isp_bandwidth_info));
+ if (isp_bandwidth_mgr.use_count) {
+ isp_bandwidth_mgr.use_count--;
+ if (isp_bandwidth_mgr.use_count) {
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return;
+ }
+
+ if (!isp_bandwidth_mgr.bus_client) {
+ pr_err("%s:%d error: bus client invalid\n",
+ __func__, __LINE__);
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return;
+ }
+
+ isp_bandwidth_mgr.deinit_bw_mgr(
+ &isp_bandwidth_mgr);
+ }
+ mutex_unlock(&bandwidth_mgr_mutex);
+}
+
+void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev,
+ struct msm_isp_statistics *stats)
+{
+ stats->isp_vfe0_active = isp_bandwidth_mgr.client_info[ISP_VFE0].active;
+ stats->isp_vfe0_ab = isp_bandwidth_mgr.client_info[ISP_VFE0].ab;
+ stats->isp_vfe0_ib = isp_bandwidth_mgr.client_info[ISP_VFE0].ib;
+
+ stats->isp_vfe1_active = isp_bandwidth_mgr.client_info[ISP_VFE1].active;
+ stats->isp_vfe1_ab = isp_bandwidth_mgr.client_info[ISP_VFE1].ab;
+ stats->isp_vfe1_ib = isp_bandwidth_mgr.client_info[ISP_VFE1].ib;
+
+ stats->isp_cpp_active = isp_bandwidth_mgr.client_info[ISP_CPP].active;
+ stats->isp_cpp_ab = isp_bandwidth_mgr.client_info[ISP_CPP].ab;
+ stats->isp_cpp_ib = isp_bandwidth_mgr.client_info[ISP_CPP].ib;
+ stats->last_overflow_ab = vfe_dev->msm_isp_last_overflow_ab;
+ stats->last_overflow_ib = vfe_dev->msm_isp_last_overflow_ib;
+ stats->vfe_clk_rate = vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate;
+ stats->cpp_clk_rate = msm_isp_cpp_clk_rate;
+}
+
+void msm_isp_util_update_clk_rate(long clock_rate)
+{
+ msm_isp_cpp_clk_rate = clock_rate;
+}
+
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern)
+{
+ switch (frame_skip_pattern) {
+ case NO_SKIP:
+ case EVERY_2FRAME:
+ case EVERY_3FRAME:
+ case EVERY_4FRAME:
+ case EVERY_5FRAME:
+ case EVERY_6FRAME:
+ case EVERY_7FRAME:
+ case EVERY_8FRAME:
+ return frame_skip_pattern + 1;
+ case EVERY_16FRAME:
+ return 16;
+ case EVERY_32FRAME:
+ return 32;
+ case SKIP_ALL:
+ return SKIP_ALL;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
+void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp,
+ struct vfe_device *vfe_dev)
+{
+ struct timespec ts;
+
+ do_gettimeofday(&(time_stamp->event_time));
+ if (vfe_dev->vt_enable) {
+ msm_isp_get_avtimer_ts(time_stamp);
+ time_stamp->buf_time.tv_sec = time_stamp->vt_time.tv_sec;
+ time_stamp->buf_time.tv_usec = time_stamp->vt_time.tv_usec;
+ } else {
+ get_monotonic_boottime(&ts);
+ time_stamp->buf_time.tv_sec = ts.tv_sec;
+ time_stamp->buf_time.tv_usec = ts.tv_nsec/1000;
+ }
+}
+
+static inline u32 msm_isp_evt_mask_to_isp_event(u32 evt_mask)
+{
+ u32 evt_id = ISP_EVENT_SUBS_MASK_NONE;
+
+ switch (evt_mask) {
+ case ISP_EVENT_MASK_INDEX_STATS_NOTIFY:
+ evt_id = ISP_EVENT_STATS_NOTIFY;
+ break;
+ case ISP_EVENT_MASK_INDEX_ERROR:
+ evt_id = ISP_EVENT_ERROR;
+ break;
+ case ISP_EVENT_MASK_INDEX_IOMMU_P_FAULT:
+ evt_id = ISP_EVENT_IOMMU_P_FAULT;
+ break;
+ case ISP_EVENT_MASK_INDEX_STREAM_UPDATE_DONE:
+ evt_id = ISP_EVENT_STREAM_UPDATE_DONE;
+ break;
+ case ISP_EVENT_MASK_INDEX_REG_UPDATE:
+ evt_id = ISP_EVENT_REG_UPDATE;
+ break;
+ case ISP_EVENT_MASK_INDEX_SOF:
+ evt_id = ISP_EVENT_SOF;
+ break;
+ case ISP_EVENT_MASK_INDEX_BUF_DIVERT:
+ evt_id = ISP_EVENT_BUF_DIVERT;
+ break;
+ case ISP_EVENT_MASK_INDEX_BUF_DONE:
+ evt_id = ISP_EVENT_BUF_DONE;
+ break;
+ case ISP_EVENT_MASK_INDEX_COMP_STATS_NOTIFY:
+ evt_id = ISP_EVENT_COMP_STATS_NOTIFY;
+ break;
+ case ISP_EVENT_MASK_INDEX_MASK_FE_READ_DONE:
+ evt_id = ISP_EVENT_FE_READ_DONE;
+ break;
+ case ISP_EVENT_MASK_INDEX_PING_PONG_MISMATCH:
+ evt_id = ISP_EVENT_PING_PONG_MISMATCH;
+ break;
+ case ISP_EVENT_MASK_INDEX_REG_UPDATE_MISSING:
+ evt_id = ISP_EVENT_REG_UPDATE_MISSING;
+ break;
+ case ISP_EVENT_MASK_INDEX_BUF_FATAL_ERROR:
+ evt_id = ISP_EVENT_BUF_FATAL_ERROR;
+ break;
+ default:
+ evt_id = ISP_EVENT_SUBS_MASK_NONE;
+ break;
+ }
+
+ return evt_id;
+}
+
+static inline int msm_isp_subscribe_event_mask(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub, int evt_mask_index,
+ u32 evt_id, bool subscribe_flag)
+{
+ int rc = 0, i, interface;
+
+ if (evt_mask_index == ISP_EVENT_MASK_INDEX_STATS_NOTIFY) {
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ sub->type = evt_id + i;
+ if (subscribe_flag)
+ rc = v4l2_event_subscribe(fh, sub,
+ MAX_ISP_V4l2_EVENTS, NULL);
+ else
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (rc != 0) {
+ pr_err("%s: Subs event_type =0x%x failed\n",
+ __func__, sub->type);
+ return rc;
+ }
+ }
+ } else if (evt_mask_index == ISP_EVENT_MASK_INDEX_SOF ||
+ evt_mask_index == ISP_EVENT_MASK_INDEX_REG_UPDATE ||
+ evt_mask_index == ISP_EVENT_MASK_INDEX_STREAM_UPDATE_DONE) {
+ for (interface = 0; interface < VFE_SRC_MAX; interface++) {
+ sub->type = evt_id | interface;
+ if (subscribe_flag)
+ rc = v4l2_event_subscribe(fh, sub,
+ MAX_ISP_V4l2_EVENTS, NULL);
+ else
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (rc != 0) {
+ pr_err("%s: Subs event_type =0x%x failed\n",
+ __func__, sub->type);
+ return rc;
+ }
+ }
+ } else {
+ sub->type = evt_id;
+ if (subscribe_flag)
+ rc = v4l2_event_subscribe(fh, sub,
+ MAX_ISP_V4l2_EVENTS, NULL);
+ else
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (rc != 0) {
+ pr_err("%s: Subs event_type =0x%x failed\n",
+ __func__, sub->type);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static inline int msm_isp_process_event_subscription(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub, bool subscribe_flag)
+{
+ int rc = 0, evt_mask_index = 0;
+ u32 evt_mask = sub->type;
+ u32 evt_id = 0;
+
+ if (evt_mask == ISP_EVENT_SUBS_MASK_NONE) {
+ pr_err("%s: Subs event_type is None=0x%x\n",
+ __func__, evt_mask);
+ return 0;
+ }
+
+ for (evt_mask_index = ISP_EVENT_MASK_INDEX_STATS_NOTIFY;
+ evt_mask_index <= ISP_EVENT_MASK_INDEX_BUF_FATAL_ERROR;
+ evt_mask_index++) {
+ if (evt_mask & (1<<evt_mask_index)) {
+ evt_id = msm_isp_evt_mask_to_isp_event(evt_mask_index);
+ rc = msm_isp_subscribe_event_mask(fh, sub,
+ evt_mask_index, evt_id, subscribe_flag);
+ if (rc != 0) {
+ pr_err("%s: Subs event index:%d failed\n",
+ __func__, evt_mask_index);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return msm_isp_process_event_subscription(fh, sub, true);
+}
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return msm_isp_process_event_subscription(fh, sub, false);
+}
+
+static int msm_isp_start_fetch_engine(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ struct msm_vfe_fetch_eng_start *fe_cfg = arg;
+ /*
+ * For Offline VFE, HAL expects same frame id
+ * for offline output which it requested in do_reprocess.
+ */
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id =
+ fe_cfg->frame_id;
+ return vfe_dev->hw_info->vfe_ops.core_ops.
+ start_fetch_eng(vfe_dev, arg);
+}
+
+static int msm_isp_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ struct msm_vfe_fetch_eng_multi_pass_start *fe_cfg = arg;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+ int i = 0, rc;
+ uint32_t wm_reload_mask = 0;
+ int vfe_idx;
+ /*
+ * For Offline VFE, HAL expects same frame id
+ * for offline output which it requested in do_reprocess.
+ */
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id =
+ fe_cfg->frame_id;
+ if (fe_cfg->offline_pass == OFFLINE_SECOND_PASS) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(fe_cfg->output_stream_id));
+ if (stream_info == NULL) {
+ pr_err("%s: Error in Offline process\n", __func__);
+ return -EINVAL;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+
+ rc = msm_isp_cfg_offline_ping_pong_address(vfe_dev, stream_info,
+ VFE_PING_FLAG, fe_cfg->output_buf_idx);
+ if (rc < 0) {
+ pr_err("%s: Fetch engine config failed\n", __func__);
+ return -EINVAL;
+ }
+ for (i = 0; i < stream_info->num_planes; i++)
+ wm_reload_mask |= (1 << stream_info->wm[vfe_idx][i]);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
+ VFE_SRC_MAX);
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
+ vfe_dev->vfe_base, wm_reload_mask);
+ }
+ return vfe_dev->hw_info->vfe_ops.core_ops.
+ start_fetch_eng_multi_pass(vfe_dev, arg);
+}
+
+void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
+ struct msm_vfe_fetch_engine_info *fetch_engine_info)
+{
+ struct msm_isp_event_data fe_rd_done_event;
+
+ memset(&fe_rd_done_event, 0, sizeof(struct msm_isp_event_data));
+ fe_rd_done_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ fe_rd_done_event.u.fetch_done.session_id =
+ fetch_engine_info->session_id;
+ fe_rd_done_event.u.fetch_done.stream_id = fetch_engine_info->stream_id;
+ fe_rd_done_event.u.fetch_done.handle = fetch_engine_info->bufq_handle;
+ fe_rd_done_event.u.fetch_done.buf_idx = fetch_engine_info->buf_idx;
+ fe_rd_done_event.u.fetch_done.fd = fetch_engine_info->fd;
+ fe_rd_done_event.u.fetch_done.offline_mode =
+ fetch_engine_info->offline_mode;
+
+ ISP_DBG("%s:VFE%d ISP_EVENT_FE_READ_DONE buf_idx %d\n",
+ __func__, vfe_dev->pdev->id, fetch_engine_info->buf_idx);
+ fetch_engine_info->is_busy = 0;
+ msm_isp_send_event(vfe_dev, ISP_EVENT_FE_READ_DONE, &fe_rd_done_event);
+}
+
+static int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
+ struct msm_vfe_input_cfg *input_cfg)
+{
+ int rc = 0;
+ struct msm_vfe_pix_cfg *pix_cfg = NULL;
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ pr_err("%s: src %d path is active\n", __func__, VFE_PIX_0);
+ return -EINVAL;
+ }
+
+ pix_cfg = &input_cfg->d.pix_cfg;
+ vfe_dev->hvx_cmd = pix_cfg->hvx_cmd;
+ vfe_dev->is_split = input_cfg->d.pix_cfg.is_split;
+
+ vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock =
+ input_cfg->input_pix_clk;
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux =
+ input_cfg->d.pix_cfg.input_mux;
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format =
+ input_cfg->d.pix_cfg.input_format;
+ vfe_dev->axi_data.src_info[VFE_PIX_0].sof_counter_step = 1;
+
+ /*
+ * Fill pixel_clock into input_pix_clk so that user space
+ * can use rounded clk rate
+ */
+ input_cfg->input_pix_clk =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock;
+
+ ISP_DBG("%s: input mux is %d CAMIF %d io_format 0x%x\n", __func__,
+ input_cfg->d.pix_cfg.input_mux, CAMIF,
+ input_cfg->d.pix_cfg.input_format);
+
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF ||
+ input_cfg->d.pix_cfg.input_mux == TESTGEN) {
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == TESTGEN)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.testgen_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.camif_cfg.subsample_cfg.
+ sof_counter_step > 0) {
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ sof_counter_step = input_cfg->d.pix_cfg.
+ camif_cfg.subsample_cfg.sof_counter_step;
+ }
+ } else if (input_cfg->d.pix_cfg.input_mux == EXTERNAL_READ) {
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.fetch_engine_cfg.buf_stride;
+ }
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_input_mux(
+ vfe_dev, &input_cfg->d.pix_cfg);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, VFE_PIX_0);
+ return rc;
+}
+
+static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev,
+ struct msm_vfe_input_cfg *input_cfg)
+{
+ int rc = 0;
+
+ if (vfe_dev->axi_data.src_info[input_cfg->input_src].active) {
+ pr_err("%s: RAW%d path is active\n", __func__,
+ input_cfg->input_src - VFE_RAW_0);
+ return -EINVAL;
+ }
+
+ vfe_dev->axi_data.
+ src_info[input_cfg->input_src].sof_counter_step = 1;
+
+ vfe_dev->axi_data.src_info[input_cfg->input_src].pixel_clock =
+ input_cfg->input_pix_clk;
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_rdi_reg(
+ vfe_dev, &input_cfg->d.rdi_cfg, input_cfg->input_src);
+ 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;
+ long pixel_clock = 0;
+
+ switch (input_cfg->input_src) {
+ case VFE_PIX_0:
+ rc = msm_isp_cfg_pix(vfe_dev, input_cfg);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ rc = msm_isp_cfg_rdi(vfe_dev, input_cfg);
+ break;
+ default:
+ pr_err("%s: Invalid input source\n", __func__);
+ rc = -EINVAL;
+ }
+
+ pixel_clock = input_cfg->input_pix_clk;
+ /*
+ * Only set rate to higher, do not lower higher
+ * rate needed by another input
+ */
+ if (pixel_clock > vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate) {
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(
+ vfe_dev,
+ &pixel_clock);
+ if (rc < 0) {
+ pr_err("%s: clock set rate failed\n", __func__);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int msm_isp_dual_hw_master_slave_sync(struct vfe_device *vfe_dev,
+ void *arg)
+{
+ int rc = 0;
+
+ struct msm_isp_dual_hw_master_slave_sync *link = arg;
+ unsigned long flags;
+ struct master_slave_resource_info *ms_res =
+ &vfe_dev->common_data->ms_resource;
+ int i;
+ struct msm_vfe_src_info *src_info = NULL;
+
+ spin_lock_irqsave(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ ms_res->dual_sync_mode = link->sync_mode;
+ if (ms_res->dual_sync_mode == MSM_ISP_DUAL_CAM_ASYNC) {
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ src_info = ms_res->src_info[i];
+ if (src_info->dual_hw_ms_info.sync_state ==
+ MSM_ISP_DUAL_CAM_ASYNC)
+ continue;
+ ms_res->active_src_mask &= ~(1 <<
+ src_info->dual_hw_ms_info.index);
+ ms_res->src_sof_mask &= ~(1 <<
+ src_info->dual_hw_ms_info.index);
+ src_info->dual_hw_ms_info.sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ }
+ }
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ return rc;
+}
+
+static int msm_isp_set_dual_HW_master_slave_mode(
+ struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_isp_set_dual_hw_ms_cmd *dual_hw_ms_cmd = NULL;
+ struct msm_vfe_src_info *src_info = NULL;
+ unsigned long flags;
+ struct master_slave_resource_info *ms_res =
+ &vfe_dev->common_data->ms_resource;
+
+ if (!vfe_dev || !arg) {
+ pr_err("%s: Error! Invalid input vfe_dev %pK arg %pK\n",
+ __func__, vfe_dev, arg);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&vfe_dev->common_data->common_dev_data_lock, flags);
+ dual_hw_ms_cmd = (struct msm_isp_set_dual_hw_ms_cmd *)arg;
+ vfe_dev->common_data->ms_resource.dual_hw_type = DUAL_HW_MASTER_SLAVE;
+ vfe_dev->vfe_ub_policy = MSM_WM_UB_EQUAL_SLICING;
+ if (dual_hw_ms_cmd->primary_intf < VFE_SRC_MAX) {
+ ISP_DBG("%s: vfe %d primary_intf %d\n", __func__,
+ vfe_dev->pdev->id, dual_hw_ms_cmd->primary_intf);
+ src_info = &vfe_dev->axi_data.
+ src_info[dual_hw_ms_cmd->primary_intf];
+ src_info->dual_hw_type = DUAL_HW_MASTER_SLAVE;
+ src_info->dual_hw_ms_info.dual_hw_ms_type =
+ dual_hw_ms_cmd->dual_hw_ms_type;
+ src_info->dual_hw_ms_info.index = dual_hw_ms_cmd->
+ primary_intf + VFE_SRC_MAX * vfe_dev->pdev->id;
+ ms_res->src_info[src_info->dual_hw_ms_info.index] = src_info;
+ ms_res->num_src++;
+ if (dual_hw_ms_cmd->dual_hw_ms_type == MS_TYPE_MASTER) {
+ ms_res->master_index = src_info->dual_hw_ms_info.index;
+ ms_res->sof_delta_threshold =
+ dual_hw_ms_cmd->sof_delta_threshold;
+ } else {
+ ms_res->primary_slv_idx =
+ src_info->dual_hw_ms_info.index;
+ }
+ }
+ ISP_DBG("%s: vfe %d num_src %d\n", __func__, vfe_dev->pdev->id,
+ dual_hw_ms_cmd->num_src);
+ if (dual_hw_ms_cmd->num_src > VFE_SRC_MAX) {
+ pr_err("%s: Error! Invalid num_src %d\n", __func__,
+ dual_hw_ms_cmd->num_src);
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ return -EINVAL;
+ }
+ /* This for loop is for non-primary intf to be marked with Master/Slave
+ * in order for frame id sync. But their timestamp is not saved.
+ * So no sof_info resource is allocated
+ */
+ for (i = 0; i < dual_hw_ms_cmd->num_src; i++) {
+ if (dual_hw_ms_cmd->input_src[i] >= VFE_SRC_MAX) {
+ pr_err("%s: Error! Invalid SRC param %d\n", __func__,
+ dual_hw_ms_cmd->input_src[i]);
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ return -EINVAL;
+ }
+ ISP_DBG("%s: vfe %d src %d type %d\n", __func__,
+ vfe_dev->pdev->id, dual_hw_ms_cmd->input_src[i],
+ dual_hw_ms_cmd->dual_hw_ms_type);
+ src_info = &vfe_dev->axi_data.
+ src_info[dual_hw_ms_cmd->input_src[i]];
+ src_info->dual_hw_type = DUAL_HW_MASTER_SLAVE;
+ src_info->dual_hw_ms_info.dual_hw_ms_type =
+ dual_hw_ms_cmd->dual_hw_ms_type;
+ src_info->dual_hw_ms_info.index = dual_hw_ms_cmd->
+ input_src[i] + VFE_SRC_MAX * vfe_dev->pdev->id;
+ ms_res->src_info[src_info->dual_hw_ms_info.index] = src_info;
+ ms_res->num_src++;
+ }
+ spin_unlock_irqrestore(&vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ return rc;
+}
+
+static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ uint32_t count = 0;
+ struct msm_vfe_cfg_cmd_list *proc_cmd =
+ (struct msm_vfe_cfg_cmd_list *)arg;
+ struct msm_vfe_cfg_cmd_list cmd, cmd_next;
+
+ if (!vfe_dev || !arg) {
+ pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__,
+ vfe_dev, arg);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd->cfg_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = *proc_cmd;
+
+ while (cmd.next) {
+ if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list)) {
+ pr_err("%s:%d failed: next size %u != expected %zu\n",
+ __func__, __LINE__, cmd.next_size,
+ sizeof(struct msm_vfe_cfg_cmd_list));
+ break;
+ }
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&cmd_next, (void __user *)cmd.next,
+ sizeof(struct msm_vfe_cfg_cmd_list))) {
+ rc = -EFAULT;
+ continue;
+ }
+
+ rc = msm_isp_proc_cmd(vfe_dev, &cmd_next.cfg_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = cmd_next;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_vfe_cfg_cmd2_32 {
+ uint16_t num_cfg;
+ uint16_t cmd_len;
+ compat_caddr_t cfg_data;
+ compat_caddr_t cfg_cmd;
+};
+
+struct msm_vfe_cfg_cmd_list_32 {
+ struct msm_vfe_cfg_cmd2_32 cfg_cmd;
+ compat_caddr_t next;
+ uint32_t next_size;
+};
+
+#define VIDIOC_MSM_VFE_REG_CFG_COMPAT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2_32)
+#define VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+14, struct msm_vfe_cfg_cmd_list_32)
+
+static void msm_isp_compat_to_proc_cmd(struct msm_vfe_cfg_cmd2 *proc_cmd,
+ struct msm_vfe_cfg_cmd2_32 *proc_cmd_ptr32)
+{
+ proc_cmd->num_cfg = proc_cmd_ptr32->num_cfg;
+ proc_cmd->cmd_len = proc_cmd_ptr32->cmd_len;
+ proc_cmd->cfg_data = compat_ptr(proc_cmd_ptr32->cfg_data);
+ proc_cmd->cfg_cmd = compat_ptr(proc_cmd_ptr32->cfg_cmd);
+}
+
+static int msm_isp_proc_cmd_list_compat(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ uint32_t count = 0;
+ struct msm_vfe_cfg_cmd_list_32 *proc_cmd =
+ (struct msm_vfe_cfg_cmd_list_32 *)arg;
+ struct msm_vfe_cfg_cmd_list_32 cmd, cmd_next;
+ struct msm_vfe_cfg_cmd2 current_cmd;
+
+ if (!vfe_dev || !arg) {
+ pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__,
+ vfe_dev, arg);
+ return -EINVAL;
+ }
+ msm_isp_compat_to_proc_cmd(¤t_cmd, &proc_cmd->cfg_cmd);
+ rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = *proc_cmd;
+
+ while (compat_ptr(cmd.next) != NULL) {
+ if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list_32)) {
+ pr_err("%s:%d failed: next size %u != expected %zu\n",
+ __func__, __LINE__, cmd.next_size,
+ sizeof(struct msm_vfe_cfg_cmd_list));
+ break;
+ }
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&cmd_next, compat_ptr(cmd.next),
+ sizeof(struct msm_vfe_cfg_cmd_list_32))) {
+ rc = -EFAULT;
+ continue;
+ }
+
+ msm_isp_compat_to_proc_cmd(¤t_cmd, &cmd_next.cfg_cmd);
+ rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = cmd_next;
+ }
+ return rc;
+}
+
+static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg)
+{
+ if (is_compat_task())
+ return msm_isp_proc_cmd_list_compat(vfe_dev, arg);
+ else
+ return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg);
+}
+#else /* CONFIG_COMPAT */
+static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg)
+{
+ return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc = 0;
+ long rc2 = 0;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ if (!vfe_dev || !vfe_dev->vfe_base) {
+ pr_err("%s:%d failed: invalid params %pK\n",
+ __func__, __LINE__, vfe_dev);
+ if (vfe_dev)
+ pr_err("%s:%d failed %pK\n", __func__,
+ __LINE__, vfe_dev->vfe_base);
+ return -EINVAL;
+ }
+
+ /* use real time mutex for hard real-time ioctls such as
+ * buffer operations and register updates.
+ * Use core mutex for other ioctls that could take
+ * longer time to complete such as start/stop ISP streams
+ * which blocks until the hardware start/stop streaming
+ */
+ ISP_DBG("%s: cmd: %d\n", __func__, _IOC_TYPE(cmd));
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_VFE_REG_LIST_CFG: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd_list(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_BUF:
+ case VIDIOC_MSM_ISP_REQUEST_BUF_VER2:
+ /* fallthrough */
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF:
+ /* fallthrough */
+ case VIDIOC_MSM_ISP_DEQUEUE_BUF:
+ /* fallthrough */
+ case VIDIOC_MSM_ISP_UNMAP_BUF: {
+ mutex_lock(&vfe_dev->buf_mgr->lock);
+ rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
+ mutex_unlock(&vfe_dev->buf_mgr->lock);
+ break;
+ }
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ if (vfe_dev->buf_mgr == NULL) {
+ pr_err("%s: buf mgr NULL! rc = -1\n", __func__);
+ rc = -EINVAL;
+ return rc;
+ }
+ mutex_lock(&vfe_dev->buf_mgr->lock);
+ rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
+ mutex_unlock(&vfe_dev->buf_mgr->lock);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_request_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_release_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_cfg_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_HALT:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_axi_halt(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_RESET:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ != HALT_ENFORCED) {
+ rc = msm_isp_stats_reset(vfe_dev);
+ rc2 = msm_isp_axi_reset(vfe_dev, arg);
+ if (!rc && rc2)
+ rc = rc2;
+ } else {
+ pr_err_ratelimited("%s: no HW reset, halt enforced.\n",
+ __func__);
+ }
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_RESTART:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ != HALT_ENFORCED) {
+ rc = msm_isp_stats_restart(vfe_dev);
+ rc2 = msm_isp_axi_restart(vfe_dev, arg);
+ if (!rc && rc2)
+ rc = rc2;
+ } else {
+ pr_err_ratelimited("%s: no AXI restart, halt enforced.\n",
+ __func__);
+ }
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_INPUT_CFG:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_cfg_input(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AHB_CLK_CFG:
+ mutex_lock(&vfe_dev->core_mutex);
+ if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.
+ ahb_clk_cfg(vfe_dev, arg);
+ else
+ rc = -EOPNOTSUPP;
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_SET_DUAL_HW_MASTER_SLAVE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_set_dual_HW_master_slave_mode(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_DUAL_HW_MASTER_SLAVE_SYNC:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_dual_hw_master_slave_sync(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_ab_ib_update_lpm_mode(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_FETCH_ENG_START:
+ case VIDIOC_MSM_ISP_MAP_BUF_START_FE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_start_fetch_engine(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+
+ case VIDIOC_MSM_ISP_FETCH_ENG_MULTI_PASS_START:
+ case VIDIOC_MSM_ISP_MAP_BUF_START_MULTI_PASS_FE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_start_fetch_engine_multi_pass(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_REG_UPDATE_CMD:
+ if (arg) {
+ enum msm_vfe_input_src frame_src =
+ *((enum msm_vfe_input_src *)arg);
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, frame_src);
+ }
+ break;
+ case VIDIOC_MSM_ISP_SET_SRC_STATE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_set_src_state(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_request_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_release_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_UPDATE_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_update_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_UPDATE_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ rc = msm_isp_update_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_SMMU_ATTACH:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_smmu_attach(vfe_dev->buf_mgr, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ vfe_dev->isp_sof_debug = 0;
+ vfe_dev->isp_raw0_debug = 0;
+ vfe_dev->isp_raw1_debug = 0;
+ vfe_dev->isp_raw2_debug = 0;
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ case MSM_SD_SHUTDOWN:
+ break;
+
+ default:
+ pr_err_ratelimited("%s: Invalid ISP command %d\n", __func__,
+ cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+
+#ifdef CONFIG_COMPAT
+static long msm_isp_ioctl_compat(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ long rc = 0;
+
+ if (!vfe_dev || !vfe_dev->vfe_base) {
+ pr_err("%s:%d failed: invalid params %pK\n",
+ __func__, __LINE__, vfe_dev);
+ if (vfe_dev)
+ pr_err("%s:%d failed %pK\n", __func__,
+ __LINE__, vfe_dev->vfe_base);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG_COMPAT: {
+ struct msm_vfe_cfg_cmd2 proc_cmd;
+
+ mutex_lock(&vfe_dev->realtime_mutex);
+ msm_isp_compat_to_proc_cmd(&proc_cmd,
+ (struct msm_vfe_cfg_cmd2_32 *) arg);
+ rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd_list(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ default:
+ return msm_isp_ioctl_unlocked(sd, cmd, arg);
+ }
+
+ return rc;
+}
+
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ return msm_isp_ioctl_compat(sd, cmd, arg);
+}
+#else /* CONFIG_COMPAT */
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ return msm_isp_ioctl_unlocked(sd, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+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, uint32_t cmd_len)
+{
+ if (!vfe_dev || !reg_cfg_cmd) {
+ pr_err("%s:%d failed: vfe_dev %pK reg_cfg_cmd %pK\n", __func__,
+ __LINE__, vfe_dev, reg_cfg_cmd);
+ return -EINVAL;
+ }
+ if ((reg_cfg_cmd->cmd_type != VFE_CFG_MASK) &&
+ (!cfg_data || !cmd_len)) {
+ pr_err("%s:%d failed: cmd type %d cfg_data %pK cmd_len %d\n",
+ __func__, __LINE__, reg_cfg_cmd->cmd_type, cfg_data,
+ cmd_len);
+ return -EINVAL;
+ }
+
+ /* Validate input parameters */
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE:
+ case VFE_READ:
+ case VFE_WRITE_MB: {
+ if ((reg_cfg_cmd->u.rw_info.reg_offset >
+ (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) ||
+ ((reg_cfg_cmd->u.rw_info.reg_offset +
+ reg_cfg_cmd->u.rw_info.len) >
+ vfe_dev->vfe_base_size) ||
+ (reg_cfg_cmd->u.rw_info.reg_offset & 0x3)) {
+ pr_err_ratelimited("%s:%d regoffset %d len %d res %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ reg_cfg_cmd->u.rw_info.len,
+ (uint32_t)vfe_dev->vfe_base_size);
+ return -EINVAL;
+ }
+
+ if ((reg_cfg_cmd->u.rw_info.cmd_data_offset >
+ (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) ||
+ ((reg_cfg_cmd->u.rw_info.cmd_data_offset +
+ reg_cfg_cmd->u.rw_info.len) > cmd_len)) {
+ pr_err_ratelimited("%s:%d cmd_data_offset %d len %d cmd_len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.rw_info.cmd_data_offset,
+ reg_cfg_cmd->u.rw_info.len, cmd_len);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VFE_WRITE_DMI_16BIT:
+ case VFE_WRITE_DMI_32BIT:
+ case VFE_WRITE_DMI_64BIT:
+ case VFE_READ_DMI_16BIT:
+ case VFE_READ_DMI_32BIT:
+ case VFE_READ_DMI_64BIT: {
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT ||
+ reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ if ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset <=
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset) ||
+ (reg_cfg_cmd->u.dmi_info.hi_tbl_offset -
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset !=
+ (sizeof(uint32_t)))) {
+ pr_err("%s:%d hi %d lo %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset);
+ return -EINVAL;
+ }
+ if (reg_cfg_cmd->u.dmi_info.len <= sizeof(uint32_t)) {
+ pr_err("%s:%d len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.len);
+ return -EINVAL;
+ }
+ if (((UINT_MAX -
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset) <
+ (reg_cfg_cmd->u.dmi_info.len -
+ sizeof(uint32_t))) ||
+ ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset +
+ reg_cfg_cmd->u.dmi_info.len -
+ sizeof(uint32_t)) > cmd_len)) {
+ pr_err("%s:%d hi_tbl_offset %d len %d cmd %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.len, cmd_len);
+ return -EINVAL;
+ }
+ }
+ if ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset >
+ (UINT_MAX - reg_cfg_cmd->u.dmi_info.len)) ||
+ ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset +
+ reg_cfg_cmd->u.dmi_info.len) > cmd_len)) {
+ pr_err("%s:%d lo_tbl_offset %d len %d cmd_len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.len, cmd_len);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE: {
+ msm_camera_io_memcpy(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ reg_cfg_cmd->u.rw_info.len);
+ break;
+ }
+ case VFE_WRITE_MB: {
+ msm_camera_io_memcpy_mb(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ reg_cfg_cmd->u.rw_info.len);
+ break;
+ }
+ case VFE_CFG_MASK: {
+ uint32_t temp;
+ bool grab_lock;
+ unsigned long flags;
+
+ if ((UINT_MAX - sizeof(temp) <
+ reg_cfg_cmd->u.mask_info.reg_offset) ||
+ (vfe_dev->vfe_base_size <
+ reg_cfg_cmd->u.mask_info.reg_offset +
+ sizeof(temp)) ||
+ (reg_cfg_cmd->u.mask_info.reg_offset & 0x3)) {
+ pr_err("%s: VFE_CFG_MASK: Invalid length\n", __func__);
+ return -EINVAL;
+ }
+ grab_lock = vfe_dev->hw_info->vfe_ops.core_ops.
+ is_module_cfg_lock_needed(reg_cfg_cmd->
+ u.mask_info.reg_offset);
+ if (grab_lock)
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ else
+ __acquire(&vfe_dev->shared_data_lock);
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.mask_info.reg_offset);
+
+ temp &= ~reg_cfg_cmd->u.mask_info.mask;
+ temp |= reg_cfg_cmd->u.mask_info.val;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->u.mask_info.reg_offset);
+ if (grab_lock)
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock,
+ flags);
+ else
+ __release(&vfe_dev->shared_data_lock);
+ break;
+ }
+ case VFE_WRITE_DMI_16BIT:
+ case VFE_WRITE_DMI_32BIT:
+ case VFE_WRITE_DMI_64BIT: {
+ int i;
+ uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
+ uint32_t hi_val, lo_val, lo_val1;
+
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT) {
+ hi_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
+ }
+ lo_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT)
+ reg_cfg_cmd->u.dmi_info.len =
+ reg_cfg_cmd->u.dmi_info.len / 2;
+ for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
+ lo_val = *lo_tbl_ptr++;
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) {
+ lo_val1 = lo_val & 0x0000FFFF;
+ lo_val = (lo_val & 0xFFFF0000)>>16;
+ msm_camera_io_w(lo_val1, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ } else if (reg_cfg_cmd->cmd_type ==
+ VFE_WRITE_DMI_64BIT) {
+ lo_tbl_ptr++;
+ hi_val = *hi_tbl_ptr;
+ hi_tbl_ptr = hi_tbl_ptr + 2;
+ msm_camera_io_w(hi_val, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset);
+ }
+ msm_camera_io_w(lo_val, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ }
+ break;
+ }
+ case VFE_READ_DMI_16BIT:
+ case VFE_READ_DMI_32BIT:
+ case VFE_READ_DMI_64BIT: {
+ int i;
+ uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
+ uint32_t hi_val, lo_val, lo_val1;
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ hi_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
+ }
+
+ lo_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT)
+ reg_cfg_cmd->u.dmi_info.len =
+ reg_cfg_cmd->u.dmi_info.len / 2;
+
+ for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
+ lo_val = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_16BIT) {
+ lo_val1 = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ lo_val |= lo_val1 << 16;
+ }
+ *lo_tbl_ptr++ = lo_val;
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ hi_val = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset);
+ *hi_tbl_ptr = hi_val;
+ hi_tbl_ptr += 2;
+ lo_tbl_ptr++;
+ }
+ }
+ break;
+ }
+ case VFE_HW_UPDATE_LOCK: {
+ uint32_t update_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id;
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id != *cfg_data
+ || update_id == *cfg_data) {
+ pr_err("%s hw update lock failed acq %d, cur id %u, last id %u\n",
+ __func__,
+ *cfg_data,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
+ update_id);
+ return -EINVAL;
+ }
+ break;
+ }
+ case VFE_HW_UPDATE_UNLOCK: {
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id
+ != *cfg_data) {
+ pr_err("hw update across frame boundary,begin id %u, end id %d\n",
+ *cfg_data,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ }
+ vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ break;
+ }
+ case VFE_READ: {
+ int i;
+ uint32_t *data_ptr = cfg_data +
+ reg_cfg_cmd->u.rw_info.cmd_data_offset/4;
+ for (i = 0; i < reg_cfg_cmd->u.rw_info.len/4; i++) {
+ if ((data_ptr < cfg_data) ||
+ (UINT_MAX / sizeof(*data_ptr) <
+ (data_ptr - cfg_data)) ||
+ (sizeof(*data_ptr) * (data_ptr - cfg_data) >=
+ cmd_len))
+ return -EINVAL;
+ *data_ptr++ = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset);
+ reg_cfg_cmd->u.rw_info.reg_offset += 4;
+ }
+ break;
+ }
+ case GET_MAX_CLK_RATE: {
+ int rc = 0;
+ unsigned long rate;
+
+ if (cmd_len != sizeof(__u32)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(__u32));
+ return -EINVAL;
+ }
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_max_clk_rate(
+ vfe_dev, &rate);
+ if (rc < 0) {
+ pr_err("%s:%d failed: rc %d\n", __func__, __LINE__, rc);
+ return -EINVAL;
+ }
+
+ *(__u32 *)cfg_data = (__u32)rate;
+
+ break;
+ }
+ case GET_CLK_RATES: {
+ int rc = 0;
+ struct msm_isp_clk_rates rates;
+ struct msm_isp_clk_rates *user_data =
+ (struct msm_isp_clk_rates *)cfg_data;
+ if (cmd_len != sizeof(struct msm_isp_clk_rates)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(struct msm_isp_clk_rates));
+ return -EINVAL;
+ }
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(
+ vfe_dev, &rates);
+ if (rc < 0) {
+ pr_err("%s:%d failed: rc %d\n", __func__, __LINE__, rc);
+ return -EINVAL;
+ }
+ user_data->svs_rate = rates.svs_rate;
+ user_data->nominal_rate = rates.nominal_rate;
+ user_data->high_rate = rates.high_rate;
+ break;
+ }
+ case GET_ISP_ID: {
+ uint32_t *isp_id = NULL;
+
+ if (cmd_len < sizeof(uint32_t)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(uint32_t));
+ return -EINVAL;
+ }
+
+ isp_id = (uint32_t *)cfg_data;
+ *isp_id = vfe_dev->pdev->id;
+ break;
+ }
+ case SET_WM_UB_SIZE:
+ break;
+ case SET_UB_POLICY: {
+
+ if (cmd_len < sizeof(vfe_dev->vfe_ub_policy)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(vfe_dev->vfe_ub_policy));
+ return -EINVAL;
+ }
+ vfe_dev->vfe_ub_policy = *cfg_data;
+ break;
+ }
+ case GET_VFE_HW_LIMIT: {
+ uint32_t *hw_limit = NULL;
+
+ if (cmd_len < sizeof(uint32_t)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(uint32_t));
+ return -EINVAL;
+ }
+
+ hw_limit = (uint32_t *)cfg_data;
+ *hw_limit = vfe_dev->vfe_hw_limit;
+ 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 = NULL;
+
+ if (!proc_cmd->num_cfg) {
+ pr_err("%s: Passed num_cfg as 0\n", __func__);
+ return -EINVAL;
+ }
+
+ reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)*
+ proc_cmd->num_cfg, GFP_KERNEL);
+ if (!reg_cfg_cmd) {
+ rc = -ENOMEM;
+ goto reg_cfg_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 (proc_cmd->cmd_len > 0) {
+ 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(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++)
+ rc = msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i],
+ cfg_data, proc_cmd->cmd_len);
+
+ 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:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ case V4L2_PIX_FMT_GREY:
+ 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:
+ case V4L2_PIX_FMT_SBGGR10DPCM6:
+ case V4L2_PIX_FMT_SGBRG10DPCM6:
+ case V4L2_PIX_FMT_SGRBG10DPCM6:
+ case V4L2_PIX_FMT_SRGGB10DPCM6:
+ case V4L2_PIX_FMT_SBGGR10DPCM8:
+ case V4L2_PIX_FMT_SGBRG10DPCM8:
+ case V4L2_PIX_FMT_SGRBG10DPCM8:
+ case V4L2_PIX_FMT_SRGGB10DPCM8:
+ case V4L2_PIX_FMT_META10:
+ val = CAL_WORD(pixel_per_line, 5, 32);
+ 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, 3, 16);
+ break;
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ val = CAL_WORD(pixel_per_line, 7, 32);
+ break;
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 6);
+ break;
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ val = CAL_WORD(pixel_per_line, 1, 5);
+ break;
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ val = CAL_WORD(pixel_per_line, 1, 4);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CAL_WORD(pixel_per_line, 2, 8);
+ break;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 4);
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ break;
+ }
+ return val;
+}
+
+enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format)
+{
+ switch (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_SBGGR10DPCM6:
+ case V4L2_PIX_FMT_SGBRG10DPCM6:
+ case V4L2_PIX_FMT_SGRBG10DPCM6:
+ case V4L2_PIX_FMT_SRGGB10DPCM6:
+ case V4L2_PIX_FMT_SBGGR10DPCM8:
+ case V4L2_PIX_FMT_SGBRG10DPCM8:
+ case V4L2_PIX_FMT_SGRBG10DPCM8:
+ case V4L2_PIX_FMT_SRGGB10DPCM8:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ return MIPI;
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ return QCOM;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ return PLAIN16;
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ break;
+ }
+ return -EINVAL;
+}
+
+int msm_isp_get_bit_per_pixel(uint32_t output_format)
+{
+ switch (output_format) {
+ case V4L2_PIX_FMT_Y4:
+ return 4;
+ case V4L2_PIX_FMT_Y6:
+ return 6;
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ case V4L2_PIX_FMT_YVU410:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YYUV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV411P:
+ case V4L2_PIX_FMT_Y41P:
+ case V4L2_PIX_FMT_YUV444:
+ case V4L2_PIX_FMT_YUV555:
+ case V4L2_PIX_FMT_YUV565:
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_YUV410:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_PAL8:
+ case V4L2_PIX_FMT_UV8:
+ case MSM_V4L2_PIX_FMT_META:
+ return 8;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR10DPCM6:
+ case V4L2_PIX_FMT_SGBRG10DPCM6:
+ case V4L2_PIX_FMT_SGRBG10DPCM6:
+ case V4L2_PIX_FMT_SRGGB10DPCM6:
+ case V4L2_PIX_FMT_SBGGR10DPCM8:
+ case V4L2_PIX_FMT_SGBRG10DPCM8:
+ case V4L2_PIX_FMT_SGRBG10DPCM8:
+ case V4L2_PIX_FMT_SRGGB10DPCM8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y10BPACK:
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ case V4L2_PIX_FMT_META10:
+ case MSM_V4L2_PIX_FMT_META10:
+ return 10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_Y12:
+ return 12;
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ return 14;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_Y16:
+ return 16;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ return 24;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ pr_err("%s: Invalid output format %x\n",
+ __func__, output_format);
+ return -EINVAL;
+ }
+}
+
+void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
+
+ error_info->info_dump_frame_count++;
+}
+
+
+static int msm_isp_process_iommu_page_fault(struct vfe_device *vfe_dev)
+{
+ int rc = vfe_dev->buf_mgr->pagefault_debug_disable;
+ uint32_t irq_status0, irq_status1;
+ uint32_t overflow_mask;
+ unsigned long irq_flags;
+
+ /* Check if any overflow bit is set */
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_overflow_mask(&overflow_mask);
+ vfe_dev->hw_info->vfe_ops.irq_ops.
+ read_irq_status(vfe_dev, &irq_status0, &irq_status1);
+ overflow_mask &= irq_status1;
+ spin_lock_irqsave(
+ &vfe_dev->common_data->common_dev_data_lock, irq_flags);
+ if (overflow_mask ||
+ atomic_read(&vfe_dev->error_info.overflow_state) !=
+ NO_OVERFLOW) {
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock, irq_flags);
+ pr_err_ratelimited("%s: overflow detected during IOMMU\n",
+ __func__);
+ /* Don't treat the Overflow + Page fault scenario as fatal.
+ * Instead try to do a recovery. Using an existing event as
+ * as opposed to creating a new event.
+ */
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_PING_PONG_MISMATCH);
+ } else {
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock, irq_flags);
+ pr_err("%s:%d] VFE%d Handle Page fault! vfe_dev %pK\n",
+ __func__, __LINE__, vfe_dev->pdev->id, vfe_dev);
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 0);
+ msm_isp_halt_send_error(vfe_dev, ISP_EVENT_IOMMU_P_FAULT);
+ }
+
+ if (vfe_dev->buf_mgr->pagefault_debug_disable == 0) {
+ vfe_dev->buf_mgr->pagefault_debug_disable = 1;
+ vfe_dev->buf_mgr->ops->buf_mgr_debug(vfe_dev->buf_mgr,
+ vfe_dev->page_fault_addr);
+ msm_isp_print_ping_pong_address(vfe_dev,
+ vfe_dev->page_fault_addr);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ read_wm_ping_pong_addr(vfe_dev);
+ }
+ return rc;
+}
+
+void msm_isp_process_error_info(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
+
+ if (error_info->error_count == 1 ||
+ !(error_info->info_dump_frame_count % 100)) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ process_error_status(vfe_dev);
+ error_info->error_mask0 = 0;
+ error_info->error_mask1 = 0;
+ error_info->camif_status = 0;
+ error_info->violation_status = 0;
+ }
+}
+
+static inline void msm_isp_update_error_info(struct vfe_device *vfe_dev,
+ uint32_t error_mask0, uint32_t error_mask1)
+{
+ vfe_dev->error_info.error_mask0 |= error_mask0;
+ vfe_dev->error_info.error_mask1 |= error_mask1;
+ vfe_dev->error_info.error_count++;
+}
+
+int msm_isp_process_overflow_irq(
+ struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1,
+ uint8_t force_overflow)
+{
+ uint32_t overflow_mask;
+ uint32_t bus_err = 0;
+ unsigned long flags;
+
+ /* if there are no active streams - do not start recovery */
+ if (!vfe_dev->axi_data.num_active_stream)
+ return 0;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.
+ get_bus_err_mask)
+ vfe_dev->hw_info->vfe_ops.core_ops.get_bus_err_mask(
+ vfe_dev, &bus_err, irq_status1);
+ /* Mask out all other irqs if recovery is started */
+ if (atomic_read(&vfe_dev->error_info.overflow_state) != NO_OVERFLOW) {
+ uint32_t halt_restart_mask0, halt_restart_mask1;
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_halt_restart_mask(&halt_restart_mask0,
+ &halt_restart_mask1);
+ *irq_status0 &= halt_restart_mask0;
+ *irq_status1 &= halt_restart_mask1;
+
+ return 0;
+ }
+
+ /* Check if any overflow bit is set */
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_overflow_mask(&overflow_mask);
+ overflow_mask &= *irq_status1;
+
+ if (overflow_mask || force_overflow) {
+ struct msm_isp_event_data error_event;
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ spin_lock_irqsave(
+ &vfe_dev->common_data->common_dev_data_lock, flags);
+
+ if (atomic_cmpxchg(&vfe_dev->error_info.overflow_state,
+ NO_OVERFLOW, OVERFLOW_DETECTED)) {
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ return 0;
+ }
+
+ if (vfe_dev->reset_pending == 1) {
+ pr_err_ratelimited("%s:%d overflow %x during reset\n",
+ __func__, __LINE__, overflow_mask);
+ /* Clear overflow bits since reset is pending */
+ *irq_status1 &= ~overflow_mask;
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ return 0;
+ }
+ pr_err_ratelimited("%s: vfe %d overflowmask %x,bus_error %x\n",
+ __func__, vfe_dev->pdev->id, overflow_mask, bus_err);
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (!axi_data->free_wm[i])
+ continue;
+ ISP_DBG("%s:wm %d assigned to stream handle %x\n",
+ __func__, i, axi_data->free_wm[i]);
+ }
+ vfe_dev->recovery_irq0_mask = vfe_dev->irq0_mask;
+ vfe_dev->recovery_irq1_mask = vfe_dev->irq1_mask;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ set_halt_restart_mask(vfe_dev);
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 0);
+ /* mask off other vfe if dual vfe is used */
+ if (vfe_dev->is_split) {
+ int other_vfe_id;
+ struct vfe_device *temp_vfe;
+
+ other_vfe_id = (vfe_dev->pdev->id == ISP_VFE0) ?
+ ISP_VFE1 : ISP_VFE0;
+ temp_vfe = vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[other_vfe_id];
+
+ atomic_set(&temp_vfe->error_info.overflow_state,
+ OVERFLOW_DETECTED);
+ temp_vfe->recovery_irq0_mask = temp_vfe->irq0_mask;
+ temp_vfe->recovery_irq1_mask = temp_vfe->irq1_mask;
+ temp_vfe->hw_info->vfe_ops.core_ops.
+ set_halt_restart_mask(temp_vfe);
+ temp_vfe->hw_info->vfe_ops.axi_ops.halt(temp_vfe, 0);
+ }
+
+ /* reset irq status so skip further process */
+ *irq_status0 = 0;
+ *irq_status1 = 0;
+
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ != HALT_ENFORCED) {
+ memset(&error_event, 0, sizeof(error_event));
+ error_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ error_event.u.error_info.err_type =
+ ISP_ERROR_BUS_OVERFLOW;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_ERROR, &error_event);
+ }
+ spin_unlock_irqrestore(
+ &vfe_dev->common_data->common_dev_data_lock,
+ flags);
+ return 1;
+ }
+ return 0;
+}
+
+void msm_isp_reset_burst_count_and_frame_drop(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info)
+{
+ if (stream_info->state != ACTIVE ||
+ stream_info->stream_type != BURST_STREAM) {
+ return;
+ }
+ if (stream_info->num_burst_capture != 0)
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+}
+
+void msm_isp_prepare_irq_debug_info(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+
+ unsigned long flags;
+ struct msm_vfe_irq_debug_info *irq_debug;
+ uint8_t current_index;
+
+ spin_lock_irqsave(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_irq_dump_lock, flags);
+ /* Fill current VFE debug info */
+ current_index = vfe_dev->common_data->vfe_irq_dump.
+ current_irq_index % MAX_VFE_IRQ_DEBUG_DUMP_SIZE;
+ irq_debug = &vfe_dev->common_data->vfe_irq_dump.
+ irq_debug[current_index];
+ irq_debug->vfe_id = vfe_dev->pdev->id;
+ irq_debug->core_id = smp_processor_id();
+ msm_isp_get_timestamp(&irq_debug->ts, vfe_dev);
+ irq_debug->irq_status0[vfe_dev->pdev->id] = irq_status0;
+ irq_debug->irq_status1[vfe_dev->pdev->id] = irq_status1;
+ irq_debug->ping_pong_status[vfe_dev->pdev->id] =
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_pingpong_status(vfe_dev);
+ if (vfe_dev->is_split &&
+ (vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[!vfe_dev->pdev->id])
+ && (vfe_dev->common_data->dual_vfe_res->
+ vfe_dev[!vfe_dev->pdev->id]->vfe_open_cnt)) {
+ /* Fill other VFE debug Info */
+ vfe_dev->hw_info->vfe_ops.irq_ops.read_irq_status(
+ vfe_dev->common_data->dual_vfe_res->
+ vfe_dev[!vfe_dev->pdev->id],
+ &irq_debug->irq_status0[!vfe_dev->pdev->id],
+ &irq_debug->irq_status1[!vfe_dev->pdev->id]);
+ irq_debug->ping_pong_status[!vfe_dev->pdev->id] =
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_pingpong_status(vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[!vfe_dev->pdev->id]);
+ }
+ vfe_dev->common_data->vfe_irq_dump.current_irq_index++;
+ spin_unlock_irqrestore(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_irq_dump_lock, flags);
+}
+
+void msm_isp_prepare_tasklet_debug_info(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp ts)
+{
+ struct msm_vfe_irq_debug_info *irq_debug;
+ uint8_t current_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_tasklet_dump_lock, flags);
+ current_index = vfe_dev->common_data->vfe_irq_dump.
+ current_tasklet_index % MAX_VFE_IRQ_DEBUG_DUMP_SIZE;
+ irq_debug = &vfe_dev->common_data->vfe_irq_dump.
+ tasklet_debug[current_index];
+ irq_debug->vfe_id = vfe_dev->pdev->id;
+ irq_debug->core_id = smp_processor_id();
+ irq_debug->ts = ts;
+ irq_debug->irq_status0[vfe_dev->pdev->id] = irq_status0;
+ irq_debug->irq_status1[vfe_dev->pdev->id] = irq_status1;
+ irq_debug->ping_pong_status[vfe_dev->pdev->id] =
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_pingpong_status(vfe_dev);
+ vfe_dev->common_data->vfe_irq_dump.current_tasklet_index++;
+ spin_unlock_irqrestore(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_tasklet_dump_lock, flags);
+}
+
+static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ unsigned long flags;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd = NULL;
+ struct msm_vfe_tasklet *tasklet;
+
+ if (vfe_dev->is_split)
+ tasklet = &vfe_dev->common_data->tasklets[MAX_VFE];
+ else
+ tasklet = &vfe_dev->common_data->tasklets[vfe_dev->pdev->id];
+
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ queue_cmd = &tasklet->tasklet_queue_cmd[tasklet->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ pr_err_ratelimited("%s: Tasklet queue overflow: %d\n",
+ __func__, vfe_dev->pdev->id);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ return;
+ }
+ atomic_add(1, &vfe_dev->irq_cnt);
+ queue_cmd->vfeInterruptStatus0 = irq_status0;
+ queue_cmd->vfeInterruptStatus1 = irq_status1;
+ msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev);
+
+ queue_cmd->cmd_used = 1;
+ queue_cmd->vfe_dev = vfe_dev;
+
+ tasklet->taskletq_idx = (tasklet->taskletq_idx + 1) %
+ MSM_VFE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &tasklet->tasklet_q);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ tasklet_schedule(&tasklet->tasklet);
+}
+
+irqreturn_t msm_isp_process_irq(int irq_num, void *data)
+{
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ uint32_t irq_status0, irq_status1;
+ uint32_t error_mask0, error_mask1;
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.
+ read_and_clear_irq_status(vfe_dev, &irq_status0, &irq_status1);
+
+ if ((irq_status0 == 0) && (irq_status1 == 0)) {
+ ISP_DBG("%s:VFE%d irq_status0 & 1 are both 0\n",
+ __func__, vfe_dev->pdev->id);
+ return IRQ_HANDLED;
+ }
+ if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) {
+ vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq(
+ vfe_dev, irq_status0);
+ }
+ if (msm_isp_process_overflow_irq(vfe_dev,
+ &irq_status0, &irq_status1, 0)) {
+ /* if overflow initiated no need to handle the interrupts */
+ pr_err("overflow processed\n");
+ return IRQ_HANDLED;
+ }
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_error_mask(&error_mask0, &error_mask1);
+ error_mask0 &= irq_status0;
+ error_mask1 &= irq_status1;
+ irq_status0 &= ~error_mask0;
+ irq_status1 &= ~error_mask1;
+ if ((error_mask0 != 0) || (error_mask1 != 0))
+ msm_isp_update_error_info(vfe_dev, error_mask0, error_mask1);
+
+ if ((irq_status0 == 0) && (irq_status1 == 0) &&
+ (!(((error_mask0 != 0) || (error_mask1 != 0)) &&
+ vfe_dev->error_info.error_count == 1))) {
+ ISP_DBG("%s: error_mask0/1 & error_count are set!\n", __func__);
+ return IRQ_HANDLED;
+ }
+ msm_isp_prepare_irq_debug_info(vfe_dev, irq_status0, irq_status1);
+ msm_isp_enqueue_tasklet_cmd(vfe_dev, irq_status0, irq_status1);
+
+ return IRQ_HANDLED;
+}
+
+void msm_isp_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct msm_vfe_tasklet *tasklet = (struct msm_vfe_tasklet *)data;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_irq_ops *irq_ops;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct msm_isp_timestamp ts;
+ uint32_t irq_status0, irq_status1;
+
+ while (1) {
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ queue_cmd = list_first_entry_or_null(&tasklet->tasklet_q,
+ struct msm_vfe_tasklet_queue_cmd, list);
+ if (!queue_cmd) {
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ break;
+ }
+ list_del_init(&queue_cmd->list);
+ vfe_dev = queue_cmd->vfe_dev;
+ queue_cmd->cmd_used = 0;
+ queue_cmd->vfe_dev = NULL;
+ irq_status0 = queue_cmd->vfeInterruptStatus0;
+ irq_status1 = queue_cmd->vfeInterruptStatus1;
+ ts = queue_cmd->ts;
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ if (vfe_dev->vfe_open_cnt == 0) {
+ pr_err("%s: VFE%d open cnt = %d, irq %x/%x\n",
+ __func__, vfe_dev->pdev->id, vfe_dev->vfe_open_cnt,
+ irq_status0, irq_status1);
+ continue;
+ }
+ atomic_sub(1, &vfe_dev->irq_cnt);
+ msm_isp_prepare_tasklet_debug_info(vfe_dev,
+ irq_status0, irq_status1, ts);
+ irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
+ irq_ops->process_reset_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_halt_irq(vfe_dev,
+ irq_status0, irq_status1);
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ != NO_OVERFLOW) {
+ ISP_DBG("%s: Recovery in processing, Ignore IRQs!!!\n",
+ __func__);
+ continue;
+ }
+ msm_isp_process_error_info(vfe_dev);
+ irq_ops->process_stats_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_axi_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_camif_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_reg_update(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_epoch_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ }
+}
+
+int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_src_state *src_state = arg;
+
+ if (src_state->input_src >= VFE_SRC_MAX)
+ return -EINVAL;
+ vfe_dev->axi_data.src_info[src_state->input_src].active =
+ src_state->src_active;
+ vfe_dev->axi_data.src_info[src_state->input_src].frame_id =
+ src_state->src_frame_id;
+ return 0;
+}
+
+static void msm_vfe_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token)
+{
+ struct vfe_device *vfe_dev = NULL;
+
+ if (token) {
+ vfe_dev = (struct vfe_device *)token;
+ vfe_dev->page_fault_addr = iova;
+ if (!vfe_dev->buf_mgr || !vfe_dev->buf_mgr->ops ||
+ !vfe_dev->axi_data.num_active_stream) {
+ pr_err("%s:%d buf_mgr %pK active strms %d\n", __func__,
+ __LINE__, vfe_dev->buf_mgr,
+ vfe_dev->axi_data.num_active_stream);
+ goto end;
+ }
+
+ mutex_lock(&vfe_dev->core_mutex);
+ if (vfe_dev->vfe_open_cnt > 0) {
+ pr_err_ratelimited("%s: fault address is %lx\n",
+ __func__, iova);
+ msm_isp_process_iommu_page_fault(vfe_dev);
+ } else {
+ pr_err("%s: no handling, vfe open cnt = %d\n",
+ __func__, vfe_dev->vfe_open_cnt);
+ }
+ mutex_unlock(&vfe_dev->core_mutex);
+ } else {
+ ISP_DBG("%s:%d] no token received: %pK\n",
+ __func__, __LINE__, token);
+ goto end;
+ }
+end:
+ return;
+}
+
+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 = 0;
+
+ ISP_DBG("%s open_cnt %u\n", __func__, vfe_dev->vfe_open_cnt);
+
+ if (vfe_dev->common_data == NULL ||
+ vfe_dev->common_data->dual_vfe_res == NULL) {
+ pr_err("%s: Error in probe. No common_data or dual vfe res\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vfe_dev->realtime_mutex);
+ mutex_lock(&vfe_dev->core_mutex);
+
+ if (vfe_dev->vfe_open_cnt++) {
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+ }
+
+ vfe_dev->reset_pending = 0;
+ vfe_dev->isp_sof_debug = 0;
+ vfe_dev->isp_raw0_debug = 0;
+ vfe_dev->isp_raw1_debug = 0;
+ vfe_dev->isp_raw2_debug = 0;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
+ pr_err("%s: init hardware failed\n", __func__);
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EBUSY;
+ }
+
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.clear_status_reg(vfe_dev);
+
+ vfe_dev->vfe_hw_version = msm_camera_io_r(vfe_dev->vfe_base);
+ ISP_DBG("%s: HW Version: 0x%x\n", __func__, vfe_dev->vfe_hw_version);
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 1, 1);
+ if (rc <= 0) {
+ pr_err("%s: reset timeout\n", __func__);
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EINVAL;
+ }
+
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+
+ vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr,
+ "msm_isp");
+
+ memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
+ memset(&vfe_dev->stats_data, 0,
+ sizeof(struct msm_vfe_stats_shared_data));
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ memset(&vfe_dev->fetch_engine_info, 0,
+ sizeof(vfe_dev->fetch_engine_info));
+ vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
+ vfe_dev->axi_data.enable_frameid_recovery = 0;
+ vfe_dev->vt_enable = 0;
+ vfe_dev->reg_update_requested = 0;
+ /* Register page fault handler */
+ vfe_dev->buf_mgr->pagefault_debug_disable = 0;
+ /* initialize pd_buf_idx with an invalid index 0xF */
+ vfe_dev->common_data->pd_buf_idx = 0xF;
+
+ cam_smmu_reg_client_page_fault_handler(
+ vfe_dev->buf_mgr->iommu_hdl,
+ msm_vfe_iommu_fault_handler,
+ NULL,
+ vfe_dev);
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+}
+
+#ifdef CONFIG_MSM_AVTIMER
+static void msm_isp_end_avtimer(void)
+{
+ avcs_core_disable_power_collapse(0);
+}
+#else
+static void msm_isp_end_avtimer(void)
+{
+ pr_err("AV Timer is not supported\n");
+}
+#endif
+
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ long rc = 0;
+ int wm;
+ int i;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ ISP_DBG("%s E open_cnt %u\n", __func__, vfe_dev->vfe_open_cnt);
+ mutex_lock(&vfe_dev->realtime_mutex);
+ mutex_lock(&vfe_dev->core_mutex);
+
+ if (!vfe_dev->vfe_open_cnt) {
+ pr_err("%s invalid state open cnt %d\n", __func__,
+ vfe_dev->vfe_open_cnt);
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EINVAL;
+ }
+
+ if (vfe_dev->vfe_open_cnt > 1) {
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+ }
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ msm_isp_release_all_axi_stream(vfe_dev);
+ msm_isp_release_all_stats_stream(vfe_dev);
+
+ /* Unregister page fault handler */
+ cam_smmu_reg_client_page_fault_handler(
+ vfe_dev->buf_mgr->iommu_hdl,
+ NULL, NULL, vfe_dev);
+
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ if (rc <= 0)
+ pr_err("%s: halt timeout rc=%ld\n", __func__, rc);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 0);
+
+ /* put scratch buf in all the wm */
+ for (wm = 0; wm < vfe_dev->axi_data.hw_info->num_wm; wm++) {
+ msm_isp_cfg_wm_scratch(vfe_dev, wm, VFE_PING_FLAG);
+ msm_isp_cfg_wm_scratch(vfe_dev, wm, VFE_PONG_FLAG);
+ }
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+ /* after regular hw stop, reduce open cnt */
+ vfe_dev->vfe_open_cnt--;
+ vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
+ if (vfe_dev->vt_enable) {
+ msm_isp_end_avtimer();
+ vfe_dev->vt_enable = 0;
+ }
+ for (i = 0; i < VFE_SRC_MAX; i++)
+ vfe_dev->axi_data.src_info[i].lpm = 0;
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
+ vfe_dev->is_split = 0;
+
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+}
+
+void msm_isp_flush_tasklet(struct vfe_device *vfe_dev)
+{
+ unsigned long flags;
+ int i;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd, *q_cmd_next;
+ struct msm_vfe_tasklet *tasklet;
+
+ for (i = 0; i <= MAX_VFE; i++) {
+ if (i != vfe_dev->pdev->id &&
+ i != MAX_VFE)
+ continue;
+ tasklet = &vfe_dev->common_data->tasklets[i];
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ list_for_each_entry_safe(queue_cmd, q_cmd_next,
+ &tasklet->tasklet_q, list) {
+ if (queue_cmd->vfe_dev != vfe_dev)
+ continue;
+ list_del_init(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+ }
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ tasklet_kill(&tasklet->tasklet);
+ }
+ atomic_set(&vfe_dev->irq_cnt, 0);
+
+}
+
+void msm_isp_irq_debug_dump(struct vfe_device *vfe_dev)
+{
+
+ uint8_t i, dump_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_irq_dump_lock, flags);
+ dump_index = vfe_dev->common_data->vfe_irq_dump.
+ current_irq_index;
+ for (i = 0; i < MAX_VFE_IRQ_DEBUG_DUMP_SIZE; i++) {
+ trace_msm_cam_ping_pong_debug_dump(
+ vfe_dev->common_data->vfe_irq_dump.
+ irq_debug[dump_index % MAX_VFE_IRQ_DEBUG_DUMP_SIZE]);
+ dump_index++;
+ }
+ spin_unlock_irqrestore(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_irq_dump_lock, flags);
+}
+
+
+void msm_isp_tasklet_debug_dump(struct vfe_device *vfe_dev)
+{
+
+ uint8_t i, dump_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_tasklet_dump_lock, flags);
+ dump_index = vfe_dev->common_data->vfe_irq_dump.
+ current_tasklet_index;
+ for (i = 0; i < MAX_VFE_IRQ_DEBUG_DUMP_SIZE; i++) {
+ trace_msm_cam_tasklet_debug_dump(
+ vfe_dev->common_data->vfe_irq_dump.
+ tasklet_debug[
+ dump_index % MAX_VFE_IRQ_DEBUG_DUMP_SIZE]);
+ dump_index++;
+ }
+ spin_unlock_irqrestore(&vfe_dev->common_data->vfe_irq_dump.
+ common_dev_tasklet_dump_lock, flags);
+}
+
+void msm_isp_dump_ping_pong_mismatch(struct vfe_device *vfe_dev)
+{
+
+ trace_msm_cam_string(" ***** msm_isp_dump_irq_debug ****");
+ msm_isp_irq_debug_dump(vfe_dev);
+ trace_msm_cam_string(" ***** msm_isp_dump_taskelet_debug ****");
+ msm_isp_tasklet_debug_dump(vfe_dev);
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
new file mode 100644
index 0000000..3ce5c47
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2013-2016, 2018, 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"
+#include <soc/qcom/camera2.h>
+#include "msm_camera_io_util.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
+
+#define ALT_VECTOR_IDX(x) {x = 3 - x; }
+
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern);
+void msm_isp_reset_burst_count_and_frame_drop(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info);
+
+int msm_isp_init_bandwidth_mgr(struct vfe_device *vfe_dev,
+ enum msm_isp_hw_client client);
+int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
+ uint64_t ab, uint64_t ib);
+void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev,
+ struct msm_isp_statistics *stats);
+void msm_isp_util_update_last_overflow_ab_ib(struct vfe_device *vfe_dev);
+void msm_isp_util_update_clk_rate(long clock_rate);
+void msm_isp_update_req_history(uint32_t client, uint64_t ab,
+ uint64_t ib,
+ struct msm_isp_bandwidth_info *client_info,
+ unsigned long long ts);
+void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client);
+
+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);
+int msm_isp_get_bit_per_pixel(uint32_t output_format);
+enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format);
+irqreturn_t msm_isp_process_irq(int irq_num, void *data);
+int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_do_tasklet(unsigned long data);
+void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev);
+void msm_isp_process_error_info(struct vfe_device *vfe_dev);
+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);
+void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
+ struct msm_vfe_fetch_engine_info *fetch_engine_info);
+void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format);
+void msm_isp_flush_tasklet(struct vfe_device *vfe_dev);
+void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp,
+ struct vfe_device *vfe_dev);
+void msm_isp_dump_ping_pong_mismatch(struct vfe_device *vfe_dev);
+int msm_isp_process_overflow_irq(
+ struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1,
+ uint8_t force_overflow);
+void msm_isp_prepare_irq_debug_info(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+void msm_isp_prepare_tasklet_debug_info(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp ts);
+void msm_isp_irq_debug_dump(struct vfe_device *vfe_dev);
+void msm_isp_tasklet_debug_dump(struct vfe_device *vfe_dev);
+int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg);
+#endif /* __MSM_ISP_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/Makefile b/drivers/media/platform/msm/camera_v2/ispif/Makefile
new file mode 100644
index 0000000..236ec73
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSM_CSID) += msm_ispif.o
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
new file mode 100644
index 0000000..512fdb9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -0,0 +1,2148 @@
+/* Copyright (c) 2013-2018, 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/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 <linux/iopoll.h>
+#include <linux/compat.h>
+#include <media/msmb_isp.h>
+#include <linux/ratelimit.h>
+
+#include "msm_ispif.h"
+#include "msm.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+#include "cam_hw_ops.h"
+#include "cam_soc_api.h"
+
+#ifdef CONFIG_MSM_ISPIF_V1
+#include "msm_ispif_hwreg_v1.h"
+#elif defined CONFIG_MSM_ISPIF_V2
+#include "msm_ispif_hwreg_v2.h"
+#else
+#include "msm_ispif_hwreg_v3.h"
+#endif
+
+#define V4L2_IDENT_ISPIF 50001
+#define MSM_ISPIF_DRV_NAME "msm_ispif"
+
+#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00
+#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01
+#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02
+
+#define ISPIF_TIMEOUT_SLEEP_US 1000
+#define ISPIF_TIMEOUT_ALL_US 1000000
+#define ISPIF_SOF_DEBUG_COUNT 5
+
+/* 3D Threshold value according guidelines for line width 1280 */
+#define STEREO_DEFAULT_3D_THRESHOLD 0x36
+
+/*
+ * Overflows before restarting interface during stereo usecase
+ * to give some tolerance for cases when the two sensors sync fails
+ * this value is chosen by experiment
+ */
+#define MAX_PIX_OVERFLOW_ERROR_COUNT 10
+static int pix_overflow_error_count[VFE_MAX] = { 0 };
+
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...)
+#endif
+
+/* Backward interface compatibility for 3D THRESHOLD calculation */
+#define ISPIF_USE_DEFAULT_THRESHOLD (0)
+#define ISPIF_CALCULATE_THRESHOLD (1)
+
+static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable);
+static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg);
+static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd,
+ struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params);
+
+static int msm_ispif_get_clk_info(struct ispif_device *ispif_dev,
+ struct platform_device *pdev);
+
+static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
+{
+ if (!ispif->enb_dump_reg)
+ return;
+
+ if (!ispif->base) {
+ pr_err("%s: null pointer for the ispif base\n", __func__);
+ return;
+ }
+
+ msm_camera_io_dump(ispif->base, 0x250, 0);
+}
+
+
+static inline int msm_ispif_is_intf_valid(uint32_t csid_version,
+ enum msm_ispif_vfe_intf intf_type)
+{
+ return ((csid_version <= CSID_VERSION_V22 && intf_type != VFE0) ||
+ (intf_type >= VFE_MAX)) ? false : true;
+}
+
+#ifdef CONFIG_COMPAT
+struct ispif_cfg_data_ext_32 {
+ enum ispif_cfg_type_t cfg_type;
+ compat_caddr_t data;
+ uint32_t size;
+};
+
+#define VIDIOC_MSM_ISPIF_CFG_EXT_COMPAT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct ispif_cfg_data_ext_32)
+#endif
+
+static void msm_ispif_get_pack_mask_from_cfg(
+ struct msm_ispif_pack_cfg *pack_cfg,
+ struct msm_ispif_params_entry *entry,
+ uint32_t *pack_mask)
+{
+ int i;
+ uint32_t temp;
+
+ if (WARN_ON(!entry))
+ return;
+
+ memset(pack_mask, 0, sizeof(uint32_t) * 2);
+ for (i = 0; i < entry->num_cids; i++) {
+ temp = (pack_cfg[entry->cids[i]].pack_mode & 0x3)|
+ (pack_cfg[entry->cids[i]].even_odd_sel & 0x1) << 2 |
+ (pack_cfg[entry->cids[i]].pixel_swap_en & 0x1) << 3;
+ temp = (temp & 0xF) << ((entry->cids[i] % CID8) * 4);
+
+ if (entry->cids[i] > CID7)
+ pack_mask[1] |= temp;
+ else
+ pack_mask[0] |= temp;
+ CDBG("%s:num %d cid %d mode %d pack_mask %x %x\n",
+ __func__, entry->num_cids, entry->cids[i],
+ pack_cfg[entry->cids[i]].pack_mode,
+ pack_mask[0], pack_mask[1]);
+
+ }
+}
+
+static int msm_ispif_config2(struct ispif_device *ispif,
+ void *data)
+{
+ int rc = 0, i = 0;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t pack_cfg_mask[2];
+ struct msm_ispif_param_data_ext *params =
+ (struct msm_ispif_param_data_ext *)data;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ int j;
+
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY)
+ return -EINVAL;
+ for (j = 0; j < params->entries[i].num_cids; j++)
+ if (params->entries[i].cids[j] >= CID_MAX)
+ return -EINVAL;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+ vfe_intf = params->entries[i].vfe_intf;
+
+ CDBG("%s, num %d intftype %x, vfe_intf %d, csid %d\n", __func__,
+ params->num, intftype, vfe_intf,
+ params->entries[i].csid);
+
+ if ((intftype >= INTF_MAX) ||
+ (vfe_intf >= ispif->vfe_info.num_vfe) ||
+ (ispif->csid_version <= CSID_VERSION_V22 &&
+ (vfe_intf > VFE0))) {
+ pr_err("%s: VFEID %d and CSID version %d mismatch\n",
+ __func__, vfe_intf, ispif->csid_version);
+ return -EINVAL;
+ }
+
+ msm_ispif_get_pack_mask_from_cfg(params->pack_cfg,
+ ¶ms->entries[i], pack_cfg_mask);
+ msm_ispif_cfg_pack_mode(ispif, intftype, vfe_intf,
+ pack_cfg_mask);
+ }
+ return rc;
+}
+
+static long msm_ispif_cmd_ext(struct v4l2_subdev *sd,
+ void *arg)
+{
+ long rc = 0;
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ struct ispif_cfg_data_ext pcdata;
+ struct msm_ispif_param_data_ext *params = NULL;
+#ifdef CONFIG_COMPAT
+ struct ispif_cfg_data_ext_32 *pcdata32 =
+ (struct ispif_cfg_data_ext_32 *)arg;
+
+ if (pcdata32 == NULL) {
+ pr_err("Invalid params passed from user\n");
+ return -EINVAL;
+ }
+ pcdata.cfg_type = pcdata32->cfg_type;
+ pcdata.size = pcdata32->size;
+ pcdata.data = compat_ptr(pcdata32->data);
+
+#else
+ struct ispif_cfg_data_ext *pcdata64 =
+ (struct ispif_cfg_data_ext *)arg;
+
+ if (pcdata64 == NULL) {
+ pr_err("Invalid params passed from user\n");
+ return -EINVAL;
+ }
+ pcdata.cfg_type = pcdata64->cfg_type;
+ pcdata.size = pcdata64->size;
+ pcdata.data = pcdata64->data;
+#endif
+ if (pcdata.size != sizeof(struct msm_ispif_param_data_ext)) {
+ pr_err("%s: payload size mismatch\n", __func__);
+ return -EINVAL;
+ }
+
+ params = kzalloc(sizeof(struct msm_ispif_param_data_ext), GFP_KERNEL);
+ if (!params) {
+ CDBG("%s: params alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user(params, (void __user *)(pcdata.data),
+ pcdata.size)) {
+ kfree(params);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ispif->mutex);
+ rc = msm_ispif_dispatch_cmd(pcdata.cfg_type, ispif, params);
+ mutex_unlock(&ispif->mutex);
+ kfree(params);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ispif_subdev_ioctl_compat(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ if (WARN_ON(!sd))
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_MSM_ISPIF_CFG_EXT_COMPAT:
+ return msm_ispif_cmd_ext(sd, arg);
+
+ default:
+ return msm_ispif_subdev_ioctl_unlocked(sd, cmd, arg);
+ }
+}
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ if (is_compat_task())
+ return msm_ispif_subdev_ioctl_compat(sd, cmd, arg);
+ else
+ return msm_ispif_subdev_ioctl_unlocked(sd, cmd, arg);
+}
+#else
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ return msm_ispif_subdev_ioctl_unlocked(sd, cmd, arg);
+}
+#endif
+static void msm_ispif_put_regulator(struct ispif_device *ispif_dev)
+{
+ int i;
+
+ for (i = 0; i < ispif_dev->ispif_vdd_count; i++) {
+ regulator_put(ispif_dev->ispif_vdd[i]);
+ ispif_dev->ispif_vdd[i] = NULL;
+ }
+ for (i = 0; i < ispif_dev->vfe_vdd_count; i++) {
+ regulator_put(ispif_dev->vfe_vdd[i]);
+ ispif_dev->vfe_vdd[i] = NULL;
+ }
+}
+
+static inline int __get_vdd(struct platform_device *pdev,
+ struct regulator **reg, const char *vdd)
+{
+ int rc = 0;
+ *reg = regulator_get(&pdev->dev, vdd);
+ if (IS_ERR_OR_NULL(*reg)) {
+ rc = PTR_ERR(*reg);
+ rc = rc ? rc : -EINVAL;
+ pr_err("%s: Regulator %s get failed %d\n", __func__, vdd, rc);
+ *reg = NULL;
+ }
+ return rc;
+}
+
+static int msm_ispif_get_regulator_info(struct ispif_device *ispif_dev,
+ struct platform_device *pdev)
+{
+ int rc;
+ const char *vdd_name;
+ struct device_node *of_node;
+ int i;
+ int count;
+
+ of_node = pdev->dev.of_node;
+
+ count = of_property_count_strings(of_node,
+ "qcom,vdd-names");
+ if (count == 0) {
+ pr_err("%s: no regulators found\n", __func__);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(count > (ISPIF_VDD_INFO_MAX + ISPIF_VFE_VDD_INFO_MAX)))
+ pr_err("%s: count is greater is 4", __func__);
+ ispif_dev->vfe_vdd_count = 0;
+ ispif_dev->ispif_vdd_count = 0;
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(
+ of_node, "qcom,vdd-names",
+ i, &vdd_name);
+ if (rc < 0) {
+ pr_err("%s: read property qcom,ispif-vdd-names at index %d failed\n",
+ __func__, i);
+ goto err;
+ }
+ if (strnstr(vdd_name, "vfe", strlen(vdd_name))) {
+ if (WARN_ON((ispif_dev->vfe_vdd_count >=
+ ISPIF_VFE_VDD_INFO_MAX))) {
+ pr_err("%s: count is greater is 4", __func__);
+ return -EINVAL;
+ };
+ rc = __get_vdd(pdev,
+ &ispif_dev->vfe_vdd[ispif_dev->vfe_vdd_count],
+ vdd_name);
+ if (rc == 0)
+ ispif_dev->vfe_vdd_count++;
+ } else {
+ if (WARN_ON((ispif_dev->vfe_vdd_count >=
+ ISPIF_VFE_VDD_INFO_MAX))) {
+ pr_err("%s: count is greater is 4", __func__);
+ return -EINVAL;
+ };
+ rc = __get_vdd(pdev,
+ &ispif_dev->ispif_vdd
+ [ispif_dev->ispif_vdd_count],
+ vdd_name);
+ if (rc == 0)
+ ispif_dev->ispif_vdd_count++;
+ }
+ if (rc)
+ goto err;
+ }
+ return 0;
+err:
+ for (i = 0; i < ispif_dev->vfe_vdd_count; i++) {
+ regulator_put(ispif_dev->vfe_vdd[i]);
+ ispif_dev->vfe_vdd[i] = NULL;
+ }
+ for (i = 0; i < ispif_dev->ispif_vdd_count; i++) {
+ regulator_put(ispif_dev->ispif_vdd[i]);
+ ispif_dev->ispif_vdd[i] = NULL;
+ }
+ ispif_dev->ispif_vdd_count = 0;
+ ispif_dev->vfe_vdd_count = 0;
+ return rc;
+}
+
+static int msm_ispif_set_regulators(struct regulator **regs, int count,
+ uint8_t enable)
+{
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (enable) {
+ rc = regulator_enable(regs[i]);
+ if (rc)
+ goto err;
+ } else {
+ rc |= regulator_disable(regs[i]);
+ }
+ }
+ if (rc)
+ pr_err("%s: Regulator disable failed\n", __func__);
+ return rc;
+err:
+ pr_err("%s: Regulator enable failed\n", __func__);
+ for (i--; i >= 0; i--)
+ regulator_disable(regs[i]);
+ return rc;
+}
+
+static int msm_ispif_reset_hw(struct ispif_device *ispif)
+{
+ int rc = 0;
+ long timeout = 0;
+
+ ispif->clk_idx = 0;
+
+ /* Turn ON VFE regulators before enabling the vfe clocks */
+ rc = msm_ispif_set_regulators(ispif->vfe_vdd, ispif->vfe_vdd_count, 1);
+ if (rc < 0)
+ return rc;
+
+ rc = msm_camera_clk_enable(&ispif->pdev->dev,
+ ispif->clk_info, ispif->clks,
+ ispif->num_clk, 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ goto reg_disable;
+ } else {
+ /* This is set when device is 8974 */
+ ispif->clk_idx = 1;
+ }
+ memset(ispif->stereo_configured, 0, sizeof(ispif->stereo_configured));
+ atomic_set(&ispif->reset_trig[VFE0], 1);
+ /* initiate reset of ISPIF */
+ msm_camera_io_w(ISPIF_RST_CMD_MASK,
+ ispif->base + ISPIF_RST_CMD_ADDR);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete[VFE0], msecs_to_jiffies(500));
+ CDBG("%s: VFE0 done\n", __func__);
+
+ if (timeout <= 0) {
+ rc = -ETIMEDOUT;
+ pr_err("%s: VFE0 reset wait timeout\n", __func__);
+ goto clk_disable;
+ }
+
+ if (ispif->hw_num_isps > 1) {
+ atomic_set(&ispif->reset_trig[VFE1], 1);
+ msm_camera_io_w(ISPIF_RST_CMD_1_MASK,
+ ispif->base + ISPIF_RST_CMD_1_ADDR);
+ timeout = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete[VFE1],
+ msecs_to_jiffies(500));
+ CDBG("%s: VFE1 done\n", __func__);
+ if (timeout <= 0) {
+ pr_err("%s: VFE1 reset wait timeout\n", __func__);
+ rc = -ETIMEDOUT;
+ }
+ }
+
+clk_disable:
+ if (ispif->clk_idx == 1) {
+ rc = rc ? rc : msm_camera_clk_enable(&ispif->pdev->dev,
+ ispif->clk_info, ispif->clks,
+ ispif->num_clk, 0);
+ }
+
+reg_disable:
+ rc = rc ? rc : msm_ispif_set_regulators(ispif->vfe_vdd,
+ ispif->vfe_vdd_count, 0);
+
+ return rc;
+}
+
+static int msm_ispif_get_clk_info(struct ispif_device *ispif_dev,
+ struct platform_device *pdev)
+{
+ uint32_t num_ahb_clk = 0, non_ahb_clk = 0;
+ size_t num_clks;
+ int i, rc;
+ int j;
+ struct clk **clks, **temp_clks;
+ struct msm_cam_clk_info *clk_info, *temp_clk_info;
+
+ struct device_node *of_node;
+
+ of_node = pdev->dev.of_node;
+
+ rc = msm_camera_get_clk_info(pdev, &clk_info,
+ &clks, &num_clks);
+
+ if (rc)
+ return rc;
+
+ /*
+ * reshuffle the clock arrays so that the ahb clocks are
+ * at the beginning of array
+ */
+ temp_clks = kcalloc(num_clks, sizeof(struct clk *),
+ GFP_KERNEL);
+ temp_clk_info = kcalloc(num_clks, sizeof(struct msm_cam_clk_info),
+ GFP_KERNEL);
+ if (!temp_clks || !temp_clk_info) {
+ rc = -ENOMEM;
+ kfree(temp_clk_info);
+ kfree(temp_clks);
+ goto alloc_fail;
+ }
+ j = 0;
+ for (i = 0; i < num_clks; i++) {
+ if (strnstr(clk_info[i].clk_name,
+ "ahb", strlen(clk_info[i].clk_name))) {
+ temp_clk_info[j] = clk_info[i];
+ temp_clks[j] = clks[i];
+ j++;
+ num_ahb_clk++;
+ }
+ }
+ for (i = 0; i < num_clks; i++) {
+ if (!strnstr(clk_info[i].clk_name,
+ "ahb", strlen(clk_info[i].clk_name))) {
+ temp_clk_info[j] = clk_info[i];
+ temp_clks[j] = clks[i];
+ j++;
+ non_ahb_clk++;
+ }
+ }
+
+ for (i = 0; i < num_clks; i++) {
+ clk_info[i] = temp_clk_info[i];
+ clks[i] = temp_clks[i];
+ }
+ kfree(temp_clk_info);
+ kfree(temp_clks);
+
+ ispif_dev->ahb_clk = clks;
+ ispif_dev->ahb_clk_info = clk_info;
+ ispif_dev->num_ahb_clk = num_ahb_clk;
+ ispif_dev->clk_info = clk_info + num_ahb_clk;
+ ispif_dev->clks = clks + num_ahb_clk;
+ ispif_dev->num_clk = non_ahb_clk;
+
+ return 0;
+alloc_fail:
+ msm_camera_put_clk_info(pdev, &clk_info, &clks, num_clks);
+ return rc;
+}
+
+static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable)
+{
+ int rc = 0;
+
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif->ahb_clk_info, ispif->ahb_clk,
+ ispif->num_ahb_clk, enable);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ }
+
+ return rc;
+}
+
+static int msm_ispif_reset(struct ispif_device *ispif)
+{
+ int rc = 0;
+ int i;
+
+ if (WARN_ON(!ispif))
+ return -EINVAL;
+
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+ for (i = 0; i < ispif->vfe_info.num_vfe; i++) {
+
+ msm_camera_io_w(1 << PIX0_LINE_BUF_EN_BIT,
+ ispif->base + ISPIF_VFE_m_CTRL_0(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_0(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_1(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_2(i));
+
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INPUT_SEL(i));
+
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
+ pr_debug("%s: base %pK", __func__, ispif->base);
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 1));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 1));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 2));
+
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CROP(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CROP(i, 1));
+ }
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ return rc;
+}
+
+static void msm_ispif_sel_csid_core(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ uint32_t data;
+
+ if (WARN_ON((!ispif)))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_INPUT_SEL(vfe_intf));
+ switch (intftype) {
+ case PIX0:
+ data &= ~(BIT(1) | BIT(0));
+ data |= (uint32_t) csid;
+ break;
+ case RDI0:
+ data &= ~(BIT(5) | BIT(4));
+ data |= ((uint32_t) csid) << 4;
+ break;
+ case PIX1:
+ data &= ~(BIT(9) | BIT(8));
+ data |= ((uint32_t) csid) << 8;
+ break;
+ case RDI1:
+ data &= ~(BIT(13) | BIT(12));
+ data |= ((uint32_t) csid) << 12;
+ break;
+ case RDI2:
+ data &= ~(BIT(21) | BIT(20));
+ data |= ((uint32_t) csid) << 20;
+ break;
+ }
+
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_VFE_m_INPUT_SEL(vfe_intf));
+}
+
+static void msm_ispif_enable_crop(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf, uint16_t start_pixel,
+ uint16_t end_pixel)
+{
+ uint32_t data;
+
+ if (WARN_ON((!ispif)))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf));
+ data |= (1 << (intftype + 7));
+ if (intftype == PIX0)
+ data |= 1 << PIX0_LINE_BUF_EN_BIT;
+ msm_camera_io_w(data,
+ ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf));
+
+ if (intftype == PIX0)
+ msm_camera_io_w_mb(start_pixel | (end_pixel << 16),
+ ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 0));
+ else if (intftype == PIX1)
+ msm_camera_io_w_mb(start_pixel | (end_pixel << 16),
+ ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 1));
+ else {
+ pr_err("%s: invalid intftype=%d\n", __func__, intftype);
+ WARN_ON(1);
+ return;
+ }
+}
+
+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 intf_addr, data;
+
+ if (WARN_ON((!ispif)))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ switch (intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__, intftype);
+ WARN_ON(1);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + intf_addr);
+ if (enable)
+ data |= (uint32_t) cid_mask;
+ else
+ data &= ~((uint32_t) cid_mask);
+ msm_camera_io_w_mb(data, ispif->base + intf_addr);
+}
+
+static int msm_ispif_validate_intf_status(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf)
+{
+ int rc = 0;
+ uint32_t data = 0;
+
+ if (WARN_ON((!ispif)))
+ return -EINVAL;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2));
+ break;
+ }
+ if ((data & 0xf) != 0xf)
+ rc = -EBUSY;
+ return rc;
+}
+
+static void msm_ispif_select_clk_mux(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ uint32_t data = 0;
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf << (vfe_intf * 8));
+ data |= (csid << (vfe_intf * 8));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI0:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (vfe_intf * 12));
+ data |= (csid << (vfe_intf * 12));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case PIX1:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf0 << (vfe_intf * 8));
+ data |= (csid << (4 + (vfe_intf * 8)));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI1:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (4 + (vfe_intf * 12)));
+ data |= (csid << (4 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case RDI2:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (8 + (vfe_intf * 12)));
+ data |= (csid << (8 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+ }
+ CDBG("%s intftype %d data %x\n", __func__, intftype, data);
+ /* ensure clk mux is enabled */
+ mb();
+}
+
+static uint16_t msm_ispif_get_cids_mask_from_cfg(
+ struct msm_ispif_params_entry *entry)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ if (WARN_ON(!entry)) {
+ pr_err("%s: invalid entry", __func__);
+ return cids_mask;
+ }
+
+ for (i = 0; i < entry->num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++)
+ cids_mask |= (1 << entry->cids[i]);
+
+ return cids_mask;
+}
+
+static uint16_t msm_ispif_get_right_cids_mask_from_cfg(
+ struct msm_ispif_right_param_entry *entry, int num_cids)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ if (WARN_ON(!entry))
+ return cids_mask;
+
+ for (i = 0; i < num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++) {
+ if (entry->cids[i] < CID_MAX)
+ cids_mask |= (1 << entry->cids[i]);
+ }
+
+ return cids_mask;
+}
+
+static int msm_ispif_config(struct ispif_device *ispif,
+ void *data)
+{
+ int rc = 0, i = 0;
+ uint16_t cid_mask = 0;
+ uint16_t cid_right_mask = 0;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf;
+ struct msm_ispif_param_data_ext *params =
+ (struct msm_ispif_param_data_ext *)data;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version,
+ vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_0(vfe_intf));
+ msm_camera_io_w(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_1(vfe_intf));
+ msm_camera_io_w_mb(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_2(vfe_intf));
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+
+ vfe_intf = params->entries[i].vfe_intf;
+
+ CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__,
+ intftype, vfe_intf, params->entries[i].csid);
+
+ if ((intftype >= INTF_MAX) ||
+ (vfe_intf >= ispif->vfe_info.num_vfe) ||
+ (ispif->csid_version <= CSID_VERSION_V22 &&
+ (vfe_intf > VFE0))) {
+ pr_err("%s: VFEID %d and CSID version %d mismatch\n",
+ __func__, vfe_intf, ispif->csid_version);
+ return -EINVAL;
+ }
+
+ if (ispif->csid_version >= CSID_VERSION_V30) {
+ msm_ispif_select_clk_mux(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+ if (intftype == PIX0 && params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX)
+ msm_ispif_select_clk_mux(ispif, PIX1,
+ params->right_entries[i].csid,
+ vfe_intf);
+ }
+
+ rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
+ if (rc) {
+ 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);
+ if (intftype == PIX0 && params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX)
+ /* configure right stereo csid */
+ msm_ispif_sel_csid_core(ispif, PIX1,
+ params->right_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);
+ if (params->stereo_enable)
+ cid_right_mask = msm_ispif_get_right_cids_mask_from_cfg(
+ ¶ms->right_entries[i],
+ params->entries[i].num_cids);
+ else
+ cid_right_mask = 0;
+ if (cid_right_mask && params->stereo_enable)
+ /* configure right stereo cids */
+ msm_ispif_enable_intf_cids(ispif, PIX1,
+ cid_right_mask, vfe_intf, 1);
+ if (params->entries[i].crop_enable)
+ msm_ispif_enable_crop(ispif, intftype, vfe_intf,
+ params->entries[i].crop_start_pixel,
+ params->entries[i].crop_end_pixel);
+ }
+
+ for (vfe_intf = 0; vfe_intf < 2; vfe_intf++) {
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_0(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_0(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_1(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_1(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_2(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_2(vfe_intf));
+ }
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ return rc;
+}
+
+static void msm_ispif_config_stereo(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params, int use_line_width) {
+
+ int i;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD;
+
+ if (params->num > MAX_PARAM_ENTRIES)
+ return;
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type %d\n", __func__,
+ vfe_intf);
+ return;
+ }
+ if (params->entries[i].intftype == PIX0 &&
+ params->stereo_enable &&
+ params->right_entries[i].csid < CSID_MAX &&
+ !ispif->stereo_configured[vfe_intf]) {
+ msm_camera_io_w_mb(0x3,
+ ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf));
+ if (use_line_width &&
+ (params->line_width[vfe_intf] > 0))
+ stereo_3d_threshold =
+ (params->line_width[vfe_intf] +
+ 2 * 6 - 1) / (2 * 6);
+ msm_camera_io_w_mb(stereo_3d_threshold,
+ ispif->base +
+ ISPIF_VFE_m_3D_THRESHOLD(vfe_intf));
+ ispif->stereo_configured[vfe_intf] = 1;
+ }
+ }
+}
+
+static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
+ struct msm_ispif_param_data_ext *params)
+{
+ uint8_t vc;
+ int i, k;
+ enum msm_ispif_intftype intf_type;
+ enum msm_ispif_cid cid;
+ enum msm_ispif_vfe_intf vfe_intf;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return;
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY) {
+ pr_err("%s: out of range of cid_num %d\n",
+ __func__, params->entries[i].num_cids);
+ return;
+ }
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ vfe_intf = params->entries[i].vfe_intf;
+ 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));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |=
+ (cmd_bits << (vc * 2 + 8));
+ } else {
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + intf_type * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + intf_type * 8));
+ }
+ if (intf_type == PIX0 && params->stereo_enable &&
+ params->right_entries[i].cids[k] < CID_MAX) {
+ cid = params->right_entries[i].cids[k];
+ vc = cid / 4;
+
+ /* fill right stereo command */
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + PIX1 * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + PIX1 * 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_VFE_m_INTF_CMD_0(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_VFE_m_INTF_CMD_1(vfe_intf));
+ }
+}
+
+static int msm_ispif_stop_immediately(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+ 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->entries[i].vfe_intf, 0);
+ if (params->stereo_enable) {
+ ispif->stereo_configured[
+ params->entries[i].vfe_intf] = 0;
+ cid_mask = msm_ispif_get_right_cids_mask_from_cfg(
+ ¶ms->right_entries[i],
+ params->entries[i].num_cids);
+ if (cid_mask)
+ msm_ispif_enable_intf_cids(ispif,
+ params->entries[i].intftype, cid_mask,
+ params->entries[i].vfe_intf, 0);
+ }
+ }
+
+ return rc;
+}
+
+static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
+{
+ int rc = 0;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ msm_ispif_config_stereo(ispif, params, ISPIF_USE_DEFAULT_THRESHOLD);
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+
+ return rc;
+}
+
+static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
+{
+ int rc = 0, i;
+ long timeout = 0;
+ uint16_t cid_mask;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t vfe_mask = 0;
+ uint32_t intf_addr;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (vfe_intf >= VFE_MAX) {
+ pr_err("%s: %d invalid i %d vfe_intf %d\n", __func__,
+ __LINE__, i, vfe_intf);
+ return -EINVAL;
+ }
+ vfe_mask |= (1 << vfe_intf);
+ }
+
+ /* Turn ON regulators before enabling the clocks*/
+ rc = msm_ispif_set_regulators(ispif->vfe_vdd,
+ ispif->vfe_vdd_count, 1);
+ if (rc < 0)
+ return -EFAULT;
+
+ rc = msm_camera_clk_enable(&ispif->pdev->dev,
+ ispif->clk_info, ispif->clks,
+ ispif->num_clk, 1);
+ if (rc < 0)
+ goto disable_regulator;
+
+ if (vfe_mask & (1 << VFE0)) {
+ atomic_set(&ispif->reset_trig[VFE0], 1);
+ /* initiate reset of ISPIF */
+ msm_camera_io_w(ISPIF_RST_CMD_MASK_RESTART,
+ ispif->base + ISPIF_RST_CMD_ADDR);
+ timeout = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete[VFE0], msecs_to_jiffies(500));
+ if (timeout <= 0) {
+ pr_err("%s: VFE0 reset wait timeout\n", __func__);
+ rc = -ETIMEDOUT;
+ goto disable_clk;
+ }
+ }
+
+ if (ispif->hw_num_isps > 1 && (vfe_mask & (1 << VFE1))) {
+ atomic_set(&ispif->reset_trig[VFE1], 1);
+ msm_camera_io_w(ISPIF_RST_CMD_1_MASK_RESTART,
+ ispif->base + ISPIF_RST_CMD_1_ADDR);
+ timeout = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete[VFE1],
+ msecs_to_jiffies(500));
+ if (timeout <= 0) {
+ pr_err("%s: VFE1 reset wait timeout\n", __func__);
+ rc = -ETIMEDOUT;
+ goto disable_clk;
+ }
+ }
+
+ pr_info("%s: ISPIF reset hw done, Restarting", __func__);
+ rc = msm_camera_clk_enable(&ispif->pdev->dev,
+ ispif->clk_info, ispif->clks,
+ ispif->num_clk, 0);
+ if (rc < 0)
+ goto disable_regulator;
+
+ /* Turn OFF regulators after disabling clocks */
+ rc = msm_ispif_set_regulators(ispif->vfe_vdd, ispif->vfe_vdd_count, 0);
+ if (rc < 0)
+ goto end;
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+ vfe_intf = params->entries[i].vfe_intf;
+
+ switch (params->entries[0].intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__,
+ params->entries[i].intftype);
+ rc = -EPERM;
+ goto end;
+ }
+
+ msm_ispif_intf_cmd(ispif,
+ ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+
+ vfe_intf = params->entries[i].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);
+ }
+ return rc;
+
+disable_clk:
+ msm_camera_clk_enable(&ispif->pdev->dev,
+ ispif->clk_info, ispif->clks,
+ ispif->num_clk, 0);
+disable_regulator:
+ /* Turn OFF regulators */
+ msm_ispif_set_regulators(ispif->vfe_vdd, ispif->vfe_vdd_count, 0);
+end:
+ return rc;
+}
+
+static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+ uint16_t cid_right_mask = 0;
+ uint32_t intf_addr;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t stop_flag = 0;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ if (!msm_ispif_is_intf_valid(ispif->csid_version,
+ params->entries[i].vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+ 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]);
+ if (params->stereo_enable)
+ cid_right_mask =
+ msm_ispif_get_right_cids_mask_from_cfg(
+ ¶ms->right_entries[i],
+ params->entries[i].num_cids);
+ else
+ cid_right_mask = 0;
+ vfe_intf = params->entries[i].vfe_intf;
+
+ switch (params->entries[i].intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__,
+ params->entries[i].intftype);
+ rc = -EPERM;
+ goto end;
+ }
+
+ rc = readl_poll_timeout(ispif->base + intf_addr, stop_flag,
+ (stop_flag & 0xF) == 0xF,
+ ISPIF_TIMEOUT_SLEEP_US,
+ ISPIF_TIMEOUT_ALL_US);
+ if (rc < 0)
+ goto end;
+ if (cid_right_mask) {
+ ispif->stereo_configured[
+ params->entries[i].vfe_intf] = 0;
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ rc = readl_poll_timeout(ispif->base + intf_addr,
+ stop_flag,
+ (stop_flag & 0xF) == 0xF,
+ ISPIF_TIMEOUT_SLEEP_US,
+ ISPIF_TIMEOUT_ALL_US);
+ if (rc < 0)
+ goto end;
+ }
+
+ /* disable CIDs in CID_MASK register */
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, vfe_intf, 0);
+ if (cid_right_mask)
+ msm_ispif_enable_intf_cids(ispif,
+ params->entries[i].intftype, cid_right_mask,
+ params->entries[i].vfe_intf, 0);
+ }
+
+end:
+ return rc;
+}
+
+static void ispif_process_irq(struct ispif_device *ispif,
+ struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id)
+{
+ if (WARN_ON(!ispif) || WARN_ON(!out)) {
+ pr_err("%s: invalid params", __func__);
+ return;
+ }
+
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ if (ispif->ispif_sof_debug < ISPIF_SOF_DEBUG_COUNT)
+ pr_err("%s: PIX0 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]);
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]++;
+ ispif->ispif_sof_debug++;
+ }
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ if (ispif->ispif_sof_debug < ISPIF_SOF_DEBUG_COUNT*2)
+ pr_err("%s: PIX1 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[PIX1]);
+ ispif->sof_count[vfe_id].sof_cnt[PIX1]++;
+ ispif->ispif_sof_debug++;
+ }
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
+ if (ispif->ispif_rdi0_debug < ISPIF_SOF_DEBUG_COUNT)
+ pr_err("%s: RDI0 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[RDI0]);
+ ispif->sof_count[vfe_id].sof_cnt[RDI0]++;
+ ispif->ispif_rdi0_debug++;
+ }
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_RDI1_SOF_MASK) {
+ if (ispif->ispif_rdi1_debug < ISPIF_SOF_DEBUG_COUNT)
+ pr_err("%s: RDI1 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[RDI1]);
+ ispif->sof_count[vfe_id].sof_cnt[RDI1]++;
+ ispif->ispif_rdi1_debug++;
+ }
+ if (out[vfe_id].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK) {
+ if (ispif->ispif_rdi2_debug < ISPIF_SOF_DEBUG_COUNT)
+ pr_err("%s: RDI2 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[RDI2]);
+ ispif->sof_count[vfe_id].sof_cnt[RDI2]++;
+ ispif->ispif_rdi2_debug++;
+ }
+}
+
+static int msm_ispif_reconfig_3d_output(struct ispif_device *ispif,
+ enum msm_ispif_vfe_intf vfe_id)
+{
+ uint32_t reg_data;
+
+ if (WARN_ON(!ispif))
+ return -EINVAL;
+
+ if (!((vfe_id == VFE0) || (vfe_id == VFE1))) {
+ pr_err("%s;%d Cannot reconfigure 3D mode for VFE%d", __func__,
+ __LINE__, vfe_id);
+ return -EINVAL;
+ }
+ pr_info("%s;%d Reconfiguring 3D mode for VFE%d", __func__, __LINE__,
+ vfe_id);
+ reg_data = 0xFFFCFFFC;
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_VFE_m_INTF_CMD_0(vfe_id));
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ if (vfe_id == VFE0) {
+ reg_data = 0;
+ reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB |
+ STROBED_RST_EN | PIX_0_CSID_RST_STB |
+ PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB);
+ msm_camera_io_w_mb(reg_data, ispif->base + ISPIF_RST_CMD_ADDR);
+ } else {
+ reg_data = 0;
+ reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB |
+ STROBED_RST_EN | PIX_0_CSID_RST_STB |
+ PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB);
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ }
+
+ reg_data = 0xFFFDFFFD;
+ msm_camera_io_w_mb(reg_data, ispif->base +
+ ISPIF_VFE_m_INTF_CMD_0(vfe_id));
+ return 0;
+}
+
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
+ void *data)
+{
+ struct ispif_device *ispif = (struct ispif_device *)data;
+ bool fatal_err = false;
+ int i = 0;
+ uint32_t reg_data;
+
+ if (WARN_ON(!ispif) || WARN_ON(!out)) {
+ pr_err("%s: invalid params", __func__);
+ return;
+ }
+
+ out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_0(VFE0));
+ msm_camera_io_w(out[VFE0].ispifIrqStatus0,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE0));
+
+ out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_1(VFE0));
+ msm_camera_io_w(out[VFE0].ispifIrqStatus1,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE0));
+
+ out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_2(VFE0));
+ msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE0));
+
+ if (ispif->vfe_info.num_vfe > 1) {
+ out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_0(VFE1));
+ msm_camera_io_w(out[VFE1].ispifIrqStatus0,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE1));
+
+ out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_1(VFE1));
+ msm_camera_io_w(out[VFE1].ispifIrqStatus1,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE1));
+
+ out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_2(VFE1));
+ msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE1));
+ }
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
+ if (out[VFE0].ispifIrqStatus0 & RESET_DONE_IRQ) {
+ if (atomic_dec_and_test(&ispif->reset_trig[VFE0]))
+ complete(&ispif->reset_complete[VFE0]);
+ }
+
+ if (out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 pix0 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE0].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 pix1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE0].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 rdi0 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE0].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 rdi1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE0].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE0 rdi2 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ ispif_process_irq(ispif, out, VFE0);
+ }
+ if (ispif->hw_num_isps > 1) {
+ if (out[VFE1].ispifIrqStatus0 & RESET_DONE_IRQ) {
+ if (atomic_dec_and_test(&ispif->reset_trig[VFE1]))
+ complete(&ispif->reset_complete[VFE1]);
+ }
+
+ if (out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 pix0 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE1].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 pix1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE1].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 rdi0 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE1].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 rdi1 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ if (out[VFE1].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ) {
+ pr_err_ratelimited("%s: VFE1 rdi2 overflow.\n",
+ __func__);
+ fatal_err = true;
+ }
+
+ ispif_process_irq(ispif, out, VFE1);
+ }
+
+ if ((out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE0].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE0].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ |
+ L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) {
+ reg_data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_OUTPUT_SEL(VFE0));
+ if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) {
+ pix_overflow_error_count[VFE0]++;
+ if (pix_overflow_error_count[VFE0] >=
+ MAX_PIX_OVERFLOW_ERROR_COUNT) {
+ msm_ispif_reconfig_3d_output(ispif, VFE0);
+ pix_overflow_error_count[VFE0] = 0;
+ }
+ fatal_err = false;
+ }
+ }
+
+ if (ispif->vfe_info.num_vfe > 1) {
+ if ((out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE1].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) ||
+ (out[VFE1].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ |
+ L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) {
+ reg_data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_OUTPUT_SEL(VFE1));
+ if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) {
+ pix_overflow_error_count[VFE1]++;
+ if (pix_overflow_error_count[VFE1] >=
+ MAX_PIX_OVERFLOW_ERROR_COUNT) {
+ msm_ispif_reconfig_3d_output(ispif,
+ VFE1);
+ pix_overflow_error_count[VFE1] = 0;
+ }
+ }
+ fatal_err = false;
+ }
+ }
+
+ if (fatal_err == true) {
+ pr_err_ratelimited("%s: fatal error, stop ispif immediately\n",
+ __func__);
+ for (i = 0; i < ispif->vfe_info.num_vfe; i++) {
+ msm_camera_io_w(0x0,
+ ispif->base + ISPIF_VFE_m_IRQ_MASK_0(i));
+ msm_camera_io_w(0x0,
+ ispif->base + ISPIF_VFE_m_IRQ_MASK_1(i));
+ msm_camera_io_w(0x0,
+ ispif->base + ISPIF_VFE_m_IRQ_MASK_2(i));
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
+ }
+ }
+}
+
+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 int msm_ispif_set_vfe_info(struct ispif_device *ispif,
+ struct msm_ispif_vfe_info *vfe_info)
+{
+ if (!vfe_info || (vfe_info->num_vfe == 0) ||
+ (vfe_info->num_vfe > ispif->hw_num_isps)) {
+ pr_err("Invalid VFE info: %pK %d\n", vfe_info,
+ (vfe_info ? vfe_info->num_vfe : 0));
+ return -EINVAL;
+ }
+
+ memcpy(&ispif->vfe_info, vfe_info, sizeof(struct msm_ispif_vfe_info));
+
+ return 0;
+}
+
+static int msm_ispif_init(struct ispif_device *ispif,
+ uint32_t csid_version)
+{
+ int rc = 0;
+
+ if (WARN_ON(!ispif)) {
+ pr_err("%s: invalid ispif params", __func__);
+ return -EINVAL;
+ }
+
+ if (ispif->ispif_state == ISPIF_POWER_UP) {
+ pr_err("%s: ispif already initted state = %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ 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_V30 && !ispif->clk_mux_base) {
+ ispif->clk_mux_base = msm_camera_get_reg_base(ispif->pdev,
+ "csi_clk_mux", 1);
+ if (!ispif->clk_mux_base)
+ return -ENOMEM;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0,
+ CAM_AHB_CLIENT_ISPIF, CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ rc = msm_ispif_reset_hw(ispif);
+ if (rc)
+ goto error_ahb;
+
+ rc = msm_ispif_reset(ispif);
+ if (rc)
+ goto error_ahb;
+ ispif->ispif_state = ISPIF_POWER_UP;
+ return 0;
+
+error_ahb:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+static void msm_ispif_release(struct ispif_device *ispif)
+{
+ if (WARN_ON(!ispif)) {
+ pr_err("%s: invalid ispif params", __func__);
+ return;
+ }
+
+ msm_camera_enable_irq(ispif->irq, 0);
+
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_ISPIF,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+}
+
+static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd,
+ struct ispif_device *ispif,
+ struct msm_ispif_param_data_ext *params)
+{
+ long rc = 0;
+
+ switch (cmd) {
+ case ISPIF_CFG:
+ rc = msm_ispif_config(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_START_FRAME_BOUNDARY:
+ rc = msm_ispif_start_frame_boundary(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RESTART_FRAME_BOUNDARY:
+ rc = msm_ispif_restart_frame_boundary(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_FRAME_BOUNDARY:
+ rc = msm_ispif_stop_frame_boundary(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_IMMEDIATELY:
+ rc = msm_ispif_stop_immediately(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RELEASE:
+ msm_ispif_reset(ispif);
+ msm_ispif_reset_hw(ispif);
+ break;
+ case ISPIF_CFG2:
+ rc = msm_ispif_config2(ispif, params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_CFG_STEREO:
+ msm_ispif_config_stereo(ispif, params,
+ ISPIF_CALCULATE_THRESHOLD);
+ break;
+ default:
+ pr_err("%s: invalid cfg_type\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ 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);
+ int i;
+ struct msm_ispif_param_data_ext params;
+
+ if (WARN_ON(!sd) || WARN_ON(!pcdata))
+ return -EINVAL;
+
+ mutex_lock(&ispif->mutex);
+ switch (pcdata->cfg_type) {
+ case ISPIF_ENABLE_REG_DUMP:
+ /* save dump config */
+ ispif->enb_dump_reg = pcdata->reg_dump;
+ break;
+ case ISPIF_INIT:
+ rc = msm_ispif_init(ispif, pcdata->csid_version);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_SET_VFE_INFO:
+ rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info);
+ break;
+ default:
+ memset(¶ms, 0, sizeof(params));
+ if (pcdata->params.num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid num entries %u\n", __func__,
+ pcdata->params.num);
+ rc = -EINVAL;
+ } else {
+ params.num = pcdata->params.num;
+ for (i = 0; i < pcdata->params.num; i++)
+ memcpy(¶ms.entries[i],
+ &pcdata->params.entries[i],
+ sizeof(struct msm_ispif_params_entry));
+ params.stereo_enable = 0;
+ rc = msm_ispif_dispatch_cmd(pcdata->cfg_type, ispif,
+ ¶ms);
+ }
+ break;
+ }
+ mutex_unlock(&ispif->mutex);
+
+ return rc;
+}
+
+static struct v4l2_file_operations msm_ispif_v4l2_subdev_fops;
+
+static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_MSM_ISPIF_CFG:
+ return msm_ispif_cmd(sd, arg);
+ case VIDIOC_MSM_ISPIF_CFG_EXT:
+ return msm_ispif_cmd_ext(sd, arg);
+ case MSM_SD_NOTIFY_FREEZE: {
+ ispif->ispif_sof_debug = 0;
+ ispif->ispif_rdi0_debug = 0;
+ ispif->ispif_rdi1_debug = 0;
+ ispif->ispif_rdi2_debug = 0;
+ return 0;
+ }
+ case MSM_SD_SHUTDOWN:
+ return 0;
+ default:
+ pr_err_ratelimited("%s: invalid cmd 0x%x received\n",
+ __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+}
+
+static long msm_ispif_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return msm_ispif_subdev_ioctl(sd, cmd, arg);
+}
+
+static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ispif_subdev_do_ioctl);
+}
+
+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) {
+ /* enable regulator and clocks on first open */
+ rc = msm_ispif_set_regulators(ispif->ispif_vdd,
+ ispif->ispif_vdd_count, 1);
+ if (rc)
+ goto unlock;
+
+ rc = msm_ispif_clk_ahb_enable(ispif, 1);
+ if (rc)
+ goto ahb_clk_enable_fail;
+ rc = msm_camera_enable_irq(ispif->irq, 1);
+ if (rc)
+ goto irq_enable_fail;
+ }
+ /* mem remap is done in init when the clock is on */
+ ispif->open_cnt++;
+ mutex_unlock(&ispif->mutex);
+ return rc;
+ahb_clk_enable_fail:
+ msm_ispif_set_regulators(ispif->ispif_vdd, ispif->ispif_vdd_count, 0);
+irq_enable_fail:
+ msm_ispif_clk_ahb_enable(ispif, 0);
+unlock:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+
+ if (!ispif) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt == 0) {
+ pr_err("%s: Invalid close\n", __func__);
+ rc = -ENODEV;
+ goto end;
+ }
+ ispif->open_cnt--;
+ if (ispif->open_cnt == 0) {
+ msm_ispif_release(ispif);
+ /* disable clocks and regulator on last close */
+ msm_ispif_clk_ahb_enable(ispif, 0);
+ msm_ispif_set_regulators(ispif->ispif_vdd,
+ ispif->ispif_vdd_count, 0);
+ }
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
+ .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 ispif_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct ispif_device *ispif;
+
+ ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
+ if (!ispif)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ rc = of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,num-isps", &ispif->hw_num_isps);
+ if (rc)
+ /* backward compatibility */
+ ispif->hw_num_isps = 1;
+ /* not an error condition */
+ rc = 0;
+ }
+
+ rc = msm_ispif_get_regulator_info(ispif, pdev);
+ if (rc < 0)
+ goto regulator_fail;
+
+ rc = msm_ispif_get_clk_info(ispif, pdev);
+ if (rc < 0) {
+ pr_err("%s: msm_isp_get_clk_info() failed", __func__);
+ rc = -EFAULT;
+ goto get_clk_fail;
+ }
+ mutex_init(&ispif->mutex);
+ ispif->base = msm_camera_get_reg_base(pdev, "ispif", 1);
+ if (!ispif->base) {
+ rc = -ENOMEM;
+ goto reg_base_fail;
+ }
+
+ ispif->irq = msm_camera_get_irq(pdev, "ispif");
+ if (!ispif->irq) {
+ rc = -ENODEV;
+ goto get_irq_fail;
+ }
+ rc = msm_camera_register_irq(pdev, ispif->irq, msm_io_ispif_irq,
+ IRQF_TRIGGER_RISING, "ispif", ispif);
+ if (rc) {
+ rc = -ENODEV;
+ goto get_irq_fail;
+ }
+ rc = msm_camera_enable_irq(ispif->irq, 0);
+ if (rc)
+ goto sd_reg_fail;
+
+ ispif->pdev = pdev;
+
+ 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_DRV_NAME);
+ v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
+
+ platform_set_drvdata(pdev, &ispif->msm_sd.sd);
+
+ media_entity_pads_init(&ispif->msm_sd.sd.entity, 0, NULL);
+ ispif->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_ISPIF;
+ ispif->msm_sd.sd.entity.name = pdev->name;
+ ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1;
+ rc = msm_sd_register(&ispif->msm_sd);
+ if (rc) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto sd_reg_fail;
+ }
+ msm_cam_copy_v4l2_subdev_fops(&msm_ispif_v4l2_subdev_fops);
+ msm_ispif_v4l2_subdev_fops.unlocked_ioctl =
+ msm_ispif_subdev_fops_ioctl;
+#ifdef CONFIG_COMPAT
+ msm_ispif_v4l2_subdev_fops.compat_ioctl32 = msm_ispif_subdev_fops_ioctl;
+#endif
+ ispif->msm_sd.sd.devnode->fops = &msm_ispif_v4l2_subdev_fops;
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+ ispif->open_cnt = 0;
+ init_completion(&ispif->reset_complete[VFE0]);
+ init_completion(&ispif->reset_complete[VFE1]);
+ atomic_set(&ispif->reset_trig[VFE0], 0);
+ atomic_set(&ispif->reset_trig[VFE1], 0);
+ return 0;
+
+sd_reg_fail:
+ msm_camera_unregister_irq(pdev, ispif->irq, ispif);
+get_irq_fail:
+ msm_camera_put_reg_base(pdev, ispif->base, "ispif", 1);
+reg_base_fail:
+ msm_camera_put_clk_info(pdev, &ispif->ahb_clk_info,
+ &ispif->ahb_clk,
+ ispif->num_ahb_clk + ispif->num_clk);
+get_clk_fail:
+ msm_ispif_put_regulator(ispif);
+regulator_fail:
+ 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/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
new file mode 100644
index 0000000..6d12e35
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2013-2018, 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"
+
+/* Maximum number of voltage supply for ispif and vfe */
+#define ISPIF_VDD_INFO_MAX 2
+#define ISPIF_VFE_VDD_INFO_MAX 2
+
+#define ISPIF_CLK_INFO_MAX 27
+
+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 *irq;
+ void __iomem *base;
+ void __iomem *clk_mux_base;
+ struct mutex mutex;
+ uint8_t start_ack_pending;
+ 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 msm_ispif_vfe_info vfe_info;
+ struct clk **ahb_clk;
+ struct msm_cam_clk_info *ahb_clk_info;
+ struct clk **clks;
+ struct msm_cam_clk_info *clk_info;
+ struct completion reset_complete[VFE_MAX];
+ atomic_t reset_trig[VFE_MAX];
+ uint32_t hw_num_isps;
+ uint32_t num_ahb_clk;
+ uint32_t num_clk;
+ uint32_t clk_idx;
+ uint32_t ispif_sof_debug;
+ uint32_t ispif_rdi0_debug;
+ uint32_t ispif_rdi1_debug;
+ uint32_t ispif_rdi2_debug;
+ struct regulator *ispif_vdd[ISPIF_VDD_INFO_MAX];
+ int ispif_vdd_count;
+ struct regulator *vfe_vdd[ISPIF_VFE_VDD_INFO_MAX];
+ int vfe_vdd_count;
+ int stereo_configured[VFE_MAX];
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
new file mode 100644
index 0000000..c556613
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h
@@ -0,0 +1,130 @@
+/* Copyright (c) 2013-2016, 2018, 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_V1_H__
+#define __MSM_ISPIF_HWREG_V1_H__
+
+/* common registers */
+#define ISPIF_RST_CMD_ADDR 0x0000
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x0124
+#define PIX0_LINE_BUF_EN_BIT 0
+
+#define ISPIF_VFE(m) (0x0)
+
+#define ISPIF_VFE_m_CTRL_0(m) (0x0008 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x0100 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x010C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x0118 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x0108 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x0114 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x0120 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x0104 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x0110 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x011C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INPUT_SEL(m) (0x000C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_0(m) (0x0004 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_1(m) (0x0030 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) (0x0010 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) (0x0014 + ISPIF_VFE(m) + \
+ ((n > 0) ? (0x20) : 0) \
+ + 8*(n))
+#define ISPIF_VFE_m_PIX_OUTPUT_n_MISR(m, n) (0x0290 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_0(m, n) (0x001C + ISPIF_VFE(m) + \
+ ((n > 0) ? (0x24) : 0) \
+ + 0xc*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_1(m, n) (0x0020 + ISPIF_VFE(m) + \
+ ((n > 0) ? (0x24) : 0) \
+ + 0xc*(n))
+#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) (0x0024 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) (0x0028 + ISPIF_VFE(m) + \
+ ((n > 0) ? (0x34) : 0) \
+ + 8*(n))
+
+/* Defines for compatibility with newer ISPIF versions */
+#define ISPIF_RST_CMD_1_ADDR (0x0000)
+#define ISPIF_VFE_m_PIX_INTF_n_CROP(m, n) (0x0000 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_3D_THRESHOLD(m) (0x0000 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_OUTPUT_SEL(m) (0x0000 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_3D_DESKEW_SIZE(m) (0x0000 + ISPIF_VFE(m))
+
+
+
+/* CSID CLK MUX SEL REGISTERS */
+#define ISPIF_RDI_CLK_MUX_SEL_ADDR 0x8
+
+/*ISPIF RESET BITS*/
+#define VFE_CLK_DOMAIN_RST BIT(31)
+#define RDI_CLK_DOMAIN_RST BIT(30)
+#define PIX_CLK_DOMAIN_RST BIT(29)
+#define AHB_CLK_DOMAIN_RST BIT(28)
+#define RDI_1_CLK_DOMAIN_RST BIT(27)
+#define PIX_1_CLK_DOMAIN_RST BIT(26)
+#define RDI_2_CLK_DOMAIN_RST BIT(25)
+#define RDI_2_MISR_RST_STB BIT(20)
+#define RDI_2_VFE_RST_STB BIT(19)
+#define RDI_2_CSID_RST_STB BIT(18)
+#define RDI_1_MISR_RST_STB BIT(14)
+#define RDI_1_VFE_RST_STB BIT(13)
+#define RDI_1_CSID_RST_STB BIT(12)
+#define PIX_1_VFE_RST_STB BIT(10)
+#define PIX_1_CSID_RST_STB BIT(9)
+#define RDI_0_MISR_RST_STB BIT(8)
+#define RDI_0_VFE_RST_STB BIT(7)
+#define RDI_0_CSID_RST_STB BIT(6)
+#define PIX_0_MISR_RST_STB BIT(5)
+#define PIX_0_VFE_RST_STB BIT(4)
+#define PIX_0_CSID_RST_STB BIT(3)
+#define SW_REG_RST_STB BIT(2)
+#define MISC_LOGIC_RST_STB BIT(1)
+#define STROBED_RST_EN BIT(0)
+
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
+#define ISPIF_RST_CMD_MASK 0xFE1C77FF
+#define ISPIF_RST_CMD_1_MASK 0xFFFFFFFF /* undefined */
+
+#define ISPIF_RST_CMD_MASK_RESTART 0x00001FF9
+#define ISPIF_RST_CMD_1_MASK_RESTART 0x00001FF9 /* undefined */
+
+/* irq_mask_0 */
+#define PIX_INTF_0_OVERFLOW_IRQ BIT(12)
+#define RAW_INTF_0_OVERFLOW_IRQ BIT(25)
+#define RESET_DONE_IRQ BIT(27)
+/* irq_mask_1 */
+#define PIX_INTF_1_OVERFLOW_IRQ BIT(12)
+#define RAW_INTF_1_OVERFLOW_IRQ BIT(25)
+/* irq_mask_2 */
+#define RAW_INTF_2_OVERFLOW_IRQ BIT(12)
+
+#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 0x000249
+#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x000249
+
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x000001
+
+#define ISPIF_STOP_INTF_IMMEDIATELY 0xAAAAAAAA
+
+/* ISPIF RDI pack mode not supported */
+static inline void msm_ispif_cfg_pack_mode(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf, uint32_t *pack_cfg_mask)
+{
+}
+#endif /* __MSM_ISPIF_HWREG_V1_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
new file mode 100644
index 0000000..dc3a75b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2013-2016, 2018, 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_V2_H__
+#define __MSM_ISPIF_HWREG_V2_H__
+
+/* common registers */
+#define ISPIF_RST_CMD_ADDR 0x008
+#define ISPIF_RST_CMD_1_ADDR 0x00C
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x01C
+#define PIX0_LINE_BUF_EN_BIT 6
+
+#define ISPIF_VFE(m) ((m) * 0x200)
+
+#define ISPIF_VFE_m_CTRL_0(m) (0x200 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_CTRL_1(m) (0x204 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x21C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x220 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x224 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x230 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x234 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x238 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INPUT_SEL(m) (0x244 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_0(m) (0x248 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_1(m) (0x24C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) (0x254 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) (0x264 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_PIX_INTF_n_CROP(m, n) (0x278 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_3D_THRESHOLD(m) (0x288 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_OUTPUT_SEL(m) (0x28C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_PIX_OUTPUT_n_MISR(m, n) (0x290 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_0(m, n) (0x298 + ISPIF_VFE(m) + 8*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_1(m, n) (0x29C + ISPIF_VFE(m) + 8*(n))
+#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) (0x2C0 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) (0x2D0 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_3D_DESKEW_SIZE(m) (0x2E4 + ISPIF_VFE(m))
+
+/* CSID CLK MUX SEL REGISTERS */
+#define ISPIF_RDI_CLK_MUX_SEL_ADDR 0x8
+
+/*ISPIF RESET BITS*/
+#define VFE_CLK_DOMAIN_RST BIT(31)
+#define PIX_1_CLK_DOMAIN_RST BIT(30)
+#define PIX_CLK_DOMAIN_RST BIT(29)
+#define RDI_2_CLK_DOMAIN_RST BIT(28)
+#define RDI_1_CLK_DOMAIN_RST BIT(27)
+#define RDI_CLK_DOMAIN_RST BIT(26)
+#define AHB_CLK_DOMAIN_RST BIT(25)
+#define RDI_2_VFE_RST_STB BIT(12)
+#define RDI_2_CSID_RST_STB BIT(11)
+#define RDI_1_VFE_RST_STB BIT(10)
+#define RDI_1_CSID_RST_STB BIT(9)
+#define RDI_0_VFE_RST_STB BIT(8)
+#define RDI_0_CSID_RST_STB BIT(7)
+#define PIX_1_VFE_RST_STB BIT(6)
+#define PIX_1_CSID_RST_STB BIT(5)
+#define PIX_0_VFE_RST_STB BIT(4)
+#define PIX_0_CSID_RST_STB BIT(3)
+#define SW_REG_RST_STB BIT(2)
+#define MISC_LOGIC_RST_STB BIT(1)
+#define STROBED_RST_EN BIT(0)
+
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
+#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
+#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
+
+#define ISPIF_RST_CMD_MASK_RESTART 0x00001FF9
+#define ISPIF_RST_CMD_1_MASK_RESTART 0x00001FF9
+
+#define PIX_INTF_0_OVERFLOW_IRQ BIT(12)
+#define PIX_INTF_1_OVERFLOW_IRQ BIT(12)
+#define RAW_INTF_0_OVERFLOW_IRQ BIT(25)
+#define RAW_INTF_1_OVERFLOW_IRQ BIT(25)
+#define RAW_INTF_2_OVERFLOW_IRQ BIT(12)
+#define RESET_DONE_IRQ BIT(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_GLOBAL_CLEAR_CMD 0x1
+
+#define ISPIF_STOP_INTF_IMMEDIATELY 0xAAAAAAAA
+
+/* ISPIF RDI pack mode not supported */
+static inline void msm_ispif_cfg_pack_mode(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf, uint32_t *pack_cfg_mask)
+{
+}
+#endif /* __MSM_ISPIF_HWREG_V2_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h
new file mode 100644
index 0000000..de9f462
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h
@@ -0,0 +1,143 @@
+/* Copyright (c) 2013-2016, 2018, 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_V3_H__
+#define __MSM_ISPIF_HWREG_V3_H__
+
+/* common registers */
+#define ISPIF_RST_CMD_ADDR 0x008
+#define ISPIF_RST_CMD_1_ADDR 0x00C
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x01C
+#define PIX0_LINE_BUF_EN_BIT 6
+
+#define ISPIF_VFE(m) ((m) * 0x200)
+
+#define ISPIF_VFE_m_CTRL_0(m) (0x200 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_CTRL_1(m) (0x204 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x21C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x220 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x224 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x230 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x234 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x238 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INPUT_SEL(m) (0x244 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_0(m) (0x248 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_INTF_CMD_1(m) (0x24C + ISPIF_VFE(m))
+#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) (0x254 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) (0x264 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_0(m, n) (0x270 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_1(m, n) (0x27C + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_PIX_INTF_n_CROP(m, n) (0x288 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_3D_THRESHOLD(m) (0x290 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_OUTPUT_SEL(m) (0x294 + ISPIF_VFE(m))
+#define ISPIF_VFE_m_PIX_OUTPUT_n_MISR(m, n) (0x298 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_0(m, n) (0x29C + ISPIF_VFE(m) + 8*(n))
+#define ISPIF_VFE_m_RDI_OUTPUT_n_MISR_1(m, n) (0x2A0 + ISPIF_VFE(m) + 8*(n))
+#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) (0x2C0 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) (0x2D0 + ISPIF_VFE(m) + 4*(n))
+#define ISPIF_VFE_m_3D_DESKEW_SIZE(m) (0x2E4 + ISPIF_VFE(m))
+
+/* CSID CLK MUX SEL REGISTERS */
+#define ISPIF_RDI_CLK_MUX_SEL_ADDR 0x8
+
+/*ISPIF RESET BITS*/
+#define VFE_CLK_DOMAIN_RST BIT(31)
+#define PIX_1_CLK_DOMAIN_RST BIT(30)
+#define PIX_CLK_DOMAIN_RST BIT(29)
+#define RDI_2_CLK_DOMAIN_RST BIT(28)
+#define RDI_1_CLK_DOMAIN_RST BIT(27)
+#define RDI_CLK_DOMAIN_RST BIT(26)
+#define AHB_CLK_DOMAIN_RST BIT(25)
+#define RDI_2_VFE_RST_STB BIT(12)
+#define RDI_2_CSID_RST_STB BIT(11)
+#define RDI_1_VFE_RST_STB BIT(10)
+#define RDI_1_CSID_RST_STB BIT(9)
+#define RDI_0_VFE_RST_STB BIT(8)
+#define RDI_0_CSID_RST_STB BIT(7)
+#define PIX_1_VFE_RST_STB BIT(6)
+#define PIX_1_CSID_RST_STB BIT(5)
+#define PIX_0_VFE_RST_STB BIT(4)
+#define PIX_0_CSID_RST_STB BIT(3)
+#define SW_REG_RST_STB BIT(2)
+#define MISC_LOGIC_RST_STB BIT(1)
+#define STROBED_RST_EN BIT(0)
+
+#define VFE_PIX_INTF_SEL_3D 0x3
+#define PIX_OUTPUT_0_MISR_RST_STB BIT(16)
+#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16)
+#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17)
+#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18)
+
+#define ISPIF_RST_CMD_MASK 0xFE7F1FFF
+#define ISPIF_RST_CMD_1_MASK 0xFC7F1FF9
+
+#define ISPIF_RST_CMD_MASK_RESTART 0x7F1FF9
+#define ISPIF_RST_CMD_1_MASK_RESTART 0x7F1FF9
+
+#define PIX_INTF_0_OVERFLOW_IRQ BIT(12)
+#define PIX_INTF_1_OVERFLOW_IRQ BIT(12)
+#define RAW_INTF_0_OVERFLOW_IRQ BIT(25)
+#define RAW_INTF_1_OVERFLOW_IRQ BIT(25)
+#define RAW_INTF_2_OVERFLOW_IRQ BIT(12)
+#define RESET_DONE_IRQ BIT(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_GLOBAL_CLEAR_CMD 0x1
+
+#define ISPIF_STOP_INTF_IMMEDIATELY 0xAAAAAAAA
+
+/* ISPIF RDI pack mode support */
+static inline void msm_ispif_cfg_pack_mode(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf, uint32_t *pack_cfg_mask)
+{
+ uint32_t pack_addr[2];
+
+ if (WARN_ON(!ispif))
+ return;
+
+ switch (intftype) {
+ case RDI0:
+ pack_addr[0] = ISPIF_VFE_m_RDI_INTF_n_PACK_0(vfe_intf, 0);
+ pack_addr[1] = ISPIF_VFE_m_RDI_INTF_n_PACK_1(vfe_intf, 0);
+ break;
+ case RDI1:
+ pack_addr[0] = ISPIF_VFE_m_RDI_INTF_n_PACK_0(vfe_intf, 1);
+ pack_addr[1] = ISPIF_VFE_m_RDI_INTF_n_PACK_1(vfe_intf, 1);
+ break;
+ case RDI2:
+ pack_addr[0] = ISPIF_VFE_m_RDI_INTF_n_PACK_0(vfe_intf, 2);
+ pack_addr[1] = ISPIF_VFE_m_RDI_INTF_n_PACK_1(vfe_intf, 2);
+ break;
+ default:
+ pr_debug("%s: pack_mode not supported on intftype=%d\n",
+ __func__, intftype);
+ return;
+ }
+ pr_debug("%s: intftype %d pack_mask %x: 0x%x, %x:0x%x\n",
+ __func__, intftype, pack_addr[0],
+ pack_cfg_mask[0], pack_addr[1],
+ pack_cfg_mask[1]);
+ msm_camera_io_w_mb(pack_cfg_mask[0], ispif->base + pack_addr[0]);
+ msm_camera_io_w_mb(pack_cfg_mask[1], ispif->base + pack_addr[1]);
+}
+#endif /* __MSM_ISPIF_HWREG_V3_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile b/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile
new file mode 100644
index 0000000..0b8dc1d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile
@@ -0,0 +1,7 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_10
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+
+obj-$(CONFIG_MSMB_JPEG) += msm_jpeg_dev.o msm_jpeg_sync.o msm_jpeg_core.o msm_jpeg_hw.o msm_jpeg_platform.o
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_common.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_common.h
new file mode 100644
index 0000000..bf49086
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_common.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012-2015, 2018, 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_JPEG_COMMON_H
+#define MSM_JPEG_COMMON_H
+
+#define JPEG_DBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define JPEG_PR_ERR pr_err
+#define JPEG_DBG_HIGH pr_debug
+
+#define JPEG_BUS_VOTED(pgmn_dev) (pgmn_dev->jpeg_bus_vote = 1)
+#define JPEG_BUS_UNVOTED(pgmn_dev) (pgmn_dev->jpeg_bus_vote = 0)
+
+enum JPEG_MODE {
+ JPEG_MODE_DISABLE,
+ JPEG_MODE_OFFLINE,
+ JPEG_MODE_REALTIME,
+ JPEG_MODE_REALTIME_ROTATION
+};
+
+enum JPEG_ROTATION {
+ JPEG_ROTATION_0,
+ JPEG_ROTATION_90,
+ JPEG_ROTATION_180,
+ JPEG_ROTATION_270
+};
+
+#endif /* MSM_JPEG_COMMON_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.c
new file mode 100644
index 0000000..dc9b664
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.c
@@ -0,0 +1,384 @@
+/* Copyright (c) 2012-2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "msm_jpeg_hw.h"
+#include "msm_jpeg_core.h"
+#include "msm_jpeg_platform.h"
+#include "msm_jpeg_common.h"
+
+int msm_jpeg_core_reset(struct msm_jpeg_device *pgmn_dev, uint8_t op_mode,
+ void *base, int size) {
+ unsigned long flags;
+ int rc = 0;
+ int tm = 500; /*500ms*/
+
+ JPEG_DBG("%s:%d] reset", __func__, __LINE__);
+ memset(&pgmn_dev->fe_pingpong_buf, 0,
+ sizeof(pgmn_dev->fe_pingpong_buf));
+ pgmn_dev->fe_pingpong_buf.is_fe = 1;
+ memset(&pgmn_dev->we_pingpong_buf, 0,
+ sizeof(pgmn_dev->we_pingpong_buf));
+ spin_lock_irqsave(&pgmn_dev->reset_lock, flags);
+ pgmn_dev->reset_done_ack = 0;
+ if (pgmn_dev->core_type == MSM_JPEG_CORE_CODEC)
+ msm_jpeg_hw_reset(base, size);
+ else
+ msm_jpeg_hw_reset_dma(base, size);
+
+ spin_unlock_irqrestore(&pgmn_dev->reset_lock, flags);
+ rc = wait_event_timeout(
+ pgmn_dev->reset_wait,
+ pgmn_dev->reset_done_ack,
+ msecs_to_jiffies(tm));
+
+ if (!pgmn_dev->reset_done_ack) {
+ JPEG_DBG("%s: reset ACK failed %d", __func__, rc);
+ return -EBUSY;
+ }
+
+ JPEG_DBG("%s: reset_done_ack rc %d", __func__, rc);
+ spin_lock_irqsave(&pgmn_dev->reset_lock, flags);
+ pgmn_dev->reset_done_ack = 0;
+ pgmn_dev->state = MSM_JPEG_RESET;
+ spin_unlock_irqrestore(&pgmn_dev->reset_lock, flags);
+
+ return 0;
+}
+
+void msm_jpeg_core_release(struct msm_jpeg_device *pgmn_dev)
+{
+ int i = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (pgmn_dev->we_pingpong_buf.buf_status[i] &&
+ pgmn_dev->release_buf)
+ msm_jpeg_platform_p2v(pgmn_dev->iommu_hdl,
+ pgmn_dev->we_pingpong_buf.buf[i].ion_fd);
+ pgmn_dev->we_pingpong_buf.buf_status[i] = 0;
+ }
+}
+
+void msm_jpeg_core_init(struct msm_jpeg_device *pgmn_dev)
+{
+ init_waitqueue_head(&pgmn_dev->reset_wait);
+ spin_lock_init(&pgmn_dev->reset_lock);
+}
+
+int msm_jpeg_core_fe_start(struct msm_jpeg_device *pgmn_dev)
+{
+ msm_jpeg_hw_fe_start(pgmn_dev->base);
+ return 0;
+}
+
+/* fetch engine */
+int msm_jpeg_core_fe_buf_update(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf)
+{
+ int rc = 0;
+
+ if (buf->cbcr_len == 0)
+ buf->cbcr_buffer_addr = 0x0;
+
+ JPEG_DBG("%s:%d] 0x%08x %d 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, buf->y_len,
+ (int) buf->cbcr_buffer_addr, buf->cbcr_len);
+
+ if (pgmn_dev->core_type == MSM_JPEG_CORE_CODEC) {
+ rc = msm_jpeg_hw_pingpong_update(&pgmn_dev->fe_pingpong_buf,
+ buf, pgmn_dev->base);
+ if (rc < 0)
+ return rc;
+ msm_jpeg_hw_fe_mmu_prefetch(buf, pgmn_dev->base,
+ pgmn_dev->decode_flag);
+ } else {
+ rc = msm_jpegdma_hw_pingpong_update(
+ &pgmn_dev->fe_pingpong_buf, buf, pgmn_dev->base);
+ if (rc < 0)
+ return rc;
+ msm_jpegdma_hw_fe_mmu_prefetch(buf, pgmn_dev->base);
+ }
+
+ return rc;
+}
+
+static void *msm_jpeg_core_fe_pingpong_irq(int jpeg_irq_status,
+ struct msm_jpeg_device *pgmn_dev)
+{
+ return msm_jpeg_hw_pingpong_irq(&pgmn_dev->fe_pingpong_buf);
+}
+
+/* write engine */
+int msm_jpeg_core_we_buf_update(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf) {
+
+ JPEG_DBG("%s:%d] 0x%08x 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, (int) buf->cbcr_buffer_addr,
+ buf->y_len);
+
+ pgmn_dev->we_pingpong_buf.buf[0] = *buf;
+ pgmn_dev->we_pingpong_buf.buf_status[0] = 1;
+
+ if (pgmn_dev->core_type == MSM_JPEG_CORE_CODEC) {
+ msm_jpeg_hw_we_buffer_update(
+ &pgmn_dev->we_pingpong_buf.buf[0], 0, pgmn_dev->base);
+ msm_jpeg_hw_we_mmu_prefetch(buf, pgmn_dev->base,
+ pgmn_dev->decode_flag);
+ } else {
+ msm_jpegdma_hw_we_buffer_update(
+ &pgmn_dev->we_pingpong_buf.buf[0], 0, pgmn_dev->base);
+ msm_jpegdma_hw_we_mmu_prefetch(buf, pgmn_dev->base);
+ }
+
+ return 0;
+}
+
+int msm_jpeg_core_we_buf_reset(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_hw_buf *buf)
+{
+ int i = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (pgmn_dev->we_pingpong_buf.buf[i].y_buffer_addr
+ == buf->y_buffer_addr)
+ pgmn_dev->we_pingpong_buf.buf_status[i] = 0;
+ }
+ return 0;
+}
+
+static void *msm_jpeg_core_we_pingpong_irq(int jpeg_irq_status,
+ struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+
+ return msm_jpeg_hw_pingpong_irq(&pgmn_dev->we_pingpong_buf);
+}
+
+static void *msm_jpeg_core_framedone_irq(int jpeg_irq_status,
+ struct msm_jpeg_device *pgmn_dev)
+{
+ struct msm_jpeg_hw_buf *buf_p;
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+
+ buf_p = msm_jpeg_hw_pingpong_active_buffer(
+ &pgmn_dev->we_pingpong_buf);
+ if (buf_p && !pgmn_dev->decode_flag) {
+ buf_p->framedone_len =
+ msm_jpeg_hw_encode_output_size(pgmn_dev->base);
+ JPEG_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+ buf_p->framedone_len);
+ }
+
+ return buf_p;
+}
+
+static void *msm_jpeg_core_reset_ack_irq(int jpeg_irq_status,
+ struct msm_jpeg_device *pgmn_dev)
+{
+ /* @todo return the status back to msm_jpeg_core_reset */
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ return NULL;
+}
+
+static void *msm_jpeg_core_err_irq(int jpeg_irq_status,
+ struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_PR_ERR("%s: Error %x\n", __func__, jpeg_irq_status);
+ return NULL;
+}
+
+static int (*msm_jpeg_irq_handler)(int, void *, void *);
+
+static void msm_jpeg_core_return_buffers(struct msm_jpeg_device *pgmn_dev,
+ int jpeg_irq_status)
+{
+ void *data = NULL;
+
+ data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(MSM_JPEG_HW_MASK_COMP_FE,
+ pgmn_dev, data);
+ data = msm_jpeg_core_we_pingpong_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(MSM_JPEG_HW_MASK_COMP_WE,
+ pgmn_dev, data);
+}
+
+irqreturn_t msm_jpeg_core_irq(int irq_num, void *context)
+{
+ void *data = NULL;
+ unsigned long flags;
+ int jpeg_irq_status;
+ struct msm_jpeg_device *pgmn_dev = (struct msm_jpeg_device *)context;
+
+ JPEG_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+ jpeg_irq_status = msm_jpeg_hw_irq_get_status(pgmn_dev->base);
+
+ JPEG_DBG("%s:%d] jpeg_irq_status = %0x\n", __func__, __LINE__,
+ jpeg_irq_status);
+
+ /*For reset and framedone IRQs, clear all bits*/
+ if (pgmn_dev->state == MSM_JPEG_IDLE) {
+ JPEG_DBG_HIGH("%s %d ] Error IRQ received state %d",
+ __func__, __LINE__, pgmn_dev->state);
+ JPEG_DBG_HIGH("%s %d ] Ignoring the Error", __func__,
+ __LINE__);
+ msm_jpeg_hw_irq_clear(JPEG_IRQ_CLEAR_BMSK,
+ JPEG_IRQ_CLEAR_ALL, pgmn_dev->base);
+ return IRQ_HANDLED;
+ } else if (jpeg_irq_status & 0x10000000) {
+ msm_jpeg_hw_irq_clear(JPEG_IRQ_CLEAR_BMSK,
+ JPEG_IRQ_CLEAR_ALL, pgmn_dev->base);
+ } else if (jpeg_irq_status & 0x1) {
+ msm_jpeg_hw_irq_clear(JPEG_IRQ_CLEAR_BMSK,
+ JPEG_IRQ_CLEAR_ALL, pgmn_dev->base);
+ if (pgmn_dev->decode_flag)
+ msm_jpeg_decode_status(pgmn_dev->base);
+ } else {
+ msm_jpeg_hw_irq_clear(JPEG_IRQ_CLEAR_BMSK,
+ jpeg_irq_status, pgmn_dev->base);
+ }
+
+ if (msm_jpeg_hw_irq_is_frame_done(jpeg_irq_status)) {
+ /* send fe ping pong irq */
+ JPEG_DBG_HIGH("%s:%d] Session done\n", __func__, __LINE__);
+ data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(MSM_JPEG_HW_MASK_COMP_FE,
+ context, data);
+ data = msm_jpeg_core_framedone_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(
+ MSM_JPEG_HW_MASK_COMP_FRAMEDONE,
+ context, data);
+ pgmn_dev->state = MSM_JPEG_INIT;
+ }
+ if (msm_jpeg_hw_irq_is_reset_ack(jpeg_irq_status)) {
+ data = msm_jpeg_core_reset_ack_irq(jpeg_irq_status,
+ pgmn_dev);
+ spin_lock_irqsave(&pgmn_dev->reset_lock, flags);
+ pgmn_dev->reset_done_ack = 1;
+ spin_unlock_irqrestore(&pgmn_dev->reset_lock, flags);
+ wake_up(&pgmn_dev->reset_wait);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(
+ MSM_JPEG_HW_MASK_COMP_RESET_ACK,
+ context, data);
+ }
+
+ /* Unexpected/unintended HW interrupt */
+ if (msm_jpeg_hw_irq_is_err(jpeg_irq_status)) {
+ if (pgmn_dev->state != MSM_JPEG_EXECUTING) {
+ /*Clear all the bits and ignore the IRQ*/
+ JPEG_DBG_HIGH("%s %d ] Error IRQ received state %d",
+ __func__, __LINE__, pgmn_dev->state);
+ JPEG_DBG_HIGH("%s %d ] Ignoring the Error", __func__,
+ __LINE__);
+ msm_jpeg_hw_irq_clear(JPEG_IRQ_CLEAR_BMSK,
+ JPEG_IRQ_CLEAR_ALL, pgmn_dev->base);
+ } else {
+ if (pgmn_dev->decode_flag)
+ msm_jpeg_decode_status(pgmn_dev->base);
+ msm_jpeg_core_return_buffers(pgmn_dev, jpeg_irq_status);
+ data = msm_jpeg_core_err_irq(jpeg_irq_status, pgmn_dev);
+ if (msm_jpeg_irq_handler) {
+ msm_jpeg_irq_handler(MSM_JPEG_HW_MASK_COMP_ERR,
+ context, data);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t msm_jpegdma_core_irq(int irq_num, void *context)
+{
+ void *data = NULL;
+ unsigned long flags;
+ int jpeg_irq_status;
+ struct msm_jpeg_device *pgmn_dev = context;
+
+ JPEG_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+ jpeg_irq_status = msm_jpegdma_hw_irq_get_status(pgmn_dev->base);
+
+ JPEG_DBG("%s:%d] jpeg_irq_status = %0x\n", __func__, __LINE__,
+ jpeg_irq_status);
+
+ /*For reset and framedone IRQs, clear all bits*/
+ if (pgmn_dev->state == MSM_JPEG_IDLE) {
+ JPEG_DBG_HIGH("%s %d ] Error IRQ received state %d",
+ __func__, __LINE__, pgmn_dev->state);
+ JPEG_DBG_HIGH("%s %d ] Ignoring the Error", __func__,
+ __LINE__);
+ msm_jpegdma_hw_irq_clear(JPEGDMA_IRQ_CLEAR_BMSK,
+ JPEGDMA_IRQ_CLEAR_ALL, pgmn_dev->base);
+ return IRQ_HANDLED;
+ } else if (jpeg_irq_status & 0x00000400) {
+ msm_jpegdma_hw_irq_clear(JPEGDMA_IRQ_CLEAR_BMSK,
+ JPEGDMA_IRQ_CLEAR_ALL, pgmn_dev->base);
+ } else if (jpeg_irq_status & 0x1) {
+ msm_jpegdma_hw_irq_clear(JPEGDMA_IRQ_CLEAR_BMSK,
+ JPEGDMA_IRQ_CLEAR_ALL, pgmn_dev->base);
+ } else {
+ msm_jpegdma_hw_irq_clear(JPEGDMA_IRQ_CLEAR_BMSK,
+ jpeg_irq_status, pgmn_dev->base);
+ }
+
+ if (msm_jpegdma_hw_irq_is_frame_done(jpeg_irq_status)) {
+ /* send fe ping pong irq */
+ JPEG_DBG_HIGH("%s:%d] Session done\n", __func__, __LINE__);
+ data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(MSM_JPEG_HW_MASK_COMP_FE,
+ context, data);
+ data = msm_jpeg_core_framedone_irq(jpeg_irq_status,
+ pgmn_dev);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(
+ MSM_JPEG_HW_MASK_COMP_FRAMEDONE,
+ context, data);
+ pgmn_dev->state = MSM_JPEG_INIT;
+ }
+ if (msm_jpegdma_hw_irq_is_reset_ack(jpeg_irq_status)) {
+ data = msm_jpeg_core_reset_ack_irq(jpeg_irq_status,
+ pgmn_dev);
+ spin_lock_irqsave(&pgmn_dev->reset_lock, flags);
+ pgmn_dev->reset_done_ack = 1;
+ spin_unlock_irqrestore(&pgmn_dev->reset_lock, flags);
+ wake_up(&pgmn_dev->reset_wait);
+ if (msm_jpeg_irq_handler)
+ msm_jpeg_irq_handler(
+ MSM_JPEG_HW_MASK_COMP_RESET_ACK,
+ context, data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void msm_jpeg_core_irq_install(int (*irq_handler) (int, void *, void *))
+{
+ msm_jpeg_irq_handler = irq_handler;
+}
+
+void msm_jpeg_core_irq_remove(void)
+{
+ msm_jpeg_irq_handler = NULL;
+}
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.h
new file mode 100644
index 0000000..4ca2ccb
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_core.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2012, 2014-2015, 2018, 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_JPEG_CORE_H
+#define MSM_JPEG_CORE_H
+
+#include <linux/interrupt.h>
+#include "msm_jpeg_hw.h"
+#include "msm_jpeg_sync.h"
+
+#define msm_jpeg_core_buf msm_jpeg_hw_buf
+
+irqreturn_t msm_jpeg_core_irq(int irq_num, void *context);
+irqreturn_t msm_jpegdma_core_irq(int irq_num, void *context);
+void msm_jpeg_core_irq_install(int (*irq_handler) (int, void *, void *));
+void msm_jpeg_core_irq_remove(void);
+
+int msm_jpeg_core_fe_buf_update(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf);
+int msm_jpeg_core_we_buf_update(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf);
+int msm_jpeg_core_we_buf_reset(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_hw_buf *buf);
+
+int msm_jpeg_core_reset(struct msm_jpeg_device *pgmn_dev, uint8_t op_mode,
+ void *base, int size);
+int msm_jpeg_core_fe_start(struct msm_jpeg_device *pgmn_dev);
+
+void msm_jpeg_core_release(struct msm_jpeg_device *pgmn_dev);
+void msm_jpeg_core_init(struct msm_jpeg_device *pgmn_dev);
+#endif /* MSM_JPEG_CORE_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
new file mode 100644
index 0000000..7d37d7e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
@@ -0,0 +1,318 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <media/msm_jpeg.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "msm_jpeg_sync.h"
+#include "msm_jpeg_common.h"
+
+#define MSM_JPEG_NAME "jpeg"
+#define DEV_NAME_LEN 10
+
+static int msm_jpeg_open(struct inode *inode, struct file *filp)
+{
+ int rc = 0;
+
+ struct msm_jpeg_device *pgmn_dev = container_of(inode->i_cdev,
+ struct msm_jpeg_device, cdev);
+ filp->private_data = pgmn_dev;
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_jpeg_open(pgmn_dev);
+
+ JPEG_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+
+ return rc;
+}
+
+static int msm_jpeg_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_jpeg_device *pgmn_dev = filp->private_data;
+
+ JPEG_DBG(KERN_INFO "%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_jpeg_release(pgmn_dev);
+
+ JPEG_DBG(KERN_INFO "%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+ return rc;
+}
+#ifdef CONFIG_COMPAT
+static long msm_jpeg_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+ struct msm_jpeg_device *pgmn_dev = filp->private_data;
+
+ JPEG_DBG("%s:%d] cmd=%d pgmn_dev=0x%pK arg=0x%lx\n", __func__,
+ __LINE__, _IOC_NR(cmd), pgmn_dev,
+ (unsigned long)arg);
+
+ rc = __msm_jpeg_compat_ioctl(pgmn_dev, cmd, arg);
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+#endif
+static long msm_jpeg_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+ struct msm_jpeg_device *pgmn_dev = filp->private_data;
+
+ JPEG_DBG("%s:%d] cmd=%d pgmn_dev=0x%pK arg=0x%lx\n", __func__,
+ __LINE__, _IOC_NR(cmd), pgmn_dev,
+ (unsigned long)arg);
+
+ rc = __msm_jpeg_ioctl(pgmn_dev, cmd, arg);
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct file_operations msm_jpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_jpeg_open,
+ .release = msm_jpeg_release,
+ .unlocked_ioctl = msm_jpeg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = msm_jpeg_compat_ioctl,
+#endif
+};
+
+static long msm_jpeg_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc;
+ struct msm_jpeg_device *pgmn_dev =
+ (struct msm_jpeg_device *)sd->host_priv;
+
+ JPEG_DBG("%s: cmd=%d\n", __func__, cmd);
+
+ JPEG_DBG("%s: pgmn_dev 0x%pK", __func__, pgmn_dev);
+
+ JPEG_DBG("%s: Calling __msm_jpeg_ioctl\n", __func__);
+
+ rc = __msm_jpeg_ioctl(pgmn_dev, cmd, (unsigned long)arg);
+ pr_debug("%s: X\n", __func__);
+ return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_jpeg_subdev_core_ops = {
+ .ioctl = msm_jpeg_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_jpeg_subdev_ops = {
+ .core = &msm_jpeg_subdev_core_ops,
+};
+
+struct msm_jpeg_priv_data {
+ enum msm_jpeg_core_type core_type;
+};
+
+static const struct msm_jpeg_priv_data msm_jpeg_priv_data_jpg = {
+ .core_type = MSM_JPEG_CORE_CODEC
+};
+static const struct msm_jpeg_priv_data msm_jpeg_priv_data_dma = {
+ .core_type = MSM_JPEG_CORE_DMA
+};
+
+static const struct of_device_id msm_jpeg_dt_match[] = {
+ {.compatible = "qcom,jpeg", .data = &msm_jpeg_priv_data_jpg},
+ {.compatible = "qcom,jpeg_dma", .data = &msm_jpeg_priv_data_dma},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_jpeg_dt_match);
+
+static int msm_jpeg_init_dev(struct platform_device *pdev)
+{
+ int rc = -1;
+ struct device *dev;
+ struct msm_jpeg_device *msm_jpeg_device_p;
+ const struct of_device_id *device_id;
+ const struct msm_jpeg_priv_data *priv_data;
+ char devname[DEV_NAME_LEN];
+
+ msm_jpeg_device_p = kzalloc(sizeof(struct msm_jpeg_device), GFP_ATOMIC);
+ if (!msm_jpeg_device_p) {
+ JPEG_PR_ERR("%s: no mem\n", __func__);
+ return -EFAULT;
+ }
+
+ msm_jpeg_device_p->pdev = pdev;
+
+ device_id = of_match_device(msm_jpeg_dt_match, &pdev->dev);
+ if (!device_id) {
+ JPEG_PR_ERR("%s: device_id is NULL\n", __func__);
+ goto fail;
+ }
+
+ priv_data = device_id->data;
+ msm_jpeg_device_p->core_type = priv_data->core_type;
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node, "cell-index",
+ &pdev->id);
+
+ snprintf(devname, sizeof(devname), "%s%d", MSM_JPEG_NAME, pdev->id);
+
+ rc = __msm_jpeg_init(msm_jpeg_device_p);
+ if (rc < -1) {
+ JPEG_PR_ERR("%s: initialization failed\n", __func__);
+ goto fail;
+ }
+
+ v4l2_subdev_init(&msm_jpeg_device_p->subdev, &msm_jpeg_subdev_ops);
+ v4l2_set_subdev_hostdata(&msm_jpeg_device_p->subdev, msm_jpeg_device_p);
+ JPEG_DBG("%s: msm_jpeg_device_p 0x%lx", __func__,
+ (unsigned long)msm_jpeg_device_p);
+
+ rc = alloc_chrdev_region(&msm_jpeg_device_p->msm_jpeg_devno, 0, 1,
+ devname);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: failed to allocate chrdev\n", __func__);
+ goto fail_1;
+ }
+
+ if (!msm_jpeg_device_p->msm_jpeg_class) {
+ msm_jpeg_device_p->msm_jpeg_class =
+ class_create(THIS_MODULE, devname);
+ if (IS_ERR(msm_jpeg_device_p->msm_jpeg_class)) {
+ rc = PTR_ERR(msm_jpeg_device_p->msm_jpeg_class);
+ JPEG_PR_ERR("%s: create device class failed\n",
+ __func__);
+ goto fail_2;
+ }
+ }
+
+ dev = device_create(msm_jpeg_device_p->msm_jpeg_class, NULL,
+ MKDEV(MAJOR(msm_jpeg_device_p->msm_jpeg_devno),
+ MINOR(msm_jpeg_device_p->msm_jpeg_devno)), NULL,
+ "%s%d", MSM_JPEG_NAME, pdev->id);
+ if (IS_ERR(dev)) {
+ JPEG_PR_ERR("%s: error creating device\n", __func__);
+ rc = -ENODEV;
+ goto fail_3;
+ }
+
+ cdev_init(&msm_jpeg_device_p->cdev, &msm_jpeg_fops);
+ msm_jpeg_device_p->cdev.owner = THIS_MODULE;
+ msm_jpeg_device_p->cdev.ops =
+ (const struct file_operations *) &msm_jpeg_fops;
+ rc = cdev_add(&msm_jpeg_device_p->cdev,
+ msm_jpeg_device_p->msm_jpeg_devno, 1);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: error adding cdev\n", __func__);
+ rc = -ENODEV;
+ goto fail_4;
+ }
+
+ platform_set_drvdata(pdev, msm_jpeg_device_p);
+
+ JPEG_DBG("%s %s%d: success\n", __func__, MSM_JPEG_NAME, pdev->id);
+
+ return rc;
+
+fail_4:
+ device_destroy(msm_jpeg_device_p->msm_jpeg_class,
+ msm_jpeg_device_p->msm_jpeg_devno);
+
+fail_3:
+ class_destroy(msm_jpeg_device_p->msm_jpeg_class);
+
+fail_2:
+ unregister_chrdev_region(msm_jpeg_device_p->msm_jpeg_devno, 1);
+
+fail_1:
+ __msm_jpeg_exit(msm_jpeg_device_p);
+ return rc;
+
+fail:
+ kfree(msm_jpeg_device_p);
+ return rc;
+
+}
+
+static void msm_jpeg_exit(struct msm_jpeg_device *msm_jpeg_device_p)
+{
+ cdev_del(&msm_jpeg_device_p->cdev);
+ device_destroy(msm_jpeg_device_p->msm_jpeg_class,
+ msm_jpeg_device_p->msm_jpeg_devno);
+ class_destroy(msm_jpeg_device_p->msm_jpeg_class);
+ unregister_chrdev_region(msm_jpeg_device_p->msm_jpeg_devno, 1);
+ cam_smmu_destroy_handle(msm_jpeg_device_p->iommu_hdl);
+
+ __msm_jpeg_exit(msm_jpeg_device_p);
+}
+
+static int __msm_jpeg_probe(struct platform_device *pdev)
+{
+ return msm_jpeg_init_dev(pdev);
+}
+
+static int __msm_jpeg_remove(struct platform_device *pdev)
+{
+ struct msm_jpeg_device *msm_jpegd_device_p;
+
+ msm_jpegd_device_p = platform_get_drvdata(pdev);
+ if (msm_jpegd_device_p)
+ msm_jpeg_exit(msm_jpegd_device_p);
+
+ return 0;
+}
+
+static struct platform_driver msm_jpeg_driver = {
+ .probe = __msm_jpeg_probe,
+ .remove = __msm_jpeg_remove,
+ .driver = {
+ .name = "msm_jpeg",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_jpeg_dt_match,
+ },
+};
+
+static int __init msm_jpeg_driver_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&msm_jpeg_driver);
+ return rc;
+}
+
+static void __exit msm_jpeg_driver_exit(void)
+{
+ platform_driver_unregister(&msm_jpeg_driver);
+}
+
+MODULE_DESCRIPTION("msm jpeg jpeg driver");
+
+module_init(msm_jpeg_driver_init);
+module_exit(msm_jpeg_driver_exit);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
new file mode 100644
index 0000000..ea82832
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
@@ -0,0 +1,955 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "msm_jpeg_hw.h"
+#include "msm_jpeg_common.h"
+#include "msm_camera_io_util.h"
+
+#include <linux/io.h>
+
+int msm_jpeg_hw_pingpong_update(struct msm_jpeg_hw_pingpong *pingpong_hw,
+ struct msm_jpeg_hw_buf *buf, void *base)
+{
+ int buf_free_index = -1;
+
+ if (!pingpong_hw->buf_status[0]) {
+ buf_free_index = 0;
+ } else if (!pingpong_hw->buf_status[1]) {
+ buf_free_index = 1;
+ } else {
+ JPEG_PR_ERR("%s:%d: pingpong buffer busy\n",
+ __func__, __LINE__);
+ return -EBUSY;
+ }
+
+ pingpong_hw->buf[buf_free_index] = *buf;
+ pingpong_hw->buf_status[buf_free_index] = 1;
+
+ if (pingpong_hw->is_fe) {
+ /* it is fe */
+ msm_jpeg_hw_fe_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index,
+ base);
+ } else {
+ /* it is we */
+ msm_jpeg_hw_we_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index,
+ base);
+ }
+ return 0;
+}
+
+int msm_jpegdma_hw_pingpong_update(struct msm_jpeg_hw_pingpong *pingpong_hw,
+ struct msm_jpeg_hw_buf *buf, void *base)
+{
+ int buf_free_index = -1;
+
+ if (!pingpong_hw->buf_status[0]) {
+ buf_free_index = 0;
+ } else if (!pingpong_hw->buf_status[1]) {
+ buf_free_index = 1;
+ } else {
+ JPEG_PR_ERR("%s:%d: pingpong buffer busy\n",
+ __func__, __LINE__);
+ return -EBUSY;
+ }
+
+ pingpong_hw->buf[buf_free_index] = *buf;
+ pingpong_hw->buf_status[buf_free_index] = 1;
+
+ if (pingpong_hw->is_fe) {
+ /* it is fe */
+ msm_jpegdma_hw_fe_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index,
+ base);
+ } else {
+ /* it is we */
+ msm_jpegdma_hw_we_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index,
+ base);
+ }
+ return 0;
+}
+void *msm_jpeg_hw_pingpong_irq(struct msm_jpeg_hw_pingpong *pingpong_hw)
+{
+ struct msm_jpeg_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index]) {
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+ pingpong_hw->buf_status[pingpong_hw->buf_active_index] = 0;
+ }
+
+ pingpong_hw->buf_active_index = !pingpong_hw->buf_active_index;
+
+ return (void *) buf_p;
+}
+
+void *msm_jpeg_hw_pingpong_active_buffer(
+ struct msm_jpeg_hw_pingpong *pingpong_hw)
+{
+ struct msm_jpeg_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index])
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+
+ return (void *) buf_p;
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_irq_get_status[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_READ, 1, JPEG_IRQ_STATUS_ADDR,
+ JPEG_IRQ_STATUS_BMSK, {0} },
+};
+
+int msm_jpeg_hw_irq_get_status(void *base)
+{
+ uint32_t n_irq_status = 0;
+
+ n_irq_status = msm_jpeg_hw_read(&hw_cmd_irq_get_status[0], base);
+ return n_irq_status;
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_irq_get_dmastatus[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_READ, 1, JPEGDMA_IRQ_STATUS_ADDR,
+ JPEGDMA_IRQ_STATUS_BMSK, {0} },
+};
+
+int msm_jpegdma_hw_irq_get_status(void *base)
+{
+ uint32_t n_irq_status = 0;
+
+ n_irq_status = msm_jpeg_hw_read(&hw_cmd_irq_get_dmastatus[0], base);
+ return n_irq_status;
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_encode_output_size[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_READ, 1,
+ JPEG_ENCODE_OUTPUT_SIZE_STATUS_ADDR,
+ JPEG_ENCODE_OUTPUT_SIZE_STATUS_BMSK, {0} },
+};
+
+long msm_jpeg_hw_encode_output_size(void *base)
+{
+ uint32_t encode_output_size = 0;
+
+ encode_output_size = msm_jpeg_hw_read(&hw_cmd_encode_output_size[0],
+ base);
+
+ return encode_output_size;
+}
+
+void msm_jpeg_hw_irq_clear(uint32_t mask, uint32_t data, void *base)
+{
+ struct msm_jpeg_hw_cmd cmd_irq_clear;
+
+ cmd_irq_clear.type = MSM_JPEG_HW_CMD_TYPE_WRITE;
+ cmd_irq_clear.n = 1;
+ cmd_irq_clear.offset = JPEG_IRQ_CLEAR_ADDR;
+ cmd_irq_clear.mask = mask;
+ cmd_irq_clear.data = data;
+ JPEG_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+ msm_jpeg_hw_write(&cmd_irq_clear, base);
+}
+
+void msm_jpegdma_hw_irq_clear(uint32_t mask, uint32_t data, void *base)
+{
+ struct msm_jpeg_hw_cmd cmd_irq_clear;
+
+ cmd_irq_clear.type = MSM_JPEG_HW_CMD_TYPE_WRITE;
+ cmd_irq_clear.n = 1;
+ cmd_irq_clear.offset = JPEGDMA_IRQ_CLEAR_ADDR;
+ cmd_irq_clear.mask = mask;
+ cmd_irq_clear.data = data;
+ JPEG_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+ msm_jpeg_hw_write(&cmd_irq_clear, base);
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_fe_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_IRQ_MASK_ADDR,
+ JPEG_IRQ_MASK_BMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_CMD_ADDR,
+ JPEG_CMD_BMSK, {JPEG_CMD_CLEAR_WRITE_PLN_QUEUES} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN0_RD_OFFSET_ADDR,
+ JPEG_PLN0_RD_OFFSET_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN0_RD_PNTR_ADDR,
+ JPEG_PLN0_RD_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_RD_OFFSET_ADDR,
+ JPEG_PLN1_RD_OFFSET_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_RD_PNTR_ADDR,
+ JPEG_PLN1_RD_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_OFFSET_ADDR,
+ JPEG_PLN1_RD_OFFSET_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_PNTR_ADDR,
+ JPEG_PLN2_RD_PNTR_BMSK, {0} },
+};
+
+void msm_jpeg_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_fe_ping_update[0];
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->y_buffer_addr;
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->cbcr_buffer_addr;
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->pln2_addr;
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+ }
+}
+
+static struct msm_jpeg_hw_cmd hw_dma_cmd_fe_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_IRQ_MASK_ADDR,
+ JPEGDMA_IRQ_MASK_BMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_CMD_ADDR,
+ JPEGDMA_CMD_BMSK, {JPEGDMA_CMD_CLEAR_READ_PLN_QUEUES} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_FE_0_RD_PNTR,
+ JPEG_PLN0_RD_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_FE_1_RD_PNTR,
+ JPEG_PLN1_RD_PNTR_BMSK, {0} },
+};
+
+void msm_jpegdma_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ if (pingpong_index != 0)
+ return;
+
+ hw_cmd_p = &hw_dma_cmd_fe_ping_update[0];
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->y_buffer_addr;
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->cbcr_buffer_addr;
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_fe_start[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_CMD_ADDR,
+ JPEG_CMD_BMSK, {JPEG_OFFLINE_CMD_START} },
+};
+
+void msm_jpeg_hw_fe_start(void *base)
+{
+ msm_jpeg_hw_write(&hw_cmd_fe_start[0], base);
+
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_we_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN0_WR_PNTR_ADDR,
+ JPEG_PLN0_WR_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_WR_PNTR_ADDR,
+ JPEG_PLN0_WR_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_WR_PNTR_ADDR,
+ JPEG_PLN0_WR_PNTR_BMSK, {0} },
+};
+
+void msm_jpeg_decode_status(void *base)
+{
+ uint32_t data;
+
+ data = msm_camera_io_r(
+ (void __iomem *)(base + JPEG_DECODE_MCUS_DECODED_STATUS));
+ JPEG_DBG_HIGH("Decode MCUs decode status %u", data);
+ data = msm_camera_io_r(
+ (void __iomem *)(base + JPEG_DECODE_BITS_CONSUMED_STATUS));
+ JPEG_DBG_HIGH("Decode bits consumed status %u", data);
+ data = msm_camera_io_r(
+ (void __iomem *)(base + JPEG_DECODE_PRED_Y_STATE));
+ JPEG_DBG_HIGH("Decode prediction Y state %u", data);
+ data = msm_camera_io_r(
+ (void __iomem *)(base + JPEG_DECODE_PRED_C_STATE));
+ JPEG_DBG_HIGH("Decode prediction C state %u", data);
+ data = msm_camera_io_r(
+ (void __iomem *)(base + JPEG_DECODE_RSM_STATE));
+ JPEG_DBG_HIGH("Decode prediction RSM state %u", data);
+}
+
+
+void msm_jpeg_hw_we_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_we_ping_update[0];
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->y_buffer_addr;
+ JPEG_DBG_HIGH("%s Output pln0 buffer address is %x\n", __func__,
+ p_input->y_buffer_addr);
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->cbcr_buffer_addr;
+ JPEG_DBG_HIGH("%s Output pln1 buffer address is %x\n", __func__,
+ p_input->cbcr_buffer_addr);
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->pln2_addr;
+ JPEG_DBG_HIGH("%s Output pln2 buffer address is %x\n", __func__,
+ p_input->pln2_addr);
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ }
+}
+
+static struct msm_jpeg_hw_cmd hw_dma_cmd_we_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_CMD_ADDR,
+ JPEGDMA_CMD_BMSK, {JPEGDMA_CMD_CLEAR_WRITE_PLN_QUEUES} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_WE_0_WR_PNTR,
+ JPEG_PLN0_WR_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_WE_1_WR_PNTR,
+ JPEG_PLN0_WR_PNTR_BMSK, {0} },
+};
+void msm_jpegdma_hw_we_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ if (pingpong_index != 0)
+ return;
+
+ hw_cmd_p = &hw_dma_cmd_we_ping_update[0];
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+
+ /* ensure write is done */
+ wmb();
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->y_buffer_addr;
+ JPEG_DBG_HIGH("%s Output we 0 buffer address is %x\n", __func__,
+ p_input->y_buffer_addr);
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = p_input->cbcr_buffer_addr;
+ JPEG_DBG_HIGH("%s Output we 1 buffer address is %x\n", __func__,
+ p_input->cbcr_buffer_addr);
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_fe_mmu_prefetch[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S0_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S0_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S0_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S0_MMU_PF_ADDR_MAX_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S1_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S1_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S1_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S1_MMU_PF_ADDR_MAX_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S2_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S2_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S2_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S2_MMU_PF_ADDR_MAX_BMSK, {0} },
+};
+
+/*
+ * msm_jpeg_hw_fe_mmu_prefetch() - writes fe min/max addrs for each plane to
+ * MMU prefetch registers.
+ * @buf: Pointer to jpeg hw buffer.
+ * @base: Pointer to base address.
+ * @decode_flag: Jpeg decode flag.
+ *
+ * This function writes fe min/max address for each plane to MMU prefetch
+ * registers, MMU prefetch hardware will only prefetch address translations
+ * within this min/max boundary.
+ *
+ * Return: None.
+ */
+void msm_jpeg_hw_fe_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base,
+ uint8_t decode_flag)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ hw_cmd_p = &hw_cmd_fe_mmu_prefetch[0];
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN y_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+ if (buf->y_len)
+ tmp_hw_cmd.data += buf->y_len - 1;
+
+ JPEG_DBG("%s:%d: MAX y_buf_addr %08x, y_len %d\n",
+ __func__, __LINE__, tmp_hw_cmd.data, buf->y_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ if (!decode_flag) {
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->cbcr_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN cbcr_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->cbcr_buffer_addr;
+ if (buf->cbcr_len)
+ tmp_hw_cmd.data += buf->cbcr_len - 1;
+
+ JPEG_DBG("%s:%d: MAX cbcr_buf_addr %08x, cbcr_len %d\n"
+ , __func__, __LINE__, tmp_hw_cmd.data, buf->cbcr_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->pln2_addr;
+
+ JPEG_DBG("%s:%d: MIN pln2_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->pln2_addr;
+ if (buf->pln2_len)
+ tmp_hw_cmd.data += buf->pln2_len - 1;
+
+ JPEG_DBG("%s:%d: MAX pln2_buf_addr %08x, pln2_len %d\n"
+ , __func__, __LINE__, tmp_hw_cmd.data, buf->pln2_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ }
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_we_mmu_prefetch[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S1_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S1_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S1_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S1_MMU_PF_ADDR_MAX_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S2_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S2_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S2_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S2_MMU_PF_ADDR_MAX_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S3_MMU_PF_ADDR_MIN,
+ MSM_JPEG_S3_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEG_S3_MMU_PF_ADDR_MAX,
+ MSM_JPEG_S3_MMU_PF_ADDR_MAX_BMSK, {0} },
+};
+
+/*
+ * msm_jpeg_hw_we_mmu_prefetch() - write we min/max addrs for each plane to
+ * MMU prefetch registers.
+ * @buf: Pointer to jpeg hw buffer.
+ * @base: Pointer to base address.
+ * @decode_flag: Jpeg decode flag.
+ *
+ * This function writes we min/max address for each plane to MMU prefetch
+ * registers, MMU prefetch hardware will only prefetch address translations
+ * within this min/max boundary.
+ *
+ * Return: None.
+ */
+void msm_jpeg_hw_we_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base,
+ uint8_t decode_flag)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ hw_cmd_p = &hw_cmd_we_mmu_prefetch[0];
+
+ /* ensure write is done */
+ wmb();
+ if (decode_flag) {
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN y_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+ if (buf->y_len)
+ tmp_hw_cmd.data += buf->y_len - 1;
+
+ JPEG_DBG("%s:%d: MAX y_buf_addr %08x, y_len %d\n",
+ __func__, __LINE__, tmp_hw_cmd.data, buf->y_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->cbcr_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN cbcr_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->cbcr_buffer_addr;
+ if (buf->cbcr_len)
+ tmp_hw_cmd.data += buf->cbcr_len - 1;
+
+ JPEG_DBG("%s:%d: MAX cbcr_buf_addr %08x, cbcr_len %d\n"
+ , __func__, __LINE__, tmp_hw_cmd.data, buf->cbcr_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->pln2_addr;
+
+ JPEG_DBG("%s:%d: MIN pln2_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->pln2_addr;
+ if (buf->pln2_len)
+ tmp_hw_cmd.data += buf->pln2_len - 1;
+
+ JPEG_DBG("%s:%d: MIN pln2_buf_addr %08x, pln2_len %d\n"
+ , __func__, __LINE__, tmp_hw_cmd.data, buf->pln2_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ } else {
+ hw_cmd_p += 4;
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN y_buf_addr %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+ if (buf->y_len)
+ tmp_hw_cmd.data += buf->y_len - 1;
+
+ JPEG_DBG("%s:%d: MAX y_buf_addr %08x, y_len %d\n",
+ __func__, __LINE__, tmp_hw_cmd.data, buf->y_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ }
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_dma_cmd_fe_mmu_prefetch[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN,
+ MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX,
+ MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX_BMSK, {0} },
+};
+
+/*
+ * msm_jpegdma_hw_fe_mmu_prefetch() - write DMA fe min/max addrs to
+ * MMU prefetch registers.
+ * @buf: Pointer to jpeg hw buffer.
+ * @base: Pointer to base address.
+ *
+ * This function writes DMA fe min/max address for each plane to MMU prefetch
+ * registers, MMU prefetch hardware will only prefetch address translations
+ * with in this min/max boundary.
+ *
+ * Return: None.
+ */
+void msm_jpegdma_hw_fe_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ hw_cmd_p = &hw_dma_cmd_fe_mmu_prefetch[0];
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN DMA addr %08x , reg offset %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data, tmp_hw_cmd.offset);
+
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+ if (buf->y_len)
+ tmp_hw_cmd.data += buf->y_len - 1;
+
+ JPEG_DBG("%s:%d: MAX DMA addr %08x , reg offset %08x , length %d\n",
+ __func__, __LINE__, tmp_hw_cmd.data, tmp_hw_cmd.offset,
+ buf->y_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_dma_cmd_we_mmu_prefetch[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN,
+ MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX,
+ MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX_BMSK, {0} },
+};
+
+/*
+ * msm_jpegdma_hw_we_mmu_prefetch() - write DMA we min/max addrs to
+ * MMU prefetch registers.
+ * @buf: Pointer to jpeg hw buffer.
+ * @base: Pointer to base address.
+ *
+ * This function writes DMA we min/max address for each plane to MMU prefetch
+ * registers, MMU prefetch hardware will only prefetch address translations
+ * with in this min/max boundary.
+ *
+ * Return: None.
+ */
+void msm_jpegdma_hw_we_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+ struct msm_jpeg_hw_cmd tmp_hw_cmd;
+
+ hw_cmd_p = &hw_dma_cmd_we_mmu_prefetch[0];
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+
+ JPEG_DBG("%s:%d: MIN DMA addr %08x , reg offset %08x\n",
+ __func__, __LINE__, tmp_hw_cmd.data, tmp_hw_cmd.offset);
+
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+
+ tmp_hw_cmd = *hw_cmd_p++;
+ tmp_hw_cmd.data = buf->y_buffer_addr;
+ if (buf->y_len)
+ tmp_hw_cmd.data += buf->y_len - 1;
+
+ JPEG_DBG("%s:%d: MAX DMA addr %08x , reg offset %08x , length %d\n",
+ __func__, __LINE__, tmp_hw_cmd.data, tmp_hw_cmd.offset,
+ buf->y_len);
+
+ msm_jpeg_hw_write(&tmp_hw_cmd, base);
+ /* ensure write is done */
+ wmb();
+}
+
+static struct msm_jpeg_hw_cmd hw_cmd_reset[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_IRQ_MASK_ADDR,
+ JPEG_IRQ_MASK_BMSK, {JPEG_IRQ_DISABLE_ALL} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_IRQ_CLEAR_ADDR,
+ JPEG_IRQ_MASK_BMSK, {JPEG_IRQ_CLEAR_ALL} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_IRQ_MASK_ADDR,
+ JPEG_IRQ_MASK_BMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_RESET_CMD_ADDR,
+ JPEG_RESET_CMD_RMSK, {JPEG_RESET_DEFAULT} },
+};
+
+void msm_jpeg_hw_reset(void *base, int size)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+
+ hw_cmd_p = &hw_cmd_reset[0];
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p, base);
+ /* ensure write is done */
+ wmb();
+
+}
+static struct msm_jpeg_hw_cmd hw_cmd_reset_dma[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_IRQ_MASK_ADDR,
+ JPEGDMA_IRQ_MASK_BMSK, {JPEGDMA_IRQ_DISABLE_ALL} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_IRQ_CLEAR_ADDR,
+ JPEGDMA_IRQ_MASK_BMSK, {JPEGDMA_IRQ_CLEAR_ALL} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_IRQ_MASK_ADDR,
+ JPEGDMA_IRQ_MASK_BMSK, {JPEGDMA_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEGDMA_RESET_CMD_ADDR,
+ JPEGDMA_RESET_CMD_BMSK, {JPEGDMA_RESET_DEFAULT} },
+};
+
+void msm_jpeg_hw_reset_dma(void *base, int size)
+{
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+
+ hw_cmd_p = &hw_cmd_reset_dma[0];
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ /* ensure write is done */
+ wmb();
+ msm_jpeg_hw_write(hw_cmd_p, base);
+ /* ensure write is done */
+ wmb();
+
+}
+
+uint32_t msm_jpeg_hw_read(struct msm_jpeg_hw_cmd *hw_cmd_p,
+ void *jpeg_region_base)
+{
+ uint32_t *paddr;
+ uint32_t data;
+
+ paddr = jpeg_region_base + hw_cmd_p->offset;
+
+ data = msm_camera_io_r((void __iomem *)paddr);
+ data &= hw_cmd_p->mask;
+
+ return data;
+}
+
+void msm_jpeg_hw_write(struct msm_jpeg_hw_cmd *hw_cmd_p,
+ void *jpeg_region_base)
+{
+ uint32_t *paddr;
+ uint32_t old_data, new_data;
+
+ paddr = jpeg_region_base + hw_cmd_p->offset;
+
+ if (hw_cmd_p->mask == 0xffffffff) {
+ old_data = 0;
+ } else {
+ old_data = msm_camera_io_r((void __iomem *)paddr);
+ old_data &= ~hw_cmd_p->mask;
+ }
+
+ new_data = hw_cmd_p->data & hw_cmd_p->mask;
+ new_data |= old_data;
+ JPEG_DBG("%s:%d] %pK %08x\n", __func__, __LINE__,
+ paddr, new_data);
+ msm_camera_io_w(new_data, (void __iomem *)paddr);
+}
+
+int msm_jpeg_hw_wait(struct msm_jpeg_hw_cmd *hw_cmd_p, int m_us,
+ void *base)
+{
+ int tm = hw_cmd_p->n;
+ uint32_t data;
+ uint32_t wait_data = hw_cmd_p->data & hw_cmd_p->mask;
+
+ data = msm_jpeg_hw_read(hw_cmd_p, base);
+ if (data != wait_data) {
+ while (tm) {
+ udelay(m_us);
+ data = msm_jpeg_hw_read(hw_cmd_p, base);
+ if (data == wait_data)
+ break;
+ tm--;
+ }
+ }
+ hw_cmd_p->data = data;
+ return tm;
+}
+
+void msm_jpeg_hw_delay(struct msm_jpeg_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+
+ while (tm) {
+ udelay(m_us);
+ tm--;
+ }
+}
+
+int msm_jpeg_hw_exec_cmds(struct msm_jpeg_hw_cmd *hw_cmd_p, uint32_t m_cmds,
+ uint32_t max_size, void *base)
+{
+ int is_copy_to_user = 0;
+ uint32_t data;
+
+ while (m_cmds--) {
+ if (hw_cmd_p->offset >= max_size) {
+ JPEG_PR_ERR("%s:%d] %d exceed hw region %d\n", __func__,
+ __LINE__, hw_cmd_p->offset, max_size);
+ return -EFAULT;
+ }
+ if (hw_cmd_p->offset & 0x3) {
+ JPEG_PR_ERR("%s:%d] %d Invalid alignment\n", __func__,
+ __LINE__, hw_cmd_p->offset);
+ return -EFAULT;
+ }
+
+ switch (hw_cmd_p->type) {
+ case MSM_JPEG_HW_CMD_TYPE_READ:
+ hw_cmd_p->data = msm_jpeg_hw_read(hw_cmd_p, base);
+ is_copy_to_user = 1;
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_WRITE:
+ msm_jpeg_hw_write(hw_cmd_p, base);
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_WRITE_OR:
+ data = msm_jpeg_hw_read(hw_cmd_p, base);
+ hw_cmd_p->data = (hw_cmd_p->data & hw_cmd_p->mask) |
+ data;
+ msm_jpeg_hw_write(hw_cmd_p, base);
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_UWAIT:
+ msm_jpeg_hw_wait(hw_cmd_p, 1, base);
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_MWAIT:
+ msm_jpeg_hw_wait(hw_cmd_p, 1000, base);
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_UDELAY:
+ msm_jpeg_hw_delay(hw_cmd_p, 1);
+ break;
+
+ case MSM_JPEG_HW_CMD_TYPE_MDELAY:
+ msm_jpeg_hw_delay(hw_cmd_p, 1000);
+ break;
+
+ default:
+ JPEG_PR_ERR("wrong hw command type\n");
+ break;
+ }
+
+ hw_cmd_p++;
+ }
+ return is_copy_to_user;
+}
+
+void msm_jpeg_io_dump(void *base, int size)
+{
+ char line_str[128];
+ void __iomem *addr = (void __iomem *)base;
+ int i;
+ void __iomem *p = addr;
+ size_t offset = 0;
+ size_t used = 0;
+ size_t min_range = 0;
+ size_t sizeof_line_str = sizeof(line_str);
+ u32 data;
+
+ JPEG_DBG_HIGH("%s:%d] %pK %d", __func__, __LINE__, addr, size);
+ line_str[0] = '\0';
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ used = snprintf(line_str + offset,
+ sizeof_line_str - offset, "%pK ", p);
+ if ((used < min_range) ||
+ (offset + used >= sizeof_line_str)) {
+ JPEG_PR_ERR("%s\n", line_str);
+ offset = 0;
+ line_str[0] = '\0';
+ } else {
+ offset += used;
+ }
+ }
+ data = msm_camera_io_r(p++);
+ used = snprintf(line_str + offset,
+ sizeof_line_str - offset, "%08x ", data);
+ if ((used < min_range) ||
+ (offset + used >= sizeof_line_str)) {
+ JPEG_PR_ERR("%s\n", line_str);
+ offset = 0;
+ line_str[0] = '\0';
+ } else {
+ offset += used;
+ }
+ if ((i + 1) % 4 == 0) {
+ JPEG_DBG_HIGH("%s\n", line_str);
+ line_str[0] = '\0';
+ offset = 0;
+ }
+ }
+ if (line_str[0] != '\0')
+ JPEG_DBG_HIGH("%s\n", line_str);
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.h
new file mode 100644
index 0000000..5d35e4e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.h
@@ -0,0 +1,142 @@
+/* Copyright (c) 2012-2015, 2018, 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_JPEG_HW_H
+#define MSM_JPEG_HW_H
+
+#include <media/msm_jpeg.h>
+#include "msm_jpeg_hw_reg.h"
+#include <linux/ion.h>
+
+struct msm_jpeg_hw_buf {
+ struct msm_jpeg_buf vbuf;
+ struct file *file;
+ uint32_t framedone_len;
+ uint32_t y_buffer_addr;
+ uint32_t y_len;
+ uint32_t cbcr_buffer_addr;
+ uint32_t cbcr_len;
+ uint32_t num_of_mcu_rows;
+ int ion_fd;
+ uint32_t pln2_addr;
+ uint32_t pln2_len;
+};
+
+struct msm_jpeg_hw_pingpong {
+ uint8_t is_fe; /* 1: fe; 0: we */
+ struct msm_jpeg_hw_buf buf[2];
+ int buf_status[2];
+ int buf_active_index;
+};
+
+int msm_jpeg_hw_pingpong_update(struct msm_jpeg_hw_pingpong *pingpong_hw,
+ struct msm_jpeg_hw_buf *buf, void *base);
+int msm_jpegdma_hw_pingpong_update(struct msm_jpeg_hw_pingpong *pingpong_hw,
+ struct msm_jpeg_hw_buf *buf, void *base);
+void *msm_jpeg_hw_pingpong_irq(struct msm_jpeg_hw_pingpong *pingpong_hw);
+void *msm_jpeg_hw_pingpong_active_buffer(struct msm_jpeg_hw_pingpong
+ *pingpong_hw);
+
+void msm_jpeg_hw_irq_clear(uint32_t mask, uint32_t data, void *base);
+void msm_jpegdma_hw_irq_clear(uint32_t mask, uint32_t data, void *base);
+int msm_jpeg_hw_irq_get_status(void *base);
+int msm_jpegdma_hw_irq_get_status(void *base);
+long msm_jpeg_hw_encode_output_size(void *base);
+#define MSM_JPEG_HW_MASK_COMP_FRAMEDONE \
+ MSM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_JPEG_HW_MASK_COMP_FE \
+ MSM_JPEG_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_JPEG_HW_MASK_COMP_WE \
+ (MSM_JPEG_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK | \
+ MSM_JPEG_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK)
+#define MSM_JPEG_HW_MASK_COMP_RESET_ACK \
+ MSM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK
+#define MSM_JPEG_HW_MASK_COMP_ERR \
+ (MSM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \
+ MSM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \
+ MSM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK)
+
+#define msm_jpeg_hw_irq_is_frame_done(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEG_HW_MASK_COMP_FRAMEDONE)
+#define msm_jpeg_hw_irq_is_fe_pingpong(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEG_HW_MASK_COMP_FE)
+#define msm_jpeg_hw_irq_is_we_pingpong(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEG_HW_MASK_COMP_WE)
+#define msm_jpeg_hw_irq_is_reset_ack(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEG_HW_MASK_COMP_RESET_ACK)
+#define msm_jpeg_hw_irq_is_err(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEG_HW_MASK_COMP_ERR)
+
+
+#define MSM_JPEGDMA_HW_MASK_COMP_FRAMEDONE \
+ MSM_JPEGDMA_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_JPEGDMA_HW_MASK_COMP_FE \
+ MSM_JPEGDMA_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_JPEGDMA_HW_MASK_COMP_WE \
+ (MSM_JPEGDMA_HW_IRQ_STATUS_WE_WR_DONE_MASK)
+#define MSM_JPEGDMA_HW_MASK_COMP_RESET_ACK \
+ MSM_JPEGDMA_HW_IRQ_STATUS_RESET_ACK_MASK
+
+
+#define msm_jpegdma_hw_irq_is_frame_done(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEGDMA_HW_MASK_COMP_FRAMEDONE)
+#define msm_jpegdma_hw_irq_is_fe_pingpong(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEGDMA_HW_MASK_COMP_FE)
+#define msm_jpegdma_hw_irq_is_we_pingpong(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEGDMA_HW_MASK_COMP_WE)
+#define msm_jpegdma_hw_irq_is_reset_ack(jpeg_irq_status) \
+ (jpeg_irq_status & MSM_JPEGDMA_HW_MASK_COMP_RESET_ACK)
+
+
+void msm_jpeg_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base);
+void msm_jpeg_hw_we_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base);
+void msm_jpegdma_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base);
+void msm_jpegdma_hw_we_buffer_update(struct msm_jpeg_hw_buf *p_input,
+ uint8_t pingpong_index, void *base);
+
+
+void msm_jpeg_hw_we_buffer_cfg(uint8_t is_realtime);
+
+void msm_jpeg_hw_fe_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base,
+ uint8_t decode_flag);
+void msm_jpeg_hw_we_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base,
+ uint8_t decode_flag);
+void msm_jpegdma_hw_fe_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base);
+void msm_jpegdma_hw_we_mmu_prefetch(struct msm_jpeg_hw_buf *buf, void *base);
+
+void msm_jpeg_hw_fe_start(void *base);
+void msm_jpeg_hw_clk_cfg(void);
+
+void msm_jpeg_hw_reset(void *base, int size);
+void msm_jpeg_hw_irq_cfg(void);
+
+uint32_t msm_jpeg_hw_read(struct msm_jpeg_hw_cmd *hw_cmd_p, void *base);
+void msm_jpeg_hw_write(struct msm_jpeg_hw_cmd *hw_cmd_p, void *base);
+int msm_jpeg_hw_wait(struct msm_jpeg_hw_cmd *hw_cmd_p, int m_us, void *base);
+void msm_jpeg_hw_delay(struct msm_jpeg_hw_cmd *hw_cmd_p, int m_us);
+int msm_jpeg_hw_exec_cmds(struct msm_jpeg_hw_cmd *hw_cmd_p,
+ uint32_t m_cmds, uint32_t max_size, void *base);
+void msm_jpeg_hw_region_dump(int size);
+void msm_jpeg_io_dump(void *base, int size);
+void msm_jpeg_decode_status(void *base);
+void msm_jpeg_hw_reset_dma(void *base, int size);
+
+#endif /* MSM_JPEG_HW_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw_reg.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw_reg.h
new file mode 100644
index 0000000..a1c00d0
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw_reg.h
@@ -0,0 +1,210 @@
+/* Copyright (c) 2012-2015, 2018, 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_JPEG_HW_REG_H
+#define MSM_JPEG_HW_REG_H
+
+#define JPEG_REG_BASE 0
+
+#define MSM_JPEG_HW_IRQ_MASK_ADDR 0x00000018
+#define MSM_JPEG_HW_IRQ_MASK_RMSK 0xFFFFFFFF
+#define MSM_JPEG_HW_IRQ_ENABLE 0xFFFFFFFF
+
+#define MSM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define MSM_JPEG_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000010
+#define MSM_JPEG_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+
+#define MSM_JPEG_HW_IRQ_STATUS_FE_RTOVF_MASK 0x00000004
+#define MSM_JPEG_HW_IRQ_STATUS_FE_RTOVF_SHIFT 0x00000002
+
+#define MSM_JPEG_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK 0x00000008
+#define MSM_JPEG_HW_IRQ_STATUS_FE_VFE_OVERFLOW_SHIFT 0x00000003
+
+#define MSM_JPEG_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK 0x00000010
+#define MSM_JPEG_HW_IRQ_STATUS_WE_Y_PINGPONG_SHIFT 0x00000004
+
+#define MSM_JPEG_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK 0x00000020
+#define MSM_JPEG_HW_IRQ_STATUS_WE_CBCR_PINGPONG_SHIFT 0x00000005
+
+#define MSM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000
+#define MSM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define MSM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF (0x1<<19)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR (0x1<<20)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR (0x1<<21)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW (0x1<<23)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM (0x1<<24)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ (0x1<<25)
+#define MSM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM (0x1<<26)
+#define MSM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK (0x1<<29)
+
+#define JPEG_OFFLINE_CMD_START 0x00000001
+
+#define JPEG_RESET_DEFAULT 0x00032093
+
+#define JPEG_IRQ_DISABLE_ALL 0x00000000
+#define JPEG_IRQ_CLEAR_ALL 0xFFFFFFFF
+
+#define JPEG_PLN0_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000038)
+#define JPEG_PLN0_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN0_RD_OFFSET_ADDR 0x0000003C
+#define JPEG_PLN0_RD_OFFSET_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN1_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000044)
+#define JPEG_PLN1_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN1_RD_OFFSET_ADDR 0x00000048
+#define JPEG_PLN1_RD_OFFSET_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000050)
+#define JPEG_PLN2_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_RD_OFFSET_ADDR 0x00000054
+#define JPEG_PLN2_RD_OFFSET_BMSK 0xFFFFFFFF
+
+#define JPEG_CMD_ADDR (JPEG_REG_BASE + 0x00000010)
+#define JPEG_CMD_BMSK 0xFFFFFFFF
+#define JPEG_CMD_CLEAR_WRITE_PLN_QUEUES 0x700
+
+#define JPEG_PLN0_WR_PNTR_ADDR (JPEG_REG_BASE + 0x000000cc)
+#define JPEG_PLN0_WR_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN1_WR_PNTR_ADDR (JPEG_REG_BASE + 0x000000D0)
+#define JPEG_PLN1_WR_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_WR_PNTR_ADDR (JPEG_REG_BASE + 0x000000D4)
+#define JPEG_PLN2_WR_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_IRQ_MASK_ADDR (JPEG_REG_BASE + 0x00000018)
+#define JPEG_IRQ_MASK_BMSK 0xFFFFFFFF
+#define JPEG_IRQ_ALLSOURCES_ENABLE 0xFFFFFFFF
+
+#define JPEG_IRQ_CLEAR_ADDR (JPEG_REG_BASE + 0x0000001c)
+#define JPEG_IRQ_CLEAR_BMSK 0xFFFFFFFF
+
+#define JPEG_RESET_CMD_ADDR (JPEG_REG_BASE + 0x00000008)
+#define JPEG_RESET_CMD_RMSK 0xFFFFFFFF
+
+#define JPEG_IRQ_STATUS_ADDR (JPEG_REG_BASE + 0x00000020)
+#define JPEG_IRQ_STATUS_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S0_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x00000310)
+#define MSM_JPEG_S0_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S0_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x00000314)
+#define MSM_JPEG_S0_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S1_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x0000031C)
+#define MSM_JPEG_S1_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S1_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x00000320)
+#define MSM_JPEG_S1_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S2_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x00000328)
+#define MSM_JPEG_S2_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S2_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x0000032C)
+#define MSM_JPEG_S2_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S3_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x00000334)
+#define MSM_JPEG_S3_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEG_S3_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x00000338)
+#define MSM_JPEG_S3_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#define JPEG_ENCODE_OUTPUT_SIZE_STATUS_ADDR (JPEG_REG_BASE + 0x00000180)
+#define JPEG_ENCODE_OUTPUT_SIZE_STATUS_BMSK 0x1FFFFFFF
+
+#define JPEG_DECODE_MCUS_DECODED_STATUS (JPEG_REG_BASE + 0x00000258)
+#define JPEG_DECODE_BITS_CONSUMED_STATUS (JPEG_REG_BASE + 0x0000025C)
+#define JPEG_DECODE_PRED_Y_STATE (JPEG_REG_BASE + 0x00000260)
+#define JPEG_DECODE_PRED_C_STATE (JPEG_REG_BASE + 0x00000264)
+#define JPEG_DECODE_RSM_STATE (JPEG_REG_BASE + 0x00000268)
+
+#define JPEG_HW_VERSION (JPEG_REG_BASE + 0x00000000)
+
+#define VBIF_BASE_ADDRESS 0xFDA60000
+#define VBIF_REGION_SIZE 0xC30
+#define JPEG_VBIF_CLKON 0x4
+#define JPEG_VBIF_IN_RD_LIM_CONF0 0xB0
+#define JPEG_VBIF_IN_RD_LIM_CONF1 0xB4
+#define JPEG_VBIF_IN_RD_LIM_CONF2 0xB8
+#define JPEG_VBIF_IN_WR_LIM_CONF0 0xC0
+#define JPEG_VBIF_IN_WR_LIM_CONF1 0xC4
+#define JPEG_VBIF_IN_WR_LIM_CONF2 0xC8
+#define JPEG_VBIF_OUT_RD_LIM_CONF0 0xD0
+#define JPEG_VBIF_OUT_WR_LIM_CONF0 0xD4
+#define JPEG_VBIF_DDR_OUT_MAX_BURST 0xD8
+#define JPEG_VBIF_OCMEM_OUT_MAX_BURST 0xDC
+#define JPEG_VBIF_ARB_CTL 0xF0
+#define JPEG_VBIF_OUT_AXI_AOOO_EN 0x178
+#define JPEG_VBIF_OUT_AXI_AOOO 0x17c
+#define JPEG_VBIF_ROUND_ROBIN_QOS_ARB 0x124
+#define JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF0 0x160
+#define JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF1 0x164
+
+#define JPEGDMA_IRQ_MASK_ADDR (JPEG_REG_BASE + 0x0000000C)
+#define JPEGDMA_IRQ_MASK_BMSK 0xFFFFFFFF
+#define JPEGDMA_IRQ_ALLSOURCES_ENABLE 0xFFFFFFFF
+
+#define JPEGDMA_IRQ_CLEAR_ADDR (JPEG_REG_BASE + 0x00000014)
+#define JPEGDMA_IRQ_CLEAR_BMSK 0xFFFFFFFF
+
+#define JPEGDMA_RESET_CMD_ADDR (JPEG_REG_BASE + 0x00000008)
+#define JPEGDMA_RESET_CMD_BMSK 0xFFFFFFFF
+
+#define JPEGDMA_IRQ_STATUS_ADDR (JPEG_REG_BASE + 0x00000010)
+#define JPEGDMA_IRQ_STATUS_BMSK 0xFFFFFFFF
+#define JPEGDMA_RESET_DEFAULT 0x00032083
+
+
+#define JPEGDMA_CMD_ADDR (JPEG_REG_BASE + 0x0000001C)
+#define JPEGDMA_CMD_BMSK (0xFFFFFFFF)
+#define JPEGDMA_CMD_CLEAR_READ_PLN_QUEUES 0x030
+#define JPEGDMA_CMD_CLEAR_WRITE_PLN_QUEUES 0x300
+
+#define JPEGDMA_IRQ_DISABLE_ALL 0x00000000
+#define JPEGDMA_IRQ_CLEAR_ALL 0x00001FFF
+#define MSM_JPEGDMA_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_JPEGDMA_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+#define MSM_JPEGDMA_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000006
+#define MSM_JPEGDMA_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+#define MSM_JPEGDMA_HW_IRQ_STATUS_WE_WR_DONE_MASK 0x00000060
+#define MSM_JPEGDMA_HW_IRQ_STATUS_WE_WR_DONE_SHIFT 0x00000005
+#define MSM_JPEGDMA_HW_IRQ_STATUS_RESET_ACK_MASK 0x00000400
+#define MSM_JPEGDMA_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_JPEGDMA_FE_0_RD_PNTR (JPEG_REG_BASE + 0x00000034)
+#define MSM_JPEGDMA_FE_1_RD_PNTR (JPEG_REG_BASE + 0x00000078)
+#define MSM_JPEGDMA_WE_0_WR_PNTR (JPEG_REG_BASE + 0x000000BC)
+#define MSM_JPEGDMA_WE_1_WR_PNTR (JPEG_REG_BASE + 0x000000EC)
+
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x00000190)
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x00000198)
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN (JPEG_REG_BASE + 0x000001A4)
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN_BMSK 0xFFFFFFFF
+
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX (JPEG_REG_BASE + 0x000001AC)
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX_BMSK 0xFFFFFFFF
+
+#endif /* MSM_JPEG_HW_REG_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
new file mode 100644
index 0000000..bb06e0a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
@@ -0,0 +1,549 @@
+/* Copyright (c) 2012-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+
+#include <linux/module.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <linux/iommu.h>
+#include <asm/dma-iommu.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-buf.h>
+
+#include "msm_camera_io_util.h"
+#include "msm_jpeg_platform.h"
+#include "msm_jpeg_sync.h"
+#include "msm_jpeg_common.h"
+#include "msm_jpeg_hw.h"
+
+#define JPEG_DT_PROP_CNT 2
+
+
+static int msm_jpeg_get_clock_index(struct msm_jpeg_device *pgmn_dev,
+ const char *clk_name)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < pgmn_dev->num_clk; i++) {
+ if (!strcmp(clk_name, pgmn_dev->jpeg_clk_info[i].clk_name))
+ return i;
+ }
+ return -EINVAL;
+}
+
+int msm_jpeg_platform_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
+ long clk_rate)
+{
+ int rc = 0;
+ int msm_jpeg_idx;
+
+ /* retrieve clock index from list of clocks */
+ msm_jpeg_idx = msm_jpeg_get_clock_index(pgmn_dev,
+ "core_clk");
+ if (msm_jpeg_idx < 0) {
+ JPEG_PR_ERR("%s:Fail to get clock index\n", __func__);
+ return -EINVAL;
+ }
+
+ /* set the rate */
+ msm_camera_clk_set_rate(&pgmn_dev->pdev->dev,
+ pgmn_dev->jpeg_clk[msm_jpeg_idx], clk_rate);
+
+ return rc;
+}
+
+void msm_jpeg_platform_p2v(int iommu_hdl, int fd)
+{
+ cam_smmu_put_phy_addr(iommu_hdl, fd);
+}
+
+uint32_t msm_jpeg_platform_v2p(struct msm_jpeg_device *pgmn_dev, int fd,
+ uint32_t len, int iommu_hdl)
+{
+ dma_addr_t paddr;
+ size_t size;
+ int rc;
+
+ rc = cam_smmu_get_phy_addr(pgmn_dev->iommu_hdl, fd, CAM_SMMU_MAP_RW,
+ &paddr, &size);
+ JPEG_DBG("%s:%d] addr 0x%x size %zu", __func__, __LINE__,
+ (uint32_t)paddr, size);
+
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: fd %d got phy addr error %d\n", __func__, fd,
+ rc);
+ goto err_get_phy;
+ }
+
+ /* validate user input */
+ if (len > size) {
+ JPEG_PR_ERR("%s: invalid offset + len\n", __func__);
+ goto err_size;
+ }
+
+ return paddr;
+err_size:
+ cam_smmu_put_phy_addr(pgmn_dev->iommu_hdl, fd);
+err_get_phy:
+ return 0;
+}
+
+static void set_vbif_params(struct msm_jpeg_device *pgmn_dev,
+ void *jpeg_vbif_base)
+{
+ msm_camera_io_w(0x1,
+ (void __iomem *)(jpeg_vbif_base + JPEG_VBIF_CLKON));
+
+ if (pgmn_dev->hw_version != JPEG_8994) {
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_RD_LIM_CONF0));
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_RD_LIM_CONF1));
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_RD_LIM_CONF2));
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_WR_LIM_CONF0));
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_WR_LIM_CONF1));
+ msm_camera_io_w(0x10101010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_IN_WR_LIM_CONF2));
+ msm_camera_io_w(0x00001010,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_RD_LIM_CONF0));
+ msm_camera_io_w(0x00000110,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_WR_LIM_CONF0));
+ msm_camera_io_w(0x00000707,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_DDR_OUT_MAX_BURST));
+ msm_camera_io_w(0x00000FFF,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_AXI_AOOO_EN));
+ msm_camera_io_w(0x0FFF0FFF,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_AXI_AOOO));
+ msm_camera_io_w(0x2222,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF1));
+ }
+
+ msm_camera_io_w(0x7,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OCMEM_OUT_MAX_BURST));
+ msm_camera_io_w(0x00000030,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_ARB_CTL));
+
+ /* FE and WE QOS configuration need to be set when
+ * QOS RR arbitration is enabled
+ */
+ if (pgmn_dev->hw_version != JPEG_8974_V1)
+ msm_camera_io_w(0x00000003,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_ROUND_ROBIN_QOS_ARB));
+ else
+ msm_camera_io_w(0x00000001,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_ROUND_ROBIN_QOS_ARB));
+
+ msm_camera_io_w(0x22222222,
+ (void __iomem *)(jpeg_vbif_base +
+ JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF0));
+
+}
+
+/*
+ * msm_jpeg_set_init_dt_parms() - get device tree config and write to registers.
+ * @pgmn_dev: Pointer to jpeg device.
+ * @dt_prop_name: Device tree property name.
+ * @base: Base address.
+ *
+ * This function reads register offsets and values from dtsi based on
+ * device tree property name and writes to jpeg registers.
+ *
+ * Return: 0 on success and negative error on failure.
+ */
+static int32_t msm_jpeg_set_init_dt_parms(struct msm_jpeg_device *pgmn_dev,
+ const char *dt_prop_name,
+ void *base)
+{
+ struct device_node *of_node;
+ int32_t i = 0, rc = 0;
+ uint32_t *dt_reg_settings = NULL;
+ uint32_t dt_count = 0;
+
+ of_node = pgmn_dev->pdev->dev.of_node;
+ JPEG_DBG("%s:%d E\n", __func__, __LINE__);
+
+ if (!of_get_property(of_node, dt_prop_name,
+ &dt_count)) {
+ JPEG_DBG("%s: Error property does not exist\n",
+ __func__);
+ return -ENOENT;
+ }
+ if (dt_count % 8) {
+ JPEG_PR_ERR("%s: Error invalid entries\n",
+ __func__);
+ return -EINVAL;
+ }
+ dt_count /= 4;
+ if (dt_count != 0) {
+ dt_reg_settings = kcalloc(dt_count, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!dt_reg_settings) {
+ JPEG_PR_ERR("%s:%d No memory\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ rc = of_property_read_u32_array(of_node,
+ dt_prop_name,
+ dt_reg_settings,
+ dt_count);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: No reg info\n",
+ __func__);
+ kfree(dt_reg_settings);
+ return -EINVAL;
+ }
+ for (i = 0; i < dt_count; i = i + 2) {
+ JPEG_DBG("%s:%d] %pK %08x\n",
+ __func__, __LINE__,
+ base + dt_reg_settings[i],
+ dt_reg_settings[i + 1]);
+ msm_camera_io_w(dt_reg_settings[i + 1],
+ (void __iomem *)
+ (base + dt_reg_settings[i]));
+ }
+ kfree(dt_reg_settings);
+ }
+ return 0;
+}
+
+static int msm_jpeg_attach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ int rc;
+
+ rc = cam_smmu_ops(pgmn_dev->iommu_hdl, CAM_SMMU_ATTACH);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: Device attach failed\n", __func__);
+ return -ENODEV;
+ }
+ JPEG_DBG("%s:%d] handle %d attach\n",
+ __func__, __LINE__, pgmn_dev->iommu_hdl);
+ return 0;
+}
+
+static int msm_jpeg_detach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d] handle %d detach\n",
+ __func__, __LINE__, pgmn_dev->iommu_hdl);
+ cam_smmu_ops(pgmn_dev->iommu_hdl, CAM_SMMU_DETACH);
+ return 0;
+}
+
+
+int msm_jpeg_platform_init(irqreturn_t (*handler)(int, void *),
+ void *context)
+{
+ int rc = -1;
+ struct msm_jpeg_device *pgmn_dev =
+ (struct msm_jpeg_device *) context;
+ struct platform_device *pdev = pgmn_dev->pdev;
+
+ pgmn_dev->state = MSM_JPEG_IDLE;
+
+ /* enable all regulators */
+ rc = msm_camera_regulator_enable(pgmn_dev->jpeg_vdd,
+ pgmn_dev->num_reg, true);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: failed to enable regulators\n", __func__);
+ goto err_reg_enable;
+ }
+
+ /* enable all clocks */
+ rc = msm_camera_clk_enable(&pgmn_dev->pdev->dev,
+ pgmn_dev->jpeg_clk_info, pgmn_dev->jpeg_clk,
+ pgmn_dev->num_clk, true);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: clk enable failed\n", __func__);
+ goto err_clk_enable;
+ }
+
+ /* attach the smmu context banks */
+ rc = msm_jpeg_attach_iommu(pgmn_dev);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: iommu attach failed\n", __func__);
+ goto err_fail_iommu;
+ }
+ rc = msm_jpeg_set_init_dt_parms(pgmn_dev, "qcom,vbif-reg-settings",
+ pgmn_dev->vbif_base);
+ if (rc == -ENOENT) {
+ JPEG_DBG("%s: No qcom,vbif-reg-settings property\n", __func__);
+ set_vbif_params(pgmn_dev, pgmn_dev->vbif_base);
+ } else if (rc < 0) {
+ JPEG_PR_ERR("%s: vbif params set fail\n", __func__);
+ goto err_fail_set_vbif;
+ }
+
+ /* register the interrupt handler */
+ rc = msm_camera_register_irq(pgmn_dev->pdev,
+ pgmn_dev->jpeg_irq_res, handler, IRQF_TRIGGER_RISING,
+ "jpeg", context);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: irq request fail\n", __func__);
+ goto err_reg_irq_fail;
+ }
+
+ pgmn_dev->hw_version = msm_camera_io_r((void __iomem *)(pgmn_dev->base +
+ JPEG_HW_VERSION));
+ JPEG_DBG_HIGH("%s:%d] jpeg HW version 0x%x", __func__, __LINE__,
+ pgmn_dev->hw_version);
+ pgmn_dev->state = MSM_JPEG_INIT;
+
+ return 0;
+err_reg_irq_fail:
+err_fail_set_vbif:
+ msm_jpeg_detach_iommu(pgmn_dev);
+err_fail_iommu:
+ msm_camera_clk_enable(&pdev->dev, pgmn_dev->jpeg_clk_info,
+ pgmn_dev->jpeg_clk, pgmn_dev->num_clk, false);
+err_clk_enable:
+ msm_camera_regulator_enable(pgmn_dev->jpeg_vdd,
+ pgmn_dev->num_reg, false);
+err_reg_enable:
+ return rc;
+}
+
+int msm_jpeg_platform_setup(struct msm_jpeg_device *pgmn_dev)
+{
+ int rc = -1;
+ int i;
+ struct resource *jpeg_irq_res;
+ void __iomem *jpeg_base, *vbif_base;
+ struct platform_device *pdev = pgmn_dev->pdev;
+
+ /* get the jpeg hardware device address */
+ jpeg_base = msm_camera_get_reg_base(pdev, "jpeg_hw", true);
+ if (!jpeg_base) {
+ JPEG_PR_ERR("%s: jpeg no mem resource?\n", __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* get the jpeg vbif device address */
+ vbif_base = msm_camera_get_reg_base(pdev, "jpeg_vbif", false);
+ if (!vbif_base) {
+ JPEG_PR_ERR("%s: vbif no mem resource?\n", __func__);
+ rc = -ENXIO;
+ goto err_vbif_base;
+ }
+
+ /* get the irq resource for the jpeg hardware */
+ jpeg_irq_res = msm_camera_get_irq(pdev, "jpeg");
+ if (!jpeg_irq_res) {
+ JPEG_PR_ERR("%s: no irq resource?\n", __func__);
+ rc = -ENXIO;
+ goto err_jpeg_irq_res;
+ }
+
+ /* get all the clocks information */
+ rc = msm_camera_get_clk_info(pdev, &pgmn_dev->jpeg_clk_info,
+ &pgmn_dev->jpeg_clk, &pgmn_dev->num_clk);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: failed to get the clocks\n", __func__);
+ rc = -ENXIO;
+ goto err_jpeg_clk;
+ }
+
+ /*set memcore and mem periphery logic flags to 0*/
+ for (i = 0; i < pgmn_dev->num_clk; i++) {
+ if ((strcmp(pgmn_dev->jpeg_clk_info[i].clk_name,
+ "core_clk") == 0) ||
+ (strcmp(pgmn_dev->jpeg_clk_info[i].clk_name,
+ "mmss_camss_jpeg_axi_clk") == 0)) {
+ msm_camera_set_clk_flags(pgmn_dev->jpeg_clk[i],
+ CLKFLAG_NORETAIN_MEM);
+ msm_camera_set_clk_flags(pgmn_dev->jpeg_clk[i],
+ CLKFLAG_NORETAIN_PERIPH);
+ }
+ }
+
+ /* get all the regulators information */
+ rc = msm_camera_get_regulator_info(pdev, &pgmn_dev->jpeg_vdd,
+ &pgmn_dev->num_reg);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: failed to get the regulators\n", __func__);
+ rc = -ENXIO;
+ goto err_jpeg_get_reg;
+ }
+
+ /* map the dtsi cell id to bus client id */
+ switch (pgmn_dev->pdev->id) {
+ case 0:
+ pgmn_dev->bus_client = CAM_BUS_CLIENT_JPEG_ENC0;
+ break;
+ case 1:
+ pgmn_dev->bus_client = CAM_BUS_CLIENT_JPEG_ENC1;
+ break;
+ case 2:
+ pgmn_dev->bus_client = CAM_BUS_CLIENT_JPEG_DEC;
+ break;
+ case 3:
+ pgmn_dev->bus_client = CAM_BUS_CLIENT_JPEG_DMA;
+ break;
+ default:
+ JPEG_PR_ERR("%s: invalid cell id :%d\n",
+ __func__, pgmn_dev->pdev->id);
+ goto err_jpeg_get_reg;
+ }
+
+ /* register the bus client */
+ rc = msm_camera_register_bus_client(pgmn_dev->pdev,
+ pgmn_dev->bus_client);
+ if (rc < 0) {
+ JPEG_PR_ERR("Fail to register bus client\n");
+ rc = -EINVAL;
+ goto err_reg_bus;
+ }
+
+ /* get the resource size of jpeg hardware */
+ pgmn_dev->res_size = msm_camera_get_res_size(pdev, "jpeg_hw");
+ if (!pgmn_dev->res_size) {
+ JPEG_PR_ERR("Fail to resource size\n");
+ rc = -EINVAL;
+ goto err_res_size;
+ }
+
+ pgmn_dev->base = (__force void *)jpeg_base;
+ pgmn_dev->vbif_base = (__force void *)vbif_base;
+ pgmn_dev->jpeg_irq_res = jpeg_irq_res;
+
+ return 0;
+
+err_res_size:
+ msm_camera_unregister_bus_client(pgmn_dev->bus_client);
+err_reg_bus:
+ msm_camera_put_regulators(pdev, &pgmn_dev->jpeg_vdd,
+ pgmn_dev->num_reg);
+err_jpeg_get_reg:
+ msm_camera_put_clk_info(pdev, &pgmn_dev->jpeg_clk_info,
+ &pgmn_dev->jpeg_clk, pgmn_dev->num_clk);
+err_jpeg_clk:
+err_jpeg_irq_res:
+ msm_camera_put_reg_base(pdev, vbif_base, "jpeg_vbif", false);
+err_vbif_base:
+ msm_camera_put_reg_base(pdev, jpeg_base, "jpeg_hw", true);
+out:
+ return rc;
+}
+
+void msm_jpeg_platform_cleanup(struct msm_jpeg_device *pgmn_dev)
+{
+ /* unregister the bus client */
+ msm_camera_unregister_bus_client(pgmn_dev->bus_client);
+ /* release the regulators */
+ msm_camera_put_regulators(pgmn_dev->pdev, &pgmn_dev->jpeg_vdd,
+ pgmn_dev->num_reg);
+ /* release all the clocks */
+ msm_camera_put_clk_info(pgmn_dev->pdev, &pgmn_dev->jpeg_clk_info,
+ &pgmn_dev->jpeg_clk, pgmn_dev->num_clk);
+ /* release the jpeg device memory */
+ msm_camera_put_reg_base(pgmn_dev->pdev,
+ (void __iomem *)pgmn_dev->vbif_base,
+ "jpeg_vbif", false);
+ /* release the jpeg vbif device memory */
+ msm_camera_put_reg_base(pgmn_dev->pdev,
+ (void __iomem *)pgmn_dev->base,
+ "jpeg_hw", true);
+}
+
+int msm_jpeg_platform_release(void *context)
+{
+ int result = 0;
+
+ struct msm_jpeg_device *pgmn_dev =
+ (struct msm_jpeg_device *) context;
+
+ /* release the irq */
+ msm_camera_unregister_irq(pgmn_dev->pdev,
+ pgmn_dev->jpeg_irq_res, context);
+
+ msm_jpeg_detach_iommu(pgmn_dev);
+
+ if (pgmn_dev->bus_client) {
+ if (pgmn_dev->jpeg_bus_vote) {
+ /* update the bw with zeroth vector */
+ msm_camera_update_bus_vector(pgmn_dev->bus_client, 0);
+ JPEG_BUS_UNVOTED(pgmn_dev);
+ JPEG_DBG("%s:%d] Bus unvoted\n", __func__, __LINE__);
+ }
+ }
+
+ /* disable all the clocks */
+ msm_camera_clk_enable(&pgmn_dev->pdev->dev, pgmn_dev->jpeg_clk_info,
+ pgmn_dev->jpeg_clk, pgmn_dev->num_clk, false);
+ JPEG_DBG("%s:%d] clock disbale done", __func__, __LINE__);
+
+ /* disable all the regulators */
+ msm_camera_regulator_enable(pgmn_dev->jpeg_vdd,
+ pgmn_dev->num_reg, false);
+ JPEG_DBG("%s:%d] regulator disable done", __func__, __LINE__);
+
+ pgmn_dev->state = MSM_JPEG_IDLE;
+ JPEG_DBG("%s:%d] success\n", __func__, __LINE__);
+ return result;
+}
+
+/*
+ * msm_jpeg_platform_set_dt_config() - set jpeg device tree configuration.
+ * @pgmn_dev: Pointer to jpeg device.
+ *
+ * This function holds an array of device tree property names and calls
+ * msm_jpeg_set_init_dt_parms() for each property.
+ *
+ * Return: 0 on success and negative error on failure.
+ */
+int msm_jpeg_platform_set_dt_config(struct msm_jpeg_device *pgmn_dev)
+{
+ int rc = 0;
+ uint8_t dt_prop_cnt = JPEG_DT_PROP_CNT;
+ char *dt_prop_name[JPEG_DT_PROP_CNT] = {"qcom,qos-reg-settings",
+ "qcom,prefetch-reg-settings"};
+
+ while (dt_prop_cnt) {
+ dt_prop_cnt--;
+ rc = msm_jpeg_set_init_dt_parms(pgmn_dev,
+ dt_prop_name[dt_prop_cnt],
+ pgmn_dev->base);
+ if (rc == -ENOENT) {
+ JPEG_DBG("%s: No %s property\n", __func__,
+ dt_prop_name[dt_prop_cnt]);
+ } else if (rc < 0) {
+ JPEG_PR_ERR("%s: %s params set fail\n", __func__,
+ dt_prop_name[dt_prop_cnt]);
+ return rc;
+ }
+ }
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
new file mode 100644
index 0000000..678582d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012-2016, 2018, 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_JPEG_PLATFORM_H
+#define MSM_JPEG_PLATFORM_H
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/ion.h>
+#include "msm_jpeg_sync.h"
+#define JPEG_CLK_RATE 266670000
+
+int msm_jpeg_platform_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
+ long clk_rate);
+void msm_jpeg_platform_p2v(int iommu_hdl, int fd);
+uint32_t msm_jpeg_platform_v2p(struct msm_jpeg_device *pgmn_dev, int fd,
+ uint32_t len, int iommu_hdl);
+
+int msm_jpeg_platform_clk_enable(void);
+int msm_jpeg_platform_clk_disable(void);
+
+int msm_jpeg_platform_init(irqreturn_t (*handler)(int, void *),
+ void *context);
+int msm_jpeg_platform_release(void *context);
+int msm_jpeg_platform_set_dt_config(struct msm_jpeg_device *pgmn_dev);
+int msm_jpeg_platform_setup(struct msm_jpeg_device *pgmn_dev);
+void msm_jpeg_platform_cleanup(struct msm_jpeg_device *pgmn_dev);
+
+#endif /* MSM_JPEG_PLATFORM_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
new file mode 100644
index 0000000..3daee21
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
@@ -0,0 +1,1593 @@
+/* Copyright (c) 2012-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/ratelimit.h>
+#include <media/msm_jpeg.h>
+#include <linux/msm-bus.h>
+#include "msm_jpeg_sync.h"
+#include "msm_jpeg_core.h"
+#include "msm_jpeg_platform.h"
+#include "msm_jpeg_common.h"
+#include "cam_hw_ops.h"
+
+#define JPEG_REG_SIZE 0x308
+#define JPEG_DEV_CNT 4
+#define JPEG_DEC_ID 2
+#define UINT32_MAX (0xFFFFFFFFU)
+#define MAX_WAIT_TIMEOUT UINT_MAX
+
+
+#ifdef CONFIG_COMPAT
+
+#define MSM_JPEG_IOCTL_GET_HW_VERSION32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 1, struct msm_jpeg_hw_cmd32)
+
+#define MSM_JPEG_IOCTL_RESET32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 2, struct msm_jpeg_ctrl_cmd32)
+
+#define MSM_JPEG_IOCTL_STOP32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 3, struct msm_jpeg_hw_cmds32)
+
+#define MSM_JPEG_IOCTL_START32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 4, struct msm_jpeg_hw_cmds32)
+
+#define MSM_JPEG_IOCTL_INPUT_BUF_ENQUEUE32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 5, struct msm_jpeg_buf32)
+
+#define MSM_JPEG_IOCTL_INPUT_GET32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 6, struct msm_jpeg_buf32)
+
+#define MSM_JPEG_IOCTL_OUTPUT_BUF_ENQUEUE32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 8, struct msm_jpeg_buf32)
+
+#define MSM_JPEG_IOCTL_OUTPUT_GET32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 9, struct msm_jpeg_buf32)
+
+#define MSM_JPEG_IOCTL_EVT_GET32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 11, struct msm_jpeg_ctrl_cmd32)
+
+#define MSM_JPEG_IOCTL_HW_CMD32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 13, struct msm_jpeg_hw_cmd32)
+
+#define MSM_JPEG_IOCTL_HW_CMDS32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 14, struct msm_jpeg_hw_cmds32)
+
+#define MSM_JPEG_IOCTL_TEST_DUMP_REGION32 \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 15, compat_ulong_t)
+
+struct msm_jpeg_ctrl_cmd32 {
+ uint32_t type;
+ uint32_t len;
+ compat_uptr_t value;
+};
+struct msm_jpeg_buf32 {
+ uint32_t type;
+ int fd;
+
+ compat_uptr_t vaddr;
+
+ uint32_t y_off;
+ uint32_t y_len;
+ uint32_t framedone_len;
+
+ uint32_t cbcr_off;
+ uint32_t cbcr_len;
+
+ uint32_t num_of_mcu_rows;
+ uint32_t offset;
+ uint32_t pln2_off;
+ uint32_t pln2_len;
+};
+
+struct msm_jpeg_hw_cmd32 {
+
+ uint32_t type:4;
+
+ /* n microseconds of timeout for WAIT */
+ /* n microseconds of time for DELAY */
+ /* repeat n times for READ/WRITE */
+ /* max is 0xFFF, 4095 */
+ uint32_t n:12;
+ uint32_t offset:16;
+ uint32_t mask;
+ union {
+ uint32_t data; /* for single READ/WRITE/WAIT, n = 1 */
+ compat_uptr_t pdata; /* for multiple READ/WRITE/WAIT, n > 1 */
+ };
+};
+
+struct msm_jpeg_hw_cmds32 {
+ uint32_t m; /* number of elements in the hw_cmd array */
+ struct msm_jpeg_hw_cmd32 hw_cmd[1];
+};
+#endif
+
+
+inline void msm_jpeg_q_init(char const *name, struct msm_jpeg_q *q_p)
+{
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, name);
+ q_p->name = name;
+ spin_lock_init(&q_p->lck);
+ INIT_LIST_HEAD(&q_p->q);
+ init_waitqueue_head(&q_p->wait);
+ q_p->unblck = 0;
+}
+
+inline void *msm_jpeg_q_out(struct msm_jpeg_q *q_p)
+{
+ unsigned long flags;
+ struct msm_jpeg_q_entry *q_entry_p = NULL;
+ void *data = NULL;
+
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ spin_lock_irqsave(&q_p->lck, flags);
+ if (!list_empty(&q_p->q)) {
+ q_entry_p = list_first_entry(&q_p->q, struct msm_jpeg_q_entry,
+ list);
+ list_del_init(&q_entry_p->list);
+ }
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ if (q_entry_p) {
+ data = q_entry_p->data;
+ kfree(q_entry_p);
+ } else {
+ JPEG_DBG("%s:%d] %s no entry\n", __func__, __LINE__,
+ q_p->name);
+ }
+
+ return data;
+}
+
+inline int msm_jpeg_q_in(struct msm_jpeg_q *q_p, void *data)
+{
+ unsigned long flags;
+
+ struct msm_jpeg_q_entry *q_entry_p;
+
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+
+ q_entry_p = kmalloc(sizeof(struct msm_jpeg_q_entry), GFP_ATOMIC);
+ if (!q_entry_p) {
+ JPEG_PR_ERR("%s: no mem\n", __func__);
+ return -EFAULT;
+ }
+ q_entry_p->data = data;
+
+ spin_lock_irqsave(&q_p->lck, flags);
+ list_add_tail(&q_entry_p->list, &q_p->q);
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ return 0;
+}
+
+inline int msm_jpeg_q_in_buf(struct msm_jpeg_q *q_p,
+ struct msm_jpeg_core_buf *buf)
+{
+ struct msm_jpeg_core_buf *buf_p;
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ buf_p = kmalloc(sizeof(struct msm_jpeg_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ JPEG_PR_ERR("%s: no mem\n", __func__);
+ return -EFAULT;
+ }
+
+ memcpy(buf_p, buf, sizeof(struct msm_jpeg_core_buf));
+
+ msm_jpeg_q_in(q_p, buf_p);
+ return 0;
+}
+
+inline int msm_jpeg_q_wait(struct msm_jpeg_q *q_p)
+{
+ long tm = MAX_WAIT_TIMEOUT; /* 500ms */
+ int rc;
+
+ JPEG_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
+ rc = wait_event_timeout(q_p->wait,
+ (!list_empty_careful(&q_p->q) || q_p->unblck),
+ msecs_to_jiffies(tm));
+ JPEG_DBG("%s:%d] %s wait done\n", __func__, __LINE__, q_p->name);
+ if (list_empty_careful(&q_p->q)) {
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ JPEG_PR_ERR("%s:%d] %s timeout\n", __func__, __LINE__,
+ q_p->name);
+ } else if (q_p->unblck) {
+ JPEG_DBG("%s:%d] %s unblock is true\n", __func__,
+ __LINE__, q_p->name);
+ q_p->unblck = 0;
+ rc = -ECANCELED;
+ }
+ }
+ return rc;
+}
+
+inline int msm_jpeg_q_wakeup(struct msm_jpeg_q *q_p)
+{
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+inline int msm_jpeg_q_unblock(struct msm_jpeg_q *q_p)
+{
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ q_p->unblck = 1;
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+inline void msm_jpeg_outbuf_q_cleanup(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_q *q_p)
+{
+ struct msm_jpeg_core_buf *buf_p;
+
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ buf_p = msm_jpeg_q_out(q_p);
+ if (buf_p) {
+ msm_jpeg_platform_p2v(pgmn_dev->iommu_hdl,
+ buf_p->ion_fd);
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(buf_p);
+ }
+ } while (buf_p);
+ q_p->unblck = 0;
+}
+
+inline void msm_jpeg_q_cleanup(struct msm_jpeg_q *q_p)
+{
+ void *data;
+
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ data = msm_jpeg_q_out(q_p);
+ if (data) {
+ JPEG_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(data);
+ }
+ } while (data);
+ q_p->unblck = 0;
+}
+
+/*************** event queue ****************/
+
+static int msm_jpeg_framedone_irq(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf_in)
+{
+ int rc = 0;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ if (buf_in) {
+ buf_in->vbuf.framedone_len = buf_in->framedone_len;
+ buf_in->vbuf.type = MSM_JPEG_EVT_SESSION_DONE;
+ JPEG_DBG("%s:%d] 0x%08x %d framedone_len %d\n",
+ __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len,
+ buf_in->vbuf.framedone_len);
+ rc = msm_jpeg_q_in_buf(&pgmn_dev->evt_q, buf_in);
+ } else {
+ JPEG_PR_ERR("%s:%d] no output return buffer\n",
+ __func__, __LINE__);
+ rc = -1;
+ }
+
+ if (buf_in)
+ rc = msm_jpeg_q_wakeup(&pgmn_dev->evt_q);
+
+ return rc;
+}
+
+static int msm_jpeg_evt_get(struct msm_jpeg_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_jpeg_core_buf *buf_p;
+ struct msm_jpeg_ctrl_cmd ctrl_cmd;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_jpeg_q_wait(&pgmn_dev->evt_q);
+ buf_p = msm_jpeg_q_out(&pgmn_dev->evt_q);
+
+ if (!buf_p) {
+ JPEG_DBG("%s:%d] no buffer\n", __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ memset(&ctrl_cmd, 0, sizeof(ctrl_cmd));
+ ctrl_cmd.type = buf_p->vbuf.type;
+ kfree(buf_p);
+
+ if (ctrl_cmd.type == MSM_JPEG_EVT_SESSION_DONE) {
+ /* update the bw with zeroth vector */
+ msm_camera_update_bus_vector(pgmn_dev->bus_client, 0);
+ JPEG_BUS_UNVOTED(pgmn_dev);
+ JPEG_DBG("%s:%d] Bus unvoted\n", __func__, __LINE__);
+ }
+
+ JPEG_DBG("%s:%d] 0x%08lx %d\n", __func__, __LINE__,
+ (unsigned long) ctrl_cmd.value, ctrl_cmd.len);
+
+ if (copy_to_user(to, &ctrl_cmd, sizeof(ctrl_cmd))) {
+ JPEG_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_jpeg_evt_get_unblock(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_jpeg_q_unblock(&pgmn_dev->evt_q);
+ return 0;
+}
+
+static void msm_jpeg_reset_ack_irq(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+}
+
+static void msm_jpeg_err_irq(struct msm_jpeg_device *pgmn_dev,
+ int event)
+{
+ int rc = 0;
+ struct msm_jpeg_core_buf buf;
+
+ JPEG_PR_ERR("%s:%d] error: %d\n", __func__, __LINE__, event);
+
+ buf.vbuf.type = MSM_JPEG_EVT_ERR;
+ rc = msm_jpeg_q_in_buf(&pgmn_dev->evt_q, &buf);
+ if (!rc)
+ rc = msm_jpeg_q_wakeup(&pgmn_dev->evt_q);
+
+ if (!rc)
+ JPEG_PR_ERR("%s:%d] err err\n", __func__, __LINE__);
+
+}
+
+/*************** output queue ****************/
+
+static int msm_jpeg_we_pingpong_irq(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_jpeg_core_buf *buf_out;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ JPEG_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_jpeg_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
+ } else {
+ JPEG_DBG("%s:%d] no output return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ return rc;
+ }
+
+ buf_out = msm_jpeg_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out) {
+ JPEG_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_out->y_buffer_addr, buf_out->y_len);
+ rc = msm_jpeg_core_we_buf_update(pgmn_dev, buf_out);
+ kfree(buf_out);
+ } else {
+ msm_jpeg_core_we_buf_reset(pgmn_dev, buf_in);
+ JPEG_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_jpeg_q_wakeup(&pgmn_dev->output_rtn_q);
+
+ return rc;
+}
+
+static int msm_jpeg_output_get(struct msm_jpeg_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_jpeg_core_buf *buf_p;
+ struct msm_jpeg_buf buf_cmd;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_jpeg_q_wait(&pgmn_dev->output_rtn_q);
+ buf_p = msm_jpeg_q_out(&pgmn_dev->output_rtn_q);
+
+ if (!buf_p) {
+ JPEG_DBG("%s:%d] no output buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ msm_jpeg_platform_p2v(pgmn_dev->iommu_hdl, buf_p->ion_fd);
+ kfree(buf_p);
+
+ JPEG_DBG("%s:%d] 0x%08lx %d\n", __func__, __LINE__,
+ (unsigned long) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ JPEG_PR_ERR("%s:%d]", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_jpeg_output_get_unblock(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_jpeg_q_unblock(&pgmn_dev->output_rtn_q);
+ return 0;
+}
+
+static inline int msm_jpeg_add_u32_check(uint32_t *p, uint32_t n, uint32_t *res)
+{
+ *res = 0;
+
+ while (n--) {
+ if ((*res + *p) < *res)
+ return -EFAULT;
+ *res += *p++;
+ }
+ return 0;
+}
+
+static int msm_jpeg_output_buf_enqueue(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_jpeg_buf buf_cmd;
+ struct msm_jpeg_core_buf *buf_p;
+ uint32_t buf_len_params[10];
+ uint32_t total_len = 0;
+ int n = 0;
+
+ memset(&buf_cmd, 0x0, sizeof(struct msm_jpeg_buf));
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_jpeg_buf))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_len_params[n++] = buf_cmd.y_len;
+ buf_len_params[n++] = buf_cmd.cbcr_len;
+ buf_len_params[n++] = buf_cmd.pln2_len;
+ buf_len_params[n++] = buf_cmd.offset;
+ buf_len_params[n++] = buf_cmd.y_off;
+ if (msm_jpeg_add_u32_check(buf_len_params, n, &total_len) < 0) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_jpeg_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ JPEG_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+
+ JPEG_DBG("%s:%d] vaddr = 0x%08lx y_len = %d\n, fd = %d",
+ __func__, __LINE__, (unsigned long) buf_cmd.vaddr,
+ buf_cmd.y_len, buf_cmd.fd);
+
+ buf_p->ion_fd = buf_cmd.fd;
+ buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
+ total_len, pgmn_dev->iommu_hdl);
+
+ if (!buf_p->y_buffer_addr) {
+ JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -EFAULT;
+ }
+
+ buf_p->y_buffer_addr += buf_cmd.offset + buf_cmd.y_off;
+
+ if (buf_cmd.cbcr_len)
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr +
+ buf_cmd.y_len;
+ else
+ buf_p->cbcr_buffer_addr = 0x0;
+
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s:%d]After v2p pln0_addr %x pln0_len %d",
+ __func__, __LINE__, buf_p->y_buffer_addr,
+ buf_cmd.y_len);
+
+ JPEG_DBG("pl1_len %d, pln1_addr %x, pln2_adrr %x,pln2_len %d",
+ buf_cmd.cbcr_len, buf_p->cbcr_buffer_addr,
+ buf_p->pln2_addr, buf_cmd.pln2_len);
+
+ buf_p->y_len = buf_cmd.y_len;
+ buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
+ buf_p->vbuf = buf_cmd;
+
+ msm_jpeg_q_in(&pgmn_dev->output_buf_q, buf_p);
+ return 0;
+}
+
+/*************** input queue ****************/
+
+static int msm_jpeg_fe_pingpong_irq(struct msm_jpeg_device *pgmn_dev,
+ struct msm_jpeg_core_buf *buf_in)
+{
+ struct msm_jpeg_core_buf *buf_out;
+ int rc = 0;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ JPEG_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_jpeg_q_in_buf(&pgmn_dev->input_rtn_q, buf_in);
+ } else {
+ JPEG_DBG("%s:%d] no input return buffer\n", __func__,
+ __LINE__);
+ rc = -EFAULT;
+ }
+
+ buf_out = msm_jpeg_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ rc = msm_jpeg_core_fe_buf_update(pgmn_dev, buf_out);
+ kfree(buf_out);
+ msm_jpeg_core_fe_start(pgmn_dev);
+ } else {
+ JPEG_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ rc = -EFAULT;
+ }
+
+ if (buf_in)
+ rc = msm_jpeg_q_wakeup(&pgmn_dev->input_rtn_q);
+
+ return rc;
+}
+
+static int msm_jpeg_input_get(struct msm_jpeg_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_jpeg_core_buf *buf_p;
+ struct msm_jpeg_buf buf_cmd;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_jpeg_q_wait(&pgmn_dev->input_rtn_q);
+ buf_p = msm_jpeg_q_out(&pgmn_dev->input_rtn_q);
+
+ if (!buf_p) {
+ JPEG_DBG("%s:%d] no input buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+
+ msm_jpeg_platform_p2v(pgmn_dev->iommu_hdl, buf_p->ion_fd);
+ kfree(buf_p);
+
+ JPEG_DBG("%s:%d] 0x%08lx %d\n", __func__, __LINE__,
+ (unsigned long) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ JPEG_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_jpeg_input_get_unblock(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_jpeg_q_unblock(&pgmn_dev->input_rtn_q);
+ return 0;
+}
+
+static int msm_jpeg_input_buf_enqueue(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_jpeg_core_buf *buf_p;
+ struct msm_jpeg_buf buf_cmd;
+ uint32_t buf_len_params[10];
+ uint32_t total_len = 0;
+ int n = 0;
+
+ memset(&buf_cmd, 0x0, sizeof(struct msm_jpeg_buf));
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_jpeg_buf))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ buf_len_params[n++] = buf_cmd.y_len;
+ buf_len_params[n++] = buf_cmd.cbcr_len;
+ buf_len_params[n++] = buf_cmd.pln2_len;
+ buf_len_params[n++] = buf_cmd.offset;
+ buf_len_params[n++] = buf_cmd.y_off;
+ if (buf_cmd.cbcr_len)
+ buf_len_params[n++] = buf_cmd.cbcr_off;
+ if (buf_cmd.pln2_len)
+ buf_len_params[n++] = buf_cmd.pln2_off;
+
+ if (msm_jpeg_add_u32_check(buf_len_params, n, &total_len) < 0) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_jpeg_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ JPEG_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ JPEG_DBG("%s:%d] 0x%08lx %d\n", __func__, __LINE__,
+ (unsigned long) buf_cmd.vaddr, buf_cmd.y_len);
+
+ buf_p->ion_fd = buf_cmd.fd;
+ buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
+ total_len, pgmn_dev->iommu_hdl);
+
+ if (!buf_p->y_buffer_addr) {
+ JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -EFAULT;
+ }
+
+ buf_p->y_buffer_addr += buf_cmd.offset + buf_cmd.y_off;
+
+ buf_p->y_len = buf_cmd.y_len;
+ buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
+ buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
+
+ if (buf_cmd.cbcr_len)
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr +
+ buf_cmd.y_len + buf_cmd.cbcr_off;
+ else
+ buf_p->cbcr_buffer_addr = 0x0;
+
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len + buf_cmd.pln2_off;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%d",
+ __func__, buf_p->y_buffer_addr, buf_p->y_len,
+ buf_p->cbcr_buffer_addr, buf_p->cbcr_len);
+ JPEG_DBG("pln2_addr = %x, pln2_len = %d, fd =%d\n",
+ buf_p->pln2_addr, buf_p->pln2_len, buf_cmd.fd);
+
+ buf_p->vbuf = buf_cmd;
+
+ msm_jpeg_q_in(&pgmn_dev->input_buf_q, buf_p);
+
+ return 0;
+}
+
+static int msm_jpeg_irq(int event, void *context, void *data)
+{
+ struct msm_jpeg_device *pgmn_dev =
+ (struct msm_jpeg_device *) context;
+
+ switch (event) {
+ case MSM_JPEG_EVT_SESSION_DONE:
+ msm_jpeg_framedone_irq(pgmn_dev, data);
+ msm_jpeg_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_JPEG_HW_MASK_COMP_FE:
+ msm_jpeg_fe_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_JPEG_HW_MASK_COMP_WE:
+ msm_jpeg_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_JPEG_HW_MASK_COMP_RESET_ACK:
+ msm_jpeg_reset_ack_irq(pgmn_dev);
+ break;
+
+ case MSM_JPEG_HW_MASK_COMP_ERR:
+ default:
+ msm_jpeg_err_irq(pgmn_dev, event);
+ break;
+ }
+
+ return 0;
+}
+
+int __msm_jpeg_open(struct msm_jpeg_device *pgmn_dev)
+{
+ int rc;
+ irqreturn_t (*core_irq)(int, void *);
+
+ mutex_lock(&pgmn_dev->lock);
+ if (pgmn_dev->open_count) {
+ /* only open once */
+ JPEG_PR_ERR("%s:%d] busy\n", __func__, __LINE__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EBUSY;
+ }
+ pgmn_dev->open_count++;
+ if (pgmn_dev->open_count == 1)
+ pgmn_dev->state = MSM_JPEG_INIT;
+
+ mutex_unlock(&pgmn_dev->lock);
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ msm_jpeg_core_irq_install(msm_jpeg_irq);
+ if (pgmn_dev->core_type == MSM_JPEG_CORE_CODEC)
+ core_irq = msm_jpeg_core_irq;
+ else
+ core_irq = msm_jpegdma_core_irq;
+
+ /* initialize the platform resources */
+ rc = msm_jpeg_platform_init(core_irq, pgmn_dev);
+ if (rc) {
+ JPEG_PR_ERR("%s:%d] platform_init fail %d\n", __func__,
+ __LINE__, rc);
+ goto platform_init_fail;
+ }
+ JPEG_DBG("%s:%d] platform resources - base %pK, irq %d\n",
+ __func__, __LINE__,
+ pgmn_dev->base, (int)pgmn_dev->jpeg_irq_res->start);
+ msm_jpeg_q_cleanup(&pgmn_dev->evt_q);
+ msm_jpeg_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_jpeg_outbuf_q_cleanup(pgmn_dev, &pgmn_dev->output_buf_q);
+ msm_jpeg_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_jpeg_q_cleanup(&pgmn_dev->input_buf_q);
+ msm_jpeg_core_init(pgmn_dev);
+
+ JPEG_DBG("%s:%d] success\n", __func__, __LINE__);
+ return rc;
+
+platform_init_fail:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+int __msm_jpeg_release(struct msm_jpeg_device *pgmn_dev)
+{
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ mutex_lock(&pgmn_dev->lock);
+ if (!pgmn_dev->open_count) {
+ JPEG_PR_ERR(KERN_ERR "%s: not opened\n", __func__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EINVAL;
+ }
+ pgmn_dev->open_count--;
+ mutex_unlock(&pgmn_dev->lock);
+
+ msm_jpeg_core_release(pgmn_dev);
+ msm_jpeg_q_cleanup(&pgmn_dev->evt_q);
+ msm_jpeg_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_jpeg_outbuf_q_cleanup(pgmn_dev, &pgmn_dev->output_buf_q);
+ msm_jpeg_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_jpeg_outbuf_q_cleanup(pgmn_dev, &pgmn_dev->input_buf_q);
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ if (pgmn_dev->open_count)
+ JPEG_PR_ERR(KERN_ERR "%s: multiple opens\n", __func__);
+
+ /* release the platform resources */
+ msm_jpeg_platform_release(pgmn_dev);
+
+ JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+
+ return 0;
+}
+
+static int msm_jpeg_ioctl_hw_cmd(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_jpeg_hw_cmd hw_cmd;
+ int is_copy_to_user;
+
+ if (copy_from_user(&hw_cmd, (const void __user *)arg,
+ sizeof(struct msm_jpeg_hw_cmd))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ is_copy_to_user = msm_jpeg_hw_exec_cmds(&hw_cmd, 1,
+ pgmn_dev->res_size, pgmn_dev->base);
+ JPEG_DBG(
+ "%s:%d] type %d, n %d, offset %d, mask %x, data %x, pdata %lx\n",
+ __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+ hw_cmd.mask, hw_cmd.data, (unsigned long) hw_cmd.pdata);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user((void __user *)arg, &hw_cmd, sizeof(hw_cmd))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ } else {
+ return is_copy_to_user;
+ }
+
+ return 0;
+}
+
+static int msm_jpeg_ioctl_hw_cmds(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ int is_copy_to_user;
+ uint32_t len;
+ uint32_t m;
+ struct msm_jpeg_hw_cmds *hw_cmds_p;
+ struct msm_jpeg_hw_cmd *hw_cmd_p;
+
+ if (copy_from_user(&m, arg, sizeof(m))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if ((m == 0) || (m > ((UINT32_MAX - sizeof(struct msm_jpeg_hw_cmds)) /
+ sizeof(struct msm_jpeg_hw_cmd)))) {
+ JPEG_PR_ERR("%s:%d] m_cmds out of range\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ len = sizeof(struct msm_jpeg_hw_cmds) +
+ sizeof(struct msm_jpeg_hw_cmd) * (m - 1);
+ hw_cmds_p = kmalloc(len, GFP_KERNEL);
+ if (!hw_cmds_p) {
+ JPEG_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(hw_cmds_p, (const void __user *)arg, len)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+
+ hw_cmd_p = (struct msm_jpeg_hw_cmd *) &(hw_cmds_p->hw_cmd);
+
+ is_copy_to_user = msm_jpeg_hw_exec_cmds(hw_cmd_p, m,
+ pgmn_dev->res_size, pgmn_dev->base);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, hw_cmds_p, len)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+ } else {
+ kfree(hw_cmds_p);
+ return is_copy_to_user;
+ }
+ kfree(hw_cmds_p);
+ return 0;
+}
+
+static int msm_jpeg_start(struct msm_jpeg_device *pgmn_dev, void __user *arg,
+ int (*hw_ioctl)(struct msm_jpeg_device *, void __user *))
+{
+ struct msm_jpeg_core_buf *buf_out;
+ struct msm_jpeg_core_buf *buf_out_free[2] = {NULL, NULL};
+ int i, rc;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_jpeg_platform_set_dt_config(pgmn_dev);
+
+ /* update the bw with vector index "1" */
+ msm_camera_update_bus_vector(pgmn_dev->bus_client, 1);
+ JPEG_BUS_VOTED(pgmn_dev);
+ JPEG_DBG("%s:%d] Bus Voted\n", __func__, __LINE__);
+
+ pgmn_dev->release_buf = 1;
+ for (i = 0; i < 2; i++) {
+ buf_out = msm_jpeg_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ msm_jpeg_core_fe_buf_update(pgmn_dev, buf_out);
+ kfree(buf_out);
+ } else {
+ JPEG_DBG("%s:%d] no input buffer\n", __func__,
+ __LINE__);
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ buf_out_free[i] = msm_jpeg_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out_free[i]) {
+ msm_jpeg_core_we_buf_update(pgmn_dev, buf_out_free[i]);
+ pgmn_dev->release_buf = 0;
+ } else {
+ JPEG_DBG("%s:%d] no output buffer\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ kfree(buf_out_free[i]);
+
+ JPEG_DBG_HIGH("%s:%d] START\n", __func__, __LINE__);
+ pgmn_dev->state = MSM_JPEG_EXECUTING;
+ /* ensure write is done */
+ wmb();
+ rc = hw_ioctl(pgmn_dev, arg);
+ /* ensure write is done */
+ wmb();
+ JPEG_DBG("%s:%d]", __func__, __LINE__);
+ return rc;
+}
+
+static int msm_jpeg_ioctl_reset(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ int rc;
+ struct msm_jpeg_ctrl_cmd ctrl_cmd, *p_ctrl_cmd;
+
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ p_ctrl_cmd = &ctrl_cmd;
+
+ if (pgmn_dev->state == MSM_JPEG_INIT) {
+ if (copy_from_user(&ctrl_cmd, arg, sizeof(ctrl_cmd))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ pgmn_dev->op_mode = p_ctrl_cmd->type;
+
+ rc = msm_jpeg_core_reset(pgmn_dev, pgmn_dev->op_mode,
+ pgmn_dev->base, pgmn_dev->res_size);
+ } else {
+ JPEG_PR_ERR("%s:%d] JPEG not been initialized Wrong state\n",
+ __func__, __LINE__);
+ rc = -1;
+ }
+ return rc;
+}
+
+static int msm_jpeg_ioctl_test_dump_region(struct msm_jpeg_device *pgmn_dev,
+ unsigned long arg)
+{
+ JPEG_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_jpeg_io_dump(pgmn_dev->base, JPEG_REG_SIZE);
+ return 0;
+}
+
+static int msm_jpeg_ioctl_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ long clk_rate;
+ int rc;
+
+ if ((pgmn_dev->state != MSM_JPEG_INIT) &&
+ (pgmn_dev->state != MSM_JPEG_RESET)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ if (get_user(clk_rate, (unsigned int __user *)arg)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ JPEG_DBG("%s:%d] Requested clk rate %ld\n", __func__, __LINE__,
+ clk_rate);
+ if (clk_rate < 0) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ rc = msm_jpeg_platform_set_clk_rate(pgmn_dev, clk_rate);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: clk failed rc = %d\n", __func__, rc);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+#ifdef CONFIG_COMPAT
+static int msm_jpeg_get_ctrl_cmd32(struct msm_jpeg_ctrl_cmd *ctrl_cmd,
+ void __user *arg)
+{
+ struct msm_jpeg_ctrl_cmd32 ctrl_cmd32;
+ unsigned long temp;
+
+ if (copy_from_user(&ctrl_cmd32, arg,
+ sizeof(struct msm_jpeg_ctrl_cmd32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ ctrl_cmd->type = ctrl_cmd32.type;
+ ctrl_cmd->len = ctrl_cmd32.len;
+ temp = (unsigned long) ctrl_cmd32.value;
+ ctrl_cmd->value = (void *) temp;
+
+ return 0;
+}
+static int msm_jpeg_put_ctrl_cmd32(struct msm_jpeg_ctrl_cmd *ctrl_cmd,
+ void __user *arg)
+{
+ struct msm_jpeg_ctrl_cmd32 ctrl_cmd32;
+ unsigned long temp;
+
+ ctrl_cmd32.type = ctrl_cmd->type;
+ ctrl_cmd32.len = ctrl_cmd->len;
+ temp = (unsigned long) ctrl_cmd->value;
+ ctrl_cmd32.value = (compat_uptr_t) temp;
+
+ if (copy_to_user(arg, &ctrl_cmd32,
+ sizeof(struct msm_jpeg_ctrl_cmd32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int msm_jpeg_get_jpeg_buf32(struct msm_jpeg_buf *jpeg_buf,
+ void __user *arg)
+{
+ struct msm_jpeg_buf32 jpeg_buf32;
+ unsigned long temp;
+
+ if (copy_from_user(&jpeg_buf32, arg, sizeof(struct msm_jpeg_buf32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ jpeg_buf->type = jpeg_buf32.type;
+ jpeg_buf->fd = jpeg_buf32.fd;
+ temp = (unsigned long) jpeg_buf32.vaddr;
+ jpeg_buf->vaddr = (void *) temp;
+ jpeg_buf->y_off = jpeg_buf32.y_off;
+ jpeg_buf->y_len = jpeg_buf32.y_len;
+ jpeg_buf->framedone_len = jpeg_buf32.framedone_len;
+ jpeg_buf->cbcr_off = jpeg_buf32.cbcr_off;
+ jpeg_buf->cbcr_len = jpeg_buf32.cbcr_len;
+ jpeg_buf->num_of_mcu_rows = jpeg_buf32.num_of_mcu_rows;
+ jpeg_buf->offset = jpeg_buf32.offset;
+ jpeg_buf->pln2_off = jpeg_buf32.pln2_off;
+ jpeg_buf->pln2_len = jpeg_buf32.pln2_len;
+
+ return 0;
+}
+static int msm_jpeg_put_jpeg_buf32(struct msm_jpeg_buf *jpeg_buf,
+ void __user *arg)
+{
+ struct msm_jpeg_buf32 jpeg_buf32;
+ unsigned long temp;
+
+ jpeg_buf32.type = jpeg_buf->type;
+ jpeg_buf32.fd = jpeg_buf->fd;
+ temp = (unsigned long) jpeg_buf->vaddr;
+ jpeg_buf32.vaddr = (compat_uptr_t) temp;
+ jpeg_buf32.y_off = jpeg_buf->y_off;
+ jpeg_buf32.y_len = jpeg_buf->y_len;
+ jpeg_buf32.framedone_len = jpeg_buf->framedone_len;
+ jpeg_buf32.cbcr_off = jpeg_buf->cbcr_off;
+ jpeg_buf32.cbcr_len = jpeg_buf->cbcr_len;
+ jpeg_buf32.num_of_mcu_rows = jpeg_buf->num_of_mcu_rows;
+ jpeg_buf32.offset = jpeg_buf->offset;
+ jpeg_buf32.pln2_off = jpeg_buf->pln2_off;
+ jpeg_buf32.pln2_len = jpeg_buf->pln2_len;
+
+ if (copy_to_user(arg, &jpeg_buf32, sizeof(struct msm_jpeg_buf32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int msm_jpeg_put_hw_cmd32(void __user *arg,
+ struct msm_jpeg_hw_cmd *phw_cmd, int copy)
+{
+ struct msm_jpeg_hw_cmd32 hw_cmd32;
+ struct msm_jpeg_hw_cmd32 *phw_cmd32;
+
+ phw_cmd32 = (__force struct msm_jpeg_hw_cmd32 *) arg;
+ if (copy)
+ phw_cmd32 = &hw_cmd32;
+
+
+ phw_cmd32->type = phw_cmd->type;
+ phw_cmd32->n = phw_cmd->n;
+ phw_cmd32->offset = phw_cmd->offset;
+ phw_cmd32->mask = phw_cmd->mask;
+ phw_cmd32->data = phw_cmd->data;
+
+ if (copy && copy_to_user(arg, &hw_cmd32, sizeof(hw_cmd32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+static int msm_jpeg_get_hw_cmd32(struct msm_jpeg_hw_cmd *phw_cmd,
+ void __user *arg, int copy)
+{
+ struct msm_jpeg_hw_cmd32 hw_cmd32;
+ struct msm_jpeg_hw_cmd32 *phw_cmd32;
+
+ if (copy) {
+ phw_cmd32 = &hw_cmd32;
+ if (copy_from_user(&hw_cmd32, arg, sizeof(hw_cmd32))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ } else {
+ phw_cmd32 = (__force struct msm_jpeg_hw_cmd32 *) arg;
+ }
+ phw_cmd->type = phw_cmd32->type;
+ phw_cmd->n = phw_cmd32->n;
+ phw_cmd->offset = phw_cmd32->offset;
+ phw_cmd->mask = phw_cmd32->mask;
+ phw_cmd->data = phw_cmd32->data;
+
+ return 0;
+}
+static int msm_jpeg_ioctl_hw_cmds32(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ int is_copy_to_user;
+ uint32_t len, len32;
+ uint32_t m;
+ struct msm_jpeg_hw_cmds32 *phw_cmds32;
+ struct msm_jpeg_hw_cmds *phw_cmds;
+
+ if (copy_from_user(&m, arg, sizeof(m))) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if ((m == 0) || (m > ((UINT32_MAX - sizeof(struct msm_jpeg_hw_cmds32)) /
+ sizeof(struct msm_jpeg_hw_cmd32)))) {
+ JPEG_PR_ERR("%s:%d] m_cmds out of range\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ len32 = sizeof(struct msm_jpeg_hw_cmds32) +
+ sizeof(struct msm_jpeg_hw_cmd32) * (m - 1);
+ phw_cmds32 = kmalloc(len32, GFP_KERNEL);
+ if (!phw_cmds32) {
+ JPEG_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len32);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(phw_cmds32, arg, len32)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(phw_cmds32);
+ return -EFAULT;
+ }
+ len = sizeof(struct msm_jpeg_hw_cmds) +
+ sizeof(struct msm_jpeg_hw_cmd) * (m - 1);
+ phw_cmds = kmalloc(len, GFP_KERNEL);
+ if (!phw_cmds) {
+ JPEG_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+ kfree(phw_cmds32);
+ return -EFAULT;
+ }
+ (phw_cmds)->m = m;
+ while (m--) {
+ struct msm_jpeg_hw_cmd32 *src;
+ struct msm_jpeg_hw_cmd *dst;
+
+ src = &phw_cmds32->hw_cmd[m];
+ dst = &(phw_cmds)->hw_cmd[m];
+ msm_jpeg_get_hw_cmd32(dst, (void __user *)src, 0);
+ }
+
+ is_copy_to_user = msm_jpeg_hw_exec_cmds(phw_cmds->hw_cmd,
+ phw_cmds->m,
+ pgmn_dev->res_size, pgmn_dev->base);
+
+ if (is_copy_to_user >= 0) {
+ m = phw_cmds->m;
+ while (m--) {
+ struct msm_jpeg_hw_cmd *src;
+ struct msm_jpeg_hw_cmd32 *dst;
+
+ dst = &phw_cmds32->hw_cmd[m];
+ src = &phw_cmds->hw_cmd[m];
+
+ msm_jpeg_put_hw_cmd32((void __user *)dst, src, 0);
+ }
+ if (copy_to_user(arg, phw_cmds32, len32)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(phw_cmds);
+ kfree(phw_cmds32);
+ return -EFAULT;
+ }
+
+ } else {
+ kfree(phw_cmds);
+ kfree(phw_cmds32);
+ return is_copy_to_user;
+ }
+ kfree(phw_cmds);
+ kfree(phw_cmds32);
+
+ return 0;
+}
+static int msm_jpeg_ioctl_hw_cmd32(struct msm_jpeg_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_jpeg_hw_cmd hw_cmd;
+ int is_copy_to_user;
+
+ if (msm_jpeg_get_hw_cmd32(&hw_cmd, (void __user *)arg, 1)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ is_copy_to_user = msm_jpeg_hw_exec_cmds(&hw_cmd, 1,
+ pgmn_dev->res_size, pgmn_dev->base);
+ JPEG_DBG("%s:%d] type %d, n %d, offst %d, mask %x, data %x pdata %lx\n",
+ __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+ hw_cmd.mask, hw_cmd.data, (unsigned long) hw_cmd.pdata);
+
+ if (is_copy_to_user >= 0) {
+ if (msm_jpeg_put_hw_cmd32((void __user *)arg, &hw_cmd, 1)) {
+ JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ } else
+ return is_copy_to_user;
+
+
+ return 0;
+}
+
+long __msm_jpeg_compat_ioctl(struct msm_jpeg_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct msm_jpeg_ctrl_cmd *pctrl_cmd = NULL, ctrl_cmd;
+ struct msm_jpeg_buf jpeg_buf;
+ mm_segment_t old_fs;
+
+ old_fs = get_fs();
+
+ switch (cmd) {
+ case MSM_JPEG_IOCTL_GET_HW_VERSION:
+ JPEG_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+ rc = msm_jpeg_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+ case MSM_JPEG_IOCTL_GET_HW_VERSION32:
+ JPEG_DBG("%s:%d] VERSION 1 32bit\n", __func__, __LINE__);
+ rc = msm_jpeg_ioctl_hw_cmd32(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_RESET:
+ rc = msm_jpeg_ioctl_reset(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_RESET32:
+ rc = msm_jpeg_get_ctrl_cmd32(&ctrl_cmd,
+ (void __user *) arg);
+ if (rc < 0)
+ break;
+
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_ioctl_reset(pgmn_dev, (void __user *) &ctrl_cmd);
+ set_fs(old_fs);
+ kfree(pctrl_cmd);
+ break;
+
+ case MSM_JPEG_IOCTL_STOP:
+ rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ pgmn_dev->state = MSM_JPEG_STOPPED;
+ break;
+
+ case MSM_JPEG_IOCTL_STOP32:
+ rc = msm_jpeg_ioctl_hw_cmds32(pgmn_dev, (void __user *) arg);
+ pgmn_dev->state = MSM_JPEG_STOPPED;
+ break;
+
+ case MSM_JPEG_IOCTL_START:
+ rc = msm_jpeg_start(pgmn_dev, (void __user *) arg,
+ msm_jpeg_ioctl_hw_cmds);
+ break;
+
+ case MSM_JPEG_IOCTL_START32:
+ rc = msm_jpeg_start(pgmn_dev, (void __user *) arg,
+ msm_jpeg_ioctl_hw_cmds32);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_BUF_ENQUEUE:
+ rc = msm_jpeg_input_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_BUF_ENQUEUE32:
+ rc = msm_jpeg_get_jpeg_buf32(&jpeg_buf, (void __user *) arg);
+ if (rc < 0)
+ break;
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_input_buf_enqueue(pgmn_dev,
+ (void __user *) &jpeg_buf);
+ set_fs(old_fs);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_GET:
+ rc = msm_jpeg_input_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_GET32:
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_input_get(pgmn_dev, (void __user *) &jpeg_buf);
+ set_fs(old_fs);
+ if (rc < 0)
+ break;
+ rc = msm_jpeg_put_jpeg_buf32(&jpeg_buf, (void __user *) arg);
+
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_GET_UNBLOCK:
+ rc = msm_jpeg_input_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_BUF_ENQUEUE:
+ rc = msm_jpeg_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_BUF_ENQUEUE32:
+ rc = msm_jpeg_get_jpeg_buf32(&jpeg_buf, (void __user *) arg);
+ if (rc < 0)
+ break;
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_output_buf_enqueue(pgmn_dev,
+ (void __user *) &jpeg_buf);
+ set_fs(old_fs);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_GET:
+ rc = msm_jpeg_output_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_GET32:
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_output_get(pgmn_dev, (void __user *) &jpeg_buf);
+ set_fs(old_fs);
+ if (rc < 0)
+ break;
+ rc = msm_jpeg_put_jpeg_buf32(&jpeg_buf, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_GET_UNBLOCK:
+ rc = msm_jpeg_output_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_EVT_GET:
+ rc = msm_jpeg_evt_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_EVT_GET32:
+ set_fs(KERNEL_DS);
+ rc = msm_jpeg_evt_get(pgmn_dev, (void __user *) &ctrl_cmd);
+ set_fs(old_fs);
+ if (rc < 0)
+ break;
+ msm_jpeg_put_ctrl_cmd32(&ctrl_cmd, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_EVT_GET_UNBLOCK:
+ rc = msm_jpeg_evt_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMD32:
+ rc = msm_jpeg_ioctl_hw_cmd32(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMD:
+ rc = msm_jpeg_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMDS32:
+ rc = msm_jpeg_ioctl_hw_cmds32(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMDS:
+ rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_TEST_DUMP_REGION:
+ rc = msm_jpeg_ioctl_test_dump_region(pgmn_dev, arg);
+ break;
+
+ case MSM_JPEG_IOCTL_TEST_DUMP_REGION32:
+ rc = msm_jpeg_ioctl_test_dump_region(pgmn_dev, arg);
+ break;
+
+ case MSM_JPEG_IOCTL_SET_CLK_RATE:
+ rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ default:
+ JPEG_PR_ERR(KERN_INFO "%s:%d] cmd = %d not supported\n",
+ __func__, __LINE__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+#else
+long __msm_jpeg_compat_ioctl(struct msm_jpeg_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+#endif
+
+long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+
+ switch (cmd) {
+ case MSM_JPEG_IOCTL_GET_HW_VERSION:
+ JPEG_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+ rc = msm_jpeg_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_RESET:
+ rc = msm_jpeg_ioctl_reset(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_STOP:
+ rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ pgmn_dev->state = MSM_JPEG_STOPPED;
+ break;
+
+ case MSM_JPEG_IOCTL_START:
+ rc = msm_jpeg_start(pgmn_dev, (void __user *) arg,
+ msm_jpeg_ioctl_hw_cmds);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_BUF_ENQUEUE:
+ rc = msm_jpeg_input_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_GET:
+ rc = msm_jpeg_input_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_INPUT_GET_UNBLOCK:
+ rc = msm_jpeg_input_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_BUF_ENQUEUE:
+ rc = msm_jpeg_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_GET:
+ rc = msm_jpeg_output_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_OUTPUT_GET_UNBLOCK:
+ rc = msm_jpeg_output_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_EVT_GET:
+ rc = msm_jpeg_evt_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_EVT_GET_UNBLOCK:
+ rc = msm_jpeg_evt_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMD:
+ rc = msm_jpeg_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_HW_CMDS:
+ rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_JPEG_IOCTL_TEST_DUMP_REGION:
+ rc = msm_jpeg_ioctl_test_dump_region(pgmn_dev, arg);
+ break;
+
+ case MSM_JPEG_IOCTL_SET_CLK_RATE:
+ rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev, (void __user *) arg);
+ break;
+ default:
+ pr_err_ratelimited("%s:%d] cmd = %d not supported\n",
+ __func__, __LINE__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev)
+{
+ int rc = 0;
+ int idx = 0;
+
+ char *iommu_name[JPEG_DEV_CNT] = {"jpeg_enc0", "jpeg_enc1",
+ "jpeg_dec", "jpeg_dma"};
+
+
+ mutex_init(&pgmn_dev->lock);
+
+ pr_err("%s:%d] Jpeg Device id %d", __func__, __LINE__,
+ pgmn_dev->pdev->id);
+ idx = pgmn_dev->pdev->id;
+ pgmn_dev->idx = idx;
+ pgmn_dev->decode_flag = (idx == JPEG_DEC_ID);
+
+ msm_jpeg_q_init("evt_q", &pgmn_dev->evt_q);
+ msm_jpeg_q_init("output_rtn_q", &pgmn_dev->output_rtn_q);
+ msm_jpeg_q_init("output_buf_q", &pgmn_dev->output_buf_q);
+ msm_jpeg_q_init("input_rtn_q", &pgmn_dev->input_rtn_q);
+ msm_jpeg_q_init("input_buf_q", &pgmn_dev->input_buf_q);
+
+ /*get device context for IOMMU*/
+ rc = cam_smmu_get_handle(iommu_name[idx], &pgmn_dev->iommu_hdl);
+ JPEG_DBG("%s:%d] hdl %d", __func__, __LINE__,
+ pgmn_dev->iommu_hdl);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: No iommu fw context found\n",
+ __func__);
+ goto err_smmu;
+ }
+
+ /* setup all the resources for the jpeg driver */
+ rc = msm_jpeg_platform_setup(pgmn_dev);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: setup failed\n",
+ __func__);
+ goto err_setup;
+ }
+
+ return rc;
+err_setup:
+err_smmu:
+ mutex_destroy(&pgmn_dev->lock);
+ return -EFAULT;
+}
+
+int __msm_jpeg_exit(struct msm_jpeg_device *pgmn_dev)
+{
+ msm_jpeg_platform_cleanup(pgmn_dev);
+ mutex_destroy(&pgmn_dev->lock);
+ kfree(pgmn_dev);
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.h
new file mode 100644
index 0000000..acf945e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.h
@@ -0,0 +1,141 @@
+/* Copyright (c) 2012-2016, 2018, 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_JPEG_SYNC_H
+#define MSM_JPEG_SYNC_H
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm_camera_io_util.h"
+#include "msm_jpeg_hw.h"
+#include "cam_smmu_api.h"
+#include "cam_soc_api.h"
+
+#define JPEG_8974_V1 0x10000000
+#define JPEG_8974_V2 0x10010000
+#define JPEG_8994 0x10020000
+#define JPEG_CLK_MAX 16
+#define JPEG_REGULATOR_MAX 3
+
+enum msm_jpeg_state {
+ MSM_JPEG_INIT,
+ MSM_JPEG_RESET,
+ MSM_JPEG_EXECUTING,
+ MSM_JPEG_STOPPED,
+ MSM_JPEG_IDLE
+};
+
+enum msm_jpeg_core_type {
+ MSM_JPEG_CORE_CODEC,
+ MSM_JPEG_CORE_DMA
+};
+
+struct msm_jpeg_q {
+ char const *name;
+ struct list_head q;
+ spinlock_t lck;
+ wait_queue_head_t wait;
+ int unblck;
+};
+
+struct msm_jpeg_q_entry {
+ struct list_head list;
+ void *data;
+};
+
+struct msm_jpeg_device {
+ struct platform_device *pdev;
+ struct resource *jpeg_irq_res;
+ void *base;
+ void *vbif_base;
+ struct clk **jpeg_clk;
+ struct msm_cam_clk_info *jpeg_clk_info;
+ size_t num_clk;
+ int num_reg;
+ struct msm_cam_regulator *jpeg_vdd;
+ uint32_t hw_version;
+
+ struct device *device;
+ struct cdev cdev;
+ struct mutex lock;
+ char open_count;
+ uint8_t op_mode;
+
+ /* Flag to store the jpeg bus vote state
+ */
+ int jpeg_bus_vote;
+
+ /* event queue including frame done & err indications
+ */
+ struct msm_jpeg_q evt_q;
+
+ /* output return queue
+ */
+ struct msm_jpeg_q output_rtn_q;
+
+ /* output buf queue
+ */
+ struct msm_jpeg_q output_buf_q;
+
+ /* input return queue
+ */
+ struct msm_jpeg_q input_rtn_q;
+
+ /* input buf queue
+ */
+ struct msm_jpeg_q input_buf_q;
+
+ struct v4l2_subdev subdev;
+
+ struct class *msm_jpeg_class;
+
+ dev_t msm_jpeg_devno;
+
+ /*iommu domain and context*/
+ int idx;
+ int iommu_hdl;
+ int decode_flag;
+ void *jpeg_vbif;
+ int release_buf;
+ struct msm_jpeg_hw_pingpong fe_pingpong_buf;
+ struct msm_jpeg_hw_pingpong we_pingpong_buf;
+ int we_pingpong_index;
+ int reset_done_ack;
+ spinlock_t reset_lock;
+ wait_queue_head_t reset_wait;
+ uint32_t res_size;
+ enum msm_jpeg_state state;
+ enum msm_jpeg_core_type core_type;
+ enum cam_bus_client bus_client;
+};
+
+int __msm_jpeg_open(struct msm_jpeg_device *pgmn_dev);
+int __msm_jpeg_release(struct msm_jpeg_device *pgmn_dev);
+
+long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_COMPAT
+long __msm_jpeg_compat_ioctl(struct msm_jpeg_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg);
+#endif
+
+int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev);
+int __msm_jpeg_exit(struct msm_jpeg_device *pgmn_dev);
+
+#endif /* MSM_JPEG_SYNC_H */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile b/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile
new file mode 100644
index 0000000..21cbadb
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile
@@ -0,0 +1,4 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+obj-$(CONFIG_MSM_JPEGDMA) += msm_jpeg_dma_dev.o msm_jpeg_dma_hw.o
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c
new file mode 100644
index 0000000..55e2646
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c
@@ -0,0 +1,1502 @@
+/* Copyright (c) 2015-2018, 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/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/ion.h>
+#include <linux/msm_ion.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/msm_jpeg_dma.h>
+#include <linux/clk/msm-clk.h>
+
+#include "msm_jpeg_dma_dev.h"
+#include "msm_jpeg_dma_hw.h"
+#include "cam_hw_ops.h"
+
+#define MSM_JPEGDMA_DRV_NAME "msm_jpegdma"
+
+/* Jpeg dma stream off timeout */
+#define MSM_JPEGDMA_STREAM_OFF_TIMEOUT_MS 500
+
+/* Jpeg dma formats lookup table */
+static struct msm_jpegdma_format formats[] = {
+ {
+ .name = "Greyscale",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .num_planes = 1,
+ .colplane_h = 1,
+ .colplane_v = 1,
+ .h_align = 1,
+ .v_align = 1,
+ .planes[0] = JPEGDMA_PLANE_TYPE_Y,
+ },
+ {
+ .name = "Y/CbCr 4:2:0",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 12,
+ .num_planes = 2,
+ .colplane_h = 1,
+ .colplane_v = 2,
+ .h_align = 2,
+ .v_align = 2,
+ .planes[0] = JPEGDMA_PLANE_TYPE_Y,
+ .planes[1] = JPEGDMA_PLANE_TYPE_CBCR,
+ },
+ {
+ .name = "Y/CrCb 4:2:0",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .depth = 12,
+ .num_planes = 2,
+ .colplane_h = 1,
+ .colplane_v = 2,
+ .h_align = 2,
+ .v_align = 2,
+ .planes[0] = JPEGDMA_PLANE_TYPE_Y,
+ .planes[1] = JPEGDMA_PLANE_TYPE_CBCR,
+ },
+ {
+ .name = "YVU 4:2:0 planar, YCrCb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 12,
+ .num_planes = 3,
+ .colplane_h = 1,
+ .colplane_v = 4,
+ .h_align = 2,
+ .v_align = 2,
+ .planes[0] = JPEGDMA_PLANE_TYPE_Y,
+ .planes[1] = JPEGDMA_PLANE_TYPE_CR,
+ .planes[2] = JPEGDMA_PLANE_TYPE_CB,
+ },
+ {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .num_planes = 3,
+ .colplane_h = 1,
+ .colplane_v = 4,
+ .h_align = 2,
+ .v_align = 2,
+ .planes[0] = JPEGDMA_PLANE_TYPE_Y,
+ .planes[1] = JPEGDMA_PLANE_TYPE_CB,
+ .planes[2] = JPEGDMA_PLANE_TYPE_CR,
+ },
+};
+
+/*
+ * msm_jpegdma_ctx_from_fh - Get dma context from v4l2 fh.
+ * @fh: Pointer to v4l2 fh.
+ */
+static inline struct jpegdma_ctx *msm_jpegdma_ctx_from_fh(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct jpegdma_ctx, fh);
+}
+
+/*
+ * msm_jpegdma_get_next_config_idx - get next configuration index.
+ * @ctx: Pointer to jpegdma context.
+ */
+static inline int msm_jpegdma_get_next_config_idx(struct jpegdma_ctx *ctx)
+{
+ return (ctx->config_idx + 1) % MSM_JPEGDMA_MAX_CONFIGS;
+}
+
+/*
+ * msm_jpegdma_schedule_next_config - Schedule next configuration.
+ * @ctx: Pointer to jpegdma context.
+ */
+static inline void msm_jpegdma_schedule_next_config(struct jpegdma_ctx *ctx)
+{
+ ctx->config_idx = (ctx->config_idx + 1) % MSM_JPEGDMA_MAX_CONFIGS;
+}
+
+/*
+ * msm_jpegdma_cast_long_to_buff_ptr - Cast long to buffer pointer.
+ * @vaddr: vaddr as long
+ * @buff_ptr_head: buffer pointer head
+ */
+static inline void msm_jpegdma_cast_long_to_buff_ptr(unsigned long vaddr,
+ struct msm_jpeg_dma_buff __user **buff_ptr_head)
+{
+#ifdef CONFIG_COMPAT
+ *buff_ptr_head = compat_ptr(vaddr);
+#else
+ *buff_ptr_head = (struct msm_jpeg_dma_buff *) vaddr;
+#endif
+}
+
+/*
+ * msm_jpegdma_get_format_idx - Get jpeg dma format lookup index.
+ * @ctx: Pointer to dma ctx.
+ * @f: v4l2 format.
+ */
+static int msm_jpegdma_get_format_idx(struct jpegdma_ctx *ctx,
+ struct v4l2_format *f)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == f->fmt.pix.pixelformat)
+ break;
+
+ if (i == ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ return i;
+}
+
+/*
+ * msm_jpegdma_fill_size_from_ctx - Fill jpeg dma format lookup index.
+ * @ctx: Pointer to dma ctx.
+ * @size: Size config.
+ */
+static void msm_jpegdma_fill_size_from_ctx(struct jpegdma_ctx *ctx,
+ struct msm_jpegdma_size_config *size)
+{
+
+ size->in_size.top = ctx->crop.top;
+ size->in_size.left = ctx->crop.left;
+ size->in_size.width = ctx->crop.width;
+ size->in_size.height = ctx->crop.height;
+ size->in_size.scanline = ctx->format_out.fmt.pix.height;
+ size->in_size.stride = ctx->format_out.fmt.pix.bytesperline;
+
+ size->out_size.top = 0;
+ size->out_size.left = 0;
+ size->out_size.width = ctx->format_cap.fmt.pix.width;
+ size->out_size.height = ctx->format_cap.fmt.pix.height;
+ size->out_size.scanline = ctx->format_cap.fmt.pix.height;
+ size->out_size.stride = ctx->format_cap.fmt.pix.bytesperline;
+}
+
+/*
+ * msm_jpegdma_align_format - Align jpeg dma format.
+ * @f: v4l2 format.
+ * @format_idx: format lookup index.
+ */
+static void msm_jpegdma_align_format(struct v4l2_format *f, int format_idx)
+{
+ unsigned int size_image;
+ int i;
+
+ if (f->fmt.pix.width > MSM_JPEGDMA_MAX_WIDTH)
+ f->fmt.pix.width = MSM_JPEGDMA_MAX_WIDTH;
+
+ if (f->fmt.pix.width < MSM_JPEGDMA_MIN_WIDTH)
+ f->fmt.pix.width = MSM_JPEGDMA_MIN_WIDTH;
+
+ if (f->fmt.pix.height > MSM_JPEGDMA_MAX_HEIGHT)
+ f->fmt.pix.height = MSM_JPEGDMA_MAX_HEIGHT;
+
+ if (f->fmt.pix.height < MSM_JPEGDMA_MIN_HEIGHT)
+ f->fmt.pix.height = MSM_JPEGDMA_MIN_HEIGHT;
+
+ if (formats[format_idx].h_align > 1)
+ f->fmt.pix.width &= ~(formats[format_idx].h_align - 1);
+
+ if (formats[format_idx].v_align > 1)
+ f->fmt.pix.height &= ~(formats[format_idx].v_align - 1);
+
+ if (f->fmt.pix.bytesperline < f->fmt.pix.width)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+
+ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+ MSM_JPEGDMA_STRIDE_ALIGN);
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+
+ size_image = f->fmt.pix.bytesperline * f->fmt.pix.height;
+
+ if (formats[format_idx].num_planes > 1)
+ for (i = 1; i < formats[format_idx].num_planes; i++)
+ size_image += (f->fmt.pix.bytesperline *
+ (f->fmt.pix.height /
+ formats[format_idx].colplane_v));
+
+ f->fmt.pix.sizeimage = size_image;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+}
+
+/*
+ * msm_jpegdma_config_ok - Check if jpeg dma format is ok for processing.
+ * @ctx: Pointer to dma ctx.
+ */
+static int msm_jpegdma_config_ok(struct jpegdma_ctx *ctx)
+{
+ int ret;
+ int cap_idx;
+ int out_idx;
+ struct msm_jpegdma_size_config size;
+
+ cap_idx = msm_jpegdma_get_format_idx(ctx, &ctx->format_cap);
+ if (cap_idx < 0)
+ return 0;
+
+ out_idx = msm_jpegdma_get_format_idx(ctx, &ctx->format_out);
+ if (out_idx < 0)
+ return 0;
+
+ /* jpeg dma can not convert formats */
+ if (cap_idx != out_idx)
+ return 0;
+
+ msm_jpegdma_fill_size_from_ctx(ctx, &size);
+
+ size.format = formats[ctx->format_idx];
+
+ ret = msm_jpegdma_hw_check_config(ctx->jdma_device, &size);
+ if (ret < 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * msm_jpegdma_update_hw_config - Update dma hw configuration/
+ * @ctx: Pointer to dma ctx.
+ */
+static int msm_jpegdma_update_hw_config(struct jpegdma_ctx *ctx)
+{
+ struct msm_jpegdma_size_config size;
+ int idx;
+ int ret = 0;
+
+ if (msm_jpegdma_config_ok(ctx)) {
+ size.fps = ctx->timeperframe.denominator /
+ ctx->timeperframe.numerator;
+
+ size.in_offset = ctx->in_offset;
+ size.out_offset = ctx->out_offset;
+
+ size.format = formats[ctx->format_idx];
+
+ msm_jpegdma_fill_size_from_ctx(ctx, &size);
+
+ idx = msm_jpegdma_get_next_config_idx(ctx);
+
+ ret = msm_jpegdma_hw_set_config(ctx->jdma_device,
+ &size, &ctx->plane_config[idx]);
+ if (ret < 0)
+ dev_err(ctx->jdma_device->dev, "Can not get hw cfg\n");
+ else
+ ctx->pending_config = 1;
+ }
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_queue_setup - vb2_ops queue_setup callback.
+ * @q: Pointer to vb2 queue struct.
+ * @fmt: Pointer to v4l2 format struct (NULL is valid argument).
+ * @num_buffers: Pointer of number of buffers requested.
+ * @num_planes: Pointer to number of planes requested.
+ * @sizes: Array containing sizes of planes.
+ * @alloc_ctxs: Array of allocated contexts for each plane.
+ */
+static int msm_jpegdma_queue_setup(struct vb2_queue *q,
+// const void *parg,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_ctxs[])
+{
+ struct jpegdma_ctx *ctx = vb2_get_drv_priv(q);
+ //struct v4l2_format *fmt = (struct v4l2_format *)parg;
+ struct v4l2_format *fmt = NULL;
+
+ if (fmt == NULL) {
+ switch (q->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ sizes[0] = ctx->format_out.fmt.pix.sizeimage;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ sizes[0] = ctx->format_cap.fmt.pix.sizeimage;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ sizes[0] = fmt->fmt.pix.sizeimage;
+ }
+
+ *num_planes = 1;
+ alloc_ctxs[0] = (struct device *)ctx->jdma_device;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_buf_queue - vb2_ops buf_queue callback.
+ * @vb: Pointer to vb2 buffer struct.
+ */
+static void msm_jpegdma_buf_queue(struct vb2_buffer *vb)
+{
+ struct jpegdma_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2_buf);
+
+}
+
+/*
+ * msm_jpegdma_start_streaming - vb2_ops start_streaming callback.
+ * @q: Pointer to vb2 queue struct.
+ * @count: Number of buffer queued before stream on call.
+ */
+static int msm_jpegdma_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct jpegdma_ctx *ctx = vb2_get_drv_priv(q);
+ int ret;
+
+ ret = msm_jpegdma_hw_get(ctx->jdma_device);
+ if (ret < 0) {
+ dev_err(ctx->jdma_device->dev, "Fail to get dma hw\n");
+ return ret;
+ }
+ if (!atomic_read(&ctx->active)) {
+ ret = msm_jpegdma_update_hw_config(ctx);
+ if (ret < 0) {
+ dev_err(ctx->jdma_device->dev, "Fail to configure hw\n");
+ return ret;
+ }
+ atomic_set(&ctx->active, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_stop_streaming - vb2_ops stop_streaming callback.
+ * @q: Pointer to vb2 queue struct.
+ */
+static void msm_jpegdma_stop_streaming(struct vb2_queue *q)
+{
+ struct jpegdma_ctx *ctx = vb2_get_drv_priv(q);
+ unsigned long time;
+ int ret = 0;
+
+ atomic_set(&ctx->active, 0);
+
+ time = wait_for_completion_timeout(&ctx->completion,
+ msecs_to_jiffies(MSM_JPEGDMA_STREAM_OFF_TIMEOUT_MS));
+ if (!time) {
+ dev_err(ctx->jdma_device->dev, "Ctx wait timeout\n");
+ ret = -ETIME;
+ }
+
+ if (ctx->jdma_device->ref_count > 0)
+ msm_jpegdma_hw_put(ctx->jdma_device);
+}
+
+/* Videobuf2 queue callbacks. */
+static struct vb2_ops msm_jpegdma_vb2_q_ops = {
+ .queue_setup = msm_jpegdma_queue_setup,
+ .buf_queue = msm_jpegdma_buf_queue,
+ .start_streaming = msm_jpegdma_start_streaming,
+ .stop_streaming = msm_jpegdma_stop_streaming,
+};
+
+/*
+ * msm_jpegdma_get_userptr - Map and get buffer handler for user pointer buffer.
+ * @alloc_ctx: Contexts allocated in buf_setup.
+ * @vaddr: Virtual addr passed from userpsace (in our case ion fd)
+ * @size: Size of the buffer
+ * @write: True if buffer will be used for writing the data.
+ */
+static void *msm_jpegdma_get_userptr(struct device *alloc_ctx,
+ unsigned long vaddr, unsigned long size,
+ enum dma_data_direction dma_dir)
+{
+ struct msm_jpegdma_device *dma = (void *)alloc_ctx;
+ struct msm_jpegdma_buf_handle *buf;
+ struct msm_jpeg_dma_buff __user *up_buff;
+ struct msm_jpeg_dma_buff kp_buff;
+ int ret;
+
+ msm_jpegdma_cast_long_to_buff_ptr(vaddr, &up_buff);
+
+ if (!access_ok(VERIFY_READ, up_buff,
+ sizeof(struct msm_jpeg_dma_buff)) ||
+ get_user(kp_buff.fd, &up_buff->fd)) {
+ dev_err(dma->dev, "Error getting user data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (!access_ok(VERIFY_WRITE, up_buff,
+ sizeof(struct msm_jpeg_dma_buff)) ||
+ put_user(kp_buff.fd, &up_buff->fd)) {
+ dev_err(dma->dev, "Error putting user data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ ret = msm_jpegdma_hw_map_buffer(dma, kp_buff.fd, buf);
+ if (ret < 0 || buf->size < size)
+ goto error;
+
+ return buf;
+error:
+ kzfree(buf);
+ return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * msm_jpegdma_put_userptr - Unmap and free buffer handler.
+ * @buf_priv: Buffer handler allocated get_userptr callback.
+ */
+static void msm_jpegdma_put_userptr(void *buf_priv)
+{
+ if (IS_ERR_OR_NULL(buf_priv))
+ return;
+
+ msm_jpegdma_hw_unmap_buffer(buf_priv);
+
+ kzfree(buf_priv);
+}
+
+/* Videobuf2 memory callbacks. */
+static struct vb2_mem_ops msm_jpegdma_vb2_mem_ops = {
+ .get_userptr = msm_jpegdma_get_userptr,
+ .put_userptr = msm_jpegdma_put_userptr,
+};
+
+/*
+ * msm_jpegdma_queue_init - m2m_ops queue_setup callback.
+ * @priv: Pointer to jpegdma ctx.
+ * @src_vq: vb2 source queue.
+ * @dst_vq: vb2 destination queue.
+ */
+static int msm_jpegdma_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct jpegdma_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->mem_ops = &msm_jpegdma_vb2_mem_ops;
+ src_vq->ops = &msm_jpegdma_vb2_q_ops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret) {
+ dev_err(ctx->jdma_device->dev, "Can not init src queue\n");
+ return ret;
+ }
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->mem_ops = &msm_jpegdma_vb2_mem_ops;
+ dst_vq->ops = &msm_jpegdma_vb2_q_ops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ dev_err(ctx->jdma_device->dev, "Can not init dst queue\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_open - Fd device open method.
+ * @file: Pointer to file struct.
+ */
+static int msm_jpegdma_open(struct file *file)
+{
+ struct msm_jpegdma_device *device = video_drvdata(file);
+ struct video_device *video = video_devdata(file);
+ struct jpegdma_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mutex_init(&ctx->lock);
+ ctx->jdma_device = device;
+ dev_dbg(ctx->jdma_device->dev, "Jpeg v4l2 dma open\n");
+ /* Set ctx defaults */
+ ctx->timeperframe.numerator = 1;
+ ctx->timeperframe.denominator = MSM_JPEGDMA_DEFAULT_FPS;
+ atomic_set(&ctx->active, 0);
+
+ v4l2_fh_init(&ctx->fh, video);
+
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(device->m2m_dev,
+ ctx, msm_jpegdma_queue_init);
+ if (IS_ERR_OR_NULL(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_m2m_init;
+ }
+ ret = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_SVS_VOTE);
+ if (ret < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ goto ahb_vote_fail;
+ }
+ init_completion(&ctx->completion);
+ complete_all(&ctx->completion);
+ dev_dbg(ctx->jdma_device->dev, "Jpeg v4l2 dma open success\n");
+
+ return 0;
+
+ahb_vote_fail:
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+error_m2m_init:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_release - Fd device release method.
+ * @file: Pointer to file struct.
+ */
+static int msm_jpegdma_release(struct file *file)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(file->private_data);
+
+ /* release all the resources */
+ if (ctx->jdma_device->ref_count > 0)
+ msm_jpegdma_hw_put(ctx->jdma_device);
+
+ atomic_set(&ctx->active, 0);
+ complete_all(&ctx->completion);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_JPEG,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_poll - Fd device pool method.
+ * @file: Pointer to file struct.
+ * @wait: Pointer to pool table struct.
+ */
+static unsigned int msm_jpegdma_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(file->private_data);
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+/* Dma device file operations callbacks */
+static const struct v4l2_file_operations fd_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_jpegdma_open,
+ .release = msm_jpegdma_release,
+ .poll = msm_jpegdma_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/*
+ * msm_jpegdma_querycap - V4l2 ioctl query capability handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @cap: Pointer to v4l2_capability struct need to be filled.
+ */
+static int msm_jpegdma_querycap(struct file *file,
+ void *fh, struct v4l2_capability *cap)
+{
+ cap->bus_info[0] = 0;
+ strlcpy(cap->driver, MSM_JPEGDMA_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MSM_JPEGDMA_DRV_NAME, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_enum_fmt_vid_cap - V4l2 ioctl enumerate output format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_fmtdesc struct need to be filled.
+ */
+static int msm_jpegdma_enum_fmt_vid_cap(struct file *file,
+ void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_enum_fmt_vid_out - V4l2 ioctl enumerate capture format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_fmtdesc struct need to be filled.
+ */
+static int msm_jpegdma_enum_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_g_fmt_cap - V4l2 ioctl get capture format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct need to be filled.
+ */
+static int msm_jpegdma_g_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ *f = ctx->format_cap;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_g_fmt_out - V4l2 ioctl get output format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct need to be filled.
+ */
+static int msm_jpegdma_g_fmt_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ *f = ctx->format_out;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_try_fmt_vid_cap - V4l2 ioctl try capture format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_jpegdma_try_fmt_vid_cap(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ msm_jpegdma_align_format(f, ctx->format_idx);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_try_fmt_vid_out - V4l2 ioctl try output format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_jpegdma_try_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ msm_jpegdma_align_format(f, ctx->format_idx);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_s_fmt_vid_cap - V4l2 ioctl set capture format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_jpegdma_s_fmt_vid_cap(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ int ret;
+
+ ret = msm_jpegdma_get_format_idx(ctx, f);
+ if (ret < 0)
+ return -EINVAL;
+
+ ctx->format_idx = ret;
+
+ msm_jpegdma_align_format(f, ctx->format_idx);
+
+ /* Initialize crop with output height */
+ ctx->crop.top = 0;
+ ctx->crop.left = 0;
+ ctx->crop.width = ctx->format_out.fmt.pix.width;
+ ctx->crop.height = ctx->format_out.fmt.pix.height;
+
+ ctx->format_cap = *f;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_s_fmt_vid_out - V4l2 ioctl set output format handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @f: Pointer to v4l2_format struct.
+ */
+static int msm_jpegdma_s_fmt_vid_out(struct file *file,
+ void *fh, struct v4l2_format *f)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ int ret;
+
+ ret = msm_jpegdma_get_format_idx(ctx, f);
+ if (ret < 0)
+ return -EINVAL;
+
+ ctx->format_idx = ret;
+
+ msm_jpegdma_align_format(f, ctx->format_idx);
+
+ /* Initialize crop */
+ ctx->crop.top = 0;
+ ctx->crop.left = 0;
+ ctx->crop.width = f->fmt.pix.width;
+ ctx->crop.height = f->fmt.pix.height;
+
+ ctx->format_out = *f;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_reqbufs - V4l2 ioctl request buffers handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @req: Pointer to v4l2_requestbuffer struct.
+ */
+static int msm_jpegdma_reqbufs(struct file *file,
+ void *fh, struct v4l2_requestbuffers *req)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, req);
+}
+
+/*
+ * msm_jpegdma_qbuf - V4l2 ioctl queue buffer handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf: Pointer to v4l2_buffer struct.
+ */
+static int msm_jpegdma_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ struct msm_jpeg_dma_buff __user *up_buff;
+ struct msm_jpeg_dma_buff kp_buff;
+ int ret;
+
+ msm_jpegdma_cast_long_to_buff_ptr(buf->m.userptr, &up_buff);
+ mutex_lock(&ctx->lock);
+ if (!access_ok(VERIFY_READ, up_buff,
+ sizeof(struct msm_jpeg_dma_buff)) ||
+ get_user(kp_buff.fd, &up_buff->fd) ||
+ get_user(kp_buff.offset, &up_buff->offset)) {
+ dev_err(ctx->jdma_device->dev, "Error getting user data\n");
+ mutex_unlock(&ctx->lock);
+ return -EFAULT;
+ }
+
+ if (!access_ok(VERIFY_WRITE, up_buff,
+ sizeof(struct msm_jpeg_dma_buff)) ||
+ put_user(kp_buff.fd, &up_buff->fd) ||
+ put_user(kp_buff.offset, &up_buff->offset)) {
+ dev_err(ctx->jdma_device->dev, "Error putting user data\n");
+ mutex_unlock(&ctx->lock);
+ return -EFAULT;
+ }
+
+ switch (buf->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ ctx->in_offset = kp_buff.offset;
+ dev_dbg(ctx->jdma_device->dev, "input buf offset %d\n",
+ ctx->in_offset);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ ctx->out_offset = kp_buff.offset;
+ dev_dbg(ctx->jdma_device->dev, "output buf offset %d\n",
+ ctx->out_offset);
+ break;
+ }
+
+ if (atomic_read(&ctx->active))
+ ret = msm_jpegdma_update_hw_config(ctx);
+
+ ret = v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+ if (ret < 0)
+ dev_err(ctx->jdma_device->dev, "QBuf fail\n");
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_dqbuf - V4l2 ioctl dequeue buffer handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf: Pointer to v4l2_buffer struct.
+ */
+static int msm_jpegdma_dqbuf(struct file *file,
+ void *fh, struct v4l2_buffer *buf)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+/*
+ * msm_jpegdma_streamon - V4l2 ioctl stream on handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf_type: V4l2 buffer type.
+ */
+static int msm_jpegdma_streamon(struct file *file,
+ void *fh, enum v4l2_buf_type buf_type)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ int ret;
+
+ if (!msm_jpegdma_config_ok(ctx))
+ return -EINVAL;
+
+ ret = v4l2_m2m_streamon(file, ctx->m2m_ctx, buf_type);
+ if (ret < 0)
+ dev_err(ctx->jdma_device->dev, "Stream on fail\n");
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_streamoff - V4l2 ioctl stream off handler.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @buf_type: V4l2 buffer type.
+ */
+static int msm_jpegdma_streamoff(struct file *file,
+ void *fh, enum v4l2_buf_type buf_type)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ int ret;
+
+ ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, buf_type);
+ if (ret < 0)
+ dev_err(ctx->jdma_device->dev, "Stream off fails\n");
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_cropcap - V4l2 ioctl crop capabilities.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @a: Pointer to v4l2_cropcap struct need to be set.
+ */
+static int msm_jpegdma_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *a)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ struct v4l2_format *format;
+
+ switch (a->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ format = &ctx->format_out;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ format = &ctx->format_cap;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->bounds.top = 0;
+ a->bounds.left = 0;
+ a->bounds.width = format->fmt.pix.width;
+ a->bounds.height = format->fmt.pix.height;
+
+ a->defrect = ctx->crop;
+
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_g_crop - V4l2 ioctl get crop.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @crop: Pointer to v4l2_crop struct need to be set.
+ */
+static int msm_jpegdma_g_crop(struct file *file, void *fh,
+ struct v4l2_crop *crop)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ switch (crop->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ crop->c = ctx->crop;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ crop->c.left = 0;
+ crop->c.top = 0;
+ crop->c.width = ctx->format_cap.fmt.pix.width;
+ crop->c.height = ctx->format_cap.fmt.pix.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * msm_jpegdma_s_crop - V4l2 ioctl set crop.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @crop: Pointer to v4l2_crop struct need to be set.
+ */
+static int msm_jpegdma_s_crop(struct file *file, void *fh,
+ const struct v4l2_crop *crop)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ int ret = 0;
+
+ /* Crop is supported only for input buffers */
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (crop->c.left < 0 || crop->c.top < 0 ||
+ crop->c.height == 0 || crop->c.width == 0)
+ return -EINVAL;
+
+ /* Upscale is not supported */
+ if (crop->c.width < ctx->format_cap.fmt.pix.width)
+ return -EINVAL;
+
+ if (crop->c.height < ctx->format_cap.fmt.pix.height)
+ return -EINVAL;
+
+ if (crop->c.width + crop->c.left > ctx->format_out.fmt.pix.width)
+ return -EINVAL;
+
+ if (crop->c.height + crop->c.top > ctx->format_out.fmt.pix.height)
+ return -EINVAL;
+
+ if (crop->c.width % formats[ctx->format_idx].h_align)
+ return -EINVAL;
+
+ if (crop->c.height % formats[ctx->format_idx].v_align)
+ return -EINVAL;
+
+ mutex_lock(&ctx->lock);
+ ctx->crop = crop->c;
+ if (atomic_read(&ctx->active))
+ ret = msm_jpegdma_update_hw_config(ctx);
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_g_crop - V4l2 ioctl get parm.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @a: Pointer to v4l2_streamparm struct need to be filled.
+ */
+static int msm_jpegdma_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ /* Get param is supported only for input buffers */
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ a->parm.output.capability = 0;
+ a->parm.output.extendedmode = 0;
+ a->parm.output.outputmode = 0;
+ a->parm.output.writebuffers = 0;
+ a->parm.output.timeperframe = ctx->timeperframe;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_s_crop - V4l2 ioctl set parm.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @a: Pointer to v4l2_streamparm struct need to be set.
+ */
+static int msm_jpegdma_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+ /* Set param is supported only for input buffers */
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (!a->parm.output.timeperframe.numerator ||
+ !a->parm.output.timeperframe.denominator)
+ return -EINVAL;
+
+ /* Frame rate is not supported during streaming */
+ if (atomic_read(&ctx->active))
+ return -EINVAL;
+
+ ctx->timeperframe = a->parm.output.timeperframe;
+ return 0;
+}
+
+/*
+ * msm_fd_g_ctrl - V4l2 ioctl get control.
+ * @file: Pointer to file struct.
+ * @fh: V4l2 File handle.
+ * @sub: Pointer to v4l2_control struct need to be filled.
+ */
+static int msm_jpegdma_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh);
+
+ switch (a->id) {
+ case V4L2_CID_JPEG_DMA_MAX_DOWN_SCALE:
+ a->value = msm_jpegdma_hw_get_max_downscale(ctx->jdma_device);
+ if (a->value < 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* V4l2 ioctl handlers */
+static const struct v4l2_ioctl_ops fd_ioctl_ops = {
+ .vidioc_querycap = msm_jpegdma_querycap,
+ .vidioc_enum_fmt_vid_out = msm_jpegdma_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_cap = msm_jpegdma_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out = msm_jpegdma_g_fmt_out,
+ .vidioc_g_fmt_vid_cap = msm_jpegdma_g_fmt_cap,
+ .vidioc_try_fmt_vid_out = msm_jpegdma_try_fmt_vid_out,
+ .vidioc_try_fmt_vid_cap = msm_jpegdma_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = msm_jpegdma_s_fmt_vid_out,
+ .vidioc_s_fmt_vid_cap = msm_jpegdma_s_fmt_vid_cap,
+ .vidioc_reqbufs = msm_jpegdma_reqbufs,
+ .vidioc_qbuf = msm_jpegdma_qbuf,
+ .vidioc_dqbuf = msm_jpegdma_dqbuf,
+ .vidioc_streamon = msm_jpegdma_streamon,
+ .vidioc_streamoff = msm_jpegdma_streamoff,
+ .vidioc_cropcap = msm_jpegdma_cropcap,
+ .vidioc_g_crop = msm_jpegdma_g_crop,
+ .vidioc_s_crop = msm_jpegdma_s_crop,
+ .vidioc_g_parm = msm_jpegdma_g_parm,
+ .vidioc_s_parm = msm_jpegdma_s_parm,
+ .vidioc_g_ctrl = msm_jpegdma_g_ctrl,
+};
+
+/*
+ * msm_jpegdma_process_buffers - Start dma processing.
+ * @ctx: Pointer dma context.
+ * @src_buf: Pointer to Vb2 source buffer.
+ * @dst_buf: Pointer to Vb2 destination buffer.
+ */
+static void msm_jpegdma_process_buffers(struct jpegdma_ctx *ctx,
+ struct vb2_v4l2_buffer *src_buf, struct vb2_v4l2_buffer *dst_buf)
+{
+ struct msm_jpegdma_buf_handle *buf_handle;
+ struct msm_jpegdma_addr addr;
+ int plane_idx;
+ int config_idx;
+
+ buf_handle = dst_buf->vb2_buf.planes[0].mem_priv;
+ addr.out_addr = buf_handle->addr;
+
+ buf_handle = src_buf->vb2_buf.planes[0].mem_priv;
+ addr.in_addr = buf_handle->addr;
+
+ plane_idx = ctx->plane_idx;
+ config_idx = ctx->config_idx;
+ msm_jpegdma_hw_start(ctx->jdma_device, &addr,
+ &ctx->plane_config[config_idx].plane[plane_idx],
+ &ctx->plane_config[config_idx].speed);
+}
+
+/*
+ * msm_jpegdma_device_run - Dma device run.
+ * @priv: Pointer dma context.
+ */
+static void msm_jpegdma_device_run(void *priv)
+{
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct jpegdma_ctx *ctx = priv;
+
+ dev_dbg(ctx->jdma_device->dev, "Jpeg v4l2 dma device run E\n");
+
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ if (src_buf == NULL || dst_buf == NULL) {
+ dev_err(ctx->jdma_device->dev, "Error, buffer list empty\n");
+ return;
+ }
+
+ if (ctx->pending_config) {
+ msm_jpegdma_schedule_next_config(ctx);
+ ctx->pending_config = 0;
+ }
+
+ msm_jpegdma_process_buffers(ctx, src_buf, dst_buf);
+ dev_dbg(ctx->jdma_device->dev, "Jpeg v4l2 dma device run X\n");
+}
+
+/*
+ * msm_jpegdma_job_abort - Dma abort job.
+ * @priv: Pointer dma context.
+ */
+static void msm_jpegdma_job_abort(void *priv)
+{
+ struct jpegdma_ctx *ctx = priv;
+
+ msm_jpegdma_hw_abort(ctx->jdma_device);
+ v4l2_m2m_job_finish(ctx->jdma_device->m2m_dev, ctx->m2m_ctx);
+}
+
+/*
+ * msm_jpegdma_job_ready - Dma check if job is ready
+ * @priv: Pointer dma context.
+ */
+static int msm_jpegdma_job_ready(void *priv)
+{
+ struct jpegdma_ctx *ctx = priv;
+
+ if (atomic_read(&ctx->active)) {
+ init_completion(&ctx->completion);
+ return 1;
+ }
+ return 0;
+}
+
+/* V4l2 mem2mem handlers */
+static struct v4l2_m2m_ops msm_jpegdma_m2m_ops = {
+ .device_run = msm_jpegdma_device_run,
+ .job_abort = msm_jpegdma_job_abort,
+ .job_ready = msm_jpegdma_job_ready,
+};
+
+/*
+ * msm_jpegdma_isr_processing_done - Invoked by dma_hw when processing is done.
+ * @dma: Pointer dma device.
+ */
+void msm_jpegdma_isr_processing_done(struct msm_jpegdma_device *dma)
+{
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct jpegdma_ctx *ctx;
+
+ mutex_lock(&dma->lock);
+
+ ctx = v4l2_m2m_get_curr_priv(dma->m2m_dev);
+ if (ctx) {
+ mutex_lock(&ctx->lock);
+ ctx->plane_idx++;
+ if (ctx->plane_idx >= formats[ctx->format_idx].num_planes) {
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ if (src_buf == NULL || dst_buf == NULL) {
+ dev_err(ctx->jdma_device->dev, "Error, buffer list empty\n");
+ mutex_unlock(&ctx->lock);
+ mutex_unlock(&dma->lock);
+ return;
+ }
+ complete_all(&ctx->completion);
+ ctx->plane_idx = 0;
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(ctx->jdma_device->m2m_dev,
+ ctx->m2m_ctx);
+ } else {
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ if (src_buf == NULL || dst_buf == NULL) {
+ dev_err(ctx->jdma_device->dev, "Error, buffer list empty\n");
+ mutex_unlock(&ctx->lock);
+ mutex_unlock(&dma->lock);
+ return;
+ }
+ msm_jpegdma_process_buffers(ctx, src_buf, dst_buf);
+ }
+ mutex_unlock(&ctx->lock);
+ }
+ mutex_unlock(&dma->lock);
+}
+
+/*
+ * jpegdma_probe - Dma device probe method.
+ * @pdev: Pointer Dma platform device.
+ */
+static int jpegdma_probe(struct platform_device *pdev)
+{
+ struct msm_jpegdma_device *jpegdma;
+ int ret;
+ int i;
+
+ dev_dbg(&pdev->dev, "jpeg v4l2 DMA probed\n");
+ /* Jpeg dma device struct */
+ jpegdma = kzalloc(sizeof(struct msm_jpegdma_device), GFP_KERNEL);
+ if (!jpegdma)
+ return -ENOMEM;
+
+ mutex_init(&jpegdma->lock);
+
+ init_completion(&jpegdma->hw_reset_completion);
+ init_completion(&jpegdma->hw_halt_completion);
+ jpegdma->dev = &pdev->dev;
+ jpegdma->pdev = pdev;
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node, "cell-index",
+ &pdev->id);
+
+ /* Get resources */
+ ret = msm_jpegdma_hw_get_mem_resources(pdev, jpegdma);
+ if (ret < 0)
+ goto error_mem_resources;
+
+ /* get all the regulators */
+ ret = msm_camera_get_regulator_info(pdev, &jpegdma->dma_vdd,
+ &jpegdma->num_reg);
+ if (ret < 0)
+ goto error_get_regulators;
+
+ /* get all the clocks */
+ ret = msm_camera_get_clk_info(pdev, &jpegdma->jpeg_clk_info,
+ &jpegdma->clk, &jpegdma->num_clk);
+ if (ret < 0)
+ goto error_get_clocks;
+
+ /*set memcore and mem periphery logic flags to 0*/
+ for (i = 0; i < jpegdma->num_clk; i++) {
+ if ((strcmp(jpegdma->jpeg_clk_info[i].clk_name,
+ "core_clk") == 0) ||
+ (strcmp(jpegdma->jpeg_clk_info[i].clk_name,
+ "mmss_camss_jpeg_axi_clk") == 0)) {
+ msm_camera_set_clk_flags(jpegdma->clk[i],
+ CLKFLAG_NORETAIN_MEM);
+ msm_camera_set_clk_flags(jpegdma->clk[i],
+ CLKFLAG_NORETAIN_PERIPH);
+ }
+ }
+
+ ret = msm_jpegdma_hw_get_qos(jpegdma);
+ if (ret < 0)
+ goto error_qos_get;
+
+ ret = msm_jpegdma_hw_get_vbif(jpegdma);
+ if (ret < 0)
+ goto error_vbif_get;
+
+ ret = msm_jpegdma_hw_get_prefetch(jpegdma);
+ if (ret < 0)
+ goto error_prefetch_get;
+
+ /* get the irq resource */
+ jpegdma->irq = msm_camera_get_irq(pdev, "jpeg");
+ if (!jpegdma->irq)
+ goto error_hw_get_irq;
+
+ switch (pdev->id) {
+ case 3:
+ jpegdma->bus_client = CAM_BUS_CLIENT_JPEG_DMA;
+ break;
+ default:
+ pr_err("%s: invalid cell id :%d\n",
+ __func__, pdev->id);
+ goto error_reg_bus;
+ }
+
+ /* register bus client */
+ ret = msm_camera_register_bus_client(pdev,
+ jpegdma->bus_client);
+ if (ret < 0) {
+ pr_err("Fail to register bus client\n");
+ ret = -EINVAL;
+ goto error_reg_bus;
+ }
+
+ ret = msm_jpegdma_hw_get_capabilities(jpegdma);
+ if (ret < 0)
+ goto error_hw_get_cap;
+
+ /* mem2mem device */
+ jpegdma->m2m_dev = v4l2_m2m_init(&msm_jpegdma_m2m_ops);
+ if (IS_ERR(jpegdma->m2m_dev)) {
+ dev_err(&pdev->dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(jpegdma->m2m_dev);
+ goto error_m2m_init;
+ }
+
+ /* v4l2 device */
+ ret = v4l2_device_register(&pdev->dev, &jpegdma->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ goto error_v4l2_register;
+ }
+
+ jpegdma->video.fops = &fd_fops;
+ jpegdma->video.ioctl_ops = &fd_ioctl_ops;
+ jpegdma->video.minor = -1;
+ jpegdma->video.release = video_device_release;
+ jpegdma->video.v4l2_dev = &jpegdma->v4l2_dev;
+ jpegdma->video.vfl_dir = VFL_DIR_M2M;
+ jpegdma->video.vfl_type = VFL_TYPE_GRABBER;
+ strlcpy(jpegdma->video.name, MSM_JPEGDMA_DRV_NAME,
+ sizeof(jpegdma->video.name));
+
+ ret = video_register_device(&jpegdma->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register video device\n");
+ goto error_video_register;
+ }
+
+ video_set_drvdata(&jpegdma->video, jpegdma);
+
+ platform_set_drvdata(pdev, jpegdma);
+
+ dev_dbg(&pdev->dev, "jpeg v4l2 DMA probe success\n");
+ return 0;
+
+error_video_register:
+ v4l2_device_unregister(&jpegdma->v4l2_dev);
+error_v4l2_register:
+ v4l2_m2m_release(jpegdma->m2m_dev);
+error_m2m_init:
+error_hw_get_cap:
+ msm_camera_unregister_bus_client(jpegdma->bus_client);
+error_reg_bus:
+error_hw_get_irq:
+ msm_jpegdma_hw_put_prefetch(jpegdma);
+error_prefetch_get:
+ msm_jpegdma_hw_put_vbif(jpegdma);
+error_vbif_get:
+ msm_jpegdma_hw_put_qos(jpegdma);
+error_qos_get:
+ msm_camera_put_clk_info(pdev, &jpegdma->jpeg_clk_info,
+ &jpegdma->clk, jpegdma->num_clk);
+error_get_clocks:
+ msm_camera_put_regulators(pdev, &jpegdma->dma_vdd,
+ jpegdma->num_reg);
+error_get_regulators:
+ msm_jpegdma_hw_release_mem_resources(jpegdma);
+error_mem_resources:
+ kfree(jpegdma);
+ return ret;
+}
+
+/*
+ * jpegdma_device_remove - Jpegdma device remove method.
+ * @pdev: Pointer jpegdma platform device.
+ */
+static int jpegdma_device_remove(struct platform_device *pdev)
+{
+ struct msm_jpegdma_device *dma;
+
+ dma = platform_get_drvdata(pdev);
+ if (dma == NULL) {
+ dev_err(&pdev->dev, "Can not get jpeg dma drvdata\n");
+ return 0;
+ }
+ video_unregister_device(&dma->video);
+ v4l2_device_unregister(&dma->v4l2_dev);
+ v4l2_m2m_release(dma->m2m_dev);
+ /* unregister bus client */
+ msm_camera_unregister_bus_client(dma->bus_client);
+ /* release all the regulators */
+ msm_camera_put_regulators(dma->pdev, &dma->dma_vdd,
+ dma->num_reg);
+ /* release all the clocks */
+ msm_camera_put_clk_info(dma->pdev, &dma->jpeg_clk_info,
+ &dma->clk, dma->num_clk);
+ msm_jpegdma_hw_release_mem_resources(dma);
+ kfree(dma);
+
+ return 0;
+}
+
+/* Device tree match struct */
+static const struct of_device_id msm_jpegdma_dt_match[] = {
+ {.compatible = "qcom,jpegdma"},
+ {}
+};
+
+/* Jpeg dma platform driver definition */
+static struct platform_driver jpegdma_driver = {
+ .probe = jpegdma_probe,
+ .remove = jpegdma_device_remove,
+ .driver = {
+ .name = MSM_JPEGDMA_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_jpegdma_dt_match,
+ },
+};
+
+static int __init msm_jpegdma_init_module(void)
+{
+ return platform_driver_register(&jpegdma_driver);
+}
+
+static void __exit msm_jpegdma_exit_module(void)
+{
+ platform_driver_unregister(&jpegdma_driver);
+}
+
+module_init(msm_jpegdma_init_module);
+module_exit(msm_jpegdma_exit_module);
+MODULE_DESCRIPTION("MSM JPEG DMA driver");
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h
new file mode 100644
index 0000000..50b7941
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h
@@ -0,0 +1,378 @@
+/* Copyright (c) 2015-2016, 2018, 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_JPEG_DMA_DEV_H__
+#define __MSM_JPEG_DMA_DEV_H__
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/msm-bus.h>
+#include "cam_soc_api.h"
+
+/* Max number of clocks defined in device tree */
+#define MSM_JPEGDMA_MAX_CLK 10
+/* Core clock index */
+#define MSM_JPEGDMA_CORE_CLK "core_clk"
+/* Max number of regulators defined in device tree */
+#define MSM_JPEGDMA_MAX_REGULATOR_NUM 3
+/* Max number of planes supported */
+#define MSM_JPEGDMA_MAX_PLANES 3
+/* Max number of hw pipes supported */
+#define MSM_JPEGDMA_MAX_PIPES 2
+/* Max number of hw configurations supported */
+#define MSM_JPEGDMA_MAX_CONFIGS 2
+/* Dma default fps */
+#define MSM_JPEGDMA_DEFAULT_FPS 30
+
+/* Dma input output size limitations */
+#define MSM_JPEGDMA_MAX_WIDTH 65536
+#define MSM_JPEGDMA_MIN_WIDTH 8
+#define MSM_JPEGDMA_MAX_HEIGHT 65536
+#define MSM_JPEGDMA_MIN_HEIGHT 8
+#define MSM_JPEGDMA_STRIDE_ALIGN 8
+
+/*
+ * enum msm_jpegdma_plane_type - Dma format.
+ * @JPEGDMA_PLANE_TYPE_Y: Y plane type.
+ * @JPEGDMA_PLANE_TYPE_CR: Chroma CB plane.
+ * @JPEGDMA_PLANE_TYPE_CB: Chroma CR plane.
+ * @JPEGDMA_PLANE_TYPE_CBCR: Interlevaed CbCr plane.
+ */
+enum msm_jpegdma_plane_type {
+ JPEGDMA_PLANE_TYPE_Y,
+ JPEGDMA_PLANE_TYPE_CR,
+ JPEGDMA_PLANE_TYPE_CB,
+ JPEGDMA_PLANE_TYPE_CBCR,
+};
+
+/*
+ * struct msm_jpegdma_format - Dma format.
+ * @name: Format name.
+ * @fourcc: v4l2 fourcc code.
+ * @depth: Number of bits per pix.
+ * @num_planes: number of planes.
+ * @colplane_h: Color plane horizontal subsample.
+ * @colplane_v: Color plane vertical subsample.
+ * @h_align: Horizontal align.
+ * @v_align: Vertical align.
+ * @planes: Array with plane types.
+ */
+struct msm_jpegdma_format {
+ char *name;
+ u32 fourcc;
+ int depth;
+ int num_planes;
+ int colplane_h;
+ int colplane_v;
+ int h_align;
+ int v_align;
+ enum msm_jpegdma_plane_type planes[MSM_JPEGDMA_MAX_PLANES];
+};
+
+/*
+ * struct msm_jpegdma_size - Dma size.
+ * @top: Top position.
+ * @left: Left position
+ * @width: Width
+ * @height: height.
+ * @scanline: Number of lines per plane.
+ * @stride: Stride bytes per line.
+ */
+struct msm_jpegdma_size {
+ unsigned int top;
+ unsigned int left;
+ unsigned int width;
+ unsigned int height;
+ unsigned int scanline;
+ unsigned int stride;
+};
+
+/*
+ * struct msm_jpegdma_size_config - Dma engine size configuration.
+ * @in_size: Input size.
+ * @out_size: Output size.
+ * @format: Format.
+ * @fps: Requested frames per second.
+ */
+struct msm_jpegdma_size_config {
+ struct msm_jpegdma_size in_size;
+ struct msm_jpegdma_size out_size;
+ struct msm_jpegdma_format format;
+ unsigned int fps;
+ unsigned int in_offset;
+ unsigned int out_offset;
+};
+
+/*
+ * struct msm_jpegdma_block - Dma hw block.
+ * @div: Block divider.
+ * @width: Block width.
+ * @reg_val: Block register value.
+ */
+struct msm_jpegdma_block {
+ unsigned int div;
+ unsigned int width;
+ unsigned int reg_val;
+};
+
+/*
+ * struct msm_jpegdma_block_config - Dma hw block configuration.
+ * @block: Block settings.
+ * @blocks_per_row: Blocks per row.
+ * @blocks_per_col: Blocks per column.
+ * @h_step: Horizontal step value
+ * @v_step: Vertical step value
+ * @h_step_last: Last horizontal step.
+ * @v_step_last: Last vertical step.
+ */
+struct msm_jpegdma_block_config {
+ struct msm_jpegdma_block block;
+ unsigned int blocks_per_row;
+ unsigned int blocks_per_col;
+ unsigned int h_step;
+ unsigned int v_step;
+ unsigned int h_step_last;
+ unsigned int v_step_last;
+};
+
+/*
+ * msm_jpegdma_scale - Dma hw scale configuration.
+ * @enable: Scale enable.
+ * @hor_scale: Horizontal scale factor in Q21 format.
+ * @ver_scale: Vertical scale factor in Q21 format.
+ */
+struct msm_jpegdma_scale {
+ int enable;
+ unsigned int hor_scale;
+ unsigned int ver_scale;
+};
+
+/*
+ * struct msm_jpegdma_config - Dma hw configuration.
+ * @size_cfg: Size configuration.
+ * @scale_cfg: Scale configuration
+ * @block_cfg: Block configuration.
+ * @phase: Starting phase.
+ * @in_offset: Input offset.
+ * @out_offset: Output offset.
+ */
+struct msm_jpegdma_config {
+ struct msm_jpegdma_size_config size_cfg;
+ struct msm_jpegdma_scale scale_cfg;
+ struct msm_jpegdma_block_config block_cfg;
+ unsigned int phase;
+ unsigned int in_offset;
+ unsigned int out_offset;
+};
+
+/*
+ * struct msm_jpegdma_plane_config - Contain input output address.
+ * @bus_ab: Bus average bandwidth.
+ * @bus_ib: Bus instantaneous bandwidth.
+ * @core_clock: Core clock freq.
+ */
+struct msm_jpegdma_speed {
+ u64 bus_ab;
+ u64 bus_ib;
+ u64 core_clock;
+};
+
+/*
+ * struct msm_jpegdma_plane_config - Contain input output address.
+ * @active_pipes: Number of active pipes.
+ * @config: Plane configurations.
+ * @type: Plane type.
+ */
+struct msm_jpegdma_plane {
+ unsigned int active_pipes;
+ struct msm_jpegdma_config config[MSM_JPEGDMA_MAX_PIPES];
+ enum msm_jpegdma_plane_type type;
+};
+
+/*
+ * struct msm_jpegdma_plane_config - Contain input output address.
+ * @num_planes: Number of planes.
+ * @plane: Plane configuration.
+ * @speed: Processing speed.
+ */
+struct msm_jpegdma_plane_config {
+ unsigned int num_planes;
+ struct msm_jpegdma_plane plane[MSM_JPEGDMA_MAX_PLANES];
+ struct msm_jpegdma_speed speed;
+};
+
+/*
+ * struct msm_jpegdma_addr - Contain input output address.
+ * @in_addr: Input dma address.
+ * @out_addr: Output dma address.
+ */
+struct msm_jpegdma_addr {
+ u32 in_addr;
+ u32 out_addr;
+};
+
+/*
+ * struct msm_jpegdma_buf_handle - Structure contain dma buffer information.
+ * @fd: ion dma from which this buffer is imported.
+ * @dma: Pointer to jpeg dma device.
+ * @size: Size of the buffer.
+ * @addr: Adders of dma mmu mapped buffer. This address should be set to dma hw.
+ */
+struct msm_jpegdma_buf_handle {
+ int fd;
+ struct msm_jpegdma_device *dma;
+ unsigned long size;
+ ion_phys_addr_t addr;
+};
+
+/*
+ * @jpegdma_ctx - Structure contains per open file handle context.
+ * @lock: Lock protecting dma ctx.
+ * @jdma_device: Pointer to dma device.
+ * @active: Set if context is active.
+ * @completion: Context processing completion.
+ * @fh: V4l2 file handle.
+ * @m2m_ctx: Memory to memory context.
+ * @format_cap: Current capture format.
+ * @format_out: Current output format.
+ * @crop: Current crop.
+ * @timeperframe: Time per frame in seconds.
+ * @config_idx: Plane configuration active index.
+ * @plane_config: Array of plane configurations.
+ * @pending_config: Flag set if there is pending plane configuration.
+ * @plane_idx: Processing plane index.
+ * @format_idx: Current format index.
+ */
+struct jpegdma_ctx {
+ struct mutex lock;
+ struct msm_jpegdma_device *jdma_device;
+ atomic_t active;
+ struct completion completion;
+ struct v4l2_fh fh;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_format format_cap;
+ struct v4l2_format format_out;
+ struct v4l2_rect crop;
+ struct v4l2_fract timeperframe;
+ unsigned int in_offset;
+ unsigned int out_offset;
+
+ unsigned int config_idx;
+ struct msm_jpegdma_plane_config plane_config[MSM_JPEGDMA_MAX_CONFIGS];
+ unsigned int pending_config;
+
+ unsigned int plane_idx;
+ unsigned int format_idx;
+};
+
+/*
+ * struct jpegdma_reg_cfg - Registry values configuration
+ * @reg: Register offset.
+ * @val: Register value.
+ */
+struct jpegdma_reg_cfg {
+ unsigned int reg;
+ unsigned int val;
+};
+
+/*
+ * enum msm_jpegdma_mem_resources - jpegdma device iomem resources.
+ * @MSM_JPEGDMA_IOMEM_CORE: Index of jpegdma core registers.
+ * @MSM_JPEGDMA_IOMEM_VBIF: Index of jpegdma vbif registers.
+ * @MSM_JPEGDMA_IOMEM_LAST: Not valid.
+ */
+enum msm_jpegdma_mem_resources {
+ MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_IOMEM_VBIF,
+ MSM_JPEGDMA_IOMEM_LAST
+};
+
+/*
+ * struct msm_jpegdma_device - FD device structure.
+ * @lock: Lock protecting dma device.
+ * @ref_count: Device reference count.
+ * @irq_num: Face detection irq number.
+ * @res_mem: Array of memory resources used by Dma device.
+ * @iomem_base: Array of register mappings used by Dma device.
+ * @ioarea: Array of register ioarea used by Dma device.
+ * @vdd: Pointer to vdd regulator.
+ * @regulator_num: Number of regulators attached to the device.
+ * @clk_num: Number of clocks attached to the device.
+ * @clk: Array of clock resources used by dma device.
+ * @clk_rates: Array of clock rates.
+ * @vbif_regs_num: number of vbif regs.
+ * @vbif_regs: Array of vbif regs need to be set.
+ * @qos_regs_num: Number of qos regs .
+ * @qos_regs: Array of qos regs need to be set.
+ * @bus_client: Memory access bus client.
+ * @bus_vectors: Bus vector
+ * @bus_paths: Bus path.
+ * @bus_scale_data: Memory access bus scale data.
+ * @iommu_hndl: Dma device iommu handle.
+ * @iommu_attached_cnt: Iommu attached devices reference count.
+ * @iommu_dev: Pointer to Ion iommu device.
+ * @dev: Pointer to device struct.
+ * @v4l2_dev: V4l2 device.
+ * @video: Video device.
+ * @m2m_dev: Memory to memory device.
+ * @hw_num_pipes: Number of dma hw pipes.
+ * @active_clock_rate: Active clock rate index.
+ * @hw_reset_completion: Dma reset completion.
+ * @hw_halt_completion: Dma halt completion.
+ */
+struct msm_jpegdma_device {
+ struct mutex lock;
+ int ref_count;
+
+ int irq_num;
+ void __iomem *iomem_base[MSM_JPEGDMA_IOMEM_LAST];
+
+ struct resource *irq;
+ struct msm_cam_regulator *dma_vdd;
+ int num_reg;
+
+ struct clk **clk;
+ size_t num_clk;
+ struct msm_cam_clk_info *jpeg_clk_info;
+
+ unsigned int vbif_regs_num;
+ struct jpegdma_reg_cfg *vbif_regs;
+ unsigned int qos_regs_num;
+ struct jpegdma_reg_cfg *qos_regs;
+ unsigned int prefetch_regs_num;
+ struct jpegdma_reg_cfg *prefetch_regs;
+
+ enum cam_bus_client bus_client;
+ struct msm_bus_vectors bus_vectors;
+ struct msm_bus_paths bus_paths;
+ struct msm_bus_scale_pdata bus_scale_data;
+
+ int iommu_hndl;
+ unsigned int iommu_attached_cnt;
+
+ struct device *iommu_dev;
+ struct device *dev;
+ struct v4l2_device v4l2_dev;
+ struct video_device video;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ int hw_num_pipes;
+ struct completion hw_reset_completion;
+ struct completion hw_halt_completion;
+ u64 active_clock_rate;
+ struct platform_device *pdev;
+};
+
+void msm_jpegdma_isr_processing_done(struct msm_jpegdma_device *dma);
+
+#endif /* __MSM_JPEG_DMA_DEV_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
new file mode 100644
index 0000000..702be49
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
@@ -0,0 +1,1874 @@
+/* Copyright (c) 2015-2018, 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/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/iommu.h>
+#include <linux/msm_ion.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <media/videobuf2-core.h>
+
+#include "msm_camera_io_util.h"
+#include "cam_smmu_api.h"
+#include "msm_jpeg_dma_dev.h"
+#include "msm_jpeg_dma_hw.h"
+#include "msm_jpeg_dma_regs.h"
+
+/* Jpeg dma scale unity */
+#define MSM_JPEGDMA_SCALE_UNI (1 << 21)
+/* Jpeg dma bw numerator */
+#define MSM_JPEGDMA_BW_NUM 38
+/* Jpeg dma bw denominator */
+#define MSM_JPEGDMA_BW_DEN 10
+/* Jpeg bus client name */
+#define MSM_JPEGDMA_BUS_CLIENT_NAME "msm_jpeg_dma"
+/* Jpeg dma engine timeout in ms */
+#define MSM_JPEGDMA_TIMEOUT_MS 500
+/* Jpeg dma smmu name */
+#define MSM_JPEGDMA_SMMU_NAME "jpeg_dma"
+
+static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = {
+ {
+ .div = 0x3C0000,
+ .width = 256,
+ .reg_val = 4,
+ },
+ {
+ .div = 0x7C0000,
+ .width = 128,
+ .reg_val = 3,
+ },
+ {
+ .div = 0xFC0000,
+ .width = 64,
+ .reg_val = 2,
+ },
+ {
+ .div = 0x1FC0000,
+ .width = 32,
+ .reg_val = 1,
+ },
+ {
+ .div = 0x4000000,
+ .width = 16,
+ .reg_val = 0,
+ },
+};
+
+/*
+ * jpegdma_do_div - long division.
+ * @num: dividend
+ * @den: divisor
+ * returns quotient value.
+ */
+static inline long long jpegdma_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+/*
+ * msm_jpegdma_hw_read_reg - dma read from register.
+ * @dma: Pointer to dma device.
+ * @base_idx: dma memory resource index.
+ * @reg: Register addr need to be read from.
+ */
+static inline u32 msm_jpegdma_hw_read_reg(struct msm_jpegdma_device *dma,
+ enum msm_jpegdma_mem_resources base_idx, u32 reg)
+{
+ return msm_camera_io_r(dma->iomem_base[base_idx] + reg);
+}
+
+/*
+ * msm_jpegdma_hw_write_reg - dma write to register.
+ * @dma: Pointer to dma device.
+ * @base_idx: dma memory resource index.
+ * @reg: Register addr need to be read from.
+ * @value: Value to be written.
+ */
+static inline void msm_jpegdma_hw_write_reg(struct msm_jpegdma_device *dma,
+ enum msm_jpegdma_mem_resources base_idx, u32 reg, u32 value)
+{
+ pr_debug("%s:%d]%pK %08x\n", __func__, __LINE__,
+ dma->iomem_base[base_idx] + reg,
+ value);
+ msm_camera_io_w(value, dma->iomem_base[base_idx] + reg);
+}
+
+/*
+ * msm_jpegdma_hw_enable_irq - Enable dma interrupts.
+ * @dma: Pointer to dma device.
+ */
+static void msm_jpegdma_hw_enable_irq(struct msm_jpegdma_device *dma)
+{
+ u32 reg;
+
+ reg = MSM_JPEGDMA_IRQ_MASK_SESSION_DONE |
+ MSM_JPEGDMA_IRQ_MASK_AXI_HALT |
+ MSM_JPEGDMA_IRQ_MASK_RST_DONE;
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_IRQ_MASK_ADDR, reg);
+}
+
+/*
+ * msm_jpegdma_hw_disable_irq - Disable dma interrupts.
+ * @dma: Pointer to dma device.
+ */
+static void msm_jpegdma_hw_disable_irq(struct msm_jpegdma_device *dma)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_IRQ_MASK_ADDR, 0);
+}
+
+/*
+ * msm_jpegdma_hw_clear_irq - Clear dma interrupts.
+ * @dma: Pointer to dma device.
+ * @status: Status to clear.
+ */
+static void msm_jpegdma_hw_clear_irq(struct msm_jpegdma_device *dma,
+ u32 status)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_IRQ_CLEAR_ADDR, status);
+}
+
+/*
+ * msm_jpegdma_hw_get_irq_status - Get dma irq status
+ * @dma: Pointer to dma device.
+ */
+static u32 msm_jpegdma_hw_get_irq_status(struct msm_jpegdma_device *dma)
+{
+ return msm_jpegdma_hw_read_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_IRQ_STATUS);
+}
+
+/*
+ * msm_jpegdma_hw_get_num_pipes - Get number of dma pipes
+ * @dma: Pointer to dma device.
+ */
+static int msm_jpegdma_hw_get_num_pipes(struct msm_jpegdma_device *dma)
+{
+ int num_pipes;
+ u32 reg;
+
+ reg = msm_jpegdma_hw_read_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_HW_CAPABILITY);
+
+ num_pipes = (reg & MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_BMSK) >>
+ MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_SHFT;
+
+ return num_pipes;
+}
+
+/*
+ * msm_jpegdma_hw_get_clock_index - Get clock index by name
+ * @dma: Pointer to dma device.
+ * @clk_name: clock name.
+ */
+static int msm_jpegdma_hw_get_clock_index(struct msm_jpegdma_device *dma,
+ const char *clk_name)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < dma->num_clk; i++) {
+ if (!strcmp(clk_name, dma->jpeg_clk_info[i].clk_name))
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * msm_jpegdma_hw_reset - Reset jpeg dma core.
+ * @dma: Pointer to dma device.
+ */
+static int msm_jpegdma_hw_reset(struct msm_jpegdma_device *dma)
+{
+ unsigned long time;
+
+ init_completion(&dma->hw_reset_completion);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_HW_JPEGDMA_RESET, MSM_HW_JPEGDMA_RESET_DEFAULT);
+
+ time = wait_for_completion_timeout(&dma->hw_reset_completion,
+ msecs_to_jiffies(MSM_JPEGDMA_TIMEOUT_MS));
+ if (!time) {
+ dev_err(dma->dev, "Jpeg dma detection reset timeout\n");
+ return -ETIME;
+ }
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_halt - Halt jpeg dma core.
+ * @dma: Pointer to dma device.
+ */
+static int msm_jpegdma_hw_halt(struct msm_jpegdma_device *dma)
+{
+ unsigned long time;
+
+ init_completion(&dma->hw_halt_completion);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_CMD_ADDR, 0x4);
+
+ time = wait_for_completion_timeout(&dma->hw_halt_completion,
+ msecs_to_jiffies(MSM_JPEGDMA_TIMEOUT_MS));
+ if (!time) {
+ dev_err(dma->dev, "Jpeg dma detection halt timeout\n");
+ return -ETIME;
+ }
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_run - Enable dma processing.
+ * @dma: Pointer to dma device.
+ */
+static int msm_jpegdma_hw_run(struct msm_jpegdma_device *dma)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_CMD_ADDR, 0x1);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_core_config - Set jpeg dma core configuration.
+ * @dma: Pointer to dma device.
+ * @num_pipes: Number of pipes.
+ * @scale_0: Scaler 0 enable.
+ * @scale_1: Scaler 1 enable.
+ */
+static int msm_jpegdma_hw_core_config(struct msm_jpegdma_device *dma,
+ int num_pipes, int scale_0, int scale_1)
+{
+ u32 reg;
+
+ reg = (scale_0 << MSM_JPEGDMA_CORE_CFG_SCALE_0_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_TEST_BUS_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_BRIDGE_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_WE_0_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_FE_0_ENABLE_SHFT);
+
+ /* Enable read write ports for second pipe */
+ if (num_pipes > 1) {
+ reg |= (scale_1 << MSM_JPEGDMA_CORE_CFG_SCALE_1_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_WE_1_ENABLE_SHFT) |
+ (0x1 << MSM_JPEGDMA_CORE_CFG_FE_1_ENABLE_SHFT);
+ }
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_CORE_CFG_ADDR, reg);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_0_block - Fetch engine 0 block configuration.
+ * @dma: Pointer to dma device.
+ * @block_config: Pointer to block configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_fe_0_block(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_block_config *block_config,
+ enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ switch (plane_type) {
+ case JPEGDMA_PLANE_TYPE_Y:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_Y <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CB:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CB <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CR:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CR <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CBCR:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CBCR <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ default:
+ dev_err(dma->dev, "Unsupported plane type %d\n", plane_type);
+ return -EINVAL;
+ }
+
+ reg |= (block_config->block.reg_val <<
+ MSM_JPEGDMA_FE_CFG_BLOCK_WIDTH_SHFT) |
+ (0x1 << MSM_JPEGDMA_FE_CFG_MAL_BOUNDARY_SHFT) |
+ (0x1 << MSM_JPEGDMA_FE_CFG_MAL_EN_SHFT) |
+ (0xF << MSM_JPEGDMA_FE_CFG_BURST_LENGTH_MAX_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_0_CFG_ADDR, reg);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_1_block - Fetch engine 1 block configuration.
+ * @dma: Pointer to dma device.
+ * @block_config: Pointer to block configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_fe_1_block(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_block_config *block_config,
+ enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ switch (plane_type) {
+ case JPEGDMA_PLANE_TYPE_Y:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_Y <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CB:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CB <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CR:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CR <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ case JPEGDMA_PLANE_TYPE_CBCR:
+ reg = MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CBCR <<
+ MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT;
+ break;
+ default:
+ dev_err(dma->dev, "Unsupported plane type %d\n", plane_type);
+ return -EINVAL;
+ }
+
+ reg |= (block_config->block.reg_val <<
+ MSM_JPEGDMA_FE_CFG_BLOCK_WIDTH_SHFT) |
+ (0xF << MSM_JPEGDMA_FE_CFG_BURST_LENGTH_MAX_SHFT) |
+ (0x1 << MSM_JPEGDMA_FE_CFG_MAL_EN_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_1_CFG_ADDR, reg);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_0_phase - Fetch engine 0 phase configuration.
+ * @dma: Pointer to dma device.
+ * @phase: Fetch engine 0 phase.
+ */
+static int msm_jpegdma_hw_fe_0_phase(struct msm_jpegdma_device *dma, int phase)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_HINIT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, phase);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_1_phase - Fetch engine 1 phase configuration.
+ * @dma: Pointer to dma device.
+ * @phase: Fetch engine 1 phase.
+ */
+static int msm_jpegdma_hw_fe_1_phase(struct msm_jpegdma_device *dma, int phase)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_HINIT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, 0x00);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, phase);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_0_size - Fetch engine 0 size configuration.
+ * @dma: Pointer to dma device.
+ * @size: Pointer to size configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_fe_0_size(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size *size, enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ reg = (size->width + size->left - 1) |
+ ((size->height + size->top - 1) <<
+ MSM_JPEGDMA_FE_RD_BUFFER_SIZE_HEIGHT_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_BUFFER_SIZE_0_ADDR, reg);
+
+ if (size->left && plane_type == JPEGDMA_PLANE_TYPE_CBCR)
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, size->left / 2);
+ else
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR, size->left);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR, size->top);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_STRIDE_ADDR, size->stride);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_1_size - Fetch engine 1 size configuration.
+ * @dma: Pointer to dma device.
+ * @size: Pointer to size configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_fe_1_size(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size *size, enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ reg = (size->width + size->left - 1) |
+ ((size->height + size->top - 1) <<
+ MSM_JPEGDMA_FE_RD_BUFFER_SIZE_HEIGHT_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_BUFFER_SIZE_1_ADDR, reg);
+
+ if (size->left && plane_type == JPEGDMA_PLANE_TYPE_CBCR)
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, size->left / 2);
+ else
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR, size->left);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR, size->top);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_STRIDE_ADDR, size->stride);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_0_addr - Set fetch engine 0 address.
+ * @dma: Pointer to dma device.
+ * @addr: Fetch engine address.
+ */
+static int msm_jpegdma_hw_fe_0_addr(struct msm_jpegdma_device *dma, u32 addr)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_CMD_ADDR, MSM_JPEGDMA_CMD_CLEAR_READ_PLN_QUEUES);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_0_PNTR_ADDR, addr);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_1_addr - Set fetch engine 1 address.
+ * @dma: Pointer to dma device.
+ * @addr: Fetch engine address.
+ */
+static int msm_jpegdma_hw_fe_1_addr(struct msm_jpegdma_device *dma, u32 addr)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_FE_RD_1_PNTR_ADDR, addr);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_fe_0_block - Write engine 0 block configuration.
+ * @dma: Pointer to dma device.
+ * @block_config: Pointer to block configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_we_0_block(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_block_config *block,
+ enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ reg = (0xF << MSM_JPEGDMA_WE_CFG_BURST_LENGTH_MAX_SHFT) |
+ (0x1 << MSM_JPEGDMA_WE_CFG_MAL_BOUNDARY_SHFT) |
+ (0x1 << MSM_JPEGDMA_WE_CFG_MAL_EN_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_CFG_ADDR, reg);
+
+ reg = ((block->blocks_per_row - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_0_BLOCKS_PER_ROW_SHFT) |
+ (block->blocks_per_col - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_CFG_0_ADDR, reg);
+
+ reg = ((block->h_step_last - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_1_LAST_H_STEP_SHFT) |
+ (block->h_step - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_CFG_1_ADDR, reg);
+
+ reg = ((block->v_step_last - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_2_LAST_V_STEP_SHFT) |
+ (block->v_step - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_CFG_2_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_CFG_3_ADDR, 0x0);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_we_1_block - Write engine 1 block configuration.
+ * @dma: Pointer to dma device.
+ * @block_config: Pointer to block configuration.
+ * @plane_type: Plane type.
+ */
+static int msm_jpegdma_hw_we_1_block(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_block_config *block,
+ enum msm_jpegdma_plane_type plane_type)
+{
+ u32 reg;
+
+ reg = ((block->blocks_per_row - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_0_BLOCKS_PER_ROW_SHFT) |
+ (block->blocks_per_col - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_CFG_0_ADDR, reg);
+
+ reg = ((block->h_step_last - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_1_LAST_H_STEP_SHFT) |
+ (block->h_step - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_CFG_1_ADDR, reg);
+
+ reg = ((block->v_step_last - 1) <<
+ MSM_JPEGDMA_WE_PLN_WR_CFG_2_LAST_V_STEP_SHFT) |
+ (block->v_step - 1);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_CFG_2_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_CFG_3_ADDR, 0x0);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_we_0_size - Write engine 0 size configuration.
+ * @dma: Pointer to dma device.
+ * @size: Pointer to size configuration.
+ */
+static int msm_jpegdma_hw_we_0_size(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size *size)
+{
+ u32 reg;
+
+ reg = (size->width) | ((size->height) <<
+ MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_HEIGHT_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_0_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_STRIDE_ADDR, size->stride);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_we_1_size - Write engine 1 size configuration.
+ * @dma: Pointer to dma device.
+ * @size: Pointer to size configuration.
+ */
+static int msm_jpegdma_hw_we_1_size(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size *size)
+{
+ u32 reg;
+
+ reg = (size->width) | ((size->height) <<
+ MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_HEIGHT_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_1_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_STRIDE_ADDR, size->stride);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_we_0_addr - Set write engine 0 address.
+ * @dma: Pointer to dma device.
+ * @addr: Fetch engine address.
+ */
+static int msm_jpegdma_hw_we_0_addr(struct msm_jpegdma_device *dma, u32 addr)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_CMD_ADDR, MSM_JPEGDMA_CMD_CLEAR_WRITE_PLN_QUEUES);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_0_WR_PNTR_ADDR, addr);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_we_1_addr - Set write engine 1 address.
+ * @dma: Pointer to dma device.
+ * @addr: Fetch engine address.
+ */
+static int msm_jpegdma_hw_we_1_addr(struct msm_jpegdma_device *dma, u32 addr)
+{
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_WE_PLN_1_WR_PNTR_ADDR, addr);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_scale_0_config - Scale configuration for 0 pipeline.
+ * @dma: Pointer to dma device.
+ * @scale: Scale configuration.
+ */
+static int msm_jpegdma_hw_scale_0_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_scale *scale)
+{
+ u32 reg;
+ u32 h_down_en;
+ u32 v_down_en;
+
+ h_down_en = (scale->hor_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1;
+ v_down_en = (scale->ver_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1;
+
+ reg = (h_down_en << MSM_JPEGDMA_PP_SCALE_CFG_HSCALE_ENABLE_SHFT) |
+ (v_down_en << MSM_JPEGDMA_PP_SCALE_CFG_VSCALE_ENABLE_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_0_SCALE_CFG_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_0_SCALE_PHASEH_STEP_ADDR, scale->hor_scale);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_0_SCALE_PHASEV_STEP_ADDR, scale->ver_scale);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_scale_1_config - Scale configuration for 1 pipeline.
+ * @dma: Pointer to dma device.
+ * @scale: Scale configuration.
+ */
+static int msm_jpegdma_hw_scale_1_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_scale *scale)
+{
+ u32 reg;
+ u32 h_down_en;
+ u32 v_down_en;
+
+ h_down_en = (scale->hor_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1;
+ v_down_en = (scale->ver_scale == MSM_JPEGDMA_SCALE_UNI) ? 0 : 1;
+
+ reg = (h_down_en << MSM_JPEGDMA_PP_SCALE_CFG_HSCALE_ENABLE_SHFT) |
+ (v_down_en << MSM_JPEGDMA_PP_SCALE_CFG_VSCALE_ENABLE_SHFT);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_1_SCALE_CFG_ADDR, reg);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_1_SCALE_PHASEH_STEP_ADDR, scale->hor_scale);
+
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_PP_1_SCALE_PHASEV_STEP_ADDR, scale->ver_scale);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_config_qos - Configure qos registers.
+ * @dma: Pointer to dma device.
+ */
+static void msm_jpegdma_hw_config_qos(struct msm_jpegdma_device *dma)
+{
+ int i;
+
+ if (!dma->qos_regs_num)
+ return;
+
+ for (i = 0; i < dma->qos_regs_num; i++)
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ dma->qos_regs[i].reg, dma->qos_regs[i].val);
+
+}
+
+/*
+ * msm_jpegdma_hw_config_vbif - Configure and vbif interface.
+ * @dma: Pointer to dma device.
+ */
+static void msm_jpegdma_hw_config_vbif(struct msm_jpegdma_device *dma)
+{
+ int i;
+
+ if (!dma->vbif_regs_num)
+ return;
+
+ for (i = 0; i < dma->vbif_regs_num; i++)
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_VBIF,
+ dma->vbif_regs[i].reg, dma->vbif_regs[i].val);
+
+}
+
+/*
+ * msm_jpegdma_hw_config_mmu_prefetch - Configure mmu prefetch registers.
+ * @dma: Pointer to dma device.
+ * @min_addr: Pointer to jpeg dma addr, containing min addrs of the plane.
+ * @max_addr: Pointer to jpeg dma addr, containing max addrs of the plane.
+ */
+static void msm_jpegdma_hw_config_mmu_prefetch(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_addr *min_addr,
+ struct msm_jpegdma_addr *max_addr)
+{
+ int i;
+
+ if (!dma->prefetch_regs_num)
+ return;
+
+ for (i = 0; i < dma->prefetch_regs_num; i++)
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_VBIF,
+ dma->prefetch_regs[i].reg, dma->prefetch_regs[i].val);
+
+ if (min_addr != NULL && max_addr != NULL) {
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN, min_addr->in_addr);
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX, max_addr->in_addr);
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN, min_addr->out_addr);
+ msm_jpegdma_hw_write_reg(dma, MSM_JPEGDMA_IOMEM_CORE,
+ MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX, max_addr->out_addr);
+ }
+}
+
+/*
+ * msm_jpegdma_hw_calc_speed - Calculate speed based on framerate and size.
+ * @dma: Pointer to dma device.
+ * @size: Dma user size configuration.
+ * @speed: Calculated speed.
+ */
+static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size,
+ struct msm_jpegdma_speed *speed)
+{
+ u64 width;
+ u64 height;
+ long real_clock;
+ u64 calc_rate;
+ int core_clk_idx;
+
+ width = size->in_size.width + size->in_size.left;
+ height = size->in_size.height + size->in_size.top;
+
+ calc_rate = (width * height * size->format.depth * size->fps) / 16;
+ core_clk_idx = msm_jpegdma_hw_get_clock_index(dma,
+ MSM_JPEGDMA_CORE_CLK);
+ if (core_clk_idx < 0) {
+ dev_err(dma->dev, "Can get clock index for dma %s\n",
+ MSM_JPEGDMA_CORE_CLK);
+ }
+
+ real_clock = clk_round_rate(dma->clk[core_clk_idx], calc_rate);
+ if (real_clock < 0) {
+ dev_err(dma->dev, "Can not round core clock\n");
+ return -EINVAL;
+ }
+
+ speed->bus_ab = calc_rate * 2;
+ speed->bus_ib = jpegdma_do_div((real_clock *
+ (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)),
+ MSM_JPEGDMA_BW_DEN);
+ speed->core_clock = real_clock;
+ dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n",
+ speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps);
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_set_speed - Configure clock and bus bandwidth based on
+ * requested speed and dma clients.
+ * @size: Jpeg dma size configuration.
+ * @speed: Requested dma speed.
+ */
+static int msm_jpegdma_hw_set_speed(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size,
+ struct msm_jpegdma_speed *speed)
+{
+ struct msm_jpegdma_speed new_sp;
+ struct msm_jpegdma_size_config new_size;
+ int ret;
+ int core_clk_idx;
+
+ if (dma->active_clock_rate >= speed->core_clock)
+ return 0;
+
+ new_sp = *speed;
+ if (dma->ref_count > 2) {
+ new_size = *size;
+ new_size.fps = size->fps * ((dma->ref_count + 1) / 2);
+ ret = msm_jpegdma_hw_calc_speed(dma, &new_size, &new_sp);
+ if (ret < 0)
+ return -EINVAL;
+ }
+
+ core_clk_idx = msm_jpegdma_hw_get_clock_index(dma,
+ MSM_JPEGDMA_CORE_CLK);
+ if (core_clk_idx < 0) {
+ dev_err(dma->dev, "Can get clock index for dma %s\n",
+ MSM_JPEGDMA_CORE_CLK);
+ }
+
+ ret = clk_set_rate(dma->clk[core_clk_idx], new_sp.core_clock);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail Core clock rate %d\n", ret);
+ return -EINVAL;
+ }
+ dma->active_clock_rate = speed->core_clock;
+
+ dma->bus_vectors.ab = new_sp.bus_ab;
+ dma->bus_vectors.ib = new_sp.bus_ib;
+
+ ret = msm_bus_scale_client_update_request(dma->bus_client, 0);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail bus scale update %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_add_plane_offset - Add plane offset to all pipelines.
+ * @plane: Jpeg dma plane configuration.
+ * @in_offset: Input plane offset.
+ * @out_offset: Output plane offset.
+ */
+static int msm_jpegdma_hw_add_plane_offset(struct msm_jpegdma_plane *plane,
+ unsigned int in_offset, unsigned int out_offset)
+{
+ int i;
+
+ for (i = 0; i < plane->active_pipes; i++) {
+ plane->config[i].in_offset += in_offset;
+ plane->config[i].out_offset += out_offset;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_calc_config - Calculate plane configuration.
+ * @size_cfg: Size configuration.
+ * @plane: Plane configuration need to be calculated.
+ */
+static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
+ struct msm_jpegdma_plane *plane)
+{
+ u64 scale_hor, scale_ver, phase;
+ u64 in_width, in_height;
+ u64 out_width, out_height;
+ struct msm_jpegdma_config *config;
+ int i;
+
+ if (!size_cfg->out_size.width || !size_cfg->out_size.height)
+ return -EINVAL;
+
+ config = &plane->config[0];
+ config->scale_cfg.enable = 0;
+
+ in_width = size_cfg->in_size.width;
+ out_width = size_cfg->out_size.width;
+ scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI),
+ out_width);
+ if (scale_hor != MSM_JPEGDMA_SCALE_UNI)
+ config->scale_cfg.enable = 1;
+
+ in_height = size_cfg->in_size.height;
+ out_height = size_cfg->out_size.height;
+ scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
+ if (scale_ver != MSM_JPEGDMA_SCALE_UNI)
+ config->scale_cfg.enable = 1;
+
+ config->scale_cfg.ver_scale = scale_ver;
+ config->scale_cfg.hor_scale = scale_hor;
+
+ for (i = 0; ARRAY_SIZE(msm_jpegdma_block_sel); i++)
+ if (scale_hor <= msm_jpegdma_block_sel[i].div)
+ break;
+
+ if (i == ARRAY_SIZE(msm_jpegdma_block_sel))
+ return -EINVAL;
+
+ config->block_cfg.block = msm_jpegdma_block_sel[i];
+
+ if (plane->active_pipes > 1) {
+ phase = jpegdma_do_div((out_height * scale_ver +
+ (plane->active_pipes - 1)), plane->active_pipes);
+ phase &= (u64)(MSM_JPEGDMA_SCALE_UNI - 1);
+ out_height = jpegdma_do_div((out_height +
+ (plane->active_pipes - 1)), plane->active_pipes);
+ in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI;
+ }
+
+ config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width,
+ config->block_cfg.block.width);
+
+ config->block_cfg.blocks_per_col = out_height;
+
+ config->block_cfg.h_step = config->block_cfg.block.width;
+ config->size_cfg.out_size.width = out_width;
+ config->block_cfg.h_step_last = (uint32_t) do_div(out_width,
+ config->block_cfg.block.width);
+ if (!config->block_cfg.h_step_last)
+ config->block_cfg.h_step_last = config->block_cfg.h_step;
+ else
+ config->block_cfg.blocks_per_row++;
+
+ config->block_cfg.v_step = 1;
+ config->block_cfg.v_step_last = 1;
+
+ config->size_cfg = *size_cfg;
+ config->size_cfg.in_size.width = in_width;
+ config->size_cfg.in_size.height = in_height;
+ config->size_cfg.out_size.height = out_height;
+ config->in_offset = 0;
+ config->out_offset = 0;
+
+ if (plane->active_pipes > 1) {
+ plane->config[1] = *config;
+ /* Recalculate offset for second pipe */
+ plane->config[1].in_offset =
+ config->size_cfg.in_size.scanline *
+ config->size_cfg.in_size.stride;
+
+ plane->config[1].out_offset =
+ config->size_cfg.out_size.scanline *
+ config->size_cfg.out_size.stride;
+
+ plane->config[1].phase = phase;
+ }
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_check_config - Check configuration based on size is possible.
+ *@dma: Pointer to dma device.
+ * @size_cfg: Size configuration.
+ */
+int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size_cfg)
+{
+ u64 in_width, in_height;
+ u64 out_width, out_height;
+ u64 scale;
+
+ if (!size_cfg->out_size.width || !size_cfg->out_size.height)
+ return -EINVAL;
+
+ in_width = size_cfg->in_size.width;
+ out_width = size_cfg->out_size.width;
+ scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)),
+ out_width);
+ if (scale < MSM_JPEGDMA_SCALE_UNI)
+ return -EINVAL;
+
+
+ in_height = size_cfg->in_size.height;
+ out_height = size_cfg->out_size.height;
+ scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
+ if (scale < MSM_JPEGDMA_SCALE_UNI)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_set_config - Set dma configuration based on size.
+ *@dma: Pointer to dma device.
+ * @size_cfg: Size configuration.
+ * @plane_cfg: Calculated plane configuration.
+ */
+int msm_jpegdma_hw_set_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size_cfg,
+ struct msm_jpegdma_plane_config *plane_cfg)
+{
+ unsigned int in_offset;
+ unsigned int out_offset;
+ struct msm_jpegdma_size_config plane_size;
+ int ret;
+ int i;
+
+ if (!size_cfg->format.colplane_h || !size_cfg->format.colplane_v)
+ return -EINVAL;
+
+ ret = msm_jpegdma_hw_calc_speed(dma, size_cfg, &plane_cfg->speed);
+ if (ret < 0)
+ return -EINVAL;
+
+ dma->active_clock_rate = 0;
+
+ plane_cfg->plane[0].active_pipes = dma->hw_num_pipes;
+ plane_cfg->plane[0].type = size_cfg->format.planes[0];
+ msm_jpegdma_hw_calc_config(size_cfg, &plane_cfg->plane[0]);
+
+ in_offset = size_cfg->in_offset;
+ out_offset = size_cfg->out_offset;
+
+ msm_jpegdma_hw_add_plane_offset(&plane_cfg->plane[0],
+ in_offset, out_offset);
+
+ if (size_cfg->format.num_planes == 1)
+ return 0;
+
+ in_offset += (size_cfg->in_size.scanline *
+ size_cfg->in_size.stride);
+ out_offset += (size_cfg->out_size.scanline *
+ size_cfg->out_size.stride);
+
+ memset(&plane_size, 0x00, sizeof(plane_size));
+ for (i = 1; i < size_cfg->format.num_planes; i++) {
+ plane_cfg->plane[i].active_pipes = dma->hw_num_pipes;
+ plane_cfg->plane[i].type = size_cfg->format.planes[i];
+
+ if (size_cfg->in_size.top)
+ plane_size.in_size.top = size_cfg->in_size.top /
+ size_cfg->format.colplane_v;
+
+ if (size_cfg->in_size.left)
+ plane_size.in_size.left = size_cfg->in_size.left /
+ size_cfg->format.colplane_h;
+
+ plane_size.in_size.width = size_cfg->in_size.width /
+ size_cfg->format.colplane_h;
+ plane_size.in_size.height = size_cfg->in_size.height /
+ size_cfg->format.colplane_v;
+ plane_size.in_size.scanline = size_cfg->in_size.scanline /
+ size_cfg->format.colplane_v;
+
+ plane_size.in_size.stride = size_cfg->in_size.stride;
+
+ plane_size.out_size.width = size_cfg->out_size.width /
+ size_cfg->format.colplane_h;
+ plane_size.out_size.height = size_cfg->out_size.height /
+ size_cfg->format.colplane_v;
+ plane_size.out_size.scanline = size_cfg->out_size.scanline /
+ size_cfg->format.colplane_v;
+
+ plane_size.out_size.stride = size_cfg->out_size.stride;
+
+ plane_size.format = size_cfg->format;
+ plane_size.fps = size_cfg->fps;
+
+ msm_jpegdma_hw_calc_config(&plane_size,
+ &plane_cfg->plane[i]);
+
+ msm_jpegdma_hw_add_plane_offset(&plane_cfg->plane[i],
+ in_offset, out_offset);
+
+ in_offset += (plane_size.in_size.scanline *
+ plane_size.in_size.stride);
+ out_offset += (plane_size.out_size.scanline *
+ plane_size.out_size.stride);
+ }
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_start - Start dma processing.
+ *@dma: Pointer to dma device.
+ * @addr: Input address.
+ * @plane: Plane configuration.
+ * @speed: Clock and bus bandwidth configuration.
+ */
+int msm_jpegdma_hw_start(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_addr *addr,
+ struct msm_jpegdma_plane *plane,
+ struct msm_jpegdma_speed *speed)
+{
+ struct msm_jpegdma_config *cfg;
+ struct msm_jpegdma_addr prefetch_max_addr;
+ unsigned int prefetch_in_size;
+ unsigned int prefetch_out_size;
+
+ int ret;
+
+ if (!plane->active_pipes)
+ return -EINVAL;
+
+ if (plane->active_pipes > MSM_JPEGDMA_MAX_PIPES)
+ return -EINVAL;
+ ret = msm_jpegdma_hw_set_speed(dma, &plane->config[0].size_cfg, speed);
+ if (ret < 0)
+ return -EINVAL;
+
+ msm_jpegdma_hw_core_config(dma, plane->active_pipes,
+ plane->config[0].scale_cfg.enable,
+ plane->config[1].scale_cfg.enable);
+
+ cfg = &plane->config[0];
+ msm_jpegdma_hw_scale_0_config(dma, &cfg->scale_cfg);
+
+ msm_jpegdma_hw_fe_0_block(dma, &cfg->block_cfg, plane->type);
+ msm_jpegdma_hw_fe_0_phase(dma, cfg->phase);
+ msm_jpegdma_hw_fe_0_size(dma, &cfg->size_cfg.in_size, plane->type);
+ msm_jpegdma_hw_fe_0_addr(dma, addr->in_addr + cfg->in_offset);
+ prefetch_in_size = cfg->size_cfg.in_size.stride *
+ cfg->size_cfg.in_size.scanline;
+
+ msm_jpegdma_hw_we_0_block(dma, &cfg->block_cfg, plane->type);
+ msm_jpegdma_hw_we_0_size(dma, &cfg->size_cfg.out_size);
+ msm_jpegdma_hw_we_0_addr(dma, addr->out_addr + cfg->out_offset);
+ prefetch_out_size = cfg->size_cfg.out_size.stride *
+ cfg->size_cfg.out_size.scanline;
+
+ if (plane->active_pipes > 1) {
+ cfg = &plane->config[1];
+ msm_jpegdma_hw_scale_1_config(dma, &cfg->scale_cfg);
+
+ msm_jpegdma_hw_fe_1_block(dma, &cfg->block_cfg, plane->type);
+ msm_jpegdma_hw_fe_1_phase(dma, cfg->phase);
+ msm_jpegdma_hw_fe_1_size(dma, &cfg->size_cfg.in_size,
+ plane->type);
+ msm_jpegdma_hw_fe_1_addr(dma, addr->in_addr + cfg->in_offset);
+ prefetch_in_size += (cfg->size_cfg.in_size.stride *
+ cfg->size_cfg.in_size.scanline);
+
+ msm_jpegdma_hw_we_1_block(dma, &cfg->block_cfg, plane->type);
+ msm_jpegdma_hw_we_1_size(dma, &cfg->size_cfg.out_size);
+ msm_jpegdma_hw_we_1_addr(dma, addr->out_addr + cfg->out_offset);
+ prefetch_out_size += (cfg->size_cfg.out_size.stride *
+ cfg->size_cfg.out_size.scanline);
+ }
+
+ if (prefetch_in_size > 0 && prefetch_out_size > 0) {
+ prefetch_max_addr.in_addr = addr->in_addr +
+ (prefetch_in_size - 1);
+ prefetch_max_addr.out_addr = addr->out_addr +
+ (prefetch_out_size - 1);
+ msm_jpegdma_hw_config_mmu_prefetch(dma, addr,
+ &prefetch_max_addr);
+ }
+
+ msm_jpegdma_hw_run(dma);
+
+ return 1;
+}
+
+/*
+ * msm_jpegdma_hw_abort - abort dma processing.
+ *@dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_abort(struct msm_jpegdma_device *dma)
+{
+ int ret;
+
+ ret = msm_jpegdma_hw_halt(dma);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to halt hw\n");
+ return ret;
+ }
+
+ ret = msm_jpegdma_hw_reset(dma);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to reset hw\n");
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * msm_jpegdma_hw_irq - Dma irq handler.
+ * @irq: Irq number.
+ * @dev_id: Pointer to dma device.
+ */
+static irqreturn_t msm_jpegdma_hw_irq(int irq, void *dev_id)
+{
+ struct msm_jpegdma_device *dma = dev_id;
+
+ u32 irq_status;
+
+ irq_status = msm_jpegdma_hw_get_irq_status(dma);
+ msm_jpegdma_hw_clear_irq(dma, irq_status);
+
+ if (irq_status & MSM_JPEGDMA_IRQ_STATUS_RST_DONE) {
+ dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ reset done\n");
+ complete_all(&dma->hw_reset_completion);
+ }
+
+ if (irq_status & MSM_JPEGDMA_IRQ_STATUS_AXI_HALT) {
+ dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ AXI halt\n");
+ complete_all(&dma->hw_halt_completion);
+ }
+
+ if (irq_status & MSM_JPEGDMA_IRQ_STATUS_SESSION_DONE) {
+ dev_dbg(dma->dev, "Jpeg v4l2 dma IRQ session done\n");
+ msm_jpegdma_isr_processing_done(dma);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * msm_jpegdma_hw_request_irq - Request dma irq.
+ * @pdev: Pointer to platform device.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_request_irq(struct platform_device *pdev,
+ struct msm_jpegdma_device *dma)
+{
+ int ret;
+
+ dma->irq_num = platform_get_irq(pdev, 0);
+ if (dma->irq_num < 0) {
+ dev_err(dma->dev, "Can not get dma core irq resource\n");
+ ret = -ENODEV;
+ goto error_irq;
+ }
+
+ ret = request_threaded_irq(dma->irq_num, NULL,
+ msm_jpegdma_hw_irq, IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ dev_name(&pdev->dev), dma);
+ if (ret) {
+ dev_err(dma->dev, "Can not claim wrapper IRQ %d\n",
+ dma->irq_num);
+ goto error_irq;
+ }
+
+ return 0;
+
+error_irq:
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_release_mem_resources - Releases memory resources.
+ * @dma: Pointer to dma device.
+ */
+void msm_jpegdma_hw_release_mem_resources(struct msm_jpegdma_device *dma)
+{
+ int i, reserve_mem_flag;
+ char *dev_name;
+
+ /* Prepare memory resources */
+ for (i = 0; i < MSM_JPEGDMA_IOMEM_LAST; i++) {
+
+ switch (i) {
+ case MSM_JPEGDMA_IOMEM_CORE:
+ dev_name = "jpeg_hw";
+ reserve_mem_flag = true;
+ break;
+ case MSM_JPEGDMA_IOMEM_VBIF:
+ dev_name = "jpeg_vbif";
+ reserve_mem_flag = false;
+ break;
+ default:
+ pr_err("%s: Invalid device : %d\n", __func__, i);
+ return;
+ }
+ /* release the device address */
+ msm_camera_put_reg_base(dma->pdev, dma->iomem_base[i], dev_name,
+ reserve_mem_flag);
+ }
+}
+
+/*
+ * msm_jpegdma_hw_get_mem_resources - Get memory resources.
+ * @pdev: Pointer to dma platform device.
+ * @dma: Pointer to dma device.
+ *
+ * Get and ioremap platform memory resources.
+ */
+int msm_jpegdma_hw_get_mem_resources(struct platform_device *pdev,
+ struct msm_jpegdma_device *dma)
+{
+ int i;
+ int ret = 0;
+ char *dev_name;
+ int reserve_mem_flag;
+
+ /* Prepare memory resources */
+ for (i = 0; i < MSM_JPEGDMA_IOMEM_LAST; i++) {
+
+ switch (i) {
+ case MSM_JPEGDMA_IOMEM_CORE:
+ dev_name = "jpeg_hw";
+ reserve_mem_flag = true;
+ break;
+ case MSM_JPEGDMA_IOMEM_VBIF:
+ dev_name = "jpeg_vbif";
+ reserve_mem_flag = false;
+ break;
+ default:
+ pr_err("%s: Invalid device : %d\n", __func__, i);
+ return -EINVAL;
+ }
+ /* get the device address base */
+ dma->iomem_base[i] =
+ msm_camera_get_reg_base(pdev, dev_name,
+ reserve_mem_flag);
+ if (!dma->iomem_base[i]) {
+ dev_err(dma->dev, "%s can not remap region\n",
+ dev_name);
+ ret = -ENODEV;
+ break;
+ }
+ }
+
+ if (ret < 0)
+ msm_jpegdma_hw_release_mem_resources(dma);
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_get_max_downscale - Get max downscale factor from dtsi.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_get_max_downscale(struct msm_jpegdma_device *dma)
+{
+ int ret;
+ int max_ds_factor;
+
+ ret = of_property_read_u32(dma->dev->of_node,
+ "qcom,max-ds-factor", &max_ds_factor);
+ if (ret < 0) {
+ dev_err(dma->dev, "cannot read qcom,max-ds-factor from dtsi\n");
+ return ret;
+ }
+ dev_dbg(dma->dev, "max_ds_factor is %d\n", max_ds_factor);
+ return max_ds_factor;
+}
+
+/*
+ * msm_jpegdma_hw_get_qos - Get dma qos settings from device-tree.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_get_qos(struct msm_jpegdma_device *dma)
+{
+ int i, j;
+ int ret;
+ unsigned int cnt;
+ const void *property;
+
+ property = of_get_property(dma->dev->of_node,
+ "qcom,qos-reg-settings", &cnt);
+ if (!property || !cnt) {
+ dev_dbg(dma->dev, "Missing qos settings\n");
+ return 0;
+ }
+
+ cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
+
+ dma->qos_regs_num = cnt / 2;
+
+ dma->qos_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->qos_regs_num), GFP_KERNEL);
+ if (!dma->qos_regs)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,qos-reg-settings", i,
+ &dma->qos_regs[j].reg);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read qos reg %d\n", j);
+ goto error;
+ }
+
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,qos-reg-settings", i + 1,
+ &dma->qos_regs[j].val);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read qos setting %d\n", j);
+ goto error;
+ }
+ dev_dbg(dma->dev, "Qos idx %d, reg %x val %x\n", j,
+ dma->qos_regs[j].reg, dma->qos_regs[j].val);
+ }
+
+ return 0;
+error:
+ kfree(dma->qos_regs);
+ dma->qos_regs = NULL;
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_put_qos - Free dma qos settings.
+ * @dma: Pointer to dma device.
+ */
+void msm_jpegdma_hw_put_qos(struct msm_jpegdma_device *dma)
+{
+ kfree(dma->qos_regs);
+ dma->qos_regs = NULL;
+}
+
+/*
+ * msm_jpegdma_hw_get_vbif - Get dma vbif settings from device-tree.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_get_vbif(struct msm_jpegdma_device *dma)
+{
+ int i, j;
+ int ret;
+ unsigned int cnt;
+ const void *property;
+
+ property = of_get_property(dma->dev->of_node, "qcom,vbif-reg-settings",
+ &cnt);
+ if (!property || !cnt) {
+ dev_dbg(dma->dev, "Missing vbif settings\n");
+ return 0;
+ }
+
+ cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
+
+ dma->vbif_regs_num = cnt / 2;
+
+ dma->vbif_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->vbif_regs_num), GFP_KERNEL);
+ if (!dma->vbif_regs)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,vbif-reg-settings", i,
+ &dma->vbif_regs[j].reg);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read vbif reg %d\n", j);
+ goto error;
+ }
+
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,vbif-reg-settings", i + 1,
+ &dma->vbif_regs[j].val);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read vbif setting %d\n", j);
+ goto error;
+ }
+
+ dev_dbg(dma->dev, "Vbif idx %d, reg %x val %x\n", j,
+ dma->vbif_regs[j].reg, dma->vbif_regs[j].val);
+ }
+
+ return 0;
+error:
+ kfree(dma->vbif_regs);
+ dma->vbif_regs = NULL;
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_put_vbif - Put dma clocks.
+ * @dma: Pointer to dma device.
+ */
+void msm_jpegdma_hw_put_vbif(struct msm_jpegdma_device *dma)
+{
+ kfree(dma->vbif_regs);
+ dma->vbif_regs = NULL;
+}
+
+/*
+ * msm_jpegdma_hw_get_prefetch - Get dma prefetch settings from device-tree.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_get_prefetch(struct msm_jpegdma_device *dma)
+{
+ int i, j;
+ int ret;
+ unsigned int cnt;
+ const void *property;
+
+ property = of_get_property(dma->dev->of_node,
+ "qcom,prefetch-reg-settings", &cnt);
+ if (!property || !cnt) {
+ dev_dbg(dma->dev, "Missing prefetch settings\n");
+ return 0;
+ }
+
+ cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
+
+ dma->prefetch_regs_num = cnt / 2;
+
+ dma->prefetch_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->prefetch_regs_num), GFP_KERNEL);
+ if (!dma->prefetch_regs)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,prefetch-reg-settings", i,
+ &dma->prefetch_regs[j].reg);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read prefetch reg %d\n", j);
+ goto error;
+ }
+
+ ret = of_property_read_u32_index(dma->dev->of_node,
+ "qcom,prefetch-reg-settings", i + 1,
+ &dma->prefetch_regs[j].val);
+ if (ret < 0) {
+ dev_err(dma->dev, "can not read prefetch setting %d\n",
+ j);
+ goto error;
+ }
+
+ dev_dbg(dma->dev, "Prefetch idx %d, reg %x val %x\n", j,
+ dma->prefetch_regs[j].reg, dma->prefetch_regs[j].val);
+ }
+
+ return 0;
+error:
+ kfree(dma->prefetch_regs);
+ dma->prefetch_regs = NULL;
+
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_put_prefetch - free prefetch settings.
+ * @dma: Pointer to dma device.
+ */
+void msm_jpegdma_hw_put_prefetch(struct msm_jpegdma_device *dma)
+{
+ kfree(dma->prefetch_regs);
+ dma->prefetch_regs = NULL;
+}
+
+/*
+ * msm_jpegdma_hw_get_capabilities - Get dma hw for performing any hw operation.
+ * @dma: Pointer to dma device.
+ */
+int msm_jpegdma_hw_get_capabilities(struct msm_jpegdma_device *dma)
+{
+ int ret = 0;
+
+ mutex_lock(&dma->lock);
+
+ /* enable all the regulators */
+ ret = msm_camera_regulator_enable(dma->dma_vdd,
+ dma->num_reg, true);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to enable regulators\n");
+ goto error_regulators_get;
+ }
+
+ /* enable all the clocks */
+ ret = msm_camera_clk_enable(&dma->pdev->dev,
+ dma->jpeg_clk_info, dma->clk,
+ dma->num_clk, true);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to enable clocks\n");
+ goto error_clocks;
+ }
+
+ dma->hw_num_pipes = msm_jpegdma_hw_get_num_pipes(dma);
+
+ /* disable all the clocks */
+ msm_camera_clk_enable(&dma->pdev->dev,
+ dma->jpeg_clk_info, dma->clk,
+ dma->num_clk, false);
+ /* disable all the regulators */
+ msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false);
+
+ mutex_unlock(&dma->lock);
+
+ return 0;
+
+error_clocks:
+ msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false);
+error_regulators_get:
+ mutex_unlock(&dma->lock);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_get - Get dma hw for performing any hw operation.
+ * @dma: Pointer to dma device.
+ * @clock_rate_idx: Clock rate index.
+ *
+ * Prepare dma hw for operation. Have reference count protected by
+ * dma device mutex.
+ */
+int msm_jpegdma_hw_get(struct msm_jpegdma_device *dma)
+{
+ int ret;
+
+ mutex_lock(&dma->lock);
+ if (dma->ref_count == 0) {
+
+ dev_dbg(dma->dev, "msm_jpegdma_hw_get E\n");
+ /* enable all the regulators */
+ ret = msm_camera_regulator_enable(dma->dma_vdd,
+ dma->num_reg, true);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to enable regulators\n");
+ goto error_regulators_get;
+ }
+
+ /* enable all the clocks */
+ ret = msm_camera_clk_enable(&dma->pdev->dev,
+ dma->jpeg_clk_info, dma->clk,
+ dma->num_clk, true);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to enable clocks\n");
+ goto error_clocks;
+ }
+
+ /* update the bus vector with valid bw */
+ msm_camera_update_bus_vector(dma->bus_client, 1);
+ msm_jpegdma_hw_config_qos(dma);
+ msm_jpegdma_hw_config_vbif(dma);
+
+ msm_camera_register_threaded_irq(dma->pdev, dma->irq, NULL,
+ msm_jpegdma_hw_irq, IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ dev_name(&dma->pdev->dev), dma);
+ msm_jpegdma_hw_enable_irq(dma);
+
+ ret = msm_jpegdma_hw_reset(dma);
+ if (ret < 0) {
+ dev_err(dma->dev, "Fail to reset hw\n");
+ goto error_hw_reset;
+ }
+ msm_jpegdma_hw_config_qos(dma);
+ msm_jpegdma_hw_config_mmu_prefetch(dma, NULL, NULL);
+ msm_jpegdma_hw_enable_irq(dma);
+ }
+ dma->ref_count++;
+ dev_dbg(dma->dev, "msm_jpegdma_hw_get X\n");
+ mutex_unlock(&dma->lock);
+
+ return 0;
+
+error_hw_reset:
+ msm_jpegdma_hw_disable_irq(dma);
+ msm_camera_clk_enable(&dma->pdev->dev, dma->jpeg_clk_info,
+ dma->clk, dma->num_clk, false);
+error_clocks:
+ msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false);
+error_regulators_get:
+ mutex_unlock(&dma->lock);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_put - Put dma hw.
+ * @dma: Pointer to dma device.
+ *
+ * Release dma hw. Have reference count protected by
+ * dma device mutex.
+ */
+void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma)
+{
+ mutex_lock(&dma->lock);
+
+ if (WARN_ON(!dma->ref_count))
+ goto err;
+
+ if (--dma->ref_count == 0) {
+ msm_jpegdma_hw_halt(dma);
+ msm_jpegdma_hw_disable_irq(dma);
+ /* release the irq */
+ msm_camera_unregister_irq(dma->pdev,
+ dma->irq, dma);
+ /* update the bus vector with zeroth vector */
+ msm_camera_update_bus_vector(dma->bus_client, 0);
+ /* disable all the clocks */
+ msm_camera_clk_enable(&dma->pdev->dev, dma->jpeg_clk_info,
+ dma->clk, dma->num_clk, false);
+ /* disable all the regulators */
+ msm_camera_regulator_enable(dma->dma_vdd, dma->num_reg, false);
+ }
+ /* Reset clock rate, need to be updated on next processing */
+ dma->active_clock_rate = -1;
+err:
+ mutex_unlock(&dma->lock);
+}
+
+/*
+ * msm_jpegdma_hw_attach_iommu - Attach iommu to jpeg dma engine.
+ * @dma: Pointer to dma device.
+ *
+ * Iommu attach have reference count protected by
+ * dma device mutex.
+ */
+static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&dma->lock);
+
+ if (dma->iommu_attached_cnt == UINT_MAX) {
+ dev_err(dma->dev, "Max count reached! can not attach iommu\n");
+ goto error;
+ }
+
+ if (dma->iommu_attached_cnt == 0) {
+ ret = cam_smmu_get_handle(MSM_JPEGDMA_SMMU_NAME,
+ &dma->iommu_hndl);
+ if (ret < 0) {
+ dev_err(dma->dev, "Smmu get handle failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+ ret = cam_smmu_ops(dma->iommu_hndl, CAM_SMMU_ATTACH);
+ if (ret < 0) {
+ dev_err(dma->dev, "Can not attach smmu.\n");
+ goto error_attach;
+ }
+ }
+ dma->iommu_attached_cnt++;
+ mutex_unlock(&dma->lock);
+
+ return 0;
+error_attach:
+ cam_smmu_destroy_handle(dma->iommu_hndl);
+error:
+ mutex_unlock(&dma->lock);
+ return ret;
+}
+
+/*
+ * msm_jpegdma_hw_detach_iommu - Detach iommu from jpeg dma engine.
+ * @dma: Pointer to dma device.
+ *
+ * Iommu detach have reference count protected by
+ * dma device mutex.
+ */
+static void msm_jpegdma_hw_detach_iommu(struct msm_jpegdma_device *dma)
+{
+ mutex_lock(&dma->lock);
+
+ if (dma->iommu_attached_cnt == 0) {
+ dev_err(dma->dev, "There is no attached device\n");
+ mutex_unlock(&dma->lock);
+ return;
+ }
+
+ if (--dma->iommu_attached_cnt == 0) {
+ cam_smmu_ops(dma->iommu_hndl, CAM_SMMU_DETACH);
+ cam_smmu_destroy_handle(dma->iommu_hndl);
+ }
+
+ mutex_unlock(&dma->lock);
+}
+
+/*
+ * msm_jpegdma_hw_map_buffer - Map buffer to dma hw mmu.
+ * @dma: Pointer to dma device.
+ * @fd: Ion fd.
+ * @buf: dma buffer handle, for storing mapped buffer information.
+ *
+ * It will map ion fd to dma hw smmu.
+ */
+int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd,
+ struct msm_jpegdma_buf_handle *buf)
+{
+ int ret;
+
+ if (!dma || fd < 0)
+ return -EINVAL;
+
+ ret = msm_jpegdma_hw_attach_iommu(dma);
+ if (ret < 0)
+ goto error;
+
+ buf->dma = dma;
+ buf->fd = fd;
+
+ ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd,
+ CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size);
+ if (ret < 0) {
+ dev_err(dma->dev, "Can not get physical address\n");
+ goto error_get_phy;
+ }
+
+ return buf->size;
+
+error_get_phy:
+ msm_jpegdma_hw_detach_iommu(dma);
+error:
+ return -ENOMEM;
+}
+
+/*
+ * msm_jpegdma_hw_unmap_buffer - Unmap buffer from dma hw mmu.
+ * @buf: dma buffer handle, for storing mapped buffer information.
+ */
+void msm_jpegdma_hw_unmap_buffer(struct msm_jpegdma_buf_handle *buf)
+{
+ if (buf->size && buf->dma) {
+ cam_smmu_put_phy_addr(buf->dma->iommu_hndl,
+ buf->fd);
+ msm_jpegdma_hw_detach_iommu(buf->dma);
+ buf->size = 0;
+ }
+ buf->fd = -1;
+ buf->dma = NULL;
+}
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.h
new file mode 100644
index 0000000..29dbb90
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2015-2016, 2018, 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_JPEG_DMA_HW_H__
+#define __MSM_JPEG_DMA_HW_H__
+
+#include "msm_jpeg_dma_dev.h"
+
+int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size_cfg);
+
+int msm_jpegdma_hw_set_config(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_size_config *size_cfg,
+ struct msm_jpegdma_plane_config *plane_cfg);
+
+int msm_jpegdma_hw_start(struct msm_jpegdma_device *dma,
+ struct msm_jpegdma_addr *addr,
+ struct msm_jpegdma_plane *plane,
+ struct msm_jpegdma_speed *speed);
+
+int msm_jpegdma_hw_abort(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_update_bus_data(struct msm_jpegdma_device *dma,
+ u64 ab, u64 ib);
+
+int msm_jpegdma_hw_handle_irq(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_request_irq(struct platform_device *pdev,
+ struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_release_irq(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_release_mem_resources(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_mem_resources(struct platform_device *pdev,
+ struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_regulators(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_put_regulators(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_clocks(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_put_clocks(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_max_downscale(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_qos(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_put_qos(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_vbif(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_put_vbif(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_prefetch(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_put_prefetch(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get_capabilities(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_get(struct msm_jpegdma_device *dma);
+
+void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma);
+
+int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd,
+ struct msm_jpegdma_buf_handle *buf);
+
+void msm_jpegdma_hw_unmap_buffer(struct msm_jpegdma_buf_handle *buf);
+
+#endif /* __MSM_JPEG_DMA_HW_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_regs.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_regs.h
new file mode 100644
index 0000000..157c743
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_regs.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2015, 2018, 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_JPEGDMA_REGS_H__
+#define __MSM_JPEGDMA_REGS_H__
+
+#define MSM_JPEGDMA_HW_REVISION 0x00
+#define MSM_JPEGDMA_HW_CAPABILITY 0x04
+#define MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_BMSK 0x06
+#define MSM_JPEGDMA_HW_CAPABILITY_NUM_PIPES_SHFT 0x01
+
+#define MSM_JPEGDMA_IRQ_MASK_ADDR 0x0C
+#define MSM_JPEGDMA_IRQ_MASK_SESSION_DONE (1 << 0)
+#define MSM_JPEGDMA_IRQ_MASK_RD_BUF_DONE (1 << 1)
+#define MSM_JPEGDMA_IRQ_MASK_WR_BUF_DONE (1 << 5)
+#define MSM_JPEGDMA_IRQ_MASK_AXI_HALT (1 << 9)
+#define MSM_JPEGDMA_IRQ_MASK_RST_DONE (1 << 10)
+
+#define MSM_JPEGDMA_IRQ_STATUS 0x10
+#define MSM_JPEGDMA_IRQ_STATUS_SESSION_DONE (1 << 0)
+#define MSM_JPEGDMA_IRQ_STATUS_RD_BUF_DONE (1 << 1)
+#define MSM_JPEGDMA_IRQ_STATUS_WR_BUF_DONE (1 << 5)
+#define MSM_JPEGDMA_IRQ_STATUS_AXI_HALT (1 << 9)
+#define MSM_JPEGDMA_IRQ_STATUS_RST_DONE (1 << 10)
+
+#define MSM_JPEGDMA_IRQ_CLEAR_ADDR 0x14
+#define MSM_JPEGDMA_IRQ_CLEAR_BMSK 0xFFFFFFFF
+
+#define MSM_JPEGDMA_CORE_CFG_ADDR 0x18
+#define MSM_JPEGDMA_CMD_ADDR 0x1C
+
+#define MSM_JPEGDMA_CORE_CFG_TEST_BUS_ENABLE_SHFT 19
+#define MSM_JPEGDMA_CORE_CFG_BRIDGE_ENABLE_SHFT 6
+#define MSM_JPEGDMA_CORE_CFG_SCALE_1_ENABLE_SHFT 5
+#define MSM_JPEGDMA_CORE_CFG_SCALE_0_ENABLE_SHFT 4
+
+#define MSM_JPEGDMA_CORE_CFG_WE_1_ENABLE_SHFT 0x03
+#define MSM_JPEGDMA_CORE_CFG_WE_0_ENABLE_SHFT 0x02
+#define MSM_JPEGDMA_CORE_CFG_FE_1_ENABLE_SHFT 0x01
+#define MSM_JPEGDMA_CORE_CFG_FE_0_ENABLE_SHFT 0x00
+
+#define MSM_JPEGDMA_FE_0_CFG_ADDR 0x2C
+#define MSM_JPEGDMA_FE_1_CFG_ADDR 0x70
+#define MSM_JPEGDMA_FE_CFG_MAL_BOUNDARY_SHFT 25
+#define MSM_JPEGDMA_FE_CFG_MAL_EN_SHFT 21
+#define MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CBCR 0x03
+#define MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CR 0x02
+#define MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_CB 0x01
+#define MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_Y 0x00
+#define MSM_JPEGDMA_FE_CFG_PLN_BLOCK_TYPE_SHFT 19
+#define MSM_JPEGDMA_FE_CFG_BLOCK_WIDTH_SHFT 0x04
+#define MSM_JPEGDMA_FE_CFG_BURST_LENGTH_MAX_SHFT 0x00
+
+#define MSM_JPEGDMA_FE_RD_0_PNTR_ADDR 0x34
+#define MSM_JPEGDMA_FE_RD_1_PNTR_ADDR 0x78
+#define MSM_JPEGDMA_FE_RD_BUFFER_SIZE_0_ADDR 0x44
+#define MSM_JPEGDMA_FE_RD_BUFFER_SIZE_1_ADDR 0x88
+#define MSM_JPEGDMA_FE_RD_BUFFER_SIZE_HEIGHT_SHFT 16
+#define MSM_JPEGDMA_FE_RD_0_STRIDE_ADDR 0x48
+#define MSM_JPEGDMA_FE_RD_1_STRIDE_ADDR 0x8C
+#define MSM_JPEGDMA_FE_RD_0_HINIT_ADDR 0x4C
+#define MSM_JPEGDMA_FE_RD_1_HINIT_ADDR 0x90
+#define MSM_JPEGDMA_FE_RD_0_HINIT_INT_ADDR 0x50
+#define MSM_JPEGDMA_FE_RD_1_HINIT_INT_ADDR 0x94
+#define MSM_JPEGDMA_FE_RD_0_VINIT_INT_ADDR 0x58
+#define MSM_JPEGDMA_FE_RD_1_VINIT_INT_ADDR 0x9C
+
+#define MSM_JPEGDMA_WE_CFG_ADDR 0xB8
+#define MSM_JPEGDMA_WE_CFG_MAL_BOUNDARY_SHFT 0x08
+#define MSM_JPEGDMA_WE_CFG_MAL_EN_SHFT 0x07
+#define MSM_JPEGDMA_WE_CFG_BURST_LENGTH_MAX_SHFT 0x00
+#define MSM_JPEGDMA_WE_PLN_0_WR_PNTR_ADDR 0xBC
+#define MSM_JPEGDMA_WE_PLN_1_WR_PNTR_ADDR 0xEC
+#define MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_0_ADDR 0xC4
+#define MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_1_ADDR 0xF4
+#define MSM_JPEGDMA_WE_PLN_WR_BUFFER_SIZE_HEIGHT_SHFT 16
+#define MSM_JPEGDMA_WE_PLN_0_WR_STRIDE_ADDR 0xC8
+#define MSM_JPEGDMA_WE_PLN_1_WR_STRIDE_ADDR 0xF8
+#define MSM_JPEGDMA_WE_PLN_0_WR_CFG_0_ADDR 0xCC
+#define MSM_JPEGDMA_WE_PLN_1_WR_CFG_0_ADDR 0xFC
+#define MSM_JPEGDMA_WE_PLN_WR_CFG_0_BLOCKS_PER_ROW_SHFT 16
+#define MSM_JPEGDMA_WE_PLN_0_WR_CFG_1_ADDR 0xD0
+#define MSM_JPEGDMA_WE_PLN_1_WR_CFG_1_ADDR 0x100
+#define MSM_JPEGDMA_WE_PLN_WR_CFG_1_LAST_H_STEP_SHFT 16
+#define MSM_JPEGDMA_WE_PLN_0_WR_CFG_2_ADDR 0xD4
+#define MSM_JPEGDMA_WE_PLN_1_WR_CFG_2_ADDR 0x104
+#define MSM_JPEGDMA_WE_PLN_WR_CFG_2_LAST_V_STEP_SHFT 16
+#define MSM_JPEGDMA_WE_PLN_0_WR_CFG_3_ADDR 0xD8
+#define MSM_JPEGDMA_WE_PLN_1_WR_CFG_3_ADDR 0x108
+
+#define MSM_JPEGDMA_PP_0_SCALE_PHASEV_STEP_ADDR 0x19C
+#define MSM_JPEGDMA_PP_1_SCALE_PHASEV_STEP_ADDR 0x1BC
+#define MSM_JPEGDMA_PP_0_SCALE_PHASEH_STEP_ADDR 0x194
+#define MSM_JPEGDMA_PP_1_SCALE_PHASEH_STEP_ADDR 0x1B4
+#define MSM_JPEGDMA_PP_0_SCALE_CFG_ADDR 0x188
+#define MSM_JPEGDMA_PP_1_SCALE_CFG_ADDR 0x1A8
+#define MSM_JPEGDMA_PP_SCALE_CFG_VSCALE_ENABLE_SHFT 0x05
+#define MSM_JPEGDMA_PP_SCALE_CFG_HSCALE_ENABLE_SHFT 0x04
+
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MIN 0x190
+#define MSM_JPEGDMA_S0_MMU_PF_ADDR_MAX 0x198
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MIN 0x1A4
+#define MSM_JPEGDMA_S1_MMU_PF_ADDR_MAX 0x1AC
+
+#define MSM_JPEGDMA_CMD_CLEAR_READ_PLN_QUEUES 0x030
+#define MSM_JPEGDMA_CMD_CLEAR_WRITE_PLN_QUEUES 0x300
+
+#define MSM_HW_JPEGDMA_RESET 0x08
+#define MSM_HW_JPEGDMA_RESET_DEFAULT 0x32083
+
+#define MSM_JPEGDMA_RESET_CMD_BMSK 0xFFFFFFFF
+
+#endif /* __MSM_JPEG_DMA_REGS_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
new file mode 100644
index 0000000..121e42a5
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -0,0 +1,1484 @@
+/* Copyright (c) 2012-2018, 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/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <media/v4l2-fh.h>
+#include "msm.h"
+#include "msm_vb2.h"
+#include "msm_sd.h"
+#include "cam_hw_ops.h"
+#include <media/msmb_generic_buf_mgr.h>
+
+static struct v4l2_device *msm_v4l2_dev;
+static struct list_head ordered_sd_list;
+static struct mutex ordered_sd_mtx;
+static struct mutex v4l2_event_mtx;
+
+static struct pm_qos_request msm_v4l2_pm_qos_request;
+
+static struct msm_queue_head *msm_session_q;
+
+/* This variable represent daemon status
+ * true = daemon present (default state)
+ * false = daemon is NOT present
+ */
+bool is_daemon_status = true;
+
+/* config node envent queue */
+static struct v4l2_fh *msm_eventq;
+static spinlock_t msm_eventq_lock;
+
+static struct pid *msm_pid;
+static spinlock_t msm_pid_lock;
+
+static uint32_t gpu_limit;
+
+/*
+ * It takes 20 bytes + NULL character to write the
+ * largest decimal value of an uint64_t
+ */
+#define LOGSYNC_PACKET_SIZE 21
+
+#define msm_dequeue(queue, type, member) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = NULL; \
+ 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 = NULL; \
+ 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); \
+ kzfree(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 = NULL; \
+ 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); \
+ kzfree(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 = NULL; \
+ 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); \
+ kzfree(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 = NULL; \
+ 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 = NULL; \
+ 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)
+{
+ if (WARN_ON(!qhead))
+ return;
+
+ 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);
+}
+
+void msm_cam_copy_v4l2_subdev_fops(struct v4l2_file_operations *d1)
+{
+ *d1 = v4l2_subdev_fops;
+}
+EXPORT_SYMBOL(msm_cam_copy_v4l2_subdev_fops);
+
+static const struct v4l2_file_operations *msm_cam_get_v4l2_subdev_fops_ptr(
+ void)
+{
+ return &v4l2_subdev_fops;
+}
+
+/* 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;
+}
+
+static void msm_pm_qos_add_request(void)
+{
+ pr_info("%s: add request", __func__);
+ pm_qos_add_request(&msm_v4l2_pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+}
+
+static void msm_pm_qos_remove_request(void)
+{
+ pr_info("%s: remove request", __func__);
+ pm_qos_remove_request(&msm_v4l2_pm_qos_request);
+}
+
+void msm_pm_qos_update_request(int val)
+{
+ pr_info("%s: update request %d", __func__, val);
+ pm_qos_update_request(&msm_v4l2_pm_qos_request, val);
+}
+
+struct msm_session *msm_session_find(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 (WARN_ON(!session))
+ return NULL;
+ return session;
+}
+EXPORT_SYMBOL(msm_session_find);
+
+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;
+ spin_lock_init(&stream->stream_lock);
+ msm_enqueue(&session->stream_q, &stream->list);
+ session->stream_q.len++;
+
+ INIT_LIST_HEAD(&stream->queued_list);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_create_stream);
+
+void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session = NULL;
+ struct msm_stream *stream = NULL;
+ unsigned long flags;
+ int try_count = 0;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+
+ if (!session)
+ return;
+
+ while (1) {
+ unsigned long wl_flags;
+
+ if (try_count > 5) {
+ pr_err("%s : not able to delete stream %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ write_lock_irqsave(&session->stream_rwlock, wl_flags);
+ try_count++;
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream) {
+ write_unlock_irqrestore(&session->stream_rwlock,
+ wl_flags);
+ return;
+ }
+
+ if (msm_vb2_get_stream_state(stream) != 1) {
+ write_unlock_irqrestore(&session->stream_rwlock,
+ wl_flags);
+ continue;
+ }
+
+ spin_lock_irqsave(&(session->stream_q.lock), flags);
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+ stream = NULL;
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ write_unlock_irqrestore(&session->stream_rwlock, wl_flags);
+ break;
+ }
+
+}
+EXPORT_SYMBOL(msm_delete_stream);
+
+static void msm_sd_unregister_subdev(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+
+ sd->devnode = NULL;
+ kzfree(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 = msm_cam_get_v4l2_subdev_fops_ptr();
+ vdev->release = msm_sd_unregister_subdev;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ kzfree(vdev);
+ goto clean_up;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.dev.major = VIDEO_MAJOR;
+ sd->entity.info.dev.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;
+}
+
+static void msm_add_sd_in_position(struct msm_sd_subdev *msm_subdev,
+ struct list_head *sd_list)
+{
+ struct msm_sd_subdev *temp_sd;
+
+ list_for_each_entry(temp_sd, sd_list, list) {
+ if (temp_sd == msm_subdev) {
+ pr_err("%s :Fail to add the same sd %d\n",
+ __func__, __LINE__);
+ return;
+ }
+ if (msm_subdev->close_seq < temp_sd->close_seq) {
+ list_add_tail(&msm_subdev->list, &temp_sd->list);
+ return;
+ }
+ }
+ list_add_tail(&msm_subdev->list, sd_list);
+}
+
+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;
+
+ mutex_lock(&ordered_sd_mtx);
+ msm_add_sd_in_position(msm_subdev, &ordered_sd_list);
+ mutex_unlock(&ordered_sd_mtx);
+ return __msm_sd_register_subdev(&msm_subdev->sd);
+}
+EXPORT_SYMBOL(msm_sd_register);
+
+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;
+}
+EXPORT_SYMBOL(msm_sd_unregister);
+
+static struct v4l2_subdev *msm_sd_find(const char *name)
+{
+ unsigned long flags;
+ struct v4l2_subdev *subdev = NULL;
+ struct v4l2_subdev *subdev_out = 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)) {
+ subdev_out = subdev;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+
+ return subdev_out;
+}
+
+int msm_create_session(unsigned int session_id, struct video_device *vdev)
+{
+ struct msm_session *session = NULL;
+
+ if (!msm_session_q) {
+ pr_err("%s : session queue not available Line %d\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (session) {
+ pr_err("%s: Session exist session_id=%d\n",
+ __func__, session_id);
+ 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);
+ mutex_init(&session->lock);
+ mutex_init(&session->lock_q);
+ mutex_init(&session->close_lock);
+ rwlock_init(&session->stream_rwlock);
+
+ if (gpu_limit) {
+ session->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0);
+ if (session->sysfs_pwr_limit)
+ kgsl_pwr_limits_set_freq(session->sysfs_pwr_limit,
+ gpu_limit);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_create_session);
+
+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) {
+ pr_err("%s : Session queue not available Line %d\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session) {
+ pr_err("%s : Session not found Line %d\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ mutex_lock(&session->lock);
+ cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL);
+ if (!cmd_ack) {
+ mutex_unlock(&session->lock);
+ pr_err("%s : memory not available Line %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ msm_init_queue(&cmd_ack->command_q);
+ INIT_LIST_HEAD(&cmd_ack->list);
+ init_completion(&cmd_ack->wait_complete);
+ cmd_ack->stream_id = stream_id;
+
+ msm_enqueue(&session->command_ack_q, &cmd_ack->list);
+ session->command_ack_q.len++;
+ mutex_unlock(&session->lock);
+ return 0;
+}
+EXPORT_SYMBOL(msm_create_command_ack_q);
+
+void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_command_ack *cmd_ack;
+ unsigned long flags;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return;
+ mutex_lock(&session->lock);
+
+ 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) {
+ mutex_unlock(&session->lock);
+ return;
+ }
+
+ msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
+
+ spin_lock_irqsave(&(session->command_ack_q.lock), flags);
+ list_del_init(&cmd_ack->list);
+ kzfree(cmd_ack);
+ session->command_ack_q.len--;
+ spin_unlock_irqrestore(&(session->command_ack_q.lock), flags);
+ mutex_unlock(&session->lock);
+}
+EXPORT_SYMBOL(msm_delete_command_ack_q);
+
+static inline int __msm_sd_close_subdevs(struct msm_sd_subdev *msm_sd,
+ struct msm_sd_close_ioctl *sd_close)
+{
+ struct v4l2_subdev *sd;
+
+ sd = &msm_sd->sd;
+ pr_debug("%s: Shutting down subdev %s", __func__, sd->name);
+
+ v4l2_subdev_call(sd, core, ioctl, MSM_SD_SHUTDOWN, sd_close);
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+ return 0;
+}
+
+static inline int __msm_sd_notify_freeze_subdevs(struct msm_sd_subdev *msm_sd,
+ int enable)
+{
+ struct v4l2_subdev *sd;
+
+ sd = &msm_sd->sd;
+
+ if (enable)
+ v4l2_subdev_call(sd, core, ioctl, MSM_SD_NOTIFY_FREEZE, NULL);
+ else
+ v4l2_subdev_call(sd, core, ioctl, MSM_SD_UNNOTIFY_FREEZE, NULL);
+
+ return 0;
+}
+
+static inline int __msm_destroy_session_streams(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ unsigned long flags;
+
+ pr_err("%s: Error: Destroyed list is not empty\n", __func__);
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ INIT_LIST_HEAD(&stream->queued_list);
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ return 0;
+}
+
+static void msm_destroy_session_streams(struct msm_session *session)
+{
+
+ if (!session)
+ return;
+
+ msm_queue_traverse_action(&session->stream_q, struct msm_stream, list,
+ __msm_destroy_session_streams, NULL);
+
+ 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;
+
+ if (!(&cmd_ack->command_q))
+ return 0;
+
+ 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) || !(&session->command_ack_q))
+ return;
+
+ mutex_lock(&session->lock);
+ /* 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);
+
+ mutex_unlock(&session->lock);
+}
+
+int msm_destroy_session(unsigned int session_id)
+{
+ struct msm_session *session;
+ struct v4l2_subdev *buf_mgr_subdev;
+ struct msm_sd_close_ioctl session_info;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ if (gpu_limit && session->sysfs_pwr_limit) {
+ kgsl_pwr_limits_set_default(session->sysfs_pwr_limit);
+ kgsl_pwr_limits_del(session->sysfs_pwr_limit);
+ }
+
+ msm_destroy_session_streams(session);
+ msm_remove_session_cmd_ack_q(session);
+ mutex_destroy(&session->lock);
+ mutex_destroy(&session->lock_q);
+ mutex_destroy(&session->close_lock);
+ msm_delete_entry(msm_session_q, struct msm_session,
+ list, session);
+ buf_mgr_subdev = msm_sd_find("msm_buf_mngr");
+ if (buf_mgr_subdev) {
+ session_info.session = session_id;
+ session_info.stream = 0;
+ v4l2_subdev_call(buf_mgr_subdev, core, ioctl,
+ MSM_SD_SHUTDOWN, &session_info);
+ } else {
+ pr_err("%s: Buff manger device node is NULL\n", __func__);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_destroy_session);
+
+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);
+
+ return 0;
+}
+
+static int __msm_wakeup_all_cmdack_session_stream(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ struct msm_session *session = d2;
+ struct msm_command_ack *cmd_ack = NULL;
+ unsigned long spin_flags = 0;
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_queue_find_command_ack_q,
+ &stream->stream_id);
+ if (cmd_ack) {
+ spin_lock_irqsave(&(session->command_ack_q.lock),
+ spin_flags);
+ complete(&cmd_ack->wait_complete);
+ spin_unlock_irqrestore(&(session->command_ack_q.lock),
+ spin_flags);
+ }
+ return 0;
+}
+
+static int __msm_close_wakeup_all_cmdack_session(void *d1, void *d2)
+{
+ struct msm_stream *stream = NULL;
+ struct msm_session *session = d1;
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_wakeup_all_cmdack_session_stream, d1);
+ return 0;
+}
+
+static long msm_private_ioctl(struct file *file, void *fh,
+ bool valid_prio, unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct msm_v4l2_event_data *event_data = arg;
+ struct v4l2_event event;
+ struct msm_session *session;
+ unsigned int session_id;
+ unsigned int stream_id;
+ unsigned long spin_flags = 0;
+ struct msm_sd_subdev *msm_sd;
+
+ if (cmd == MSM_CAM_V4L2_IOCTL_DAEMON_DISABLED) {
+ is_daemon_status = false;
+ return 0;
+ }
+
+ if (!event_data)
+ return -EINVAL;
+
+ switch (cmd) {
+ case MSM_CAM_V4L2_IOCTL_NOTIFY:
+ case MSM_CAM_V4L2_IOCTL_CMD_ACK:
+ case MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG:
+ case MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR:
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ memset(&event, 0, sizeof(struct v4l2_event));
+ 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;
+ }
+ event.type = event_data->v4l2_event_type;
+ event.id = event_data->v4l2_event_id;
+ memcpy(&event.u.data, event_data,
+ sizeof(struct msm_v4l2_event_data));
+ v4l2_event_queue(session->event_q.vdev,
+ &event);
+ }
+ 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)) {
+ kzfree(ret_cmd);
+ rc = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&(session->command_ack_q.lock),
+ spin_flags);
+ event.type = event_data->v4l2_event_type;
+ event.id = event_data->v4l2_event_id;
+ memcpy(&event.u.data, event_data,
+ sizeof(struct msm_v4l2_event_data));
+ memcpy(&ret_cmd->event, &event, sizeof(struct v4l2_event));
+ msm_enqueue(&cmd_ack->command_q, &ret_cmd->list);
+ complete(&cmd_ack->wait_complete);
+ spin_unlock_irqrestore(&(session->command_ack_q.lock),
+ spin_flags);
+ }
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG: {
+ if (event_data->status) {
+ pr_err("%s:Notifying subdevs about potential sof freeze\n",
+ __func__);
+ } else {
+ pr_err("%s:Notifying subdevs about sof recover\n",
+ __func__);
+ }
+
+ mutex_lock(&ordered_sd_mtx);
+ if (!list_empty(&msm_v4l2_dev->subdevs)) {
+ list_for_each_entry(msm_sd, &ordered_sd_list, list)
+ __msm_sd_notify_freeze_subdevs(msm_sd,
+ event_data->status);
+ }
+ mutex_unlock(&ordered_sd_mtx);
+ }
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR:
+ /* send v4l2_event to HAL next*/
+ msm_queue_traverse_action(msm_session_q,
+ struct msm_session, list,
+ __msm_close_destry_session_notify_apps, NULL);
+ break;
+
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc;
+
+ mutex_lock(&v4l2_event_mtx);
+ rc = v4l2_event_unsubscribe(fh, sub);
+ mutex_unlock(&v4l2_event_mtx);
+
+ return rc;
+}
+
+static int msm_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc;
+
+ mutex_lock(&v4l2_event_mtx);
+ rc = v4l2_event_subscribe(fh, sub, 5, NULL);
+ mutex_unlock(&v4l2_event_mtx);
+
+ return rc;
+}
+
+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;
+
+ if (WARN_ON(!eventq))
+ goto err;
+
+ poll_wait(f, &eventq->wait, pll_table);
+
+ if (v4l2_event_pending(eventq))
+ rc = POLLIN | POLLRDNORM;
+
+err:
+ return rc;
+}
+
+static void msm_print_event_error(struct v4l2_event *event)
+{
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+
+ pr_err("Evt_type=%x Evt_id=%d Evt_cmd=%x\n", event->type,
+ event->id, event_data->command);
+ pr_err("Evt_session_id=%d Evt_stream_id=%d Evt_arg=%d\n",
+ event_data->session_id, event_data->stream_id,
+ event_data->arg_value);
+}
+
+/* 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);
+ pr_err("%s : msm event queue not available Line %d\n",
+ __func__, __LINE__);
+ 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)) {
+ pr_err("%s : session not found Line %d\n",
+ __func__, __LINE__);
+ return -EIO;
+ }
+ mutex_lock(&session->lock);
+ 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)) {
+ mutex_unlock(&session->lock);
+ pr_err("%s : cmd_ack not found Line %d\n",
+ __func__, __LINE__);
+ return -EIO;
+ }
+
+ /*re-init wait_complete */
+ reinit_completion(&cmd_ack->wait_complete);
+
+ v4l2_event_queue(vdev, event);
+
+ if (timeout < 0) {
+ mutex_unlock(&session->lock);
+ pr_debug("%s : timeout cannot be negative Line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ /* should wait on session based condition */
+ rc = wait_for_completion_timeout(&cmd_ack->wait_complete,
+ msecs_to_jiffies(timeout));
+
+
+ if (list_empty_careful(&cmd_ack->command_q.list)) {
+ if (!rc) {
+ pr_err("%s: Timed out\n", __func__);
+ msm_print_event_error(event);
+ mutex_unlock(&session->lock);
+ return -ETIMEDOUT;
+ }
+ pr_err("%s: Error: No timeout but list empty!",
+ __func__);
+ msm_print_event_error(event);
+ mutex_unlock(&session->lock);
+ return -EINVAL;
+ }
+
+ cmd = msm_dequeue(&cmd_ack->command_q,
+ struct msm_command, list);
+ if (!cmd) {
+ mutex_unlock(&session->lock);
+ pr_err("%s : cmd dequeue failed Line %d\n",
+ __func__, __LINE__);
+ 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)) {
+ pr_err("%s : Either event type or id didnot match Line %d\n",
+ __func__, __LINE__);
+ pr_err("%s : event->type %d event->id %d\n", __func__,
+ event->type, event->id);
+ pr_err("%s : cmd->event.type %d cmd->event.id %d\n", __func__,
+ cmd->event.type, cmd->event.id);
+ rc = -EINVAL;
+ }
+
+ *event = cmd->event;
+
+ kzfree(cmd);
+ mutex_unlock(&session->lock);
+ return rc;
+}
+EXPORT_SYMBOL(msm_post_event);
+
+static int msm_close(struct file *filep)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct msm_sd_close_ioctl sd_close;
+ struct msm_sd_subdev *msm_sd;
+
+ /*stop all hardware blocks immediately*/
+ mutex_lock(&ordered_sd_mtx);
+ if (!list_empty(&msm_v4l2_dev->subdevs))
+ list_for_each_entry(msm_sd, &ordered_sd_list, list)
+ __msm_sd_close_subdevs(msm_sd, &sd_close);
+ mutex_unlock(&ordered_sd_mtx);
+
+ /* remove msm_v4l2_pm_qos_request */
+ msm_pm_qos_remove_request();
+
+ /* send v4l2_event to HAL next*/
+ msm_queue_traverse_action(msm_session_q, struct msm_session, list,
+ __msm_close_destry_session_notify_apps, NULL);
+
+ msm_queue_traverse_action(msm_session_q, struct msm_session, list,
+ __msm_close_wakeup_all_cmdack_session, NULL);
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ msm_eventq = NULL;
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ v4l2_fh_release(filep);
+
+ 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 = -1;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+
+ if (WARN_ON(!pvdev))
+ return rc;
+
+ /* !!! only ONE open is allowed !!! */
+ if (atomic_cmpxchg(&pvdev->opened, 0, 1))
+ return -EBUSY;
+
+ 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);
+
+ /* register msm_v4l2_pm_qos_request */
+ msm_pm_qos_add_request();
+ return rc;
+}
+
+static struct v4l2_file_operations msm_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .poll = msm_poll,
+ .release = msm_close,
+ .unlocked_ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = video_ioctl2,
+#endif
+};
+
+struct msm_session *msm_get_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 ERR_PTR(-EINVAL);
+
+ return session;
+}
+EXPORT_SYMBOL(msm_get_session);
+
+
+struct msm_stream *msm_get_stream(struct msm_session *session,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+
+ 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;
+}
+EXPORT_SYMBOL(msm_get_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;
+}
+EXPORT_SYMBOL(msm_get_stream_vb2q);
+
+struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+ unsigned long flags1;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&msm_session_q->lock, flags1);
+ list_for_each_entry(session, &(msm_session_q->list), list) {
+ spin_lock_irqsave(&(session->stream_q.lock), flags2);
+ list_for_each_entry(
+ stream, &(session->stream_q.list), list) {
+ if (stream->vb2_q == q) {
+ spin_unlock_irqrestore
+ (&(session->stream_q.lock), flags2);
+ spin_unlock_irqrestore
+ (&msm_session_q->lock, flags1);
+ return stream;
+ }
+ }
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags2);
+ }
+ spin_unlock_irqrestore(&msm_session_q->lock, flags1);
+ return NULL;
+}
+EXPORT_SYMBOL(msm_get_stream_from_vb2q);
+
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+ unsigned long flags1;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&msm_session_q->lock, flags1);
+ list_for_each_entry(session, &(msm_session_q->list), list) {
+ spin_lock_irqsave(&(session->stream_q.lock), flags2);
+ list_for_each_entry(
+ stream, &(session->stream_q.list), list) {
+ if (stream->vb2_q == q) {
+ spin_unlock_irqrestore
+ (&(session->stream_q.lock), flags2);
+ spin_unlock_irqrestore
+ (&msm_session_q->lock, flags1);
+ return session;
+ }
+ }
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags2);
+ }
+ spin_unlock_irqrestore(&msm_session_q->lock, flags1);
+ return NULL;
+}
+EXPORT_SYMBOL(msm_get_session_from_vb2q);
+
+
+#ifdef CONFIG_COMPAT
+long msm_copy_camera_private_ioctl_args(unsigned long arg,
+ struct msm_camera_private_ioctl_arg *k_ioctl,
+ void __user **tmp_compat_ioctl_ptr)
+{
+ struct msm_camera_private_ioctl_arg up_ioctl;
+
+ if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr))
+ return -EIO;
+
+ if (copy_from_user(&up_ioctl,
+ (void __user *)arg,
+ sizeof(struct msm_camera_private_ioctl_arg)))
+ return -EFAULT;
+
+ k_ioctl->id = up_ioctl.id;
+ k_ioctl->size = up_ioctl.size;
+ k_ioctl->result = up_ioctl.result;
+ k_ioctl->reserved = up_ioctl.reserved;
+ *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl.ioctl_ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_copy_camera_private_ioctl_args);
+#endif
+
+static void msm_sd_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ int rc = 0;
+ struct v4l2_subdev *subdev = NULL;
+
+ if (WARN_ON(!sd))
+ return;
+
+ if (WARN_ON(!arg))
+ return;
+
+ /* 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 ssize_t write_logsync(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[LOGSYNC_PACKET_SIZE] = {0};
+ uint64_t seq_num = 0;
+ int ret;
+
+ if (copy_from_user(lbuf, buf, sizeof(lbuf)))
+ return -EFAULT;
+
+ ret = kstrtoull(lbuf, 0, &seq_num);
+ if (ret != 1)
+ pr_err("LOGSYNC (Kernel): Bad or malformed sequence number\n");
+ else
+ pr_debug("LOGSYNC (Kernel): seq_num = %llu\n", seq_num);
+
+ return count;
+}
+
+
+static const struct file_operations logsync_fops = {
+ .write = write_logsync,
+};
+
+static int msm_probe(struct platform_device *pdev)
+{
+ struct msm_video_device *pvdev = NULL;
+ static struct dentry *cam_debugfs_root;
+ 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;
+ }
+ media_device_init(msm_v4l2_dev->mdev);
+ 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_pads_init(&pvdev->vdev->entity,
+ 0, NULL)) < 0))
+ goto entity_fail;
+
+ pvdev->vdev->entity.function = MEDIA_ENT_F_IO_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);
+ mutex_init(&ordered_sd_mtx);
+ mutex_init(&v4l2_event_mtx);
+ INIT_LIST_HEAD(&ordered_sd_list);
+
+ cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR,
+ NULL);
+ if (!cam_debugfs_root) {
+ pr_warn("NON-FATAL: failed to create logsync base directory\n");
+ } else {
+ if (!debugfs_create_file(MSM_CAM_LOGSYNC_FILE_NAME,
+ 0660,
+ cam_debugfs_root,
+ NULL,
+ &logsync_fops))
+ pr_warn("NON-FATAL: failed to create logsync debugfs file\n");
+ }
+
+ rc = cam_ahb_clk_init(pdev);
+ if (rc < 0) {
+ pr_err("%s: failed to register ahb clocks\n", __func__);
+ goto v4l2_fail;
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "qcom,gpu-limit", &gpu_limit);
+
+ 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:
+ kzfree(msm_v4l2_dev->mdev);
+mdev_fail:
+#endif
+ video_device_release(pvdev->vdev);
+video_fail:
+ kzfree(pvdev);
+pvdev_fail:
+ kzfree(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/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
new file mode 100644
index 0000000..2ada45e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -0,0 +1,151 @@
+/* Copyright (c) 2012-2018, 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/completion.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/msm_kgsl.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/msmb_camera.h>
+
+/* Setting MAX timeout to 6.5seconds considering
+ * backend will operate @ .6fps in certain usecases
+ * like Long exposure usecase and isp needs max of 2 frames
+ * to stop the hardware which will be around 3 seconds
+ */
+#define MSM_POST_EVT_TIMEOUT 6500
+#define MSM_POST_EVT_NOTIMEOUT 0xFFFFFFFF
+#define MSM_CAMERA_STREAM_CNT_BITS 32
+
+#define CAMERA_DISABLE_PC_LATENCY 100
+#define CAMERA_ENABLE_PC_LATENCY PM_QOS_DEFAULT_VALUE
+
+extern bool is_daemon_status;
+
+struct msm_video_device {
+ struct video_device *vdev;
+ atomic_t opened;
+ struct mutex video_drvdata_mutex;
+};
+
+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;
+ struct completion wait_complete;
+ int stream_id;
+};
+
+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;
+ struct mutex lock;
+ struct mutex lock_q;
+ struct mutex close_lock;
+ rwlock_t stream_rwlock;
+ struct kgsl_pwr_limit *sysfs_pwr_limit;
+};
+
+static inline bool msm_is_daemon_present(void)
+{
+ return is_daemon_status;
+}
+
+void msm_pm_qos_update_request(int val);
+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_session *msm_get_session(unsigned int session_id);
+struct msm_stream *msm_get_stream(struct msm_session *session,
+ unsigned int stream_id);
+struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
+ unsigned int stream_id);
+struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q);
+struct msm_session *msm_session_find(unsigned int session_id);
+#ifdef CONFIG_COMPAT
+long msm_copy_camera_private_ioctl_args(unsigned long arg,
+ struct msm_camera_private_ioctl_arg *k_ioctl,
+ void __user **tmp_compat_ioctl_ptr);
+#endif
+#endif /*_MSM_H */
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/Makefile b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/Makefile
new file mode 100644
index 0000000..8832457
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/Makefile
@@ -0,0 +1,2 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+obj-$(CONFIG_MSMB_CAMERA) += msm_generic_buf_mgr.o
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
new file mode 100644
index 0000000..8a91c3e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -0,0 +1,903 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-BUFMGR %s:%d " fmt, __func__, __LINE__
+
+#include "msm_generic_buf_mgr.h"
+
+static struct msm_buf_mngr_device *msm_buf_mngr_dev;
+
+struct v4l2_subdev *msm_buf_mngr_get_subdev(void)
+{
+ return &msm_buf_mngr_dev->subdev.sd;
+}
+
+static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned int i;
+ struct msm_buf_mngr_user_buf_cont_info *cbuf, *cont_save;
+
+ list_for_each_entry_safe(cbuf, cont_save, &dev->cont_qhead, entry) {
+ if ((cbuf->sessid == buf_info->session_id) &&
+ (cbuf->index == buf_info->index) &&
+ (cbuf->strid == buf_info->stream_id)) {
+ buf_info->user_buf.buf_cnt = cbuf->paddr->buf_cnt;
+ if (buf_info->user_buf.buf_cnt >
+ MSM_CAMERA_MAX_USER_BUFF_CNT) {
+ pr_err("Invalid cnt%d,%d,%d\n",
+ cbuf->paddr->buf_cnt,
+ buf_info->session_id,
+ buf_info->stream_id);
+ return -EINVAL;
+ }
+ for (i = 0 ; i < buf_info->user_buf.buf_cnt; i++) {
+ buf_info->user_buf.buf_idx[i] =
+ cbuf->paddr->buf_idx[i];
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev,
+ void *argp)
+{
+ unsigned long flags;
+ int32_t rc = 0;
+ struct msm_buf_mngr_info *buf_info =
+ (struct msm_buf_mngr_info *)argp;
+ struct msm_get_bufs *new_entry =
+ kzalloc(sizeof(struct msm_get_bufs), GFP_KERNEL);
+
+ if (!new_entry) {
+ pr_err("%s:No mem\n", __func__);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&new_entry->entry);
+ new_entry->vb2_v4l2_buf = dev->vb2_ops.get_buf(buf_info->session_id,
+ buf_info->stream_id);
+ if (!new_entry->vb2_v4l2_buf) {
+ pr_debug("%s:Get buf is null\n", __func__);
+ kfree(new_entry);
+ return -EINVAL;
+ }
+ new_entry->session_id = buf_info->session_id;
+ new_entry->stream_id = buf_info->stream_id;
+ new_entry->index = new_entry->vb2_v4l2_buf->vb2_buf.index;
+ spin_lock_irqsave(&dev->buf_q_spinlock, flags);
+ list_add_tail(&new_entry->entry, &dev->buf_qhead);
+ spin_unlock_irqrestore(&dev->buf_q_spinlock, flags);
+ buf_info->index = new_entry->vb2_v4l2_buf->vb2_buf.index;
+ if (buf_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) {
+ mutex_lock(&dev->cont_mutex);
+ if (!list_empty(&dev->cont_qhead)) {
+ rc = msm_buf_mngr_hdl_cont_get_buf(dev, buf_info);
+ } else {
+ pr_err("Nothing mapped in user buf for %d,%d\n",
+ buf_info->session_id, buf_info->stream_id);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dev->cont_mutex);
+ }
+ return rc;
+}
+
+static int32_t msm_buf_mngr_get_buf_by_idx(struct msm_buf_mngr_device *dev,
+ void *argp)
+{
+ unsigned long flags;
+ int32_t rc = 0;
+ struct msm_buf_mngr_info *buf_info =
+ (struct msm_buf_mngr_info *)argp;
+ struct msm_get_bufs *new_entry =
+ kzalloc(sizeof(struct msm_get_bufs), GFP_KERNEL);
+
+ if (!new_entry)
+ return -ENOMEM;
+
+ if (!buf_info) {
+ kfree(new_entry);
+ return -EIO;
+ }
+
+ INIT_LIST_HEAD(&new_entry->entry);
+ new_entry->vb2_v4l2_buf = dev->vb2_ops.get_buf_by_idx(
+ buf_info->session_id, buf_info->stream_id, buf_info->index);
+ if (!new_entry->vb2_v4l2_buf) {
+ pr_debug("%s:Get buf is null\n", __func__);
+ kfree(new_entry);
+ return -EINVAL;
+ }
+ new_entry->session_id = buf_info->session_id;
+ new_entry->stream_id = buf_info->stream_id;
+ new_entry->index = new_entry->vb2_v4l2_buf->vb2_buf.index;
+ spin_lock_irqsave(&dev->buf_q_spinlock, flags);
+ list_add_tail(&new_entry->entry, &dev->buf_qhead);
+ spin_unlock_irqrestore(&dev->buf_q_spinlock, flags);
+ if (buf_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) {
+ mutex_lock(&dev->cont_mutex);
+ if (!list_empty(&dev->cont_qhead)) {
+ rc = msm_buf_mngr_hdl_cont_get_buf(dev, buf_info);
+ } else {
+ pr_err("Nothing mapped in user buf for %d,%d\n",
+ buf_info->session_id, buf_info->stream_id);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dev->cont_mutex);
+ }
+ return rc;
+}
+
+static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id) &&
+ (bufs->index == buf_info->index)) {
+ ret = buf_mngr_dev->vb2_ops.buf_done
+ (bufs->vb2_v4l2_buf,
+ buf_info->session_id,
+ buf_info->stream_id,
+ buf_info->frame_id,
+ &buf_info->timestamp,
+ buf_info->reserved);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ return ret;
+}
+
+
+static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id) &&
+ (bufs->index == buf_info->index)) {
+ ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_v4l2_buf,
+ buf_info->session_id, buf_info->stream_id);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ return ret;
+}
+
+static int32_t msm_generic_buf_mngr_flush(
+ struct msm_buf_mngr_device *buf_mngr_dev,
+ struct msm_buf_mngr_info *buf_info)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+ int32_t ret = -EINVAL;
+ struct timeval ts;
+
+ spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
+ /*
+ * Sanity check on client buf list, remove buf mgr
+ * queue entries in case any
+ */
+ list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
+ if ((bufs->session_id == buf_info->session_id) &&
+ (bufs->stream_id == buf_info->stream_id)) {
+ ret = buf_mngr_dev->vb2_ops.buf_done(bufs->vb2_v4l2_buf,
+ buf_info->session_id,
+ buf_info->stream_id, 0, &ts, 0);
+ pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n",
+ buf_info->stream_id, bufs->index,
+ ret);
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ }
+ }
+ spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
+ /* Flush the remaining vb2 buffers in stream list */
+ ret = buf_mngr_dev->vb2_ops.flush_buf(buf_info->session_id,
+ buf_info->stream_id);
+ return ret;
+}
+
+static int32_t msm_buf_mngr_find_cont_stream(struct msm_buf_mngr_device *dev,
+ uint32_t *cnt, uint32_t *tstream,
+ struct msm_sd_close_ioctl *session)
+{
+ struct msm_buf_mngr_user_buf_cont_info *cont_bufs, *cont_save;
+ int32_t ret = -1;
+
+ list_for_each_entry_safe(cont_bufs,
+ cont_save, &dev->cont_qhead, entry) {
+ if (cont_bufs->sessid == session->session) {
+ *cnt = cont_bufs->cnt;
+ *tstream = cont_bufs->strid;
+ return 0;
+ }
+ }
+ return ret;
+}
+
+static void msm_buf_mngr_contq_listdel(struct msm_buf_mngr_device *dev,
+ uint32_t session, int32_t stream,
+ bool unmap, uint32_t cnt)
+{
+ struct msm_buf_mngr_user_buf_cont_info *cont_bufs, *cont_save;
+
+ list_for_each_entry_safe(cont_bufs,
+ cont_save, &dev->cont_qhead, entry) {
+ if ((cont_bufs->sessid == session) &&
+ (cont_bufs->strid == stream)) {
+ if (cnt == 1 && unmap == 1) {
+ ion_unmap_kernel(dev->ion_client,
+ cont_bufs->ion_handle);
+ ion_free(dev->ion_client,
+ cont_bufs->ion_handle);
+ }
+ list_del_init(&cont_bufs->entry);
+ kfree(cont_bufs);
+ cnt--;
+ }
+ }
+ if (cnt != 0)
+ pr_err("Buffers pending cnt = %d\n", cnt);
+}
+
+static void msm_buf_mngr_contq_cleanup(struct msm_buf_mngr_device *dev,
+ struct msm_sd_close_ioctl *session)
+{
+ int32_t stream = -1, found = -1;
+ uint32_t cnt = 0;
+
+ do {
+ found = msm_buf_mngr_find_cont_stream(dev, &cnt,
+ &stream, session);
+ if (found == -1)
+ break;
+ msm_buf_mngr_contq_listdel(dev, session->session,
+ stream, 1, cnt);
+ } while (found == 0);
+}
+
+static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *dev,
+ struct msm_sd_close_ioctl *session)
+{
+ unsigned long flags;
+ struct msm_get_bufs *bufs, *save;
+
+ if (WARN_ON(!dev))
+ return;
+ if (WARN_ON(!session))
+ return;
+
+ spin_lock_irqsave(&dev->buf_q_spinlock, flags);
+ if (!list_empty(&dev->buf_qhead)) {
+ list_for_each_entry_safe(bufs,
+ save, &dev->buf_qhead, entry) {
+ pr_info("%s: Delete invalid bufs =%pK, session_id=%u, bufs->ses_id=%d, str_id=%d, idx=%d\n",
+ __func__, (void *)bufs, session->session,
+ bufs->session_id, bufs->stream_id,
+ bufs->index);
+ if (session->session == bufs->session_id) {
+ list_del_init(&bufs->entry);
+ kfree(bufs);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dev->buf_q_spinlock, flags);
+ mutex_lock(&dev->cont_mutex);
+ if (!list_empty(&dev->cont_qhead))
+ msm_buf_mngr_contq_cleanup(dev, session);
+ mutex_unlock(&dev->cont_mutex);
+}
+
+static int msm_buf_mngr_handle_cont_cmd(struct msm_buf_mngr_device *dev,
+ struct msm_buf_mngr_main_cont_info
+ *cont_cmd)
+{
+ int rc = 0, i = 0;
+ struct ion_handle *ion_handle = NULL;
+ struct msm_camera_user_buf_cont_t *iaddr, *temp_addr;
+ struct msm_buf_mngr_user_buf_cont_info *new_entry, *bufs, *save;
+ size_t size;
+
+ if ((cont_cmd->cmd >= MSM_CAMERA_BUF_MNGR_CONT_MAX) ||
+ (cont_cmd->cmd < 0) ||
+ (cont_cmd->cnt > VB2_MAX_FRAME) ||
+ (cont_cmd->cont_fd < 0)) {
+ pr_debug("Invalid arg passed Cmd:%d, cnt:%d, fd:%d\n",
+ cont_cmd->cmd, cont_cmd->cnt,
+ cont_cmd->cont_fd);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->cont_mutex);
+
+ if (cont_cmd->cmd == MSM_CAMERA_BUF_MNGR_CONT_MAP) {
+ if (!list_empty(&dev->cont_qhead)) {
+ list_for_each_entry_safe(bufs,
+ save, &dev->cont_qhead, entry) {
+ if ((bufs->sessid == cont_cmd->session_id) &&
+ (bufs->strid == cont_cmd->stream_id)) {
+ pr_err("Map exist %d,%d unmap first\n",
+ cont_cmd->session_id,
+ cont_cmd->stream_id);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+ }
+ ion_handle = ion_import_dma_buf_fd(dev->ion_client,
+ cont_cmd->cont_fd);
+ if (IS_ERR_OR_NULL(ion_handle)) {
+ pr_err("Failed to create ion handle for fd %d\n",
+ cont_cmd->cont_fd);
+ rc = -EINVAL;
+ goto end;
+ }
+ if (ion_handle_get_size(dev->ion_client,
+ ion_handle, &size) < 0) {
+ pr_err("Get ion size failed\n");
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ if ((size == 0) || (size <
+ (sizeof(struct msm_camera_user_buf_cont_t) *
+ cont_cmd->cnt))) {
+ pr_err("Invalid or zero size ION buffer %zu\n", size);
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ iaddr = ion_map_kernel(dev->ion_client, ion_handle);
+ if (IS_ERR_OR_NULL(iaddr)) {
+ pr_err("Mapping cont buff failed\n");
+ rc = -EINVAL;
+ goto free_ion_handle;
+ }
+ for (i = 0; i < cont_cmd->cnt; i++) {
+ temp_addr = iaddr + i;
+ if (temp_addr->buf_cnt >
+ MSM_CAMERA_MAX_USER_BUFF_CNT) {
+ pr_err("%s:Invalid buf_cnt:%d for cont:%d\n",
+ __func__, temp_addr->buf_cnt, i);
+ rc = -EINVAL;
+ goto free_list;
+ }
+ new_entry = kzalloc(sizeof(
+ struct msm_buf_mngr_user_buf_cont_info),
+ GFP_KERNEL);
+ if (!new_entry) {
+ pr_err("%s:No mem\n", __func__);
+ rc = -ENOMEM;
+ goto free_list;
+ }
+ INIT_LIST_HEAD(&new_entry->entry);
+ new_entry->sessid = cont_cmd->session_id;
+ new_entry->strid = cont_cmd->stream_id;
+ new_entry->index = i;
+ new_entry->main_fd = cont_cmd->cont_fd;
+ new_entry->ion_handle = ion_handle;
+ new_entry->cnt = cont_cmd->cnt;
+ new_entry->paddr = temp_addr;
+ list_add_tail(&new_entry->entry, &dev->cont_qhead);
+ }
+ goto end;
+ } else if (cont_cmd->cmd == MSM_CAMERA_BUF_MNGR_CONT_UNMAP) {
+ if (!list_empty(&dev->cont_qhead)) {
+ msm_buf_mngr_contq_listdel(dev, cont_cmd->session_id,
+ cont_cmd->stream_id, 1, cont_cmd->cnt);
+ } else {
+ pr_err("Nothing mapped for %d,%d\n",
+ cont_cmd->session_id, cont_cmd->stream_id);
+ rc = -EINVAL;
+ }
+ goto end;
+ }
+
+free_list:
+ if (i != 0) {
+ if (!list_empty(&dev->cont_qhead)) {
+ msm_buf_mngr_contq_listdel(dev, cont_cmd->session_id,
+ cont_cmd->stream_id, 0, i);
+ }
+ }
+ ion_unmap_kernel(dev->ion_client, ion_handle);
+free_ion_handle:
+ ion_free(dev->ion_client, ion_handle);
+end:
+ mutex_unlock(&dev->cont_mutex);
+ return rc;
+}
+
+static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ return rc;
+}
+
+static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ return rc;
+}
+
+static int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp)
+{
+ int rc = 0;
+
+ if (!msm_buf_mngr_dev)
+ return -ENODEV;
+ if (!argp)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ rc = msm_buf_mngr_get_buf(msm_buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ rc = msm_buf_mngr_buf_done(msm_buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
+ rc = msm_buf_mngr_put_buf(msm_buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: {
+ struct msm_camera_private_ioctl_arg *k_ioctl = argp;
+
+ switch (k_ioctl->id) {
+ case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: {
+ struct msm_buf_mngr_info *tmp = NULL;
+
+ if (!k_ioctl->ioctl_ptr)
+ return -EINVAL;
+ if (k_ioctl->size != sizeof(struct msm_buf_mngr_info))
+ return -EINVAL;
+
+ MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr,
+ sizeof(tmp));
+ rc = msm_buf_mngr_get_buf_by_idx(msm_buf_mngr_dev,
+ tmp);
+ }
+ break;
+ default:
+ pr_debug("unimplemented id %d", k_ioctl->id);
+ return -EINVAL;
+ }
+ break;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return rc;
+}
+
+int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct)
+{
+ if (!msm_buf_mngr_dev)
+ return -ENODEV;
+ if (!cb_struct)
+ return -EINVAL;
+
+ cb_struct->msm_cam_buf_mgr_ops = msm_cam_buf_mgr_ops;
+ return 0;
+}
+
+static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ void *argp = arg;
+
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: {
+ struct msm_camera_private_ioctl_arg k_ioctl, *ptr;
+
+ if (!arg)
+ return -EINVAL;
+ ptr = arg;
+ k_ioctl = *ptr;
+ switch (k_ioctl.id) {
+ case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: {
+
+ if (k_ioctl.size != sizeof(struct msm_buf_mngr_info))
+ return -EINVAL;
+ if (!k_ioctl.ioctl_ptr)
+ return -EINVAL;
+#ifndef CONFIG_COMPAT
+ {
+ struct msm_buf_mngr_info buf_info, *tmp = NULL;
+
+ MSM_CAM_GET_IOCTL_ARG_PTR(&tmp,
+ &k_ioctl.ioctl_ptr, sizeof(tmp));
+ if (copy_from_user(&buf_info, tmp,
+ sizeof(struct msm_buf_mngr_info))) {
+ return -EFAULT;
+ }
+ k_ioctl.ioctl_ptr = (uintptr_t)&buf_info;
+ }
+#endif
+ argp = &k_ioctl;
+ rc = msm_cam_buf_mgr_ops(cmd, argp);
+ }
+ break;
+ default:
+ pr_debug("unimplemented id %d", k_ioctl.id);
+ return -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
+ rc = msm_cam_buf_mgr_ops(cmd, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_INIT:
+ rc = msm_generic_buf_mngr_open(sd, NULL);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_DEINIT:
+ rc = msm_generic_buf_mngr_close(sd, NULL);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case VIDIOC_MSM_BUF_MNGR_FLUSH:
+ rc = msm_generic_buf_mngr_flush(buf_mngr_dev, argp);
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN:
+ msm_buf_mngr_sd_shutdown(buf_mngr_dev, argp);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD:
+ rc = msm_buf_mngr_handle_cont_cmd(buf_mngr_dev, argp);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_camera_buf_mgr_fetch_buf_info(
+ struct msm_buf_mngr_info32_t *buf_info32,
+ struct msm_buf_mngr_info *buf_info, unsigned long arg)
+{
+ if (!arg || !buf_info32 || !buf_info)
+ return -EINVAL;
+
+ if (copy_from_user(buf_info32, (void __user *)arg,
+ sizeof(struct msm_buf_mngr_info32_t)))
+ return -EFAULT;
+
+ buf_info->session_id = buf_info32->session_id;
+ buf_info->stream_id = buf_info32->stream_id;
+ buf_info->frame_id = buf_info32->frame_id;
+ buf_info->index = buf_info32->index;
+ buf_info->timestamp.tv_sec = (long) buf_info32->timestamp.tv_sec;
+ buf_info->timestamp.tv_usec = (long) buf_info32->
+ timestamp.tv_usec;
+ buf_info->reserved = buf_info32->reserved;
+ buf_info->type = buf_info32->type;
+ return 0;
+}
+
+static long msm_camera_buf_mgr_update_buf_info(
+ struct msm_buf_mngr_info32_t *buf_info32,
+ struct msm_buf_mngr_info *buf_info, unsigned long arg)
+{
+ if (!arg || !buf_info32 || !buf_info)
+ return -EINVAL;
+
+ buf_info32->session_id = buf_info->session_id;
+ buf_info32->stream_id = buf_info->stream_id;
+ buf_info32->index = buf_info->index;
+ buf_info32->timestamp.tv_sec = (int32_t) buf_info->
+ timestamp.tv_sec;
+ buf_info32->timestamp.tv_usec = (int32_t) buf_info->timestamp.
+ tv_usec;
+ buf_info32->reserved = buf_info->reserved;
+ buf_info32->type = buf_info->type;
+ buf_info32->user_buf.buf_cnt = buf_info->user_buf.buf_cnt;
+ memcpy(&buf_info32->user_buf.buf_idx,
+ &buf_info->user_buf.buf_idx,
+ sizeof(buf_info->user_buf.buf_idx));
+ if (copy_to_user((void __user *)arg, buf_info32,
+ sizeof(struct msm_buf_mngr_info32_t)))
+ return -EFAULT;
+ return 0;
+}
+static long msm_camera_buf_mgr_internal_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ long rc = 0;
+ struct msm_camera_private_ioctl_arg k_ioctl;
+ void __user *tmp_compat_ioctl_ptr = NULL;
+
+ rc = msm_copy_camera_private_ioctl_args(arg,
+ &k_ioctl, &tmp_compat_ioctl_ptr);
+ if (rc < 0) {
+ pr_err("Subdev cmd %d failed\n", cmd);
+ return rc;
+ }
+
+ switch (k_ioctl.id) {
+ case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: {
+ struct msm_buf_mngr_info32_t buf_info32;
+ struct msm_buf_mngr_info buf_info;
+
+ if (k_ioctl.size != sizeof(struct msm_buf_mngr_info32_t)) {
+ pr_err("Invalid size for id %d with size %d",
+ k_ioctl.id, k_ioctl.size);
+ return -EINVAL;
+ }
+ if (!tmp_compat_ioctl_ptr) {
+ pr_err("Invalid ptr for id %d", k_ioctl.id);
+ return -EINVAL;
+ }
+ k_ioctl.ioctl_ptr = (__u64)&buf_info;
+ k_ioctl.size = sizeof(struct msm_buf_mngr_info);
+ rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info,
+ (unsigned long)tmp_compat_ioctl_ptr);
+ if (rc < 0) {
+ pr_err("Fetch buf info failed for cmd=%d", cmd);
+ return rc;
+ }
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &k_ioctl);
+ if (rc < 0) {
+ pr_err("Subdev cmd %d failed for id %d", cmd,
+ k_ioctl.id);
+ return rc;
+ }
+ }
+ break;
+ default:
+ pr_debug("unimplemented id %d", k_ioctl.id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ int32_t rc = 0;
+
+ /* Convert 32 bit IOCTL ID's to 64 bit IOCTL ID's
+ * except VIDIOC_MSM_CPP_CFG32, which needs special
+ * processing
+ */
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF32:
+ cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE32:
+ cmd = VIDIOC_MSM_BUF_MNGR_BUF_DONE;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF32:
+ cmd = VIDIOC_MSM_BUF_MNGR_PUT_BUF;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD:
+ break;
+ case VIDIOC_MSM_BUF_MNGR_FLUSH32:
+ cmd = VIDIOC_MSM_BUF_MNGR_FLUSH;
+ break;
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD:
+ break;
+ default:
+ pr_debug("unsupported compat type\n");
+ return -ENOIOCTLCMD;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ case VIDIOC_MSM_BUF_MNGR_FLUSH:
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF: {
+ struct msm_buf_mngr_info32_t buf_info32;
+ struct msm_buf_mngr_info buf_info;
+
+ rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info,
+ arg);
+ if (rc < 0) {
+ pr_err("Fetch buf info failed for cmd=%d\n", cmd);
+ return rc;
+ }
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &buf_info);
+ if (rc < 0) {
+ pr_debug("Subdev cmd %d fail\n", cmd);
+ return rc;
+ }
+ rc = msm_camera_buf_mgr_update_buf_info(&buf_info32, &buf_info,
+ arg);
+ if (rc < 0) {
+ pr_err("Update buf info failed for cmd=%d\n", cmd);
+ return rc;
+ }
+ break;
+ }
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: {
+ rc = msm_camera_buf_mgr_internal_compat_ioctl(file, cmd, arg);
+ if (rc < 0) {
+ pr_debug("Subdev cmd %d fail\n", cmd);
+ return rc;
+ }
+ }
+ break;
+ case VIDIOC_MSM_BUF_MNGR_CONT_CMD: {
+ struct msm_buf_mngr_main_cont_info cont_cmd;
+
+ if (copy_from_user(&cont_cmd, (void __user *)arg,
+ sizeof(struct msm_buf_mngr_main_cont_info)))
+ return -EFAULT;
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &cont_cmd);
+ if (rc < 0) {
+ pr_debug("Subdev cmd %d fail\n", cmd);
+ return rc;
+ }
+ }
+ break;
+ default:
+ pr_debug("unsupported compat type\n");
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+#endif
+
+static struct v4l2_subdev_core_ops msm_buf_mngr_subdev_core_ops = {
+ .ioctl = msm_buf_mngr_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_internal_ops
+ msm_generic_buf_mngr_subdev_internal_ops = {
+ .open = msm_generic_buf_mngr_open,
+ .close = msm_generic_buf_mngr_close,
+};
+
+static const struct v4l2_subdev_ops msm_buf_mngr_subdev_ops = {
+ .core = &msm_buf_mngr_subdev_core_ops,
+};
+
+static const struct of_device_id msm_buf_mngr_dt_match[] = {
+ {}
+};
+
+static struct v4l2_file_operations msm_buf_v4l2_subdev_fops;
+
+static long msm_bmgr_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+}
+
+
+static long msm_buf_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_bmgr_subdev_do_ioctl);
+}
+
+static int32_t __init msm_buf_mngr_init(void)
+{
+ int32_t rc = 0;
+
+ msm_buf_mngr_dev = kzalloc(sizeof(*msm_buf_mngr_dev),
+ GFP_KERNEL);
+ if (WARN_ON(!msm_buf_mngr_dev)) {
+ pr_err("%s: not enough memory", __func__);
+ return -ENOMEM;
+ }
+ /* Sub-dev */
+ v4l2_subdev_init(&msm_buf_mngr_dev->subdev.sd,
+ &msm_buf_mngr_subdev_ops);
+ msm_cam_copy_v4l2_subdev_fops(&msm_buf_v4l2_subdev_fops);
+ msm_buf_v4l2_subdev_fops.unlocked_ioctl = msm_buf_subdev_fops_ioctl;
+#ifdef CONFIG_COMPAT
+ msm_buf_v4l2_subdev_fops.compat_ioctl32 =
+ msm_bmgr_subdev_fops_compat_ioctl;
+#endif
+ snprintf(msm_buf_mngr_dev->subdev.sd.name,
+ ARRAY_SIZE(msm_buf_mngr_dev->subdev.sd.name), "msm_buf_mngr");
+ msm_buf_mngr_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(&msm_buf_mngr_dev->subdev.sd, msm_buf_mngr_dev);
+
+ media_entity_pads_init(&msm_buf_mngr_dev->subdev.sd.entity, 0, NULL);
+ msm_buf_mngr_dev->subdev.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ //msm_buf_mngr_dev->subdev.sd.entity.group_id =
+ // MSM_CAMERA_SUBDEV_BUF_MNGR;
+ msm_buf_mngr_dev->subdev.sd.internal_ops =
+ &msm_generic_buf_mngr_subdev_internal_ops;
+ msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY;
+ rc = msm_sd_register(&msm_buf_mngr_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto end;
+ }
+
+ msm_buf_mngr_dev->subdev.sd.devnode->fops = &msm_buf_v4l2_subdev_fops;
+
+ v4l2_subdev_notify(&msm_buf_mngr_dev->subdev.sd, MSM_SD_NOTIFY_REQ_CB,
+ &msm_buf_mngr_dev->vb2_ops);
+
+ INIT_LIST_HEAD(&msm_buf_mngr_dev->buf_qhead);
+ spin_lock_init(&msm_buf_mngr_dev->buf_q_spinlock);
+
+ mutex_init(&msm_buf_mngr_dev->cont_mutex);
+ INIT_LIST_HEAD(&msm_buf_mngr_dev->cont_qhead);
+ msm_buf_mngr_dev->ion_client =
+ msm_ion_client_create("msm_cam_generic_buf_mgr");
+ if (!msm_buf_mngr_dev->ion_client) {
+ pr_err("%s: Failed to create ion client\n", __func__);
+ rc = -EBADFD;
+ }
+
+end:
+ return rc;
+}
+
+static void __exit msm_buf_mngr_exit(void)
+{
+ msm_sd_unregister(&msm_buf_mngr_dev->subdev);
+ mutex_destroy(&msm_buf_mngr_dev->cont_mutex);
+ kfree(msm_buf_mngr_dev);
+}
+
+module_init(msm_buf_mngr_init);
+module_exit(msm_buf_mngr_exit);
+MODULE_DESCRIPTION("MSM Buffer Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
new file mode 100644
index 0000000..e13deae
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2013-2016, 2018, 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_BUF_GENERIC_MNGR_H__
+#define __MSM_BUF_GENERIC_MNGR_H__
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+#include <media/msmb_generic_buf_mgr.h>
+
+#include "msm.h"
+#include "msm_sd.h"
+
+struct msm_get_bufs {
+ struct list_head entry;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf;
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t index;
+};
+
+struct msm_buf_mngr_device {
+ struct list_head buf_qhead;
+ spinlock_t buf_q_spinlock;
+ struct ion_client *ion_client;
+ struct msm_sd_subdev subdev;
+ struct msm_sd_req_vb2_q vb2_ops;
+ struct list_head cont_qhead;
+ struct mutex cont_mutex;
+};
+
+struct msm_buf_mngr_user_buf_cont_info {
+ struct list_head entry;
+ uint32_t sessid;
+ uint32_t strid;
+ uint32_t index;
+ int32_t main_fd;
+ struct msm_camera_user_buf_cont_t *paddr;
+ uint32_t cnt;
+ struct ion_handle *ion_handle;
+};
+
+/* kernel space functions*/
+struct msm_cam_buf_mgr_req_ops {
+ int (*msm_cam_buf_mgr_ops)(unsigned int cmd, void *argp);
+};
+
+/* API to register callback from client. This assumes cb_struct is allocated by
+ * client.
+ */
+int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/msm_sd.h b/drivers/media/platform/msm/camera_v2/msm_sd.h
new file mode 100644
index 0000000..f45c0d7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_sd.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012-2016, 2018, 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)
+
+#define MSM_SD_NOTIFY_FREEZE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 30, struct msm_sd_close_ioctl)
+
+#define MSM_SD_UNNOTIFY_FREEZE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 31, 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
+#define MSM_SD_CLOSE_4TH_CATEGORY 0x00040000
+
+struct msm_sd_subdev {
+ struct v4l2_subdev sd;
+ int close_seq;
+ struct list_head list;
+};
+
+struct msm_sd_req_sd {
+ char *name;
+ struct v4l2_subdev *subdev;
+};
+
+struct msm_sd_req_vb2_q {
+ struct vb2_v4l2_buffer * (*get_buf)(int session_id,
+ unsigned int stream_id);
+ struct vb2_queue * (*get_vb2_queue)(int session_id,
+ unsigned int stream_id);
+ struct vb2_v4l2_buffer * (*get_buf_by_idx)(int session_id,
+ unsigned int stream_id, uint32_t index);
+ int (*put_buf)(struct vb2_v4l2_buffer *vb2_buf, int session_id,
+ unsigned int stream_id);
+ int (*buf_done)(struct vb2_v4l2_buffer *vb2_v4l2_buf, int session_id,
+ unsigned int stream_id, uint32_t sequence, struct timeval *ts,
+ uint32_t reserved);
+ int (*flush_buf)(int session_id, unsigned int stream_id);
+};
+
+#define MSM_SD_NOTIFY_GET_SD 0x00000001
+#define MSM_SD_NOTIFY_PUT_SD 0x00000002
+#define MSM_SD_NOTIFY_REQ_CB 0x00000003
+
+#define MSM_CAM_GET_IOCTL_ARG_PTR(ptr, \
+ ioctl_ptr, len) memcpy(ptr, ioctl_ptr, len)
+
+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);
+void msm_cam_copy_v4l2_subdev_fops(struct v4l2_file_operations *d1);
+
+#endif /*_MSM_SD_H */
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/Makefile b/drivers/media/platform/msm/camera_v2/msm_vb2/Makefile
new file mode 100644
index 0000000..2673bdd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += msm_vb2.o
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
new file mode 100644
index 0000000..1322707
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -0,0 +1,561 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "CAM-VB2 %s:%d " fmt, __func__, __LINE__
+#include "msm_vb2.h"
+
+static int msm_vb2_queue_setup(struct vb2_queue *q,
+// const void *parg,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_ctxs[])
+{
+ int i;
+ struct msm_v4l2_format_data *data = q->drv_priv;
+
+ if (!data) {
+ pr_err("%s: drv_priv NULL\n", __func__);
+ return -EINVAL;
+ }
+ 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;
+}
+
+static int msm_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct msm_vb2_buffer *msm_vb2_buf;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ unsigned long rl_flags;
+
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream_from_vb2q(vb->vb2_queue);
+ if (!stream) {
+ pr_err("%s: Couldn't find stream\n", __func__);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return -EINVAL;
+ }
+ msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
+ msm_vb2_buf->in_freeq = 0;
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return 0;
+}
+
+static void msm_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2;
+ struct msm_stream *stream;
+ struct msm_session *session;
+ unsigned long flags, rl_flags;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
+ if (!msm_vb2) {
+ pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__);
+ return;
+ }
+
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream_from_vb2q(vb->vb2_queue);
+ if (!stream) {
+ pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_add_tail(&msm_vb2->list, &stream->queued_list);
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+}
+
+static void msm_vb2_buf_finish(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2;
+ struct msm_stream *stream;
+ struct msm_session *session;
+ unsigned long flags, rl_flags;
+ struct msm_vb2_buffer *msm_vb2_entry, *temp;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ msm_vb2 = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
+ if (!msm_vb2) {
+ pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__);
+ return;
+ }
+
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream_from_vb2q(vb->vb2_queue);
+ if (!stream) {
+ pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2_entry, temp, &(stream->queued_list),
+ list) {
+ if (msm_vb2_entry == msm_vb2) {
+ list_del_init(&msm_vb2_entry->list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+}
+
+static void msm_vb2_stop_stream(struct vb2_queue *q)
+{
+ struct msm_vb2_buffer *msm_vb2, *temp;
+ struct msm_stream *stream;
+ struct msm_session *session;
+ unsigned long flags, rl_flags;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf;
+
+ session = msm_get_session_from_vb2q(q);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream_from_vb2q(q);
+ if (!stream) {
+ pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return;
+ }
+
+ /*
+ * Release all the buffers enqueued to driver
+ * when streamoff is issued
+ */
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list),
+ list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if (vb2_v4l2_buf->vb2_buf.state == VB2_BUF_STATE_DONE)
+ continue;
+ vb2_buffer_done(&vb2_v4l2_buf->vb2_buf,
+ VB2_BUF_STATE_DONE);
+ msm_vb2->in_freeq = 0;
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+}
+
+int msm_vb2_get_stream_state(struct msm_stream *stream)
+{
+ struct msm_vb2_buffer *msm_vb2, *temp;
+ unsigned long flags;
+ int rc = 1;
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) {
+ if (msm_vb2->in_freeq != 0) {
+ rc = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ return rc;
+}
+EXPORT_SYMBOL(msm_vb2_get_stream_state);
+
+
+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,
+ .buf_finish = msm_vb2_buf_finish,
+ .stop_streaming = msm_vb2_stop_stream,
+};
+
+
+struct vb2_ops *msm_vb2_get_q_ops(void)
+{
+ return &msm_vb2_get_q_op;
+}
+
+static void *msm_vb2_dma_contig_get_userptr(struct device *alloc_ctx,
+ unsigned long vaddr, unsigned long size,
+ enum dma_data_direction dma_dir)
+{
+ 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)
+{
+ kzfree(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_v4l2_buffer *msm_vb2_get_buf(int session_id,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_vb2_buffer *msm_vb2 = NULL;
+ unsigned long flags, rl_flags;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return NULL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+
+ if (!stream->vb2_q) {
+ pr_err("%s: stream q not available\n", __func__);
+ goto end;
+ }
+
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if (vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE)
+ continue;
+
+ if (msm_vb2->in_freeq)
+ continue;
+
+ msm_vb2->in_freeq = 1;
+ goto end;
+ }
+ msm_vb2 = NULL;
+ vb2_v4l2_buf = NULL;
+end:
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return vb2_v4l2_buf;
+}
+
+static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
+ unsigned int stream_id, uint32_t index)
+{
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_vb2_buffer *msm_vb2 = NULL;
+ unsigned long flags, rl_flags;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return NULL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+
+ if (!stream->vb2_q) {
+ pr_err("%s: stream q not available\n", __func__);
+ goto end;
+ }
+
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if ((vb2_v4l2_buf->vb2_buf.index != index) || msm_vb2->in_freeq
+ || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE)
+ continue;
+
+ msm_vb2->in_freeq = 1;
+ goto end;
+ }
+ msm_vb2 = NULL;
+ vb2_v4l2_buf = NULL;
+end:
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return vb2_v4l2_buf;
+}
+
+static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct msm_vb2_buffer *msm_vb2;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ int rc = 0;
+ unsigned long flags, rl_flags;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ if (vb) {
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if (vb2_v4l2_buf == vb)
+ break;
+ }
+ if (vb2_v4l2_buf != vb) {
+ pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n",
+ vb, session_id, stream_id);
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock,
+ rl_flags);
+ return -EINVAL;
+ }
+ msm_vb2 =
+ container_of(vb2_v4l2_buf, struct msm_vb2_buffer,
+ vb2_v4l2_buf);
+ if (msm_vb2->in_freeq) {
+ msm_vb2->in_freeq = 0;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err(" VB buffer is null for ses_id=%d, str_id=%d\n",
+ session_id, stream_id);
+ rc = -EINVAL;
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return rc;
+}
+
+static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
+ unsigned int stream_id, uint32_t sequence,
+ struct timeval *ts, uint32_t reserved)
+{
+ unsigned long flags, rl_flags;
+ struct msm_vb2_buffer *msm_vb2;
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ int rc = 0;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ if (vb) {
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if (vb2_v4l2_buf == vb)
+ break;
+ }
+ if (vb2_v4l2_buf != vb) {
+ pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n",
+ session_id, stream_id, vb);
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock,
+ rl_flags);
+ return -EINVAL;
+ }
+ msm_vb2 =
+ container_of(vb2_v4l2_buf, struct msm_vb2_buffer,
+ vb2_v4l2_buf);
+ /* put buf before buf done */
+ if (msm_vb2->in_freeq) {
+ vb2_v4l2_buf->sequence = sequence;
+ //vb2_v4l2_buf->timestamp = *ts;
+ vb2_buffer_done(&vb2_v4l2_buf->vb2_buf,
+ VB2_BUF_STATE_DONE);
+ msm_vb2->in_freeq = 0;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err(" VB buffer is NULL for ses_id=%d, str_id=%d\n",
+ session_id, stream_id);
+ rc = -EINVAL;
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return rc;
+}
+
+long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
+ uint32_t index)
+{
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+ struct msm_vb2_buffer *msm_vb2 = NULL;
+ unsigned long flags, rl_flags;
+ long rc = -EINVAL;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return rc;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+
+ if (!stream->vb2_q) {
+ pr_err("%s: stream q not available\n", __func__);
+ goto end;
+ }
+
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ if ((vb2_v4l2_buf->vb2_buf.index != index)
+ || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE)
+ continue;
+
+ if (!msm_vb2->in_freeq) {
+ vb2_buffer_done(&vb2_v4l2_buf->vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ rc = 0;
+ } else {
+ rc = -EINVAL;
+ }
+ break;
+ }
+
+end:
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return rc;
+}
+EXPORT_SYMBOL(msm_vb2_return_buf_by_idx);
+
+static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
+{
+ unsigned long flags, rl_flags;
+ struct msm_vb2_buffer *msm_vb2;
+ struct msm_stream *stream;
+ struct msm_session *session;
+ struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock_irqsave(&session->stream_rwlock, rl_flags);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
+ vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
+ /* Do buf done for all buffers*/
+ vb2_buffer_done(&vb2_v4l2_buf->vb2_buf, VB2_BUF_STATE_DONE);
+ msm_vb2->in_freeq = 0;
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
+ return 0;
+}
+
+
+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_buf_by_idx = msm_vb2_get_buf_by_idx;
+ req->get_vb2_queue = msm_vb2_get_queue;
+ req->put_buf = msm_vb2_put_buf;
+ req->buf_done = msm_vb2_buf_done;
+ req->flush_buf = msm_vb2_flush_buf;
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
new file mode 100644
index 0000000..1a64c54
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2012-2018, 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/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/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_v4l2_buffer vb2_v4l2_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 */
+ struct device *alloc_ctx;
+};
+
+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;
+ spinlock_t stream_lock;
+ struct list_head queued_list;
+};
+
+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);
+long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
+ uint32_t index);
+int msm_vb2_get_stream_state(struct msm_stream *stream);
+
+#endif /*_MSM_VB_H */
diff --git a/drivers/media/platform/msm/camera_v2/pproc/Makefile b/drivers/media/platform/msm/camera_v2/pproc/Makefile
new file mode 100644
index 0000000..854e4e7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MSMB_CAMERA) += cpp/
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
new file mode 100644
index 0000000..2198352
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
@@ -0,0 +1,6 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/isp/
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/
+obj-$(CONFIG_MSM_CPP) += msm_cpp_soc.o msm_cpp.o
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
new file mode 100644
index 0000000..6fd9c4d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -0,0 +1,4812 @@
+/* Copyright (c) 2013-2018, 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) "MSM-CPP %s:%d " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/ion.h>
+#include <linux/proc_fs.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/clk/msm-clk.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msmb_camera.h>
+#include <media/msmb_generic_buf_mgr.h>
+#include <media/msmb_pproc.h>
+#include "msm_cpp.h"
+#include "msm_isp_util.h"
+#include "msm_camera_io_util.h"
+#include <linux/debugfs.h>
+#include "cam_smmu_api.h"
+
+#define MSM_CPP_DRV_NAME "msm_cpp"
+
+#define MSM_CPP_MAX_BUFF_QUEUE 16
+
+#define CONFIG_MSM_CPP_DBG 0
+
+#define ENABLE_CPP_LOW 0
+
+#define CPP_CMD_TIMEOUT_MS 300
+#define MSM_CPP_INVALID_OFFSET 0x00000000
+#define MSM_CPP_NOMINAL_CLOCK 266670000
+#define MSM_CPP_TURBO_CLOCK 320000000
+
+#define CPP_FW_VERSION_1_2_0 0x10020000
+#define CPP_FW_VERSION_1_4_0 0x10040000
+#define CPP_FW_VERSION_1_6_0 0x10060000
+#define CPP_FW_VERSION_1_8_0 0x10080000
+#define CPP_FW_VERSION_1_10_0 0x10100000
+
+/* dump the frame command before writing to the hardware */
+#define MSM_CPP_DUMP_FRM_CMD 0
+
+#define CPP_CLK_INFO_MAX 16
+
+#define MSM_CPP_IRQ_MASK_VAL 0x7c8
+
+#define CPP_GDSCR_SW_COLLAPSE_ENABLE 0xFFFFFFFE
+#define CPP_GDSCR_SW_COLLAPSE_DISABLE 0xFFFFFFFD
+#define CPP_GDSCR_HW_CONTROL_ENABLE 0x2
+#define CPP_GDSCR_HW_CONTROL_DISABLE 0x1
+#define PAYLOAD_NUM_PLANES 3
+#define TNR_MASK 0x4
+#define UBWC_MASK 0x20
+#define CDS_MASK 0x40
+#define MMU_PF_MASK 0x80
+#define POP_FRONT 1
+#define POP_BACK 0
+#define BATCH_DUP_MASK 0x100
+
+#define IS_BATCH_BUFFER_ON_PREVIEW(new_frame) \
+ (((new_frame->batch_info.batch_mode == BATCH_MODE_PREVIEW) && \
+ new_frame->duplicate_output) ? 1 : 0)
+
+#define SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame, iden, swap_iden) { \
+ if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \
+ iden = swap_iden; \
+}
+
+#define SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame, buff_mgr_info, \
+ cur_index, swap_index) { \
+ if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \
+ buff_mgr_info.index = swap_index; \
+ else \
+ buff_mgr_info.index = cur_index; \
+}
+
+/*
+ * Default value for get buf to be used - 0xFFFFFFFF
+ * 0 is a valid index
+ * no valid index from userspace, use last buffer from queue.
+ */
+#define DEFAULT_OUTPUT_BUF_INDEX 0xFFFFFFFF
+#define IS_DEFAULT_OUTPUT_BUF_INDEX(index) \
+ ((index == DEFAULT_OUTPUT_BUF_INDEX) ? 1 : 0)
+
+static struct msm_cpp_vbif_data cpp_vbif;
+static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t ids, void *arg);
+
+static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
+ struct msm_queue_cmd *frame_qcmd);
+static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev,
+ uint32_t *cmd_msg, uint32_t payload_size);
+
+static int msm_cpp_update_gdscr_status(struct cpp_device *cpp_dev,
+ bool status);
+static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t id, void *arg);
+static void msm_cpp_set_micro_irq_mask(struct cpp_device *cpp_dev,
+ uint8_t enable, uint32_t irq_mask);
+static void msm_cpp_flush_queue_and_release_buffer(struct cpp_device *cpp_dev,
+ int queue_len);
+static int msm_cpp_dump_frame_cmd(struct msm_cpp_frame_info_t *frame_info);
+static int msm_cpp_dump_addr(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *frame_info);
+static int32_t msm_cpp_reset_vbif_and_load_fw(struct cpp_device *cpp_dev);
+
+#if CONFIG_MSM_CPP_DBG
+#define CPP_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CPP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define CPP_LOW(fmt, args...) do { \
+ if (ENABLE_CPP_LOW) \
+ pr_info(fmt, ##args); \
+ } while (0)
+
+#define ERR_USER_COPY(to) pr_err("copy %s user\n", \
+ ((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+
+#define msm_dequeue(queue, member, pop_dir) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = NULL; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = pop_dir ? list_first_entry(&__q->list, \
+ struct msm_queue_cmd, member) : \
+ list_last_entry(&__q->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+})
+
+#define MSM_CPP_MAX_TIMEOUT_TRIAL 1
+
+struct msm_cpp_timer_data_t {
+ struct cpp_device *cpp_dev;
+ struct msm_cpp_frame_info_t *processed_frame[MAX_CPP_PROCESSING_FRAME];
+ spinlock_t processed_frame_lock;
+};
+
+struct msm_cpp_timer_t {
+ atomic_t used;
+ struct msm_cpp_timer_data_t data;
+ struct timer_list cpp_timer;
+};
+
+static struct msm_cpp_timer_t cpp_timer;
+static void msm_cpp_set_vbif_reg_values(struct cpp_device *cpp_dev);
+
+
+void msm_cpp_vbif_register_error_handler(void *dev,
+ enum cpp_vbif_client client,
+ int (*client_vbif_error_handler)(void *, uint32_t))
+{
+ if (dev == NULL || client >= VBIF_CLIENT_MAX) {
+ pr_err("%s: Fail to register handler! dev = %pK, client %d\n",
+ __func__, dev, client);
+ return;
+ }
+
+ if (client_vbif_error_handler != NULL) {
+ cpp_vbif.dev[client] = dev;
+ cpp_vbif.err_handler[client] = client_vbif_error_handler;
+ } else {
+ /* if handler = NULL, is unregister case */
+ cpp_vbif.dev[client] = NULL;
+ cpp_vbif.err_handler[client] = NULL;
+ }
+}
+static int msm_cpp_init_bandwidth_mgr(struct cpp_device *cpp_dev)
+{
+ int rc = 0;
+
+ rc = msm_camera_register_bus_client(cpp_dev->pdev, CAM_BUS_CLIENT_CPP);
+ if (rc < 0) {
+ pr_err("Fail to register bus client\n");
+ return -ENOENT;
+ }
+
+ rc = msm_camera_update_bus_bw(CAM_BUS_CLIENT_CPP, 0, 0);
+ if (rc < 0) {
+ msm_camera_unregister_bus_client(CAM_BUS_CLIENT_CPP);
+ pr_err("Fail bus scale update %d\n", rc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_cpp_update_bandwidth(struct cpp_device *cpp_dev,
+ uint64_t ab, uint64_t ib)
+{
+
+ int rc;
+
+ rc = msm_camera_update_bus_bw(CAM_BUS_CLIENT_CPP, ab, ib);
+ if (rc < 0) {
+ pr_err("Fail bus scale update %d\n", rc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void msm_cpp_deinit_bandwidth_mgr(struct cpp_device *cpp_dev)
+{
+ int rc = 0;
+
+ rc = msm_camera_unregister_bus_client(CAM_BUS_CLIENT_CPP);
+ if (rc < 0) {
+ pr_err("Failed to unregister %d\n", rc);
+ return;
+ }
+}
+
+static int msm_cpp_update_bandwidth_setting(struct cpp_device *cpp_dev,
+ uint64_t ab, uint64_t ib) {
+ int rc;
+
+ if (cpp_dev->bus_master_flag)
+ rc = msm_cpp_update_bandwidth(cpp_dev, ab, ib);
+ else
+ rc = msm_isp_update_bandwidth(ISP_CPP, ab, ib);
+ return rc;
+}
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ CPP_DBG("E\n");
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_debug("queue %s new max is %d\n", queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ CPP_DBG("woke up %s\n", queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+#define msm_cpp_empty_list(queue, member) { \
+ unsigned long flags; \
+ struct msm_queue_cmd *qcmd = NULL; \
+ if (queue) { \
+ spin_lock_irqsave(&queue->lock, flags); \
+ while (!list_empty(&queue->list)) { \
+ queue->len--; \
+ qcmd = list_first_entry(&queue->list, \
+ struct msm_queue_cmd, member); \
+ list_del_init(&qcmd->member); \
+ kfree(qcmd); \
+ } \
+ spin_unlock_irqrestore(&queue->lock, flags); \
+ } \
+}
+
+
+
+static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
+ uint8_t put_buf);
+static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin);
+static void cpp_timer_callback(unsigned long data);
+
+static uint8_t induce_error;
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev);
+
+static void msm_cpp_write(u32 data, void __iomem *cpp_base)
+{
+ msm_camera_io_w((data), cpp_base + MSM_CPP_MICRO_FIFO_RX_DATA);
+}
+
+static void msm_cpp_clear_timer(struct cpp_device *cpp_dev)
+{
+ uint32_t i = 0;
+
+ if (atomic_read(&cpp_timer.used)) {
+ atomic_set(&cpp_timer.used, 0);
+ del_timer(&cpp_timer.cpp_timer);
+ for (i = 0; i < MAX_CPP_PROCESSING_FRAME; i++)
+ cpp_timer.data.processed_frame[i] = NULL;
+ cpp_dev->timeout_trial_cnt = 0;
+ }
+}
+
+static void msm_cpp_timer_queue_update(struct cpp_device *cpp_dev)
+{
+ uint32_t i;
+ unsigned long flags;
+
+ CPP_DBG("Frame done qlen %d\n", cpp_dev->processing_q.len);
+ if (cpp_dev->processing_q.len <= 1) {
+ msm_cpp_clear_timer(cpp_dev);
+ } else {
+ spin_lock_irqsave(&cpp_timer.data.processed_frame_lock, flags);
+ for (i = 0; i < cpp_dev->processing_q.len - 1; i++)
+ cpp_timer.data.processed_frame[i] =
+ cpp_timer.data.processed_frame[i + 1];
+ cpp_timer.data.processed_frame[i] = NULL;
+ cpp_dev->timeout_trial_cnt = 0;
+ spin_unlock_irqrestore(&cpp_timer.data.processed_frame_lock,
+ flags);
+
+ mod_timer(&cpp_timer.cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ }
+}
+
+static uint32_t msm_cpp_read(void __iomem *cpp_base)
+{
+ uint32_t tmp, retry = 0;
+
+ do {
+ tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_TX_STAT);
+ } while (((tmp & 0x2) == 0x0) && (retry++ < 10));
+ if (retry < 10) {
+ tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_TX_DATA);
+ CPP_DBG("Read data: 0%x\n", tmp);
+ } else {
+ CPP_DBG("Read failed\n");
+ tmp = 0xDEADBEEF;
+ }
+
+ return tmp;
+}
+
+static struct msm_cpp_buff_queue_info_t *msm_cpp_get_buff_queue_entry(
+ struct cpp_device *cpp_dev, uint32_t session_id, uint32_t stream_id)
+{
+ uint32_t i = 0;
+ struct msm_cpp_buff_queue_info_t *buff_queue_info = NULL;
+
+ for (i = 0; i < cpp_dev->num_buffq; i++) {
+ if ((cpp_dev->buff_queue[i].used == 1) &&
+ (cpp_dev->buff_queue[i].session_id == session_id) &&
+ (cpp_dev->buff_queue[i].stream_id == stream_id)) {
+ buff_queue_info = &cpp_dev->buff_queue[i];
+ break;
+ }
+ }
+
+ if (buff_queue_info == NULL) {
+ CPP_DBG("buffer queue entry for sess:%d strm:%d not found\n",
+ session_id, stream_id);
+ }
+ return buff_queue_info;
+}
+
+static unsigned long msm_cpp_get_phy_addr(struct cpp_device *cpp_dev,
+ struct msm_cpp_buff_queue_info_t *buff_queue_info, uint32_t buff_index,
+ uint8_t native_buff, int32_t *fd)
+{
+ unsigned long phy_add = 0;
+ struct list_head *buff_head;
+ struct msm_cpp_buffer_map_list_t *buff, *save;
+
+ if (native_buff)
+ buff_head = &buff_queue_info->native_buff_head;
+ else
+ buff_head = &buff_queue_info->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buff_index) {
+ phy_add = buff->map_info.phy_addr;
+ *fd = buff->map_info.buff_info.fd;
+ break;
+ }
+ }
+
+ return phy_add;
+}
+
+static unsigned long msm_cpp_queue_buffer_info(struct cpp_device *cpp_dev,
+ struct msm_cpp_buff_queue_info_t *buff_queue,
+ struct msm_cpp_buffer_info_t *buffer_info)
+{
+ struct list_head *buff_head;
+ struct msm_cpp_buffer_map_list_t *buff, *save;
+ int rc = 0;
+
+ if (buffer_info->native_buff)
+ buff_head = &buff_queue->native_buff_head;
+ else
+ buff_head = &buff_queue->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buffer_info->index) {
+ pr_err("error buf index already queued\n");
+ pr_err("error buf, fd %d idx %d native %d ssid %d %d\n",
+ buffer_info->fd, buffer_info->index,
+ buffer_info->native_buff,
+ buff_queue->session_id,
+ buff_queue->stream_id);
+ pr_err("existing buf,fd %d idx %d native %d id %x\n",
+ buff->map_info.buff_info.fd,
+ buff->map_info.buff_info.index,
+ buff->map_info.buff_info.native_buff,
+ buff->map_info.buff_info.identity);
+ goto error;
+ }
+ }
+
+ buff = kzalloc(
+ sizeof(struct msm_cpp_buffer_map_list_t), GFP_KERNEL);
+ if (!buff)
+ goto error;
+
+ buff->map_info.buff_info = *buffer_info;
+ buff->map_info.buf_fd = buffer_info->fd;
+
+ pr_debug("fd %d index %d native_buff %d ssid %d %d\n",
+ buffer_info->fd, buffer_info->index,
+ buffer_info->native_buff, buff_queue->session_id,
+ buff_queue->stream_id);
+
+ if (buff_queue->security_mode == SECURE_MODE)
+ rc = cam_smmu_get_stage2_phy_addr(cpp_dev->iommu_hdl,
+ buffer_info->fd, CAM_SMMU_MAP_RW,
+ cpp_dev->ion_client, &buff->map_info.phy_addr,
+ (size_t *)&buff->map_info.len);
+ else
+ rc = cam_smmu_get_phy_addr(cpp_dev->iommu_hdl,
+ buffer_info->fd, CAM_SMMU_MAP_RW,
+ &buff->map_info.phy_addr,
+ (size_t *)&buff->map_info.len);
+ if (rc < 0) {
+ pr_err("ION mmap for CPP buffer failed\n");
+ kzfree(buff);
+ goto error;
+ }
+
+ INIT_LIST_HEAD(&buff->entry);
+ list_add_tail(&buff->entry, buff_head);
+
+ return buff->map_info.phy_addr;
+error:
+ return 0;
+}
+
+static void msm_cpp_dequeue_buffer_info(struct cpp_device *cpp_dev,
+ struct msm_cpp_buff_queue_info_t *buff_queue,
+ struct msm_cpp_buffer_map_list_t *buff)
+{
+ int ret = -1;
+
+ pr_debug("fd %d index %d native_buf %d ssid %d %d\n",
+ buff->map_info.buf_fd, buff->map_info.buff_info.index,
+ buff->map_info.buff_info.native_buff, buff_queue->session_id,
+ buff_queue->stream_id);
+
+ if (buff_queue->security_mode == SECURE_MODE)
+ ret = cam_smmu_put_stage2_phy_addr(cpp_dev->iommu_hdl,
+ buff->map_info.buf_fd);
+ else
+ ret = cam_smmu_put_phy_addr(cpp_dev->iommu_hdl,
+ buff->map_info.buf_fd);
+ if (ret < 0)
+ pr_err("Error: cannot put the iommu handle back to ion fd\n");
+
+ list_del_init(&buff->entry);
+ kzfree(buff);
+}
+
+static unsigned long msm_cpp_fetch_buffer_info(struct cpp_device *cpp_dev,
+ struct msm_cpp_buffer_info_t *buffer_info, uint32_t session_id,
+ uint32_t stream_id, int32_t *fd)
+{
+ unsigned long phy_addr = 0;
+ struct msm_cpp_buff_queue_info_t *buff_queue_info;
+ uint8_t native_buff = buffer_info->native_buff;
+
+ buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return phy_addr;
+ }
+
+ phy_addr = msm_cpp_get_phy_addr(cpp_dev, buff_queue_info,
+ buffer_info->index, native_buff, fd);
+ if ((phy_addr == 0) && (native_buff)) {
+ phy_addr = msm_cpp_queue_buffer_info(cpp_dev, buff_queue_info,
+ buffer_info);
+ *fd = buffer_info->fd;
+ }
+
+ return phy_addr;
+}
+
+static int32_t msm_cpp_dequeue_buff_info_list(struct cpp_device *cpp_dev,
+ struct msm_cpp_buff_queue_info_t *buff_queue_info)
+{
+ struct msm_cpp_buffer_map_list_t *buff, *save;
+ struct list_head *buff_head;
+
+ buff_head = &buff_queue_info->native_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_cpp_dequeue_buffer_info(cpp_dev, buff_queue_info, buff);
+ }
+
+ buff_head = &buff_queue_info->vb2_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_cpp_dequeue_buffer_info(cpp_dev, buff_queue_info, buff);
+ }
+
+ return 0;
+}
+
+static int32_t msm_cpp_dequeue_buff(struct cpp_device *cpp_dev,
+ struct msm_cpp_buff_queue_info_t *buff_queue_info, uint32_t buff_index,
+ uint8_t native_buff)
+{
+ struct msm_cpp_buffer_map_list_t *buff, *save;
+ struct list_head *buff_head;
+
+ if (native_buff)
+ buff_head = &buff_queue_info->native_buff_head;
+ else
+ buff_head = &buff_queue_info->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buff_index) {
+ msm_cpp_dequeue_buffer_info(cpp_dev, buff_queue_info,
+ buff);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int32_t msm_cpp_add_buff_queue_entry(struct cpp_device *cpp_dev,
+ uint16_t session_id, uint16_t stream_id)
+{
+ uint32_t i;
+ struct msm_cpp_buff_queue_info_t *buff_queue_info;
+
+ for (i = 0; i < cpp_dev->num_buffq; i++) {
+ if (cpp_dev->buff_queue[i].used == 0) {
+ buff_queue_info = &cpp_dev->buff_queue[i];
+ buff_queue_info->used = 1;
+ buff_queue_info->session_id = session_id;
+ buff_queue_info->stream_id = stream_id;
+ buff_queue_info->security_mode =
+ cpp_dev->security_mode;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+ }
+ }
+ pr_err("buffer queue full. error for sessionid: %d streamid: %d\n",
+ session_id, stream_id);
+ return -EINVAL;
+}
+
+static int32_t msm_cpp_free_buff_queue_entry(struct cpp_device *cpp_dev,
+ uint32_t session_id, uint32_t stream_id)
+{
+ struct msm_cpp_buff_queue_info_t *buff_queue_info;
+
+ buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return -EINVAL;
+ }
+
+ buff_queue_info->used = 0;
+ buff_queue_info->session_id = 0;
+ buff_queue_info->stream_id = 0;
+ buff_queue_info->security_mode = NON_SECURE_MODE;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+}
+
+static int32_t msm_cpp_create_buff_queue(struct cpp_device *cpp_dev,
+ uint32_t num_buffq)
+{
+ struct msm_cpp_buff_queue_info_t *buff_queue;
+
+ buff_queue = kzalloc(
+ sizeof(struct msm_cpp_buff_queue_info_t) * num_buffq,
+ GFP_KERNEL);
+ if (!buff_queue) {
+ pr_err("Buff queue allocation failure\n");
+ return -ENOMEM;
+ }
+
+ if (cpp_dev->buff_queue) {
+ pr_err("Buff queue not empty\n");
+ kzfree(buff_queue);
+ return -EINVAL;
+ }
+ cpp_dev->buff_queue = buff_queue;
+ cpp_dev->num_buffq = num_buffq;
+ return 0;
+}
+
+static void msm_cpp_delete_buff_queue(struct cpp_device *cpp_dev)
+{
+ uint32_t i;
+
+ for (i = 0; i < cpp_dev->num_buffq; i++) {
+ if (cpp_dev->buff_queue[i].used == 1) {
+ pr_warn("Queue not free sessionid: %d, streamid: %d\n",
+ cpp_dev->buff_queue[i].session_id,
+ cpp_dev->buff_queue[i].stream_id);
+ msm_cpp_dequeue_buff_info_list
+ (cpp_dev, &cpp_dev->buff_queue[i]);
+ msm_cpp_free_buff_queue_entry(cpp_dev,
+ cpp_dev->buff_queue[i].session_id,
+ cpp_dev->buff_queue[i].stream_id);
+ }
+ }
+ kzfree(cpp_dev->buff_queue);
+ cpp_dev->buff_queue = NULL;
+ cpp_dev->num_buffq = 0;
+}
+
+static int32_t msm_cpp_poll(void __iomem *cpp_base, u32 val)
+{
+ uint32_t tmp, retry = 0;
+ int32_t rc = 0;
+
+ do {
+ tmp = msm_cpp_read(cpp_base);
+ if (tmp != 0xDEADBEEF)
+ CPP_LOW("poll: 0%x\n", tmp);
+ usleep_range(200, 250);
+ } while ((tmp != val) && (retry++ < MSM_CPP_POLL_RETRIES));
+ if (retry < MSM_CPP_POLL_RETRIES) {
+ CPP_LOW("Poll finished\n");
+ } else {
+ pr_err("Poll failed: expect: 0x%x\n", val);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int32_t msm_cpp_poll_rx_empty(void __iomem *cpp_base)
+{
+ uint32_t tmp, retry = 0;
+ int32_t rc = 0;
+
+ tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_RX_STAT);
+ while (((tmp & 0x2) != 0x0) && (retry++ < MSM_CPP_POLL_RETRIES)) {
+ /*
+ * Below usleep values are chosen based on experiments
+ * and this was the smallest number which works. This
+ * sleep is needed to leave enough time for Microcontroller
+ * to read rx fifo.
+ */
+ usleep_range(200, 300);
+ tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_RX_STAT);
+ }
+
+ if (retry < MSM_CPP_POLL_RETRIES) {
+ CPP_LOW("Poll rx empty\n");
+ } else {
+ pr_err("Poll rx empty failed\n");
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int msm_cpp_dump_addr(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *frame_info)
+{
+ int32_t s_base, p_base;
+ uint32_t rd_off, wr0_off, wr1_off, wr2_off, wr3_off;
+ uint32_t wr0_mdata_off, wr1_mdata_off, wr2_mdata_off, wr3_mdata_off;
+ uint32_t rd_ref_off, wr_ref_off;
+ uint32_t s_size, p_size;
+ uint8_t tnr_enabled, ubwc_enabled, cds_en;
+ int32_t i = 0;
+ uint32_t *cpp_frame_msg;
+
+ cpp_frame_msg = frame_info->cpp_cmd_msg;
+
+ /* Update stripe/plane size and base offsets */
+ s_base = cpp_dev->payload_params.stripe_base;
+ s_size = cpp_dev->payload_params.stripe_size;
+ p_base = cpp_dev->payload_params.plane_base;
+ p_size = cpp_dev->payload_params.plane_size;
+
+ /* Fetch engine Offset */
+ rd_off = cpp_dev->payload_params.rd_pntr_off;
+ /* Write engine offsets */
+ wr0_off = cpp_dev->payload_params.wr_0_pntr_off;
+ wr1_off = wr0_off + 1;
+ wr2_off = wr1_off + 1;
+ wr3_off = wr2_off + 1;
+ /* Reference engine offsets */
+ rd_ref_off = cpp_dev->payload_params.rd_ref_pntr_off;
+ wr_ref_off = cpp_dev->payload_params.wr_ref_pntr_off;
+ /* Meta data offsets */
+ wr0_mdata_off =
+ cpp_dev->payload_params.wr_0_meta_data_wr_pntr_off;
+ wr1_mdata_off = (wr0_mdata_off + 1);
+ wr2_mdata_off = (wr1_mdata_off + 1);
+ wr3_mdata_off = (wr2_mdata_off + 1);
+
+ tnr_enabled = ((frame_info->feature_mask & TNR_MASK) >> 2);
+ ubwc_enabled = ((frame_info->feature_mask & UBWC_MASK) >> 5);
+ cds_en = ((frame_info->feature_mask & CDS_MASK) >> 6);
+
+ for (i = 0; i < frame_info->num_strips; i++) {
+ pr_err("stripe %d: in %x, out1 %x out2 %x, out3 %x, out4 %x\n",
+ i, cpp_frame_msg[s_base + rd_off + i * s_size],
+ cpp_frame_msg[s_base + wr0_off + i * s_size],
+ cpp_frame_msg[s_base + wr1_off + i * s_size],
+ cpp_frame_msg[s_base + wr2_off + i * s_size],
+ cpp_frame_msg[s_base + wr3_off + i * s_size]);
+
+ if (tnr_enabled) {
+ pr_err("stripe %d: read_ref %x, write_ref %x\n", i,
+ cpp_frame_msg[s_base + rd_ref_off + i * s_size],
+ cpp_frame_msg[s_base + wr_ref_off + i * s_size]
+ );
+ }
+
+ if (cds_en) {
+ pr_err("stripe %d:, dsdn_off %x\n", i,
+ cpp_frame_msg[s_base + rd_ref_off + i * s_size]
+ );
+ }
+
+ if (ubwc_enabled) {
+ pr_err("stripe %d: metadata %x, %x, %x, %x\n", i,
+ cpp_frame_msg[s_base + wr0_mdata_off +
+ i * s_size],
+ cpp_frame_msg[s_base + wr1_mdata_off +
+ i * s_size],
+ cpp_frame_msg[s_base + wr2_mdata_off +
+ i * s_size],
+ cpp_frame_msg[s_base + wr3_mdata_off +
+ i * s_size]
+ );
+ }
+
+ }
+ return 0;
+}
+
+static void msm_cpp_iommu_fault_reset_handler(
+ struct iommu_domain *domain, struct device *dev,
+ void *token)
+{
+ struct cpp_device *cpp_dev = NULL;
+
+ if (!token) {
+ pr_err("Invalid token\n");
+ return;
+ }
+
+ cpp_dev = token;
+
+ if (cpp_dev->fault_status != CPP_IOMMU_FAULT_NONE) {
+ pr_err("fault already detected %d\n", cpp_dev->fault_status);
+ return;
+ }
+
+ cpp_dev->fault_status = CPP_IOMMU_FAULT_DETECTED;
+
+ /* mask IRQ status */
+ msm_camera_io_w(0xB, cpp_dev->cpp_hw_base + 0xC);
+
+ pr_err("Issue CPP HALT %d\n", cpp_dev->fault_status);
+
+ /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/
+ msm_camera_io_w(0x1, cpp_dev->cpp_hw_base + 0x16C);
+
+}
+
+static void msm_cpp_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token)
+{
+ struct cpp_device *cpp_dev = NULL;
+ struct msm_cpp_frame_info_t *processed_frame[MAX_CPP_PROCESSING_FRAME];
+ int32_t i = 0, queue_len = 0;
+ struct msm_device_queue *queue = NULL;
+ int32_t ifd, ofd, dfd, t0fd, t1fd;
+ int counter = 0;
+ u32 result;
+
+ if (token) {
+ cpp_dev = token;
+
+ if (cpp_dev->fault_status != CPP_IOMMU_FAULT_DETECTED) {
+ pr_err("fault recovery already done %d\n",
+ cpp_dev->fault_status);
+ return;
+ }
+
+ disable_irq(cpp_dev->irq->start);
+ if (atomic_read(&cpp_timer.used)) {
+ atomic_set(&cpp_timer.used, 0);
+ del_timer_sync(&cpp_timer.cpp_timer);
+ }
+ tasklet_kill(&cpp_dev->cpp_tasklet);
+
+ pr_err("in recovery, HALT status = 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+ while (counter < MSM_CPP_POLL_RETRIES) {
+ result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10);
+ if (result & 0x2)
+ break;
+ usleep_range(100, 200);
+ counter++;
+ }
+ /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+ pr_err("counter %d HALT status later = 0x%x\n",
+ counter,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+ /* MMSS_A_CPP_RST_CMD_0 = 0x8 firmware reset = 0x3FFFF */
+ msm_camera_io_w(0x3FFFF, cpp_dev->cpp_hw_base + 0x8);
+
+ counter = 0;
+ while (counter < MSM_CPP_POLL_RETRIES) {
+ result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10);
+ if (result & 0x1)
+ break;
+ usleep_range(100, 200);
+ counter++;
+ }
+
+ /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+ pr_err("counter %d after reset IRQ_STATUS_0 = 0x%x\n",
+ counter,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+ /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/
+ msm_camera_io_w(0x0, cpp_dev->cpp_hw_base + 0x16C);
+
+ queue = &cpp_timer.data.cpp_dev->processing_q;
+ queue_len = queue->len;
+ if (!queue_len)
+ pr_err("%s:%d: Invalid queuelen\n", __func__, __LINE__);
+
+ for (i = 0; i < queue_len; i++) {
+ if (cpp_timer.data.processed_frame[i]) {
+ processed_frame[i] =
+ cpp_timer.data.processed_frame[i];
+ ifd = processed_frame[i]->input_buffer_info.fd;
+ ofd = processed_frame[i]->
+ output_buffer_info[0].fd;
+ dfd = processed_frame[i]->
+ duplicate_buffer_info.fd;
+ t0fd = processed_frame[i]->
+ tnr_scratch_buffer_info[0].fd;
+ t1fd = processed_frame[i]->
+ tnr_scratch_buffer_info[1].fd;
+ pr_err("Fault on identity=0x%x, frame_id=%03d\n",
+ processed_frame[i]->identity,
+ processed_frame[i]->frame_id);
+ pr_err("ifd %d ofd %d dfd %d t0fd %d t1fd %d\n",
+ ifd, ofd, dfd, t0fd, t1fd);
+ msm_cpp_dump_addr(cpp_dev, processed_frame[i]);
+ msm_cpp_dump_frame_cmd(processed_frame[i]);
+ }
+ }
+ msm_cpp_flush_queue_and_release_buffer(cpp_dev, queue_len);
+ cpp_dev->fault_status = CPP_IOMMU_FAULT_RECOVERED;
+ pr_err("fault recovery successful\n");
+ }
+}
+
+static int cpp_init_mem(struct cpp_device *cpp_dev)
+{
+ int rc = 0;
+ int iommu_hdl;
+
+ if (cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_5_0_0 ||
+ cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_5_1_0)
+ rc = cam_smmu_get_handle("cpp_0", &iommu_hdl);
+ else
+ rc = cam_smmu_get_handle("cpp", &iommu_hdl);
+
+ if (rc < 0) {
+ pr_err("smmu get handle failed\n");
+ return -ENODEV;
+ }
+
+ cpp_dev->iommu_hdl = iommu_hdl;
+ cam_smmu_reg_client_page_fault_handler(
+ cpp_dev->iommu_hdl,
+ msm_cpp_iommu_fault_handler,
+ msm_cpp_iommu_fault_reset_handler,
+ cpp_dev);
+ return 0;
+}
+
+
+static irqreturn_t msm_cpp_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t tx_level;
+ uint32_t irq_status;
+ uint32_t i;
+ uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
+ struct cpp_device *cpp_dev = data;
+ struct msm_cpp_tasklet_queue_cmd *queue_cmd;
+
+ irq_status = msm_camera_io_r(cpp_dev->base + MSM_CPP_MICRO_IRQGEN_STAT);
+
+ if (irq_status & 0x8) {
+ tx_level = msm_camera_io_r(cpp_dev->base +
+ MSM_CPP_MICRO_FIFO_TX_STAT) >> 2;
+ for (i = 0; i < tx_level; i++) {
+ tx_fifo[i] = msm_camera_io_r(cpp_dev->base +
+ MSM_CPP_MICRO_FIFO_TX_DATA);
+ }
+ spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
+ queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ pr_err("%s:%d] cpp tasklet queue overflow tx %d rc %x",
+ __func__, __LINE__, tx_level, irq_status);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &cpp_dev->irq_cnt);
+ }
+ queue_cmd->irq_status = irq_status;
+ queue_cmd->tx_level = tx_level;
+ memset(&queue_cmd->tx_fifo[0], 0, sizeof(queue_cmd->tx_fifo));
+ for (i = 0; i < tx_level; i++)
+ queue_cmd->tx_fifo[i] = tx_fifo[i];
+
+ queue_cmd->cmd_used = 1;
+ cpp_dev->taskletq_idx =
+ (cpp_dev->taskletq_idx + 1) % MSM_CPP_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &cpp_dev->tasklet_q);
+ spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
+
+ tasklet_schedule(&cpp_dev->cpp_tasklet);
+ } else if (irq_status & 0x7C0) {
+ pr_debug("irq_status: 0x%x\n", irq_status);
+ pr_debug("DEBUG_SP: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x40));
+ pr_debug("DEBUG_T: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x44));
+ pr_debug("DEBUG_N: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x48));
+ pr_debug("DEBUG_R: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x4C));
+ pr_debug("DEBUG_OPPC: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x50));
+ pr_debug("DEBUG_MO: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x54));
+ pr_debug("DEBUG_TIMER0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x60));
+ pr_debug("DEBUG_TIMER1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x64));
+ pr_debug("DEBUG_GPI: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x70));
+ pr_debug("DEBUG_GPO: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x74));
+ pr_debug("DEBUG_T0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x80));
+ pr_debug("DEBUG_R0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x84));
+ pr_debug("DEBUG_T1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x88));
+ pr_debug("DEBUG_R1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->base + 0x8C));
+ }
+ msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR);
+ return IRQ_HANDLED;
+}
+
+static void msm_cpp_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ uint32_t irq_status;
+ uint32_t tx_level;
+ uint32_t msg_id, cmd_len;
+ uint32_t i;
+ uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
+ struct cpp_device *cpp_dev = (struct cpp_device *) data;
+ struct msm_cpp_tasklet_queue_cmd *queue_cmd;
+
+ while (atomic_read(&cpp_dev->irq_cnt)) {
+ spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&cpp_dev->tasklet_q,
+ struct msm_cpp_tasklet_queue_cmd, list);
+
+ if (!queue_cmd) {
+ atomic_set(&cpp_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &cpp_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+ irq_status = queue_cmd->irq_status;
+ tx_level = queue_cmd->tx_level;
+ for (i = 0; i < tx_level; i++)
+ tx_fifo[i] = queue_cmd->tx_fifo[i];
+
+ spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
+
+ for (i = 0; i < tx_level; i++) {
+ if (tx_fifo[i] == MSM_CPP_MSG_ID_CMD) {
+ cmd_len = tx_fifo[i+1];
+ msg_id = tx_fifo[i+2];
+ if (msg_id == MSM_CPP_MSG_ID_FRAME_ACK) {
+ CPP_DBG("Frame done!!\n");
+ /* delete CPP timer */
+ CPP_DBG("delete timer.\n");
+ msm_cpp_timer_queue_update(cpp_dev);
+ msm_cpp_notify_frame_done(cpp_dev, 0);
+ } else if (msg_id ==
+ MSM_CPP_MSG_ID_FRAME_NACK) {
+ pr_err("NACK error from hw!!\n");
+ CPP_DBG("delete timer.\n");
+ msm_cpp_timer_queue_update(cpp_dev);
+ msm_cpp_notify_frame_done(cpp_dev, 0);
+ }
+ i += cmd_len + 2;
+ }
+ }
+ }
+}
+
+static int cpp_init_hardware(struct cpp_device *cpp_dev)
+{
+ int rc = 0;
+ uint32_t vbif_version;
+
+ cpp_dev->turbo_vote = 0;
+ cpp_dev->fault_status = CPP_IOMMU_FAULT_NONE;
+
+ rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd,
+ cpp_dev->num_reg, true);
+ if (rc < 0) {
+ pr_err("%s: failed to enable regulators\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ if (cpp_dev->micro_reset) {
+ rc = msm_cpp_set_micro_clk(cpp_dev);
+ if (rc < 0) {
+ pr_err("%s: reset micro clk failed\n", __func__);
+ goto clk_failed;
+ }
+ }
+
+ rc = msm_camera_clk_enable(&cpp_dev->pdev->dev, cpp_dev->clk_info,
+ cpp_dev->cpp_clk, cpp_dev->num_clks, true);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto clk_failed;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ goto ahb_vote_fail;
+ }
+
+ if (cpp_dev->state != CPP_STATE_BOOT) {
+ rc = msm_camera_register_irq(cpp_dev->pdev, cpp_dev->irq,
+ msm_cpp_irq, IRQF_TRIGGER_RISING, "cpp", cpp_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ goto req_irq_fail;
+ }
+ rc = msm_cam_buf_mgr_register_ops(&cpp_dev->buf_mgr_ops);
+ if (rc < 0) {
+ pr_err("buf mngr req ops failed\n");
+ msm_camera_unregister_irq(cpp_dev->pdev,
+ cpp_dev->irq, cpp_dev);
+ goto req_irq_fail;
+ }
+ }
+
+ cpp_dev->hw_info.cpp_hw_version =
+ msm_camera_io_r(cpp_dev->cpp_hw_base);
+ if (cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_4_1_0) {
+ vbif_version = msm_camera_io_r(cpp_dev->vbif_base);
+ if (vbif_version == VBIF_VERSION_2_3_0)
+ cpp_dev->hw_info.cpp_hw_version = CPP_HW_VERSION_4_0_0;
+ }
+ pr_info("CPP HW Version: 0x%x\n", cpp_dev->hw_info.cpp_hw_version);
+ cpp_dev->hw_info.cpp_hw_caps =
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4);
+
+ rc = msm_update_freq_tbl(cpp_dev);
+ if (rc < 0)
+ goto pwr_collapse_reset;
+
+ pr_debug("CPP HW Caps: 0x%x\n", cpp_dev->hw_info.cpp_hw_caps);
+ msm_camera_io_w(0x1, cpp_dev->vbif_base + 0x4);
+ cpp_dev->taskletq_idx = 0;
+ atomic_set(&cpp_dev->irq_cnt, 0);
+ rc = msm_cpp_create_buff_queue(cpp_dev, MSM_CPP_MAX_BUFF_QUEUE);
+ if (rc < 0) {
+ pr_err("%s: create buff queue failed with err %d\n",
+ __func__, rc);
+ goto pwr_collapse_reset;
+ }
+ pr_err("stream_cnt:%d\n", cpp_dev->stream_cnt);
+ cpp_dev->stream_cnt = 0;
+ if (cpp_dev->fw_name_bin) {
+ msm_camera_enable_irq(cpp_dev->irq, false);
+ rc = cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
+ if (rc < 0) {
+ pr_err("%s: load firmware failure %d-retry\n",
+ __func__, rc);
+ rc = msm_cpp_reset_vbif_and_load_fw(cpp_dev);
+ if (rc < 0) {
+ msm_camera_enable_irq(cpp_dev->irq, true);
+ goto pwr_collapse_reset;
+ }
+ }
+ msm_camera_enable_irq(cpp_dev->irq, true);
+ msm_camera_io_w_mb(0x7C8, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ }
+
+ msm_cpp_set_vbif_reg_values(cpp_dev);
+ return rc;
+
+pwr_collapse_reset:
+ msm_cpp_update_gdscr_status(cpp_dev, false);
+ msm_camera_unregister_irq(cpp_dev->pdev, cpp_dev->irq, cpp_dev);
+req_irq_fail:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ahb_vote_fail:
+ msm_camera_clk_enable(&cpp_dev->pdev->dev, cpp_dev->clk_info,
+ cpp_dev->cpp_clk, cpp_dev->num_clks, false);
+clk_failed:
+ msm_camera_regulator_enable(cpp_dev->cpp_vdd,
+ cpp_dev->num_reg, false);
+reg_enable_failed:
+ return rc;
+}
+
+static void cpp_release_hardware(struct cpp_device *cpp_dev)
+{
+ int32_t rc;
+
+ if (cpp_dev->state != CPP_STATE_BOOT) {
+ msm_camera_unregister_irq(cpp_dev->pdev, cpp_dev->irq, cpp_dev);
+ tasklet_kill(&cpp_dev->cpp_tasklet);
+ atomic_set(&cpp_dev->irq_cnt, 0);
+ }
+ msm_cpp_delete_buff_queue(cpp_dev);
+ msm_cpp_update_gdscr_status(cpp_dev, false);
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ msm_camera_clk_enable(&cpp_dev->pdev->dev, cpp_dev->clk_info,
+ cpp_dev->cpp_clk, cpp_dev->num_clks, false);
+ msm_camera_regulator_enable(cpp_dev->cpp_vdd, cpp_dev->num_reg, false);
+ if (cpp_dev->stream_cnt > 0) {
+ pr_warn("stream count active\n");
+ rc = msm_cpp_update_bandwidth_setting(cpp_dev, 0, 0);
+ }
+ cpp_dev->stream_cnt = 0;
+ pr_info("cpp hw release done\n");
+}
+
+static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
+{
+ uint32_t i;
+ uint32_t *ptr_bin = NULL;
+ int32_t rc = 0, ret = 0;
+
+ if (!fw_name_bin) {
+ pr_err("%s:%d] invalid fw name", __func__, __LINE__);
+ rc = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s:%d] FW file: %s\n", __func__, __LINE__, fw_name_bin);
+ if (cpp_dev->fw == NULL) {
+ pr_err("%s:%d] fw NULL", __func__, __LINE__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ ptr_bin = (uint32_t *)cpp_dev->fw->data;
+ if (!ptr_bin) {
+ pr_err("%s:%d] Fw bin NULL", __func__, __LINE__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_NOMINAL_VOTE);
+ if (rc < 0) {
+ pr_err("%s:%d: failed to vote for AHB\n", __func__, __LINE__);
+ goto end;
+ }
+
+ msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
+ msm_camera_io_w(0x1, cpp_dev->base +
+ MSM_CPP_MICRO_BOOT_START);
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_CMD, rc);
+ goto vote;
+ }
+
+ msm_camera_io_w(0xFFFFFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto vote;
+ }
+ /*Start firmware loading*/
+ msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
+ msm_cpp_write(cpp_dev->fw->size, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto vote;
+ }
+ for (i = 0; i < cpp_dev->fw->size/4; i++) {
+ msm_cpp_write(*ptr_bin, cpp_dev->base);
+ if (i % MSM_CPP_RX_FIFO_LEVEL == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto vote;
+ }
+ }
+ ptr_bin++;
+ }
+ msm_camera_io_w_mb(0x00, cpp_dev->cpp_hw_base + 0xC);
+ rc = msm_cpp_update_gdscr_status(cpp_dev, true);
+ if (rc < 0)
+ pr_err("update cpp gdscr status failed\n");
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_OK, rc);
+ goto vote;
+ }
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_CMD, rc);
+ goto vote;
+ }
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto vote;
+ }
+ /*Trigger MC to jump to start address*/
+ msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_JUMP_ADDRESS, cpp_dev->base);
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_CMD, rc);
+ goto vote;
+ }
+
+ rc = msm_cpp_poll(cpp_dev->base, 0x1);
+ if (rc) {
+ pr_err("%s:%d] poll command 0x1 failed %d", __func__, __LINE__,
+ rc);
+ goto vote;
+ }
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_JUMP_ACK, rc);
+ goto vote;
+ }
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_JUMP_ACK, rc);
+ }
+
+vote:
+ ret = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_SVS_VOTE);
+ if (ret < 0) {
+ pr_err("%s:%d: failed to vote for AHB\n", __func__, __LINE__);
+ rc = ret;
+ }
+end:
+ return rc;
+}
+
+static int32_t msm_cpp_reset_vbif_clients(struct cpp_device *cpp_dev)
+{
+ uint32_t i;
+
+ pr_warn("%s: handle vbif hang...\n", __func__);
+ for (i = 0; i < VBIF_CLIENT_MAX; i++) {
+ if (cpp_dev->vbif_data->err_handler[i] == NULL)
+ continue;
+
+ cpp_dev->vbif_data->err_handler[i](
+ cpp_dev->vbif_data->dev[i], CPP_VBIF_ERROR_HANG);
+ }
+ return 0;
+}
+
+static int32_t msm_cpp_reset_vbif_and_load_fw(struct cpp_device *cpp_dev)
+{
+ int32_t rc = 0;
+
+ msm_cpp_reset_vbif_clients(cpp_dev);
+
+ rc = cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
+ if (rc < 0)
+ pr_err("Reset and load fw failed %d\n", rc);
+
+ return rc;
+}
+
+static int cpp_vbif_error_handler(void *dev, uint32_t vbif_error)
+{
+ struct cpp_device *cpp_dev = NULL;
+
+ if (dev == NULL || vbif_error >= CPP_VBIF_ERROR_MAX) {
+ pr_err("failed: dev %pK, vbif error %d\n", dev, vbif_error);
+ return -EINVAL;
+ }
+
+ cpp_dev = (struct cpp_device *) dev;
+
+ /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+ pr_err("%s: before reset halt... read MMSS_A_CPP_IRQ_STATUS_0 = 0x%x",
+ __func__, msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+ pr_err("%s: start reset bus bridge on FD + CPP!\n", __func__);
+ /* MMSS_A_CPP_RST_CMD_0 = 0x8, firmware reset = 0x3DF77 */
+ msm_camera_io_w(0x3DF77, cpp_dev->cpp_hw_base + 0x8);
+
+ /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+ pr_err("%s: after reset halt... read MMSS_A_CPP_IRQ_STATUS_0 = 0x%x",
+ __func__, msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+ return 0;
+}
+
+static int cpp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ int rc;
+ uint32_t i;
+ struct cpp_device *cpp_dev = NULL;
+
+ CPP_DBG("E");
+
+ if (!sd || !fh) {
+ pr_err("Wrong input parameters sd %pK fh %pK!",
+ sd, fh);
+ return -EINVAL;
+ }
+ cpp_dev = v4l2_get_subdevdata(sd);
+ if (!cpp_dev) {
+ pr_err("failed: cpp_dev %pK\n", cpp_dev);
+ return -EINVAL;
+ }
+ mutex_lock(&cpp_dev->mutex);
+ if (cpp_dev->cpp_open_cnt == MAX_ACTIVE_CPP_INSTANCE) {
+ pr_err("No free CPP instance\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+ if (cpp_dev->cpp_subscribe_list[i].active == 0) {
+ cpp_dev->cpp_subscribe_list[i].active = 1;
+ cpp_dev->cpp_subscribe_list[i].vfh = &fh->vfh;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_CPP_INSTANCE) {
+ pr_err("No free instance\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -ENODEV;
+ }
+
+ CPP_DBG("open %d %pK\n", i, &fh->vfh);
+ cpp_dev->cpp_open_cnt++;
+
+ msm_cpp_vbif_register_error_handler(cpp_dev,
+ VBIF_CLIENT_CPP, cpp_vbif_error_handler);
+
+ if (cpp_dev->cpp_open_cnt == 1) {
+ rc = cpp_init_hardware(cpp_dev);
+ if (rc < 0) {
+ cpp_dev->cpp_open_cnt--;
+ cpp_dev->cpp_subscribe_list[i].active = 0;
+ cpp_dev->cpp_subscribe_list[i].vfh = NULL;
+ mutex_unlock(&cpp_dev->mutex);
+ return rc;
+ }
+
+ rc = cpp_init_mem(cpp_dev);
+ if (rc < 0) {
+ pr_err("Error: init memory fail\n");
+ cpp_dev->cpp_open_cnt--;
+ cpp_dev->cpp_subscribe_list[i].active = 0;
+ cpp_dev->cpp_subscribe_list[i].vfh = NULL;
+ mutex_unlock(&cpp_dev->mutex);
+ return rc;
+ }
+
+ cpp_dev->state = CPP_STATE_IDLE;
+
+ CPP_DBG("Invoking msm_ion_client_create()\n");
+ cpp_dev->ion_client = msm_ion_client_create("cpp");
+ if (cpp_dev->ion_client == NULL) {
+ pr_err("msm_ion_client_create() failed\n");
+ mutex_unlock(&cpp_dev->mutex);
+ rc = -ENOMEM;
+ }
+ }
+
+ mutex_unlock(&cpp_dev->mutex);
+ return 0;
+}
+
+static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ uint32_t i;
+ int rc = -1;
+ struct cpp_device *cpp_dev = NULL;
+ struct msm_device_queue *processing_q = NULL;
+ struct msm_device_queue *eventData_q = NULL;
+
+ if (!sd) {
+ pr_err("Wrong input sd parameter");
+ return -EINVAL;
+ }
+ cpp_dev = v4l2_get_subdevdata(sd);
+
+ if (!cpp_dev) {
+ pr_err("failed: cpp_dev %pK\n", cpp_dev);
+ return -EINVAL;
+ }
+
+ mutex_lock(&cpp_dev->mutex);
+
+ processing_q = &cpp_dev->processing_q;
+ eventData_q = &cpp_dev->eventData_q;
+
+ if (cpp_dev->cpp_open_cnt == 0) {
+ mutex_unlock(&cpp_dev->mutex);
+ return 0;
+ }
+
+ for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+ if (cpp_dev->cpp_subscribe_list[i].active == 1) {
+ cpp_dev->cpp_subscribe_list[i].active = 0;
+ cpp_dev->cpp_subscribe_list[i].vfh = NULL;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_CPP_INSTANCE) {
+ pr_err("Invalid close\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -ENODEV;
+ }
+
+ if (cpp_dev->turbo_vote == 1) {
+ rc = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (rc)
+ pr_err("cx_ipeak_update failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+
+ cpp_dev->cpp_open_cnt--;
+ if (cpp_dev->cpp_open_cnt == 0) {
+ pr_debug("irq_status: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4));
+ pr_debug("DEBUG_SP: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_debug("DEBUG_T: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_debug("DEBUG_N: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_debug("DEBUG_R: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_debug("DEBUG_OPPC: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_debug("DEBUG_MO: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_debug("DEBUG_TIMER0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_debug("DEBUG_TIMER1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_debug("DEBUG_GPI: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_debug("DEBUG_GPO: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_debug("DEBUG_T0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_debug("DEBUG_R0: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_debug("DEBUG_T1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_debug("DEBUG_R1: 0x%x\n",
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
+ msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
+ msm_cpp_clear_timer(cpp_dev);
+ cpp_release_hardware(cpp_dev);
+ if (cpp_dev->iommu_state == CPP_IOMMU_STATE_ATTACHED) {
+ if (cpp_dev->security_mode == SECURE_MODE)
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_DETACH_SEC_CPP);
+ else
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_DETACH);
+
+ if (rc < 0)
+ pr_err("Error: Detach fail in release\n");
+ cpp_dev->iommu_state = CPP_IOMMU_STATE_DETACHED;
+ }
+ cam_smmu_destroy_handle(cpp_dev->iommu_hdl);
+ msm_cpp_empty_list(processing_q, list_frame);
+ msm_cpp_empty_list(eventData_q, list_eventdata);
+ cpp_dev->state = CPP_STATE_OFF;
+
+ if (cpp_dev->ion_client) {
+ CPP_DBG("Invoking ion_client_destroy()\n");
+ ion_client_destroy(cpp_dev->ion_client);
+ cpp_dev->ion_client = NULL;
+ }
+ }
+
+ /* unregister vbif error handler */
+ msm_cpp_vbif_register_error_handler(cpp_dev,
+ VBIF_CLIENT_CPP, NULL);
+ mutex_unlock(&cpp_dev->mutex);
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_cpp_internal_ops = {
+ .open = cpp_open_node,
+ .close = cpp_close_node,
+};
+
+static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t ids,
+ void *arg)
+{
+ int rc = -EINVAL;
+
+ switch (buff_mgr_ops) {
+ case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: {
+ rc = msm_cpp_buffer_private_ops(cpp_dev, buff_mgr_ops,
+ ids, arg);
+ break;
+ }
+ case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
+ case VIDIOC_MSM_BUF_MNGR_BUF_DONE:
+ case VIDIOC_MSM_BUF_MNGR_GET_BUF:
+ default: {
+ struct msm_buf_mngr_info *buff_mgr_info =
+ (struct msm_buf_mngr_info *)arg;
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops,
+ buff_mgr_info);
+ break;
+ }
+ }
+ if (rc < 0)
+ pr_debug("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev,
+ uint8_t put_buf)
+{
+ struct v4l2_event v4l2_evt;
+ struct msm_queue_cmd *frame_qcmd = NULL;
+ struct msm_queue_cmd *event_qcmd = NULL;
+ struct msm_cpp_frame_info_t *processed_frame = NULL;
+ struct msm_device_queue *queue = &cpp_dev->processing_q;
+ struct msm_buf_mngr_info buff_mgr_info;
+ int rc = 0;
+
+ frame_qcmd = msm_dequeue(queue, list_frame, POP_FRONT);
+ if (frame_qcmd) {
+ processed_frame = frame_qcmd->command;
+ do_gettimeofday(&(processed_frame->out_time));
+ kfree(frame_qcmd);
+ event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_ATOMIC);
+ if (!event_qcmd) {
+ pr_err("Insufficient memory\n");
+ return -ENOMEM;
+ }
+ atomic_set(&event_qcmd->on_heap, 1);
+ event_qcmd->command = processed_frame;
+ CPP_DBG("fid %d\n", processed_frame->frame_id);
+ msm_enqueue(&cpp_dev->eventData_q, &event_qcmd->list_eventdata);
+
+ if ((processed_frame->partial_frame_indicator != 0) &&
+ (processed_frame->last_payload == 0))
+ goto NOTIFY_FRAME_DONE;
+
+ if (!processed_frame->output_buffer_info[0].processed_divert &&
+ !processed_frame->output_buffer_info[0].native_buff &&
+ !processed_frame->we_disable) {
+
+ int32_t iden = processed_frame->identity;
+
+ SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(processed_frame,
+ iden, processed_frame->duplicate_identity);
+
+ memset(&buff_mgr_info, 0,
+ sizeof(struct msm_buf_mngr_info));
+
+ buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (iden & 0xFFFF);
+ buff_mgr_info.frame_id = processed_frame->frame_id;
+ buff_mgr_info.timestamp = processed_frame->timestamp;
+ if (processed_frame->batch_info.batch_mode ==
+ BATCH_MODE_VIDEO ||
+ (IS_BATCH_BUFFER_ON_PREVIEW(
+ processed_frame))) {
+ buff_mgr_info.index =
+ processed_frame->batch_info.cont_idx;
+ } else {
+ buff_mgr_info.index = processed_frame->
+ output_buffer_info[0].index;
+ }
+ if (put_buf) {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ 0x0, &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error putting buffer\n");
+ rc = -EINVAL;
+ }
+ } else {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ 0x0, &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error putting buffer\n");
+ rc = -EINVAL;
+ }
+ }
+ }
+
+ if (processed_frame->duplicate_output &&
+ !processed_frame->
+ duplicate_buffer_info.processed_divert &&
+ !processed_frame->we_disable) {
+ int32_t iden = processed_frame->duplicate_identity;
+
+ SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(processed_frame,
+ iden, processed_frame->identity);
+
+ memset(&buff_mgr_info, 0,
+ sizeof(struct msm_buf_mngr_info));
+
+ buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (iden & 0xFFFF);
+ buff_mgr_info.frame_id = processed_frame->frame_id;
+ buff_mgr_info.timestamp = processed_frame->timestamp;
+ buff_mgr_info.index =
+ processed_frame->duplicate_buffer_info.index;
+ if (put_buf) {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ 0x0, &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error putting buffer\n");
+ rc = -EINVAL;
+ }
+ } else {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ 0x0, &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error putting buffer\n");
+ rc = -EINVAL;
+ }
+ }
+ }
+NOTIFY_FRAME_DONE:
+ v4l2_evt.id = processed_frame->inst_id;
+ v4l2_evt.type = V4L2_EVENT_CPP_FRAME_DONE;
+ v4l2_event_queue(cpp_dev->msm_sd.sd.devnode, &v4l2_evt);
+ }
+ return rc;
+}
+
+#if MSM_CPP_DUMP_FRM_CMD
+static int msm_cpp_dump_frame_cmd(struct msm_cpp_frame_info_t *frame_info)
+{
+ int i, i1, i2;
+ struct cpp_device *cpp_dev = cpp_timer.data.cpp_dev;
+
+ CPP_DBG("-- start: cpp frame cmd for identity=0x%x, frame_id=%d --\n",
+ frame_info->identity, frame_info->frame_id);
+
+ CPP_DBG("msg[%03d] = 0x%08x\n", 0, 0x6);
+ /* send top level and plane level */
+ for (i = 0; i < cpp_dev->payload_params.stripe_base; i++)
+ CPP_DBG("msg[%03d] = 0x%08x\n", i,
+ frame_info->cpp_cmd_msg[i]);
+ /* send stripes */
+ i1 = cpp_dev->payload_params.stripe_base +
+ cpp_dev->payload_params.stripe_size *
+ frame_info->first_stripe_index;
+ i2 = cpp_dev->payload_params.stripe_size *
+ (frame_info->last_stripe_index -
+ frame_info->first_stripe_index + 1);
+ for (i = 0; i < i2; i++)
+ CPP_DBG("msg[%03d] = 0x%08x\n", i+i1,
+ frame_info->cpp_cmd_msg[i+i1]);
+ /* send trailer */
+ CPP_DBG("msg[%03d] = 0x%08x\n", i+i1, MSM_CPP_MSG_ID_TRAILER);
+ CPP_DBG("-- end: cpp frame cmd for identity=0x%x, frame_id=%d --\n",
+ frame_info->identity, frame_info->frame_id);
+ return 0;
+}
+#else
+static int msm_cpp_dump_frame_cmd(struct msm_cpp_frame_info_t *frame_info)
+{
+ return 0;
+}
+#endif
+
+static void msm_cpp_flush_queue_and_release_buffer(struct cpp_device *cpp_dev,
+ int queue_len) {
+ uint32_t i;
+
+ while (queue_len) {
+ msm_cpp_notify_frame_done(cpp_dev, 1);
+ queue_len--;
+ }
+ atomic_set(&cpp_timer.used, 0);
+ for (i = 0; i < MAX_CPP_PROCESSING_FRAME; i++)
+ cpp_timer.data.processed_frame[i] = NULL;
+}
+
+static void msm_cpp_set_micro_irq_mask(struct cpp_device *cpp_dev,
+ uint8_t enable, uint32_t irq_mask)
+{
+ msm_camera_io_w_mb(irq_mask, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ if (enable)
+ enable_irq(cpp_dev->irq->start);
+}
+
+static void msm_cpp_do_timeout_work(struct work_struct *work)
+{
+ uint32_t j = 0, i = 0, i1 = 0, i2 = 0;
+ int32_t queue_len = 0, rc = 0, fifo_counter = 0;
+ struct msm_device_queue *queue = NULL;
+ struct msm_cpp_frame_info_t *processed_frame[MAX_CPP_PROCESSING_FRAME];
+ struct cpp_device *cpp_dev = cpp_timer.data.cpp_dev;
+
+ pr_warn("cpp_timer_callback called. (jiffies=%lu)\n",
+ jiffies);
+ mutex_lock(&cpp_dev->mutex);
+
+ if (!work || (cpp_timer.data.cpp_dev->state != CPP_STATE_ACTIVE)) {
+ pr_err("Invalid work:%pK or state:%d\n", work,
+ cpp_timer.data.cpp_dev->state);
+ /* Do not flush queue here as it is not a fatal error */
+ goto end;
+ }
+ if (!atomic_read(&cpp_timer.used)) {
+ pr_warn("Delayed trigger, IRQ serviced\n");
+ /* Do not flush queue here as it is not a fatal error */
+ goto end;
+ }
+
+ msm_camera_enable_irq(cpp_timer.data.cpp_dev->irq, false);
+ /* make sure all the pending queued entries are scheduled */
+ tasklet_kill(&cpp_dev->cpp_tasklet);
+
+ queue = &cpp_timer.data.cpp_dev->processing_q;
+ queue_len = queue->len;
+ if (!queue_len) {
+ pr_err("%s:%d: irq serviced after timeout.Ignore timeout\n",
+ __func__, __LINE__);
+ msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
+ goto end;
+ }
+
+ pr_debug("Reloading firmware %d\n", queue_len);
+ rc = cpp_load_fw(cpp_timer.data.cpp_dev,
+ cpp_timer.data.cpp_dev->fw_name_bin);
+ if (rc) {
+ pr_warn("Firmware loading failed-retry\n");
+ rc = msm_cpp_reset_vbif_and_load_fw(cpp_dev);
+ if (rc < 0) {
+ pr_err("Firmware loading failed\n");
+ goto error;
+ }
+ } else {
+ pr_debug("Firmware loading done\n");
+ }
+
+ if (!atomic_read(&cpp_timer.used)) {
+ pr_warn("Delayed trigger, IRQ serviced\n");
+ /* Do not flush queue here as it is not a fatal error */
+ msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
+ cpp_dev->timeout_trial_cnt = 0;
+ goto end;
+ }
+
+ if (cpp_dev->timeout_trial_cnt >=
+ cpp_dev->max_timeout_trial_cnt) {
+ pr_warn("Max trial reached\n");
+ msm_cpp_flush_queue_and_release_buffer(cpp_dev, queue_len);
+ msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
+ goto end;
+ }
+
+ for (i = 0; i < queue_len; i++) {
+ processed_frame[i] = cpp_timer.data.processed_frame[i];
+ if (!processed_frame[i]) {
+ pr_warn("process frame null , queue len %d", queue_len);
+ msm_cpp_flush_queue_and_release_buffer(cpp_dev,
+ queue_len);
+ msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
+ goto end;
+ }
+ }
+
+ atomic_set(&cpp_timer.used, 1);
+ pr_warn("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ mod_timer(&cpp_timer.cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+
+ msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
+
+ for (i = 0; i < queue_len; i++) {
+ pr_warn("Rescheduling for identity=0x%x, frame_id=%03d\n",
+ processed_frame[i]->identity,
+ processed_frame[i]->frame_id);
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d: Reschedule payload failed %d\n",
+ __func__, __LINE__, rc);
+ goto error;
+ }
+ msm_cpp_write(0x6, cpp_dev->base);
+ fifo_counter++;
+ /* send top level and plane level */
+ for (j = 0; j < cpp_dev->payload_params.stripe_base; j++,
+ fifo_counter++) {
+ if (fifo_counter % MSM_CPP_RX_FIFO_LEVEL == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll failed %d rc %d",
+ __func__, __LINE__, j, rc);
+ goto error;
+ }
+ fifo_counter = 0;
+ }
+ msm_cpp_write(processed_frame[i]->cpp_cmd_msg[j],
+ cpp_dev->base);
+ }
+ if (rc) {
+ pr_err("%s:%d: Rescheduling plane info failed %d\n",
+ __func__, __LINE__, rc);
+ goto error;
+ }
+ /* send stripes */
+ i1 = cpp_dev->payload_params.stripe_base +
+ cpp_dev->payload_params.stripe_size *
+ processed_frame[i]->first_stripe_index;
+ i2 = cpp_dev->payload_params.stripe_size *
+ (processed_frame[i]->last_stripe_index -
+ processed_frame[i]->first_stripe_index + 1);
+ for (j = 0; j < i2; j++, fifo_counter++) {
+ if (fifo_counter % MSM_CPP_RX_FIFO_LEVEL == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll failed %d rc %d",
+ __func__, __LINE__, j, rc);
+ break;
+ }
+ fifo_counter = 0;
+ }
+ msm_cpp_write(processed_frame[i]->cpp_cmd_msg[j+i1],
+ cpp_dev->base);
+ }
+ if (rc) {
+ pr_err("%s:%d] Rescheduling stripe info failed %d\n",
+ __func__, __LINE__, rc);
+ goto error;
+ }
+ /* send trailer */
+
+ if (fifo_counter % MSM_CPP_RX_FIFO_LEVEL == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] Reschedule trailer failed %d\n",
+ __func__, __LINE__, rc);
+ goto error;
+ }
+ fifo_counter = 0;
+ }
+ msm_cpp_write(0xabcdefaa, cpp_dev->base);
+ pr_debug("After frame:%d write\n", i+1);
+ }
+
+ cpp_timer.data.cpp_dev->timeout_trial_cnt++;
+
+end:
+ mutex_unlock(&cpp_dev->mutex);
+ pr_debug("%s:%d] exit\n", __func__, __LINE__);
+ return;
+error:
+ cpp_dev->state = CPP_STATE_OFF;
+ /* flush the queue */
+ msm_cpp_flush_queue_and_release_buffer(cpp_dev,
+ queue_len);
+ msm_cpp_set_micro_irq_mask(cpp_dev, 0, 0x0);
+ cpp_dev->timeout_trial_cnt = 0;
+ mutex_unlock(&cpp_dev->mutex);
+ pr_debug("%s:%d] exit\n", __func__, __LINE__);
+}
+
+static void cpp_timer_callback(unsigned long data)
+{
+ struct msm_cpp_work_t *work =
+ cpp_timer.data.cpp_dev->work;
+ queue_work(cpp_timer.data.cpp_dev->timer_wq,
+ (struct work_struct *)work);
+}
+
+static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
+ struct msm_queue_cmd *frame_qcmd)
+{
+ unsigned long flags;
+ uint32_t i, i1, i2;
+ int32_t rc = -EAGAIN;
+ struct msm_cpp_frame_info_t *process_frame;
+ struct msm_queue_cmd *qcmd = NULL;
+ uint32_t queue_len = 0, fifo_counter = 0;
+
+ if (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
+ process_frame = frame_qcmd->command;
+ msm_cpp_dump_frame_cmd(process_frame);
+ spin_lock_irqsave(&cpp_timer.data.processed_frame_lock, flags);
+ msm_enqueue(&cpp_dev->processing_q,
+ &frame_qcmd->list_frame);
+ cpp_timer.data.processed_frame[cpp_dev->processing_q.len - 1] =
+ process_frame;
+ queue_len = cpp_dev->processing_q.len;
+ spin_unlock_irqrestore(&cpp_timer.data.processed_frame_lock,
+ flags);
+ atomic_set(&cpp_timer.used, 1);
+
+ CPP_DBG("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ if (mod_timer(&cpp_timer.cpp_timer,
+ (jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS))) != 0)
+ CPP_DBG("Timer has not expired yet\n");
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d: Scheduling payload failed %d",
+ __func__, __LINE__, rc);
+ goto dequeue_frame;
+ }
+ msm_cpp_write(0x6, cpp_dev->base);
+ fifo_counter++;
+ /* send top level and plane level */
+ for (i = 0; i < cpp_dev->payload_params.stripe_base; i++,
+ fifo_counter++) {
+ if ((fifo_counter % MSM_CPP_RX_FIFO_LEVEL) == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc)
+ break;
+ fifo_counter = 0;
+ }
+ msm_cpp_write(process_frame->cpp_cmd_msg[i],
+ cpp_dev->base);
+ }
+ if (rc) {
+ pr_err("%s:%d: Scheduling plane info failed %d\n",
+ __func__, __LINE__, rc);
+ goto dequeue_frame;
+ }
+ /* send stripes */
+ i1 = cpp_dev->payload_params.stripe_base +
+ cpp_dev->payload_params.stripe_size *
+ process_frame->first_stripe_index;
+ i2 = cpp_dev->payload_params.stripe_size *
+ (process_frame->last_stripe_index -
+ process_frame->first_stripe_index + 1);
+ for (i = 0; i < i2; i++, fifo_counter++) {
+ if ((fifo_counter % MSM_CPP_RX_FIFO_LEVEL) == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc)
+ break;
+ fifo_counter = 0;
+ }
+ msm_cpp_write(process_frame->cpp_cmd_msg[i+i1],
+ cpp_dev->base);
+ }
+ if (rc) {
+ pr_err("%s:%d: Scheduling stripe info failed %d\n",
+ __func__, __LINE__, rc);
+ goto dequeue_frame;
+ }
+ /* send trailer */
+ if ((fifo_counter % MSM_CPP_RX_FIFO_LEVEL) == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s: Scheduling trailer failed %d\n",
+ __func__, rc);
+ goto dequeue_frame;
+ }
+ fifo_counter = 0;
+ }
+ msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
+
+ do_gettimeofday(&(process_frame->in_time));
+ rc = 0;
+ } else {
+ pr_err("process queue full. drop frame\n");
+ goto end;
+ }
+
+dequeue_frame:
+ if (rc < 0) {
+ qcmd = msm_dequeue(&cpp_dev->processing_q, list_frame,
+ POP_BACK);
+ if (!qcmd)
+ pr_warn("%s:%d: no queue cmd\n", __func__, __LINE__);
+ spin_lock_irqsave(&cpp_timer.data.processed_frame_lock,
+ flags);
+ queue_len = cpp_dev->processing_q.len;
+ spin_unlock_irqrestore(
+ &cpp_timer.data.processed_frame_lock, flags);
+ if (queue_len == 0) {
+ atomic_set(&cpp_timer.used, 0);
+ del_timer(&cpp_timer.cpp_timer);
+ }
+ }
+end:
+ return rc;
+}
+
+static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev,
+ uint32_t *cmd_msg, uint32_t payload_size)
+{
+ uint32_t i;
+ int rc = 0;
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto end;
+ }
+
+ for (i = 0; i < payload_size; i++) {
+ msm_cpp_write(cmd_msg[i], cpp_dev->base);
+ if (i % MSM_CPP_RX_FIFO_LEVEL == 0) {
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto end;
+ }
+ }
+ }
+end:
+ return rc;
+}
+
+static int msm_cpp_flush_frames(struct cpp_device *cpp_dev)
+{
+ return 0;
+}
+
+static struct msm_cpp_frame_info_t *msm_cpp_get_frame(
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ uint32_t *cpp_frame_msg;
+ struct msm_cpp_frame_info_t *new_frame = NULL;
+ int32_t rc = 0;
+
+ new_frame = kzalloc(sizeof(struct msm_cpp_frame_info_t), GFP_KERNEL);
+
+ if (!new_frame) {
+ rc = -ENOMEM;
+ goto no_mem_err;
+ }
+
+ rc = (copy_from_user(new_frame, (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_cpp_frame_info_t)) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ goto frame_err;
+ }
+
+ if ((new_frame->msg_len == 0) ||
+ (new_frame->msg_len > MSM_CPP_MAX_FRAME_LENGTH)) {
+ pr_err("%s:%d: Invalid frame len:%d\n", __func__,
+ __LINE__, new_frame->msg_len);
+ goto frame_err;
+ }
+
+ cpp_frame_msg = kcalloc(new_frame->msg_len, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!cpp_frame_msg)
+ goto frame_err;
+
+ rc = (copy_from_user(cpp_frame_msg,
+ (void __user *)new_frame->cpp_cmd_msg,
+ sizeof(uint32_t) * new_frame->msg_len) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ goto frame_msg_err;
+ }
+ new_frame->cpp_cmd_msg = cpp_frame_msg;
+ return new_frame;
+
+frame_msg_err:
+ kfree(cpp_frame_msg);
+frame_err:
+ kfree(new_frame);
+no_mem_err:
+ return NULL;
+}
+
+static int msm_cpp_check_buf_type(struct msm_buf_mngr_info *buff_mgr_info,
+ struct msm_cpp_frame_info_t *new_frame)
+{
+ int32_t num_output_bufs = 0;
+ uint32_t i = 0;
+
+ if (buff_mgr_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) {
+ new_frame->batch_info.cont_idx =
+ buff_mgr_info->index;
+ num_output_bufs = buff_mgr_info->user_buf.buf_cnt;
+ if (buff_mgr_info->user_buf.buf_cnt <
+ new_frame->batch_info.batch_size) {
+ /* Less bufs than Input buffer */
+ num_output_bufs = buff_mgr_info->user_buf.buf_cnt;
+ } else {
+ /* More or equal bufs as Input buffer */
+ num_output_bufs = new_frame->batch_info.batch_size;
+ }
+ if (num_output_bufs > MSM_OUTPUT_BUF_CNT)
+ return 0;
+ for (i = 0; i < num_output_bufs; i++) {
+ new_frame->output_buffer_info[i].index =
+ buff_mgr_info->user_buf.buf_idx[i];
+ }
+ } else {
+ /* For non-group case use first buf slot */
+ new_frame->output_buffer_info[0].index = buff_mgr_info->index;
+ num_output_bufs = 1;
+ }
+
+ return num_output_bufs;
+}
+
+static void msm_cpp_update_frame_msg_phy_address(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *new_frame, unsigned long in_phyaddr,
+ unsigned long out_phyaddr0, unsigned long out_phyaddr1,
+ unsigned long tnr_scratch_buffer0, unsigned long tnr_scratch_buffer1)
+{
+ int32_t stripe_base, plane_base;
+ uint32_t rd_pntr_off, wr_0_pntr_off, wr_1_pntr_off,
+ wr_2_pntr_off, wr_3_pntr_off;
+ uint32_t wr_0_meta_data_wr_pntr_off, wr_1_meta_data_wr_pntr_off,
+ wr_2_meta_data_wr_pntr_off, wr_3_meta_data_wr_pntr_off;
+ uint32_t rd_ref_pntr_off, wr_ref_pntr_off;
+ uint32_t stripe_size, plane_size;
+ uint32_t fe_mmu_pf_ptr_off, ref_fe_mmu_pf_ptr_off, we_mmu_pf_ptr_off,
+ dup_we_mmu_pf_ptr_off, ref_we_mmu_pf_ptr_off;
+ uint8_t tnr_enabled, ubwc_enabled, mmu_pf_en, cds_en;
+ int32_t i = 0;
+ uint32_t *cpp_frame_msg;
+
+ cpp_frame_msg = new_frame->cpp_cmd_msg;
+
+ /* Update stripe/plane size and base offsets */
+ stripe_base = cpp_dev->payload_params.stripe_base;
+ stripe_size = cpp_dev->payload_params.stripe_size;
+ plane_base = cpp_dev->payload_params.plane_base;
+ plane_size = cpp_dev->payload_params.plane_size;
+
+ /* Fetch engine Offset */
+ rd_pntr_off = cpp_dev->payload_params.rd_pntr_off;
+ /* Write engine offsets */
+ wr_0_pntr_off = cpp_dev->payload_params.wr_0_pntr_off;
+ wr_1_pntr_off = wr_0_pntr_off + 1;
+ wr_2_pntr_off = wr_1_pntr_off + 1;
+ wr_3_pntr_off = wr_2_pntr_off + 1;
+ /* Reference engine offsets */
+ rd_ref_pntr_off = cpp_dev->payload_params.rd_ref_pntr_off;
+ wr_ref_pntr_off = cpp_dev->payload_params.wr_ref_pntr_off;
+ /* Meta data offsets */
+ wr_0_meta_data_wr_pntr_off =
+ cpp_dev->payload_params.wr_0_meta_data_wr_pntr_off;
+ wr_1_meta_data_wr_pntr_off = (wr_0_meta_data_wr_pntr_off + 1);
+ wr_2_meta_data_wr_pntr_off = (wr_1_meta_data_wr_pntr_off + 1);
+ wr_3_meta_data_wr_pntr_off = (wr_2_meta_data_wr_pntr_off + 1);
+ /* MMU PF offsets */
+ fe_mmu_pf_ptr_off = cpp_dev->payload_params.fe_mmu_pf_ptr_off;
+ ref_fe_mmu_pf_ptr_off = cpp_dev->payload_params.ref_fe_mmu_pf_ptr_off;
+ we_mmu_pf_ptr_off = cpp_dev->payload_params.we_mmu_pf_ptr_off;
+ dup_we_mmu_pf_ptr_off = cpp_dev->payload_params.dup_we_mmu_pf_ptr_off;
+ ref_we_mmu_pf_ptr_off = cpp_dev->payload_params.ref_we_mmu_pf_ptr_off;
+
+ pr_debug("feature_mask 0x%x\n", new_frame->feature_mask);
+
+ /* Update individual module status from feature mask */
+ tnr_enabled = ((new_frame->feature_mask & TNR_MASK) >> 2);
+ ubwc_enabled = ((new_frame->feature_mask & UBWC_MASK) >> 5);
+ cds_en = ((new_frame->feature_mask & CDS_MASK) >> 6);
+ mmu_pf_en = ((new_frame->feature_mask & MMU_PF_MASK) >> 7);
+
+ /*
+ * Update the stripe based addresses for fetch/write/reference engines.
+ * Update meta data offset for ubwc.
+ * Update ref engine address for cds / tnr.
+ */
+ for (i = 0; i < new_frame->num_strips; i++) {
+ cpp_frame_msg[stripe_base + rd_pntr_off + i * stripe_size] +=
+ (uint32_t) in_phyaddr;
+ cpp_frame_msg[stripe_base + wr_0_pntr_off + i * stripe_size] +=
+ (uint32_t) out_phyaddr0;
+ cpp_frame_msg[stripe_base + wr_1_pntr_off + i * stripe_size] +=
+ (uint32_t) out_phyaddr1;
+ cpp_frame_msg[stripe_base + wr_2_pntr_off + i * stripe_size] +=
+ (uint32_t) out_phyaddr0;
+ cpp_frame_msg[stripe_base + wr_3_pntr_off + i * stripe_size] +=
+ (uint32_t) out_phyaddr1;
+ if (tnr_enabled) {
+ cpp_frame_msg[stripe_base + rd_ref_pntr_off +
+ i * stripe_size] +=
+ (uint32_t)tnr_scratch_buffer0;
+ cpp_frame_msg[stripe_base + wr_ref_pntr_off +
+ i * stripe_size] +=
+ (uint32_t)tnr_scratch_buffer1;
+ } else if (cds_en) {
+ cpp_frame_msg[stripe_base + rd_ref_pntr_off +
+ i * stripe_size] +=
+ (uint32_t)in_phyaddr;
+ }
+ if (ubwc_enabled) {
+ cpp_frame_msg[stripe_base + wr_0_meta_data_wr_pntr_off +
+ i * stripe_size] += (uint32_t) out_phyaddr0;
+ cpp_frame_msg[stripe_base + wr_1_meta_data_wr_pntr_off +
+ i * stripe_size] += (uint32_t) out_phyaddr1;
+ cpp_frame_msg[stripe_base + wr_2_meta_data_wr_pntr_off +
+ i * stripe_size] += (uint32_t) out_phyaddr0;
+ cpp_frame_msg[stripe_base + wr_3_meta_data_wr_pntr_off +
+ i * stripe_size] += (uint32_t) out_phyaddr1;
+ }
+ }
+
+ if (!mmu_pf_en)
+ goto exit;
+
+ /* Update mmu prefetch related plane specific address */
+ for (i = 0; i < PAYLOAD_NUM_PLANES; i++) {
+ cpp_frame_msg[plane_base + fe_mmu_pf_ptr_off +
+ i * plane_size] += (uint32_t)in_phyaddr;
+ cpp_frame_msg[plane_base + fe_mmu_pf_ptr_off +
+ i * plane_size + 1] += (uint32_t)in_phyaddr;
+ cpp_frame_msg[plane_base + ref_fe_mmu_pf_ptr_off +
+ i * plane_size] += (uint32_t)tnr_scratch_buffer0;
+ cpp_frame_msg[plane_base + ref_fe_mmu_pf_ptr_off +
+ i * plane_size + 1] += (uint32_t)tnr_scratch_buffer0;
+ cpp_frame_msg[plane_base + we_mmu_pf_ptr_off +
+ i * plane_size] += (uint32_t)out_phyaddr0;
+ cpp_frame_msg[plane_base + we_mmu_pf_ptr_off +
+ i * plane_size + 1] += (uint32_t)out_phyaddr0;
+ cpp_frame_msg[plane_base + dup_we_mmu_pf_ptr_off +
+ i * plane_size] += (uint32_t)out_phyaddr1;
+ cpp_frame_msg[plane_base + dup_we_mmu_pf_ptr_off +
+ i * plane_size + 1] += (uint32_t)out_phyaddr1;
+ cpp_frame_msg[plane_base + ref_we_mmu_pf_ptr_off +
+ i * plane_size] += (uint32_t)tnr_scratch_buffer1;
+ cpp_frame_msg[plane_base + ref_we_mmu_pf_ptr_off +
+ i * plane_size + 1] += (uint32_t)tnr_scratch_buffer1;
+ }
+exit:
+ return;
+}
+
+static int32_t msm_cpp_set_group_buffer_duplicate(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *new_frame, unsigned long out_phyaddr,
+ uint32_t num_output_bufs)
+{
+
+ uint32_t *set_group_buffer_w_duplication = NULL;
+ uint32_t *ptr;
+ unsigned long out_phyaddr0, out_phyaddr1, distance;
+ int32_t rc = 0;
+ uint32_t set_group_buffer_len, set_group_buffer_len_bytes,
+ dup_frame_off, ubwc_enabled, j, i = 0;
+
+ do {
+ int iden = new_frame->identity;
+
+ set_group_buffer_len =
+ cpp_dev->payload_params.set_group_buffer_len;
+ if (!set_group_buffer_len) {
+ pr_err("%s: invalid set group buffer cmd len %d\n",
+ __func__, set_group_buffer_len);
+ rc = -EINVAL;
+ break;
+ }
+
+ /*
+ * Length of MSM_CPP_CMD_GROUP_BUFFER_DUP command +
+ * 4 byte for header + 4 byte for the length field +
+ * 4 byte for the trailer + 4 byte for
+ * MSM_CPP_CMD_GROUP_BUFFER_DUP prefix before the payload
+ */
+ set_group_buffer_len += 4;
+ set_group_buffer_len_bytes = set_group_buffer_len *
+ sizeof(uint32_t);
+ set_group_buffer_w_duplication =
+ kzalloc(set_group_buffer_len_bytes, GFP_KERNEL);
+ if (!set_group_buffer_w_duplication) {
+ pr_err("%s: set group buffer data alloc failed\n",
+ __func__);
+ rc = -ENOMEM;
+ break;
+ }
+
+ memset(set_group_buffer_w_duplication, 0x0,
+ set_group_buffer_len_bytes);
+ dup_frame_off =
+ cpp_dev->payload_params.dup_frame_indicator_off;
+ /* Add a factor of 1 as command is prefixed to the payload. */
+ dup_frame_off += 1;
+ ubwc_enabled = ((new_frame->feature_mask & UBWC_MASK) >> 5);
+ ptr = set_group_buffer_w_duplication;
+ /*create and send Set Group Buffer with Duplicate command*/
+ *ptr++ = MSM_CPP_CMD_GROUP_BUFFER_DUP;
+ *ptr++ = MSM_CPP_MSG_ID_CMD;
+ /*
+ * This field is the value read from dt and stands for length of
+ * actual data in payload
+ */
+ *ptr++ = cpp_dev->payload_params.set_group_buffer_len;
+ *ptr++ = MSM_CPP_CMD_GROUP_BUFFER_DUP;
+ *ptr++ = 0;
+ out_phyaddr0 = out_phyaddr;
+
+ SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame,
+ iden, new_frame->duplicate_identity);
+
+ for (i = 1; i < num_output_bufs; i++) {
+ out_phyaddr1 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->output_buffer_info[i],
+ ((iden >> 16) & 0xFFFF),
+ (iden & 0xFFFF),
+ &new_frame->output_buffer_info[i].fd);
+ if (!out_phyaddr1) {
+ pr_err("%s: error getting o/p phy addr\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ distance = out_phyaddr1 - out_phyaddr0;
+ out_phyaddr0 = out_phyaddr1;
+ for (j = 0; j < PAYLOAD_NUM_PLANES; j++)
+ *ptr++ = distance;
+
+ for (j = 0; j < PAYLOAD_NUM_PLANES; j++)
+ *ptr++ = ubwc_enabled ? distance : 0;
+ }
+ if (rc)
+ break;
+
+ if (new_frame->duplicate_output)
+ set_group_buffer_w_duplication[dup_frame_off] =
+ 1 << new_frame->batch_info.pick_preview_idx;
+ else
+ set_group_buffer_w_duplication[dup_frame_off] = 0;
+
+ /*
+ * Index for cpp message id trailer is length of payload for
+ * set group buffer minus 1
+ */
+ set_group_buffer_w_duplication[set_group_buffer_len - 1] =
+ MSM_CPP_MSG_ID_TRAILER;
+ rc = msm_cpp_send_command_to_hardware(cpp_dev,
+ set_group_buffer_w_duplication, set_group_buffer_len);
+ if (rc < 0) {
+ pr_err("%s: Send Command Error rc %d\n", __func__, rc);
+ break;
+ }
+
+ } while (0);
+
+ kfree(set_group_buffer_w_duplication);
+ return rc;
+}
+
+static int32_t msm_cpp_set_group_buffer(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *new_frame, unsigned long out_phyaddr,
+ uint32_t num_output_bufs)
+{
+ uint32_t set_group_buffer_len;
+ uint32_t *set_group_buffer = NULL;
+ uint32_t *ptr;
+ unsigned long out_phyaddr0, out_phyaddr1, distance;
+ int32_t rc = 0;
+ uint32_t set_group_buffer_len_bytes, i = 0;
+ bool batching_valid = false;
+
+ if ((IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) ||
+ new_frame->batch_info.batch_mode == BATCH_MODE_VIDEO)
+ batching_valid = true;
+
+ if (!batching_valid) {
+ pr_debug("%s: batch mode %d, batching valid %d\n",
+ __func__, new_frame->batch_info.batch_mode,
+ batching_valid);
+ return rc;
+ }
+
+ if (new_frame->batch_info.batch_size <= 1) {
+ pr_debug("%s: batch size is invalid %d\n", __func__,
+ new_frame->batch_info.batch_size);
+ return rc;
+ }
+
+ if ((new_frame->feature_mask & BATCH_DUP_MASK) >> 8) {
+ return msm_cpp_set_group_buffer_duplicate(cpp_dev, new_frame,
+ out_phyaddr, num_output_bufs);
+ }
+
+ if (new_frame->duplicate_output) {
+ pr_err("cannot support duplication enable\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ set_group_buffer_len =
+ 2 + 3 * (num_output_bufs - 1);
+ /*
+ * Length of MSM_CPP_CMD_GROUP_BUFFER command +
+ * 4 byte for header + 4 byte for the length field +
+ * 4 byte for the trailer + 4 byte for
+ * MSM_CPP_CMD_GROUP_BUFFER prefix before the payload
+ */
+ set_group_buffer_len += 4;
+ set_group_buffer_len_bytes = set_group_buffer_len *
+ sizeof(uint32_t);
+ set_group_buffer =
+ kzalloc(set_group_buffer_len_bytes, GFP_KERNEL);
+ if (!set_group_buffer) {
+ pr_err("%s: set group buffer data alloc failed\n",
+ __func__);
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ memset(set_group_buffer, 0x0,
+ set_group_buffer_len_bytes);
+ ptr = set_group_buffer;
+ /*Create and send Set Group Buffer*/
+ *ptr++ = MSM_CPP_CMD_GROUP_BUFFER;
+ *ptr++ = MSM_CPP_MSG_ID_CMD;
+ /*
+ * This field is the value read from dt and stands
+ * for length of actual data in payload
+ */
+ *ptr++ = set_group_buffer_len - 4;
+ *ptr++ = MSM_CPP_CMD_GROUP_BUFFER;
+ *ptr++ = 0;
+ out_phyaddr0 = out_phyaddr;
+
+ for (i = 1; i < num_output_bufs; i++) {
+ out_phyaddr1 =
+ msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->output_buffer_info[i],
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF),
+ &new_frame->output_buffer_info[i].fd);
+ if (!out_phyaddr1) {
+ pr_err("%s: error getting o/p phy addr\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_and_exit;
+ }
+ distance = out_phyaddr1 - out_phyaddr0;
+ out_phyaddr0 = out_phyaddr1;
+ *ptr++ = distance;
+ *ptr++ = distance;
+ *ptr++ = distance;
+ }
+ if (rc)
+ goto free_and_exit;
+
+ /*
+ * Index for cpp message id trailer is length of
+ * payload for set group buffer minus 1
+ */
+ set_group_buffer[set_group_buffer_len - 1] =
+ MSM_CPP_MSG_ID_TRAILER;
+ rc = msm_cpp_send_command_to_hardware(cpp_dev,
+ set_group_buffer, set_group_buffer_len);
+ if (rc < 0)
+ pr_err("Send Command Error rc %d\n", rc);
+
+free_and_exit:
+ kfree(set_group_buffer);
+exit:
+ return rc;
+}
+
+static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
+ struct msm_cpp_frame_info_t *new_frame)
+{
+ int32_t rc = 0;
+ struct msm_queue_cmd *frame_qcmd = NULL;
+ uint32_t *cpp_frame_msg;
+ unsigned long in_phyaddr, out_phyaddr0 = (unsigned long)NULL;
+ unsigned long out_phyaddr1;
+ unsigned long tnr_scratch_buffer0, tnr_scratch_buffer1;
+ uint16_t num_stripes = 0;
+ struct msm_buf_mngr_info buff_mgr_info, dup_buff_mgr_info;
+ int32_t in_fd;
+ int32_t num_output_bufs = 1;
+ uint32_t stripe_base = 0;
+ uint32_t stripe_size;
+ uint8_t tnr_enabled;
+ enum msm_camera_buf_mngr_buf_type buf_type =
+ MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
+ uint32_t ioctl_cmd, idx;
+ uint32_t op_index, dup_index;
+
+ stripe_base = cpp_dev->payload_params.stripe_base;
+ stripe_size = cpp_dev->payload_params.stripe_size;
+
+ if (!new_frame) {
+ pr_err("%s: Frame is Null\n", __func__);
+ return -EINVAL;
+ }
+
+ if (cpp_dev->state == CPP_STATE_OFF) {
+ pr_err("%s: cpp state is off, return fatal error\n", __func__);
+ return -EINVAL;
+ }
+
+ cpp_frame_msg = new_frame->cpp_cmd_msg;
+
+ if (cpp_frame_msg == NULL ||
+ (new_frame->msg_len < MSM_CPP_MIN_FRAME_LENGTH)) {
+ pr_err("Length is not correct or frame message is missing\n");
+ return -EINVAL;
+ }
+
+ if (cpp_dev->fault_status == CPP_IOMMU_FAULT_RECOVERED) {
+ pr_err("Error, page fault occurred %d\n",
+ cpp_dev->fault_status);
+ return -EINVAL;
+ } else if (cpp_dev->fault_status == CPP_IOMMU_FAULT_DETECTED) {
+ pr_err("drop frame, page fault occurred %d\n",
+ cpp_dev->fault_status);
+ return -EFAULT;
+ }
+
+ if (cpp_frame_msg[new_frame->msg_len - 1] !=
+ MSM_CPP_MSG_ID_TRAILER) {
+ pr_err("Invalid frame message\n");
+ return -EINVAL;
+ }
+
+ /* Stripe index starts at zero */
+ if ((!new_frame->num_strips) ||
+ (new_frame->first_stripe_index >= new_frame->num_strips) ||
+ (new_frame->last_stripe_index >= new_frame->num_strips) ||
+ (new_frame->first_stripe_index >
+ new_frame->last_stripe_index)) {
+ pr_err("Invalid frame message, #stripes=%d, stripe indices=[%d,%d]\n",
+ new_frame->num_strips,
+ new_frame->first_stripe_index,
+ new_frame->last_stripe_index);
+ return -EINVAL;
+ }
+
+ if (!stripe_size) {
+ pr_err("Invalid frame message, invalid stripe_size (%d)!\n",
+ stripe_size);
+ return -EINVAL;
+ }
+
+ if ((stripe_base == UINT_MAX) ||
+ (new_frame->num_strips >
+ (UINT_MAX - 1 - stripe_base) / stripe_size)) {
+ pr_err("Invalid frame message, num_strips %d is large\n",
+ new_frame->num_strips);
+ return -EINVAL;
+ }
+
+ if ((stripe_base + new_frame->num_strips * stripe_size + 1) !=
+ new_frame->msg_len) {
+ pr_err("Invalid frame message,len=%d,expected=%d\n",
+ new_frame->msg_len,
+ (stripe_base +
+ new_frame->num_strips * stripe_size + 1));
+ return -EINVAL;
+ }
+
+ if (cpp_dev->iommu_state != CPP_IOMMU_STATE_ATTACHED) {
+ pr_err("IOMMU is not attached\n");
+ return -EAGAIN;
+ }
+
+ in_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->input_buffer_info,
+ ((new_frame->input_buffer_info.identity >> 16) & 0xFFFF),
+ (new_frame->input_buffer_info.identity & 0xFFFF), &in_fd);
+ if (!in_phyaddr) {
+ pr_err("%s: error gettting input physical address\n", __func__);
+ rc = -EINVAL;
+ goto frame_msg_err;
+ }
+
+ op_index = new_frame->output_buffer_info[0].index;
+ dup_index = new_frame->duplicate_buffer_info.index;
+
+ if (new_frame->we_disable == 0) {
+ int32_t iden = new_frame->identity;
+
+ if ((new_frame->output_buffer_info[0].native_buff == 0) &&
+ (new_frame->first_payload)) {
+ memset(&buff_mgr_info, 0,
+ sizeof(struct msm_buf_mngr_info));
+ if ((new_frame->batch_info.batch_mode ==
+ BATCH_MODE_VIDEO) ||
+ (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)))
+ buf_type = MSM_CAMERA_BUF_MNGR_BUF_USER;
+
+ SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame,
+ iden, new_frame->duplicate_identity);
+
+ /*
+ * Swap the input buffer index for batch mode with
+ * buffer on preview
+ */
+ SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame,
+ buff_mgr_info, op_index, dup_index);
+
+ buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (iden & 0xFFFF);
+ buff_mgr_info.type = buf_type;
+
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(buff_mgr_info.index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx =
+ MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ ioctl_cmd, idx, &buff_mgr_info);
+ if (rc < 0) {
+ rc = -EAGAIN;
+ pr_debug("%s:get_buf err rc:%d, index %d\n",
+ __func__, rc,
+ new_frame->output_buffer_info[0].index);
+ goto frame_msg_err;
+ }
+ num_output_bufs =
+ msm_cpp_check_buf_type(&buff_mgr_info,
+ new_frame);
+ if (!num_output_bufs) {
+ pr_err("%s: error getting buffer %d\n",
+ __func__, num_output_bufs);
+ rc = -EINVAL;
+ goto phyaddr_err;
+ }
+ }
+
+ out_phyaddr0 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->output_buffer_info[0],
+ ((iden >> 16) & 0xFFFF),
+ (iden & 0xFFFF),
+ &new_frame->output_buffer_info[0].fd);
+ if (!out_phyaddr0) {
+ pr_err("%s: error gettting output physical address\n",
+ __func__);
+ rc = -EINVAL;
+ goto phyaddr_err;
+ }
+ }
+ out_phyaddr1 = out_phyaddr0;
+
+ /* get buffer for duplicate output */
+ if (new_frame->duplicate_output) {
+ int32_t iden = new_frame->duplicate_identity;
+
+ CPP_DBG("duplication enabled, dup_id=0x%x",
+ new_frame->duplicate_identity);
+
+ SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame,
+ iden, new_frame->identity);
+
+ memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+
+ /*
+ * Swap the input buffer index for batch mode with
+ * buffer on preview
+ */
+ SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame,
+ dup_buff_mgr_info, dup_index, op_index);
+
+ dup_buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF);
+ dup_buff_mgr_info.stream_id = (iden & 0xFFFF);
+ dup_buff_mgr_info.type =
+ MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(dup_buff_mgr_info.index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
+ rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx,
+ &dup_buff_mgr_info);
+ if (rc < 0) {
+ rc = -EAGAIN;
+ pr_debug("%s: get_buf err rc:%d, index %d\n",
+ __func__, rc,
+ new_frame->duplicate_buffer_info.index);
+ goto phyaddr_err;
+ }
+ new_frame->duplicate_buffer_info.index =
+ dup_buff_mgr_info.index;
+ out_phyaddr1 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->duplicate_buffer_info,
+ ((iden >> 16) & 0xFFFF),
+ (iden & 0xFFFF),
+ &new_frame->duplicate_buffer_info.fd);
+ if (!out_phyaddr1) {
+ pr_err("error gettting output physical address\n");
+ rc = -EINVAL;
+ msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ 0x0, &dup_buff_mgr_info);
+ goto phyaddr_err;
+ }
+ /* set duplicate enable bit */
+ cpp_frame_msg[5] |= 0x1;
+ CPP_DBG("out_phyaddr1= %08x\n", (uint32_t)out_phyaddr1);
+ }
+
+ tnr_enabled = ((new_frame->feature_mask & TNR_MASK) >> 2);
+ if (tnr_enabled) {
+ tnr_scratch_buffer0 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->tnr_scratch_buffer_info[0],
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF),
+ &new_frame->tnr_scratch_buffer_info[0].fd);
+ if (!tnr_scratch_buffer0) {
+ pr_err("error getting scratch buffer physical address\n");
+ rc = -EINVAL;
+ goto phyaddr_err;
+ }
+
+ tnr_scratch_buffer1 = msm_cpp_fetch_buffer_info(cpp_dev,
+ &new_frame->tnr_scratch_buffer_info[1],
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF),
+ &new_frame->tnr_scratch_buffer_info[1].fd);
+ if (!tnr_scratch_buffer1) {
+ pr_err("error getting scratch buffer physical address\n");
+ rc = -EINVAL;
+ goto phyaddr_err;
+ }
+ } else {
+ tnr_scratch_buffer0 = 0;
+ tnr_scratch_buffer1 = 0;
+ }
+
+
+ msm_cpp_update_frame_msg_phy_address(cpp_dev, new_frame,
+ in_phyaddr, out_phyaddr0, out_phyaddr1,
+ tnr_scratch_buffer0, tnr_scratch_buffer1);
+ if (tnr_enabled) {
+ cpp_frame_msg[10] = tnr_scratch_buffer1 -
+ tnr_scratch_buffer0;
+ }
+
+ rc = msm_cpp_set_group_buffer(cpp_dev, new_frame, out_phyaddr0,
+ num_output_bufs);
+ if (rc) {
+ pr_err("%s: set group buffer failure %d\n", __func__, rc);
+ goto phyaddr_err;
+ }
+
+ num_stripes = new_frame->last_stripe_index -
+ new_frame->first_stripe_index + 1;
+ cpp_frame_msg[1] = stripe_base - 2 + num_stripes * stripe_size;
+
+ frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!frame_qcmd) {
+ rc = -ENOMEM;
+ goto qcmd_err;
+ }
+
+ atomic_set(&frame_qcmd->on_heap, 1);
+ frame_qcmd->command = new_frame;
+ rc = msm_cpp_send_frame_to_hardware(cpp_dev, frame_qcmd);
+ if (rc < 0) {
+ pr_err("%s: error cannot send frame to hardware\n", __func__);
+ rc = -EINVAL;
+ goto qcmd_err;
+ }
+
+ return rc;
+qcmd_err:
+ kfree(frame_qcmd);
+phyaddr_err:
+ if (new_frame->output_buffer_info[0].native_buff == 0)
+ msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ 0x0, &buff_mgr_info);
+frame_msg_err:
+ kfree(cpp_frame_msg);
+ kfree(new_frame);
+ return rc;
+}
+
+static int msm_cpp_cfg(struct cpp_device *cpp_dev,
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ struct msm_cpp_frame_info_t *frame = NULL;
+ struct msm_cpp_frame_info_t k_frame_info;
+ int32_t rc = 0;
+ uint32_t i = 0;
+ uint32_t num_buff = sizeof(k_frame_info.output_buffer_info) /
+ sizeof(struct msm_cpp_buffer_info_t);
+
+ if (copy_from_user(&k_frame_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(k_frame_info)))
+ return -EFAULT;
+
+ frame = msm_cpp_get_frame(ioctl_ptr);
+ if (!frame) {
+ pr_err("%s: Error allocating frame\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = msm_cpp_cfg_frame(cpp_dev, frame);
+ if (rc >= 0) {
+ for (i = 0; i < num_buff; i++) {
+ k_frame_info.output_buffer_info[i] =
+ frame->output_buffer_info[i];
+ }
+ }
+ }
+
+ ioctl_ptr->trans_code = rc;
+
+ if (copy_to_user((void __user *)k_frame_info.status, &rc,
+ sizeof(int32_t)))
+ pr_err("error cannot copy error\n");
+
+
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &k_frame_info, sizeof(k_frame_info))) {
+ pr_err("Error: cannot copy k_frame_info");
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static void msm_cpp_clean_queue(struct cpp_device *cpp_dev)
+{
+ struct msm_queue_cmd *frame_qcmd = NULL;
+ struct msm_cpp_frame_info_t *processed_frame = NULL;
+ struct msm_device_queue *queue = NULL;
+
+ while (cpp_dev->processing_q.len) {
+ pr_debug("queue len:%d\n", cpp_dev->processing_q.len);
+ queue = &cpp_dev->processing_q;
+ frame_qcmd = msm_dequeue(queue, list_frame, POP_FRONT);
+ if (frame_qcmd) {
+ processed_frame = frame_qcmd->command;
+ kfree(frame_qcmd);
+ if (processed_frame)
+ kfree(processed_frame->cpp_cmd_msg);
+ kfree(processed_frame);
+ }
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int msm_cpp_copy_from_ioctl_ptr(void *dst_ptr,
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ int ret;
+
+ if ((ioctl_ptr->ioctl_ptr == NULL) || (ioctl_ptr->len == 0)) {
+ pr_err("%s: Wrong ioctl_ptr %pK / len %zu\n", __func__,
+ ioctl_ptr, ioctl_ptr->len);
+ return -EINVAL;
+ }
+
+ /* For compat task, source ptr is in kernel space */
+ if (is_compat_task()) {
+ memcpy(dst_ptr, (__force void *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len);
+ ret = 0;
+ } else {
+ ret = copy_from_user(dst_ptr,
+ (void __user *)ioctl_ptr->ioctl_ptr, ioctl_ptr->len);
+ if (ret)
+ pr_err("Copy from user fail %d\n", ret);
+ }
+ return ret ? -EFAULT : 0;
+}
+#else
+static int msm_cpp_copy_from_ioctl_ptr(void *dst_ptr,
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ int ret;
+
+ if ((ioctl_ptr->ioctl_ptr == NULL) || (ioctl_ptr->len == 0)) {
+ pr_err("%s: Wrong ioctl_ptr %pK / len %zu\n", __func__,
+ ioctl_ptr, ioctl_ptr->len);
+ return -EINVAL;
+ }
+
+ ret = copy_from_user(dst_ptr,
+ (void __user *)ioctl_ptr->ioctl_ptr, ioctl_ptr->len);
+ if (ret)
+ pr_err("Copy from user fail %d\n", ret);
+
+ return ret ? -EFAULT : 0;
+}
+#endif
+
+static int32_t msm_cpp_fw_version(struct cpp_device *cpp_dev)
+{
+ int32_t rc = 0;
+
+ rc = msm_cpp_poll_rx_empty(cpp_dev->base);
+ if (rc) {
+ pr_err("%s:%d] poll rx empty failed %d",
+ __func__, __LINE__, rc);
+ goto end;
+ }
+ /*Get Firmware Version*/
+ msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_MSG_ID_CMD, cpp_dev->base);
+ msm_cpp_write(0x1, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_CMD, rc);
+ goto end;
+ }
+ rc = msm_cpp_poll(cpp_dev->base, 0x2);
+ if (rc) {
+ pr_err("%s:%d] poll command 0x2 failed %d", __func__, __LINE__,
+ rc);
+ goto end;
+ }
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_FW_VER, rc);
+ goto end;
+ }
+
+ cpp_dev->fw_version = msm_cpp_read(cpp_dev->base);
+ pr_debug("CPP FW Version: 0x%08x\n", cpp_dev->fw_version);
+
+ rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
+ if (rc) {
+ pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
+ MSM_CPP_MSG_ID_TRAILER, rc);
+ }
+
+end:
+
+ return rc;
+}
+
+static int msm_cpp_validate_input(unsigned int cmd, void *arg,
+ struct msm_camera_v4l2_ioctl_t **ioctl_ptr)
+{
+ switch (cmd) {
+ case MSM_SD_SHUTDOWN:
+ case MSM_SD_NOTIFY_FREEZE:
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ default: {
+ if (ioctl_ptr == NULL) {
+ pr_err("Wrong ioctl_ptr for cmd %u\n", cmd);
+ return -EINVAL;
+ }
+
+ *ioctl_ptr = arg;
+ if (((*ioctl_ptr) == NULL) ||
+ ((*ioctl_ptr)->ioctl_ptr == NULL) ||
+ ((*ioctl_ptr)->len == 0)) {
+ pr_err("Error invalid ioctl argument cmd %u", cmd);
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static unsigned long cpp_cx_ipeak_update(struct cpp_device *cpp_dev,
+ unsigned long clock, int idx)
+{
+ unsigned long clock_rate = 0;
+ int ret = 0;
+
+ if ((clock >= cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) &&
+ (cpp_dev->turbo_vote == 0)) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, true);
+ if (ret) {
+ pr_err("cx_ipeak voting failed setting clock below turbo");
+ clock = cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 2];
+ } else {
+ cpp_dev->turbo_vote = 1;
+ }
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ } else if (clock < cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ if (cpp_dev->turbo_vote == 1) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (ret)
+ pr_err("cx_ipeak unvoting failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+ }
+ return clock_rate;
+}
+
+static long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct cpp_device *cpp_dev = NULL;
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = NULL;
+ int rc = 0;
+
+ if (sd == NULL) {
+ pr_err("sd %pK\n", sd);
+ return -EINVAL;
+ }
+ cpp_dev = v4l2_get_subdevdata(sd);
+ if (cpp_dev == NULL) {
+ pr_err("cpp_dev is null\n");
+ return -EINVAL;
+ }
+
+ if (_IOC_DIR(cmd) == _IOC_NONE) {
+ pr_err("Invalid ioctl/subdev cmd %u", cmd);
+ return -EINVAL;
+ }
+
+ rc = msm_cpp_validate_input(cmd, arg, &ioctl_ptr);
+ if (rc != 0) {
+ pr_err("input validation failed\n");
+ return rc;
+ }
+ mutex_lock(&cpp_dev->mutex);
+
+ CPP_DBG("E cmd: 0x%x\n", cmd);
+ switch (cmd) {
+ case VIDIOC_MSM_CPP_GET_HW_INFO: {
+ CPP_DBG("VIDIOC_MSM_CPP_GET_HW_INFO\n");
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ &cpp_dev->hw_info,
+ sizeof(struct cpp_hw_info))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+ break;
+ }
+
+ case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
+ CPP_DBG("VIDIOC_MSM_CPP_LOAD_FIRMWARE\n");
+ if (cpp_dev->is_firmware_loaded == 0) {
+ if (cpp_dev->fw_name_bin != NULL) {
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ }
+ if (cpp_dev->fw) {
+ release_firmware(cpp_dev->fw);
+ cpp_dev->fw = NULL;
+ }
+ if ((ioctl_ptr->len == 0) ||
+ (ioctl_ptr->len > MSM_CPP_MAX_FW_NAME_LEN)) {
+ pr_err("ioctl_ptr->len is 0\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ cpp_dev->fw_name_bin = kzalloc(ioctl_ptr->len+1,
+ GFP_KERNEL);
+ if (!cpp_dev->fw_name_bin) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ if (ioctl_ptr->ioctl_ptr == NULL) {
+ pr_err("ioctl_ptr->ioctl_ptr=NULL\n");
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ rc = (copy_from_user(cpp_dev->fw_name_bin,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ *(cpp_dev->fw_name_bin+ioctl_ptr->len) = '\0';
+ rc = request_firmware(&cpp_dev->fw,
+ cpp_dev->fw_name_bin,
+ &cpp_dev->pdev->dev);
+ if (rc) {
+ dev_err(&cpp_dev->pdev->dev,
+ "Fail to loc blob %s dev %pK, rc:%d\n",
+ cpp_dev->fw_name_bin,
+ &cpp_dev->pdev->dev, rc);
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ cpp_dev->fw = NULL;
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ msm_camera_enable_irq(cpp_dev->irq, false);
+ rc = cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
+ if (rc < 0) {
+ pr_err("%s: load firmware failure %d-retry\n",
+ __func__, rc);
+ rc = msm_cpp_reset_vbif_and_load_fw(cpp_dev);
+ if (rc < 0) {
+ enable_irq(cpp_dev->irq->start);
+ mutex_unlock(&cpp_dev->mutex);
+ return rc;
+ }
+ }
+ rc = msm_cpp_fw_version(cpp_dev);
+ if (rc < 0) {
+ pr_err("%s: get firmware failure %d\n",
+ __func__, rc);
+ enable_irq(cpp_dev->irq->start);
+ mutex_unlock(&cpp_dev->mutex);
+ return rc;
+ }
+ msm_camera_enable_irq(cpp_dev->irq, true);
+ cpp_dev->is_firmware_loaded = 1;
+ }
+ break;
+ }
+ case VIDIOC_MSM_CPP_CFG:
+ CPP_DBG("VIDIOC_MSM_CPP_CFG\n");
+ rc = msm_cpp_cfg(cpp_dev, ioctl_ptr);
+ break;
+ case VIDIOC_MSM_CPP_FLUSH_QUEUE:
+ CPP_DBG("VIDIOC_MSM_CPP_FLUSH_QUEUE\n");
+ rc = msm_cpp_flush_frames(cpp_dev);
+ break;
+ case VIDIOC_MSM_CPP_DELETE_STREAM_BUFF:
+ case VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO:
+ case VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO: {
+ uint32_t j;
+ struct msm_cpp_stream_buff_info_t *u_stream_buff_info = NULL;
+ struct msm_cpp_stream_buff_info_t k_stream_buff_info;
+ struct msm_cpp_buff_queue_info_t *buff_queue_info = NULL;
+
+ memset(&k_stream_buff_info, 0, sizeof(k_stream_buff_info));
+ CPP_DBG("VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO\n");
+ if (sizeof(struct msm_cpp_stream_buff_info_t) !=
+ ioctl_ptr->len) {
+ pr_err("%s:%d: invalid length\n", __func__, __LINE__);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ u_stream_buff_info = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!u_stream_buff_info) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = msm_cpp_copy_from_ioctl_ptr(u_stream_buff_info,
+ ioctl_ptr);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ kfree(u_stream_buff_info);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ k_stream_buff_info.num_buffs = u_stream_buff_info->num_buffs;
+ k_stream_buff_info.identity = u_stream_buff_info->identity;
+
+ if (k_stream_buff_info.num_buffs > MSM_CAMERA_MAX_STREAM_BUF) {
+ pr_err("%s:%d: unexpected large num buff requested\n",
+ __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (u_stream_buff_info->num_buffs != 0) {
+ k_stream_buff_info.buffer_info =
+ kzalloc(k_stream_buff_info.num_buffs *
+ sizeof(struct msm_cpp_buffer_info_t),
+ GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(k_stream_buff_info.buffer_info)) {
+ pr_err("%s:%d: malloc error\n",
+ __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(k_stream_buff_info.buffer_info,
+ (void __user *)u_stream_buff_info->buffer_info,
+ k_stream_buff_info.num_buffs *
+ sizeof(struct msm_cpp_buffer_info_t)) ?
+ -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ }
+
+ buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev,
+ (k_stream_buff_info.identity >> 16) & 0xFFFF,
+ k_stream_buff_info.identity & 0xFFFF);
+
+ if (buff_queue_info == NULL) {
+ if (cmd == VIDIOC_MSM_CPP_DELETE_STREAM_BUFF)
+ goto STREAM_BUFF_END;
+
+ rc = msm_cpp_add_buff_queue_entry(cpp_dev,
+ ((k_stream_buff_info.identity >> 16) & 0xFFFF),
+ (k_stream_buff_info.identity & 0xFFFF));
+
+ if (rc)
+ goto STREAM_BUFF_END;
+
+ if (cpp_dev->stream_cnt == 0) {
+ cpp_dev->state = CPP_STATE_ACTIVE;
+ msm_cpp_clear_timer(cpp_dev);
+ msm_cpp_clean_queue(cpp_dev);
+ }
+ cpp_dev->stream_cnt++;
+ CPP_DBG("stream_cnt:%d\n", cpp_dev->stream_cnt);
+ }
+ buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev,
+ ((k_stream_buff_info.identity >> 16) & 0xFFFF),
+ (k_stream_buff_info.identity & 0xFFFF));
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry identity:%d\n",
+ k_stream_buff_info.identity);
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ cpp_dev->stream_cnt--;
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ if (cmd == VIDIOC_MSM_CPP_DELETE_STREAM_BUFF) {
+ for (j = 0; j < k_stream_buff_info.num_buffs; j++) {
+ msm_cpp_dequeue_buff(cpp_dev, buff_queue_info,
+ k_stream_buff_info.buffer_info[j].index,
+ k_stream_buff_info.buffer_info[j].native_buff);
+ }
+ } else {
+ for (j = 0; j < k_stream_buff_info.num_buffs; j++) {
+ msm_cpp_queue_buffer_info(cpp_dev,
+ buff_queue_info,
+ &k_stream_buff_info.buffer_info[j]);
+ }
+ }
+
+STREAM_BUFF_END:
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+
+ break;
+ }
+ case VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO: {
+ uint32_t identity;
+ struct msm_cpp_buff_queue_info_t *buff_queue_info;
+
+ CPP_DBG("VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO\n");
+ if (ioctl_ptr->len != sizeof(uint32_t)) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = msm_cpp_copy_from_ioctl_ptr(&identity, ioctl_ptr);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev,
+ ((identity >> 16) & 0xFFFF), (identity & 0xFFFF));
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for identity:%d\n",
+ identity);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ msm_cpp_dequeue_buff_info_list(cpp_dev, buff_queue_info);
+ rc = msm_cpp_free_buff_queue_entry(cpp_dev,
+ buff_queue_info->session_id,
+ buff_queue_info->stream_id);
+ if (cpp_dev->stream_cnt > 0) {
+ cpp_dev->stream_cnt--;
+ pr_debug("stream_cnt:%d\n", cpp_dev->stream_cnt);
+ if (cpp_dev->stream_cnt == 0) {
+ rc = msm_cpp_update_bandwidth_setting(cpp_dev,
+ 0, 0);
+ if (rc < 0)
+ pr_err("Bandwidth Reset Failed!\n");
+ cpp_dev->state = CPP_STATE_IDLE;
+ msm_cpp_clear_timer(cpp_dev);
+ msm_cpp_clean_queue(cpp_dev);
+ }
+ } else {
+ pr_err("error: stream count underflow %d\n",
+ cpp_dev->stream_cnt);
+ }
+ break;
+ }
+ case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD: {
+ struct msm_device_queue *queue = &cpp_dev->eventData_q;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_cpp_frame_info_t *process_frame;
+
+ CPP_DBG("VIDIOC_MSM_CPP_GET_EVENTPAYLOAD\n");
+ event_qcmd = msm_dequeue(queue, list_eventdata, POP_FRONT);
+ if (!event_qcmd) {
+ pr_err("no queue cmd available");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ process_frame = event_qcmd->command;
+ CPP_DBG("fid %d\n", process_frame->frame_id);
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ process_frame,
+ sizeof(struct msm_cpp_frame_info_t))) {
+ mutex_unlock(&cpp_dev->mutex);
+ kfree(process_frame->cpp_cmd_msg);
+ kfree(process_frame);
+ kfree(event_qcmd);
+ return -EFAULT;
+ }
+
+ kfree(process_frame->cpp_cmd_msg);
+ kfree(process_frame);
+ kfree(event_qcmd);
+ break;
+ }
+ case VIDIOC_MSM_CPP_SET_CLOCK: {
+ int msm_cpp_core_clk_idx;
+ struct msm_cpp_clock_settings_t clock_settings;
+ unsigned long clock_rate = 0;
+
+ CPP_DBG("VIDIOC_MSM_CPP_SET_CLOCK\n");
+ if (ioctl_ptr->len == 0) {
+ pr_err("ioctl_ptr->len is 0\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (ioctl_ptr->ioctl_ptr == NULL) {
+ pr_err("ioctl_ptr->ioctl_ptr is NULL\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (ioctl_ptr->len != sizeof(struct msm_cpp_clock_settings_t)) {
+ pr_err("Not valid ioctl_ptr->len\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = msm_cpp_copy_from_ioctl_ptr(&clock_settings, ioctl_ptr);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (clock_settings.clock_rate > 0) {
+ msm_cpp_core_clk_idx = msm_cpp_get_clock_index(cpp_dev,
+ "cpp_core_clk");
+ if (msm_cpp_core_clk_idx < 0) {
+ pr_err(" Fail to get clock index\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ rc = msm_cpp_update_bandwidth_setting(cpp_dev,
+ clock_settings.avg,
+ clock_settings.inst);
+ if (rc < 0) {
+ pr_err("Bandwidth Set Failed!\n");
+ rc = msm_cpp_update_bandwidth_setting(cpp_dev,
+ 0, 0);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ if (cpp_dev->cpp_cx_ipeak) {
+ clock_rate = cpp_cx_ipeak_update(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ } else {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ }
+ if (rc < 0) {
+ pr_err("Fail to set core clk\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ if (clock_rate != clock_settings.clock_rate)
+ pr_err("clock rate differ from settings\n");
+ msm_isp_util_update_clk_rate(clock_settings.clock_rate);
+ }
+ break;
+ }
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN:
+ CPP_DBG("MSM_SD_SHUTDOWN\n");
+ mutex_unlock(&cpp_dev->mutex);
+ pr_warn("shutdown cpp node. open cnt:%d\n",
+ cpp_dev->cpp_open_cnt);
+
+ if (atomic_read(&cpp_timer.used))
+ pr_debug("Timer state not cleared\n");
+
+ while (cpp_dev->cpp_open_cnt != 0)
+ cpp_close_node(sd, NULL);
+ mutex_lock(&cpp_dev->mutex);
+ rc = 0;
+ break;
+ case VIDIOC_MSM_CPP_QUEUE_BUF: {
+ struct msm_pproc_queue_buf_info queue_buf_info;
+
+ CPP_DBG("VIDIOC_MSM_CPP_QUEUE_BUF\n");
+
+ if (ioctl_ptr->len != sizeof(struct msm_pproc_queue_buf_info)) {
+ pr_err("%s: Not valid ioctl_ptr->len\n", __func__);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ rc = msm_cpp_copy_from_ioctl_ptr(&queue_buf_info, ioctl_ptr);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ break;
+ }
+
+ if (queue_buf_info.is_buf_dirty) {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ 0x0, &queue_buf_info.buff_mgr_info);
+ } else {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ 0x0, &queue_buf_info.buff_mgr_info);
+ }
+ if (rc < 0) {
+ pr_err("error in buf done\n");
+ rc = -EINVAL;
+ }
+
+ break;
+ }
+ case VIDIOC_MSM_CPP_POP_STREAM_BUFFER: {
+ struct msm_buf_mngr_info buff_mgr_info;
+ struct msm_cpp_frame_info_t frame_info;
+ uint32_t ioctl_cmd, idx;
+
+ if (ioctl_ptr->ioctl_ptr == NULL ||
+ (ioctl_ptr->len !=
+ sizeof(struct msm_cpp_frame_info_t))) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = msm_cpp_copy_from_ioctl_ptr(&frame_info, ioctl_ptr);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ break;
+ }
+
+ memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+ buff_mgr_info.session_id =
+ ((frame_info.identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (frame_info.identity & 0xFFFF);
+ buff_mgr_info.type =
+ MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
+ if (IS_DEFAULT_OUTPUT_BUF_INDEX(
+ frame_info.output_buffer_info[0].index)) {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF;
+ idx = 0x0;
+ } else {
+ ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD;
+ idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ }
+ rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx,
+ &buff_mgr_info);
+ if (rc < 0) {
+ rc = -EAGAIN;
+ pr_err_ratelimited("POP: get_buf err rc:%d, index %d\n",
+ rc, frame_info.output_buffer_info[0].index);
+ break;
+ }
+ buff_mgr_info.frame_id = frame_info.frame_id;
+ rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ 0x0, &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error in buf done\n");
+ rc = -EAGAIN;
+ }
+ break;
+ }
+ default:
+ pr_err_ratelimited("invalid value: cmd=0x%x\n", cmd);
+ break;
+ case VIDIOC_MSM_CPP_IOMMU_ATTACH: {
+ if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) {
+ int32_t stall_disable;
+ struct msm_camera_smmu_attach_type cpp_attach_info;
+
+ if (ioctl_ptr->len !=
+ sizeof(struct msm_camera_smmu_attach_type)) {
+ rc = -EINVAL;
+ break;
+ }
+
+ memset(&cpp_attach_info, 0, sizeof(cpp_attach_info));
+ rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info,
+ ioctl_ptr);
+ if (rc < 0) {
+ pr_err("CPP_IOMMU_ATTACH copy from user fail");
+ break;
+ }
+
+ cpp_dev->security_mode = cpp_attach_info.attach;
+ stall_disable = 1;
+ /* disable smmu stall on fault */
+ cam_smmu_set_attr(cpp_dev->iommu_hdl,
+ DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable);
+ if (cpp_dev->security_mode == SECURE_MODE) {
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_ATTACH_SEC_CPP);
+ } else {
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_ATTACH);
+ }
+ if (rc < 0) {
+ pr_err("%s:%diommu_attach_device failed\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ cpp_dev->iommu_state = CPP_IOMMU_STATE_ATTACHED;
+ } else {
+ pr_err("%s:%d IOMMMU attach triggered in invalid state\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_MSM_CPP_IOMMU_DETACH: {
+ if ((cpp_dev->iommu_state == CPP_IOMMU_STATE_ATTACHED) &&
+ (cpp_dev->stream_cnt == 0)) {
+ struct msm_camera_smmu_attach_type cpp_attach_info;
+
+ if (ioctl_ptr->len !=
+ sizeof(struct msm_camera_smmu_attach_type)) {
+ rc = -EINVAL;
+ break;
+ }
+
+ memset(&cpp_attach_info, 0, sizeof(cpp_attach_info));
+ rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info,
+ ioctl_ptr);
+ if (rc < 0) {
+ pr_err("CPP_IOMMU_DETTACH copy from user fail");
+ break;
+ }
+
+ cpp_dev->security_mode = cpp_attach_info.attach;
+
+ if (cpp_dev->security_mode == SECURE_MODE)
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_DETACH_SEC_CPP);
+ else
+ rc = cam_smmu_ops(cpp_dev->iommu_hdl,
+ CAM_SMMU_DETACH);
+ if (rc < 0) {
+ pr_err("%s:%diommu detach failed\n", __func__,
+ __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ cpp_dev->iommu_state = CPP_IOMMU_STATE_DETACHED;
+ } else {
+ pr_err("%s:%d IOMMMU attach triggered in invalid state\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ }
+ mutex_unlock(&cpp_dev->mutex);
+ CPP_DBG("X\n");
+ return rc;
+}
+
+static int msm_cpp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ CPP_DBG("Called\n");
+ return v4l2_event_subscribe(fh, sub, MAX_CPP_V4l2_EVENTS, NULL);
+}
+
+static int msm_cpp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ CPP_DBG("Called\n");
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static struct v4l2_subdev_core_ops msm_cpp_subdev_core_ops = {
+ .ioctl = msm_cpp_subdev_ioctl,
+ .subscribe_event = msm_cpp_subscribe_event,
+ .unsubscribe_event = msm_cpp_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_ops msm_cpp_subdev_ops = {
+ .core = &msm_cpp_subdev_core_ops,
+};
+
+static long msm_cpp_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ struct v4l2_fh *vfh = NULL;
+
+ if ((arg == NULL) || (file == NULL)) {
+ pr_err("Invalid input parameters arg %pK, file %pK\n",
+ arg, file);
+ return -EINVAL;
+ }
+ vdev = video_devdata(file);
+ sd = vdev_to_v4l2_subdev(vdev);
+
+ if (sd == NULL) {
+ pr_err("Invalid input parameter sd %pK\n", sd);
+ return -EINVAL;
+ }
+ vfh = file->private_data;
+
+ switch (cmd) {
+ case VIDIOC_DQEVENT:
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+ case VIDIOC_MSM_CPP_GET_INST_INFO: {
+ uint32_t i;
+ struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ struct msm_cpp_frame_info_t inst_info;
+
+ memset(&inst_info, 0, sizeof(struct msm_cpp_frame_info_t));
+ for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+ if (cpp_dev->cpp_subscribe_list[i].vfh == vfh) {
+ inst_info.inst_id = i;
+ break;
+ }
+ }
+ if (copy_to_user(
+ (void __user *)ioctl_ptr->ioctl_ptr, &inst_info,
+ sizeof(struct msm_cpp_frame_info_t))) {
+ return -EFAULT;
+ }
+ }
+ break;
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+
+ return 0;
+}
+
+static long msm_cpp_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_cpp_subdev_do_ioctl);
+}
+
+
+#ifdef CONFIG_COMPAT
+static struct msm_cpp_frame_info_t *get_64bit_cpp_frame_from_compat(
+ struct msm_camera_v4l2_ioctl_t *kp_ioctl)
+{
+ struct msm_cpp_frame_info32_t *new_frame32 = NULL;
+ struct msm_cpp_frame_info_t *new_frame = NULL;
+ uint32_t *cpp_frame_msg;
+ void __user *cpp_cmd_msg_64bit;
+ int32_t rc, i;
+
+ new_frame32 = kzalloc(sizeof(struct msm_cpp_frame_info32_t),
+ GFP_KERNEL);
+ if (!new_frame32)
+ goto no_mem32;
+
+ new_frame = kzalloc(sizeof(struct msm_cpp_frame_info_t), GFP_KERNEL);
+ if (!new_frame)
+ goto no_mem;
+
+ rc = (copy_from_user(new_frame32, (void __user *)kp_ioctl->ioctl_ptr,
+ sizeof(struct msm_cpp_frame_info32_t)) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ goto frame_err;
+ }
+
+ new_frame->frame_id = new_frame32->frame_id;
+ new_frame->inst_id = new_frame32->inst_id;
+ new_frame->client_id = new_frame32->client_id;
+ new_frame->frame_type = new_frame32->frame_type;
+ new_frame->num_strips = new_frame32->num_strips;
+
+ new_frame->src_fd = new_frame32->src_fd;
+ new_frame->dst_fd = new_frame32->dst_fd;
+
+ new_frame->timestamp.tv_sec =
+ (unsigned long)new_frame32->timestamp.tv_sec;
+ new_frame->timestamp.tv_usec =
+ (unsigned long)new_frame32->timestamp.tv_usec;
+
+ new_frame->in_time.tv_sec =
+ (unsigned long)new_frame32->in_time.tv_sec;
+ new_frame->in_time.tv_usec =
+ (unsigned long)new_frame32->in_time.tv_usec;
+
+ new_frame->out_time.tv_sec =
+ (unsigned long)new_frame32->out_time.tv_sec;
+ new_frame->out_time.tv_usec =
+ (unsigned long)new_frame32->out_time.tv_usec;
+
+ new_frame->msg_len = new_frame32->msg_len;
+ new_frame->identity = new_frame32->identity;
+ new_frame->input_buffer_info = new_frame32->input_buffer_info;
+ new_frame->output_buffer_info[0] =
+ new_frame32->output_buffer_info[0];
+ new_frame->output_buffer_info[1] =
+ new_frame32->output_buffer_info[1];
+ new_frame->output_buffer_info[2] =
+ new_frame32->output_buffer_info[2];
+ new_frame->output_buffer_info[3] =
+ new_frame32->output_buffer_info[3];
+ new_frame->output_buffer_info[4] =
+ new_frame32->output_buffer_info[4];
+ new_frame->output_buffer_info[5] =
+ new_frame32->output_buffer_info[5];
+ new_frame->output_buffer_info[6] =
+ new_frame32->output_buffer_info[6];
+ new_frame->output_buffer_info[7] =
+ new_frame32->output_buffer_info[7];
+ new_frame->duplicate_buffer_info =
+ new_frame32->duplicate_buffer_info;
+ new_frame->tnr_scratch_buffer_info[0] =
+ new_frame32->tnr_scratch_buffer_info[0];
+ new_frame->tnr_scratch_buffer_info[1] =
+ new_frame32->tnr_scratch_buffer_info[1];
+ new_frame->duplicate_output = new_frame32->duplicate_output;
+ new_frame->we_disable = new_frame32->we_disable;
+ new_frame->duplicate_identity = new_frame32->duplicate_identity;
+ new_frame->feature_mask = new_frame32->feature_mask;
+ new_frame->partial_frame_indicator =
+ new_frame32->partial_frame_indicator;
+ new_frame->first_payload = new_frame32->first_payload;
+ new_frame->last_payload = new_frame32->last_payload;
+ new_frame->first_stripe_index = new_frame32->first_stripe_index;
+ new_frame->last_stripe_index = new_frame32->last_stripe_index;
+ new_frame->stripe_info_offset =
+ new_frame32->stripe_info_offset;
+ new_frame->stripe_info = new_frame32->stripe_info;
+ new_frame->batch_info.batch_mode =
+ new_frame32->batch_info.batch_mode;
+ new_frame->batch_info.batch_size =
+ new_frame32->batch_info.batch_size;
+ new_frame->batch_info.cont_idx =
+ new_frame32->batch_info.cont_idx;
+ for (i = 0; i < MAX_PLANES; i++)
+ new_frame->batch_info.intra_plane_offset[i] =
+ new_frame32->batch_info.intra_plane_offset[i];
+ new_frame->batch_info.pick_preview_idx =
+ new_frame32->batch_info.pick_preview_idx;
+
+ /* Convert the 32 bit pointer to 64 bit pointer */
+ new_frame->cookie = compat_ptr(new_frame32->cookie);
+ cpp_cmd_msg_64bit = compat_ptr(new_frame32->cpp_cmd_msg);
+ if ((new_frame->msg_len == 0) ||
+ (new_frame->msg_len > MSM_CPP_MAX_FRAME_LENGTH)) {
+ pr_err("%s:%d: Invalid frame len:%d\n", __func__,
+ __LINE__, new_frame->msg_len);
+ goto frame_err;
+ }
+
+ cpp_frame_msg = kcalloc(new_frame->msg_len, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!cpp_frame_msg)
+ goto frame_err;
+
+ rc = (copy_from_user(cpp_frame_msg,
+ (void __user *)cpp_cmd_msg_64bit,
+ sizeof(uint32_t)*new_frame->msg_len) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ goto frame_msg_err;
+ }
+ new_frame->cpp_cmd_msg = cpp_frame_msg;
+
+ kfree(new_frame32);
+ return new_frame;
+
+frame_msg_err:
+ kfree(cpp_frame_msg);
+frame_err:
+ kfree(new_frame);
+no_mem:
+ kfree(new_frame32);
+no_mem32:
+ return NULL;
+}
+
+static void get_compat_frame_from_64bit(struct msm_cpp_frame_info_t *frame,
+ struct msm_cpp_frame_info32_t *k32_frame)
+{
+ int32_t i;
+
+ k32_frame->frame_id = frame->frame_id;
+ k32_frame->inst_id = frame->inst_id;
+ k32_frame->client_id = frame->client_id;
+ k32_frame->frame_type = frame->frame_type;
+ k32_frame->num_strips = frame->num_strips;
+
+ k32_frame->src_fd = frame->src_fd;
+ k32_frame->dst_fd = frame->dst_fd;
+
+ k32_frame->timestamp.tv_sec = (uint32_t)frame->timestamp.tv_sec;
+ k32_frame->timestamp.tv_usec = (uint32_t)frame->timestamp.tv_usec;
+
+ k32_frame->in_time.tv_sec = (uint32_t)frame->in_time.tv_sec;
+ k32_frame->in_time.tv_usec = (uint32_t)frame->in_time.tv_usec;
+
+ k32_frame->out_time.tv_sec = (uint32_t)frame->out_time.tv_sec;
+ k32_frame->out_time.tv_usec = (uint32_t)frame->out_time.tv_usec;
+
+ k32_frame->msg_len = frame->msg_len;
+ k32_frame->identity = frame->identity;
+ k32_frame->input_buffer_info = frame->input_buffer_info;
+ k32_frame->output_buffer_info[0] = frame->output_buffer_info[0];
+ k32_frame->output_buffer_info[1] = frame->output_buffer_info[1];
+ k32_frame->output_buffer_info[2] = frame->output_buffer_info[2];
+ k32_frame->output_buffer_info[3] = frame->output_buffer_info[3];
+ k32_frame->output_buffer_info[4] = frame->output_buffer_info[4];
+ k32_frame->output_buffer_info[5] = frame->output_buffer_info[5];
+ k32_frame->output_buffer_info[6] = frame->output_buffer_info[6];
+ k32_frame->output_buffer_info[7] = frame->output_buffer_info[7];
+ k32_frame->duplicate_buffer_info = frame->duplicate_buffer_info;
+ k32_frame->duplicate_output = frame->duplicate_output;
+ k32_frame->we_disable = frame->we_disable;
+ k32_frame->duplicate_identity = frame->duplicate_identity;
+ k32_frame->feature_mask = frame->feature_mask;
+ k32_frame->cookie = ptr_to_compat(frame->cookie);
+ k32_frame->partial_frame_indicator = frame->partial_frame_indicator;
+ k32_frame->first_payload = frame->first_payload;
+ k32_frame->last_payload = frame->last_payload;
+ k32_frame->first_stripe_index = frame->first_stripe_index;
+ k32_frame->last_stripe_index = frame->last_stripe_index;
+ k32_frame->stripe_info_offset = frame->stripe_info_offset;
+ k32_frame->stripe_info = frame->stripe_info;
+ k32_frame->batch_info.batch_mode = frame->batch_info.batch_mode;
+ k32_frame->batch_info.batch_size = frame->batch_info.batch_size;
+ k32_frame->batch_info.cont_idx = frame->batch_info.cont_idx;
+ for (i = 0; i < MAX_PLANES; i++)
+ k32_frame->batch_info.intra_plane_offset[i] =
+ frame->batch_info.intra_plane_offset[i];
+ k32_frame->batch_info.pick_preview_idx =
+ frame->batch_info.pick_preview_idx;
+}
+
+static long msm_cpp_subdev_fops_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct cpp_device *cpp_dev = NULL;
+
+ int32_t rc = 0;
+ struct msm_camera_v4l2_ioctl_t kp_ioctl;
+ struct msm_camera_v4l2_ioctl32_t up32_ioctl;
+ struct msm_cpp_clock_settings_t clock_settings;
+ struct msm_pproc_queue_buf_info k_queue_buf;
+ struct msm_cpp_stream_buff_info_t k_cpp_buff_info;
+ struct msm_cpp_frame_info32_t k32_frame_info;
+ struct msm_cpp_frame_info_t k64_frame_info;
+ struct msm_camera_smmu_attach_type kb_cpp_smmu_attach_info;
+ uint32_t identity_k = 0;
+ bool is_copytouser_req = true;
+ void __user *up = (void __user *)arg;
+
+ if (sd == NULL) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return -EINVAL;
+ }
+ cpp_dev = v4l2_get_subdevdata(sd);
+ if (!vdev || !cpp_dev) {
+ pr_err("Invalid vdev %pK or cpp_dev %pK structures!",
+ vdev, cpp_dev);
+ return -EINVAL;
+ }
+ mutex_lock(&cpp_dev->mutex);
+ /*
+ * copy the user space 32 bit pointer to kernel space 32 bit compat
+ * pointer
+ */
+ if (copy_from_user(&up32_ioctl, (void __user *)up,
+ sizeof(up32_ioctl))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+
+ /* copy the data from 32 bit compat to kernel space 64 bit pointer */
+ kp_ioctl.id = up32_ioctl.id;
+ kp_ioctl.len = up32_ioctl.len;
+ kp_ioctl.trans_code = up32_ioctl.trans_code;
+ /* Convert the 32 bit pointer to 64 bit pointer */
+ kp_ioctl.ioctl_ptr = compat_ptr(up32_ioctl.ioctl_ptr);
+ if (!kp_ioctl.ioctl_ptr) {
+ pr_err("%s: Invalid ioctl pointer\n", __func__);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ /*
+ * Convert 32 bit IOCTL ID's to 64 bit IOCTL ID's
+ * except VIDIOC_MSM_CPP_CFG32, which needs special
+ * processing
+ */
+ switch (cmd) {
+ case VIDIOC_MSM_CPP_CFG32:
+ {
+ struct msm_cpp_frame_info32_t k32_frame_info;
+ struct msm_cpp_frame_info_t *cpp_frame = NULL;
+ void __user *status;
+
+ if (copy_from_user(&k32_frame_info,
+ (void __user *)kp_ioctl.ioctl_ptr,
+ sizeof(k32_frame_info))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+ /* Get the cpp frame pointer */
+ cpp_frame = get_64bit_cpp_frame_from_compat(&kp_ioctl);
+
+ /* Configure the cpp frame */
+ if (cpp_frame) {
+ rc = msm_cpp_cfg_frame(cpp_dev, cpp_frame);
+ /* Cpp_frame can be free'd by cfg_frame in error */
+ if (rc >= 0) {
+ k32_frame_info.output_buffer_info[0] =
+ cpp_frame->output_buffer_info[0];
+ k32_frame_info.output_buffer_info[1] =
+ cpp_frame->output_buffer_info[1];
+ }
+ } else {
+ pr_err("%s: Error getting frame\n", __func__);
+ mutex_unlock(&cpp_dev->mutex);
+ rc = -EINVAL;
+ }
+
+ kp_ioctl.trans_code = rc;
+
+ /* Convert the 32 bit pointer to 64 bit pointer */
+ status = compat_ptr(k32_frame_info.status);
+
+ if (copy_to_user(status, &rc,
+ sizeof(void *)))
+ pr_err("error cannot copy error\n");
+
+ if (copy_to_user((void __user *)kp_ioctl.ioctl_ptr,
+ &k32_frame_info,
+ sizeof(k32_frame_info))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+
+ cmd = VIDIOC_MSM_CPP_CFG;
+ break;
+ }
+ case VIDIOC_MSM_CPP_GET_HW_INFO32:
+ {
+ struct cpp_hw_info_32_t u32_cpp_hw_info;
+ uint32_t i;
+
+ u32_cpp_hw_info.cpp_hw_version =
+ cpp_dev->hw_info.cpp_hw_version;
+ u32_cpp_hw_info.cpp_hw_caps = cpp_dev->hw_info.cpp_hw_caps;
+ memset(&u32_cpp_hw_info.freq_tbl, 0x00,
+ sizeof(u32_cpp_hw_info.freq_tbl));
+ for (i = 0; i < cpp_dev->hw_info.freq_tbl_count; i++)
+ u32_cpp_hw_info.freq_tbl[i] =
+ cpp_dev->hw_info.freq_tbl[i];
+
+ u32_cpp_hw_info.freq_tbl_count =
+ cpp_dev->hw_info.freq_tbl_count;
+ if (copy_to_user((void __user *)kp_ioctl.ioctl_ptr,
+ &u32_cpp_hw_info, sizeof(struct cpp_hw_info_32_t))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+
+ cmd = VIDIOC_MSM_CPP_GET_HW_INFO;
+ break;
+ }
+ case VIDIOC_MSM_CPP_LOAD_FIRMWARE32:
+ cmd = VIDIOC_MSM_CPP_LOAD_FIRMWARE;
+ break;
+ case VIDIOC_MSM_CPP_GET_INST_INFO32:
+ {
+ struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+ struct msm_cpp_frame_info32_t inst_info;
+ struct v4l2_fh *vfh = NULL;
+ uint32_t i;
+
+ vfh = file->private_data;
+ memset(&inst_info, 0, sizeof(struct msm_cpp_frame_info32_t));
+ for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+ if (cpp_dev->cpp_subscribe_list[i].vfh == vfh) {
+ inst_info.inst_id = i;
+ break;
+ }
+ }
+ if (copy_to_user((void __user *)kp_ioctl.ioctl_ptr,
+ &inst_info, sizeof(struct msm_cpp_frame_info32_t))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+ cmd = VIDIOC_MSM_CPP_GET_INST_INFO;
+ break;
+ }
+ case VIDIOC_MSM_CPP_FLUSH_QUEUE32:
+ cmd = VIDIOC_MSM_CPP_FLUSH_QUEUE;
+ break;
+ case VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO32:
+ case VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32:
+ case VIDIOC_MSM_CPP_DELETE_STREAM_BUFF32:
+ {
+ compat_uptr_t p;
+ struct msm_cpp_stream_buff_info32_t __user *u32_cpp_buff_info =
+ (struct msm_cpp_stream_buff_info32_t __user *)
+ kp_ioctl.ioctl_ptr;
+
+ get_user(k_cpp_buff_info.identity,
+ &u32_cpp_buff_info->identity);
+ get_user(k_cpp_buff_info.num_buffs,
+ &u32_cpp_buff_info->num_buffs);
+ get_user(p, &u32_cpp_buff_info->buffer_info);
+ k_cpp_buff_info.buffer_info =
+ (__force struct msm_cpp_buffer_info_t *)compat_ptr(p);
+
+ kp_ioctl.ioctl_ptr = (__force void __user *)&k_cpp_buff_info;
+ if (is_compat_task()) {
+ if (kp_ioctl.len != sizeof(
+ struct msm_cpp_stream_buff_info32_t)) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ kp_ioctl.len =
+ sizeof(struct msm_cpp_stream_buff_info_t);
+ }
+ is_copytouser_req = false;
+ if (cmd == VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32)
+ cmd = VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO;
+ else if (cmd == VIDIOC_MSM_CPP_DELETE_STREAM_BUFF32)
+ cmd = VIDIOC_MSM_CPP_DELETE_STREAM_BUFF;
+ else
+ cmd = VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO;
+ break;
+ }
+ case VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO32: {
+ uint32_t __user *identity_u =
+ (uint32_t __user *)kp_ioctl.ioctl_ptr;
+
+ get_user(identity_k, identity_u);
+ kp_ioctl.ioctl_ptr = (__force void __user *)&identity_k;
+ kp_ioctl.len = sizeof(uint32_t);
+ is_copytouser_req = false;
+ cmd = VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO;
+ break;
+ }
+ case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD32:
+ {
+ struct msm_device_queue *queue = &cpp_dev->eventData_q;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_cpp_frame_info_t *process_frame;
+ struct msm_cpp_frame_info32_t k32_process_frame;
+
+ CPP_DBG("VIDIOC_MSM_CPP_GET_EVENTPAYLOAD\n");
+ event_qcmd = msm_dequeue(queue, list_eventdata, POP_FRONT);
+ if (!event_qcmd) {
+ pr_err("no queue cmd available");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ process_frame = event_qcmd->command;
+
+ memset(&k32_process_frame, 0, sizeof(k32_process_frame));
+ get_compat_frame_from_64bit(process_frame, &k32_process_frame);
+
+ CPP_DBG("fid %d\n", process_frame->frame_id);
+ if (copy_to_user((void __user *)kp_ioctl.ioctl_ptr,
+ &k32_process_frame,
+ sizeof(struct msm_cpp_frame_info32_t))) {
+ kfree(process_frame->cpp_cmd_msg);
+ kfree(process_frame);
+ kfree(event_qcmd);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+
+ kfree(process_frame->cpp_cmd_msg);
+ kfree(process_frame);
+ kfree(event_qcmd);
+ cmd = VIDIOC_MSM_CPP_GET_EVENTPAYLOAD;
+ break;
+ }
+ case VIDIOC_MSM_CPP_SET_CLOCK32:
+ {
+ struct msm_cpp_clock_settings32_t __user *clock_settings32 =
+ (struct msm_cpp_clock_settings32_t __user *)
+ kp_ioctl.ioctl_ptr;
+ get_user(clock_settings.clock_rate,
+ &clock_settings32->clock_rate);
+ get_user(clock_settings.avg, &clock_settings32->avg);
+ get_user(clock_settings.inst, &clock_settings32->inst);
+ kp_ioctl.ioctl_ptr = (__force void __user *)&clock_settings;
+ if (is_compat_task()) {
+ if (kp_ioctl.len != sizeof(
+ struct msm_cpp_clock_settings32_t)) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ kp_ioctl.len =
+ sizeof(struct msm_cpp_clock_settings_t);
+ }
+ is_copytouser_req = false;
+ cmd = VIDIOC_MSM_CPP_SET_CLOCK;
+ break;
+ }
+ case VIDIOC_MSM_CPP_QUEUE_BUF32:
+ {
+ struct msm_pproc_queue_buf_info32_t __user *u32_queue_buf =
+ (struct msm_pproc_queue_buf_info32_t __user *)
+ kp_ioctl.ioctl_ptr;
+
+ get_user(k_queue_buf.is_buf_dirty,
+ &u32_queue_buf->is_buf_dirty);
+ get_user(k_queue_buf.buff_mgr_info.session_id,
+ &u32_queue_buf->buff_mgr_info.session_id);
+ get_user(k_queue_buf.buff_mgr_info.stream_id,
+ &u32_queue_buf->buff_mgr_info.stream_id);
+ get_user(k_queue_buf.buff_mgr_info.frame_id,
+ &u32_queue_buf->buff_mgr_info.frame_id);
+ get_user(k_queue_buf.buff_mgr_info.index,
+ &u32_queue_buf->buff_mgr_info.index);
+ get_user(k_queue_buf.buff_mgr_info.timestamp.tv_sec,
+ &u32_queue_buf->buff_mgr_info.timestamp.tv_sec);
+ get_user(k_queue_buf.buff_mgr_info.timestamp.tv_usec,
+ &u32_queue_buf->buff_mgr_info.timestamp.tv_usec);
+
+ kp_ioctl.ioctl_ptr = (__force void __user *)&k_queue_buf;
+ kp_ioctl.len = sizeof(struct msm_pproc_queue_buf_info);
+ is_copytouser_req = false;
+ cmd = VIDIOC_MSM_CPP_QUEUE_BUF;
+ break;
+ }
+ case VIDIOC_MSM_CPP_POP_STREAM_BUFFER32:
+ {
+ if (kp_ioctl.len != sizeof(struct msm_cpp_frame_info32_t)) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ kp_ioctl.len = sizeof(struct msm_cpp_frame_info_t);
+
+ if (copy_from_user(&k32_frame_info,
+ (void __user *)kp_ioctl.ioctl_ptr,
+ sizeof(k32_frame_info))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EFAULT;
+ }
+
+ memset(&k64_frame_info, 0, sizeof(k64_frame_info));
+ k64_frame_info.identity = k32_frame_info.identity;
+ k64_frame_info.frame_id = k32_frame_info.frame_id;
+
+ kp_ioctl.ioctl_ptr = (__force void __user *)&k64_frame_info;
+
+ is_copytouser_req = false;
+ cmd = VIDIOC_MSM_CPP_POP_STREAM_BUFFER;
+ break;
+ }
+ case VIDIOC_MSM_CPP_IOMMU_ATTACH32:
+ case VIDIOC_MSM_CPP_IOMMU_DETACH32:
+ {
+ if ((kp_ioctl.len != sizeof(struct msm_camera_smmu_attach_type))
+ || (copy_from_user(&kb_cpp_smmu_attach_info,
+ (void __user *)kp_ioctl.ioctl_ptr,
+ sizeof(kb_cpp_smmu_attach_info)))) {
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ kp_ioctl.ioctl_ptr =
+ (__force void __user *)&kb_cpp_smmu_attach_info;
+ is_copytouser_req = false;
+ cmd = (cmd == VIDIOC_MSM_CPP_IOMMU_ATTACH32) ?
+ VIDIOC_MSM_CPP_IOMMU_ATTACH :
+ VIDIOC_MSM_CPP_IOMMU_DETACH;
+ break;
+ }
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN:
+ cmd = MSM_SD_SHUTDOWN;
+ break;
+ default:
+ pr_err_ratelimited("%s: unsupported compat type :%x LOAD %lu\n",
+ __func__, cmd, VIDIOC_MSM_CPP_LOAD_FIRMWARE);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&cpp_dev->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_CPP_LOAD_FIRMWARE:
+ case VIDIOC_MSM_CPP_FLUSH_QUEUE:
+ case VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO:
+ case VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO:
+ case VIDIOC_MSM_CPP_DELETE_STREAM_BUFF:
+ case VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO:
+ case VIDIOC_MSM_CPP_SET_CLOCK:
+ case VIDIOC_MSM_CPP_QUEUE_BUF:
+ case VIDIOC_MSM_CPP_POP_STREAM_BUFFER:
+ case VIDIOC_MSM_CPP_IOMMU_ATTACH:
+ case VIDIOC_MSM_CPP_IOMMU_DETACH:
+ case MSM_SD_SHUTDOWN:
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, &kp_ioctl);
+ break;
+ case VIDIOC_MSM_CPP_GET_HW_INFO:
+ case VIDIOC_MSM_CPP_CFG:
+ case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD:
+ case VIDIOC_MSM_CPP_GET_INST_INFO:
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ default:
+ pr_err_ratelimited("%s: unsupported compat type :%d\n",
+ __func__, cmd);
+ return -EINVAL;
+ }
+
+ if (is_copytouser_req) {
+ up32_ioctl.id = kp_ioctl.id;
+ up32_ioctl.len = kp_ioctl.len;
+ up32_ioctl.trans_code = kp_ioctl.trans_code;
+ up32_ioctl.ioctl_ptr = ptr_to_compat(kp_ioctl.ioctl_ptr);
+
+ if (copy_to_user((void __user *)up, &up32_ioctl,
+ sizeof(up32_ioctl)))
+ return -EFAULT;
+ }
+
+ return rc;
+}
+#endif
+
+static struct v4l2_file_operations msm_cpp_v4l2_subdev_fops = {
+ .unlocked_ioctl = msm_cpp_subdev_fops_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = msm_cpp_subdev_fops_compat_ioctl,
+#endif
+};
+static int msm_cpp_update_gdscr_status(struct cpp_device *cpp_dev,
+ bool status)
+{
+ int rc = 0;
+ int msm_cpp_reg_idx;
+
+ if (!cpp_dev) {
+ pr_err("%s: cpp device invalid\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ msm_cpp_reg_idx = msm_cpp_get_regulator_index(cpp_dev, "vdd");
+ if (msm_cpp_reg_idx < 0) {
+ pr_err(" Fail to regulator index\n");
+ return -EINVAL;
+ }
+ rc = msm_camera_regulator_set_mode(cpp_dev->cpp_vdd +
+ msm_cpp_reg_idx, 1, status);
+ if (rc < 0)
+ pr_err("update cpp gdscr status failed\n");
+
+end:
+ return rc;
+}
+static void msm_cpp_set_vbif_reg_values(struct cpp_device *cpp_dev)
+{
+ int i, reg, val;
+ const u32 *vbif_qos_arr = NULL;
+ int vbif_qos_len = 0;
+ struct platform_device *pdev;
+
+ pr_debug("%s\n", __func__);
+ if (cpp_dev != NULL) {
+ pdev = cpp_dev->pdev;
+ vbif_qos_arr = of_get_property(pdev->dev.of_node,
+ "qcom,vbif-qos-setting",
+ &vbif_qos_len);
+ if (!vbif_qos_arr || (vbif_qos_len & 1)) {
+ pr_debug("%s: vbif qos setting not found\n",
+ __func__);
+ vbif_qos_len = 0;
+ }
+ vbif_qos_len /= sizeof(u32);
+ pr_debug("%s: vbif_qos_len %d\n", __func__, vbif_qos_len);
+ if (cpp_dev->vbif_base) {
+ for (i = 0; i < vbif_qos_len; i = i+2) {
+ reg = be32_to_cpu(vbif_qos_arr[i]);
+ val = be32_to_cpu(vbif_qos_arr[i+1]);
+ pr_debug("%s: DT: offset %x, val %x\n",
+ __func__, reg, val);
+ pr_debug("%s: before write to register 0x%x\n",
+ __func__, msm_camera_io_r(
+ cpp_dev->vbif_base + reg));
+ msm_camera_io_w(val, cpp_dev->vbif_base + reg);
+ pr_debug("%s: after write to register 0x%x\n",
+ __func__, msm_camera_io_r(
+ cpp_dev->vbif_base + reg));
+ }
+ }
+ }
+}
+
+static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, uint32_t id, void *arg) {
+
+ int32_t rc = 0;
+
+ switch (id) {
+ case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: {
+ struct msm_camera_private_ioctl_arg ioctl_arg;
+ struct msm_buf_mngr_info *buff_mgr_info =
+ (struct msm_buf_mngr_info *)arg;
+
+ ioctl_arg.id = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX;
+ ioctl_arg.size = sizeof(struct msm_buf_mngr_info);
+ ioctl_arg.result = 0;
+ ioctl_arg.reserved = 0x0;
+ ioctl_arg.ioctl_ptr = 0x0;
+ MSM_CAM_GET_IOCTL_ARG_PTR(&ioctl_arg.ioctl_ptr, &buff_mgr_info,
+ sizeof(void *));
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops,
+ &ioctl_arg);
+ /* Use VIDIOC_MSM_BUF_MNGR_GET_BUF if getbuf with indx fails */
+ if (rc < 0) {
+ pr_err_ratelimited("get_buf_by_idx for %d err %d,use get_buf\n",
+ buff_mgr_info->index, rc);
+ rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(
+ VIDIOC_MSM_BUF_MNGR_GET_BUF, buff_mgr_info);
+ }
+ break;
+ }
+ default: {
+ pr_err("unsupported buffer manager ioctl\n");
+ break;
+ }
+ }
+ return rc;
+}
+
+static int cpp_probe(struct platform_device *pdev)
+{
+ struct cpp_device *cpp_dev;
+ int rc = 0;
+ int i = 0;
+
+ CPP_DBG("E");
+
+ cpp_dev = kzalloc(sizeof(struct cpp_device), GFP_KERNEL);
+ if (!cpp_dev)
+ return -ENOMEM;
+
+ v4l2_subdev_init(&cpp_dev->msm_sd.sd, &msm_cpp_subdev_ops);
+ cpp_dev->msm_sd.sd.internal_ops = &msm_cpp_internal_ops;
+ snprintf(cpp_dev->msm_sd.sd.name, ARRAY_SIZE(cpp_dev->msm_sd.sd.name),
+ "cpp");
+ cpp_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ cpp_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&cpp_dev->msm_sd.sd, cpp_dev);
+ platform_set_drvdata(pdev, &cpp_dev->msm_sd.sd);
+ mutex_init(&cpp_dev->mutex);
+ spin_lock_init(&cpp_dev->tasklet_lock);
+ spin_lock_init(&cpp_timer.data.processed_frame_lock);
+
+ cpp_dev->pdev = pdev;
+ memset(&cpp_vbif, 0, sizeof(struct msm_cpp_vbif_data));
+ cpp_dev->vbif_data = &cpp_vbif;
+
+ cpp_dev->base =
+ msm_camera_get_reg_base(pdev, "cpp", true);
+ if (!cpp_dev->base) {
+ rc = -ENOMEM;
+ pr_err("failed to get cpp_base\n");
+ goto cpp_base_failed;
+ }
+
+ cpp_dev->vbif_base =
+ msm_camera_get_reg_base(pdev, "cpp_vbif", false);
+ if (!cpp_dev->vbif_base) {
+ rc = -ENOMEM;
+ pr_err("failed to get vbif_base\n");
+ goto vbif_base_failed;
+ }
+
+ cpp_dev->cpp_hw_base =
+ msm_camera_get_reg_base(pdev, "cpp_hw", true);
+ if (!cpp_dev->cpp_hw_base) {
+ rc = -ENOMEM;
+ pr_err("failed to get cpp_hw_base\n");
+ goto cpp_hw_base_failed;
+ }
+
+ cpp_dev->irq = msm_camera_get_irq(pdev, "cpp");
+ if (!cpp_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto mem_err;
+ }
+
+ rc = msm_camera_get_clk_info(pdev, &cpp_dev->clk_info,
+ &cpp_dev->cpp_clk, &cpp_dev->num_clks);
+ if (rc < 0) {
+ pr_err("%s: failed to get the clocks\n", __func__);
+ goto mem_err;
+ }
+
+ /* set memcore and mem periphery logic flags to 0 */
+ for (i = 0; i < cpp_dev->num_clks; i++) {
+ if ((strcmp(cpp_dev->clk_info[i].clk_name,
+ "cpp_core_clk") == 0) ||
+ (strcmp(cpp_dev->clk_info[i].clk_name,
+ "camss_cpp_axi_clk") == 0) ||
+ (strcmp(cpp_dev->clk_info[i].clk_name,
+ "micro_iface_clk") == 0)) {
+ msm_camera_set_clk_flags(cpp_dev->cpp_clk[i],
+ CLKFLAG_NORETAIN_MEM);
+ msm_camera_set_clk_flags(cpp_dev->cpp_clk[i],
+ CLKFLAG_NORETAIN_PERIPH);
+ }
+ }
+
+ if (of_find_property(pdev->dev.of_node, "qcom,cpp-cx-ipeak", NULL)) {
+ cpp_dev->cpp_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,cpp-cx-ipeak");
+ if (cpp_dev->cpp_cx_ipeak)
+ CPP_DBG("Cx ipeak Registration Successful ");
+ else
+ pr_err("Cx ipeak Registration Unsuccessful");
+ }
+
+ rc = msm_camera_get_reset_info(pdev,
+ &cpp_dev->micro_iface_reset);
+ if (rc < 0) {
+ cpp_dev->micro_iface_reset = NULL;
+ pr_err("%s: failed to get micro_iface_reset\n",
+ __func__);
+ goto get_reg_err;
+ }
+
+ rc = msm_camera_get_regulator_info(pdev, &cpp_dev->cpp_vdd,
+ &cpp_dev->num_reg);
+ if (rc < 0) {
+ pr_err("%s: failed to get the regulators\n", __func__);
+ goto get_reset_err;
+ }
+
+ msm_cpp_fetch_dt_params(cpp_dev);
+
+ rc = msm_cpp_read_payload_params_from_dt(cpp_dev);
+ if (rc)
+ goto cpp_probe_init_error;
+
+ if (cpp_dev->bus_master_flag)
+ rc = msm_cpp_init_bandwidth_mgr(cpp_dev);
+ else
+ rc = msm_isp_init_bandwidth_mgr(NULL, ISP_CPP);
+ if (rc < 0) {
+ pr_err("%s: Bandwidth registration Failed!\n", __func__);
+ goto cpp_probe_init_error;
+ }
+
+ cpp_dev->state = CPP_STATE_BOOT;
+ rc = cpp_init_hardware(cpp_dev);
+ if (rc < 0)
+ goto bus_de_init;
+
+ media_entity_pads_init(&cpp_dev->msm_sd.sd.entity, 0, NULL);
+ cpp_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ cpp_dev->msm_sd.sd.entity.name = pdev->name;
+ cpp_dev->msm_sd.close_seq = MSM_SD_CLOSE_3RD_CATEGORY;
+ msm_sd_register(&cpp_dev->msm_sd);
+ msm_cam_copy_v4l2_subdev_fops(&msm_cpp_v4l2_subdev_fops);
+ msm_cpp_v4l2_subdev_fops.unlocked_ioctl = msm_cpp_subdev_fops_ioctl;
+#ifdef CONFIG_COMPAT
+ msm_cpp_v4l2_subdev_fops.compat_ioctl32 =
+ msm_cpp_subdev_fops_compat_ioctl;
+#endif
+
+ cpp_dev->msm_sd.sd.devnode->fops = &msm_cpp_v4l2_subdev_fops;
+
+
+ msm_camera_io_w(0x0, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ msm_camera_io_w(0x80000000, cpp_dev->base + 0xF0);
+ cpp_release_hardware(cpp_dev);
+ cpp_dev->state = CPP_STATE_OFF;
+ msm_cpp_enable_debugfs(cpp_dev);
+
+ msm_queue_init(&cpp_dev->eventData_q, "eventdata");
+ msm_queue_init(&cpp_dev->processing_q, "frame");
+ INIT_LIST_HEAD(&cpp_dev->tasklet_q);
+ tasklet_init(&cpp_dev->cpp_tasklet, msm_cpp_do_tasklet,
+ (unsigned long)cpp_dev);
+ cpp_dev->timer_wq = create_workqueue("msm_cpp_workqueue");
+ cpp_dev->work = kmalloc(sizeof(struct msm_cpp_work_t),
+ GFP_KERNEL);
+
+ if (!cpp_dev->work) {
+ pr_err("no enough memory\n");
+ rc = -ENOMEM;
+ goto bus_de_init;
+ }
+
+ INIT_WORK((struct work_struct *)cpp_dev->work, msm_cpp_do_timeout_work);
+ cpp_dev->cpp_open_cnt = 0;
+ cpp_dev->is_firmware_loaded = 0;
+ cpp_dev->iommu_state = CPP_IOMMU_STATE_DETACHED;
+ cpp_timer.data.cpp_dev = cpp_dev;
+ atomic_set(&cpp_timer.used, 0);
+ /* install timer for cpp timeout */
+ CPP_DBG("Installing cpp_timer\n");
+ setup_timer(&cpp_timer.cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timer);
+ cpp_dev->fw_name_bin = NULL;
+ cpp_dev->max_timeout_trial_cnt = MSM_CPP_MAX_TIMEOUT_TRIAL;
+
+
+ if (rc == 0)
+ CPP_DBG("SUCCESS.");
+ else
+ CPP_DBG("FAILED.");
+ return rc;
+
+bus_de_init:
+ if (cpp_dev->bus_master_flag)
+ msm_cpp_deinit_bandwidth_mgr(cpp_dev);
+ else
+ msm_isp_deinit_bandwidth_mgr(ISP_CPP);
+cpp_probe_init_error:
+ media_entity_cleanup(&cpp_dev->msm_sd.sd.entity);
+ msm_sd_unregister(&cpp_dev->msm_sd);
+get_reset_err:
+ reset_control_put(cpp_dev->micro_iface_reset);
+get_reg_err:
+ msm_camera_put_clk_info(pdev, &cpp_dev->clk_info, &cpp_dev->cpp_clk,
+ cpp_dev->num_clks);
+mem_err:
+ msm_camera_put_reg_base(pdev, cpp_dev->cpp_hw_base, "cpp_hw", true);
+cpp_hw_base_failed:
+ msm_camera_put_reg_base(pdev, cpp_dev->vbif_base, "cpp_vbif", false);
+vbif_base_failed:
+ msm_camera_put_reg_base(pdev, cpp_dev->base, "cpp", true);
+cpp_base_failed:
+ msm_camera_put_reg_base(pdev, cpp_dev->camss_cpp_base,
+ "camss_cpp", true);
+
+ kfree(cpp_dev);
+ return rc;
+}
+
+static const struct of_device_id msm_cpp_dt_match[] = {
+ {.compatible = "qcom,cpp"},
+ {}
+};
+
+static int cpp_device_remove(struct platform_device *dev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(dev);
+ struct cpp_device *cpp_dev;
+
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ cpp_dev = (struct cpp_device *)v4l2_get_subdevdata(sd);
+ if (!cpp_dev) {
+ pr_err("%s: cpp device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (cpp_dev->fw) {
+ release_firmware(cpp_dev->fw);
+ cpp_dev->fw = NULL;
+ }
+ if (cpp_dev->bus_master_flag)
+ msm_cpp_deinit_bandwidth_mgr(cpp_dev);
+ else
+ msm_isp_deinit_bandwidth_mgr(ISP_CPP);
+ msm_sd_unregister(&cpp_dev->msm_sd);
+ msm_camera_put_reg_base(dev, cpp_dev->camss_cpp_base,
+ "camss_cpp", true);
+ msm_camera_put_reg_base(dev, cpp_dev->base, "cpp", true);
+ msm_camera_put_reg_base(dev, cpp_dev->vbif_base, "cpp_vbif", false);
+ msm_camera_put_reg_base(dev, cpp_dev->cpp_hw_base, "cpp_hw", true);
+ msm_camera_put_regulators(dev, &cpp_dev->cpp_vdd,
+ cpp_dev->num_reg);
+ msm_camera_put_clk_info(dev, &cpp_dev->clk_info,
+ &cpp_dev->cpp_clk, cpp_dev->num_clks);
+ msm_camera_unregister_bus_client(CAM_BUS_CLIENT_CPP);
+ mutex_destroy(&cpp_dev->mutex);
+ kfree(cpp_dev->work);
+
+ reset_control_put(cpp_dev->micro_iface_reset);
+
+ destroy_workqueue(cpp_dev->timer_wq);
+ kfree(cpp_dev->cpp_clk);
+ kfree(cpp_dev);
+ return 0;
+}
+
+static struct platform_driver cpp_driver = {
+ .probe = cpp_probe,
+ .remove = cpp_device_remove,
+ .driver = {
+ .name = MSM_CPP_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cpp_dt_match,
+ },
+};
+
+static int __init msm_cpp_init_module(void)
+{
+ return platform_driver_register(&cpp_driver);
+}
+
+static void __exit msm_cpp_exit_module(void)
+{
+ platform_driver_unregister(&cpp_driver);
+}
+
+static int msm_cpp_debugfs_error_s(void *data, u64 val)
+{
+ pr_err("setting error inducement");
+ induce_error = val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_error, NULL,
+ msm_cpp_debugfs_error_s, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+ struct dentry *debugfs_base;
+
+ debugfs_base = debugfs_create_dir("msm_cpp", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("error", 0644, debugfs_base,
+ (void *)cpp_dev, &cpp_debugfs_error))
+ return -ENOMEM;
+
+ return 0;
+}
+
+module_init(msm_cpp_init_module);
+module_exit(msm_cpp_exit_module);
+MODULE_DESCRIPTION("MSM CPP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
new file mode 100644
index 0000000..d20d217
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -0,0 +1,311 @@
+/* Copyright (c) 2013-2018, 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_CPP_H__
+#define __MSM_CPP_H__
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-subdev.h>
+#include "msm_generic_buf_mgr.h"
+#include "msm_sd.h"
+#include "cam_soc_api.h"
+#include "cam_hw_ops.h"
+#include <media/msmb_pproc.h>
+#include <soc/qcom/cx_ipeak.h>
+
+/* hw version info:
+ * 31:28 Major version
+ * 27:16 Minor version
+ * 15:0 Revision bits
+ */
+#define CPP_HW_VERSION_1_1_0 0x10010000
+#define CPP_HW_VERSION_1_1_1 0x10010001
+#define CPP_HW_VERSION_2_0_0 0x20000000
+#define CPP_HW_VERSION_4_0_0 0x40000000
+#define CPP_HW_VERSION_4_1_0 0x40010000
+#define CPP_HW_VERSION_5_0_0 0x50000000
+#define CPP_HW_VERSION_5_1_0 0x50010000
+
+#define VBIF_VERSION_2_3_0 0x20030000
+
+#define MAX_ACTIVE_CPP_INSTANCE 8
+#define MAX_CPP_PROCESSING_FRAME 2
+#define MAX_CPP_V4l2_EVENTS 30
+
+#define MSM_CPP_MICRO_BASE 0x4000
+#define MSM_CPP_MICRO_HW_VERSION 0x0000
+#define MSM_CPP_MICRO_IRQGEN_STAT 0x0004
+#define MSM_CPP_MICRO_IRQGEN_CLR 0x0008
+#define MSM_CPP_MICRO_IRQGEN_MASK 0x000C
+#define MSM_CPP_MICRO_FIFO_TX_DATA 0x0010
+#define MSM_CPP_MICRO_FIFO_TX_STAT 0x0014
+#define MSM_CPP_MICRO_FIFO_RX_DATA 0x0018
+#define MSM_CPP_MICRO_FIFO_RX_STAT 0x001C
+#define MSM_CPP_MICRO_BOOT_START 0x0020
+#define MSM_CPP_MICRO_BOOT_LDORG 0x0024
+#define MSM_CPP_MICRO_CLKEN_CTL 0x0030
+
+#define MSM_CPP_CMD_GET_BOOTLOADER_VER 0x1
+#define MSM_CPP_CMD_FW_LOAD 0x2
+#define MSM_CPP_CMD_EXEC_JUMP 0x3
+#define MSM_CPP_CMD_RESET_HW 0x5
+#define MSM_CPP_CMD_PROCESS_FRAME 0x6
+#define MSM_CPP_CMD_FLUSH_STREAM 0x7
+#define MSM_CPP_CMD_CFG_MEM_PARAM 0x8
+#define MSM_CPP_CMD_ERROR_REQUEST 0x9
+#define MSM_CPP_CMD_GET_STATUS 0xA
+#define MSM_CPP_CMD_GET_FW_VER 0xB
+#define MSM_CPP_CMD_GROUP_BUFFER_DUP 0x12
+#define MSM_CPP_CMD_GROUP_BUFFER 0xF
+
+#define MSM_CPP_MSG_ID_CMD 0x3E646D63
+#define MSM_CPP_MSG_ID_OK 0x0A0A4B4F
+#define MSM_CPP_MSG_ID_TRAILER 0xABCDEFAA
+
+#define MSM_CPP_MSG_ID_JUMP_ACK 0x00000001
+#define MSM_CPP_MSG_ID_FRAME_ACK 0x00000002
+#define MSM_CPP_MSG_ID_FRAME_NACK 0x00000003
+#define MSM_CPP_MSG_ID_FLUSH_ACK 0x00000004
+#define MSM_CPP_MSG_ID_FLUSH_NACK 0x00000005
+#define MSM_CPP_MSG_ID_CFG_MEM_ACK 0x00000006
+#define MSM_CPP_MSG_ID_CFG_MEM_INV 0x00000007
+#define MSM_CPP_MSG_ID_ERROR_STATUS 0x00000008
+#define MSM_CPP_MSG_ID_INVALID_CMD 0x00000009
+#define MSM_CPP_MSG_ID_GEN_STATUS 0x0000000A
+#define MSM_CPP_MSG_ID_FLUSHED 0x0000000B
+#define MSM_CPP_MSG_ID_FW_VER 0x0000000C
+
+#define MSM_CPP_JUMP_ADDRESS 0x20
+#define MSM_CPP_START_ADDRESS 0x0
+#define MSM_CPP_END_ADDRESS 0x3F00
+
+#define MSM_CPP_POLL_RETRIES 200
+#define MSM_CPP_TASKLETQ_SIZE 16
+#define MSM_CPP_TX_FIFO_LEVEL 16
+#define MSM_CPP_RX_FIFO_LEVEL 512
+
+enum cpp_vbif_error {
+ CPP_VBIF_ERROR_HANG,
+ CPP_VBIF_ERROR_MAX,
+};
+
+enum cpp_vbif_client {
+ VBIF_CLIENT_CPP,
+ VBIF_CLIENT_FD,
+ VBIF_CLIENT_MAX,
+};
+
+struct msm_cpp_vbif_data {
+ int (*err_handler[VBIF_CLIENT_MAX])(void *, uint32_t);
+ void *dev[VBIF_CLIENT_MAX];
+};
+
+struct cpp_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum cpp_state {
+ CPP_STATE_BOOT,
+ CPP_STATE_IDLE,
+ CPP_STATE_ACTIVE,
+ CPP_STATE_OFF,
+};
+
+enum cpp_iommu_state {
+ CPP_IOMMU_STATE_DETACHED,
+ CPP_IOMMU_STATE_ATTACHED,
+};
+
+enum cpp_iommu_fault_state {
+ CPP_IOMMU_FAULT_NONE,
+ CPP_IOMMU_FAULT_DETECTED,
+ CPP_IOMMU_FAULT_RECOVERED,
+};
+
+enum msm_queue {
+ MSM_CAM_Q_CTRL, /* control command or control command status */
+ MSM_CAM_Q_VFE_EVT, /* adsp event */
+ MSM_CAM_Q_VFE_MSG, /* adsp message */
+ MSM_CAM_Q_V4L2_REQ, /* v4l2 request */
+ MSM_CAM_Q_VPE_MSG, /* vpe message */
+ MSM_CAM_Q_PP_MSG, /* pp message */
+};
+
+struct msm_queue_cmd {
+ struct list_head list_config;
+ struct list_head list_control;
+ struct list_head list_frame;
+ struct list_head list_pict;
+ struct list_head list_vpe_frame;
+ struct list_head list_eventdata;
+ enum msm_queue type;
+ void *command;
+ atomic_t on_heap;
+ struct timespec ts;
+ uint32_t error_code;
+ uint32_t trans_code;
+};
+
+struct msm_device_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int max;
+ int len;
+ const char *name;
+};
+
+struct msm_cpp_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t irq_status;
+ uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
+ uint32_t tx_level;
+ uint8_t cmd_used;
+};
+
+struct msm_cpp_buffer_map_info_t {
+ unsigned long len;
+ dma_addr_t phy_addr;
+ int buf_fd;
+ struct msm_cpp_buffer_info_t buff_info;
+};
+
+struct msm_cpp_buffer_map_list_t {
+ struct msm_cpp_buffer_map_info_t map_info;
+ struct list_head entry;
+};
+
+struct msm_cpp_buff_queue_info_t {
+ uint32_t used;
+ uint16_t session_id;
+ uint16_t stream_id;
+ enum smmu_attach_mode security_mode;
+ struct list_head vb2_buff_head;
+ struct list_head native_buff_head;
+};
+
+struct msm_cpp_work_t {
+ struct work_struct my_work;
+ struct cpp_device *cpp_dev;
+};
+
+struct msm_cpp_payload_params {
+ uint32_t stripe_base;
+ uint32_t stripe_size;
+ uint32_t plane_base;
+ uint32_t plane_size;
+
+ /* offsets for stripe/plane pointers in payload */
+ uint32_t rd_pntr_off;
+ uint32_t wr_0_pntr_off;
+ uint32_t rd_ref_pntr_off;
+ uint32_t wr_ref_pntr_off;
+ uint32_t wr_0_meta_data_wr_pntr_off;
+ uint32_t fe_mmu_pf_ptr_off;
+ uint32_t ref_fe_mmu_pf_ptr_off;
+ uint32_t we_mmu_pf_ptr_off;
+ uint32_t dup_we_mmu_pf_ptr_off;
+ uint32_t ref_we_mmu_pf_ptr_off;
+ uint32_t set_group_buffer_len;
+ uint32_t dup_frame_indicator_off;
+};
+
+struct cpp_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *irq;
+ void __iomem *vbif_base;
+ void __iomem *base;
+ void __iomem *cpp_hw_base;
+ void __iomem *camss_cpp_base;
+ struct clk **cpp_clk;
+ struct msm_cam_clk_info *clk_info;
+ size_t num_clks;
+ struct reset_control *micro_iface_reset;
+ struct msm_cam_regulator *cpp_vdd;
+ int num_reg;
+ struct mutex mutex;
+ enum cpp_state state;
+ enum cpp_iommu_state iommu_state;
+ uint8_t is_firmware_loaded;
+ char *fw_name_bin;
+ const struct firmware *fw;
+ struct workqueue_struct *timer_wq;
+ struct msm_cpp_work_t *work;
+ uint32_t fw_version;
+ uint8_t stream_cnt;
+ uint8_t timeout_trial_cnt;
+ uint8_t max_timeout_trial_cnt;
+
+ int domain_num;
+ struct iommu_domain *domain;
+ struct device *iommu_ctx;
+ uint32_t num_clk;
+ uint32_t min_clk_rate;
+
+ int iommu_hdl;
+ struct ion_client *ion_client;
+ enum smmu_attach_mode security_mode;
+ /* Reusing proven tasklet from msm isp */
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct cpp_tasklet;
+ struct msm_cpp_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_CPP_TASKLETQ_SIZE];
+
+ struct cpp_subscribe_info cpp_subscribe_list[MAX_ACTIVE_CPP_INSTANCE];
+ uint32_t cpp_open_cnt;
+ struct cpp_hw_info hw_info;
+
+ struct msm_device_queue eventData_q; /* V4L2 Event Payload Queue */
+
+ /* Processing Queue
+ * store frame info for frames sent to microcontroller
+ */
+ struct msm_device_queue processing_q;
+
+ struct msm_cpp_buff_queue_info_t *buff_queue;
+ uint32_t num_buffq;
+ struct msm_cam_buf_mgr_req_ops buf_mgr_ops;
+
+ uint32_t bus_client;
+ uint32_t bus_idx;
+ uint32_t bus_master_flag;
+ uint32_t micro_reset;
+ struct msm_cpp_payload_params payload_params;
+ struct msm_cpp_vbif_data *vbif_data;
+ bool turbo_vote;
+ struct cx_ipeak_client *cpp_cx_ipeak;
+ enum cpp_iommu_fault_state fault_status;
+};
+
+int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev);
+int msm_update_freq_tbl(struct cpp_device *cpp_dev);
+int msm_cpp_get_clock_index(struct cpp_device *cpp_dev, const char *clk_name);
+int msm_cpp_get_regulator_index(struct cpp_device *cpp_dev,
+ const char *regulator_name);
+long msm_cpp_set_core_clk(struct cpp_device *cpp_dev, long rate, int idx);
+void msm_cpp_fetch_dt_params(struct cpp_device *cpp_dev);
+int msm_cpp_read_payload_params_from_dt(struct cpp_device *cpp_dev);
+void msm_cpp_vbif_register_error_handler(void *dev,
+ enum cpp_vbif_client client,
+ int (*client_vbif_error_handler)(void *, uint32_t));
+
+#endif /* __MSM_CPP_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
new file mode 100644
index 0000000..d733d2a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2016, 2018, 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) "MSM-CPP-SOC %s:%d " fmt, __func__, __LINE__
+
+#include <linux/clk/msm-clk.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/delay.h>
+#include <media/msmb_pproc.h>
+#include "msm_cpp.h"
+
+
+#define CPP_DT_READ_U32_ERR(_dev, _key, _str, _ret, _out) { \
+ _key = _str; \
+ _ret = of_property_read_u32(_dev, _key, &_out); \
+ if (_ret) \
+ break; \
+ }
+
+#define CPP_DT_READ_U32(_dev, _str, _out) { \
+ of_property_read_u32(_dev, _str, &_out); \
+ }
+
+void msm_cpp_fetch_dt_params(struct cpp_device *cpp_dev)
+{
+ int rc = 0;
+ struct device_node *of_node = cpp_dev->pdev->dev.of_node;
+
+ if (!of_node) {
+ pr_err("%s: invalid params\n", __func__);
+ return;
+ }
+
+ of_property_read_u32(of_node, "cell-index", &cpp_dev->pdev->id);
+
+ rc = of_property_read_u32(of_node, "qcom,min-clock-rate",
+ &cpp_dev->min_clk_rate);
+ if (rc < 0) {
+ pr_debug("min-clk-rate not defined, setting it to 0\n");
+ cpp_dev->min_clk_rate = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,bus-master",
+ &cpp_dev->bus_master_flag);
+ if (rc)
+ cpp_dev->bus_master_flag = 0;
+
+ if (of_property_read_bool(of_node, "qcom,micro-reset"))
+ cpp_dev->micro_reset = 1;
+ else
+ cpp_dev->micro_reset = 0;
+}
+
+int msm_cpp_get_clock_index(struct cpp_device *cpp_dev, const char *clk_name)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < cpp_dev->num_clks; i++) {
+ if (!strcmp(clk_name, cpp_dev->clk_info[i].clk_name))
+ return i;
+ }
+ return -EINVAL;
+}
+
+int msm_cpp_get_regulator_index(struct cpp_device *cpp_dev,
+ const char *regulator_name)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < cpp_dev->num_reg; i++) {
+ if (!strcmp(regulator_name, cpp_dev->cpp_vdd[i].name))
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cpp_get_clk_freq_tbl_dt(struct cpp_device *cpp_dev)
+{
+ uint32_t i, count, min_clk_rate;
+ uint32_t idx = 0;
+ struct device_node *of_node;
+ uint32_t *rates;
+ int32_t rc = 0;
+ struct cpp_hw_info *hw_info;
+
+ if (cpp_dev == NULL) {
+ pr_err("Bad parameter\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ of_node = cpp_dev->pdev->dev.of_node;
+ min_clk_rate = cpp_dev->min_clk_rate;
+ hw_info = &cpp_dev->hw_info;
+
+ if ((hw_info == NULL) || (of_node == NULL)) {
+ pr_err("Invalid hw_info %pK or ofnode %pK\n", hw_info, of_node);
+ rc = -EINVAL;
+ goto err;
+
+ }
+ count = of_property_count_u32_elems(of_node, "qcom,src-clock-rates");
+ if ((count == 0) || (count > MAX_FREQ_TBL)) {
+ pr_err("Clock count is invalid\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rates = devm_kcalloc(&cpp_dev->pdev->dev, count, sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!rates) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,src-clock-rates",
+ rates, count);
+ if (rc) {
+ rc = -EINVAL;
+ goto mem_free;
+ }
+
+ for (i = 0; i < count; i++) {
+ pr_debug("entry=%d\n", rates[i]);
+ if (rates[i] >= min_clk_rate) {
+ hw_info->freq_tbl[idx++] = rates[i];
+ pr_debug("tbl[%d]=%d\n", idx-1, rates[i]);
+ }
+ }
+
+ pr_debug("%s: idx %d\n", __func__, idx);
+ hw_info->freq_tbl_count = idx;
+
+mem_free:
+ devm_kfree(&cpp_dev->pdev->dev, rates);
+err:
+ return rc;
+}
+
+int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev)
+{
+ int rc;
+
+ rc = reset_control_assert(cpp_dev->micro_iface_reset);
+ if (rc) {
+ pr_err("%s:micro_iface_reset assert failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Below usleep values are chosen based on experiments
+ * and this was the smallest number which works. This
+ * sleep is needed to leave enough time for Microcontroller
+ * to resets all its registers.
+ */
+ usleep_range(1000, 1200);
+
+ rc = reset_control_deassert(cpp_dev->micro_iface_reset);
+ if (rc) {
+ pr_err("%s:micro_iface_reset de-assert failed\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Below usleep values are chosen based on experiments
+ * and this was the smallest number which works. This
+ * sleep is needed to leave enough time for Microcontroller
+ * to resets all its registers.
+ */
+ usleep_range(1000, 1200);
+ return 0;
+}
+
+int msm_update_freq_tbl(struct cpp_device *cpp_dev)
+{
+ int msm_cpp_core_clk_idx;
+ int rc = 0;
+
+ msm_cpp_core_clk_idx = msm_cpp_get_clock_index(cpp_dev, "cpp_core_clk");
+ if (msm_cpp_core_clk_idx < 0) {
+ pr_err("%s: fail to get clock index\n", __func__);
+ rc = msm_cpp_core_clk_idx;
+ return rc;
+ }
+ rc = cpp_get_clk_freq_tbl_dt(cpp_dev);
+ if (rc < 0) {
+ pr_err("%s: fail to get frequency table\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+long msm_cpp_set_core_clk(struct cpp_device *cpp_dev, long rate, int idx)
+{
+ long rc = 0;
+
+ rc = msm_camera_clk_set_rate(&cpp_dev->pdev->dev,
+ cpp_dev->cpp_clk[idx], rate);
+ if (rc < 0) {
+ pr_err("%s: fail to get frequency table\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+int msm_cpp_read_payload_params_from_dt(struct cpp_device *cpp_dev)
+{
+ struct platform_device *pdev = cpp_dev->pdev;
+ struct device_node *fw_info_node = NULL, *dev_node = NULL;
+ char *key = "qcom,cpp-fw-payload-info";
+ struct msm_cpp_payload_params *payload_params;
+ int ret = 0;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("%s: Invalid platform device/node\n", __func__);
+ ret = -ENODEV;
+ goto no_cpp_node;
+ }
+
+ dev_node = pdev->dev.of_node;
+ fw_info_node = of_find_node_by_name(dev_node, key);
+ if (!fw_info_node) {
+ ret = -ENODEV;
+ goto no_binding;
+ }
+ payload_params = &cpp_dev->payload_params;
+ memset(payload_params, 0x0, sizeof(struct msm_cpp_payload_params));
+
+ do {
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,stripe-base", ret,
+ payload_params->stripe_base);
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,plane-base", ret,
+ payload_params->plane_base);
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,stripe-size", ret,
+ payload_params->stripe_size);
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,plane-size", ret,
+ payload_params->plane_size);
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,fe-ptr-off", ret,
+ payload_params->rd_pntr_off);
+ CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,we-ptr-off", ret,
+ payload_params->wr_0_pntr_off);
+
+ CPP_DT_READ_U32(fw_info_node, "qcom,ref-fe-ptr-off",
+ payload_params->rd_ref_pntr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,ref-we-ptr-off",
+ payload_params->wr_ref_pntr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,we-meta-ptr-off",
+ payload_params->wr_0_meta_data_wr_pntr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,fe-mmu-pf-ptr-off",
+ payload_params->fe_mmu_pf_ptr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,ref-fe-mmu-pf-ptr-off",
+ payload_params->ref_fe_mmu_pf_ptr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,we-mmu-pf-ptr-off",
+ payload_params->we_mmu_pf_ptr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,dup-we-mmu-pf-ptr-off",
+ payload_params->dup_we_mmu_pf_ptr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,ref-we-mmu-pf-ptr-off",
+ payload_params->ref_we_mmu_pf_ptr_off);
+ CPP_DT_READ_U32(fw_info_node, "qcom,set-group-buffer-len",
+ payload_params->set_group_buffer_len);
+ CPP_DT_READ_U32(fw_info_node, "qcom,dup-frame-indicator-off",
+ payload_params->dup_frame_indicator_off);
+ } while (0);
+
+no_binding:
+ if (ret)
+ pr_err("%s: Error reading binding %s, ret %d\n",
+ __func__, key, ret);
+no_cpp_node:
+ return ret;
+}
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile b/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile
new file mode 100644
index 0000000..65a7e34
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_vpe.o
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
new file mode 100644
index 0000000..404aebd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
@@ -0,0 +1,1691 @@
+/* Copyright (c) 2012-2016, 2018, 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) "MSM-VPE %s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/msm_iommu_domains.h>
+#include <linux/qcom_iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/media-entity.h>
+#include <media/msmb_generic_buf_mgr.h>
+#include <media/msmb_pproc.h>
+#include <asm/dma-iommu.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-buf.h>
+#include "msm_vpe.h"
+#include "msm_camera_io_util.h"
+
+#define MSM_VPE_IDENT_TO_SESSION_ID(identity) ((identity >> 16) & 0xFFFF)
+#define MSM_VPE_IDENT_TO_STREAM_ID(identity) (identity & 0xFFFF)
+
+#define MSM_VPE_DRV_NAME "msm_vpe"
+
+#define MSM_VPE_MAX_BUFF_QUEUE 16
+
+#define CONFIG_MSM_VPE_DBG 0
+
+#if CONFIG_MSM_VPE_DBG
+#define VPE_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define VPE_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+static void vpe_mem_dump(const char * const name, const void * const addr,
+ int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+
+ VPE_DBG("%s: (%s) %pK %d\n", __func__, name, 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, "%pK: ", p);
+ p_str += 10;
+ }
+ data = *p++;
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ VPE_DBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ VPE_DBG("%s\n", line_str);
+}
+
+static inline long long vpe_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+#define msm_dequeue(queue, member) ({ \
+ unsigned long flags; \
+ struct msm_device_queue *__q = (queue); \
+ struct msm_queue_cmd *qcmd = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ qcmd = list_first_entry(&__q->list, \
+ struct msm_queue_cmd, \
+ member); \
+ list_del_init(&qcmd->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ qcmd; \
+ })
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+ spin_lock_init(&queue->lock);
+ queue->len = 0;
+ queue->max = 0;
+ queue->name = name;
+ INIT_LIST_HEAD(&queue->list);
+ init_waitqueue_head(&queue->wait);
+}
+
+static struct msm_cam_clk_info vpe_clk_info[] = {
+ {"vpe_clk", 160000000},
+ {"vpe_pclk", -1},
+};
+
+static int msm_vpe_notify_frame_done(struct vpe_device *vpe_dev);
+
+static void msm_enqueue(struct msm_device_queue *queue,
+ struct list_head *entry)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->len++;
+ if (queue->len > queue->max) {
+ queue->max = queue->len;
+ pr_debug("queue %s new max is %d\n", queue->name, queue->max);
+ }
+ list_add_tail(entry, &queue->list);
+ wake_up(&queue->wait);
+ VPE_DBG("woke up %s\n", queue->name);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static struct msm_vpe_buff_queue_info_t *msm_vpe_get_buff_queue_entry(
+ struct vpe_device *vpe_dev, uint32_t session_id, uint32_t stream_id)
+{
+ uint32_t i = 0;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info = NULL;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if ((vpe_dev->buff_queue[i].used == 1) &&
+ (vpe_dev->buff_queue[i].session_id == session_id) &&
+ (vpe_dev->buff_queue[i].stream_id == stream_id)) {
+ buff_queue_info = &vpe_dev->buff_queue[i];
+ break;
+ }
+ }
+
+ if (buff_queue_info == NULL) {
+ pr_err("error buffer queue entry for sess:%d strm:%d not found\n",
+ session_id, stream_id);
+ }
+ return buff_queue_info;
+}
+
+static unsigned long msm_vpe_get_phy_addr(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue_info, uint32_t buff_index,
+ uint8_t native_buff)
+{
+ unsigned long phy_add = 0;
+ struct list_head *buff_head;
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+
+ if (native_buff)
+ buff_head = &buff_queue_info->native_buff_head;
+ else
+ buff_head = &buff_queue_info->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buff_index) {
+ phy_add = buff->map_info.phy_addr;
+ break;
+ }
+ }
+
+ return phy_add;
+}
+
+static unsigned long msm_vpe_queue_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue,
+ struct msm_vpe_buffer_info_t *buffer_info)
+{
+ struct list_head *buff_head;
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+ int rc = 0;
+
+ if (buffer_info->native_buff)
+ buff_head = &buff_queue->native_buff_head;
+ else
+ buff_head = &buff_queue->vb2_buff_head;
+
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ if (buff->map_info.buff_info.index == buffer_info->index) {
+ pr_err("error buffer index already queued\n");
+ return -EINVAL;
+ }
+ }
+
+ buff = kzalloc(
+ sizeof(struct msm_vpe_buffer_map_list_t), GFP_KERNEL);
+ if (!buff)
+ return -EINVAL;
+
+ buff->map_info.buff_info = *buffer_info;
+ buff->map_info.dbuf = dma_buf_get(buffer_info->fd);
+ if (IS_ERR_OR_NULL(buff->map_info.dbuf)) {
+ pr_err("Ion dma get buf failed\n");
+ rc = PTR_ERR(buff->map_info.dbuf);
+ goto err_get;
+ }
+
+ buff->map_info.attachment = dma_buf_attach(buff->map_info.dbuf,
+ &vpe_dev->pdev->dev);
+ if (IS_ERR_OR_NULL(buff->map_info.attachment)) {
+ pr_err("Ion dma buf attach failed\n");
+ rc = PTR_ERR(buff->map_info.attachment);
+ goto err_put;
+ }
+
+ buff->map_info.table =
+ dma_buf_map_attachment(buff->map_info.attachment,
+ DMA_BIDIRECTIONAL);
+ if (IS_ERR_OR_NULL(buff->map_info.table)) {
+ pr_err("DMA buf map attachment failed\n");
+ rc = PTR_ERR(buff->map_info.table);
+ goto err_detach;
+ }
+ if (msm_map_dma_buf(buff->map_info.dbuf, buff->map_info.table,
+ vpe_dev->domain_num, 0, SZ_4K, 0,
+ &buff->map_info.phy_addr,
+ &buff->map_info.len, 0, 0)) {
+ pr_err("%s: cannot map address", __func__);
+ goto err_detachment;
+ }
+
+ INIT_LIST_HEAD(&buff->entry);
+ list_add_tail(&buff->entry, buff_head);
+
+ return buff->map_info.phy_addr;
+
+err_detachment:
+ dma_buf_unmap_attachment(buff->map_info.attachment,
+ buff->map_info.table, DMA_BIDIRECTIONAL);
+err_detach:
+ dma_buf_detach(buff->map_info.dbuf, buff->map_info.attachment);
+err_put:
+ dma_buf_put(buff->map_info.dbuf);
+err_get:
+ kzfree(buff);
+ return 0;
+}
+
+static void msm_vpe_dequeue_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buffer_map_list_t *buff)
+{
+ msm_unmap_dma_buf(buff->map_info.table, vpe_dev->domain_num, 0);
+ dma_buf_unmap_attachment(buff->map_info.attachment,
+ buff->map_info.table, DMA_BIDIRECTIONAL);
+ dma_buf_detach(buff->map_info.dbuf, buff->map_info.attachment);
+ dma_buf_put(buff->map_info.dbuf);
+ list_del_init(&buff->entry);
+ kzfree(buff);
+}
+
+static unsigned long msm_vpe_fetch_buffer_info(struct vpe_device *vpe_dev,
+ struct msm_vpe_buffer_info_t *buffer_info, uint32_t session_id,
+ uint32_t stream_id)
+{
+ unsigned long phy_addr = 0;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+ uint8_t native_buff = buffer_info->native_buff;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return phy_addr;
+ }
+
+ phy_addr = msm_vpe_get_phy_addr(vpe_dev, buff_queue_info,
+ buffer_info->index, native_buff);
+ if ((phy_addr == 0) && (native_buff)) {
+ phy_addr = msm_vpe_queue_buffer_info(vpe_dev, buff_queue_info,
+ buffer_info);
+ }
+ return phy_addr;
+}
+
+static int32_t msm_vpe_enqueue_buff_info_list(struct vpe_device *vpe_dev,
+ struct msm_vpe_stream_buff_info_t *stream_buff_info)
+{
+ uint32_t j;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev,
+ (stream_buff_info->identity >> 16) & 0xFFFF,
+ stream_buff_info->identity & 0xFFFF);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ (stream_buff_info->identity >> 16) & 0xFFFF,
+ stream_buff_info->identity & 0xFFFF);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < stream_buff_info->num_buffs; j++) {
+ msm_vpe_queue_buffer_info(vpe_dev, buff_queue_info,
+ &stream_buff_info->buffer_info[j]);
+ }
+ return 0;
+}
+
+static int32_t msm_vpe_dequeue_buff_info_list(struct vpe_device *vpe_dev,
+ struct msm_vpe_buff_queue_info_t *buff_queue_info)
+{
+ struct msm_vpe_buffer_map_list_t *buff, *save;
+ struct list_head *buff_head;
+
+ buff_head = &buff_queue_info->native_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_vpe_dequeue_buffer_info(vpe_dev, buff);
+ }
+
+ buff_head = &buff_queue_info->vb2_buff_head;
+ list_for_each_entry_safe(buff, save, buff_head, entry) {
+ msm_vpe_dequeue_buffer_info(vpe_dev, buff);
+ }
+
+ return 0;
+}
+
+static int32_t msm_vpe_add_buff_queue_entry(struct vpe_device *vpe_dev,
+ uint16_t session_id, uint16_t stream_id)
+{
+ uint32_t i;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if (vpe_dev->buff_queue[i].used == 0) {
+ buff_queue_info = &vpe_dev->buff_queue[i];
+ buff_queue_info->used = 1;
+ buff_queue_info->session_id = session_id;
+ buff_queue_info->stream_id = stream_id;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+ }
+ }
+ pr_err("buffer queue full. error for sessionid: %d streamid: %d\n",
+ session_id, stream_id);
+ return -EINVAL;
+}
+
+static int32_t msm_vpe_free_buff_queue_entry(struct vpe_device *vpe_dev,
+ uint32_t session_id, uint32_t stream_id)
+{
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev, session_id,
+ stream_id);
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for sessid:%d strmid:%d\n",
+ session_id, stream_id);
+ return -EINVAL;
+ }
+
+ buff_queue_info->used = 0;
+ buff_queue_info->session_id = 0;
+ buff_queue_info->stream_id = 0;
+ INIT_LIST_HEAD(&buff_queue_info->vb2_buff_head);
+ INIT_LIST_HEAD(&buff_queue_info->native_buff_head);
+ return 0;
+}
+
+static int32_t msm_vpe_create_buff_queue(struct vpe_device *vpe_dev,
+ uint32_t num_buffq)
+{
+ struct msm_vpe_buff_queue_info_t *buff_queue;
+
+ buff_queue = kzalloc(
+ sizeof(struct msm_vpe_buff_queue_info_t) * num_buffq,
+ GFP_KERNEL);
+ if (!buff_queue) {
+ pr_err("Buff queue allocation failure\n");
+ return -ENOMEM;
+ }
+
+ if (vpe_dev->buff_queue) {
+ pr_err("Buff queue not empty\n");
+ kzfree(buff_queue);
+ return -EINVAL;
+ }
+ vpe_dev->buff_queue = buff_queue;
+ vpe_dev->num_buffq = num_buffq;
+ return 0;
+}
+
+static void msm_vpe_delete_buff_queue(struct vpe_device *vpe_dev)
+{
+ uint32_t i;
+
+ for (i = 0; i < vpe_dev->num_buffq; i++) {
+ if (vpe_dev->buff_queue[i].used == 1) {
+ pr_err("Queue not free sessionid: %d, streamid: %d\n",
+ vpe_dev->buff_queue[i].session_id,
+ vpe_dev->buff_queue[i].stream_id);
+ msm_vpe_free_buff_queue_entry(vpe_dev,
+ vpe_dev->buff_queue[i].session_id,
+ vpe_dev->buff_queue[i].stream_id);
+ }
+ }
+ kzfree(vpe_dev->buff_queue);
+ vpe_dev->buff_queue = NULL;
+ vpe_dev->num_buffq = 0;
+}
+
+void vpe_release_ion_client(struct kref *ref)
+{
+ struct vpe_device *vpe_dev = container_of(ref,
+ struct vpe_device, refcount);
+ ion_client_destroy(vpe_dev->client);
+}
+
+static int vpe_init_mem(struct vpe_device *vpe_dev)
+{
+ kref_init(&vpe_dev->refcount);
+ kref_get(&vpe_dev->refcount);
+ vpe_dev->client = msm_ion_client_create("vpe");
+
+ if (!vpe_dev->client) {
+ pr_err("couldn't create ion client\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void vpe_deinit_mem(struct vpe_device *vpe_dev)
+{
+ kref_put(&vpe_dev->refcount, vpe_release_ion_client);
+}
+
+static irqreturn_t msm_vpe_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ uint32_t irq_status;
+ struct msm_vpe_tasklet_queue_cmd *queue_cmd;
+ struct vpe_device *vpe_dev = (struct vpe_device *) data;
+
+ irq_status = msm_camera_io_r_mb(vpe_dev->base +
+ VPE_INTR_STATUS_OFFSET);
+
+ spin_lock_irqsave(&vpe_dev->tasklet_lock, flags);
+ queue_cmd = &vpe_dev->tasklet_queue_cmd[vpe_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ VPE_DBG("%s: vpe tasklet queue overflow\n", __func__);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &vpe_dev->irq_cnt);
+ }
+ queue_cmd->irq_status = irq_status;
+
+ queue_cmd->cmd_used = 1;
+ vpe_dev->taskletq_idx =
+ (vpe_dev->taskletq_idx + 1) % MSM_VPE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &vpe_dev->tasklet_q);
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+
+ tasklet_schedule(&vpe_dev->vpe_tasklet);
+
+ msm_camera_io_w_mb(irq_status, vpe_dev->base + VPE_INTR_CLEAR_OFFSET);
+ msm_camera_io_w(0, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ VPE_DBG("%s: irq_status=0x%x.\n", __func__, irq_status);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_vpe_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vpe_device *vpe_dev = (struct vpe_device *)data;
+ struct msm_vpe_tasklet_queue_cmd *queue_cmd;
+
+ while (atomic_read(&vpe_dev->irq_cnt)) {
+ spin_lock_irqsave(&vpe_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&vpe_dev->tasklet_q,
+ struct msm_vpe_tasklet_queue_cmd, list);
+ if (!queue_cmd) {
+ atomic_set(&vpe_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &vpe_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+
+ spin_unlock_irqrestore(&vpe_dev->tasklet_lock, flags);
+
+ VPE_DBG("Frame done!!\n");
+ msm_vpe_notify_frame_done(vpe_dev);
+ }
+}
+
+static int vpe_init_hardware(struct vpe_device *vpe_dev)
+{
+ int rc = 0;
+
+ if (vpe_dev->fs_vpe == NULL) {
+ vpe_dev->fs_vpe =
+ regulator_get(&vpe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vpe_dev->fs_vpe)) {
+ pr_err("Regulator vpe vdd get failed %ld\n",
+ PTR_ERR(vpe_dev->fs_vpe));
+ vpe_dev->fs_vpe = NULL;
+ rc = -ENODEV;
+ goto fail;
+ } else if (regulator_enable(vpe_dev->fs_vpe)) {
+ pr_err("Regulator vpe vdd enable failed\n");
+ regulator_put(vpe_dev->fs_vpe);
+ vpe_dev->fs_vpe = NULL;
+ rc = -ENODEV;
+ goto fail;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vpe_dev->pdev->dev, vpe_clk_info,
+ vpe_dev->vpe_clk, ARRAY_SIZE(vpe_clk_info), 1);
+ if (rc < 0) {
+ pr_err("clk enable failed\n");
+ goto disable_and_put_regulator;
+ }
+
+ vpe_dev->base = ioremap(vpe_dev->mem->start,
+ resource_size(vpe_dev->mem));
+ if (!vpe_dev->base) {
+ rc = -ENOMEM;
+ pr_err("ioremap failed\n");
+ goto disable_and_put_regulator;
+ }
+
+ if (vpe_dev->state != VPE_STATE_BOOT) {
+ rc = request_irq(vpe_dev->irq->start, msm_vpe_irq,
+ IRQF_TRIGGER_RISING,
+ "vpe", vpe_dev);
+ if (rc < 0) {
+ pr_err("irq request fail! start=%u\n",
+ (uint32_t) vpe_dev->irq->start);
+ rc = -EBUSY;
+ goto unmap_base;
+ } else {
+ VPE_DBG("Got irq! %d\n", (int)vpe_dev->irq->start);
+ }
+ } else {
+ VPE_DBG("Skip requesting the irq since device is booting\n");
+ }
+ vpe_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev();
+
+ msm_vpe_create_buff_queue(vpe_dev, MSM_VPE_MAX_BUFF_QUEUE);
+ return rc;
+
+unmap_base:
+ iounmap(vpe_dev->base);
+disable_and_put_regulator:
+ regulator_disable(vpe_dev->fs_vpe);
+ regulator_put(vpe_dev->fs_vpe);
+fail:
+ return rc;
+}
+
+static int vpe_release_hardware(struct vpe_device *vpe_dev)
+{
+ if (vpe_dev->state != VPE_STATE_BOOT) {
+ free_irq(vpe_dev->irq->start, vpe_dev);
+ tasklet_kill(&vpe_dev->vpe_tasklet);
+ atomic_set(&vpe_dev->irq_cnt, 0);
+ }
+
+ msm_vpe_delete_buff_queue(vpe_dev);
+ iounmap(vpe_dev->base);
+ msm_cam_clk_enable(&vpe_dev->pdev->dev, vpe_clk_info,
+ vpe_dev->vpe_clk, ARRAY_SIZE(vpe_clk_info), 0);
+ return 0;
+}
+
+static int vpe_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&vpe_dev->mutex);
+ if (vpe_dev->vpe_open_cnt == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("No free VPE instance\n");
+ rc = -ENODEV;
+ goto err_mutex_unlock;
+ }
+
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].active == 0) {
+ vpe_dev->vpe_subscribe_list[i].active = 1;
+ vpe_dev->vpe_subscribe_list[i].vfh = &fh->vfh;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("No free instance\n");
+ rc = -ENODEV;
+ goto err_mutex_unlock;
+ }
+
+ VPE_DBG("open %d %pK\n", i, &fh->vfh);
+ vpe_dev->vpe_open_cnt++;
+ if (vpe_dev->vpe_open_cnt == 1) {
+ rc = vpe_init_hardware(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init vpe hardware\n", __func__);
+ vpe_dev->vpe_open_cnt--;
+ goto err_fixup_sub_list;
+ }
+ rc = vpe_init_mem(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init mem\n", __func__);
+ vpe_dev->vpe_open_cnt--;
+ rc = -ENODEV;
+ goto err_release_hardware;
+ }
+ vpe_dev->state = VPE_STATE_IDLE;
+ }
+ mutex_unlock(&vpe_dev->mutex);
+
+ return rc;
+
+err_release_hardware:
+ vpe_release_hardware(vpe_dev);
+err_fixup_sub_list:
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == &fh->vfh) {
+ vpe_dev->vpe_subscribe_list[i].active = 0;
+ vpe_dev->vpe_subscribe_list[i].vfh = NULL;
+ break;
+ }
+ }
+err_mutex_unlock:
+ mutex_unlock(&vpe_dev->mutex);
+ return rc;
+}
+
+static int vpe_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&vpe_dev->mutex);
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == &fh->vfh) {
+ vpe_dev->vpe_subscribe_list[i].active = 0;
+ vpe_dev->vpe_subscribe_list[i].vfh = NULL;
+ break;
+ }
+ }
+ if (i == MAX_ACTIVE_VPE_INSTANCE) {
+ pr_err("Invalid close\n");
+ mutex_unlock(&vpe_dev->mutex);
+ return -ENODEV;
+ }
+
+ VPE_DBG("close %d %pK\n", i, &fh->vfh);
+ vpe_dev->vpe_open_cnt--;
+ if (vpe_dev->vpe_open_cnt == 0) {
+ vpe_deinit_mem(vpe_dev);
+ vpe_release_hardware(vpe_dev);
+ vpe_dev->state = VPE_STATE_OFF;
+ }
+ mutex_unlock(&vpe_dev->mutex);
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_vpe_internal_ops = {
+ .open = vpe_open_node,
+ .close = vpe_close_node,
+};
+
+static int msm_vpe_buffer_ops(struct vpe_device *vpe_dev,
+ uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info)
+{
+ int rc = -EINVAL;
+
+ rc = v4l2_subdev_call(vpe_dev->buf_mgr_subdev, core, ioctl,
+ buff_mgr_ops, buff_mgr_info);
+ if (rc < 0)
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static int msm_vpe_notify_frame_done(struct vpe_device *vpe_dev)
+{
+ struct v4l2_event v4l2_evt;
+ struct msm_queue_cmd *frame_qcmd;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_vpe_frame_info_t *processed_frame;
+ struct msm_device_queue *queue = &vpe_dev->processing_q;
+ struct msm_buf_mngr_info buff_mgr_info;
+ int rc = 0;
+
+ if (queue->len > 0) {
+ frame_qcmd = msm_dequeue(queue, list_frame);
+ if (!frame_qcmd) {
+ pr_err("%s: %d frame_qcmd is NULL\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ processed_frame = frame_qcmd->command;
+ do_gettimeofday(&(processed_frame->out_time));
+ kfree(frame_qcmd);
+ event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_ATOMIC);
+ if (!event_qcmd) {
+ pr_err("%s: Insufficient memory\n", __func__);
+ return -ENOMEM;
+ }
+ atomic_set(&event_qcmd->on_heap, 1);
+ event_qcmd->command = processed_frame;
+ VPE_DBG("fid %d\n", processed_frame->frame_id);
+ msm_enqueue(&vpe_dev->eventData_q, &event_qcmd->list_eventdata);
+
+ if (!processed_frame->output_buffer_info.processed_divert) {
+ memset(&buff_mgr_info, 0,
+ sizeof(buff_mgr_info));
+ buff_mgr_info.session_id =
+ ((processed_frame->identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id =
+ (processed_frame->identity & 0xFFFF);
+ buff_mgr_info.frame_id = processed_frame->frame_id;
+ buff_mgr_info.timestamp = processed_frame->timestamp;
+ buff_mgr_info.index =
+ processed_frame->output_buffer_info.index;
+ rc = msm_vpe_buffer_ops(vpe_dev,
+ VIDIOC_MSM_BUF_MNGR_BUF_DONE,
+ &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("%s: error doing VIDIOC_MSM_BUF_MNGR_BUF_DONE\n",
+ __func__);
+ rc = -EINVAL;
+ }
+ }
+
+ v4l2_evt.id = processed_frame->inst_id;
+ v4l2_evt.type = V4L2_EVENT_VPE_FRAME_DONE;
+ v4l2_event_queue(vpe_dev->msm_sd.sd.devnode, &v4l2_evt);
+ }
+ return rc;
+}
+
+static void vpe_update_scaler_params(struct vpe_device *vpe_dev,
+ struct msm_vpe_frame_strip_info strip_info)
+{
+ uint32_t out_ROI_width, out_ROI_height;
+ uint32_t src_ROI_width, src_ROI_height;
+
+ /*
+ * phase_step_x, phase_step_y, phase_init_x and phase_init_y
+ * are represented in fixed-point, unsigned 3.29 format
+ */
+ uint32_t phase_step_x = 0;
+ uint32_t phase_step_y = 0;
+ uint32_t phase_init_x = 0;
+ uint32_t phase_init_y = 0;
+
+ uint32_t src_roi, src_x, src_y, src_xy, temp;
+ uint32_t yscale_filter_sel, xscale_filter_sel;
+ uint32_t scale_unit_sel_x, scale_unit_sel_y;
+ uint64_t numerator, denominator;
+
+ /*
+ * assumption is both direction need zoom. this can be
+ * improved.
+ */
+ temp = msm_camera_io_r(vpe_dev->base + VPE_OP_MODE_OFFSET) | 0x3;
+ msm_camera_io_w(temp, vpe_dev->base + VPE_OP_MODE_OFFSET);
+
+ src_ROI_width = strip_info.src_w;
+ src_ROI_height = strip_info.src_h;
+ out_ROI_width = strip_info.dst_w;
+ out_ROI_height = strip_info.dst_h;
+
+ VPE_DBG("src w = %u, h=%u, dst w = %u, h =%u.\n",
+ src_ROI_width, src_ROI_height, out_ROI_width,
+ out_ROI_height);
+ src_roi = (src_ROI_height << 16) + src_ROI_width;
+
+ msm_camera_io_w(src_roi, vpe_dev->base + VPE_SRC_SIZE_OFFSET);
+
+ src_x = strip_info.src_x;
+ src_y = strip_info.src_y;
+
+ VPE_DBG("src_x = %d, src_y=%d.\n", src_x, src_y);
+
+ src_xy = src_y*(1<<16) + src_x;
+ msm_camera_io_w(src_xy, vpe_dev->base +
+ VPE_SRC_XY_OFFSET);
+ VPE_DBG("src_xy = 0x%x, src_roi=0x%x.\n", src_xy, src_roi);
+
+ /* decide whether to use FIR or M/N for scaling */
+ if ((out_ROI_width == 1 && src_ROI_width < 4) ||
+ (src_ROI_width < 4 * out_ROI_width - 3))
+ scale_unit_sel_x = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_x = 1;/* use M/N scalar */
+
+ if ((out_ROI_height == 1 && src_ROI_height < 4) ||
+ (src_ROI_height < 4 * out_ROI_height - 3))
+ scale_unit_sel_y = 0;/* use FIR scalar */
+ else
+ scale_unit_sel_y = 1;/* use M/N scalar */
+
+ /* calculate phase step for the x direction */
+
+ /*
+ * if destination is only 1 pix wide, the value of
+ * phase_step_x is unimportant. Assigning phase_step_x to src
+ * ROI width as an arbitrary value.
+ */
+ if (out_ROI_width == 1)
+ phase_step_x = (uint32_t) ((src_ROI_width) <<
+ SCALER_PHASE_BITS);
+
+ /* if using FIR scalar */
+ else if (scale_unit_sel_x == 0) {
+
+ /*
+ * Calculate the quotient ( src_ROI_width - 1 ) (
+ * out_ROI_width - 1) with u3.29 precision. Quotient
+ * is rounded up to the larger 29th decimal point
+ */
+ numerator = (uint64_t)(src_ROI_width - 1) <<
+ SCALER_PHASE_BITS;
+ /*
+ * never equals to 0 because of the "(out_ROI_width ==
+ * 1 )"
+ */
+ denominator = (uint64_t)(out_ROI_width - 1);
+ /*
+ * divide and round up to the larger 29th decimal
+ * point.
+ */
+ phase_step_x = (uint32_t) vpe_do_div((numerator +
+ denominator - 1), denominator);
+ } else if (scale_unit_sel_x == 1) { /* if M/N scalar */
+ /*
+ * Calculate the quotient ( src_ROI_width ) / (
+ * out_ROI_width) with u3.29 precision. Quotient is
+ * rounded down to the smaller 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_width) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_width);
+ phase_step_x =
+ (uint32_t) vpe_do_div(numerator, denominator);
+ }
+ /* calculate phase step for the y direction */
+
+ /*
+ * if destination is only 1 pix wide, the value of
+ * phase_step_x is unimportant. Assigning phase_step_x to src
+ * ROI width as an arbitrary value.
+ */
+ if (out_ROI_height == 1)
+ phase_step_y =
+ (uint32_t) ((src_ROI_height) << SCALER_PHASE_BITS);
+
+ /* if FIR scalar */
+ else if (scale_unit_sel_y == 0) {
+ /*
+ * Calculate the quotient ( src_ROI_height - 1 ) / (
+ * out_ROI_height - 1) with u3.29 precision. Quotient
+ * is rounded up to the larger 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_height - 1) <<
+ SCALER_PHASE_BITS;
+ /*
+ * never equals to 0 because of the " ( out_ROI_height
+ * == 1 )" case
+ */
+ denominator = (uint64_t)(out_ROI_height - 1);
+ /*
+ * Quotient is rounded up to the larger 29th decimal
+ * point.
+ */
+ phase_step_y =
+ (uint32_t) vpe_do_div(
+ (numerator + denominator - 1), denominator);
+ } else if (scale_unit_sel_y == 1) { /* if M/N scalar */
+ /*
+ * Calculate the quotient ( src_ROI_height ) (
+ * out_ROI_height) with u3.29 precision. Quotient is
+ * rounded down to the smaller 29th decimal point.
+ */
+ numerator = (uint64_t)(src_ROI_height) <<
+ SCALER_PHASE_BITS;
+ denominator = (uint64_t)(out_ROI_height);
+ phase_step_y = (uint32_t) vpe_do_div(
+ numerator, denominator);
+ }
+
+ /* decide which set of FIR coefficients to use */
+ if (phase_step_x > HAL_MDP_PHASE_STEP_2P50)
+ xscale_filter_sel = 0;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P66)
+ xscale_filter_sel = 1;
+ else if (phase_step_x > HAL_MDP_PHASE_STEP_1P25)
+ xscale_filter_sel = 2;
+ else
+ xscale_filter_sel = 3;
+
+ if (phase_step_y > HAL_MDP_PHASE_STEP_2P50)
+ yscale_filter_sel = 0;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P66)
+ yscale_filter_sel = 1;
+ else if (phase_step_y > HAL_MDP_PHASE_STEP_1P25)
+ yscale_filter_sel = 2;
+ else
+ yscale_filter_sel = 3;
+
+ /* calculate phase init for the x direction */
+
+ /* if using FIR scalar */
+ if (scale_unit_sel_x == 0) {
+ if (out_ROI_width == 1)
+ phase_init_x =
+ (uint32_t) ((src_ROI_width - 1) <<
+ SCALER_PHASE_BITS);
+ else
+ phase_init_x = 0;
+ } else if (scale_unit_sel_x == 1) /* M over N scalar */
+ phase_init_x = 0;
+
+ /*
+ * calculate phase init for the y direction if using FIR
+ * scalar
+ */
+ if (scale_unit_sel_y == 0) {
+ if (out_ROI_height == 1)
+ phase_init_y =
+ (uint32_t) ((src_ROI_height -
+ 1) << SCALER_PHASE_BITS);
+ else
+ phase_init_y = 0;
+ } else if (scale_unit_sel_y == 1) /* M over N scalar */
+ phase_init_y = 0;
+
+ strip_info.phase_step_x = phase_step_x;
+ strip_info.phase_step_y = phase_step_y;
+ strip_info.phase_init_x = phase_init_x;
+ strip_info.phase_init_y = phase_init_y;
+ VPE_DBG("phase step x = %d, step y = %d.\n",
+ strip_info.phase_step_x, strip_info.phase_step_y);
+ VPE_DBG("phase init x = %d, init y = %d.\n",
+ strip_info.phase_init_x, strip_info.phase_init_y);
+
+ msm_camera_io_w(strip_info.phase_step_x, vpe_dev->base +
+ VPE_SCALE_PHASEX_STEP_OFFSET);
+ msm_camera_io_w(strip_info.phase_step_y, vpe_dev->base +
+ VPE_SCALE_PHASEY_STEP_OFFSET);
+
+ msm_camera_io_w(strip_info.phase_init_x, vpe_dev->base +
+ VPE_SCALE_PHASEX_INIT_OFFSET);
+ msm_camera_io_w(strip_info.phase_init_y, vpe_dev->base +
+ VPE_SCALE_PHASEY_INIT_OFFSET);
+}
+
+static void vpe_program_buffer_addresses(
+ struct vpe_device *vpe_dev,
+ unsigned long srcP0,
+ unsigned long srcP1,
+ unsigned long outP0,
+ unsigned long outP1)
+{
+ VPE_DBG("%s VPE Configured with:\n"
+ "Src %x, %x Dest %x, %x",
+ __func__, (uint32_t)srcP0, (uint32_t)srcP1,
+ (uint32_t)outP0, (uint32_t)outP1);
+
+ msm_camera_io_w(srcP0, vpe_dev->base + VPE_SRCP0_ADDR_OFFSET);
+ msm_camera_io_w(srcP1, vpe_dev->base + VPE_SRCP1_ADDR_OFFSET);
+ msm_camera_io_w(outP0, vpe_dev->base + VPE_OUTP0_ADDR_OFFSET);
+ msm_camera_io_w(outP1, vpe_dev->base + VPE_OUTP1_ADDR_OFFSET);
+}
+
+static int vpe_start(struct vpe_device *vpe_dev)
+{
+ /* enable the frame irq, bit 0 = Display list 0 ROI done */
+ msm_camera_io_w_mb(1, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ msm_camera_io_dump(vpe_dev->base, 0x120, CONFIG_MSM_VPE_DBG);
+ msm_camera_io_dump(vpe_dev->base + 0x00400, 0x18, CONFIG_MSM_VPE_DBG);
+ msm_camera_io_dump(vpe_dev->base + 0x10000, 0x250, CONFIG_MSM_VPE_DBG);
+ msm_camera_io_dump(vpe_dev->base + 0x30000, 0x20, CONFIG_MSM_VPE_DBG);
+ msm_camera_io_dump(vpe_dev->base + 0x50000, 0x30, CONFIG_MSM_VPE_DBG);
+ msm_camera_io_dump(vpe_dev->base + 0x50400, 0x10, CONFIG_MSM_VPE_DBG);
+
+ /*
+ * This triggers the operation. When the VPE is done,
+ * msm_vpe_irq will fire.
+ */
+ msm_camera_io_w_mb(1, vpe_dev->base + VPE_DL0_START_OFFSET);
+ return 0;
+}
+
+static void vpe_config_axi_default(struct vpe_device *vpe_dev)
+{
+ msm_camera_io_w(0x25, vpe_dev->base + VPE_AXI_ARB_2_OFFSET);
+}
+
+static int vpe_reset(struct vpe_device *vpe_dev)
+{
+ uint32_t vpe_version;
+ uint32_t rc = 0;
+
+ vpe_version = msm_camera_io_r(
+ vpe_dev->base + VPE_HW_VERSION_OFFSET);
+ VPE_DBG("vpe_version = 0x%x\n", vpe_version);
+ /* disable all interrupts.*/
+ msm_camera_io_w(0, vpe_dev->base + VPE_INTR_ENABLE_OFFSET);
+ /* clear all pending interrupts*/
+ msm_camera_io_w(0x1fffff, vpe_dev->base + VPE_INTR_CLEAR_OFFSET);
+ /* write sw_reset to reset the core. */
+ msm_camera_io_w(0x10, vpe_dev->base + VPE_SW_RESET_OFFSET);
+ /* then poll the reset bit, it should be self-cleared. */
+ while (1) {
+ rc = msm_camera_io_r(
+ vpe_dev->base + VPE_SW_RESET_OFFSET) & 0x10;
+ if (rc == 0)
+ break;
+ cpu_relax();
+ }
+ /*
+ * at this point, hardware is reset. Then pogram to default
+ * values.
+ */
+ msm_camera_io_w(VPE_AXI_RD_ARB_CONFIG_VALUE,
+ vpe_dev->base + VPE_AXI_RD_ARB_CONFIG_OFFSET);
+
+ msm_camera_io_w(VPE_CGC_ENABLE_VALUE,
+ vpe_dev->base + VPE_CGC_EN_OFFSET);
+ msm_camera_io_w(1, vpe_dev->base + VPE_CMD_MODE_OFFSET);
+ msm_camera_io_w(VPE_DEFAULT_OP_MODE_VALUE,
+ vpe_dev->base + VPE_OP_MODE_OFFSET);
+ msm_camera_io_w(VPE_DEFAULT_SCALE_CONFIG,
+ vpe_dev->base + VPE_SCALE_CONFIG_OFFSET);
+ vpe_config_axi_default(vpe_dev);
+ return rc;
+}
+
+static int vpe_update_scale_coef(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ uint32_t i, offset;
+
+ offset = *p;
+
+ if (offset > VPE_SCALE_COEFF_MAX_N-VPE_SCALE_COEFF_NUM) {
+ pr_err("%s: invalid offset %d passed in", __func__, offset);
+ return -EINVAL;
+ }
+
+ for (i = offset; i < (VPE_SCALE_COEFF_NUM + offset); i++) {
+ VPE_DBG("Setting scale table %d\n", i);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SCALE_COEFF_LSBn(i));
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SCALE_COEFF_MSBn(i));
+ }
+
+ return 0;
+}
+
+static void vpe_input_plane_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_SRC_FORMAT_OFFSET);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_SRC_UNPACK_PATTERN1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_IMAGE_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_YSTRIDE1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_SRC_XY_OFFSET);
+}
+
+static void vpe_output_plane_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_OUT_FORMAT_OFFSET);
+ msm_camera_io_w(*(++p),
+ vpe_dev->base + VPE_OUT_PACK_PATTERN1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_YSTRIDE1_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_SIZE_OFFSET);
+ msm_camera_io_w(*(++p), vpe_dev->base + VPE_OUT_XY_OFFSET);
+}
+
+static void vpe_operation_config(struct vpe_device *vpe_dev, uint32_t *p)
+{
+ msm_camera_io_w(*p, vpe_dev->base + VPE_OP_MODE_OFFSET);
+}
+
+/**
+ * msm_vpe_transaction_setup() - send setup for one frame to VPE
+ * @vpe_dev: vpe device
+ * @data: packed setup commands
+ *
+ * See msm_vpe.h for the expected format of `data'
+ */
+static void msm_vpe_transaction_setup(struct vpe_device *vpe_dev, void *data)
+{
+ int i, rc = 0;
+ void *iter = data;
+
+ vpe_mem_dump("vpe_transaction", data, VPE_TRANSACTION_SETUP_CONFIG_LEN);
+
+ for (i = 0; i < VPE_NUM_SCALER_TABLES; ++i) {
+ rc = vpe_update_scale_coef(vpe_dev, (uint32_t *)iter);
+ if (rc != 0)
+ return;
+
+ iter += VPE_SCALER_CONFIG_LEN;
+ }
+ vpe_input_plane_config(vpe_dev, (uint32_t *)iter);
+ iter += VPE_INPUT_PLANE_CFG_LEN;
+ vpe_output_plane_config(vpe_dev, (uint32_t *)iter);
+ iter += VPE_OUTPUT_PLANE_CFG_LEN;
+ vpe_operation_config(vpe_dev, (uint32_t *)iter);
+}
+
+static int msm_vpe_send_frame_to_hardware(struct vpe_device *vpe_dev,
+ struct msm_queue_cmd *frame_qcmd)
+{
+ struct msm_vpe_frame_info_t *process_frame;
+
+ if (vpe_dev->processing_q.len < MAX_VPE_PROCESSING_FRAME) {
+ process_frame = frame_qcmd->command;
+ msm_enqueue(&vpe_dev->processing_q,
+ &frame_qcmd->list_frame);
+
+ vpe_update_scaler_params(vpe_dev, process_frame->strip_info);
+ vpe_program_buffer_addresses(
+ vpe_dev,
+ process_frame->src_phyaddr,
+ process_frame->src_phyaddr
+ + process_frame->src_chroma_plane_offset,
+ process_frame->dest_phyaddr,
+ process_frame->dest_phyaddr
+ + process_frame->dest_chroma_plane_offset);
+ vpe_start(vpe_dev);
+ do_gettimeofday(&(process_frame->in_time));
+ }
+ return 0;
+}
+
+static int msm_vpe_cfg(struct vpe_device *vpe_dev,
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+ int rc = 0;
+ struct msm_queue_cmd *frame_qcmd = NULL;
+ struct msm_vpe_frame_info_t *new_frame =
+ kzalloc(sizeof(struct msm_vpe_frame_info_t), GFP_KERNEL);
+ unsigned long in_phyaddr, out_phyaddr;
+ struct msm_buf_mngr_info buff_mgr_info;
+
+ if (!new_frame) {
+ pr_err("Insufficient memory. return\n");
+ return -ENOMEM;
+ }
+
+ rc = copy_from_user(new_frame, (void __user *)ioctl_ptr->ioctl_ptr,
+ sizeof(struct msm_vpe_frame_info_t));
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ in_phyaddr = msm_vpe_fetch_buffer_info(vpe_dev,
+ &new_frame->input_buffer_info,
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF));
+ if (!in_phyaddr) {
+ pr_err("error gettting input physical address\n");
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ memset(&new_frame->output_buffer_info, 0,
+ sizeof(struct msm_vpe_buffer_info_t));
+ memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+ buff_mgr_info.session_id = ((new_frame->identity >> 16) & 0xFFFF);
+ buff_mgr_info.stream_id = (new_frame->identity & 0xFFFF);
+ buff_mgr_info.type = MSM_CAMERA_BUF_MNGR_BUF_PLANAR;
+ rc = msm_vpe_buffer_ops(vpe_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
+ &buff_mgr_info);
+ if (rc < 0) {
+ pr_err("error getting buffer\n");
+ rc = -EINVAL;
+ goto err_free_new_frame;
+ }
+
+ new_frame->output_buffer_info.index = buff_mgr_info.index;
+ out_phyaddr = msm_vpe_fetch_buffer_info(vpe_dev,
+ &new_frame->output_buffer_info,
+ ((new_frame->identity >> 16) & 0xFFFF),
+ (new_frame->identity & 0xFFFF));
+ if (!out_phyaddr) {
+ pr_err("error gettting output physical address\n");
+ rc = -EINVAL;
+ goto err_put_buf;
+ }
+
+ new_frame->src_phyaddr = in_phyaddr;
+ new_frame->dest_phyaddr = out_phyaddr;
+
+ frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+ if (!frame_qcmd) {
+ rc = -ENOMEM;
+ goto err_put_buf;
+ }
+
+ atomic_set(&frame_qcmd->on_heap, 1);
+ frame_qcmd->command = new_frame;
+ rc = msm_vpe_send_frame_to_hardware(vpe_dev, frame_qcmd);
+ if (rc < 0) {
+ pr_err("error cannot send frame to hardware\n");
+ rc = -EINVAL;
+ goto err_free_frame_qcmd;
+ }
+
+ return rc;
+
+err_free_frame_qcmd:
+ kfree(frame_qcmd);
+err_put_buf:
+ msm_vpe_buffer_ops(vpe_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ &buff_mgr_info);
+err_free_new_frame:
+ kfree(new_frame);
+ return rc;
+}
+
+static long msm_vpe_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ int rc = 0;
+
+ mutex_lock(&vpe_dev->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_VPE_TRANSACTION_SETUP: {
+ struct msm_vpe_transaction_setup_cfg *cfg;
+
+ VPE_DBG("VIDIOC_MSM_VPE_TRANSACTION_SETUP\n");
+ if (sizeof(*cfg) != ioctl_ptr->len) {
+ pr_err("%s: size mismatch cmd=%d, len=%zu, expected=%zu",
+ __func__, cmd, ioctl_ptr->len,
+ sizeof(*cfg));
+ rc = -EINVAL;
+ break;
+ }
+
+ cfg = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!cfg) {
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(cfg, (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(cfg);
+ break;
+ }
+
+ msm_vpe_transaction_setup(vpe_dev, (void *)cfg);
+ kfree(cfg);
+ break;
+ }
+ case VIDIOC_MSM_VPE_CFG: {
+ VPE_DBG("VIDIOC_MSM_VPE_CFG\n");
+ rc = msm_vpe_cfg(vpe_dev, ioctl_ptr);
+ break;
+ }
+ case VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO: {
+ struct msm_vpe_stream_buff_info_t *u_stream_buff_info;
+ struct msm_vpe_stream_buff_info_t k_stream_buff_info;
+
+ VPE_DBG("VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO\n");
+
+ if (sizeof(struct msm_vpe_stream_buff_info_t) !=
+ ioctl_ptr->len) {
+ pr_err("%s:%d: invalid length\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ u_stream_buff_info = kzalloc(ioctl_ptr->len, GFP_KERNEL);
+ if (!u_stream_buff_info) {
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(u_stream_buff_info,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ if ((u_stream_buff_info->num_buffs == 0) ||
+ (u_stream_buff_info->num_buffs >
+ MSM_CAMERA_MAX_STREAM_BUF)) {
+ pr_err("%s:%d: Invalid number of buffers\n", __func__,
+ __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+ k_stream_buff_info.num_buffs = u_stream_buff_info->num_buffs;
+ k_stream_buff_info.identity = u_stream_buff_info->identity;
+ k_stream_buff_info.buffer_info =
+ kzalloc(k_stream_buff_info.num_buffs *
+ sizeof(struct msm_vpe_buffer_info_t), GFP_KERNEL);
+ if (!k_stream_buff_info.buffer_info) {
+ pr_err("%s:%d: malloc error\n", __func__, __LINE__);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(k_stream_buff_info.buffer_info,
+ (void __user *)u_stream_buff_info->buffer_info,
+ k_stream_buff_info.num_buffs *
+ sizeof(struct msm_vpe_buffer_info_t)) ?
+ -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = msm_vpe_add_buff_queue_entry(vpe_dev,
+ ((k_stream_buff_info.identity >> 16) & 0xFFFF),
+ (k_stream_buff_info.identity & 0xFFFF));
+ if (!rc)
+ rc = msm_vpe_enqueue_buff_info_list(vpe_dev,
+ &k_stream_buff_info);
+
+ kfree(k_stream_buff_info.buffer_info);
+ kfree(u_stream_buff_info);
+ break;
+ }
+ case VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO: {
+ uint32_t identity;
+ struct msm_vpe_buff_queue_info_t *buff_queue_info;
+
+ VPE_DBG("VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO\n");
+ if (ioctl_ptr->len != sizeof(uint32_t)) {
+ pr_err("%s:%d Invalid len\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(&identity,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ pr_err("%s:%d copy from user\n", __func__, __LINE__);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ buff_queue_info = msm_vpe_get_buff_queue_entry(vpe_dev,
+ ((identity >> 16) & 0xFFFF), (identity & 0xFFFF));
+ if (buff_queue_info == NULL) {
+ pr_err("error finding buffer queue entry for identity:%d\n",
+ identity);
+ mutex_unlock(&vpe_dev->mutex);
+ return -EINVAL;
+ }
+
+ msm_vpe_dequeue_buff_info_list(vpe_dev, buff_queue_info);
+ rc = msm_vpe_free_buff_queue_entry(vpe_dev,
+ buff_queue_info->session_id,
+ buff_queue_info->stream_id);
+ break;
+ }
+ case VIDIOC_MSM_VPE_GET_EVENTPAYLOAD: {
+ struct msm_device_queue *queue = &vpe_dev->eventData_q;
+ struct msm_queue_cmd *event_qcmd;
+ struct msm_vpe_frame_info_t *process_frame;
+
+ VPE_DBG("VIDIOC_MSM_VPE_GET_EVENTPAYLOAD\n");
+ event_qcmd = msm_dequeue(queue, list_eventdata);
+ if (!event_qcmd) {
+ pr_err("%s: %d event_qcmd is NULL\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ process_frame = event_qcmd->command;
+ VPE_DBG("fid %d\n", process_frame->frame_id);
+ if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+ process_frame,
+ sizeof(struct msm_vpe_frame_info_t))) {
+ mutex_unlock(&vpe_dev->mutex);
+ kfree(process_frame);
+ kfree(event_qcmd);
+ return -EINVAL;
+ }
+
+ kfree(process_frame);
+ kfree(event_qcmd);
+ break;
+ }
+ }
+ mutex_unlock(&vpe_dev->mutex);
+ return rc;
+}
+
+static int msm_vpe_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_subscribe(fh, sub, MAX_VPE_V4l2_EVENTS, NULL);
+}
+
+static int msm_vpe_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static struct v4l2_subdev_core_ops msm_vpe_subdev_core_ops = {
+ .ioctl = msm_vpe_subdev_ioctl,
+ .subscribe_event = msm_vpe_subscribe_event,
+ .unsubscribe_event = msm_vpe_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_ops msm_vpe_subdev_ops = {
+ .core = &msm_vpe_subdev_core_ops,
+};
+
+static struct v4l2_file_operations msm_vpe_v4l2_subdev_fops;
+
+static long msm_vpe_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+
+ switch (cmd) {
+ case VIDIOC_DQEVENT:
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+ case VIDIOC_MSM_VPE_GET_INST_INFO: {
+ uint32_t i;
+ struct vpe_device *vpe_dev = v4l2_get_subdevdata(sd);
+ struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+ struct msm_vpe_frame_info_t inst_info;
+
+ memset(&inst_info, 0, sizeof(struct msm_vpe_frame_info_t));
+ for (i = 0; i < MAX_ACTIVE_VPE_INSTANCE; i++) {
+ if (vpe_dev->vpe_subscribe_list[i].vfh == vfh) {
+ inst_info.inst_id = i;
+ break;
+ }
+ }
+ if (copy_to_user(
+ (void __user *)ioctl_ptr->ioctl_ptr, &inst_info,
+ sizeof(struct msm_vpe_frame_info_t))) {
+ return -EINVAL;
+ }
+ }
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+
+ return 0;
+}
+
+static long msm_vpe_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_vpe_subdev_do_ioctl);
+}
+
+static int vpe_register_domain(void)
+{
+ struct msm_iova_partition vpe_iommu_partition = {
+ /* TODO: verify that these are correct? */
+ .start = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ };
+ struct msm_iova_layout vpe_iommu_layout = {
+ .partitions = &vpe_iommu_partition,
+ .npartitions = 1,
+ .client_name = "camera_vpe",
+ .domain_flags = 0,
+ };
+
+ return msm_register_domain(&vpe_iommu_layout);
+}
+
+static int vpe_probe(struct platform_device *pdev)
+{
+ struct vpe_device *vpe_dev;
+ int rc = 0;
+
+ vpe_dev = kzalloc(sizeof(struct vpe_device), GFP_KERNEL);
+ if (!vpe_dev)
+ return -ENOMEM;
+
+ vpe_dev->vpe_clk = kzalloc(sizeof(struct clk *) *
+ ARRAY_SIZE(vpe_clk_info), GFP_KERNEL);
+ if (!vpe_dev->vpe_clk) {
+ rc = -ENOMEM;
+ goto err_free_vpe_dev;
+ }
+
+ v4l2_subdev_init(&vpe_dev->msm_sd.sd, &msm_vpe_subdev_ops);
+ vpe_dev->msm_sd.sd.internal_ops = &msm_vpe_internal_ops;
+ snprintf(vpe_dev->msm_sd.sd.name, ARRAY_SIZE(vpe_dev->msm_sd.sd.name),
+ "vpe");
+ vpe_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vpe_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&vpe_dev->msm_sd.sd, vpe_dev);
+ platform_set_drvdata(pdev, &vpe_dev->msm_sd.sd);
+ mutex_init(&vpe_dev->mutex);
+ spin_lock_init(&vpe_dev->tasklet_lock);
+
+ vpe_dev->pdev = pdev;
+
+ vpe_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "vpe");
+ if (!vpe_dev->mem) {
+ pr_err("no mem resource?\n");
+ rc = -ENODEV;
+ goto err_free_vpe_clk;
+ }
+
+ vpe_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "vpe");
+ if (!vpe_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->domain_num = vpe_register_domain();
+ if (vpe_dev->domain_num < 0) {
+ pr_err("%s: could not register domain\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->domain =
+ msm_get_iommu_domain(vpe_dev->domain_num);
+ if (!vpe_dev->domain) {
+ pr_err("%s: cannot find domain\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ vpe_dev->iommu_ctx_src = msm_iommu_get_ctx("vpe_src");
+ vpe_dev->iommu_ctx_dst = msm_iommu_get_ctx("vpe_dst");
+ if (!vpe_dev->iommu_ctx_src || !vpe_dev->iommu_ctx_dst) {
+ pr_err("%s: cannot get iommu_ctx\n", __func__);
+ rc = -ENODEV;
+ goto err_release_mem;
+ }
+
+ media_entity_init(&vpe_dev->msm_sd.sd.entity, 0, NULL, 0);
+ vpe_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ vpe_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_VPE;
+ vpe_dev->msm_sd.sd.entity.name = pdev->name;
+ msm_sd_register(&vpe_dev->msm_sd);
+ msm_cam_copy_v4l2_subdev_fops(&msm_vpe_v4l2_subdev_fops);
+ vpe_dev->msm_sd.sd.devnode->fops = &msm_vpe_v4l2_subdev_fops;
+ vpe_dev->msm_sd.sd.entity.revision = vpe_dev->msm_sd.sd.devnode->num;
+ vpe_dev->state = VPE_STATE_BOOT;
+ rc = vpe_init_hardware(vpe_dev);
+ if (rc < 0) {
+ pr_err("%s: Couldn't init vpe hardware\n", __func__);
+ goto err_unregister_sd;
+ }
+ vpe_reset(vpe_dev);
+ vpe_release_hardware(vpe_dev);
+ vpe_dev->state = VPE_STATE_OFF;
+
+ rc = iommu_attach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+ if (rc < 0) {
+ pr_err("Couldn't attach to vpe_src context bank\n");
+ rc = -ENODEV;
+ goto err_unregister_sd;
+ }
+ rc = iommu_attach_device(vpe_dev->domain, vpe_dev->iommu_ctx_dst);
+ if (rc < 0) {
+ pr_err("Couldn't attach to vpe_dst context bank\n");
+ rc = -ENODEV;
+ goto err_detach_src;
+ }
+
+ vpe_dev->state = VPE_STATE_OFF;
+
+ msm_queue_init(&vpe_dev->eventData_q, "vpe-eventdata");
+ msm_queue_init(&vpe_dev->processing_q, "vpe-frame");
+ INIT_LIST_HEAD(&vpe_dev->tasklet_q);
+ tasklet_init(&vpe_dev->vpe_tasklet, msm_vpe_do_tasklet,
+ (unsigned long)vpe_dev);
+ vpe_dev->vpe_open_cnt = 0;
+
+ return rc;
+
+err_detach_src:
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+err_unregister_sd:
+ msm_sd_unregister(&vpe_dev->msm_sd);
+err_release_mem:
+ release_mem_region(vpe_dev->mem->start, resource_size(vpe_dev->mem));
+err_free_vpe_clk:
+ kfree(vpe_dev->vpe_clk);
+err_free_vpe_dev:
+ kfree(vpe_dev);
+ return rc;
+}
+
+static int vpe_device_remove(struct platform_device *dev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(dev);
+ struct vpe_device *vpe_dev;
+
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ vpe_dev = (struct vpe_device *)v4l2_get_subdevdata(sd);
+ if (!vpe_dev) {
+ pr_err("%s: vpe device is NULL\n", __func__);
+ return 0;
+ }
+
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_dst);
+ iommu_detach_device(vpe_dev->domain, vpe_dev->iommu_ctx_src);
+ msm_sd_unregister(&vpe_dev->msm_sd);
+ release_mem_region(vpe_dev->mem->start, resource_size(vpe_dev->mem));
+ mutex_destroy(&vpe_dev->mutex);
+ kfree(vpe_dev);
+ return 0;
+}
+
+static struct platform_driver vpe_driver = {
+ .probe = vpe_probe,
+ .remove = vpe_device_remove,
+ .driver = {
+ .name = MSM_VPE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_vpe_init_module(void)
+{
+ return platform_driver_register(&vpe_driver);
+}
+
+static void __exit msm_vpe_exit_module(void)
+{
+ platform_driver_unregister(&vpe_driver);
+}
+
+module_init(msm_vpe_init_module);
+module_exit(msm_vpe_exit_module);
+MODULE_DESCRIPTION("MSM VPE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h
new file mode 100644
index 0000000..80be8db
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.h
@@ -0,0 +1,259 @@
+/* Copyright (c) 2013-2014, 2016, 2018 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_VPE_H__
+#define __MSM_VPE_H__
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-subdev.h>
+#include "msm_sd.h"
+
+/*********** start of register offset *********************/
+#define VPE_INTR_ENABLE_OFFSET 0x0020
+#define VPE_INTR_STATUS_OFFSET 0x0024
+#define VPE_INTR_CLEAR_OFFSET 0x0028
+#define VPE_DL0_START_OFFSET 0x0030
+#define VPE_HW_VERSION_OFFSET 0x0070
+#define VPE_SW_RESET_OFFSET 0x0074
+#define VPE_AXI_RD_ARB_CONFIG_OFFSET 0x0078
+#define VPE_SEL_CLK_OR_HCLK_TEST_BUS_OFFSET 0x007C
+#define VPE_CGC_EN_OFFSET 0x0100
+#define VPE_CMD_STATUS_OFFSET 0x10008
+#define VPE_PROFILE_EN_OFFSET 0x10010
+#define VPE_PROFILE_COUNT_OFFSET 0x10014
+#define VPE_CMD_MODE_OFFSET 0x10060
+#define VPE_SRC_SIZE_OFFSET 0x10108
+#define VPE_SRCP0_ADDR_OFFSET 0x1010C
+#define VPE_SRCP1_ADDR_OFFSET 0x10110
+#define VPE_SRC_YSTRIDE1_OFFSET 0x1011C
+#define VPE_SRC_FORMAT_OFFSET 0x10124
+#define VPE_SRC_UNPACK_PATTERN1_OFFSET 0x10128
+#define VPE_OP_MODE_OFFSET 0x10138
+#define VPE_SCALE_PHASEX_INIT_OFFSET 0x1013C
+#define VPE_SCALE_PHASEY_INIT_OFFSET 0x10140
+#define VPE_SCALE_PHASEX_STEP_OFFSET 0x10144
+#define VPE_SCALE_PHASEY_STEP_OFFSET 0x10148
+#define VPE_OUT_FORMAT_OFFSET 0x10150
+#define VPE_OUT_PACK_PATTERN1_OFFSET 0x10154
+#define VPE_OUT_SIZE_OFFSET 0x10164
+#define VPE_OUTP0_ADDR_OFFSET 0x10168
+#define VPE_OUTP1_ADDR_OFFSET 0x1016C
+#define VPE_OUT_YSTRIDE1_OFFSET 0x10178
+#define VPE_OUT_XY_OFFSET 0x1019C
+#define VPE_SRC_XY_OFFSET 0x10200
+#define VPE_SRC_IMAGE_SIZE_OFFSET 0x10208
+#define VPE_SCALE_CONFIG_OFFSET 0x10230
+#define VPE_DEINT_STATUS_OFFSET 0x30000
+#define VPE_DEINT_DECISION_OFFSET 0x30004
+#define VPE_DEINT_COEFF0_OFFSET 0x30010
+#define VPE_SCALE_STATUS_OFFSET 0x50000
+#define VPE_SCALE_SVI_PARAM_OFFSET 0x50010
+#define VPE_SCALE_SHARPEN_CFG_OFFSET 0x50020
+#define VPE_SCALE_COEFF_LSP_0_OFFSET 0x50400
+#define VPE_SCALE_COEFF_MSP_0_OFFSET 0x50404
+
+#define VPE_AXI_ARB_1_OFFSET 0x00408
+#define VPE_AXI_ARB_2_OFFSET 0x0040C
+
+#define VPE_SCALE_COEFF_LSBn(n) (0x50400 + 8 * (n))
+#define VPE_SCALE_COEFF_MSBn(n) (0x50404 + 8 * (n))
+#define VPE_SCALE_COEFF_NUM 32
+#define VPE_SCALE_COEFF_MAX_N 127
+
+/*********** end of register offset ********************/
+
+
+#define VPE_HARDWARE_VERSION 0x00080308
+#define VPE_SW_RESET_VALUE 0x00000010 /* bit 4 for PPP*/
+#define VPE_AXI_RD_ARB_CONFIG_VALUE 0x124924
+#define VPE_CMD_MODE_VALUE 0x1
+#define VPE_DEFAULT_OP_MODE_VALUE 0x40FC0004
+#define VPE_CGC_ENABLE_VALUE 0xffff
+#define VPE_DEFAULT_SCALE_CONFIG 0x3c
+
+#define VPE_NORMAL_MODE_CLOCK_RATE 150000000
+#define VPE_TURBO_MODE_CLOCK_RATE 200000000
+#define VPE_SUBDEV_MAX_EVENTS 30
+
+/**************************************************/
+/*********** End of command id ********************/
+/**************************************************/
+
+#define SCALER_PHASE_BITS 29
+#define HAL_MDP_PHASE_STEP_2P50 0x50000000
+#define HAL_MDP_PHASE_STEP_1P66 0x35555555
+#define HAL_MDP_PHASE_STEP_1P25 0x28000000
+
+
+#define MAX_ACTIVE_VPE_INSTANCE 8
+#define MAX_VPE_PROCESSING_FRAME 2
+#define MAX_VPE_V4l2_EVENTS 30
+
+#define MSM_VPE_TASKLETQ_SIZE 16
+
+/**
+ * The format of the msm_vpe_transaction_setup_cfg is as follows:
+ *
+ * - vpe_update_scale_coef (65*4 uint32_t's)
+ * - Each table is 65 uint32_t's long
+ * - 1st uint32_t in each table indicates offset
+ * - Following 64 uint32_t's are the data
+ *
+ * - vpe_input_plane_config (6 uint32_t's)
+ * - VPE_SRC_FORMAT_OFFSET
+ * - VPE_SRC_UNPACK_PATTERN1_OFFSET
+ * - VPE_SRC_IMAGE_SIZE_OFFSET
+ * - VPE_SRC_YSTRIDE1_OFFSET
+ * - VPE_SRC_SIZE_OFFSET
+ * - VPE_SRC_XY_OFFSET
+ *
+ * - vpe_output_plane_config (5 uint32_t's)
+ * - VPE_OUT_FORMAT_OFFSET
+ * - VPE_OUT_PACK_PATTERN1_OFFSET
+ * - VPE_OUT_YSTRIDE1_OFFSET
+ * - VPE_OUT_SIZE_OFFSET
+ * - VPE_OUT_XY_OFFSET
+ *
+ * - vpe_operation_config (1 uint32_t)
+ * - VPE_OP_MODE_OFFSET
+ *
+ */
+
+#define VPE_SCALER_CONFIG_LEN 260
+#define VPE_INPUT_PLANE_CFG_LEN 24
+#define VPE_OUTPUT_PLANE_CFG_LEN 20
+#define VPE_OPERATION_MODE_CFG_LEN 4
+#define VPE_NUM_SCALER_TABLES 4
+
+#define VPE_TRANSACTION_SETUP_CONFIG_LEN ( \
+ (VPE_SCALER_CONFIG_LEN * VPE_NUM_SCALER_TABLES) \
+ + VPE_INPUT_PLANE_CFG_LEN \
+ + VPE_OUTPUT_PLANE_CFG_LEN \
+ + VPE_OPERATION_MODE_CFG_LEN)
+/* VPE_TRANSACTION_SETUP_CONFIG_LEN = 1088 */
+
+struct msm_vpe_transaction_setup_cfg {
+ uint8_t scaler_cfg[VPE_TRANSACTION_SETUP_CONFIG_LEN];
+};
+
+struct vpe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum vpe_state {
+ VPE_STATE_BOOT,
+ VPE_STATE_IDLE,
+ VPE_STATE_ACTIVE,
+ VPE_STATE_OFF,
+};
+
+struct msm_queue_cmd {
+ struct list_head list_config;
+ struct list_head list_control;
+ struct list_head list_frame;
+ struct list_head list_pict;
+ struct list_head list_vpe_frame;
+ struct list_head list_eventdata;
+ void *command;
+ atomic_t on_heap;
+ struct timespec ts;
+ uint32_t error_code;
+ uint32_t trans_code;
+};
+
+struct msm_device_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int max;
+ int len;
+ const char *name;
+};
+
+struct msm_vpe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t irq_status;
+ uint8_t cmd_used;
+};
+
+struct msm_vpe_buffer_map_info_t {
+ unsigned long len;
+ dma_addr_t phy_addr;
+ struct dma_buf *dbuf;
+ struct dma_buf_attachment *attachment;
+ struct sg_table *table;
+ struct msm_vpe_buffer_info_t buff_info;
+};
+
+struct msm_vpe_buffer_map_list_t {
+ struct msm_vpe_buffer_map_info_t map_info;
+ struct list_head entry;
+};
+
+struct msm_vpe_buff_queue_info_t {
+ uint32_t used;
+ uint16_t session_id;
+ uint16_t stream_id;
+ struct list_head vb2_buff_head;
+ struct list_head native_buff_head;
+};
+
+struct vpe_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ void __iomem *base;
+ struct clk **vpe_clk;
+ struct regulator *fs_vpe;
+ struct mutex mutex;
+ enum vpe_state state;
+
+ int domain_num;
+ struct iommu_domain *domain;
+ struct device *iommu_ctx_src;
+ struct device *iommu_ctx_dst;
+ struct ion_client *client;
+ struct kref refcount;
+
+ /* Reusing proven tasklet from msm isp */
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct vpe_tasklet;
+ struct msm_vpe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VPE_TASKLETQ_SIZE];
+
+ struct vpe_subscribe_info vpe_subscribe_list[MAX_ACTIVE_VPE_INSTANCE];
+ uint32_t vpe_open_cnt;
+
+ struct msm_device_queue eventData_q; /* V4L2 Event Payload Queue */
+
+ /*
+ * Processing Queue: store frame info for frames sent to
+ * microcontroller
+ */
+ struct msm_device_queue processing_q;
+
+ struct msm_vpe_buff_queue_info_t *buff_queue;
+ uint32_t num_buffq;
+ struct v4l2_subdev *buf_mgr_subdev;
+};
+
+#endif /* __MSM_VPE_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
new file mode 100644
index 0000000..b04560f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -0,0 +1,9 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_led/ ir_cut/
+obj-$(CONFIG_MSMB_CAMERA) += laser_led/
+obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/Makefile b/drivers/media/platform/msm/camera_v2/sensor/actuator/Makefile
new file mode 100644
index 0000000..37dda0f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += msm_actuator.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
new file mode 100644
index 0000000..295e203
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -0,0 +1,2131 @@
+/* Copyright (c) 2011-2018, 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) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include "msm_sd.h"
+#include "msm_actuator.h"
+#include "msm_cci.h"
+
+DEFINE_MSM_MUTEX(msm_actuator_mutex);
+
+#undef CDBG
+#ifdef MSM_ACTUATOR_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define PARK_LENS_LONG_STEP 7
+#define PARK_LENS_MID_STEP 5
+#define PARK_LENS_SMALL_STEP 3
+#define MAX_QVALUE 4096
+
+static struct v4l2_file_operations msm_actuator_v4l2_subdev_fops;
+static int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl);
+static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl);
+
+static struct msm_actuator msm_vcm_actuator_table;
+static struct msm_actuator msm_piezo_actuator_table;
+static struct msm_actuator msm_hvcm_actuator_table;
+static struct msm_actuator msm_bivcm_actuator_table;
+
+static struct i2c_driver msm_actuator_i2c_driver;
+static struct msm_actuator *actuators[] = {
+ &msm_vcm_actuator_table,
+ &msm_piezo_actuator_table,
+ &msm_hvcm_actuator_table,
+ &msm_bivcm_actuator_table,
+};
+
+static int32_t msm_actuator_piezo_set_default_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_move_params_t *move_params)
+{
+ int32_t rc = 0;
+ struct msm_camera_i2c_reg_setting reg_setting;
+
+ CDBG("Enter\n");
+
+ if (a_ctrl->curr_step_pos != 0) {
+ a_ctrl->i2c_tbl_index = 0;
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ a_ctrl->initial_code, 0, 0);
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ a_ctrl->initial_code, 0, 0);
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ rc = a_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("%s: i2c write error:%d\n",
+ __func__, rc);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ a_ctrl->curr_step_pos = 0;
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl,
+ int16_t next_lens_position, uint32_t hw_params, uint16_t delay)
+{
+ struct msm_actuator_reg_params_t *write_arr = NULL;
+ uint32_t hw_dword = hw_params;
+ uint16_t i2c_byte1 = 0, i2c_byte2 = 0;
+ uint16_t value = 0;
+ uint32_t size = 0, i = 0;
+ struct msm_camera_i2c_reg_array *i2c_tbl = NULL;
+
+ CDBG("Enter\n");
+
+ if (a_ctrl == NULL) {
+ pr_err("failed. actuator ctrl is NULL");
+ return;
+ }
+
+ if (a_ctrl->i2c_reg_tbl == NULL) {
+ pr_err("failed. i2c reg tabl is NULL");
+ return;
+ }
+
+ size = a_ctrl->reg_tbl_size;
+ write_arr = a_ctrl->reg_tbl;
+ i2c_tbl = a_ctrl->i2c_reg_tbl;
+
+ for (i = 0; i < size; i++) {
+ if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) {
+ value = (next_lens_position <<
+ write_arr[i].data_shift) |
+ ((hw_dword & write_arr[i].hw_mask) >>
+ write_arr[i].hw_shift);
+
+ if (write_arr[i].reg_addr != 0xFFFF) {
+ i2c_byte1 = write_arr[i].reg_addr;
+ i2c_byte2 = value;
+ if (size != (i+1)) {
+ i2c_byte2 = value & 0xFF;
+ CDBG("byte1:0x%x, byte2:0x%x\n",
+ i2c_byte1, i2c_byte2);
+ if (a_ctrl->i2c_tbl_index >
+ a_ctrl->total_steps) {
+ pr_err("failed:i2c table index out of bound\n");
+ break;
+ }
+ i2c_tbl[a_ctrl->i2c_tbl_index].
+ reg_addr = i2c_byte1;
+ i2c_tbl[a_ctrl->i2c_tbl_index].
+ reg_data = i2c_byte2;
+ i2c_tbl[a_ctrl->i2c_tbl_index].
+ delay = 0;
+ a_ctrl->i2c_tbl_index++;
+ i++;
+ i2c_byte1 = write_arr[i].reg_addr;
+ i2c_byte2 = (value & 0xFF00) >> 8;
+ }
+ } else {
+ i2c_byte1 = (value & 0xFF00) >> 8;
+ i2c_byte2 = value & 0xFF;
+ }
+ } else {
+ i2c_byte1 = write_arr[i].reg_addr;
+ i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >>
+ write_arr[i].hw_shift;
+ }
+ if (a_ctrl->i2c_tbl_index > a_ctrl->total_steps) {
+ pr_err("failed: i2c table index out of bound\n");
+ break;
+ }
+ CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2);
+ i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1;
+ i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2;
+ i2c_tbl[a_ctrl->i2c_tbl_index].delay = delay;
+ a_ctrl->i2c_tbl_index++;
+ }
+ CDBG("Exit\n");
+}
+
+static int msm_actuator_bivcm_handle_i2c_ops(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ int16_t next_lens_position, uint32_t hw_params, uint16_t delay)
+{
+ struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl;
+ uint32_t hw_dword = hw_params;
+ uint16_t i2c_byte1 = 0, i2c_byte2 = 0;
+ uint16_t value = 0, reg_data = 0;
+ uint32_t size = a_ctrl->reg_tbl_size, i = 0;
+ int32_t rc = 0;
+ struct msm_camera_i2c_reg_array i2c_tbl;
+ struct msm_camera_i2c_reg_setting reg_setting;
+ enum msm_camera_i2c_reg_addr_type save_addr_type =
+ a_ctrl->i2c_client.addr_type;
+
+ for (i = 0; i < size; i++) {
+ reg_setting.size = 1;
+ switch (write_arr[i].reg_write_type) {
+ case MSM_ACTUATOR_WRITE_DAC:
+ value = (next_lens_position <<
+ write_arr[i].data_shift) |
+ ((hw_dword & write_arr[i].hw_mask) >>
+ write_arr[i].hw_shift);
+ if (write_arr[i].reg_addr != 0xFFFF) {
+ i2c_byte1 = write_arr[i].reg_addr;
+ i2c_byte2 = value;
+ } else {
+ i2c_byte1 = (value & 0xFF00) >> 8;
+ i2c_byte2 = value & 0xFF;
+ }
+ i2c_tbl.reg_addr = i2c_byte1;
+ i2c_tbl.reg_data = i2c_byte2;
+ i2c_tbl.delay = delay;
+ a_ctrl->i2c_tbl_index++;
+
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+ rc = a_ctrl->i2c_client.
+ i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ break;
+ case MSM_ACTUATOR_WRITE:
+ i2c_tbl.reg_data = write_arr[i].reg_data;
+ i2c_tbl.reg_addr = write_arr[i].reg_addr;
+ i2c_tbl.delay = write_arr[i].delay;
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = write_arr[i].data_type;
+ switch (write_arr[i].addr_type) {
+ case MSM_ACTUATOR_BYTE_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_BYTE_ADDR;
+ break;
+ case MSM_ACTUATOR_WORD_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_WORD_ADDR;
+ break;
+ default:
+ pr_err("Unsupport addr type: %d\n",
+ write_arr[i].addr_type);
+ break;
+ }
+
+ rc = a_ctrl->i2c_client.
+ i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ break;
+ case MSM_ACTUATOR_WRITE_DIR_REG:
+ i2c_tbl.reg_data = hw_dword & 0xFFFF;
+ i2c_tbl.reg_addr = write_arr[i].reg_addr;
+ i2c_tbl.delay = write_arr[i].delay;
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = write_arr[i].data_type;
+ switch (write_arr[i].addr_type) {
+ case MSM_ACTUATOR_BYTE_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_BYTE_ADDR;
+ break;
+ case MSM_ACTUATOR_WORD_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_WORD_ADDR;
+ break;
+ default:
+ pr_err("Unsupport addr type: %d\n",
+ write_arr[i].addr_type);
+ break;
+ }
+
+ rc = a_ctrl->i2c_client.
+ i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ break;
+ case MSM_ACTUATOR_POLL:
+ switch (write_arr[i].addr_type) {
+ case MSM_ACTUATOR_BYTE_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_BYTE_ADDR;
+ break;
+ case MSM_ACTUATOR_WORD_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_WORD_ADDR;
+ break;
+ default:
+ pr_err("Unsupport addr type: %d\n",
+ write_arr[i].addr_type);
+ break;
+ }
+
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &a_ctrl->i2c_client,
+ write_arr[i].reg_addr,
+ write_arr[i].reg_data,
+ write_arr[i].data_type,
+ write_arr[i].delay);
+ if (rc < 0) {
+ pr_err("i2c poll error:%d\n", rc);
+ return rc;
+ }
+ break;
+ case MSM_ACTUATOR_READ_WRITE:
+ i2c_tbl.reg_addr = write_arr[i].reg_addr;
+ i2c_tbl.delay = write_arr[i].delay;
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = write_arr[i].data_type;
+
+ switch (write_arr[i].addr_type) {
+ case MSM_ACTUATOR_BYTE_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_BYTE_ADDR;
+ break;
+ case MSM_ACTUATOR_WORD_ADDR:
+ a_ctrl->i2c_client.addr_type =
+ MSM_CAMERA_I2C_WORD_ADDR;
+ break;
+ default:
+ pr_err("Unsupport addr type: %d\n",
+ write_arr[i].addr_type);
+ break;
+ }
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_read(
+ &a_ctrl->i2c_client,
+ write_arr[i].reg_addr,
+ ®_data,
+ write_arr[i].data_type);
+ if (rc < 0) {
+ pr_err("i2c poll error:%d\n", rc);
+ return rc;
+ }
+
+ i2c_tbl.reg_addr = write_arr[i].reg_data;
+ i2c_tbl.reg_data = reg_data;
+ i2c_tbl.delay = write_arr[i].delay;
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = write_arr[i].data_type;
+
+ rc = a_ctrl->i2c_client.
+ i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ break;
+ case MSM_ACTUATOR_WRITE_HW_DAMP:
+ i2c_tbl.reg_addr = write_arr[i].reg_addr;
+ i2c_tbl.reg_data = (hw_dword & write_arr[i].hw_mask) >>
+ write_arr[i].hw_shift;
+ i2c_tbl.delay = 0;
+ reg_setting.reg_setting = &i2c_tbl;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+
+ rc = a_ctrl->i2c_client.
+ i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ break;
+ default:
+ pr_err("%s:%d Invalid selection\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ a_ctrl->i2c_client.addr_type = save_addr_type;
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl,
+ uint16_t size, struct reg_settings_t *settings)
+{
+ int32_t rc = -EFAULT;
+ int32_t i = 0;
+ enum msm_camera_i2c_reg_addr_type save_addr_type;
+
+ CDBG("Enter\n");
+
+ save_addr_type = a_ctrl->i2c_client.addr_type;
+ for (i = 0; i < size; i++) {
+
+ switch (settings[i].addr_type) {
+ case MSM_CAMERA_I2C_BYTE_ADDR:
+ a_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
+ break;
+ case MSM_CAMERA_I2C_WORD_ADDR:
+ a_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR;
+ break;
+ default:
+ pr_err("Unsupport addr type: %d\n",
+ settings[i].addr_type);
+ break;
+ }
+
+ switch (settings[i].i2c_operation) {
+ case MSM_ACT_WRITE:
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &a_ctrl->i2c_client,
+ settings[i].reg_addr,
+ settings[i].reg_data,
+ settings[i].data_type);
+ if (settings[i].delay > 20)
+ msleep(settings[i].delay);
+ else if (settings[i].delay != 0)
+ usleep_range(settings[i].delay * 1000,
+ (settings[i].delay * 1000) + 1000);
+ break;
+ case MSM_ACT_POLL:
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &a_ctrl->i2c_client,
+ settings[i].reg_addr,
+ settings[i].reg_data,
+ settings[i].data_type,
+ settings[i].delay);
+ break;
+ default:
+ pr_err("Unsupport i2c_operation: %d\n",
+ settings[i].i2c_operation);
+ break;
+ }
+
+ if (rc < 0) {
+ pr_err("%s:%d fail addr = 0X%X, data = 0X%X, dt = %d",
+ __func__, __LINE__, settings[i].reg_addr,
+ settings[i].reg_data, settings[i].data_type);
+ break;
+ }
+ }
+
+ a_ctrl->curr_step_pos = 0;
+ /*
+ * Recover register addr_type after the init
+ * settings are written.
+ */
+ a_ctrl->i2c_client.addr_type = save_addr_type;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static void msm_actuator_write_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ uint16_t curr_lens_pos,
+ struct damping_params_t *damping_params,
+ int8_t sign_direction,
+ int16_t code_boundary)
+{
+ int16_t next_lens_pos = 0;
+ uint16_t damping_code_step = 0;
+ uint16_t wait_time = 0;
+
+ CDBG("Enter\n");
+
+ damping_code_step = damping_params->damping_step;
+ wait_time = damping_params->damping_delay;
+
+ /* Write code based on damping_code_step in a loop */
+ for (next_lens_pos =
+ curr_lens_pos + (sign_direction * damping_code_step);
+ (sign_direction * next_lens_pos) <=
+ (sign_direction * code_boundary);
+ next_lens_pos =
+ (next_lens_pos +
+ (sign_direction * damping_code_step))) {
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ next_lens_pos, damping_params->hw_params, wait_time);
+ curr_lens_pos = next_lens_pos;
+ }
+
+ if (curr_lens_pos != code_boundary) {
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ code_boundary, damping_params->hw_params, wait_time);
+ }
+ CDBG("Exit\n");
+}
+
+static int msm_actuator_bivcm_write_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ uint16_t curr_lens_pos,
+ struct damping_params_t *damping_params,
+ int8_t sign_direction,
+ int16_t code_boundary)
+{
+ int16_t next_lens_pos = 0;
+ uint16_t damping_code_step = 0;
+ uint16_t wait_time = 0;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ damping_code_step = damping_params->damping_step;
+ wait_time = damping_params->damping_delay;
+
+ /* Write code based on damping_code_step in a loop */
+ for (next_lens_pos =
+ curr_lens_pos + (sign_direction * damping_code_step);
+ (sign_direction * next_lens_pos) <=
+ (sign_direction * code_boundary);
+ next_lens_pos =
+ (next_lens_pos +
+ (sign_direction * damping_code_step))) {
+ rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl,
+ next_lens_pos, damping_params->hw_params, wait_time);
+ if (rc < 0) {
+ pr_err("%s:%d msm_actuator_bivcm_handle_i2c_ops failed\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ curr_lens_pos = next_lens_pos;
+ }
+
+ if (curr_lens_pos != code_boundary) {
+ rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl,
+ code_boundary, damping_params->hw_params, wait_time);
+ if (rc < 0) {
+ pr_err("%s:%d msm_actuator_bivcm_handle_i2c_ops failed\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_piezo_move_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_move_params_t *move_params)
+{
+ int32_t dest_step_position = move_params->dest_step_pos;
+ struct damping_params_t ringing_params_kernel;
+ int32_t rc = 0;
+ int32_t num_steps = move_params->num_steps;
+ struct msm_camera_i2c_reg_setting reg_setting;
+
+ CDBG("Enter\n");
+
+ if (copy_from_user(&ringing_params_kernel,
+ &(move_params->ringing_params[0]),
+ sizeof(struct damping_params_t))) {
+ pr_err("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ if (num_steps <= 0 || num_steps > MAX_NUMBER_OF_STEPS) {
+ pr_err("num_steps out of range = %d\n",
+ num_steps);
+ return -EFAULT;
+ }
+
+ if (dest_step_position > a_ctrl->total_steps) {
+ pr_err("Step pos greater than total steps = %d\n",
+ dest_step_position);
+ return -EFAULT;
+ }
+
+ a_ctrl->i2c_tbl_index = 0;
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ (num_steps *
+ a_ctrl->region_params[0].code_per_step),
+ ringing_params_kernel.hw_params, 0);
+
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ a_ctrl->curr_step_pos = dest_step_position;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_move_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_move_params_t *move_params)
+{
+ int32_t rc = 0;
+ struct damping_params_t *ringing_params_kernel = NULL;
+ int8_t sign_dir = move_params->sign_dir;
+ uint16_t step_boundary = 0;
+ uint16_t target_step_pos = 0;
+ uint16_t target_lens_pos = 0;
+ int16_t dest_step_pos = move_params->dest_step_pos;
+ uint16_t curr_lens_pos = 0;
+ int dir = move_params->dir;
+ int32_t num_steps = move_params->num_steps;
+ struct msm_camera_i2c_reg_setting reg_setting;
+
+ CDBG("called, dir %d, num_steps %d\n", dir, num_steps);
+
+ if ((dest_step_pos == a_ctrl->curr_step_pos) ||
+ ((dest_step_pos <= a_ctrl->total_steps) &&
+ (a_ctrl->step_position_table[dest_step_pos] ==
+ a_ctrl->step_position_table[a_ctrl->curr_step_pos])))
+ return rc;
+
+ if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) ||
+ (sign_dir < MSM_ACTUATOR_MOVE_SIGNED_FAR)) {
+ pr_err("Invalid sign_dir = %d\n", sign_dir);
+ return -EFAULT;
+ }
+ if ((dir > MOVE_FAR) || (dir < MOVE_NEAR)) {
+ pr_err("Invalid direction = %d\n", dir);
+ return -EFAULT;
+ }
+ if (dest_step_pos > a_ctrl->total_steps) {
+ pr_err("Step pos greater than total steps = %d\n",
+ dest_step_pos);
+ return -EFAULT;
+ }
+ if ((a_ctrl->region_size <= 0) ||
+ (a_ctrl->region_size > MAX_ACTUATOR_REGION) ||
+ (!move_params->ringing_params)) {
+ pr_err("Invalid-region size = %d, ringing_params = %pK\n",
+ a_ctrl->region_size, move_params->ringing_params);
+ return -EFAULT;
+ }
+ /*Allocate memory for damping parameters of all regions*/
+ ringing_params_kernel = kmalloc(
+ sizeof(struct damping_params_t)*(a_ctrl->region_size),
+ GFP_KERNEL);
+ if (!ringing_params_kernel) {
+ pr_err("kmalloc for damping parameters failed\n");
+ return -EFAULT;
+ }
+ if (copy_from_user(ringing_params_kernel,
+ &(move_params->ringing_params[0]),
+ (sizeof(struct damping_params_t))*(a_ctrl->region_size))) {
+ pr_err("copy_from_user failed\n");
+ /*Free the allocated memory for damping parameters*/
+ kfree(ringing_params_kernel);
+ return -EFAULT;
+ }
+ curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
+ a_ctrl->i2c_tbl_index = 0;
+ CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n",
+ a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos);
+
+ while (a_ctrl->curr_step_pos != dest_step_pos) {
+ step_boundary =
+ a_ctrl->region_params[a_ctrl->curr_region_index].
+ step_bound[dir];
+ if ((dest_step_pos * sign_dir) <=
+ (step_boundary * sign_dir)) {
+
+ target_step_pos = dest_step_pos;
+ target_lens_pos =
+ a_ctrl->step_position_table[target_step_pos];
+ a_ctrl->func_tbl->actuator_write_focus(a_ctrl,
+ curr_lens_pos,
+ &ringing_params_kernel
+ [a_ctrl->curr_region_index],
+ sign_dir,
+ target_lens_pos);
+ curr_lens_pos = target_lens_pos;
+
+ } else {
+ target_step_pos = step_boundary;
+ target_lens_pos =
+ a_ctrl->step_position_table[target_step_pos];
+ a_ctrl->func_tbl->actuator_write_focus(a_ctrl,
+ curr_lens_pos,
+ &ringing_params_kernel
+ [a_ctrl->curr_region_index],
+ sign_dir,
+ target_lens_pos);
+ curr_lens_pos = target_lens_pos;
+
+ a_ctrl->curr_region_index += sign_dir;
+ }
+ a_ctrl->curr_step_pos = target_step_pos;
+ }
+ /*Free the memory allocated for damping parameters*/
+ kfree(ringing_params_kernel);
+
+ move_params->curr_lens_pos = curr_lens_pos;
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("i2c write error:%d\n", rc);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ CDBG("Exit\n");
+
+ return rc;
+}
+
+static int32_t msm_actuator_bivcm_move_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_move_params_t *move_params)
+{
+ int32_t rc = 0;
+ struct damping_params_t *ringing_params_kernel = NULL;
+ int8_t sign_dir = move_params->sign_dir;
+ uint16_t step_boundary = 0;
+ uint16_t target_step_pos = 0;
+ uint16_t target_lens_pos = 0;
+ int16_t dest_step_pos = move_params->dest_step_pos;
+ uint16_t curr_lens_pos = 0;
+ int dir = move_params->dir;
+ int32_t num_steps = move_params->num_steps;
+
+ if (a_ctrl->step_position_table == NULL) {
+ pr_err("Step Position Table is NULL");
+ return -EFAULT;
+ }
+
+ CDBG("called, dir %d, num_steps %d\n", dir, num_steps);
+
+ if (dest_step_pos == a_ctrl->curr_step_pos)
+ return rc;
+
+ if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) ||
+ (sign_dir < MSM_ACTUATOR_MOVE_SIGNED_FAR)) {
+ pr_err("Invalid sign_dir = %d\n", sign_dir);
+ return -EFAULT;
+ }
+ if ((dir > MOVE_FAR) || (dir < MOVE_NEAR)) {
+ pr_err("Invalid direction = %d\n", dir);
+ return -EFAULT;
+ }
+ if (dest_step_pos > a_ctrl->total_steps) {
+ pr_err("Step pos greater than total steps = %d\n",
+ dest_step_pos);
+ return -EFAULT;
+ }
+ if ((a_ctrl->region_size <= 0) ||
+ (a_ctrl->region_size > MAX_ACTUATOR_REGION) ||
+ (!move_params->ringing_params)) {
+ pr_err("Invalid-region size = %d, ringing_params = %pK\n",
+ a_ctrl->region_size, move_params->ringing_params);
+ return -EFAULT;
+ }
+ /*Allocate memory for damping parameters of all regions*/
+ ringing_params_kernel = kmalloc(
+ sizeof(struct damping_params_t)*(a_ctrl->region_size),
+ GFP_KERNEL);
+ if (!ringing_params_kernel) {
+ pr_err("kmalloc for damping parameters failed\n");
+ return -EFAULT;
+ }
+ if (copy_from_user(ringing_params_kernel,
+ &(move_params->ringing_params[0]),
+ (sizeof(struct damping_params_t))*(a_ctrl->region_size))) {
+ pr_err("copy_from_user failed\n");
+ /*Free the allocated memory for damping parameters*/
+ kfree(ringing_params_kernel);
+ return -EFAULT;
+ }
+ curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
+ a_ctrl->i2c_tbl_index = 0;
+ CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n",
+ a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos);
+
+ while (a_ctrl->curr_step_pos != dest_step_pos) {
+ step_boundary =
+ a_ctrl->region_params[a_ctrl->curr_region_index].
+ step_bound[dir];
+ if ((dest_step_pos * sign_dir) <=
+ (step_boundary * sign_dir)) {
+
+ target_step_pos = dest_step_pos;
+ target_lens_pos =
+ a_ctrl->step_position_table[target_step_pos];
+ rc = msm_actuator_bivcm_write_focus(a_ctrl,
+ curr_lens_pos,
+ &ringing_params_kernel
+ [a_ctrl->curr_region_index],
+ sign_dir,
+ target_lens_pos);
+ if (rc < 0) {
+ kfree(ringing_params_kernel);
+ return rc;
+ }
+ curr_lens_pos = target_lens_pos;
+ } else {
+ target_step_pos = step_boundary;
+ target_lens_pos =
+ a_ctrl->step_position_table[target_step_pos];
+ rc = msm_actuator_bivcm_write_focus(a_ctrl,
+ curr_lens_pos,
+ &ringing_params_kernel
+ [a_ctrl->curr_region_index],
+ sign_dir,
+ target_lens_pos);
+ if (rc < 0) {
+ kfree(ringing_params_kernel);
+ return rc;
+ }
+ curr_lens_pos = target_lens_pos;
+
+ a_ctrl->curr_region_index += sign_dir;
+ }
+ a_ctrl->curr_step_pos = target_step_pos;
+ }
+ /*Free the memory allocated for damping parameters*/
+ kfree(ringing_params_kernel);
+
+ move_params->curr_lens_pos = curr_lens_pos;
+ a_ctrl->i2c_tbl_index = 0;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_park_lens(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t next_lens_pos = 0;
+ struct msm_camera_i2c_reg_setting reg_setting;
+
+ a_ctrl->i2c_tbl_index = 0;
+ if ((a_ctrl->curr_step_pos > a_ctrl->total_steps) ||
+ (!a_ctrl->park_lens.max_step) ||
+ (!a_ctrl->step_position_table) ||
+ (!a_ctrl->i2c_reg_tbl) ||
+ (!a_ctrl->func_tbl) ||
+ (!a_ctrl->func_tbl->actuator_parse_i2c_params)) {
+ pr_err("%s:%d Failed to park lens.\n",
+ __func__, __LINE__);
+ return 0;
+ }
+
+ if (a_ctrl->park_lens.max_step > a_ctrl->max_code_size)
+ a_ctrl->park_lens.max_step = a_ctrl->max_code_size;
+
+ next_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
+ while (next_lens_pos) {
+ /* conditions which help to reduce park lens time */
+ if (next_lens_pos > (a_ctrl->park_lens.max_step *
+ PARK_LENS_LONG_STEP)) {
+ next_lens_pos = next_lens_pos -
+ (a_ctrl->park_lens.max_step *
+ PARK_LENS_LONG_STEP);
+ } else if (next_lens_pos > (a_ctrl->park_lens.max_step *
+ PARK_LENS_MID_STEP)) {
+ next_lens_pos = next_lens_pos -
+ (a_ctrl->park_lens.max_step *
+ PARK_LENS_MID_STEP);
+ } else if (next_lens_pos > (a_ctrl->park_lens.max_step *
+ PARK_LENS_SMALL_STEP)) {
+ next_lens_pos = next_lens_pos -
+ (a_ctrl->park_lens.max_step *
+ PARK_LENS_SMALL_STEP);
+ } else {
+ next_lens_pos = (next_lens_pos >
+ a_ctrl->park_lens.max_step) ?
+ (next_lens_pos - a_ctrl->park_lens.
+ max_step) : 0;
+ }
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ next_lens_pos, a_ctrl->park_lens.hw_params,
+ a_ctrl->park_lens.damping_delay);
+
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+
+ rc = a_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("%s Failed I2C write Line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ /* Use typical damping time delay to avoid tick sound */
+ usleep_range(10000, 12000);
+ }
+
+ return 0;
+}
+
+static int32_t msm_actuator_bivcm_init_step_table(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_info_t *set_info)
+{
+ int16_t code_per_step = 0;
+ int16_t cur_code = 0;
+ uint16_t step_index = 0, region_index = 0;
+ uint16_t step_boundary = 0;
+ uint32_t max_code_size = 1;
+ uint16_t data_size = set_info->actuator_params.data_size;
+ uint16_t mask = 0, i = 0;
+ uint32_t qvalue = 0;
+
+ CDBG("Enter\n");
+
+ for (; data_size > 0; data_size--) {
+ max_code_size *= 2;
+ mask |= (1 << i++);
+ }
+
+ a_ctrl->max_code_size = max_code_size;
+ kfree(a_ctrl->step_position_table);
+ a_ctrl->step_position_table = NULL;
+
+ if (set_info->af_tuning_params.total_steps
+ > MAX_ACTUATOR_AF_TOTAL_STEPS) {
+ pr_err("Max actuator totalsteps exceeded = %d\n",
+ set_info->af_tuning_params.total_steps);
+ return -EFAULT;
+ }
+ /* Fill step position table */
+ a_ctrl->step_position_table =
+ kmalloc(sizeof(uint16_t) *
+ (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
+
+ if (a_ctrl->step_position_table == NULL)
+ return -ENOMEM;
+
+ cur_code = set_info->af_tuning_params.initial_code;
+ a_ctrl->step_position_table[step_index++] = cur_code;
+ for (region_index = 0;
+ region_index < a_ctrl->region_size;
+ region_index++) {
+ code_per_step =
+ a_ctrl->region_params[region_index].code_per_step;
+ step_boundary =
+ a_ctrl->region_params[region_index].
+ step_bound[MOVE_NEAR];
+ if (step_boundary >
+ set_info->af_tuning_params.total_steps) {
+ pr_err("invalid step_boundary = %d, max_val = %d",
+ step_boundary,
+ set_info->af_tuning_params.total_steps);
+ kfree(a_ctrl->step_position_table);
+ a_ctrl->step_position_table = NULL;
+ return -EINVAL;
+ }
+ qvalue = a_ctrl->region_params[region_index].qvalue;
+ for (; step_index <= step_boundary;
+ step_index++) {
+ if (qvalue > 1 && qvalue <= MAX_QVALUE)
+ cur_code = step_index * code_per_step / qvalue;
+ else
+ cur_code = step_index * code_per_step;
+ cur_code = (set_info->af_tuning_params.initial_code +
+ cur_code) & mask;
+ if (cur_code < max_code_size)
+ a_ctrl->step_position_table[step_index] =
+ cur_code;
+ else {
+ for (; step_index <
+ set_info->af_tuning_params.total_steps;
+ step_index++)
+ a_ctrl->
+ step_position_table[
+ step_index] =
+ max_code_size;
+ }
+ CDBG("step_position_table[%d] = %d\n", step_index,
+ a_ctrl->step_position_table[step_index]);
+ }
+ }
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_actuator_init_step_table(struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_info_t *set_info)
+{
+ int16_t code_per_step = 0;
+ uint32_t qvalue = 0;
+ int16_t cur_code = 0;
+ uint16_t step_index = 0, region_index = 0;
+ uint16_t step_boundary = 0;
+ uint32_t max_code_size = 1;
+ uint16_t data_size = set_info->actuator_params.data_size;
+
+ CDBG("Enter\n");
+
+ /* validate the actuator state */
+ if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) {
+ pr_err("%s:%d invalid actuator_state %d\n"
+ , __func__, __LINE__, a_ctrl->actuator_state);
+ return -EINVAL;
+ }
+ for (; data_size > 0; data_size--)
+ max_code_size *= 2;
+
+ a_ctrl->max_code_size = max_code_size;
+
+ /* free the step_position_table to allocate a new one */
+ kfree(a_ctrl->step_position_table);
+ a_ctrl->step_position_table = NULL;
+
+ if (set_info->af_tuning_params.total_steps
+ > MAX_ACTUATOR_AF_TOTAL_STEPS) {
+ pr_err("Max actuator totalsteps exceeded = %d\n",
+ set_info->af_tuning_params.total_steps);
+ return -EFAULT;
+ }
+ /* Fill step position table */
+ a_ctrl->step_position_table =
+ kmalloc(sizeof(uint16_t) *
+ (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
+
+ if (a_ctrl->step_position_table == NULL)
+ return -ENOMEM;
+
+ cur_code = set_info->af_tuning_params.initial_code;
+ a_ctrl->step_position_table[step_index++] = cur_code;
+ for (region_index = 0;
+ region_index < a_ctrl->region_size;
+ region_index++) {
+ code_per_step =
+ a_ctrl->region_params[region_index].code_per_step;
+ qvalue =
+ a_ctrl->region_params[region_index].qvalue;
+ step_boundary =
+ a_ctrl->region_params[region_index].
+ step_bound[MOVE_NEAR];
+ if (step_boundary >
+ set_info->af_tuning_params.total_steps) {
+ pr_err("invalid step_boundary = %d, max_val = %d",
+ step_boundary,
+ set_info->af_tuning_params.total_steps);
+ kfree(a_ctrl->step_position_table);
+ a_ctrl->step_position_table = NULL;
+ return -EINVAL;
+ }
+ for (; step_index <= step_boundary;
+ step_index++) {
+ if (qvalue > 1 && qvalue <= MAX_QVALUE)
+ cur_code = step_index * code_per_step / qvalue;
+ else
+ cur_code = step_index * code_per_step;
+ cur_code += set_info->af_tuning_params.initial_code;
+ if (cur_code < max_code_size)
+ a_ctrl->step_position_table[step_index] =
+ cur_code;
+ else {
+ for (; step_index <
+ set_info->af_tuning_params.total_steps;
+ step_index++)
+ a_ctrl->
+ step_position_table[
+ step_index] =
+ max_code_size;
+ }
+ CDBG("step_position_table[%d] = %d\n", step_index,
+ a_ctrl->step_position_table[step_index]);
+ }
+ }
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_actuator_set_default_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_move_params_t *move_params)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ if (a_ctrl->curr_step_pos != 0)
+ rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, move_params);
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_vreg_control(struct msm_actuator_ctrl_t *a_ctrl,
+ int config)
+{
+ int rc = 0, i, cnt;
+ struct msm_actuator_vreg *vreg_cfg;
+
+ vreg_cfg = &a_ctrl->vreg_cfg;
+ cnt = vreg_cfg->num_vreg;
+ if (!cnt)
+ return 0;
+
+ if (cnt >= MSM_ACTUATOR_MAX_VREGS) {
+ pr_err("%s failed %d cnt %d\n", __func__, __LINE__, cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = msm_camera_config_single_vreg(&(a_ctrl->pdev->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ } else if (a_ctrl->act_device_type ==
+ MSM_CAMERA_I2C_DEVICE) {
+ rc = msm_camera_config_single_vreg(
+ &(a_ctrl->i2c_client.client->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int32_t rc = 0;
+ enum msm_sensor_power_seq_gpio_t gpio;
+
+ CDBG("Enter\n");
+ if (a_ctrl->actuator_state != ACT_DISABLE_STATE) {
+
+ if (a_ctrl->func_tbl && a_ctrl->func_tbl->actuator_park_lens) {
+ rc = a_ctrl->func_tbl->actuator_park_lens(a_ctrl);
+ if (rc < 0)
+ pr_err("%s:%d Lens park failed.\n",
+ __func__, __LINE__);
+ }
+
+ rc = msm_actuator_vreg_control(a_ctrl, 0);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ for (gpio = SENSOR_GPIO_AF_PWDM;
+ gpio < SENSOR_GPIO_MAX; gpio++) {
+ if (a_ctrl->gconf &&
+ a_ctrl->gconf->gpio_num_info &&
+ a_ctrl->gconf->gpio_num_info->
+ valid[gpio] == 1) {
+
+ gpio_set_value_cansleep(
+ a_ctrl->gconf->gpio_num_info->
+ gpio_num[gpio],
+ GPIOF_OUT_INIT_LOW);
+
+ if (a_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ a_ctrl->pinctrl_info.pinctrl,
+ a_ctrl->pinctrl_info.
+ gpio_state_suspend);
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to suspend state: %d",
+ __func__, __LINE__, rc);
+
+ devm_pinctrl_put(
+ a_ctrl->pinctrl_info.pinctrl);
+ }
+ a_ctrl->cam_pinctrl_status = 0;
+ rc = msm_camera_request_gpio_table(
+ a_ctrl->gconf->cam_gpio_req_tbl,
+ a_ctrl->gconf->cam_gpio_req_tbl_size,
+ 0);
+ if (rc < 0)
+ pr_err("ERR:%s:Failed in selecting state in actuator power down: %d\n",
+ __func__, rc);
+ }
+ }
+
+ kfree(a_ctrl->step_position_table);
+ a_ctrl->step_position_table = NULL;
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ a_ctrl->i2c_tbl_index = 0;
+ a_ctrl->actuator_state = ACT_OPS_INACTIVE;
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_set_position(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_position_t *set_pos)
+{
+ int32_t rc = 0;
+ int32_t index;
+ uint16_t next_lens_position;
+ uint16_t delay;
+ uint32_t hw_params = 0;
+ struct msm_camera_i2c_reg_setting reg_setting;
+
+ CDBG("%s Enter %d\n", __func__, __LINE__);
+ if (set_pos->number_of_steps <= 0 ||
+ set_pos->number_of_steps > MAX_NUMBER_OF_STEPS) {
+ pr_err("num_steps out of range = %d\n",
+ set_pos->number_of_steps);
+ return -EFAULT;
+ }
+
+ if (!a_ctrl || !a_ctrl->func_tbl ||
+ !a_ctrl->func_tbl->actuator_parse_i2c_params) {
+ pr_err("failed. NULL actuator pointers.");
+ return -EFAULT;
+ }
+
+ if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) {
+ pr_err("failed. Invalid actuator state.");
+ return -EFAULT;
+ }
+
+ a_ctrl->i2c_tbl_index = 0;
+ hw_params = set_pos->hw_params;
+ for (index = 0; index < set_pos->number_of_steps; index++) {
+ next_lens_position = set_pos->pos[index];
+ delay = set_pos->delay[index];
+ a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
+ next_lens_position, hw_params, delay);
+
+ reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
+ reg_setting.size = a_ctrl->i2c_tbl_index;
+ reg_setting.data_type = a_ctrl->i2c_data_type;
+
+ rc = a_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_table_w_microdelay(
+ &a_ctrl->i2c_client, ®_setting);
+ if (rc < 0) {
+ pr_err("%s Failed I2C write Line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ a_ctrl->i2c_tbl_index = 0;
+ }
+ CDBG("%s exit %d\n", __func__, __LINE__);
+ return rc;
+}
+
+static int32_t msm_actuator_bivcm_set_position(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_position_t *set_pos)
+{
+ int32_t rc = 0;
+ int32_t index;
+ uint16_t next_lens_position;
+ uint16_t delay;
+ uint32_t hw_params = 0;
+
+ CDBG("%s Enter %d\n", __func__, __LINE__);
+ if (set_pos->number_of_steps <= 0 ||
+ set_pos->number_of_steps > MAX_NUMBER_OF_STEPS) {
+ pr_err("num_steps out of range = %d\n",
+ set_pos->number_of_steps);
+ return -EFAULT;
+ }
+
+ if (!a_ctrl) {
+ pr_err("failed. NULL actuator pointers.");
+ return -EFAULT;
+ }
+
+ if (a_ctrl->actuator_state != ACT_OPS_ACTIVE) {
+ pr_err("failed. Invalid actuator state.");
+ return -EFAULT;
+ }
+
+ a_ctrl->i2c_tbl_index = 0;
+ hw_params = set_pos->hw_params;
+ for (index = 0; index < set_pos->number_of_steps; index++) {
+ next_lens_position = set_pos->pos[index];
+ delay = set_pos->delay[index];
+ rc = msm_actuator_bivcm_handle_i2c_ops(a_ctrl,
+ next_lens_position, hw_params, delay);
+ a_ctrl->i2c_tbl_index = 0;
+ }
+ CDBG("%s exit %d\n", __func__, __LINE__);
+ return rc;
+}
+
+static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl,
+ struct msm_actuator_set_info_t *set_info) {
+ struct reg_settings_t *init_settings = NULL;
+ int32_t rc = -EFAULT;
+ uint16_t i = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+
+ for (i = 0; i < ARRAY_SIZE(actuators); i++) {
+ if (set_info->actuator_params.act_type ==
+ actuators[i]->act_type) {
+ a_ctrl->func_tbl = &actuators[i]->func_tbl;
+ rc = 0;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("Actuator function table not found\n");
+ return rc;
+ }
+ if (set_info->af_tuning_params.total_steps
+ > MAX_ACTUATOR_AF_TOTAL_STEPS) {
+ pr_err("Max actuator totalsteps exceeded = %d\n",
+ set_info->af_tuning_params.total_steps);
+ return -EFAULT;
+ }
+ if (set_info->af_tuning_params.region_size
+ > MAX_ACTUATOR_REGION) {
+ pr_err("MAX_ACTUATOR_REGION is exceeded.\n");
+ return -EFAULT;
+ }
+
+ a_ctrl->region_size = set_info->af_tuning_params.region_size;
+ a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step;
+ a_ctrl->total_steps = set_info->af_tuning_params.total_steps;
+
+ if (copy_from_user(&a_ctrl->region_params,
+ (void __user *)set_info->af_tuning_params.region_params,
+ a_ctrl->region_size * sizeof(struct region_params_t))) {
+ a_ctrl->total_steps = 0;
+ pr_err("Error copying region_params\n");
+ return -EFAULT;
+ }
+ if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = a_ctrl->i2c_client.cci_client;
+ cci_client->sid =
+ set_info->actuator_params.i2c_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->cci_i2c_master = a_ctrl->cci_master;
+ cci_client->i2c_freq_mode =
+ set_info->actuator_params.i2c_freq_mode;
+ } else {
+ a_ctrl->i2c_client.client->addr =
+ set_info->actuator_params.i2c_addr;
+ }
+
+ a_ctrl->i2c_data_type = set_info->actuator_params.i2c_data_type;
+ a_ctrl->i2c_client.addr_type = set_info->actuator_params.i2c_addr_type;
+ if (set_info->actuator_params.reg_tbl_size <=
+ MAX_ACTUATOR_REG_TBL_SIZE) {
+ a_ctrl->reg_tbl_size = set_info->actuator_params.reg_tbl_size;
+ } else {
+ a_ctrl->reg_tbl_size = 0;
+ pr_err("MAX_ACTUATOR_REG_TBL_SIZE is exceeded.\n");
+ return -EFAULT;
+ }
+
+ if ((a_ctrl->actuator_state == ACT_OPS_ACTIVE) &&
+ (a_ctrl->i2c_reg_tbl != NULL)) {
+ kfree(a_ctrl->i2c_reg_tbl);
+ }
+ a_ctrl->i2c_reg_tbl = NULL;
+ a_ctrl->i2c_reg_tbl =
+ kmalloc(sizeof(struct msm_camera_i2c_reg_array) *
+ (set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
+ if (!a_ctrl->i2c_reg_tbl) {
+ pr_err("kmalloc fail\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(&a_ctrl->reg_tbl,
+ (void __user *)set_info->actuator_params.reg_tbl_params,
+ a_ctrl->reg_tbl_size *
+ sizeof(struct msm_actuator_reg_params_t))) {
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ return -EFAULT;
+ }
+
+ if (set_info->actuator_params.init_setting_size &&
+ set_info->actuator_params.init_setting_size
+ <= MAX_ACTUATOR_INIT_SET) {
+ if (a_ctrl->func_tbl->actuator_init_focus) {
+ init_settings = kmalloc(sizeof(struct reg_settings_t) *
+ (set_info->actuator_params.init_setting_size),
+ GFP_KERNEL);
+ if (init_settings == NULL) {
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ pr_err("Error allocating memory for init_settings\n");
+ return -EFAULT;
+ }
+ if (copy_from_user(init_settings,
+ (void __user *)
+ set_info->actuator_params.init_settings,
+ set_info->actuator_params.init_setting_size *
+ sizeof(struct reg_settings_t))) {
+ kfree(init_settings);
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ pr_err("Error copying init_settings\n");
+ return -EFAULT;
+ }
+ rc = a_ctrl->func_tbl->actuator_init_focus(a_ctrl,
+ set_info->actuator_params.init_setting_size,
+ init_settings);
+ kfree(init_settings);
+ if (rc < 0) {
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ pr_err("Error actuator_init_focus\n");
+ return -EFAULT;
+ }
+ }
+ }
+
+ /* Park lens data */
+ a_ctrl->park_lens = set_info->actuator_params.park_lens;
+ a_ctrl->initial_code = set_info->af_tuning_params.initial_code;
+ if (a_ctrl->func_tbl->actuator_init_step_table)
+ rc = a_ctrl->func_tbl->
+ actuator_init_step_table(a_ctrl, set_info);
+
+ a_ctrl->curr_step_pos = 0;
+ a_ctrl->curr_region_index = 0;
+ CDBG("Exit\n");
+
+ return rc;
+}
+
+static int msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int rc = 0;
+
+ CDBG("Enter\n");
+ if (!a_ctrl) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &a_ctrl->i2c_client, MSM_CCI_INIT);
+ if (rc < 0)
+ pr_err("cci_init failed\n");
+ }
+ a_ctrl->actuator_state = ACT_OPS_ACTIVE;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl,
+ void *argp)
+{
+ struct msm_actuator_cfg_data *cdata =
+ (struct msm_actuator_cfg_data *)argp;
+ int32_t rc = -EINVAL;
+
+ mutex_lock(a_ctrl->actuator_mutex);
+ CDBG("Enter\n");
+ CDBG("%s type %d\n", __func__, cdata->cfgtype);
+
+ if (cdata->cfgtype != CFG_ACTUATOR_INIT &&
+ cdata->cfgtype != CFG_ACTUATOR_POWERUP &&
+ a_ctrl->actuator_state == ACT_DISABLE_STATE) {
+ pr_err("actuator disabled %d\n", rc);
+ mutex_unlock(a_ctrl->actuator_mutex);
+ return rc;
+ }
+
+ switch (cdata->cfgtype) {
+ case CFG_ACTUATOR_INIT:
+ rc = msm_actuator_init(a_ctrl);
+ if (rc < 0)
+ pr_err("msm_actuator_init failed %d\n", rc);
+ break;
+ case CFG_GET_ACTUATOR_INFO:
+ cdata->is_af_supported = 1;
+ cdata->cfg.cam_name = a_ctrl->cam_name;
+ rc = 0;
+ break;
+
+ case CFG_SET_ACTUATOR_INFO:
+ rc = msm_actuator_set_param(a_ctrl, &cdata->cfg.set_info);
+ if (rc < 0)
+ pr_err("init table failed %d\n", rc);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ if (a_ctrl->func_tbl &&
+ a_ctrl->func_tbl->actuator_set_default_focus)
+ rc = a_ctrl->func_tbl->actuator_set_default_focus(
+ a_ctrl, &cdata->cfg.move);
+ if (rc < 0)
+ pr_err("move focus failed %d\n", rc);
+ break;
+
+ case CFG_MOVE_FOCUS:
+ if (a_ctrl->func_tbl &&
+ a_ctrl->func_tbl->actuator_move_focus)
+ rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl,
+ &cdata->cfg.move);
+ if (rc < 0)
+ pr_err("move focus failed %d\n", rc);
+ break;
+ case CFG_ACTUATOR_POWERDOWN:
+ rc = msm_actuator_power_down(a_ctrl);
+ if (rc < 0)
+ pr_err("msm_actuator_power_down failed %d\n", rc);
+ break;
+
+ case CFG_SET_POSITION:
+ if (a_ctrl->func_tbl &&
+ a_ctrl->func_tbl->actuator_set_position)
+ rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl,
+ &cdata->cfg.setpos);
+ if (rc < 0)
+ pr_err("actuator_set_position failed %d\n", rc);
+ break;
+
+ case CFG_ACTUATOR_POWERUP:
+ rc = msm_actuator_power_up(a_ctrl);
+ if (rc < 0)
+ pr_err("Failed actuator power up%d\n", rc);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(a_ctrl->actuator_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_actuator_get_subdev_id(struct msm_actuator_ctrl_t *a_ctrl,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ *subdev_id = a_ctrl->pdev->id;
+ else
+ *subdev_id = a_ctrl->subdev_id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+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,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+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,
+ .i2c_poll = msm_camera_qup_i2c_poll,
+};
+
+static int msm_actuator_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter\n");
+ if (!a_ctrl) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ mutex_lock(a_ctrl->actuator_mutex);
+ if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE &&
+ a_ctrl->actuator_state != ACT_DISABLE_STATE) {
+ rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &a_ctrl->i2c_client, MSM_CCI_RELEASE);
+ if (rc < 0)
+ pr_err("cci_init failed\n");
+ }
+ kfree(a_ctrl->i2c_reg_tbl);
+ a_ctrl->i2c_reg_tbl = NULL;
+ a_ctrl->actuator_state = ACT_DISABLE_STATE;
+ mutex_unlock(a_ctrl->actuator_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_actuator_internal_ops = {
+ .close = msm_actuator_close,
+};
+
+static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc;
+ struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
+ void *argp = (void *)arg;
+
+ CDBG("Enter\n");
+ CDBG("%s:%d a_ctrl %pK argp %pK\n", __func__, __LINE__, a_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_actuator_get_subdev_id(a_ctrl, argp);
+ case VIDIOC_MSM_ACTUATOR_CFG:
+ return msm_actuator_config(a_ctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!a_ctrl->i2c_client.i2c_func_tbl) {
+ pr_err("a_ctrl->i2c_client.i2c_func_tbl NULL\n");
+ return -EINVAL;
+ }
+ mutex_lock(a_ctrl->actuator_mutex);
+ rc = msm_actuator_power_down(a_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d Actuator Power down failed\n",
+ __func__, __LINE__);
+ }
+ mutex_unlock(a_ctrl->actuator_mutex);
+ return msm_actuator_close(sd, NULL);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_actuator_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct msm_actuator_cfg_data32 *u32 =
+ (struct msm_actuator_cfg_data32 *)arg;
+ struct msm_actuator_cfg_data actuator_data;
+ void *parg = arg;
+ long rc;
+
+ switch (cmd) {
+ case VIDIOC_MSM_ACTUATOR_CFG32:
+ cmd = VIDIOC_MSM_ACTUATOR_CFG;
+ switch (u32->cfgtype) {
+ case CFG_SET_ACTUATOR_INFO:
+ actuator_data.cfgtype = u32->cfgtype;
+ actuator_data.is_af_supported = u32->is_af_supported;
+ actuator_data.cfg.set_info.actuator_params.act_type =
+ u32->cfg.set_info.actuator_params.act_type;
+
+ actuator_data.cfg.set_info.actuator_params
+ .reg_tbl_size =
+ u32->cfg.set_info.actuator_params.reg_tbl_size;
+
+ actuator_data.cfg.set_info.actuator_params.data_size =
+ u32->cfg.set_info.actuator_params.data_size;
+
+ actuator_data.cfg.set_info.actuator_params
+ .init_setting_size =
+ u32->cfg.set_info.actuator_params
+ .init_setting_size;
+
+ actuator_data.cfg.set_info.actuator_params.i2c_addr =
+ u32->cfg.set_info.actuator_params.i2c_addr;
+
+ actuator_data.cfg.set_info.actuator_params.
+ i2c_freq_mode =
+ u32->cfg.set_info.actuator_params.i2c_freq_mode;
+
+ actuator_data.cfg.set_info.actuator_params
+ .i2c_addr_type =
+ u32->cfg.set_info.actuator_params.i2c_addr_type;
+
+ actuator_data.cfg.set_info.actuator_params
+ .i2c_data_type =
+ u32->cfg.set_info.actuator_params.i2c_data_type;
+
+ actuator_data.cfg.set_info.actuator_params
+ .reg_tbl_params =
+ compat_ptr(
+ u32->cfg.set_info.actuator_params
+ .reg_tbl_params);
+
+ actuator_data.cfg.set_info.actuator_params
+ .init_settings =
+ compat_ptr(
+ u32->cfg.set_info.actuator_params
+ .init_settings);
+
+ actuator_data.cfg.set_info.af_tuning_params
+ .initial_code =
+ u32->cfg.set_info.af_tuning_params.initial_code;
+
+ actuator_data.cfg.set_info.af_tuning_params.pwd_step =
+ u32->cfg.set_info.af_tuning_params.pwd_step;
+
+ actuator_data.cfg.set_info.af_tuning_params
+ .region_size =
+ u32->cfg.set_info.af_tuning_params.region_size;
+
+ actuator_data.cfg.set_info.af_tuning_params
+ .total_steps =
+ u32->cfg.set_info.af_tuning_params.total_steps;
+
+ actuator_data.cfg.set_info.af_tuning_params
+ .region_params = compat_ptr(
+ u32->cfg.set_info.af_tuning_params
+ .region_params);
+
+ actuator_data.cfg.set_info.actuator_params.park_lens =
+ u32->cfg.set_info.actuator_params.park_lens;
+
+ parg = &actuator_data;
+ break;
+ case CFG_SET_DEFAULT_FOCUS:
+ case CFG_MOVE_FOCUS:
+ actuator_data.cfgtype = u32->cfgtype;
+ actuator_data.is_af_supported = u32->is_af_supported;
+ actuator_data.cfg.move.dir = u32->cfg.move.dir;
+
+ actuator_data.cfg.move.sign_dir =
+ u32->cfg.move.sign_dir;
+
+ actuator_data.cfg.move.dest_step_pos =
+ u32->cfg.move.dest_step_pos;
+
+ actuator_data.cfg.move.num_steps =
+ u32->cfg.move.num_steps;
+
+ actuator_data.cfg.move.curr_lens_pos =
+ u32->cfg.move.curr_lens_pos;
+
+ actuator_data.cfg.move.ringing_params =
+ compat_ptr(u32->cfg.move.ringing_params);
+ parg = &actuator_data;
+ break;
+ case CFG_SET_POSITION:
+ actuator_data.cfgtype = u32->cfgtype;
+ actuator_data.is_af_supported = u32->is_af_supported;
+ memcpy(&actuator_data.cfg.setpos, &(u32->cfg.setpos),
+ sizeof(struct msm_actuator_set_position_t));
+ break;
+ default:
+ actuator_data.cfgtype = u32->cfgtype;
+ parg = &actuator_data;
+ break;
+ }
+ break;
+ case VIDIOC_MSM_ACTUATOR_CFG:
+ pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
+ return -EINVAL;
+ }
+
+ rc = msm_actuator_subdev_ioctl(sd, cmd, parg);
+
+ switch (cmd) {
+
+ case VIDIOC_MSM_ACTUATOR_CFG:
+
+ switch (u32->cfgtype) {
+
+ case CFG_SET_DEFAULT_FOCUS:
+ case CFG_MOVE_FOCUS:
+ u32->cfg.move.curr_lens_pos =
+ actuator_data.cfg.move.curr_lens_pos;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static long msm_actuator_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_actuator_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int rc = 0;
+ enum msm_sensor_power_seq_gpio_t gpio;
+
+ CDBG("%s called\n", __func__);
+
+ rc = msm_actuator_vreg_control(a_ctrl, 1);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ for (gpio = SENSOR_GPIO_AF_PWDM; gpio < SENSOR_GPIO_MAX; gpio++) {
+ if (a_ctrl->gconf &&
+ a_ctrl->gconf->gpio_num_info &&
+ a_ctrl->gconf->gpio_num_info->valid[gpio] == 1) {
+ rc = msm_camera_request_gpio_table(
+ a_ctrl->gconf->cam_gpio_req_tbl,
+ a_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state for actuator: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ if (a_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ a_ctrl->pinctrl_info.pinctrl,
+ a_ctrl->pinctrl_info.gpio_state_active);
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ gpio_set_value_cansleep(
+ a_ctrl->gconf->gpio_num_info->gpio_num[gpio],
+ 1);
+ }
+ }
+
+ /* VREG needs some delay to power up */
+ usleep_range(2000, 3000);
+ a_ctrl->actuator_state = ACT_ENABLE_STATE;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_actuator_subdev_core_ops = {
+ .ioctl = msm_actuator_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_actuator_subdev_ops = {
+ .core = &msm_actuator_subdev_core_ops,
+};
+
+static const struct i2c_device_id msm_actuator_i2c_id[] = {
+ {"qcom,actuator", (kernel_ulong_t)NULL},
+ { }
+};
+
+static int32_t msm_actuator_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_actuator_ctrl_t *act_ctrl_t = NULL;
+ struct msm_actuator_vreg *vreg_cfg = NULL;
+
+ CDBG("Enter\n");
+
+ if (client == NULL) {
+ pr_err("msm_actuator_i2c_probe: client is null\n");
+ return -EINVAL;
+ }
+
+ act_ctrl_t = kzalloc(sizeof(struct msm_actuator_ctrl_t),
+ GFP_KERNEL);
+ if (!act_ctrl_t)
+ return -ENOMEM;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("i2c_check_functionality failed\n");
+ goto probe_failure;
+ }
+
+ CDBG("client = 0x%pK\n", client);
+
+ rc = of_property_read_u32(client->dev.of_node, "cell-index",
+ &act_ctrl_t->subdev_id);
+ CDBG("cell-index %d, rc %d\n", act_ctrl_t->subdev_id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto probe_failure;
+ }
+
+ if (of_find_property(client->dev.of_node,
+ "qcom,cam-vreg-name", NULL)) {
+ vreg_cfg = &act_ctrl_t->vreg_cfg;
+ rc = msm_camera_get_dt_vreg_data(client->dev.of_node,
+ &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto probe_failure;
+ }
+ }
+
+ act_ctrl_t->i2c_driver = &msm_actuator_i2c_driver;
+ act_ctrl_t->i2c_client.client = client;
+ act_ctrl_t->curr_step_pos = 0,
+ act_ctrl_t->curr_region_index = 0,
+ /* Set device type as I2C */
+ act_ctrl_t->act_device_type = MSM_CAMERA_I2C_DEVICE;
+ act_ctrl_t->i2c_client.i2c_func_tbl = &msm_sensor_qup_func_tbl;
+ act_ctrl_t->act_v4l2_subdev_ops = &msm_actuator_subdev_ops;
+ act_ctrl_t->actuator_mutex = &msm_actuator_mutex;
+ act_ctrl_t->cam_name = act_ctrl_t->subdev_id;
+ CDBG("act_ctrl_t->cam_name: %d", act_ctrl_t->cam_name);
+ /* Assign name for sub device */
+ snprintf(act_ctrl_t->msm_sd.sd.name, sizeof(act_ctrl_t->msm_sd.sd.name),
+ "%s", act_ctrl_t->i2c_driver->driver.name);
+
+ /* Initialize sub device */
+ v4l2_i2c_subdev_init(&act_ctrl_t->msm_sd.sd,
+ act_ctrl_t->i2c_client.client,
+ act_ctrl_t->act_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&act_ctrl_t->msm_sd.sd, act_ctrl_t);
+ act_ctrl_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops;
+ act_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&act_ctrl_t->msm_sd.sd.entity, 0, NULL);
+ act_ctrl_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ act_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
+ msm_sd_register(&act_ctrl_t->msm_sd);
+ msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_actuator_v4l2_subdev_fops.compat_ioctl32 =
+ msm_actuator_subdev_fops_ioctl;
+#endif
+ act_ctrl_t->msm_sd.sd.devnode->fops =
+ &msm_actuator_v4l2_subdev_fops;
+ act_ctrl_t->actuator_state = ACT_DISABLE_STATE;
+ pr_info("msm_actuator_i2c_probe: succeeded\n");
+ CDBG("Exit\n");
+
+ return 0;
+
+probe_failure:
+ kfree(act_ctrl_t);
+ return rc;
+}
+
+static int32_t msm_actuator_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+ struct msm_actuator_ctrl_t *msm_actuator_t = NULL;
+ struct msm_actuator_vreg *vreg_cfg;
+
+ CDBG("Enter\n");
+
+ if (!pdev->dev.of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ msm_actuator_t = kzalloc(sizeof(struct msm_actuator_ctrl_t),
+ GFP_KERNEL);
+ if (!msm_actuator_t)
+ return -ENOMEM;
+ rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index",
+ &pdev->id);
+ CDBG("cell-index %d, rc %d\n", pdev->id, rc);
+ if (rc < 0) {
+ kfree(msm_actuator_t);
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master",
+ &msm_actuator_t->cci_master);
+ CDBG("qcom,cci-master %d, rc %d\n", msm_actuator_t->cci_master, rc);
+ if (rc < 0 || msm_actuator_t->cci_master >= MASTER_MAX) {
+ kfree(msm_actuator_t);
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ if (of_find_property((&pdev->dev)->of_node,
+ "qcom,cam-vreg-name", NULL)) {
+ vreg_cfg = &msm_actuator_t->vreg_cfg;
+ rc = msm_camera_get_dt_vreg_data((&pdev->dev)->of_node,
+ &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
+ if (rc < 0) {
+ kfree(msm_actuator_t);
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+ }
+ rc = msm_sensor_driver_get_gpio_data(&(msm_actuator_t->gconf),
+ (&pdev->dev)->of_node);
+ if (rc <= 0) {
+ pr_err("%s: No/Error Actuator GPIOs\n", __func__);
+ } else {
+ msm_actuator_t->cam_pinctrl_status = 1;
+ rc = msm_camera_pinctrl_init(
+ &(msm_actuator_t->pinctrl_info), &(pdev->dev));
+ if (rc < 0) {
+ pr_err("ERR:%s: Error in reading actuator pinctrl\n",
+ __func__);
+ msm_actuator_t->cam_pinctrl_status = 0;
+ }
+ }
+
+ msm_actuator_t->act_v4l2_subdev_ops = &msm_actuator_subdev_ops;
+ msm_actuator_t->actuator_mutex = &msm_actuator_mutex;
+ msm_actuator_t->cam_name = pdev->id;
+
+ /* Set platform device handle */
+ msm_actuator_t->pdev = pdev;
+ /* Set device type as platform device */
+ msm_actuator_t->act_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ msm_actuator_t->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ msm_actuator_t->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!msm_actuator_t->i2c_client.cci_client) {
+ kfree(msm_actuator_t->vreg_cfg.cam_vreg);
+ kfree(msm_actuator_t);
+ pr_err("failed no memory\n");
+ return -ENOMEM;
+ }
+
+ cci_client = msm_actuator_t->i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = msm_actuator_t->cci_master;
+ v4l2_subdev_init(&msm_actuator_t->msm_sd.sd,
+ msm_actuator_t->act_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&msm_actuator_t->msm_sd.sd, msm_actuator_t);
+ msm_actuator_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops;
+ msm_actuator_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(msm_actuator_t->msm_sd.sd.name,
+ ARRAY_SIZE(msm_actuator_t->msm_sd.sd.name), "msm_actuator");
+ media_entity_pads_init(&msm_actuator_t->msm_sd.sd.entity, 0, NULL);
+ msm_actuator_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_actuator_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
+ msm_sd_register(&msm_actuator_t->msm_sd);
+ msm_actuator_t->actuator_state = ACT_DISABLE_STATE;
+ msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_actuator_v4l2_subdev_fops.compat_ioctl32 =
+ msm_actuator_subdev_fops_ioctl;
+#endif
+ msm_actuator_t->msm_sd.sd.devnode->fops =
+ &msm_actuator_v4l2_subdev_fops;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static const struct of_device_id msm_actuator_i2c_dt_match[] = {
+ {.compatible = "qcom,actuator"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_actuator_i2c_dt_match);
+
+static struct i2c_driver msm_actuator_i2c_driver = {
+ .id_table = msm_actuator_i2c_id,
+ .probe = msm_actuator_i2c_probe,
+ .remove = __exit_p(msm_actuator_i2c_remove),
+ .driver = {
+ .name = "qcom,actuator",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_actuator_i2c_dt_match,
+ },
+};
+
+static const struct of_device_id msm_actuator_dt_match[] = {
+ {.compatible = "qcom,actuator", .data = NULL},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_actuator_dt_match);
+
+static struct platform_driver msm_actuator_platform_driver = {
+ .probe = msm_actuator_platform_probe,
+ .driver = {
+ .name = "qcom,actuator",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_actuator_dt_match,
+ },
+};
+
+static int __init msm_actuator_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_actuator_platform_driver);
+ if (!rc)
+ return rc;
+
+ CDBG("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&msm_actuator_i2c_driver);
+}
+
+static struct msm_actuator msm_vcm_actuator_table = {
+ .act_type = ACTUATOR_VCM,
+ .func_tbl = {
+ .actuator_init_step_table = msm_actuator_init_step_table,
+ .actuator_move_focus = msm_actuator_move_focus,
+ .actuator_write_focus = msm_actuator_write_focus,
+ .actuator_set_default_focus = msm_actuator_set_default_focus,
+ .actuator_init_focus = msm_actuator_init_focus,
+ .actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
+ .actuator_set_position = msm_actuator_set_position,
+ .actuator_park_lens = msm_actuator_park_lens,
+ },
+};
+
+static struct msm_actuator msm_piezo_actuator_table = {
+ .act_type = ACTUATOR_PIEZO,
+ .func_tbl = {
+ .actuator_init_step_table = NULL,
+ .actuator_move_focus = msm_actuator_piezo_move_focus,
+ .actuator_write_focus = NULL,
+ .actuator_set_default_focus =
+ msm_actuator_piezo_set_default_focus,
+ .actuator_init_focus = msm_actuator_init_focus,
+ .actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
+ .actuator_park_lens = NULL,
+ },
+};
+
+static struct msm_actuator msm_hvcm_actuator_table = {
+ .act_type = ACTUATOR_HVCM,
+ .func_tbl = {
+ .actuator_init_step_table = msm_actuator_init_step_table,
+ .actuator_move_focus = msm_actuator_move_focus,
+ .actuator_write_focus = msm_actuator_write_focus,
+ .actuator_set_default_focus = msm_actuator_set_default_focus,
+ .actuator_init_focus = msm_actuator_init_focus,
+ .actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
+ .actuator_set_position = msm_actuator_set_position,
+ .actuator_park_lens = msm_actuator_park_lens,
+ },
+};
+
+static struct msm_actuator msm_bivcm_actuator_table = {
+ .act_type = ACTUATOR_BIVCM,
+ .func_tbl = {
+ .actuator_init_step_table = msm_actuator_bivcm_init_step_table,
+ .actuator_move_focus = msm_actuator_bivcm_move_focus,
+ .actuator_write_focus = NULL,
+ .actuator_set_default_focus = msm_actuator_set_default_focus,
+ .actuator_init_focus = msm_actuator_init_focus,
+ .actuator_parse_i2c_params = NULL,
+ .actuator_set_position = msm_actuator_bivcm_set_position,
+ .actuator_park_lens = NULL,
+ },
+};
+
+module_init(msm_actuator_init_module);
+MODULE_DESCRIPTION("MSM ACTUATOR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
new file mode 100644
index 0000000..7af1d42
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2011-2016, 2018, 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_ACTUATOR_H
+#define MSM_ACTUATOR_H
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <soc/qcom/camera2.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+#define MSM_ACTUATOR_MAX_VREGS (10)
+#define ACTUATOR_MAX_POLL_COUNT 10
+
+struct msm_actuator_ctrl_t;
+
+enum msm_actuator_state_t {
+ ACT_ENABLE_STATE,
+ ACT_OPS_ACTIVE,
+ ACT_OPS_INACTIVE,
+ ACT_DISABLE_STATE,
+};
+
+struct msm_actuator_func_tbl {
+ int32_t (*actuator_i2c_write_b_af)(struct msm_actuator_ctrl_t *,
+ uint8_t,
+ uint8_t);
+ int32_t (*actuator_init_step_table)(struct msm_actuator_ctrl_t *,
+ struct msm_actuator_set_info_t *);
+ int32_t (*actuator_init_focus)(struct msm_actuator_ctrl_t *,
+ uint16_t, struct reg_settings_t *);
+ int32_t (*actuator_set_default_focus)(struct msm_actuator_ctrl_t *,
+ struct msm_actuator_move_params_t *);
+ int32_t (*actuator_move_focus)(struct msm_actuator_ctrl_t *,
+ struct msm_actuator_move_params_t *);
+ void (*actuator_parse_i2c_params)(struct msm_actuator_ctrl_t *,
+ int16_t, uint32_t, uint16_t);
+ void (*actuator_write_focus)(struct msm_actuator_ctrl_t *,
+ uint16_t,
+ struct damping_params_t *,
+ int8_t,
+ int16_t);
+ int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *,
+ struct msm_actuator_set_position_t *);
+ int32_t (*actuator_park_lens)(struct msm_actuator_ctrl_t *);
+};
+
+struct msm_actuator {
+ enum actuator_type act_type;
+ struct msm_actuator_func_tbl func_tbl;
+};
+
+struct msm_actuator_vreg {
+ struct camera_vreg_t *cam_vreg;
+ void *data[MSM_ACTUATOR_MAX_VREGS];
+ int num_vreg;
+};
+
+struct msm_actuator_ctrl_t {
+ struct i2c_driver *i2c_driver;
+ struct platform_driver *pdriver;
+ struct platform_device *pdev;
+ struct msm_camera_i2c_client i2c_client;
+ enum msm_camera_device_type_t act_device_type;
+ struct msm_sd_subdev msm_sd;
+ enum af_camera_name cam_name;
+ struct mutex *actuator_mutex;
+ struct msm_actuator_func_tbl *func_tbl;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ struct v4l2_subdev sdev;
+ struct v4l2_subdev_ops *act_v4l2_subdev_ops;
+
+ int16_t curr_step_pos;
+ uint16_t curr_region_index;
+ uint16_t *step_position_table;
+ struct region_params_t region_params[MAX_ACTUATOR_REGION];
+ uint16_t reg_tbl_size;
+ struct msm_actuator_reg_params_t reg_tbl[MAX_ACTUATOR_REG_TBL_SIZE];
+ uint16_t region_size;
+ void *user_data;
+ uint32_t total_steps;
+ uint16_t pwd_step;
+ uint16_t initial_code;
+ struct msm_camera_i2c_reg_array *i2c_reg_tbl;
+ uint16_t i2c_tbl_index;
+ enum cci_i2c_master_t cci_master;
+ uint32_t subdev_id;
+ enum msm_actuator_state_t actuator_state;
+ struct msm_actuator_vreg vreg_cfg;
+ struct park_lens_data_t park_lens;
+ uint32_t max_code_size;
+ struct msm_camera_gpio_conf *gconf;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/Makefile b/drivers/media/platform/msm/camera_v2/sensor/cci/Makefile
new file mode 100644
index 0000000..5815bbe
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSM_CCI) += msm_cci.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
new file mode 100644
index 0000000..afd5846
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012-2015, 2018, 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 0x0f73f3f7
+#define CCI_M0_RESET_RMSK 0x3F1
+#define CCI_M1_RESET_RMSK 0x3F001
+#define CCI_QUEUE_START_ADDR 0x00000008
+#define CCI_SET_CID_SYNC_TIMER_ADDR 0x00000010
+#define CCI_SET_CID_SYNC_TIMER_OFFSET 0x00000004
+#define CCI_I2C_M0_SCL_CTL_ADDR 0x00000100
+#define CCI_I2C_M0_SDA_CTL_0_ADDR 0x00000104
+#define CCI_I2C_M0_SDA_CTL_1_ADDR 0x00000108
+#define CCI_I2C_M0_SDA_CTL_2_ADDR 0x0000010c
+#define CCI_I2C_M0_READ_DATA_ADDR 0x00000118
+#define CCI_I2C_M0_MISC_CTL_ADDR 0x00000110
+#define CCI_I2C_M0_READ_BUF_LEVEL_ADDR 0x0000011C
+#define CCI_HALT_REQ_ADDR 0x00000034
+#define CCI_M0_HALT_REQ_RMSK 0x1
+#define CCI_M1_HALT_REQ_RMSK 0x2
+#define CCI_I2C_M1_SCL_CTL_ADDR 0x00000200
+#define CCI_I2C_M1_SDA_CTL_0_ADDR 0x00000204
+#define CCI_I2C_M1_SDA_CTL_1_ADDR 0x00000208
+#define CCI_I2C_M1_SDA_CTL_2_ADDR 0x0000020c
+#define CCI_I2C_M1_MISC_CTL_ADDR 0x00000210
+#define CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR 0x00000304
+#define CCI_I2C_M0_Q0_CUR_CMD_ADDR 0x00000308
+#define CCI_I2C_M0_Q0_REPORT_STATUS_ADDR 0x0000030c
+#define CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR 0x00000300
+#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310
+#define CCI_IRQ_MASK_0_ADDR 0x00000c04
+#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
+#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
+#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
+#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000
+#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000
+#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
+#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
+
+#define DEBUG_TOP_REG_START 0x0
+#define DEBUG_TOP_REG_COUNT 14
+#define DEBUG_MASTER_REG_START 0x100
+#define DEBUG_MASTER_REG_COUNT 8
+#define DEBUG_MASTER_QUEUE_REG_START 0x300
+#define DEBUG_MASTER_QUEUE_REG_COUNT 6
+#define DEBUG_INTR_REG_START 0xC00
+#define DEBUG_INTR_REG_COUNT 7
+#endif /* __MSM_CAM_CCI_HWREG__ */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
new file mode 100644
index 0000000..0f4bd88
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -0,0 +1,2237 @@
+/* Copyright (c) 2012-2017, 2018, 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 "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_cam_cci_hwreg.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "cam_hw_ops.h"
+
+#define V4L2_IDENT_CCI 50005
+#define CCI_I2C_QUEUE_0_SIZE 64
+#define CCI_I2C_Q0_SIZE_128W 128
+#define CCI_I2C_QUEUE_1_SIZE 16
+#define CCI_I2C_Q1_SIZE_32W 32
+#define CYCLES_PER_MICRO_SEC_DEFAULT 4915
+#define CCI_MAX_DELAY 1000000
+
+#define CCI_TIMEOUT msecs_to_jiffies(100)
+
+/* TODO move this somewhere else */
+#define MSM_CCI_DRV_NAME "msm_cci"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#undef CCI_DBG
+#ifdef MSM_CCI_DEBUG
+#define CCI_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CCI_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define CCI_DUMP_REG 0
+
+/* Max bytes that can be read per CCI read transaction */
+#define CCI_READ_MAX 12
+#define CCI_I2C_READ_MAX_RETRIES 3
+#define CCI_I2C_MAX_READ 8192
+#define CCI_I2C_MAX_WRITE 8192
+
+#define PRIORITY_QUEUE (QUEUE_0)
+#define SYNC_QUEUE (QUEUE_1)
+
+static struct v4l2_subdev *g_cci_subdev;
+
+static void msm_cci_dump_registers(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master, enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t i = 0;
+ uint32_t reg_offset = 0;
+
+ /* CCI Top Registers */
+ CCI_DBG(" **** %s : %d CCI TOP Registers ****\n", __func__, __LINE__);
+ for (i = 0; i < DEBUG_TOP_REG_COUNT; i++) {
+ reg_offset = DEBUG_TOP_REG_START + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d Registers ****\n",
+ __func__, __LINE__, master);
+ for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) {
+ if (i == 6)
+ continue;
+ reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Master Queue registers */
+ CCI_DBG(" **** %s : %d CCI MASTER%d QUEUE%d Registers ****\n",
+ __func__, __LINE__, master, queue);
+ for (i = 0; i < DEBUG_MASTER_QUEUE_REG_COUNT; i++) {
+ reg_offset = DEBUG_MASTER_QUEUE_REG_START + master*0x200 +
+ queue*0x100 + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+
+ /* CCI Interrupt registers */
+ CCI_DBG(" **** %s : %d CCI Interrupt Registers ****\n",
+ __func__, __LINE__);
+ for (i = 0; i < DEBUG_INTR_REG_COUNT; i++) {
+ reg_offset = DEBUG_INTR_REG_START + i * 4;
+ read_val = msm_camera_io_r_mb(cci_dev->base + reg_offset);
+ CCI_DBG("%s : %d offset = 0x%X value = 0x%X\n",
+ __func__, __LINE__, reg_offset, read_val);
+ }
+}
+
+static int32_t msm_cci_set_clk_param(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct msm_cci_clk_params_t *clk_params = NULL;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+
+ if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) {
+ pr_err("%s:%d invalid i2c_freq_mode = %d",
+ __func__, __LINE__, i2c_freq_mode);
+ return -EINVAL;
+ }
+
+ if (cci_dev->i2c_freq_mode[master] == i2c_freq_mode)
+ return 0;
+
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+ if (master == MASTER_0) {
+ msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
+ msm_camera_io_w_mb(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_mb(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_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR);
+ } else if (master == MASTER_1) {
+ msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
+ clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
+ msm_camera_io_w_mb(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_mb(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_mb(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
+ msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR);
+ }
+ cci_dev->i2c_freq_mode[master] = i2c_freq_mode;
+ return 0;
+}
+
+static void msm_cci_flush_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master)
+{
+ int32_t rc = 0;
+
+ msm_camera_io_w_mb(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
+}
+
+static int32_t 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;
+ unsigned long flags;
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ if ((read_val + len + 1) > cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) {
+ uint32_t reg_val = 0;
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ msm_camera_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d, queue: %d\n",
+ __func__, __LINE__, read_val, queue);
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ reg_val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ CDBG("%s line %d wait_for_completion_timeout\n",
+ __func__, __LINE__);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ msm_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0)
+ pr_err("%s failed rc %d\n", __func__, rc);
+ }
+ return rc;
+}
+
+static int32_t 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;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ 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 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ return rc;
+}
+
+static uint32_t msm_cci_wait(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+
+ if (!cci_dev) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].report_q[queue], CCI_TIMEOUT);
+ CDBG("%s line %d wait DONE_for_completion_timeout\n",
+ __func__, __LINE__);
+
+ if (rc <= 0) {
+ if (CCI_DUMP_REG)
+ msm_cci_dump_registers(cci_dev, master, queue);
+
+ pr_err("%s: %d wait for queue: %d\n",
+ __func__, __LINE__, queue);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ msm_cci_flush_queue(cci_dev, master);
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int32_t msm_cci_addr_to_num_bytes(
+ enum msm_camera_i2c_reg_addr_type addr_type)
+{
+ int32_t retVal;
+
+ switch (addr_type) {
+ case MSM_CAMERA_I2C_BYTE_ADDR:
+ retVal = 1;
+ break;
+ case MSM_CAMERA_I2C_WORD_ADDR:
+ retVal = 2;
+ break;
+ case MSM_CAMERA_I2C_3B_ADDR:
+ retVal = 3;
+ break;
+ case MSM_CAMERA_I2C_DWORD_ADDR:
+ retVal = 4;
+ break;
+ default:
+ pr_err("%s: %d failed: %d\n", __func__, __LINE__, addr_type);
+ retVal = 1;
+ break;
+ }
+ return retVal;
+}
+
+static int32_t msm_cci_data_to_num_bytes(
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t retVal;
+
+ switch (data_type) {
+ case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ retVal = 1;
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ retVal = 2;
+ break;
+ case MSM_CAMERA_I2C_DWORD_DATA:
+ retVal = 4;
+ break;
+ default:
+ pr_err("%s: %d failed: %d\n", __func__, __LINE__, data_type);
+ retVal = 1;
+ break;
+ }
+ return retVal;
+}
+
+static int32_t msm_cci_calc_cmd_len(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, uint32_t cmd_size,
+ struct msm_camera_i2c_reg_array *i2c_cmd, uint32_t *pack)
+{
+ uint8_t i;
+ uint32_t len = 0;
+ uint8_t data_len = 0, addr_len = 0;
+ uint8_t pack_max_len;
+ struct msm_camera_i2c_reg_setting *msg;
+ struct msm_camera_i2c_reg_array *cmd = i2c_cmd;
+ uint32_t size = cmd_size;
+
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ msg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ *pack = 0;
+
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) {
+ addr_len = msm_cci_addr_to_num_bytes(msg->addr_type);
+ len = (size + addr_len) <= (cci_dev->payload_size) ?
+ (size + addr_len):cci_dev->payload_size;
+ } else {
+ addr_len = msm_cci_addr_to_num_bytes(msg->addr_type);
+ data_len = msm_cci_data_to_num_bytes(msg->data_type);
+ len = data_len + addr_len;
+ pack_max_len = size < (cci_dev->payload_size-len) ?
+ size : (cci_dev->payload_size-len);
+ for (i = 0; i < pack_max_len;) {
+ if (cmd->delay || ((cmd - i2c_cmd) >= (cmd_size - 1)))
+ break;
+ if (cmd->reg_addr + 1 ==
+ (cmd+1)->reg_addr) {
+ len += data_len;
+ if (len > cci_dev->payload_size) {
+ len = len - data_len;
+ break;
+ }
+ *pack += data_len;
+ } else
+ break;
+ i += data_len;
+ cmd++;
+ }
+ }
+
+ if (len > cci_dev->payload_size) {
+ pr_err("Len error: %d", len);
+ return -EINVAL;
+ }
+
+ len += 1; /*add i2c WR command*/
+ len = len/4 + 1;
+
+ return len;
+}
+
+static void msm_cci_load_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ uint32_t read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+
+ CDBG("%s:%d CCI_I2C_REPORT_CMD curr_w_cnt: %d\n",
+ __func__, __LINE__, read_val);
+ msm_camera_io_w_mb(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n",
+ __func__, __LINE__, read_val);
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+}
+
+static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ unsigned long flags;
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ msm_cci_load_report_cmd(cci_dev, master, queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ return msm_cci_wait(cci_dev, master, queue);
+}
+
+static void msm_cci_process_half_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ unsigned long flags;
+ uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
+ msm_cci_load_report_cmd(cci_dev, master, queue);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ msm_camera_io_w_mb(reg_val, cci_dev->base +
+ CCI_QUEUE_START_ADDR);
+ }
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+}
+
+static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 1) {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ rc = msm_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_cci_lock_queue(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue, uint32_t en)
+{
+ uint32_t val;
+
+ if (queue != PRIORITY_QUEUE)
+ return 0;
+
+ val = en ? CCI_I2C_LOCK_CMD : CCI_I2C_UNLOCK_CMD;
+ return msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+}
+
+static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ } else {
+ atomic_set(&cci_dev->cci_master_info[master].
+ done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ rc = msm_cci_wait(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_cci_get_queue_free_size(struct cci_device *cci_dev,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d max %d\n",
+ __func__, __LINE__, read_val,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ return (cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) -
+ read_val;
+}
+
+static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
+ int32_t rc = 0, free_size = 0, en_seq_write = 0;
+ uint32_t cmd = 0, delay = 0;
+ uint8_t data[12];
+ uint16_t reg_addr = 0;
+ struct msm_camera_i2c_reg_setting *i2c_msg =
+ &c_ctrl->cfg.cci_i2c_write_cfg;
+ uint16_t cmd_size = i2c_msg->size;
+ struct msm_camera_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+
+ uint32_t read_val = 0;
+ uint32_t reg_offset;
+ uint32_t val = 0;
+ uint32_t max_queue_size;
+ unsigned long flags;
+
+ if (i2c_cmd == NULL) {
+ pr_err("%s:%d Failed line\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) {
+ pr_err("%s:%d failed: invalid cmd_size %d\n",
+ __func__, __LINE__, cmd_size);
+ return -EINVAL;
+ }
+
+ CDBG("%s addr type %d data type %d cmd_size %d\n", __func__,
+ i2c_msg->addr_type, i2c_msg->data_type, cmd_size);
+
+ if (i2c_msg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid addr_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->addr_type);
+ return -EINVAL;
+ }
+ if (i2c_msg->data_type >= MSM_CAMERA_I2C_DATA_TYPE_MAX) {
+ pr_err("%s:%d failed: invalid data_type 0x%X\n",
+ __func__, __LINE__, i2c_msg->data_type);
+ return -EINVAL;
+ }
+ reg_offset = master * 0x200 + queue * 0x100;
+
+ msm_camera_io_w_mb(cci_dev->cci_wait_sync_cfg.cid,
+ cci_dev->base + CCI_SET_CID_SYNC_TIMER_ADDR +
+ cci_dev->cci_wait_sync_cfg.csid *
+ CCI_SET_CID_SYNC_TIMER_OFFSET);
+
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+ atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+
+ max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
+ max_queue_size;
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (sync_en == MSM_SYNC_ENABLE && cci_dev->valid_sync &&
+ cmd_size < max_queue_size) {
+ val = CCI_I2C_WAIT_SYNC_CMD |
+ ((cci_dev->cci_wait_sync_cfg.line) << 4);
+ msm_camera_io_w_mb(val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ }
+
+ rc = msm_cci_lock_queue(cci_dev, master, queue, 1);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ while (cmd_size) {
+ uint32_t pack = 0;
+
+ len = msm_cci_calc_cmd_len(cci_dev, c_ctrl, cmd_size,
+ i2c_cmd, &pack);
+ if (len <= 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len, max_queue_size);
+ /* + 1 - space alocation for Report CMD*/
+ if ((read_val + len + 1) > max_queue_size/2) {
+ if ((read_val + len + 1) > max_queue_size) {
+ rc = msm_cci_process_full_q(cci_dev,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ continue;
+ }
+ msm_cci_process_half_q(cci_dev, master, queue);
+ }
+
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__,
+ cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
+ delay = i2c_cmd->delay;
+ i = 0;
+ data[i++] = CCI_I2C_WRITE_CMD;
+
+ /* in case of multiple command
+ * MSM_CCI_I2C_WRITE : address is not continuous, so update
+ * address for a new packet.
+ * MSM_CCI_I2C_WRITE_SEQ : address is continuous, need to keep
+ * the incremented address for a
+ * new packet
+ */
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_ASYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_SYNC_BLOCK)
+ reg_addr = i2c_cmd->reg_addr;
+
+ if (en_seq_write == 0) {
+ /* either byte or word addr */
+ if (i2c_msg->addr_type == 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) <= cci_dev->payload_size) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr++;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ --cmd_size;
+ } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) &&
+ (cmd_size > 0) && (i <= cci_dev->payload_size));
+ free_size = msm_cci_get_queue_free_size(cci_dev, master,
+ queue);
+ if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) &&
+ ((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) &&
+ cci_dev->support_seq_write && cmd_size > 0 &&
+ free_size > BURST_MIN_FREE_SIZE) {
+ data[0] |= 0xF0;
+ en_seq_write = 1;
+ } else {
+ data[0] |= ((i-1) << 4);
+ en_seq_write = 0;
+ }
+ len = ((i-1)/4) + 1;
+
+ read_val = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ for (h = 0, k = 0; h < len; h++) {
+ cmd = 0;
+ for (j = 0; (j < 4 && k < i); j++)
+ cmd |= (data[k++] << (j * 8));
+ CDBG("%s LOAD_DATA_ADDR 0x%x, q: %d, len:%d, cnt: %d\n",
+ __func__, cmd, queue, len, read_val);
+ msm_camera_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ read_val += 1;
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+
+ if ((delay > 0) && (delay < CCI_MAX_DELAY) &&
+ en_seq_write == 0) {
+ cmd = (uint32_t)((delay * cci_dev->cycles_per_us) /
+ 0x100);
+ cmd <<= 4;
+ cmd |= CCI_I2C_WAIT_CMD;
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
+ __func__, cmd);
+ msm_camera_io_w_mb(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+ read_val += 1;
+ msm_camera_io_w_mb(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ }
+ }
+
+ rc = msm_cci_transfer_end(cci_dev, master, queue);
+ if (rc < 0) {
+ pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ int32_t read_words = 0, exp_words = 0;
+ int32_t index = 0, first_byte = 0;
+ uint32_t i = 0;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_1;
+ struct cci_device *cci_dev = NULL;
+ struct 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;
+
+ if (master >= MASTER_MAX || master < 0) {
+ pr_err("%s:%d Invalid I2C master %d\n",
+ __func__, __LINE__, master);
+ return -EINVAL;
+ }
+
+ mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+
+ /* Set the I2C Frequency */
+ rc = msm_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->data == NULL) {
+ pr_err("%s:%d Data ptr is NULL\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+
+ 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_ADDR_TYPE_MAX) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4);
+ for (i = 0; i < read_cfg->addr_type; i++) {
+ val |= ((read_cfg->addr >> (i << 3)) & 0xFF) <<
+ ((read_cfg->addr_type - i) << 3);
+ }
+
+ rc = 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_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+ CDBG("%s cur word cnt 0x%x\n", __func__, val);
+ msm_camera_io_w_mb(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_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
+ __LINE__);
+
+ rc = wait_for_completion_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ if (CCI_DUMP_REG)
+ msm_cci_dump_registers(cci_dev, master, queue);
+
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ pr_err("%s: %d wait_for_completion_timeout rc = %d\n",
+ __func__, __LINE__, rc);
+ msm_cci_flush_queue(cci_dev, master);
+ goto ERROR;
+ } else {
+ rc = 0;
+ }
+
+ read_words = msm_camera_io_r_mb(cci_dev->base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ exp_words = ((read_cfg->num_byte / 4) + 1);
+ if (read_words != exp_words) {
+ pr_err("%s:%d read_words = %d, exp words = %d\n", __func__,
+ __LINE__, read_words, exp_words);
+ memset(read_cfg->data, 0, read_cfg->num_byte);
+ rc = -EINVAL;
+ goto 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_mb(cci_dev->base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ CDBG("%s read val 0x%x\n", __func__, val);
+ for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) {
+ CDBG("%s i %d index %d\n", __func__, i, index);
+ if (!first_byte) {
+ CDBG("%s sid 0x%x\n", __func__, val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CDBG("%s data[%d] 0x%x\n", __func__, index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read_bytes(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev = NULL;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
+ uint16_t read_bytes = 0;
+
+ if (!sd || !c_ctrl) {
+ pr_err("%s:%d sd %pK c_ctrl %pK\n", __func__,
+ __LINE__, sd, c_ctrl);
+ return -EINVAL;
+ }
+ if (!c_ctrl->cci_info) {
+ pr_err("%s:%d cci_info NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev) {
+ pr_err("%s:%d cci_dev NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) {
+ pr_err("%s:%d read num bytes 0\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ read_bytes = read_cfg->num_byte;
+ do {
+ if (read_bytes > CCI_READ_MAX)
+ read_cfg->num_byte = CCI_READ_MAX;
+ else
+ read_cfg->num_byte = read_bytes;
+ rc = msm_cci_i2c_read(sd, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ if (read_bytes > CCI_READ_MAX) {
+ read_cfg->addr += CCI_READ_MAX;
+ read_cfg->data += CCI_READ_MAX;
+ read_bytes -= CCI_READ_MAX;
+ } else {
+ read_bytes = 0;
+ }
+ } while (read_bytes);
+ERROR:
+ return rc;
+}
+
+static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+
+ /* Set the I2C Frequency */
+ rc = msm_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_cci_set_clk_param failed rc = %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_write call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size-1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ pr_err("%s:%d More than max retries\n", __func__,
+ __LINE__);
+ goto ERROR;
+ }
+ rc = msm_cci_data_queue(cci_dev, c_ctrl, queue, sync_en);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ERROR:
+ return rc;
+}
+
+static void msm_cci_write_async_helper(struct work_struct *work)
+{
+ int rc;
+ struct cci_device *cci_dev;
+ struct cci_write_async *write_async =
+ container_of(work, struct cci_write_async, work);
+ struct msm_camera_i2c_reg_setting *i2c_msg;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_dev = write_async->cci_dev;
+ i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+ master = write_async->c_ctrl.cci_info->cci_i2c_master;
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ mutex_lock(&cci_master_info->mutex_q[write_async->queue]);
+ rc = msm_cci_i2c_write(&cci_dev->msm_sd.sd,
+ &write_async->c_ctrl, write_async->queue, write_async->sync_en);
+ mutex_unlock(&cci_master_info->mutex_q[write_async->queue]);
+ if (rc < 0)
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+
+ kfree(write_async->c_ctrl.cfg.cci_i2c_write_cfg.reg_setting);
+ kfree(write_async);
+
+ CDBG("%s: %d Exit\n", __func__, __LINE__);
+}
+
+static int32_t msm_cci_i2c_write_async(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue,
+ enum cci_i2c_sync sync_en)
+{
+ struct cci_write_async *write_async;
+ struct cci_device *cci_dev;
+ struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg;
+ struct msm_camera_i2c_reg_setting *cci_i2c_write_cfg_w;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ CDBG("%s: %d Enter\n", __func__, __LINE__);
+
+ write_async = kzalloc(sizeof(*write_async), GFP_KERNEL);
+ if (!write_async)
+ return -ENOMEM;
+
+ INIT_WORK(&write_async->work, msm_cci_write_async_helper);
+ write_async->cci_dev = cci_dev;
+ write_async->c_ctrl = *c_ctrl;
+ write_async->queue = queue;
+ write_async->sync_en = sync_en;
+
+ cci_i2c_write_cfg = &c_ctrl->cfg.cci_i2c_write_cfg;
+ cci_i2c_write_cfg_w = &write_async->c_ctrl.cfg.cci_i2c_write_cfg;
+
+ if (cci_i2c_write_cfg->size == 0) {
+ pr_err("%s: %d Size = 0\n", __func__, __LINE__);
+ kfree(write_async);
+ return -EINVAL;
+ }
+
+ cci_i2c_write_cfg_w->reg_setting =
+ kzalloc(sizeof(struct msm_camera_i2c_reg_array)*
+ cci_i2c_write_cfg->size, GFP_KERNEL);
+ if (!cci_i2c_write_cfg_w->reg_setting) {
+ pr_err("%s: %d Couldn't allocate memory\n", __func__, __LINE__);
+ kfree(write_async);
+ return -ENOMEM;
+ }
+ memcpy(cci_i2c_write_cfg_w->reg_setting,
+ cci_i2c_write_cfg->reg_setting,
+ (sizeof(struct msm_camera_i2c_reg_array)*
+ cci_i2c_write_cfg->size));
+
+ cci_i2c_write_cfg_w->addr_type = cci_i2c_write_cfg->addr_type;
+ cci_i2c_write_cfg_w->data_type = cci_i2c_write_cfg->data_type;
+ cci_i2c_write_cfg_w->size = cci_i2c_write_cfg->size;
+ cci_i2c_write_cfg_w->delay = cci_i2c_write_cfg->delay;
+
+ queue_work(cci_dev->write_wq[write_async->queue], &write_async->work);
+
+ CDBG("%s: %d Exit\n", __func__, __LINE__);
+
+ return 0;
+}
+
+static int32_t msm_cci_pinctrl_init(struct cci_device *cci_dev)
+{
+ struct msm_pinctrl_info *cci_pctrl = NULL;
+
+ cci_pctrl = &cci_dev->cci_pinctrl;
+ cci_pctrl->pinctrl = devm_pinctrl_get(&cci_dev->pdev->dev);
+ if (IS_ERR_OR_NULL(cci_pctrl->pinctrl)) {
+ pr_err("%s:%d devm_pinctrl_get cci_pinctrl failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_active = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_active)) {
+ pr_err("%s:%d look up state for active state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ cci_pctrl->gpio_state_suspend = pinctrl_lookup_state(
+ cci_pctrl->pinctrl,
+ CCI_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_suspend)) {
+ pr_err("%s:%d look up state for suspend state failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static uint32_t msm_cci_cycles_per_ms(unsigned long clk)
+{
+ uint32_t cycles_per_us;
+
+ if (clk)
+ cycles_per_us = ((clk/1000)*256)/1000;
+ else {
+ pr_err("%s:%d, failed: Can use default: %d",
+ __func__, __LINE__, CYCLES_PER_MICRO_SEC_DEFAULT);
+ cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ }
+ return cycles_per_us;
+}
+
+static uint32_t *msm_cci_get_clk_rates(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint32_t j;
+ int32_t idx;
+ uint32_t cci_clk_src;
+ unsigned long clk;
+
+ struct msm_cci_clk_params_t *clk_params = NULL;
+ enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+
+ if ((i2c_freq_mode >= I2C_MAX_MODES) || (i2c_freq_mode < 0)) {
+ pr_err("%s:%d invalid i2c_freq_mode %d\n",
+ __func__, __LINE__, i2c_freq_mode);
+ return NULL;
+ }
+
+ clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];
+ cci_clk_src = clk_params->cci_clk_src;
+
+ idx = of_property_match_string(of_node,
+ "clock-names", CCI_CLK_SRC_NAME);
+ if (idx < 0) {
+ cci_dev->cycles_per_us = CYCLES_PER_MICRO_SEC_DEFAULT;
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ if (cci_clk_src == 0) {
+ clk = cci_dev->cci_clk_rates[0][idx];
+ cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk);
+ return cci_dev->cci_clk_rates[0];
+ }
+
+ for (j = 0; j < cci_dev->num_clk_cases; j++) {
+ clk = cci_dev->cci_clk_rates[j][idx];
+ if (clk == cci_clk_src) {
+ cci_dev->cycles_per_us = msm_cci_cycles_per_ms(clk);
+ cci_dev->cci_clk_src = cci_clk_src;
+ return cci_dev->cci_clk_rates[j];
+ }
+ }
+
+ return NULL;
+}
+
+static int32_t msm_cci_i2c_set_sync_prms(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+ cci_dev->cci_wait_sync_cfg = c_ctrl->cfg.cci_wait_sync_cfg;
+ cci_dev->valid_sync = cci_dev->cci_wait_sync_cfg.csid < 0 ? 0 : 1;
+
+ return rc;
+}
+
+static int32_t msm_cci_init(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint8_t i = 0, j = 0;
+ int32_t rc = 0, ret = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master = MASTER_0;
+ uint32_t *clk_rates = NULL;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ if (cci_dev->ref_count++) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s:%d master %d\n", __func__, __LINE__, master);
+ if (master < MASTER_MAX && master >= 0) {
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ mutex_lock(&cci_dev->cci_master_info[master].
+ mutex_q[PRIORITY_QUEUE]);
+ mutex_lock(&cci_dev->cci_master_info[master].
+ mutex_q[SYNC_QUEUE]);
+ flush_workqueue(cci_dev->write_wq[master]);
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->
+ cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->
+ cci_master_info[master].report_q[i]);
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+ /* Set proper mask to RESET CMD address */
+ if (master == MASTER_0)
+ msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ /* wait for reset done irq */
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].
+ reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__,
+ __LINE__, rc);
+ mutex_unlock(&cci_dev->cci_master_info[master].
+ mutex_q[SYNC_QUEUE]);
+ mutex_unlock(&cci_dev->cci_master_info[master].
+ mutex_q[PRIORITY_QUEUE]);
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ }
+ return 0;
+ }
+ ret = msm_cci_pinctrl_init(cci_dev);
+ if (ret < 0) {
+ pr_err("%s:%d Initialization of pinctrl failed\n",
+ __func__, __LINE__);
+ cci_dev->cci_pinctrl_status = 0;
+ } else {
+ cci_dev->cci_pinctrl_status = 1;
+ }
+ rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 1);
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_active);
+ if (ret)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ if (rc < 0) {
+ CDBG("%s: request gpio failed\n", __func__);
+ goto request_gpio_failed;
+ }
+
+ rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci config_vreg failed\n", __func__, __LINE__);
+ goto clk_enable_failed;
+ }
+
+ rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d cci enable_vreg failed\n", __func__, __LINE__);
+ goto reg_enable_failed;
+ }
+
+ clk_rates = msm_cci_get_clk_rates(cci_dev, c_ctrl);
+ if (!clk_rates) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ for (i = 0; i < cci_dev->num_clk; i++) {
+ cci_dev->cci_clk_info[i].clk_rate =
+ clk_rates[i];
+ }
+ rc = msm_camera_clk_enable(&cci_dev->pdev->dev,
+ cci_dev->cci_clk_info, cci_dev->cci_clk,
+ cci_dev->num_clk, true);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n", __func__);
+ goto reg_enable_failed;
+ }
+
+ /* Re-initialize the completion */
+ reinit_completion(&cci_dev->cci_master_info[master].reset_complete);
+ for (i = 0; i < NUM_QUEUES; i++)
+ reinit_completion(&cci_dev->cci_master_info[master].
+ report_q[i]);
+ rc = msm_camera_enable_irq(cci_dev->irq, true);
+ if (rc < 0)
+ pr_err("%s: irq enable failed\n", __func__);
+ cci_dev->hw_version = msm_camera_io_r_mb(cci_dev->base +
+ CCI_HW_VERSION_ADDR);
+ pr_info("%s:%d: hw_version = 0x%x\n", __func__, __LINE__,
+ cci_dev->hw_version);
+ cci_dev->payload_size =
+ MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10;
+ cci_dev->support_seq_write = 0;
+ if (cci_dev->hw_version >= 0x10020000) {
+ cci_dev->payload_size =
+ MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
+ cci_dev->support_seq_write = 1;
+ }
+ for (i = 0; i < NUM_MASTERS; i++) {
+ for (j = 0; j < NUM_QUEUES; j++) {
+ if (j == QUEUE_0) {
+ if (cci_dev->hw_version >= 0x10060000)
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_Q0_SIZE_128W;
+ else
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_0_SIZE;
+ } else {
+ if (cci_dev->hw_version >= 0x10060000)
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_Q1_SIZE_32W;
+ else
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size =
+ CCI_I2C_QUEUE_1_SIZE;
+ }
+ CDBG("CCI Master[%d] :: Q0 size: %d Q1 size: %d\n", i,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size,
+ cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size);
+ }
+ }
+
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w_mb(CCI_RESET_CMD_RMSK, cci_dev->base +
+ CCI_RESET_CMD_ADDR);
+ msm_camera_io_w_mb(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
+ rc = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[MASTER_0].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ goto reset_complete_failed;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_MASK_0_ADDR);
+ msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ for (i = 0; i < MASTER_MAX; i++) {
+ if (!cci_dev->write_wq[i]) {
+ pr_err("Failed to flush write wq\n");
+ rc = -ENOMEM;
+ goto reset_complete_failed;
+ } else {
+ flush_workqueue(cci_dev->write_wq[i]);
+ }
+ }
+ cci_dev->cci_state = CCI_STATE_ENABLED;
+
+ return 0;
+
+reset_complete_failed:
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info,
+ cci_dev->cci_clk, cci_dev->num_clk, false);
+reg_enable_failed:
+ msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+clk_enable_failed:
+ if (cci_dev->cci_pinctrl_status) {
+ ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state\n",
+ __func__, __LINE__);
+ }
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+request_gpio_failed:
+ cci_dev->ref_count--;
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+static int32_t msm_cci_release(struct v4l2_subdev *sd)
+{
+ int32_t rc = 0;
+ uint8_t i = 0;
+ 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);
+ rc = -EINVAL;
+ goto ahb_vote_suspend;
+ }
+ if (--cci_dev->ref_count) {
+ CDBG("%s ref_count Exit %d\n", __func__, cci_dev->ref_count);
+ rc = 0;
+ goto ahb_vote_suspend;
+ }
+ for (i = 0; i < MASTER_MAX; i++)
+ if (cci_dev->write_wq[i])
+ flush_workqueue(cci_dev->write_wq[i]);
+
+ msm_camera_enable_irq(cci_dev->irq, false);
+ msm_camera_clk_enable(&cci_dev->pdev->dev, cci_dev->cci_clk_info,
+ cci_dev->cci_clk, cci_dev->num_clk, false);
+
+ rc = msm_camera_enable_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci disable_vreg failed\n", __func__, __LINE__);
+
+ rc = msm_camera_config_vreg(&cci_dev->pdev->dev, cci_dev->cci_vreg,
+ cci_dev->regulator_count, NULL, 0, &cci_dev->cci_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d cci unconfig_vreg failed\n", __func__, __LINE__);
+
+ if (cci_dev->cci_pinctrl_status) {
+ rc = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
+ cci_dev->cci_pinctrl.gpio_state_suspend);
+ if (rc)
+ pr_err("%s:%d cannot set pin to active state\n",
+ __func__, __LINE__);
+ }
+ cci_dev->cci_pinctrl_status = 0;
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+ for (i = 0; i < MASTER_MAX; i++)
+ cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
+ cci_dev->cci_state = CCI_STATE_DISABLED;
+ cci_dev->cycles_per_us = 0;
+ cci_dev->cci_clk_src = 0;
+
+ahb_vote_suspend:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CCI,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return rc;
+}
+
+static int32_t msm_cci_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ enum cci_i2c_master_t master;
+ struct msm_camera_cci_master_info *cci_master_info;
+ uint32_t i;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ if (!cci_dev || !c_ctrl) {
+ pr_err("%s:%d failed: invalid params %pK %pK\n", __func__,
+ __LINE__, cci_dev, c_ctrl);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ if (cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid cci state %d\n",
+ __func__, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ master = c_ctrl->cci_info->cci_i2c_master;
+ cci_master_info = &cci_dev->cci_master_info[master];
+
+ switch (c_ctrl->cmd) {
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ mutex_lock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ rc = msm_cci_i2c_write(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ mutex_unlock(&cci_master_info->mutex_q[SYNC_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_SYNC:
+ rc = msm_cci_i2c_write_async(sd, c_ctrl,
+ SYNC_QUEUE, MSM_SYNC_ENABLE);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ for (i = 0; i < NUM_QUEUES; i++) {
+ if (mutex_trylock(&cci_master_info->mutex_q[i])) {
+ rc = msm_cci_i2c_write(sd, c_ctrl, i,
+ MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[i]);
+ return rc;
+ }
+ }
+ mutex_lock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ rc = msm_cci_i2c_write(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ mutex_unlock(&cci_master_info->mutex_q[PRIORITY_QUEUE]);
+ break;
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ rc = msm_cci_i2c_write_async(sd, c_ctrl,
+ PRIORITY_QUEUE, MSM_SYNC_DISABLE);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ return rc;
+}
+
+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, cci_ctrl);
+ break;
+ case MSM_CCI_RELEASE:
+ rc = msm_cci_release(sd);
+ break;
+ case MSM_CCI_I2C_READ:
+ rc = msm_cci_i2c_read_bytes(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ case MSM_CCI_I2C_WRITE_SEQ:
+ case MSM_CCI_I2C_WRITE_SYNC:
+ case MSM_CCI_I2C_WRITE_ASYNC:
+ case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
+ rc = msm_cci_write(sd, cci_ctrl);
+ break;
+ case MSM_CCI_GPIO_WRITE:
+ break;
+ case MSM_CCI_SET_SYNC_CID:
+ rc = msm_cci_i2c_set_sync_prms(sd, cci_ctrl);
+ 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;
+ unsigned long flags;
+ struct cci_device *cci_dev = data;
+
+ irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
+ msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w_mb(0x1, 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);
+ }
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
+ atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_0]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
+ }
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
+ struct msm_camera_cci_master_info *cci_master_info;
+
+ cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
+ atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
+ cci_master_info->status = 0;
+ if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
+ complete(&cci_master_info->report_q[QUEUE_1]);
+ atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
+ }
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
+ }
+ 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_mb(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
+ msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_0 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w_mb(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_1 error 0x%x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w_mb(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ }
+ 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;
+ case MSM_SD_NOTIFY_FREEZE:
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ break;
+ case MSM_SD_SHUTDOWN: {
+ struct msm_camera_cci_ctrl ctrl_cmd;
+
+ ctrl_cmd.cmd = MSM_CCI_RELEASE;
+ rc = msm_cci_config(sd, &ctrl_cmd);
+ 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 = {
+ .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++) {
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].report_q[j]);
+ spin_lock_init(&new_cci_dev->
+ cci_master_info[i].lock_q[j]);
+ }
+ }
+}
+
+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 = kcalloc(tbl_size, sizeof(uint32_t), GFP_KERNEL);
+ if (!val_array) {
+ 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_default_clk_params(struct cci_device *cci_dev,
+ uint8_t index)
+{
+ /* default clock params are for 100Khz */
+ cci_dev->cci_clk_params[index].hw_thigh = 201;
+ cci_dev->cci_clk_params[index].hw_tlow = 174;
+ cci_dev->cci_clk_params[index].hw_tsu_sto = 204;
+ cci_dev->cci_clk_params[index].hw_tsu_sta = 231;
+ cci_dev->cci_clk_params[index].hw_thd_dat = 22;
+ cci_dev->cci_clk_params[index].hw_thd_sta = 162;
+ cci_dev->cci_clk_params[index].hw_tbuf = 227;
+ cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0;
+ cci_dev->cci_clk_params[index].hw_trdhld = 6;
+ cci_dev->cci_clk_params[index].hw_tsp = 3;
+ cci_dev->cci_clk_params[index].cci_clk_src = 37500000;
+}
+
+static void msm_cci_init_clk_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ uint8_t count = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+ struct device_node *src_node = NULL;
+
+ for (count = 0; count < I2C_MAX_MODES; count++) {
+
+ if (count == I2C_STANDARD_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_standard_mode");
+ else if (count == I2C_FAST_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_mode");
+ else if (count == I2C_FAST_PLUS_MODE)
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_fast_plus_mode");
+ else
+ src_node = of_find_node_by_name(of_node,
+ "qcom,i2c_custom_mode");
+
+ rc = of_property_read_u32(src_node, "qcom,hw-thigh", &val);
+ CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thigh = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tlow",
+ &val);
+ CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tlow = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sto",
+ &val);
+ CDBG("%s qcom,hw-tsu-sto %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sto = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsu-sta",
+ &val);
+ CDBG("%s qcom,hw-tsu-sta %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsu_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-dat",
+ &val);
+ CDBG("%s qcom,hw-thd-dat %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_dat = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-thd-sta",
+ &val);
+ CDBG("%s qcom,hw-thd-sta %d, rc %d\n", __func__,
+ val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_thd_sta = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tbuf",
+ &val);
+ CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tbuf = val;
+ rc = of_property_read_u32(src_node,
+ "qcom,hw-scl-stretch-en", &val);
+ CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_scl_stretch_en = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-trdhld",
+ &val);
+ CDBG("%s qcom,hw-trdhld %d, rc %d\n",
+ __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_trdhld = val;
+ rc = of_property_read_u32(src_node, "qcom,hw-tsp",
+ &val);
+ CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
+ }
+ if (!rc) {
+ cci_dev->cci_clk_params[count].hw_tsp = val;
+ val = 0;
+ rc = of_property_read_u32(src_node, "qcom,cci-clk-src",
+ &val);
+ CDBG("%s qcom,cci-clk-src %d, rc %d\n",
+ __func__, val, rc);
+ cci_dev->cci_clk_params[count].cci_clk_src = val;
+ } else {
+ msm_cci_init_default_clk_params(cci_dev, count);
+ }
+
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+}
+
+struct v4l2_subdev *msm_cci_get_subdev(void)
+{
+ return g_cci_subdev;
+}
+
+static int msm_cci_probe(struct platform_device *pdev)
+{
+ struct cci_device *new_cci_dev;
+ int rc = 0, i = 0;
+
+ CDBG("%s: pdev %pK device id = %d\n", __func__, pdev, pdev->id);
+ new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL);
+ if (!new_cci_dev) {
+ pr_err("%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 %pK\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);
+
+ rc = msm_camera_get_clk_info_and_rates(pdev,
+ &new_cci_dev->cci_clk_info, &new_cci_dev->cci_clk,
+ &new_cci_dev->cci_clk_rates, &new_cci_dev->num_clk_cases,
+ &new_cci_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: msm_cci_get_clk_info() failed", __func__);
+ kfree(new_cci_dev);
+ return -EFAULT;
+ }
+
+ new_cci_dev->ref_count = 0;
+ new_cci_dev->base = msm_camera_get_reg_base(pdev, "cci", true);
+ if (!new_cci_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->irq = msm_camera_get_irq(pdev, "cci");
+ if (!new_cci_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ CDBG("%s line %d cci irq start %d end %d\n", __func__,
+ __LINE__,
+ (int) new_cci_dev->irq->start,
+ (int) new_cci_dev->irq->end);
+ rc = msm_camera_register_irq(pdev, new_cci_dev->irq,
+ msm_cci_irq, IRQF_TRIGGER_RISING, "cci", new_cci_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto cci_release_mem;
+ }
+
+ msm_camera_enable_irq(new_cci_dev->irq, false);
+ new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
+ 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 = msm_camera_get_dt_vreg_data(new_cci_dev->pdev->dev.of_node,
+ &(new_cci_dev->cci_vreg), &(new_cci_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__);
+ rc = -EFAULT;
+ goto cci_release_mem;
+ }
+
+ if ((new_cci_dev->regulator_count < 0) ||
+ (new_cci_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_cci_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto cci_invalid_vreg_data;
+ }
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc)
+ pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc);
+ new_cci_dev->cci_state = CCI_STATE_DISABLED;
+ g_cci_subdev = &new_cci_dev->msm_sd.sd;
+ for (i = 0; i < MASTER_MAX; i++) {
+ new_cci_dev->write_wq[i] = create_singlethread_workqueue(
+ "msm_cci_wq");
+ if (!new_cci_dev->write_wq[i])
+ pr_err("Failed to create write wq\n");
+ }
+ CDBG("%s cci subdev %pK\n", __func__, &new_cci_dev->msm_sd.sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+ return 0;
+
+cci_invalid_vreg_data:
+ kfree(new_cci_dev->cci_vreg);
+cci_release_mem:
+ msm_camera_put_reg_base(pdev, new_cci_dev->base, "cci", true);
+cci_no_resource:
+ kfree(new_cci_dev);
+ return rc;
+}
+
+static int msm_cci_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct cci_device *cci_dev =
+ v4l2_get_subdevdata(subdev);
+
+ msm_camera_put_clk_info_and_rates(pdev,
+ &cci_dev->cci_clk_info, &cci_dev->cci_clk,
+ &cci_dev->cci_clk_rates, cci_dev->num_clk_cases,
+ cci_dev->num_clk);
+
+ msm_camera_put_reg_base(pdev, cci_dev->base, "cci", true);
+ 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/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
new file mode 100644
index 0000000..c997ca9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
@@ -0,0 +1,239 @@
+/* Copyright (c) 2012-2018, 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 <linux/workqueue.h>
+#include <media/msm_cam_sensor.h>
+#include <soc/qcom/camera2.h>
+#include "msm_sd.h"
+#include "cam_soc_api.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+#define CCI_PINCTRL_STATE_DEFAULT "cci_default"
+#define CCI_PINCTRL_STATE_SLEEP "cci_suspend"
+
+#define CCI_NUM_CLK_MAX 16
+#define CCI_NUM_CLK_CASES 5
+#define CCI_CLK_SRC_NAME "cci_src_clk"
+#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_10 10
+#define MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11 11
+#define BURST_MIN_FREE_SIZE 8
+
+enum cci_i2c_sync {
+ MSM_SYNC_DISABLE,
+ MSM_SYNC_ENABLE,
+};
+
+enum cci_i2c_queue_t {
+ QUEUE_0,
+ QUEUE_1,
+ QUEUE_INVALID,
+};
+
+struct msm_camera_cci_client {
+ struct v4l2_subdev *cci_subdev;
+ uint32_t freq;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum cci_i2c_master_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t timeout;
+ uint16_t retries;
+ uint16_t id_map;
+};
+
+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_I2C_WRITE_SEQ,
+ MSM_CCI_I2C_WRITE_ASYNC,
+ MSM_CCI_GPIO_WRITE,
+ MSM_CCI_I2C_WRITE_SYNC,
+ MSM_CCI_I2C_WRITE_SYNC_BLOCK,
+};
+
+struct msm_camera_cci_wait_sync_cfg {
+ uint16_t cid;
+ int16_t csid;
+ 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_read_cfg {
+ uint32_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_i2c_reg_setting 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;
+ atomic_t q_free[NUM_QUEUES];
+ uint8_t q_lock[NUM_QUEUES];
+ uint8_t reset_pending;
+ struct mutex mutex;
+ struct completion reset_complete;
+ struct mutex mutex_q[NUM_QUEUES];
+ struct completion report_q[NUM_QUEUES];
+ atomic_t done_pending[NUM_QUEUES];
+ spinlock_t lock_q[NUM_QUEUES];
+};
+
+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;
+ uint32_t cci_clk_src;
+};
+
+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 *irq;
+ void __iomem *base;
+
+ uint32_t hw_version;
+ uint8_t ref_count;
+ enum msm_cci_state_t cci_state;
+ size_t num_clk;
+ size_t num_clk_cases;
+ struct clk **cci_clk;
+ uint32_t **cci_clk_rates;
+ struct msm_cam_clk_info *cci_clk_info;
+ 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];
+ enum i2c_freq_mode_t i2c_freq_mode[NUM_MASTERS];
+ struct msm_cci_clk_params_t cci_clk_params[I2C_MAX_MODES];
+ struct gpio *cci_gpio_tbl;
+ uint8_t cci_gpio_tbl_size;
+ struct msm_pinctrl_info cci_pinctrl;
+ uint8_t cci_pinctrl_status;
+ uint32_t cycles_per_us;
+ uint32_t cci_clk_src;
+ struct camera_vreg_t *cci_vreg;
+ struct regulator *cci_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+ uint8_t payload_size;
+ uint8_t support_seq_write;
+ struct workqueue_struct *write_wq[MASTER_MAX];
+ struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg;
+ uint8_t valid_sync;
+};
+
+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 cci_write_async {
+ struct cci_device *cci_dev;
+ struct msm_camera_cci_ctrl c_ctrl;
+ enum cci_i2c_queue_t queue;
+ struct work_struct work;
+ enum cci_i2c_sync sync_en;
+};
+
+#ifdef CONFIG_MSM_CCI
+struct v4l2_subdev *msm_cci_get_subdev(void);
+#else
+static inline struct v4l2_subdev *msm_cci_get_subdev(void)
+{
+ return NULL;
+}
+#endif
+
+#define VIDIOC_MSM_CCI_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct msm_camera_cci_ctrl *)
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile b/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile
new file mode 100644
index 0000000..c51555d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSM_CSID) += msm_csid.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_0_hwreg.h
new file mode 100644
index 0000000..3bd444c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_0_hwreg.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2014-2015, 2018, 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_2_0_HWREG_H
+#define MSM_CSID_2_0_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v2_0 = {
+
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x4,
+ 0x8,
+ 0xc,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x5c,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6c,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x9C,
+ 0xA0,
+ 0xA8,
+ 0xAC,
+ 0xB0,
+ 11,
+ 0x7FFF,
+ 0x2,
+ 17,
+ 0x02000011,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_2_hwreg.h
new file mode 100644
index 0000000..697ae4c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_2_2_hwreg.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2014-2015, 2018, 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_2_2_HWREG_H
+#define MSM_CSID_2_2_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v2_2 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x4,
+ 0x8,
+ 0xc,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x5c,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6c,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x9C,
+ 0xA0,
+ 0xA8,
+ 0xAC,
+ 0xB0,
+ 11,
+ 0x7FFF,
+ 0x2,
+ 17,
+ 0x02001000,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_0_hwreg.h
new file mode 100644
index 0000000..4d5378d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_0_hwreg.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2014-2015, 2018, 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_3_0_HWREG_H
+#define MSM_CSID_3_0_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v3_0 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30000000,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_1_hwreg.h
new file mode 100644
index 0000000..952434e
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_1_hwreg.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2014-2015, 2018, 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_3_1_HWREG_H
+#define MSM_CSID_3_1_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v3_1 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30010000,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_2_hwreg.h
new file mode 100644
index 0000000..f6bf2f0
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_2_hwreg.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2014-2015, 2018, 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_3_2_HWREG_H
+#define MSM_CSID_3_2_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v3_2 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30020000,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_1_hwreg.h
new file mode 100644
index 0000000..bddcc74
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_1_hwreg.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2015, 2018, 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_3_4_1_HWREG_H
+#define MSM_CSID_3_4_1_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+static uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+
+static struct csid_reg_parms_t csid_v3_4_1 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30040001,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_2_hwreg.h
new file mode 100644
index 0000000..b829db6
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_2_hwreg.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2015, 2018, 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_3_4_2_HWREG_H
+#define MSM_CSID_3_4_2_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static struct csid_reg_parms_t csid_v3_4_2 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30040002,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h
new file mode 100644
index 0000000..8c48c6d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2015, 2018, 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_3_5_1_HWREG_H
+#define MSM_CSID_3_5_1_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+
+static struct csid_reg_parms_t csid_v3_5_1 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x24,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0x9C,
+ 0xA0,
+ 0xA8,
+ 0xAC,
+ 0xB4,
+ 0xB8,
+ 0xBC,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30050001,
+ 0xC,
+ 0x84,
+ 0xA4,
+ 0x7f010800,
+ 20,
+ 17,
+ 16,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_hwreg.h
new file mode 100644
index 0000000..d5463443
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_hwreg.h
@@ -0,0 +1,64 @@
+/* Copyright (c) 2015, 2018, 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_3_5_HWREG_H
+#define MSM_CSID_3_5_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+
+static struct csid_reg_parms_t csid_v3_5 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x24,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0x9C,
+ 0xA0,
+ 0xA8,
+ 0xAC,
+ 0xB4,
+ 0xB8,
+ 0xBC,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30050000,
+ 0xC,
+ 0x84,
+ 0xA4,
+ 0x7f010800,
+ 20,
+ 17,
+ 16,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_6_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_6_0_hwreg.h
new file mode 100644
index 0000000..b568a91
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_6_0_hwreg.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2015, 2018, 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_3_6_0_HWREG_H
+#define MSM_CSID_3_6_0_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4};
+static struct csid_reg_parms_t csid_v3_6_0 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30060000,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
new file mode 100644
index 0000000..057472a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -0,0 +1,1251 @@
+/* Copyright (c) 2011-2018, 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_sd.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "include/msm_csid_2_0_hwreg.h"
+#include "include/msm_csid_2_2_hwreg.h"
+#include "include/msm_csid_3_0_hwreg.h"
+#include "include/msm_csid_3_1_hwreg.h"
+#include "include/msm_csid_3_2_hwreg.h"
+#include "include/msm_csid_3_5_hwreg.h"
+#include "include/msm_csid_3_4_1_hwreg.h"
+#include "include/msm_csid_3_4_2_hwreg.h"
+#include "include/msm_csid_3_6_0_hwreg.h"
+#include "include/msm_csid_3_5_1_hwreg.h"
+#include "cam_hw_ops.h"
+
+#define V4L2_IDENT_CSID 50002
+#define CSID_VERSION_V20 0x02000011
+#define CSID_VERSION_V22 0x02001000
+#define CSID_VERSION_V30 0x30000000
+#define CSID_VERSION_V31 0x30010000
+#define CSID_VERSION_V31_1 0x30010001
+#define CSID_VERSION_V31_3 0x30010003
+#define CSID_VERSION_V32 0x30020000
+#define CSID_VERSION_V33 0x30030000
+#define CSID_VERSION_V34 0x30040000
+#define CSID_VERSION_V34_1 0x30040001
+#define CSID_VERSION_V34_2 0x30040002
+#define CSID_VERSION_V36 0x30060000
+#define CSID_VERSION_V37 0x30070000
+#define CSID_VERSION_V35 0x30050000
+#define CSID_VERSION_V35_1 0x30050001
+#define CSID_VERSION_V40 0x40000000
+#define CSID_VERSION_V50 0x50000000
+#define MSM_CSID_DRV_NAME "msm_csid"
+
+#define DBG_CSID 0
+#define SHORT_PKT_CAPTURE 0
+#define SHORT_PKT_OFFSET 0x200
+#define ENABLE_3P_BIT 1
+#define SOF_DEBUG_ENABLE 1
+#define SOF_DEBUG_DISABLE 0
+
+#define TRUE 1
+#define FALSE 0
+
+#define MAX_LANE_COUNT 4
+#define CSID_TIMEOUT msecs_to_jiffies(100)
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct camera_vreg_t csid_vreg_info[] = {
+ {"qcom,mipi-csi-vdd", 0, 0, 12000},
+};
+
+#ifdef CONFIG_COMPAT
+static struct v4l2_file_operations msm_csid_v4l2_subdev_fops;
+#endif
+
+static int msm_csid_cid_lut(
+ struct msm_camera_csid_lut_params *csid_lut_params,
+ struct csid_device *csid_dev)
+{
+ 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;
+ }
+ if (csid_lut_params->num_cid > MAX_CID) {
+ pr_err("%s:%d num_cid exceeded limit num_cid = %d max = %d\n",
+ __func__, __LINE__, csid_lut_params->num_cid, MAX_CID);
+ return -EINVAL;
+ }
+ for (i = 0; i < csid_lut_params->num_cid; i++) {
+ if (csid_lut_params->vc_cfg[i]->cid >= MAX_CID) {
+ pr_err("%s: cid outside range %d\n",
+ __func__, csid_lut_params->vc_cfg[i]->cid);
+ return -EINVAL;
+ }
+ CDBG("%s lut params num_cid = %d, cid = %d\n",
+ __func__,
+ csid_lut_params->num_cid,
+ csid_lut_params->vc_cfg[i]->cid);
+ CDBG("%s lut params dt = 0x%x, df = %d\n", __func__,
+ 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(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.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, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.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, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.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(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params)
+{
+ uint32_t val = 0;
+
+ if ((csid_dev->hw_dts_version == CSID_VERSION_V34_1) ||
+ (csid_dev->hw_dts_version == CSID_VERSION_V36)) {
+ val = ((1 << csid_params->lane_cnt) - 1) << 20;
+ msm_camera_io_w(0x7f010800 | val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ msm_camera_io_w(0x7f010800 | val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ } else {
+ if (csid_dev->csid_3p_enabled == 1) {
+ val = ((1 << csid_params->lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_3p;
+ } else {
+ val = ((1 << csid_params->lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_2p;
+ }
+ val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val;
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ }
+}
+#elif(SHORT_PKT_CAPTURE)
+static void msm_csid_set_debug_reg(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params)
+{
+ uint32_t val = 0;
+
+ if ((csid_dev->hw_dts_version == CSID_VERSION_V34_1) ||
+ (csid_dev->hw_dts_version == CSID_VERSION_V36)) {
+ val = ((1 << csid_params->lane_cnt) - 1) << 20;
+ msm_camera_io_w(0x7f010a00 | val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ msm_camera_io_w(0x7f010a00 | val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ } else {
+ if (csid_dev->csid_3p_enabled == 1) {
+ val = ((1 << csid_params->lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_3p;
+ } else {
+ val = ((1 << csid_params->lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_2p;
+ }
+ val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val;
+ val |= SHORT_PKT_OFFSET;
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ }
+}
+#else
+static void msm_csid_set_debug_reg(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params) {}
+#endif
+
+static void msm_csid_set_sof_freeze_debug_reg(
+ struct csid_device *csid_dev, uint8_t irq_enable)
+{
+ uint32_t val = 0;
+
+ if (!irq_enable) {
+ val = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ msm_camera_io_w(0, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ return;
+ }
+
+ if (csid_dev->csid_3p_enabled == 1) {
+ val = ((1 << csid_dev->current_csid_params.lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_3p;
+ } else {
+ val = ((1 << csid_dev->current_csid_params.lane_cnt) - 1) <<
+ csid_dev->ctrl_reg->
+ csid_reg.csid_err_lane_overflow_offset_2p;
+ }
+ val |= csid_dev->ctrl_reg->csid_reg.csid_irq_mask_val;
+ val |= SHORT_PKT_OFFSET;
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+ msm_camera_io_w(val, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+}
+
+static int msm_csid_reset(struct csid_device *csid_dev)
+{
+ int32_t rc = 0;
+
+ msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all,
+ csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr);
+ rc = wait_for_completion_timeout(&csid_dev->reset_complete,
+ CSID_TIMEOUT);
+ if (rc < 0) {
+ pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n",
+ rc);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ }
+ return rc;
+}
+
+static bool msm_csid_find_max_clk_rate(struct csid_device *csid_dev)
+{
+ int i;
+ bool ret = FALSE;
+
+ for (i = 0; i < csid_dev->num_clk; i++) {
+ if (!strcmp(csid_dev->csid_clk_info[i].clk_name,
+ "csi_src_clk")) {
+ CDBG("%s:%d, copy csi_src_clk, clk_rate[%d] = %ld",
+ __func__, __LINE__, i,
+ csid_dev->csid_clk_info[i].clk_rate);
+ csid_dev->csid_max_clk =
+ csid_dev->csid_clk_info[i].clk_rate;
+ csid_dev->csid_clk_index = i;
+ ret = TRUE;
+ break;
+ }
+ }
+ return ret;
+}
+static int msm_csid_config(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ long clk_rate = 0;
+ uint32_t input_sel;
+ uint32_t lane_assign = 0;
+ uint8_t lane_num = 0;
+ uint8_t i, j;
+ void __iomem *csidbase;
+
+ csidbase = csid_dev->base;
+ if (!csidbase || !csid_params) {
+ pr_err("%s:%d csidbase %pK, csid params %pK\n", __func__,
+ __LINE__, csidbase, csid_params);
+ return -EINVAL;
+ }
+
+ CDBG("%s csid_params, lane_cnt = %d, lane_assign = 0x%x\n",
+ __func__,
+ csid_params->lane_cnt,
+ csid_params->lane_assign);
+ CDBG("%s csid_params phy_sel = %d\n", __func__,
+ csid_params->phy_sel);
+ if ((csid_params->lane_cnt == 0) ||
+ (csid_params->lane_cnt > MAX_LANE_COUNT)) {
+ pr_err("%s:%d invalid lane count = %d\n",
+ __func__, __LINE__, csid_params->lane_cnt);
+ return -EINVAL;
+ }
+
+ csid_dev->csid_lane_cnt = csid_params->lane_cnt;
+ rc = msm_csid_reset(csid_dev);
+ if (rc < 0) {
+ pr_err("%s:%d msm_csid_reset failed\n", __func__, __LINE__);
+ return rc;
+ }
+
+ if (!msm_csid_find_max_clk_rate(csid_dev))
+ pr_err("msm_csid_find_max_clk_rate failed\n");
+
+ clk_rate = (csid_params->csi_clk > 0) ?
+ (csid_params->csi_clk) : csid_dev->csid_max_clk;
+
+ clk_rate = msm_camera_clk_set_rate(&csid_dev->pdev->dev,
+ csid_dev->csid_clk[csid_dev->csid_clk_index], clk_rate);
+ if (clk_rate < 0) {
+ pr_err("csi_src_clk set failed\n");
+ return -EINVAL;
+ }
+
+ if (csid_dev->is_testmode == 1) {
+ struct msm_camera_csid_testmode_parms *tm;
+
+ tm = &csid_dev->testmode_params;
+
+ /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT, 1:0 VC */
+ val = ((tm->v_blanking_count & 0xFF) << 24) |
+ ((tm->h_blanking_count & 0x7FF) << 13);
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_tg_vc_cfg_addr);
+ CDBG("[TG] CSID_TG_VC_CFG_ADDR 0x%08x\n", val);
+
+ /* 28:16 bytes per lines, 12:0 num of lines */
+ val = ((tm->num_bytes_per_line & 0x1FFF) << 16) |
+ (tm->num_lines & 0x1FFF);
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_0_addr);
+ CDBG("[TG] CSID_TG_DT_n_CFG_0_ADDR 0x%08x\n", val);
+
+ /* 5:0 data type */
+ val = csid_params->lut_params.vc_cfg[0]->dt;
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_1_addr);
+ CDBG("[TG] CSID_TG_DT_n_CFG_1_ADDR 0x%08x\n", val);
+
+ /* 2:0 output random */
+ msm_camera_io_w(csid_dev->testmode_params.payload_mode,
+ csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_tg_dt_n_cfg_2_addr);
+ } else {
+ val = csid_params->lane_cnt - 1;
+
+ for (i = 0, j = 0; i < PHY_LANE_MAX; i++) {
+ if (i == PHY_LANE_CLK)
+ continue;
+ lane_num = (csid_params->lane_assign >> j) & 0xF;
+ if (lane_num >= PHY_LANE_MAX) {
+ pr_err("%s:%d invalid lane number %d\n",
+ __func__, __LINE__, lane_num);
+ return -EINVAL;
+ }
+ if (csid_dev->ctrl_reg->csid_lane_assign[lane_num] >=
+ PHY_LANE_MAX){
+ pr_err("%s:%d invalid lane map %d\n",
+ __func__, __LINE__,
+ csid_dev->ctrl_reg->
+ csid_lane_assign[lane_num]);
+ return -EINVAL;
+ }
+ lane_assign |=
+ csid_dev->ctrl_reg->csid_lane_assign[lane_num]
+ << j;
+ j += 4;
+ }
+
+ CDBG("%s csid_params calculated lane_assign = 0x%X\n",
+ __func__, lane_assign);
+
+ val |= lane_assign <<
+ csid_dev->ctrl_reg->csid_reg.csid_dl_input_sel_shift;
+ if (csid_dev->hw_version < CSID_VERSION_V30) {
+ val |= (0xF << 10);
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_0_addr);
+ } else {
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_0_addr);
+ val = csid_params->phy_sel <<
+ csid_dev->ctrl_reg->csid_reg.csid_phy_sel_shift;
+ val |= 0xF;
+ msm_camera_io_w(val, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_core_ctrl_1_addr);
+ }
+ if (csid_dev->hw_version >= CSID_VERSION_V35 &&
+ csid_params->csi_3p_sel == 1) {
+ csid_dev->csid_3p_enabled = 1;
+ val = (csid_params->lane_cnt - 1) << ENABLE_3P_BIT;
+
+ for (i = 0; i < csid_params->lane_cnt; i++) {
+ input_sel =
+ (csid_params->lane_assign >> (4*i))
+ & 0xF;
+ val |= input_sel << (4*(i+1));
+ }
+ val |= csid_params->phy_sel <<
+ csid_dev->ctrl_reg->csid_reg.csid_phy_sel_shift_3p;
+ val |= ENABLE_3P_BIT;
+ msm_camera_io_w(val, csidbase + csid_dev->ctrl_reg
+ ->csid_reg.csid_3p_ctrl_0_addr);
+ }
+ }
+
+ rc = msm_csid_cid_lut(&csid_params->lut_params, csid_dev);
+ if (rc < 0) {
+ pr_err("%s:%d config cid lut failed\n", __func__, __LINE__);
+ return rc;
+ }
+ msm_csid_set_debug_reg(csid_dev, csid_params);
+
+ if (csid_dev->is_testmode == 1)
+ msm_camera_io_w(0x00A06437, csidbase +
+ csid_dev->ctrl_reg->csid_reg.csid_tg_ctrl_addr);
+
+ return rc;
+}
+
+#if SHORT_PKT_CAPTURE
+static irqreturn_t msm_csid_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ uint32_t short_dt = 0;
+ uint32_t count = 0, dt = 0;
+ struct csid_device *csid_dev = data;
+
+ 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_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 <<
+ csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift))
+ complete(&csid_dev->reset_complete);
+ if (irq & SHORT_PKT_OFFSET) {
+ short_dt = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->
+ csid_reg.csid_captured_short_pkt_addr);
+ count = (short_dt >> 8) & 0xffff;
+ dt = short_dt >> 24;
+ CDBG("CSID:: %s:%d core %d dt: 0x%x, count: %d\n",
+ __func__, __LINE__, csid_dev->pdev->id, dt, count);
+ msm_camera_io_w(0x101, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr);
+ }
+ msm_camera_io_w(irq, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ return IRQ_HANDLED;
+}
+#else
+static irqreturn_t msm_csid_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct csid_device *csid_dev = data;
+
+ if (!csid_dev) {
+ pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__);
+ return IRQ_HANDLED;
+ }
+
+ if (csid_dev->csid_sof_debug == SOF_DEBUG_ENABLE) {
+ if (csid_dev->csid_sof_debug_count < CSID_SOF_DEBUG_COUNT)
+ csid_dev->csid_sof_debug_count++;
+ else {
+ msm_csid_set_sof_freeze_debug_reg(csid_dev, false);
+ return IRQ_HANDLED;
+ }
+ }
+
+ irq = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 <<
+ csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift))
+ complete(&csid_dev->reset_complete);
+ msm_camera_io_w(irq, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ return IRQ_HANDLED;
+}
+#endif
+
+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_init(struct csid_device *csid_dev, uint32_t *csid_version)
+{
+ int rc = 0;
+
+ if (!csid_version) {
+ pr_err("%s:%d csid_version NULL\n", __func__, __LINE__);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ csid_dev->csid_sof_debug_count = 0;
+ csid_dev->reg_ptr = NULL;
+
+ if (csid_dev->csid_state == CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ return -EINVAL;
+ }
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ pr_info("%s: CSID_VERSION = 0x%x\n", __func__,
+ csid_dev->ctrl_reg->csid_reg.csid_version);
+ /* power up */
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg,
+ csid_dev->regulator_count, NULL, 0,
+ &csid_dev->csid_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csid config_vreg failed\n", __func__, __LINE__);
+ goto top_vreg_config_failed;
+ }
+
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto csid_vreg_config_failed;
+ }
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg,
+ csid_dev->regulator_count, NULL, 0,
+ &csid_dev->csid_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csid enable_vreg failed\n", __func__, __LINE__);
+ goto top_vreg_enable_failed;
+ }
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto csid_vreg_enable_failed;
+ }
+ rc = msm_camera_clk_enable(&csid_dev->pdev->dev,
+ csid_dev->csid_clk_info, csid_dev->csid_clk,
+ csid_dev->num_clk, true);
+ if (rc < 0) {
+ pr_err("%s:%d clock enable failed\n",
+ __func__, __LINE__);
+ goto clk_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csid_dev->hw_version =
+ msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.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;
+ csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE;
+
+ csid_dev->is_testmode = 0;
+
+ init_completion(&csid_dev->reset_complete);
+
+ rc = msm_camera_enable_irq(csid_dev->irq, true);
+ if (rc < 0)
+ pr_err("%s: irq enable failed\n", __func__);
+ rc = msm_csid_reset(csid_dev);
+ if (rc < 0) {
+ pr_err("%s:%d msm_csid_reset failed\n", __func__, __LINE__);
+ goto msm_csid_reset_fail;
+ }
+
+ csid_dev->csid_state = CSID_POWER_UP;
+ return rc;
+
+msm_csid_reset_fail:
+ msm_camera_enable_irq(csid_dev->irq, false);
+ msm_camera_clk_enable(&csid_dev->pdev->dev, csid_dev->csid_clk_info,
+ csid_dev->csid_clk, csid_dev->num_clk, false);
+clk_enable_failed:
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+csid_vreg_enable_failed:
+ msm_camera_enable_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg,
+ csid_dev->regulator_count, NULL, 0,
+ &csid_dev->csid_reg_ptr[0], 0);
+top_vreg_enable_failed:
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+csid_vreg_config_failed:
+ msm_camera_config_vreg(&csid_dev->pdev->dev, csid_dev->csid_vreg,
+ csid_dev->regulator_count, NULL, 0,
+ &csid_dev->csid_reg_ptr[0], 0);
+top_vreg_config_failed:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote from AHB\n", __func__);
+ return rc;
+}
+
+static int msm_csid_release(struct csid_device *csid_dev)
+{
+ uint32_t irq;
+
+ if (csid_dev->csid_state != CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ return -EINVAL;
+ }
+
+ CDBG("%s:%d, hw_version = 0x%x\n", __func__, __LINE__,
+ csid_dev->hw_version);
+
+ irq = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ msm_camera_io_w(irq, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_clear_cmd_addr);
+ msm_camera_io_w(0, csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_mask_addr);
+
+ msm_camera_enable_irq(csid_dev->irq, false);
+
+ msm_camera_clk_enable(&csid_dev->pdev->dev,
+ csid_dev->csid_clk_info,
+ csid_dev->csid_clk,
+ csid_dev->num_clk, false);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_dev->csid_vreg, csid_dev->regulator_count, NULL,
+ 0, &csid_dev->csid_reg_ptr[0], 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_dev->csid_vreg, csid_dev->regulator_count, NULL,
+ 0, &csid_dev->csid_reg_ptr[0], 0);
+
+ if (!IS_ERR_OR_NULL(csid_dev->reg_ptr)) {
+ regulator_disable(csid_dev->reg_ptr);
+ regulator_put(csid_dev->reg_ptr);
+ }
+
+ csid_dev->csid_state = CSID_POWER_DOWN;
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSID,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote from AHB\n", __func__);
+ return 0;
+}
+
+static int32_t 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 %pK, cdata %pK\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 0x%x\n", __func__,
+ cdata->cfg.csid_version);
+ break;
+ case CSID_TESTMODE_CFG: {
+ csid_dev->is_testmode = 1;
+ if (copy_from_user(&csid_dev->testmode_params,
+ (void __user *)cdata->cfg.csid_testmode_params,
+ sizeof(struct msm_camera_csid_testmode_parms))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case CSID_CFG: {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csid_vc_cfg *vc_cfg = NULL;
+ int i = 0;
+
+ if (copy_from_user(&csid_params,
+ (void __user *)cdata->cfg.csid_params,
+ sizeof(struct msm_camera_csid_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ if (csid_params.lut_params.num_cid < 1 ||
+ csid_params.lut_params.num_cid > MAX_CID) {
+ pr_err("%s: %d num_cid outside range\n",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ for (i = 0; i < csid_params.lut_params.num_cid; i++) {
+ vc_cfg = kzalloc(sizeof(struct msm_camera_csid_vc_cfg),
+ GFP_KERNEL);
+ if (!vc_cfg) {
+ rc = -ENOMEM;
+ goto MEM_CLEAN;
+ }
+ if (copy_from_user(vc_cfg,
+ (void __user *)csid_params.lut_params.vc_cfg[i],
+ sizeof(struct msm_camera_csid_vc_cfg))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ kfree(vc_cfg);
+ rc = -EFAULT;
+ goto MEM_CLEAN;
+ }
+ csid_params.lut_params.vc_cfg[i] = vc_cfg;
+ }
+ csid_dev->current_csid_params = csid_params;
+ csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE;
+ rc = msm_csid_config(csid_dev, &csid_params);
+MEM_CLEAN:
+ 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 MSM_SD_NOTIFY_FREEZE:
+ if (csid_dev->csid_state != CSID_POWER_UP)
+ break;
+ if (csid_dev->csid_sof_debug == SOF_DEBUG_DISABLE) {
+ csid_dev->csid_sof_debug = SOF_DEBUG_ENABLE;
+ msm_csid_set_sof_freeze_debug_reg(csid_dev, true);
+ }
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ if (csid_dev->csid_state != CSID_POWER_UP)
+ break;
+ csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE;
+ msm_csid_set_sof_freeze_debug_reg(csid_dev, false);
+ break;
+ case VIDIOC_MSM_CSID_RELEASE:
+ case MSM_SD_SHUTDOWN:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err_ratelimited("%s: command not found\n", __func__);
+ }
+ CDBG("%s:%d\n", __func__, __LINE__);
+ mutex_unlock(&csid_dev->mutex);
+ return rc;
+}
+
+
+#ifdef CONFIG_COMPAT
+static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void *arg)
+{
+ int rc = 0;
+ struct csid_cfg_data *cdata;
+ struct csid_cfg_data32 *arg32 = (struct csid_cfg_data32 *) (arg);
+ struct csid_cfg_data local_arg;
+
+ local_arg.cfgtype = arg32->cfgtype;
+ cdata = &local_arg;
+
+ if (!csid_dev || !cdata) {
+ pr_err("%s:%d csid_dev %pK, cdata %pK\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);
+ arg32->cfg.csid_version = local_arg.cfg.csid_version;
+ CDBG("%s csid version 0x%x\n", __func__,
+ cdata->cfg.csid_version);
+ break;
+ case CSID_TESTMODE_CFG: {
+ csid_dev->is_testmode = 1;
+ if (copy_from_user(&csid_dev->testmode_params,
+ (void __user *)
+ compat_ptr(arg32->cfg.csid_testmode_params),
+ sizeof(struct msm_camera_csid_testmode_parms))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case CSID_CFG: {
+
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csid_vc_cfg *vc_cfg = NULL;
+ int i = 0;
+ struct msm_camera_csid_lut_params32 lut_par32;
+ struct msm_camera_csid_params32 csid_params32;
+ struct msm_camera_csid_vc_cfg vc_cfg32;
+
+ if (copy_from_user(&csid_params32,
+ (void __user *)compat_ptr(arg32->cfg.csid_params),
+ sizeof(struct msm_camera_csid_params32))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ csid_params.lane_cnt = csid_params32.lane_cnt;
+ csid_params.lane_assign = csid_params32.lane_assign;
+ csid_params.phy_sel = csid_params32.phy_sel;
+ csid_params.csi_clk = csid_params32.csi_clk;
+ csid_params.csi_3p_sel = csid_params32.csi_3p_sel;
+
+ lut_par32 = csid_params32.lut_params;
+ csid_params.lut_params.num_cid = lut_par32.num_cid;
+
+ if (csid_params.lut_params.num_cid < 1 ||
+ csid_params.lut_params.num_cid > MAX_CID) {
+ pr_err("%s: %d num_cid outside range %d\n", __func__,
+ __LINE__, csid_params.lut_params.num_cid);
+ rc = -EINVAL;
+ break;
+ }
+
+ for (i = 0; i < lut_par32.num_cid; i++) {
+ vc_cfg = kzalloc(sizeof(struct msm_camera_csid_vc_cfg),
+ GFP_KERNEL);
+ if (!vc_cfg) {
+ rc = -ENOMEM;
+ goto MEM_CLEAN32;
+ }
+ /* msm_camera_csid_vc_cfg size
+ * does not change in COMPAT MODE
+ */
+ if (copy_from_user(&vc_cfg32,
+ (void __user *)compat_ptr(lut_par32.vc_cfg[i]),
+ sizeof(vc_cfg32))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ kfree(vc_cfg);
+ vc_cfg = NULL;
+ rc = -EFAULT;
+ goto MEM_CLEAN32;
+ }
+ vc_cfg->cid = vc_cfg32.cid;
+ vc_cfg->dt = vc_cfg32.dt;
+ vc_cfg->decode_format = vc_cfg32.decode_format;
+ csid_params.lut_params.vc_cfg[i] = vc_cfg;
+ }
+ rc = msm_csid_config(csid_dev, &csid_params);
+ csid_dev->current_csid_params = csid_params;
+
+MEM_CLEAN32:
+ for (i--; i >= 0; i--) {
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ csid_params.lut_params.vc_cfg[i] = NULL;
+ }
+ 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 long msm_csid_subdev_ioctl32(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_CFG32:
+ rc = msm_csid_cmd32(csid_dev, arg);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ if (csid_dev->csid_state != CSID_POWER_UP)
+ break;
+ if (csid_dev->csid_sof_debug == SOF_DEBUG_DISABLE) {
+ csid_dev->csid_sof_debug = SOF_DEBUG_ENABLE;
+ msm_csid_set_sof_freeze_debug_reg(csid_dev, true);
+ }
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ if (csid_dev->csid_state != CSID_POWER_UP)
+ break;
+ csid_dev->csid_sof_debug = SOF_DEBUG_DISABLE;
+ msm_csid_set_sof_freeze_debug_reg(csid_dev, false);
+ break;
+ case VIDIOC_MSM_CSID_RELEASE:
+ case MSM_SD_SHUTDOWN:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err_ratelimited("%s: command not found\n", __func__);
+ }
+ CDBG("%s:%d\n", __func__, __LINE__);
+ mutex_unlock(&csid_dev->mutex);
+ return rc;
+}
+
+static long msm_csid_subdev_do_ioctl32(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return msm_csid_subdev_ioctl32(sd, cmd, arg);
+}
+
+static long msm_csid_subdev_fops_ioctl32(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_csid_subdev_do_ioctl32);
+}
+#endif
+static const struct v4l2_subdev_internal_ops msm_csid_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = {
+ .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 csid_probe(struct platform_device *pdev)
+{
+ struct csid_device *new_csid_dev;
+ uint32_t csi_vdd_voltage = 0;
+ int rc = 0;
+
+ new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
+ if (!new_csid_dev)
+ return -ENOMEM;
+
+ new_csid_dev->csid_3p_enabled = 0;
+ new_csid_dev->ctrl_reg = NULL;
+ new_csid_dev->ctrl_reg = kzalloc(sizeof(struct csid_ctrl_t),
+ GFP_KERNEL);
+ if (!new_csid_dev->ctrl_reg) {
+ kfree(new_csid_dev);
+ 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) {
+ rc = of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ if (rc < 0) {
+ pr_err("%s:%d failed to read cell-index\n", __func__,
+ __LINE__);
+ goto csid_no_resource;
+ }
+ CDBG("%s device id %d\n", __func__, pdev->id);
+
+ rc = of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,csi-vdd-voltage", &csi_vdd_voltage);
+ if (rc < 0) {
+ pr_err("%s:%d failed to read qcom,csi-vdd-voltage\n",
+ __func__, __LINE__);
+ goto csid_no_resource;
+ }
+ CDBG("%s:%d reading mipi_csi_vdd is %d\n", __func__, __LINE__,
+ csi_vdd_voltage);
+
+ csid_vreg_info[0].min_voltage = csi_vdd_voltage;
+ csid_vreg_info[0].max_voltage = csi_vdd_voltage;
+ }
+
+ rc = msm_camera_get_clk_info(pdev, &new_csid_dev->csid_clk_info,
+ &new_csid_dev->csid_clk, &new_csid_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: msm_camera_get_clk_info failed", __func__);
+ rc = -EFAULT;
+ goto csid_no_resource;
+ }
+
+ rc = msm_camera_get_dt_vreg_data(pdev->dev.of_node,
+ &(new_csid_dev->csid_vreg), &(new_csid_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: get vreg data from dtsi fail\n", __func__);
+ rc = -EFAULT;
+ goto csid_no_resource;
+ }
+
+ if ((new_csid_dev->regulator_count < 0) ||
+ (new_csid_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_csid_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto csid_no_resource;
+ }
+
+ new_csid_dev->base = msm_camera_get_reg_base(pdev, "csid", true);
+ if (!new_csid_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_invalid_vreg_data;
+ }
+ new_csid_dev->irq = msm_camera_get_irq(pdev, "csid");
+ if (!new_csid_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_invalid_irq;
+ }
+ 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_pads_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL);
+ new_csid_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ new_csid_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x5;
+ msm_sd_register(&new_csid_dev->msm_sd);
+
+#ifdef CONFIG_COMPAT
+ msm_cam_copy_v4l2_subdev_fops(&msm_csid_v4l2_subdev_fops);
+ msm_csid_v4l2_subdev_fops.compat_ioctl32 = msm_csid_subdev_fops_ioctl32;
+ new_csid_dev->msm_sd.sd.devnode->fops = &msm_csid_v4l2_subdev_fops;
+#endif
+
+ rc = msm_camera_register_irq(pdev, new_csid_dev->irq,
+ msm_csid_irq, IRQF_TRIGGER_RISING, "csid", new_csid_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csid_invalid_irq;
+ }
+ rc = msm_camera_enable_irq(new_csid_dev->irq, false);
+ if (rc < 0) {
+ pr_err("%s Error registering irq ", __func__);
+ rc = -EBUSY;
+ goto csid_invalid_irq;
+ }
+
+ if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v2.0")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v2_0;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v2_0;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V20;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v2.2")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v2_2;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v2_2;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V22;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.0")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_0;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_0;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V30;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v4.0")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_0;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_0;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V40;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.1")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_1;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_1;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V31;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.2")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_2;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_2;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V32;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.4.1")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_1;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V34_1;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_4_1;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.4.2")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_2;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V34_2;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_4_2;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.6.0")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_6_0;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V36;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_6_0;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.5")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_5;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_5;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V35;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v5.0")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_5;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_5;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V50;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.5.1")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_5_1;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_5_1;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V35_1;
+ } else {
+ pr_err("%s:%d, invalid hw version : 0x%x", __func__, __LINE__,
+ new_csid_dev->hw_dts_version);
+ rc = -EINVAL;
+ goto csid_invalid_irq;
+ }
+
+ new_csid_dev->csid_state = CSID_POWER_DOWN;
+ return 0;
+
+csid_invalid_irq:
+ msm_camera_put_reg_base(pdev, new_csid_dev->base, "csid", true);
+csid_invalid_vreg_data:
+ kfree(new_csid_dev->csid_vreg);
+csid_no_resource:
+ mutex_destroy(&new_csid_dev->mutex);
+ kfree(new_csid_dev->ctrl_reg);
+ kfree(new_csid_dev);
+ return rc;
+}
+
+static int msm_csid_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct csid_device *csid_dev =
+ v4l2_get_subdevdata(subdev);
+
+ msm_camera_put_clk_info(pdev, &csid_dev->csid_clk_info,
+ &csid_dev->csid_clk, csid_dev->num_clk);
+ msm_camera_put_reg_base(pdev, csid_dev->base, "csid", true);
+ kfree(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,
+ .remove = msm_csid_exit,
+ .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/platform/msm/camera_v2/sensor/csid/msm_csid.h b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h
new file mode 100644
index 0000000..79eaf59
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2011-2016, 2018, 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"
+#include "cam_soc_api.h"
+
+#define CSID_SOF_DEBUG_COUNT 3
+
+enum csiphy_lane_assign {
+ PHY_LANE_D0,
+ PHY_LANE_CLK,
+ PHY_LANE_D1,
+ PHY_LANE_D2,
+ PHY_LANE_D3,
+ PHY_LANE_MAX,
+};
+
+struct csid_reg_parms_t {
+/* MIPI CSID registers */
+ uint32_t csid_hw_version_addr;
+ uint32_t csid_core_ctrl_0_addr;
+ uint32_t csid_core_ctrl_1_addr;
+ uint32_t csid_rst_cmd_addr;
+ uint32_t csid_cid_lut_vc_0_addr;
+ uint32_t csid_cid_lut_vc_1_addr;
+ uint32_t csid_cid_lut_vc_2_addr;
+ uint32_t csid_cid_lut_vc_3_addr;
+ uint32_t csid_cid_n_cfg_addr;
+ uint32_t csid_irq_clear_cmd_addr;
+ uint32_t csid_irq_mask_addr;
+ uint32_t csid_irq_status_addr;
+ uint32_t csid_captured_unmapped_long_pkt_hdr_addr;
+ uint32_t csid_captured_mmaped_long_pkt_hdr_addr;
+ uint32_t csid_captured_short_pkt_addr;
+ uint32_t csid_captured_long_pkt_hdr_addr;
+ uint32_t csid_captured_long_pkt_ftr_addr;
+ uint32_t csid_pif_misr_dl0_addr;
+ uint32_t csid_pif_misr_dl1_addr;
+ uint32_t csid_pif_misr_dl2_addr;
+ uint32_t csid_pif_misr_dl3_addr;
+ uint32_t csid_stats_total_pkts_rcvd_addr;
+ uint32_t csid_stats_ecc_addr;
+ uint32_t csid_stats_crc_addr;
+ uint32_t csid_tg_ctrl_addr;
+ uint32_t csid_tg_vc_cfg_addr;
+ uint32_t csid_tg_dt_n_cfg_0_addr;
+ uint32_t csid_tg_dt_n_cfg_1_addr;
+ uint32_t csid_tg_dt_n_cfg_2_addr;
+ uint32_t csid_rst_done_irq_bitshift;
+ uint32_t csid_rst_stb_all;
+ uint32_t csid_dl_input_sel_shift;
+ uint32_t csid_phy_sel_shift;
+ uint32_t csid_version;
+ uint32_t csid_3p_ctrl_0_addr;
+ uint32_t csid_3p_pkt_hdr_addr;
+ uint32_t csid_test_bus_ctrl;
+ uint32_t csid_irq_mask_val;
+ uint32_t csid_err_lane_overflow_offset_2p;
+ uint32_t csid_err_lane_overflow_offset_3p;
+ uint32_t csid_phy_sel_shift_3p;
+};
+
+struct csid_ctrl_t {
+ struct csid_reg_parms_t csid_reg;
+ uint8_t *csid_lane_assign;
+};
+
+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 *irq;
+ struct regulator *csi_vdd;
+ void __iomem *base;
+ struct mutex mutex;
+ struct completion reset_complete;
+ uint32_t hw_version;
+ uint32_t hw_dts_version;
+ enum msm_csid_state_t csid_state;
+ struct csid_ctrl_t *ctrl_reg;
+ struct regulator *reg_ptr;
+ size_t num_clk;
+ struct clk **csid_clk;
+ struct msm_cam_clk_info *csid_clk_info;
+ uint32_t csid_clk_index;
+ uint32_t csid_max_clk;
+ uint32_t csid_3p_enabled;
+ struct camera_vreg_t *csid_vreg;
+ struct regulator *csid_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+ uint8_t is_testmode;
+ struct msm_camera_csid_testmode_parms testmode_params;
+ struct msm_camera_csid_params current_csid_params;
+ uint32_t csid_sof_debug;
+ uint32_t csid_lane_cnt;
+ uint32_t csid_sof_debug_count;
+};
+
+#define VIDIOC_MSM_CSID_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct v4l2_subdev*)
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile b/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile
new file mode 100644
index 0000000..e0cbdb8
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSM_CSIPHY) += msm_csiphy.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h
new file mode 100644
index 0000000..f7da4a7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, 2018, 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_2_0_HWREG_H
+#define MSM_CSIPHY_2_0_HWREG_H
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v2_0 = {
+ /*MIPI CSI PHY registers*/
+ 0x17C,
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x100,
+ 0x104,
+ 0x108,
+ 0x10C,
+ 0x110,
+ 0x128,
+ 0x140,
+ 0x144,
+ 0x164,
+ 0x180,
+ 0x1A0,
+ 0x6F,
+ 0x1A4,
+ 0x1C0,
+ 0x1C4,
+ 0x4,
+ 0x1E0,
+ 0x1E8,
+ 0x0,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h
new file mode 100644
index 0000000..d8a0e99
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, 2018, 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_2_2_HWREG_H
+#define MSM_CSIPHY_2_2_HWREG_H
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v2_2 = {
+ /*MIPI CSI PHY registers*/
+ 0x17C,
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x100,
+ 0x104,
+ 0x108,
+ 0x10C,
+ 0x110,
+ 0x128,
+ 0x140,
+ 0x144,
+ 0x164,
+ 0x180,
+ 0x1A0,
+ 0x6F,
+ 0x1A4,
+ 0x1C0,
+ 0x1C4,
+ 0x4,
+ 0x1E0,
+ 0x1E8,
+ 0x1,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h
new file mode 100644
index 0000000..9b2b00f8
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, 2018, 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_3_0_HWREG_H
+#define MSM_CSIPHY_3_0_HWREG_H
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v3_0 = {
+ /*MIPI CSI PHY registers*/
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x100,
+ 0x104,
+ 0x108,
+ 0x10C,
+ 0x110,
+ 0x128,
+ 0x140,
+ 0x144,
+ 0x164,
+ 0x188,
+ 0x18C,
+ 0x1AC,
+ 0x3F,
+ 0x1AC,
+ 0x1CC,
+ 0x1CC,
+ 0x4,
+ 0x1EC,
+ 0x1F4,
+ 0x10,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h
new file mode 100644
index 0000000..2b924b9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, 2018, 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_3_1_HWREG_H
+#define MSM_CSIPHY_3_1_HWREG_H
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v3_1 = {
+ /*MIPI CSI PHY registers*/
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x100,
+ 0x104,
+ 0x108,
+ 0x10C,
+ 0x1C,
+ 0x28,
+ 0x140,
+ 0x144,
+ 0x164,
+ 0x188,
+ 0x18C,
+ 0x1AC,
+ 0x3F,
+ 0x1AC,
+ 0x1CC,
+ 0x1CC,
+ 0x4,
+ 0x1EC,
+ 0x1F4,
+ 0x31,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h
new file mode 100644
index 0000000..5445f2f
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, 2018, 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_3_2_HWREG_H
+#define MSM_CSIPHY_3_2_HWREG_H
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v3_2 = {
+ /*MIPI CSI PHY registers*/
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x100,
+ 0x104,
+ 0x108,
+ 0x10C,
+ 0x110,
+ 0x128,
+ 0x140,
+ 0x144,
+ 0x164,
+ 0x188,
+ 0x18C,
+ 0x1AC,
+ 0x3F,
+ 0x1AC,
+ 0x1CC,
+ 0x1CC,
+ 0x4,
+ 0x1EC,
+ 0x1F4,
+ 0x32,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h
new file mode 100644
index 0000000..04b7376
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h
@@ -0,0 +1,95 @@
+/* Copyright (c) 2015, 2018, 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_3_4_2_HWREG_H
+#define MSM_CSIPHY_3_4_2_HWREG_H
+
+#define ULPM_WAKE_UP_TIMER_MODE 2
+#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v3_4_2 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .combo_clk_mask = 0x10,
+};
+
+static struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = {
+ /*MIPI CSI PHY registers*/
+ {0x814, 0x0},
+ {0x818, 0x1},
+ {0x188, 0x7F},
+ {0x18C, 0x7F},
+ {0x190, 0x0},
+ {0x104, 0x6},
+ {0x108, 0x0},
+ {0x10c, 0x0},
+ {0x114, 0x20},
+ {0x118, 0x3E},
+ {0x11c, 0x41},
+ {0x120, 0x41},
+ {0x124, 0x7F},
+ {0x128, 0x0},
+ {0x12c, 0x0},
+ {0x130, 0x1},
+ {0x134, 0x0},
+ {0x138, 0x0},
+ {0x13C, 0x10},
+ {0x140, 0x1},
+ {0x144, GLITCH_ELIMINATION_NUM},
+ {0x148, 0xFE},
+ {0x14C, 0x1},
+ {0x154, 0x0},
+ {0x15C, 0x33},
+ {0x160, ULPM_WAKE_UP_TIMER_MODE},
+ {0x164, 0x48},
+ {0x168, 0xA0},
+ {0x16C, 0x17},
+ {0x170, 0x41},
+ {0x174, 0x41},
+ {0x178, 0x3E},
+ {0x17C, 0x0},
+ {0x180, 0x0},
+ {0x184, 0x7F},
+ {0x1cc, 0x10},
+ {0x81c, 0x6},
+ {0x82c, 0xFF},
+ {0x830, 0xFF},
+ {0x834, 0xFB},
+ {0x838, 0xFF},
+ {0x83c, 0x7F},
+ {0x840, 0xFF},
+ {0x844, 0xFF},
+ {0x848, 0xEF},
+ {0x84c, 0xFF},
+ {0x850, 0xFF},
+ {0x854, 0xFF},
+ {0x28, 0x0},
+ {0x800, 0x2},
+ {0x0, 0x8E},
+ {0x4, 0x8},
+ {0x8, 0x0},
+ {0xC, 0xFF},
+ {0x10, 0x56},
+ {0x2C, 0x1},
+ {0x30, 0x0},
+ {0x34, 0x3},
+ {0x38, 0xfe},
+ {0x3C, 0xB8},
+ {0x1C, 0xE7},
+ {0x14, 0x0},
+ {0x14, 0x60},
+ {0x700, 0x80}
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h
new file mode 100644
index 0000000..90b8e1c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h
@@ -0,0 +1,95 @@
+/* Copyright (c) 2015-2016, 2018, 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_3_5_HWREG_H
+#define MSM_CSIPHY_3_5_HWREG_H
+
+#define ULPM_WAKE_UP_TIMER_MODE 2
+#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v3_5 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .combo_clk_mask = 0x10,
+};
+
+static struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = {
+ /*MIPI CSI PHY registers*/
+ {0x814, 0x0},
+ {0x818, 0x1},
+ {0x188, 0x7F},
+ {0x18C, 0x7F},
+ {0x190, 0x0},
+ {0x104, 0x6},
+ {0x108, 0x0},
+ {0x10c, 0x0},
+ {0x114, 0x20},
+ {0x118, 0x3E},
+ {0x11c, 0x41},
+ {0x120, 0x41},
+ {0x124, 0x7F},
+ {0x128, 0x0},
+ {0x12c, 0x0},
+ {0x130, 0x1},
+ {0x134, 0x0},
+ {0x138, 0x0},
+ {0x13C, 0x10},
+ {0x140, 0x1},
+ {0x144, GLITCH_ELIMINATION_NUM},
+ {0x148, 0xFE},
+ {0x14C, 0x1},
+ {0x154, 0x0},
+ {0x15C, 0x33},
+ {0x160, ULPM_WAKE_UP_TIMER_MODE},
+ {0x164, 0x48},
+ {0x168, 0xA0},
+ {0x16C, 0x17},
+ {0x170, 0x41},
+ {0x174, 0x41},
+ {0x178, 0x3E},
+ {0x17C, 0x0},
+ {0x180, 0x0},
+ {0x184, 0x7F},
+ {0x1cc, 0x10},
+ {0x81c, 0x6},
+ {0x82c, 0xFF},
+ {0x830, 0xFF},
+ {0x834, 0xFB},
+ {0x838, 0xFF},
+ {0x83c, 0x7F},
+ {0x840, 0xFF},
+ {0x844, 0xFF},
+ {0x848, 0xEF},
+ {0x84c, 0xFF},
+ {0x850, 0xFF},
+ {0x854, 0xFF},
+ {0x28, 0x0},
+ {0x800, 0x0},
+ {0x0, 0xD7},
+ {0x4, 0x8},
+ {0x8, 0x0},
+ {0xC, 0xA5},
+ {0x10, 0x52},
+ {0x2C, 0x1},
+ {0x30, 0x2},
+ {0x34, 0x3},
+ {0x38, 0x1},
+ {0x3C, 0xB8},
+ {0x1C, 0xA},
+ {0x14, 0x0},
+ {0x0, 0x0},
+ {0x700, 0xC0},
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
new file mode 100644
index 0000000..d92614d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
@@ -0,0 +1,163 @@
+/* Copyright (c) 2016-2018, 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_5_0_1_HWREG_H
+#define MSM_CSIPHY_5_0_1_HWREG_H
+
+#define ULPM_WAKE_UP_TIMER_MODE 2
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v5_0_1 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .combo_clk_mask = 0x10,
+};
+
+static struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = {
+ /*MIPI CSI PHY registers*/
+ {0x814, 0xD5},
+ {0x818, 0x1},
+ {0x188, 0x7F},
+ {0x18C, 0x7F},
+ {0x190, 0x0},
+ {0x104, 0x6},
+ {0x108, 0x1},
+ {0x10c, 0x12},
+ {0x114, 0x20},
+ {0x118, 0x3E},
+ {0x11c, 0x41},
+ {0x120, 0x41},
+ {0x124, 0x7F},
+ {0x128, 0x0},
+ {0x12c, 0x0},
+ {0x130, 0x1},
+ {0x134, 0x0},
+ {0x138, 0x0},
+ {0x13C, 0x10},
+ {0x140, 0x1},
+ {0x144, 0x12},
+ {0x148, 0xFE},
+ {0x14C, 0x1},
+ {0x154, 0x0},
+ {0x15C, 0x63},
+ {0x160, ULPM_WAKE_UP_TIMER_MODE},
+ {0x164, 0x00},
+ {0x168, 0xAC},
+ {0x16C, 0xA5},
+ {0x170, 0x41},
+ {0x174, 0x41},
+ {0x178, 0x3E},
+ {0x17C, 0x0},
+ {0x180, 0x0},
+ {0x184, 0x7F},
+ {0x1cc, 0x41},
+ {0x81c, 0x2},
+ {0x82c, 0xFF},
+ {0x830, 0xFF},
+ {0x834, 0xFB},
+ {0x838, 0xFF},
+ {0x83c, 0x7F},
+ {0x840, 0xFF},
+ {0x844, 0xFF},
+ {0x848, 0xEF},
+ {0x84c, 0xFF},
+ {0x850, 0xFF},
+ {0x854, 0xFF},
+ {0x28, 0x0},
+ {0x800, 0x0},
+ {0x4, 0xC},
+ {0x8, 0x14},
+ {0x8, 0x14},
+ {0x10, 0x52},
+ {0x14, 0x60},
+ {0x14, 0x60},
+ {0x1C, 0xa},
+ {0x1c, 0xa},
+ {0x38, 0x1},
+ {0x3C, 0xB8},
+ {0x3C, 0xB8},
+ {0x14, 0x0},
+ {0x14, 0x0},
+ {0x700, 0xC0},
+ {0x150, 0},
+ {0x1dc, 0x51},
+ {0x2C, 0x1},
+ {0x34, 0xf},
+ {0x728, 0x4},
+ {0x0, 0x91},
+ {0x70C, 0xA5},
+ {0x38, 0xFE},
+ {0x81c, 0x2},
+ {0x700, 0x80},
+ {0x724, 0x04},
+ {0x024, 0x04},
+};
+
+static struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = {
+ {
+ {0x818, 0x1},
+ {0x81c, 0x2},
+ {0x004, 0x0C},
+ {0x704, 0x0C},
+ {0x204, 0x0C},
+ {0x404, 0x0C},
+ {0x604, 0x0C},
+ {0x02c, 0x1},
+ {0x22c, 0x1},
+ {0x42c, 0x1},
+ {0x62c, 0x1},
+ {0x72c, 0x1},
+ {0x034, 0x0f},
+ {0x234, 0x0f},
+ {0x434, 0x0f},
+ {0x634, 0x0f},
+ {0x734, 0x0f},
+ {0x01c, 0x0a},
+ {0x21c, 0x0a},
+ {0x41c, 0x0a},
+ {0x61c, 0x0a},
+ {0x71c, 0x0a},
+ {0x014, 0x60},
+ {0x214, 0x60},
+ {0x414, 0x60},
+ {0x614, 0x60},
+ {0x714, 0x60},
+ {0x728, 0x4},
+ {0x428, 0x0a},
+ {0x628, 0x0e},
+ {0x03c, 0xb8},
+ {0x73c, 0xb8},
+ {0x23c, 0xb8},
+ {0x43c, 0xb8},
+ {0x63c, 0xb8},
+ {0x000, 0x91},
+ {0x700, 0x80},
+ {0x200, 0x91},
+ {0x400, 0x91},
+ {0x600, 0x80},
+ {0x70c, 0xA5},
+ {0x60c, 0xA5},
+ {0x010, 0x52},
+ {0x710, 0x52},
+ {0x210, 0x52},
+ {0x410, 0x52},
+ {0x610, 0x52},
+ {0x038, 0xfe},
+ {0x738, 0x1f},
+ {0x238, 0xfe},
+ {0x438, 0xfe},
+ {0x638, 0x1f},
+ }
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
new file mode 100644
index 0000000..f247958
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
@@ -0,0 +1,164 @@
+/* Copyright (c) 2016-2018, 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_5_0_HWREG_H
+#define MSM_CSIPHY_5_0_HWREG_H
+
+#define ULPM_WAKE_UP_TIMER_MODE 2
+#define GLITCH_ELIMINATE_NUM 0x32
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+static struct csiphy_reg_parms_t csiphy_v5_0 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .combo_clk_mask = 0x10,
+};
+
+static struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = {
+ /*MIPI CSI PHY registers*/
+ {0x814, 0x2A},
+ {0x818, 0x1},
+ {0x188, 0x7F},
+ {0x18C, 0x7F},
+ {0x190, 0x0},
+ {0x104, 0x6},
+ {0x108, 0x1},
+ {0x10c, 0x12},
+ {0x114, 0x20},
+ {0x118, 0x3E},
+ {0x11c, 0x41},
+ {0x120, 0x41},
+ {0x124, 0x7F},
+ {0x128, 0x0},
+ {0x12c, 0x0},
+ {0x130, 0x1},
+ {0x134, 0x0},
+ {0x138, 0x0},
+ {0x13C, 0x10},
+ {0x140, 0x1},
+ {0x144, GLITCH_ELIMINATE_NUM},
+ {0x148, 0xFE},
+ {0x14C, 0x1},
+ {0x154, 0x0},
+ {0x15C, 0x23},
+ {0x160, ULPM_WAKE_UP_TIMER_MODE},
+ {0x164, 0x00},
+ {0x168, 0xA0},
+ {0x16C, 0x25},
+ {0x170, 0x41},
+ {0x174, 0x41},
+ {0x178, 0x3E},
+ {0x17C, 0x0},
+ {0x180, 0x0},
+ {0x184, 0x7F},
+ {0x1cc, 0x41},
+ {0x81c, 0x2},
+ {0x82c, 0xFF},
+ {0x830, 0xFF},
+ {0x834, 0xFB},
+ {0x838, 0xFF},
+ {0x83c, 0x7F},
+ {0x840, 0xFF},
+ {0x844, 0xFF},
+ {0x848, 0xEF},
+ {0x84c, 0xFF},
+ {0x850, 0xFF},
+ {0x854, 0xFF},
+ {0x28, 0x0},
+ {0x800, 0x0},
+ {0x4, 0x8},
+ {0x8, 0x4},
+ {0x8, 0x4},
+ {0x10, 0x52},
+ {0x14, 0x60},
+ {0x14, 0x60},
+ {0x1C, 0xa},
+ {0x1c, 0xa},
+ {0x38, 0x1},
+ {0x3C, 0xB8},
+ {0x3C, 0xB8},
+ {0x14, 0x0},
+ {0x0, 0x0},
+ {0x700, 0xC0},
+ {0x150, 0},
+ {0x1dc, 0x51},
+ {0x2C, 0x1},
+ {0x34, 0xf},
+ {0x728, 0x4},
+ {0x0, 0x91},
+ {0x70C, 0x16},
+ {0x38, 0xFE},
+ {0x81c, 0x6},
+ {0x700, 0x80},
+ {0x724, 0x04},
+ {0x024, 0x04},
+};
+
+static struct csiphy_settings_t csiphy_combo_mode_v5_0 = {
+ {
+ {0x818, 0x1},
+ {0x81c, 0x2},
+ {0x004, 0x08},
+ {0x704, 0x08},
+ {0x204, 0x08},
+ {0x404, 0x08},
+ {0x604, 0x08},
+ {0x02c, 0x1},
+ {0x22c, 0x1},
+ {0x42c, 0x1},
+ {0x62c, 0x1},
+ {0x72c, 0x1},
+ {0x034, 0x0f},
+ {0x234, 0x0f},
+ {0x434, 0x0f},
+ {0x634, 0x0f},
+ {0x734, 0x0f},
+ {0x01c, 0x0a},
+ {0x21c, 0x0a},
+ {0x41c, 0x0a},
+ {0x61c, 0x0a},
+ {0x71c, 0x0a},
+ {0x014, 0x60},
+ {0x214, 0x60},
+ {0x414, 0x60},
+ {0x614, 0x60},
+ {0x714, 0x60},
+ {0x728, 0x4},
+ {0x428, 0x0a},
+ {0x628, 0x0e},
+ {0x03c, 0xb8},
+ {0x73c, 0xb8},
+ {0x23c, 0xb8},
+ {0x43c, 0xb8},
+ {0x63c, 0xb8},
+ {0x000, 0x91},
+ {0x700, 0x80},
+ {0x200, 0x91},
+ {0x400, 0x91},
+ {0x600, 0x80},
+ {0x70c, 0xA5},
+ {0x60c, 0xA5},
+ {0x010, 0x52},
+ {0x710, 0x52},
+ {0x210, 0x52},
+ {0x410, 0x52},
+ {0x610, 0x52},
+ {0x038, 0xfe},
+ {0x738, 0x1f},
+ {0x238, 0xfe},
+ {0x438, 0xfe},
+ {0x638, 0x1f},
+ }
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
new file mode 100644
index 0000000..04f7732
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -0,0 +1,1930 @@
+/* Copyright (c) 2011-2018, 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 "msm_csiphy.h"
+#include "msm_sd.h"
+#include "include/msm_csiphy_2_0_hwreg.h"
+#include "include/msm_csiphy_2_2_hwreg.h"
+#include "include/msm_csiphy_3_0_hwreg.h"
+#include "include/msm_csiphy_3_1_hwreg.h"
+#include "include/msm_csiphy_3_2_hwreg.h"
+#include "include/msm_csiphy_3_4_2_hwreg.h"
+#include "include/msm_csiphy_3_5_hwreg.h"
+#include "include/msm_csiphy_5_0_hwreg.h"
+#include "include/msm_csiphy_5_0_1_hwreg.h"
+#include "cam_hw_ops.h"
+
+#define DBG_CSIPHY 0
+#define SOF_DEBUG_ENABLE 1
+#define SOF_DEBUG_DISABLE 0
+
+#define V4L2_IDENT_CSIPHY 50003
+#define CSIPHY_VERSION_V22 0x01
+#define CSIPHY_VERSION_V20 0x00
+#define CSIPHY_VERSION_V30 0x10
+#define CSIPHY_VERSION_V31 0x31
+#define CSIPHY_VERSION_V32 0x32
+#define CSIPHY_VERSION_V342 0x342
+#define CSIPHY_VERSION_V35 0x35
+#define CSIPHY_VERSION_V50 0x500
+#define CSIPHY_VERSION_V501 0x501
+#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
+#define CLK_LANE_OFFSET 1
+#define NUM_LANES_OFFSET 4
+#define CLOCK_LANE 0x02
+
+#define CSI_3PHASE_HW 1
+#define MAX_DPHY_DATA_LN 4
+#define CLOCK_OFFSET 0x700
+#define CSIPHY_SOF_DEBUG_COUNT 2
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct v4l2_file_operations msm_csiphy_v4l2_subdev_fops;
+
+static void msm_csiphy_write_settings(
+ struct csiphy_device *csiphy_dev,
+ struct csiphy_settings_t csiphy_settings)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_CSIPHY_SETTINGS; i++) {
+ if (csiphy_settings.settings[i].addr == 0 &&
+ csiphy_settings.settings[i].data == 0)
+ break;
+
+ msm_camera_io_w(csiphy_settings.settings[i].data,
+ csiphy_dev->base + csiphy_settings.settings[i].addr);
+ }
+}
+
+static void msm_csiphy_cphy_irq_config(
+ struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl11.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl11.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl12.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl12.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl13.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl13.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl14.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl14.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl15.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl15.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl16.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl16.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl17.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl17.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl18.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl18.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl19.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl19.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl20.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl20.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl21.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl21.addr);
+}
+
+static int msm_csiphy_3phase_lane_config(
+ struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ uint8_t i = 0;
+ uint16_t lane_mask = 0, lane_enable = 0, temp;
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ lane_mask = csiphy_params->lane_mask & 0x7;
+ while (lane_mask != 0) {
+ temp = (i << 1)+1;
+ lane_enable |= ((lane_mask & 0x1) << temp);
+ lane_mask >>= 1;
+ i++;
+ }
+ msm_camera_io_w(lane_enable,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+ lane_mask = csiphy_params->lane_mask & 0x7;
+ i = 0;
+ while (lane_mask & 0x7) {
+ if (!(lane_mask & 0x1)) {
+ i++;
+ lane_mask >>= 1;
+ continue;
+ }
+
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl21.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl21.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl26.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl26.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl27.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl27.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl1.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl1.addr + 0x200*i);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl2.addr + 0x200*i);
+ msm_camera_io_w((csiphy_params->settle_cnt & 0xff),
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl3.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl5.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl5.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl6.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl7.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl7.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl8.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl8.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl9.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl9.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl10.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl10.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl11.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl11.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl12.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl12.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl15.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl15.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl16.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl16.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl17.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl17.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl18.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl18.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl19.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl19.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl24.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl24.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl28.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl28.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl29.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl29.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl30.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl30.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl33.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl33.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl34.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl34.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl35.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl35.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl36.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl36.addr + 0x200*i);
+
+ if (ULPM_WAKE_UP_TIMER_MODE == 0x22) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl51.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.mipi_csiphy_3ph_lnn_ctrl51.addr +
+ 0x200*i);
+ }
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl25.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i);
+
+ lane_mask >>= 1;
+ i++;
+ }
+ if (csiphy_params->combo_mode == 1) {
+ msm_camera_io_w(0x2,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
+ } else {
+ msm_camera_io_w(0x6,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
+ }
+ /* Delay for stabilizing the regulator*/
+ usleep_range(10, 15);
+ msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params);
+ return 0;
+}
+
+static int msm_csiphy_3phase_lane_config_v50(
+ struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ uint8_t i = 0;
+ uint16_t lane_mask = 0, lane_enable = 0, temp;
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ lane_mask = csiphy_params->lane_mask & 0x7;
+ while (lane_mask != 0) {
+ temp = (i << 1)+1;
+ lane_enable |= ((lane_mask & 0x1) << temp);
+ lane_mask >>= 1;
+ i++;
+ }
+ msm_camera_io_w(lane_enable,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7_cphy.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7_cphy.addr);
+
+ lane_mask = csiphy_params->lane_mask & 0x7;
+ i = 0;
+ while (lane_mask & 0x7) {
+ if (!(lane_mask & 0x1)) {
+ i++;
+ lane_mask >>= 1;
+ continue;
+ }
+
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl23.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl26.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl26.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl27.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl27.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl1.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl1.addr + 0x200*i);
+ msm_camera_io_w((csiphy_params->settle_cnt & 0xff),
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl3.addr + 0x200*i);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl2.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl5.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl5.addr + 0x200*i);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl20.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl6.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl7.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl7.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl8.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl8.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl9.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl9.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl10.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl10.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl11.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl11.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl17.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl17.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl24.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl24.addr + 0x200*i);
+ if (ULPM_WAKE_UP_TIMER_MODE == 0x22) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl51.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.mipi_csiphy_3ph_lnn_ctrl51.addr +
+ 0x200*i);
+ }
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl25.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl55.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnn_ctrl55.addr + 0x200*i);
+
+ lane_mask >>= 1;
+ i++;
+ }
+ /* Delay for stabilizing the regulator*/
+ usleep_range(10, 15);
+ msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params);
+ return 0;
+}
+
+static int msm_csiphy_2phase_lane_config(
+ struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ uint32_t val = 0, lane_enable = 0, clk_lane, mask = 1;
+ uint16_t lane_mask = 0, i = 0, offset;
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ lane_mask = csiphy_params->lane_mask & 0x1f;
+ for (i = 0; i < MAX_DPHY_DATA_LN; i++) {
+ if (mask == 0x2) {
+ if (lane_mask & mask)
+ lane_enable |= 0x80;
+ i--;
+ } else if (lane_mask & mask)
+ lane_enable |= 0x1 << (i<<1);
+ mask <<= 1;
+ }
+ CDBG("%s:%d lane_enable: %d\n", __func__, __LINE__, lane_enable);
+
+ msm_camera_io_w(lane_enable,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+
+ for (i = 0, mask = 0x1; i < MAX_DPHY_DATA_LN; i++) {
+ if (!(lane_mask & mask)) {
+ if (mask == 0x2)
+ i--;
+ mask <<= 0x1;
+ continue;
+ }
+ if (mask == 0x2) {
+ val = 4;
+ offset = CLOCK_OFFSET;
+ clk_lane = 1;
+ i--;
+ } else {
+ offset = 0x200*i;
+ val = 0;
+ clk_lane = 0;
+ }
+
+ if (csiphy_params->combo_mode == 1) {
+ val |= 0xA;
+ if (mask == csiphy_dev->ctrl_reg->
+ csiphy_reg.combo_clk_mask) {
+ val |= 0x4;
+ clk_lane = 1;
+ }
+ }
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg6.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg8.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg8.addr + offset);
+ msm_camera_io_w(val, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_misc1.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg2.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg2.addr + offset);
+
+ msm_camera_io_w((csiphy_params->settle_cnt & 0xFF),
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg3.addr + offset);
+
+ if (clk_lane == 1) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnck_cfg1.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_lnck_cfg1.addr);
+
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.addr + offset);
+ } else {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.data,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.addr + offset);
+ }
+ if (csiphy_dev->hw_version == CSIPHY_VERSION_V342 &&
+ csiphy_params->combo_mode == 1) {
+ msm_camera_io_w(0x52,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.addr + offset);
+ } else {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.data,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.addr + offset);
+ }
+ if (clk_lane == 1 &&
+ csiphy_dev->hw_version == CSIPHY_VERSION_V342) {
+ msm_camera_io_w(0x1f,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg9.addr + offset);
+ } else {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg9.data,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg9.addr + offset);
+ }
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_test_imp.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_test_imp.addr + offset);
+ if (csiphy_dev->hw_version == CSIPHY_VERSION_V342) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl5.data,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl5.addr + offset);
+ }
+ mask <<= 1;
+ }
+ if (csiphy_dev->hw_version == CSIPHY_VERSION_V342 &&
+ csiphy_params->combo_mode != 1) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl0.data,
+ csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl0.addr);
+ }
+ msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params);
+ return 0;
+}
+
+static int msm_csiphy_2phase_lane_config_v50(
+ struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ uint32_t lane_enable = 0, mask = 1;
+ uint16_t lane_mask = 0, i = 0, offset;
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ lane_mask = csiphy_params->lane_mask & 0x1f;
+
+ lane_enable = msm_camera_io_r(csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+
+ /* write settle count and lane_enable */
+ for (i = 0; i < MAX_DPHY_DATA_LN; i++) {
+ if (mask == 0x2) {
+ if (lane_mask & mask)
+ lane_enable |= 0x80;
+ i--;
+ offset = CLOCK_OFFSET;
+ } else if (lane_mask & mask) {
+ lane_enable |= 0x1 << (i<<1);
+ offset = 0x200*i;
+ }
+
+ if (lane_mask & mask)
+ msm_camera_io_w((csiphy_params->settle_cnt & 0xFF),
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg2.addr + offset);
+ mask <<= 1;
+ }
+ CDBG("%s:%d lane_enable: 0x%x\n", __func__, __LINE__, lane_enable);
+
+ msm_camera_io_w(lane_enable,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+
+ /* write mode specific settings */
+ if (csiphy_params->combo_mode == 1)
+ msm_csiphy_write_settings(csiphy_dev,
+ csiphy_dev->ctrl_reg->csiphy_combo_mode_settings);
+ else {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.data,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl10.data,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl10.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl3.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl3.addr);
+
+ for (i = 0, mask = 0x1; i < MAX_DPHY_DATA_LN; i++) {
+ if (!(lane_mask & mask)) {
+ if (mask == 0x2)
+ i--;
+ mask <<= 0x1;
+ continue;
+ }
+ if (mask == 0x2) {
+ offset = CLOCK_OFFSET;
+ i--;
+ } else {
+ offset = 0x200*i;
+ }
+
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl11.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl11.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl13.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl13.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
+ if (mask == CLOCK_LANE) {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.addr);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl9.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl9.addr);
+ } else {
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.addr +
+ offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl9.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl9.addr +
+ offset);
+ }
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl14.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl14.addr + offset);
+ mask <<= 1;
+ }
+ }
+ msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params);
+ return 0;
+}
+
+static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ int rc = 0;
+ int j = 0, curr_lane = 0;
+ uint32_t val = 0;
+ long clk_rate = 0;
+ uint8_t lane_cnt = 0;
+ uint16_t lane_mask = 0;
+ void __iomem *csiphybase;
+ uint8_t csiphy_id = csiphy_dev->pdev->id;
+ int32_t lane_val = 0, lane_right = 0, num_lanes = 0;
+ int ratio = 1;
+
+ csiphybase = csiphy_dev->base;
+ if (!csiphybase) {
+ pr_err("%s: csiphybase NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ csiphy_dev->lane_mask[csiphy_id] |= csiphy_params->lane_mask;
+ lane_mask = csiphy_dev->lane_mask[csiphy_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;
+ }
+
+ clk_rate = (csiphy_params->csiphy_clk > 0)
+ ? csiphy_params->csiphy_clk :
+ csiphy_dev->csiphy_max_clk;
+ clk_rate = msm_camera_clk_set_rate(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_clk[csiphy_dev->csiphy_clk_index],
+ clk_rate);
+ if (clk_rate < 0) {
+ pr_err("csiphy_clk_set_rate failed\n");
+ return -EINVAL;
+ }
+
+ if (clk_rate < csiphy_dev->csiphy_max_clk &&
+ clk_rate > 0) {
+ ratio = csiphy_dev->csiphy_max_clk/clk_rate;
+ csiphy_params->settle_cnt = csiphy_params->settle_cnt/ratio;
+ }
+ CDBG("%s csiphy_params, mask = 0x%x cnt = %d\n",
+ __func__,
+ csiphy_params->lane_mask,
+ csiphy_params->lane_cnt);
+ CDBG("%s csiphy_params, settle cnt = 0x%x csid %d\n",
+ __func__, csiphy_params->settle_cnt,
+ csiphy_params->csid_core);
+
+ if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30 &&
+ csiphy_dev->clk_mux_base != NULL &&
+ csiphy_dev->hw_version < CSIPHY_VERSION_V50) {
+ val = msm_camera_io_r(csiphy_dev->clk_mux_base);
+ if (csiphy_params->combo_mode &&
+ (csiphy_params->lane_mask & 0x18) == 0x18) {
+ val &= ~0xf0;
+ val |= csiphy_params->csid_core << 4;
+ } else {
+ val &= ~0xf;
+ val |= (uint32_t)csiphy_params->csid_core;
+ }
+ msm_camera_io_w(val, csiphy_dev->clk_mux_base);
+ CDBG("%s clk mux addr %pK val 0x%x\n", __func__,
+ csiphy_dev->clk_mux_base, val);
+ /* ensure write is done */
+ mb();
+ }
+
+ csiphy_dev->csi_3phase = csiphy_params->csi_3phase;
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) {
+ if (csiphy_dev->csi_3phase == 1) {
+ rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_3p_clk_info,
+ csiphy_dev->csiphy_3p_clk, 2, true);
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
+ rc = msm_csiphy_3phase_lane_config_v50(
+ csiphy_dev, csiphy_params);
+ else
+ rc = msm_csiphy_3phase_lane_config(csiphy_dev,
+ csiphy_params);
+ csiphy_dev->num_irq_registers = 20;
+ } else {
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
+ rc = msm_csiphy_2phase_lane_config_v50(
+ csiphy_dev, csiphy_params);
+ else
+ rc = msm_csiphy_2phase_lane_config(csiphy_dev,
+ csiphy_params);
+ csiphy_dev->num_irq_registers = 11;
+ }
+ if (rc < 0) {
+ pr_err("%s:%d: Error in setting lane configuration\n",
+ __func__, __LINE__);
+ }
+ return rc;
+ }
+
+ msm_camera_io_w(0x1, csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_t_init_cfg0_addr);
+ msm_camera_io_w(0x1, csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_t_wakeup_cfg0_addr);
+
+ if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
+ val = 0x3;
+ msm_camera_io_w((lane_mask << 2) | val,
+ csiphybase +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_pwr_cfg_addr);
+ msm_camera_io_w(0x10, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg2_addr);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg3_addr);
+ msm_camera_io_w(0x24,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_interrupt_mask0_addr);
+ msm_camera_io_w(0x24,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_interrupt_clear0_addr);
+ } else {
+ val = 0x1;
+ msm_camera_io_w((lane_mask << 1) | val,
+ csiphybase +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_pwr_cfg_addr);
+ msm_camera_io_w(csiphy_params->combo_mode <<
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_mode_config_shift,
+ csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ 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 + csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg2_addr + 0x40*j);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg3_addr + 0x40*j);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_mask_val, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_mask_addr + 0x4*j);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_mask_val, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_clear_addr + 0x4*j);
+ if (csiphy_dev->is_3_1_20nm_hw == 1) {
+ if (j > CLK_LANE_OFFSET) {
+ lane_right = 0x8;
+ num_lanes = (lane_cnt - curr_lane)
+ << NUM_LANES_OFFSET;
+ if (lane_cnt < curr_lane) {
+ pr_err("%s: Lane_cnt is less than curr_lane number\n",
+ __func__);
+ return -EINVAL;
+ }
+ lane_val = lane_right|num_lanes;
+ } else if (j == 1) {
+ lane_val = 0x4;
+ }
+ if (csiphy_params->combo_mode == 1) {
+ /*
+ * In the case of combo mode, the clock is
+ * always 4th lane for the second sensor.
+ * So check whether the sensor is of one lane
+ * sensor and curr_lane for 0.
+ */
+ if (curr_lane == 0 &&
+ ((csiphy_params->lane_mask &
+ 0x18) == 0x18))
+ lane_val = 0x4;
+ }
+ msm_camera_io_w(lane_val, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_misc1_addr + 0x40*j);
+ msm_camera_io_w(0x17, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_test_imp + 0x40*j);
+ curr_lane++;
+ }
+ j++;
+ lane_mask >>= 1;
+ }
+ return rc;
+}
+
+static void msm_csiphy_disable_irq(
+ struct csiphy_device *csiphy_dev)
+{
+ void __iomem *csiphybase;
+
+ csiphybase = csiphy_dev->base;
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl11.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl12.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl13.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl14.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl15.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl16.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl17.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl18.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl19.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl20.addr);
+ msm_camera_io_w(0,
+ csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl21.addr);
+}
+
+static irqreturn_t msm_csiphy_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ int i;
+ struct csiphy_device *csiphy_dev = data;
+
+ if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_ENABLE) {
+ if (csiphy_dev->csiphy_sof_debug_count < CSIPHY_SOF_DEBUG_COUNT)
+ csiphy_dev->csiphy_sof_debug_count++;
+ else {
+ msm_csiphy_disable_irq(csiphy_dev);
+ return IRQ_HANDLED;
+ }
+ }
+
+ for (i = 0; i < csiphy_dev->num_irq_registers; i++) {
+ irq = msm_camera_io_r(
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_status0_addr + 0x4*i);
+ msm_camera_io_w(irq,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_clear0_addr + 0x4*i);
+ pr_err_ratelimited(
+ "%s CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x\n",
+ __func__, csiphy_dev->pdev->id, i, irq);
+ msm_camera_io_w(0x0,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_interrupt_clear0_addr + 0x4*i);
+ }
+ msm_camera_io_w(0x1, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr);
+ return IRQ_HANDLED;
+}
+
+static void msm_csiphy_reset(struct csiphy_device *csiphy_dev)
+{
+ msm_camera_io_w(0x1, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.mipi_csiphy_glbl_reset_addr);
+ usleep_range(5000, 8000);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.mipi_csiphy_glbl_reset_addr);
+}
+
+static void msm_csiphy_3ph_reset(struct csiphy_device *csiphy_dev)
+{
+ msm_camera_io_w(0x1, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl0.addr);
+ usleep_range(5000, 8000);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl0.addr);
+}
+
+#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->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ 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__);
+
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ csiphy_dev->ref_count--;
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy config_vreg failed\n",
+ __func__, __LINE__);
+ goto csiphy_vreg_config_fail;
+ }
+ rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy enable_vreg failed\n",
+ __func__, __LINE__);
+ goto top_vreg_enable_failed;
+ }
+
+ rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, true);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ goto csiphy_enable_clk_fail;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_enable_irq(csiphy_dev->irq, true);
+ if (rc < 0)
+ pr_err("%s: irq enable failed\n", __func__);
+
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW)
+ msm_csiphy_3ph_reset(csiphy_dev);
+ else
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V30)
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_hw_version_addr);
+ else
+ csiphy_dev->hw_version = csiphy_dev->hw_dts_version;
+
+ CDBG("%s:%d called csiphy_dev->hw_version 0x%x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+top_vreg_enable_failed:
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+csiphy_vreg_config_fail:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+}
+#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;
+ }
+ csiphy_dev->csiphy_sof_debug_count = 0;
+
+ 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__);
+ 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__);
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0) {
+ csiphy_dev->ref_count--;
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+ }
+ rc = msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy config_vreg failed\n",
+ __func__, __LINE__);
+ goto csiphy_vreg_config_fail;
+ }
+ rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy enable_vreg failed\n",
+ __func__, __LINE__);
+ goto top_vreg_enable_failed;
+ }
+
+ rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, true);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ goto csiphy_enable_clk_fail;
+ }
+ CDBG("%s:%d clk enable success\n", __func__, __LINE__);
+
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW)
+ msm_csiphy_3ph_reset(csiphy_dev);
+ else
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V30)
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base +
+ csiphy_dev->ctrl_reg->
+ csiphy_reg.mipi_csiphy_hw_version_addr);
+ else
+ csiphy_dev->hw_version = csiphy_dev->hw_dts_version;
+
+ csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE;
+ CDBG("%s:%d called csiphy_dev->hw_version 0x%x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+top_vreg_enable_failed:
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+csiphy_vreg_config_fail:
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ return rc;
+}
+#endif
+
+#if DBG_CSIPHY
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0;
+ int rc = 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;
+
+ 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;
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) {
+ msm_camera_io_w(0x0,
+ csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+ msm_camera_io_w(0x0,
+ csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
+ msm_camera_io_w(0x0,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
+ } else if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg2_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg2_addr);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_glbl_pwr_cfg_addr);
+ } else {
+ if (!csi_lane_params) {
+ pr_err("%s:%d failed: csi_lane_params %pK\n", __func__,
+ __LINE__, csi_lane_params);
+ return -EINVAL;
+ }
+ csi_lane_mask = (csi_lane_params->csi_lane_mask & 0x1F);
+
+ CDBG("%s csiphy_params, lane assign 0x%x mask = 0x%x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (!csi_lane_mask)
+ csi_lane_mask = 0x1f;
+
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg2_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_misc1_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_test_imp + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg2_addr);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_glbl_pwr_cfg_addr);
+ }
+
+ rc = msm_camera_enable_irq(csiphy_dev->irq, false);
+
+ msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, false);
+
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW &&
+ csiphy_dev->csi_3phase == 1) {
+ msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_3p_clk_info,
+ csiphy_dev->csiphy_3p_clk, 2, false);
+ }
+
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return 0;
+}
+#else
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0, rc = 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;
+
+ 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;
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW) {
+ msm_camera_io_w(0x0,
+ csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+ msm_camera_io_w(0x0,
+ csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
+ msm_camera_io_w(0x0,
+ csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
+ } else if (csiphy_dev->hw_version < CSIPHY_VERSION_V30) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg2_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg2_addr);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_glbl_pwr_cfg_addr);
+ } else {
+ if (!csi_lane_params) {
+ pr_err("%s:%d failed: csi_lane_params %pK\n", __func__,
+ __LINE__, csi_lane_params);
+ return -EINVAL;
+ }
+ csi_lane_mask = (csi_lane_params->csi_lane_mask & 0x1F);
+
+ CDBG("%s csiphy_params, lane assign 0x%x mask = 0x%x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (!csi_lane_mask)
+ csi_lane_mask = 0x1f;
+
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_cfg2_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_misc1_addr + 0x40*i);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnn_test_imp + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_lnck_cfg2_addr);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ csiphy_dev->ctrl_reg->csiphy_reg.
+ mipi_csiphy_glbl_pwr_cfg_addr);
+ }
+ if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_ENABLE)
+ rc = msm_camera_enable_irq(csiphy_dev->irq, false);
+
+ msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
+ csiphy_dev->num_clk, false);
+ if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW &&
+ csiphy_dev->csi_3phase == 1) {
+ msm_camera_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_3p_clk_info,
+ csiphy_dev->csiphy_3p_clk, 2, false);
+ }
+
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+
+ if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
+ CAM_AHB_SUSPEND_VOTE) < 0)
+ pr_err("%s: failed to remove vote for AHB\n", __func__);
+ return 0;
+}
+
+#endif
+static int32_t 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 __user *)cdata->cfg.csiphy_params,
+ sizeof(struct msm_camera_csiphy_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE;
+ rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params);
+ break;
+ case CSIPHY_RELEASE:
+ if (copy_from_user(&csi_lane_params,
+ (void __user *)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);
+
+ if (!csiphy_dev) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ 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:
+ case MSM_SD_SHUTDOWN:
+ rc = msm_csiphy_release(csiphy_dev, arg);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ if (!csiphy_dev || !csiphy_dev->ctrl_reg ||
+ !csiphy_dev->ref_count)
+ break;
+ if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_DISABLE) {
+ csiphy_dev->csiphy_sof_debug = SOF_DEBUG_ENABLE;
+ rc = msm_camera_enable_irq(csiphy_dev->irq, true);
+ }
+ break;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ if (!csiphy_dev || !csiphy_dev->ctrl_reg ||
+ !csiphy_dev->ref_count)
+ break;
+ csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE;
+ rc = msm_camera_enable_irq(csiphy_dev->irq, false);
+ break;
+ default:
+ pr_err_ratelimited("%s: command not found\n", __func__);
+ }
+ mutex_unlock(&csiphy_dev->mutex);
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_csiphy_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct csiphy_cfg_data32 *u32 =
+ (struct csiphy_cfg_data32 *)arg;
+ struct csiphy_cfg_data csiphy_data;
+
+ switch (cmd) {
+ case VIDIOC_MSM_CSIPHY_IO_CFG32:
+ cmd = VIDIOC_MSM_CSIPHY_IO_CFG;
+ csiphy_data.cfgtype = u32->cfgtype;
+ csiphy_data.cfg.csiphy_params =
+ compat_ptr(u32->cfg.csiphy_params);
+ return msm_csiphy_subdev_ioctl(sd, cmd, &csiphy_data);
+ default:
+ return msm_csiphy_subdev_ioctl(sd, cmd, arg);
+ }
+}
+
+static long msm_csiphy_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_csiphy_subdev_do_ioctl);
+}
+#endif
+
+static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = {
+ .ioctl = &msm_csiphy_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = {
+ .core = &msm_csiphy_subdev_core_ops,
+};
+
+static int msm_csiphy_get_clk_info(struct csiphy_device *csiphy_dev,
+ struct platform_device *pdev)
+{
+ int i, rc = 0;
+ char *csi_3p_clk_name = "csi_phy_3p_clk";
+ char *csi_3p_clk_src_name = "csiphy_3p_clk_src";
+ uint32_t clk_cnt = 0;
+
+ rc = msm_camera_get_clk_info(csiphy_dev->pdev,
+ &csiphy_dev->csiphy_all_clk_info,
+ &csiphy_dev->csiphy_all_clk,
+ &csiphy_dev->num_all_clk);
+ if (rc < 0) {
+ pr_err("%s:%d, failed\n", __func__, __LINE__);
+ return rc;
+ }
+ if (csiphy_dev->num_all_clk > CSIPHY_NUM_CLK_MAX) {
+ pr_err("%s: invalid count=%zu, max is %d\n", __func__,
+ csiphy_dev->num_all_clk, CSIPHY_NUM_CLK_MAX);
+ rc = -EINVAL;
+ goto MAX_CLK_ERROR;
+ }
+
+ for (i = 0; i < csiphy_dev->num_all_clk; i++) {
+ if (!strcmp(csiphy_dev->csiphy_all_clk_info[i].clk_name,
+ csi_3p_clk_src_name)) {
+ csiphy_dev->csiphy_3p_clk_info[0].clk_name =
+ csiphy_dev->csiphy_all_clk_info[i].clk_name;
+ csiphy_dev->csiphy_3p_clk_info[0].clk_rate =
+ csiphy_dev->csiphy_all_clk_info[i].clk_rate;
+ csiphy_dev->csiphy_3p_clk[0] =
+ csiphy_dev->csiphy_all_clk[i];
+ continue;
+ } else if (!strcmp(csiphy_dev->csiphy_all_clk_info[i].clk_name,
+ csi_3p_clk_name)) {
+ csiphy_dev->csiphy_3p_clk_info[1].clk_name =
+ csiphy_dev->csiphy_all_clk_info[i].clk_name;
+ csiphy_dev->csiphy_3p_clk_info[1].clk_rate =
+ csiphy_dev->csiphy_all_clk_info[i].clk_rate;
+ csiphy_dev->csiphy_3p_clk[1] =
+ csiphy_dev->csiphy_all_clk[i];
+ continue;
+ }
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_name =
+ csiphy_dev->csiphy_all_clk_info[i].clk_name;
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate =
+ csiphy_dev->csiphy_all_clk_info[i].clk_rate;
+ csiphy_dev->csiphy_clk[clk_cnt] =
+ csiphy_dev->csiphy_all_clk[clk_cnt];
+ if (!strcmp(csiphy_dev->csiphy_clk_info[clk_cnt].clk_name,
+ "csiphy_timer_src_clk")) {
+ CDBG("%s:%d, copy csiphy_timer_src_clk",
+ __func__, __LINE__);
+ csiphy_dev->csiphy_max_clk =
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate;
+ csiphy_dev->csiphy_clk_index = clk_cnt;
+ }
+ CDBG("%s: clk_rate[%d] = %ld\n", __func__, clk_cnt,
+ csiphy_dev->csiphy_clk_info[clk_cnt].clk_rate);
+ clk_cnt++;
+ }
+
+ csiphy_dev->num_clk = clk_cnt;
+ return rc;
+MAX_CLK_ERROR:
+ msm_camera_put_clk_info(csiphy_dev->pdev,
+ &csiphy_dev->csiphy_all_clk_info,
+ &csiphy_dev->csiphy_all_clk,
+ csiphy_dev->num_all_clk);
+
+ return rc;
+}
+
+static int 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)
+ return -ENOMEM;
+ new_csiphy_dev->is_3_1_20nm_hw = 0;
+ new_csiphy_dev->ctrl_reg = NULL;
+ new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t),
+ GFP_KERNEL);
+ if (!new_csiphy_dev->ctrl_reg)
+ 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->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_pads_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL);
+ new_csiphy_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ new_csiphy_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x4;
+ msm_sd_register(&new_csiphy_dev->msm_sd);
+
+ new_csiphy_dev->csiphy_3phase = 0;
+ new_csiphy_dev->num_irq_registers = 0x8;
+
+ if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v2.0")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v2_0;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V20;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v2.2")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v2_2;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V22;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.0")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_0;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V30;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.1")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_1;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V31;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.1.1")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_1;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V31;
+ new_csiphy_dev->is_3_1_20nm_hw = 1;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.2")) {
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_2;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V32;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.4.2")) {
+ new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v3_4_2_3ph;
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_4_2;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V342;
+ new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v3.5")) {
+ new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v3_5_3ph;
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v3_5;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V35;
+ new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v5.0")) {
+ new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v5_0_3ph;
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v5_0;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V50;
+ new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings =
+ csiphy_combo_mode_v5_0;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v5.01")) {
+ new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v5_0_1_3ph;
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v5_0_1;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V501;
+ new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings =
+ csiphy_combo_mode_v5_0_1;
+ } else {
+ pr_err("%s:%d, invalid hw version : 0x%x\n", __func__, __LINE__,
+ new_csiphy_dev->hw_dts_version);
+ rc = -EINVAL;
+ goto csiphy_no_resource;
+ }
+
+ rc = msm_camera_get_dt_vreg_data(pdev->dev.of_node,
+ &(new_csiphy_dev->csiphy_vreg),
+ &(new_csiphy_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: get vreg data from dtsi fail\n", __func__);
+ rc = -EFAULT;
+ goto csiphy_no_resource;
+ }
+ /* ToDo: Enable 3phase clock for dynamic clock enable/disable */
+ rc = msm_csiphy_get_clk_info(new_csiphy_dev, pdev);
+ if (rc < 0) {
+ pr_err("%s: msm_csiphy_get_clk_info() failed", __func__);
+ rc = -EFAULT;
+ goto csiphy_no_resource;
+ }
+
+ new_csiphy_dev->base = msm_camera_get_reg_base(pdev, "csiphy", true);
+ if (!new_csiphy_dev->base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+
+ if (new_csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) {
+ new_csiphy_dev->clk_mux_base = msm_camera_get_reg_base(pdev,
+ "csiphy_clk_mux", true);
+ if (!new_csiphy_dev->clk_mux_base)
+ pr_err("%s: no mem resource?\n", __func__);
+ }
+ new_csiphy_dev->irq = msm_camera_get_irq(pdev, "csiphy");
+ if (!new_csiphy_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_irq_resource;
+ }
+ rc = msm_camera_register_irq(pdev, new_csiphy_dev->irq,
+ msm_csiphy_irq, IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_irq_resource;
+ }
+ msm_camera_enable_irq(new_csiphy_dev->irq, false);
+
+ msm_cam_copy_v4l2_subdev_fops(&msm_csiphy_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_csiphy_v4l2_subdev_fops.compat_ioctl32 =
+ msm_csiphy_subdev_fops_ioctl;
+#endif
+ new_csiphy_dev->msm_sd.sd.devnode->fops =
+ &msm_csiphy_v4l2_subdev_fops;
+ new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+
+csiphy_no_irq_resource:
+ if (new_csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) {
+ msm_camera_put_reg_base(pdev, new_csiphy_dev->clk_mux_base,
+ "csiphy_clk_mux", true);
+ }
+ msm_camera_put_reg_base(pdev, new_csiphy_dev->base, "csiphy", true);
+csiphy_no_resource:
+ mutex_destroy(&new_csiphy_dev->mutex);
+ kfree(new_csiphy_dev->ctrl_reg);
+ kfree(new_csiphy_dev);
+ return rc;
+}
+
+static int msm_csiphy_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct csiphy_device *csiphy_dev =
+ v4l2_get_subdevdata(subdev);
+
+ msm_camera_put_clk_info(pdev,
+ &csiphy_dev->csiphy_all_clk_info,
+ &csiphy_dev->csiphy_all_clk,
+ csiphy_dev->num_all_clk);
+
+ msm_camera_put_reg_base(pdev, csiphy_dev->base, "csiphy", true);
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) {
+ msm_camera_put_reg_base(pdev, csiphy_dev->clk_mux_base,
+ "csiphy_clk_mux", true);
+ }
+ kfree(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,
+ .remove = msm_csiphy_exit,
+ .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/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
new file mode 100644
index 0000000..bc8f2d9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2011-2018, 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"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "cam_soc_api.h"
+
+#define MAX_CSIPHY 3
+#define CSIPHY_NUM_CLK_MAX 16
+#define MAX_CSIPHY_SETTINGS 120
+
+struct csiphy_reg_t {
+ uint32_t addr;
+ uint32_t data;
+};
+
+struct csiphy_settings_t {
+ struct csiphy_reg_t settings[MAX_CSIPHY_SETTINGS];
+};
+
+struct csiphy_reg_parms_t {
+/*MIPI CSI PHY registers*/
+ uint32_t mipi_csiphy_lnn_cfg1_addr;
+ uint32_t mipi_csiphy_lnn_cfg2_addr;
+ uint32_t mipi_csiphy_lnn_cfg3_addr;
+ uint32_t mipi_csiphy_lnn_cfg4_addr;
+ uint32_t mipi_csiphy_lnn_cfg5_addr;
+ uint32_t mipi_csiphy_lnck_cfg1_addr;
+ uint32_t mipi_csiphy_lnck_cfg2_addr;
+ uint32_t mipi_csiphy_lnck_cfg3_addr;
+ uint32_t mipi_csiphy_lnck_cfg4_addr;
+ uint32_t mipi_csiphy_lnn_test_imp;
+ uint32_t mipi_csiphy_lnn_misc1_addr;
+ uint32_t mipi_csiphy_glbl_reset_addr;
+ uint32_t mipi_csiphy_glbl_pwr_cfg_addr;
+ uint32_t mipi_csiphy_glbl_irq_cmd_addr;
+ uint32_t mipi_csiphy_hw_version_addr;
+ uint32_t mipi_csiphy_interrupt_status0_addr;
+ uint32_t mipi_csiphy_interrupt_mask0_addr;
+ uint32_t mipi_csiphy_interrupt_mask_val;
+ uint32_t mipi_csiphy_interrupt_mask_addr;
+ uint32_t mipi_csiphy_interrupt_clear0_addr;
+ uint32_t mipi_csiphy_interrupt_clear_addr;
+ uint32_t mipi_csiphy_mode_config_shift;
+ uint32_t mipi_csiphy_glbl_t_init_cfg0_addr;
+ uint32_t mipi_csiphy_t_wakeup_cfg0_addr;
+ uint32_t csiphy_version;
+ uint32_t combo_clk_mask;
+};
+
+struct csiphy_reg_3ph_parms_t {
+/*MIPI CSI PHY registers*/
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl5;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl6;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl34;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl35;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl36;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl1;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl2;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl3;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl5;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl6;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl7;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl8;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl9;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl10;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl11;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl12;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl13;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl14;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl15;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl16;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl17;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl18;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl19;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl21;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl23;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl24;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl25;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl26;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl27;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl28;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl29;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl30;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl31;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl32;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl33;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl51;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl11;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl12;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl13;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl14;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl15;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl16;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl17;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl18;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl19;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl20;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl21;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_misc1;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl0;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg1;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg2;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg3;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg4;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg5;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg6;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg7;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg8;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_cfg9;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl15;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_test_imp;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_test_force;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl5;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnck_cfg1;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl20;
+ struct csiphy_reg_t mipi_csiphy_3ph_lnn_ctrl55;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl11;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl13;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl10;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl0;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl3;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl14;
+ struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7_cphy;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl0;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl9;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl9;
+};
+
+struct csiphy_ctrl_t {
+ struct csiphy_reg_parms_t csiphy_reg;
+ struct csiphy_reg_3ph_parms_t csiphy_3ph_reg;
+ struct csiphy_settings_t csiphy_combo_mode_settings;
+};
+
+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 *irq;
+ void __iomem *base;
+ void __iomem *clk_mux_base;
+ struct mutex mutex;
+ uint32_t hw_version;
+ uint32_t hw_dts_version;
+ enum msm_csiphy_state_t csiphy_state;
+ struct csiphy_ctrl_t *ctrl_reg;
+ size_t num_all_clk;
+ struct clk **csiphy_all_clk;
+ struct msm_cam_clk_info *csiphy_all_clk_info;
+ uint32_t num_clk;
+ struct clk *csiphy_clk[CSIPHY_NUM_CLK_MAX];
+ struct msm_cam_clk_info csiphy_clk_info[CSIPHY_NUM_CLK_MAX];
+ struct clk *csiphy_3p_clk[2];
+ struct msm_cam_clk_info csiphy_3p_clk_info[2];
+ unsigned char csi_3phase;
+ int32_t ref_count;
+ uint16_t lane_mask[MAX_CSIPHY];
+ uint32_t is_3_1_20nm_hw;
+ uint32_t csiphy_clk_index;
+ uint32_t csiphy_max_clk;
+ uint8_t csiphy_3phase;
+ uint8_t num_irq_registers;
+ uint32_t csiphy_sof_debug;
+ uint32_t csiphy_sof_debug_count;
+ struct camera_vreg_t *csiphy_vreg;
+ struct regulator *csiphy_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+};
+
+#define VIDIOC_MSM_CSIPHY_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, void *)
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/Makefile b/drivers/media/platform/msm/camera_v2/sensor/eeprom/Makefile
new file mode 100644
index 0000000..ba78a65
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_MSM_EEPROM) += msm_eeprom.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
new file mode 100644
index 0000000..30a3113
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -0,0 +1,1887 @@
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_eeprom.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_eeprom_mutex);
+#ifdef CONFIG_COMPAT
+static struct v4l2_file_operations msm_eeprom_v4l2_subdev_fops;
+#endif
+
+/*
+ * msm_get_read_mem_size - Get the total size for allocation
+ * @eeprom_map_array: mem map
+ *
+ * Returns size after computation size, returns error in case of error
+ */
+static int msm_get_read_mem_size
+ (struct msm_eeprom_memory_map_array *eeprom_map_array) {
+ int size = 0, i, j;
+ struct msm_eeprom_mem_map_t *eeprom_map;
+
+ if (eeprom_map_array->msm_size_of_max_mappings >
+ MSM_EEPROM_MAX_MEM_MAP_CNT) {
+ pr_err("%s:%d Memory map cnt greter then expected: %d",
+ __func__, __LINE__,
+ eeprom_map_array->msm_size_of_max_mappings);
+ return -EINVAL;
+ }
+ for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) {
+ eeprom_map = &(eeprom_map_array->memory_map[j]);
+ if (eeprom_map->memory_map_size >
+ MSM_EEPROM_MEMORY_MAP_MAX_SIZE) {
+ pr_err("%s:%d Memory map size greter then expected: %d",
+ __func__, __LINE__,
+ eeprom_map->memory_map_size);
+ return -EINVAL;
+ }
+ for (i = 0; i < eeprom_map->memory_map_size; i++) {
+ if (eeprom_map->mem_settings[i].i2c_operation ==
+ MSM_CAM_READ) {
+ size += eeprom_map->mem_settings[i].reg_data;
+ }
+ }
+ }
+ CDBG("Total Data Size: %d\n", size);
+ return size;
+}
+
+/*
+ * msm_eeprom_verify_sum - verify crc32 checksum
+ * @mem: data buffer
+ * @size: size of data buffer
+ * @sum: expected checksum
+ *
+ * Returns 0 if checksum match, -EINVAL otherwise.
+ */
+static int msm_eeprom_verify_sum(const char *mem, uint32_t size, uint32_t sum)
+{
+ uint32_t crc = ~0;
+
+ /* check overflow */
+ if (size > crc - sizeof(uint32_t))
+ return -EINVAL;
+
+ crc = crc32_le(crc, mem, size);
+ if (~crc != sum) {
+ CDBG("%s: expect 0x%x, result 0x%x\n", __func__, sum, ~crc);
+ return -EINVAL;
+ }
+ CDBG("%s: checksum pass 0x%x\n", __func__, sum);
+ return 0;
+}
+
+/*
+ * msm_eeprom_match_crc - verify multiple regions using crc
+ * @data: data block to be verified
+ *
+ * Iterates through all regions stored in @data. Regions with odd index
+ * are treated as data, and its next region is treated as checksum. Thus
+ * regions of even index must have valid_size of 4 or 0 (skip verification).
+ * Returns a bitmask of verified regions, starting from LSB. 1 indicates
+ * a checksum match, while 0 indicates checksum mismatch or not verified.
+ */
+static uint32_t msm_eeprom_match_crc(struct msm_eeprom_memory_block_t *data)
+{
+ int j, rc;
+ uint32_t *sum;
+ uint32_t ret = 0;
+ uint8_t *memptr;
+ struct msm_eeprom_memory_map_t *map;
+
+ if (!data) {
+ pr_err("%s data is NULL", __func__);
+ return -EINVAL;
+ }
+ map = data->map;
+ memptr = data->mapdata;
+
+ for (j = 0; j + 1 < data->num_map; j += 2) {
+ /* empty table or no checksum */
+ if (!map[j].mem.valid_size || !map[j+1].mem.valid_size) {
+ memptr += map[j].mem.valid_size
+ + map[j+1].mem.valid_size;
+ continue;
+ }
+ if (map[j+1].mem.valid_size != sizeof(uint32_t)) {
+ CDBG("%s: malformatted data mapping\n", __func__);
+ return -EINVAL;
+ }
+ sum = (uint32_t *) (memptr + map[j].mem.valid_size);
+ rc = msm_eeprom_verify_sum(memptr, map[j].mem.valid_size,
+ *sum);
+ if (!rc)
+ ret |= 1 << (j/2);
+ memptr += map[j].mem.valid_size + map[j+1].mem.valid_size;
+ }
+ return ret;
+}
+
+/*
+ * read_eeprom_memory() - read map data into buffer
+ * @e_ctrl: eeprom control struct
+ * @block: block to be read
+ *
+ * This function iterates through blocks stored in block->map, reads each
+ * region and concatenate them into the pre-allocated block->mapdata
+ */
+static int read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_memory_block_t *block)
+{
+ int rc = 0;
+ int j;
+ struct msm_eeprom_memory_map_t *emap = block->map;
+ struct msm_eeprom_board_info *eb_info;
+ uint8_t *memptr = block->mapdata;
+
+ if (!e_ctrl) {
+ pr_err("%s e_ctrl is NULL", __func__);
+ return -EINVAL;
+ }
+
+ eb_info = e_ctrl->eboard_info;
+
+ for (j = 0; j < block->num_map; j++) {
+ if (emap[j].saddr.addr) {
+ eb_info->i2c_slaveaddr = emap[j].saddr.addr;
+ e_ctrl->i2c_client.cci_client->sid =
+ eb_info->i2c_slaveaddr >> 1;
+ pr_err("qcom,slave-addr = 0x%X\n",
+ eb_info->i2c_slaveaddr);
+ }
+
+ if (emap[j].page.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].page.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].page.addr,
+ emap[j].page.data, emap[j].page.data_t);
+ msleep(emap[j].page.delay);
+ if (rc < 0) {
+ pr_err("%s: page write failed\n", __func__);
+ return rc;
+ }
+ }
+ if (emap[j].pageen.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].pageen.addr,
+ emap[j].pageen.data, emap[j].pageen.data_t);
+ msleep(emap[j].pageen.delay);
+ if (rc < 0) {
+ pr_err("%s: page enable failed\n", __func__);
+ return rc;
+ }
+ }
+ if (emap[j].poll.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].poll.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &(e_ctrl->i2c_client), emap[j].poll.addr,
+ emap[j].poll.data, emap[j].poll.data_t,
+ emap[j].poll.delay);
+ if (rc < 0) {
+ pr_err("%s: poll failed\n", __func__);
+ return rc;
+ }
+ }
+
+ if (emap[j].mem.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].mem.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_read_seq(
+ &(e_ctrl->i2c_client), emap[j].mem.addr,
+ memptr, emap[j].mem.valid_size);
+ if (rc < 0) {
+ pr_err("%s: read failed\n", __func__);
+ return rc;
+ }
+ memptr += emap[j].mem.valid_size;
+ }
+ if (emap[j].pageen.valid_size) {
+ e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client), emap[j].pageen.addr,
+ 0, emap[j].pageen.data_t);
+ if (rc < 0) {
+ pr_err("%s: page disable failed\n", __func__);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+/*
+ * msm_eeprom_parse_memory_map() - parse memory map in device node
+ * @of: device node
+ * @data: memory block for output
+ *
+ * This functions parses @of to fill @data. It allocates map itself, parses
+ * the @of node, calculate total data length, and allocates required buffer.
+ * It only fills the map, but does not perform actual reading.
+ */
+static int msm_eeprom_parse_memory_map(struct device_node *of,
+ struct msm_eeprom_memory_block_t *data)
+{
+ int i, rc = 0;
+ char property[PROPERTY_MAXSIZE];
+ uint32_t count = 6;
+ struct msm_eeprom_memory_map_t *map;
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,num-blocks");
+ rc = of_property_read_u32(of, property, &data->num_map);
+ CDBG("%s: %s %d\n", __func__, property, data->num_map);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ return rc;
+ }
+
+ map = kzalloc((sizeof(*map) * data->num_map), GFP_KERNEL);
+ if (!map) {
+ rc = -ENOMEM;
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ data->map = map;
+
+ for (i = 0; i < data->num_map; i++) {
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,page%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].page, count);
+ if (rc < 0) {
+ pr_err("%s: failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ snprintf(property, PROPERTY_MAXSIZE,
+ "qcom,pageen%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].pageen, count);
+ if (rc < 0)
+ CDBG("%s: pageen not needed\n", __func__);
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,saddr%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].saddr.addr, 1);
+ if (rc < 0)
+ CDBG("%s: saddr not needed - block %d\n", __func__, i);
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,poll%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].poll, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ snprintf(property, PROPERTY_MAXSIZE, "qcom,mem%d", i);
+ rc = of_property_read_u32_array(of, property,
+ (uint32_t *) &map[i].mem, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ data->num_data += map[i].mem.valid_size;
+ }
+
+ CDBG("%s num_bytes %d\n", __func__, data->num_data);
+
+ data->mapdata = kzalloc(data->num_data, GFP_KERNEL);
+ if (!data->mapdata) {
+ rc = -ENOMEM;
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ return rc;
+
+ERROR:
+ kfree(data->map);
+ memset(data, 0, sizeof(*data));
+ return rc;
+}
+
+/*
+ * eeprom_parse_memory_map - Parse mem map
+ * @e_ctrl: ctrl structure
+ * @eeprom_map_array: eeprom map
+ *
+ * Returns success or failure
+ */
+static int eeprom_parse_memory_map(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_memory_map_array *eeprom_map_array)
+{
+ int rc = 0, i, j;
+ uint8_t *memptr;
+ struct msm_eeprom_mem_map_t *eeprom_map;
+
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.num_data = msm_get_read_mem_size(eeprom_map_array);
+ if (e_ctrl->cal_data.num_data <= 0) {
+ pr_err("%s:%d Error in reading mem size\n",
+ __func__, __LINE__);
+ e_ctrl->cal_data.num_data = 0;
+ return -EINVAL;
+ }
+ e_ctrl->cal_data.mapdata =
+ kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL);
+ if (!e_ctrl->cal_data.mapdata)
+ return -ENOMEM;
+
+ memptr = e_ctrl->cal_data.mapdata;
+ for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) {
+ eeprom_map = &(eeprom_map_array->memory_map[j]);
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->sid =
+ eeprom_map->slave_addr >> 1;
+ } else if (e_ctrl->i2c_client.client) {
+ e_ctrl->i2c_client.client->addr =
+ eeprom_map->slave_addr >> 1;
+ }
+ CDBG("Slave Addr: 0x%X\n", eeprom_map->slave_addr);
+ CDBG("Memory map Size: %d",
+ eeprom_map->memory_map_size);
+ for (i = 0; i < eeprom_map->memory_map_size; i++) {
+ switch (eeprom_map->mem_settings[i].i2c_operation) {
+ case MSM_CAM_WRITE: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ eeprom_map->mem_settings[i].reg_data,
+ eeprom_map->mem_settings[i].data_type);
+ msleep(eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: page write failed\n",
+ __func__);
+ goto clean_up;
+ }
+ }
+ break;
+ case MSM_CAM_POLL: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_poll(
+ &(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ eeprom_map->mem_settings[i].reg_data,
+ eeprom_map->mem_settings[i].data_type,
+ eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: poll failed\n",
+ __func__);
+ goto clean_up;
+ }
+ }
+ break;
+ case MSM_CAM_READ: {
+ e_ctrl->i2c_client.addr_type =
+ eeprom_map->mem_settings[i].addr_type;
+ rc = e_ctrl->i2c_client.i2c_func_tbl->
+ i2c_read_seq(&(e_ctrl->i2c_client),
+ eeprom_map->mem_settings[i].reg_addr,
+ memptr,
+ eeprom_map->mem_settings[i].reg_data);
+ msleep(eeprom_map->mem_settings[i].delay);
+ if (rc < 0) {
+ pr_err("%s: read failed\n",
+ __func__);
+ goto clean_up;
+ }
+ memptr += eeprom_map->mem_settings[i].reg_data;
+ }
+ break;
+ default:
+ pr_err("%s: %d Invalid i2c operation LC:%d\n",
+ __func__, __LINE__, i);
+ return -EINVAL;
+ }
+ }
+ }
+ memptr = e_ctrl->cal_data.mapdata;
+ for (i = 0; i < e_ctrl->cal_data.num_data; i++)
+ CDBG("memory_data[%d] = 0x%X\n", i, memptr[i]);
+ return rc;
+
+clean_up:
+ kfree(e_ctrl->cal_data.mapdata);
+ e_ctrl->cal_data.num_data = 0;
+ e_ctrl->cal_data.mapdata = NULL;
+ return rc;
+}
+
+/*
+ * msm_eeprom_power_up - Do eeprom power up here
+ * @e_ctrl: ctrl structure
+ * @power_info: power up info for eeprom
+ *
+ * Returns success or failure
+ */
+static int msm_eeprom_power_up(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_camera_power_ctrl_t *power_info) {
+ int32_t rc = 0;
+
+ rc = msm_camera_fill_vreg_params(
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info->power_setting, power_info->power_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d failed in camera_fill_vreg_params rc %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ /* Parse and fill vreg params for powerdown settings*/
+ rc = msm_camera_fill_vreg_params(
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info->power_down_setting,
+ power_info->power_down_setting_size);
+ if (rc < 0) {
+ pr_err("%s:%d failed msm_camera_fill_vreg_params for PDOWN rc %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("%s:%d failed in eeprom Power up rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+/*
+ * msm_eeprom_power_up - Do power up, parse and power down
+ * @e_ctrl: ctrl structure
+ * Returns success or failure
+ */
+static int eeprom_init_config(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *argp)
+{
+ int rc = 0;
+ struct msm_eeprom_cfg_data *cdata = argp;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_camera_power_ctrl_t *power_info;
+ struct msm_eeprom_memory_map_array *memory_map_arr = NULL;
+
+ power_setting_array =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array),
+ GFP_KERNEL);
+ if (!power_setting_array) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ memory_map_arr = kzalloc(sizeof(struct msm_eeprom_memory_map_array),
+ GFP_KERNEL);
+ if (!memory_map_arr) {
+ rc = -ENOMEM;
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ goto free_mem;
+ }
+
+ if (copy_from_user(power_setting_array,
+ (void __user *)cdata->cfg.eeprom_info.power_setting_array,
+ sizeof(struct msm_sensor_power_setting_array))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+ CDBG("%s:%d Size of power setting array: %d\n",
+ __func__, __LINE__, power_setting_array->size);
+ if (copy_from_user(memory_map_arr,
+ (void __user *)cdata->cfg.eeprom_info.mem_map_array,
+ sizeof(struct msm_eeprom_memory_map_array))) {
+ rc = -EINVAL;
+ pr_err("%s copy_from_user failed for memory map%d\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ power_info = &(e_ctrl->eboard_info->power_info);
+
+ power_info->power_setting =
+ power_setting_array->power_setting_a;
+ power_info->power_down_setting =
+ power_setting_array->power_down_setting_a;
+
+ power_info->power_setting_size =
+ power_setting_array->size;
+ power_info->power_down_setting_size =
+ power_setting_array->size_down;
+
+ if ((power_info->power_setting_size >
+ MAX_POWER_CONFIG) ||
+ (power_info->power_down_setting_size >
+ MAX_POWER_CONFIG) ||
+ (!power_info->power_down_setting_size) ||
+ (!power_info->power_setting_size)) {
+ rc = -EINVAL;
+ pr_err("%s:%d Invalid power setting size :%d, %d\n",
+ __func__, __LINE__,
+ power_info->power_setting_size,
+ power_info->power_down_setting_size);
+ goto free_mem;
+ }
+
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ cdata->cfg.eeprom_info.i2c_freq_mode;
+ if (e_ctrl->i2c_client.cci_client->i2c_freq_mode >
+ I2C_MAX_MODES) {
+ pr_err("%s::%d Improper I2C freq mode\n",
+ __func__, __LINE__);
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ I2C_STANDARD_MODE;
+ }
+ }
+
+ /* Fill vreg power info and power up here */
+ rc = msm_eeprom_power_up(e_ctrl, power_info);
+ if (rc < 0) {
+ pr_err("Power Up failed for eeprom\n");
+ goto free_mem;
+ }
+
+ rc = eeprom_parse_memory_map(e_ctrl, memory_map_arr);
+ if (rc < 0) {
+ pr_err("%s::%d memory map parse failed\n", __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("%s:%d Power down failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto free_mem;
+ }
+
+free_mem:
+ kfree(power_setting_array);
+ kfree(memory_map_arr);
+ power_setting_array = NULL;
+ memory_map_arr = NULL;
+ return rc;
+}
+
+static int msm_eeprom_get_cmm_data(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_cfg_data *cdata)
+{
+ int rc = 0;
+ struct msm_eeprom_cmm_t *cmm_data = &e_ctrl->eboard_info->cmm_data;
+
+ cdata->cfg.get_cmm_data.cmm_support = cmm_data->cmm_support;
+ cdata->cfg.get_cmm_data.cmm_compression = cmm_data->cmm_compression;
+ cdata->cfg.get_cmm_data.cmm_size = cmm_data->cmm_size;
+ return rc;
+}
+
+static int eeprom_config_read_cal_data(struct msm_eeprom_ctrl_t *e_ctrl,
+ struct msm_eeprom_cfg_data *cdata)
+{
+ int rc;
+
+ /* check range */
+ if (cdata->cfg.read_data.num_bytes >
+ e_ctrl->cal_data.num_data) {
+ CDBG("%s: Invalid size. exp %u, req %u\n", __func__,
+ e_ctrl->cal_data.num_data,
+ cdata->cfg.read_data.num_bytes);
+ return -EINVAL;
+ }
+
+ rc = copy_to_user((void __user *)cdata->cfg.read_data.dbuffer,
+ e_ctrl->cal_data.mapdata,
+ cdata->cfg.read_data.num_bytes);
+
+ return rc;
+}
+
+static int msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *argp)
+{
+ struct msm_eeprom_cfg_data *cdata =
+ (struct msm_eeprom_cfg_data *)argp;
+ int rc = 0;
+ size_t length = 0;
+
+ CDBG("%s E\n", __func__);
+ switch (cdata->cfgtype) {
+ case CFG_EEPROM_GET_INFO:
+ if (e_ctrl->userspace_probe == 1) {
+ pr_err("%s:%d Eeprom name should be module driver",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__);
+ cdata->is_supported = e_ctrl->is_supported;
+ length = strlen(e_ctrl->eboard_info->eeprom_name) + 1;
+ if (length > MAX_EEPROM_NAME) {
+ pr_err("%s:%d invalid eeprom_name length %d\n",
+ __func__, __LINE__, (int)length);
+ rc = -EINVAL;
+ break;
+ }
+ memcpy(cdata->cfg.eeprom_name,
+ e_ctrl->eboard_info->eeprom_name, length);
+ break;
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
+ cdata->cfg.get_data.num_bytes =
+ e_ctrl->cal_data.num_data;
+ break;
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
+ rc = eeprom_config_read_cal_data(e_ctrl, cdata);
+ break;
+ case CFG_EEPROM_GET_MM_INFO:
+ CDBG("%s E CFG_EEPROM_GET_MM_INFO\n", __func__);
+ rc = msm_eeprom_get_cmm_data(e_ctrl, cdata);
+ break;
+ case CFG_EEPROM_INIT:
+ if (e_ctrl->userspace_probe == 0) {
+ pr_err("%s:%d Eeprom already probed at kernel boot",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ if (e_ctrl->cal_data.num_data == 0) {
+ rc = eeprom_init_config(e_ctrl, argp);
+ if (rc < 0) {
+ pr_err("%s:%d Eeprom init failed\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ } else {
+ CDBG("%s:%d Already read eeprom\n",
+ __func__, __LINE__);
+ }
+ break;
+ default:
+ break;
+ }
+
+ CDBG("%s X rc: %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_eeprom_get_subdev_id(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("%s E\n", __func__);
+ if (!subdev_id) {
+ pr_err("%s failed\n", __func__);
+ return -EINVAL;
+ }
+ *subdev_id = e_ctrl->subdev_id;
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("%s X\n", __func__);
+ return 0;
+}
+
+static long msm_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ void *argp = (void *)arg;
+
+ CDBG("%s E\n", __func__);
+ CDBG("%s:%d a_ctrl %pK argp %pK\n", __func__, __LINE__, e_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_eeprom_get_subdev_id(e_ctrl, argp);
+ case VIDIOC_MSM_EEPROM_CFG:
+ return msm_eeprom_config(e_ctrl, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ CDBG("%s X\n", __func__);
+}
+
+static struct msm_camera_i2c_fn_t msm_eeprom_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_seq = msm_camera_cci_i2c_write_seq,
+ .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,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+static struct msm_camera_i2c_fn_t msm_eeprom_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,
+};
+
+static struct msm_camera_i2c_fn_t msm_eeprom_spi_func_tbl = {
+ .i2c_read = msm_camera_spi_read,
+ .i2c_read_seq = msm_camera_spi_read_seq,
+};
+
+static int msm_eeprom_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("%s E\n", __func__);
+ if (!e_ctrl) {
+ pr_err("%s failed e_ctrl is NULL\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s X\n", __func__);
+ return rc;
+}
+
+static int msm_eeprom_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("%s E\n", __func__);
+ if (!e_ctrl) {
+ pr_err("%s failed e_ctrl is NULL\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s X\n", __func__);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_eeprom_internal_ops = {
+ .open = msm_eeprom_open,
+ .close = msm_eeprom_close,
+};
+
+static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = {
+ .ioctl = msm_eeprom_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_eeprom_subdev_ops = {
+ .core = &msm_eeprom_subdev_core_ops,
+};
+
+static int msm_eeprom_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+
+ CDBG("%s E\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s i2c_check_functionality failed\n", __func__);
+ goto probe_failure;
+ }
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl)
+ return -ENOMEM;
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+ CDBG("%s client = 0x%pK\n", __func__, client);
+ e_ctrl->eboard_info = (struct msm_eeprom_board_info *)(id->driver_data);
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s:%d board info NULL\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto ectrl_free;
+ }
+ e_ctrl->i2c_client.client = client;
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->is_supported = 1;
+
+ /* Set device type as I2C */
+ e_ctrl->eeprom_device_type = MSM_CAMERA_I2C_DEVICE;
+ e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_qup_func_tbl;
+
+ if (e_ctrl->eboard_info->i2c_slaveaddr != 0)
+ e_ctrl->i2c_client.client->addr =
+ e_ctrl->eboard_info->i2c_slaveaddr;
+
+ /*Get clocks information*/
+ rc = msm_camera_i2c_dev_get_clk_info(
+ &e_ctrl->i2c_client.client->dev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ &e_ctrl->eboard_info->power_info.clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto ectrl_free;
+ }
+
+ /*IMPLEMENT READING PART*/
+ /* Initialize sub device */
+ v4l2_i2c_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->i2c_client.client,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
+ e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_sd_register(&e_ctrl->msm_sd);
+ CDBG("%s success result=%d X\n", __func__, rc);
+ return rc;
+
+ectrl_free:
+ kfree(e_ctrl);
+probe_failure:
+ pr_err("%s failed! rc = %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_eeprom_i2c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: eboard_info is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_camera_i2c_dev_put_clk_info(&e_ctrl->i2c_client.client->dev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ e_ctrl->cal_data.mapdata = NULL;
+ kfree(e_ctrl);
+ e_ctrl = NULL;
+
+ return 0;
+}
+
+static int msm_eeprom_spi_parse_of(struct msm_camera_spi_client *spic)
+{
+ int rc = -EFAULT;
+ uint32_t tmp[3];
+
+ if (of_property_read_u32_array(
+ spic->spi_master->dev.of_node,
+ "qcom,spiop,read", tmp, 3)) {
+ return -EFAULT;
+ }
+ spic->cmd_tbl.read.opcode = tmp[0];
+ spic->cmd_tbl.read.addr_len = tmp[1];
+ spic->cmd_tbl.read.dummy_len = tmp[2];
+
+ if (of_property_read_u32_array(
+ spic->spi_master->dev.of_node,
+ "qcom,spiop,readseq", tmp, 3)) {
+ return -EFAULT;
+ }
+ spic->cmd_tbl.read_seq.opcode = tmp[0];
+ spic->cmd_tbl.read_seq.addr_len = tmp[1];
+ spic->cmd_tbl.read_seq.dummy_len = tmp[2];
+
+ if (of_property_read_u32_array(
+ spic->spi_master->dev.of_node,
+ "qcom,spiop,queryid", tmp, 3)) {
+ return -EFAULT;
+ }
+ spic->cmd_tbl.query_id.opcode = tmp[0];
+ spic->cmd_tbl.query_id.addr_len = tmp[1];
+ spic->cmd_tbl.query_id.dummy_len = tmp[2];
+
+ rc = of_property_read_u32_array(spic->spi_master->dev.of_node,
+ "qcom,eeprom-id", tmp, 2);
+ if (rc) {
+ pr_err("%s: Failed to get eeprom id\n", __func__);
+ return rc;
+ }
+ spic->mfr_id0 = tmp[0];
+ spic->device_id0 = tmp[1];
+
+ return 0;
+}
+
+static int msm_eeprom_match_id(struct msm_eeprom_ctrl_t *e_ctrl)
+{
+ int rc;
+ struct msm_camera_i2c_client *client = &e_ctrl->i2c_client;
+ uint8_t id[2];
+
+ rc = msm_camera_spi_query_id(client, 0, &id[0], 2);
+ if (rc < 0)
+ return rc;
+ CDBG("%s: read 0x%x 0x%x, check 0x%x 0x%x\n", __func__, id[0],
+ id[1], client->spi_client->mfr_id0,
+ client->spi_client->device_id0);
+ if (id[0] != client->spi_client->mfr_id0
+ || id[1] != client->spi_client->device_id0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int msm_eeprom_get_dt_data(struct msm_eeprom_ctrl_t *e_ctrl)
+{
+ int rc = 0, i = 0;
+ struct msm_eeprom_board_info *eb_info;
+ struct msm_camera_power_ctrl_t *power_info =
+ &e_ctrl->eboard_info->power_info;
+ struct device_node *of_node = NULL;
+ struct msm_camera_gpio_conf *gconf = NULL;
+ int8_t gpio_array_size = 0;
+ uint16_t *gpio_array = NULL;
+
+ eb_info = e_ctrl->eboard_info;
+ if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE)
+ of_node = e_ctrl->i2c_client.
+ spi_client->spi_master->dev.of_node;
+ else if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ of_node = e_ctrl->pdev->dev.of_node;
+
+ if (!of_node) {
+ pr_err("%s: %d of_node is NULL\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ rc = msm_camera_get_dt_vreg_data(of_node, &power_info->cam_vreg,
+ &power_info->num_vreg);
+ if (rc < 0)
+ return rc;
+
+ if (e_ctrl->userspace_probe == 0) {
+ rc = msm_camera_get_dt_power_setting_data(of_node,
+ power_info->cam_vreg, power_info->num_vreg,
+ power_info);
+ if (rc < 0)
+ goto ERROR1;
+ }
+
+ power_info->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!power_info->gpio_conf) {
+ rc = -ENOMEM;
+ goto ERROR2;
+ }
+ gconf = power_info->gpio_conf;
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size > 0) {
+ gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t),
+ GFP_KERNEL);
+ if (!gpio_array)
+ goto ERROR3;
+ 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_camera_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_camera_init_gpio_pin_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+ kfree(gpio_array);
+ }
+
+ return rc;
+ERROR4:
+ kfree(gpio_array);
+ERROR3:
+ kfree(power_info->gpio_conf);
+ERROR2:
+ kfree(power_info->cam_vreg);
+ERROR1:
+ kfree(power_info->power_setting);
+ return rc;
+}
+
+
+static int msm_eeprom_cmm_dts(struct msm_eeprom_board_info *eb_info,
+ struct device_node *of_node)
+{
+ int rc = 0;
+ struct msm_eeprom_cmm_t *cmm_data = &eb_info->cmm_data;
+
+ cmm_data->cmm_support =
+ of_property_read_bool(of_node, "qcom,cmm-data-support");
+ if (!cmm_data->cmm_support)
+ return -EINVAL;
+ cmm_data->cmm_compression =
+ of_property_read_bool(of_node, "qcom,cmm-data-compressed");
+ if (!cmm_data->cmm_compression)
+ CDBG("No MM compression data\n");
+
+ rc = of_property_read_u32(of_node, "qcom,cmm-data-offset",
+ &cmm_data->cmm_offset);
+ if (rc < 0)
+ CDBG("No MM offset data\n");
+
+ rc = of_property_read_u32(of_node, "qcom,cmm-data-size",
+ &cmm_data->cmm_size);
+ if (rc < 0)
+ CDBG("No MM size data\n");
+
+ CDBG("cmm_support: cmm_compr %d, cmm_offset %d, cmm_size %d\n",
+ cmm_data->cmm_compression,
+ cmm_data->cmm_offset,
+ cmm_data->cmm_size);
+ return 0;
+}
+
+static int msm_eeprom_spi_setup(struct spi_device *spi)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+ struct msm_camera_i2c_client *client = NULL;
+ struct msm_camera_spi_client *spi_client;
+ struct msm_eeprom_board_info *eb_info;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+ int rc = 0;
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl)
+ return -ENOMEM;
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+ client = &e_ctrl->i2c_client;
+ e_ctrl->is_supported = 0;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+
+ spi_client = kzalloc(sizeof(*spi_client), GFP_KERNEL);
+ if (!spi_client) {
+ kfree(e_ctrl);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32(spi->dev.of_node, "cell-index",
+ &e_ctrl->subdev_id);
+ CDBG("cell-index %d, rc %d\n", e_ctrl->subdev_id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ eb_info = kzalloc(sizeof(*eb_info), GFP_KERNEL);
+ if (!eb_info)
+ goto spi_free;
+ e_ctrl->eboard_info = eb_info;
+
+ rc = of_property_read_string(spi->dev.of_node, "qcom,eeprom-name",
+ &eb_info->eeprom_name);
+ CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__,
+ eb_info->eeprom_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ e_ctrl->userspace_probe = 1;
+ goto board_free;
+ }
+
+ e_ctrl->eeprom_device_type = MSM_CAMERA_SPI_DEVICE;
+ client->spi_client = spi_client;
+ spi_client->spi_master = spi;
+ client->i2c_func_tbl = &msm_eeprom_spi_func_tbl;
+ client->addr_type = MSM_CAMERA_I2C_3B_ADDR;
+
+ rc = msm_eeprom_cmm_dts(e_ctrl->eboard_info, spi->dev.of_node);
+ if (rc < 0)
+ CDBG("%s MM data miss:%d\n", __func__, __LINE__);
+
+ power_info = &eb_info->power_info;
+ power_info->dev = &spi->dev;
+
+ /*Get clocks information*/
+ rc = msm_camera_i2c_dev_get_clk_info(
+ &spi->dev,
+ &power_info->clk_info,
+ &power_info->clk_ptr,
+ &power_info->clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto board_free;
+ }
+
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc < 0)
+ goto board_free;
+
+ /* set spi instruction info */
+ spi_client->retry_delay = 1;
+ spi_client->retries = 0;
+
+ rc = msm_eeprom_spi_parse_of(spi_client);
+ if (rc < 0) {
+ dev_err(&spi->dev,
+ "%s: Error parsing device properties\n", __func__);
+ goto board_free;
+ }
+
+ if (e_ctrl->userspace_probe == 0) {
+ /* prepare memory buffer */
+ rc = msm_eeprom_parse_memory_map(spi->dev.of_node,
+ &e_ctrl->cal_data);
+ if (rc < 0)
+ CDBG("%s: no cal memory map\n", __func__);
+
+ /* power up eeprom for reading */
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto caldata_free;
+ }
+
+ /* check eeprom id */
+ rc = msm_eeprom_match_id(e_ctrl);
+ if (rc < 0) {
+ CDBG("%s: eeprom not matching %d\n", __func__, rc);
+ goto power_down;
+ }
+ /* read eeprom */
+ if (e_ctrl->cal_data.map) {
+ rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data);
+ if (rc < 0) {
+ pr_err("%s: read cal data failed\n", __func__);
+ goto power_down;
+ }
+ e_ctrl->is_supported |= msm_eeprom_match_crc(
+ &e_ctrl->cal_data);
+ }
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto caldata_free;
+ }
+ } else
+ e_ctrl->is_supported = 1;
+
+ /* initiazlie subdev */
+ v4l2_spi_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->i2c_client.spi_client->spi_master,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
+ e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_sd_register(&e_ctrl->msm_sd);
+ e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1;
+ CDBG("%s success result=%d supported=%x X\n", __func__, rc,
+ e_ctrl->is_supported);
+
+ return 0;
+
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+caldata_free:
+ msm_camera_i2c_dev_put_clk_info(
+ &e_ctrl->i2c_client.spi_client->spi_master->dev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+board_free:
+ kfree(e_ctrl->eboard_info);
+spi_free:
+ kfree(spi_client);
+ kfree(e_ctrl);
+ return rc;
+}
+
+static int msm_eeprom_spi_probe(struct spi_device *spi)
+{
+ int irq, cs, cpha, cpol, cs_high;
+
+ CDBG("%s\n", __func__);
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ spi_setup(spi);
+
+ irq = spi->irq;
+ cs = spi->chip_select;
+ cpha = (spi->mode & SPI_CPHA) ? 1 : 0;
+ cpol = (spi->mode & SPI_CPOL) ? 1 : 0;
+ cs_high = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
+ CDBG("%s: irq[%d] cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x]\n",
+ __func__, irq, cs, cpha, cpol, cs_high);
+ CDBG("%s: max_speed[%u]\n", __func__, spi->max_speed_hz);
+
+ return msm_eeprom_spi_setup(spi);
+}
+
+static int msm_eeprom_spi_remove(struct spi_device *sdev)
+{
+ struct v4l2_subdev *sd = spi_get_drvdata(sdev);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: board info is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_camera_i2c_dev_put_clk_info(
+ &e_ctrl->i2c_client.spi_client->spi_master->dev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->i2c_client.spi_client);
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ e_ctrl->cal_data.mapdata = NULL;
+ kfree(e_ctrl);
+ e_ctrl = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static void msm_eeprom_copy_power_settings_compat(
+ struct msm_sensor_power_setting_array *ps,
+ struct msm_sensor_power_setting_array32 *ps32)
+{
+ uint16_t i = 0;
+
+ ps->size = ps32->size;
+ for (i = 0; i < ps32->size; i++) {
+ ps->power_setting_a[i].config_val =
+ ps32->power_setting_a[i].config_val;
+ ps->power_setting_a[i].delay =
+ ps32->power_setting_a[i].delay;
+ ps->power_setting_a[i].seq_type =
+ ps32->power_setting_a[i].seq_type;
+ ps->power_setting_a[i].seq_val =
+ ps32->power_setting_a[i].seq_val;
+ }
+
+ ps->size_down = ps32->size_down;
+ for (i = 0; i < ps32->size_down; i++) {
+ ps->power_down_setting_a[i].config_val =
+ ps32->power_down_setting_a[i].config_val;
+ ps->power_down_setting_a[i].delay =
+ ps32->power_down_setting_a[i].delay;
+ ps->power_down_setting_a[i].seq_type =
+ ps32->power_down_setting_a[i].seq_type;
+ ps->power_down_setting_a[i].seq_val =
+ ps32->power_down_setting_a[i].seq_val;
+ }
+}
+
+static int eeprom_config_read_cal_data32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *arg)
+{
+ int rc;
+ uint8_t __user *ptr_dest = NULL;
+ struct msm_eeprom_cfg_data32 *cdata32 =
+ (struct msm_eeprom_cfg_data32 *) arg;
+ struct msm_eeprom_cfg_data cdata;
+
+ cdata.cfgtype = cdata32->cfgtype;
+ cdata.is_supported = cdata32->is_supported;
+ cdata.cfg.read_data.num_bytes = cdata32->cfg.read_data.num_bytes;
+ /* check range */
+ if (cdata.cfg.read_data.num_bytes >
+ e_ctrl->cal_data.num_data) {
+ CDBG("%s: Invalid size. exp %u, req %u\n", __func__,
+ e_ctrl->cal_data.num_data,
+ cdata.cfg.read_data.num_bytes);
+ return -EINVAL;
+ }
+ if (!e_ctrl->cal_data.mapdata)
+ return -EFAULT;
+
+ ptr_dest = (uint8_t __user *)compat_ptr(cdata32->cfg.read_data.dbuffer);
+
+ rc = copy_to_user(ptr_dest, e_ctrl->cal_data.mapdata,
+ cdata.cfg.read_data.num_bytes);
+
+ return rc;
+}
+
+static int eeprom_init_config32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *argp)
+{
+ int rc = 0;
+ struct msm_eeprom_cfg_data32 *cdata32 = argp;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting_array32 *power_setting_array32 = NULL;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+ struct msm_eeprom_memory_map_array *mem_map_array = NULL;
+
+ power_setting_array32 =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array32),
+ GFP_KERNEL);
+ if (!power_setting_array32) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ power_setting_array =
+ kzalloc(sizeof(struct msm_sensor_power_setting_array),
+ GFP_KERNEL);
+ if (power_setting_array == NULL) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto free_mem;
+ }
+ mem_map_array =
+ kzalloc(sizeof(struct msm_eeprom_memory_map_array),
+ GFP_KERNEL);
+ if (mem_map_array == NULL) {
+ pr_err("%s:%d Mem Alloc Fail\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (copy_from_user(power_setting_array32,
+ (void __user *)compat_ptr(cdata32->cfg.eeprom_info.
+ power_setting_array),
+ sizeof(struct msm_sensor_power_setting_array32))) {
+ pr_err("%s:%d copy_from_user failed\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+ CDBG("%s:%d Size of power setting array: %d",
+ __func__, __LINE__, power_setting_array32->size);
+ if (copy_from_user(mem_map_array,
+ (void __user *)
+ compat_ptr(cdata32->cfg.eeprom_info.mem_map_array),
+ sizeof(struct msm_eeprom_memory_map_array))) {
+ pr_err("%s:%d copy_from_user failed for memory map\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ power_info = &(e_ctrl->eboard_info->power_info);
+
+ if ((power_setting_array32->size > MAX_POWER_CONFIG) ||
+ (power_setting_array32->size_down > MAX_POWER_CONFIG) ||
+ (!power_setting_array32->size) ||
+ (!power_setting_array32->size_down)) {
+ pr_err("%s:%d invalid power setting size=%d size_down=%d\n",
+ __func__, __LINE__, power_setting_array32->size,
+ power_setting_array32->size_down);
+ rc = -EINVAL;
+ goto free_mem;
+ }
+ msm_eeprom_copy_power_settings_compat(
+ power_setting_array,
+ power_setting_array32);
+
+ power_info->power_setting =
+ power_setting_array->power_setting_a;
+ power_info->power_down_setting =
+ power_setting_array->power_down_setting_a;
+
+ power_info->power_setting_size =
+ power_setting_array->size;
+ power_info->power_down_setting_size =
+ power_setting_array->size_down;
+
+ if (e_ctrl->i2c_client.cci_client) {
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ cdata32->cfg.eeprom_info.i2c_freq_mode;
+ if (e_ctrl->i2c_client.cci_client->i2c_freq_mode >
+ I2C_MAX_MODES) {
+ pr_err("%s::%d Improper I2C Freq Mode\n",
+ __func__, __LINE__);
+ e_ctrl->i2c_client.cci_client->i2c_freq_mode =
+ I2C_STANDARD_MODE;
+ }
+ CDBG("%s:%d Not CCI probe", __func__, __LINE__);
+ }
+ /* Fill vreg power info and power up here */
+ rc = msm_eeprom_power_up(e_ctrl, power_info);
+ if (rc < 0) {
+ pr_err("%s:%d Power Up failed for eeprom\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = eeprom_parse_memory_map(e_ctrl, mem_map_array);
+ if (rc < 0) {
+ pr_err("%s:%d memory map parse failed\n",
+ __func__, __LINE__);
+ goto free_mem;
+ }
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc < 0)
+ pr_err("%s:%d Power down failed rc %d\n",
+ __func__, __LINE__, rc);
+
+free_mem:
+ kfree(power_setting_array32);
+ kfree(power_setting_array);
+ kfree(mem_map_array);
+ power_setting_array32 = NULL;
+ power_setting_array = NULL;
+ mem_map_array = NULL;
+ return rc;
+}
+
+static int msm_eeprom_config32(struct msm_eeprom_ctrl_t *e_ctrl,
+ void *argp)
+{
+ struct msm_eeprom_cfg_data32 *cdata =
+ (struct msm_eeprom_cfg_data32 *)argp;
+ int rc = 0;
+ size_t length = 0;
+
+ CDBG("%s E\n", __func__);
+ switch (cdata->cfgtype) {
+ case CFG_EEPROM_GET_INFO:
+ if (e_ctrl->userspace_probe == 1) {
+ pr_err("%s:%d Eeprom name should be module driver",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ CDBG("%s E CFG_EEPROM_GET_INFO\n", __func__);
+ cdata->is_supported = e_ctrl->is_supported;
+ length = strlen(e_ctrl->eboard_info->eeprom_name) + 1;
+ if (length > MAX_EEPROM_NAME) {
+ pr_err("%s:%d invalid eeprom_name length %d\n",
+ __func__, __LINE__, (int)length);
+ rc = -EINVAL;
+ break;
+ }
+ memcpy(cdata->cfg.eeprom_name,
+ e_ctrl->eboard_info->eeprom_name, length);
+ break;
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
+ cdata->cfg.get_data.num_bytes =
+ e_ctrl->cal_data.num_data;
+ break;
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
+ rc = eeprom_config_read_cal_data32(e_ctrl, argp);
+ break;
+ case CFG_EEPROM_INIT:
+ if (e_ctrl->userspace_probe == 0) {
+ pr_err("%s:%d Eeprom already probed at kernel boot",
+ __func__, __LINE__);
+ rc = -EINVAL;
+ break;
+ }
+ if (e_ctrl->cal_data.num_data == 0) {
+ rc = eeprom_init_config32(e_ctrl, argp);
+ if (rc < 0)
+ pr_err("%s:%d Eeprom init failed\n",
+ __func__, __LINE__);
+ } else {
+ CDBG("%s:%d Already read eeprom\n",
+ __func__, __LINE__);
+ }
+ break;
+ default:
+ break;
+ }
+
+ CDBG("%s X rc: %d\n", __func__, rc);
+ return rc;
+}
+
+static long msm_eeprom_subdev_ioctl32(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_eeprom_ctrl_t *e_ctrl = v4l2_get_subdevdata(sd);
+ void *argp = (void *)arg;
+
+ CDBG("%s E\n", __func__);
+ CDBG("%s:%d a_ctrl %pK argp %pK\n", __func__, __LINE__, e_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_eeprom_get_subdev_id(e_ctrl, argp);
+ case VIDIOC_MSM_EEPROM_CFG32:
+ return msm_eeprom_config32(e_ctrl, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ CDBG("%s X\n", __func__);
+}
+
+static long msm_eeprom_subdev_do_ioctl32(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return msm_eeprom_subdev_ioctl32(sd, cmd, arg);
+}
+
+static long msm_eeprom_subdev_fops_ioctl32(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_eeprom_subdev_do_ioctl32);
+}
+
+#endif
+
+static int msm_eeprom_platform_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ int j = 0;
+ uint32_t temp;
+
+ struct msm_camera_cci_client *cci_client = NULL;
+ struct msm_eeprom_ctrl_t *e_ctrl = NULL;
+ struct msm_eeprom_board_info *eb_info = NULL;
+ struct device_node *of_node = pdev->dev.of_node;
+ struct msm_camera_power_ctrl_t *power_info = NULL;
+
+ CDBG("%s E\n", __func__);
+
+ e_ctrl = kzalloc(sizeof(*e_ctrl), GFP_KERNEL);
+ if (!e_ctrl)
+ return -ENOMEM;
+ e_ctrl->eeprom_v4l2_subdev_ops = &msm_eeprom_subdev_ops;
+ e_ctrl->eeprom_mutex = &msm_eeprom_mutex;
+
+ e_ctrl->cal_data.mapdata = NULL;
+ e_ctrl->cal_data.map = NULL;
+ e_ctrl->userspace_probe = 0;
+ e_ctrl->is_supported = 0;
+ if (!of_node) {
+ pr_err("%s dev.of_node NULL\n", __func__);
+ rc = -EINVAL;
+ goto ectrl_free;
+ }
+
+ /* Set platform device handle */
+ e_ctrl->pdev = pdev;
+ /* Set device type as platform device */
+ e_ctrl->eeprom_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ e_ctrl->i2c_client.i2c_func_tbl = &msm_eeprom_cci_func_tbl;
+ e_ctrl->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!e_ctrl->i2c_client.cci_client) {
+ rc = -ENOMEM;
+ goto ectrl_free;
+ }
+
+ e_ctrl->eboard_info = kzalloc(sizeof(
+ struct msm_eeprom_board_info), GFP_KERNEL);
+ if (!e_ctrl->eboard_info) {
+ rc = -ENOMEM;
+ goto cciclient_free;
+ }
+
+ eb_info = e_ctrl->eboard_info;
+ power_info = &eb_info->power_info;
+ cci_client = e_ctrl->i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ power_info->dev = &pdev->dev;
+
+ /*Get clocks information*/
+ rc = msm_camera_get_clk_info(e_ctrl->pdev,
+ &power_info->clk_info,
+ &power_info->clk_ptr,
+ &power_info->clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto board_free;
+ }
+
+ rc = of_property_read_u32(of_node, "cell-index",
+ &pdev->id);
+ CDBG("cell-index %d, rc %d\n", pdev->id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto board_free;
+ }
+ e_ctrl->subdev_id = pdev->id;
+
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &e_ctrl->cci_master);
+ CDBG("qcom,cci-master %d, rc %d\n", e_ctrl->cci_master, rc);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ goto board_free;
+ }
+ cci_client->cci_i2c_master = e_ctrl->cci_master;
+
+ rc = of_property_read_string(of_node, "qcom,eeprom-name",
+ &eb_info->eeprom_name);
+ CDBG("%s qcom,eeprom-name %s, rc %d\n", __func__,
+ eb_info->eeprom_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ e_ctrl->userspace_probe = 1;
+ }
+
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc < 0)
+ goto board_free;
+
+ if (e_ctrl->userspace_probe == 0) {
+ rc = of_property_read_u32(of_node, "qcom,slave-addr",
+ &temp);
+ if (rc < 0) {
+ pr_err("%s failed rc %d\n", __func__, rc);
+ goto board_free;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,i2c-freq-mode",
+ &e_ctrl->i2c_freq_mode);
+ CDBG("qcom,i2c_freq_mode %d, rc %d\n",
+ e_ctrl->i2c_freq_mode, rc);
+ if (rc < 0) {
+ pr_err("%s qcom,i2c-freq-mode read fail. Setting to 0 %d\n",
+ __func__, rc);
+ e_ctrl->i2c_freq_mode = 0;
+ }
+ if (e_ctrl->i2c_freq_mode >= I2C_MAX_MODES) {
+ pr_err("%s:%d invalid i2c_freq_mode = %d\n",
+ __func__, __LINE__, e_ctrl->i2c_freq_mode);
+ e_ctrl->i2c_freq_mode = 0;
+ }
+ eb_info->i2c_slaveaddr = temp;
+ CDBG("qcom,slave-addr = 0x%X\n", eb_info->i2c_slaveaddr);
+ eb_info->i2c_freq_mode = e_ctrl->i2c_freq_mode;
+ cci_client->i2c_freq_mode = e_ctrl->i2c_freq_mode;
+ cci_client->sid = eb_info->i2c_slaveaddr >> 1;
+
+ rc = msm_eeprom_parse_memory_map(of_node, &e_ctrl->cal_data);
+ if (rc < 0)
+ goto board_free;
+
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
+ rc = read_eeprom_memory(e_ctrl, &e_ctrl->cal_data);
+ if (rc < 0) {
+ pr_err("%s read_eeprom_memory failed\n", __func__);
+ goto power_down;
+ }
+ for (j = 0; j < e_ctrl->cal_data.num_data; j++)
+ CDBG("memory_data[%d] = 0x%X\n", j,
+ e_ctrl->cal_data.mapdata[j]);
+
+ e_ctrl->is_supported |= msm_eeprom_match_crc(&e_ctrl->cal_data);
+
+ rc = msm_camera_power_down(power_info,
+ e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
+ } else
+ e_ctrl->is_supported = 1;
+
+ v4l2_subdev_init(&e_ctrl->msm_sd.sd,
+ e_ctrl->eeprom_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
+ platform_set_drvdata(pdev, &e_ctrl->msm_sd.sd);
+ e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
+ e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(e_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(e_ctrl->msm_sd.sd.name), "msm_eeprom");
+ media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
+ e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_sd_register(&e_ctrl->msm_sd);
+
+#ifdef CONFIG_COMPAT
+ msm_cam_copy_v4l2_subdev_fops(&msm_eeprom_v4l2_subdev_fops);
+ msm_eeprom_v4l2_subdev_fops.compat_ioctl32 =
+ msm_eeprom_subdev_fops_ioctl32;
+ e_ctrl->msm_sd.sd.devnode->fops = &msm_eeprom_v4l2_subdev_fops;
+#endif
+
+ e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1;
+ CDBG("%s X\n", __func__);
+ return rc;
+
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+memdata_free:
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+board_free:
+ kfree(e_ctrl->eboard_info);
+cciclient_free:
+ kfree(e_ctrl->i2c_client.cci_client);
+ectrl_free:
+ kfree(e_ctrl);
+ return rc;
+}
+
+static int msm_eeprom_platform_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct msm_eeprom_ctrl_t *e_ctrl;
+
+ if (!sd) {
+ pr_err("%s: Subdevice is NULL\n", __func__);
+ return 0;
+ }
+
+ e_ctrl = (struct msm_eeprom_ctrl_t *)v4l2_get_subdevdata(sd);
+ if (!e_ctrl) {
+ pr_err("%s: eeprom device is NULL\n", __func__);
+ return 0;
+ }
+
+ if (!e_ctrl->eboard_info) {
+ pr_err("%s: board info is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_camera_put_clk_info(e_ctrl->pdev,
+ &e_ctrl->eboard_info->power_info.clk_info,
+ &e_ctrl->eboard_info->power_info.clk_ptr,
+ e_ctrl->eboard_info->power_info.clk_info_size);
+
+ kfree(e_ctrl->i2c_client.cci_client);
+ kfree(e_ctrl->cal_data.mapdata);
+ kfree(e_ctrl->cal_data.map);
+ if (e_ctrl->eboard_info) {
+ kfree(e_ctrl->eboard_info->power_info.gpio_conf);
+ kfree(e_ctrl->eboard_info);
+ }
+ kfree(e_ctrl);
+ return 0;
+}
+
+static const struct of_device_id msm_eeprom_dt_match[] = {
+ { .compatible = "qcom,eeprom" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_eeprom_dt_match);
+
+static struct platform_driver msm_eeprom_platform_driver = {
+ .driver = {
+ .name = "qcom,eeprom",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_eeprom_dt_match,
+ },
+ .probe = msm_eeprom_platform_probe,
+ .remove = msm_eeprom_platform_remove,
+};
+
+static const struct i2c_device_id msm_eeprom_i2c_id[] = {
+ { "msm_eeprom", (kernel_ulong_t)NULL},
+ { }
+};
+
+static struct i2c_driver msm_eeprom_i2c_driver = {
+ .id_table = msm_eeprom_i2c_id,
+ .probe = msm_eeprom_i2c_probe,
+ .remove = msm_eeprom_i2c_remove,
+ .driver = {
+ .name = "msm_eeprom",
+ },
+};
+
+static struct spi_driver msm_eeprom_spi_driver = {
+ .driver = {
+ .name = "qcom_eeprom",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_eeprom_dt_match,
+ },
+ .probe = msm_eeprom_spi_probe,
+ .remove = msm_eeprom_spi_remove,
+};
+
+static int __init msm_eeprom_init_module(void)
+{
+ int rc = 0;
+
+ CDBG("%s E\n", __func__);
+ rc = platform_driver_register(&msm_eeprom_platform_driver);
+ CDBG("%s:%d platform rc %d\n", __func__, __LINE__, rc);
+ rc = spi_register_driver(&msm_eeprom_spi_driver);
+ CDBG("%s:%d spi rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&msm_eeprom_i2c_driver);
+}
+
+static void __exit msm_eeprom_exit_module(void)
+{
+ platform_driver_unregister(&msm_eeprom_platform_driver);
+ spi_unregister_driver(&msm_eeprom_spi_driver);
+ i2c_del_driver(&msm_eeprom_i2c_driver);
+}
+
+module_init(msm_eeprom_init_module);
+module_exit(msm_eeprom_exit_module);
+MODULE_DESCRIPTION("MSM EEPROM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.h b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.h
new file mode 100644
index 0000000..5596846
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2011-2015, 2018, 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_EEPROM_H
+#define MSM_EEPROM_H
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <soc/qcom/camera2.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_spi.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+
+struct msm_eeprom_ctrl_t;
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+#define PROPERTY_MAXSIZE 32
+
+struct msm_eeprom_ctrl_t {
+ struct platform_device *pdev;
+ struct mutex *eeprom_mutex;
+
+ struct v4l2_subdev sdev;
+ struct v4l2_subdev_ops *eeprom_v4l2_subdev_ops;
+ enum msm_camera_device_type_t eeprom_device_type;
+ struct msm_sd_subdev msm_sd;
+ enum cci_i2c_master_t cci_master;
+ enum i2c_freq_mode_t i2c_freq_mode;
+
+ struct msm_camera_i2c_client i2c_client;
+ struct msm_eeprom_board_info *eboard_info;
+ uint32_t subdev_id;
+ int32_t userspace_probe;
+ struct msm_eeprom_memory_block_t cal_data;
+ uint8_t is_supported;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile
new file mode 100644
index 0000000..6a28da5
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_flash.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
new file mode 100644
index 0000000..b26fee4
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -0,0 +1,1404 @@
+/* Copyright (c) 2009-2018, 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) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/leds-qpnp-flash.h>
+#include "msm_flash.h"
+#include "msm_camera_dt_util.h"
+#include "msm_cci.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_flash_mutex);
+
+static struct v4l2_file_operations msm_flash_v4l2_subdev_fops;
+static struct led_trigger *torch_trigger;
+
+static const struct of_device_id msm_flash_dt_match[] = {
+ {.compatible = "qcom,camera-flash", .data = NULL},
+ {}
+};
+
+static struct msm_flash_table msm_i2c_flash_table;
+static struct msm_flash_table msm_gpio_flash_table;
+static struct msm_flash_table msm_pmic_flash_table;
+
+static struct msm_flash_table *flash_table[] = {
+ &msm_i2c_flash_table,
+ &msm_gpio_flash_table,
+ &msm_pmic_flash_table
+};
+
+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,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+static void msm_torch_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (!torch_trigger) {
+ pr_err("No torch trigger found, can't set brightness\n");
+ return;
+ }
+
+ led_trigger_event(torch_trigger, value);
+};
+
+static struct led_classdev msm_torch_led[MAX_LED_TRIGGERS] = {
+ {
+ .name = "torch-light0",
+ .brightness_set = msm_torch_brightness_set,
+ .brightness = LED_OFF,
+ },
+ {
+ .name = "torch-light1",
+ .brightness_set = msm_torch_brightness_set,
+ .brightness = LED_OFF,
+ },
+ {
+ .name = "torch-light2",
+ .brightness_set = msm_torch_brightness_set,
+ .brightness = LED_OFF,
+ },
+};
+
+static int32_t msm_torch_create_classdev(struct platform_device *pdev,
+ void *data)
+{
+ int32_t rc = 0;
+ int32_t i = 0;
+ struct msm_flash_ctrl_t *fctrl =
+ (struct msm_flash_ctrl_t *)data;
+
+ if (!fctrl) {
+ pr_err("Invalid fctrl\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < fctrl->torch_num_sources; i++) {
+ if (fctrl->torch_trigger[i]) {
+ torch_trigger = fctrl->torch_trigger[i];
+ CDBG("%s:%d msm_torch_brightness_set for torch %d",
+ __func__, __LINE__, i);
+ msm_torch_brightness_set(&msm_torch_led[i],
+ LED_OFF);
+
+ rc = led_classdev_register(&pdev->dev,
+ &msm_torch_led[i]);
+ if (rc) {
+ pr_err("Failed to register %d led dev. rc = %d\n",
+ i, rc);
+ return rc;
+ }
+ } else {
+ pr_err("Invalid fctrl->torch_trigger[%d]\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+};
+
+static int32_t msm_flash_get_subdev_id(
+ struct msm_flash_ctrl_t *flash_ctrl, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (flash_ctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ *subdev_id = flash_ctrl->pdev->id;
+ else
+ *subdev_id = flash_ctrl->subdev_id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_flash_i2c_write_table(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_camera_i2c_reg_setting_array *settings)
+{
+ struct msm_camera_i2c_reg_setting conf_array;
+
+ conf_array.addr_type = settings->addr_type;
+ conf_array.data_type = settings->data_type;
+ conf_array.delay = settings->delay;
+ conf_array.reg_setting = settings->reg_setting_a;
+ conf_array.size = settings->size;
+
+ /* Validate the settings size */
+ if ((!conf_array.size) || (conf_array.size > MAX_I2C_REG_SET)) {
+ pr_err("failed: invalid size %d", conf_array.size);
+ return -EINVAL;
+ }
+
+ return flash_ctrl->flash_i2c_client.i2c_func_tbl->i2c_write_table(
+ &flash_ctrl->flash_i2c_client, &conf_array);
+}
+
+#ifdef CONFIG_COMPAT
+static void msm_flash_copy_power_settings_compat(
+ struct msm_sensor_power_setting *ps,
+ struct msm_sensor_power_setting32 *ps32, uint32_t size)
+{
+ uint16_t i = 0;
+
+ for (i = 0; i < size; i++) {
+ ps[i].config_val = ps32[i].config_val;
+ ps[i].delay = ps32[i].delay;
+ ps[i].seq_type = ps32[i].seq_type;
+ ps[i].seq_val = ps32[i].seq_val;
+ }
+}
+#endif
+
+static int32_t msm_flash_i2c_init(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ int32_t rc = 0;
+ struct msm_flash_init_info_t *flash_init_info =
+ flash_data->cfg.flash_init_info;
+ struct msm_camera_i2c_reg_setting_array *settings = NULL;
+ struct msm_camera_cci_client *cci_client = NULL;
+#ifdef CONFIG_COMPAT
+ struct msm_sensor_power_setting_array32 *power_setting_array32 = NULL;
+#endif
+ if (!flash_init_info || !flash_init_info->power_setting_array) {
+ pr_err("%s:%d failed: Null pointer\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ power_setting_array32 = kzalloc(
+ sizeof(struct msm_sensor_power_setting_array32),
+ GFP_KERNEL);
+ if (!power_setting_array32) {
+ pr_err("%s mem allocation failed %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(power_setting_array32,
+ (void __user *)flash_init_info->power_setting_array,
+ sizeof(struct msm_sensor_power_setting_array32))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ kfree(power_setting_array32);
+ return -EFAULT;
+ }
+
+ flash_ctrl->power_setting_array.size =
+ power_setting_array32->size;
+ flash_ctrl->power_setting_array.size_down =
+ power_setting_array32->size_down;
+
+ flash_ctrl->power_setting_array.power_down_setting =
+ kzalloc(sizeof(struct msm_sensor_power_setting)*
+ flash_ctrl->power_setting_array.size_down, GFP_KERNEL);
+ if (!flash_ctrl->power_setting_array.power_down_setting)
+ return -ENOMEM;
+ if (copy_from_user(
+ flash_ctrl->power_setting_array.power_down_setting,
+ (void __user *)
+ compat_ptr(power_setting_array32->power_down_setting),
+ sizeof(struct msm_sensor_power_setting)*
+ flash_ctrl->power_setting_array.size_down)) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ flash_ctrl->power_setting_array.power_setting =
+ kzalloc(sizeof(struct msm_sensor_power_setting)*
+ flash_ctrl->power_setting_array.size, GFP_KERNEL);
+ if (!flash_ctrl->power_setting_array.power_setting)
+ return -ENOMEM;
+ if (copy_from_user(
+ flash_ctrl->power_setting_array.power_setting,
+ (void __user *)
+ compat_ptr(power_setting_array32->power_setting),
+ sizeof(struct msm_sensor_power_setting)*
+ flash_ctrl->power_setting_array.size)) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ /* Validate power_up array size and power_down array size */
+ if ((!flash_ctrl->power_setting_array.size) ||
+ (flash_ctrl->power_setting_array.size >
+ MAX_POWER_CONFIG) ||
+ (!flash_ctrl->power_setting_array.size_down) ||
+ (flash_ctrl->power_setting_array.size_down >
+ MAX_POWER_CONFIG)) {
+
+ pr_err("failed: invalid size %d, size_down %d",
+ flash_ctrl->power_setting_array.size,
+ flash_ctrl->power_setting_array.size_down);
+ kfree(power_setting_array32);
+ power_setting_array32 = NULL;
+ return -EINVAL;
+ }
+ /* Copy the settings from compat struct to regular struct */
+ msm_flash_copy_power_settings_compat(
+ flash_ctrl->power_setting_array.power_setting_a,
+ power_setting_array32->power_setting_a,
+ flash_ctrl->power_setting_array.size);
+
+ msm_flash_copy_power_settings_compat(
+ flash_ctrl->power_setting_array.power_down_setting_a,
+ power_setting_array32->power_down_setting_a,
+ flash_ctrl->power_setting_array.size_down);
+ } else
+#endif
+ if (copy_from_user(&flash_ctrl->power_setting_array,
+ (void __user *)flash_init_info->power_setting_array,
+ sizeof(struct msm_sensor_power_setting_array))) {
+ pr_err("%s copy_from_user failed %d\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (flash_ctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = flash_ctrl->flash_i2c_client.cci_client;
+ cci_client->sid = flash_init_info->slave_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->i2c_freq_mode = flash_init_info->i2c_freq_mode;
+ }
+
+ flash_ctrl->power_info.power_setting =
+ flash_ctrl->power_setting_array.power_setting_a;
+ flash_ctrl->power_info.power_down_setting =
+ flash_ctrl->power_setting_array.power_down_setting_a;
+ flash_ctrl->power_info.power_setting_size =
+ flash_ctrl->power_setting_array.size;
+ flash_ctrl->power_info.power_down_setting_size =
+ flash_ctrl->power_setting_array.size_down;
+
+ if ((flash_ctrl->power_info.power_setting_size > MAX_POWER_CONFIG) ||
+ (flash_ctrl->power_info.power_down_setting_size > MAX_POWER_CONFIG)) {
+ pr_err("%s:%d invalid power setting size=%d size_down=%d\n",
+ __func__, __LINE__,
+ flash_ctrl->power_info.power_setting_size,
+ flash_ctrl->power_info.power_down_setting_size);
+ rc = -EINVAL;
+ goto msm_flash_i2c_init_fail;
+ }
+
+ rc = msm_camera_power_up(&flash_ctrl->power_info,
+ flash_ctrl->flash_device_type,
+ &flash_ctrl->flash_i2c_client);
+ if (rc < 0) {
+ pr_err("%s msm_camera_power_up failed %d\n",
+ __func__, __LINE__);
+ goto msm_flash_i2c_init_fail;
+ }
+
+ if (flash_data->cfg.flash_init_info->settings) {
+ settings = kzalloc(sizeof(
+ struct msm_camera_i2c_reg_setting_array), GFP_KERNEL);
+ if (!settings)
+ return -ENOMEM;
+
+ if (copy_from_user(settings, (void __user *)
+ flash_init_info->settings,
+ sizeof(struct msm_camera_i2c_reg_setting_array))) {
+ kfree(settings);
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ rc = msm_flash_i2c_write_table(flash_ctrl, settings);
+ kfree(settings);
+
+ if (rc < 0) {
+ pr_err("%s:%d msm_flash_i2c_write_table rc %d failed\n",
+ __func__, __LINE__, rc);
+ }
+ }
+
+ return 0;
+
+msm_flash_i2c_init_fail:
+ return rc;
+}
+
+static int32_t msm_flash_gpio_init(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ int32_t i = 0;
+ int32_t rc = 0;
+
+ CDBG("Enter");
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++)
+ flash_ctrl->flash_op_current[i] = LED_FULL;
+
+ for (i = 0; i < flash_ctrl->torch_num_sources; i++)
+ flash_ctrl->torch_op_current[i] = LED_HALF;
+
+ for (i = 0; i < flash_ctrl->torch_num_sources; i++) {
+ if (!flash_ctrl->torch_trigger[i]) {
+ if (i < flash_ctrl->flash_num_sources)
+ flash_ctrl->torch_trigger[i] =
+ flash_ctrl->flash_trigger[i];
+ else
+ flash_ctrl->torch_trigger[i] =
+ flash_ctrl->flash_trigger[
+ flash_ctrl->flash_num_sources - 1];
+ }
+ }
+
+ rc = flash_ctrl->func_tbl->camera_flash_off(flash_ctrl, flash_data);
+
+ CDBG("Exit");
+ return rc;
+}
+
+static int32_t msm_flash_i2c_release(
+ struct msm_flash_ctrl_t *flash_ctrl)
+{
+ int32_t rc = 0;
+
+ if (!(&flash_ctrl->power_info) || !(&flash_ctrl->flash_i2c_client)) {
+ pr_err("%s:%d failed: %pK %pK\n",
+ __func__, __LINE__, &flash_ctrl->power_info,
+ &flash_ctrl->flash_i2c_client);
+ return -EINVAL;
+ }
+
+ rc = msm_camera_power_down(&flash_ctrl->power_info,
+ flash_ctrl->flash_device_type,
+ &flash_ctrl->flash_i2c_client);
+ if (rc < 0) {
+ pr_err("%s msm_camera_power_down failed %d\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int32_t msm_flash_off(struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ int32_t i = 0;
+
+ CDBG("Enter\n");
+
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++)
+ if (flash_ctrl->flash_trigger[i])
+ led_trigger_event(flash_ctrl->flash_trigger[i], 0);
+
+ for (i = 0; i < flash_ctrl->torch_num_sources; i++)
+ if (flash_ctrl->torch_trigger[i])
+ led_trigger_event(flash_ctrl->torch_trigger[i], 0);
+ if (flash_ctrl->switch_trigger)
+ led_trigger_event(flash_ctrl->switch_trigger, 0);
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_flash_i2c_write_setting_array(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ int32_t rc = 0;
+ struct msm_camera_i2c_reg_setting_array *settings = NULL;
+
+ if (!flash_data->cfg.settings) {
+ pr_err("%s:%d failed: Null pointer\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ settings = kzalloc(sizeof(struct msm_camera_i2c_reg_setting_array),
+ GFP_KERNEL);
+ if (!settings)
+ return -ENOMEM;
+
+ if (copy_from_user(settings, (void __user *)flash_data->cfg.settings,
+ sizeof(struct msm_camera_i2c_reg_setting_array))) {
+ kfree(settings);
+ pr_err("%s copy_from_user failed %d\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ rc = msm_flash_i2c_write_table(flash_ctrl, settings);
+ kfree(settings);
+
+ if (rc < 0) {
+ pr_err("%s:%d msm_flash_i2c_write_table rc = %d failed\n",
+ __func__, __LINE__, rc);
+ }
+ return rc;
+}
+
+static int32_t msm_flash_init(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ uint32_t i = 0;
+ int32_t rc = -EFAULT;
+ enum msm_flash_driver_type flash_driver_type = FLASH_DRIVER_DEFAULT;
+
+ CDBG("Enter");
+
+ if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT) {
+ pr_err("%s:%d Invalid flash state = %d",
+ __func__, __LINE__, flash_ctrl->flash_state);
+ return 0;
+ }
+
+ if (flash_data->cfg.flash_init_info->flash_driver_type ==
+ FLASH_DRIVER_DEFAULT) {
+ flash_driver_type = flash_ctrl->flash_driver_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_data->flash_current[i] =
+ flash_ctrl->flash_max_current[i];
+ flash_data->flash_duration[i] =
+ flash_ctrl->flash_max_duration[i];
+ }
+ } else if (flash_data->cfg.flash_init_info->flash_driver_type ==
+ flash_ctrl->flash_driver_type) {
+ flash_driver_type = flash_ctrl->flash_driver_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_ctrl->flash_max_current[i] =
+ flash_data->flash_current[i];
+ flash_ctrl->flash_max_duration[i] =
+ flash_data->flash_duration[i];
+ }
+ }
+
+ if (flash_driver_type == FLASH_DRIVER_DEFAULT) {
+ pr_err("%s:%d invalid flash_driver_type", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
+ if (flash_driver_type == flash_table[i]->flash_driver_type) {
+ flash_ctrl->func_tbl = &flash_table[i]->func_tbl;
+ rc = 0;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("%s:%d failed invalid flash_driver_type %d\n",
+ __func__, __LINE__,
+ flash_data->cfg.flash_init_info->flash_driver_type);
+ }
+
+ if (flash_ctrl->func_tbl->camera_flash_init) {
+ rc = flash_ctrl->func_tbl->camera_flash_init(
+ flash_ctrl, flash_data);
+ if (rc < 0) {
+ pr_err("%s:%d camera_flash_init failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ }
+
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_INIT;
+
+ CDBG("Exit");
+ return 0;
+}
+
+static int32_t msm_flash_init_prepare(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+#ifdef CONFIG_COMPAT
+ struct msm_flash_cfg_data_t flash_data_k;
+ struct msm_flash_init_info_t flash_init_info;
+ int32_t i = 0;
+
+ if (!is_compat_task()) {
+ /*for 64-bit usecase,it need copy the data to local memory*/
+ flash_data_k.cfg_type = flash_data->cfg_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_data_k.flash_current[i] =
+ flash_data->flash_current[i];
+ flash_data_k.flash_duration[i] =
+ flash_data->flash_duration[i];
+ }
+
+ flash_data_k.cfg.flash_init_info = &flash_init_info;
+ if (copy_from_user(&flash_init_info,
+ (void __user *)(flash_data->cfg.flash_init_info),
+ sizeof(struct msm_flash_init_info_t))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ return msm_flash_init(flash_ctrl, &flash_data_k);
+ }
+ /*
+ * for 32-bit usecase,it already copy the userspace
+ * data to local memory in msm_flash_subdev_do_ioctl()
+ * so here do not need copy from user
+ */
+ return msm_flash_init(flash_ctrl, flash_data);
+#else
+ struct msm_flash_cfg_data_t flash_data_k;
+ struct msm_flash_init_info_t flash_init_info;
+ int32_t i = 0;
+
+ flash_data_k.cfg_type = flash_data->cfg_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_data_k.flash_current[i] =
+ flash_data->flash_current[i];
+ flash_data_k.flash_duration[i] =
+ flash_data->flash_duration[i];
+ }
+
+ flash_data_k.cfg.flash_init_info = &flash_init_info;
+ if (copy_from_user(&flash_init_info,
+ (void __user *)(flash_data->cfg.flash_init_info),
+ sizeof(struct msm_flash_init_info_t))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ return msm_flash_init(flash_ctrl, &flash_data_k);
+#endif
+}
+
+static int32_t msm_flash_prepare(
+ struct msm_flash_ctrl_t *flash_ctrl)
+{
+ int32_t ret = 0;
+
+ CDBG("%s:%d: State : %d\n",
+ __func__, __LINE__, flash_ctrl->flash_state);
+
+ if (flash_ctrl->switch_trigger == NULL) {
+ pr_err("%s:%d Invalid argument\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT &&
+ flash_ctrl->is_regulator_enabled == 0) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ ENABLE_REGULATOR, NULL);
+ if (ret < 0) {
+ pr_err("%s:%d regulator enable failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ flash_ctrl->is_regulator_enabled = 1;
+ } else if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_RELEASE &&
+ flash_ctrl->is_regulator_enabled) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ DISABLE_REGULATOR, NULL);
+ if (ret < 0) {
+ pr_err("%s:%d regulator disable failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ flash_ctrl->is_regulator_enabled = 0;
+ }
+ CDBG("%s:%d:Exit\n", __func__, __LINE__);
+ return ret;
+}
+
+static int32_t msm_flash_low(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ uint32_t curr = 0, max_current = 0;
+ int32_t i = 0;
+
+ CDBG("Enter\n");
+ /* Turn off flash triggers */
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++)
+ if (flash_ctrl->flash_trigger[i])
+ led_trigger_event(flash_ctrl->flash_trigger[i], 0);
+
+ /* Turn on flash triggers */
+ for (i = 0; i < flash_ctrl->torch_num_sources; i++) {
+ if (flash_ctrl->torch_trigger[i]) {
+ max_current = flash_ctrl->torch_max_current[i];
+ if (flash_data->flash_current[i] >= 0 &&
+ flash_data->flash_current[i] <
+ max_current) {
+ curr = flash_data->flash_current[i];
+ } else {
+ curr = flash_ctrl->torch_op_current[i];
+ pr_debug("LED current clamped to %d\n",
+ curr);
+ }
+ CDBG("low_flash_current[%d] = %d", i, curr);
+ led_trigger_event(flash_ctrl->torch_trigger[i],
+ curr);
+ }
+ }
+ if (flash_ctrl->switch_trigger)
+ led_trigger_event(flash_ctrl->switch_trigger, 1);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_flash_high(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_cfg_data_t *flash_data)
+{
+ int32_t curr = 0;
+ int32_t max_current = 0;
+ int32_t i = 0;
+
+ /* Turn off torch triggers */
+ for (i = 0; i < flash_ctrl->torch_num_sources; i++)
+ if (flash_ctrl->torch_trigger[i])
+ led_trigger_event(flash_ctrl->torch_trigger[i], 0);
+
+ /* Turn on flash triggers */
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++) {
+ if (flash_ctrl->flash_trigger[i]) {
+ max_current = flash_ctrl->flash_max_current[i];
+ if (flash_data->flash_current[i] >= 0 &&
+ flash_data->flash_current[i] <
+ max_current) {
+ curr = flash_data->flash_current[i];
+ } else {
+ curr = flash_ctrl->flash_op_current[i];
+ pr_debug("LED flash_current[%d] clamped %d\n",
+ i, curr);
+ }
+ CDBG("high_flash_current[%d] = %d", i, curr);
+ led_trigger_event(flash_ctrl->flash_trigger[i],
+ curr);
+ }
+ }
+ if (flash_ctrl->switch_trigger)
+ led_trigger_event(flash_ctrl->switch_trigger, 1);
+ return 0;
+}
+
+static int32_t msm_flash_query_current(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_query_data_t *flash_query_data)
+{
+ int32_t ret = -EINVAL;
+ int32_t max_current = -EINVAL;
+
+ if (flash_ctrl->switch_trigger) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ QUERY_MAX_CURRENT, &max_current);
+ if (ret < 0) {
+ pr_err("%s:%d Query max_avail_curr failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+ flash_query_data->max_avail_curr = max_current;
+ CDBG("%s: %d: max_avail_curr : %d\n", __func__, __LINE__,
+ flash_query_data->max_avail_curr);
+ return 0;
+}
+
+static int32_t msm_flash_release(
+ struct msm_flash_ctrl_t *flash_ctrl)
+{
+ int32_t rc = 0;
+
+ rc = flash_ctrl->func_tbl->camera_flash_off(flash_ctrl, NULL);
+ if (rc < 0) {
+ pr_err("%s:%d camera_flash_init failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE;
+ return 0;
+}
+
+static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl,
+ void *argp)
+{
+ int32_t rc = 0;
+ struct msm_flash_cfg_data_t *flash_data =
+ (struct msm_flash_cfg_data_t *) argp;
+
+ mutex_lock(flash_ctrl->flash_mutex);
+
+ CDBG("Enter %s type %d\n", __func__, flash_data->cfg_type);
+
+ switch (flash_data->cfg_type) {
+ case CFG_FLASH_INIT:
+ rc = msm_flash_init_prepare(flash_ctrl, flash_data);
+ break;
+ case CFG_FLASH_RELEASE:
+ if (flash_ctrl->flash_state != MSM_CAMERA_FLASH_RELEASE) {
+ rc = flash_ctrl->func_tbl->camera_flash_release(
+ flash_ctrl);
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
+ break;
+ case CFG_FLASH_OFF:
+ if ((flash_ctrl->flash_state != MSM_CAMERA_FLASH_RELEASE) &&
+ (flash_ctrl->flash_state != MSM_CAMERA_FLASH_OFF)) {
+ rc = flash_ctrl->func_tbl->camera_flash_off(
+ flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_OFF;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
+ break;
+ case CFG_FLASH_LOW:
+ if ((flash_ctrl->flash_state == MSM_CAMERA_FLASH_OFF) ||
+ (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)) {
+ rc = flash_ctrl->func_tbl->camera_flash_low(
+ flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_LOW;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
+ break;
+ case CFG_FLASH_HIGH:
+ if ((flash_ctrl->flash_state == MSM_CAMERA_FLASH_OFF) ||
+ (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)) {
+ rc = flash_ctrl->func_tbl->camera_flash_high(
+ flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_HIGH;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(flash_ctrl->flash_mutex);
+
+ rc = msm_flash_prepare(flash_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d Enable/Disable Regulator failed ret = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ CDBG("Exit %s type %d\n", __func__, flash_data->cfg_type);
+
+ return rc;
+}
+
+static int32_t msm_flash_query_data(struct msm_flash_ctrl_t *flash_ctrl,
+ void *argp)
+{
+ int32_t rc = -EINVAL, i = 0;
+ struct msm_flash_query_data_t *flash_query =
+ (struct msm_flash_query_data_t *) argp;
+
+ CDBG("Enter %s type %d\n", __func__, flash_query->query_type);
+
+ switch (flash_query->query_type) {
+ case FLASH_QUERY_CURRENT:
+ if (flash_ctrl->func_tbl && flash_ctrl->func_tbl->
+ camera_flash_query_current != NULL)
+ rc = flash_ctrl->func_tbl->
+ camera_flash_query_current(
+ flash_ctrl, flash_query);
+ else {
+ flash_query->max_avail_curr = 0;
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++) {
+ flash_query->max_avail_curr +=
+ flash_ctrl->flash_op_current[i];
+ }
+ rc = 0;
+ CDBG("%s: max_avail_curr: %d\n", __func__,
+ flash_query->max_avail_curr);
+ }
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ CDBG("Exit %s type %d\n", __func__, flash_query->query_type);
+
+ return rc;
+}
+
+static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_flash_ctrl_t *fctrl = NULL;
+ void *argp = (void *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err("sd NULL\n");
+ return -EINVAL;
+ }
+ fctrl = v4l2_get_subdevdata(sd);
+ if (!fctrl) {
+ pr_err("fctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_flash_get_subdev_id(fctrl, argp);
+ case VIDIOC_MSM_FLASH_CFG:
+ return msm_flash_config(fctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!fctrl->func_tbl) {
+ pr_err("fctrl->func_tbl NULL\n");
+ } else {
+ fctrl->func_tbl->camera_flash_release(fctrl);
+ return msm_flash_prepare(fctrl);
+ }
+ return -EINVAL;
+ case VIDIOC_MSM_FLASH_QUERY_DATA:
+ return msm_flash_query_data(fctrl, argp);
+ default:
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_flash_subdev_core_ops = {
+ .ioctl = msm_flash_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_flash_subdev_ops = {
+ .core = &msm_flash_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_flash_internal_ops;
+
+static int32_t msm_flash_get_pmic_source_info(
+ struct device_node *of_node,
+ struct msm_flash_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+ uint32_t count = 0, i = 0;
+ struct device_node *flash_src_node = NULL;
+ struct device_node *torch_src_node = NULL;
+ struct device_node *switch_src_node = NULL;
+
+ switch_src_node = of_parse_phandle(of_node, "qcom,switch-source", 0);
+ if (!switch_src_node) {
+ CDBG("%s:%d switch_src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_string(switch_src_node,
+ "qcom,default-led-trigger",
+ &fctrl->switch_trigger_name);
+ if (rc < 0) {
+ rc = of_property_read_string(switch_src_node,
+ "linux,default-trigger",
+ &fctrl->switch_trigger_name);
+ if (rc < 0)
+ pr_err("default-trigger read failed\n");
+ }
+ of_node_put(switch_src_node);
+ switch_src_node = NULL;
+ if (!rc) {
+ CDBG("switch trigger %s\n",
+ fctrl->switch_trigger_name);
+ led_trigger_register_simple(
+ fctrl->switch_trigger_name,
+ &fctrl->switch_trigger);
+ }
+ }
+
+ if (of_get_property(of_node, "qcom,flash-source", &count)) {
+ count /= sizeof(uint32_t);
+ CDBG("count %d\n", count);
+ if (count > MAX_LED_TRIGGERS) {
+ pr_err("invalid count\n");
+ return -EINVAL;
+ }
+ fctrl->flash_num_sources = count;
+ CDBG("%s:%d flash_num_sources = %d",
+ __func__, __LINE__, fctrl->flash_num_sources);
+ for (i = 0; i < count; i++) {
+ flash_src_node = of_parse_phandle(of_node,
+ "qcom,flash-source", i);
+ if (!flash_src_node) {
+ pr_err("flash_src_node NULL\n");
+ continue;
+ }
+
+ rc = of_property_read_string(flash_src_node,
+ "qcom,default-led-trigger",
+ &fctrl->flash_trigger_name[i]);
+ if (rc < 0) {
+ rc = of_property_read_string(flash_src_node,
+ "linux,default-trigger",
+ &fctrl->flash_trigger_name[i]);
+ if (rc < 0) {
+ pr_err("default-trigger read failed\n");
+ of_node_put(flash_src_node);
+ continue;
+ }
+ }
+
+ CDBG("default trigger %s\n",
+ fctrl->flash_trigger_name[i]);
+
+ /* Read operational-current */
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,current",
+ &fctrl->flash_op_current[i]);
+ if (rc < 0) {
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,current-ma",
+ &fctrl->flash_op_current[i]);
+ if (rc < 0) {
+ pr_err("current: read failed\n");
+ of_node_put(flash_src_node);
+ continue;
+ }
+ }
+
+ /* Read max-current */
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,max-current",
+ &fctrl->flash_max_current[i]);
+ if (rc < 0) {
+ pr_err("current: read failed\n");
+ of_node_put(flash_src_node);
+ continue;
+ }
+
+ /* Read max-duration */
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,duration",
+ &fctrl->flash_max_duration[i]);
+ if (rc < 0) {
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,duration-ms",
+ &fctrl->flash_max_duration[i]);
+ if (rc < 0) {
+ pr_err("duration: read failed\n");
+ of_node_put(flash_src_node);
+ }
+ /* Non-fatal; this property is optional */
+ }
+
+ of_node_put(flash_src_node);
+
+ CDBG("max_current[%d] %d\n",
+ i, fctrl->flash_op_current[i]);
+
+ led_trigger_register_simple(
+ fctrl->flash_trigger_name[i],
+ &fctrl->flash_trigger[i]);
+ }
+ if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT)
+ fctrl->flash_driver_type = FLASH_DRIVER_PMIC;
+ CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__,
+ fctrl->flash_driver_type);
+ }
+
+ if (of_get_property(of_node, "qcom,torch-source", &count)) {
+ count /= sizeof(uint32_t);
+ CDBG("count %d\n", count);
+ if (count > MAX_LED_TRIGGERS) {
+ pr_err("invalid count\n");
+ return -EINVAL;
+ }
+ fctrl->torch_num_sources = count;
+ CDBG("%s:%d torch_num_sources = %d",
+ __func__, __LINE__, fctrl->torch_num_sources);
+ for (i = 0; i < count; i++) {
+ torch_src_node = of_parse_phandle(of_node,
+ "qcom,torch-source", i);
+ if (!torch_src_node) {
+ pr_err("torch_src_node NULL\n");
+ continue;
+ }
+
+ rc = of_property_read_string(torch_src_node,
+ "qcom,default-led-trigger",
+ &fctrl->torch_trigger_name[i]);
+ if (rc < 0) {
+ rc = of_property_read_string(torch_src_node,
+ "linux,default-trigger",
+ &fctrl->torch_trigger_name[i]);
+ if (rc < 0) {
+ pr_err("default-trigger read failed\n");
+ of_node_put(torch_src_node);
+ continue;
+ }
+ }
+
+ CDBG("default trigger %s\n",
+ fctrl->torch_trigger_name[i]);
+
+ /* Read operational-current */
+ rc = of_property_read_u32(torch_src_node,
+ "qcom,current",
+ &fctrl->torch_op_current[i]);
+ if (rc < 0) {
+ rc = of_property_read_u32(torch_src_node,
+ "qcom,current-ma",
+ &fctrl->torch_op_current[i]);
+ if (rc < 0) {
+ pr_err("current: read failed\n");
+ of_node_put(torch_src_node);
+ continue;
+ }
+ }
+
+ /* Read max-current */
+ rc = of_property_read_u32(torch_src_node,
+ "qcom,max-current",
+ &fctrl->torch_max_current[i]);
+ if (rc < 0) {
+ pr_err("current: read failed\n");
+ of_node_put(torch_src_node);
+ continue;
+ }
+
+ of_node_put(torch_src_node);
+
+ CDBG("max_current[%d] %d\n",
+ i, fctrl->torch_op_current[i]);
+
+ led_trigger_register_simple(
+ fctrl->torch_trigger_name[i],
+ &fctrl->torch_trigger[i]);
+ }
+ if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT)
+ fctrl->flash_driver_type = FLASH_DRIVER_PMIC;
+ CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__,
+ fctrl->flash_driver_type);
+ }
+
+ return 0;
+}
+
+static int32_t msm_flash_get_dt_data(struct device_node *of_node,
+ struct msm_flash_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("called\n");
+
+ if (!of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ /* Read the sub device */
+ rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ CDBG("subdev id %d\n", fctrl->subdev_id);
+
+ fctrl->flash_driver_type = FLASH_DRIVER_DEFAULT;
+
+ /* Read the CCI master. Use M0 if not available in the node */
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &fctrl->cci_i2c_master);
+ CDBG("%s qcom,cci-master %d, rc %d\n", __func__, fctrl->cci_i2c_master,
+ rc);
+ if (rc < 0) {
+ /* Set default master 0 */
+ fctrl->cci_i2c_master = MASTER_0;
+ rc = 0;
+ } else {
+ fctrl->flash_driver_type = FLASH_DRIVER_I2C;
+ }
+
+ /* Read the flash and torch source info from device tree node */
+ rc = msm_flash_get_pmic_source_info(of_node, fctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_flash_get_pmic_source_info failed rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ /* Read the gpio information from device tree */
+ rc = msm_sensor_driver_get_gpio_data(
+ &(fctrl->power_info.gpio_conf), of_node);
+ if (rc < 0) {
+ pr_err("%s:%d msm_sensor_driver_get_gpio_data failed rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ if (fctrl->flash_driver_type == FLASH_DRIVER_DEFAULT)
+ fctrl->flash_driver_type = FLASH_DRIVER_GPIO;
+ CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__,
+ fctrl->flash_driver_type);
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_flash_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t i = 0;
+ int32_t rc = 0;
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ struct msm_flash_cfg_data_t32 *u32;
+ struct msm_flash_cfg_data_t flash_data;
+ struct msm_flash_init_info_t32 flash_init_info32;
+ struct msm_flash_init_info_t flash_init_info;
+
+ CDBG("Enter");
+
+ if (!file || !arg) {
+ pr_err("%s:failed NULL parameter\n", __func__);
+ return -EINVAL;
+ }
+ vdev = video_devdata(file);
+ sd = vdev_to_v4l2_subdev(vdev);
+ u32 = (struct msm_flash_cfg_data_t32 *)arg;
+
+ switch (cmd) {
+ case VIDIOC_MSM_FLASH_CFG32:
+ flash_data.cfg_type = u32->cfg_type;
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ flash_data.flash_current[i] = u32->flash_current[i];
+ flash_data.flash_duration[i] = u32->flash_duration[i];
+ }
+ cmd = VIDIOC_MSM_FLASH_CFG;
+ switch (flash_data.cfg_type) {
+ case CFG_FLASH_OFF:
+ case CFG_FLASH_LOW:
+ case CFG_FLASH_HIGH:
+ flash_data.cfg.settings = compat_ptr(u32->cfg.settings);
+ break;
+ case CFG_FLASH_INIT:
+ flash_data.cfg.flash_init_info = &flash_init_info;
+ if (copy_from_user(&flash_init_info32,
+ (void __user *)
+ compat_ptr(u32->cfg.flash_init_info),
+ sizeof(struct msm_flash_init_info_t32))) {
+ pr_err("%s copy_from_user failed %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ flash_init_info.flash_driver_type =
+ flash_init_info32.flash_driver_type;
+ flash_init_info.slave_addr =
+ flash_init_info32.slave_addr;
+ flash_init_info.i2c_freq_mode =
+ flash_init_info32.i2c_freq_mode;
+ flash_init_info.settings =
+ compat_ptr(flash_init_info32.settings);
+ flash_init_info.power_setting_array =
+ compat_ptr(
+ flash_init_info32.power_setting_array);
+ break;
+ default:
+ break;
+ }
+ break;
+ case VIDIOC_MSM_FLASH_CFG:
+ pr_err("invalid cmd 0x%x received\n", cmd);
+ return -EINVAL;
+ default:
+ return msm_flash_subdev_ioctl(sd, cmd, arg);
+ }
+
+ rc = msm_flash_subdev_ioctl(sd, cmd, &flash_data);
+ for (i = 0; i < MAX_LED_TRIGGERS; i++) {
+ u32->flash_current[i] = flash_data.flash_current[i];
+ u32->flash_duration[i] = flash_data.flash_duration[i];
+ }
+ CDBG("Exit");
+ return rc;
+}
+
+static long msm_flash_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_flash_subdev_do_ioctl);
+}
+#endif
+static int32_t msm_flash_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_flash_ctrl_t *flash_ctrl = NULL;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter");
+ if (!pdev->dev.of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ flash_ctrl = kzalloc(sizeof(struct msm_flash_ctrl_t), GFP_KERNEL);
+ if (!flash_ctrl)
+ return -ENOMEM;
+
+ memset(flash_ctrl, 0, sizeof(struct msm_flash_ctrl_t));
+
+ flash_ctrl->pdev = pdev;
+
+ rc = msm_flash_get_dt_data(pdev->dev.of_node, flash_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_flash_get_dt_data failed\n",
+ __func__, __LINE__);
+ kfree(flash_ctrl);
+ return -EINVAL;
+ }
+
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE;
+ flash_ctrl->power_info.dev = &flash_ctrl->pdev->dev;
+ flash_ctrl->flash_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ flash_ctrl->flash_mutex = &msm_flash_mutex;
+ flash_ctrl->flash_i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ flash_ctrl->flash_i2c_client.cci_client = kzalloc(
+ sizeof(struct msm_camera_cci_client), GFP_KERNEL);
+ if (!flash_ctrl->flash_i2c_client.cci_client) {
+ kfree(flash_ctrl);
+ pr_err("failed no memory\n");
+ return -ENOMEM;
+ }
+
+ cci_client = flash_ctrl->flash_i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = flash_ctrl->cci_i2c_master;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&flash_ctrl->msm_sd.sd, &msm_flash_subdev_ops);
+ v4l2_set_subdevdata(&flash_ctrl->msm_sd.sd, flash_ctrl);
+
+ flash_ctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops;
+ flash_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(flash_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(flash_ctrl->msm_sd.sd.name),
+ "msm_camera_flash");
+ media_entity_pads_init(&flash_ctrl->msm_sd.sd.entity, 0, NULL);
+ flash_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ flash_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&flash_ctrl->msm_sd);
+
+ CDBG("%s:%d flash sd name = %s", __func__, __LINE__,
+ flash_ctrl->msm_sd.sd.entity.name);
+ msm_cam_copy_v4l2_subdev_fops(&msm_flash_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_flash_v4l2_subdev_fops.compat_ioctl32 =
+ msm_flash_subdev_fops_ioctl;
+#endif
+ flash_ctrl->msm_sd.sd.devnode->fops = &msm_flash_v4l2_subdev_fops;
+
+ if (flash_ctrl->flash_driver_type == FLASH_DRIVER_PMIC)
+ rc = msm_torch_create_classdev(pdev, flash_ctrl);
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_flash_dt_match);
+
+static struct platform_driver msm_flash_platform_driver = {
+ .probe = msm_flash_platform_probe,
+ .driver = {
+ .name = "qcom,camera-flash",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_flash_dt_match,
+ },
+};
+
+static int __init msm_flash_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_flash_platform_driver);
+ if (rc)
+ pr_err("platform probe for flash failed");
+
+ return rc;
+}
+
+static void __exit msm_flash_exit_module(void)
+{
+ platform_driver_unregister(&msm_flash_platform_driver);
+}
+
+static struct msm_flash_table msm_pmic_flash_table = {
+ .flash_driver_type = FLASH_DRIVER_PMIC,
+ .func_tbl = {
+ .camera_flash_init = NULL,
+ .camera_flash_release = msm_flash_release,
+ .camera_flash_off = msm_flash_off,
+ .camera_flash_low = msm_flash_low,
+ .camera_flash_high = msm_flash_high,
+ .camera_flash_query_current = msm_flash_query_current,
+ },
+};
+
+static struct msm_flash_table msm_gpio_flash_table = {
+ .flash_driver_type = FLASH_DRIVER_GPIO,
+ .func_tbl = {
+ .camera_flash_init = msm_flash_gpio_init,
+ .camera_flash_release = msm_flash_release,
+ .camera_flash_off = msm_flash_off,
+ .camera_flash_low = msm_flash_low,
+ .camera_flash_high = msm_flash_high,
+ .camera_flash_query_current = NULL,
+ },
+};
+
+static struct msm_flash_table msm_i2c_flash_table = {
+ .flash_driver_type = FLASH_DRIVER_I2C,
+ .func_tbl = {
+ .camera_flash_init = msm_flash_i2c_init,
+ .camera_flash_release = msm_flash_i2c_release,
+ .camera_flash_off = msm_flash_i2c_write_setting_array,
+ .camera_flash_low = msm_flash_i2c_write_setting_array,
+ .camera_flash_high = msm_flash_i2c_write_setting_array,
+ .camera_flash_query_current = NULL,
+ },
+};
+
+module_init(msm_flash_init_module);
+module_exit(msm_flash_exit_module);
+MODULE_DESCRIPTION("MSM FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h
new file mode 100644
index 0000000..eda6ce4
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h
@@ -0,0 +1,127 @@
+/* Copyright (c) 2009-2016, 2018, 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_FLASH_H
+#define MSM_FLASH_H
+
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msm_cam_sensor.h>
+#include <soc/qcom/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_flash_state_t {
+ MSM_CAMERA_FLASH_INIT,
+ MSM_CAMERA_FLASH_OFF,
+ MSM_CAMERA_FLASH_LOW,
+ MSM_CAMERA_FLASH_HIGH,
+ MSM_CAMERA_FLASH_RELEASE,
+};
+
+struct msm_flash_ctrl_t;
+
+struct msm_flash_func_t {
+ int32_t (*camera_flash_init)(struct msm_flash_ctrl_t *,
+ struct msm_flash_cfg_data_t *);
+ int32_t (*camera_flash_release)(struct msm_flash_ctrl_t *);
+ int32_t (*camera_flash_off)(struct msm_flash_ctrl_t *,
+ struct msm_flash_cfg_data_t *);
+ int32_t (*camera_flash_low)(struct msm_flash_ctrl_t *,
+ struct msm_flash_cfg_data_t *);
+ int32_t (*camera_flash_high)(struct msm_flash_ctrl_t *,
+ struct msm_flash_cfg_data_t *);
+ int32_t (*camera_flash_query_current)(struct msm_flash_ctrl_t *,
+ struct msm_flash_query_data_t *);
+
+};
+
+struct msm_flash_table {
+ enum msm_flash_driver_type flash_driver_type;
+ struct msm_flash_func_t func_tbl;
+};
+
+struct msm_flash_reg_t {
+ struct msm_camera_i2c_reg_setting *init_setting;
+ struct msm_camera_i2c_reg_setting *off_setting;
+ struct msm_camera_i2c_reg_setting *release_setting;
+ struct msm_camera_i2c_reg_setting *low_setting;
+ struct msm_camera_i2c_reg_setting *high_setting;
+};
+
+struct msm_flash_ctrl_t {
+ struct msm_camera_i2c_client flash_i2c_client;
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct msm_flash_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+
+ /* Switch node to trigger led */
+ const char *switch_trigger_name;
+ struct led_trigger *switch_trigger;
+ uint32_t is_regulator_enabled;
+
+ /* Flash */
+ uint32_t flash_num_sources;
+ const char *flash_trigger_name[MAX_LED_TRIGGERS];
+ struct led_trigger *flash_trigger[MAX_LED_TRIGGERS];
+ uint32_t flash_op_current[MAX_LED_TRIGGERS];
+ uint32_t flash_max_current[MAX_LED_TRIGGERS];
+ uint32_t flash_max_duration[MAX_LED_TRIGGERS];
+
+ /* Torch */
+ uint32_t torch_num_sources;
+ const char *torch_trigger_name[MAX_LED_TRIGGERS];
+ struct led_trigger *torch_trigger[MAX_LED_TRIGGERS];
+ uint32_t torch_op_current[MAX_LED_TRIGGERS];
+ uint32_t torch_max_current[MAX_LED_TRIGGERS];
+
+ void *data;
+ enum msm_camera_device_type_t flash_device_type;
+ enum cci_i2c_master_t cci_i2c_master;
+ uint32_t subdev_id;
+ struct mutex *flash_mutex;
+ struct msm_sensor_power_setting_array power_setting_array;
+
+ /* flash driver type */
+ enum msm_flash_driver_type flash_driver_type;
+
+ /* flash state */
+ enum msm_camera_flash_state_t flash_state;
+};
+
+int msm_flash_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+
+int msm_flash_probe(struct platform_device *pdev, const void *data);
+
+int32_t msm_flash_create_v4lsubdev(struct platform_device *pdev,
+ void *data);
+int32_t msm_led_i2c_flash_create_v4lsubdev(void *data);
+
+int32_t msm_led_i2c_trigger_get_subdev_id(struct msm_flash_ctrl_t *fctrl,
+ void *arg);
+
+int32_t msm_led_i2c_trigger_config(struct msm_flash_ctrl_t *fctrl,
+ void *data);
+
+int msm_flash_led_init(struct msm_flash_ctrl_t *fctrl);
+int msm_flash_led_release(struct msm_flash_ctrl_t *fctrl);
+int msm_flash_led_off(struct msm_flash_ctrl_t *fctrl);
+int msm_flash_led_low(struct msm_flash_ctrl_t *fctrl);
+int msm_flash_led_high(struct msm_flash_ctrl_t *fctrl);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/Makefile b/drivers/media/platform/msm/camera_v2/sensor/io/Makefile
new file mode 100644
index 0000000..549c35a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/Makefile
@@ -0,0 +1,6 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+ccflags-y += -Idrivers/misc/
+obj-$(CONFIG_MSMB_CAMERA) += msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_spi.o msm_camera_dt_util.o msm_camera_tz_i2c.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
new file mode 100644
index 0000000..ae75ca9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
@@ -0,0 +1,579 @@
+/* Copyright (c) 2011-2018, 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 <soc/qcom/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_cci.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#define MAX_I2C_ADDR_TYPE_SIZE (MSM_CAMERA_I2C_3B_ADDR + 1)
+#define MAX_I2C_DATA_TYPE_SIZE (MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA + 1)
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[MAX_I2C_ADDR_TYPE_SIZE + MAX_I2C_DATA_TYPE_SIZE];
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_3B_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,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf = NULL;
+ 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
+ && client->addr_type != MSM_CAMERA_I2C_3B_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_DWORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ if (num_byte > I2C_REG_DATA_MAX) {
+ S_I2C_DBG("%s: Error num_byte:0x%x exceeds max:0x%x\n",
+ __func__, num_byte, I2C_REG_DATA_MAX);
+ return rc;
+ }
+
+ buf = kzalloc(num_byte, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ 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;
+ cci_ctrl.status = -EFAULT;
+ 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]);
+ }
+ kfree(buf);
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_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_array 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;
+ reg_conf_tbl.delay = 0;
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = ®_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,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ uint32_t i = 0;
+ struct msm_camera_cci_ctrl cci_ctrl;
+ struct msm_camera_i2c_reg_array *reg_conf_tbl = NULL;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ if (num_byte > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: num_byte=%d clamped to max supported %d\n",
+ __func__, num_byte, I2C_SEQ_REG_DATA_MAX);
+ return rc;
+ }
+
+ S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+ __func__, addr, num_byte);
+
+ reg_conf_tbl = kzalloc(num_byte *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_conf_tbl)
+ return -ENOMEM;
+
+ reg_conf_tbl[0].reg_addr = addr;
+ for (i = 0; i < num_byte; i++) {
+ reg_conf_tbl[i].reg_data = data[i];
+ reg_conf_tbl[i].delay = 0;
+ }
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE_SEQ;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = reg_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = MSM_CAMERA_I2C_BYTE_DATA;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = num_byte;
+ cci_ctrl.status = -EFAULT;
+ 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;
+ kfree(reg_conf_tbl);
+ reg_conf_tbl = NULL;
+ return rc;
+}
+
+static int32_t msm_camera_cci_i2c_write_table_cmd(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting,
+ enum msm_cci_cmd_type cmd)
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ 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;
+
+ cci_ctrl.cmd = cmd;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting =
+ write_setting->reg_setting;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ return msm_camera_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE_ASYNC);
+}
+
+int32_t msm_camera_cci_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ return msm_camera_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE_SYNC);
+}
+
+int32_t msm_camera_cci_i2c_write_table_sync_block(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ return msm_camera_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE_SYNC_BLOCK);
+}
+
+int32_t msm_camera_cci_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ return msm_camera_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE);
+}
+
+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 Invalid 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;
+
+ if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: number of bytes %u exceeding the max supported %d\n",
+ __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX);
+ return rc;
+ }
+
+ 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_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->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;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting =
+ write_setting->reg_setting;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = write_setting->data_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = write_setting->size;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ return rc;
+}
+
+static int32_t msm_camera_cci_i2c_compare(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data = 0;
+ int data_len = 0;
+
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ data_len = data_type;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ data_len = MSM_CAMERA_I2C_BYTE_DATA;
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ data_len = MSM_CAMERA_I2C_WORD_DATA;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ rc = msm_camera_cci_i2c_read(client, addr, ®_data, data_len);
+ if (rc < 0)
+ return rc;
+
+ rc = I2C_COMPARE_MISMATCH;
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ if (data == reg_data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ if ((reg_data & data) == data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ if (!(reg_data & data))
+ rc = I2C_COMPARE_MATCH;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ S_I2C_DBG("%s: Register and data match result %d\n", __func__,
+ rc);
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type, uint32_t delay_ms)
+{
+ int32_t rc = -EFAULT;
+ int32_t i = 0;
+
+ S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+ __func__, addr, data, data_type);
+
+ if (delay_ms > MAX_POLL_DELAY_MS) {
+ pr_err("%s:%d invalid delay = %d max_delay = %d\n",
+ __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS);
+ return -EINVAL;
+ }
+ for (i = 0; i < delay_ms; i++) {
+ rc = msm_camera_cci_i2c_compare(client,
+ addr, data, data_type);
+ if (!rc)
+ return rc;
+ usleep_range(1000, 1010);
+ }
+
+ /* If rc is 1 then read is successful but poll is failure */
+ if (rc == 1)
+ pr_err("%s:%d poll failed rc=%d(non-fatal)\n",
+ __func__, __LINE__, rc);
+
+ if (rc < 0)
+ pr_err("%s:%d poll failed rc=%d\n", __func__, __LINE__, rc);
+
+ return rc;
+}
+
+static int32_t msm_camera_cci_i2c_set_mask(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t mask,
+ enum msm_camera_i2c_data_type data_type, uint16_t set_mask)
+{
+ int32_t rc = -EFAULT;
+ uint16_t reg_data;
+
+ rc = msm_camera_cci_i2c_read(client, addr, ®_data, data_type);
+ if (rc < 0) {
+ S_I2C_DBG("%s read fail\n", __func__);
+ return rc;
+ }
+ S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n",
+ __func__, addr, reg_data, mask);
+
+ if (set_mask)
+ reg_data |= mask;
+ else
+ reg_data &= ~mask;
+ S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data);
+
+ rc = msm_camera_cci_i2c_write(client, addr, reg_data, data_type);
+ if (rc < 0)
+ S_I2C_DBG("%s write fail\n", __func__);
+
+ return rc;
+}
+
+static int32_t msm_camera_cci_i2c_set_write_mask_data(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data, int16_t mask,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data;
+
+ CDBG("%s\n", __func__);
+ if (mask == -1)
+ return 0;
+ if (mask == 0) {
+ rc = msm_camera_cci_i2c_write(client, addr, data, data_type);
+ } else {
+ rc = msm_camera_cci_i2c_read(client, addr, ®_data,
+ data_type);
+ if (rc < 0) {
+ CDBG("%s read fail\n", __func__);
+ return rc;
+ }
+ reg_data &= ~mask;
+ reg_data |= (data & mask);
+ rc = msm_camera_cci_i2c_write(client, addr, reg_data,
+ data_type);
+ if (rc < 0)
+ CDBG("%s write fail\n", __func__);
+ }
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ for (i = 0; i < size; i++) {
+ enum msm_camera_i2c_data_type dt;
+
+ if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+ rc = msm_camera_cci_i2c_poll(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->dt, I2C_POLL_TIME_MS);
+ } else {
+ if (reg_conf_tbl->dt == 0)
+ dt = data_type;
+ else
+ dt = reg_conf_tbl->dt;
+ switch (dt) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ rc = msm_camera_cci_i2c_write(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data, dt);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ rc = msm_camera_cci_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ rc = msm_camera_cci_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ rc = msm_camera_cci_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ rc = msm_camera_cci_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+ rc = msm_camera_cci_i2c_set_write_mask_data(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->mask,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n",
+ __func__, dt);
+ break;
+ }
+ }
+ if (rc < 0)
+ break;
+ reg_conf_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/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
new file mode 100644
index 0000000..d044a50
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
@@ -0,0 +1,1740 @@
+/* Copyright (c) 2013-2018, 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_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+#include "msm_cci.h"
+
+#define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend"
+#define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default"
+/*#define CONFIG_MSM_CAMERA_DT_DEBUG*/
+
+#define VALIDATE_VOLTAGE(min, max, config_val) ((config_val) && \
+ (config_val >= min) && (config_val <= max))
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg,
+ int num_vreg, struct msm_sensor_power_setting *power_setting,
+ uint16_t power_setting_size)
+{
+ uint16_t i = 0;
+ int j = 0;
+
+ /* Validate input parameters */
+ if (!cam_vreg || !power_setting) {
+ pr_err("%s:%d failed: cam_vreg %pK power_setting %pK", __func__,
+ __LINE__, cam_vreg, power_setting);
+ return -EINVAL;
+ }
+
+ /* Validate size of num_vreg */
+ if (num_vreg <= 0) {
+ pr_err("failed: num_vreg %d", num_vreg);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < power_setting_size; i++) {
+ if (power_setting[i].seq_type != SENSOR_VREG)
+ continue;
+
+ switch (power_setting[i].seq_val) {
+ case CAM_VDIG:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vdig")) {
+ CDBG("%s:%d i %d j %d cam_vdig\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ break;
+
+ case CAM_VIO:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vio")) {
+ CDBG("%s:%d i %d j %d cam_vio\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ break;
+
+ case CAM_VANA:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vana")) {
+ CDBG("%s:%d i %d j %d cam_vana\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ break;
+
+ case CAM_VAF:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name, "cam_vaf")) {
+ CDBG("%s:%d i %d j %d cam_vaf\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ break;
+
+ case CAM_V_CUSTOM1:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name,
+ "cam_v_custom1")) {
+ CDBG("%s:%d i %d j %d cam_vcustom1\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+
+ case CAM_V_CUSTOM2:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(cam_vreg[j].reg_name,
+ "cam_v_custom2")) {
+ CDBG("%s:%d i %d j %d cam_vcustom2\n",
+ __func__, __LINE__, i, j);
+ power_setting[i].seq_val = j;
+ if (VALIDATE_VOLTAGE(
+ cam_vreg[j].min_voltage,
+ cam_vreg[j].max_voltage,
+ power_setting[i].config_val)) {
+ cam_vreg[j].min_voltage =
+ cam_vreg[j].max_voltage =
+ power_setting[i].config_val;
+ }
+ break;
+ }
+ }
+ break;
+
+ default:
+ pr_err("%s:%d invalid seq_val %d\n", __func__,
+ __LINE__, power_setting[i].seq_val);
+ break;
+ }
+ }
+ return 0;
+}
+
+int msm_sensor_get_sub_module_index(struct device_node *of_node,
+ struct msm_sensor_info_t **s_info)
+{
+ int rc = 0, i = 0;
+ uint32_t val = 0, count = 0;
+ uint32_t *val_array = NULL;
+ struct device_node *src_node = NULL;
+ struct msm_sensor_info_t *sensor_info;
+
+ sensor_info = kzalloc(sizeof(*sensor_info), GFP_KERNEL);
+ if (!sensor_info)
+ return -ENOMEM;
+ for (i = 0; i < SUB_MODULE_MAX; i++) {
+ sensor_info->subdev_id[i] = -1;
+ /* Subdev expose additional interface for same sub module*/
+ sensor_info->subdev_intf[i] = -1;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,actuator-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,actuator cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,ois-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ois cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_OIS] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,eeprom-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d eeprom src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,eeprom cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_EEPROM] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,eeprom-sd-index", &val);
+ if (rc != -EINVAL) {
+ 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);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_EEPROM] = val;
+ } else {
+ rc = 0;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,led flash cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,ir-led-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ir led cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_IR_LED] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,ir-cut-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ir cut cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_IR_CUT] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,strobe-flash-sd-index", &val);
+ if (rc != -EINVAL) {
+ 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);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_STROBE_FLASH] = val;
+ } else {
+ rc = 0;
+ }
+
+ 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);
+ goto ERROR;
+ }
+ val_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!val_array) {
+ rc = -ENOMEM;
+ goto ERROR;
+ }
+
+ 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);
+ goto ERROR;
+ }
+ for (i = 0; i < count; i++) {
+ 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__);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ 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);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ val_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!val_array) {
+ rc = -ENOMEM;
+ goto ERROR;
+ }
+
+ 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);
+ goto ERROR;
+ }
+ for (i = 0; i < count; i++) {
+ 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__);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ *s_info = sensor_info;
+ return rc;
+ERROR:
+ kfree(sensor_info);
+ return rc;
+}
+
+int msm_sensor_get_dt_actuator_data(struct device_node *of_node,
+ struct msm_actuator_info **act_info)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ struct msm_actuator_info *actuator_info;
+
+ 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;
+
+ actuator_info = kzalloc(sizeof(*actuator_info), GFP_KERNEL);
+ if (!actuator_info) {
+ rc = -ENOMEM;
+ goto ERROR;
+ }
+
+ 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)
+ 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)
+ actuator_info->vcm_enable = val;
+
+ *act_info = actuator_info;
+ return 0;
+ERROR:
+ kfree(actuator_info);
+ return rc;
+}
+
+int msm_sensor_get_dt_csi_data(struct device_node *of_node,
+ struct msm_camera_csi_lane_params **csi_lane_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ struct msm_camera_csi_lane_params *clp;
+
+ clp = kzalloc(sizeof(*clp), GFP_KERNEL);
+ if (!clp)
+ return -ENOMEM;
+ *csi_lane_params = clp;
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-assign", &val);
+ CDBG("%s qcom,csi-lane-assign 0x%x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ clp->csi_lane_assign = val;
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-mask", &val);
+ CDBG("%s qcom,csi-lane-mask 0x%x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+ clp->csi_lane_mask = val;
+
+ return rc;
+ERROR:
+ kfree(clp);
+ return rc;
+}
+
+int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+ struct camera_vreg_t *cam_vreg, int num_vreg,
+ struct msm_camera_power_ctrl_t *power_info)
+{
+ int rc = 0, i, j;
+ int count = 0;
+ const char *seq_name = NULL;
+ uint32_t *array = NULL;
+ struct msm_sensor_power_setting *ps;
+
+ struct msm_sensor_power_setting *power_setting;
+ uint16_t *power_setting_size, size = 0;
+ bool need_reverse = 0;
+
+ if (!power_info)
+ return -EINVAL;
+
+ power_setting = power_info->power_setting;
+ power_setting_size = &power_info->power_setting_size;
+
+ count = of_property_count_strings(of_node, "qcom,cam-power-seq-type");
+ *power_setting_size = count;
+
+ CDBG("%s qcom,cam-power-seq-type count %d\n", __func__, count);
+
+ if (count <= 0)
+ return 0;
+
+ ps = kcalloc(count, sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ return -ENOMEM;
+ power_setting = ps;
+ power_info->power_setting = ps;
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-power-seq-type", i,
+ &seq_name);
+ CDBG("%s seq_name[%d] = %s\n", __func__, i,
+ seq_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ if (!strcmp(seq_name, "sensor_vreg")) {
+ ps[i].seq_type = SENSOR_VREG;
+ CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__,
+ i, ps[i].seq_type);
+ } else if (!strcmp(seq_name, "sensor_gpio")) {
+ ps[i].seq_type = SENSOR_GPIO;
+ CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__,
+ i, ps[i].seq_type);
+ } else if (!strcmp(seq_name, "sensor_clk")) {
+ ps[i].seq_type = SENSOR_CLK;
+ CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__,
+ i, ps[i].seq_type);
+ } else if (!strcmp(seq_name, "sensor_i2c_mux")) {
+ ps[i].seq_type = SENSOR_I2C_MUX;
+ CDBG("%s:%d seq_type[%d] %d\n", __func__, __LINE__,
+ i, ps[i].seq_type);
+ } else {
+ CDBG("%s: unrecognized seq-type\n", __func__);
+ rc = -EILSEQ;
+ goto ERROR1;
+ }
+ }
+
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-power-seq-val", i,
+ &seq_name);
+ CDBG("%s seq_name[%d] = %s\n", __func__, i,
+ seq_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ switch (ps[i].seq_type) {
+ case SENSOR_VREG:
+ for (j = 0; j < num_vreg; j++) {
+ if (!strcmp(seq_name, cam_vreg[j].reg_name))
+ break;
+ }
+ if (j < num_vreg)
+ ps[i].seq_val = j;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_GPIO:
+ if (!strcmp(seq_name, "sensor_gpio_reset"))
+ ps[i].seq_val = SENSOR_GPIO_RESET;
+ else if (!strcmp(seq_name, "sensor_gpio_standby"))
+ ps[i].seq_val = SENSOR_GPIO_STANDBY;
+ else if (!strcmp(seq_name, "sensor_gpio_vdig"))
+ ps[i].seq_val = SENSOR_GPIO_VDIG;
+ else if (!strcmp(seq_name, "sensor_gpio_vana"))
+ ps[i].seq_val = SENSOR_GPIO_VANA;
+ else if (!strcmp(seq_name, "sensor_gpio_vaf"))
+ ps[i].seq_val = SENSOR_GPIO_VAF;
+ else if (!strcmp(seq_name, "sensor_gpio_vio"))
+ ps[i].seq_val = SENSOR_GPIO_VIO;
+ else if (!strcmp(seq_name, "sensor_gpio_custom1"))
+ ps[i].seq_val = SENSOR_GPIO_CUSTOM1;
+ else if (!strcmp(seq_name, "sensor_gpio_custom2"))
+ ps[i].seq_val = SENSOR_GPIO_CUSTOM2;
+ else if (!strcmp(seq_name, "sensor_gpio_custom3"))
+ ps[i].seq_val = SENSOR_GPIO_CUSTOM3;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_CLK:
+ if (!strcmp(seq_name, "sensor_cam_mclk"))
+ ps[i].seq_val = SENSOR_CAM_MCLK;
+ else if (!strcmp(seq_name, "sensor_cam_clk"))
+ ps[i].seq_val = SENSOR_CAM_CLK;
+ else
+ rc = -EILSEQ;
+ break;
+ case SENSOR_I2C_MUX:
+ if (!strcmp(seq_name, "none"))
+ ps[i].seq_val = 0;
+ else
+ rc = -EILSEQ;
+ break;
+ default:
+ rc = -EILSEQ;
+ break;
+ }
+ if (rc < 0) {
+ CDBG("%s: unrecognized seq-val\n", __func__);
+ goto ERROR1;
+ }
+ }
+
+ array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!array) {
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-cfg-val",
+ array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ if (ps[i].seq_type == SENSOR_GPIO) {
+ if (array[i] == 0)
+ ps[i].config_val = GPIO_OUT_LOW;
+ else if (array[i] == 1)
+ ps[i].config_val = GPIO_OUT_HIGH;
+ } else {
+ ps[i].config_val = array[i];
+ }
+ CDBG("%s power_setting[%d].config_val = %ld\n", __func__, i,
+ ps[i].config_val);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-power-seq-delay",
+ array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ ps[i].delay = array[i];
+ CDBG("%s power_setting[%d].delay = %d\n", __func__,
+ i, ps[i].delay);
+ }
+ kfree(array);
+
+ size = *power_setting_size;
+
+ if (NULL != ps && 0 != size)
+ need_reverse = 1;
+
+ power_info->power_down_setting =
+ kzalloc(sizeof(*ps) * size, GFP_KERNEL);
+
+ if (!power_info->power_down_setting) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ memcpy(power_info->power_down_setting,
+ ps, sizeof(*ps) * size);
+
+ power_info->power_down_setting_size = size;
+
+ if (need_reverse) {
+ int c, end = size - 1;
+ struct msm_sensor_power_setting power_down_setting_t;
+
+ for (c = 0; c < size/2; c++) {
+ power_down_setting_t =
+ power_info->power_down_setting[c];
+ power_info->power_down_setting[c] =
+ power_info->power_down_setting[end];
+ power_info->power_down_setting[end] =
+ power_down_setting_t;
+ end--;
+ }
+ }
+ return rc;
+ERROR2:
+ kfree(array);
+ERROR1:
+ kfree(ps);
+ power_setting_size = NULL;
+ return rc;
+}
+
+int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int 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 = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!val_array)
+ return -ENOMEM;
+
+ gconf->cam_gpio_req_tbl = kcalloc(count, sizeof(struct gpio),
+ GFP_KERNEL);
+ if (!gconf->cam_gpio_req_tbl) {
+ rc = -ENOMEM;
+ goto 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;
+}
+
+int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int rc = 0, val = 0;
+
+ gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info),
+ GFP_KERNEL);
+ if (!gconf->gpio_num_info) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-ir-p", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-ir-p failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-ir-p invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_P] = 1;
+
+ CDBG("%s qcom,gpio-ir-p %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-ir-m", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-ir-m failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-ir-m invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M] =
+ gpio_array[val];
+
+ gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_M] = 1;
+
+ CDBG("%s qcom,gpio-ir-m %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vana failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vana invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_VANA] = 1;
+ CDBG("%s qcom,gpio-vana %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VANA]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vio", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vio failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vio invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_VIO] = 1;
+ CDBG("%s qcom,gpio-vio %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VIO]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vaf", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vaf failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vaf invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_VAF] = 1;
+ CDBG("%s qcom,gpio-vaf %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VAF]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-vdig", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-vdig failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-vdig invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_VDIG] = 1;
+ CDBG("%s qcom,gpio-vdig %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_VDIG]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-reset failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-reset invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_RESET] = 1;
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-standby", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-standby failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-standby invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_STANDBY] = 1;
+ CDBG("%s qcom,gpio-standby %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-af-pwdm", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-af-pwdm failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-af-pwdm invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_AF_PWDM] = 1;
+ CDBG("%s qcom,gpio-af-pwdm %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_AF_PWDM]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-flash-en", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-flash-en failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-flash-en invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_EN] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_FL_EN] = 1;
+ CDBG("%s qcom,gpio-flash-en %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_EN]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-flash-now", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-flash-now failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-flash-now invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_NOW] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_FL_NOW] = 1;
+ CDBG("%s qcom,gpio-flash-now %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_NOW]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-flash-reset", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%dread qcom,gpio-flash-reset failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-flash-reset invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_RESET] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_FL_RESET] = 1;
+ CDBG("%s qcom,gpio-flash-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_FL_RESET]);
+ } else
+ rc = 0;
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom1", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom1 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom1 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM1] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM1] = 1;
+ CDBG("%s qcom,gpio-custom1 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM1]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom2", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom2 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom2 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM2] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM2] = 1;
+ CDBG("%s qcom,gpio-custom2 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM2]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-custom3", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-custom3 failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-custom3 invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[SENSOR_GPIO_CUSTOM3] = 1;
+ CDBG("%s qcom,gpio-custom3 %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_CUSTOM3]);
+ } else {
+ rc = 0;
+ }
+
+ return rc;
+
+ERROR:
+ kfree(gconf->gpio_num_info);
+ gconf->gpio_num_info = NULL;
+ return rc;
+}
+
+int msm_camera_get_dt_vreg_data(struct device_node *of_node,
+ struct camera_vreg_t **cam_vreg, int *num_vreg)
+{
+ int rc = 0, i = 0;
+ int32_t count = 0;
+ uint32_t *vreg_array = NULL;
+ struct camera_vreg_t *vreg = NULL;
+ bool custom_vreg_name = false;
+
+ count = of_property_count_strings(of_node, "qcom,cam-vreg-name");
+ CDBG("%s qcom,cam-vreg-name count %d\n", __func__, count);
+
+ if (!count || (count == -EINVAL)) {
+ pr_err("%s:%d number of entries is 0 or not present in dts\n",
+ __func__, __LINE__);
+ *num_vreg = 0;
+ return 0;
+ }
+
+ vreg = kcalloc(count, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+ *cam_vreg = vreg;
+ *num_vreg = count;
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-vreg-name", i,
+ &vreg[i].reg_name);
+ CDBG("%s reg_name[%d] = %s\n", __func__, i,
+ vreg[i].reg_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ }
+
+ custom_vreg_name = of_property_read_bool(of_node,
+ "qcom,cam-custom-vreg-name");
+ if (custom_vreg_name) {
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-custom-vreg-name", i,
+ &vreg[i].custom_vreg_name);
+ CDBG("%s sub reg_name[%d] = %s\n", __func__, i,
+ vreg[i].custom_vreg_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ }
+ }
+
+ vreg_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL);
+ if (!vreg_array) {
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ for (i = 0; i < count; i++)
+ vreg[i].type = VREG_TYPE_DEFAULT;
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-type",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].type = vreg_array[i];
+ CDBG("%s cam_vreg[%d].type = %d\n",
+ __func__, i, vreg[i].type);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-type entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-min-voltage",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].min_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].min_voltage = %d\n",
+ __func__, i, vreg[i].min_voltage);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-min-voltage entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-max-voltage",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].max_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].max_voltage = %d\n",
+ __func__, i, vreg[i].max_voltage);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-max-voltage entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-op-mode",
+ vreg_array, count);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ } else {
+ for (i = 0; i < count; i++) {
+ vreg[i].op_mode = vreg_array[i];
+ CDBG("%s cam_vreg[%d].op_mode = %d\n",
+ __func__, i, vreg[i].op_mode);
+ }
+ }
+ } else {
+ CDBG("%s:%d no qcom,cam-vreg-op-mode entries in dts\n",
+ __func__, __LINE__);
+ rc = 0;
+ }
+
+ kfree(vreg_array);
+ return rc;
+ERROR2:
+ kfree(vreg_array);
+ERROR1:
+ kfree(vreg);
+ *num_vreg = 0;
+ return rc;
+}
+
+static int msm_camera_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 int msm_camera_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;
+}
+
+int msm_camera_pinctrl_init(
+ struct msm_pinctrl_info *sensor_pctrl, struct device *dev) {
+
+ sensor_pctrl->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) {
+ pr_err("%s:%d Getting pinctrl handle failed\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ sensor_pctrl->gpio_state_active =
+ pinctrl_lookup_state(sensor_pctrl->pinctrl,
+ CAM_SENSOR_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) {
+ pr_err("%s:%d Failed to get the active state pinctrl handle\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ sensor_pctrl->gpio_state_suspend
+ = pinctrl_lookup_state(sensor_pctrl->pinctrl,
+ CAM_SENSOR_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) {
+ pr_err("%s:%d Failed to get the suspend state pinctrl handle\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_cam_sensor_handle_reg_gpio(int seq_val,
+ struct msm_camera_gpio_conf *gconf, int val) {
+
+ int gpio_offset = -1;
+
+ if (!gconf) {
+ pr_err("ERR:%s: Input Parameters are not proper\n", __func__);
+ return -EINVAL;
+ }
+ CDBG("%s: %d Seq val: %d, config: %d", __func__, __LINE__,
+ seq_val, val);
+
+ switch (seq_val) {
+ case CAM_VDIG:
+ gpio_offset = SENSOR_GPIO_VDIG;
+ break;
+
+ case CAM_VIO:
+ gpio_offset = SENSOR_GPIO_VIO;
+ break;
+
+ case CAM_VANA:
+ gpio_offset = SENSOR_GPIO_VANA;
+ break;
+
+ case CAM_VAF:
+ gpio_offset = SENSOR_GPIO_VAF;
+ break;
+
+ case CAM_V_CUSTOM1:
+ gpio_offset = SENSOR_GPIO_CUSTOM1;
+ break;
+
+ case CAM_V_CUSTOM2:
+ gpio_offset = SENSOR_GPIO_CUSTOM2;
+ break;
+
+ default:
+ pr_err("%s:%d Invalid VREG seq val %d\n", __func__,
+ __LINE__, seq_val);
+ return -EINVAL;
+ }
+
+ CDBG("%s: %d GPIO offset: %d, seq_val: %d\n", __func__, __LINE__,
+ gpio_offset, seq_val);
+
+ if ((gconf->gpio_num_info->valid[gpio_offset] == 1)) {
+ gpio_set_value_cansleep(
+ gconf->gpio_num_info->gpio_num
+ [gpio_offset], val);
+ }
+ return 0;
+}
+
+int32_t msm_sensor_driver_get_gpio_data(
+ struct msm_camera_gpio_conf **gpio_conf,
+ struct device_node *of_node)
+{
+ int32_t rc = 0, i = 0;
+ uint16_t *gpio_array = NULL;
+ int16_t gpio_array_size = 0;
+ struct msm_camera_gpio_conf *gconf = NULL;
+
+ /* Validate input parameters */
+ if (!of_node) {
+ pr_err("failed: invalid param of_node %pK", of_node);
+ return -EINVAL;
+ }
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("gpio count %d\n", gpio_array_size);
+ if (gpio_array_size <= 0)
+ return 0;
+
+ gconf = kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!gconf)
+ return -ENOMEM;
+
+ *gpio_conf = gconf;
+
+ gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), GFP_KERNEL);
+ if (!gpio_array)
+ goto FREE_GPIO_CONF;
+
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ CDBG("gpio_array[%d] = %d", i, gpio_array[i]);
+ }
+ rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, gpio_array,
+ gpio_array_size);
+ if (rc < 0) {
+ pr_err("failed in msm_camera_get_dt_gpio_req_tbl\n");
+ goto FREE_GPIO_CONF;
+ }
+
+ rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, gpio_array,
+ gpio_array_size);
+ if (rc < 0) {
+ pr_err("failed in msm_camera_init_gpio_pin_tbl\n");
+ goto FREE_GPIO_REQ_TBL;
+ }
+ kfree(gpio_array);
+ return rc;
+
+FREE_GPIO_REQ_TBL:
+ kfree(gconf->cam_gpio_req_tbl);
+FREE_GPIO_CONF:
+ kfree(gconf);
+ kfree(gpio_array);
+ *gpio_conf = NULL;
+ return rc;
+}
+
+int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl,
+ enum msm_camera_device_type_t device_type,
+ struct msm_camera_i2c_client *sensor_i2c_client)
+{
+ int rc = 0, index = 0, no_gpio = 0, ret = 0;
+ struct msm_sensor_power_setting *power_setting = NULL;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ if (!ctrl || !sensor_i2c_client) {
+ pr_err("failed ctrl %pK sensor_i2c_client %pK\n", ctrl,
+ sensor_i2c_client);
+ return -EINVAL;
+ }
+ if (ctrl->gpio_conf->cam_gpiomux_conf_tbl != NULL)
+ pr_err("%s:%d mux install\n", __func__, __LINE__);
+
+ ret = msm_camera_pinctrl_init(&(ctrl->pinctrl_info), ctrl->dev);
+ if (ret < 0) {
+ pr_err("%s:%d Initialization of pinctrl failed\n",
+ __func__, __LINE__);
+ ctrl->cam_pinctrl_status = 0;
+ } else {
+ ctrl->cam_pinctrl_status = 1;
+ }
+ rc = msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0)
+ no_gpio = rc;
+ if (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_active);
+ if (ret)
+ pr_err("%s:%d cannot set pin to active state",
+ __func__, __LINE__);
+ }
+ for (index = 0; index < ctrl->power_setting_size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &ctrl->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ if (power_setting->seq_val >= ctrl->clk_info_size) {
+ pr_err_ratelimited("%s clk index %d >= max %zu\n",
+ __func__, power_setting->seq_val,
+ ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ if (power_setting->config_val)
+ ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+ rc = msm_camera_clk_enable(ctrl->dev,
+ ctrl->clk_info, ctrl->clk_ptr,
+ ctrl->clk_info_size, true);
+ if (rc < 0) {
+ pr_err_ratelimited("%s: clk enable failed\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_GPIO:
+ if (no_gpio) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return no_gpio;
+ }
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !ctrl->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;
+ }
+ if (!ctrl->gpio_conf->gpio_num_info->valid
+ [power_setting->seq_val])
+ continue;
+ CDBG("%s:%d gpio set val %d\n", __func__, __LINE__,
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+ gpio_set_value_cansleep(
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val],
+ (int) 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;
+ }
+ if (power_setting->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [power_setting->seq_val],
+ (struct regulator **)
+ &power_setting->data[0],
+ 1);
+ else
+ pr_err("%s: %d usr_idx:%d dts_idx:%d\n",
+ __func__, __LINE__,
+ power_setting->seq_val, ctrl->num_vreg);
+
+ rc = msm_cam_sensor_handle_reg_gpio(
+ power_setting->seq_val,
+ ctrl->gpio_conf, 1);
+ if (rc < 0) {
+ pr_err("ERR:%s Error in handling VREG GPIO\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_I2C_MUX:
+ if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux)
+ msm_camera_enable_i2c_mux(ctrl->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 (device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = sensor_i2c_client->i2c_func_tbl->i2c_util(
+ sensor_i2c_client, MSM_CCI_INIT);
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ goto power_up_failed;
+ }
+ }
+ CDBG("%s exit\n", __func__);
+ return 0;
+power_up_failed:
+ pr_err_ratelimited("%s:%d failed\n", __func__, __LINE__);
+ for (index--; index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &ctrl->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_GPIO:
+ if (!ctrl->gpio_conf->gpio_num_info)
+ continue;
+ if (!ctrl->gpio_conf->gpio_num_info->valid
+ [power_setting->seq_val])
+ continue;
+ gpio_set_value_cansleep(
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [power_setting->seq_val],
+ (struct regulator **)
+ &power_setting->data[0],
+ 0);
+ else
+ pr_err("%s:%d:seq_val: %d > num_vreg: %d\n",
+ __func__, __LINE__,
+ power_setting->seq_val, ctrl->num_vreg);
+
+ msm_cam_sensor_handle_reg_gpio(power_setting->seq_val,
+ ctrl->gpio_conf, GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_I2C_MUX:
+ if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux)
+ msm_camera_disable_i2c_mux(ctrl->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 (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state\n",
+ __func__, __LINE__);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ }
+ ctrl->cam_pinctrl_status = 0;
+ msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 0);
+ return rc;
+}
+
+static struct msm_sensor_power_setting*
+msm_camera_get_power_settings(struct msm_camera_power_ctrl_t *ctrl,
+ enum msm_sensor_power_seq_type_t seq_type,
+ uint16_t seq_val)
+{
+ struct msm_sensor_power_setting *power_setting, *ps = NULL;
+ int idx;
+
+ for (idx = 0; idx < ctrl->power_setting_size; idx++) {
+ power_setting = &ctrl->power_setting[idx];
+ if (power_setting->seq_type == seq_type &&
+ power_setting->seq_val == seq_val) {
+ ps = power_setting;
+ return ps;
+ }
+
+ }
+ return ps;
+}
+
+int msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl,
+ enum msm_camera_device_type_t device_type,
+ struct msm_camera_i2c_client *sensor_i2c_client)
+{
+ int index = 0, ret = 0;
+ struct msm_sensor_power_setting *pd = NULL;
+ struct msm_sensor_power_setting *ps;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ if (!ctrl || !sensor_i2c_client) {
+ pr_err("failed ctrl %pK sensor_i2c_client %pK\n", ctrl,
+ sensor_i2c_client);
+ return -EINVAL;
+ }
+ if (device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ sensor_i2c_client->i2c_func_tbl->i2c_util(
+ sensor_i2c_client, MSM_CCI_RELEASE);
+
+ for (index = 0; index < ctrl->power_down_setting_size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ pd = &ctrl->power_down_setting[index];
+ ps = NULL;
+ CDBG("%s type %d\n", __func__, pd->seq_type);
+ switch (pd->seq_type) {
+ case SENSOR_CLK:
+ msm_camera_clk_enable(ctrl->dev,
+ ctrl->clk_info, ctrl->clk_ptr,
+ ctrl->clk_info_size, false);
+ break;
+ case SENSOR_GPIO:
+ if (pd->seq_val >= SENSOR_GPIO_MAX ||
+ !ctrl->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ pd->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ if (!ctrl->gpio_conf->gpio_num_info->valid
+ [pd->seq_val])
+ continue;
+ gpio_set_value_cansleep(
+ ctrl->gpio_conf->gpio_num_info->gpio_num
+ [pd->seq_val],
+ (int) pd->config_val);
+ break;
+ case SENSOR_VREG:
+ if (pd->seq_val == INVALID_VREG)
+ break;
+ if (pd->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ pd->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+
+ ps = msm_camera_get_power_settings(ctrl,
+ pd->seq_type,
+ pd->seq_val);
+ if (ps) {
+ if (pd->seq_val < ctrl->num_vreg)
+ msm_camera_config_single_vreg(ctrl->dev,
+ &ctrl->cam_vreg
+ [pd->seq_val],
+ (struct regulator **)
+ &ps->data[0],
+ 0);
+ else
+ pr_err("%s:%d:seq_val:%d > num_vreg: %d\n",
+ __func__, __LINE__, pd->seq_val,
+ ctrl->num_vreg);
+ } else
+ pr_err("%s error in power up/down seq data\n",
+ __func__);
+ ret = msm_cam_sensor_handle_reg_gpio(pd->seq_val,
+ ctrl->gpio_conf, GPIOF_OUT_INIT_LOW);
+ if (ret < 0)
+ pr_err("ERR:%s Error while disabling VREG GPIO\n",
+ __func__);
+ break;
+ case SENSOR_I2C_MUX:
+ if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux)
+ msm_camera_disable_i2c_mux(ctrl->i2c_conf);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ pd->seq_type);
+ break;
+ }
+ if (pd->delay > 20) {
+ msleep(pd->delay);
+ } else if (pd->delay) {
+ usleep_range(pd->delay * 1000,
+ (pd->delay * 1000) + 1000);
+ }
+ }
+ if (ctrl->cam_pinctrl_status) {
+ ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+ ctrl->pinctrl_info.gpio_state_suspend);
+ if (ret)
+ pr_err("%s:%d cannot set pin to suspend state",
+ __func__, __LINE__);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ }
+ ctrl->cam_pinctrl_status = 0;
+ msm_camera_request_gpio_table(
+ ctrl->gpio_conf->cam_gpio_req_tbl,
+ ctrl->gpio_conf->cam_gpio_req_tbl_size, 0);
+ CDBG("%s exit\n", __func__);
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h
new file mode 100644
index 0000000..545ebdc
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2013-2016, 2018, 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_DT_UTIL_H__
+#define MSM_CAMERA_DT_UTIL_H__
+
+#include <soc/qcom/camera2.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include "msm_camera_i2c.h"
+#include "cam_soc_api.h"
+
+
+#define INVALID_VREG 100
+
+int msm_sensor_get_sub_module_index(struct device_node *of_node,
+ struct msm_sensor_info_t **s_info);
+
+int msm_sensor_get_dt_actuator_data(struct device_node *of_node,
+ struct msm_actuator_info **act_info);
+
+int msm_sensor_get_dt_csi_data(struct device_node *of_node,
+ struct msm_camera_csi_lane_params **csi_lane_params);
+
+int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
+ struct camera_vreg_t *cam_vreg, int num_vreg,
+ struct msm_camera_power_ctrl_t *power_info);
+
+int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size);
+
+int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size);
+
+int msm_camera_get_dt_vreg_data(struct device_node *of_node,
+ struct camera_vreg_t **cam_vreg, int *num_vreg);
+
+int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl,
+ enum msm_camera_device_type_t device_type,
+ struct msm_camera_i2c_client *sensor_i2c_client);
+
+int msm_camera_power_down(struct msm_camera_power_ctrl_t *ctrl,
+ enum msm_camera_device_type_t device_type,
+ struct msm_camera_i2c_client *sensor_i2c_client);
+
+int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg,
+ int num_vreg, struct msm_sensor_power_setting *power_setting,
+ uint16_t power_setting_size);
+
+int msm_camera_pinctrl_init
+ (struct msm_pinctrl_info *sensor_pctrl, struct device *dev);
+
+int32_t msm_sensor_driver_get_gpio_data(
+ struct msm_camera_gpio_conf **gpio_conf,
+ struct device_node *of_node);
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
new file mode 100644
index 0000000..c8e8a42
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
@@ -0,0 +1,211 @@
+/* Copyright (c) 2011-2016, 2018 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>
+
+#define I2C_POLL_TIME_MS 5
+#define MAX_POLL_DELAY_MS 100
+
+#define I2C_COMPARE_MATCH 0
+#define I2C_COMPARE_MISMATCH 1
+
+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;
+ struct msm_camera_spi_client *spi_client;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+};
+
+struct msm_camera_i2c_fn_t {
+ int (*i2c_read)(struct msm_camera_i2c_client *, uint32_t, uint16_t *,
+ enum msm_camera_i2c_data_type);
+ int32_t (*i2c_read_seq)(struct msm_camera_i2c_client *, uint32_t,
+ uint8_t *, uint32_t);
+ int (*i2c_write)(struct msm_camera_i2c_client *, uint32_t, uint16_t,
+ enum msm_camera_i2c_data_type);
+ int (*i2c_write_seq)(struct msm_camera_i2c_client *, uint32_t,
+ uint8_t *, uint32_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_setting *);
+ int32_t (*i2c_util)(struct msm_camera_i2c_client *, uint16_t);
+ int32_t (*i2c_write_conf_tbl)(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+ int32_t (*i2c_poll)(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type, uint32_t delay_ms);
+ int32_t (*i2c_read_burst)(struct msm_camera_i2c_client *client,
+ uint32_t read_byte, uint8_t *buffer, uint32_t addr,
+ enum msm_camera_i2c_data_type data_type);
+ int32_t (*i2c_write_burst)(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
+ uint32_t buf_len, uint32_t addr,
+ enum msm_camera_i2c_data_type data_type);
+ int32_t (*i2c_write_table_async)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_setting *);
+ int32_t (*i2c_write_table_sync)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_setting *);
+ int32_t (*i2c_write_table_sync_block)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_setting *);
+};
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_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,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_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,
+ uint32_t addr, uint8_t *data, uint32_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_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_table_sync_block(
+ 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_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_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_cci_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type, uint32_t delay_ms);
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_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,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_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,
+ uint32_t addr, uint8_t *data, uint32_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_setting *write_setting);
+
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type, uint32_t delay_ms);
+
+int32_t msm_camera_tz_i2c_register_sensor(void *s_ctrl_p);
+
+int32_t msm_camera_tz_i2c_power_up(struct msm_camera_i2c_client *client);
+
+int32_t msm_camera_tz_i2c_power_down(struct msm_camera_i2c_client *client);
+
+int32_t msm_camera_tz_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_tz_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_tz_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_tz_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_tz_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_sync_block(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_sensor_tz_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd);
+
+int32_t msm_camera_tz_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.c
new file mode 100644
index 0000000..af794bb
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.c
@@ -0,0 +1,186 @@
+/* Copyright (c) 2011-2014, 2016, 2018, 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 "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) {
+ 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);
+ }
+ 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 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_base = msm_camera_get_reg_base(pdev,
+ "i2c_mux_ctl", true);
+ if (!mux_device->ctl_base) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto ctl_base_failed;
+ }
+ mux_device->rw_base = msm_camera_get_reg_base(pdev, "i2c_mux_rw", true);
+ if (!mux_device->rw_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto rw_base_failed;
+ }
+ mux_device->pdev = pdev;
+ return 0;
+
+rw_base_failed:
+ msm_camera_put_reg_base(pdev, mux_device->ctl_base,
+ "i2c_mux_ctl", true);
+ctl_base_failed:
+ mutex_destroy(&mux_device->mutex);
+ kfree(mux_device);
+ return 0;
+}
+
+static int i2c_mux_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sub_dev = platform_get_drvdata(pdev);
+ struct i2c_mux_device *mux_device;
+
+ if (!sub_dev) {
+ pr_err("%s: sub device is NULL\n", __func__);
+ return 0;
+ }
+
+ mux_device = (struct mux_device *)v4l2_get_subdevdata(sub_dev);
+ if (!mux_device) {
+ pr_err("%s: sub device is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_camera_put_reg_base(pdev, mux_device->rw_base, "i2c_mux_ctl", true);
+ msm_camera_put_reg_base(pdev, mux_device->ctl_base, "i2c_mux_rw", true);
+}
+
+static struct platform_driver i2c_mux_driver = {
+ .probe = i2c_mux_probe,
+ .remove = i2c_mux_remove,
+ .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/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.h
new file mode 100644
index 0000000..6c1a4212a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c_mux.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2011-2014, 2016, 2018, 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;
+ void __iomem *ctl_base;
+ void __iomem *rw_base;
+ struct mutex mutex;
+ unsigned int 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/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
new file mode 100644
index 0000000..e16629c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
@@ -0,0 +1,616 @@
+/* Copyright (c) 2011, 2013-2016, 2018, 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 <soc/qcom/camera2.h>
+#include "msm_camera_i2c.h"
+
+#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 failed 0x%x\n", saddr);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf = NULL;
+
+ 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 > UINT_MAX - data_type) {
+ S_I2C_DBG("%s: integer overflow prevented\n", __func__);
+ return rc;
+ }
+
+ buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type,
+ GFP_KERNEL);
+ if (!buf) {
+ S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ 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__);
+ kfree(buf);
+ buf = NULL;
+ 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);
+ kfree(buf);
+ buf = NULL;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf = NULL;
+ 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 (num_byte > I2C_REG_DATA_MAX) {
+ S_I2C_DBG("%s: Error num_byte:0x%x exceeds max:0x%x\n",
+ __func__, num_byte, I2C_REG_DATA_MAX);
+ return rc;
+ }
+ if (client->addr_type > UINT_MAX - num_byte) {
+ S_I2C_DBG("%s: integer overflow prevented\n", __func__);
+ return rc;
+ }
+
+ buf = kzalloc(client->addr_type+num_byte, GFP_KERNEL);
+ if (!buf) {
+ S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ 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__);
+ kfree(buf);
+ buf = NULL;
+ 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]);
+ }
+ kfree(buf);
+ buf = NULL;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf = NULL;
+ 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;
+
+ buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type,
+ GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ 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,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char *buf;
+ 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;
+
+ buf = kzalloc(client->addr_type+num_byte, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ 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;
+ }
+ if (num_byte > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: num_byte=%d clamped to max supported %d\n",
+ __func__, num_byte, I2C_SEQ_REG_DATA_MAX);
+ num_byte = I2C_SEQ_REG_DATA_MAX;
+ }
+ 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 0x%x data 0x%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 Invalid 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;
+
+ if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: number of bytes %u exceeding the max supported %d\n",
+ __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX);
+ return rc;
+ }
+
+ 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_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->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;
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_qup_i2c_write(client, reg_setting->reg_addr,
+ reg_setting->reg_data, write_setting->data_type);
+ if (rc < 0)
+ break;
+ if (reg_setting->delay)
+ usleep_range(reg_setting->delay,
+ reg_setting->delay + 1000);
+ reg_setting++;
+ }
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_compare(
+ struct msm_camera_i2c_client *client, uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data = 0;
+ int data_len = 0;
+
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ data_len = data_type;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ data_len = MSM_CAMERA_I2C_BYTE_DATA;
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ data_len = MSM_CAMERA_I2C_WORD_DATA;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_len);
+ if (rc < 0)
+ return rc;
+
+ rc = I2C_COMPARE_MISMATCH;
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ if (data == reg_data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ if ((reg_data & data) == data)
+ rc = I2C_COMPARE_MATCH;
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ if (!(reg_data & data))
+ rc = I2C_COMPARE_MATCH;
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n", __func__, data_type);
+ break;
+ }
+
+ S_I2C_DBG("%s: Register and data match result %d\n", __func__,
+ rc);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type, uint32_t delay_ms)
+{
+ int32_t rc = 0;
+ int i;
+
+ S_I2C_DBG("%s: addr: 0x%x data: 0x%x dt: %d\n",
+ __func__, addr, data, data_type);
+
+ if (delay_ms > MAX_POLL_DELAY_MS) {
+ pr_err("%s:%d invalid delay = %d max_delay = %d\n",
+ __func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < delay_ms; i++) {
+ rc = msm_camera_qup_i2c_compare(client,
+ addr, data, data_type);
+ if (rc < 0) {
+ pr_err("%s:%d qup_i2c_compare failed rc = %d", __func__,
+ __LINE__, rc);
+ break;
+ }
+ if (rc == I2C_COMPARE_MISMATCH)
+ break;
+ usleep_range(1000, 1010);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_mask(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t mask,
+ enum msm_camera_i2c_data_type data_type, uint16_t set_mask)
+{
+ int32_t rc;
+ uint16_t reg_data;
+
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data, data_type);
+ if (rc < 0) {
+ S_I2C_DBG("%s read fail\n", __func__);
+ return rc;
+ }
+ S_I2C_DBG("%s addr: 0x%x data: 0x%x setmask: 0x%x\n",
+ __func__, addr, reg_data, mask);
+
+ if (set_mask)
+ reg_data |= mask;
+ else
+ reg_data &= ~mask;
+ S_I2C_DBG("%s write: 0x%x\n", __func__, reg_data);
+
+ rc = msm_camera_qup_i2c_write(client, addr, reg_data, data_type);
+ if (rc < 0)
+ S_I2C_DBG("%s write fail\n", __func__);
+
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_set_write_mask_data(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data, int16_t mask,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc;
+ uint16_t reg_data;
+
+ CDBG("%s\n", __func__);
+ if (mask == -1)
+ return 0;
+ if (mask == 0) {
+ rc = msm_camera_qup_i2c_write(client, addr, data, data_type);
+ } else {
+ rc = msm_camera_qup_i2c_read(client, addr, ®_data,
+ data_type);
+ if (rc < 0) {
+ CDBG("%s read fail\n", __func__);
+ return rc;
+ }
+ reg_data &= ~mask;
+ reg_data |= (data & mask);
+ rc = msm_camera_qup_i2c_write(client, addr, reg_data,
+ data_type);
+ if (rc < 0)
+ CDBG("%s write fail\n", __func__);
+ }
+ return rc;
+}
+
+
+int32_t msm_camera_qup_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ pr_err("%s, E. ", __func__);
+ for (i = 0; i < size; i++) {
+ enum msm_camera_i2c_data_type dt;
+
+ if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+ rc = msm_camera_qup_i2c_poll(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->dt, I2C_POLL_TIME_MS);
+ } else {
+ if (reg_conf_tbl->dt == 0)
+ dt = data_type;
+ else
+ dt = reg_conf_tbl->dt;
+ switch (dt) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ rc = msm_camera_qup_i2c_write(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data, dt);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ rc = msm_camera_qup_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA:
+ rc = msm_camera_qup_i2c_set_write_mask_data(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ reg_conf_tbl->mask,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n",
+ __func__, dt);
+ break;
+ }
+ }
+ if (rc < 0)
+ break;
+ reg_conf_tbl++;
+ }
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c
new file mode 100644
index 0000000..713ca06
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.c
@@ -0,0 +1,854 @@
+/* Copyright (c) 2013-2016, 2018, 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 <soc/qcom/camera2.h>
+#include "msm_camera_spi.h"
+
+#undef SPIDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define SPIDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define SPIDBG(fmt, args...) do { } while (0)
+#define S_I2C_DBG(fmt, args...) do { } while (0)
+#endif
+
+static int msm_camera_spi_txfr(struct spi_device *spi, char *txbuf,
+ char *rxbuf, int num_byte)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = txbuf;
+ t.rx_buf = rxbuf;
+ t.len = num_byte;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ return spi_sync(spi, &m);
+}
+
+static int msm_camera_spi_txfr_read(struct spi_device *spi, char *txbuf,
+ char *rxbuf, int txlen, int rxlen)
+{
+ struct spi_transfer tx;
+ struct spi_transfer rx;
+ struct spi_message m;
+
+ memset(&tx, 0, sizeof(tx));
+ memset(&rx, 0, sizeof(rx));
+ tx.tx_buf = txbuf;
+ rx.rx_buf = rxbuf;
+ tx.len = txlen;
+ rx.len = rxlen;
+ spi_message_init(&m);
+ spi_message_add_tail(&tx, &m);
+ spi_message_add_tail(&rx, &m);
+ return spi_sync(spi, &m);
+}
+
+
+/*
+ * msm_camera_set_addr() - helper function to set transfer address
+ * @addr: device address
+ * @addr_len: the addr field length of an instruction
+ * @type: type (i.e. byte-length) of @addr
+ * @str: shifted address output, must be zeroed when passed in
+ *
+ * This helper function sets @str based on the addr field length of an
+ * instruction and the data length.
+ */
+static void msm_camera_set_addr(uint32_t addr, uint8_t addr_len,
+ enum msm_camera_i2c_reg_addr_type type,
+ char *str)
+{
+ int i, len;
+
+ if (!addr_len)
+ return;
+
+ if (addr_len < type)
+ SPIDBG("%s: omitting higher bits in address\n", __func__);
+
+ /* only support transfer MSB first for now */
+ len = addr_len - type;
+ for (i = len; i < addr_len; i++) {
+ if (i >= 0)
+ str[i] = (addr >> (BITS_PER_BYTE * (addr_len - i - 1)))
+ & 0xFF;
+ }
+
+}
+
+/*
+ * msm_camera_spi_tx_helper() - wrapper for SPI transaction
+ * @client: io client
+ * @inst: inst of this transaction
+ * @addr: device addr following the inst
+ * @data: output byte array (could be NULL)
+ * @num_byte: size of @data
+ * @tx, rx: optional transfer buffer. It must be at least header
+ * + @num_byte long.
+ *
+ * This is the core function for SPI transaction, except for writes. It first
+ * checks address type, then allocates required memory for tx/rx buffers.
+ * It sends out <opcode><addr>, and optionally receives @num_byte of response,
+ * if @data is not NULL. This function does not check for wait conditions,
+ * and will return immediately once bus transaction finishes.
+ *
+ * This function will allocate buffers of header + @num_byte long. For
+ * large transfers, the allocation could fail. External buffer @tx, @rx
+ * should be passed in to bypass allocation. The size of buffer should be
+ * at least header + num_byte long. Since buffer is managed externally,
+ * @data will be ignored, and read results will be in @rx.
+ * @tx, @rx also can be used for repeated transfers to improve performance.
+ */
+static int32_t msm_camera_spi_tx_helper(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+ uint32_t num_byte, char *tx, char *rx)
+{
+ int32_t rc = -EINVAL;
+ struct spi_device *spi = client->spi_client->spi_master;
+ char *ctx = NULL, *crx = NULL;
+ uint32_t len, hlen;
+ uint8_t retries = client->spi_client->retries;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
+ return rc;
+
+ hlen = msm_camera_spi_get_hlen(inst);
+ len = hlen + num_byte;
+
+ if (tx)
+ ctx = tx;
+ else
+ ctx = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (num_byte) {
+ if (rx)
+ crx = rx;
+ else
+ crx = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!crx) {
+ if (!tx)
+ kfree(ctx);
+ return -ENOMEM;
+ }
+ } else {
+ crx = NULL;
+ }
+
+ ctx[0] = inst->opcode;
+ msm_camera_set_addr(addr, inst->addr_len, client->addr_type, ctx + 1);
+ while ((rc = msm_camera_spi_txfr(spi, ctx, crx, len)) && retries) {
+ retries--;
+ msleep(client->spi_client->retry_delay);
+ }
+ if (rc < 0) {
+ SPIDBG("%s: failed %d\n", __func__, rc);
+ goto out;
+ }
+ if (data && num_byte && !rx)
+ memcpy(data, crx + hlen, num_byte);
+
+out:
+ if (!tx)
+ kfree(ctx);
+ if (!rx)
+ kfree(crx);
+ return rc;
+}
+
+static int32_t msm_camera_spi_tx_read(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+ uint32_t num_byte, char *tx, char *rx)
+{
+ int32_t rc = -EINVAL;
+ struct spi_device *spi = client->spi_client->spi_master;
+ char *ctx = NULL, *crx = NULL;
+ uint32_t hlen;
+ uint8_t retries = client->spi_client->retries;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
+ return rc;
+
+ hlen = msm_camera_spi_get_hlen(inst);
+ if (tx)
+ ctx = tx;
+ else
+ ctx = kzalloc(hlen, GFP_KERNEL | GFP_DMA);
+ if (!ctx)
+ return -ENOMEM;
+ if (num_byte) {
+ if (rx)
+ crx = rx;
+ else
+ crx = kzalloc(num_byte, GFP_KERNEL | GFP_DMA);
+ if (!crx) {
+ if (!tx)
+ kfree(ctx);
+ return -ENOMEM;
+ }
+ } else {
+ crx = NULL;
+ }
+
+ ctx[0] = inst->opcode;
+ if (client->addr_type == MSM_CAMERA_I2C_3B_ADDR) {
+ msm_camera_set_addr(addr, inst->addr_len, client->addr_type,
+ ctx + 1);
+ } else {
+ ctx[1] = (addr >> BITS_PER_BYTE) & 0xFF;
+ ctx[2] = (addr & 0xFF);
+ ctx[3] = 0;
+ }
+ SPIDBG("%s: tx(%u): %02x %02x %02x %02x\n", __func__,
+ hlen, ctx[0], ctx[1], ctx[2], ctx[3]);
+ while ((rc = msm_camera_spi_txfr_read(spi, ctx, crx, hlen, num_byte))
+ && retries) {
+ retries--;
+ msleep(client->spi_client->retry_delay);
+ }
+ if (rc < 0) {
+ pr_err("%s: failed %d\n", __func__, rc);
+ goto out;
+ }
+ if (data && num_byte && !rx)
+ memcpy(data, crx, num_byte);
+out:
+ if (!tx)
+ kfree(ctx);
+ if (!rx)
+ kfree(crx);
+ return rc;
+}
+
+int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EINVAL;
+ uint8_t temp[2];
+
+ if ((data_type != MSM_CAMERA_I2C_BYTE_DATA)
+ && (data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ rc = msm_camera_spi_tx_read(client,
+ &client->spi_client->cmd_tbl.read, addr, &temp[0],
+ data_type, NULL, NULL);
+ if (rc < 0) {
+ pr_err("%s: failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+ *data = temp[0];
+ else
+ *data = (temp[0] << BITS_PER_BYTE) | temp[1];
+
+ SPIDBG("%s: addr 0x%x, data %u\n", __func__, addr, *data);
+ return rc;
+}
+
+int32_t msm_camera_spi_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.read_seq, addr, data, num_byte,
+ NULL, NULL);
+}
+
+/*
+ * msm_camera_spi_read_seq_l()- function for large SPI reads
+ * @client: io client
+ * @addr: device address to read
+ * @num_byte: read length
+ * @tx,rx: pre-allocated SPI buffer. Its size must be at least
+ * header + num_byte
+ *
+ * This function is used for large transactions. Instead of allocating SPI
+ * buffer each time, caller is responsible for pre-allocating memory buffers.
+ * Memory buffer must be at least header + num_byte. Header length can be
+ * obtained by msm_camera_spi_get_hlen().
+ */
+int32_t msm_camera_spi_read_seq_l(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t num_byte, char *tx, char *rx)
+{
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.read_seq, addr, NULL, num_byte,
+ tx, rx);
+}
+
+int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ return msm_camera_spi_tx_helper(client,
+ &client->spi_client->cmd_tbl.query_id, addr, data, num_byte,
+ NULL, NULL);
+}
+
+static int32_t msm_camera_spi_read_status_reg(
+ struct msm_camera_i2c_client *client, uint8_t *status)
+{
+ struct msm_camera_spi_inst *rs =
+ &client->spi_client->cmd_tbl.read_status;
+ if (rs->addr_len != 0) {
+ pr_err("%s: not implemented yet\n", __func__);
+ return -EINVAL;
+ }
+ return msm_camera_spi_tx_helper(client, rs, 0, status, 1, NULL, NULL);
+}
+
+static int32_t msm_camera_spi_device_busy(struct msm_camera_i2c_client *client,
+ uint8_t *busy)
+{
+ int rc;
+ uint8_t st = 0;
+
+ rc = msm_camera_spi_read_status_reg(client, &st);
+ if (rc < 0) {
+ pr_err("%s: failed to read status reg\n", __func__);
+ return rc;
+ }
+ *busy = st & client->spi_client->busy_mask;
+ return 0;
+}
+
+static int32_t msm_camera_spi_wait(struct msm_camera_i2c_client *client,
+ struct msm_camera_spi_inst *inst)
+{
+ uint8_t busy;
+ int i, rc;
+
+ SPIDBG("%s: op 0x%x wait start\n", __func__, inst->opcode);
+ for (i = 0; i < inst->delay_count; i++) {
+ rc = msm_camera_spi_device_busy(client, &busy);
+ if (rc < 0)
+ return rc;
+ if (!busy)
+ break;
+
+ msleep(inst->delay_intv);
+ SPIDBG("%s: op 0x%x wait\n", __func__, inst->opcode);
+ }
+ if (i > inst->delay_count) {
+ pr_err("%s: op %x timed out\n", __func__, inst->opcode);
+ return -ETIMEDOUT;
+ }
+ SPIDBG("%s: op %x finished\n", __func__, inst->opcode);
+ return 0;
+}
+
+static int32_t msm_camera_spi_write_enable(
+ struct msm_camera_i2c_client *client)
+{
+ struct msm_camera_spi_inst *we =
+ &client->spi_client->cmd_tbl.write_enable;
+ int rc;
+
+ if (we->opcode == 0)
+ return 0;
+ if (we->addr_len != 0) {
+ pr_err("%s: not implemented yet\n", __func__);
+ return -EINVAL;
+ }
+ rc = msm_camera_spi_tx_helper(client, we, 0, NULL, 0, NULL, NULL);
+ if (rc < 0)
+ pr_err("%s: write enable failed\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_spi_erase(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t size)
+{
+ struct msm_camera_spi_inst *se = &client->spi_client->cmd_tbl.erase;
+ int rc = 0;
+ uint32_t cur;
+ uint32_t end = addr + size;
+ uint32_t erase_size = client->spi_client->erase_size;
+
+ end = addr + size;
+ for (cur = rounddown(addr, erase_size); cur < end; cur += erase_size) {
+ SPIDBG("%s: erasing 0x%x\n", __func__, cur);
+ rc = msm_camera_spi_write_enable(client);
+ if (rc < 0)
+ return rc;
+ rc = msm_camera_spi_tx_helper(client, se, cur, NULL, 0,
+ NULL, NULL);
+ if (rc < 0) {
+ pr_err("%s: erase failed\n", __func__);
+ return rc;
+ }
+ rc = msm_camera_spi_wait(client, se);
+ if (rc < 0) {
+ pr_err("%s: erase timedout\n", __func__);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+/*
+ * msm_camera_spi_page_program() - core function to perform write
+ * @client: need for obtaining SPI device
+ * @addr: address to program on device
+ * @data: data to write
+ * @len: size of data
+ * @tx: tx buffer, size >= header + len
+ *
+ * This function performs SPI write, and has no boundary check. Writing range
+ * should not cross page boundary, or data will be corrupted. Transaction is
+ * guaranteed to be finished when it returns. This function should never be
+ * used outside msm_camera_spi_write_seq().
+ */
+static int32_t msm_camera_spi_page_program(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint16_t len, uint8_t *tx)
+{
+ int rc;
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.page_program;
+ struct spi_device *spi = client->spi_client->spi_master;
+ uint8_t retries = client->spi_client->retries;
+ uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
+
+ SPIDBG("%s: addr 0x%x, size 0x%x\n", __func__, addr, len);
+ rc = msm_camera_spi_write_enable(client);
+ if (rc < 0)
+ return rc;
+ memset(tx, 0, header_len);
+ tx[0] = pg->opcode;
+ msm_camera_set_addr(addr, pg->addr_len, client->addr_type, tx + 1);
+ memcpy(tx + header_len, data, len);
+ SPIDBG("%s: tx(%u): %02x %02x %02x %02x\n", __func__,
+ len, tx[0], tx[1], tx[2], tx[3]);
+ while ((rc = spi_write(spi, tx, len + header_len)) && retries) {
+ rc = msm_camera_spi_wait(client, pg);
+ msleep(client->spi_client->retry_delay);
+ retries--;
+ }
+ if (rc < 0) {
+ pr_err("%s: failed %d\n", __func__, rc);
+ return rc;
+ }
+ rc = msm_camera_spi_wait(client, pg);
+ return rc;
+}
+
+int32_t msm_camera_spi_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.page_program;
+ const uint32_t page_size = client->spi_client->page_size;
+ uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
+ uint16_t len;
+ uint32_t cur_len, end;
+ char *tx, *pdata = data;
+ int rc = -EINVAL;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
+ return rc;
+ /* single page write */
+ if ((addr % page_size) + num_byte <= page_size) {
+ len = header_len + num_byte;
+ tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!tx)
+ goto NOMEM;
+ rc = msm_camera_spi_page_program(client, addr, data,
+ num_byte, tx);
+ if (rc < 0)
+ goto ERROR;
+ goto OUT;
+ }
+ /* multi page write */
+ len = header_len + page_size;
+ tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!tx)
+ goto NOMEM;
+ while (num_byte) {
+ end = min(page_size, (addr % page_size) + num_byte);
+ cur_len = end - (addr % page_size);
+ rc = msm_camera_spi_page_program(client, addr, pdata,
+ cur_len, tx);
+ if (rc < 0)
+ goto ERROR;
+ addr += cur_len;
+ pdata += cur_len;
+ num_byte -= cur_len;
+ }
+ goto OUT;
+NOMEM:
+ pr_err("%s: memory allocation failed\n", __func__);
+ return -ENOMEM;
+ERROR:
+ pr_err("%s: error write\n", __func__);
+OUT:
+ kfree(tx);
+ return rc;
+}
+
+int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data, enum msm_camera_i2c_data_type data_type)
+{
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.page_program;
+ uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
+ uint16_t len = 0;
+ char *buf = NULL;
+ char *tx;
+ int rc = -EINVAL;
+
+ if (((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ && (client->addr_type != MSM_CAMERA_I2C_3B_ADDR))
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+ buf = kzalloc(data_type, GFP_KERNEL);
+ if (!buf)
+ goto NOMEM;
+ S_I2C_DBG("Data: 0x%x\n", data);
+ len = header_len + (uint8_t)data_type;
+ tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!tx)
+ goto NOMEM;
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ buf[0] = data;
+ SPIDBG("Byte %d: 0x%x\n", len, buf[0]);
+ } else if (data_type == MSM_CAMERA_I2C_WORD_DATA) {
+ buf[0] = (data >> BITS_PER_BYTE) & 0x00FF;
+ buf[1] = (data & 0x00FF);
+ }
+ rc = msm_camera_spi_page_program(client, addr, buf,
+ (uint16_t)data_type, tx);
+ if (rc < 0)
+ goto ERROR;
+ goto OUT;
+NOMEM:
+ pr_err("%s: memory allocation failed\n", __func__);
+ return -ENOMEM;
+ERROR:
+ pr_err("%s: error write\n", __func__);
+OUT:
+ kfree(tx);
+ return rc;
+}
+int32_t msm_camera_spi_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++) {
+ SPIDBG("%s addr %x data %x\n", __func__,
+ reg_setting->reg_addr, reg_setting->reg_data);
+ rc = msm_camera_spi_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;
+}
+static uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting,
+ uint32_t reg_size, uint32_t index, uint16_t burst_addr)
+{
+ uint32_t i;
+ uint32_t cnt = 0;
+
+ for (i = index; i < reg_size; i++) {
+ if (reg_setting[i].reg_addr == burst_addr)
+ cnt++;
+ else
+ break;
+ }
+ return cnt;
+}
+
+#ifdef SPI_DYNAMIC_ALLOC
+static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
+ struct msm_camera_burst_info *info,
+ enum msm_camera_i2c_data_type data_type)
+{
+ uint32_t i, j, k;
+ int32_t rc = 0;
+ uint32_t chunk_num, residue;
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.page_program;
+ uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
+ uint8_t *ctx, *data;
+ uint32_t len;
+
+ if (info->burst_len == 0 || info->chunk_size == 0) {
+ pr_err("%s:%d Invalid argument\n", __func__, __LINE__);
+ return rc;
+ }
+ if (info->burst_start + info->burst_len > reg_size) {
+ pr_err("%s too big burst size, index=%d, size=%d\n", __func__,
+ info->burst_start, info->burst_len);
+ return rc;
+ }
+ chunk_num = info->burst_len / info->chunk_size;
+ residue = info->burst_len % info->chunk_size;
+ SPIDBG("%s header_len=%d, chunk nb=%d, residue=%d\n",
+ __func__, header_len, chunk_num, residue);
+ len = info->chunk_size * data_type + header_len;
+ SPIDBG("buffer allocation size = %d\n", len);
+ ctx = kmalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!ctx) {
+ pr_err("%s %d memory alloc fail!\n", __func__, __LINE__);
+ return rc;
+ }
+ ctx[0] = pg->opcode;
+ ctx[1] = (info->burst_addr >> 8) & 0xff;
+ ctx[2] = info->burst_addr & 0xff;
+ k = info->burst_start;
+ for (i = 0; i < chunk_num; i++) {
+ data = ctx + header_len;
+ for (j = 0; j < info->chunk_size; j++) {
+ *data++ = (reg_setting[k+j].reg_data >> 8) & 0xff;
+ *data++ = reg_setting[k+j].reg_data & 0xff;
+ }
+ rc = msm_camera_spi_txfr(client->spi_client->spi_master,
+ (void *) ctx, NULL,
+ info->chunk_size * data_type + header_len);
+ if (rc < 0) {
+ pr_err("%s %d spi sending error = %d!!\n",
+ __func__, __LINE__, rc);
+ goto fail;
+ }
+ k += info->chunk_size;
+ }
+ SPIDBG("%s burst chunk start=%d, residue=%d\n",
+ __func__, k, residue);
+ if (residue) {
+ data = ctx + header_len;
+ for (j = 0; j < residue; j++) {
+ *data++ = (reg_setting[k+j].reg_data >> 8) & 0xff;
+ *data++ = reg_setting[k+j].reg_data & 0xff;
+ }
+ rc = msm_camera_spi_txfr(client->spi_client->spi_master,
+ (void *)ctx, NULL,
+ residue*data_type+header_len);
+ if (rc < 0) {
+ pr_err("%s %d spi sending error = %d!!\n", __func__,
+ __LINE__, rc);
+ goto fail;
+ }
+ }
+fail:
+ kfree(ctx);
+ return rc;
+}
+#else /* SPI_DYNAMIC_ALLOC */
+int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
+ struct msm_camera_burst_info *info,
+ enum msm_camera_i2c_data_type data_type)
+{
+ uint32_t i, j, k;
+ int32_t rc = 0;
+ uint32_t chunk_num, residue;
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.page_program;
+ uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
+ struct msm_spi_write_burst_packet tx_buf;
+
+ if (info->burst_len == 0 || info->burst_len == 0
+ || info->chunk_size == 0) {
+ pr_err("%s %d Invalid argument\n", __func__, __LINE__);
+ return rc;
+ }
+ if (info->burst_start + info->burst_len > reg_size) {
+ pr_err("%s too big burst size, index=%d, size=%d\n", __func__,
+ info->burst_start, info->burst_len);
+ return rc;
+ }
+ chunk_num = info->burst_len / info->chunk_size;
+ residue = info->burst_len % info->chunk_size;
+ SPIDBG("%s header_len=%d, chunk nb=%d, residue=%d\n",
+ __func__, header_len, chunk_num, residue);
+ tx_buf.cmd = pg->opcode;
+ tx_buf.addr_msb = (info->burst_addr >> 8) & 0xff;
+ tx_buf.addr_lsb = info->burst_addr & 0xff;
+ SPIDBG("%s cmd=%d, addr_msb=0x%x, addr_lsb=0x%x\n", __func__,
+ tx_buf.cmd, tx_buf.addr_msb, tx_buf.addr_lsb);
+ k = info->burst_start;
+ for (i = 0; i < chunk_num; i++) {
+ SPIDBG("%s burst chunk start=%d, chunk_size=%d, chunk_num=%d\n",
+ __func__,
+ k, info->chunk_size, i);
+ for (j = 0; j < info->chunk_size; j++) {
+ tx_buf.data_arr[j].data_msb =
+ (reg_setting[k+j].reg_data >> 8) & 0xff;
+ tx_buf.data_arr[j].data_lsb =
+ reg_setting[k+j].reg_data & 0xff;
+ }
+ rc = msm_camera_spi_txfr(client->spi_client->spi_master,
+ (void *)&tx_buf, NULL,
+ info->chunk_size * data_type+header_len);
+ if (rc < 0) {
+ pr_err("%s %d spi sending error = %d!!\n", __func__,
+ __LINE__, rc);
+ goto fail;
+ }
+ k += info->chunk_size;
+ }
+ SPIDBG("%s burst chunk start=%d, residue=%d\n", __func__, k, residue);
+ if (residue) {
+ for (j = 0; j < residue; j++) {
+ tx_buf.data_arr[j].data_msb = (reg_setting[k+j].reg_data
+ >> 8) & 0xff;
+ tx_buf.data_arr[j].data_lsb = reg_setting[k+j].reg_data
+ & 0xff;
+ }
+ rc = msm_camera_spi_txfr(client->spi_client->spi_master,
+ (void *)&tx_buf, NULL,
+ residue * data_type+header_len);
+ if (rc < 0) {
+ pr_err("%s %d spi sending error = %d!!\n", __func__,
+ __LINE__, rc);
+ goto fail;
+ }
+ }
+fail:
+ return rc;
+}
+#endif /* SPI_DYNAMIC_ALLOC */
+
+int32_t msm_camera_spi_write_burst(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
+ uint32_t buf_len, uint32_t burst_addr,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int k = 0;
+ int32_t rc = -EFAULT;
+ struct msm_camera_burst_info burst_info;
+
+ SPIDBG(" %s: start\n", __func__);
+ if (buf_len <= 0) {
+ pr_err("%s Invalid parameter, buf_len = %d\n",
+ __func__, buf_len);
+ return rc;
+ }
+ if (reg_size <= 0 || reg_setting == NULL) {
+ pr_err("%s Invalid parameter, array_size = %d\n",
+ __func__, reg_size);
+ return rc;
+ }
+
+ if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ SPIDBG(" %s: buf_len=%d, reg_size=%d\n", __func__, buf_len, reg_size);
+ while (k < reg_size) {
+ if (reg_setting[k].reg_addr == burst_addr) {
+ memset(&burst_info, 0x00,
+ sizeof(struct msm_camera_burst_info));
+ burst_info.burst_addr = burst_addr;
+ burst_info.burst_start = k;
+ burst_info.chunk_size = buf_len;
+ burst_info.burst_len =
+ msm_get_burst_size(reg_setting, reg_size, k,
+ burst_addr);
+ SPIDBG("%s burst start = %d, length = %d\n", __func__,
+ k, burst_info.burst_len);
+ rc = msm_camera_spi_send_burst(client, reg_setting,
+ reg_size, &burst_info, data_type);
+ if (rc < 0) {
+ pr_err("[%s::%d][spi_sync Error::%d]\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ k += burst_info.burst_len;
+ } else {
+ SPIDBG("%s word write, start = %d\n", __func__, k);
+ msm_camera_spi_write(client, reg_setting[k].reg_addr,
+ reg_setting[k].reg_data, data_type);
+ k++;
+ }
+ }
+ SPIDBG("%s: end\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_spi_read_burst(struct msm_camera_i2c_client *client,
+ uint32_t read_byte, uint8_t *buffer, uint32_t burst_addr,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_spi_inst *pg =
+ &client->spi_client->cmd_tbl.read;
+ uint32_t len = msm_camera_spi_get_hlen(pg);
+ uint8_t *tx_buf = NULL;
+ uint8_t *r = buffer;
+
+ SPIDBG("%s: start\n", __func__);
+
+ if (buffer == NULL || read_byte == 0 || len == 0) {
+ pr_err("%s %d Invalid parameters!!\n", __func__, __LINE__);
+ return rc;
+ }
+
+ if ((client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+ tx_buf = kzalloc(len, GFP_KERNEL | GFP_DMA);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ tx_buf[0] = pg->opcode;
+ tx_buf[1] = (burst_addr >> 8) & 0xff;
+ tx_buf[2] = burst_addr & 0xff;
+ tx_buf[3] = 0; /* dummy */
+ rc = msm_camera_spi_txfr_read(client->spi_client->spi_master,
+ &tx_buf[0], r, len, read_byte);
+ if (rc < 0)
+ pr_err("[%s::%d][spi_sync Error::%d]\n", __func__,
+ __LINE__, rc);
+
+ kfree(tx_buf);
+
+ SPIDBG("%s: end\n", __func__);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h
new file mode 100644
index 0000000..4a122cf
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_spi.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2013-2014, 2018, 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_SPI_H
+#define __MSM_CAMERA_SPI_H
+
+#include <linux/spi/spi.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_camera_i2c.h"
+
+#define MAX_SPI_SIZE 110
+#define SPI_DYNAMIC_ALLOC
+
+/*
+ * Common SPI communication scheme
+ * tx: <opcode>[addr][wait][write buffer]
+ * rx: [read buffer]
+ * Some inst require polling busy reg until it's done
+ */
+struct msm_camera_spi_inst {
+ uint8_t opcode; /* one-byte opcode */
+ uint8_t addr_len; /* addr len in bytes */
+ uint8_t dummy_len; /* setup cycles */
+ uint8_t delay_intv; /* delay intv for this inst (ms) */
+ uint8_t delay_count; /* total delay count for this inst */
+};
+
+struct msm_spi_write_burst_data {
+ u8 data_msb;
+ u8 data_lsb;
+};
+
+struct msm_spi_write_burst_packet {
+ u8 cmd;
+ u8 addr_msb;
+ u8 addr_lsb;
+ struct msm_spi_write_burst_data data_arr[MAX_SPI_SIZE];
+};
+
+struct msm_camera_burst_info {
+ uint32_t burst_addr;
+ uint32_t burst_start;
+ uint32_t burst_len;
+ uint32_t chunk_size;
+};
+
+struct msm_camera_spi_inst_tbl {
+ struct msm_camera_spi_inst read;
+ struct msm_camera_spi_inst read_seq;
+ struct msm_camera_spi_inst query_id;
+ struct msm_camera_spi_inst page_program;
+ struct msm_camera_spi_inst write_enable;
+ struct msm_camera_spi_inst read_status;
+ struct msm_camera_spi_inst erase;
+};
+
+struct msm_camera_spi_client {
+ struct spi_device *spi_master;
+ struct msm_camera_spi_inst_tbl cmd_tbl;
+ uint8_t device_id0;
+ uint8_t device_id1;
+ uint8_t mfr_id0;
+ uint8_t mfr_id1;
+ uint8_t retry_delay; /* ms */
+ uint8_t retries; /* retry times upon failure */
+ uint8_t busy_mask; /* busy bit in status reg */
+ uint16_t page_size; /* page size for page program */
+ uint32_t erase_size; /* minimal erase size */
+};
+
+static __always_inline
+uint16_t msm_camera_spi_get_hlen(struct msm_camera_spi_inst *inst)
+{
+ return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len;
+}
+
+int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_spi_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_spi_read_seq_l(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t num_byte, char *tx, char *rx);
+
+int32_t msm_camera_spi_query_id(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_spi_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_spi_erase(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint32_t size);
+
+int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data, enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_spi_write_burst(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size,
+ uint32_t buf_len, uint32_t addr,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_spi_read_burst(struct msm_camera_i2c_client *client,
+ uint32_t read_byte, uint8_t *buffer, uint32_t addr,
+ enum msm_camera_i2c_data_type data_type);
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
new file mode 100644
index 0000000..df22d84
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
@@ -0,0 +1,882 @@
+/* Copyright (c) 2016, 2017-2018, 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/ktime.h>
+#include <linux/mutex.h>
+#include <soc/qcom/camera2.h>
+#include "qseecom_kernel.h"
+#include "msm_camera_i2c.h"
+#include "msm_camera_tz_util.h"
+#include "msm_cci.h"
+#include "msm_sensor.h"
+
+#undef CDBG
+#define MSM_CAMERA_TZ_I2C_VERBOSE
+
+#ifdef CONFIG_MSM_SEC_CCI_DEBUG
+ #define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \
+ ((ret < 0) ? i2c_fn(__VA_ARGS__):ret)
+#else /* CONFIG_MSM_SEC_CCI_DEBUG */
+ #define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \
+ ((ret < 0) ? -EFAULT:ret)
+#endif /* CONFIG_MSM_SEC_CCI_DEBUG */
+
+#ifdef MSM_CAMERA_TZ_I2C_VERBOSE
+ #define CDBG(fmt, args...) \
+ pr_info(CONFIG_MSM_SEC_CCI_TA_NAME "::%s:%d - " fmt, \
+ __func__, __LINE__, ##args)
+#else /* MSM_CAMERA_TZ_I2C_VERBOSE */
+ #define CDBG(fmt, args...) \
+ pr_debug("%s:%d - " fmt, __func__, __LINE__, ##args)
+#endif /* MSM_CAMERA_TZ_I2C_VERBOSE */
+
+#pragma pack(push, msm_camera_tz_i2c, 1)
+
+struct msm_camera_tz_i2c_cci_generic_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ enum msm_camera_tz_cmd_id_t cci_cmd_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+};
+
+#define msm_camera_tz_i2c_cci_generic_rsp_t msm_camera_tz_generic_rsp_t
+
+/* MSM_CAMERA_TZ_CMD_POWER_UP */
+struct msm_camera_tz_i2c_power_up_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+};
+
+#define msm_camera_tz_i2c_power_up_rsp_t msm_camera_tz_generic_rsp_t
+
+/* MSM_CAMERA_TZ_CMD_POWER_DOWN */
+struct msm_camera_tz_i2c_power_down_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+};
+
+#define msm_camera_tz_i2c_power_down_rsp_t msm_camera_tz_generic_rsp_t
+
+/* MSM_CAMERA_TZ_CMD_CCI_READ */
+struct msm_camera_tz_i2c_cci_read_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t addr;
+ uint32_t data_type;
+};
+
+struct msm_camera_tz_i2c_cci_read_rsp_t {
+ enum msm_camera_tz_status_t rc;
+ uint16_t data;
+};
+
+/* MSM_CAMERA_TZ_CMD_CCI_WRITE */
+struct msm_camera_tz_i2c_cci_write_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t addr;
+ uint16_t data;
+ uint32_t data_type;
+};
+
+#define msm_camera_tz_i2c_cci_write_rsp_t msm_camera_tz_generic_rsp_t
+
+/* MSM_CAMERA_TZ_CMD_CCI_UTIL */
+struct msm_camera_tz_i2c_cci_util_req_t {
+ enum msm_camera_tz_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint16_t cci_cmd;
+};
+
+#define msm_camera_tz_i2c_cci_util_rsp_t msm_camera_tz_generic_rsp_t
+
+#pragma pack(pop, msm_camera_tz_i2c)
+
+/* Camera control structure */
+struct msm_camera_tz_i2c_sensor_info_t {
+ struct msm_sensor_ctrl_t *s_ctrl;
+ struct msm_camera_i2c_fn_t *saved_sensor_i2c_fn;
+ uint32_t secure;
+ uint32_t ready;
+};
+
+static struct msm_camera_tz_i2c_sensor_info_t sensor_info[MAX_CAMERAS];
+
+static int32_t msm_camera_tz_i2c_is_sensor_secure(
+ struct msm_camera_i2c_client *client)
+{
+ uint32_t index;
+
+ if (client == NULL) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (index = 0; index < MAX_CAMERAS; index++) {
+ if ((sensor_info[index].s_ctrl != NULL) &&
+ sensor_info[index].secure &&
+ (sensor_info[index].s_ctrl->sensor_i2c_client ==
+ client)) {
+ CDBG("Found secure sensor ID = %d\n",
+ sensor_info[index].s_ctrl->id);
+ return sensor_info[index].s_ctrl->id;
+ }
+ }
+ return -EINVAL;
+}
+
+static int32_t msm_camera_tz_i2c_ta_power_up(
+ struct qseecom_handle *ta_qseecom_handle,
+ int32_t sensor_id,
+ uint32_t *sensor_secure)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_power_up_req_t *cmd;
+ struct msm_camera_tz_i2c_power_up_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ if (sensor_secure == NULL) {
+ pr_err("%s:%d - Bad parameter\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ *sensor_secure = 0;
+
+ if ((ta_qseecom_handle == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_power_up_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_power_up_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_POWER_UP;
+ cmd->sensor_id = sensor_id;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Unable to get sensor secure status, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+
+ if (rsp->rc == MSM_CAMERA_TZ_STATUS_SUCCESS)
+ *sensor_secure = 1;
+ CDBG("Sensor %d is %s\n", sensor_id,
+ (*sensor_secure)?"SECURE":"NON-SECURE");
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_power_down(
+ struct qseecom_handle *ta_qseecom_handle,
+ int32_t sensor_id)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_power_down_req_t *cmd;
+ struct msm_camera_tz_i2c_power_down_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ if ((ta_qseecom_handle == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_power_down_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_power_down_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_POWER_DOWN;
+ cmd->sensor_id = sensor_id;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_generic(
+ struct msm_camera_i2c_client *client,
+ enum msm_camera_tz_cmd_id_t cci_cmd_id)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_generic_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_generic_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ ta_qseecom_handle = msm_camera_tz_get_ta_handle();
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_generic_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_generic_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_CCI_GENERIC;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_cmd_id = cci_cmd_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ }
+ CDBG("Done: rc=%d, SN=%d, MS=%d, SID=%d, CID=%d, CMD=%d - %lluus\n",
+ rc, sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ cci_cmd_id,
+ ktime_us_delta(ktime_get(), startTime));
+
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_read(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr,
+ uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_read_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_read_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ if ((client == NULL) ||
+ (data == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ ta_qseecom_handle = msm_camera_tz_get_ta_handle();
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_read_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_read_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_CCI_READ;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->addr = addr;
+ cmd->data_type = data_type;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ *data = rsp->data;
+ }
+ CDBG("Done: rc=%d, SN=%d, MS=%d, SID=%d, CID=%d, ", rc,
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ CDBG("Addr=0x%X, Type=%d, Data=0x%X - %lluus\n",
+ addr, data_type, *data,
+ ktime_us_delta(ktime_get(), startTime));
+
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_write(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr,
+ uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_write_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_write_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ ta_qseecom_handle = msm_camera_tz_get_ta_handle();
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_write_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_write_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_CCI_WRITE;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->addr = addr;
+ cmd->data = data;
+ cmd->data_type = data_type;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed:, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ }
+ CDBG("Done: rc=%d, SN=%d, MS=%d, SID=%d, CID=%d, ", rc,
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ CDBG("Addr=0x%X, Data=0x%X Type=%d - %lluus\n",
+ addr, data, data_type,
+ ktime_us_delta(ktime_get(), startTime));
+
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_util(
+ struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_util_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_util_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ ta_qseecom_handle = msm_camera_tz_get_ta_handle();
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_util_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_util_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = MSM_CAMERA_TZ_CMD_CCI_UTIL;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->cci_cmd = cci_cmd;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ }
+ CDBG("Done: rc=%d, SN=%d, MS=%d, SID=%d, CID=%d, CMD=%d - %lluus\n",
+ rc, sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ cci_cmd,
+ ktime_us_delta(ktime_get(), startTime));
+
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_probe(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t sensor_id = -1;
+
+ CDBG("Enter\n");
+ sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ if ((sensor_id >= 0) &&
+ (sensor_id < MAX_CAMERAS) &&
+ (sensor_info[sensor_id].ready != 0)) {
+ msm_camera_tz_lock();
+ return sensor_id;
+ }
+ return -EINVAL;
+}
+
+static int32_t msm_camera_tz_i2c_ta_done(void)
+{
+ CDBG("Enter\n");
+ msm_camera_tz_unlock();
+ return 0;
+}
+
+int32_t msm_camera_tz_i2c_power_up(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t rc = 0;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ CDBG("Enter (sensor_id=%d)\n", sensor_id);
+ if ((sensor_id >= 0) && (sensor_id < MAX_CAMERAS)) {
+ rc = msm_camera_tz_load_ta();
+ if (!rc) {
+ uint32_t sensor_secure = 0;
+
+ msm_camera_tz_lock();
+ /* Notify TA & get sensor secure status */
+ rc = msm_camera_tz_i2c_ta_power_up(
+ msm_camera_tz_get_ta_handle(),
+ sensor_id,
+ &sensor_secure);
+ if (!rc && sensor_secure)
+ /* Sensor validated by TA*/
+ sensor_info[sensor_id].ready++;
+ else {
+ msm_camera_tz_unload_ta();
+ rc = -EFAULT;
+ }
+ msm_camera_tz_unlock();
+ }
+ } else
+ rc = -EFAULT;
+ CDBG("Power UP sensor = %d, %s(%d) - %lluus\n",
+ sensor_id,
+ (!rc)?"Ok":"Failed", rc,
+ ktime_us_delta(ktime_get(), startTime));
+ return rc;
+}
+
+int32_t msm_camera_tz_i2c_power_down(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t rc = 0;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ ktime_t startTime = ktime_get();
+
+ CDBG("Enter (sensor_id=%d)\n", sensor_id);
+ if ((sensor_id >= 0) &&
+ (sensor_id < MAX_CAMERAS) &&
+ (sensor_info[sensor_id].ready != 0)) {
+
+ msm_camera_tz_lock();
+ rc = msm_camera_tz_i2c_ta_power_down(
+ msm_camera_tz_get_ta_handle(),
+ sensor_id);
+ sensor_info[sensor_id].ready--;
+ msm_camera_tz_unlock();
+ if (!sensor_info[sensor_id].ready)
+ rc = msm_camera_tz_unload_ta();
+ } else
+ rc = -EFAULT;
+ CDBG("Power DOWN sensor = %d, %s(%d) - %lluus\n",
+ sensor_id,
+ (!rc)?"Ok":"Failed", rc,
+ ktime_us_delta(ktime_get(), startTime));
+ return rc;
+}
+
+int32_t msm_camera_tz_i2c_register_sensor(
+ void *s_ctrl_p)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *)s_ctrl_p;
+
+ if (s_ctrl == NULL) {
+ pr_err("%s:%d - invalid parameter)\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (s_ctrl->id >= MAX_CAMERAS) {
+ pr_err("%s:%d - invalid ID: %d\n",
+ __func__, __LINE__, s_ctrl->id);
+ return -EINVAL;
+ }
+
+ CDBG("id=%d, client=%pK\n", s_ctrl->id, s_ctrl);
+ sensor_info[s_ctrl->id].s_ctrl = s_ctrl;
+ sensor_info[s_ctrl->id].secure = s_ctrl->is_secure;
+ return 0;
+}
+
+int32_t msm_camera_tz_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_read(
+ client, addr, data, data_type);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_read, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X, num=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ num_byte);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_READ_SEQ);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_read_seq, client, addr, data, num_byte);
+}
+
+int32_t msm_camera_tz_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_write(
+ client, addr, data, data_type);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X, num=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ num_byte);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_SEQ);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_seq, client, addr, data, num_byte);
+}
+
+int32_t msm_camera_tz_i2c_write_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_ASYNC);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_async, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_SYNC);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_sync, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_sync_block(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_SYNC_BLOCK);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_sync_block, client,
+ write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_SEQ_TABLE);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_seq_table, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_TABLE_W_MICRODELAY);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_w_microdelay, client,
+ write_setting);
+}
+
+int32_t msm_camera_tz_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_POLL);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_poll, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, MSM_CAMERA_TZ_CMD_CCI_WRITE_CONF_TBL);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_conf_tbl, client, reg_conf_tbl, size,
+ data_type);
+}
+
+int32_t msm_sensor_tz_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, cci_cmd=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid, cci_cmd);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_util(client, cci_cmd);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_sensor_cci_i2c_util, client, cci_cmd);
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile
new file mode 100644
index 0000000..8950c1c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_ir_cut.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
new file mode 100644
index 0000000..165f0c3
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
@@ -0,0 +1,664 @@
+/* Copyright (c) 2016, 2018, 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) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include "msm_ir_cut.h"
+#include "msm_camera_dt_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_ir_cut_mutex);
+
+static struct v4l2_file_operations msm_ir_cut_v4l2_subdev_fops;
+
+static const struct of_device_id msm_ir_cut_dt_match[] = {
+ {.compatible = "qcom,ir-cut", .data = NULL},
+ {}
+};
+
+static struct msm_ir_cut_table msm_gpio_ir_cut_table;
+
+static struct msm_ir_cut_table *ir_cut_table[] = {
+ &msm_gpio_ir_cut_table,
+};
+
+static int32_t msm_ir_cut_get_subdev_id(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (ir_cut_ctrl->ir_cut_device_type != MSM_CAMERA_PLATFORM_DEVICE) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+
+ *subdev_id = ir_cut_ctrl->pdev->id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_init(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter");
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, ir_cut_data);
+
+ CDBG("Exit");
+ return rc;
+}
+
+static int32_t msm_ir_cut_release(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl)
+{
+ int32_t rc = 0;
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_RELEASE) {
+ pr_err("%s:%d Invalid ir_cut state = %d",
+ __func__, __LINE__, ir_cut_ctrl->ir_cut_state);
+ return 0;
+ }
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, NULL);
+ if (rc < 0) {
+ pr_err("%s:%d camera_ir_cut_on failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE;
+ return 0;
+}
+
+static int32_t msm_ir_cut_off(struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int rc = 0;
+
+ CDBG("Enter cut off\n");
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ if (ir_cut_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ ir_cut_ctrl->pinctrl_info.pinctrl,
+ ir_cut_ctrl->pinctrl_info.gpio_state_active);
+
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[0] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P]);
+
+ CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[1] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M]);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P],
+ 0);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M],
+ 1);
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_on(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int rc = 0;
+
+ CDBG("Enter ir cut on\n");
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ if (ir_cut_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ ir_cut_ctrl->pinctrl_info.pinctrl,
+ ir_cut_ctrl->pinctrl_info.gpio_state_active);
+
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[0] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P]);
+
+ CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[1] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M]);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P],
+ 1);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->
+ gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M],
+ 1);
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_handle_init(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ uint32_t i = 0;
+ int32_t rc = -EFAULT;
+ enum msm_ir_cut_driver_type ir_cut_driver_type =
+ ir_cut_ctrl->ir_cut_driver_type;
+
+ CDBG("Enter");
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) {
+ pr_err("%s:%d Invalid ir_cut state = %d",
+ __func__, __LINE__, ir_cut_ctrl->ir_cut_state);
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ir_cut_table); i++) {
+ if (ir_cut_driver_type == ir_cut_table[i]->ir_cut_driver_type) {
+ ir_cut_ctrl->func_tbl = &ir_cut_table[i]->func_tbl;
+ rc = 0;
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("%s:%d failed invalid ir_cut_driver_type %d\n",
+ __func__, __LINE__, ir_cut_driver_type);
+ return -EINVAL;
+ }
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_init(
+ ir_cut_ctrl, ir_cut_data);
+ if (rc < 0) {
+ pr_err("%s:%d camera_ir_cut_init failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_INIT;
+
+ CDBG("Exit");
+ return 0;
+}
+
+static int32_t msm_ir_cut_config(struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ void *argp)
+{
+ int32_t rc = -EINVAL;
+ struct msm_ir_cut_cfg_data_t *ir_cut_data =
+ (struct msm_ir_cut_cfg_data_t *) argp;
+
+ mutex_lock(ir_cut_ctrl->ir_cut_mutex);
+
+ CDBG("Enter %s type %d\n", __func__, ir_cut_data->cfg_type);
+
+ switch (ir_cut_data->cfg_type) {
+ case CFG_IR_CUT_INIT:
+ rc = msm_ir_cut_handle_init(ir_cut_ctrl, ir_cut_data);
+ break;
+ case CFG_IR_CUT_RELEASE:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release(
+ ir_cut_ctrl);
+ break;
+ case CFG_IR_CUT_OFF:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_off(
+ ir_cut_ctrl, ir_cut_data);
+ break;
+ case CFG_IR_CUT_ON:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(
+ ir_cut_ctrl, ir_cut_data);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(ir_cut_ctrl->ir_cut_mutex);
+
+ CDBG("Exit %s type %d\n", __func__, ir_cut_data->cfg_type);
+
+ return rc;
+}
+
+static long msm_ir_cut_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_ir_cut_ctrl_t *fctrl = NULL;
+ void *argp = (void *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err("sd NULL\n");
+ return -EINVAL;
+ }
+ fctrl = v4l2_get_subdevdata(sd);
+ if (!fctrl) {
+ pr_err("fctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_ir_cut_get_subdev_id(fctrl, argp);
+ case VIDIOC_MSM_IR_CUT_CFG:
+ return msm_ir_cut_config(fctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!fctrl->func_tbl) {
+ pr_err("fctrl->func_tbl NULL\n");
+ return -EINVAL;
+ } else {
+ return fctrl->func_tbl->camera_ir_cut_release(fctrl);
+ }
+ default:
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_ir_cut_subdev_core_ops = {
+ .ioctl = msm_ir_cut_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_ir_cut_subdev_ops = {
+ .core = &msm_ir_cut_subdev_core_ops,
+};
+static int msm_ir_cut_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+
+ int rc = 0;
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter\n");
+
+ if (!ir_cut_ctrl) {
+ pr_err("%s: failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release(
+ ir_cut_ctrl);
+
+ CDBG("Exit\n");
+
+ return rc;
+};
+
+static const struct v4l2_subdev_internal_ops msm_ir_cut_internal_ops = {
+ .close = msm_ir_cut_close,
+};
+
+static int32_t msm_ir_cut_get_gpio_dt_data(struct device_node *of_node,
+ struct msm_ir_cut_ctrl_t *fctrl)
+{
+ int32_t rc = 0, i = 0;
+ uint16_t *gpio_array = NULL;
+ int16_t gpio_array_size = 0;
+ struct msm_camera_gpio_conf *gconf = NULL;
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size > 0) {
+ fctrl->power_info.gpio_conf =
+ kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!fctrl->power_info.gpio_conf) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ gconf = fctrl->power_info.gpio_conf;
+
+ gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t),
+ GFP_KERNEL);
+ if (!gpio_array)
+ return -ENOMEM;
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ if (((int16_t)gpio_array[i]) < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto free_gpio_array;
+ }
+ CDBG("%s gpio_array[%d] = %d\n", __func__, i,
+ gpio_array[i]);
+ }
+
+ rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_gpio_array;
+ }
+ kfree(gpio_array);
+
+ if (fctrl->ir_cut_driver_type == IR_CUT_DRIVER_DEFAULT)
+ fctrl->ir_cut_driver_type = IR_CUT_DRIVER_GPIO;
+ CDBG("%s:%d fctrl->ir_cut_driver_type = %d", __func__, __LINE__,
+ fctrl->ir_cut_driver_type);
+ }
+
+ return rc;
+
+free_gpio_array:
+ kfree(gpio_array);
+ return rc;
+}
+
+static int32_t msm_ir_cut_get_dt_data(struct device_node *of_node,
+ struct msm_ir_cut_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("called\n");
+
+ if (!of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ /* Read the sub device */
+ rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ fctrl->ir_cut_driver_type = IR_CUT_DRIVER_DEFAULT;
+
+ /* Read the gpio information from device tree */
+ rc = msm_ir_cut_get_gpio_dt_data(of_node, fctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_ir_cut_get_gpio_dt_data failed rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ir_cut_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct msm_ir_cut_cfg_data_t32 *u32 =
+ (struct msm_ir_cut_cfg_data_t32 *)arg;
+ struct msm_ir_cut_cfg_data_t ir_cut_data;
+
+ CDBG("Enter");
+ ir_cut_data.cfg_type = u32->cfg_type;
+
+ switch (cmd) {
+ case VIDIOC_MSM_IR_CUT_CFG32:
+ cmd = VIDIOC_MSM_IR_CUT_CFG;
+ break;
+ default:
+ return msm_ir_cut_subdev_ioctl(sd, cmd, arg);
+ }
+
+ rc = msm_ir_cut_subdev_ioctl(sd, cmd, &ir_cut_data);
+
+ CDBG("Exit");
+ return rc;
+}
+
+static long msm_ir_cut_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ir_cut_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_ir_cut_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0, i = 0;
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl = NULL;
+
+ CDBG("Enter");
+ if (!pdev->dev.of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ ir_cut_ctrl = kzalloc(sizeof(struct msm_ir_cut_ctrl_t), GFP_KERNEL);
+ if (!ir_cut_ctrl)
+ return -ENOMEM;
+
+ memset(ir_cut_ctrl, 0, sizeof(struct msm_ir_cut_ctrl_t));
+
+ ir_cut_ctrl->pdev = pdev;
+
+ rc = msm_ir_cut_get_dt_data(pdev->dev.of_node, ir_cut_ctrl);
+
+ if (rc < 0) {
+ pr_err("%s:%d msm_ir_cut_get_dt_data failed\n",
+ __func__, __LINE__);
+ kfree(ir_cut_ctrl);
+ return -EINVAL;
+ }
+
+ rc = msm_sensor_driver_get_gpio_data(&(ir_cut_ctrl->gconf),
+ (&pdev->dev)->of_node);
+
+ if ((rc < 0) || (ir_cut_ctrl->gconf == NULL)) {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+
+ kfree(ir_cut_ctrl);
+ return -EINVAL;
+ }
+
+ CDBG("%s: gpio_request_table_size = %d\n",
+ __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size);
+
+ for (i = 0;
+ i < ir_cut_ctrl->gconf->cam_gpio_req_tbl_size; i++) {
+ CDBG("%s: gpio = %d\n", __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].gpio);
+ CDBG("%s: gpio-flags = %lu\n", __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].flags);
+ CDBG("%s: gconf->gpio_num_info->gpio_num[%d] = %d\n",
+ __func__, i,
+ ir_cut_ctrl->gconf->gpio_num_info->gpio_num[i]);
+ }
+
+ ir_cut_ctrl->cam_pinctrl_status = 1;
+
+ rc = msm_camera_pinctrl_init(
+ &(ir_cut_ctrl->pinctrl_info), &(pdev->dev));
+
+ if (rc < 0) {
+ pr_err("ERR:%s: Error in reading IR CUT pinctrl\n",
+ __func__);
+ ir_cut_ctrl->cam_pinctrl_status = 0;
+ }
+
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE;
+ ir_cut_ctrl->power_info.dev = &ir_cut_ctrl->pdev->dev;
+ ir_cut_ctrl->ir_cut_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ ir_cut_ctrl->ir_cut_mutex = &msm_ir_cut_mutex;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&ir_cut_ctrl->msm_sd.sd, &msm_ir_cut_subdev_ops);
+ v4l2_set_subdevdata(&ir_cut_ctrl->msm_sd.sd, ir_cut_ctrl);
+
+ ir_cut_ctrl->msm_sd.sd.internal_ops = &msm_ir_cut_internal_ops;
+ ir_cut_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(ir_cut_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(ir_cut_ctrl->msm_sd.sd.name),
+ "msm_camera_ir_cut");
+ media_entity_pads_init(&ir_cut_ctrl->msm_sd.sd.entity, 0, NULL);
+ ir_cut_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ir_cut_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&ir_cut_ctrl->msm_sd);
+
+ CDBG("%s:%d ir_cut sd name = %s", __func__, __LINE__,
+ ir_cut_ctrl->msm_sd.sd.entity.name);
+ msm_ir_cut_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_ir_cut_v4l2_subdev_fops.compat_ioctl32 =
+ msm_ir_cut_subdev_fops_ioctl;
+#endif
+ ir_cut_ctrl->msm_sd.sd.devnode->fops = &msm_ir_cut_v4l2_subdev_fops;
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_ir_cut_dt_match);
+
+static struct platform_driver msm_ir_cut_platform_driver = {
+ .probe = msm_ir_cut_platform_probe,
+ .driver = {
+ .name = "qcom,ir-cut",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ir_cut_dt_match,
+ },
+};
+
+static int __init msm_ir_cut_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_ir_cut_platform_driver);
+ if (!rc)
+ return rc;
+
+ pr_err("platform probe for ir_cut failed");
+
+ return rc;
+}
+
+static void __exit msm_ir_cut_exit_module(void)
+{
+ platform_driver_unregister(&msm_ir_cut_platform_driver);
+}
+
+static struct msm_ir_cut_table msm_gpio_ir_cut_table = {
+ .ir_cut_driver_type = IR_CUT_DRIVER_GPIO,
+ .func_tbl = {
+ .camera_ir_cut_init = msm_ir_cut_init,
+ .camera_ir_cut_release = msm_ir_cut_release,
+ .camera_ir_cut_off = msm_ir_cut_off,
+ .camera_ir_cut_on = msm_ir_cut_on,
+ },
+};
+
+module_init(msm_ir_cut_init_module);
+module_exit(msm_ir_cut_exit_module);
+MODULE_DESCRIPTION("MSM IR CUT");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h
new file mode 100644
index 0000000..91401f4
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2016, 2018, 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_IR_CUT_H
+#define MSM_IR_CUT_H
+
+#include <soc/qcom/camera2.h>
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_ir_cut_state_t {
+ MSM_CAMERA_IR_CUT_INIT,
+ MSM_CAMERA_IR_CUT_RELEASE,
+};
+
+enum msm_ir_cut_driver_type {
+ IR_CUT_DRIVER_GPIO,
+ IR_CUT_DRIVER_DEFAULT,
+};
+
+struct msm_ir_cut_ctrl_t;
+
+struct msm_ir_cut_func_t {
+ int32_t (*camera_ir_cut_init)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+ int32_t (*camera_ir_cut_release)(struct msm_ir_cut_ctrl_t *);
+ int32_t (*camera_ir_cut_off)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+ int32_t (*camera_ir_cut_on)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+};
+
+struct msm_ir_cut_table {
+ enum msm_ir_cut_driver_type ir_cut_driver_type;
+ struct msm_ir_cut_func_t func_tbl;
+};
+
+struct msm_ir_cut_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct msm_ir_cut_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+
+ enum msm_camera_device_type_t ir_cut_device_type;
+ struct mutex *ir_cut_mutex;
+
+ /* ir_cut driver type */
+ enum msm_ir_cut_driver_type ir_cut_driver_type;
+
+ /* ir_cut state */
+ enum msm_camera_ir_cut_state_t ir_cut_state;
+
+ struct msm_camera_gpio_conf *gconf;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile
new file mode 100644
index 0000000..6e99ecc
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_ir_led.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
new file mode 100644
index 0000000..00ce821
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
@@ -0,0 +1,461 @@
+/* Copyright (c) 2016, 2018, 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) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include "msm_ir_led.h"
+#include "msm_camera_dt_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_ir_led_mutex);
+
+static struct v4l2_file_operations msm_ir_led_v4l2_subdev_fops;
+
+static const struct of_device_id msm_ir_led_dt_match[] = {
+ {.compatible = "qcom,ir-led", .data = NULL},
+ {}
+};
+
+static struct msm_ir_led_table msm_default_ir_led_table;
+
+static struct msm_ir_led_table *ir_led_table[] = {
+ &msm_default_ir_led_table,
+};
+
+static int32_t msm_ir_led_get_subdev_id(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("subdevice ID is not valid\n");
+ return -EINVAL;
+ }
+ if (ir_led_ctrl->ir_led_device_type != MSM_CAMERA_PLATFORM_DEVICE) {
+ pr_err("device type is not matching\n");
+ return -EINVAL;
+ }
+
+ *subdev_id = ir_led_ctrl->pdev->id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_init(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, ir_led_data);
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ir_led_release(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl)
+{
+ int32_t rc = 0;
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_RELEASE) {
+ pr_err("Invalid ir_led state = %d\n",
+ ir_led_ctrl->ir_led_state);
+ return 0;
+ }
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, NULL);
+ if (rc < 0) {
+ pr_err("camera_ir_led_off failed (%d)\n", rc);
+ return rc;
+ }
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE;
+ return 0;
+}
+
+static int32_t msm_ir_led_off(struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->pwm_dev)
+ pwm_disable(ir_led_ctrl->pwm_dev);
+ else
+ pr_err("pwm device is null\n");
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_on(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ int rc;
+
+ CDBG("pwm duty on(ns) %d, pwm period(ns) %d\n",
+ ir_led_data->pwm_duty_on_ns, ir_led_data->pwm_period_ns);
+
+ if (ir_led_ctrl->pwm_dev) {
+ rc = pwm_config(ir_led_ctrl->pwm_dev,
+ ir_led_data->pwm_duty_on_ns,
+ ir_led_data->pwm_period_ns);
+ if (rc) {
+ pr_err("PWM config failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = pwm_enable(ir_led_ctrl->pwm_dev);
+ if (rc) {
+ pr_err("PWM enable failed(%d)\n", rc);
+ return rc;
+ }
+ } else
+ pr_err("pwm device is null\n");
+
+ return 0;
+}
+
+static int32_t msm_ir_led_handle_init(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ uint32_t i = 0;
+ int32_t rc = -EFAULT;
+ enum msm_ir_led_driver_type ir_led_driver_type =
+ ir_led_ctrl->ir_led_driver_type;
+
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) {
+ pr_err("Invalid ir_led state = %d\n",
+ ir_led_ctrl->ir_led_state);
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ir_led_table); i++) {
+ if (ir_led_driver_type == ir_led_table[i]->ir_led_driver_type) {
+ ir_led_ctrl->func_tbl = &ir_led_table[i]->func_tbl;
+ rc = 0;
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("failed invalid ir_led_driver_type %d\n",
+ ir_led_driver_type);
+ return -EINVAL;
+ }
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_init(
+ ir_led_ctrl, ir_led_data);
+ if (rc < 0) {
+ pr_err("camera_ir_led_init failed (%d)\n", rc);
+ return rc;
+ }
+
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_INIT;
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_config(struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ void *argp)
+{
+ int32_t rc = -EINVAL;
+ struct msm_ir_led_cfg_data_t *ir_led_data =
+ (struct msm_ir_led_cfg_data_t *) argp;
+
+ mutex_lock(ir_led_ctrl->ir_led_mutex);
+
+ CDBG("type %d\n", ir_led_data->cfg_type);
+
+ switch (ir_led_data->cfg_type) {
+ case CFG_IR_LED_INIT:
+ rc = msm_ir_led_handle_init(ir_led_ctrl, ir_led_data);
+ break;
+ case CFG_IR_LED_RELEASE:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_release(
+ ir_led_ctrl);
+ break;
+ case CFG_IR_LED_OFF:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(
+ ir_led_ctrl, ir_led_data);
+ break;
+ case CFG_IR_LED_ON:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_on(
+ ir_led_ctrl, ir_led_data);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(ir_led_ctrl->ir_led_mutex);
+
+ CDBG("Exit: type %d\n", ir_led_data->cfg_type);
+
+ return rc;
+}
+
+static long msm_ir_led_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_ir_led_ctrl_t *fctrl = NULL;
+ void *argp = (void *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err(" v4l2 ir led subdevice is NULL\n");
+ return -EINVAL;
+ }
+ fctrl = v4l2_get_subdevdata(sd);
+ if (!fctrl) {
+ pr_err("fctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_ir_led_get_subdev_id(fctrl, argp);
+ case VIDIOC_MSM_IR_LED_CFG:
+ return msm_ir_led_config(fctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!fctrl->func_tbl) {
+ pr_err("No call back funcions\n");
+ return -EINVAL;
+ } else {
+ return fctrl->func_tbl->camera_ir_led_release(fctrl);
+ }
+ default:
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_ir_led_subdev_core_ops = {
+ .ioctl = msm_ir_led_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_ir_led_subdev_ops = {
+ .core = &msm_ir_led_subdev_core_ops,
+};
+
+static int msm_ir_led_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+
+ int rc = 0;
+ struct msm_ir_led_ctrl_t *ir_led_ctrl = v4l2_get_subdevdata(sd);
+
+ if (!ir_led_ctrl) {
+ pr_err("v4l2 subdevice data read failed\n");
+ return -EINVAL;
+ }
+
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_release(
+ ir_led_ctrl);
+
+ CDBG("Exit (%d)\n", rc);
+
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_ir_led_internal_ops = {
+ .close = msm_ir_led_close,
+};
+
+static int32_t msm_ir_led_get_dt_data(struct device_node *of_node,
+ struct msm_ir_led_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("called\n");
+
+ /* Read the sub device */
+ rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id);
+ if (rc < 0) {
+ pr_err("reading cell-index for ir-led node is failed(rc) %d\n",
+ rc);
+ return rc;
+ }
+
+ fctrl->ir_led_driver_type = IR_LED_DRIVER_DEFAULT;
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ir_led_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct msm_ir_led_cfg_data_t32 *u32 =
+ (struct msm_ir_led_cfg_data_t32 *)arg;
+ struct msm_ir_led_cfg_data_t ir_led_data;
+
+ CDBG("Enter\n");
+ ir_led_data.cfg_type = u32->cfg_type;
+ ir_led_data.pwm_duty_on_ns = u32->pwm_duty_on_ns;
+ ir_led_data.pwm_period_ns = u32->pwm_period_ns;
+
+ switch (cmd) {
+ case VIDIOC_MSM_IR_LED_CFG32:
+ cmd = VIDIOC_MSM_IR_LED_CFG;
+ break;
+ default:
+ return msm_ir_led_subdev_ioctl(sd, cmd, arg);
+ }
+
+ rc = msm_ir_led_subdev_ioctl(sd, cmd, &ir_led_data);
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static long msm_ir_led_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ir_led_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_ir_led_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_ir_led_ctrl_t *ir_led_ctrl = NULL;
+
+ CDBG("Enter\n");
+ if (!pdev->dev.of_node) {
+ pr_err("IR LED device node is not present in device tree\n");
+ return -EINVAL;
+ }
+
+ ir_led_ctrl = devm_kzalloc(&pdev->dev, sizeof(struct msm_ir_led_ctrl_t),
+ GFP_KERNEL);
+ if (!ir_led_ctrl)
+ return -ENOMEM;
+
+ ir_led_ctrl->pdev = pdev;
+
+ /* Reading PWM device node */
+ ir_led_ctrl->pwm_dev = of_pwm_get(pdev->dev.of_node, NULL);
+
+ if (IS_ERR(ir_led_ctrl->pwm_dev)) {
+ rc = PTR_ERR(ir_led_ctrl->pwm_dev);
+ pr_err("Cannot get PWM device (%d)\n", rc);
+ ir_led_ctrl->pwm_dev = NULL;
+ }
+
+ rc = msm_ir_led_get_dt_data(pdev->dev.of_node, ir_led_ctrl);
+ if (rc < 0) {
+ pr_err("msm_ir_led_get_dt_data failed\n");
+ devm_kfree(&pdev->dev, ir_led_ctrl);
+ return -EINVAL;
+ }
+
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE;
+ ir_led_ctrl->power_info.dev = &ir_led_ctrl->pdev->dev;
+ ir_led_ctrl->ir_led_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ ir_led_ctrl->ir_led_mutex = &msm_ir_led_mutex;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&ir_led_ctrl->msm_sd.sd, &msm_ir_led_subdev_ops);
+ v4l2_set_subdevdata(&ir_led_ctrl->msm_sd.sd, ir_led_ctrl);
+
+ ir_led_ctrl->msm_sd.sd.internal_ops = &msm_ir_led_internal_ops;
+ ir_led_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(ir_led_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(ir_led_ctrl->msm_sd.sd.name),
+ "msm_camera_ir_led");
+ media_entity_pads_init(&ir_led_ctrl->msm_sd.sd.entity, 0, NULL);
+ ir_led_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ir_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&ir_led_ctrl->msm_sd);
+
+ CDBG("ir_led sd name = %s\n",
+ ir_led_ctrl->msm_sd.sd.entity.name);
+ msm_ir_led_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_ir_led_v4l2_subdev_fops.compat_ioctl32 =
+ msm_ir_led_subdev_fops_ioctl;
+#endif
+ ir_led_ctrl->msm_sd.sd.devnode->fops = &msm_ir_led_v4l2_subdev_fops;
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_ir_led_dt_match);
+
+static struct platform_driver msm_ir_led_platform_driver = {
+ .probe = msm_ir_led_platform_probe,
+ .driver = {
+ .name = "qcom,ir-led",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ir_led_dt_match,
+ },
+};
+
+static int __init msm_ir_led_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_ir_led_platform_driver);
+ if (!rc)
+ return rc;
+
+ pr_err("ir-led driver register failed (%d)\n", rc);
+
+ return rc;
+}
+
+static void __exit msm_ir_led_exit_module(void)
+{
+ platform_driver_unregister(&msm_ir_led_platform_driver);
+}
+
+static struct msm_ir_led_table msm_default_ir_led_table = {
+ .ir_led_driver_type = IR_LED_DRIVER_DEFAULT,
+ .func_tbl = {
+ .camera_ir_led_init = msm_ir_led_init,
+ .camera_ir_led_release = msm_ir_led_release,
+ .camera_ir_led_off = msm_ir_led_off,
+ .camera_ir_led_on = msm_ir_led_on,
+ },
+};
+
+module_init(msm_ir_led_init_module);
+module_exit(msm_ir_led_exit_module);
+MODULE_DESCRIPTION("MSM IR LED");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h
new file mode 100644
index 0000000..8330c97
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2016, 2018, 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_IR_LED_H
+#define MSM_IR_LED_H
+
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msm_cam_sensor.h>
+#include <soc/qcom/camera2.h>
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_ir_led_state_t {
+ MSM_CAMERA_IR_LED_INIT,
+ MSM_CAMERA_IR_LED_RELEASE,
+};
+
+enum msm_ir_led_driver_type {
+ IR_LED_DRIVER_GPIO,
+ IR_LED_DRIVER_DEFAULT,
+};
+
+struct msm_ir_led_ctrl_t;
+
+struct msm_ir_led_func_t {
+ int32_t (*camera_ir_led_init)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+ int32_t (*camera_ir_led_release)(struct msm_ir_led_ctrl_t *);
+ int32_t (*camera_ir_led_off)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+ int32_t (*camera_ir_led_on)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+};
+
+struct msm_ir_led_table {
+ enum msm_ir_led_driver_type ir_led_driver_type;
+ struct msm_ir_led_func_t func_tbl;
+};
+
+struct msm_ir_led_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct pwm_device *pwm_dev;
+ struct msm_ir_led_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+
+ enum msm_camera_device_type_t ir_led_device_type;
+ struct mutex *ir_led_mutex;
+
+ /* ir_led driver type */
+ enum msm_ir_led_driver_type ir_led_driver_type;
+
+ /* ir_led state */
+ enum msm_camera_ir_led_state_t ir_led_state;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile b/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile
new file mode 100644
index 0000000..e981fc2
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += msm_laser_led.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c
new file mode 100644
index 0000000..d30fc95
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c
@@ -0,0 +1,576 @@
+/* Copyright (c) 2017-2018, 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_laser_led.h"
+#include "msm_camera_dt_util.h"
+#include "msm_sd.h"
+#include "msm_cci.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_laser_led_mutex);
+
+static struct v4l2_file_operations msm_laser_led_v4l2_subdev_fops;
+
+static const struct of_device_id msm_laser_led_dt_match[] = {
+ {.compatible = "qcom,laser-led", .data = NULL},
+ {}
+};
+
+static long msm_laser_led_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg);
+
+static int32_t msm_laser_led_get_subdev_id(
+ struct msm_laser_led_ctrl_t *laser_led_ctrl, void __user *arg)
+{
+ int32_t __user *subdev_id = (int32_t __user *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("subdevice ID is not valid\n");
+ return -EINVAL;
+ }
+
+ if (laser_led_ctrl->laser_led_device_type !=
+ MSM_CAMERA_PLATFORM_DEVICE) {
+ pr_err("device type is not matching\n");
+ return -EINVAL;
+ }
+
+ if (copy_to_user(arg, &laser_led_ctrl->pdev->id,
+ sizeof(int32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ CDBG("Exit: subdev_id %d\n", laser_led_ctrl->pdev->id);
+ return 0;
+}
+
+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,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+#ifdef CONFIG_COMPAT
+static int32_t msm_laser_led_init(
+ struct msm_laser_led_ctrl_t *laser_led_ctrl,
+ struct msm_laser_led_cfg_data_t32 __user *laser_led_data)
+#else
+static int32_t msm_laser_led_init(
+ struct msm_laser_led_ctrl_t *laser_led_ctrl,
+ struct msm_laser_led_cfg_data_t __user *laser_led_data)
+#endif
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+
+ if (laser_led_ctrl->laser_led_state == MSM_CAMERA_LASER_LED_INIT) {
+ pr_err("Invalid laser_led state = %d\n",
+ laser_led_ctrl->laser_led_state);
+ return 0;
+ }
+
+ rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &laser_led_ctrl->i2c_client, MSM_CCI_INIT);
+ if (rc < 0)
+ pr_err("cci_init failed\n");
+
+ cci_client = laser_led_ctrl->i2c_client.cci_client;
+
+ if (copy_from_user(&(cci_client->sid),
+ &(laser_led_data->i2c_addr),
+ sizeof(uint16_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ cci_client->sid = cci_client->sid >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+
+ if (copy_from_user(&(cci_client->i2c_freq_mode),
+ &(laser_led_data->i2c_freq_mode),
+ sizeof(enum i2c_freq_mode_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_INIT;
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int msm_laser_led_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_laser_led_ctrl_t *l_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter\n");
+ if (!l_ctrl) {
+ pr_err("failed: subdev data is null\n");
+ return -EINVAL;
+ }
+ mutex_lock(l_ctrl->laser_led_mutex);
+ if (l_ctrl->laser_led_device_type == MSM_CAMERA_PLATFORM_DEVICE &&
+ l_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_RELEASE) {
+ rc = l_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &l_ctrl->i2c_client, MSM_CCI_RELEASE);
+ if (rc < 0)
+ pr_err("cci_init failed: %d\n", rc);
+ }
+ l_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE;
+ mutex_unlock(l_ctrl->laser_led_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_laser_led_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ CDBG("Enter\n");
+ switch (cmd) {
+ case VIDIOC_MSM_LASER_LED_CFG32:
+ cmd = VIDIOC_MSM_LASER_LED_CFG;
+ default:
+ rc = msm_laser_led_subdev_ioctl(sd, cmd, arg);
+ }
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static long msm_laser_led_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return msm_laser_led_subdev_do_ioctl(file, cmd, (void *)arg);
+}
+
+static int32_t msm_laser_led_control32(
+ struct msm_laser_led_ctrl_t *laser_led_ctrl,
+ void __user *argp)
+{
+ struct msm_camera_i2c_reg_setting32 conf_array32;
+ struct msm_camera_i2c_reg_setting conf_array;
+ int32_t rc = 0;
+ struct msm_laser_led_cfg_data_t32 laser_led_data;
+ uint32_t *debug_reg;
+ int i;
+ uint16_t local_data;
+
+ if (laser_led_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_INIT) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, laser_led_ctrl->laser_led_state);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&laser_led_data,
+ argp,
+ sizeof(struct msm_laser_led_cfg_data_t32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&conf_array32,
+ (compat_ptr)(laser_led_data.setting),
+ sizeof(struct msm_camera_i2c_reg_setting32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ conf_array.addr_type = conf_array32.addr_type;
+ conf_array.data_type = conf_array32.data_type;
+ conf_array.delay = conf_array32.delay;
+ conf_array.size = conf_array32.size;
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_REG_DATA_MAX) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ conf_array.reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!conf_array.reg_setting)
+ return -ENOMEM;
+
+ if (copy_from_user(conf_array.reg_setting,
+ (compat_ptr)(conf_array32.reg_setting),
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(conf_array.reg_setting);
+ return -EFAULT;
+ }
+
+ debug_reg = kzalloc(laser_led_data.debug_reg_size *
+ (sizeof(uint32_t)), GFP_KERNEL);
+ if (!debug_reg) {
+ kfree(conf_array.reg_setting);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(debug_reg,
+ (void __user *)compat_ptr(laser_led_data.debug_reg),
+ laser_led_data.debug_reg_size *
+ sizeof(uint32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(conf_array.reg_setting);
+ kfree(debug_reg);
+ return -EFAULT;
+ }
+
+ laser_led_ctrl->i2c_client.addr_type = conf_array.addr_type;
+
+ rc = laser_led_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_table(&(laser_led_ctrl->i2c_client),
+ &conf_array);
+
+ for (i = 0; i < laser_led_data.debug_reg_size; i++) {
+ rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_read(
+ &(laser_led_ctrl->i2c_client),
+ debug_reg[i],
+ &local_data, conf_array.data_type);
+ }
+
+ kfree(conf_array.reg_setting);
+ kfree(debug_reg);
+
+ return rc;
+}
+#endif
+
+static int32_t msm_laser_led_control(
+ struct msm_laser_led_ctrl_t *laser_led_ctrl,
+ void __user *argp)
+{
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_laser_led_cfg_data_t laser_led_data;
+
+ uint32_t *debug_reg;
+ int i;
+ uint16_t local_data;
+ int32_t rc = 0;
+
+ if (laser_led_ctrl->laser_led_state != MSM_CAMERA_LASER_LED_INIT) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, laser_led_ctrl->laser_led_state);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&laser_led_data,
+ argp,
+ sizeof(struct msm_laser_led_cfg_data_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&conf_array,
+ (laser_led_data.setting),
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_REG_DATA_MAX) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ conf_array.reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!conf_array.reg_setting)
+ return -ENOMEM;
+
+ if (copy_from_user(conf_array.reg_setting, (void __user *)(
+ conf_array.reg_setting),
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(conf_array.reg_setting);
+ return -EFAULT;
+ }
+
+ debug_reg = kzalloc(laser_led_data.debug_reg_size *
+ (sizeof(uint32_t)), GFP_KERNEL);
+ if (!debug_reg) {
+ kfree(conf_array.reg_setting);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(debug_reg,
+ (laser_led_data.debug_reg),
+ laser_led_data.debug_reg_size *
+ sizeof(uint32_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(debug_reg);
+ kfree(conf_array.reg_setting);
+ return -EFAULT;
+ }
+
+ laser_led_ctrl->i2c_client.addr_type = conf_array.addr_type;
+
+ rc = laser_led_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_table(&(laser_led_ctrl->i2c_client),
+ &conf_array);
+
+ for (i = 0; i < laser_led_data.debug_reg_size; i++) {
+ rc = laser_led_ctrl->i2c_client.i2c_func_tbl->i2c_read(
+ &(laser_led_ctrl->i2c_client),
+ debug_reg[i],
+ &local_data, conf_array.data_type);
+ }
+
+ kfree(conf_array.reg_setting);
+ kfree(debug_reg);
+
+ return rc;
+}
+
+static int32_t msm_laser_led_config(struct msm_laser_led_ctrl_t *laser_led_ctrl,
+ void __user *argp)
+{
+ int32_t rc = -EINVAL;
+ enum msm_laser_led_cfg_type_t cfg_type;
+
+#ifdef CONFIG_COMPAT
+ struct msm_laser_led_cfg_data_t32 __user *laser_led_data =
+ (struct msm_laser_led_cfg_data_t32 __user *) argp;
+#else
+ struct msm_laser_led_cfg_data_t __user *laser_led_data =
+ (struct msm_laser_led_cfg_data_t __user *) argp;
+#endif
+
+ mutex_lock(laser_led_ctrl->laser_led_mutex);
+
+ if (copy_from_user(&(cfg_type),
+ &(laser_led_data->cfg_type),
+ sizeof(enum msm_laser_led_cfg_type_t))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ mutex_unlock(laser_led_ctrl->laser_led_mutex);
+ return -EFAULT;
+ }
+
+ CDBG("type %d\n", cfg_type);
+
+ switch (cfg_type) {
+ case CFG_LASER_LED_INIT:
+ rc = msm_laser_led_init(laser_led_ctrl, laser_led_data);
+ break;
+ case CFG_LASER_LED_CONTROL:
+#ifdef CONFIG_COMPAT
+ if (is_compat_task())
+ rc = msm_laser_led_control32(laser_led_ctrl, argp);
+ else
+#endif
+ rc = msm_laser_led_control(laser_led_ctrl, argp);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(laser_led_ctrl->laser_led_mutex);
+
+ CDBG("Exit: type %d\n", cfg_type);
+
+ return rc;
+}
+
+static long msm_laser_led_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_laser_led_ctrl_t *lctrl = NULL;
+ void __user *argp = (void __user *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err(" v4l2 ir led subdevice is NULL\n");
+ return -EINVAL;
+ }
+ lctrl = v4l2_get_subdevdata(sd);
+ if (!lctrl) {
+ pr_err("lctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_laser_led_get_subdev_id(lctrl, argp);
+ case VIDIOC_MSM_LASER_LED_CFG:
+ return msm_laser_led_config(lctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!lctrl->i2c_client.i2c_func_tbl) {
+ pr_err("a_ctrl->i2c_client.i2c_func_tbl NULL\n");
+ return -EINVAL;
+ }
+ return msm_laser_led_close(sd, NULL);
+
+ default:
+ pr_err("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_laser_led_subdev_core_ops = {
+ .ioctl = msm_laser_led_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_laser_led_subdev_ops = {
+ .core = &msm_laser_led_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_laser_led_internal_ops = {
+ .close = msm_laser_led_close,
+};
+
+static int32_t msm_laser_led_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_laser_led_ctrl_t *laser_led_ctrl = NULL;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+ if (!pdev->dev.of_node) {
+ pr_err("IR LED device node is not present in device tree\n");
+ return -EINVAL;
+ }
+
+ laser_led_ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_laser_led_ctrl_t), GFP_KERNEL);
+ if (!laser_led_ctrl)
+ return -ENOMEM;
+
+ laser_led_ctrl->pdev = pdev;
+
+ rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index",
+ &pdev->id);
+ CDBG("cell-index %d, rc %d\n", pdev->id, rc);
+ if (rc < 0) {
+ kfree(laser_led_ctrl);
+ pr_err("reading cell index failed: rc %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master",
+ &laser_led_ctrl->cci_master);
+ CDBG("qcom,cci-master %d, rc %d\n", laser_led_ctrl->cci_master, rc);
+ if (rc < 0 || laser_led_ctrl->cci_master >= MASTER_MAX) {
+ kfree(laser_led_ctrl);
+ pr_err("invalid cci master info: rc %d\n", rc);
+ return rc;
+ }
+
+ laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE;
+ laser_led_ctrl->power_info.dev = &laser_led_ctrl->pdev->dev;
+ laser_led_ctrl->laser_led_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ laser_led_ctrl->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ laser_led_ctrl->laser_led_mutex = &msm_laser_led_mutex;
+
+ laser_led_ctrl->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!laser_led_ctrl->i2c_client.cci_client)
+ return -ENOMEM;
+
+ cci_client = laser_led_ctrl->i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = laser_led_ctrl->cci_master;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&laser_led_ctrl->msm_sd.sd, &msm_laser_led_subdev_ops);
+ v4l2_set_subdevdata(&laser_led_ctrl->msm_sd.sd, laser_led_ctrl);
+
+ laser_led_ctrl->msm_sd.sd.internal_ops = &msm_laser_led_internal_ops;
+ laser_led_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(laser_led_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(laser_led_ctrl->msm_sd.sd.name),
+ "msm_camera_laser_led");
+ media_entity_pads_init(&laser_led_ctrl->msm_sd.sd.entity, 0, NULL);
+ laser_led_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ /*
+ * laser_led_ctrl->msm_sd.sd.entity.group_id
+ * = MSM_CAMERA_SUBDEV_LASER_LED;
+ */
+ laser_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&laser_led_ctrl->msm_sd);
+
+ laser_led_ctrl->laser_led_state = MSM_CAMERA_LASER_LED_RELEASE;
+
+ CDBG("laser_led sd name = %s\n",
+ laser_led_ctrl->msm_sd.sd.entity.name);
+ msm_laser_led_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_laser_led_v4l2_subdev_fops.compat_ioctl32 =
+ msm_laser_led_subdev_fops_ioctl;
+#endif
+ laser_led_ctrl->msm_sd.sd.devnode->fops =
+ &msm_laser_led_v4l2_subdev_fops;
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_laser_led_dt_match);
+
+static struct platform_driver msm_laser_led_platform_driver = {
+ .probe = msm_laser_led_platform_probe,
+ .driver = {
+ .name = "qcom,laser-led",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_laser_led_dt_match,
+ },
+};
+
+static int __init msm_laser_led_init_module(void)
+{
+ int32_t rc;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_laser_led_platform_driver);
+ if (!rc) {
+ CDBG("Exit\n");
+ return rc;
+ }
+ pr_err("laser-led driver register failed: %d\n", rc);
+
+ return rc;
+}
+
+static void __exit msm_laser_led_exit_module(void)
+{
+ platform_driver_unregister(&msm_laser_led_platform_driver);
+}
+
+module_init(msm_laser_led_init_module);
+module_exit(msm_laser_led_exit_module);
+MODULE_DESCRIPTION("MSM IR LED");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h
new file mode 100644
index 0000000..7874aa3
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2017-2018, 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_LASER_LED_H
+#define MSM_LASER_LED_H
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <soc/qcom/camera2.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+#include "msm_sd.h"
+
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_laser_led_state_t {
+ MSM_CAMERA_LASER_LED_INIT,
+ MSM_CAMERA_LASER_LED_RELEASE,
+};
+
+struct msm_laser_led_ctrl_t;
+
+struct msm_laser_led_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct msm_laser_led_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+ struct i2c_driver *i2c_driver;
+ struct platform_driver *pdriver;
+ struct msm_camera_i2c_client i2c_client;
+ enum msm_camera_device_type_t laser_led_device_type;
+ struct v4l2_subdev sdev;
+ struct v4l2_subdev_ops *laser_led_v4l2_subdev_ops;
+ struct mutex *laser_led_mutex;
+ enum msm_camera_laser_led_state_t laser_led_state;
+ enum cci_i2c_master_t cci_master;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
new file mode 100644
index 0000000..c0c83e5
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -0,0 +1,1554 @@
+/* Copyright (c) 2011-2018, 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"
+#include "msm_sd.h"
+#include "camera.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <linux/regulator/consumer.h>
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl;
+static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl;
+
+static void msm_sensor_adjust_mclk(struct msm_camera_power_ctrl_t *ctrl)
+{
+ int idx;
+ struct msm_sensor_power_setting *power_setting;
+
+ for (idx = 0; idx < ctrl->power_setting_size; idx++) {
+ power_setting = &ctrl->power_setting[idx];
+ if (power_setting->seq_type == SENSOR_CLK &&
+ power_setting->seq_val == SENSOR_CAM_MCLK) {
+ if (power_setting->config_val == 24000000) {
+ power_setting->config_val = 23880000;
+ CDBG("%s MCLK request adjusted to 23.88MHz\n"
+ , __func__);
+ }
+ break;
+ }
+ }
+
+}
+
+static void msm_sensor_misc_regulator(
+ struct msm_sensor_ctrl_t *sctrl, uint32_t enable)
+{
+ int32_t rc = 0;
+
+ if (enable) {
+ sctrl->misc_regulator = (void *)rpm_regulator_get(
+ &sctrl->pdev->dev, sctrl->sensordata->misc_regulator);
+ if (sctrl->misc_regulator) {
+ rc = rpm_regulator_set_mode(sctrl->misc_regulator,
+ RPM_REGULATOR_MODE_HPM);
+ if (rc < 0) {
+ pr_err("%s: Failed to set for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ rpm_regulator_put(sctrl->misc_regulator);
+ }
+ } else {
+ pr_err("%s: Failed to vote for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ }
+ } else {
+ if (sctrl->misc_regulator) {
+ rc = rpm_regulator_set_mode(
+ (struct rpm_regulator *)sctrl->misc_regulator,
+ RPM_REGULATOR_MODE_AUTO);
+ if (rc < 0)
+ pr_err("%s: Failed to set for rpm regulator on %s: %d\n",
+ __func__,
+ sctrl->sensordata->misc_regulator, rc);
+ rpm_regulator_put(sctrl->misc_regulator);
+ }
+ }
+}
+
+int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ if (!s_ctrl->pdev && !s_ctrl->sensor_i2c_client->client)
+ return 0;
+ kfree(s_ctrl->sensordata->slave_info);
+ kfree(s_ctrl->sensordata->cam_slave_info);
+ kfree(s_ctrl->sensordata->actuator_info);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf);
+ kfree(s_ctrl->sensordata->power_info.cam_vreg);
+ kfree(s_ctrl->sensordata->power_info.power_setting);
+ kfree(s_ctrl->sensordata->power_info.power_down_setting);
+ kfree(s_ctrl->sensordata->csi_lane_params);
+ kfree(s_ctrl->sensordata->sensor_info);
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_I2C_DEVICE) {
+ msm_camera_i2c_dev_put_clk_info(
+ &s_ctrl->sensor_i2c_client->client->dev,
+ &s_ctrl->sensordata->power_info.clk_info,
+ &s_ctrl->sensordata->power_info.clk_ptr,
+ s_ctrl->sensordata->power_info.clk_info_size);
+ } else {
+ msm_camera_put_clk_info(s_ctrl->pdev,
+ &s_ctrl->sensordata->power_info.clk_info,
+ &s_ctrl->sensordata->power_info.clk_ptr,
+ s_ctrl->sensordata->power_info.clk_info_size);
+ }
+
+ kfree(s_ctrl->sensordata);
+ return 0;
+}
+
+int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ struct msm_camera_power_ctrl_t *power_info;
+ enum msm_camera_device_type_t sensor_device_type;
+ struct msm_camera_i2c_client *sensor_i2c_client;
+
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: s_ctrl %pK\n",
+ __func__, __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ if (s_ctrl->is_csid_tg_mode)
+ return 0;
+
+ power_info = &s_ctrl->sensordata->power_info;
+ sensor_device_type = s_ctrl->sensor_device_type;
+ sensor_i2c_client = s_ctrl->sensor_i2c_client;
+
+ if (!power_info || !sensor_i2c_client) {
+ pr_err("%s:%d failed: power_info %pK sensor_i2c_client %pK\n",
+ __func__, __LINE__, power_info, sensor_i2c_client);
+ return -EINVAL;
+ }
+
+ /* Power down secure session if it exist*/
+ if (s_ctrl->is_secure)
+ msm_camera_tz_i2c_power_down(sensor_i2c_client);
+
+ return msm_camera_power_down(power_info, sensor_device_type,
+ sensor_i2c_client);
+}
+
+int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc;
+ struct msm_camera_power_ctrl_t *power_info;
+ struct msm_camera_i2c_client *sensor_i2c_client;
+ struct msm_camera_slave_info *slave_info;
+ const char *sensor_name;
+ uint32_t retry = 0;
+
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: %pK\n",
+ __func__, __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ if (s_ctrl->is_csid_tg_mode)
+ return 0;
+
+ power_info = &s_ctrl->sensordata->power_info;
+ sensor_i2c_client = s_ctrl->sensor_i2c_client;
+ slave_info = s_ctrl->sensordata->slave_info;
+ sensor_name = s_ctrl->sensordata->sensor_name;
+
+ if (!power_info || !sensor_i2c_client || !slave_info ||
+ !sensor_name) {
+ pr_err("%s:%d failed: %pK %pK %pK %pK\n",
+ __func__, __LINE__, power_info,
+ sensor_i2c_client, slave_info, sensor_name);
+ return -EINVAL;
+ }
+
+ if (s_ctrl->set_mclk_23880000)
+ msm_sensor_adjust_mclk(power_info);
+
+ CDBG("Sensor %d tagged as %s\n", s_ctrl->id,
+ (s_ctrl->is_secure)?"SECURE":"NON-SECURE");
+
+ for (retry = 0; retry < 3; retry++) {
+ if (s_ctrl->is_secure) {
+ rc = msm_camera_tz_i2c_power_up(sensor_i2c_client);
+ if (rc < 0) {
+#ifdef CONFIG_MSM_SEC_CCI_DEBUG
+ CDBG("Secure Sensor %d use cci\n", s_ctrl->id);
+ /* session is not secure */
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_cci_func_tbl;
+#else /* CONFIG_MSM_SEC_CCI_DEBUG */
+ return rc;
+#endif /* CONFIG_MSM_SEC_CCI_DEBUG */
+ } else {
+ /* session is secure */
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_secure_func_tbl;
+ }
+ }
+ rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type,
+ sensor_i2c_client);
+ if (rc < 0)
+ return rc;
+ rc = msm_sensor_check_id(s_ctrl);
+ if (rc < 0) {
+ msm_camera_power_down(power_info,
+ s_ctrl->sensor_device_type, sensor_i2c_client);
+ msleep(20);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static uint16_t msm_sensor_id_by_mask(struct msm_sensor_ctrl_t *s_ctrl,
+ uint16_t chipid)
+{
+ uint16_t sensor_id = chipid;
+ int16_t sensor_id_mask = s_ctrl->sensordata->slave_info->sensor_id_mask;
+
+ if (!sensor_id_mask)
+ sensor_id_mask = ~sensor_id_mask;
+
+ sensor_id &= sensor_id_mask;
+ sensor_id_mask &= -sensor_id_mask;
+ sensor_id_mask -= 1;
+ while (sensor_id_mask) {
+ sensor_id_mask >>= 1;
+ sensor_id >>= 1;
+ }
+ return sensor_id;
+}
+
+int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc = 0;
+ uint16_t chipid = 0;
+ struct msm_camera_i2c_client *sensor_i2c_client;
+ struct msm_camera_slave_info *slave_info;
+ const char *sensor_name;
+
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: %pK\n",
+ __func__, __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+ sensor_i2c_client = s_ctrl->sensor_i2c_client;
+ slave_info = s_ctrl->sensordata->slave_info;
+ sensor_name = s_ctrl->sensordata->sensor_name;
+
+ if (!sensor_i2c_client || !slave_info || !sensor_name) {
+ pr_err("%s:%d failed: %pK %pK %pK\n",
+ __func__, __LINE__, sensor_i2c_client, slave_info,
+ sensor_name);
+ return -EINVAL;
+ }
+
+ rc = sensor_i2c_client->i2c_func_tbl->i2c_read(
+ sensor_i2c_client, slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_WORD_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__, sensor_name);
+ return rc;
+ }
+
+ pr_debug("%s: read id: 0x%x expected id 0x%x:\n",
+ __func__, chipid, slave_info->sensor_id);
+ if (msm_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
+ pr_err("%s chip id %x does not match %x\n",
+ __func__, chipid, slave_info->sensor_id);
+ return -ENODEV;
+ }
+ return rc;
+}
+
+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)
+{
+ int32_t rc = 0;
+
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ if (s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) {
+ 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);
+ s_ctrl->stop_setting.reg_setting = NULL;
+
+ if (s_ctrl->func_tbl->sensor_power_down) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 0);
+
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__,
+ __LINE__, rc);
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ CDBG("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
+ pr_err("s_ctrl->func_tbl NULL\n");
+ }
+ }
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+}
+
+static int msm_sensor_get_af_status(struct msm_sensor_ctrl_t *s_ctrl,
+ void *argp)
+{
+ /* TO-DO: Need to set AF status register address and expected value
+ * We need to check the AF status in the sensor register and
+ * set the status in the *status variable accordingly
+ */
+ return 0;
+}
+
+static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+ void *argp = (void *)arg;
+
+ if (!s_ctrl) {
+ pr_err("%s s_ctrl NULL\n", __func__);
+ return -EBADF;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_CFG:
+#ifdef CONFIG_COMPAT
+ if (is_compat_task())
+ rc = s_ctrl->func_tbl->sensor_config32(s_ctrl, argp);
+ else
+#endif
+ rc = s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
+ return rc;
+ case VIDIOC_MSM_SENSOR_GET_AF_STATUS:
+ return msm_sensor_get_af_status(s_ctrl, argp);
+ case VIDIOC_MSM_SENSOR_RELEASE:
+ case MSM_SD_SHUTDOWN:
+ msm_sensor_stop_stream(s_ctrl);
+ return 0;
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_UNNOTIFY_FREEZE:
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_sensor_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_CFG32:
+ cmd = VIDIOC_MSM_SENSOR_CFG;
+ default:
+ return msm_sensor_subdev_ioctl(sd, cmd, arg);
+ }
+}
+
+long msm_sensor_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_sensor_subdev_do_ioctl);
+}
+
+static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
+ void *argp)
+{
+ struct sensorb_cfg_data32 *cdata = (struct sensorb_cfg_data32 *)argp;
+ int32_t 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];
+ cdata->cfg.sensor_info.subdev_intf[i] =
+ s_ctrl->sensordata->sensor_info->subdev_intf[i];
+ }
+ cdata->cfg.sensor_info.is_mount_angle_valid =
+ s_ctrl->sensordata->sensor_info->is_mount_angle_valid;
+ cdata->cfg.sensor_info.sensor_mount_angle =
+ s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ cdata->cfg.sensor_info.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_info.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
+ 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]);
+ CDBG("%s:%d subdev_intf[%d] %d\n", __func__, __LINE__,
+ i, cdata->cfg.sensor_info.subdev_intf[i]);
+ }
+ CDBG("%s:%d mount angle valid %d value %d\n", __func__,
+ __LINE__, cdata->cfg.sensor_info.is_mount_angle_valid,
+ cdata->cfg.sensor_info.sensor_mount_angle);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
+ cdata->cfg.sensor_init_params.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_init_params.sensor_mount_angle =
+ s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ 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_WRITE_I2C_ARRAY:
+ case CFG_WRITE_I2C_ARRAY_SYNC:
+ case CFG_WRITE_I2C_ARRAY_SYNC_BLOCK:
+ case CFG_WRITE_I2C_ARRAY_ASYNC: {
+ struct msm_camera_i2c_reg_setting32 conf_array32;
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&conf_array32,
+ (void __user *)compat_ptr(cdata->cfg.setting),
+ sizeof(struct msm_camera_i2c_reg_setting32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.addr_type = conf_array32.addr_type;
+ conf_array.data_type = conf_array32.data_type;
+ conf_array.delay = conf_array32.delay;
+ conf_array.size = conf_array32.size;
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_REG_DATA_MAX) {
+ 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) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting,
+ (void __user *)
+ compat_ptr(conf_array32.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;
+
+ if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY_ASYNC)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_async(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY_SYNC_BLOCK)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_sync_block(
+ s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_sync(s_ctrl->sensor_i2c_client,
+ &conf_array);
+
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_SLAVE_READ_I2C: {
+ struct msm_camera_i2c_read_config read_config;
+ struct msm_camera_i2c_read_config *read_config_ptr = NULL;
+ uint16_t local_data = 0;
+ uint16_t orig_slave_addr = 0, read_slave_addr = 0;
+ uint16_t orig_addr_type = 0, read_addr_type = 0;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ read_config_ptr =
+ (__force struct msm_camera_i2c_read_config *)
+ compat_ptr(cdata->cfg.setting);
+
+ if (copy_from_user(&read_config,
+ (void __user *) read_config_ptr,
+ sizeof(struct msm_camera_i2c_read_config))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ read_slave_addr = read_config.slave_addr;
+ read_addr_type = read_config.addr_type;
+
+ CDBG("%s:CFG_SLAVE_READ_I2C:", __func__);
+ CDBG("%s:slave_addr=0x%x reg_addr=0x%x, data_type=%d\n",
+ __func__, read_config.slave_addr,
+ read_config.reg_addr, read_config.data_type);
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ read_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ read_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x",
+ __func__, orig_slave_addr,
+ read_slave_addr >> 1);
+
+ orig_addr_type = s_ctrl->sensor_i2c_client->addr_type;
+ s_ctrl->sensor_i2c_client->addr_type = read_addr_type;
+
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ read_config.reg_addr,
+ &local_data, read_config.data_type);
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ orig_slave_addr;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ s_ctrl->sensor_i2c_client->client->addr =
+ orig_slave_addr;
+ }
+ s_ctrl->sensor_i2c_client->addr_type = orig_addr_type;
+
+ pr_debug("slave_read %x %x %x\n", read_slave_addr,
+ read_config.reg_addr, local_data);
+
+ if (rc < 0) {
+ pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
+ break;
+ }
+ if (copy_to_user((void __user *)(&read_config_ptr->data),
+ &local_data, sizeof(local_data))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case CFG_SLAVE_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_array_write_config32 write_config32;
+ struct msm_camera_i2c_array_write_config write_config;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ uint16_t orig_slave_addr = 0, write_slave_addr = 0;
+ uint16_t orig_addr_type = 0, write_addr_type = 0;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (copy_from_user(&write_config32,
+ (void __user *)compat_ptr(cdata->cfg.setting),
+ sizeof(
+ struct msm_camera_i2c_array_write_config32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ write_config.slave_addr = write_config32.slave_addr;
+ write_config.conf_array.addr_type =
+ write_config32.conf_array.addr_type;
+ write_config.conf_array.data_type =
+ write_config32.conf_array.data_type;
+ write_config.conf_array.delay =
+ write_config32.conf_array.delay;
+ write_config.conf_array.size =
+ write_config32.conf_array.size;
+
+ pr_debug("%s:CFG_SLAVE_WRITE_I2C_ARRAY:\n", __func__);
+ pr_debug("%s:slave_addr=0x%x, array_size=%d addr_type=%d data_type=%d\n",
+ __func__,
+ write_config.slave_addr,
+ write_config.conf_array.size,
+ write_config.conf_array.addr_type,
+ write_config.conf_array.data_type);
+
+ if (!write_config.conf_array.size ||
+ write_config.conf_array.size > I2C_REG_DATA_MAX) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(write_config.conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting,
+ (void __user *)
+ compat_ptr(
+ write_config32.conf_array.reg_setting),
+ write_config.conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ write_config.conf_array.reg_setting = reg_setting;
+ write_slave_addr = write_config.slave_addr;
+ write_addr_type = write_config.conf_array.addr_type;
+
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ write_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ write_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.",
+ __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ pr_debug("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x\n",
+ __func__, orig_slave_addr,
+ write_slave_addr >> 1);
+ orig_addr_type = s_ctrl->sensor_i2c_client->addr_type;
+ s_ctrl->sensor_i2c_client->addr_type = write_addr_type;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &(write_config.conf_array));
+
+ s_ctrl->sensor_i2c_client->addr_type = orig_addr_type;
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ orig_slave_addr;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ s_ctrl->sensor_i2c_client->client->addr =
+ orig_slave_addr;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.\n",
+ __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting32 conf_array32;
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&conf_array32,
+ (void __user *)compat_ptr(cdata->cfg.setting),
+ sizeof(struct msm_camera_i2c_seq_reg_setting32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.addr_type = conf_array32.addr_type;
+ conf_array.delay = conf_array32.delay;
+ conf_array.size = conf_array32.size;
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_SEQ_REG_DATA_MAX) {
+ 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 __user *)
+ compat_ptr(conf_array32.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->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_up) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 1);
+
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_UP;
+ CDBG("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
+ rc = -EFAULT;
+ }
+ break;
+ case CFG_POWER_DOWN:
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ kfree(s_ctrl->stop_setting.reg_setting);
+ s_ctrl->stop_setting.reg_setting = NULL;
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_down) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 0);
+
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ CDBG("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
+ rc = -EFAULT;
+ }
+ break;
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting32 stop_setting32;
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (copy_from_user(&stop_setting32,
+ (void __user *)compat_ptr((cdata->cfg.setting)),
+ sizeof(struct msm_camera_i2c_reg_setting32))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ stop_setting->addr_type = stop_setting32.addr_type;
+ stop_setting->data_type = stop_setting32.data_type;
+ stop_setting->delay = stop_setting32.delay;
+ stop_setting->size = stop_setting32.size;
+
+ if (!stop_setting->size) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void __user *)
+ compat_ptr(stop_setting32.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;
+ }
+
+ case CFG_SET_I2C_SYNC_PARAM: {
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ s_ctrl->sensor_i2c_client->cci_client->cid =
+ cdata->cfg.sensor_i2c_sync_params.cid;
+ s_ctrl->sensor_i2c_client->cci_client->id_map =
+ cdata->cfg.sensor_i2c_sync_params.csid;
+
+ CDBG("I2C_SYNC_PARAM CID:%d, line:%d delay:%d, cdid:%d\n",
+ s_ctrl->sensor_i2c_client->cci_client->cid,
+ cdata->cfg.sensor_i2c_sync_params.line,
+ cdata->cfg.sensor_i2c_sync_params.delay,
+ cdata->cfg.sensor_i2c_sync_params.csid);
+
+ cci_ctrl.cmd = MSM_CCI_SET_SYNC_CID;
+ cci_ctrl.cfg.cci_wait_sync_cfg.line =
+ cdata->cfg.sensor_i2c_sync_params.line;
+ cci_ctrl.cfg.cci_wait_sync_cfg.delay =
+ cdata->cfg.sensor_i2c_sync_params.delay;
+ cci_ctrl.cfg.cci_wait_sync_cfg.cid =
+ cdata->cfg.sensor_i2c_sync_params.cid;
+ cci_ctrl.cfg.cci_wait_sync_cfg.csid =
+ cdata->cfg.sensor_i2c_sync_params.csid;
+ rc = v4l2_subdev_call(s_ctrl->sensor_i2c_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);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+DONE:
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+#endif
+
+int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ int32_t 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];
+ cdata->cfg.sensor_info.subdev_intf[i] =
+ s_ctrl->sensordata->sensor_info->subdev_intf[i];
+ }
+ cdata->cfg.sensor_info.is_mount_angle_valid =
+ s_ctrl->sensordata->sensor_info->is_mount_angle_valid;
+ cdata->cfg.sensor_info.sensor_mount_angle =
+ s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ cdata->cfg.sensor_info.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_info.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
+ 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]);
+ CDBG("%s:%d subdev_intf[%d] %d\n", __func__, __LINE__,
+ i, cdata->cfg.sensor_info.subdev_intf[i]);
+ }
+ CDBG("%s:%d mount angle valid %d value %d\n", __func__,
+ __LINE__, cdata->cfg.sensor_info.is_mount_angle_valid,
+ cdata->cfg.sensor_info.sensor_mount_angle);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
+ cdata->cfg.sensor_init_params.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_init_params.sensor_mount_angle =
+ s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ 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_WRITE_I2C_ARRAY:
+ case CFG_WRITE_I2C_ARRAY_SYNC:
+ case CFG_WRITE_I2C_ARRAY_SYNC_BLOCK:
+ case CFG_WRITE_I2C_ARRAY_ASYNC: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&conf_array,
+ (void __user *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_REG_DATA_MAX) {
+ 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) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting,
+ (void __user *)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;
+ if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY_ASYNC)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_async(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY_SYNC_BLOCK)
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_sync_block(
+ s_ctrl->sensor_i2c_client,
+ &conf_array);
+ else
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_table_sync(s_ctrl->sensor_i2c_client,
+ &conf_array);
+
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_SLAVE_READ_I2C: {
+ struct msm_camera_i2c_read_config read_config;
+ struct msm_camera_i2c_read_config *read_config_ptr = NULL;
+ uint16_t local_data = 0;
+ uint16_t orig_slave_addr = 0, read_slave_addr = 0;
+ uint16_t orig_addr_type = 0, read_addr_type = 0;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ read_config_ptr =
+ (struct msm_camera_i2c_read_config *)cdata->cfg.setting;
+ if (copy_from_user(&read_config,
+ (void __user *)read_config_ptr,
+ sizeof(struct msm_camera_i2c_read_config))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ read_slave_addr = read_config.slave_addr;
+ read_addr_type = read_config.addr_type;
+ CDBG("%s:CFG_SLAVE_READ_I2C:", __func__);
+ CDBG("%s:slave_addr=0x%x reg_addr=0x%x, data_type=%d\n",
+ __func__, read_config.slave_addr,
+ read_config.reg_addr, read_config.data_type);
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ read_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ read_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x",
+ __func__, orig_slave_addr,
+ read_slave_addr >> 1);
+
+ orig_addr_type = s_ctrl->sensor_i2c_client->addr_type;
+ s_ctrl->sensor_i2c_client->addr_type = read_addr_type;
+
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ read_config.reg_addr,
+ &local_data, read_config.data_type);
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ orig_slave_addr;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ s_ctrl->sensor_i2c_client->client->addr =
+ orig_slave_addr;
+ }
+ s_ctrl->sensor_i2c_client->addr_type = orig_addr_type;
+
+ if (rc < 0) {
+ pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
+ break;
+ }
+ if (copy_to_user((void __user *)(&read_config_ptr->data),
+ &local_data, sizeof(local_data))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case CFG_SLAVE_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_array_write_config write_config;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ uint16_t orig_slave_addr = 0, write_slave_addr = 0;
+ uint16_t orig_addr_type = 0, write_addr_type = 0;
+
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (copy_from_user(&write_config,
+ (void __user *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_array_write_config))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:CFG_SLAVE_WRITE_I2C_ARRAY:", __func__);
+ CDBG("%s:slave_addr=0x%x, array_size=%d\n", __func__,
+ write_config.slave_addr,
+ write_config.conf_array.size);
+
+ if (!write_config.conf_array.size ||
+ write_config.conf_array.size > I2C_REG_DATA_MAX) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(write_config.conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting,
+ (void __user *)
+ (write_config.conf_array.reg_setting),
+ write_config.conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ write_config.conf_array.reg_setting = reg_setting;
+ write_slave_addr = write_config.slave_addr;
+ write_addr_type = write_config.conf_array.addr_type;
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->cci_client->sid;
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ write_slave_addr >> 1;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ orig_slave_addr =
+ s_ctrl->sensor_i2c_client->client->addr;
+ s_ctrl->sensor_i2c_client->client->addr =
+ write_slave_addr >> 1;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ CDBG("%s:orig_slave_addr=0x%x, new_slave_addr=0x%x",
+ __func__, orig_slave_addr,
+ write_slave_addr >> 1);
+ orig_addr_type = s_ctrl->sensor_i2c_client->addr_type;
+ s_ctrl->sensor_i2c_client->addr_type = write_addr_type;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &(write_config.conf_array));
+ s_ctrl->sensor_i2c_client->addr_type = orig_addr_type;
+ if (s_ctrl->sensor_i2c_client->cci_client) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ orig_slave_addr;
+ } else if (s_ctrl->sensor_i2c_client->client) {
+ s_ctrl->sensor_i2c_client->client->addr =
+ orig_slave_addr;
+ } else {
+ pr_err("%s: error: no i2c/cci client found.", __func__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+ 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 (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&conf_array,
+ (void __user *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_SEQ_REG_DATA_MAX) {
+ 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 __user *)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->is_csid_tg_mode)
+ goto DONE;
+
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_up) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 1);
+
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_UP;
+ CDBG("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } else {
+ rc = -EFAULT;
+ }
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ kfree(s_ctrl->stop_setting.reg_setting);
+ s_ctrl->stop_setting.reg_setting = NULL;
+ if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) {
+ pr_err("%s:%d failed: invalid state %d\n", __func__,
+ __LINE__, s_ctrl->sensor_state);
+ rc = -EFAULT;
+ break;
+ }
+ if (s_ctrl->func_tbl->sensor_power_down) {
+ if (s_ctrl->sensordata->misc_regulator)
+ msm_sensor_misc_regulator(s_ctrl, 0);
+
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__,
+ __LINE__, rc);
+ break;
+ }
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ CDBG("%s:%d sensor state %d\n", __func__, __LINE__,
+ s_ctrl->sensor_state);
+ } 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 (s_ctrl->is_csid_tg_mode)
+ goto DONE;
+
+ if (copy_from_user(stop_setting,
+ (void __user *)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;
+
+ if (!stop_setting->size) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void __user *)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;
+ }
+
+ case CFG_SET_I2C_SYNC_PARAM: {
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ s_ctrl->sensor_i2c_client->cci_client->cid =
+ cdata->cfg.sensor_i2c_sync_params.cid;
+ s_ctrl->sensor_i2c_client->cci_client->id_map =
+ cdata->cfg.sensor_i2c_sync_params.csid;
+
+ CDBG("I2C_SYNC_PARAM CID:%d, line:%d delay:%d, cdid:%d\n",
+ s_ctrl->sensor_i2c_client->cci_client->cid,
+ cdata->cfg.sensor_i2c_sync_params.line,
+ cdata->cfg.sensor_i2c_sync_params.delay,
+ cdata->cfg.sensor_i2c_sync_params.csid);
+
+ cci_ctrl.cmd = MSM_CCI_SET_SYNC_CID;
+ cci_ctrl.cfg.cci_wait_sync_cfg.line =
+ cdata->cfg.sensor_i2c_sync_params.line;
+ cci_ctrl.cfg.cci_wait_sync_cfg.delay =
+ cdata->cfg.sensor_i2c_sync_params.delay;
+ cci_ctrl.cfg.cci_wait_sync_cfg.cid =
+ cdata->cfg.sensor_i2c_sync_params.cid;
+ cci_ctrl.cfg.cci_wait_sync_cfg.csid =
+ cdata->cfg.sensor_i2c_sync_params.csid;
+ rc = v4l2_subdev_call(s_ctrl->sensor_i2c_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);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+DONE:
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+int msm_sensor_check_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int rc;
+
+ 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);
+ return rc;
+}
+
+static int 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 && s_ctrl->sensor_state == MSM_SENSOR_POWER_UP) {
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ s_ctrl->sensor_state = MSM_SENSOR_POWER_DOWN;
+ }
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+ return rc;
+}
+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_ops msm_sensor_subdev_ops = {
+ .core = &msm_sensor_subdev_core_ops,
+};
+
+static struct msm_sensor_fn_t msm_sensor_func_tbl = {
+ .sensor_config = msm_sensor_config,
+#ifdef CONFIG_COMPAT
+ .sensor_config32 = msm_sensor_config32,
+#endif
+ .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,
+ .i2c_write_conf_tbl = msm_camera_cci_i2c_write_conf_tbl,
+ .i2c_write_table_async = msm_camera_cci_i2c_write_table_async,
+ .i2c_write_table_sync = msm_camera_cci_i2c_write_table_sync,
+ .i2c_write_table_sync_block = msm_camera_cci_i2c_write_table_sync_block,
+
+};
+
+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,
+ .i2c_write_conf_tbl = msm_camera_qup_i2c_write_conf_tbl,
+ .i2c_write_table_async = msm_camera_qup_i2c_write_table,
+ .i2c_write_table_sync = msm_camera_qup_i2c_write_table,
+ .i2c_write_table_sync_block = msm_camera_qup_i2c_write_table,
+};
+
+static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl = {
+ .i2c_read = msm_camera_tz_i2c_read,
+ .i2c_read_seq = msm_camera_tz_i2c_read_seq,
+ .i2c_write = msm_camera_tz_i2c_write,
+ .i2c_write_table = msm_camera_tz_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_tz_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_tz_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_tz_i2c_util,
+ .i2c_write_conf_tbl = msm_camera_tz_i2c_write_conf_tbl,
+ .i2c_write_table_async = msm_camera_tz_i2c_write_table_async,
+ .i2c_write_table_sync = msm_camera_tz_i2c_write_table_sync,
+ .i2c_write_table_sync_block = msm_camera_tz_i2c_write_table_sync_block,
+};
+
+int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ struct msm_camera_cci_client *cci_client = NULL;
+ unsigned long mount_pos = 0;
+
+ /* Validate input parameters */
+ if (!s_ctrl) {
+ pr_err("%s:%d failed: invalid params s_ctrl %pK\n", __func__,
+ __LINE__, s_ctrl);
+ return -EINVAL;
+ }
+
+ if (!s_ctrl->sensor_i2c_client) {
+ pr_err("%s:%d failed: invalid params sensor_i2c_client %pK\n",
+ __func__, __LINE__, s_ctrl->sensor_i2c_client);
+ return -EINVAL;
+ }
+
+ /* Initialize cci_client */
+ s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!s_ctrl->sensor_i2c_client->cci_client)
+ return -ENOMEM;
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = s_ctrl->sensor_i2c_client->cci_client;
+
+ /* Get CCI subdev */
+ cci_client->cci_subdev = msm_cci_get_subdev();
+
+ if (s_ctrl->is_secure)
+ msm_camera_tz_i2c_register_sensor((void *)s_ctrl);
+
+ /* Update CCI / I2C function table */
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_cci_func_tbl;
+ } else {
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl) {
+ CDBG("%s:%d\n", __func__, __LINE__);
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_qup_func_tbl;
+ }
+ }
+
+ /* Update function table driven by ioctl */
+ if (!s_ctrl->func_tbl)
+ s_ctrl->func_tbl = &msm_sensor_func_tbl;
+
+ /* Update v4l2 subdev ops table */
+ if (!s_ctrl->sensor_v4l2_subdev_ops)
+ s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
+
+ /* Update sensor mount angle and position in media entity flag */
+ mount_pos = s_ctrl->sensordata->sensor_info->position << 16;
+ mount_pos = mount_pos | ((s_ctrl->sensordata->sensor_info->
+ sensor_mount_angle / 90) << 8);
+ s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT;
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
new file mode 100644
index 0000000..753e85a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
@@ -0,0 +1,127 @@
+/* Copyright (c) 2011-2016, 2018, 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/clk.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 <soc/qcom/camera2.h>
+#include <media/msm_cam_sensor.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_dt_util.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_sensor_sensor_slave_info_type {
+ MSM_SENSOR_SLAVEADDR_DATA,
+ MSM_SENSOR_IDREGADDR_DATA,
+ MSM_SENSOR_SENSOR_ID_DATA,
+ MSM_SENSOR_SENIDMASK_DATA,
+ MSM_SENSOR_NUM_ID_INFO_DATA,
+};
+
+struct msm_sensor_ctrl_t;
+
+enum msm_sensor_state_t {
+ MSM_SENSOR_POWER_DOWN,
+ MSM_SENSOR_POWER_UP,
+};
+
+struct msm_sensor_fn_t {
+ int (*sensor_config)(struct msm_sensor_ctrl_t *, void *);
+#ifdef CONFIG_COMPAT
+ int (*sensor_config32)(struct msm_sensor_ctrl_t *, void *);
+#endif
+ int (*sensor_power_down)(struct msm_sensor_ctrl_t *);
+ int (*sensor_power_up)(struct msm_sensor_ctrl_t *);
+ int (*sensor_match_id)(struct msm_sensor_ctrl_t *);
+};
+
+struct msm_sensor_ctrl_t {
+ struct platform_device *pdev;
+ struct mutex *msm_sensor_mutex;
+
+ enum msm_camera_device_type_t sensor_device_type;
+ struct msm_camera_sensor_board_info *sensordata;
+ struct msm_sensor_power_setting_array power_setting_array;
+ struct msm_sensor_packed_cfg_t *cfg_override;
+ struct msm_sd_subdev msm_sd;
+ enum cci_i2c_master_t cci_i2c_master;
+
+ struct msm_camera_i2c_client *sensor_i2c_client;
+ 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;
+ void *misc_regulator;
+ enum msm_sensor_state_t sensor_state;
+ uint8_t is_probe_succeed;
+ uint32_t id;
+ struct device_node *of_node;
+ enum msm_camera_stream_type_t camera_stream_type;
+ uint32_t set_mclk_23880000;
+ uint8_t is_csid_tg_mode;
+ uint32_t is_secure;
+ uint8_t bypass_video_node_creation;
+};
+
+int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp);
+
+int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl);
+
+int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl);
+
+int msm_sensor_check_id(struct msm_sensor_ctrl_t *s_ctrl);
+
+int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
+
+int msm_sensor_update_cfg(struct msm_sensor_ctrl_t *s_ctrl);
+
+int msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl);
+
+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 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 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);
+#ifdef CONFIG_COMPAT
+long msm_sensor_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+#endif
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
new file mode 100644
index 0000000..80149a9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -0,0 +1,1501 @@
+/* Copyright (c) 2013-2018, 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 SENSOR_DRIVER_I2C "i2c_camera"
+/* Header file declaration */
+#include "msm_sensor.h"
+#include "msm_sd.h"
+#include "camera.h"
+#include "msm_cci.h"
+#include "msm_camera_dt_util.h"
+#include "msm_sensor_driver.h"
+
+/* Logging macro */
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define SENSOR_MAX_MOUNTANGLE (360)
+
+static struct v4l2_file_operations msm_sensor_v4l2_subdev_fops;
+static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev);
+
+/* Static declaration */
+static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];
+
+static int msm_sensor_platform_remove(struct platform_device *pdev)
+{
+ struct msm_sensor_ctrl_t *s_ctrl;
+
+ pr_err("%s: sensor FREE\n", __func__);
+
+ s_ctrl = g_sctrl[pdev->id];
+ if (!s_ctrl) {
+ pr_err("%s: sensor device is NULL\n", __func__);
+ return 0;
+ }
+
+ msm_sensor_free_sensor_data(s_ctrl);
+ kfree(s_ctrl->msm_sensor_mutex);
+ kfree(s_ctrl->sensor_i2c_client);
+ kfree(s_ctrl);
+ g_sctrl[pdev->id] = NULL;
+
+ return 0;
+}
+
+
+static const struct of_device_id msm_sensor_driver_dt_match[] = {
+ {.compatible = "qcom,camera"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_sensor_driver_dt_match);
+
+static struct platform_driver msm_sensor_platform_driver = {
+ .probe = msm_sensor_driver_platform_probe,
+ .driver = {
+ .name = "qcom,camera",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_sensor_driver_dt_match,
+ },
+ .remove = msm_sensor_platform_remove,
+};
+
+static struct v4l2_subdev_info msm_sensor_driver_subdev_info[] = {
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static int32_t msm_sensor_driver_create_i2c_v4l_subdev
+ (struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t session_id = 0;
+ struct i2c_client *client = s_ctrl->sensor_i2c_client->client;
+
+ CDBG("%s %s I2c probe succeeded\n", __func__, client->name);
+ if (s_ctrl->bypass_video_node_creation == 0) {
+ rc = camera_init_v4l2(&client->dev, &session_id);
+ if (rc < 0) {
+ pr_err("failed: camera_init_i2c_v4l2 rc %d", rc);
+ return rc;
+ }
+ }
+
+ CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
+ snprintf(s_ctrl->msm_sd.sd.name,
+ sizeof(s_ctrl->msm_sd.sd.name), "%s",
+ s_ctrl->sensordata->sensor_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_pads_init(&s_ctrl->msm_sd.sd.entity, 0, NULL);
+ s_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+ s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
+ rc = msm_sd_register(&s_ctrl->msm_sd);
+ if (rc < 0) {
+ pr_err("failed: msm_sd_register rc %d", rc);
+ return rc;
+ }
+ msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_sensor_v4l2_subdev_fops.compat_ioctl32 =
+ msm_sensor_subdev_fops_ioctl;
+#endif
+ s_ctrl->msm_sd.sd.devnode->fops =
+ &msm_sensor_v4l2_subdev_fops;
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+static int32_t msm_sensor_driver_create_v4l_subdev
+ (struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t session_id = 0;
+
+ if (s_ctrl->bypass_video_node_creation == 0) {
+ rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
+ if (rc < 0) {
+ pr_err("failed: camera_init_v4l2 rc %d", rc);
+ return rc;
+ }
+ }
+
+ CDBG("rc %d session_id %d", rc, session_id);
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+
+ /* Create /dev/v4l-subdevX device */
+ 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, s_ctrl->pdev);
+ s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&s_ctrl->msm_sd.sd.entity, 0, NULL);
+ s_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
+ s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
+ rc = msm_sd_register(&s_ctrl->msm_sd);
+ if (rc < 0) {
+ pr_err("failed: msm_sd_register rc %d", rc);
+ return rc;
+ }
+ msm_cam_copy_v4l2_subdev_fops(&msm_sensor_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_sensor_v4l2_subdev_fops.compat_ioctl32 =
+ msm_sensor_subdev_fops_ioctl;
+#endif
+ s_ctrl->msm_sd.sd.devnode->fops =
+ &msm_sensor_v4l2_subdev_fops;
+
+ return rc;
+}
+
+static int32_t msm_sensor_fill_eeprom_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ const char *eeprom_name;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0, eeprom_name_len;
+ int32_t *eeprom_subdev_id, i, userspace_probe = 0;
+ int32_t count = 0;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+ const void *p;
+
+ if (!s_ctrl->sensordata->eeprom_name || !of_node)
+ return -EINVAL;
+
+ eeprom_name_len = strlen(s_ctrl->sensordata->eeprom_name);
+ if (eeprom_name_len >= MAX_SENSOR_NAME)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ eeprom_subdev_id = &sensor_info->subdev_id[SUB_MODULE_EEPROM];
+ /*
+ * string for eeprom name is valid, set sudev id to -1
+ * and try to found new id
+ */
+ *eeprom_subdev_id = -1;
+
+ if (eeprom_name_len == 0)
+ return 0;
+
+ p = of_get_property(of_node, "qcom,eeprom-src", &count);
+ if (!p || !count)
+ return 0;
+
+ count /= sizeof(uint32_t);
+ for (i = 0; i < count; i++) {
+ userspace_probe = 0;
+ eeprom_name = NULL;
+ src_node = of_parse_phandle(of_node, "qcom,eeprom-src", i);
+ if (!src_node) {
+ pr_err("eeprom src node NULL\n");
+ continue;
+ }
+ /* In the case of eeprom probe from kernel eeprom name
+ * should be present, Otherwise it will throw as errors
+ */
+ rc = of_property_read_string(src_node, "qcom,eeprom-name",
+ &eeprom_name);
+ if (rc < 0) {
+ pr_err("%s:%d Eeprom userspace probe for %s\n",
+ __func__, __LINE__,
+ s_ctrl->sensordata->eeprom_name);
+ of_node_put(src_node);
+ userspace_probe = 1;
+ if (count > 1)
+ return -EINVAL;
+ }
+ if (!userspace_probe &&
+ strcmp(eeprom_name, s_ctrl->sensordata->eeprom_name))
+ continue;
+
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ if (rc < 0) {
+ pr_err("%s qcom,eeprom cell index %d, rc %d\n",
+ __func__, val, rc);
+ of_node_put(src_node);
+ if (userspace_probe)
+ return -EINVAL;
+ continue;
+ }
+
+ *eeprom_subdev_id = val;
+ CDBG("%s:%d Eeprom subdevice id is %d\n",
+ __func__, __LINE__, val);
+ of_node_put(src_node);
+ src_node = NULL;
+ break;
+ }
+
+ return rc;
+}
+
+static int32_t msm_sensor_fill_actuator_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0, actuator_name_len;
+ int32_t *actuator_subdev_id;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+
+ if (!s_ctrl->sensordata->actuator_name || !of_node)
+ return -EINVAL;
+
+ actuator_name_len = strlen(s_ctrl->sensordata->actuator_name);
+ if (actuator_name_len >= MAX_SENSOR_NAME)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ actuator_subdev_id = &sensor_info->subdev_id[SUB_MODULE_ACTUATOR];
+ /*
+ * string for actuator name is valid, set sudev id to -1
+ * and try to found new id
+ */
+ *actuator_subdev_id = -1;
+
+ if (actuator_name_len == 0)
+ return 0;
+
+ src_node = of_parse_phandle(of_node, "qcom,actuator-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,actuator cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *actuator_subdev_id = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_sensor_fill_laser_led_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0;
+ int32_t *laser_led_subdev_id;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+
+ if (!of_node)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ laser_led_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LASER_LED];
+ /* set sudev id to -1 and try to found new id */
+ *laser_led_subdev_id = -1;
+
+
+ src_node = of_parse_phandle(of_node, "qcom,laserled-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,laser led cell index %d, rc %d\n", __func__,
+ val, rc);
+ of_node_put(src_node);
+ src_node = NULL;
+ if (rc < 0) {
+ pr_err("%s cell index not found %d\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ *laser_led_subdev_id = val;
+ }
+
+ return rc;
+}
+
+static int32_t msm_sensor_fill_flash_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0, flash_name_len;
+ int32_t *flash_subdev_id;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+
+ if (!of_node || !s_ctrl->sensordata->flash_name)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ flash_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LED_FLASH];
+
+ *flash_subdev_id = -1;
+
+ flash_name_len = strlen(s_ctrl->sensordata->flash_name);
+ if (flash_name_len >= MAX_SENSOR_NAME)
+ return -EINVAL;
+
+ if (flash_name_len == 0)
+ return 0;
+
+ src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,flash cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *flash_subdev_id = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+ return rc;
+}
+
+static int32_t msm_sensor_fill_ois_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0, ois_name_len;
+ int32_t *ois_subdev_id;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+
+ if (!s_ctrl->sensordata->ois_name || !of_node)
+ return -EINVAL;
+
+ ois_name_len = strlen(s_ctrl->sensordata->ois_name);
+ if (ois_name_len >= MAX_SENSOR_NAME)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ ois_subdev_id = &sensor_info->subdev_id[SUB_MODULE_OIS];
+ /*
+ * string for ois name is valid, set sudev id to -1
+ * and try to found new id
+ */
+ *ois_subdev_id = -1;
+
+ if (ois_name_len == 0)
+ return 0;
+
+ src_node = of_parse_phandle(of_node, "qcom,ois-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ois cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *ois_subdev_id = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ return rc;
+}
+
+static int32_t msm_sensor_fill_slave_info_init_params(
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_sensor_info_t *sensor_info)
+{
+ struct msm_sensor_init_params *sensor_init_params;
+
+ if (!slave_info || !sensor_info)
+ return -EINVAL;
+
+ sensor_init_params = &slave_info->sensor_init_params;
+ if (sensor_init_params->position != INVALID_CAMERA_B)
+ sensor_info->position =
+ sensor_init_params->position;
+
+ if (sensor_init_params->sensor_mount_angle < SENSOR_MAX_MOUNTANGLE) {
+ sensor_info->sensor_mount_angle =
+ sensor_init_params->sensor_mount_angle;
+ sensor_info->is_mount_angle_valid = 1;
+ }
+
+ if (sensor_init_params->modes_supported != CAMERA_MODE_INVALID)
+ sensor_info->modes_supported =
+ sensor_init_params->modes_supported;
+
+ return 0;
+}
+
+
+static int32_t msm_sensor_validate_slave_info(
+ struct msm_sensor_info_t *sensor_info)
+{
+ if (sensor_info->position == INVALID_CAMERA_B) {
+ sensor_info->position = BACK_CAMERA_B;
+ CDBG("%s:%d Set default sensor position\n",
+ __func__, __LINE__);
+ }
+ if (sensor_info->modes_supported == CAMERA_MODE_INVALID) {
+ sensor_info->modes_supported = CAMERA_MODE_2D_B;
+ CDBG("%s:%d Set default sensor modes_supported\n",
+ __func__, __LINE__);
+ }
+ if (sensor_info->sensor_mount_angle >= SENSOR_MAX_MOUNTANGLE) {
+ sensor_info->sensor_mount_angle = 0;
+ CDBG("%s:%d Set default sensor mount angle\n",
+ __func__, __LINE__);
+ sensor_info->is_mount_angle_valid = 1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static int32_t msm_sensor_get_pw_settings_compat(
+ struct msm_sensor_power_setting *ps,
+ struct msm_sensor_power_setting *us_ps, uint32_t size)
+{
+ int32_t rc = 0, i = 0;
+ struct msm_sensor_power_setting32 *ps32 =
+ kzalloc(sizeof(*ps32) * size, GFP_KERNEL);
+
+ if (!ps32) {
+ pr_err("failed: no memory ps32");
+ return -ENOMEM;
+ }
+ if (copy_from_user(ps32, (void __user *)us_ps, sizeof(*ps32) * size)) {
+ pr_err("failed: copy_from_user");
+ kfree(ps32);
+ return -EFAULT;
+ }
+ for (i = 0; i < size; i++) {
+ ps[i].config_val = ps32[i].config_val;
+ ps[i].delay = ps32[i].delay;
+ ps[i].seq_type = ps32[i].seq_type;
+ ps[i].seq_val = ps32[i].seq_val;
+ }
+ kfree(ps32);
+ return rc;
+}
+#endif
+
+static int32_t msm_sensor_create_pd_settings(void *setting,
+ struct msm_sensor_power_setting *pd, uint32_t size_down,
+ struct msm_sensor_power_setting *pu)
+{
+ int32_t rc = 0;
+ int c, end;
+ struct msm_sensor_power_setting pd_tmp;
+
+ pr_err("Generating power_down_setting");
+
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ rc = msm_sensor_get_pw_settings_compat(
+ pd, pu, size_down);
+ if (rc < 0) {
+ pr_err("failed");
+ return -EFAULT;
+ }
+ } else
+#endif
+ {
+ if (copy_from_user(
+ pd, (void __user *)pu, sizeof(*pd) * size_down)) {
+ pr_err("failed: copy_from_user");
+ return -EFAULT;
+ }
+ }
+ /* reverse */
+ end = size_down - 1;
+ for (c = 0; c < size_down/2; c++) {
+ pd_tmp = pd[c];
+ pd[c] = pd[end];
+ pd[end] = pd_tmp;
+ end--;
+ }
+ return rc;
+}
+
+static int32_t msm_sensor_get_power_down_settings(void *setting,
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_camera_power_ctrl_t *power_info)
+{
+ int32_t rc = 0;
+ uint16_t size_down = 0;
+ uint16_t i = 0;
+ struct msm_sensor_power_setting *pd = NULL;
+
+ /* DOWN */
+ size_down = slave_info->power_setting_array.size_down;
+ if (!size_down || size_down > MAX_POWER_CONFIG)
+ size_down = slave_info->power_setting_array.size;
+ /* Validate size_down */
+ if (size_down > MAX_POWER_CONFIG) {
+ pr_err("failed: invalid size_down %d", size_down);
+ return -EINVAL;
+ }
+ /* Allocate memory for power down setting */
+ pd = kcalloc(size_down, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -EFAULT;
+
+ if (slave_info->power_setting_array.power_down_setting) {
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ rc = msm_sensor_get_pw_settings_compat(
+ pd, slave_info->power_setting_array.
+ power_down_setting, size_down);
+ if (rc < 0) {
+ pr_err("failed");
+ kfree(pd);
+ return -EFAULT;
+ }
+ } else
+#endif
+ if (copy_from_user(
+ pd, (void __user *)slave_info->power_setting_array.
+ power_down_setting, sizeof(*pd) * size_down)) {
+ pr_err("failed: copy_from_user");
+ kfree(pd);
+ return -EFAULT;
+ }
+ } else {
+
+ rc = msm_sensor_create_pd_settings(setting, pd, size_down,
+ slave_info->power_setting_array.power_setting);
+ if (rc < 0) {
+ pr_err("failed");
+ kfree(pd);
+ return -EFAULT;
+ }
+ }
+
+ /* Fill power down setting and power down setting size */
+ power_info->power_down_setting = pd;
+ power_info->power_down_setting_size = size_down;
+
+ /* Print power setting */
+ for (i = 0; i < size_down; i++) {
+ CDBG("DOWN seq_type %d seq_val %d config_val %ld delay %d",
+ pd[i].seq_type, pd[i].seq_val,
+ pd[i].config_val, pd[i].delay);
+ }
+ return rc;
+}
+
+static int32_t msm_sensor_get_power_up_settings(void *setting,
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_camera_power_ctrl_t *power_info)
+{
+ int32_t rc = 0;
+ uint16_t size = 0;
+ uint16_t i = 0;
+ struct msm_sensor_power_setting *pu = NULL;
+
+ size = slave_info->power_setting_array.size;
+
+ /* Validate size */
+ if ((size == 0) || (size > MAX_POWER_CONFIG)) {
+ pr_err("failed: invalid power_setting size_up = %d\n", size);
+ return -EINVAL;
+ }
+
+ /* Allocate memory for power up setting */
+ pu = kcalloc(size, sizeof(*pu), GFP_KERNEL);
+ if (!pu)
+ return -ENOMEM;
+
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ rc = msm_sensor_get_pw_settings_compat(pu,
+ slave_info->power_setting_array.
+ power_setting, size);
+ if (rc < 0) {
+ pr_err("failed");
+ kfree(pu);
+ return -EFAULT;
+ }
+ } else
+#endif
+ {
+ if (copy_from_user(pu,
+ (void __user *)
+ slave_info->power_setting_array.power_setting,
+ sizeof(*pu) * size)) {
+ pr_err("failed: copy_from_user");
+ kfree(pu);
+ return -EFAULT;
+ }
+ }
+
+ /* Print power setting */
+ for (i = 0; i < size; i++) {
+ CDBG("UP seq_type %d seq_val %d config_val %ld delay %d",
+ pu[i].seq_type, pu[i].seq_val,
+ pu[i].config_val, pu[i].delay);
+ }
+
+
+ /* Fill power up setting and power up setting size */
+ power_info->power_setting = pu;
+ power_info->power_setting_size = size;
+
+ return rc;
+}
+
+static int32_t msm_sensor_get_power_settings(void *setting,
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_camera_power_ctrl_t *power_info)
+{
+ int32_t rc = 0;
+
+ rc = msm_sensor_get_power_up_settings(setting, slave_info, power_info);
+ if (rc < 0) {
+ pr_err("failed");
+ return -EINVAL;
+ }
+
+ rc = msm_sensor_get_power_down_settings(setting, slave_info,
+ power_info);
+ if (rc < 0) {
+ pr_err("failed");
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static void msm_sensor_fill_sensor_info(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_info_t *sensor_info, char *entity_name)
+{
+ uint32_t i;
+
+ if (!s_ctrl || !sensor_info) {
+ pr_err("%s:failed\n", __func__);
+ return;
+ }
+
+ strlcpy(sensor_info->sensor_name, s_ctrl->sensordata->sensor_name,
+ MAX_SENSOR_NAME);
+
+ sensor_info->session_id = s_ctrl->sensordata->sensor_info->session_id;
+
+ s_ctrl->sensordata->sensor_info->subdev_id[SUB_MODULE_SENSOR] =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++) {
+ sensor_info->subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ sensor_info->subdev_intf[i] =
+ s_ctrl->sensordata->sensor_info->subdev_intf[i];
+ }
+
+ sensor_info->is_mount_angle_valid =
+ s_ctrl->sensordata->sensor_info->is_mount_angle_valid;
+ sensor_info->sensor_mount_angle =
+ s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ sensor_info->modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
+ sensor_info->position =
+ s_ctrl->sensordata->sensor_info->position;
+
+ strlcpy(entity_name, s_ctrl->msm_sd.sd.entity.name, MAX_SENSOR_NAME);
+}
+
+/* static function definition */
+static int32_t msm_sensor_driver_is_special_support(
+ struct msm_sensor_ctrl_t *s_ctrl,
+ char *sensor_name)
+{
+ int32_t rc = 0, i = 0;
+ struct msm_camera_sensor_board_info *sensordata = s_ctrl->sensordata;
+
+ for (i = 0; i < sensordata->special_support_size; i++) {
+ if (!strcmp(sensordata->special_support_sensors[i],
+ sensor_name)) {
+ rc = TRUE;
+ break;
+ }
+ }
+ return rc;
+}
+
+/* static function definition */
+int32_t msm_sensor_driver_probe(void *setting,
+ struct msm_sensor_info_t *probed_info, char *entity_name)
+{
+ int32_t rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl = NULL;
+ struct msm_camera_cci_client *cci_client = NULL;
+ struct msm_camera_sensor_slave_info *slave_info = NULL;
+ struct msm_camera_slave_info *camera_info = NULL;
+
+ unsigned long mount_pos = 0;
+ uint32_t is_yuv;
+
+ /* Validate input parameters */
+ if (!setting) {
+ pr_err("failed: slave_info %pK", setting);
+ return -EINVAL;
+ }
+
+ /* Allocate memory for slave info */
+ slave_info = kzalloc(sizeof(*slave_info), GFP_KERNEL);
+ if (!slave_info)
+ return -ENOMEM;
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ struct msm_camera_sensor_slave_info32 *slave_info32 =
+ kzalloc(sizeof(*slave_info32), GFP_KERNEL);
+ if (!slave_info32) {
+ pr_err("failed: no memory for slave_info32 %pK\n",
+ slave_info32);
+ rc = -ENOMEM;
+ goto free_slave_info;
+ }
+ if (copy_from_user(slave_info32, (void __user *)setting,
+ sizeof(*slave_info32))) {
+ pr_err("failed: copy_from_user");
+ rc = -EFAULT;
+ kfree(slave_info32);
+ goto free_slave_info;
+ }
+
+ strlcpy(slave_info->actuator_name, slave_info32->actuator_name,
+ sizeof(slave_info->actuator_name));
+
+ strlcpy(slave_info->eeprom_name, slave_info32->eeprom_name,
+ sizeof(slave_info->eeprom_name));
+
+ strlcpy(slave_info->sensor_name, slave_info32->sensor_name,
+ sizeof(slave_info->sensor_name));
+
+ strlcpy(slave_info->ois_name, slave_info32->ois_name,
+ sizeof(slave_info->ois_name));
+
+ strlcpy(slave_info->flash_name, slave_info32->flash_name,
+ sizeof(slave_info->flash_name));
+
+ slave_info->addr_type = slave_info32->addr_type;
+ slave_info->camera_id = slave_info32->camera_id;
+
+ slave_info->i2c_freq_mode = slave_info32->i2c_freq_mode;
+ slave_info->sensor_id_info = slave_info32->sensor_id_info;
+
+ slave_info->slave_addr = slave_info32->slave_addr;
+ slave_info->power_setting_array.size =
+ slave_info32->power_setting_array.size;
+ slave_info->power_setting_array.size_down =
+ slave_info32->power_setting_array.size_down;
+ slave_info->power_setting_array.size_down =
+ slave_info32->power_setting_array.size_down;
+
+ slave_info->power_setting_array.power_down_setting =
+ (__force struct msm_sensor_power_setting *)
+ (compat_ptr(slave_info32->
+ power_setting_array.power_down_setting));
+
+ slave_info->power_setting_array.power_setting =
+ (__force struct msm_sensor_power_setting *)
+ (compat_ptr(slave_info32->
+ power_setting_array.power_setting));
+
+ slave_info->sensor_init_params =
+ slave_info32->sensor_init_params;
+ slave_info->output_format =
+ slave_info32->output_format;
+ slave_info->bypass_video_node_creation =
+ !!slave_info32->bypass_video_node_creation;
+ kfree(slave_info32);
+ } else
+#endif
+ {
+ if (copy_from_user(slave_info,
+ (void __user *)setting, sizeof(*slave_info))) {
+ pr_err("failed: copy_from_user");
+ rc = -EFAULT;
+ goto free_slave_info;
+ }
+ }
+
+ if (strlen(slave_info->sensor_name) >= MAX_SENSOR_NAME ||
+ strlen(slave_info->eeprom_name) >= MAX_SENSOR_NAME ||
+ strlen(slave_info->actuator_name) >= MAX_SENSOR_NAME ||
+ strlen(slave_info->ois_name) >= MAX_SENSOR_NAME) {
+ pr_err("failed: name len greater than 32.\n");
+ pr_err("sensor name len:%zu, eeprom name len: %zu.\n",
+ strlen(slave_info->sensor_name),
+ strlen(slave_info->eeprom_name));
+ pr_err("actuator name len: %zu, ois name len:%zu.\n",
+ strlen(slave_info->actuator_name),
+ strlen(slave_info->ois_name));
+ rc = -EINVAL;
+ goto free_slave_info;
+ }
+
+ /* Print slave info */
+ CDBG("camera id %d Slave addr 0x%X addr_type %d\n",
+ slave_info->camera_id, slave_info->slave_addr,
+ slave_info->addr_type);
+ CDBG("sensor_id_reg_addr 0x%X sensor_id 0x%X sensor id mask %d",
+ slave_info->sensor_id_info.sensor_id_reg_addr,
+ slave_info->sensor_id_info.sensor_id,
+ slave_info->sensor_id_info.sensor_id_mask);
+ CDBG("power up size %d power down size %d\n",
+ slave_info->power_setting_array.size,
+ slave_info->power_setting_array.size_down);
+ CDBG("position %d",
+ slave_info->sensor_init_params.position);
+ CDBG("mount %d",
+ slave_info->sensor_init_params.sensor_mount_angle);
+ CDBG("bypass video node creation %d",
+ slave_info->bypass_video_node_creation);
+ /* Validate camera id */
+ if (slave_info->camera_id >= MAX_CAMERAS) {
+ pr_err("failed: invalid camera id %d max %d",
+ slave_info->camera_id, MAX_CAMERAS);
+ rc = -EINVAL;
+ goto free_slave_info;
+ }
+
+ /* Extract s_ctrl from camera id */
+ s_ctrl = g_sctrl[slave_info->camera_id];
+ if (!s_ctrl) {
+ pr_err("failed: s_ctrl %pK for camera_id %d", s_ctrl,
+ slave_info->camera_id);
+ rc = -EINVAL;
+ goto free_slave_info;
+ }
+
+ CDBG("s_ctrl[%d] %pK", slave_info->camera_id, s_ctrl);
+
+ if (s_ctrl->sensordata->special_support_size > 0) {
+ if (!msm_sensor_driver_is_special_support(s_ctrl,
+ slave_info->sensor_name)) {
+ pr_err("%s:%s is not support on this board\n",
+ __func__, slave_info->sensor_name);
+ rc = 0;
+ goto free_slave_info;
+ }
+ }
+
+ if (s_ctrl->is_probe_succeed == 1) {
+ /*
+ * Different sensor on this camera slot has been connected
+ * and probe already succeeded for that sensor. Ignore this
+ * probe
+ */
+ if (slave_info->sensor_id_info.sensor_id ==
+ s_ctrl->sensordata->cam_slave_info->
+ sensor_id_info.sensor_id &&
+ !(strcmp(slave_info->sensor_name,
+ s_ctrl->sensordata->cam_slave_info->sensor_name))) {
+ pr_err("slot%d: sensor name: %s sensor id%d already probed\n",
+ slave_info->camera_id,
+ slave_info->sensor_name,
+ s_ctrl->sensordata->cam_slave_info->
+ sensor_id_info.sensor_id);
+ msm_sensor_fill_sensor_info(s_ctrl,
+ probed_info, entity_name);
+ } else
+ pr_err("slot %d has some other sensor\n",
+ slave_info->camera_id);
+
+ rc = 0;
+ goto free_slave_info;
+ }
+
+ if (slave_info->power_setting_array.size == 0 &&
+ slave_info->slave_addr == 0) {
+ s_ctrl->is_csid_tg_mode = 1;
+ goto CSID_TG;
+ }
+
+ rc = msm_sensor_get_power_settings(setting, slave_info,
+ &s_ctrl->sensordata->power_info);
+ if (rc < 0) {
+ pr_err("failed");
+ goto free_slave_info;
+ }
+
+
+ camera_info = kzalloc(sizeof(struct msm_camera_slave_info), GFP_KERNEL);
+ if (!camera_info)
+ goto free_slave_info;
+
+ s_ctrl->sensordata->slave_info = camera_info;
+
+ /* Fill sensor slave info */
+ camera_info->sensor_slave_addr = slave_info->slave_addr;
+ camera_info->sensor_id_reg_addr =
+ slave_info->sensor_id_info.sensor_id_reg_addr;
+ camera_info->sensor_id = slave_info->sensor_id_info.sensor_id;
+ camera_info->sensor_id_mask = slave_info->sensor_id_info.sensor_id_mask;
+
+ /* Fill CCI master, slave address and CCI default params */
+ if (!s_ctrl->sensor_i2c_client) {
+ pr_err("failed: sensor_i2c_client %pK",
+ s_ctrl->sensor_i2c_client);
+ rc = -EINVAL;
+ goto free_camera_info;
+ }
+ /* Fill sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type = slave_info->addr_type;
+ if (s_ctrl->sensor_i2c_client->client)
+ s_ctrl->sensor_i2c_client->client->addr =
+ camera_info->sensor_slave_addr;
+
+ cci_client = s_ctrl->sensor_i2c_client->cci_client;
+ if (!cci_client) {
+ pr_err("failed: cci_client %pK", cci_client);
+ goto free_camera_info;
+ }
+ cci_client->cci_i2c_master = s_ctrl->cci_i2c_master;
+ cci_client->sid = slave_info->slave_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->i2c_freq_mode = slave_info->i2c_freq_mode;
+
+ /* Parse and fill vreg params for powerup settings */
+ rc = msm_camera_fill_vreg_params(
+ s_ctrl->sensordata->power_info.cam_vreg,
+ s_ctrl->sensordata->power_info.num_vreg,
+ s_ctrl->sensordata->power_info.power_setting,
+ s_ctrl->sensordata->power_info.power_setting_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_dt_power_setting_data rc %d",
+ rc);
+ goto free_camera_info;
+ }
+
+ /* Parse and fill vreg params for powerdown settings*/
+ rc = msm_camera_fill_vreg_params(
+ s_ctrl->sensordata->power_info.cam_vreg,
+ s_ctrl->sensordata->power_info.num_vreg,
+ s_ctrl->sensordata->power_info.power_down_setting,
+ s_ctrl->sensordata->power_info.power_down_setting_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_fill_vreg_params for PDOWN rc %d",
+ rc);
+ goto free_camera_info;
+ }
+
+CSID_TG:
+ /* Update sensor, actuator and eeprom name in
+ * sensor control structure
+ */
+ s_ctrl->sensordata->sensor_name = slave_info->sensor_name;
+ s_ctrl->sensordata->eeprom_name = slave_info->eeprom_name;
+ s_ctrl->sensordata->actuator_name = slave_info->actuator_name;
+ s_ctrl->sensordata->ois_name = slave_info->ois_name;
+ s_ctrl->sensordata->flash_name = slave_info->flash_name;
+ /*
+ * Update eeporm subdevice Id by input eeprom name
+ */
+ rc = msm_sensor_fill_eeprom_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+ /*
+ * Update actuator subdevice Id by input actuator name
+ */
+ rc = msm_sensor_fill_actuator_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+ rc = msm_sensor_fill_laser_led_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+
+ rc = msm_sensor_fill_ois_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+
+ rc = msm_sensor_fill_flash_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+
+ /* Power up and probe sensor */
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s power up failed", slave_info->sensor_name);
+ goto free_camera_info;
+ }
+
+ pr_err("%s probe succeeded", slave_info->sensor_name);
+
+ s_ctrl->bypass_video_node_creation =
+ slave_info->bypass_video_node_creation;
+
+ /*
+ * Create /dev/videoX node, comment for now until dummy /dev/videoX
+ * node is created and used by HAL
+ */
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ rc = msm_sensor_driver_create_v4l_subdev(s_ctrl);
+ else
+ rc = msm_sensor_driver_create_i2c_v4l_subdev(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: camera creat v4l2 rc %d", rc);
+ goto camera_power_down;
+ }
+
+ /* Power down */
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+
+ rc = msm_sensor_fill_slave_info_init_params(
+ slave_info,
+ s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Fill slave info failed", slave_info->sensor_name);
+ goto free_camera_info;
+ }
+ rc = msm_sensor_validate_slave_info(s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Validate slave info failed",
+ slave_info->sensor_name);
+ goto free_camera_info;
+ }
+ /* Update sensor mount angle and position in media entity flag */
+ is_yuv = (slave_info->output_format == MSM_SENSOR_YCBCR) ? 1 : 0;
+ mount_pos = ((s_ctrl->is_secure & 0x1) << 26) | is_yuv << 25 |
+ (s_ctrl->sensordata->sensor_info->position << 16) |
+ ((s_ctrl->sensordata->
+ sensor_info->sensor_mount_angle / 90) << 8);
+
+ s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT;
+
+ /*Save sensor info*/
+ s_ctrl->sensordata->cam_slave_info = slave_info;
+
+ msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name);
+
+ /*
+ * Set probe succeeded flag to 1 so that no other camera shall
+ * probed on this slot
+ */
+ s_ctrl->is_probe_succeed = 1;
+ return rc;
+
+camera_power_down:
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+free_camera_info:
+ kfree(camera_info);
+free_slave_info:
+ kfree(slave_info);
+ return rc;
+}
+
+static int32_t msm_sensor_driver_get_dt_data(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, i = 0;
+ struct msm_camera_sensor_board_info *sensordata = NULL;
+ struct device_node *of_node = s_ctrl->of_node;
+ uint32_t cell_id;
+
+ s_ctrl->sensordata = kzalloc(sizeof(*sensordata), GFP_KERNEL);
+ if (!s_ctrl->sensordata)
+ return -ENOMEM;
+
+ sensordata = s_ctrl->sensordata;
+
+ /*
+ * Read cell index - this cell index will be the camera slot where
+ * this camera will be mounted
+ */
+ rc = of_property_read_u32(of_node, "cell-index", &cell_id);
+ if (rc < 0) {
+ pr_err("failed: cell-index rc %d", rc);
+ goto FREE_SENSOR_DATA;
+ }
+ s_ctrl->id = cell_id;
+
+ /* Validate cell_id */
+ if (cell_id >= MAX_CAMERAS) {
+ pr_err("failed: invalid cell_id %d", cell_id);
+ rc = -EINVAL;
+ goto FREE_SENSOR_DATA;
+ }
+
+ /* Check whether g_sctrl is already filled for this cell_id */
+ if (g_sctrl[cell_id]) {
+ pr_err("failed: sctrl already filled for cell_id %d", cell_id);
+ rc = -EINVAL;
+ goto FREE_SENSOR_DATA;
+ }
+
+ sensordata->special_support_size =
+ of_property_count_strings(of_node,
+ "qcom,special-support-sensors");
+
+ if (sensordata->special_support_size < 0)
+ sensordata->special_support_size = 0;
+
+ if (sensordata->special_support_size > MAX_SPECIAL_SUPPORT_SIZE) {
+ pr_debug("%s:support_size exceed max support size\n", __func__);
+ sensordata->special_support_size = MAX_SPECIAL_SUPPORT_SIZE;
+ }
+
+ if (sensordata->special_support_size) {
+ for (i = 0; i < sensordata->special_support_size; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,special-support-sensors", i,
+ &(sensordata->special_support_sensors[i]));
+ if (rc < 0) {
+ /* if read sensor support names failed,
+ * set support all sensors, break;
+ */
+ sensordata->special_support_size = 0;
+ break;
+ }
+ CDBG("%s special_support_sensors[%d] = %s\n", __func__,
+ i, sensordata->special_support_sensors[i]);
+ }
+ }
+
+ /* Read subdev info */
+ rc = msm_sensor_get_sub_module_index(of_node, &sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("failed");
+ goto FREE_SENSOR_DATA;
+ }
+
+ /* Read vreg information */
+ rc = msm_camera_get_dt_vreg_data(of_node,
+ &sensordata->power_info.cam_vreg,
+ &sensordata->power_info.num_vreg);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_dt_vreg_data rc %d", rc);
+ goto FREE_SUB_MODULE_DATA;
+ }
+
+ /* Read gpio information */
+ rc = msm_sensor_driver_get_gpio_data
+ (&(sensordata->power_info.gpio_conf), of_node);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_driver_get_gpio_data rc %d", rc);
+ goto FREE_VREG_DATA;
+ }
+
+ /* Get custom mode */
+ rc = of_property_read_u32(of_node, "qcom,secure",
+ &s_ctrl->is_secure);
+ CDBG("qcom,secure = %d, rc %d", s_ctrl->is_secure, rc);
+ if (rc < 0) {
+ /* Set default to non-secure mode */
+ s_ctrl->is_secure = 0;
+ rc = 0;
+ }
+
+ /* Get CCI master */
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &s_ctrl->cci_i2c_master);
+ CDBG("qcom,cci-master %d, rc %d", s_ctrl->cci_i2c_master, rc);
+ if (rc < 0) {
+ /* Set default master 0 */
+ s_ctrl->cci_i2c_master = MASTER_0;
+ rc = 0;
+ }
+
+ /* Get mount angle */
+ if (of_property_read_u32(of_node, "qcom,mount-angle",
+ &sensordata->sensor_info->sensor_mount_angle) < 0) {
+ /* Invalidate mount angle flag */
+ sensordata->sensor_info->is_mount_angle_valid = 0;
+ sensordata->sensor_info->sensor_mount_angle = 0;
+ } else {
+ sensordata->sensor_info->is_mount_angle_valid = 1;
+ }
+ CDBG("%s qcom,mount-angle %d\n", __func__,
+ sensordata->sensor_info->sensor_mount_angle);
+ if (of_property_read_u32(of_node, "qcom,sensor-position",
+ &sensordata->sensor_info->position) < 0) {
+ CDBG("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->sensor_info->position = INVALID_CAMERA_B;
+ }
+ if (of_property_read_u32(of_node, "qcom,sensor-mode",
+ &sensordata->sensor_info->modes_supported) < 0) {
+ CDBG("%s:%d Invalid sensor mode supported\n",
+ __func__, __LINE__);
+ sensordata->sensor_info->modes_supported = CAMERA_MODE_INVALID;
+ }
+ /* Get vdd-cx regulator */
+ /*Optional property, don't return error if absent */
+ of_property_read_string(of_node, "qcom,vdd-cx-name",
+ &sensordata->misc_regulator);
+ CDBG("qcom,misc_regulator %s", sensordata->misc_regulator);
+
+ s_ctrl->set_mclk_23880000 = of_property_read_bool(of_node,
+ "qcom,mclk-23880000");
+
+ CDBG("%s qcom,mclk-23880000 = %d\n", __func__,
+ s_ctrl->set_mclk_23880000);
+
+ return rc;
+
+FREE_VREG_DATA:
+ kfree(sensordata->power_info.cam_vreg);
+FREE_SUB_MODULE_DATA:
+ kfree(sensordata->sensor_info);
+FREE_SENSOR_DATA:
+ kfree(sensordata);
+ return rc;
+}
+
+static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter");
+ /* Validate input parameters */
+
+
+ /* Allocate memory for sensor_i2c_client */
+ s_ctrl->sensor_i2c_client = kzalloc(sizeof(*s_ctrl->sensor_i2c_client),
+ GFP_KERNEL);
+ if (!s_ctrl->sensor_i2c_client)
+ return -ENOMEM;
+
+ /* Allocate memory for mutex */
+ s_ctrl->msm_sensor_mutex = kzalloc(sizeof(*s_ctrl->msm_sensor_mutex),
+ GFP_KERNEL);
+ if (!s_ctrl->msm_sensor_mutex) {
+ rc = -ENOMEM;
+ goto FREE_SENSOR_I2C_CLIENT;
+ }
+
+ /* Parse dt information and store in sensor control structure */
+ rc = msm_sensor_driver_get_dt_data(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: rc %d", rc);
+ goto FREE_MUTEX;
+ }
+
+ /* Initialize mutex */
+ mutex_init(s_ctrl->msm_sensor_mutex);
+
+ /* Initialize v4l2 subdev info */
+ s_ctrl->sensor_v4l2_subdev_info = msm_sensor_driver_subdev_info;
+ s_ctrl->sensor_v4l2_subdev_info_size =
+ ARRAY_SIZE(msm_sensor_driver_subdev_info);
+
+ /* Initialize default parameters */
+ rc = msm_sensor_init_default_params(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_init_default_params rc %d", rc);
+ goto FREE_DT_DATA;
+ }
+
+ /* Store sensor control structure in static database */
+ g_sctrl[s_ctrl->id] = s_ctrl;
+ CDBG("g_sctrl[%d] %pK", s_ctrl->id, g_sctrl[s_ctrl->id]);
+
+ return rc;
+
+FREE_DT_DATA:
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl);
+ kfree(s_ctrl->sensordata->power_info.gpio_conf);
+ kfree(s_ctrl->sensordata->power_info.cam_vreg);
+ kfree(s_ctrl->sensordata);
+FREE_MUTEX:
+ kfree(s_ctrl->msm_sensor_mutex);
+FREE_SENSOR_I2C_CLIENT:
+ kfree(s_ctrl->sensor_i2c_client);
+ return rc;
+}
+
+static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl = NULL;
+
+ /* Create sensor control structure */
+ s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL);
+ if (!s_ctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, s_ctrl);
+
+ /* Initialize sensor device type */
+ s_ctrl->sensor_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ s_ctrl->of_node = pdev->dev.of_node;
+
+ /*fill in platform device*/
+ s_ctrl->pdev = pdev;
+
+ rc = msm_sensor_driver_parse(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_driver_parse rc %d", rc);
+ goto FREE_S_CTRL;
+ }
+
+ /* Get clocks information */
+ rc = msm_camera_get_clk_info(s_ctrl->pdev,
+ &s_ctrl->sensordata->power_info.clk_info,
+ &s_ctrl->sensordata->power_info.clk_ptr,
+ &s_ctrl->sensordata->power_info.clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_get_clk_info rc %d", rc);
+ goto FREE_S_CTRL;
+ }
+
+ /* Fill platform device id*/
+ pdev->id = s_ctrl->id;
+
+ /* Fill device in power info */
+ s_ctrl->sensordata->power_info.dev = &pdev->dev;
+ return rc;
+FREE_S_CTRL:
+ kfree(s_ctrl);
+ return rc;
+}
+
+static int32_t msm_sensor_driver_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int32_t rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl;
+
+ CDBG("\n\nEnter: msm_sensor_driver_i2c_probe");
+ 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;
+ }
+
+ /* Create sensor control structure */
+ s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL);
+ if (!s_ctrl)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, s_ctrl);
+
+ /* Initialize sensor device type */
+ s_ctrl->sensor_device_type = MSM_CAMERA_I2C_DEVICE;
+ s_ctrl->of_node = client->dev.of_node;
+
+ rc = msm_sensor_driver_parse(s_ctrl);
+ if (rc < 0) {
+ pr_err("failed: msm_sensor_driver_parse rc %d", rc);
+ goto FREE_S_CTRL;
+ }
+
+ if (s_ctrl->sensor_i2c_client != NULL) {
+ s_ctrl->sensor_i2c_client->client = client;
+ s_ctrl->sensordata->power_info.dev = &client->dev;
+
+ /* Get clocks information */
+ rc = msm_camera_i2c_dev_get_clk_info(
+ &s_ctrl->sensor_i2c_client->client->dev,
+ &s_ctrl->sensordata->power_info.clk_info,
+ &s_ctrl->sensordata->power_info.clk_ptr,
+ &s_ctrl->sensordata->power_info.clk_info_size);
+ if (rc < 0) {
+ pr_err("failed: msm_camera_i2c_dev_get_clk_info rc %d",
+ rc);
+ goto FREE_S_CTRL;
+ }
+ return rc;
+ }
+FREE_S_CTRL:
+ kfree(s_ctrl);
+ return rc;
+}
+
+static int msm_sensor_driver_i2c_remove(struct i2c_client *client)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client);
+
+ pr_err("%s: sensor FREE\n", __func__);
+
+ if (!s_ctrl) {
+ pr_err("%s: sensor device is NULL\n", __func__);
+ return 0;
+ }
+
+ g_sctrl[s_ctrl->id] = NULL;
+ msm_sensor_free_sensor_data(s_ctrl);
+ kfree(s_ctrl->msm_sensor_mutex);
+ kfree(s_ctrl->sensor_i2c_client);
+ kfree(s_ctrl);
+
+ return 0;
+}
+
+static const struct i2c_device_id i2c_id[] = {
+ {SENSOR_DRIVER_I2C, (kernel_ulong_t)NULL},
+ { }
+};
+
+static struct i2c_driver msm_sensor_driver_i2c = {
+ .id_table = i2c_id,
+ .probe = msm_sensor_driver_i2c_probe,
+ .remove = msm_sensor_driver_i2c_remove,
+ .driver = {
+ .name = SENSOR_DRIVER_I2C,
+ },
+};
+
+static int __init msm_sensor_driver_init(void)
+{
+ int32_t rc = 0;
+
+ CDBG("%s Enter\n", __func__);
+ rc = platform_driver_register(&msm_sensor_platform_driver);
+ if (rc)
+ pr_err("%s platform_driver_register failed rc = %d",
+ __func__, rc);
+ rc = i2c_add_driver(&msm_sensor_driver_i2c);
+ if (rc)
+ pr_err("%s i2c_add_driver failed rc = %d", __func__, rc);
+
+ return rc;
+}
+
+static void __exit msm_sensor_driver_exit(void)
+{
+ CDBG("Enter");
+ platform_driver_unregister(&msm_sensor_platform_driver);
+ i2c_del_driver(&msm_sensor_driver_i2c);
+}
+
+module_init(msm_sensor_driver_init);
+module_exit(msm_sensor_driver_exit);
+MODULE_DESCRIPTION("msm_sensor_driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.h
new file mode 100644
index 0000000..28e4336
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2013-2014, 2018, 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_DRIVER_H
+#define MSM_SENSOR_DRIVER_H
+
+#include "msm_sensor.h"
+
+int32_t msm_sensor_driver_probe(void *setting,
+ struct msm_sensor_info_t *probed_info, char *entity_name);
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
new file mode 100644
index 0000000..b9fafc8
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
@@ -0,0 +1,225 @@
+/* Copyright (c) 2013-2018, 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) "MSM-SENSOR-INIT %s:%d " fmt "\n", __func__, __LINE__
+
+/* Header files */
+#include "msm_sensor_init.h"
+#include "msm_sensor_driver.h"
+#include "msm_sensor.h"
+#include "msm_sd.h"
+
+/* Logging macro */
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct msm_sensor_init_t *s_init;
+static struct v4l2_file_operations msm_sensor_init_v4l2_subdev_fops;
+/* Static function declaration */
+static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg);
+
+/* Static structure declaration */
+static struct v4l2_subdev_core_ops msm_sensor_init_subdev_core_ops = {
+ .ioctl = msm_sensor_init_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_sensor_init_subdev_ops = {
+ .core = &msm_sensor_init_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_sensor_init_internal_ops;
+
+static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init)
+{
+ int rc;
+ int tm = 20000;
+
+ if (s_init->module_init_status == 1) {
+ CDBG("msm_cam_get_module_init_status -2\n");
+ return 0;
+ }
+ rc = wait_event_timeout(s_init->state_wait,
+ (s_init->module_init_status == 1), msecs_to_jiffies(tm));
+ if (rc == 0)
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ return rc;
+}
+
+/* Static function definition */
+static int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init,
+ void *arg)
+{
+ int32_t rc = 0;
+ struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg;
+
+ /* Validate input parameters */
+ if (!s_init || !cfg) {
+ pr_err("failed: s_init %pK cfg %pK", s_init, cfg);
+ return -EINVAL;
+ }
+
+ switch (cfg->cfgtype) {
+ case CFG_SINIT_PROBE:
+ mutex_lock(&s_init->imutex);
+ s_init->module_init_status = 0;
+ rc = msm_sensor_driver_probe(cfg->cfg.setting,
+ &cfg->probed_info,
+ cfg->entity_name);
+ mutex_unlock(&s_init->imutex);
+ if (rc < 0)
+ pr_err_ratelimited("%s failed (non-fatal) rc %d",
+ __func__, rc);
+ break;
+
+ case CFG_SINIT_PROBE_DONE:
+ s_init->module_init_status = 1;
+ wake_up(&s_init->state_wait);
+ break;
+
+ case CFG_SINIT_PROBE_WAIT_DONE:
+ msm_sensor_wait_for_probe_done(s_init);
+ break;
+
+ default:
+ pr_err("default");
+ break;
+ }
+
+ return rc;
+}
+
+static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc = 0;
+ struct msm_sensor_init_t *s_init = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter");
+
+ /* Validate input parameters */
+ if (!s_init) {
+ pr_err("failed: s_init %pK", s_init);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_INIT_CFG:
+ rc = msm_sensor_driver_cmd(s_init, arg);
+ break;
+
+ default:
+ pr_err_ratelimited("default\n");
+ break;
+ }
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_sensor_init_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct sensor_init_cfg_data32 *u32 =
+ (struct sensor_init_cfg_data32 *)arg;
+ struct sensor_init_cfg_data sensor_init_data;
+
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_INIT_CFG32:
+ memset(&sensor_init_data, 0, sizeof(sensor_init_data));
+ sensor_init_data.cfgtype = u32->cfgtype;
+ sensor_init_data.cfg.setting =
+ (__force void *)compat_ptr(u32->cfg.setting);
+ cmd = VIDIOC_MSM_SENSOR_INIT_CFG;
+ rc = msm_sensor_init_subdev_ioctl(sd, cmd, &sensor_init_data);
+ if (rc < 0) {
+ pr_err_ratelimited("%s:%d VIDIOC_MSM_SENSOR_INIT_CFG failed (non-fatal)",
+ __func__, __LINE__);
+ return rc;
+ }
+ u32->probed_info = sensor_init_data.probed_info;
+ strlcpy(u32->entity_name, sensor_init_data.entity_name,
+ sizeof(sensor_init_data.entity_name));
+ return 0;
+ default:
+ return msm_sensor_init_subdev_ioctl(sd, cmd, arg);
+ }
+}
+
+static long msm_sensor_init_subdev_fops_ioctl(
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_sensor_init_subdev_do_ioctl);
+}
+#endif
+
+static int __init msm_sensor_init_module(void)
+{
+ int ret = 0;
+ /* Allocate memory for msm_sensor_init control structure */
+ s_init = kzalloc(sizeof(struct msm_sensor_init_t), GFP_KERNEL);
+ if (!s_init)
+ return -ENOMEM;
+
+ CDBG("MSM_SENSOR_INIT_MODULE %pK", NULL);
+
+ /* Initialize mutex */
+ mutex_init(&s_init->imutex);
+
+ /* Create /dev/v4l-subdevX for msm_sensor_init */
+ v4l2_subdev_init(&s_init->msm_sd.sd, &msm_sensor_init_subdev_ops);
+ snprintf(s_init->msm_sd.sd.name, sizeof(s_init->msm_sd.sd.name), "%s",
+ "msm_sensor_init");
+ v4l2_set_subdevdata(&s_init->msm_sd.sd, s_init);
+ s_init->msm_sd.sd.internal_ops = &msm_sensor_init_internal_ops;
+ s_init->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&s_init->msm_sd.sd.entity, 0, NULL);
+ s_init->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_init->msm_sd.sd.entity.name = s_init->msm_sd.sd.name;
+ s_init->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
+ ret = msm_sd_register(&s_init->msm_sd);
+ if (ret) {
+ CDBG("%s: msm_sd_register error = %d\n", __func__, ret);
+ goto error;
+ }
+ msm_cam_copy_v4l2_subdev_fops(&msm_sensor_init_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_sensor_init_v4l2_subdev_fops.compat_ioctl32 =
+ msm_sensor_init_subdev_fops_ioctl;
+#endif
+ s_init->msm_sd.sd.devnode->fops =
+ &msm_sensor_init_v4l2_subdev_fops;
+
+ init_waitqueue_head(&s_init->state_wait);
+
+ return 0;
+error:
+ mutex_destroy(&s_init->imutex);
+ kfree(s_init);
+ return ret;
+}
+
+static void __exit msm_sensor_exit_module(void)
+{
+ msm_sd_unregister(&s_init->msm_sd);
+ mutex_destroy(&s_init->imutex);
+ kfree(s_init);
+}
+
+module_init(msm_sensor_init_module);
+module_exit(msm_sensor_exit_module);
+MODULE_DESCRIPTION("msm_sensor_init");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.h
new file mode 100644
index 0000000..4048eb0
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2013-2014, 2018, 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_INIT_H
+#define MSM_SENSOR_INIT_H
+
+#include "msm_sensor.h"
+
+struct msm_sensor_init_t {
+ struct mutex imutex;
+ struct msm_sd_subdev msm_sd;
+ int module_init_status;
+ wait_queue_head_t state_wait;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ois/Makefile
new file mode 100644
index 0000000..f09c92a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/Makefile
@@ -0,0 +1,5 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += msm_ois.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
new file mode 100644
index 0000000..2253eee
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
@@ -0,0 +1,1110 @@
+/* Copyright (c) 2014-2018, 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) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "msm_sd.h"
+#include "msm_ois.h"
+#include "msm_cci.h"
+
+DEFINE_MSM_MUTEX(msm_ois_mutex);
+/*#define MSM_OIS_DEBUG*/
+#undef CDBG
+#ifdef MSM_OIS_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+static struct v4l2_file_operations msm_ois_v4l2_subdev_fops;
+static int32_t msm_ois_power_up(struct msm_ois_ctrl_t *o_ctrl);
+static int32_t msm_ois_power_down(struct msm_ois_ctrl_t *o_ctrl);
+
+static struct i2c_driver msm_ois_i2c_driver;
+
+static int32_t data_type_to_num_bytes(
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t ret_val;
+
+ switch (data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ ret_val = 1;
+ break;
+ case MSM_CAMERA_I2C_WORD_DATA:
+ ret_val = 2;
+ break;
+ case MSM_CAMERA_I2C_DWORD_DATA:
+ ret_val = 4;
+ break;
+ default:
+ pr_err("unsupported data type: %d\n",
+ data_type);
+ ret_val = 1;
+ break;
+ }
+ return ret_val;
+}
+
+static int32_t msm_ois_download(struct msm_ois_ctrl_t *o_ctrl)
+{
+ uint16_t bytes_in_tx = 0;
+ uint16_t total_bytes = 0;
+ uint8_t *ptr = NULL;
+ int32_t rc = 0;
+ const struct firmware *fw = NULL;
+ const char *fw_name_prog = NULL;
+ const char *fw_name_coeff = NULL;
+ char name_prog[MAX_SENSOR_NAME] = {0};
+ char name_coeff[MAX_SENSOR_NAME] = {0};
+ struct device *dev = &(o_ctrl->pdev->dev);
+ enum msm_camera_i2c_reg_addr_type save_addr_type;
+
+ CDBG("Enter\n");
+ save_addr_type = o_ctrl->i2c_client.addr_type;
+ o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
+
+ snprintf(name_coeff, MAX_SENSOR_NAME, "%s.coeff",
+ o_ctrl->oboard_info->ois_name);
+
+ snprintf(name_prog, MAX_SENSOR_NAME, "%s.prog",
+ o_ctrl->oboard_info->ois_name);
+
+ /* cast pointer as const pointer*/
+ fw_name_prog = name_prog;
+ fw_name_coeff = name_coeff;
+
+ /* Load FW */
+ rc = request_firmware(&fw, fw_name_prog, dev);
+ if (rc) {
+ dev_err(dev, "Failed to locate %s\n", fw_name_prog);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+ return rc;
+ }
+
+ total_bytes = fw->size;
+ for (ptr = (uint8_t *)fw->data; total_bytes;
+ total_bytes -= bytes_in_tx, ptr += bytes_in_tx) {
+ bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes;
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq(
+ &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.prog,
+ ptr, bytes_in_tx);
+ if (rc < 0) {
+ pr_err("Failed:remaining bytes to be downloaded:%d\n",
+ bytes_in_tx);
+ /* abort download fw and return error*/
+ goto release_firmware;
+ }
+ }
+ release_firmware(fw);
+
+ rc = request_firmware(&fw, fw_name_coeff, dev);
+ if (rc) {
+ dev_err(dev, "Failed to locate %s\n", fw_name_coeff);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+ return rc;
+ }
+ total_bytes = fw->size;
+ for (ptr = (uint8_t *)fw->data; total_bytes;
+ total_bytes -= bytes_in_tx, ptr += bytes_in_tx) {
+ bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes;
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq(
+ &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.coeff,
+ ptr, bytes_in_tx);
+ if (rc < 0) {
+ pr_err("Failed:remaining bytes to be downloaded:%d\n",
+ total_bytes);
+ /* abort download fw*/
+ break;
+ }
+ }
+release_firmware:
+ release_firmware(fw);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+
+ return rc;
+}
+
+static int32_t msm_ois_data_config(struct msm_ois_ctrl_t *o_ctrl,
+ struct msm_ois_slave_info *slave_info)
+{
+ int rc = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+ if (!slave_info) {
+ pr_err("failed : invalid slave_info\n");
+ return -EINVAL;
+ }
+ /* fill ois slave info*/
+ if (strlcpy(o_ctrl->oboard_info->ois_name, slave_info->ois_name,
+ sizeof(o_ctrl->oboard_info->ois_name)) == 0) {
+ pr_err("failed: invalid ois_name\n");
+ return -EFAULT;
+ }
+ memcpy(&(o_ctrl->oboard_info->opcode), &(slave_info->opcode),
+ sizeof(struct msm_ois_opcode));
+ o_ctrl->oboard_info->i2c_slaveaddr = slave_info->i2c_addr;
+
+ /* config cci_client*/
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = o_ctrl->i2c_client.cci_client;
+ cci_client->sid =
+ o_ctrl->oboard_info->i2c_slaveaddr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->cci_i2c_master = o_ctrl->cci_master;
+ } else {
+ o_ctrl->i2c_client.client->addr =
+ o_ctrl->oboard_info->i2c_slaveaddr;
+ }
+ o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ois_write_settings(struct msm_ois_ctrl_t *o_ctrl,
+ uint16_t size, struct reg_settings_ois_t *settings)
+{
+ int32_t rc = -EFAULT;
+ int32_t i = 0, num_byte_seq = 0;
+ uint8_t *reg_data_seq;
+
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+
+ CDBG("Enter\n");
+
+ for (i = 0; i < size; i++) {
+ switch (settings[i].i2c_operation) {
+ case MSM_OIS_WRITE: {
+ switch (settings[i].data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write(
+ &o_ctrl->i2c_client,
+ settings[i].reg_addr,
+ settings[i].reg_data,
+ settings[i].data_type);
+ break;
+ case MSM_CAMERA_I2C_DWORD_DATA:
+ reg_setting =
+ kzalloc(sizeof(struct msm_camera_i2c_seq_reg_array),
+ GFP_KERNEL);
+ if (!reg_setting)
+ return -ENOMEM;
+
+ reg_setting->reg_addr = settings[i].reg_addr;
+ reg_setting->reg_data[0] = (uint8_t)
+ ((settings[i].reg_data &
+ 0xFF000000) >> 24);
+ reg_setting->reg_data[1] = (uint8_t)
+ ((settings[i].reg_data &
+ 0x00FF0000) >> 16);
+ reg_setting->reg_data[2] = (uint8_t)
+ ((settings[i].reg_data &
+ 0x0000FF00) >> 8);
+ reg_setting->reg_data[3] = (uint8_t)
+ (settings[i].reg_data & 0x000000FF);
+ reg_setting->reg_data_size = 4;
+ rc = o_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_seq(&o_ctrl->i2c_client,
+ reg_setting->reg_addr,
+ reg_setting->reg_data,
+ reg_setting->reg_data_size);
+ kfree(reg_setting);
+ reg_setting = NULL;
+ if (rc < 0)
+ return rc;
+ break;
+
+ default:
+ pr_err("Unsupport data type: %d\n",
+ settings[i].data_type);
+ break;
+ }
+ if (settings[i].delay > 20)
+ msleep(settings[i].delay);
+ else if (settings[i].delay != 0)
+ usleep_range(settings[i].delay * 1000,
+ (settings[i].delay * 1000) + 1000);
+ }
+ break;
+
+ case MSM_OIS_POLL: {
+ switch (settings[i].data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+
+ rc = o_ctrl->i2c_client.i2c_func_tbl
+ ->i2c_poll(&o_ctrl->i2c_client,
+ settings[i].reg_addr,
+ settings[i].reg_data,
+ settings[i].data_type,
+ settings[i].delay);
+ break;
+
+ default:
+ pr_err("Unsupport data type: %d\n",
+ settings[i].data_type);
+ break;
+ }
+ break;
+ }
+ case MSM_OIS_READ: {
+ switch (settings[i].data_type) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ case MSM_CAMERA_I2C_DWORD_DATA:
+
+ num_byte_seq =
+ data_type_to_num_bytes
+ (settings[i].data_type);
+ reg_data_seq = kzalloc(sizeof(uint32_t),
+ GFP_KERNEL);
+ if (!reg_data_seq)
+ return -ENOMEM;
+
+ rc = msm_camera_cci_i2c_read_seq
+ (&o_ctrl->i2c_client,
+ settings[i].reg_addr,
+ reg_data_seq,
+ num_byte_seq);
+
+ memcpy(&settings[i].reg_data,
+ reg_data_seq, sizeof(uint32_t));
+
+ CDBG("ois data read 0x%x from address 0x%x",
+ settings[i].reg_addr,
+ settings[i].reg_data);
+
+ kfree(reg_data_seq);
+ reg_data_seq = NULL;
+
+ break;
+ default:
+ pr_err("Unsupport data type for MSM_OIS_READ: %d\n",
+ settings[i].data_type);
+ break;
+ }
+ break;
+ }
+
+ if (rc < 0)
+ break;
+ }
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ois_vreg_control(struct msm_ois_ctrl_t *o_ctrl,
+ int config)
+{
+ int rc = 0, i, cnt;
+ struct msm_ois_vreg *vreg_cfg;
+
+ vreg_cfg = &o_ctrl->vreg_cfg;
+ cnt = vreg_cfg->num_vreg;
+ if (!cnt)
+ return 0;
+
+ if (cnt >= MSM_OIS_MAX_VREGS) {
+ pr_err("%s failed %d cnt %d\n", __func__, __LINE__, cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ rc = msm_camera_config_single_vreg(&(o_ctrl->pdev->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ }
+ return rc;
+}
+
+static int32_t msm_ois_power_down(struct msm_ois_ctrl_t *o_ctrl)
+{
+ int32_t rc = 0;
+ enum msm_sensor_power_seq_gpio_t gpio;
+
+ CDBG("Enter\n");
+ if (o_ctrl->ois_state != OIS_DISABLE_STATE) {
+
+ rc = msm_ois_vreg_control(o_ctrl, 0);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ for (gpio = SENSOR_GPIO_AF_PWDM; gpio < SENSOR_GPIO_MAX;
+ gpio++) {
+ if (o_ctrl->gconf &&
+ o_ctrl->gconf->gpio_num_info &&
+ o_ctrl->gconf->
+ gpio_num_info->valid[gpio] == 1) {
+ gpio_set_value_cansleep(
+ o_ctrl->gconf->gpio_num_info
+ ->gpio_num[gpio],
+ GPIOF_OUT_INIT_LOW);
+
+ if (o_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ o_ctrl->pinctrl_info.pinctrl,
+ o_ctrl->pinctrl_info.
+ gpio_state_suspend);
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to suspend state: %d",
+ __func__, __LINE__, rc);
+ devm_pinctrl_put(
+ o_ctrl->pinctrl_info.pinctrl);
+ }
+ o_ctrl->cam_pinctrl_status = 0;
+ rc = msm_camera_request_gpio_table(
+ o_ctrl->gconf->cam_gpio_req_tbl,
+ o_ctrl->gconf->cam_gpio_req_tbl_size,
+ 0);
+ if (rc < 0)
+ pr_err("ERR:%s:Failed in selecting state in ois power down: %d\n",
+ __func__, rc);
+ }
+ }
+
+ o_ctrl->i2c_tbl_index = 0;
+ o_ctrl->ois_state = OIS_OPS_INACTIVE;
+ }
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int msm_ois_init(struct msm_ois_ctrl_t *o_ctrl)
+{
+ int rc = 0;
+
+ CDBG("Enter\n");
+
+ if (!o_ctrl) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &o_ctrl->i2c_client, MSM_CCI_INIT);
+ if (rc < 0)
+ pr_err("cci_init failed\n");
+ }
+ o_ctrl->ois_state = OIS_OPS_ACTIVE;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl,
+ struct msm_ois_set_info_t *set_info)
+{
+ struct reg_settings_ois_t *settings = NULL;
+ int32_t rc = 0, i = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = o_ctrl->i2c_client.cci_client;
+ cci_client->sid =
+ set_info->ois_params.i2c_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->cci_i2c_master = o_ctrl->cci_master;
+ cci_client->i2c_freq_mode = set_info->ois_params.i2c_freq_mode;
+ } else {
+ o_ctrl->i2c_client.client->addr =
+ set_info->ois_params.i2c_addr;
+ }
+ o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR;
+
+
+ if (set_info->ois_params.setting_size > 0 &&
+ set_info->ois_params.setting_size
+ < MAX_OIS_REG_SETTINGS) {
+ settings = kmalloc(
+ sizeof(struct reg_settings_ois_t) *
+ (set_info->ois_params.setting_size),
+ GFP_KERNEL);
+ if (settings == NULL) {
+ pr_err("Error allocating memory\n");
+ return -EFAULT;
+ }
+ if (copy_from_user(settings,
+ (void __user *)set_info->ois_params.settings,
+ set_info->ois_params.setting_size *
+ sizeof(struct reg_settings_ois_t))) {
+ kfree(settings);
+ pr_err("Error copying\n");
+ return -EFAULT;
+ }
+
+ rc = msm_ois_write_settings(o_ctrl,
+ set_info->ois_params.setting_size,
+ settings);
+
+ for (i = 0; i < set_info->ois_params.setting_size; i++) {
+ if (settings[i].i2c_operation
+ == MSM_OIS_READ) {
+ if (copy_to_user(
+ (void __user *)
+ (&set_info->ois_params.settings[i]),
+ &settings[i],
+ sizeof(struct reg_settings_ois_t))) {
+ kfree(settings);
+ pr_err("Error copying\n");
+ return -EFAULT;
+ }
+ CDBG("ois_data at addr 0x%x is 0x%x",
+ settings[i].reg_addr,
+ settings[i].reg_data);
+ }
+ }
+
+ kfree(settings);
+ if (rc < 0) {
+ pr_err("Error\n");
+ return -EFAULT;
+ }
+ }
+
+ CDBG("Exit\n");
+
+ return rc;
+}
+
+static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
+ void *argp)
+{
+ struct msm_ois_cfg_data *cdata =
+ (struct msm_ois_cfg_data *)argp;
+ int32_t rc = 0;
+
+ mutex_lock(o_ctrl->ois_mutex);
+ CDBG("Enter\n");
+ CDBG("%s type %d\n", __func__, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_OIS_INIT:
+ rc = msm_ois_init(o_ctrl);
+ if (rc < 0)
+ pr_err("msm_ois_init failed %d\n", rc);
+ break;
+ case CFG_OIS_POWERDOWN:
+ rc = msm_ois_power_down(o_ctrl);
+ if (rc < 0)
+ pr_err("msm_ois_power_down failed %d\n", rc);
+ break;
+ case CFG_OIS_POWERUP:
+ rc = msm_ois_power_up(o_ctrl);
+ if (rc < 0)
+ pr_err("Failed ois power up%d\n", rc);
+ break;
+ case CFG_OIS_CONTROL:
+ rc = msm_ois_control(o_ctrl, &cdata->cfg.set_info);
+ if (rc < 0)
+ pr_err("Failed ois control%d\n", rc);
+ break;
+ case CFG_OIS_I2C_WRITE_SEQ_TABLE: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ memcpy(&conf_array,
+ (void *)cdata->cfg.settings,
+ sizeof(struct msm_camera_i2c_seq_reg_setting));
+ } else
+#endif
+ if (copy_from_user(&conf_array,
+ (void __user *)cdata->cfg.settings,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ if (!conf_array.size ||
+ conf_array.size > I2C_SEQ_REG_DATA_MAX) {
+ 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 __user *)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 = o_ctrl->i2c_client.i2c_func_tbl->
+ i2c_write_seq_table(&o_ctrl->i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ default:
+ break;
+ }
+ mutex_unlock(o_ctrl->ois_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ois_config_download(struct msm_ois_ctrl_t *o_ctrl,
+ void *argp)
+{
+ struct msm_ois_cfg_download_data *cdata =
+ (struct msm_ois_cfg_download_data *)argp;
+ int32_t rc = 0;
+
+ if (!o_ctrl || !cdata) {
+ pr_err("failed: Invalid data\n");
+ return -EINVAL;
+ }
+ mutex_lock(o_ctrl->ois_mutex);
+ CDBG("Enter\n");
+ CDBG("%s type %d\n", __func__, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_OIS_DATA_CONFIG:
+ rc = msm_ois_data_config(o_ctrl, &cdata->slave_info);
+ if (rc < 0)
+ pr_err("Failed ois data config %d\n", rc);
+ break;
+ case CFG_OIS_DOWNLOAD:
+ rc = msm_ois_download(o_ctrl);
+ if (rc < 0)
+ pr_err("Failed ois download %d\n", rc);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(o_ctrl->ois_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+
+static int32_t msm_ois_get_subdev_id(struct msm_ois_ctrl_t *o_ctrl,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ *subdev_id = o_ctrl->pdev->id;
+ else
+ *subdev_id = o_ctrl->subdev_id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+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 = msm_camera_cci_i2c_write_seq,
+ .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,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+
+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 = msm_camera_qup_i2c_write_seq,
+ .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_qup_i2c_write_table_w_microdelay,
+ .i2c_poll = msm_camera_qup_i2c_poll,
+};
+
+static int msm_ois_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+ int rc = 0;
+ struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter\n");
+ if (!o_ctrl) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ mutex_lock(o_ctrl->ois_mutex);
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE &&
+ o_ctrl->ois_state != OIS_DISABLE_STATE) {
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_util(
+ &o_ctrl->i2c_client, MSM_CCI_RELEASE);
+ if (rc < 0)
+ pr_err("cci_init failed\n");
+ }
+ o_ctrl->ois_state = OIS_DISABLE_STATE;
+ mutex_unlock(o_ctrl->ois_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_ois_internal_ops = {
+ .close = msm_ois_close,
+};
+
+static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc;
+ struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd);
+ void *argp = (void *)arg;
+
+ CDBG("Enter\n");
+ CDBG("%s:%d o_ctrl %pK argp %pK\n", __func__, __LINE__, o_ctrl, argp);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_ois_get_subdev_id(o_ctrl, argp);
+ case VIDIOC_MSM_OIS_CFG:
+ return msm_ois_config(o_ctrl, argp);
+ case VIDIOC_MSM_OIS_CFG_DOWNLOAD:
+ return msm_ois_config_download(o_ctrl, argp);
+ case MSM_SD_SHUTDOWN:
+ if (!o_ctrl->i2c_client.i2c_func_tbl) {
+ pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n");
+ return -EINVAL;
+ }
+ mutex_lock(o_ctrl->ois_mutex);
+ rc = msm_ois_power_down(o_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d OIS Power down failed\n",
+ __func__, __LINE__);
+ }
+ mutex_unlock(o_ctrl->ois_mutex);
+ return msm_ois_close(sd, NULL);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int32_t msm_ois_power_up(struct msm_ois_ctrl_t *o_ctrl)
+{
+ int rc = 0;
+ enum msm_sensor_power_seq_gpio_t gpio;
+
+ CDBG("%s called\n", __func__);
+
+ rc = msm_ois_vreg_control(o_ctrl, 1);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+
+ for (gpio = SENSOR_GPIO_AF_PWDM;
+ gpio < SENSOR_GPIO_MAX; gpio++) {
+ if (o_ctrl->gconf && o_ctrl->gconf->gpio_num_info &&
+ o_ctrl->gconf->gpio_num_info->valid[gpio] == 1) {
+ rc = msm_camera_request_gpio_table(
+ o_ctrl->gconf->cam_gpio_req_tbl,
+ o_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state for ois: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ if (o_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ o_ctrl->pinctrl_info.pinctrl,
+ o_ctrl->pinctrl_info.gpio_state_active);
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ gpio_set_value_cansleep(
+ o_ctrl->gconf->gpio_num_info->gpio_num[gpio],
+ 1);
+ }
+ }
+
+ o_ctrl->ois_state = OIS_ENABLE_STATE;
+ CDBG("Exit\n");
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_ois_subdev_core_ops = {
+ .ioctl = msm_ois_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_ois_subdev_ops = {
+ .core = &msm_ois_subdev_core_ops,
+};
+
+static const struct i2c_device_id msm_ois_i2c_id[] = {
+ {"qcom,ois", (kernel_ulong_t)NULL},
+ { }
+};
+
+static int32_t msm_ois_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_ois_ctrl_t *ois_ctrl_t = NULL;
+
+ CDBG("Enter\n");
+
+ if (client == NULL) {
+ pr_err("msm_ois_i2c_probe: client is null\n");
+ return -EINVAL;
+ }
+
+ ois_ctrl_t = kzalloc(sizeof(struct msm_ois_ctrl_t),
+ GFP_KERNEL);
+ if (!ois_ctrl_t)
+ return -ENOMEM;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("i2c_check_functionality failed\n");
+ rc = -EINVAL;
+ goto probe_failure;
+ }
+
+ CDBG("client = 0x%pK\n", client);
+
+ rc = of_property_read_u32(client->dev.of_node, "cell-index",
+ &ois_ctrl_t->subdev_id);
+ CDBG("cell-index %d, rc %d\n", ois_ctrl_t->subdev_id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto probe_failure;
+ }
+
+ ois_ctrl_t->i2c_driver = &msm_ois_i2c_driver;
+ ois_ctrl_t->i2c_client.client = client;
+ /* Set device type as I2C */
+ ois_ctrl_t->ois_device_type = MSM_CAMERA_I2C_DEVICE;
+ ois_ctrl_t->i2c_client.i2c_func_tbl = &msm_sensor_qup_func_tbl;
+ ois_ctrl_t->ois_v4l2_subdev_ops = &msm_ois_subdev_ops;
+ ois_ctrl_t->ois_mutex = &msm_ois_mutex;
+
+ /* Assign name for sub device */
+ snprintf(ois_ctrl_t->msm_sd.sd.name, sizeof(ois_ctrl_t->msm_sd.sd.name),
+ "%s", ois_ctrl_t->i2c_driver->driver.name);
+
+ /* Initialize sub device */
+ v4l2_i2c_subdev_init(&ois_ctrl_t->msm_sd.sd,
+ ois_ctrl_t->i2c_client.client,
+ ois_ctrl_t->ois_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&ois_ctrl_t->msm_sd.sd, ois_ctrl_t);
+ ois_ctrl_t->msm_sd.sd.internal_ops = &msm_ois_internal_ops;
+ ois_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&ois_ctrl_t->msm_sd.sd.entity, 0, NULL);
+ ois_ctrl_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ois_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
+ msm_sd_register(&ois_ctrl_t->msm_sd);
+ ois_ctrl_t->ois_state = OIS_DISABLE_STATE;
+ pr_info("msm_ois_i2c_probe: succeeded\n");
+ CDBG("Exit\n");
+
+probe_failure:
+ kfree(ois_ctrl_t);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ois_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ long rc = 0;
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ struct msm_ois_cfg_data32 *u32;
+ struct msm_ois_cfg_data ois_data;
+ void *parg;
+ struct msm_camera_i2c_seq_reg_setting settings;
+ struct msm_camera_i2c_seq_reg_setting32 settings32;
+
+ if (!file || !arg) {
+ pr_err("%s:failed NULL parameter\n", __func__);
+ return -EINVAL;
+ }
+ vdev = video_devdata(file);
+ sd = vdev_to_v4l2_subdev(vdev);
+ u32 = (struct msm_ois_cfg_data32 *)arg;
+ parg = arg;
+
+ switch (cmd) {
+ case VIDIOC_MSM_OIS_CFG32:
+ cmd = VIDIOC_MSM_OIS_CFG;
+ ois_data.cfgtype = u32->cfgtype;
+
+ switch (u32->cfgtype) {
+ case CFG_OIS_CONTROL:
+ ois_data.cfg.set_info.ois_params.setting_size =
+ u32->cfg.set_info.ois_params.setting_size;
+ ois_data.cfg.set_info.ois_params.i2c_addr =
+ u32->cfg.set_info.ois_params.i2c_addr;
+ ois_data.cfg.set_info.ois_params.i2c_freq_mode =
+ u32->cfg.set_info.ois_params.i2c_freq_mode;
+ ois_data.cfg.set_info.ois_params.i2c_addr_type =
+ u32->cfg.set_info.ois_params.i2c_addr_type;
+ ois_data.cfg.set_info.ois_params.i2c_data_type =
+ u32->cfg.set_info.ois_params.i2c_data_type;
+ ois_data.cfg.set_info.ois_params.settings =
+ compat_ptr(u32->cfg.set_info.ois_params.
+ settings);
+ parg = &ois_data;
+ break;
+ case CFG_OIS_I2C_WRITE_SEQ_TABLE:
+ if (copy_from_user(&settings32,
+ (void __user *)compat_ptr(u32->cfg.settings),
+ sizeof(
+ struct msm_camera_i2c_seq_reg_setting32))) {
+ pr_err("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ settings.addr_type = settings32.addr_type;
+ settings.delay = settings32.delay;
+ settings.size = settings32.size;
+
+ settings.reg_setting =
+ kzalloc(
+ sizeof(struct msm_camera_i2c_seq_reg_array),
+ GFP_KERNEL);
+ if (!settings.reg_setting)
+ return -ENOMEM;
+ if (copy_from_user(settings.reg_setting,
+ (void __user *)
+ compat_ptr(settings32.reg_setting),
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ ois_data.cfg.settings = &settings;
+ parg = &ois_data;
+ break;
+ default:
+ parg = &ois_data;
+ break;
+ }
+ break;
+ case VIDIOC_MSM_OIS_CFG:
+ pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
+ return -EINVAL;
+ }
+ rc = msm_ois_subdev_ioctl(sd, cmd, parg);
+
+ return rc;
+}
+
+static long msm_ois_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ois_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_ois_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+ struct msm_ois_ctrl_t *msm_ois_t = NULL;
+ struct msm_ois_vreg *vreg_cfg;
+
+ CDBG("Enter\n");
+
+ if (!pdev->dev.of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ msm_ois_t = kzalloc(sizeof(struct msm_ois_ctrl_t),
+ GFP_KERNEL);
+ if (!msm_ois_t)
+ return -ENOMEM;
+
+ msm_ois_t->oboard_info = kzalloc(sizeof(
+ struct msm_ois_board_info), GFP_KERNEL);
+ if (!msm_ois_t->oboard_info) {
+ kfree(msm_ois_t);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index",
+ &pdev->id);
+ CDBG("cell-index %d, rc %d\n", pdev->id, rc);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto release_memory;
+ }
+
+ rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master",
+ &msm_ois_t->cci_master);
+ CDBG("qcom,cci-master %d, rc %d\n", msm_ois_t->cci_master, rc);
+ if (rc < 0 || msm_ois_t->cci_master >= MASTER_MAX) {
+ pr_err("failed rc %d\n", rc);
+ goto release_memory;
+ }
+
+ if (of_find_property((&pdev->dev)->of_node,
+ "qcom,cam-vreg-name", NULL)) {
+ vreg_cfg = &msm_ois_t->vreg_cfg;
+ rc = msm_camera_get_dt_vreg_data((&pdev->dev)->of_node,
+ &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ goto release_memory;
+ }
+ }
+
+ rc = msm_sensor_driver_get_gpio_data(&(msm_ois_t->gconf),
+ (&pdev->dev)->of_node);
+ if (rc < 0) {
+ pr_err("%s: No/Error OIS GPIO\n", __func__);
+ } else {
+ msm_ois_t->cam_pinctrl_status = 1;
+ rc = msm_camera_pinctrl_init(
+ &(msm_ois_t->pinctrl_info), &(pdev->dev));
+ if (rc < 0) {
+ pr_err("ERR:%s: Error in reading OIS pinctrl\n",
+ __func__);
+ msm_ois_t->cam_pinctrl_status = 0;
+ }
+ }
+
+ msm_ois_t->ois_v4l2_subdev_ops = &msm_ois_subdev_ops;
+ msm_ois_t->ois_mutex = &msm_ois_mutex;
+
+ /* Set platform device handle */
+ msm_ois_t->pdev = pdev;
+ /* Set device type as platform device */
+ msm_ois_t->ois_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ msm_ois_t->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ msm_ois_t->i2c_client.cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!msm_ois_t->i2c_client.cci_client) {
+ kfree(msm_ois_t->vreg_cfg.cam_vreg);
+ rc = -ENOMEM;
+ goto release_memory;
+ }
+
+ cci_client = msm_ois_t->i2c_client.cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = msm_ois_t->cci_master;
+ v4l2_subdev_init(&msm_ois_t->msm_sd.sd,
+ msm_ois_t->ois_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&msm_ois_t->msm_sd.sd, msm_ois_t);
+ msm_ois_t->msm_sd.sd.internal_ops = &msm_ois_internal_ops;
+ msm_ois_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(msm_ois_t->msm_sd.sd.name,
+ ARRAY_SIZE(msm_ois_t->msm_sd.sd.name), "msm_ois");
+ media_entity_pads_init(&msm_ois_t->msm_sd.sd.entity, 0, NULL);
+ msm_ois_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_ois_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
+ msm_sd_register(&msm_ois_t->msm_sd);
+ msm_ois_t->ois_state = OIS_DISABLE_STATE;
+ msm_cam_copy_v4l2_subdev_fops(&msm_ois_v4l2_subdev_fops);
+#ifdef CONFIG_COMPAT
+ msm_ois_v4l2_subdev_fops.compat_ioctl32 =
+ msm_ois_subdev_fops_ioctl;
+#endif
+ msm_ois_t->msm_sd.sd.devnode->fops =
+ &msm_ois_v4l2_subdev_fops;
+
+ CDBG("Exit\n");
+ return rc;
+release_memory:
+ kfree(msm_ois_t->oboard_info);
+ kfree(msm_ois_t);
+ return rc;
+}
+
+static const struct of_device_id msm_ois_i2c_dt_match[] = {
+ {.compatible = "qcom,ois"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ois_i2c_dt_match);
+
+static struct i2c_driver msm_ois_i2c_driver = {
+ .id_table = msm_ois_i2c_id,
+ .probe = msm_ois_i2c_probe,
+ .remove = __exit_p(msm_ois_i2c_remove),
+ .driver = {
+ .name = "qcom,ois",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ois_i2c_dt_match,
+ },
+};
+
+static const struct of_device_id msm_ois_dt_match[] = {
+ {.compatible = "qcom,ois", .data = NULL},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ois_dt_match);
+
+static struct platform_driver msm_ois_platform_driver = {
+ .probe = msm_ois_platform_probe,
+ .driver = {
+ .name = "qcom,ois",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ois_dt_match,
+ },
+};
+
+static int __init msm_ois_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_ois_platform_driver);
+ if (!rc)
+ return rc;
+ CDBG("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&msm_ois_i2c_driver);
+}
+
+static void __exit msm_ois_exit_module(void)
+{
+ platform_driver_unregister(&msm_ois_platform_driver);
+ i2c_del_driver(&msm_ois_i2c_driver);
+}
+
+module_init(msm_ois_init_module);
+module_exit(msm_ois_exit_module);
+MODULE_DESCRIPTION("MSM OIS");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h
new file mode 100644
index 0000000..9cfd00c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2014-2016, 2018, 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_OIS_H
+#define MSM_OIS_H
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <soc/qcom/camera2.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+#define MSM_OIS_MAX_VREGS (10)
+
+struct msm_ois_ctrl_t;
+
+enum msm_ois_state_t {
+ OIS_ENABLE_STATE,
+ OIS_OPS_ACTIVE,
+ OIS_OPS_INACTIVE,
+ OIS_DISABLE_STATE,
+};
+
+struct msm_ois_vreg {
+ struct camera_vreg_t *cam_vreg;
+ void *data[MSM_OIS_MAX_VREGS];
+ int num_vreg;
+};
+
+struct msm_ois_board_info {
+ char ois_name[MAX_OIS_NAME_SIZE];
+ uint32_t i2c_slaveaddr;
+ struct msm_ois_opcode opcode;
+};
+
+struct msm_ois_ctrl_t {
+ struct i2c_driver *i2c_driver;
+ struct platform_driver *pdriver;
+ struct platform_device *pdev;
+ struct msm_camera_i2c_client i2c_client;
+ enum msm_camera_device_type_t ois_device_type;
+ struct msm_sd_subdev msm_sd;
+ struct mutex *ois_mutex;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ struct v4l2_subdev sdev;
+ struct v4l2_subdev_ops *ois_v4l2_subdev_ops;
+ void *user_data;
+ uint16_t i2c_tbl_index;
+ enum cci_i2c_master_t cci_master;
+ uint32_t subdev_id;
+ enum msm_ois_state_t ois_state;
+ struct msm_ois_vreg vreg_cfg;
+ struct msm_camera_gpio_conf *gconf;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+ struct msm_ois_board_info *oboard_info;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index b6f206e..3636aa1 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -575,9 +575,12 @@ static int get_hfi_extradata_index(enum hal_extradata_id index)
case HAL_EXTRADATA_STREAM_USERDATA:
ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA;
break;
- case HAL_EXTRADATA_FRAME_QP:
+ case HAL_EXTRADATA_DEC_FRAME_QP:
ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA;
break;
+ case HAL_EXTRADATA_ENC_FRAME_QP:
+ ret = HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA;
+ break;
case HAL_EXTRADATA_FRAME_BITS_INFO:
ret = HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA;
break;
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index a80990c..e627ee4 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -675,7 +675,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_ROI_QP,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -695,7 +695,8 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
(1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
- (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)|
+ (1UL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP)
),
.qmenu = mpeg_video_vidc_extradata,
},
@@ -1803,6 +1804,7 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
break;
case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+ case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP:
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
inst->bufq[CAPTURE_PORT].num_planes = 2;
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 5938bd3..a6af12a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -70,6 +70,7 @@ const char *const mpeg_video_vidc_extradata[] = {
"Extradata display VUI",
"Extradata vpx color space",
"Extradata UBWC CR stats info",
+ "Extradata enc frame QP"
};
static void handle_session_error(enum hal_command_response cmd, void *data);
@@ -5240,7 +5241,10 @@ enum hal_extradata_id msm_comm_get_hal_extradata_index(
ret = HAL_EXTRADATA_STREAM_USERDATA;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP:
- ret = HAL_EXTRADATA_FRAME_QP;
+ ret = HAL_EXTRADATA_DEC_FRAME_QP;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP:
+ ret = HAL_EXTRADATA_ENC_FRAME_QP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO:
ret = HAL_EXTRADATA_FRAME_BITS_INFO;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 38c42ca..90253fd 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -429,7 +429,7 @@ struct msm_vidc_ctrl {
s64 maximum;
s64 default_value;
u32 step;
- u32 menu_skip_mask;
+ u64 menu_skip_mask;
u32 flags;
const char * const *qmenu;
};
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 54cbdfc..2d9f3f1 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -111,7 +111,8 @@ enum hal_extradata_id {
HAL_EXTRADATA_ASPECT_RATIO,
HAL_EXTRADATA_MPEG2_SEQDISP,
HAL_EXTRADATA_STREAM_USERDATA,
- HAL_EXTRADATA_FRAME_QP,
+ HAL_EXTRADATA_DEC_FRAME_QP,
+ HAL_EXTRADATA_ENC_FRAME_QP,
HAL_EXTRADATA_FRAME_BITS_INFO,
HAL_EXTRADATA_INPUT_CROP,
HAL_EXTRADATA_DIGITAL_ZOOM,
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 5ed9b72..0abc7a3 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1916,7 +1916,11 @@ static long qfprom_read(struct device *dev, const char *name)
err = PTR_ERR(buf);
}
} else {
- val = *buf;
+ /*
+ * 30 bits from bit offset 0 would be read.
+ * We're interested in bits 28:29
+ */
+ val = (*buf >> 28) & 0x3;
kfree(buf);
}
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index e20ddba..bf498f9 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -3230,6 +3230,18 @@ int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs)
return ret;
}
+/**
+ * ipa_pm_is_used() - Returns if IPA PM framework is used
+ */
+bool ipa_pm_is_used(void)
+{
+ bool ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_pm_is_used);
+
+ return ret;
+}
+
static const struct dev_pm_ops ipa_pm_ops = {
.suspend_noirq = ipa_ap_suspend,
.resume_noirq = ipa_ap_resume,
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index f3e62b8..79d0c70 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -421,6 +421,8 @@ struct ipa_api_controller {
int (*ipa_get_smmu_params)(struct ipa_smmu_in_params *in,
struct ipa_smmu_out_params *out);
int (*ipa_is_vlan_mode)(enum ipa_vlan_ifaces iface, bool *res);
+
+ bool (*ipa_pm_is_used)(void);
};
#ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 52ffa52..78d1c96 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -3925,7 +3925,6 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
ipa_ctx->skip_uc_pipe_reset = resource_p->skip_uc_pipe_reset;
ipa_ctx->use_dma_zone = resource_p->use_dma_zone;
ipa_ctx->tethered_flow_control = resource_p->tethered_flow_control;
- ipa_ctx->use_ipa_pm = resource_p->use_ipa_pm;
/* Setting up IPA RX Polling Timeout Seconds */
ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec,
@@ -4451,20 +4450,12 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
return result;
}
-bool ipa_pm_is_used(void)
-{
- return (ipa_ctx) ? ipa_ctx->use_ipa_pm : false;
-}
-
static int get_ipa_dts_configuration(struct platform_device *pdev,
struct ipa_plat_drv_res *ipa_drv_res)
{
int result;
struct resource *resource;
- ipa_drv_res->use_ipa_pm = of_property_read_bool(pdev->dev.of_node,
- "qcom,use-ipa-pm");
- IPADBG("use_ipa_pm=%d\n", ipa_drv_res->use_ipa_pm);
/* initialize ipa_res */
ipa_drv_res->ipa_pipe_mem_start_ofst = IPA_PIPE_MEM_START_OFST;
ipa_drv_res->ipa_pipe_mem_size = IPA_PIPE_MEM_SIZE;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index ec4942f..91017a5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -1206,7 +1206,6 @@ struct ipa_context {
int num_ipa_cne_evt_req;
struct mutex ipa_cne_evt_lock;
bool ipa_uc_monitor_holb;
- bool use_ipa_pm;
};
/**
@@ -1263,7 +1262,6 @@ struct ipa_plat_drv_res {
u32 ipa_rx_polling_sleep_msec;
u32 ipa_polling_iteration;
bool ipa_uc_monitor_holb;
- bool use_ipa_pm;
};
struct ipa_mem_partition {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index e06f6cf..27120c8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -4984,6 +4984,11 @@ static void ipa2_set_tag_process_before_gating(bool val)
ipa_ctx->tag_process_before_gating = val;
}
+static bool ipa2_pm_is_used(void)
+{
+ return false;
+}
+
int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
struct ipa_api_controller *api_ctrl)
{
@@ -5159,6 +5164,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes;
api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes;
api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes;
+ api_ctrl->ipa_pm_is_used = ipa2_pm_is_used;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 068c6c5..c86b0df 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -773,7 +773,7 @@ static int find_vchannel_name_index(const char *vchannel_name)
{
int i;
- for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) {
+ for (i = 0; i < rmnet_index; i++) {
if (strcmp(mux_channel[i].vchannel_name, vchannel_name) == 0)
return i;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index da3b399..4488df3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -4394,8 +4394,12 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
/* Prevent consequent calls from trying to load the FW again. */
if (ipa3_ctx->ipa_initialization_complete)
return 0;
+
+ IPADBG("active clients = %d\n",
+ atomic_read(&ipa3_ctx->ipa3_active_clients.cnt));
/* move proxy vote for modem on ipa3_post_init */
- IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE");
+ if (ipa3_ctx->ipa_hw_type != IPA_HW_v4_0)
+ ipa3_proxy_clk_vote();
/*
* indication whether working in MHI config or non MHI config is given
@@ -4532,9 +4536,6 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
ipa3_register_panic_hdlr();
- ipa3_ctx->q6_proxy_clk_vote_valid = true;
- ipa3_ctx->q6_proxy_clk_vote_cnt++;
-
mutex_lock(&ipa3_ctx->lock);
ipa3_ctx->ipa_initialization_complete = true;
mutex_unlock(&ipa3_ctx->lock);
@@ -4551,6 +4552,7 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
gsi_deregister_device(ipa3_ctx->gsi_dev_hdl, false);
fail_register_device:
ipa3_destroy_flt_tbl_idrs();
+ ipa3_proxy_clk_unvote();
return result;
}
@@ -4860,6 +4862,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
int result = 0;
int i;
struct ipa3_rt_tbl_set *rset;
+ struct ipa_active_client_logging_info log_info;
IPADBG("IPA Driver initialization started\n");
@@ -5050,7 +5053,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
}
mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
- /* move proxy vote for modem to ipa3_post_init() */
+
+ IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
+ ipa3_active_clients_log_inc(&log_info, false);
+ ipa3_ctx->q6_proxy_clk_vote_valid = true;
+ ipa3_ctx->q6_proxy_clk_vote_cnt = 1;
+
+ /*Updating the proxy vote cnt 1 */
atomic_set(&ipa3_ctx->ipa3_active_clients.cnt, 1);
/* Create workqueues for power management */
@@ -5181,7 +5190,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
mutex_init(&ipa3_ctx->lock);
mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex);
mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
- ipa3_ctx->q6_proxy_clk_vote_cnt = 0;
idr_init(&ipa3_ctx->ipa_idr);
spin_lock_init(&ipa3_ctx->idr_lock);
@@ -5296,8 +5304,14 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
IPADBG("ipa cdev added successful. major:%d minor:%d\n",
MAJOR(ipa3_ctx->dev_num),
MINOR(ipa3_ctx->dev_num));
+ /*
+ * for IPA 4.0 offline charge is not needed and we need to prevent
+ * power collapse until IPA uC is loaded.
+ */
+
/* proxy vote for modem is added in ipa3_post_init() phase */
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ if (ipa3_ctx->ipa_hw_type != IPA_HW_v4_0)
+ ipa3_proxy_clk_unvote();
return 0;
fail_cdev_add:
@@ -5381,11 +5395,6 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
return result;
}
-bool ipa_pm_is_used(void)
-{
- return (ipa3_ctx) ? ipa3_ctx->use_ipa_pm : false;
-}
-
static int get_ipa_dts_pm_info(struct platform_device *pdev,
struct ipa3_plat_drv_res *ipa_drv_res)
{
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 9974b87..b3726e1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -4168,9 +4168,8 @@ bool ipa3_is_client_handle_valid(u32 clnt_hdl)
*/
void ipa3_proxy_clk_unvote(void)
{
- if (!ipa3_is_ready())
+ if (ipa3_ctx == NULL)
return;
-
mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
if (ipa3_ctx->q6_proxy_clk_vote_valid) {
IPA_ACTIVE_CLIENTS_DEC_SPECIAL("PROXY_CLK_VOTE");
@@ -4188,9 +4187,8 @@ void ipa3_proxy_clk_unvote(void)
*/
void ipa3_proxy_clk_vote(void)
{
- if (!ipa3_is_ready())
+ if (ipa3_ctx == NULL)
return;
-
mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
if (!ipa3_ctx->q6_proxy_clk_vote_valid ||
(ipa3_ctx->q6_proxy_clk_vote_cnt > 0)) {
@@ -4336,6 +4334,11 @@ static int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
return 0;
}
+static bool ipa3_pm_is_used(void)
+{
+ return (ipa3_ctx) ? ipa3_ctx->use_ipa_pm : false;
+}
+
int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
struct ipa_api_controller *api_ctrl)
{
@@ -4523,6 +4526,7 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_tz_unlock_reg = ipa3_tz_unlock_reg;
api_ctrl->ipa_get_smmu_params = ipa3_get_smmu_params;
api_ctrl->ipa_is_vlan_mode = ipa3_is_vlan_mode;
+ api_ctrl->ipa_pm_is_used = ipa3_pm_is_used;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 2d86200..23dafe1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -802,7 +802,7 @@ static int find_vchannel_name_index(const char *vchannel_name)
{
int i;
- for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) {
+ for (i = 0; i < rmnet_ipa3_ctx->rmnet_index; i++) {
if (strcmp(rmnet_ipa3_ctx->mux_channel[i].vchannel_name,
vchannel_name) == 0)
return i;
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 93692fa..f4a36a3 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -324,6 +324,9 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(parallel_fcc_max),
POWER_SUPPLY_ATTR(min_icl),
POWER_SUPPLY_ATTR(moisture_detected),
+ POWER_SUPPLY_ATTR(batt_profile_version),
+ POWER_SUPPLY_ATTR(batt_full_current),
+ POWER_SUPPLY_ATTR(recharge_soc),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index c8e731a..e653475 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -115,4 +115,13 @@
USB power-delivery. The driver adds support to report these type-C
parameters via the power-supply framework.
+config QPNP_QG
+ bool "QPNP Qgauge driver"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here to enable the Qualcomm Technologies, Inc. QGauge driver
+ which uses the periodic sampling of the battery voltage and current
+ to determine the battery state-of-charge (SOC) and supports other
+ battery management features.
+
endmenu
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 870a67e..6c7fc78 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
+obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
obj-$(CONFIG_QPNP_TYPEC) += qpnp-typec.o
obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o
diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c
new file mode 100644
index 0000000..b86c7f5
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-battery-profile.c
@@ -0,0 +1,503 @@
+/* Copyright (c) 2018 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) "QG-K: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/qg-profile.h>
+#include "qg-profile-lib.h"
+#include "qg-defs.h"
+
+struct qg_battery_data {
+ /* battery-data class node */
+ dev_t dev_no;
+ struct class *battery_class;
+ struct device *battery_device;
+ struct cdev battery_cdev;
+
+ /* profile */
+ struct device_node *profile_node;
+ struct profile_table_data profile[TABLE_MAX];
+};
+
+struct tables {
+ int table_index;
+ char *table_name;
+};
+
+static struct tables table[] = {
+ {TABLE_SOC_OCV1, "qcom,pc-temp-v1-lut"},
+ {TABLE_SOC_OCV2, "qcom,pc-temp-v2-lut"},
+ {TABLE_FCC1, "qcom,fcc1-temp-lut"},
+ {TABLE_FCC2, "qcom,fcc2-temp-lut"},
+ {TABLE_Z1, "qcom,pc-temp-z1-lut"},
+ {TABLE_Z2, "qcom,pc-temp-z2-lut"},
+ {TABLE_Z3, "qcom,pc-temp-z3-lut"},
+ {TABLE_Z4, "qcom,pc-temp-z4-lut"},
+ {TABLE_Z5, "qcom,pc-temp-z5-lut"},
+ {TABLE_Z6, "qcom,pc-temp-z6-lut"},
+ {TABLE_Y1, "qcom,pc-temp-y1-lut"},
+ {TABLE_Y2, "qcom,pc-temp-y2-lut"},
+ {TABLE_Y3, "qcom,pc-temp-y3-lut"},
+ {TABLE_Y4, "qcom,pc-temp-y4-lut"},
+ {TABLE_Y5, "qcom,pc-temp-y5-lut"},
+ {TABLE_Y6, "qcom,pc-temp-y6-lut"},
+};
+
+static struct qg_battery_data *the_battery;
+
+static int qg_battery_data_open(struct inode *inode, struct file *file)
+{
+ struct qg_battery_data *battery = container_of(inode->i_cdev,
+ struct qg_battery_data, battery_cdev);
+
+ file->private_data = battery;
+
+ return 0;
+}
+
+static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct qg_battery_data *battery = file->private_data;
+ struct battery_params __user *bp_user =
+ (struct battery_params __user *)arg;
+ struct battery_params bp;
+ int rc = 0, soc, ocv_uv, fcc_mah, var, slope;
+
+ if (!battery->profile_node) {
+ pr_err("Battery data not set!\n");
+ return -EINVAL;
+ }
+
+ if (!bp_user) {
+ pr_err("Invalid battery-params user pointer\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&bp, bp_user, sizeof(bp))) {
+ pr_err("Failed in copy_from_user\n");
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case BPIOCXSOC:
+ if (bp.table_index != TABLE_SOC_OCV1 &&
+ bp.table_index != TABLE_SOC_OCV2) {
+ pr_err("Invalid table index %d for SOC-OCV lookup\n",
+ bp.table_index);
+ rc = -EINVAL;
+ } else {
+ /* OCV is passed as deci-uV - 10^-4 V */
+ soc = interpolate_soc(&battery->profile[bp.table_index],
+ bp.batt_temp, UV_TO_DECIUV(bp.ocv_uv));
+ soc = CAP(QG_MIN_SOC, QG_MAX_SOC, soc);
+ rc = put_user(soc, &bp_user->soc);
+ if (rc < 0) {
+ pr_err("BPIOCXSOC: Failed rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSOC: lut=%s ocv=%d batt_temp=%d soc=%d\n",
+ battery->profile[bp.table_index].name,
+ bp.ocv_uv, bp.batt_temp, soc);
+ }
+ break;
+ case BPIOCXOCV:
+ if (bp.table_index != TABLE_SOC_OCV1 &&
+ bp.table_index != TABLE_SOC_OCV2) {
+ pr_err("Invalid table index %d for SOC-OCV lookup\n",
+ bp.table_index);
+ rc = -EINVAL;
+ } else {
+ ocv_uv = interpolate_var(
+ &battery->profile[bp.table_index],
+ bp.batt_temp, bp.soc);
+ ocv_uv = DECIUV_TO_UV(ocv_uv);
+ ocv_uv = CAP(QG_MIN_OCV_UV, QG_MAX_OCV_UV, ocv_uv);
+ rc = put_user(ocv_uv, &bp_user->ocv_uv);
+ if (rc < 0) {
+ pr_err("BPIOCXOCV: Failed rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXOCV: lut=%s ocv=%d batt_temp=%d soc=%d\n",
+ battery->profile[bp.table_index].name,
+ ocv_uv, bp.batt_temp, bp.soc);
+ }
+ break;
+ case BPIOCXFCC:
+ if (bp.table_index != TABLE_FCC1 &&
+ bp.table_index != TABLE_FCC2) {
+ pr_err("Invalid table index %d for FCC lookup\n",
+ bp.table_index);
+ rc = -EINVAL;
+ } else {
+ fcc_mah = interpolate_single_row_lut(
+ &battery->profile[bp.table_index],
+ bp.batt_temp, DEGC_SCALE);
+ fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
+ rc = put_user(fcc_mah, &bp_user->fcc_mah);
+ if (rc) {
+ pr_err("BPIOCXFCC: Failed rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXFCC: lut=%s batt_temp=%d fcc_mah=%d\n",
+ battery->profile[bp.table_index].name,
+ bp.batt_temp, fcc_mah);
+ }
+ break;
+ case BPIOCXVAR:
+ if (bp.table_index < TABLE_Z1 || bp.table_index > TABLE_MAX) {
+ pr_err("Invalid table index %d for VAR lookup\n",
+ bp.table_index);
+ rc = -EINVAL;
+ } else {
+ var = interpolate_var(&battery->profile[bp.table_index],
+ bp.batt_temp, bp.soc);
+ var = CAP(QG_MIN_VAR, QG_MAX_VAR, var);
+ rc = put_user(var, &bp_user->var);
+ if (rc < 0) {
+ pr_err("BPIOCXVAR: Failed rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXVAR: lut=%s var=%d batt_temp=%d soc=%d\n",
+ battery->profile[bp.table_index].name,
+ var, bp.batt_temp, bp.soc);
+ }
+ break;
+ case BPIOCXSLOPE:
+ if (bp.table_index != TABLE_SOC_OCV1 &&
+ bp.table_index != TABLE_SOC_OCV2) {
+ pr_err("Invalid table index %d for Slope lookup\n",
+ bp.table_index);
+ rc = -EINVAL;
+ } else {
+ slope = interpolate_slope(
+ &battery->profile[bp.table_index],
+ bp.batt_temp, bp.soc);
+ slope = CAP(QG_MIN_SLOPE, QG_MAX_SLOPE, slope);
+ rc = put_user(slope, &bp_user->slope);
+ if (rc) {
+ pr_err("BPIOCXSLOPE: Failed rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSLOPE: lut=%s soc=%d batt_temp=%d slope=%d\n",
+ battery->profile[bp.table_index].name,
+ bp.soc, bp.batt_temp, slope);
+ }
+ break;
+ default:
+ pr_err("IOCTL %d not supported\n", cmd);
+ rc = -EINVAL;
+ }
+ret_err:
+ return rc;
+}
+
+static int qg_battery_data_release(struct inode *inode, struct file *file)
+{
+ pr_debug("battery_data device closed\n");
+
+ return 0;
+}
+
+static const struct file_operations qg_battery_data_fops = {
+ .owner = THIS_MODULE,
+ .open = qg_battery_data_open,
+ .unlocked_ioctl = qg_battery_data_ioctl,
+ .compat_ioctl = qg_battery_data_ioctl,
+ .release = qg_battery_data_release,
+};
+
+static int get_length(struct device_node *node,
+ int *length, char *prop_name, bool ignore_null)
+{
+ struct property *prop;
+
+ prop = of_find_property(node, prop_name, NULL);
+ if (!prop) {
+ if (ignore_null) {
+ *length = 1;
+ return 0;
+ }
+ pr_err("Failed to find %s property\n", prop_name);
+ return -ENODATA;
+ } else if (!prop->value) {
+ pr_err("Failed to find value for %s property\n", prop_name);
+ return -ENODATA;
+ }
+
+ *length = prop->length / sizeof(u32);
+
+ return 0;
+}
+
+static int qg_parse_battery_profile(struct qg_battery_data *battery)
+{
+ int i, j, k, rows = 0, cols = 0, lut_length = 0, rc = 0;
+ struct device_node *node;
+ struct property *prop;
+ const __be32 *data;
+
+ for (i = 0; i < TABLE_MAX; i++) {
+ node = of_find_node_by_name(battery->profile_node,
+ table[i].table_name);
+ if (!node) {
+ pr_err("%s table not found\n", table[i].table_name);
+ rc = -ENODEV;
+ goto cleanup;
+ }
+
+ rc = get_length(node, &cols, "qcom,lut-col-legend", false);
+ if (rc < 0) {
+ pr_err("Failed to get col-length for %s table rc=%d\n",
+ table[i].table_name, rc);
+ goto cleanup;
+ }
+
+ rc = get_length(node, &rows, "qcom,lut-row-legend", true);
+ if (rc < 0) {
+ pr_err("Failed to get row-length for %s table rc=%d\n",
+ table[i].table_name, rc);
+ goto cleanup;
+ }
+
+ rc = get_length(node, &lut_length, "qcom,lut-data", false);
+ if (rc < 0) {
+ pr_err("Failed to get lut-length for %s table rc=%d\n",
+ table[i].table_name, rc);
+ goto cleanup;
+ }
+
+ if (lut_length != cols * rows) {
+ pr_err("Invalid lut-length for %s table\n",
+ table[i].table_name);
+ rc = -EINVAL;
+ goto cleanup;
+ }
+
+ battery->profile[i].name = kzalloc(strlen(table[i].table_name)
+ + 1, GFP_KERNEL);
+ if (!battery->profile[i].name) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ strlcpy(battery->profile[i].name, table[i].table_name,
+ strlen(table[i].table_name));
+ battery->profile[i].rows = rows;
+ battery->profile[i].cols = cols;
+
+ if (rows != 1) {
+ battery->profile[i].row_entries = kcalloc(rows,
+ sizeof(*battery->profile[i].row_entries),
+ GFP_KERNEL);
+ if (!battery->profile[i].row_entries) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+
+ battery->profile[i].col_entries = kcalloc(cols,
+ sizeof(*battery->profile[i].col_entries),
+ GFP_KERNEL);
+ if (!battery->profile[i].col_entries) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ battery->profile[i].data = kcalloc(rows,
+ sizeof(*battery->profile[i].data), GFP_KERNEL);
+ if (!battery->profile[i].data) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ for (j = 0; j < rows; j++) {
+ battery->profile[i].data[j] = kcalloc(cols,
+ sizeof(**battery->profile[i].data),
+ GFP_KERNEL);
+ if (!battery->profile[i].data[j]) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+
+ /* read profile data */
+ rc = of_property_read_u32_array(node, "qcom,lut-col-legend",
+ battery->profile[i].col_entries, cols);
+ if (rc < 0) {
+ pr_err("Failed to read cols values for table %s rc=%d\n",
+ table[i].table_name, rc);
+ goto cleanup;
+ }
+
+ if (rows != 1) {
+ rc = of_property_read_u32_array(node,
+ "qcom,lut-row-legend",
+ battery->profile[i].row_entries, rows);
+ if (rc < 0) {
+ pr_err("Failed to read row values for table %s rc=%d\n",
+ table[i].table_name, rc);
+ goto cleanup;
+ }
+ }
+
+ prop = of_find_property(node, "qcom,lut-data", NULL);
+ if (!prop) {
+ pr_err("Failed to find lut-data\n");
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ data = prop->value;
+ for (j = 0; j < rows; j++) {
+ for (k = 0; k < cols; k++)
+ battery->profile[i].data[j][k] =
+ be32_to_cpup(data++);
+ }
+
+ pr_debug("Profile table %s parsed rows=%d cols=%d\n",
+ battery->profile[i].name, battery->profile[i].rows,
+ battery->profile[i].cols);
+ }
+
+ return 0;
+
+cleanup:
+ for (; i >= 0; i++) {
+ kfree(battery->profile[i].name);
+ kfree(battery->profile[i].row_entries);
+ kfree(battery->profile[i].col_entries);
+ for (j = 0; j < battery->profile[i].rows; j++) {
+ if (battery->profile[i].data)
+ kfree(battery->profile[i].data[j]);
+ }
+ kfree(battery->profile[i].data);
+ }
+ return rc;
+}
+
+int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging)
+{
+ u8 table_index = charging ? TABLE_SOC_OCV1 : TABLE_SOC_OCV2;
+
+ if (!the_battery || !the_battery->profile) {
+ pr_err("Battery profile not loaded\n");
+ return -ENODEV;
+ }
+
+ *soc = interpolate_soc(&the_battery->profile[table_index],
+ batt_temp, UV_TO_DECIUV(ocv_uv));
+
+ *soc = CAP(0, 100, DIV_ROUND_CLOSEST(*soc, 100));
+
+ return 0;
+}
+
+int qg_batterydata_init(struct device_node *profile_node)
+{
+ int rc = 0;
+ struct qg_battery_data *battery;
+
+ battery = kzalloc(sizeof(*battery), GFP_KERNEL);
+ if (!battery)
+ return -ENOMEM;
+
+ battery->profile_node = profile_node;
+
+ /* char device to access battery-profile data */
+ rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "qg_battery");
+ if (rc < 0) {
+ pr_err("Failed to allocate chrdev rc=%d\n", rc);
+ goto free_battery;
+ }
+
+ cdev_init(&battery->battery_cdev, &qg_battery_data_fops);
+ rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
+ if (rc) {
+ pr_err("Failed to add battery_cdev rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ battery->battery_class = class_create(THIS_MODULE, "qg_battery");
+ if (IS_ERR_OR_NULL(battery->battery_class)) {
+ pr_err("Failed to create qg-battery class\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ battery->battery_device = device_create(battery->battery_class,
+ NULL, battery->dev_no,
+ NULL, "qg_battery");
+ if (IS_ERR_OR_NULL(battery->battery_device)) {
+ pr_err("Failed to create battery_device device\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ /* parse the battery profile */
+ rc = qg_parse_battery_profile(battery);
+ if (rc < 0) {
+ pr_err("Failed to parse battery profile rc=%d\n", rc);
+ goto destroy_device;
+ }
+
+ the_battery = battery;
+
+ pr_info("QG Battery-profile loaded, '/dev/qg_battery' created!\n");
+
+ return 0;
+
+destroy_device:
+ device_destroy(battery->battery_class, battery->dev_no);
+delete_cdev:
+ cdev_del(&battery->battery_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(battery->dev_no, 1);
+free_battery:
+ kfree(battery);
+ return rc;
+}
+
+void qg_batterydata_exit(void)
+{
+ int i, j;
+
+ if (the_battery) {
+ /* unregister the device node */
+ device_destroy(the_battery->battery_class, the_battery->dev_no);
+ cdev_del(&the_battery->battery_cdev);
+ unregister_chrdev_region(the_battery->dev_no, 1);
+
+ /* delete all the battery profile memory */
+ for (i = 0; i < TABLE_MAX; i++) {
+ kfree(the_battery->profile[i].name);
+ kfree(the_battery->profile[i].row_entries);
+ kfree(the_battery->profile[i].col_entries);
+ for (j = 0; j < the_battery->profile[i].rows; j++) {
+ if (the_battery->profile[i].data)
+ kfree(the_battery->profile[i].data[j]);
+ }
+ kfree(the_battery->profile[i].data);
+ }
+ }
+
+ kfree(the_battery);
+ the_battery = NULL;
+}
diff --git a/drivers/power/supply/qcom/qg-battery-profile.h b/drivers/power/supply/qcom/qg-battery-profile.h
new file mode 100644
index 0000000..143a4f2
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-battery-profile.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2018 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 __QG_BATTERY_PROFILE_H__
+#define __QG_BATTERY_PROFILE_H__
+
+int qg_batterydata_init(struct device_node *node);
+void qg_batterydata_exit(void);
+int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging);
+
+#endif /* __QG_BATTERY_PROFILE_H__ */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
new file mode 100644
index 0000000..aadef7f
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -0,0 +1,143 @@
+/* Copyright (c) 2018 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 __QG_CORE_H__
+#define __QG_CORE_H__
+
+struct qg_batt_props {
+ const char *batt_type_str;
+ int float_volt_uv;
+ int vbatt_full_mv;
+ int fastchg_curr_ma;
+ int qg_profile_version;
+};
+
+struct qg_irq_info {
+ const char *name;
+ const irq_handler_t handler;
+ const bool wake;
+ int irq;
+};
+
+struct qg_dt {
+ int vbatt_empty_mv;
+ int vbatt_low_mv;
+ int vbatt_cutoff_mv;
+ int iterm_ma;
+ int s2_fifo_length;
+ int s2_vbat_low_fifo_length;
+ int s2_acc_length;
+ int s2_acc_intvl_ms;
+ int ocv_timer_expiry_min;
+ int ocv_tol_threshold_uv;
+ int s3_entry_fifo_length;
+ int s3_entry_ibat_ua;
+ int s3_exit_ibat_ua;
+ int delta_soc;
+ int rbat_conn_mohm;
+};
+
+struct qpnp_qg {
+ struct device *dev;
+ struct pmic_revid_data *pmic_rev_id;
+ struct regmap *regmap;
+ struct qpnp_vadc_chip *vadc_dev;
+ struct power_supply *qg_psy;
+ struct class *qg_class;
+ struct device *qg_device;
+ struct cdev qg_cdev;
+ dev_t dev_no;
+ struct work_struct udata_work;
+ struct work_struct scale_soc_work;
+ struct work_struct qg_status_change_work;
+ struct notifier_block nb;
+ struct mutex bus_lock;
+ struct mutex data_lock;
+ struct mutex soc_lock;
+ wait_queue_head_t qg_wait_q;
+ struct votable *awake_votable;
+ struct votable *vbatt_irq_disable_votable;
+ struct votable *fifo_irq_disable_votable;
+ struct votable *good_ocv_irq_disable_votable;
+ u32 qg_base;
+
+ /* local data variables */
+ u32 batt_id_ohm;
+ struct qg_kernel_data kdata;
+ struct qg_user_data udata;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *parallel_psy;
+
+ /* status variable */
+ u32 *debug_mask;
+ bool profile_loaded;
+ bool battery_missing;
+ bool data_ready;
+ bool suspend_data;
+ bool vbat_low;
+ bool charge_done;
+ bool parallel_enabled;
+ bool usb_present;
+ int charge_status;
+ int charge_type;
+ int next_wakeup_ms;
+ u32 wa_flags;
+ ktime_t last_user_update_time;
+ ktime_t last_fifo_update_time;
+
+ /* soc params */
+ int catch_up_soc;
+ int msoc;
+ int pon_soc;
+ struct alarm alarm_timer;
+ u32 sdam_data[SDAM_MAX];
+
+ /* DT */
+ struct qg_dt dt;
+ struct qg_batt_props bp;
+};
+
+enum ocv_type {
+ PON_OCV,
+ GOOD_OCV,
+};
+
+enum debug_mask {
+ QG_DEBUG_PON = BIT(0),
+ QG_DEBUG_PROFILE = BIT(1),
+ QG_DEBUG_DEVICE = BIT(2),
+ QG_DEBUG_STATUS = BIT(3),
+ QG_DEBUG_FIFO = BIT(4),
+ QG_DEBUG_IRQ = BIT(5),
+ QG_DEBUG_SOC = BIT(6),
+ QG_DEBUG_PM = BIT(7),
+ QG_DEBUG_BUS_READ = BIT(8),
+ QG_DEBUG_BUS_WRITE = BIT(9),
+};
+
+enum qg_irq {
+ QG_BATT_MISSING_IRQ,
+ QG_VBATT_LOW_IRQ,
+ QG_VBATT_EMPTY_IRQ,
+ QG_FIFO_UPDATE_DONE_IRQ,
+ QG_GOOD_OCV_IRQ,
+ QG_FSM_STAT_CHG_IRQ,
+ QG_EVENT_IRQ,
+ QG_MAX_IRQ,
+};
+
+enum qg_wa_flags {
+ QG_VBAT_LOW_WA = BIT(0),
+};
+
+
+#endif /* __QG_CORE_H__ */
diff --git a/drivers/power/supply/qcom/qg-defs.h b/drivers/power/supply/qcom/qg-defs.h
new file mode 100644
index 0000000..bbbc7ee
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-defs.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2018 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 __QG_DEFS_H__
+#define __QG_DEFS_H__
+
+#define qg_dbg(chip, reason, fmt, ...) \
+ do { \
+ if (*chip->debug_mask & (reason)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+ else \
+ pr_debug(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define is_between(left, right, value) \
+ (((left) >= (right) && (left) >= (value) \
+ && (value) >= (right)) \
+ || ((left) <= (right) && (left) <= (value) \
+ && (value) <= (right)))
+
+#define UDATA_READY_VOTER "UDATA_READY_VOTER"
+#define FIFO_DONE_VOTER "FIFO_DONE_VOTER"
+#define FIFO_RT_DONE_VOTER "FIFO_RT_DONE_VOTER"
+#define SUSPEND_DATA_VOTER "SUSPEND_DATA_VOTER"
+#define GOOD_OCV_VOTER "GOOD_OCV_VOTER"
+#define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE"
+#define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE"
+
+#define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000)
+#define I_RAW_TO_UA(I_RAW) div_s64(152588LL * (s64)I_RAW, 1000)
+
+#define DEGC_SCALE 10
+#define UV_TO_DECIUV(a) (a / 100)
+#define DECIUV_TO_UV(a) (a * 100)
+
+#define CAP(min, max, value) \
+ ((min > value) ? min : ((value > max) ? max : value))
+
+#endif /* __QG_DEFS_H__ */
diff --git a/drivers/power/supply/qcom/qg-profile-lib.c b/drivers/power/supply/qcom/qg-profile-lib.c
new file mode 100644
index 0000000..2af997e
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-profile-lib.c
@@ -0,0 +1,311 @@
+/* Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include "qg-profile-lib.h"
+#include "qg-defs.h"
+
+static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
+{
+ if (y0 == y1 || x == x0)
+ return y0;
+ if (x1 == x0 || x == x1)
+ return y1;
+
+ return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
+}
+
+int interpolate_single_row_lut(struct profile_table_data *lut,
+ int x, int scale)
+{
+ int i, result;
+ int cols = lut->cols;
+
+ if (x < lut->col_entries[0] * scale) {
+ pr_debug("x %d less than known range return y = %d lut = %s\n",
+ x, lut->data[0][0], lut->name);
+ return lut->data[0][0];
+ }
+
+ if (x > lut->col_entries[cols-1] * scale) {
+ pr_debug("x %d more than known range return y = %d lut = %s\n",
+ x, lut->data[0][cols-1], lut->name);
+ return lut->data[0][cols-1];
+ }
+
+ for (i = 0; i < cols; i++) {
+ if (x <= lut->col_entries[i] * scale)
+ break;
+ }
+
+ if (x == lut->col_entries[i] * scale) {
+ result = lut->data[0][i];
+ } else {
+ result = linear_interpolate(
+ lut->data[0][i-1],
+ lut->col_entries[i-1] * scale,
+ lut->data[0][i],
+ lut->col_entries[i] * scale,
+ x);
+ }
+
+ return result;
+}
+
+int interpolate_soc(struct profile_table_data *lut,
+ int batt_temp, int ocv)
+{
+ int i, j, soc_high, soc_low, soc;
+ int rows = lut->rows;
+ int cols = lut->cols;
+
+ if (batt_temp < lut->col_entries[0] * DEGC_SCALE) {
+ pr_debug("batt_temp %d < known temp range\n", batt_temp);
+ batt_temp = lut->col_entries[0] * DEGC_SCALE;
+ }
+
+ if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE) {
+ pr_debug("batt_temp %d > known temp range\n", batt_temp);
+ batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
+ }
+
+ for (j = 0; j < cols; j++)
+ if (batt_temp <= lut->col_entries[j] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == lut->col_entries[j] * DEGC_SCALE) {
+ /* found an exact match for temp in the table */
+ if (ocv >= lut->data[0][j])
+ return lut->row_entries[0];
+ if (ocv <= lut->data[rows - 1][j])
+ return lut->row_entries[rows - 1];
+ for (i = 0; i < rows; i++) {
+ if (ocv >= lut->data[i][j]) {
+ if (ocv == lut->data[i][j])
+ return lut->row_entries[i];
+ soc = linear_interpolate(
+ lut->row_entries[i],
+ lut->data[i][j],
+ lut->row_entries[i - 1],
+ lut->data[i - 1][j],
+ ocv);
+ return soc;
+ }
+ }
+ }
+
+ /* batt_temp is within temperature for column j-1 and j */
+ if (ocv >= lut->data[0][j])
+ return lut->row_entries[0];
+ if (ocv <= lut->data[rows - 1][j - 1])
+ return lut->row_entries[rows - 1];
+
+ soc_low = soc_high = 0;
+ for (i = 0; i < rows-1; i++) {
+ if (soc_high == 0 && is_between(lut->data[i][j],
+ lut->data[i+1][j], ocv)) {
+ soc_high = linear_interpolate(
+ lut->row_entries[i],
+ lut->data[i][j],
+ lut->row_entries[i + 1],
+ lut->data[i+1][j],
+ ocv);
+ }
+
+ if (soc_low == 0 && is_between(lut->data[i][j-1],
+ lut->data[i+1][j-1], ocv)) {
+ soc_low = linear_interpolate(
+ lut->row_entries[i],
+ lut->data[i][j-1],
+ lut->row_entries[i + 1],
+ lut->data[i+1][j-1],
+ ocv);
+ }
+
+ if (soc_high && soc_low) {
+ soc = linear_interpolate(
+ soc_low,
+ lut->col_entries[j-1] * DEGC_SCALE,
+ soc_high,
+ lut->col_entries[j] * DEGC_SCALE,
+ batt_temp);
+ return soc;
+ }
+ }
+
+ if (soc_high)
+ return soc_high;
+
+ if (soc_low)
+ return soc_low;
+
+ pr_debug("%d ocv wasn't found for temp %d in the LUT %s returning 100%%\n",
+ ocv, batt_temp, lut->name);
+ return 10000;
+}
+
+int interpolate_var(struct profile_table_data *lut,
+ int batt_temp, int soc)
+{
+ int i, var1, var2, var, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+
+ rows = lut->rows;
+ cols = lut->cols;
+ if (soc > lut->row_entries[0]) {
+ pr_debug("soc %d greater than known soc ranges for %s lut\n",
+ soc, lut->name);
+ row1 = 0;
+ row2 = 0;
+ } else if (soc < lut->row_entries[rows - 1]) {
+ pr_debug("soc %d less than known soc ranges for %s lut\n",
+ soc, lut->name);
+ row1 = rows - 1;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (soc == lut->row_entries[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (soc > lut->row_entries[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < lut->col_entries[0] * DEGC_SCALE)
+ batt_temp = lut->col_entries[0] * DEGC_SCALE;
+ if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE)
+ batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= lut->col_entries[i] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == lut->col_entries[i] * DEGC_SCALE) {
+ var = linear_interpolate(
+ lut->data[row1][i],
+ lut->row_entries[row1],
+ lut->data[row2][i],
+ lut->row_entries[row2],
+ soc);
+ return var;
+ }
+
+ var1 = linear_interpolate(
+ lut->data[row1][i - 1],
+ lut->col_entries[i - 1] * DEGC_SCALE,
+ lut->data[row1][i],
+ lut->col_entries[i] * DEGC_SCALE,
+ batt_temp);
+
+ var2 = linear_interpolate(
+ lut->data[row2][i - 1],
+ lut->col_entries[i - 1] * DEGC_SCALE,
+ lut->data[row2][i],
+ lut->col_entries[i] * DEGC_SCALE,
+ batt_temp);
+
+ var = linear_interpolate(
+ var1,
+ lut->row_entries[row1],
+ var2,
+ lut->row_entries[row2],
+ soc);
+
+ return var;
+}
+
+int interpolate_slope(struct profile_table_data *lut,
+ int batt_temp, int soc)
+{
+ int i, ocvrow1, ocvrow2, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+ int slope;
+
+ rows = lut->rows;
+ cols = lut->cols;
+ if (soc >= lut->row_entries[0]) {
+ pr_debug("soc %d >= max soc range - use the slope at soc=%d for lut %s\n",
+ soc, lut->row_entries[0], lut->name);
+ row1 = 0;
+ row2 = 1;
+ } else if (soc <= lut->row_entries[rows - 1]) {
+ pr_debug("soc %d is <= min soc range - use the slope at soc=%d for lut %s\n",
+ soc, lut->row_entries[rows - 1], lut->name);
+ row1 = rows - 2;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (soc >= lut->row_entries[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < lut->col_entries[0] * DEGC_SCALE)
+ batt_temp = lut->col_entries[0] * DEGC_SCALE;
+ if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE)
+ batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++) {
+ if (batt_temp <= lut->col_entries[i] * DEGC_SCALE)
+ break;
+ }
+
+ if (batt_temp == lut->col_entries[i] * DEGC_SCALE) {
+ slope = (lut->data[row1][i] - lut->data[row2][i]);
+ if (slope <= 0) {
+ pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n",
+ slope, soc);
+ slope = 1;
+ }
+ slope *= 10000;
+ slope /= (lut->row_entries[row1] -
+ lut->row_entries[row2]);
+ return slope;
+ }
+ ocvrow1 = linear_interpolate(
+ lut->data[row1][i - 1],
+ lut->col_entries[i - 1] * DEGC_SCALE,
+ lut->data[row1][i],
+ lut->col_entries[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocvrow2 = linear_interpolate(
+ lut->data[row2][i - 1],
+ lut->col_entries[i - 1] * DEGC_SCALE,
+ lut->data[row2][i],
+ lut->col_entries[i] * DEGC_SCALE,
+ batt_temp);
+
+ slope = (ocvrow1 - ocvrow2);
+ if (slope <= 0) {
+ pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n",
+ slope, soc);
+ slope = 1;
+ }
+ slope *= 10000;
+ slope /= (lut->row_entries[row1] - lut->row_entries[row2]);
+
+ return slope;
+}
diff --git a/drivers/power/supply/qcom/qg-profile-lib.h b/drivers/power/supply/qcom/qg-profile-lib.h
new file mode 100644
index 0000000..eb7263d
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-profile-lib.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2018 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 __QG_PROFILE_LIB_H__
+#define __QG_PROFILE_LIB_H__
+
+struct profile_table_data {
+ char *name;
+ int rows;
+ int cols;
+ int *row_entries;
+ int *col_entries;
+ int **data;
+};
+
+int interpolate_single_row_lut(struct profile_table_data *lut,
+ int x, int scale);
+int interpolate_soc(struct profile_table_data *lut,
+ int batt_temp, int ocv);
+int interpolate_var(struct profile_table_data *lut,
+ int batt_temp, int soc);
+int interpolate_slope(struct profile_table_data *lut,
+ int batt_temp, int soc);
+
+#endif /*__QG_PROFILE_LIB_H__ */
diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h
new file mode 100644
index 0000000..1f42a8c
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-reg.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2018 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 __QG_REG_H__
+#define __QG_REG_H__
+
+#define PERPH_TYPE_REG 0x04
+#define QG_TYPE 0x0D
+
+#define QG_STATUS1_REG 0x08
+#define BATTERY_PRESENT_BIT BIT(0)
+
+#define QG_STATUS2_REG 0x09
+#define GOOD_OCV_BIT BIT(1)
+
+#define QG_STATUS3_REG 0x0A
+#define COUNT_FIFO_RT_MASK GENMASK(3, 0)
+
+#define QG_INT_RT_STS_REG 0x10
+#define FIFO_UPDATE_DONE_RT_STS_BIT BIT(3)
+#define VBAT_LOW_INT_RT_STS_BIT BIT(1)
+
+#define QG_INT_LATCHED_STS_REG 0x18
+#define FIFO_UPDATE_DONE_INT_LAT_STS_BIT BIT(3)
+
+#define QG_DATA_CTL1_REG 0x41
+#define MASTER_HOLD_OR_CLR_BIT BIT(0)
+
+#define QG_MODE_CTL1_REG 0x43
+#define PARALLEL_IBAT_SENSE_EN_BIT BIT(7)
+
+#define QG_VBAT_EMPTY_THRESHOLD_REG 0x4B
+#define QG_VBAT_LOW_THRESHOLD_REG 0x4C
+
+#define QG_S2_NORMAL_MEAS_CTL2_REG 0x51
+#define FIFO_LENGTH_MASK GENMASK(5, 3)
+#define FIFO_LENGTH_SHIFT 3
+#define NUM_OF_ACCUM_MASK GENMASK(2, 0)
+
+#define QG_S2_NORMAL_MEAS_CTL3_REG 0x52
+
+#define QG_S3_SLEEP_OCV_MEAS_CTL4_REG 0x59
+#define S3_SLEEP_OCV_TIMER_MASK GENMASK(2, 0)
+
+#define QG_S3_SLEEP_OCV_TREND_CTL2_REG 0x5C
+#define TREND_TOL_MASK GENMASK(5, 0)
+
+#define QG_S3_SLEEP_OCV_IBAT_CTL1_REG 0x5D
+#define SLEEP_IBAT_QUALIFIED_LENGTH_MASK GENMASK(2, 0)
+
+#define QG_S3_ENTRY_IBAT_THRESHOLD_REG 0x5E
+#define QG_S3_EXIT_IBAT_THRESHOLD_REG 0x5F
+
+#define QG_S7_PON_OCV_V_DATA0_REG 0x70
+#define QG_S7_PON_OCV_I_DATA0_REG 0x72
+#define QG_S3_GOOD_OCV_V_DATA0_REG 0x74
+#define QG_S3_GOOD_OCV_I_DATA0_REG 0x76
+
+#define QG_V_ACCUM_DATA0_RT_REG 0x88
+#define QG_I_ACCUM_DATA0_RT_REG 0x8B
+#define QG_ACCUM_CNT_RT_REG 0x8E
+
+#define QG_V_FIFO0_DATA0_REG 0x90
+#define QG_I_FIFO0_DATA0_REG 0xA0
+
+#define QG_SOC_MONOTONIC_REG 0xBF
+
+#define QG_LAST_ADC_V_DATA0_REG 0xC0
+#define QG_LAST_ADC_I_DATA0_REG 0xC2
+
+/* SDAM offsets */
+#define QG_SDAM_VALID_OFFSET 0x46
+#define QG_SDAM_SOC_OFFSET 0x47
+#define QG_SDAM_TEMP_OFFSET 0x48
+#define QG_SDAM_RBAT_OFFSET 0x4A
+#define QG_SDAM_OCV_OFFSET 0x4C
+#define QG_SDAM_IBAT_OFFSET 0x50
+#define QG_SDAM_TIME_OFFSET 0x54
+
+#endif
diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c
new file mode 100644
index 0000000..65bebab
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-sdam.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2018 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) "QG-K: %s: " fmt, __func__
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include "qg-sdam.h"
+#include "qg-reg.h"
+
+static struct qg_sdam *the_chip;
+
+struct qg_sdam_info {
+ char *name;
+ u32 offset;
+ u32 length;
+};
+
+static struct qg_sdam_info sdam_info[] = {
+ [SDAM_VALID] = {
+ .name = "VALID",
+ .offset = QG_SDAM_VALID_OFFSET,
+ .length = 1,
+ },
+ [SDAM_SOC] = {
+ .name = "SOC",
+ .offset = QG_SDAM_SOC_OFFSET,
+ .length = 1,
+ },
+ [SDAM_TEMP] = {
+ .name = "BATT_TEMP",
+ .offset = QG_SDAM_TEMP_OFFSET,
+ .length = 2,
+ },
+ [SDAM_RBAT_MOHM] = {
+ .name = "RBAT_MOHM",
+ .offset = QG_SDAM_RBAT_OFFSET,
+ .length = 2,
+ },
+ [SDAM_OCV_UV] = {
+ .name = "OCV_UV",
+ .offset = QG_SDAM_OCV_OFFSET,
+ .length = 4,
+ },
+ [SDAM_IBAT_UA] = {
+ .name = "IBAT_UA",
+ .offset = QG_SDAM_IBAT_OFFSET,
+ .length = 4,
+ },
+ [SDAM_TIME_SEC] = {
+ .name = "TIME_SEC",
+ .offset = QG_SDAM_TIME_OFFSET,
+ .length = 4,
+ },
+};
+
+int qg_sdam_write(u8 param, u32 data)
+{
+ int rc;
+ struct qg_sdam *chip = the_chip;
+ u32 offset;
+ size_t length;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ if (param >= SDAM_MAX) {
+ pr_err("Invalid SDAM param %d\n", param);
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + sdam_info[param].offset;
+ length = sdam_info[param].length;
+ rc = regmap_bulk_write(chip->regmap, offset, (u8 *)&data, length);
+ if (rc < 0)
+ pr_err("Failed to write offset=%0x4x param=%d value=%d\n",
+ offset, param, data);
+ else
+ pr_debug("QG SDAM write param=%s value=%d\n",
+ sdam_info[param].name, data);
+
+ return rc;
+}
+
+int qg_sdam_read(u8 param, u32 *data)
+{
+ int rc;
+ struct qg_sdam *chip = the_chip;
+ u32 offset;
+ size_t length;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ if (param >= SDAM_MAX) {
+ pr_err("Invalid SDAM param %d\n", param);
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + sdam_info[param].offset;
+ length = sdam_info[param].length;
+ rc = regmap_raw_read(chip->regmap, offset, (u8 *)data, length);
+ if (rc < 0)
+ pr_err("Failed to read offset=%0x4x param=%d\n",
+ offset, param);
+ else
+ pr_debug("QG SDAM read param=%s value=%d\n",
+ sdam_info[param].name, *data);
+
+ return rc;
+}
+
+int qg_sdam_read_all(u32 *sdam_data)
+{
+ int i, rc;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDAM_MAX; i++) {
+ rc = qg_sdam_read(i, &sdam_data[i]);
+ if (rc < 0) {
+ pr_err("Failed to read SDAM param=%s rc=%d\n",
+ sdam_info[i].name, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int qg_sdam_write_all(u32 *sdam_data)
+{
+ int i, rc;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDAM_MAX; i++) {
+ rc = qg_sdam_write(i, sdam_data[i]);
+ if (rc < 0) {
+ pr_err("Failed to write SDAM param=%s rc=%d\n",
+ sdam_info[i].name, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int qg_sdam_init(struct device *dev)
+{
+ int rc;
+ u32 base = 0, type = 0;
+ struct qg_sdam *chip;
+ struct device_node *child, *node = dev->of_node;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return 0;
+
+ chip->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Parent regmap is unavailable\n");
+ return -ENXIO;
+ }
+
+ /* get the SDAM base address */
+ for_each_available_child_of_node(node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ pr_err("Failed to read base address rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regmap_read(chip->regmap, base + PERPH_TYPE_REG, &type);
+ if (rc < 0) {
+ pr_err("Failed to read type rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (type) {
+ case SDAM_TYPE:
+ chip->sdam_base = base;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!chip->sdam_base) {
+ pr_err("QG SDAM node not defined\n");
+ return -EINVAL;
+ }
+
+ the_chip = chip;
+
+ return 0;
+}
diff --git a/drivers/power/supply/qcom/qg-sdam.h b/drivers/power/supply/qcom/qg-sdam.h
new file mode 100644
index 0000000..a75ead9
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-sdam.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2018 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 __QG_SDAM_H__
+#define __QG_SDAM_H__
+
+#define SDAM_TYPE 0x2E
+
+enum qg_sdam_param {
+ SDAM_VALID,
+ SDAM_SOC,
+ SDAM_TEMP,
+ SDAM_RBAT_MOHM,
+ SDAM_OCV_UV,
+ SDAM_IBAT_UA,
+ SDAM_TIME_SEC,
+ SDAM_MAX,
+};
+
+struct qg_sdam {
+ struct regmap *regmap;
+ u16 sdam_base;
+};
+
+int qg_sdam_init(struct device *dev);
+int qg_sdam_write(u8 param, u32 data);
+int qg_sdam_read(u8 param, u32 *data);
+int qg_sdam_write_all(u32 *sdam_data);
+int qg_sdam_read_all(u32 *sdam_data);
+
+#endif
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
new file mode 100644
index 0000000..4fdd258
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2018 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) "QG-K: %s: " fmt, __func__
+
+#include <linux/alarmtimer.h>
+#include <linux/power_supply.h>
+#include <uapi/linux/qg.h>
+#include "qg-sdam.h"
+#include "qg-core.h"
+#include "qg-reg.h"
+#include "qg-util.h"
+#include "qg-defs.h"
+
+#define DEFAULT_UPDATE_TIME_MS 64000
+#define SOC_SCALE_HYST_MS 2000
+static void get_next_update_time(struct qpnp_qg *chip, int *time_ms)
+{
+ int rc = 0, full_time_ms = 0, rt_time_ms = 0;
+
+ *time_ms = DEFAULT_UPDATE_TIME_MS;
+
+ rc = get_fifo_done_time(chip, false, &full_time_ms);
+ if (rc < 0)
+ return;
+
+ rc = get_fifo_done_time(chip, true, &rt_time_ms);
+ if (rc < 0)
+ return;
+
+ *time_ms = full_time_ms - rt_time_ms;
+
+ if (*time_ms < 0)
+ *time_ms = 0;
+
+ qg_dbg(chip, QG_DEBUG_SOC, "SOC scale next-update-time %d secs\n",
+ *time_ms / 1000);
+}
+
+static bool is_scaling_required(struct qpnp_qg *chip)
+{
+ if ((abs(chip->catch_up_soc - chip->msoc) < chip->dt.delta_soc) &&
+ chip->catch_up_soc != 0 && chip->catch_up_soc != 100)
+ return false;
+
+ if (chip->catch_up_soc == chip->msoc)
+ /* SOC has not changed */
+ return false;
+
+
+ if (chip->catch_up_soc > chip->msoc && !is_usb_present(chip))
+ /* USB is not present and SOC has increased */
+ return false;
+
+ return true;
+}
+
+static void update_msoc(struct qpnp_qg *chip)
+{
+ int rc;
+
+ if (chip->catch_up_soc > chip->msoc) {
+ /* SOC increased */
+ if (is_usb_present(chip)) /* Increment if USB is present */
+ chip->msoc += chip->dt.delta_soc;
+ } else {
+ /* SOC dropped */
+ chip->msoc -= chip->dt.delta_soc;
+ }
+ chip->msoc = CAP(0, 100, chip->msoc);
+
+ /* update the SOC register */
+ rc = qg_write_monotonic_soc(chip, chip->msoc);
+ if (rc < 0)
+ pr_err("Failed to update MSOC register rc=%d\n", rc);
+
+ /* update SDAM with the new MSOC */
+ rc = qg_sdam_write(SDAM_SOC, chip->msoc);
+ if (rc < 0)
+ pr_err("Failed to update SDAM with MSOC rc=%d\n", rc);
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale: Update msoc=%d catch_up_soc=%d delta_soc=%d\n",
+ chip->msoc, chip->catch_up_soc, chip->dt.delta_soc);
+}
+
+static void scale_soc_stop(struct qpnp_qg *chip)
+{
+ chip->next_wakeup_ms = 0;
+ alarm_cancel(&chip->alarm_timer);
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale stopped: msoc=%d catch_up_soc=%d\n",
+ chip->msoc, chip->catch_up_soc);
+}
+
+static void scale_soc_work(struct work_struct *work)
+{
+ struct qpnp_qg *chip = container_of(work,
+ struct qpnp_qg, scale_soc_work);
+
+ mutex_lock(&chip->soc_lock);
+
+ if (!is_scaling_required(chip)) {
+ scale_soc_stop(chip);
+ goto done;
+ }
+
+ update_msoc(chip);
+
+ if (is_scaling_required(chip)) {
+ alarm_start_relative(&chip->alarm_timer,
+ ms_to_ktime(chip->next_wakeup_ms));
+ } else {
+ scale_soc_stop(chip);
+ goto done_psy;
+ }
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale: Work msoc=%d catch_up_soc=%d delta_soc=%d next_wakeup=%d sec\n",
+ chip->msoc, chip->catch_up_soc, chip->dt.delta_soc,
+ chip->next_wakeup_ms / 1000);
+
+done_psy:
+ power_supply_changed(chip->qg_psy);
+done:
+ pm_relax(chip->dev);
+ mutex_unlock(&chip->soc_lock);
+}
+
+static enum alarmtimer_restart
+ qpnp_msoc_timer(struct alarm *alarm, ktime_t now)
+{
+ struct qpnp_qg *chip = container_of(alarm,
+ struct qpnp_qg, alarm_timer);
+
+ /* timer callback runs in atomic context, cannot use voter */
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->scale_soc_work);
+
+ return ALARMTIMER_NORESTART;
+}
+
+int qg_scale_soc(struct qpnp_qg *chip, bool force_soc)
+{
+ int soc_points = 0;
+ int rc = 0, time_ms = 0;
+
+ mutex_lock(&chip->soc_lock);
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale: Start msoc=%d catch_up_soc=%d delta_soc=%d\n",
+ chip->msoc, chip->catch_up_soc, chip->dt.delta_soc);
+
+ if (force_soc) {
+ chip->msoc = chip->catch_up_soc;
+ rc = qg_write_monotonic_soc(chip, chip->msoc);
+ if (rc < 0)
+ pr_err("Failed to update MSOC register rc=%d\n", rc);
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale: Forced msoc=%d\n", chip->msoc);
+ goto done_psy;
+ }
+
+ if (!is_scaling_required(chip)) {
+ scale_soc_stop(chip);
+ goto done;
+ }
+
+ update_msoc(chip);
+
+ if (is_scaling_required(chip)) {
+ get_next_update_time(chip, &time_ms);
+ soc_points = abs(chip->msoc - chip->catch_up_soc)
+ / chip->dt.delta_soc;
+ chip->next_wakeup_ms = (time_ms / (soc_points + 1))
+ - SOC_SCALE_HYST_MS;
+ if (chip->next_wakeup_ms < 0)
+ chip->next_wakeup_ms = 1; /* wake up immediately */
+ alarm_start_relative(&chip->alarm_timer,
+ ms_to_ktime(chip->next_wakeup_ms));
+ } else {
+ scale_soc_stop(chip);
+ goto done_psy;
+ }
+
+ qg_dbg(chip, QG_DEBUG_SOC,
+ "SOC scale: msoc=%d catch_up_soc=%d delta_soc=%d soc_points=%d next_wakeup=%d sec\n",
+ chip->msoc, chip->catch_up_soc, chip->dt.delta_soc,
+ soc_points, chip->next_wakeup_ms / 1000);
+
+done_psy:
+ power_supply_changed(chip->qg_psy);
+done:
+ mutex_unlock(&chip->soc_lock);
+ return rc;
+}
+
+int qg_soc_init(struct qpnp_qg *chip)
+{
+ if (alarmtimer_get_rtcdev()) {
+ alarm_init(&chip->alarm_timer, ALARM_BOOTTIME,
+ qpnp_msoc_timer);
+ } else {
+ pr_err("Failed to get soc alarm-timer\n");
+ return -EINVAL;
+ }
+ INIT_WORK(&chip->scale_soc_work, scale_soc_work);
+
+ return 0;
+}
+
+void qg_soc_exit(struct qpnp_qg *chip)
+{
+ alarm_cancel(&chip->alarm_timer);
+}
diff --git a/drivers/power/supply/qcom/qg-soc.h b/drivers/power/supply/qcom/qg-soc.h
new file mode 100644
index 0000000..3b4eb60
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-soc.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2018 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 __QG_SOC_H__
+#define __QG_SOC_H__
+
+int qg_scale_soc(struct qpnp_qg *chip, bool force_soc);
+int qg_soc_init(struct qpnp_qg *chip);
+void qg_soc_exit(struct qpnp_qg *chip);
+
+#endif /* __QG_SOC_H__ */
diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c
new file mode 100644
index 0000000..44e5048
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-util.c
@@ -0,0 +1,291 @@
+/* Copyright (c) 2018 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/alarmtimer.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <uapi/linux/qg.h>
+#include "qg-sdam.h"
+#include "qg-core.h"
+#include "qg-reg.h"
+#include "qg-defs.h"
+
+static inline bool is_sticky_register(u32 addr)
+{
+ if ((addr & 0xFF) == QG_STATUS2_REG)
+ return true;
+
+ return false;
+}
+
+int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len)
+{
+ int rc, i;
+ u32 dummy = 0;
+
+ if (is_sticky_register(addr)) {
+ rc = regmap_write(chip->regmap, addr, dummy);
+ if (rc < 0) {
+ pr_err("Failed regmap_write for %04x rc=%d\n",
+ addr, rc);
+ return rc;
+ }
+ }
+
+ rc = regmap_bulk_read(chip->regmap, addr, val, len);
+ if (rc < 0) {
+ pr_err("Failed regmap_read for address %04x rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ if (*chip->debug_mask & QG_DEBUG_BUS_READ) {
+ pr_info("length %d addr=%04x\n", len, addr);
+ for (i = 0; i < len; i++)
+ pr_info("val[%d]: %02x\n", i, val[i]);
+ }
+
+ return 0;
+}
+
+int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len)
+{
+ int rc, i;
+
+ mutex_lock(&chip->bus_lock);
+
+ if (len > 1)
+ rc = regmap_bulk_write(chip->regmap, addr, val, len);
+ else
+ rc = regmap_write(chip->regmap, addr, *val);
+
+ if (rc < 0) {
+ pr_err("Failed regmap_write for address %04x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+
+ if (*chip->debug_mask & QG_DEBUG_BUS_WRITE) {
+ pr_info("length %d addr=%04x\n", len, addr);
+ for (i = 0; i < len; i++)
+ pr_info("val[%d]: %02x\n", i, val[i]);
+ }
+out:
+ mutex_unlock(&chip->bus_lock);
+ return rc;
+}
+
+int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+
+ rc = regmap_update_bits(chip->regmap, addr, mask, val);
+ if (rc < 0) {
+ pr_err("Failed regmap_update_bits for address %04x rc=%d\n",
+ addr, rc);
+ goto out;
+ }
+
+ if (*chip->debug_mask & QG_DEBUG_BUS_WRITE)
+ pr_info("addr=%04x mask: %02x val: %02x\n", addr, mask, val);
+
+out:
+ mutex_unlock(&chip->bus_lock);
+ return rc;
+}
+
+int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt)
+{
+ int rc;
+ u8 reg = 0;
+ u32 addr;
+
+ addr = rt ? QG_STATUS3_REG : QG_S2_NORMAL_MEAS_CTL2_REG;
+ rc = qg_read(chip, chip->qg_base + addr, ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read FIFO length rc=%d\n", rc);
+ return rc;
+ }
+
+ if (rt) {
+ *fifo_length &= COUNT_FIFO_RT_MASK;
+ } else {
+ *fifo_length = (reg & FIFO_LENGTH_MASK) >> FIFO_LENGTH_SHIFT;
+ *fifo_length += 1;
+ }
+
+ return rc;
+}
+
+int get_sample_count(struct qpnp_qg *chip, u32 *sample_count)
+{
+ int rc;
+ u8 reg = 0;
+
+ rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read FIFO sample count rc=%d\n", rc);
+ return rc;
+ }
+
+ *sample_count = 1 << ((reg & NUM_OF_ACCUM_MASK) + 1);
+
+ return rc;
+}
+
+int get_sample_interval(struct qpnp_qg *chip, u32 *sample_interval)
+{
+ int rc;
+ u8 reg = 0;
+
+ rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL3_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read FIFO sample interval rc=%d\n", rc);
+ return rc;
+ }
+
+ *sample_interval = reg * 10;
+
+ return rc;
+}
+
+int get_rtc_time(unsigned long *rtc_time)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("Failed to open rtc device (%s)\n",
+ CONFIG_RTC_HCTOSYS_DEVICE);
+ return -EINVAL;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Failed to read rtc time (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, rtc_time);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+int get_fifo_done_time(struct qpnp_qg *chip, bool rt, int *time_ms)
+{
+ int rc, length = 0;
+ u32 sample_count = 0, sample_interval = 0, acc_count = 0;
+
+ rc = get_fifo_length(chip, &length, rt ? true : false);
+ if (rc < 0)
+ return rc;
+
+ rc = get_sample_count(chip, &sample_count);
+ if (rc < 0)
+ return rc;
+
+ rc = get_sample_interval(chip, &sample_interval);
+ if (rc < 0)
+ return rc;
+
+ *time_ms = length * sample_count * sample_interval;
+
+ if (rt) {
+ rc = qg_read(chip, chip->qg_base + QG_ACCUM_CNT_RT_REG,
+ (u8 *)&acc_count, 1);
+ if (rc < 0)
+ return rc;
+
+ *time_ms += ((sample_count - acc_count) * sample_interval);
+ }
+
+ return 0;
+}
+
+static bool is_usb_available(struct qpnp_qg *chip)
+{
+ if (chip->usb_psy)
+ return true;
+
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy)
+ return false;
+
+ return true;
+}
+
+bool is_usb_present(struct qpnp_qg *chip)
+{
+ union power_supply_propval pval = {0, };
+
+ if (is_usb_available(chip))
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+
+ return pval.intval ? true : false;
+}
+
+static bool is_parallel_available(struct qpnp_qg *chip)
+{
+ if (chip->parallel_psy)
+ return true;
+
+ chip->parallel_psy = power_supply_get_by_name("parallel");
+ if (!chip->parallel_psy)
+ return false;
+
+ return true;
+}
+
+bool is_parallel_enabled(struct qpnp_qg *chip)
+{
+ union power_supply_propval pval = {0, };
+
+ if (is_parallel_available(chip)) {
+ power_supply_get_property(chip->parallel_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
+ }
+
+ return pval.intval ? true : false;
+}
+
+int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc)
+{
+ u8 reg = 0;
+ int rc;
+
+ reg = (msoc * 255) / 100;
+ rc = qg_write(chip, chip->qg_base + QG_SOC_MONOTONIC_REG,
+ ®, 1);
+ if (rc < 0)
+ pr_err("Failed to update QG_SOC_MONOTINIC reg rc=%d\n", rc);
+
+ return rc;
+}
diff --git a/drivers/power/supply/qcom/qg-util.h b/drivers/power/supply/qcom/qg-util.h
new file mode 100644
index 0000000..a3664e1
--- /dev/null
+++ b/drivers/power/supply/qcom/qg-util.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2018 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 __QG_UTIL_H__
+#define __QG_UTIL_H__
+
+int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len);
+int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len);
+int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val);
+int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt);
+int get_sample_count(struct qpnp_qg *chip, u32 *sample_count);
+int get_sample_interval(struct qpnp_qg *chip, u32 *sample_interval);
+int get_fifo_done_time(struct qpnp_qg *chip, bool rt, int *time_ms);
+int get_rtc_time(unsigned long *rtc_time);
+bool is_usb_present(struct qpnp_qg *chip);
+bool is_parallel_enabled(struct qpnp_qg *chip);
+int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc);
+
+#endif
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
new file mode 100644
index 0000000..55cb5ef
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -0,0 +1,2376 @@
+/* Copyright (c) 2018 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) "QG-K: %s: " fmt, __func__
+
+#include <linux/alarmtimer.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_batterydata.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/uaccess.h>
+#include <linux/pmic-voter.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <uapi/linux/qg.h>
+#include "qg-sdam.h"
+#include "qg-core.h"
+#include "qg-reg.h"
+#include "qg-util.h"
+#include "qg-soc.h"
+#include "qg-battery-profile.h"
+#include "qg-defs.h"
+
+static int qg_debug_mask;
+module_param_named(
+ debug_mask, qg_debug_mask, int, 0600
+);
+
+static int qg_get_battery_temp(struct qpnp_qg *chip, int *batt_temp);
+
+static bool is_battery_present(struct qpnp_qg *chip)
+{
+ u8 reg = 0;
+ int rc;
+
+ rc = qg_read(chip, chip->qg_base + QG_STATUS1_REG, ®, 1);
+ if (rc < 0)
+ pr_err("Failed to read battery presence, rc=%d\n", rc);
+
+ return !!(reg & BATTERY_PRESENT_BIT);
+}
+
+#define DEBUG_BATT_ID_LOW 6000
+#define DEBUG_BATT_ID_HIGH 8500
+static bool is_debug_batt_id(struct qpnp_qg *chip)
+{
+ if (is_between(DEBUG_BATT_ID_LOW, DEBUG_BATT_ID_HIGH,
+ chip->batt_id_ohm))
+ return true;
+
+ return false;
+}
+
+static int qg_read_ocv(struct qpnp_qg *chip, u32 *ocv_uv, u8 type)
+{
+ int rc, addr;
+ u64 temp = 0;
+
+ switch (type) {
+ case GOOD_OCV:
+ addr = QG_S3_GOOD_OCV_V_DATA0_REG;
+ break;
+ case PON_OCV:
+ addr = QG_S7_PON_OCV_V_DATA0_REG;
+ break;
+ default:
+ pr_err("Invalid OCV type %d\n", type);
+ return -EINVAL;
+ }
+
+ rc = qg_read(chip, chip->qg_base + addr, (u8 *)&temp, 2);
+ if (rc < 0) {
+ pr_err("Failed to read ocv, rc=%d\n", rc);
+ return rc;
+ }
+
+ *ocv_uv = V_RAW_TO_UV(temp);
+
+ pr_debug("%s: OCV=%duV\n",
+ type == GOOD_OCV ? "GOOD_OCV" : "PON_OCV", *ocv_uv);
+
+ return rc;
+}
+
+static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length)
+{
+ int rc;
+
+ if (!length || length > 8) {
+ pr_err("Invalid FIFO length %d\n", length);
+ return -EINVAL;
+ }
+
+ rc = qg_masked_write(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG,
+ FIFO_LENGTH_MASK, (length - 1) << FIFO_LENGTH_SHIFT);
+ if (rc < 0)
+ pr_err("Failed to write S2 FIFO length, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qg_master_hold(struct qpnp_qg *chip, bool hold)
+{
+ int rc;
+
+ /* clear the master */
+ rc = qg_masked_write(chip, chip->qg_base + QG_DATA_CTL1_REG,
+ MASTER_HOLD_OR_CLR_BIT, 0);
+ if (rc < 0)
+ return rc;
+
+ if (hold) {
+ /* 0 -> 1, hold the master */
+ rc = qg_masked_write(chip, chip->qg_base + QG_DATA_CTL1_REG,
+ MASTER_HOLD_OR_CLR_BIT,
+ MASTER_HOLD_OR_CLR_BIT);
+ if (rc < 0)
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS, "Master hold = %d\n", hold);
+
+ return rc;
+}
+
+static void qg_notify_charger(struct qpnp_qg *chip)
+{
+ union power_supply_propval prop = {0, };
+ int rc;
+
+ if (!chip->batt_psy)
+ return;
+
+ if (is_debug_batt_id(chip)) {
+ prop.intval = 1;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_DEBUG_BATTERY, &prop);
+ return;
+ }
+
+ if (!chip->profile_loaded)
+ return;
+
+ prop.intval = chip->bp.float_volt_uv;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
+ if (rc < 0) {
+ pr_err("Failed to set voltage_max property on batt_psy, rc=%d\n",
+ rc);
+ return;
+ }
+
+ prop.intval = chip->bp.fastchg_curr_ma * 1000;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop);
+ if (rc < 0) {
+ pr_err("Failed to set constant_charge_current_max property on batt_psy, rc=%d\n",
+ rc);
+ return;
+ }
+
+ pr_debug("Notified charger on float voltage and FCC\n");
+}
+
+static bool is_batt_available(struct qpnp_qg *chip)
+{
+ if (chip->batt_psy)
+ return true;
+
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (!chip->batt_psy)
+ return false;
+
+ /* batt_psy is initialized, set the fcc and fv */
+ qg_notify_charger(chip);
+
+ return true;
+}
+
+static int qg_update_sdam_params(struct qpnp_qg *chip)
+{
+ int rc, batt_temp = 0, i;
+ unsigned long rtc_sec = 0;
+
+ rc = get_rtc_time(&rtc_sec);
+ if (rc < 0)
+ pr_err("Failed to get RTC time, rc=%d\n", rc);
+ else
+ chip->sdam_data[SDAM_TIME_SEC] = rtc_sec;
+
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0)
+ pr_err("Failed to get battery-temp, rc = %d\n", rc);
+ else
+ chip->sdam_data[SDAM_TEMP] = (u32)batt_temp;
+
+ rc = qg_sdam_write_all(chip->sdam_data);
+ if (rc < 0)
+ pr_err("Failed to write to SDAM rc=%d\n", rc);
+
+ for (i = 0; i < SDAM_MAX; i++)
+ qg_dbg(chip, QG_DEBUG_STATUS, "SDAM write param %d value=%d\n",
+ i, chip->sdam_data[i]);
+
+ return rc;
+}
+
+static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length)
+{
+ int rc = 0, i, j = 0, temp;
+ u8 v_fifo[MAX_FIFO_LENGTH * 2], i_fifo[MAX_FIFO_LENGTH * 2];
+ u32 sample_interval = 0, sample_count = 0, fifo_v = 0, fifo_i = 0;
+
+ if (!fifo_length) {
+ pr_debug("No FIFO data\n");
+ return 0;
+ }
+
+ qg_dbg(chip, QG_DEBUG_FIFO, "FIFO length=%d\n", fifo_length);
+
+ rc = get_sample_interval(chip, &sample_interval);
+ if (rc < 0) {
+ pr_err("Failed to get FIFO sample interval, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = get_sample_count(chip, &sample_count);
+ if (rc < 0) {
+ pr_err("Failed to get FIFO sample count, rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < fifo_length * 2; i = i + 2, j++) {
+ rc = qg_read(chip, chip->qg_base + QG_V_FIFO0_DATA0_REG + i,
+ &v_fifo[i], 2);
+ if (rc < 0) {
+ pr_err("Failed to read QG_V_FIFO, rc=%d\n", rc);
+ return rc;
+ }
+ rc = qg_read(chip, chip->qg_base + QG_I_FIFO0_DATA0_REG + i,
+ &i_fifo[i], 2);
+ if (rc < 0) {
+ pr_err("Failed to read QG_I_FIFO, rc=%d\n", rc);
+ return rc;
+ }
+
+ fifo_v = v_fifo[i] | (v_fifo[i + 1] << 8);
+ fifo_i = i_fifo[i] | (i_fifo[i + 1] << 8);
+
+ temp = sign_extend32(fifo_i, 15);
+
+ chip->kdata.fifo[j].v = V_RAW_TO_UV(fifo_v);
+ chip->kdata.fifo[j].i = I_RAW_TO_UA(temp);
+ chip->kdata.fifo[j].interval = sample_interval;
+ chip->kdata.fifo[j].count = sample_count;
+
+ qg_dbg(chip, QG_DEBUG_FIFO, "FIFO %d raw_v=%d uV=%d raw_i=%d uA=%d interval=%d count=%d\n",
+ j, fifo_v,
+ chip->kdata.fifo[j].v,
+ fifo_i,
+ (int)chip->kdata.fifo[j].i,
+ chip->kdata.fifo[j].interval,
+ chip->kdata.fifo[j].count);
+ }
+
+ chip->kdata.fifo_length = fifo_length;
+
+ return rc;
+}
+
+static int qg_process_accumulator(struct qpnp_qg *chip)
+{
+ int rc, sample_interval = 0;
+ u8 count, index = chip->kdata.fifo_length;
+ u64 acc_v = 0, acc_i = 0;
+ s64 temp = 0;
+
+ rc = qg_read(chip, chip->qg_base + QG_ACCUM_CNT_RT_REG,
+ &count, 1);
+ if (rc < 0) {
+ pr_err("Failed to read ACC count, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!count) {
+ pr_debug("No ACCUMULATOR data!\n");
+ return 0;
+ }
+
+ rc = get_sample_interval(chip, &sample_interval);
+ if (rc < 0) {
+ pr_err("Failed to get ACC sample interval, rc=%d\n", rc);
+ return 0;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_V_ACCUM_DATA0_RT_REG,
+ (u8 *)&acc_v, 3);
+ if (rc < 0) {
+ pr_err("Failed to read ACC RT V data, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_I_ACCUM_DATA0_RT_REG,
+ (u8 *)&acc_i, 3);
+ if (rc < 0) {
+ pr_err("Failed to read ACC RT I data, rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = sign_extend64(acc_i, 23);
+
+ chip->kdata.fifo[index].v = V_RAW_TO_UV(div_u64(acc_v, count));
+ chip->kdata.fifo[index].i = I_RAW_TO_UA(div_s64(temp, count));
+ chip->kdata.fifo[index].interval = sample_interval;
+ chip->kdata.fifo[index].count = count;
+ chip->kdata.fifo_length++;
+
+ qg_dbg(chip, QG_DEBUG_FIFO, "ACC v_avg=%duV i_avg=%duA interval=%d count=%d\n",
+ chip->kdata.fifo[index].v,
+ (int)chip->kdata.fifo[index].i,
+ chip->kdata.fifo[index].interval,
+ chip->kdata.fifo[index].count);
+
+ return rc;
+}
+
+static int qg_process_rt_fifo(struct qpnp_qg *chip)
+{
+ int rc;
+ u32 fifo_length = 0;
+
+ /* Get the real-time FIFO length */
+ rc = get_fifo_length(chip, &fifo_length, true);
+ if (rc < 0) {
+ pr_err("Failed to read RT FIFO length, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_process_fifo(chip, fifo_length);
+ if (rc < 0) {
+ pr_err("Failed to process FIFO data, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_process_accumulator(chip);
+ if (rc < 0) {
+ pr_err("Failed to process ACC data, rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define VBAT_LOW_HYST_UV 50000 /* 50mV */
+static int qg_vbat_low_wa(struct qpnp_qg *chip)
+{
+ int rc, i;
+ u32 vbat_low_uv = chip->dt.vbatt_low_mv * 1000 + VBAT_LOW_HYST_UV;
+
+ if (!(chip->wa_flags & QG_VBAT_LOW_WA) || !chip->vbat_low)
+ return 0;
+
+ /*
+ * PMI632 1.0 does not generate a falling VBAT_LOW IRQ.
+ * To exit from VBAT_LOW config, check if any of the FIFO
+ * averages is > vbat_low threshold and reconfigure the
+ * FIFO length to normal.
+ */
+ for (i = 0; i < chip->kdata.fifo_length; i++) {
+ if (chip->kdata.fifo[i].v > vbat_low_uv) {
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ goto done;
+ }
+ rc = qg_update_fifo_length(chip,
+ chip->dt.s2_fifo_length);
+ if (rc < 0)
+ goto done;
+
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ goto done;
+ }
+ /* FIFOs restarted */
+ chip->last_fifo_update_time = ktime_get();
+
+ chip->vbat_low = false;
+ pr_info("Exit VBAT_LOW vbat_avg=%duV vbat_low=%duV updated fifo_length=%d\n",
+ chip->kdata.fifo[i].v, vbat_low_uv,
+ chip->dt.s2_fifo_length);
+ break;
+ }
+ }
+
+ return 0;
+
+done:
+ qg_master_hold(chip, false);
+ return rc;
+}
+
+#define MIN_FIFO_FULL_TIME_MS 12000
+static int process_rt_fifo_data(struct qpnp_qg *chip,
+ bool vbat_low, bool update_smb)
+{
+ int rc = 0;
+ ktime_t now = ktime_get();
+ s64 time_delta;
+
+ /*
+ * Reject the FIFO read event if there are back-to-back requests
+ * This is done to gaurantee that there is always a minimum FIFO
+ * data to be processed, ignore this if vbat_low is set.
+ */
+ time_delta = ktime_ms_delta(now, chip->last_user_update_time);
+
+ qg_dbg(chip, QG_DEBUG_FIFO, "time_delta=%lld ms vbat_low=%d\n",
+ time_delta, vbat_low);
+
+ if (time_delta > MIN_FIFO_FULL_TIME_MS || vbat_low || update_smb) {
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = qg_process_rt_fifo(chip);
+ if (rc < 0) {
+ pr_err("Failed to process FIFO real-time, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (vbat_low) {
+ /* change FIFO length */
+ rc = qg_update_fifo_length(chip,
+ chip->dt.s2_vbat_low_fifo_length);
+ if (rc < 0)
+ goto done;
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "FIFO length updated to %d vbat_low=%d\n",
+ chip->dt.s2_vbat_low_fifo_length,
+ vbat_low);
+ }
+
+ if (update_smb) {
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_MODE_CTL1_REG, PARALLEL_IBAT_SENSE_EN_BIT,
+ chip->parallel_enabled ?
+ PARALLEL_IBAT_SENSE_EN_BIT : 0);
+ if (rc < 0) {
+ pr_err("Failed to update SMB_EN, rc=%d\n", rc);
+ goto done;
+ }
+ qg_dbg(chip, QG_DEBUG_STATUS, "Parallel SENSE %d\n",
+ chip->parallel_enabled);
+ }
+
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ goto done;
+ }
+ /* FIFOs restarted */
+ chip->last_fifo_update_time = ktime_get();
+
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+ chip->last_user_update_time = now;
+
+ /* vote to stay awake until userspace reads data */
+ vote(chip->awake_votable, FIFO_RT_DONE_VOTER, true, 0);
+ } else {
+ qg_dbg(chip, QG_DEBUG_FIFO, "FIFO processing too early time_delta=%lld\n",
+ time_delta);
+ }
+done:
+ qg_master_hold(chip, false);
+ return rc;
+}
+
+static void process_udata_work(struct work_struct *work)
+{
+ struct qpnp_qg *chip = container_of(work,
+ struct qpnp_qg, udata_work);
+ int rc;
+
+ if (chip->udata.param[QG_SOC].valid) {
+ qg_dbg(chip, QG_DEBUG_SOC, "udata SOC=%d last SOC=%d\n",
+ chip->udata.param[QG_SOC].data, chip->catch_up_soc);
+
+ /* Only scale if SOC has changed */
+ if (chip->catch_up_soc != chip->udata.param[QG_SOC].data) {
+ chip->catch_up_soc = chip->udata.param[QG_SOC].data;
+ qg_scale_soc(chip, false);
+ }
+
+ /* update parameters to SDAM */
+ chip->sdam_data[SDAM_SOC] =
+ chip->udata.param[QG_SOC].data;
+ chip->sdam_data[SDAM_OCV_UV] =
+ chip->udata.param[QG_OCV_UV].data;
+ chip->sdam_data[SDAM_RBAT_MOHM] =
+ chip->udata.param[QG_RBAT_MOHM].data;
+ chip->sdam_data[SDAM_VALID] = 1;
+
+ rc = qg_update_sdam_params(chip);
+ if (rc < 0)
+ pr_err("Failed to update SDAM params, rc=%d\n", rc);
+ }
+
+ vote(chip->awake_votable, UDATA_READY_VOTER, false, 0);
+}
+
+static irqreturn_t qg_default_irq_handler(int irq, void *data)
+{
+ struct qpnp_qg *chip = data;
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+
+ return IRQ_HANDLED;
+}
+
+#define MAX_FIFO_DELTA_PERCENT 10
+static irqreturn_t qg_fifo_update_done_handler(int irq, void *data)
+{
+ ktime_t now = ktime_get();
+ int rc, hw_delta_ms = 0, margin_ms = 0;
+ u32 fifo_length = 0;
+ s64 time_delta_ms = 0;
+ struct qpnp_qg *chip = data;
+
+ time_delta_ms = ktime_ms_delta(now, chip->last_fifo_update_time);
+ chip->last_fifo_update_time = now;
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+ mutex_lock(&chip->data_lock);
+
+ rc = get_fifo_length(chip, &fifo_length, false);
+ if (rc < 0) {
+ pr_err("Failed to get FIFO length, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = qg_process_fifo(chip, fifo_length);
+ if (rc < 0) {
+ pr_err("Failed to process QG FIFO, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = qg_vbat_low_wa(chip);
+ if (rc < 0) {
+ pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = get_fifo_done_time(chip, false, &hw_delta_ms);
+ if (rc < 0)
+ hw_delta_ms = 0;
+ else
+ margin_ms = (hw_delta_ms * MAX_FIFO_DELTA_PERCENT) / 100;
+
+ if (abs(hw_delta_ms - time_delta_ms) < margin_ms) {
+ chip->kdata.param[QG_FIFO_TIME_DELTA].data = time_delta_ms;
+ chip->kdata.param[QG_FIFO_TIME_DELTA].valid = true;
+ qg_dbg(chip, QG_DEBUG_FIFO, "FIFO_done time_delta_ms=%lld\n",
+ time_delta_ms);
+ }
+
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+
+ /* vote to stay awake until userspace reads data */
+ vote(chip->awake_votable, FIFO_DONE_VOTER, true, 0);
+
+done:
+ mutex_unlock(&chip->data_lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qg_vbat_low_handler(int irq, void *data)
+{
+ int rc;
+ struct qpnp_qg *chip = data;
+ u8 status = 0;
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+ mutex_lock(&chip->data_lock);
+
+ rc = qg_read(chip, chip->qg_base + QG_INT_RT_STS_REG, &status, 1);
+ if (rc < 0) {
+ pr_err("Failed to read RT status, rc=%d\n", rc);
+ goto done;
+ }
+ chip->vbat_low = !!(status & VBAT_LOW_INT_RT_STS_BIT);
+
+ rc = process_rt_fifo_data(chip, chip->vbat_low, false);
+ if (rc < 0)
+ pr_err("Failed to process RT FIFO data, rc=%d\n", rc);
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "VBAT_LOW = %d\n", chip->vbat_low);
+done:
+ mutex_unlock(&chip->data_lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qg_vbat_empty_handler(int irq, void *data)
+{
+ struct qpnp_qg *chip = data;
+ u32 ocv_uv = 0;
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+ pr_warn("VBATT EMPTY SOC = 0\n");
+
+ chip->catch_up_soc = 0;
+ qg_scale_soc(chip, true);
+
+ qg_sdam_read(SDAM_OCV_UV, &ocv_uv);
+ chip->sdam_data[SDAM_SOC] = 0;
+ chip->sdam_data[SDAM_OCV_UV] = ocv_uv;
+ chip->sdam_data[SDAM_VALID] = 1;
+
+ qg_update_sdam_params(chip);
+
+ if (chip->qg_psy)
+ power_supply_changed(chip->qg_psy);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qg_good_ocv_handler(int irq, void *data)
+{
+ int rc;
+ u32 ocv_uv;
+ struct qpnp_qg *chip = data;
+
+ qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+
+ mutex_lock(&chip->data_lock);
+
+ rc = qg_read_ocv(chip, &ocv_uv, GOOD_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read good_ocv, rc=%d\n", rc);
+ goto done;
+ }
+
+ chip->kdata.param[QG_GOOD_OCV_UV].data = ocv_uv;
+ chip->kdata.param[QG_GOOD_OCV_UV].valid = true;
+
+ vote(chip->awake_votable, GOOD_OCV_VOTER, true, 0);
+
+ /* signal the readd thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+done:
+ mutex_unlock(&chip->data_lock);
+ return IRQ_HANDLED;
+}
+
+static struct qg_irq_info qg_irqs[] = {
+ [QG_BATT_MISSING_IRQ] = {
+ .name = "qg-batt-missing",
+ .handler = qg_default_irq_handler,
+ },
+ [QG_VBATT_LOW_IRQ] = {
+ .name = "qg-vbat-low",
+ .handler = qg_vbat_low_handler,
+ .wake = true,
+ },
+ [QG_VBATT_EMPTY_IRQ] = {
+ .name = "qg-vbat-empty",
+ .handler = qg_vbat_empty_handler,
+ .wake = true,
+ },
+ [QG_FIFO_UPDATE_DONE_IRQ] = {
+ .name = "qg-fifo-done",
+ .handler = qg_fifo_update_done_handler,
+ .wake = true,
+ },
+ [QG_GOOD_OCV_IRQ] = {
+ .name = "qg-good-ocv",
+ .handler = qg_good_ocv_handler,
+ .wake = true,
+ },
+ [QG_FSM_STAT_CHG_IRQ] = {
+ .name = "qg-fsm-state-chg",
+ .handler = qg_default_irq_handler,
+ },
+ [QG_EVENT_IRQ] = {
+ .name = "qg-event",
+ .handler = qg_default_irq_handler,
+ },
+};
+
+static int qg_awake_cb(struct votable *votable, void *data, int awake,
+ const char *client)
+{
+ struct qpnp_qg *chip = data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ pr_debug("client: %s awake: %d\n", client, awake);
+ return 0;
+}
+
+static int qg_fifo_irq_disable_cb(struct votable *votable, void *data,
+ int disable, const char *client)
+{
+ if (disable) {
+ if (qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].wake)
+ disable_irq_wake(
+ qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq);
+ if (qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq)
+ disable_irq_nosync(
+ qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq);
+ } else {
+ if (qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq)
+ enable_irq(qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq);
+ if (qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].wake)
+ enable_irq_wake(
+ qg_irqs[QG_FIFO_UPDATE_DONE_IRQ].irq);
+ }
+
+ return 0;
+}
+
+static int qg_vbatt_irq_disable_cb(struct votable *votable, void *data,
+ int disable, const char *client)
+{
+ if (disable) {
+ if (qg_irqs[QG_VBATT_LOW_IRQ].wake)
+ disable_irq_wake(qg_irqs[QG_VBATT_LOW_IRQ].irq);
+ if (qg_irqs[QG_VBATT_EMPTY_IRQ].wake)
+ disable_irq_wake(qg_irqs[QG_VBATT_EMPTY_IRQ].irq);
+ if (qg_irqs[QG_VBATT_LOW_IRQ].irq)
+ disable_irq_nosync(qg_irqs[QG_VBATT_LOW_IRQ].irq);
+ if (qg_irqs[QG_VBATT_EMPTY_IRQ].irq)
+ disable_irq_nosync(qg_irqs[QG_VBATT_EMPTY_IRQ].irq);
+ } else {
+ if (qg_irqs[QG_VBATT_LOW_IRQ].irq)
+ enable_irq(qg_irqs[QG_VBATT_LOW_IRQ].irq);
+ if (qg_irqs[QG_VBATT_EMPTY_IRQ].irq)
+ enable_irq(qg_irqs[QG_VBATT_EMPTY_IRQ].irq);
+ if (qg_irqs[QG_VBATT_LOW_IRQ].wake)
+ enable_irq_wake(qg_irqs[QG_VBATT_LOW_IRQ].irq);
+ if (qg_irqs[QG_VBATT_EMPTY_IRQ].wake)
+ enable_irq_wake(qg_irqs[QG_VBATT_EMPTY_IRQ].irq);
+ }
+
+ return 0;
+}
+
+static int qg_good_ocv_irq_disable_cb(struct votable *votable, void *data,
+ int disable, const char *client)
+{
+ if (disable) {
+ if (qg_irqs[QG_GOOD_OCV_IRQ].wake)
+ disable_irq_wake(qg_irqs[QG_GOOD_OCV_IRQ].irq);
+ if (qg_irqs[QG_GOOD_OCV_IRQ].irq)
+ disable_irq_nosync(qg_irqs[QG_GOOD_OCV_IRQ].irq);
+ } else {
+ if (qg_irqs[QG_GOOD_OCV_IRQ].irq)
+ enable_irq(qg_irqs[QG_GOOD_OCV_IRQ].irq);
+ if (qg_irqs[QG_GOOD_OCV_IRQ].wake)
+ enable_irq_wake(qg_irqs[QG_GOOD_OCV_IRQ].irq);
+ }
+
+ return 0;
+}
+
+#define DEFAULT_BATT_TYPE "Unknown Battery"
+#define MISSING_BATT_TYPE "Missing Battery"
+#define DEBUG_BATT_TYPE "Debug Board"
+static const char *qg_get_battery_type(struct qpnp_qg *chip)
+{
+ if (chip->battery_missing)
+ return MISSING_BATT_TYPE;
+
+ if (is_debug_batt_id(chip))
+ return DEBUG_BATT_TYPE;
+
+ if (chip->bp.batt_type_str) {
+ if (chip->profile_loaded)
+ return chip->bp.batt_type_str;
+ }
+
+ return DEFAULT_BATT_TYPE;
+}
+
+static int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua)
+{
+ int rc = 0, last_ibat = 0;
+
+ if (chip->battery_missing) {
+ *ibat_ua = 0;
+ return 0;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_I_DATA0_REG,
+ (u8 *)&last_ibat, 2);
+ if (rc < 0) {
+ pr_err("Failed to read LAST_ADV_I reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ last_ibat = sign_extend32(last_ibat, 15);
+ *ibat_ua = I_RAW_TO_UA(last_ibat);
+
+ return rc;
+}
+
+static int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv)
+{
+ int rc = 0;
+ u64 last_vbat = 0;
+
+ if (chip->battery_missing) {
+ *vbat_uv = 3700000;
+ return 0;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_V_DATA0_REG,
+ (u8 *)&last_vbat, 2);
+ if (rc < 0) {
+ pr_err("Failed to read LAST_ADV_V reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ *vbat_uv = V_RAW_TO_UV(last_vbat);
+
+ return rc;
+}
+
+#define DEBUG_BATT_SOC 67
+#define BATT_MISSING_SOC 50
+#define EMPTY_SOC 0
+static int qg_get_battery_capacity(struct qpnp_qg *chip, int *soc)
+{
+ if (is_debug_batt_id(chip)) {
+ *soc = DEBUG_BATT_SOC;
+ return 0;
+ }
+
+ if (chip->battery_missing || !chip->profile_loaded) {
+ *soc = BATT_MISSING_SOC;
+ return 0;
+ }
+
+ *soc = chip->msoc;
+
+ return 0;
+}
+
+static int qg_get_battery_temp(struct qpnp_qg *chip, int *temp)
+{
+ int rc = 0;
+ struct qpnp_vadc_result result;
+
+ if (chip->battery_missing) {
+ *temp = 250;
+ return 0;
+ }
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VADC_BAT_THERM_PU2, &result);
+ if (rc) {
+ pr_err("Failed reading adc channel=%d, rc=%d\n",
+ VADC_BAT_THERM_PU2, rc);
+ return rc;
+ }
+ pr_debug("batt_temp = %lld meas = 0x%llx\n",
+ result.physical, result.measurement);
+
+ *temp = (int)result.physical;
+
+ return rc;
+}
+
+static int qg_psy_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *pval)
+{
+ return 0;
+}
+
+static int qg_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *pval)
+{
+ struct qpnp_qg *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ pval->intval = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ rc = qg_get_battery_capacity(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ rc = qg_get_battery_voltage(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ rc = qg_get_battery_current(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ rc = qg_sdam_read(SDAM_OCV_UV, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = qg_get_battery_temp(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_ID:
+ pval->intval = chip->batt_id_ohm;
+ break;
+ case POWER_SUPPLY_PROP_DEBUG_BATTERY:
+ pval->intval = is_debug_batt_id(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ rc = qg_sdam_read(SDAM_RBAT_MOHM, &pval->intval);
+ if (!rc)
+ pval->intval *= 1000;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE:
+ pval->intval = chip->dt.rbat_conn_mohm;
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ pval->strval = qg_get_battery_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ pval->intval = chip->dt.vbatt_cutoff_mv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ pval->intval = chip->bp.float_volt_uv;
+ break;
+ case POWER_SUPPLY_PROP_BATT_FULL_CURRENT:
+ pval->intval = chip->dt.iterm_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_BATT_PROFILE_VERSION:
+ pval->intval = chip->bp.qg_profile_version;
+ break;
+ default:
+ pr_debug("Unsupported property %d\n", psp);
+ break;
+ }
+
+ return rc;
+}
+
+static int qg_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return 0;
+}
+
+static enum power_supply_property qg_psy_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
+ POWER_SUPPLY_PROP_DEBUG_BATTERY,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_BATT_FULL_CURRENT,
+ POWER_SUPPLY_PROP_BATT_PROFILE_VERSION,
+};
+
+static const struct power_supply_desc qg_psy_desc = {
+ .name = "bms",
+ .type = POWER_SUPPLY_TYPE_BMS,
+ .properties = qg_psy_props,
+ .num_properties = ARRAY_SIZE(qg_psy_props),
+ .get_property = qg_psy_get_property,
+ .set_property = qg_psy_set_property,
+ .property_is_writeable = qg_property_is_writeable,
+};
+
+static int qg_charge_full_update(struct qpnp_qg *chip)
+{
+
+ vote(chip->good_ocv_irq_disable_votable,
+ QG_INIT_STATE_IRQ_DISABLE, !chip->charge_done, 0);
+
+ /* TODO: add hold-soc-at-full logic */
+ return 0;
+}
+
+static int qg_parallel_status_update(struct qpnp_qg *chip)
+{
+ int rc;
+ bool parallel_enabled = is_parallel_enabled(chip);
+
+ if (parallel_enabled == chip->parallel_enabled)
+ return 0;
+
+ chip->parallel_enabled = parallel_enabled;
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "Parallel status changed Enabled=%d\n", parallel_enabled);
+
+ mutex_lock(&chip->data_lock);
+
+ rc = process_rt_fifo_data(chip, false, true);
+ if (rc < 0)
+ pr_err("Failed to process RT FIFO data, rc=%d\n", rc);
+
+ mutex_unlock(&chip->data_lock);
+
+ return 0;
+}
+
+static int qg_usb_status_update(struct qpnp_qg *chip)
+{
+ bool usb_present = is_usb_present(chip);
+
+ if (chip->usb_present != usb_present) {
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "USB status changed Present=%d\n",
+ usb_present);
+ qg_scale_soc(chip, false);
+ }
+
+ chip->usb_present = usb_present;
+
+ return 0;
+}
+
+static void qg_status_change_work(struct work_struct *work)
+{
+ struct qpnp_qg *chip = container_of(work,
+ struct qpnp_qg, qg_status_change_work);
+ union power_supply_propval prop = {0, };
+ int rc = 0;
+
+ if (!is_batt_available(chip)) {
+ pr_debug("batt-psy not available\n");
+ goto out;
+ }
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &prop);
+ if (rc < 0)
+ pr_err("Failed to get charger status, rc=%d\n", rc);
+ else
+ chip->charge_status = prop.intval;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_DONE, &prop);
+ if (rc < 0)
+ pr_err("Failed to get charge done status, rc=%d\n", rc);
+ else
+ chip->charge_done = prop.intval;
+
+ rc = qg_parallel_status_update(chip);
+ if (rc < 0)
+ pr_err("Failed to update parallel-status, rc=%d\n", rc);
+
+ rc = qg_usb_status_update(chip);
+ if (rc < 0)
+ pr_err("Failed to update usb status, rc=%d\n", rc);
+
+ rc = qg_charge_full_update(chip);
+ if (rc < 0)
+ pr_err("Failed in charge_full_update, rc=%d\n", rc);
+out:
+ pm_relax(chip->dev);
+}
+
+static int qg_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+ struct qpnp_qg *chip = container_of(nb, struct qpnp_qg, nb);
+
+ if (event != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if (work_pending(&chip->qg_status_change_work))
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "battery") == 0)
+ || (strcmp(psy->desc->name, "parallel") == 0)
+ || (strcmp(psy->desc->name, "usb") == 0)) {
+ /*
+ * We cannot vote for awake votable here as that takes
+ * a mutex lock and this is executed in an atomic context.
+ */
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->qg_status_change_work);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int qg_init_psy(struct qpnp_qg *chip)
+{
+ struct power_supply_config qg_psy_cfg;
+ int rc;
+
+ qg_psy_cfg.drv_data = chip;
+ qg_psy_cfg.of_node = NULL;
+ qg_psy_cfg.supplied_to = NULL;
+ qg_psy_cfg.num_supplicants = 0;
+ chip->qg_psy = devm_power_supply_register(chip->dev,
+ &qg_psy_desc, &qg_psy_cfg);
+ if (IS_ERR_OR_NULL(chip->qg_psy)) {
+ pr_err("Failed to register qg_psy rc = %ld\n",
+ PTR_ERR(chip->qg_psy));
+ return -ENODEV;
+ }
+
+ chip->nb.notifier_call = qg_notifier_cb;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0)
+ pr_err("Failed register psy notifier rc = %d\n", rc);
+
+ return rc;
+}
+
+static ssize_t qg_device_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int rc;
+ struct qpnp_qg *chip = file->private_data;
+ unsigned long data_size = sizeof(chip->kdata);
+
+ /* non-blocking access, return */
+ if (!chip->data_ready && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ /* blocking access wait on data_ready */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ rc = wait_event_interruptible(chip->qg_wait_q,
+ chip->data_ready);
+ if (rc < 0) {
+ pr_debug("Failed wait! rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ mutex_lock(&chip->data_lock);
+
+ if (!chip->data_ready) {
+ pr_debug("No Data, false wakeup\n");
+ rc = -EFAULT;
+ goto fail_read;
+ }
+
+ if (copy_to_user(buf, &chip->kdata, data_size)) {
+ pr_err("Failed in copy_to_user\n");
+ rc = -EFAULT;
+ goto fail_read;
+ }
+ chip->data_ready = false;
+
+ /* clear data */
+ memset(&chip->kdata, 0, sizeof(chip->kdata));
+
+ /* release all wake sources */
+ vote(chip->awake_votable, GOOD_OCV_VOTER, false, 0);
+ vote(chip->awake_votable, FIFO_DONE_VOTER, false, 0);
+ vote(chip->awake_votable, FIFO_RT_DONE_VOTER, false, 0);
+ vote(chip->awake_votable, SUSPEND_DATA_VOTER, false, 0);
+
+ qg_dbg(chip, QG_DEBUG_DEVICE,
+ "QG device read complete Size=%ld\n", data_size);
+
+ mutex_unlock(&chip->data_lock);
+
+ return data_size;
+
+fail_read:
+ mutex_unlock(&chip->data_lock);
+ return rc;
+}
+
+static ssize_t qg_device_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc = -EINVAL;
+ struct qpnp_qg *chip = file->private_data;
+ unsigned long data_size = sizeof(chip->udata);
+
+ mutex_lock(&chip->data_lock);
+ if (count == 0) {
+ pr_err("No data!\n");
+ goto fail;
+ }
+
+ if (count != 0 && count < data_size) {
+ pr_err("Invalid datasize %zu expected %zu\n", count, data_size);
+ goto fail;
+ }
+
+ if (copy_from_user(&chip->udata, buf, data_size)) {
+ pr_err("Failed in copy_from_user\n");
+ rc = -EFAULT;
+ goto fail;
+ }
+
+ rc = data_size;
+ vote(chip->awake_votable, UDATA_READY_VOTER, true, 0);
+ schedule_work(&chip->udata_work);
+ qg_dbg(chip, QG_DEBUG_DEVICE, "QG write complete size=%d\n", rc);
+fail:
+ mutex_unlock(&chip->data_lock);
+ return rc;
+}
+
+static unsigned int qg_device_poll(struct file *file, poll_table *wait)
+{
+ struct qpnp_qg *chip = file->private_data;
+ unsigned int mask;
+
+ poll_wait(file, &chip->qg_wait_q, wait);
+
+ if (chip->data_ready)
+ mask = POLLIN | POLLRDNORM;
+ else
+ mask = POLLERR;
+
+ return mask;
+}
+
+static int qg_device_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_qg *chip = container_of(inode->i_cdev,
+ struct qpnp_qg, qg_cdev);
+
+ file->private_data = chip;
+ qg_dbg(chip, QG_DEBUG_DEVICE, "QG device opened!\n");
+
+ return 0;
+}
+
+static const struct file_operations qg_fops = {
+ .owner = THIS_MODULE,
+ .open = qg_device_open,
+ .read = qg_device_read,
+ .write = qg_device_write,
+ .poll = qg_device_poll,
+};
+
+static int qg_register_device(struct qpnp_qg *chip)
+{
+ int rc;
+
+ rc = alloc_chrdev_region(&chip->dev_no, 0, 1, "qg");
+ if (rc < 0) {
+ pr_err("Failed to allocate chardev rc=%d\n", rc);
+ return rc;
+ }
+
+ cdev_init(&chip->qg_cdev, &qg_fops);
+ rc = cdev_add(&chip->qg_cdev, chip->dev_no, 1);
+ if (rc < 0) {
+ pr_err("Failed to cdev_add rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ chip->qg_class = class_create(THIS_MODULE, "qg");
+ if (IS_ERR_OR_NULL(chip->qg_class)) {
+ pr_err("Failed to create qg class\n");
+ rc = -EINVAL;
+ goto delete_cdev;
+ }
+ chip->qg_device = device_create(chip->qg_class, NULL, chip->dev_no,
+ NULL, "qg");
+ if (IS_ERR(chip->qg_device)) {
+ pr_err("Failed to create qg_device\n");
+ rc = -EINVAL;
+ goto destroy_class;
+ }
+
+ qg_dbg(chip, QG_DEBUG_DEVICE, "'/dev/qg' successfully created\n");
+
+ return 0;
+
+destroy_class:
+ class_destroy(chip->qg_class);
+delete_cdev:
+ cdev_del(&chip->qg_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(chip->dev_no, 1);
+ return rc;
+}
+
+#define BID_RPULL_OHM 100000
+#define BID_VREF_MV 1875
+static int get_batt_id_ohm(struct qpnp_qg *chip, u32 *batt_id_ohm)
+{
+ int rc, batt_id_mv;
+ int64_t denom;
+ struct qpnp_vadc_result result;
+
+ /* Read battery-id */
+ rc = qpnp_vadc_read(chip->vadc_dev, VADC_BAT_ID_PU2, &result);
+ if (rc) {
+ pr_err("Failed to read BATT_ID over vadc, rc=%d\n", rc);
+ return rc;
+ }
+
+ batt_id_mv = result.physical / 1000;
+ if (batt_id_mv == 0) {
+ pr_debug("batt_id_mv = 0 from ADC\n");
+ return 0;
+ }
+
+ denom = div64_s64(BID_VREF_MV * 1000, batt_id_mv) - 1000;
+ if (denom <= 0) {
+ /* batt id connector might be open, return 0 kohms */
+ return 0;
+ }
+
+ *batt_id_ohm = div64_u64(BID_RPULL_OHM * 1000 + denom / 2, denom);
+
+ qg_dbg(chip, QG_DEBUG_PROFILE, "batt_id_mv=%d, batt_id_ohm=%d\n",
+ batt_id_mv, *batt_id_ohm);
+
+ return 0;
+}
+
+static int qg_load_battery_profile(struct qpnp_qg *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ struct device_node *batt_node, *profile_node;
+ int rc;
+
+ batt_node = of_find_node_by_name(node, "qcom,battery-data");
+ if (!batt_node) {
+ pr_err("Batterydata not available\n");
+ return -ENXIO;
+ }
+
+ profile_node = of_batterydata_get_best_profile(batt_node,
+ chip->batt_id_ohm / 1000, NULL);
+ if (IS_ERR(profile_node)) {
+ rc = PTR_ERR(profile_node);
+ pr_err("Failed to detect valid QG battery profile %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_string(profile_node, "qcom,battery-type",
+ &chip->bp.batt_type_str);
+ if (rc < 0) {
+ pr_err("Failed to detect battery type rc:%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_batterydata_init(profile_node);
+ if (rc < 0) {
+ pr_err("Failed to initialize battery-profile rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+ &chip->bp.float_volt_uv);
+ if (rc < 0) {
+ pr_err("Failed to read battery float-voltage rc:%d\n", rc);
+ chip->bp.float_volt_uv = -EINVAL;
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
+ &chip->bp.fastchg_curr_ma);
+ if (rc < 0) {
+ pr_err("Failed to read battery fastcharge current rc:%d\n", rc);
+ chip->bp.fastchg_curr_ma = -EINVAL;
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,qg-batt-profile-ver",
+ &chip->bp.qg_profile_version);
+ if (rc < 0) {
+ pr_err("Failed to read QG profile version rc:%d\n", rc);
+ chip->bp.qg_profile_version = -EINVAL;
+ }
+
+ qg_dbg(chip, QG_DEBUG_PROFILE, "profile=%s FV=%duV FCC=%dma\n",
+ chip->bp.batt_type_str, chip->bp.float_volt_uv,
+ chip->bp.fastchg_curr_ma);
+
+ return 0;
+}
+
+static int qg_setup_battery(struct qpnp_qg *chip)
+{
+ int rc;
+
+ if (!is_battery_present(chip)) {
+ qg_dbg(chip, QG_DEBUG_PROFILE, "Battery Missing!\n");
+ chip->battery_missing = true;
+ chip->profile_loaded = false;
+ } else {
+ /* battery present */
+ rc = get_batt_id_ohm(chip, &chip->batt_id_ohm);
+ if (rc < 0) {
+ pr_err("Failed to detect batt_id rc=%d\n", rc);
+ chip->profile_loaded = false;
+ } else {
+ rc = qg_load_battery_profile(chip);
+ if (rc < 0)
+ pr_err("Failed to load battery-profile rc=%d\n",
+ rc);
+ else
+ chip->profile_loaded = true;
+ }
+ }
+
+ qg_dbg(chip, QG_DEBUG_PROFILE, "battery_missing=%d batt_id_ohm=%d Ohm profile_loaded=%d profile=%s\n",
+ chip->battery_missing, chip->batt_id_ohm,
+ chip->profile_loaded, chip->bp.batt_type_str);
+
+ return 0;
+}
+
+static int qg_determine_pon_soc(struct qpnp_qg *chip)
+{
+ u8 status;
+ int rc, batt_temp = 0;
+ bool use_pon_ocv = false;
+ unsigned long rtc_sec = 0;
+ u32 ocv_uv = 0, soc = 0, shutdown[SDAM_MAX] = {0};
+
+ if (!chip->profile_loaded) {
+ qg_dbg(chip, QG_DEBUG_PON, "No Profile, skipping PON soc\n");
+ return 0;
+ }
+
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc) {
+ pr_err("Failed to read BATT_TEMP at PON rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG,
+ &status, 1);
+ if (rc < 0) {
+ pr_err("Failed to read status2 register rc=%d\n", rc);
+ return rc;
+ }
+
+ if (status & GOOD_OCV_BIT) {
+ qg_dbg(chip, QG_DEBUG_PON, "Using GOOD_OCV @ PON\n");
+ rc = qg_read_ocv(chip, &ocv_uv, GOOD_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read good_ocv rc=%d\n", rc);
+ use_pon_ocv = true;
+ } else {
+ rc = lookup_soc_ocv(&soc, ocv_uv, batt_temp, false);
+ if (rc < 0) {
+ pr_err("Failed to lookup SOC (GOOD_OCV) @ PON rc=%d\n",
+ rc);
+ use_pon_ocv = true;
+ }
+ }
+ } else {
+ rc = get_rtc_time(&rtc_sec);
+ if (rc < 0) {
+ pr_err("Failed to read RTC time rc=%d\n", rc);
+ use_pon_ocv = true;
+ goto done;
+ }
+
+ rc = qg_sdam_read_all(shutdown);
+ if (rc < 0) {
+ pr_err("Failed to read shutdown params rc=%d\n", rc);
+ use_pon_ocv = true;
+ goto done;
+ }
+ qg_dbg(chip, QG_DEBUG_PON, "Shutdown: SOC=%d OCV=%duV time=%dsecs, time_now=%ldsecs\n",
+ shutdown[SDAM_SOC],
+ shutdown[SDAM_OCV_UV],
+ shutdown[SDAM_TIME_SEC],
+ rtc_sec);
+ /*
+ * Use the shutdown SOC if
+ * 1. The device was powered off for < 180 seconds
+ * 2. SDAM read is a success & SDAM data is valid
+ */
+ use_pon_ocv = true;
+ if (!rc && shutdown[SDAM_VALID] &&
+ ((rtc_sec - shutdown[SDAM_TIME_SEC]) < 180)) {
+ use_pon_ocv = false;
+ ocv_uv = shutdown[SDAM_OCV_UV];
+ soc = shutdown[SDAM_SOC];
+ qg_dbg(chip, QG_DEBUG_PON, "Using SHUTDOWN_SOC @ PON\n");
+ }
+ }
+done:
+ /*
+ * Use PON OCV if
+ * OCV_UV is not set or shutdown SOC is invalid.
+ */
+ if (use_pon_ocv || !ocv_uv || !rtc_sec) {
+ qg_dbg(chip, QG_DEBUG_PON, "Using PON_OCV @ PON\n");
+ rc = qg_read_ocv(chip, &ocv_uv, PON_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read HW PON ocv rc=%d\n", rc);
+ return rc;
+ }
+ rc = lookup_soc_ocv(&soc, ocv_uv, batt_temp, false);
+ if (rc < 0) {
+ pr_err("Failed to lookup SOC @ PON rc=%d\n", rc);
+ soc = 50;
+ }
+ }
+
+ chip->pon_soc = chip->catch_up_soc = chip->msoc = soc;
+ chip->kdata.param[QG_PON_OCV_UV].data = ocv_uv;
+ chip->kdata.param[QG_PON_OCV_UV].valid = true;
+
+ /* write back to SDAM */
+ chip->sdam_data[SDAM_SOC] = soc;
+ chip->sdam_data[SDAM_OCV_UV] = ocv_uv;
+ chip->sdam_data[SDAM_VALID] = 1;
+
+ rc = qg_write_monotonic_soc(chip, chip->msoc);
+ if (rc < 0)
+ pr_err("Failed to update MSOC register rc=%d\n", rc);
+
+ rc = qg_update_sdam_params(chip);
+ if (rc < 0)
+ pr_err("Failed to update sdam params rc=%d\n", rc);
+
+ pr_info("use_pon_ocv=%d good_ocv=%d ocv_uv=%duV temp=%d soc=%d\n",
+ use_pon_ocv, !!(status & GOOD_OCV_BIT),
+ ocv_uv, batt_temp, chip->msoc);
+
+ return 0;
+}
+
+static int qg_set_wa_flags(struct qpnp_qg *chip)
+{
+ switch (chip->pmic_rev_id->pmic_subtype) {
+ case PMI632_SUBTYPE:
+ if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4)
+ chip->wa_flags |= QG_VBAT_LOW_WA;
+ break;
+ default:
+ pr_err("Unsupported PMIC subtype %d\n",
+ chip->pmic_rev_id->pmic_subtype);
+ return -EINVAL;
+ }
+
+ qg_dbg(chip, QG_DEBUG_PON, "wa_flags = %x\n", chip->wa_flags);
+
+ return 0;
+}
+
+static int qg_hw_init(struct qpnp_qg *chip)
+{
+ int rc, temp;
+ u8 reg;
+
+ rc = qg_set_wa_flags(chip);
+ if (rc < 0) {
+ pr_err("Failed to update PMIC type flags, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ goto done_fifo;
+ }
+
+ rc = qg_process_rt_fifo(chip);
+ if (rc < 0) {
+ pr_err("Failed to process FIFO real-time, rc=%d\n", rc);
+ goto done_fifo;
+ }
+
+ /* update the changed S2 fifo DT parameters */
+ if (chip->dt.s2_fifo_length > 0) {
+ rc = qg_update_fifo_length(chip, chip->dt.s2_fifo_length);
+ if (rc < 0)
+ goto done_fifo;
+ }
+
+ if (chip->dt.s2_acc_length > 0) {
+ reg = ilog2(chip->dt.s2_acc_length) - 1;
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_S2_NORMAL_MEAS_CTL2_REG,
+ NUM_OF_ACCUM_MASK, reg);
+ if (rc < 0) {
+ pr_err("Failed to write S2 ACC length, rc=%d\n", rc);
+ goto done_fifo;
+ }
+ }
+
+ if (chip->dt.s2_acc_intvl_ms > 0) {
+ reg = chip->dt.s2_acc_intvl_ms / 10;
+ rc = qg_write(chip, chip->qg_base +
+ QG_S2_NORMAL_MEAS_CTL3_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write S2 ACC intrvl, rc=%d\n", rc);
+ goto done_fifo;
+ }
+ }
+
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+
+done_fifo:
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ return rc;
+ }
+ chip->last_fifo_update_time = ktime_get();
+
+ if (chip->dt.ocv_timer_expiry_min != -EINVAL) {
+ if (chip->dt.ocv_timer_expiry_min < 2)
+ chip->dt.ocv_timer_expiry_min = 2;
+ else if (chip->dt.ocv_timer_expiry_min > 30)
+ chip->dt.ocv_timer_expiry_min = 30;
+
+ reg = (chip->dt.ocv_timer_expiry_min - 2) / 4;
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_S3_SLEEP_OCV_MEAS_CTL4_REG,
+ SLEEP_IBAT_QUALIFIED_LENGTH_MASK, reg);
+ if (rc < 0) {
+ pr_err("Failed to write OCV timer, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.ocv_tol_threshold_uv != -EINVAL) {
+ if (chip->dt.ocv_tol_threshold_uv < 0)
+ chip->dt.ocv_tol_threshold_uv = 0;
+ else if (chip->dt.ocv_tol_threshold_uv > 12262)
+ chip->dt.ocv_tol_threshold_uv = 12262;
+
+ reg = chip->dt.ocv_tol_threshold_uv / 195;
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_S3_SLEEP_OCV_TREND_CTL2_REG,
+ TREND_TOL_MASK, reg);
+ if (rc < 0) {
+ pr_err("Failed to write OCV tol-thresh, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.s3_entry_fifo_length != -EINVAL) {
+ if (chip->dt.s3_entry_fifo_length < 1)
+ chip->dt.s3_entry_fifo_length = 1;
+ else if (chip->dt.s3_entry_fifo_length > 8)
+ chip->dt.s3_entry_fifo_length = 8;
+
+ reg = chip->dt.s3_entry_fifo_length - 1;
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+ SLEEP_IBAT_QUALIFIED_LENGTH_MASK, reg);
+ if (rc < 0) {
+ pr_err("Failed to write S3-entry fifo-length, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.s3_entry_ibat_ua != -EINVAL) {
+ if (chip->dt.s3_entry_ibat_ua < 0)
+ chip->dt.s3_entry_ibat_ua = 0;
+ else if (chip->dt.s3_entry_ibat_ua > 155550)
+ chip->dt.s3_entry_ibat_ua = 155550;
+
+ reg = chip->dt.s3_entry_ibat_ua / 610;
+ rc = qg_write(chip, chip->qg_base +
+ QG_S3_ENTRY_IBAT_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write S3-entry ibat-uA, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.s3_exit_ibat_ua != -EINVAL) {
+ if (chip->dt.s3_exit_ibat_ua < 0)
+ chip->dt.s3_exit_ibat_ua = 0;
+ else if (chip->dt.s3_exit_ibat_ua > 155550)
+ chip->dt.s3_exit_ibat_ua = 155550;
+
+ rc = qg_read(chip, chip->qg_base +
+ QG_S3_ENTRY_IBAT_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read S3-entry ibat-uA, rc=%d", rc);
+ return rc;
+ }
+ temp = reg * 610;
+ if (chip->dt.s3_exit_ibat_ua < temp)
+ chip->dt.s3_exit_ibat_ua = temp;
+ else
+ chip->dt.s3_exit_ibat_ua -= temp;
+
+ reg = chip->dt.s3_exit_ibat_ua / 610;
+ rc = qg_write(chip,
+ chip->qg_base + QG_S3_EXIT_IBAT_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write S3-entry ibat-uA, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* vbat low */
+ if (chip->dt.vbatt_low_mv < 0)
+ chip->dt.vbatt_low_mv = 0;
+ else if (chip->dt.vbatt_low_mv > 12750)
+ chip->dt.vbatt_low_mv = 12750;
+
+ reg = chip->dt.vbatt_low_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-low, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* vbat empty */
+ if (chip->dt.vbatt_empty_mv < 0)
+ chip->dt.vbatt_empty_mv = 0;
+ else if (chip->dt.vbatt_empty_mv > 12750)
+ chip->dt.vbatt_empty_mv = 12750;
+
+ reg = chip->dt.vbatt_empty_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qg_post_init(struct qpnp_qg *chip)
+{
+ /* disable all IRQs if profile is not loaded */
+ if (!chip->profile_loaded) {
+ vote(chip->vbatt_irq_disable_votable,
+ PROFILE_IRQ_DISABLE, true, 0);
+ vote(chip->fifo_irq_disable_votable,
+ PROFILE_IRQ_DISABLE, true, 0);
+ vote(chip->good_ocv_irq_disable_votable,
+ PROFILE_IRQ_DISABLE, true, 0);
+ } else {
+ /* disable GOOD_OCV IRQ at init */
+ vote(chip->good_ocv_irq_disable_votable,
+ QG_INIT_STATE_IRQ_DISABLE, true, 0);
+ }
+
+ return 0;
+}
+
+static int qg_get_irq_index_byname(const char *irq_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(qg_irqs); i++) {
+ if (strcmp(qg_irqs[i].name, irq_name) == 0)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+static int qg_request_interrupt(struct qpnp_qg *chip,
+ struct device_node *node, const char *irq_name)
+{
+ int rc, irq, irq_index;
+
+ irq = of_irq_get_byname(node, irq_name);
+ if (irq < 0) {
+ pr_err("Failed to get irq %s byname\n", irq_name);
+ return irq;
+ }
+
+ irq_index = qg_get_irq_index_byname(irq_name);
+ if (irq_index < 0) {
+ pr_err("%s is not a defined irq\n", irq_name);
+ return irq_index;
+ }
+
+ if (!qg_irqs[irq_index].handler)
+ return 0;
+
+ rc = devm_request_threaded_irq(chip->dev, irq, NULL,
+ qg_irqs[irq_index].handler,
+ IRQF_ONESHOT, irq_name, chip);
+ if (rc < 0) {
+ pr_err("Failed to request irq %d\n", irq);
+ return rc;
+ }
+
+ qg_irqs[irq_index].irq = irq;
+ if (qg_irqs[irq_index].wake)
+ enable_irq_wake(irq);
+
+ qg_dbg(chip, QG_DEBUG_PON, "IRQ %s registered wakeable=%d\n",
+ qg_irqs[irq_index].name, qg_irqs[irq_index].wake);
+
+ return 0;
+}
+
+static int qg_request_irqs(struct qpnp_qg *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ struct device_node *child;
+ const char *name;
+ struct property *prop;
+ int rc = 0;
+
+ for_each_available_child_of_node(node, child) {
+ of_property_for_each_string(child, "interrupt-names",
+ prop, name) {
+ rc = qg_request_interrupt(chip, child, name);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+
+ return 0;
+}
+
+#define DEFAULT_VBATT_EMPTY_MV 3200
+#define DEFAULT_VBATT_CUTOFF_MV 3400
+#define DEFAULT_VBATT_LOW_MV 3500
+#define DEFAULT_ITERM_MA 100
+#define DEFAULT_S2_FIFO_LENGTH 5
+#define DEFAULT_S2_VBAT_LOW_LENGTH 2
+#define DEFAULT_S2_ACC_LENGTH 128
+#define DEFAULT_S2_ACC_INTVL_MS 100
+#define DEFAULT_DELTA_SOC 1
+static int qg_parse_dt(struct qpnp_qg *chip)
+{
+ int rc = 0;
+ struct device_node *revid_node, *child, *node = chip->dev->of_node;
+ u32 base, temp;
+ u8 type;
+
+ if (!node) {
+ pr_err("Failed to find device-tree node\n");
+ return -ENXIO;
+ }
+
+ revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+ if (!revid_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ chip->pmic_rev_id = get_revid_data(revid_node);
+ of_node_put(revid_node);
+ if (IS_ERR_OR_NULL(chip->pmic_rev_id)) {
+ pr_err("Failed to get pmic_revid, rc=%ld\n",
+ PTR_ERR(chip->pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ qg_dbg(chip, QG_DEBUG_PON, "PMIC subtype %d Digital major %d\n",
+ chip->pmic_rev_id->pmic_subtype, chip->pmic_rev_id->rev4);
+
+ for_each_available_child_of_node(node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ pr_err("Failed to read base address, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_read(chip, base + PERPH_TYPE_REG, &type, 1);
+ if (rc < 0) {
+ pr_err("Failed to read type, rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (type) {
+ case QG_TYPE:
+ chip->qg_base = base;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!chip->qg_base) {
+ pr_err("QG device node missing\n");
+ return -EINVAL;
+ }
+
+ /* S2 state params */
+ rc = of_property_read_u32(node, "qcom,s2-fifo-length", &temp);
+ if (rc < 0)
+ chip->dt.s2_fifo_length = DEFAULT_S2_FIFO_LENGTH;
+ else
+ chip->dt.s2_fifo_length = temp;
+
+ rc = of_property_read_u32(node, "qcom,s2-vbat-low-fifo-length", &temp);
+ if (rc < 0)
+ chip->dt.s2_vbat_low_fifo_length = DEFAULT_S2_VBAT_LOW_LENGTH;
+ else
+ chip->dt.s2_vbat_low_fifo_length = temp;
+
+ rc = of_property_read_u32(node, "qcom,s2-acc-length", &temp);
+ if (rc < 0)
+ chip->dt.s2_acc_length = DEFAULT_S2_ACC_LENGTH;
+ else
+ chip->dt.s2_acc_length = temp;
+
+ rc = of_property_read_u32(node, "qcom,s2-acc-interval-ms", &temp);
+ if (rc < 0)
+ chip->dt.s2_acc_intvl_ms = DEFAULT_S2_ACC_INTVL_MS;
+ else
+ chip->dt.s2_acc_intvl_ms = temp;
+
+ qg_dbg(chip, QG_DEBUG_PON, "DT: S2 FIFO length=%d low_vbat_length=%d acc_length=%d acc_interval=%d\n",
+ chip->dt.s2_fifo_length, chip->dt.s2_vbat_low_fifo_length,
+ chip->dt.s2_acc_length, chip->dt.s2_acc_intvl_ms);
+
+ /* OCV params */
+ rc = of_property_read_u32(node, "qcom,ocv-timer-expiry-min", &temp);
+ if (rc < 0)
+ chip->dt.ocv_timer_expiry_min = -EINVAL;
+ else
+ chip->dt.ocv_timer_expiry_min = temp;
+
+ rc = of_property_read_u32(node, "qcom,ocv-tol-threshold-uv", &temp);
+ if (rc < 0)
+ chip->dt.ocv_tol_threshold_uv = -EINVAL;
+ else
+ chip->dt.ocv_tol_threshold_uv = temp;
+
+ qg_dbg(chip, QG_DEBUG_PON, "DT: OCV timer_expiry =%dmin ocv_tol_threshold=%duV\n",
+ chip->dt.ocv_timer_expiry_min, chip->dt.ocv_tol_threshold_uv);
+
+ /* S3 sleep configuration */
+ rc = of_property_read_u32(node, "qcom,s3-entry-fifo-length", &temp);
+ if (rc < 0)
+ chip->dt.s3_entry_fifo_length = -EINVAL;
+ else
+ chip->dt.s3_entry_fifo_length = temp;
+
+ rc = of_property_read_u32(node, "qcom,s3-entry-ibat-ua", &temp);
+ if (rc < 0)
+ chip->dt.s3_entry_ibat_ua = -EINVAL;
+ else
+ chip->dt.s3_entry_ibat_ua = temp;
+
+ rc = of_property_read_u32(node, "qcom,s3-entry-ibat-ua", &temp);
+ if (rc < 0)
+ chip->dt.s3_exit_ibat_ua = -EINVAL;
+ else
+ chip->dt.s3_exit_ibat_ua = temp;
+
+ /* VBAT thresholds */
+ rc = of_property_read_u32(node, "qcom,vbatt-empty-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_empty_mv = DEFAULT_VBATT_EMPTY_MV;
+ else
+ chip->dt.vbatt_empty_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,vbatt-low-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_low_mv = DEFAULT_VBATT_LOW_MV;
+ else
+ chip->dt.vbatt_low_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,vbatt-cutoff-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_cutoff_mv = DEFAULT_VBATT_CUTOFF_MV;
+ else
+ chip->dt.vbatt_cutoff_mv = temp;
+
+ /* IBAT thresholds */
+ rc = of_property_read_u32(node, "qcom,qg-iterm-ma", &temp);
+ if (rc < 0)
+ chip->dt.iterm_ma = DEFAULT_ITERM_MA;
+ else
+ chip->dt.iterm_ma = temp;
+
+ rc = of_property_read_u32(node, "qcom,delta-soc", &temp);
+ if (rc < 0)
+ chip->dt.delta_soc = DEFAULT_DELTA_SOC;
+ else
+ chip->dt.delta_soc = temp;
+
+ rc = of_property_read_u32(node, "qcom,rbat-conn-mohm", &temp);
+ if (rc < 0)
+ chip->dt.rbat_conn_mohm = 0;
+ else
+ chip->dt.rbat_conn_mohm = temp;
+
+ qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d\n",
+ chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv,
+ chip->dt.delta_soc);
+
+ return 0;
+}
+
+static int process_suspend(struct qpnp_qg *chip)
+{
+ int rc;
+ u32 fifo_rt_length = 0, sleep_fifo_length = 0;
+
+ /* ignore any suspend processing if we are charging */
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+ qg_dbg(chip, QG_DEBUG_PM, "Charging @ suspend - ignore processing\n");
+ return 0;
+ }
+
+ rc = get_fifo_length(chip, &fifo_rt_length, true);
+ if (rc < 0) {
+ pr_err("Failed to read FIFO RT count, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_read(chip, chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+ (u8 *)&sleep_fifo_length, 1);
+ if (rc < 0) {
+ pr_err("Failed to read sleep FIFO count, rc=%d\n", rc);
+ return rc;
+ }
+ sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK;
+ /*
+ * If the real-time FIFO count is greater than
+ * the the #fifo to enter sleep, save the FIFO data
+ * and reset the fifo count.
+ */
+ if (fifo_rt_length >= (chip->dt.s2_fifo_length - sleep_fifo_length)) {
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_process_rt_fifo(chip);
+ if (rc < 0) {
+ pr_err("Failed to process FIFO real-time, rc=%d\n", rc);
+ qg_master_hold(chip, false);
+ return rc;
+ }
+
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ return rc;
+ }
+ /* FIFOs restarted */
+ chip->last_fifo_update_time = ktime_get();
+
+ chip->suspend_data = true;
+ }
+
+ qg_dbg(chip, QG_DEBUG_PM, "FIFO rt_length=%d sleep_fifo_length=%d default_s2_count=%d suspend_data=%d\n",
+ fifo_rt_length, sleep_fifo_length,
+ chip->dt.s2_fifo_length, chip->suspend_data);
+
+ return rc;
+}
+
+static int process_resume(struct qpnp_qg *chip)
+{
+ int rc, batt_temp = 0;
+ u8 status2 = 0, rt_status = 0;
+ u32 ocv_uv = 0, soc = 0;
+
+ rc = qg_read(chip, chip->qg_base + QG_STATUS2_REG, &status2, 1);
+ if (rc < 0) {
+ pr_err("Failed to read status2 register, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (status2 & GOOD_OCV_BIT) {
+ rc = qg_read_ocv(chip, &ocv_uv, GOOD_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read good_ocv, rc=%d\n", rc);
+ return rc;
+ }
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Failed to read BATT_TEMP, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->kdata.param[QG_GOOD_OCV_UV].data = ocv_uv;
+ chip->kdata.param[QG_GOOD_OCV_UV].valid = true;
+ chip->suspend_data = false;
+ rc = lookup_soc_ocv(&soc, ocv_uv, batt_temp, false);
+ if (rc < 0) {
+ pr_err("Failed to lookup OCV, rc=%d\n", rc);
+ return rc;
+ }
+ chip->catch_up_soc = soc;
+ /* update the SOC immediately */
+ qg_scale_soc(chip, true);
+
+ qg_dbg(chip, QG_DEBUG_PM, "GOOD OCV @ resume good_ocv=%d uV soc=%d\n",
+ ocv_uv, soc);
+ }
+ /*
+ * If the wakeup was not because of FIFO_DONE
+ * send the pending data collected during suspend.
+ */
+ rc = qg_read(chip, chip->qg_base + QG_INT_LATCHED_STS_REG,
+ &rt_status, 1);
+ if (rc < 0) {
+ pr_err("Failed to read latched status register, rc=%d\n", rc);
+ return rc;
+ }
+ rt_status &= FIFO_UPDATE_DONE_INT_LAT_STS_BIT;
+
+ if (!rt_status && chip->suspend_data) {
+ vote(chip->awake_votable, SUSPEND_DATA_VOTER, true, 0);
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+ }
+
+ qg_dbg(chip, QG_DEBUG_PM, "fifo_done rt_status=%d suspend_data=%d data_ready=%d\n",
+ !!rt_status, chip->suspend_data, chip->data_ready);
+
+ chip->suspend_data = false;
+
+ return rc;
+}
+
+static int qpnp_qg_suspend_noirq(struct device *dev)
+{
+ int rc;
+ struct qpnp_qg *chip = dev_get_drvdata(dev);
+
+ mutex_lock(&chip->data_lock);
+
+ rc = process_suspend(chip);
+ if (rc < 0)
+ pr_err("Failed to process QG suspend, rc=%d\n", rc);
+
+ mutex_unlock(&chip->data_lock);
+
+ return 0;
+}
+
+static int qpnp_qg_resume_noirq(struct device *dev)
+{
+ int rc;
+ struct qpnp_qg *chip = dev_get_drvdata(dev);
+
+ mutex_lock(&chip->data_lock);
+
+ rc = process_resume(chip);
+ if (rc < 0)
+ pr_err("Failed to process QG resume, rc=%d\n", rc);
+
+ mutex_unlock(&chip->data_lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_qg_pm_ops = {
+ .suspend_noirq = qpnp_qg_suspend_noirq,
+ .resume_noirq = qpnp_qg_resume_noirq,
+};
+
+static int qpnp_qg_probe(struct platform_device *pdev)
+{
+ int rc = 0, soc = 0;
+ struct qpnp_qg *chip;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Parent regmap is unavailable\n");
+ return -ENXIO;
+ }
+
+ /* VADC for BID */
+ chip->vadc_dev = qpnp_get_vadc(&pdev->dev, "qg");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Failed to find VADC node, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ chip->dev = &pdev->dev;
+ chip->debug_mask = &qg_debug_mask;
+ platform_set_drvdata(pdev, chip);
+ INIT_WORK(&chip->udata_work, process_udata_work);
+ INIT_WORK(&chip->qg_status_change_work, qg_status_change_work);
+ mutex_init(&chip->bus_lock);
+ mutex_init(&chip->soc_lock);
+ mutex_init(&chip->data_lock);
+ init_waitqueue_head(&chip->qg_wait_q);
+
+ rc = qg_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Failed to parse DT, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_hw_init(chip);
+ if (rc < 0) {
+ pr_err("Failed to hw_init, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_setup_battery(chip);
+ if (rc < 0) {
+ pr_err("Failed to setup battery, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_register_device(chip);
+ if (rc < 0) {
+ pr_err("Failed to register QG char device, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_sdam_init(chip->dev);
+ if (rc < 0) {
+ pr_err("Failed to initialize QG SDAM, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_soc_init(chip);
+ if (rc < 0) {
+ pr_err("Failed to initialize SOC scaling init rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qg_determine_pon_soc(chip);
+ if (rc < 0) {
+ pr_err("Failed to determine initial state, rc=%d\n", rc);
+ goto fail_device;
+ }
+
+ chip->awake_votable = create_votable("QG_WS", VOTE_SET_ANY,
+ qg_awake_cb, chip);
+ if (IS_ERR(chip->awake_votable)) {
+ rc = PTR_ERR(chip->awake_votable);
+ chip->awake_votable = NULL;
+ goto fail_device;
+ }
+
+ chip->vbatt_irq_disable_votable = create_votable("QG_VBATT_IRQ_DISABLE",
+ VOTE_SET_ANY, qg_vbatt_irq_disable_cb, chip);
+ if (IS_ERR(chip->vbatt_irq_disable_votable)) {
+ rc = PTR_ERR(chip->vbatt_irq_disable_votable);
+ chip->vbatt_irq_disable_votable = NULL;
+ goto fail_device;
+ }
+
+ chip->fifo_irq_disable_votable = create_votable("QG_FIFO_IRQ_DISABLE",
+ VOTE_SET_ANY, qg_fifo_irq_disable_cb, chip);
+ if (IS_ERR(chip->fifo_irq_disable_votable)) {
+ rc = PTR_ERR(chip->fifo_irq_disable_votable);
+ chip->fifo_irq_disable_votable = NULL;
+ goto fail_device;
+ }
+
+ chip->good_ocv_irq_disable_votable =
+ create_votable("QG_GOOD_IRQ_DISABLE",
+ VOTE_SET_ANY, qg_good_ocv_irq_disable_cb, chip);
+ if (IS_ERR(chip->good_ocv_irq_disable_votable)) {
+ rc = PTR_ERR(chip->good_ocv_irq_disable_votable);
+ chip->good_ocv_irq_disable_votable = NULL;
+ goto fail_device;
+ }
+
+ rc = qg_init_psy(chip);
+ if (rc < 0) {
+ pr_err("Failed to initialize QG psy, rc=%d\n", rc);
+ goto fail_votable;
+ }
+
+ rc = qg_request_irqs(chip);
+ if (rc < 0) {
+ pr_err("Failed to register QG interrupts, rc=%d\n", rc);
+ goto fail_votable;
+ }
+
+ rc = qg_post_init(chip);
+ if (rc < 0) {
+ pr_err("Failed in qg_post_init rc=%d\n", rc);
+ goto fail_votable;
+ }
+
+ qg_get_battery_capacity(chip, &soc);
+ pr_info("QG initialized! battery_profile=%s SOC=%d\n",
+ qg_get_battery_type(chip), soc);
+
+ return rc;
+
+fail_votable:
+ destroy_votable(chip->awake_votable);
+fail_device:
+ device_destroy(chip->qg_class, chip->dev_no);
+ cdev_del(&chip->qg_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+ return rc;
+}
+
+static int qpnp_qg_remove(struct platform_device *pdev)
+{
+ struct qpnp_qg *chip = platform_get_drvdata(pdev);
+
+ qg_batterydata_exit();
+ qg_soc_exit(chip);
+
+ cancel_work_sync(&chip->udata_work);
+ cancel_work_sync(&chip->qg_status_change_work);
+ device_destroy(chip->qg_class, chip->dev_no);
+ cdev_del(&chip->qg_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+ mutex_destroy(&chip->bus_lock);
+ mutex_destroy(&chip->data_lock);
+ mutex_destroy(&chip->soc_lock);
+ if (chip->awake_votable)
+ destroy_votable(chip->awake_votable);
+
+ return 0;
+}
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "qcom,qpnp-qg", },
+ { },
+};
+
+static struct platform_driver qpnp_qg_driver = {
+ .driver = {
+ .name = "qcom,qpnp-qg",
+ .owner = THIS_MODULE,
+ .of_match_table = match_table,
+ .pm = &qpnp_qg_pm_ops,
+ },
+ .probe = qpnp_qg_probe,
+ .remove = qpnp_qg_remove,
+};
+module_platform_driver(qpnp_qg_driver);
+
+MODULE_DESCRIPTION("QPNP QG Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 3903a4b..b07efdf 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -370,6 +370,7 @@ static enum power_supply_property smb5_usb_props[] = {
POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
POWER_SUPPLY_PROP_CONNECTOR_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
};
static int smb5_usb_get_prop(struct power_supply *psy,
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 4fc60a1..dbadd95 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -1569,6 +1569,18 @@ static int smblib_dm_pulse(struct smb_charger *chg)
return rc;
}
+static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
+{
+ int rc;
+
+ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
+ rc);
+
+ return rc;
+}
+
int smblib_dp_dm(struct smb_charger *chg, int val)
{
int target_icl_ua, rc = 0;
@@ -1619,6 +1631,21 @@ int smblib_dp_dm(struct smb_charger *chg, int val)
smblib_dbg(chg, PR_PARALLEL, "ICL DOWN ICL=%d reduction=%d\n",
target_icl_ua, chg->usb_icl_delta_ua);
break;
+ case POWER_SUPPLY_DP_DM_FORCE_5V:
+ rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT);
+ if (rc < 0)
+ pr_err("Failed to force 5V\n");
+ break;
+ case POWER_SUPPLY_DP_DM_FORCE_9V:
+ rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT);
+ if (rc < 0)
+ pr_err("Failed to force 9V\n");
+ break;
+ case POWER_SUPPLY_DP_DM_FORCE_12V:
+ rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT);
+ if (rc < 0)
+ pr_err("Failed to force 12V\n");
+ break;
case POWER_SUPPLY_DP_DM_ICL_UP:
default:
break;
@@ -1745,8 +1772,13 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
{
switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
case POWER_SUPPLY_TYPE_USB_PD:
- val->intval = MICRO_12V;
+ if (chg->smb_version == PMI632_SUBTYPE)
+ val->intval = MICRO_9V;
+ else
+ val->intval = MICRO_12V;
+ break;
default:
val->intval = MICRO_5V;
break;
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index dfc303e..9a418c8 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -178,6 +178,8 @@ enum {
#define APSD_RERUN_BIT BIT(0)
#define CMD_HVDCP_2_REG (USBIN_BASE + 0x43)
+#define FORCE_12V_BIT BIT(5)
+#define FORCE_9V_BIT BIT(4)
#define FORCE_5V_BIT BIT(3)
#define SINGLE_DECREMENT_BIT BIT(1)
#define SINGLE_INCREMENT_BIT BIT(0)
diff --git a/drivers/regulator/msm_gfx_ldo.c b/drivers/regulator/msm_gfx_ldo.c
index 2800607..115a9b7 100644
--- a/drivers/regulator/msm_gfx_ldo.c
+++ b/drivers/regulator/msm_gfx_ldo.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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
@@ -773,12 +773,34 @@ static int msm_gfx_ldo_is_enabled(struct regulator_dev *rdev)
return ldo_vreg->vreg_enabled;
}
+/**
+ * msm_gfx_ldo_list_corner_voltage() - return the ldo voltage mapped to
+ * the specified voltage corner
+ * @rdev: Regulator device pointer for the msm_gfx_ldo
+ * @corner: Voltage corner
+ *
+ * Return: voltage value in microvolts or -EINVAL if the corner is out of range
+ */
+static int msm_gfx_ldo_list_corner_voltage(struct regulator_dev *rdev,
+ int corner)
+{
+ struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+
+ corner -= MIN_CORNER_OFFSET;
+
+ if (corner >= 0 && corner < ldo_vreg->num_corners)
+ return ldo_vreg->open_loop_volt[corner];
+ else
+ return -EINVAL;
+}
+
static struct regulator_ops msm_gfx_ldo_corner_ops = {
- .enable = msm_gfx_ldo_corner_enable,
- .disable = msm_gfx_ldo_disable,
- .is_enabled = msm_gfx_ldo_is_enabled,
- .set_voltage = msm_gfx_ldo_set_corner,
- .get_voltage = msm_gfx_ldo_get_corner,
+ .enable = msm_gfx_ldo_corner_enable,
+ .disable = msm_gfx_ldo_disable,
+ .is_enabled = msm_gfx_ldo_is_enabled,
+ .set_voltage = msm_gfx_ldo_set_corner,
+ .get_voltage = msm_gfx_ldo_get_corner,
+ .list_corner_voltage = msm_gfx_ldo_list_corner_voltage,
};
static int msm_gfx_ldo_get_bypass(struct regulator_dev *rdev,
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index c3b2ca8..770f056 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -250,6 +250,17 @@
deadlocks. It does not run during the bootup process, so it will
not catch any early lockups.
+config QCOM_WDOG_IPI_ENABLE
+ bool "Qcom WDT pet optimization"
+ depends on QCOM_WATCHDOG_V2
+ default n
+ help
+ When this option is enabled, watchdog sends IPI to cores in low power
+ mode also. For power optimizations, by default watchdog don't ping
+ cores in low power mode at pettime.
+
+ To track CPUs health on LPM, or on debug builds enable it.
+
config QPNP_PBS
tristate "PBS trigger support for QPNP PMIC"
depends on SPMI
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 0255761..c882403 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -65,9 +65,9 @@
obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o
obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/
+obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
-obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
diff --git a/drivers/soc/qcom/ipc_router_glink_xprt.c b/drivers/soc/qcom/ipc_router_glink_xprt.c
index ada7d5e..c93e0e1 100644
--- a/drivers/soc/qcom/ipc_router_glink_xprt.c
+++ b/drivers/soc/qcom/ipc_router_glink_xprt.c
@@ -44,6 +44,7 @@ if (ipc_router_glink_xprt_debug_mask) \
#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
#define IPC_RTR_XPRT_NAME_LEN (2 * GLINK_NAME_SIZE)
#define PIL_SUBSYSTEM_NAME_LEN 32
+#define IPC_RTR_WS_NAME_LEN ((2 * GLINK_NAME_SIZE) + 4)
#define MAX_NUM_LO_INTENTS 5
#define MAX_NUM_MD_INTENTS 3
@@ -60,6 +61,7 @@ if (ipc_router_glink_xprt_debug_mask) \
* @transport: Physical Transport Name as identified by Glink.
* @pil_edge: Edge name understood by PIL.
* @ipc_rtr_xprt_name: XPRT Name to be registered with IPC Router.
+ * @notify_rx_ws_name: Name of wakesource used in notify rx path.
* @xprt: IPC Router XPRT structure to contain XPRT specific info.
* @ch_hndl: Opaque Channel handle returned by GLink.
* @xprt_wq: Workqueue to queue read & other XPRT related works.
@@ -80,9 +82,11 @@ struct ipc_router_glink_xprt {
char transport[GLINK_NAME_SIZE];
char pil_edge[PIL_SUBSYSTEM_NAME_LEN];
char ipc_rtr_xprt_name[IPC_RTR_XPRT_NAME_LEN];
+ char notify_rx_ws_name[IPC_RTR_WS_NAME_LEN];
struct msm_ipc_router_xprt xprt;
void *ch_hndl;
struct workqueue_struct *xprt_wq;
+ struct wakeup_source notify_rxv_ws;
struct rw_semaphore ss_reset_rwlock;
int ss_reset;
void *pil;
@@ -380,6 +384,7 @@ static void glink_xprt_read_data(struct work_struct *work)
glink_rx_done(glink_xprtp->ch_hndl, rx_work->iovec, reuse_intent);
kfree(rx_work);
up_read(&glink_xprtp->ss_reset_rwlock);
+ __pm_relax(&glink_xprtp->notify_rxv_ws);
}
static void glink_xprt_open_event(struct work_struct *work)
@@ -494,6 +499,8 @@ static void glink_xprt_notify_rxv(void *handle, const void *priv,
rx_work->iovec_size = size;
rx_work->vbuf_provider = vbuf_provider;
rx_work->pbuf_provider = pbuf_provider;
+ if (!glink_xprtp->dynamic_wakeup_source)
+ __pm_stay_awake(&glink_xprtp->notify_rxv_ws);
INIT_WORK(&rx_work->work, glink_xprt_read_data);
queue_work(glink_xprtp->xprt_wq, &rx_work->work);
}
@@ -762,7 +769,10 @@ static int ipc_router_glink_config_init(
kfree(glink_xprtp);
return -EFAULT;
}
-
+ scnprintf(glink_xprtp->notify_rx_ws_name, IPC_RTR_WS_NAME_LEN,
+ "%s_%s_rx", glink_xprtp->ch_name, glink_xprtp->edge);
+ wakeup_source_init(&glink_xprtp->notify_rxv_ws,
+ glink_xprtp->notify_rx_ws_name);
mutex_lock(&glink_xprt_list_lock_lha1);
list_add(&glink_xprtp->list, &glink_xprt_list);
mutex_unlock(&glink_xprt_list_lock_lha1);
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index bc47a95..80dd2f7 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -367,10 +367,16 @@ int pil_mss_shutdown(struct pil_desc *pil)
ret);
}
- pil_mss_restart_reg(drv, true);
+ pil_mss_pdc_sync(drv, true);
+ /* Wait 6 32kHz sleep cycles for PDC SYNC true */
+ udelay(200);
+ pil_mss_restart_reg(drv, 1);
/* Wait 6 32kHz sleep cycles for reset */
udelay(200);
- ret = pil_mss_restart_reg(drv, false);
+ ret = pil_mss_restart_reg(drv, 0);
+ /* Wait 6 32kHz sleep cycles for reset false */
+ udelay(200);
+ pil_mss_pdc_sync(drv, false);
if (drv->is_booted) {
pil_mss_disable_clks(drv);
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
index b7472a4..105367e 100644
--- a/drivers/soc/qcom/qbt1000.c
+++ b/drivers/soc/qcom/qbt1000.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -42,9 +42,9 @@
#define QBT1000_IN_DEV_NAME "qbt1000_key_input"
#define QBT1000_IN_DEV_VERSION 0x0100
#define MAX_FW_EVENTS 128
-#define FP_APP_CMD_RX_IPC 132
#define FW_MAX_IPC_MSG_DATA_SIZE 0x500
-#define IPC_MSG_ID_CBGE_REQUIRED 29
+#define SEND_TZ_CMD_RETRY_CNT 10
+#define SEND_TZ_CMD_DELAY 50
/*
* shared buffer size - init with max value,
@@ -53,6 +53,27 @@
static uint32_t g_app_buf_size = SZ_256K;
static char const *const FP_APP_NAME = "fingerpr";
+enum fp_app_cmd {
+ FP_APP_CMD_RX_IPC = 132,
+
+};
+
+enum ipc_msg_id {
+ IPC_MSG_ID_CBGE_REQUIRED = 29,
+ IPC_MSG_ID_GESTURE_SWIPE_DOWN = 50,
+ IPC_MSG_ID_GESTURE_SWIPE_UP = 51,
+ IPC_MSG_ID_GESTURE_SWIPE_LEFT = 52,
+ IPC_MSG_ID_GESTURE_SWIPE_RIGHT = 53,
+ IPC_MSG_ID_GESTURE_LONG_PRESS = 54,
+ IPC_MSG_ID_FINGER_ON_SENSOR = 55,
+ IPC_MSG_ID_FINGER_OFF_SENSOR = 56,
+};
+
+enum fd_indication_mode {
+ FD_IND_MODE_BIOMETRIC = 0,
+ FD_IND_MODE_GESTURES,
+};
+
struct finger_detect_gpio {
int gpio;
int active_low;
@@ -93,6 +114,9 @@ struct qbt1000_drvdata {
wait_queue_head_t read_wait_queue;
struct qseecom_handle *app_handle;
struct qseecom_handle *fp_app_handle;
+ enum fd_indication_mode fd_ind_mode;
+ bool ipc_is_stale;
+ bool gestures_enabled;
};
/*
@@ -129,7 +153,9 @@ struct ipc_msg_type_to_fw_event {
/* mapping between firmware IPC message types to HLOS firmware events */
struct ipc_msg_type_to_fw_event g_msg_to_event[] = {
- {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED}
+ {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED},
+ {IPC_MSG_ID_FINGER_ON_SENSOR, FW_EVENT_FINGER_DOWN},
+ {IPC_MSG_ID_FINGER_OFF_SENSOR, FW_EVENT_FINGER_UP},
};
/**
@@ -529,9 +555,7 @@ static long qbt1000_ioctl(
pr_err("failed copy from user space %d\n", rc);
goto end;
}
-
drvdata->fd_gpio.key_code = set_fd_key.key_code;
-
break;
}
case QBT1000_CONFIGURE_POWER_KEY:
@@ -547,7 +571,27 @@ static long qbt1000_ioctl(
}
drvdata->fd_gpio.power_key_enabled = power_key.enable;
+ break;
+ }
+ case QBT1000_ENABLE_GESTURES:
+ {
+ struct qbt1000_enable_gestures enable_gestures;
+ if (copy_from_user(&enable_gestures, priv_arg,
+ sizeof(enable_gestures))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ if (enable_gestures.enable) {
+ drvdata->fd_ind_mode = FD_IND_MODE_GESTURES;
+ drvdata->gestures_enabled = true;
+ } else {
+ drvdata->fd_ind_mode = FD_IND_MODE_BIOMETRIC;
+ drvdata->gestures_enabled = false;
+ }
break;
}
default:
@@ -734,6 +778,18 @@ static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata)
BIT_MASK(KEY_VOLUMEDOWN);
drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |=
BIT_MASK(KEY_POWER);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_UP)] |=
+ BIT_MASK(KEY_FP_GESTURE_UP);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_DOWN)] |=
+ BIT_MASK(KEY_FP_GESTURE_DOWN);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_LEFT)] |=
+ BIT_MASK(KEY_FP_GESTURE_LEFT);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_RIGHT)] |=
+ BIT_MASK(KEY_FP_GESTURE_RIGHT);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_LONG_PRESS)] |=
+ BIT_MASK(KEY_FP_GESTURE_LONG_PRESS);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_FP_GESTURE_TAP)] |=
+ BIT_MASK(KEY_FP_GESTURE_TAP);
input_set_abs_params(drvdata->in_dev, ABS_X,
0,
@@ -773,14 +829,10 @@ static void purge_finger_events(struct qbt1000_drvdata *drvdata)
}
}
-static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
+static void gpio_report_event(struct qbt1000_drvdata *drvdata, int state)
{
- int state;
struct fw_event_desc fw_event;
- state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0)
- ^ drvdata->fd_gpio.active_low;
-
if (drvdata->fd_gpio.event_reported
&& state == drvdata->fd_gpio.last_gpio_state)
return;
@@ -790,40 +842,62 @@ static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
drvdata->fd_gpio.event_reported = 1;
drvdata->fd_gpio.last_gpio_state = state;
- if (drvdata->fd_gpio.key_code) {
- input_event(drvdata->in_dev, EV_KEY,
- drvdata->fd_gpio.key_code, !!state);
+ if (drvdata->fd_ind_mode == FD_IND_MODE_GESTURES) {
+ pr_debug("tap detected\n");
+ /*
+ * If a gesture IPC was sent but not yet decrypted and
+ * dealt with mark it as stale and don't report it
+ */
+ drvdata->ipc_is_stale = true;
+ input_event(drvdata->in_dev,
+ EV_KEY, KEY_FP_GESTURE_TAP, 1);
input_sync(drvdata->in_dev);
- }
-
- if (state && drvdata->fd_gpio.power_key_enabled) {
- input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1);
+ input_event(drvdata->in_dev,
+ EV_KEY, KEY_FP_GESTURE_TAP, 0);
input_sync(drvdata->in_dev);
- input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0);
- input_sync(drvdata->in_dev);
+ } else if (drvdata->fd_ind_mode == FD_IND_MODE_BIOMETRIC) {
+ if (state && drvdata->fd_gpio.power_key_enabled) {
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1);
+ input_sync(drvdata->in_dev);
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0);
+ input_sync(drvdata->in_dev);
+ } else if (!drvdata->gestures_enabled) {
+ input_event(drvdata->in_dev, EV_KEY,
+ drvdata->fd_gpio.key_code, !!state);
+ input_sync(drvdata->in_dev);
+ }
+ fw_event.ev = state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+
+ if (kfifo_is_full(&drvdata->fw_events)) {
+ struct fw_event_desc dummy_fw_event;
+
+ pr_warn("fw events fifo: full, dropping oldest item\n");
+ if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event))
+ pr_err("fw events fifo: could not remove oldest item\n");
+ }
+
+ purge_finger_events(drvdata);
+
+ if (!kfifo_put(&drvdata->fw_events, fw_event))
+ pr_err("fw events fifo: error adding item\n");
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ wake_up_interruptible(&drvdata->read_wait_queue);
+ } else {
+ pr_err("invalid mode %d\n", drvdata->fd_ind_mode);
}
-
- fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP);
-
- mutex_lock(&drvdata->fw_events_mutex);
-
- if (kfifo_is_full(&drvdata->fw_events)) {
- struct fw_event_desc dummy_fw_event;
-
- pr_warn("fw events fifo: full, dropping oldest item\n");
- if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event))
- pr_err("fw events fifo: could not remove oldest item\n");
- }
-
- purge_finger_events(drvdata);
-
- if (!kfifo_put(&drvdata->fw_events, fw_event))
- pr_err("fw events fifo: error adding item\n");
-
- mutex_unlock(&drvdata->fw_events_mutex);
- wake_up_interruptible(&drvdata->read_wait_queue);
}
+static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
+{
+ int state;
+
+ state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0)
+ ^ drvdata->fd_gpio.active_low;
+ gpio_report_event(drvdata, state);
+}
static void qbt1000_gpio_work_func(struct work_struct *work)
{
struct qbt1000_drvdata *drvdata =
@@ -867,7 +941,7 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
uint32_t rxipc = FP_APP_CMD_RX_IPC;
struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id;
int rc = 0;
- uint32_t retry_count = 10;
+ uint32_t retry_count = SEND_TZ_CMD_RETRY_CNT;
pm_stay_awake(drvdata->dev);
@@ -884,6 +958,12 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
if (!drvdata->fp_app_handle)
goto end;
+ /*
+ * Indicate that IPC is valid (not stale)
+ * This is relevant only for gestures IPCs
+ */
+ drvdata->ipc_is_stale = false;
+
while (retry_count > 0) {
/*
* send the TZ command to fetch the message from firmware
@@ -893,7 +973,8 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
&rxipc, sizeof(rxipc),
(void *)&rx_cmd, sizeof(*rx_cmd));
if (rc < 0) {
- msleep(50); // sleep for 50ms before retry
+ /* sleep before retry */
+ msleep(SEND_TZ_CMD_DELAY);
retry_count -= 1;
continue;
} else {
@@ -915,28 +996,76 @@ static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
msg_buffer = rx_cmd->msg_data;
for (j = 0; j < rx_cmd->numMsgs; j++) {
+ unsigned int key_event_code = 0;
+
header = (struct fw_ipc_header *) msg_buffer;
- /*
- * given the IPC message type, search for a corresponding
- * event for the driver client. If found, add to the events
- * FIFO
- */
- for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
- if (g_msg_to_event[i].msg_type == header->msg_type) {
- enum qbt1000_fw_event ev =
+
+ switch (header->msg_type) {
+ case IPC_MSG_ID_GESTURE_SWIPE_UP:
+ key_event_code = KEY_FP_GESTURE_UP;
+ break;
+ case IPC_MSG_ID_GESTURE_SWIPE_DOWN:
+ key_event_code = KEY_FP_GESTURE_DOWN;
+ break;
+ case IPC_MSG_ID_GESTURE_SWIPE_LEFT:
+ key_event_code = KEY_FP_GESTURE_LEFT;
+ break;
+ case IPC_MSG_ID_GESTURE_SWIPE_RIGHT:
+ key_event_code = KEY_FP_GESTURE_RIGHT;
+ break;
+ case IPC_MSG_ID_GESTURE_LONG_PRESS:
+ key_event_code = KEY_FP_GESTURE_LONG_PRESS;
+ break;
+ default:
+ key_event_code = 0;
+ break;
+ }
+
+ if (key_event_code != 0) {
+ pr_debug("geseture detected %d\n", key_event_code);
+ /*
+ * Send gesture event if no tap arrived
+ * since the IPC was sent
+ */
+ if (drvdata->ipc_is_stale == false) {
+ input_event(drvdata->in_dev, EV_KEY,
+ key_event_code, 1);
+ input_sync(drvdata->in_dev);
+ input_event(drvdata->in_dev, EV_KEY,
+ key_event_code, 0);
+ input_sync(drvdata->in_dev);
+ }
+ } else {
+
+ /*
+ * given the IPC message type, search for a
+ * corresponding event for the driver client.
+ * If found, add to the events FIFO
+ */
+ for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
+ uint32_t msg_type = g_msg_to_event[i].msg_type;
+
+ if (msg_type == header->msg_type) {
+ enum qbt1000_fw_event ev =
g_msg_to_event[i].fw_event;
- struct fw_event_desc fw_ev_desc;
+ struct fw_event_desc fw_ev_desc;
- mutex_lock(&drvdata->fw_events_mutex);
- pr_debug("fw events: add %d\n", (int) ev);
- fw_ev_desc.ev = ev;
-
- if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
- pr_err("fw events: fifo full, drop event %d\n",
+ mutex_lock(&drvdata->fw_events_mutex);
+ pr_debug("fw events: add %d\n",
(int) ev);
+ fw_ev_desc.ev = ev;
- mutex_unlock(&drvdata->fw_events_mutex);
- break;
+ purge_finger_events(drvdata);
+
+ if (!kfifo_put(&drvdata->fw_events,
+ fw_ev_desc))
+ pr_err("fw events: fifo full");
+ pr_err(", drop event %d\n",
+ (int) ev);
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ break;
+ }
}
}
msg_buffer += sizeof(*header) + header->msg_len;
@@ -998,6 +1127,7 @@ static int setup_ipc_irq(struct platform_device *pdev,
drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio);
pr_debug("\nirq %d gpio %d\n",
drvdata->fw_ipc.irq, drvdata->fw_ipc.gpio);
+
if (drvdata->fw_ipc.irq < 0) {
rc = drvdata->fw_ipc.irq;
pr_err("no irq for gpio %d, error=%d\n",
@@ -1136,6 +1266,10 @@ static int qbt1000_probe(struct platform_device *pdev)
if (rc < 0)
goto end;
+ drvdata->fd_ind_mode = FD_IND_MODE_BIOMETRIC;
+ drvdata->ipc_is_stale = false;
+ drvdata->gestures_enabled = false;
+
end:
pr_debug("qbt1000_probe end : %d\n", rc);
return rc;
diff --git a/drivers/soc/qcom/rpmh_master_stat.c b/drivers/soc/qcom/rpmh_master_stat.c
index 2c379a0..bc665fd 100644
--- a/drivers/soc/qcom/rpmh_master_stat.c
+++ b/drivers/soc/qcom/rpmh_master_stat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -13,21 +13,25 @@
#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME
-#include <linux/debugfs.h>
-#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
-#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/uaccess.h>
#include <soc/qcom/smem.h>
+#include "rpmh_master_stat.h"
+
+#define UNIT_DIST 0x14
+#define REG_VALID 0x0
+#define REG_DATA_LO 0x4
+#define REG_DATA_HI 0x8
+
+#define GET_ADDR(REG, UNIT_NO) (REG + (UNIT_DIST * UNIT_NO))
enum master_smem_id {
MPSS = 605,
@@ -48,6 +52,14 @@ enum master_pid {
PID_DISPLAY = PID_APSS,
};
+enum profile_data {
+ POWER_DOWN_START,
+ POWER_UP_END,
+ POWER_DOWN_END,
+ POWER_UP_START,
+ NUM_UNIT,
+};
+
struct msm_rpmh_master_data {
char *master_name;
enum master_smem_id smem_id;
@@ -66,16 +78,24 @@ static const struct msm_rpmh_master_data rpmh_masters[] = {
struct msm_rpmh_master_stats {
uint32_t version_id;
uint32_t counts;
- uint64_t last_entered_at;
- uint64_t last_exited_at;
+ uint64_t last_entered;
+ uint64_t last_exited;
uint64_t accumulated_duration;
};
+struct msm_rpmh_profile_unit {
+ uint64_t value;
+ uint64_t valid;
+};
+
struct rpmh_master_stats_prv_data {
struct kobj_attribute ka;
struct kobject *kobj;
};
+static struct msm_rpmh_master_stats apss_master_stats;
+static void __iomem *rpmh_unit_base;
+
static DEFINE_MUTEX(rpmh_stats_mutex);
static ssize_t msm_rpmh_master_stats_print_data(char *prvbuf, ssize_t length,
@@ -88,7 +108,7 @@ static ssize_t msm_rpmh_master_stats_print_data(char *prvbuf, ssize_t length,
"\tSleep Last Exited At:0x%llx\n"
"\tSleep Accumulated Duration:0x%llx\n\n",
name, record->version_id, record->counts,
- record->last_entered_at, record->last_exited_at,
+ record->last_entered, record->last_exited,
record->accumulated_duration);
}
@@ -100,13 +120,16 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj,
unsigned int size = 0;
struct msm_rpmh_master_stats *record = NULL;
- /*
- * Read SMEM data written by masters
- */
-
mutex_lock(&rpmh_stats_mutex);
- for (i = 0, length = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
+ /* First Read APSS master stats */
+
+ length = msm_rpmh_master_stats_print_data(buf, PAGE_SIZE,
+ &apss_master_stats, "APSS");
+
+ /* Read SMEM data written by other masters */
+
+ for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) {
record = (struct msm_rpmh_master_stats *) smem_get_entry(
rpmh_masters[i].smem_id, &size,
rpmh_masters[i].pid, 0);
@@ -122,30 +145,66 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj,
return length;
}
+static inline void msm_rpmh_apss_master_stats_update(
+ struct msm_rpmh_profile_unit *profile_unit)
+{
+ apss_master_stats.counts++;
+ apss_master_stats.last_entered = profile_unit[POWER_DOWN_END].value;
+ apss_master_stats.last_exited = profile_unit[POWER_UP_START].value;
+ apss_master_stats.accumulated_duration +=
+ (apss_master_stats.last_exited
+ - apss_master_stats.last_entered);
+}
+
+void msm_rpmh_master_stats_update(void)
+{
+ int i;
+ struct msm_rpmh_profile_unit profile_unit[NUM_UNIT];
+
+ if (!rpmh_unit_base)
+ return;
+
+ for (i = POWER_DOWN_END; i < NUM_UNIT; i++) {
+ profile_unit[i].valid = readl_relaxed(rpmh_unit_base +
+ GET_ADDR(REG_VALID, i));
+
+ /*
+ * Do not update APSS stats if valid bit is not set.
+ * It means APSS did not execute cx-off sequence.
+ * This can be due to fall through at some point.
+ */
+
+ if (!(profile_unit[i].valid & BIT(REG_VALID)))
+ return;
+
+ profile_unit[i].value = readl_relaxed(rpmh_unit_base +
+ GET_ADDR(REG_DATA_LO, i));
+ profile_unit[i].value |= ((uint64_t)
+ readl_relaxed(rpmh_unit_base +
+ GET_ADDR(REG_DATA_HI, i)) << 32);
+ }
+ msm_rpmh_apss_master_stats_update(profile_unit);
+}
+EXPORT_SYMBOL(msm_rpmh_master_stats_update);
+
static int msm_rpmh_master_stats_probe(struct platform_device *pdev)
{
struct rpmh_master_stats_prv_data *prvdata = NULL;
struct kobject *rpmh_master_stats_kobj = NULL;
- int ret = 0;
+ int ret = -ENOMEM;
if (!pdev)
return -EINVAL;
- prvdata = kzalloc(sizeof(struct rpmh_master_stats_prv_data),
- GFP_KERNEL);
- if (!prvdata) {
- ret = -ENOMEM;
- goto fail;
- }
+ prvdata = devm_kzalloc(&pdev->dev, sizeof(*prvdata), GFP_KERNEL);
+ if (!prvdata)
+ return ret;
rpmh_master_stats_kobj = kobject_create_and_add(
"rpmh_stats",
power_kobj);
- if (!rpmh_master_stats_kobj) {
- ret = -ENOMEM;
- kfree(prvdata);
- goto fail;
- }
+ if (!rpmh_master_stats_kobj)
+ return ret;
prvdata->kobj = rpmh_master_stats_kobj;
@@ -158,14 +217,24 @@ static int msm_rpmh_master_stats_probe(struct platform_device *pdev)
ret = sysfs_create_file(prvdata->kobj, &prvdata->ka.attr);
if (ret) {
pr_err("sysfs_create_file failed\n");
- kobject_put(prvdata->kobj);
- kfree(prvdata);
- goto fail;
+ goto fail_sysfs;
}
- platform_set_drvdata(pdev, prvdata);
+ rpmh_unit_base = of_iomap(pdev->dev.of_node, 0);
+ if (!rpmh_unit_base) {
+ pr_err("Failed to get rpmh_unit_base\n");
+ ret = -ENOMEM;
+ goto fail_iomap;
+ }
-fail:
+ apss_master_stats.version_id = 0x1;
+ platform_set_drvdata(pdev, prvdata);
+ return ret;
+
+fail_iomap:
+ sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr);
+fail_sysfs:
+ kobject_put(prvdata->kobj);
return ret;
}
@@ -181,14 +250,14 @@ static int msm_rpmh_master_stats_remove(struct platform_device *pdev)
sysfs_remove_file(prvdata->kobj, &prvdata->ka.attr);
kobject_put(prvdata->kobj);
- kfree(prvdata);
platform_set_drvdata(pdev, NULL);
+ iounmap(rpmh_unit_base);
return 0;
}
static const struct of_device_id rpmh_master_table[] = {
- {.compatible = "qcom,rpmh-master-stats"},
+ {.compatible = "qcom,rpmh-master-stats-v1"},
{},
};
@@ -197,24 +266,11 @@ static struct platform_driver msm_rpmh_master_stats_driver = {
.remove = msm_rpmh_master_stats_remove,
.driver = {
.name = "msm_rpmh_master_stats",
- .owner = THIS_MODULE,
.of_match_table = rpmh_master_table,
},
};
-static int __init msm_rpmh_master_stats_init(void)
-{
- return platform_driver_register(&msm_rpmh_master_stats_driver);
-}
-
-static void __exit msm_rpmh_master_stats_exit(void)
-{
- platform_driver_unregister(&msm_rpmh_master_stats_driver);
-}
-
-module_init(msm_rpmh_master_stats_init);
-module_exit(msm_rpmh_master_stats_exit);
-
+module_platform_driver(msm_rpmh_master_stats_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM RPMH Master Statistics driver");
MODULE_ALIAS("platform:msm_rpmh_master_stat_log");
diff --git a/drivers/soc/qcom/rpmh_master_stat.h b/drivers/soc/qcom/rpmh_master_stat.h
new file mode 100644
index 0000000..c3fe7dc
--- /dev/null
+++ b/drivers/soc/qcom/rpmh_master_stat.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 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.
+ *
+ */
+
+#if defined(CONFIG_QTI_RPMH_API) && defined(CONFIG_QTI_RPM_STATS_LOG)
+
+void msm_rpmh_master_stats_update(void);
+
+#else
+
+static inline void msm_rpmh_master_stats_update(void) {}
+
+#endif
diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c
index 480a33c..9c6edbf 100644
--- a/drivers/soc/qcom/system_pm.c
+++ b/drivers/soc/qcom/system_pm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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,11 +14,10 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <asm/arch_timer.h>
-
-#include <soc/qcom/rpmh.h>
-#include <soc/qcom/system_pm.h>
-
#include <clocksource/arm_arch_timer.h>
+#include "rpmh_master_stat.h"
+#include <soc/qcom/lpm_levels.h>
+#include <soc/qcom/rpmh.h>
#define PDC_TIME_VALID_SHIFT 31
#define PDC_TIME_UPPER_MASK 0xFFFFFF
@@ -36,7 +35,7 @@ static int setup_wakeup(uint32_t lo, uint32_t hi)
return rpmh_write_control(rpmh_client, cmd, ARRAY_SIZE(cmd));
}
-int system_sleep_update_wakeup(void)
+static int system_sleep_update_wakeup(bool from_idle)
{
uint32_t lo = ~0U, hi = ~0U;
@@ -45,16 +44,14 @@ int system_sleep_update_wakeup(void)
return setup_wakeup(lo, hi);
}
-EXPORT_SYMBOL(system_sleep_update_wakeup);
/**
* system_sleep_allowed() - Returns if its okay to enter system low power modes
*/
-bool system_sleep_allowed(void)
+static bool system_sleep_allowed(void)
{
return (rpmh_ctrlr_idle(rpmh_client) == 0);
}
-EXPORT_SYMBOL(system_sleep_allowed);
/**
* system_sleep_enter() - Activties done when entering system low power modes
@@ -62,22 +59,25 @@ EXPORT_SYMBOL(system_sleep_allowed);
* Returns 0 for success or error values from writing the sleep/wake values to
* the hardware block.
*/
-int system_sleep_enter(void)
+static int system_sleep_enter(struct cpumask *mask)
{
- if (IS_ERR_OR_NULL(rpmh_client))
- return -EFAULT;
-
return rpmh_flush(rpmh_client);
}
-EXPORT_SYMBOL(system_sleep_enter);
/**
* system_sleep_exit() - Activities done when exiting system low power modes
*/
-void system_sleep_exit(void)
+static void system_sleep_exit(void)
{
+ msm_rpmh_master_stats_update();
}
-EXPORT_SYMBOL(system_sleep_exit);
+
+static struct system_pm_ops pm_ops = {
+ .enter = system_sleep_enter,
+ .exit = system_sleep_exit,
+ .update_wakeup = system_sleep_update_wakeup,
+ .sleep_allowed = system_sleep_allowed,
+};
static int sys_pm_probe(struct platform_device *pdev)
{
@@ -85,7 +85,7 @@ static int sys_pm_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(rpmh_client))
return PTR_ERR(rpmh_client);
- return 0;
+ return register_system_pm_ops(&pm_ops);
}
static const struct of_device_id sys_pm_drv_match[] = {
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 6d58d6b..8040d6d 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -112,12 +112,22 @@ static long WDT_HZ = 32765;
module_param(WDT_HZ, long, 0);
/*
+ * Watchdog ipi optimization:
+ * Does not ping cores in low power mode at pet time to save power.
+ * This feature is enabled by default.
+ *
* On the kernel command line specify
- * watchdog_v2.ipi_opt_en=1 to enable the watchdog ipi ping
- * optimization. By default it is turned off
+ * watchdog_v2.ipi_en=1 to disable this optimization.
+ * Or, can be turned off, by enabling CONFIG_QCOM_WDOG_IPI_ENABLE.
*/
-static int ipi_opt_en;
-module_param(ipi_opt_en, int, 0);
+#ifdef CONFIG_QCOM_WDOG_IPI_ENABLE
+#define IPI_CORES_IN_LPM 1
+#else
+#define IPI_CORES_IN_LPM 0
+#endif
+
+static int ipi_en = IPI_CORES_IN_LPM;
+module_param(ipi_en, int, 0444);
static void dump_cpu_alive_mask(struct msm_watchdog_data *wdog_dd)
{
@@ -463,7 +473,7 @@ static int msm_watchdog_remove(struct platform_device *pdev)
struct msm_watchdog_data *wdog_dd =
(struct msm_watchdog_data *)platform_get_drvdata(pdev);
- if (ipi_opt_en)
+ if (!ipi_en)
cpu_pm_unregister_notifier(&wdog_cpu_pm_nb);
mutex_lock(&wdog_dd->disable_lock);
@@ -709,7 +719,7 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd)
if (wdog_dd->irq_ppi)
enable_percpu_irq(wdog_dd->bark_irq, 0);
- if (ipi_opt_en)
+ if (!ipi_en)
cpu_pm_register_notifier(&wdog_cpu_pm_nb);
dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n");
}
diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c
index eeeeb28..e8028d2 100644
--- a/drivers/staging/android/ion/ion_carveout_heap.c
+++ b/drivers/staging/android/ion/ion_carveout_heap.c
@@ -77,6 +77,7 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap,
struct sg_table *table;
ion_phys_addr_t paddr;
int ret;
+ struct device *dev = heap->priv;
if (align > PAGE_SIZE)
return -EINVAL;
@@ -95,8 +96,19 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap,
}
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
+ /*
+ * This is not correct - sg_dma_address needs a dma_addr_t that is valid
+ * for the targeted device, but this works on the currently targeted
+ * hardware.
+ */
+ sg_dma_address(table->sgl) = sg_phys(table->sgl);
+
buffer->priv_virt = table;
+ if (ion_buffer_cached(buffer))
+ dma_sync_sg_for_cpu(dev, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
+
return 0;
err_free_table:
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 83b46d4..a1602e4 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -510,17 +510,36 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
if (test_task_flag(tsk, TIF_MM_RELEASED))
continue;
- if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
- if (test_task_lmk_waiting(tsk)) {
- rcu_read_unlock();
- mutex_unlock(&scan_mutex);
- return 0;
- }
- }
+ if (oom_reaper) {
+ p = find_lock_task_mm(tsk);
+ if (!p)
+ continue;
- p = find_lock_task_mm(tsk);
- if (!p)
- continue;
+ if (test_bit(MMF_OOM_VICTIM, &p->mm->flags)) {
+ if (test_bit(MMF_OOM_SKIP, &p->mm->flags)) {
+ task_unlock(p);
+ continue;
+ } else if (time_before_eq(jiffies,
+ lowmem_deathpending_timeout)) {
+ task_unlock(p);
+ rcu_read_unlock();
+ mutex_unlock(&scan_mutex);
+ return 0;
+ }
+ }
+ } else {
+ if (time_before_eq(jiffies,
+ lowmem_deathpending_timeout))
+ if (test_task_lmk_waiting(tsk)) {
+ rcu_read_unlock();
+ mutex_unlock(&scan_mutex);
+ return 0;
+ }
+
+ p = find_lock_task_mm(tsk);
+ if (!p)
+ continue;
+ }
oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj < min_score_adj) {
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 27bf54b..1259654 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -58,4 +58,4 @@
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_THERMAL_QPNP_ADC_TM) += qpnp-adc-tm.o
-obj-$(CONFIG_THERMAL_TSENS) += msm-tsens.o tsens2xxx.o tsens-dbg.o tsens-mtc.o
+obj-$(CONFIG_THERMAL_TSENS) += msm-tsens.o tsens2xxx.o tsens-dbg.o tsens-mtc.o tsens1xxx.o
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index fe0a7c7..c137d3d 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -49,6 +49,11 @@ static int tsens_init(struct tsens_device *tmdev)
return tmdev->ops->hw_init(tmdev);
}
+static int tsens_calib(struct tsens_device *tmdev)
+{
+ return tmdev->ops->calibrate(tmdev);
+}
+
static int tsens_register_interrupts(struct tsens_device *tmdev)
{
if (tmdev->ops->interrupts_reg)
@@ -82,6 +87,9 @@ static const struct of_device_id tsens_table[] = {
{ .compatible = "qcom,tsens24xx",
.data = &data_tsens24xx,
},
+ { .compatible = "qcom,msm8937-tsens",
+ .data = &data_tsens14xx,
+ },
{}
};
MODULE_DEVICE_TABLE(of, tsens_table);
@@ -97,6 +105,7 @@ static int get_device_tree_data(struct platform_device *pdev,
struct device_node *of_node = pdev->dev.of_node;
const struct of_device_id *id;
const struct tsens_data *data;
+ int rc = 0;
struct resource *res_tsens_mem;
if (!of_match_node(tsens_table, of_node)) {
@@ -150,7 +159,27 @@ static int get_device_tree_data(struct platform_device *pdev,
return PTR_ERR(tmdev->tsens_tm_addr);
}
- return 0;
+ /* TSENS eeprom register region */
+ res_tsens_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tsens_eeprom_physical");
+ if (!res_tsens_mem) {
+ pr_debug("Could not get tsens physical address resource\n");
+ } else {
+ tmdev->tsens_calib_addr = devm_ioremap_resource(&pdev->dev,
+ res_tsens_mem);
+ if (IS_ERR(tmdev->tsens_calib_addr)) {
+ dev_err(&pdev->dev, "Failed to IO map TSENS EEPROM registers.\n");
+ rc = PTR_ERR(tmdev->tsens_calib_addr);
+ } else {
+ rc = tsens_calib(tmdev);
+ if (rc) {
+ pr_err("Error initializing TSENS controller\n");
+ return rc;
+ }
+ }
+ }
+
+ return rc;
}
static int tsens_thermal_zone_register(struct tsens_device *tmdev)
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index ae4741d..885b15c 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -23,10 +23,15 @@
#define DEBUG_SIZE 10
#define TSENS_MAX_SENSORS 16
+#define TSENS_1x_MAX_SENSORS 11
#define TSENS_CONTROLLER_ID(n) (n)
#define TSENS_CTRL_ADDR(n) (n)
#define TSENS_TM_SN_STATUS(n) ((n) + 0xa0)
+#define ONE_PT_CALIB 0x1
+#define ONE_PT_CALIB2 0x2
+#define TWO_PT_CALIB 0x3
+
enum tsens_dbg_type {
TSENS_DBG_POLL,
TSENS_DBG_LOG_TEMP_READS,
@@ -70,6 +75,8 @@ struct tsens_context {
int high_temp;
int low_temp;
int crit_temp;
+ int high_adc_code;
+ int low_adc_code;
};
struct tsens_sensor {
@@ -79,6 +86,8 @@ struct tsens_sensor {
u32 id;
const char *sensor_name;
struct tsens_context thr_state;
+ int offset;
+ int slope;
};
/**
@@ -93,6 +102,7 @@ struct tsens_ops {
int (*interrupts_reg)(struct tsens_device *);
int (*dbg)(struct tsens_device *, u32, u32, int *);
int (*sensor_en)(struct tsens_device *, u32);
+ int (*calibrate)(struct tsens_device *);
};
struct tsens_irqs {
@@ -116,14 +126,15 @@ struct tsens_data {
bool wd_bark;
u32 wd_bark_mask;
bool mtc;
+ bool valid_status_check;
};
struct tsens_mtc_sysfs {
- uint32_t zone_log;
+ u32 zone_log;
int zone_mtc;
int th1;
int th2;
- uint32_t zone_hist;
+ u32 zone_hist;
};
struct tsens_device {
@@ -134,6 +145,7 @@ struct tsens_device {
struct regmap_field *status_field;
void __iomem *tsens_srot_addr;
void __iomem *tsens_tm_addr;
+ void __iomem *tsens_calib_addr;
const struct tsens_ops *ops;
struct tsens_dbg_context tsens_dbg;
spinlock_t tsens_crit_lock;
@@ -144,6 +156,7 @@ struct tsens_device {
};
extern const struct tsens_data data_tsens2xxx, data_tsens23xx, data_tsens24xx;
+extern const struct tsens_data data_tsens14xx;
extern struct list_head tsens_device_list;
#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
new file mode 100644
index 0000000..e2fad32
--- /dev/null
+++ b/drivers/thermal/tsens1xxx.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/vmalloc.h>
+#include "tsens.h"
+#include "thermal_core.h"
+
+#define TSENS_DRIVER_NAME "msm-tsens"
+
+#define TSENS_UPPER_LOWER_INTERRUPT_CTRL(n) (n)
+#define TSENS_INTERRUPT_EN BIT(0)
+
+#define TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(n) ((n) + 0x04)
+#define TSENS_UPPER_STATUS_CLR BIT(21)
+#define TSENS_LOWER_STATUS_CLR BIT(20)
+#define TSENS_UPPER_THRESHOLD_MASK 0xffc00
+#define TSENS_LOWER_THRESHOLD_MASK 0x3ff
+#define TSENS_UPPER_THRESHOLD_SHIFT 10
+
+#define TSENS_S0_STATUS_ADDR(n) ((n) + 0x30)
+#define TSENS_SN_ADDR_OFFSET 0x4
+#define TSENS_SN_STATUS_TEMP_MASK 0x3ff
+#define TSENS_SN_STATUS_LOWER_STATUS BIT(11)
+#define TSENS_SN_STATUS_UPPER_STATUS BIT(12)
+#define TSENS_STATUS_ADDR_OFFSET 2
+
+#define TSENS_TRDY_MASK BIT(0)
+
+#define TSENS_SN_STATUS_ADDR(n) ((n) + 0x44)
+#define TSENS_SN_STATUS_VALID BIT(14)
+#define TSENS_SN_STATUS_VALID_MASK 0x4000
+#define TSENS_TRDY_ADDR(n) ((n) + 0x84)
+
+#define TSENS_CTRL_ADDR(n) (n)
+#define TSENS_EN BIT(0)
+#define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0x7ff)
+#define TSENS_TRDY_RDY_MIN_TIME 2000
+#define TSENS_TRDY_RDY_MAX_TIME 2100
+#define TSENS_THRESHOLD_MAX_CODE 0x3ff
+#define TSENS_THRESHOLD_MIN_CODE 0x0
+
+/* eeprom layout data for 8937 */
+#define BASE0_MASK 0x000000ff
+#define BASE1_MASK 0xff000000
+#define BASE1_SHIFT 24
+
+#define S0_P1_MASK 0x000001f8
+#define S1_P1_MASK 0x001f8000
+#define S2_P1_MASK_0_4 0xf8000000
+#define S2_P1_MASK_5 0x00000001
+#define S3_P1_MASK 0x00001f80
+#define S4_P1_MASK 0x01f80000
+#define S5_P1_MASK 0x00003f00
+#define S6_P1_MASK 0x03f00000
+#define S7_P1_MASK 0x0000003f
+#define S8_P1_MASK 0x0003f000
+#define S9_P1_MASK 0x0000003f
+#define S10_P1_MASK 0x0003f000
+
+#define S0_P2_MASK 0x00007e00
+#define S1_P2_MASK 0x07e00000
+#define S2_P2_MASK 0x0000007e
+#define S3_P2_MASK 0x0007e000
+#define S4_P2_MASK 0x7e000000
+#define S5_P2_MASK 0x000fc000
+#define S6_P2_MASK 0xfc000000
+#define S7_P2_MASK 0x00000fc0
+#define S8_P2_MASK 0x00fc0000
+#define S9_P2_MASK 0x00000fc0
+#define S10_P2_MASK 0x00fc0000
+
+#define S0_P1_SHIFT 3
+#define S1_P1_SHIFT 15
+#define S2_P1_SHIFT_0_4 27
+#define S2_P1_SHIFT_5 5
+#define S3_P1_SHIFT 7
+#define S4_P1_SHIFT 19
+#define S5_P1_SHIFT 8
+#define S6_P1_SHIFT 20
+#define S8_P1_SHIFT 12
+#define S10_P1_SHIFT 12
+
+#define S0_P2_SHIFT 9
+#define S1_P2_SHIFT 21
+#define S2_P2_SHIFT 1
+#define S3_P2_SHIFT 13
+#define S4_P2_SHIFT 25
+#define S5_P2_SHIFT 14
+#define S6_P2_SHIFT 26
+#define S7_P2_SHIFT 6
+#define S8_P2_SHIFT 18
+#define S9_P2_SHIFT 6
+#define S10_P2_SHIFT 18
+
+#define CAL_SEL_MASK 0x00000007
+
+#define CAL_DEGC_PT1 30
+#define CAL_DEGC_PT2 120
+#define SLOPE_FACTOR 1000
+#define SLOPE_DEFAULT 3200
+
+/*
+ * Use this function on devices where slope and offset calculations
+ * depend on calibration data read from qfprom. On others the slope
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
+ * resp.
+ */
+static void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
+ u32 *p2, u32 mode)
+{
+ int i;
+ int num, den;
+
+ for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
+ pr_debug(
+ "sensor%d - data_point1:%#x data_point2:%#x\n",
+ i, p1[i], p2[i]);
+
+ tmdev->sensor[i].slope = SLOPE_DEFAULT;
+ if (mode == TWO_PT_CALIB) {
+ /*
+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+ * temp_120_degc - temp_30_degc (x2 - x1)
+ */
+ num = p2[i] - p1[i];
+ num *= SLOPE_FACTOR;
+ den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
+ tmdev->sensor[i].slope = num / den;
+ }
+
+ tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+ (CAL_DEGC_PT1 *
+ tmdev->sensor[i].slope);
+ pr_debug("offset:%d\n", tmdev->sensor[i].offset);
+ }
+}
+
+static int code_to_degc(u32 adc_code, const struct tsens_sensor *sensor)
+{
+ int degc, num, den;
+
+ num = (adc_code * SLOPE_FACTOR) - sensor->offset;
+ den = sensor->slope;
+
+ if (num > 0)
+ degc = num + (den / 2);
+ else if (num < 0)
+ degc = num - (den / 2);
+ else
+ degc = num;
+
+ degc /= den;
+
+ return degc;
+}
+
+static int degc_to_code(int degc, const struct tsens_sensor *sensor)
+{
+ int code = ((degc * sensor->slope)
+ + sensor->offset)/SLOPE_FACTOR;
+
+ if (code > TSENS_THRESHOLD_MAX_CODE)
+ code = TSENS_THRESHOLD_MAX_CODE;
+ else if (code < TSENS_THRESHOLD_MIN_CODE)
+ code = TSENS_THRESHOLD_MIN_CODE;
+ pr_debug("raw_code:0x%x, degc:%d\n",
+ code, degc);
+ return code;
+}
+
+static int calibrate_8937(struct tsens_device *tmdev)
+{
+ int base0 = 0, base1 = 0, i;
+ u32 p1[TSENS_1x_MAX_SENSORS], p2[TSENS_1x_MAX_SENSORS];
+ int mode = 0, tmp = 0;
+ u32 qfprom_cdata[5] = {0, 0, 0, 0, 0};
+
+ qfprom_cdata[0] = readl_relaxed(tmdev->tsens_calib_addr + 0x1D8);
+ qfprom_cdata[1] = readl_relaxed(tmdev->tsens_calib_addr + 0x1DC);
+ qfprom_cdata[2] = readl_relaxed(tmdev->tsens_calib_addr + 0x210);
+ qfprom_cdata[3] = readl_relaxed(tmdev->tsens_calib_addr + 0x214);
+ qfprom_cdata[4] = readl_relaxed(tmdev->tsens_calib_addr + 0x230);
+
+ mode = (qfprom_cdata[2] & CAL_SEL_MASK);
+ pr_debug("calibration mode is %d\n", mode);
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
+ p2[0] = (qfprom_cdata[2] & S0_P2_MASK) >> S0_P2_SHIFT;
+ p2[1] = (qfprom_cdata[2] & S1_P2_MASK) >> S1_P2_SHIFT;
+ p2[2] = (qfprom_cdata[3] & S2_P2_MASK) >> S2_P2_SHIFT;
+ p2[3] = (qfprom_cdata[3] & S3_P2_MASK) >> S3_P2_SHIFT;
+ p2[4] = (qfprom_cdata[3] & S4_P2_MASK) >> S4_P2_SHIFT;
+ p2[5] = (qfprom_cdata[0] & S5_P2_MASK) >> S5_P2_SHIFT;
+ p2[6] = (qfprom_cdata[0] & S6_P2_MASK) >> S6_P2_SHIFT;
+ p2[7] = (qfprom_cdata[1] & S7_P2_MASK) >> S7_P2_SHIFT;
+ p2[8] = (qfprom_cdata[1] & S8_P2_MASK) >> S8_P2_SHIFT;
+ p2[9] = (qfprom_cdata[4] & S9_P2_MASK) >> S9_P2_SHIFT;
+ p2[10] = (qfprom_cdata[4] & S10_P2_MASK) >> S10_P2_SHIFT;
+
+ for (i = 0; i < TSENS_1x_MAX_SENSORS; i++)
+ p2[i] = ((base1 + p2[i]) << 2);
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ base0 = (qfprom_cdata[0] & BASE0_MASK);
+ p1[0] = (qfprom_cdata[2] & S0_P1_MASK) >> S0_P1_SHIFT;
+ p1[1] = (qfprom_cdata[2] & S1_P1_MASK) >> S1_P1_SHIFT;
+ p1[2] = (qfprom_cdata[2] & S2_P1_MASK_0_4) >> S2_P1_SHIFT_0_4;
+ tmp = (qfprom_cdata[3] & S2_P1_MASK_5) << S2_P1_SHIFT_5;
+ p1[2] |= tmp;
+ p1[3] = (qfprom_cdata[3] & S3_P1_MASK) >> S3_P1_SHIFT;
+ p1[4] = (qfprom_cdata[3] & S4_P1_MASK) >> S4_P1_SHIFT;
+ p1[5] = (qfprom_cdata[0] & S5_P1_MASK) >> S5_P1_SHIFT;
+ p1[6] = (qfprom_cdata[0] & S6_P1_MASK) >> S6_P1_SHIFT;
+ p1[7] = (qfprom_cdata[1] & S7_P1_MASK);
+ p1[8] = (qfprom_cdata[1] & S8_P1_MASK) >> S8_P1_SHIFT;
+ p1[9] = (qfprom_cdata[4] & S9_P1_MASK);
+ p1[10] = (qfprom_cdata[4] & S10_P1_MASK) >> S10_P1_SHIFT;
+
+ for (i = 0; i < TSENS_1x_MAX_SENSORS; i++)
+ p1[i] = (((base0) + p1[i]) << 2);
+ break;
+ default:
+ for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
+ p1[i] = 500;
+ p2[i] = 780;
+ }
+ break;
+ }
+
+ compute_intercept_slope(tmdev, p1, p2, mode);
+
+ return 0;
+}
+
+static int tsens1xxx_get_temp(struct tsens_sensor *sensor, int *temp)
+{
+ struct tsens_device *tmdev = NULL;
+ unsigned int code;
+ void __iomem *sensor_addr;
+ void __iomem *trdy_addr;
+ int last_temp = 0, last_temp2 = 0, last_temp3 = 0;
+ bool last_temp_valid = false, last_temp2_valid = false;
+ bool last_temp3_valid = false;
+
+ if (!sensor)
+ return -EINVAL;
+
+ tmdev = sensor->tmdev;
+
+ trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_tm_addr);
+ sensor_addr = TSENS_SN_STATUS_ADDR(tmdev->tsens_tm_addr);
+
+ code = readl_relaxed(sensor_addr +
+ (sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
+ last_temp = code & TSENS_SN_STATUS_TEMP_MASK;
+
+ if (tmdev->ctrl_data->valid_status_check) {
+ if (code & TSENS_SN_STATUS_VALID)
+ last_temp_valid = true;
+ else {
+ code = readl_relaxed(sensor_addr +
+ (sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
+ last_temp2 = code & TSENS_SN_STATUS_TEMP_MASK;
+ if (code & TSENS_SN_STATUS_VALID) {
+ last_temp = last_temp2;
+ last_temp2_valid = true;
+ } else {
+ code = readl_relaxed(sensor_addr +
+ (sensor->hw_id <<
+ TSENS_STATUS_ADDR_OFFSET));
+ last_temp3 = code & TSENS_SN_STATUS_TEMP_MASK;
+ if (code & TSENS_SN_STATUS_VALID) {
+ last_temp = last_temp3;
+ last_temp3_valid = true;
+ }
+ }
+ }
+ }
+
+ if ((tmdev->ctrl_data->valid_status_check) &&
+ (!last_temp_valid && !last_temp2_valid && !last_temp3_valid)) {
+ if (last_temp == last_temp2)
+ last_temp = last_temp2;
+ else if (last_temp2 == last_temp3)
+ last_temp = last_temp3;
+ }
+
+ *temp = code_to_degc(last_temp, sensor);
+
+ return 0;
+}
+
+static int tsens_tz_activate_trip_type(struct tsens_sensor *tm_sensor,
+ int trip, enum thermal_device_mode mode)
+{
+ struct tsens_device *tmdev = NULL;
+ unsigned int reg_cntl, code, hi_code, lo_code, mask;
+
+ /* clear the interrupt and unmask */
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tmdev;
+ if (!tmdev)
+ return -EINVAL;
+
+ lo_code = TSENS_THRESHOLD_MIN_CODE;
+ hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+ reg_cntl = readl_relaxed((TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id *
+ TSENS_SN_ADDR_OFFSET)));
+
+ switch (trip) {
+ case THERMAL_TRIP_CONFIGURABLE_HI:
+ tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode;
+
+ code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
+ >> TSENS_UPPER_THRESHOLD_SHIFT;
+ mask = TSENS_UPPER_STATUS_CLR;
+
+ if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+ lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
+ break;
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
+ tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode;
+
+ code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
+ mask = TSENS_LOWER_STATUS_CLR;
+
+ if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+ hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
+ >> TSENS_UPPER_THRESHOLD_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mode == THERMAL_DEVICE_DISABLED)
+ writel_relaxed(reg_cntl | mask,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
+ else
+ writel_relaxed(reg_cntl & ~mask,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
+ /* Enable the thresholds */
+ mb();
+
+ return 0;
+}
+
+static int tsens1xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
+ int low_temp, int high_temp)
+{
+ unsigned int reg_cntl;
+ unsigned long flags;
+ struct tsens_device *tmdev = NULL;
+ int high_code, low_code, rc = 0;
+
+ if (!tm_sensor)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tmdev;
+ if (!tmdev)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
+
+ if (high_temp != INT_MAX) {
+ high_code = degc_to_code(high_temp, tm_sensor);
+ tmdev->sensor[tm_sensor->hw_id].thr_state.high_adc_code =
+ high_code;
+ tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp =
+ high_temp;
+
+ reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id *
+ TSENS_SN_ADDR_OFFSET));
+
+ high_code <<= TSENS_UPPER_THRESHOLD_SHIFT;
+ reg_cntl &= ~TSENS_UPPER_THRESHOLD_MASK;
+ writel_relaxed(reg_cntl | high_code,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id *
+ TSENS_SN_ADDR_OFFSET)));
+ }
+
+ if (low_temp != INT_MIN) {
+ low_code = degc_to_code(low_temp, tm_sensor);
+ tmdev->sensor[tm_sensor->hw_id].thr_state.low_adc_code =
+ low_code;
+ tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp =
+ low_temp;
+
+ reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id *
+ TSENS_SN_ADDR_OFFSET));
+
+ reg_cntl &= ~TSENS_LOWER_THRESHOLD_MASK;
+ writel_relaxed(reg_cntl | low_code,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id *
+ TSENS_SN_ADDR_OFFSET)));
+ }
+ /* Set trip temperature thresholds */
+ mb();
+
+ if (high_temp != INT_MAX) {
+ rc = tsens_tz_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ THERMAL_DEVICE_ENABLED);
+ if (rc) {
+ pr_err("trip high enable error :%d\n", rc);
+ goto fail;
+ }
+ } else {
+ rc = tsens_tz_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ THERMAL_DEVICE_DISABLED);
+ if (rc) {
+ pr_err("trip high disable error :%d\n", rc);
+ goto fail;
+ }
+ }
+
+ if (low_temp != INT_MIN) {
+ rc = tsens_tz_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_DEVICE_ENABLED);
+ if (rc) {
+ pr_err("trip low enable activation error :%d\n", rc);
+ goto fail;
+ }
+ } else {
+ rc = tsens_tz_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_DEVICE_DISABLED);
+ if (rc) {
+ pr_err("trip low disable error :%d\n", rc);
+ goto fail;
+ }
+ }
+
+fail:
+ spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
+ return rc;
+}
+
+static irqreturn_t tsens_irq_thread(int irq, void *data)
+{
+ struct tsens_device *tm = data;
+ unsigned int i, status, threshold, temp, th_temp;
+ unsigned long flags;
+ void __iomem *sensor_status_addr;
+ void __iomem *sensor_status_ctrl_addr;
+ u32 rc = 0, addr_offset;
+
+ sensor_status_addr = TSENS_SN_STATUS_ADDR(tm->tsens_tm_addr);
+ sensor_status_ctrl_addr =
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tm->tsens_tm_addr);
+
+ for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
+ bool upper_thr = false, lower_thr = false;
+
+ if (IS_ERR(tm->sensor[i].tzd))
+ continue;
+
+ rc = tsens1xxx_get_temp(&tm->sensor[i], &temp);
+ if (rc) {
+ pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
+ continue;
+ }
+
+ spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
+
+ addr_offset = tm->sensor[i].hw_id *
+ TSENS_SN_ADDR_OFFSET;
+ status = readl_relaxed(sensor_status_addr + addr_offset);
+ threshold = readl_relaxed(sensor_status_ctrl_addr +
+ addr_offset);
+
+ if (status & TSENS_SN_STATUS_UPPER_STATUS) {
+ writel_relaxed(threshold | TSENS_UPPER_STATUS_CLR,
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
+ tm->tsens_tm_addr + addr_offset));
+ th_temp = code_to_degc((threshold &
+ TSENS_UPPER_THRESHOLD_MASK) >>
+ TSENS_UPPER_THRESHOLD_SHIFT,
+ tm->sensor);
+ if (th_temp > temp) {
+ pr_debug("Re-arm high threshold\n");
+ rc = tsens_tz_activate_trip_type(
+ &tm->sensor[i],
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ THERMAL_DEVICE_ENABLED);
+ if (rc)
+ pr_err("high rearm failed");
+ } else {
+ upper_thr = true;
+ tm->sensor[i].thr_state.high_th_state =
+ THERMAL_DEVICE_DISABLED;
+ }
+ }
+
+ if (status & TSENS_SN_STATUS_LOWER_STATUS) {
+ writel_relaxed(threshold | TSENS_LOWER_STATUS_CLR,
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
+ tm->tsens_tm_addr + addr_offset));
+ th_temp = code_to_degc((threshold &
+ TSENS_LOWER_THRESHOLD_MASK),
+ tm->sensor);
+ if (th_temp < temp) {
+ pr_debug("Re-arm Low threshold\n");
+ rc = tsens_tz_activate_trip_type(
+ &tm->sensor[i],
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_DEVICE_ENABLED);
+ if (rc)
+ pr_err("low rearm failed");
+ } else {
+ lower_thr = true;
+ tm->sensor[i].thr_state.low_th_state =
+ THERMAL_DEVICE_DISABLED;
+ }
+ }
+ spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
+
+ if (upper_thr || lower_thr) {
+ pr_debug("sensor:%d trigger temp (%d degC)\n",
+ tm->sensor[i].hw_id,
+ code_to_degc((status &
+ TSENS_SN_STATUS_TEMP_MASK),
+ tm->sensor));
+ of_thermal_handle_trip(tm->sensor[i].tzd);
+ }
+ }
+
+ /* Disable monitoring sensor trip threshold for triggered sensor */
+ mb();
+
+ return IRQ_HANDLED;
+}
+
+static int tsens1xxx_hw_sensor_en(struct tsens_device *tmdev,
+ u32 sensor_id)
+{
+ void __iomem *srot_addr;
+ unsigned int srot_val, sensor_en;
+
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+ srot_val = readl_relaxed(srot_addr);
+ srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
+
+ sensor_en = ((1 << sensor_id) & srot_val);
+
+ return sensor_en;
+}
+
+static int tsens1xxx_hw_init(struct tsens_device *tmdev)
+{
+ void __iomem *srot_addr;
+ unsigned int srot_val;
+
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+ srot_val = readl_relaxed(srot_addr);
+ if (!(srot_val & TSENS_EN)) {
+ pr_err("TSENS device is not enabled\n");
+ return -ENODEV;
+ }
+
+ writel_relaxed(TSENS_INTERRUPT_EN,
+ TSENS_UPPER_LOWER_INTERRUPT_CTRL(tmdev->tsens_tm_addr));
+
+ spin_lock_init(&tmdev->tsens_upp_low_lock);
+
+ return 0;
+}
+
+static const struct tsens_irqs tsens1xxx_irqs[] = {
+ { "tsens-upper-lower", tsens_irq_thread},
+};
+
+static int tsens1xxx_register_interrupts(struct tsens_device *tmdev)
+{
+ struct platform_device *pdev;
+ int i, rc;
+
+ if (!tmdev)
+ return -EINVAL;
+
+ pdev = tmdev->pdev;
+
+ for (i = 0; i < ARRAY_SIZE(tsens1xxx_irqs); i++) {
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, tsens1xxx_irqs[i].name);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq %s\n",
+ tsens1xxx_irqs[i].name);
+ return irq;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ tsens1xxx_irqs[i].handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ tsens1xxx_irqs[i].name, tmdev);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to get irq %s\n",
+ tsens1xxx_irqs[i].name);
+ return rc;
+ }
+ enable_irq_wake(irq);
+ }
+
+ return 0;
+}
+
+static const struct tsens_ops ops_tsens1xxx = {
+ .hw_init = tsens1xxx_hw_init,
+ .get_temp = tsens1xxx_get_temp,
+ .set_trips = tsens1xxx_set_trip_temp,
+ .interrupts_reg = tsens1xxx_register_interrupts,
+ .sensor_en = tsens1xxx_hw_sensor_en,
+ .calibrate = calibrate_8937,
+};
+
+const struct tsens_data data_tsens14xx = {
+ .ops = &ops_tsens1xxx,
+ .valid_status_check = true,
+ .mtc = true,
+};
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 89bf6b7..a91dceb 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3518,6 +3518,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
mdwc->no_vbus_vote_type_c = of_property_read_bool(node,
"qcom,no-vbus-vote-with-type-C");
+ mutex_init(&mdwc->suspend_resume_mutex);
/* Mark type-C as true by default */
mdwc->type_c = true;
@@ -3543,7 +3544,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
if (ret)
goto put_psy;
- mutex_init(&mdwc->suspend_resume_mutex);
/* Update initial VBUS/ID state from extcon */
if (mdwc->extcon_vbus && extcon_get_state(mdwc->extcon_vbus,
EXTCON_USB))
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 28aaa1f..a5659f99 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3678,6 +3678,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget)
spin_lock_irqsave(udc->lock, flags);
}
+ udc->driver = NULL;
spin_unlock_irqrestore(udc->lock, flags);
usb_ep_free_request(&udc->ep0in.ep, udc->status);
@@ -3841,6 +3842,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
(unsigned long) mEp);
}
+ arch_setup_dma_ops(&udc->gadget.dev, 0, DMA_BIT_MASK(32), NULL, false);
for (i = 0; i < hw_ep_max/2; i++) {
for (j = RX; j <= TX; j++) {
int k = i + j * hw_ep_max/2;
@@ -3855,6 +3857,20 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
mEp->ep.name = mEp->name;
mEp->ep.ops = &usb_ep_ops;
+
+ if (i == 0) {
+ mEp->ep.caps.type_control = true;
+ } else {
+ mEp->ep.caps.type_iso = true;
+ mEp->ep.caps.type_bulk = true;
+ mEp->ep.caps.type_int = true;
+ }
+
+ if (j == TX)
+ mEp->ep.caps.dir_in = true;
+ else
+ mEp->ep.caps.dir_out = true;
+
usb_ep_set_maxpacket_limit(&mEp->ep,
k ? USHRT_MAX : CTRL_PAYLOAD_MAX);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 0fec59c..0390c72 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -36,7 +36,7 @@
SSUSB_GADGET_VBUS_DRAW : CONFIG_USB_GADGET_VBUS_DRAW)
/* disable LPM by default */
-static bool disable_l1_for_hs = true;
+static bool disable_l1_for_hs;
module_param(disable_l1_for_hs, bool, 0644);
MODULE_PARM_DESC(disable_l1_for_hs,
"Disable support for L1 LPM for HS devices");
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 05a66b2..2373262 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -597,14 +597,38 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
- ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
- if (ret)
- return ret;
+ if (!alt) {
+ usb_ep_disable(audio->in_ep);
+ return 0;
+ }
- usb_ep_enable(audio->in_ep);
+ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+ if (ret) {
+ audio->in_ep->desc = NULL;
+ pr_err("config_ep fail for audio ep ret %d\n", ret);
+ return ret;
+ }
+ ret = usb_ep_enable(audio->in_ep);
+ if (ret) {
+ audio->in_ep->desc = NULL;
+ pr_err("failed to enable audio ret %d\n", ret);
+ return ret;
+ }
+
return 0;
}
+/*
+ * Because the data interface supports multiple altsettings,
+ * this audio_source function *MUST* implement a get_alt() method.
+ */
+static int audio_get_alt(struct usb_function *f, unsigned int intf)
+{
+ struct audio_dev *audio = func_to_audio(f);
+
+ return audio->in_ep->enabled ? 1 : 0;
+}
+
static void audio_disable(struct usb_function *f)
{
struct audio_dev *audio = func_to_audio(f);
@@ -869,6 +893,7 @@ static struct audio_dev _audio_dev = {
.bind = audio_bind,
.unbind = audio_unbind,
.set_alt = audio_set_alt,
+ .get_alt = audio_get_alt,
.setup = audio_setup,
.disable = audio_disable,
.free_func = audio_free_func,
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 4bdfadf..41aa62d 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -1655,11 +1655,10 @@ static void gsi_rndis_command_complete(struct usb_ep *ep,
buf = (rndis_init_msg_type *)req->buf;
if (buf->MessageType == RNDIS_MSG_INIT) {
- gsi->d_port.in_aggr_size = min_t(u32, gsi->d_port.in_aggr_size,
- gsi->params->dl_max_xfer_size);
- log_event_dbg("RNDIS host dl_aggr_size:%d in_aggr_size:%d\n",
- gsi->params->dl_max_xfer_size,
- gsi->d_port.in_aggr_size);
+ /* honor host dl aggr size */
+ gsi->d_port.in_aggr_size = gsi->params->dl_max_xfer_size;
+ log_event_dbg("RNDIS host dl_aggr_size:%d\n",
+ gsi->params->dl_max_xfer_size);
}
}
@@ -2565,7 +2564,7 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f)
info.out_epname = "gsi-epout";
info.in_req_buf_len = GSI_IN_BUFF_SIZE;
gsi->d_port.in_aggr_size = GSI_IN_RNDIS_AGGR_SIZE;
- info.in_req_num_buf = GSI_NUM_IN_BUFFERS;
+ info.in_req_num_buf = GSI_NUM_IN_RNDIS_BUFFERS;
gsi->d_port.out_aggr_size = GSI_OUT_AGGR_SIZE;
info.out_req_buf_len = GSI_OUT_AGGR_SIZE;
info.out_req_num_buf = GSI_NUM_OUT_BUFFERS;
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 58a7706..71bea5e 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -34,13 +34,13 @@
#define GSI_MAX_CTRL_PKT_SIZE 4096
#define GSI_CTRL_DTR (1 << 0)
-
+#define GSI_NUM_IN_RNDIS_BUFFERS 50
#define GSI_NUM_IN_BUFFERS 15
#define GSI_IN_BUFF_SIZE 2048
#define GSI_NUM_OUT_BUFFERS 14
#define GSI_OUT_AGGR_SIZE 24576
-#define GSI_IN_RNDIS_AGGR_SIZE 9216
+#define GSI_IN_RNDIS_AGGR_SIZE 16384
#define GSI_IN_MBIM_AGGR_SIZE 16384
#define GSI_IN_RMNET_AGGR_SIZE 16384
#define GSI_ECM_AGGR_SIZE 2048
diff --git a/drivers/usb/gadget/function/u_bam_dmux.c b/drivers/usb/gadget/function/u_bam_dmux.c
new file mode 100644
index 0000000..2ad184a
--- /dev/null
+++ b/drivers/usb/gadget/function/u_bam_dmux.c
@@ -0,0 +1,2645 @@
+/* Copyright (c) 2011-2018, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <soc/qcom/smd.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+
+#include <soc/qcom/bam_dmux.h>
+
+#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/usb_ctrl_qti.h>
+#include <linux/usb_bam.h>
+
+#include "usb_gadget_xport.h"
+#include "u_rmnet.h"
+
+#define BAM_N_PORTS 2
+#define BAM2BAM_N_PORTS 4
+
+static struct workqueue_struct *gbam_wq;
+static int n_bam_ports;
+static int n_bam2bam_ports;
+static unsigned int n_tx_req_queued;
+
+static unsigned int bam_ch_ids[BAM_N_PORTS] = {
+ BAM_DMUX_USB_RMNET_0,
+ BAM_DMUX_USB_DPL
+};
+
+static char bam_ch_names[BAM_N_PORTS][BAM_DMUX_CH_NAME_MAX_LEN];
+
+static const enum ipa_client_type usb_prod[BAM2BAM_N_PORTS] = {
+ IPA_CLIENT_USB_PROD, IPA_CLIENT_USB2_PROD,
+ IPA_CLIENT_USB3_PROD, IPA_CLIENT_USB4_PROD
+};
+static const enum ipa_client_type usb_cons[BAM2BAM_N_PORTS] = {
+ IPA_CLIENT_USB_CONS, IPA_CLIENT_USB2_CONS,
+ IPA_CLIENT_USB3_CONS, IPA_CLIENT_USB4_CONS
+};
+
+#define BAM_PENDING_PKTS_LIMIT 220
+#define BAM_MUX_TX_PKT_DROP_THRESHOLD 1000
+#define BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD 500
+#define BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD 300
+#define BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT 1
+
+#define BAM_MUX_HDR 8
+
+#define BAM_MUX_RX_Q_SIZE 128
+#define BAM_MUX_TX_Q_SIZE 200
+#define BAM_MUX_RX_REQ_SIZE 2048 /* Must be 1KB aligned */
+
+#define DL_INTR_THRESHOLD 20
+#define BAM_PENDING_BYTES_LIMIT (50 * BAM_MUX_RX_REQ_SIZE)
+#define BAM_PENDING_BYTES_FCTRL_EN_TSHOLD (BAM_PENDING_BYTES_LIMIT / 3)
+
+/* Extra buffer size to allocate for tx */
+#define EXTRA_ALLOCATION_SIZE_U_BAM 128
+
+static unsigned int bam_pending_pkts_limit = BAM_PENDING_PKTS_LIMIT;
+module_param(bam_pending_pkts_limit, uint, 0644);
+
+static unsigned int bam_pending_bytes_limit = BAM_PENDING_BYTES_LIMIT;
+module_param(bam_pending_bytes_limit, uint, 0644);
+
+static unsigned int bam_pending_bytes_fctrl_en_thold =
+ BAM_PENDING_BYTES_FCTRL_EN_TSHOLD;
+module_param(bam_pending_bytes_fctrl_en_thold, uint, 0644);
+
+static unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD;
+module_param(bam_mux_tx_pkt_drop_thld, uint, 0644);
+
+static unsigned int bam_mux_rx_fctrl_en_thld = BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD;
+module_param(bam_mux_rx_fctrl_en_thld, uint, 0644);
+
+static unsigned int bam_mux_rx_fctrl_support = BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT;
+module_param(bam_mux_rx_fctrl_support, uint, 0644);
+
+static unsigned int bam_mux_rx_fctrl_dis_thld = BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD;
+module_param(bam_mux_rx_fctrl_dis_thld, uint, 0644);
+
+static unsigned int bam_mux_tx_q_size = BAM_MUX_TX_Q_SIZE;
+module_param(bam_mux_tx_q_size, uint, 0644);
+
+static unsigned int bam_mux_rx_q_size = BAM_MUX_RX_Q_SIZE;
+module_param(bam_mux_rx_q_size, uint, 0644);
+
+static unsigned long bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE;
+module_param(bam_mux_rx_req_size, ulong, 0444);
+
+static unsigned int dl_intr_threshold = DL_INTR_THRESHOLD;
+module_param(dl_intr_threshold, uint, 0644);
+
+#define BAM_CH_OPENED BIT(0)
+#define BAM_CH_READY BIT(1)
+#define BAM_CH_WRITE_INPROGRESS BIT(2)
+
+enum u_bam_event_type {
+ U_BAM_DISCONNECT_E = 0,
+ U_BAM_CONNECT_E,
+ U_BAM_SUSPEND_E,
+ U_BAM_RESUME_E
+};
+
+struct sys2ipa_sw {
+ void *teth_priv;
+ ipa_notify_cb teth_cb;
+};
+
+struct bam_ch_info {
+ unsigned long flags;
+ unsigned int id;
+
+ struct list_head tx_idle;
+ struct sk_buff_head tx_skb_q;
+
+ struct list_head rx_idle;
+ struct sk_buff_head rx_skb_q;
+ struct sk_buff_head rx_skb_idle;
+
+ struct gbam_port *port;
+ struct work_struct write_tobam_w;
+ struct work_struct write_tohost_w;
+
+ struct usb_request *rx_req;
+ struct usb_request *tx_req;
+ bool tx_req_dequeued;
+ bool rx_req_dequeued;
+
+ u32 src_pipe_idx;
+ u32 dst_pipe_idx;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
+
+ enum transport_type trans;
+ struct usb_bam_connect_ipa_params ipa_params;
+
+ /* added to support sys to ipa sw UL path */
+ struct sys2ipa_sw ul_params;
+ enum usb_bam_pipe_type src_pipe_type;
+ enum usb_bam_pipe_type dst_pipe_type;
+
+ /* stats */
+ unsigned int pending_pkts_with_bam;
+ unsigned int pending_bytes_with_bam;
+ unsigned int tohost_drp_cnt;
+ unsigned int tomodem_drp_cnt;
+ unsigned int tx_len;
+ unsigned int rx_len;
+ unsigned long to_modem;
+ unsigned long to_host;
+ unsigned int rx_flow_control_disable;
+ unsigned int rx_flow_control_enable;
+ unsigned int rx_flow_control_triggered;
+ unsigned int max_num_pkts_pending_with_bam;
+ unsigned int max_bytes_pending_with_bam;
+ unsigned int delayed_bam_mux_write_done;
+ unsigned long skb_expand_cnt;
+};
+
+struct gbam_port {
+ bool is_connected;
+ enum u_bam_event_type last_event;
+ unsigned int port_num;
+ spinlock_t port_lock_ul;
+ spinlock_t port_lock_dl;
+ spinlock_t port_lock;
+
+ struct grmnet *port_usb;
+ struct usb_gadget *gadget;
+
+ struct bam_ch_info data_ch;
+
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ struct work_struct suspend_w;
+ struct work_struct resume_w;
+};
+
+static struct bam_portmaster {
+ struct gbam_port *port;
+ struct platform_driver pdrv;
+} bam_ports[BAM_N_PORTS];
+
+struct u_bam_data_connect_info {
+ u32 usb_bam_pipe_idx;
+ u32 peer_pipe_idx;
+ unsigned long usb_bam_handle;
+};
+
+struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
+static void gbam_start_rx(struct gbam_port *port);
+static void gbam_start_endless_rx(struct gbam_port *port);
+static void gbam_start_endless_tx(struct gbam_port *port);
+static void gbam_notify(void *p, int event, unsigned long data);
+static void gbam_data_write_tobam(struct work_struct *w);
+
+/*---------------misc functions---------------- */
+static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,
+ int num,
+ void (*cb)(struct usb_ep *ep, struct usb_request *),
+ gfp_t flags)
+{
+ int i;
+ struct usb_request *req;
+
+ pr_debug("%s: ep:%pK head:%pK num:%d cb:%pK", __func__,
+ ep, head, num, cb);
+
+ for (i = 0; i < num; i++) {
+ req = usb_ep_alloc_request(ep, flags);
+ if (!req) {
+ pr_debug("%s: req allocated:%d\n", __func__, i);
+ return list_empty(head) ? -ENOMEM : 0;
+ }
+ req->complete = cb;
+ list_add(&req->list, head);
+ }
+
+ return 0;
+}
+
+static inline dma_addr_t gbam_get_dma_from_skb(struct sk_buff *skb)
+{
+ return *((dma_addr_t *)(skb->cb));
+}
+
+/* This function should be called with port_lock_ul lock held */
+static struct sk_buff *gbam_alloc_skb_from_pool(struct gbam_port *port)
+{
+ struct bam_ch_info *d;
+ struct sk_buff *skb;
+ dma_addr_t skb_buf_dma_addr;
+ struct usb_gadget *gadget;
+
+ if (!port)
+ return NULL;
+
+ d = &port->data_ch;
+ if (!d)
+ return NULL;
+
+ if (d->rx_skb_idle.qlen == 0) {
+ /*
+ * In case skb idle pool is empty, we allow to allocate more
+ * skbs so we dynamically enlarge the pool size when needed.
+ * Therefore, in steady state this dynamic allocation will
+ * stop when the pool will arrive to its optimal size.
+ */
+ pr_debug("%s: allocate skb\n", __func__);
+ skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
+
+ if (!skb)
+ goto alloc_exit;
+
+ skb_reserve(skb, BAM_MUX_HDR);
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+
+ gadget = port->port_usb->gadget;
+
+ skb_buf_dma_addr =
+ dma_map_single(&gadget->dev, skb->data,
+ bam_mux_rx_req_size, DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(&gadget->dev, skb_buf_dma_addr)) {
+ pr_err("%s: Could not DMA map SKB buffer\n",
+ __func__);
+ skb_buf_dma_addr = DMA_ERROR_CODE;
+ }
+ } else {
+ skb_buf_dma_addr = DMA_ERROR_CODE;
+ }
+
+
+ memcpy(skb->cb, &skb_buf_dma_addr,
+ sizeof(skb_buf_dma_addr));
+
+ } else {
+ pr_debug("%s: pull skb from pool\n", __func__);
+ skb = __skb_dequeue(&d->rx_skb_idle);
+ if (skb_headroom(skb) < BAM_MUX_HDR)
+ skb_reserve(skb, BAM_MUX_HDR);
+ }
+
+alloc_exit:
+ return skb;
+}
+
+/* This function should be called with port_lock_ul lock held */
+static void gbam_free_skb_to_pool(struct gbam_port *port, struct sk_buff *skb)
+{
+ struct bam_ch_info *d;
+
+ if (!port)
+ return;
+ d = &port->data_ch;
+
+ skb->len = 0;
+ skb_reset_tail_pointer(skb);
+ __skb_queue_tail(&d->rx_skb_idle, skb);
+}
+
+static void gbam_free_rx_skb_idle_list(struct gbam_port *port)
+{
+ struct bam_ch_info *d;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ struct usb_gadget *gadget = NULL;
+
+ if (!port)
+ return;
+ d = &port->data_ch;
+
+ gadget = port->port_usb->gadget;
+
+ while (d->rx_skb_idle.qlen > 0) {
+ skb = __skb_dequeue(&d->rx_skb_idle);
+ dma_addr = gbam_get_dma_from_skb(skb);
+
+ if (gadget && dma_addr != DMA_ERROR_CODE) {
+ dma_unmap_single(&gadget->dev, dma_addr,
+ bam_mux_rx_req_size, DMA_BIDIRECTIONAL);
+
+ dma_addr = DMA_ERROR_CODE;
+ memcpy(skb->cb, &dma_addr,
+ sizeof(dma_addr));
+ }
+ dev_kfree_skb_any(skb);
+ }
+}
+
+/*----- sys2bam towards the IPA --------------- */
+static void gbam_ipa_sys2bam_notify_cb(void *priv, enum ipa_dp_evt_type event,
+ unsigned long data)
+{
+ struct sys2ipa_sw *ul = (struct sys2ipa_sw *)priv;
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ switch (event) {
+ case IPA_WRITE_DONE:
+ d = container_of(ul, struct bam_ch_info, ul_params);
+ port = container_of(d, struct gbam_port, data_ch);
+ /* call into bam_demux functionality that'll recycle the data */
+ gbam_notify(port, BAM_DMUX_WRITE_DONE, data);
+ break;
+ case IPA_RECEIVE:
+ /* call the callback given by tethering driver init function
+ * (and was given to ipa_connect)
+ */
+ if (ul->teth_cb)
+ ul->teth_cb(ul->teth_priv, event, data);
+ break;
+ default:
+ /* unexpected event */
+ pr_err("%s: unexpected event %d\n", __func__, event);
+ break;
+ }
+}
+
+
+/*--------------------------------------------- */
+
+/*------------data_path----------------------------*/
+static void gbam_write_data_tohost(struct gbam_port *port)
+{
+ unsigned long flags;
+ struct bam_ch_info *d = &port->data_ch;
+ struct sk_buff *skb;
+ struct sk_buff *new_skb;
+ int ret;
+ int tail_room = 0;
+ int extra_alloc = 0;
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ return;
+ }
+
+ ep = port->port_usb->in;
+
+ while (!list_empty(&d->tx_idle)) {
+ skb = __skb_dequeue(&d->tx_skb_q);
+ if (!skb)
+ break;
+
+ /*
+ * Some UDC requires allocation of some extra bytes for
+ * TX buffer due to hardware requirement. Check if extra
+ * bytes are already there, otherwise allocate new buffer
+ * with extra bytes and do memcpy.
+ */
+ if (port->gadget->extra_buf_alloc)
+ extra_alloc = EXTRA_ALLOCATION_SIZE_U_BAM;
+ tail_room = skb_tailroom(skb);
+ if (tail_room < extra_alloc) {
+ pr_debug("%s: tail_room %d less than %d\n", __func__,
+ tail_room, extra_alloc);
+ new_skb = skb_copy_expand(skb, 0, extra_alloc -
+ tail_room, GFP_ATOMIC);
+ if (!new_skb) {
+ pr_err("skb_copy_expand failed\n");
+ break;
+ }
+ dev_kfree_skb_any(skb);
+ skb = new_skb;
+ d->skb_expand_cnt++;
+ }
+
+ req = list_first_entry(&d->tx_idle,
+ struct usb_request,
+ list);
+ req->context = skb;
+ req->buf = skb->data;
+ req->length = skb->len;
+ n_tx_req_queued++;
+ if (n_tx_req_queued == dl_intr_threshold) {
+ req->no_interrupt = 0;
+ n_tx_req_queued = 0;
+ } else {
+ req->no_interrupt = 1;
+ }
+
+ /* Send ZLP in case packet length is multiple of maxpacksize */
+ req->zero = 1;
+
+ list_del(&req->list);
+
+ spin_unlock(&port->port_lock_dl);
+ ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock_dl);
+ if (ret) {
+ pr_err_ratelimited("%s: usb epIn failed with %d\n",
+ __func__, ret);
+ list_add(&req->list, &d->tx_idle);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ d->to_host++;
+ }
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+}
+
+static void gbam_write_data_tohost_w(struct work_struct *w)
+{
+ struct bam_ch_info *d;
+ struct gbam_port *port;
+
+ d = container_of(w, struct bam_ch_info, write_tohost_w);
+ port = d->port;
+
+ gbam_write_data_tohost(port);
+}
+
+void gbam_data_recv_cb(void *p, struct sk_buff *skb)
+{
+ struct gbam_port *port = p;
+ struct bam_ch_info *d = &port->data_ch;
+ unsigned long flags;
+
+ if (!skb)
+ return;
+
+ pr_debug("%s: p:%pK#%d d:%pK skb_len:%d\n", __func__,
+ port, port->port_num, d, skb->len);
+
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ if (d->tx_skb_q.qlen > bam_mux_tx_pkt_drop_thld) {
+ d->tohost_drp_cnt++;
+ if (printk_ratelimited())
+ pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n",
+ __func__, d->tohost_drp_cnt);
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ __skb_queue_tail(&d->tx_skb_q, skb);
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+
+ gbam_write_data_tohost(port);
+}
+
+void gbam_data_write_done(void *p, struct sk_buff *skb)
+{
+ struct gbam_port *port = p;
+ struct bam_ch_info *d = &port->data_ch;
+ unsigned long flags;
+
+ if (!skb)
+ return;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+
+ d->pending_pkts_with_bam--;
+ d->pending_bytes_with_bam -= skb->len;
+ gbam_free_skb_to_pool(port, skb);
+
+ pr_debug("%s:port:%pK d:%pK tom:%lu ppkt:%u pbytes:%u pno:%d\n",
+ __func__, port, d, d->to_modem, d->pending_pkts_with_bam,
+ d->pending_bytes_with_bam, port->port_num);
+
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+ /*
+ * If BAM doesn't have much pending data then push new data from here:
+ * write_complete notify only to avoid any underruns due to wq latency
+ */
+ if (d->pending_bytes_with_bam <= bam_pending_bytes_fctrl_en_thold) {
+ gbam_data_write_tobam(&d->write_tobam_w);
+ } else {
+ d->delayed_bam_mux_write_done++;
+ queue_work(gbam_wq, &d->write_tobam_w);
+ }
+}
+
+/* This function should be called with port_lock_ul spinlock acquired */
+static bool gbam_ul_bam_limit_reached(struct bam_ch_info *data_ch)
+{
+ unsigned int curr_pending_pkts = data_ch->pending_pkts_with_bam;
+ unsigned int curr_pending_bytes = data_ch->pending_bytes_with_bam;
+ struct sk_buff *skb;
+
+ if (curr_pending_pkts >= bam_pending_pkts_limit)
+ return true;
+
+ /* check if next skb length doesn't exceed pending_bytes_limit */
+ skb = skb_peek(&data_ch->rx_skb_q);
+ if (!skb)
+ return false;
+
+ if ((curr_pending_bytes + skb->len) > bam_pending_bytes_limit)
+ return true;
+ else
+ return false;
+}
+
+static void gbam_data_write_tobam(struct work_struct *w)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int ret;
+ int qlen;
+
+ d = container_of(w, struct bam_ch_info, write_tobam_w);
+ port = d->port;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ return;
+ }
+ /* Bail out if already in progress */
+ if (test_bit(BAM_CH_WRITE_INPROGRESS, &d->flags)) {
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ return;
+ }
+
+ set_bit(BAM_CH_WRITE_INPROGRESS, &d->flags);
+
+ while (!gbam_ul_bam_limit_reached(d) &&
+ (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA ||
+ usb_bam_get_prod_granted(d->usb_bam_type,
+ d->dst_connection_idx))) {
+ skb = __skb_dequeue(&d->rx_skb_q);
+ if (!skb)
+ break;
+
+ d->pending_pkts_with_bam++;
+ d->pending_bytes_with_bam += skb->len;
+ d->to_modem++;
+
+ pr_debug("%s: port:%pK d:%pK tom:%lu ppkts:%u pbytes:%u pno:%d\n",
+ __func__, port, d,
+ d->to_modem, d->pending_pkts_with_bam,
+ d->pending_bytes_with_bam, port->port_num);
+
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
+ dma_addr_t skb_dma_addr;
+ struct ipa_tx_meta ipa_meta = {0x0};
+
+ skb_dma_addr = gbam_get_dma_from_skb(skb);
+ if (skb_dma_addr != DMA_ERROR_CODE) {
+ ipa_meta.dma_address = skb_dma_addr;
+ ipa_meta.dma_address_valid = true;
+ }
+
+ ret = ipa_tx_dp(usb_prod[port->port_num],
+ skb,
+ &ipa_meta);
+ } else {
+ ret = msm_bam_dmux_write(d->id, skb);
+ }
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (ret) {
+ pr_debug("%s: write error:%d\n", __func__, ret);
+ d->pending_pkts_with_bam--;
+ d->pending_bytes_with_bam -= skb->len;
+ d->to_modem--;
+ d->tomodem_drp_cnt++;
+ gbam_free_skb_to_pool(port, skb);
+ break;
+ }
+ if (d->pending_pkts_with_bam > d->max_num_pkts_pending_with_bam)
+ d->max_num_pkts_pending_with_bam =
+ d->pending_pkts_with_bam;
+ if (d->pending_bytes_with_bam > d->max_bytes_pending_with_bam)
+ d->max_bytes_pending_with_bam =
+ d->pending_bytes_with_bam;
+ }
+
+ qlen = d->rx_skb_q.qlen;
+
+ clear_bit(BAM_CH_WRITE_INPROGRESS, &d->flags);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+ if (qlen < bam_mux_rx_fctrl_dis_thld) {
+ if (d->rx_flow_control_triggered) {
+ d->rx_flow_control_disable++;
+ d->rx_flow_control_triggered = 0;
+ }
+ gbam_start_rx(port);
+ }
+}
+/*-------------------------------------------------------------*/
+
+static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gbam_port *port = ep->driver_data;
+ struct bam_ch_info *d;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+
+ switch (status) {
+ case 0:
+ /* successful completion */
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* connection gone */
+ dev_kfree_skb_any(skb);
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ pr_err("%s: data tx ep error %d\n",
+ __func__, status);
+ break;
+ }
+
+ dev_kfree_skb_any(skb);
+
+ if (!port)
+ return;
+
+ spin_lock(&port->port_lock_dl);
+ d = &port->data_ch;
+ list_add_tail(&req->list, &d->tx_idle);
+ spin_unlock(&port->port_lock_dl);
+
+ queue_work(gbam_wq, &d->write_tohost_w);
+}
+
+static void
+gbam_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gbam_port *port = ep->driver_data;
+ struct bam_ch_info *d = &port->data_ch;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+ int queue = 0;
+
+ switch (status) {
+ case 0:
+ skb_put(skb, req->actual);
+ queue = 1;
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* cable disconnection */
+ spin_lock(&port->port_lock_ul);
+ gbam_free_skb_to_pool(port, skb);
+ spin_unlock(&port->port_lock_ul);
+ req->buf = 0;
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ if (printk_ratelimited())
+ pr_err("%s: %s response error %d, %d/%d\n",
+ __func__, ep->name, status,
+ req->actual, req->length);
+ spin_lock(&port->port_lock_ul);
+ gbam_free_skb_to_pool(port, skb);
+ spin_unlock(&port->port_lock_ul);
+ break;
+ }
+
+ spin_lock(&port->port_lock_ul);
+
+ if (queue) {
+ __skb_queue_tail(&d->rx_skb_q, skb);
+ if ((d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) &&
+ !usb_bam_get_prod_granted(d->usb_bam_type,
+ d->dst_connection_idx)) {
+ list_add_tail(&req->list, &d->rx_idle);
+ spin_unlock(&port->port_lock_ul);
+ return;
+ }
+
+ queue_work(gbam_wq, &d->write_tobam_w);
+ }
+
+ /* TODO: Handle flow control gracefully by having
+ * having call back mechanism from bam driver
+ */
+ if (bam_mux_rx_fctrl_support &&
+ d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) {
+ if (!d->rx_flow_control_triggered) {
+ d->rx_flow_control_triggered = 1;
+ d->rx_flow_control_enable++;
+ }
+ list_add_tail(&req->list, &d->rx_idle);
+ spin_unlock(&port->port_lock_ul);
+ return;
+ }
+
+ skb = gbam_alloc_skb_from_pool(port);
+ if (!skb) {
+ list_add_tail(&req->list, &d->rx_idle);
+ spin_unlock(&port->port_lock_ul);
+ return;
+ }
+ spin_unlock(&port->port_lock_ul);
+
+ req->buf = skb->data;
+ req->dma = gbam_get_dma_from_skb(skb);
+ req->length = bam_mux_rx_req_size;
+
+ if (req->dma != DMA_ERROR_CODE)
+ req->dma_pre_mapped = true;
+ else
+ req->dma_pre_mapped = false;
+
+ req->context = skb;
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ spin_lock(&port->port_lock_ul);
+ gbam_free_skb_to_pool(port, skb);
+ spin_unlock(&port->port_lock_ul);
+
+ if (printk_ratelimited())
+ pr_err("%s: data rx enqueue err %d\n",
+ __func__, status);
+
+ spin_lock(&port->port_lock_ul);
+ list_add_tail(&req->list, &d->rx_idle);
+ spin_unlock(&port->port_lock_ul);
+ }
+}
+
+static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_start_rx(struct gbam_port *port)
+{
+ struct usb_request *req;
+ struct bam_ch_info *d;
+ struct usb_ep *ep;
+ unsigned long flags;
+ int ret;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (!port->port_usb || !port->port_usb->out) {
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ return;
+ }
+
+ d = &port->data_ch;
+ ep = port->port_usb->out;
+
+ while (port->port_usb && !list_empty(&d->rx_idle)) {
+
+ if (bam_mux_rx_fctrl_support &&
+ d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld)
+ break;
+
+ req = list_first_entry(&d->rx_idle, struct usb_request, list);
+
+ skb = gbam_alloc_skb_from_pool(port);
+ if (!skb)
+ break;
+
+ list_del(&req->list);
+ req->buf = skb->data;
+ req->dma = gbam_get_dma_from_skb(skb);
+ req->length = bam_mux_rx_req_size;
+
+ if (req->dma != DMA_ERROR_CODE)
+ req->dma_pre_mapped = true;
+ else
+ req->dma_pre_mapped = false;
+
+ req->context = skb;
+
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (ret) {
+ gbam_free_skb_to_pool(port, skb);
+
+ if (printk_ratelimited())
+ pr_err("%s: rx queue failed %d\n",
+ __func__, ret);
+
+ if (port->port_usb)
+ list_add(&req->list, &d->rx_idle);
+ else
+ usb_ep_free_request(ep, req);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_start_endless_rx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+ struct usb_ep *ep;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (!port->port_usb || !d->rx_req) {
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+
+ ep = port->port_usb->out;
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ pr_debug("%s: enqueue\n", __func__);
+ status = usb_ep_queue(ep, d->rx_req, GFP_ATOMIC);
+ if (status)
+ pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_endless_tx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+ struct usb_ep *ep;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+ if (!port->port_usb || !d->tx_req) {
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+
+ ep = port->port_usb->in;
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ pr_debug("%s: enqueue\n", __func__);
+ status = usb_ep_queue(ep, d->tx_req, GFP_ATOMIC);
+ if (status)
+ pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_stop_endless_rx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+ unsigned long flags;
+ struct usb_ep *ep;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+
+ ep = port->port_usb->out;
+ d->rx_req_dequeued = true;
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ pr_debug("%s: dequeue\n", __func__);
+ status = usb_ep_dequeue(ep, d->rx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_stop_endless_tx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+ unsigned long flags;
+ struct usb_ep *ep;
+
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ pr_err("%s: port->port_usb is NULL", __func__);
+ return;
+ }
+
+ ep = port->port_usb->in;
+ d->tx_req_dequeued = true;
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ pr_debug("%s: dequeue\n", __func__);
+ status = usb_ep_dequeue(ep, d->tx_req);
+ if (status)
+ pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
+}
+
+
+/*
+ * This function configured data fifo based on index passed to get bam2bam
+ * configuration.
+ */
+static void configure_data_fifo(enum usb_ctrl bam_type, u8 idx,
+ struct usb_ep *ep, enum usb_bam_pipe_type pipe_type)
+{
+ struct u_bam_data_connect_info bam_info;
+ struct sps_mem_buffer data_fifo = {0};
+
+ if (pipe_type == USB_BAM_PIPE_BAM2BAM) {
+ get_bam2bam_connection_info(bam_type, idx,
+ &bam_info.usb_bam_pipe_idx,
+ NULL, &data_fifo, NULL);
+
+ msm_data_fifo_config(ep,
+ data_fifo.phys_base,
+ data_fifo.size,
+ bam_info.usb_bam_pipe_idx);
+ }
+}
+
+
+static void gbam_start(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct gbam_port *port = param;
+ struct usb_gadget *gadget = NULL;
+ struct bam_ch_info *d;
+ unsigned long flags;
+
+ if (port == NULL) {
+ pr_err("%s: port is NULL\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb == NULL) {
+ pr_err("%s: port_usb is NULL, disconnected\n", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ gadget = port->port_usb->gadget;
+ d = &port->data_ch;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (gadget == NULL) {
+ pr_err("%s: gadget is NULL\n", __func__);
+ return;
+ }
+
+ if (dir == USB_TO_PEER_PERIPHERAL) {
+ if (port->data_ch.src_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ gbam_start_endless_rx(port);
+ else {
+ gbam_start_rx(port);
+ queue_work(gbam_wq, &d->write_tobam_w);
+ }
+ } else {
+ if (gadget_is_dwc3(gadget) &&
+ msm_dwc3_reset_ep_after_lpm(gadget)) {
+ configure_data_fifo(d->usb_bam_type,
+ d->dst_connection_idx,
+ port->port_usb->in, d->dst_pipe_type);
+ }
+ gbam_start_endless_tx(port);
+ }
+}
+
+static void gbam_stop(void *param, enum usb_bam_pipe_dir dir)
+{
+ struct gbam_port *port = param;
+
+ if (dir == USB_TO_PEER_PERIPHERAL) {
+ /*
+ * Only handling BAM2BAM, as there is no equivalent to
+ * gbam_stop_endless_rx() for the SYS2BAM use case
+ */
+ if (port->data_ch.src_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ gbam_stop_endless_rx(port);
+ } else {
+ gbam_stop_endless_tx(port);
+ }
+}
+
+static int _gbam_start_io(struct gbam_port *port, bool in)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct usb_ep *ep;
+ struct list_head *idle;
+ unsigned int queue_size;
+ spinlock_t *spinlock;
+ void (*ep_complete)(struct usb_ep *, struct usb_request *);
+
+ if (in)
+ spinlock = &port->port_lock_dl;
+ else
+ spinlock = &port->port_lock_ul;
+
+ spin_lock_irqsave(spinlock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(spinlock, flags);
+ return -EBUSY;
+ }
+
+ if (in) {
+ ep = port->port_usb->in;
+ idle = &port->data_ch.tx_idle;
+ queue_size = bam_mux_tx_q_size;
+ ep_complete = gbam_epin_complete;
+ } else {
+ ep = port->port_usb->out;
+ if (!ep)
+ goto out;
+ idle = &port->data_ch.rx_idle;
+ queue_size = bam_mux_rx_q_size;
+ ep_complete = gbam_epout_complete;
+ }
+
+ ret = gbam_alloc_requests(ep, idle, queue_size, ep_complete,
+ GFP_ATOMIC);
+out:
+ spin_unlock_irqrestore(spinlock, flags);
+ if (ret)
+ pr_err("%s: allocation failed\n", __func__);
+
+ return ret;
+}
+
+static void gbam_start_io(struct gbam_port *port)
+{
+ unsigned long flags;
+
+ pr_debug("%s: port:%pK\n", __func__, port);
+
+ if (_gbam_start_io(port, true))
+ return;
+
+ if (_gbam_start_io(port, false)) {
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+ if (port->port_usb)
+ gbam_free_requests(port->port_usb->in,
+ &port->data_ch.tx_idle);
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+ return;
+ }
+
+ /* queue out requests */
+ gbam_start_rx(port);
+}
+
+static void gbam_notify(void *p, int event, unsigned long data)
+{
+ struct gbam_port *port = p;
+ struct bam_ch_info *d;
+ struct sk_buff *skb;
+
+ if (port == NULL)
+ pr_err("BAM DMUX notifying after channel close\n");
+
+ switch (event) {
+ case BAM_DMUX_RECEIVE:
+ skb = (struct sk_buff *)data;
+ if (port)
+ gbam_data_recv_cb(p, skb);
+ else
+ dev_kfree_skb_any(skb);
+ break;
+ case BAM_DMUX_WRITE_DONE:
+ skb = (struct sk_buff *)data;
+ if (port)
+ gbam_data_write_done(p, skb);
+ else
+ dev_kfree_skb_any(skb);
+ break;
+ case BAM_DMUX_TRANSMIT_SIZE:
+ d = &port->data_ch;
+ if (test_bit(BAM_CH_OPENED, &d->flags))
+ pr_warn("%s, BAM channel opened already", __func__);
+ bam_mux_rx_req_size = data;
+ pr_debug("%s rx_req_size: %lu", __func__, bam_mux_rx_req_size);
+ break;
+ }
+}
+
+static void gbam_free_rx_buffers(struct gbam_port *port)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct bam_ch_info *d;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+
+ if (!port->port_usb || !port->port_usb->out)
+ goto free_rx_buf_out;
+
+ d = &port->data_ch;
+ gbam_free_requests(port->port_usb->out, &d->rx_idle);
+
+ while ((skb = __skb_dequeue(&d->rx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+ gbam_free_rx_skb_idle_list(port);
+
+free_rx_buf_out:
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+}
+
+static void gbam_free_tx_buffers(struct gbam_port *port)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct bam_ch_info *d;
+
+ spin_lock_irqsave(&port->port_lock_dl, flags);
+
+ if (!port->port_usb)
+ goto free_tx_buf_out;
+
+ d = &port->data_ch;
+ gbam_free_requests(port->port_usb->in, &d->tx_idle);
+
+ while ((skb = __skb_dequeue(&d->tx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+free_tx_buf_out:
+ spin_unlock_irqrestore(&port->port_lock_dl, flags);
+}
+
+static void gbam_free_buffers(struct gbam_port *port)
+{
+ gbam_free_rx_buffers(port);
+ gbam_free_tx_buffers(port);
+}
+
+static void gbam_disconnect_work(struct work_struct *w)
+{
+ struct gbam_port *port =
+ container_of(w, struct gbam_port, disconnect_w);
+ struct bam_ch_info *d = &port->data_ch;
+
+ if (!test_bit(BAM_CH_OPENED, &d->flags)) {
+ pr_err("%s: Bam channel is not opened\n", __func__);
+ goto exit;
+ }
+
+ msm_bam_dmux_close(d->id);
+ clear_bit(BAM_CH_OPENED, &d->flags);
+exit:
+ return;
+}
+
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+ struct gbam_port *port =
+ container_of(w, struct gbam_port, disconnect_w);
+ struct bam_ch_info *d;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (!port->is_connected) {
+ pr_debug("%s: Port already disconnected. Bailing out.\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ port->is_connected = false;
+ d = &port->data_ch;
+
+ /*
+ * Unlock the port here and not at the end of this work,
+ * because we do not want to activate usb_bam, ipa and
+ * tethe bridge logic in atomic context and wait uneeded time.
+ * Either way other works will not fire until end of this work
+ * and event functions (as bam_data_connect) will not influance
+ * while lower layers connect pipes, etc.
+ */
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ ret = usb_bam_disconnect_ipa(d->usb_bam_type, &d->ipa_params);
+ if (ret)
+ pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
+ __func__, ret);
+ usb_bam_free_fifos(d->usb_bam_type, d->src_connection_idx);
+ usb_bam_free_fifos(d->usb_bam_type, d->dst_connection_idx);
+ teth_bridge_disconnect(d->ipa_params.src_client);
+ /*
+ * Decrement usage count which was incremented upon cable
+ * connect or cable disconnect in suspended state
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+ }
+}
+
+static void gbam_connect_work(struct work_struct *w)
+{
+ struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct bam_ch_info *d = &port->data_ch;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+ if (!port->port_usb) {
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ return;
+ }
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+ if (!test_bit(BAM_CH_READY, &d->flags)) {
+ pr_err("%s: Bam channel is not ready\n", __func__);
+ return;
+ }
+
+ ret = msm_bam_dmux_open(d->id, port, gbam_notify);
+ if (ret) {
+ pr_err("%s: unable open bam ch:%d err:%d\n",
+ __func__, d->id, ret);
+ return;
+ }
+
+ set_bit(BAM_CH_OPENED, &d->flags);
+
+ gbam_start_io(port);
+
+ pr_debug("%s: done\n", __func__);
+}
+
+static void gbam2bam_connect_work(struct work_struct *w)
+{
+ struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct usb_gadget *gadget = NULL;
+ struct teth_bridge_connect_params connect_params;
+ struct teth_bridge_init_params teth_bridge_params;
+ struct bam_ch_info *d;
+ u32 sps_params;
+ int ret;
+ unsigned long flags, flags_ul;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (port->last_event == U_BAM_DISCONNECT_E) {
+ pr_debug("%s: Port is about to disconnected. Bailing out.\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ if (port->is_connected) {
+ pr_debug("%s: Port already connected. Bail out.\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ port->is_connected = true;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags_ul);
+ spin_lock(&port->port_lock_dl);
+ if (!port->port_usb) {
+ pr_debug("%s: usb cable is disconnected, exiting\n", __func__);
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ gadget = port->port_usb->gadget;
+ if (!gadget) {
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_err("%s: port_usb.gadget is NULL, exiting\n", __func__);
+ return;
+ }
+ d = &port->data_ch;
+
+ /*
+ * Unlock the port here and not at the end of this work,
+ * because we do not want to activate usb_bam, ipa and
+ * tethe bridge logic in atomic context and wait uneeded time.
+ * Either way other works will not fire until end of this work
+ * and event functions (as bam_data_connect) will not influance
+ * while lower layers connect pipes, etc.
+ */
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+
+ d->ipa_params.usb_connection_speed = gadget->speed;
+
+ /*
+ * Invalidate prod and cons client handles from previous
+ * disconnect.
+ */
+ d->ipa_params.cons_clnt_hdl = -1;
+ d->ipa_params.prod_clnt_hdl = -1;
+
+ if (usb_bam_get_pipe_type(d->usb_bam_type, d->ipa_params.src_idx,
+ &d->src_pipe_type) ||
+ usb_bam_get_pipe_type(d->usb_bam_type, d->ipa_params.dst_idx,
+ &d->dst_pipe_type)) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_err("%s:usb_bam_get_pipe_type() failed\n", __func__);
+ return;
+ }
+ if (d->dst_pipe_type != USB_BAM_PIPE_BAM2BAM) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_err("%s: no software preparation for DL not using bam2bam\n",
+ __func__);
+ return;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_bam_alloc_fifos(d->usb_bam_type, d->src_connection_idx);
+ usb_bam_alloc_fifos(d->usb_bam_type, d->dst_connection_idx);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (!port || !port->port_usb) {
+ pr_debug("%s: cable is disconnected.\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock,
+ flags);
+ goto free_fifos;
+ }
+ if (gadget_is_dwc3(gadget)) {
+ /* Configure for RX */
+ configure_data_fifo(d->usb_bam_type, d->src_connection_idx,
+ port->port_usb->out, d->src_pipe_type);
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | MSM_PRODUCER |
+ d->src_pipe_idx;
+ d->rx_req->length = 32*1024;
+ d->rx_req->udc_priv = sps_params;
+ msm_ep_config(port->port_usb->out, d->rx_req);
+
+ /* Configure for TX */
+ configure_data_fifo(d->usb_bam_type, d->dst_connection_idx,
+ port->port_usb->in, d->dst_pipe_type);
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | d->dst_pipe_idx;
+ d->tx_req->length = 32*1024;
+ d->tx_req->udc_priv = sps_params;
+ msm_ep_config(port->port_usb->in, d->tx_req);
+
+ } else {
+ /* Configure for RX */
+ get_bam2bam_connection_info(d->usb_bam_type,
+ d->src_connection_idx,
+ &d->src_pipe_idx,
+ NULL, NULL, NULL);
+ sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
+ MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+ d->rx_req->udc_priv = sps_params;
+
+ /* Configure for TX */
+ get_bam2bam_connection_info(d->usb_bam_type,
+ d->dst_connection_idx,
+ &d->dst_pipe_idx,
+ NULL, NULL, NULL);
+ sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
+ MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
+ d->tx_req->udc_priv = sps_params;
+
+ }
+
+ teth_bridge_params.client = d->ipa_params.src_client;
+ ret = teth_bridge_init(&teth_bridge_params);
+ if (ret) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_err("%s:teth_bridge_init() failed\n", __func__);
+ goto ep_unconfig;
+ }
+
+ /* Support for UL using system-to-IPA */
+ if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
+ d->ul_params.teth_priv =
+ teth_bridge_params.private_data;
+ d->ul_params.teth_cb =
+ teth_bridge_params.usb_notify_cb;
+ d->ipa_params.notify = gbam_ipa_sys2bam_notify_cb;
+ d->ipa_params.priv = &d->ul_params;
+ d->ipa_params.reset_pipe_after_lpm = false;
+
+ } else {
+ d->ipa_params.notify =
+ teth_bridge_params.usb_notify_cb;
+ d->ipa_params.priv =
+ teth_bridge_params.private_data;
+ d->ipa_params.reset_pipe_after_lpm =
+ (gadget_is_dwc3(gadget) &&
+ msm_dwc3_reset_ep_after_lpm(gadget));
+ }
+ d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
+ d->ipa_params.skip_ep_cfg = teth_bridge_params.skip_ep_cfg;
+ d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = usb_bam_connect_ipa(d->usb_bam_type, &d->ipa_params);
+ if (ret) {
+ pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+ __func__, ret);
+ goto ep_unconfig;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (port->last_event == U_BAM_DISCONNECT_E) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_debug("%s:%d: cable is disconnected.\n",
+ __func__, __LINE__);
+ goto ep_unconfig;
+ }
+
+ /* Remove support for UL using system-to-IPA towards DL */
+ if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
+ d->ipa_params.notify = d->ul_params.teth_cb;
+ d->ipa_params.priv = d->ul_params.teth_priv;
+ }
+ if (d->dst_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ d->ipa_params.reset_pipe_after_lpm =
+ (gadget_is_dwc3(gadget) &&
+ msm_dwc3_reset_ep_after_lpm(gadget));
+ else
+ d->ipa_params.reset_pipe_after_lpm = false;
+ d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = usb_bam_connect_ipa(d->usb_bam_type, &d->ipa_params);
+ if (ret) {
+ pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+ __func__, ret);
+ goto ep_unconfig;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (port->last_event == U_BAM_DISCONNECT_E) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_debug("%s:%d: cable is disconnected.\n",
+ __func__, __LINE__);
+ goto ep_unconfig;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ gqti_ctrl_update_ipa_pipes(port->port_usb, port->port_num,
+ d->ipa_params.ipa_prod_ep_idx,
+ d->ipa_params.ipa_cons_ep_idx);
+
+ connect_params.ipa_usb_pipe_hdl = d->ipa_params.prod_clnt_hdl;
+ connect_params.usb_ipa_pipe_hdl = d->ipa_params.cons_clnt_hdl;
+ connect_params.tethering_mode = TETH_TETHERING_MODE_RMNET;
+ connect_params.client_type = d->ipa_params.src_client;
+ ret = teth_bridge_connect(&connect_params);
+ if (ret) {
+ pr_err("%s:teth_bridge_connect() failed\n", __func__);
+ goto ep_unconfig;
+ }
+
+ /* queue in & out requests */
+ if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM) {
+ gbam_start_endless_rx(port);
+ } else {
+ /* The use-case of UL (OUT) ports using sys2bam is based on
+ * partial reuse of the system-to-bam_demux code. The following
+ * lines perform the branching out of the standard bam2bam flow
+ * on the USB side of the UL channel
+ */
+ if (_gbam_start_io(port, false)) {
+ pr_err("%s: _gbam_start_io failed\n", __func__);
+ return;
+ }
+ gbam_start_rx(port);
+ }
+ gbam_start_endless_tx(port);
+
+ pr_debug("%s: done\n", __func__);
+ return;
+
+ep_unconfig:
+ if (gadget_is_dwc3(gadget)) {
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (port->port_usb) {
+ msm_ep_unconfig(port->port_usb->in);
+ msm_ep_unconfig(port->port_usb->out);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+free_fifos:
+ usb_bam_free_fifos(d->usb_bam_type, d->src_connection_idx);
+ usb_bam_free_fifos(d->usb_bam_type, d->dst_connection_idx);
+
+}
+
+static int gbam_wake_cb(void *param)
+{
+ struct gbam_port *port = (struct gbam_port *)param;
+ struct usb_gadget *gadget;
+ unsigned long flags;
+ struct usb_function *func;
+ int ret;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ pr_debug("%s: usb cable is disconnected, exiting\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return -ENODEV;
+ }
+
+ gadget = port->port_usb->gadget;
+ func = port->port_usb->f;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_debug("%s: woken up by peer\n", __func__);
+
+ if ((gadget->speed == USB_SPEED_SUPER) &&
+ (func->func_is_suspended))
+ ret = usb_func_wakeup(func);
+ else
+ ret = usb_gadget_wakeup(gadget);
+
+ if ((ret == -EBUSY) || (ret == -EAGAIN))
+ pr_debug("Remote wakeup is delayed due to LPM exit\n");
+ else if (ret)
+ pr_err("Failed to wake up the USB core. ret=%d\n", ret);
+
+ return ret;
+}
+
+static void gbam2bam_suspend_work(struct work_struct *w)
+{
+ struct gbam_port *port = container_of(w, struct gbam_port, suspend_w);
+ struct bam_ch_info *d;
+ int ret;
+ unsigned long flags;
+
+ pr_debug("%s: suspend work started\n", __func__);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if ((port->last_event == U_BAM_DISCONNECT_E) ||
+ (port->last_event == U_BAM_RESUME_E)) {
+ pr_debug("%s: Port is about to disconnect/resume. Bail out\n",
+ __func__);
+ goto exit;
+ }
+
+ d = &port->data_ch;
+
+ ret = usb_bam_register_wake_cb(d->usb_bam_type, d->dst_connection_idx,
+ gbam_wake_cb, port);
+ if (ret) {
+ pr_err("%s(): Failed to register BAM wake callback.\n",
+ __func__);
+ goto exit;
+ }
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ usb_bam_register_start_stop_cbs(d->usb_bam_type,
+ d->dst_connection_idx, gbam_start, gbam_stop, port);
+
+ /*
+ * release lock here because gbam_start() or
+ * gbam_stop() called from usb_bam_suspend()
+ * re-acquires port lock.
+ */
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_bam_suspend(d->usb_bam_type, &d->ipa_params);
+ spin_lock_irqsave(&port->port_lock, flags);
+ }
+
+exit:
+ /*
+ * Decrement usage count after IPA handshake is done to allow gadget
+ * parent to go to lpm. This counter was incremented upon cable connect
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void gbam2bam_resume_work(struct work_struct *w)
+{
+ struct gbam_port *port = container_of(w, struct gbam_port, resume_w);
+ struct bam_ch_info *d;
+ struct usb_gadget *gadget = NULL;
+ int ret;
+ unsigned long flags;
+
+ pr_debug("%s: resume work started\n", __func__);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (port->last_event == U_BAM_DISCONNECT_E || !port->port_usb) {
+ pr_debug("%s: usb cable is disconnected, exiting\n",
+ __func__);
+ goto exit;
+ }
+
+ d = &port->data_ch;
+ gadget = port->port_usb->gadget;
+
+ ret = usb_bam_register_wake_cb(d->usb_bam_type, d->dst_connection_idx,
+ NULL, NULL);
+ if (ret) {
+ pr_err("%s(): Failed to register BAM wake callback.\n",
+ __func__);
+ goto exit;
+ }
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ if (gadget_is_dwc3(gadget) &&
+ msm_dwc3_reset_ep_after_lpm(gadget)) {
+ if (d->tx_req_dequeued) {
+ msm_ep_unconfig(port->port_usb->in);
+ configure_data_fifo(d->usb_bam_type,
+ d->dst_connection_idx,
+ port->port_usb->in, d->dst_pipe_type);
+ }
+ if (d->rx_req_dequeued) {
+ msm_ep_unconfig(port->port_usb->out);
+ configure_data_fifo(d->usb_bam_type,
+ d->src_connection_idx,
+ port->port_usb->out, d->src_pipe_type);
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ if (d->tx_req_dequeued)
+ msm_dwc3_reset_dbm_ep(port->port_usb->in);
+ if (d->rx_req_dequeued)
+ msm_dwc3_reset_dbm_ep(port->port_usb->out);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb) {
+ if (d->tx_req_dequeued)
+ msm_ep_config(port->port_usb->in,
+ d->tx_req);
+ if (d->rx_req_dequeued)
+ msm_ep_config(port->port_usb->out,
+ d->rx_req);
+ }
+ }
+ d->tx_req_dequeued = false;
+ d->rx_req_dequeued = false;
+ usb_bam_resume(d->usb_bam_type, &d->ipa_params);
+ }
+
+exit:
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+/* BAM data channel ready, allow attempt to open */
+static int gbam_data_ch_probe(struct platform_device *pdev)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ int i;
+ unsigned long flags;
+ bool do_work = false;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ for (i = 0; i < n_bam_ports; i++) {
+ port = bam_ports[i].port;
+ d = &port->data_ch;
+
+ if (!strcmp(bam_ch_names[i], pdev->name)) {
+ set_bit(BAM_CH_READY, &d->flags);
+
+ /* if usb is online, try opening bam_ch */
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+ if (port->port_usb)
+ do_work = true;
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+ if (do_work)
+ queue_work(gbam_wq, &port->connect_w);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* BAM data channel went inactive, so close it */
+static int gbam_data_ch_remove(struct platform_device *pdev)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ struct usb_ep *ep_in = NULL;
+ struct usb_ep *ep_out = NULL;
+ unsigned long flags;
+ int i;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ for (i = 0; i < n_bam_ports; i++) {
+ if (!strcmp(bam_ch_names[i], pdev->name)) {
+ port = bam_ports[i].port;
+ d = &port->data_ch;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+ if (port->port_usb) {
+ ep_in = port->port_usb->in;
+ ep_out = port->port_usb->out;
+ }
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+
+ if (ep_in)
+ usb_ep_fifo_flush(ep_in);
+ if (ep_out)
+ usb_ep_fifo_flush(ep_out);
+
+ gbam_free_buffers(port);
+
+ msm_bam_dmux_close(d->id);
+
+ /* bam dmux will free all pending skbs */
+ d->pending_pkts_with_bam = 0;
+ d->pending_bytes_with_bam = 0;
+
+ clear_bit(BAM_CH_READY, &d->flags);
+ clear_bit(BAM_CH_OPENED, &d->flags);
+ }
+ }
+
+ return 0;
+}
+
+static void gbam_port_free(int portno)
+{
+ struct gbam_port *port = bam_ports[portno].port;
+ struct platform_driver *pdrv = &bam_ports[portno].pdrv;
+
+ if (port)
+ platform_driver_unregister(pdrv);
+
+ kfree(port);
+}
+
+static void gbam2bam_port_free(int portno)
+{
+ struct gbam_port *port = bam2bam_ports[portno];
+
+ kfree(port);
+}
+
+static int gbam_port_alloc(int portno)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ struct platform_driver *pdrv;
+
+ port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->port_num = portno;
+
+ /* port initialization */
+ port->is_connected = false;
+ spin_lock_init(&port->port_lock_ul);
+ spin_lock_init(&port->port_lock_dl);
+ spin_lock_init(&port->port_lock);
+ INIT_WORK(&port->connect_w, gbam_connect_work);
+ INIT_WORK(&port->disconnect_w, gbam_disconnect_work);
+
+ /* data ch */
+ d = &port->data_ch;
+ d->port = port;
+ INIT_LIST_HEAD(&d->tx_idle);
+ INIT_LIST_HEAD(&d->rx_idle);
+ INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
+ INIT_WORK(&d->write_tohost_w, gbam_write_data_tohost_w);
+ skb_queue_head_init(&d->tx_skb_q);
+ skb_queue_head_init(&d->rx_skb_q);
+ skb_queue_head_init(&d->rx_skb_idle);
+ d->id = bam_ch_ids[portno];
+
+ bam_ports[portno].port = port;
+
+ scnprintf(bam_ch_names[portno], BAM_DMUX_CH_NAME_MAX_LEN,
+ "bam_dmux_ch_%d", bam_ch_ids[portno]);
+ pdrv = &bam_ports[portno].pdrv;
+ pdrv->probe = gbam_data_ch_probe;
+ pdrv->remove = gbam_data_ch_remove;
+ pdrv->driver.name = bam_ch_names[portno];
+ pdrv->driver.owner = THIS_MODULE;
+
+ platform_driver_register(pdrv);
+ pr_debug("%s: port:%pK portno:%d\n", __func__, port, portno);
+
+ return 0;
+}
+
+static int gbam2bam_port_alloc(int portno)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->port_num = portno;
+
+ /* port initialization */
+ port->is_connected = false;
+ spin_lock_init(&port->port_lock_ul);
+ spin_lock_init(&port->port_lock_dl);
+ spin_lock_init(&port->port_lock);
+
+ INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+ INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
+ INIT_WORK(&port->suspend_w, gbam2bam_suspend_work);
+ INIT_WORK(&port->resume_w, gbam2bam_resume_work);
+
+ /* data ch */
+ d = &port->data_ch;
+ d->port = port;
+ d->ipa_params.src_client = usb_prod[portno];
+ d->ipa_params.dst_client = usb_cons[portno];
+ bam2bam_ports[portno] = port;
+
+ /* UL workaround requirements */
+ skb_queue_head_init(&d->rx_skb_q);
+ skb_queue_head_init(&d->rx_skb_idle);
+ INIT_LIST_HEAD(&d->rx_idle);
+ INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
+
+ pr_debug("%s: port:%pK portno:%d\n", __func__, port, portno);
+
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t gbam_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ char *buf;
+ unsigned long flags;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < n_bam_ports; i++) {
+ port = bam_ports[i].port;
+ if (!port)
+ continue;
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+
+ d = &port->data_ch;
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "#PORT:%d port:%pK data_ch:%pK#\n"
+ "dpkts_to_usbhost: %lu\n"
+ "dpkts_to_modem: %lu\n"
+ "dpkts_pwith_bam: %u\n"
+ "dbytes_pwith_bam: %u\n"
+ "to_usbhost_dcnt: %u\n"
+ "tomodem__dcnt: %u\n"
+ "rx_flow_control_disable_count: %u\n"
+ "rx_flow_control_enable_count: %u\n"
+ "rx_flow_control_triggered: %u\n"
+ "max_num_pkts_pending_with_bam: %u\n"
+ "max_bytes_pending_with_bam: %u\n"
+ "delayed_bam_mux_write_done: %u\n"
+ "tx_buf_len: %u\n"
+ "rx_buf_len: %u\n"
+ "data_ch_open: %d\n"
+ "data_ch_ready: %d\n"
+ "skb_expand_cnt: %lu\n",
+ i, port, &port->data_ch,
+ d->to_host, d->to_modem,
+ d->pending_pkts_with_bam,
+ d->pending_bytes_with_bam,
+ d->tohost_drp_cnt, d->tomodem_drp_cnt,
+ d->rx_flow_control_disable,
+ d->rx_flow_control_enable,
+ d->rx_flow_control_triggered,
+ d->max_num_pkts_pending_with_bam,
+ d->max_bytes_pending_with_bam,
+ d->delayed_bam_mux_write_done,
+ d->tx_skb_q.qlen, d->rx_skb_q.qlen,
+ test_bit(BAM_CH_OPENED, &d->flags),
+ test_bit(BAM_CH_READY, &d->flags),
+ d->skb_expand_cnt);
+
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t gbam_reset_stats(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ int i;
+ unsigned long flags;
+
+ for (i = 0; i < n_bam_ports; i++) {
+ port = bam_ports[i].port;
+ if (!port)
+ continue;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags);
+ spin_lock(&port->port_lock_dl);
+
+ d = &port->data_ch;
+
+ d->to_host = 0;
+ d->to_modem = 0;
+ d->pending_pkts_with_bam = 0;
+ d->pending_bytes_with_bam = 0;
+ d->tohost_drp_cnt = 0;
+ d->tomodem_drp_cnt = 0;
+ d->rx_flow_control_disable = 0;
+ d->rx_flow_control_enable = 0;
+ d->rx_flow_control_triggered = 0;
+ d->max_num_pkts_pending_with_bam = 0;
+ d->max_bytes_pending_with_bam = 0;
+ d->delayed_bam_mux_write_done = 0;
+ d->skb_expand_cnt = 0;
+
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags);
+ }
+ return count;
+}
+
+const struct file_operations gbam_stats_ops = {
+ .read = gbam_read_stats,
+ .write = gbam_reset_stats,
+};
+
+static ssize_t gbam_rw_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct gbam_port *port = bam2bam_ports[0];
+ struct usb_function *func;
+ struct usb_gadget *gadget;
+ unsigned long flags;
+
+ if (!port)
+ return -ENODEV;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ pr_debug("%s: usb cable is disconnected, exiting\n",
+ __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return -ENODEV;
+ }
+
+ gadget = port->port_usb->gadget;
+ func = port->port_usb->f;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if ((gadget->speed == USB_SPEED_SUPER) && (func->func_is_suspended)) {
+ pr_debug("%s Initiating usb_func rwakeup\n", __func__);
+ usb_func_wakeup(func);
+ }
+
+ return count;
+}
+
+
+const struct file_operations debug_remote_wakeup_fops = {
+ .write = gbam_rw_write,
+};
+
+struct dentry *gbam_dent;
+static void gbam_debugfs_init(void)
+{
+ struct dentry *dfile;
+
+ if (gbam_dent)
+ return;
+
+ gbam_dent = debugfs_create_dir("usb_rmnet", 0);
+ if (!gbam_dent || IS_ERR(gbam_dent))
+ return;
+
+ debugfs_create_file("remote_wakeup", 0444, gbam_dent, 0,
+ &debug_remote_wakeup_fops);
+
+ dfile = debugfs_create_file("status", 0444, gbam_dent, 0,
+ &gbam_stats_ops);
+ if (!dfile || IS_ERR(dfile)) {
+ debugfs_remove(gbam_dent);
+ gbam_dent = NULL;
+ return;
+ }
+}
+static void gbam_debugfs_remove(void)
+{
+ debugfs_remove_recursive(gbam_dent);
+}
+#else
+static inline void gbam_debugfs_init(void) {}
+static inline void gbam_debugfs_remove(void) {}
+#endif
+
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ unsigned long flags, flags_ul, flags_dl;
+ struct bam_ch_info *d;
+
+ pr_debug("%s: grmnet:%pK port#%d\n", __func__, gr, port_num);
+
+ if (trans == USB_GADGET_XPORT_BAM2BAM) {
+ pr_err("%s: invalid xport#%d\n", __func__, trans);
+ return;
+ }
+ if (trans == USB_GADGET_XPORT_BAM_DMUX &&
+ port_num >= n_bam_ports) {
+ pr_err("%s: invalid bam portno#%d\n",
+ __func__, port_num);
+ return;
+ }
+
+ if ((trans == USB_GADGET_XPORT_BAM2BAM_IPA) &&
+ port_num >= n_bam2bam_ports) {
+ pr_err("%s: invalid bam2bam portno#%d\n",
+ __func__, port_num);
+ return;
+ }
+
+ if (!gr) {
+ pr_err("%s: grmnet port is null\n", __func__);
+ return;
+ }
+ if (trans == USB_GADGET_XPORT_BAM_DMUX)
+ port = bam_ports[port_num].port;
+ else
+ port = bam2bam_ports[port_num];
+
+ if (!port) {
+ pr_err("%s: NULL port", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ d = &port->data_ch;
+ /* Already disconnected due to suspend with remote wake disabled */
+ if (port->last_event == U_BAM_DISCONNECT_E) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ /*
+ * Suspend with remote wakeup enabled. Increment usage
+ * count when disconnect happens in suspended state.
+ * Corresponding decrement happens in the end of this
+ * function if IPA handshake is already done or it is done
+ * in disconnect work after finishing IPA handshake.
+ */
+ if (port->last_event == U_BAM_SUSPEND_E)
+ usb_gadget_autopm_get_noresume(port->gadget);
+
+ port->port_usb = gr;
+
+ if (trans == USB_GADGET_XPORT_BAM_DMUX)
+ gbam_free_buffers(port);
+ else if (trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ gbam_free_rx_buffers(port);
+
+ spin_lock_irqsave(&port->port_lock_ul, flags_ul);
+ spin_lock(&port->port_lock_dl);
+ port->port_usb = 0;
+ n_tx_req_queued = 0;
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+
+ usb_ep_disable(gr->in);
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ spin_lock_irqsave(&port->port_lock_dl, flags_dl);
+ if (d->tx_req) {
+ usb_ep_free_request(gr->in, d->tx_req);
+ d->tx_req = NULL;
+ }
+ spin_unlock_irqrestore(&port->port_lock_dl, flags_dl);
+ }
+ /* disable endpoints */
+ if (gr->out) {
+ usb_ep_disable(gr->out);
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ spin_lock_irqsave(&port->port_lock_ul, flags_ul);
+ if (d->rx_req) {
+ usb_ep_free_request(gr->out, d->rx_req);
+ d->rx_req = NULL;
+ }
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+ }
+ }
+
+ /*
+ * Set endless flag to false as USB Endpoint is already
+ * disable.
+ */
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+
+ if (d->dst_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ gr->in->endless = false;
+
+ if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM && gr->out)
+ gr->out->endless = false;
+ }
+
+ gr->in->driver_data = NULL;
+ if (gr->out)
+ gr->out->driver_data = NULL;
+
+ port->last_event = U_BAM_DISCONNECT_E;
+ /* Disable usb irq for CI gadget. It will be enabled in
+ * usb_bam_disconnect_pipe() after disconnecting all pipes
+ * and USB BAM reset is done.
+ */
+ if (!gadget_is_dwc3(port->gadget) &&
+ (trans == USB_GADGET_XPORT_BAM2BAM_IPA))
+ msm_usb_irq_disable(true);
+
+ queue_work(gbam_wq, &port->disconnect_w);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+int gbam_connect(struct grmnet *gr, u8 port_num,
+ enum transport_type trans, u8 src_connection_idx,
+ u8 dst_connection_idx)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ int ret;
+ unsigned long flags, flags_ul;
+
+ pr_debug("%s: grmnet:%pK port#%d\n", __func__, gr, port_num);
+
+ if (!gr) {
+ pr_err("%s: grmnet port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!gr->gadget) {
+ pr_err("%s: gadget handle not passed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (trans == USB_GADGET_XPORT_BAM2BAM) {
+ pr_err("%s: invalid xport#%d\n", __func__, trans);
+ return -EINVAL;
+ }
+
+ if (trans == USB_GADGET_XPORT_BAM_DMUX && port_num >= n_bam_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ if ((trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ && port_num >= n_bam2bam_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ if (trans == USB_GADGET_XPORT_BAM_DMUX)
+ port = bam_ports[port_num].port;
+ else
+ port = bam2bam_ports[port_num];
+
+ if (!port) {
+ pr_err("%s: NULL port", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ d = &port->data_ch;
+ d->trans = trans;
+
+ spin_lock_irqsave(&port->port_lock_ul, flags_ul);
+ spin_lock(&port->port_lock_dl);
+ port->port_usb = gr;
+ port->gadget = port->port_usb->gadget;
+
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ d->rx_req = usb_ep_alloc_request(port->port_usb->out,
+ GFP_ATOMIC);
+ if (!d->rx_req) {
+ pr_err("%s: RX request allocation failed\n", __func__);
+ d->rx_req = NULL;
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return -ENOMEM;
+ }
+
+ d->rx_req->context = port;
+ d->rx_req->complete = gbam_endless_rx_complete;
+ d->rx_req->length = 0;
+ d->rx_req->no_interrupt = 1;
+
+ d->tx_req = usb_ep_alloc_request(port->port_usb->in,
+ GFP_ATOMIC);
+ if (!d->tx_req) {
+ pr_err("%s: TX request allocation failed\n", __func__);
+ d->tx_req = NULL;
+ usb_ep_free_request(port->port_usb->out, d->rx_req);
+ d->rx_req = NULL;
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return -ENOMEM;
+ }
+
+ d->tx_req->context = port;
+ d->tx_req->complete = gbam_endless_tx_complete;
+ d->tx_req->length = 0;
+ d->tx_req->no_interrupt = 1;
+ }
+
+ if (d->trans == USB_GADGET_XPORT_BAM_DMUX) {
+ d->to_host = 0;
+ d->to_modem = 0;
+ d->pending_pkts_with_bam = 0;
+ d->pending_bytes_with_bam = 0;
+ d->tohost_drp_cnt = 0;
+ d->tomodem_drp_cnt = 0;
+ d->rx_flow_control_disable = 0;
+ d->rx_flow_control_enable = 0;
+ d->rx_flow_control_triggered = 0;
+ d->max_num_pkts_pending_with_bam = 0;
+ d->max_bytes_pending_with_bam = 0;
+ d->delayed_bam_mux_write_done = 0;
+ }
+
+ spin_unlock(&port->port_lock_dl);
+ spin_unlock_irqrestore(&port->port_lock_ul, flags_ul);
+
+ if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+ d->src_connection_idx = src_connection_idx;
+ d->dst_connection_idx = dst_connection_idx;
+ d->usb_bam_type = usb_bam_get_bam_type(gr->gadget->name);
+ d->ipa_params.src_pipe = &(d->src_pipe_idx);
+ d->ipa_params.dst_pipe = &(d->dst_pipe_idx);
+ d->ipa_params.src_idx = src_connection_idx;
+ d->ipa_params.dst_idx = dst_connection_idx;
+
+ /*
+ * Query pipe type using IPA src/dst index with
+ * usbbam driver. It is being set either as
+ * BAM2BAM or SYS2BAM.
+ */
+ if (usb_bam_get_pipe_type(d->usb_bam_type,
+ d->ipa_params.src_idx, &d->src_pipe_type) ||
+ usb_bam_get_pipe_type(d->usb_bam_type,
+ d->ipa_params.dst_idx, &d->dst_pipe_type)) {
+ pr_err("%s:usb_bam_get_pipe_type() failed\n",
+ __func__);
+ ret = -EINVAL;
+ usb_ep_free_request(port->port_usb->out, d->rx_req);
+ d->rx_req = NULL;
+ usb_ep_free_request(port->port_usb->in, d->tx_req);
+ d->tx_req = NULL;
+ goto exit;
+ }
+ /*
+ * Check for pipe_type. If it is BAM2BAM, then it is required
+ * to disable Xfer complete and Xfer not ready interrupts for
+ * that particular endpoint. Hence it set endless flag based
+ * it which is considered into UDC driver while enabling
+ * USB Endpoint.
+ */
+ if (d->dst_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->in->endless = true;
+
+ if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->out->endless = true;
+ }
+
+ ret = usb_ep_enable(gr->in);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:IN ep:%pK",
+ __func__, gr->in);
+ usb_ep_free_request(port->port_usb->out, d->rx_req);
+ d->rx_req = NULL;
+ usb_ep_free_request(port->port_usb->in, d->tx_req);
+ d->tx_req = NULL;
+ if (d->dst_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->in->endless = false;
+
+ if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->out->endless = false;
+ goto exit;
+ }
+ gr->in->driver_data = port;
+
+ /*
+ * DPL traffic is routed through BAM-DMUX on some targets.
+ * DPL function has only 1 IN endpoint. Add out endpoint
+ * checks for BAM-DMUX transport.
+ */
+ if (gr->out) {
+ ret = usb_ep_enable(gr->out);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:OUT ep:%pK",
+ __func__, gr->out);
+ gr->in->driver_data = 0;
+ usb_ep_disable(gr->in);
+ usb_ep_free_request(port->port_usb->out, d->rx_req);
+ d->rx_req = NULL;
+ usb_ep_free_request(port->port_usb->in, d->tx_req);
+ d->tx_req = NULL;
+ if (d->dst_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->in->endless = false;
+
+ if (d->src_pipe_type == USB_BAM_PIPE_BAM2BAM)
+ port->port_usb->out->endless = false;
+ goto exit;
+ }
+ gr->out->driver_data = port;
+ }
+
+ port->last_event = U_BAM_CONNECT_E;
+ /*
+ * Increment usage count upon cable connect. Decrement after IPA
+ * handshake is done in disconnect work (due to cable disconnect)
+ * or in suspend work.
+ */
+ if (trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(gbam_wq, &port->connect_w);
+
+ ret = 0;
+exit:
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return ret;
+}
+
+void gbam_data_flush_workqueue(void)
+{
+ pr_debug("%s(): Flushing workqueue\n", __func__);
+ flush_workqueue(gbam_wq);
+}
+
+int gbam_setup(unsigned int no_bam_port)
+{
+ int i;
+ int ret;
+ int bam_port_start = n_bam_ports;
+ int total_bam_ports = bam_port_start + no_bam_port;
+
+ pr_debug("%s: requested BAM ports:%d\n", __func__, no_bam_port);
+
+ if (!no_bam_port || total_bam_ports > BAM_N_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, no_bam_port);
+ return -EINVAL;
+ }
+
+ if (!gbam_wq) {
+ gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1);
+ if (!gbam_wq) {
+ pr_err("%s: Unable to create workqueue gbam_wq\n",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+
+ for (i = bam_port_start; i < (bam_port_start + no_bam_port); i++) {
+ n_bam_ports++;
+ pr_debug("gbam_port_alloc called for %d\n", i);
+ ret = gbam_port_alloc(i);
+ if (ret) {
+ n_bam_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_bam_ports;
+ }
+ }
+
+ gbam_debugfs_init();
+
+ return bam_port_start;
+
+free_bam_ports:
+ for (i = 0; i < n_bam_ports; i++)
+ gbam_port_free(i);
+ destroy_workqueue(gbam_wq);
+
+ return ret;
+}
+
+int gbam2bam_setup(unsigned int no_bam2bam_port)
+{
+ int i;
+ int ret;
+ int bam2bam_port_start = n_bam2bam_ports;
+ int total_bam2bam_ports = bam2bam_port_start + no_bam2bam_port;
+
+ pr_debug("%s: requested BAM2BAM ports:%d\n", __func__, no_bam2bam_port);
+
+ if (!no_bam2bam_port || total_bam2bam_ports > BAM2BAM_N_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, no_bam2bam_port);
+ return -EINVAL;
+ }
+
+ if (!gbam_wq) {
+ gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1);
+ if (!gbam_wq) {
+ pr_err("%s: Unable to create workqueue gbam_wq\n",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+
+ for (i = bam2bam_port_start; i < (bam2bam_port_start +
+ no_bam2bam_port); i++) {
+ n_bam2bam_ports++;
+ ret = gbam2bam_port_alloc(i);
+ if (ret) {
+ n_bam2bam_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_bam2bam_ports;
+ }
+ }
+
+ gbam_debugfs_init();
+
+ return bam2bam_port_start;
+
+free_bam2bam_ports:
+ for (i = 0; i < n_bam2bam_ports; i++)
+ gbam2bam_port_free(i);
+ destroy_workqueue(gbam_wq);
+
+ return ret;
+}
+
+void gbam_cleanup(void)
+{
+ gbam_debugfs_remove();
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ unsigned long flags;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM_IPA)
+ return;
+
+ port = bam2bam_ports[port_num];
+
+ if (!port) {
+ pr_err("%s: NULL port", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ d = &port->data_ch;
+
+ pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+ port->last_event = U_BAM_SUSPEND_E;
+ queue_work(gbam_wq, &port->suspend_w);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+ unsigned long flags;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM_IPA)
+ return;
+
+ port = bam2bam_ports[port_num];
+
+ if (!port) {
+ pr_err("%s: NULL port", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ d = &port->data_ch;
+
+ pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+ port->last_event = U_BAM_RESUME_E;
+ /*
+ * Increment usage count here to disallow gadget parent suspend.
+ * This counter will decrement after IPA handshake is done in
+ * disconnect work (due to cable disconnect) or in bam_disconnect
+ * in suspended state.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(gbam_wq, &port->resume_w);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+int gbam_mbim_connect(struct usb_gadget *g, struct usb_ep *in,
+ struct usb_ep *out)
+{
+ struct grmnet *gr;
+
+ gr = kzalloc(sizeof(*gr), GFP_ATOMIC);
+ if (!gr)
+ return -ENOMEM;
+ gr->in = in;
+ gr->out = out;
+ gr->gadget = g;
+
+ return gbam_connect(gr, 0, USB_GADGET_XPORT_BAM_DMUX, 0, 0);
+}
+
+void gbam_mbim_disconnect(void)
+{
+ struct gbam_port *port = bam_ports[0].port;
+ struct grmnet *gr = port->port_usb;
+
+ if (!gr) {
+ pr_err("%s: port_usb is NULL\n", __func__);
+ return;
+ }
+
+ gbam_disconnect(gr, 0, USB_GADGET_XPORT_BAM_DMUX);
+ kfree(gr);
+}
+
+int gbam_mbim_setup(void)
+{
+ int ret = 0;
+
+ /*
+ * MBIM requires only 1 USB_GADGET_XPORT_BAM_DMUX
+ * port. The port is always 0 and is shared
+ * between RMNET and MBIM.
+ */
+ if (!n_bam_ports)
+ ret = gbam_setup(1);
+
+ return ret;
+}
diff --git a/drivers/usb/gadget/function/u_ctrl_qti.c b/drivers/usb/gadget/function/u_ctrl_qti.c
index 96018f7..21839a3e 100644
--- a/drivers/usb/gadget/function/u_ctrl_qti.c
+++ b/drivers/usb/gadget/function/u_ctrl_qti.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, 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
@@ -330,7 +330,7 @@ void gqti_ctrl_update_ipa_pipes(void *gr, enum qti_port_type qport,
static int qti_ctrl_open(struct inode *ip, struct file *fp)
{
unsigned long flags;
- struct qti_ctrl_port *port = container_of(fp->private_data,
+ struct qti_ctrl_port *port = container_of(ip->i_cdev,
struct qti_ctrl_port,
ctrl_device);
@@ -342,6 +342,7 @@ static int qti_ctrl_open(struct inode *ip, struct file *fp)
return -EBUSY;
}
+ fp->private_data = port;
spin_lock_irqsave(&port->lock, flags);
port->is_open = true;
spin_unlock_irqrestore(&port->lock, flags);
@@ -352,7 +353,7 @@ static int qti_ctrl_open(struct inode *ip, struct file *fp)
static int qti_ctrl_release(struct inode *ip, struct file *fp)
{
unsigned long flags;
- struct qti_ctrl_port *port = container_of(fp->private_data,
+ struct qti_ctrl_port *port = container_of(ip->i_cdev,
struct qti_ctrl_port,
ctrl_device);
@@ -370,9 +371,7 @@ static int qti_ctrl_release(struct inode *ip, struct file *fp)
static ssize_t
qti_ctrl_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
{
- struct qti_ctrl_port *port = container_of(fp->private_data,
- struct qti_ctrl_port,
- ctrl_device);
+ struct qti_ctrl_port *port = fp->private_data;
struct rmnet_ctrl_pkt *cpkt = NULL;
unsigned long flags;
int ret = 0;
@@ -444,9 +443,7 @@ static ssize_t
qti_ctrl_write(struct file *fp, const char __user *buf, size_t count,
loff_t *pos)
{
- struct qti_ctrl_port *port = container_of(fp->private_data,
- struct qti_ctrl_port,
- ctrl_device);
+ struct qti_ctrl_port *port = fp->private_data;
void *kbuf;
unsigned long flags;
int ret = 0;
@@ -523,9 +520,7 @@ qti_ctrl_write(struct file *fp, const char __user *buf, size_t count,
static long qti_ctrl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
- struct qti_ctrl_port *port = container_of(fp->private_data,
- struct qti_ctrl_port,
- ctrl_device);
+ struct qti_ctrl_port *port = fp->private_data;
struct grmnet *gr = NULL;
struct ep_info info;
int val, ret = 0;
@@ -621,9 +616,7 @@ static long qti_ctrl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
static unsigned int qti_ctrl_poll(struct file *file, poll_table *wait)
{
- struct qti_ctrl_port *port = container_of(file->private_data,
- struct qti_ctrl_port,
- ctrl_device);
+ struct qti_ctrl_port *port = file->private_data;
unsigned long flags;
unsigned int mask = 0;
@@ -756,18 +749,19 @@ static const struct file_operations dpl_qti_ctrl_fops = {
#endif
.poll = qti_ctrl_poll,
};
+#define RMNET_MODULE_NAME "rmnet_ctrl"
static int qti_ctrl_alloc_chardev_region(void)
{
int ret;
- rmnet_class = class_create(THIS_MODULE, MODULE_NAME);
+ rmnet_class = class_create(THIS_MODULE, RMNET_MODULE_NAME);
if (IS_ERR(rmnet_class)) {
pr_err("class_create() failed ENOMEM\n");
ret = -ENOMEM;
}
ret = alloc_chrdev_region(&qti_ctrl_dev, 0, QTI_NUM_PORTS,
- MODULE_NAME);
+ RMNET_MODULE_NAME);
if (ret < 0) {
pr_err("alloc_chrdev_region() failed ret:%i\n", ret);
return ret;
@@ -819,18 +813,6 @@ int gqti_ctrl_init(void)
snprintf(port->name, sz, "%s%d",
RMNET_CTRL_QTI_NAME, i);
- port->ctrl_device.owner = THIS_MODULE;
- if (i == QTI_PORT_DPL)
- cdev_init(&port->ctrl_device, &dpl_qti_ctrl_fops);
- else
- cdev_init(&port->ctrl_device, &qti_ctrl_fops);
-
- ret = cdev_add(&port->ctrl_device, qti_ctrl_dev + i, 1);
- if (ret < 0) {
- pr_err("cdev_add() failed ret:%d\n", ret);
- goto fail_cdev;
- }
-
port->dev = device_create(rmnet_class, NULL, qti_ctrl_dev+i,
port->dev, port->name);
if (IS_ERR(port->dev)) {
@@ -838,12 +820,24 @@ int gqti_ctrl_init(void)
ret = -ENOMEM;
goto fail_device_create;
}
+
+ if (i == QTI_PORT_DPL)
+ cdev_init(&port->ctrl_device, &dpl_qti_ctrl_fops);
+ else
+ cdev_init(&port->ctrl_device, &qti_ctrl_fops);
+
+ port->ctrl_device.owner = THIS_MODULE;
+ ret = cdev_add(&port->ctrl_device, qti_ctrl_dev + i, 1);
+ if (ret < 0) {
+ pr_err("cdev_add() failed ret:%d\n", ret);
+ goto fail_cdev;
+ }
}
qti_ctrl_debugfs_init();
return ret;
-fail_device_create:
- cdev_del(&port->ctrl_device);
fail_cdev:
+ cdev_del(&port->ctrl_device);
+fail_device_create:
class_destroy(rmnet_class);
unregister_chrdev_region(MAJOR(qti_ctrl_dev), QTI_NUM_PORTS);
fail_init:
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 54220a5..d50510f 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -21,6 +21,8 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
#include "u_ether.h"
@@ -89,6 +91,8 @@ struct eth_dev {
struct work_struct rx_work;
unsigned long todo;
+ unsigned long flags;
+ unsigned short rx_needed_headroom;
#define WORK_RX_MEMORY 0
bool zlp;
@@ -161,6 +165,25 @@ static int ueth_change_mtu(struct net_device *net, int new_mtu)
return 0;
}
+static int ueth_change_mtu_ip(struct net_device *net, int new_mtu)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (new_mtu <= 0)
+ status = -EINVAL;
+ else
+ net->mtu = new_mtu;
+
+ DBG(dev, "[%s] MTU change: old=%d new=%d\n", net->name,
+ net->mtu, new_mtu);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return status;
+}
+
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
{
struct eth_dev *dev = netdev_priv(net);
@@ -900,15 +923,176 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
return 18;
}
+static int ether_ioctl(struct net_device *, struct ifreq *, int);
+
static const struct net_device_ops eth_netdev_ops = {
.ndo_open = eth_open,
.ndo_stop = eth_stop,
.ndo_start_xmit = eth_start_xmit,
+ .ndo_do_ioctl = ether_ioctl,
.ndo_change_mtu = ueth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
+static const struct net_device_ops eth_netdev_ops_ip = {
+ .ndo_open = eth_open,
+ .ndo_stop = eth_stop,
+ .ndo_start_xmit = eth_start_xmit,
+ .ndo_do_ioctl = ether_ioctl,
+ .ndo_change_mtu = ueth_change_mtu_ip,
+ .ndo_set_mac_address = NULL,
+ .ndo_validate_addr = NULL,
+};
+
+static int rmnet_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
+{
+ struct rmnet_ioctl_extended_s ext_cmd;
+ struct eth_dev *eth_dev = netdev_priv(dev);
+ int rc = 0;
+
+ rc = copy_from_user(&ext_cmd, ifr->ifr_ifru.ifru_data,
+ sizeof(struct rmnet_ioctl_extended_s));
+
+ if (rc) {
+ DBG(eth_dev, "%s(): copy_from_user() failed\n", __func__);
+ return rc;
+ }
+
+ switch (ext_cmd.extended_ioctl) {
+ case RMNET_IOCTL_GET_SUPPORTED_FEATURES:
+ ext_cmd.u.data = 0;
+ break;
+
+ case RMNET_IOCTL_SET_MRU:
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* 16K max */
+ if ((size_t)ext_cmd.u.data > 0x4000)
+ return -EINVAL;
+
+ if (eth_dev->port_usb) {
+ eth_dev->port_usb->is_fixed = true;
+ eth_dev->port_usb->fixed_out_len =
+ (size_t) ext_cmd.u.data;
+ DBG(eth_dev, "[%s] rmnet_ioctl(): SET MRU to %u\n",
+ dev->name, eth_dev->port_usb->fixed_out_len);
+ } else {
+ pr_err("[%s]: %s: SET MRU failed. Cable disconnected\n",
+ dev->name, __func__);
+ return -ENODEV;
+ }
+ break;
+
+ case RMNET_IOCTL_GET_MRU:
+ if (eth_dev->port_usb) {
+ ext_cmd.u.data = eth_dev->port_usb->is_fixed ?
+ eth_dev->port_usb->fixed_out_len :
+ dev->mtu;
+ } else {
+ pr_err("[%s]: %s: GET MRU failed. Cable disconnected\n",
+ dev->name, __func__);
+ return -ENODEV;
+ }
+ break;
+
+ case RMNET_IOCTL_GET_DRIVER_NAME:
+ strlcpy(ext_cmd.u.if_name, dev->name,
+ sizeof(ext_cmd.u.if_name));
+ break;
+
+ default:
+ break;
+ }
+
+ rc = copy_to_user(ifr->ifr_ifru.ifru_data, &ext_cmd,
+ sizeof(struct rmnet_ioctl_extended_s));
+
+ if (rc)
+ DBG(eth_dev, "%s(): copy_to_user() failed\n", __func__);
+ return rc;
+}
+
+static int ether_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct eth_dev *eth_dev = netdev_priv(dev);
+ void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
+ int prev_mtu = dev->mtu;
+ u32 state, old_opmode;
+ int rc = -EFAULT;
+
+ old_opmode = eth_dev->flags;
+ /* Process IOCTL command */
+ switch (cmd) {
+ case RMNET_IOCTL_SET_LLP_ETHERNET: /*Set Ethernet protocol*/
+ /* Perform Ethernet config only if in IP mode currently*/
+ if (test_bit(RMNET_MODE_LLP_IP, ð_dev->flags)) {
+ ether_setup(dev);
+ dev->mtu = prev_mtu;
+ dev->netdev_ops = ð_netdev_ops;
+ clear_bit(RMNET_MODE_LLP_IP, ð_dev->flags);
+ set_bit(RMNET_MODE_LLP_ETH, ð_dev->flags);
+ DBG(eth_dev, "[%s] ioctl(): set Ethernet proto mode\n",
+ dev->name);
+ }
+ if (test_bit(RMNET_MODE_LLP_ETH, ð_dev->flags))
+ rc = 0;
+ break;
+
+ case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol*/
+ /* Perform IP config only if in Ethernet mode currently*/
+ if (test_bit(RMNET_MODE_LLP_ETH, ð_dev->flags)) {
+ /* Undo config done in ether_setup() */
+ dev->header_ops = NULL; /* No header */
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->mtu = prev_mtu;
+ dev->addr_len = 0;
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ dev->netdev_ops = ð_netdev_ops_ip;
+ clear_bit(RMNET_MODE_LLP_ETH, ð_dev->flags);
+ set_bit(RMNET_MODE_LLP_IP, ð_dev->flags);
+ DBG(eth_dev, "[%s] ioctl(): set IP protocol mode\n",
+ dev->name);
+ }
+ if (test_bit(RMNET_MODE_LLP_IP, ð_dev->flags))
+ rc = 0;
+ break;
+
+ case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
+ state = eth_dev->flags & (RMNET_MODE_LLP_ETH
+ | RMNET_MODE_LLP_IP);
+ if (copy_to_user(addr, &state, sizeof(state)))
+ break;
+ rc = 0;
+ break;
+
+ case RMNET_IOCTL_SET_RX_HEADROOM: /* Set RX headroom */
+ if (copy_from_user(ð_dev->rx_needed_headroom, addr,
+ sizeof(eth_dev->rx_needed_headroom)))
+ break;
+ DBG(eth_dev, "[%s] ioctl(): set RX HEADROOM: %x\n",
+ dev->name, eth_dev->rx_needed_headroom);
+ rc = 0;
+ break;
+
+ case RMNET_IOCTL_EXTENDED:
+ rc = rmnet_ioctl_extended(dev, ifr);
+ break;
+
+ default:
+ pr_err("[%s] error: ioctl called for unsupported cmd[%d]",
+ dev->name, cmd);
+ rc = -EINVAL;
+ }
+
+ DBG(eth_dev, "[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08lx\n",
+ dev->name, __func__, cmd, old_opmode, eth_dev->flags);
+
+ return rc;
+}
+
static struct device_type gadget_type = {
.name = "gadget",
};
@@ -968,6 +1152,9 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
net->ethtool_ops = &ops;
+ /* set operation mode to eth by default */
+ set_bit(RMNET_MODE_LLP_ETH, &dev->flags);
+
dev->gadget = g;
SET_NETDEV_DEV(net, &g->dev);
SET_NETDEV_DEVTYPE(net, &gadget_type);
@@ -1025,6 +1212,10 @@ struct net_device *gether_setup_name_default(const char *netname)
net->netdev_ops = ð_netdev_ops;
net->ethtool_ops = &ops;
+
+ /* set operation mode to eth by default */
+ set_bit(RMNET_MODE_LLP_ETH, &dev->flags);
+
SET_NETDEV_DEVTYPE(net, &gadget_type);
return net;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 72176a8..6959071 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1172,6 +1172,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
+ dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask);
+ gadget->dev.dma_parms = parent->dma_parms;
+ gadget->dev.dma_mask = parent->dma_mask;
+
if (release)
gadget->dev.release = release;
else
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 19f3f18..ad4c0a3 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -71,6 +71,8 @@
#define SQ_CTRL1_CHIRP_DISABLE 0x20
#define SQ_CTRL2_CHIRP_DISABLE 0x80
+#define DEBUG_CTRL1_OVERRIDE_VAL 0x09
+
/* PERIPH_SS_PHY_REFGEN_NORTH_BG_CTRL register bits */
#define BANDGAP_BYPASS BIT(0)
@@ -84,6 +86,7 @@ enum qusb_phy_reg {
BIAS_CTRL_2,
SQ_CTRL1,
SQ_CTRL2,
+ DEBUG_CTRL1,
USB2_PHY_REG_MAX,
};
@@ -180,7 +183,11 @@ static long qfprom_read(struct device *dev, const char *name)
err = PTR_ERR(buf);
}
} else {
- val = *buf;
+ /*
+ * The bits are read from bit-0 to bit-29
+ * We're interested in bits 28:29
+ */
+ val = (*buf >> 28) & 0x3;
kfree(buf);
}
@@ -553,6 +560,11 @@ static int qusb_phy_init(struct usb_phy *phy)
writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+ /* if soc revision is mentioned override DEBUG_CTRL1 value */
+ if (qphy->soc_min_rev)
+ writel_relaxed(DEBUG_CTRL1_OVERRIDE_VAL,
+ qphy->base + qphy->phy_reg[DEBUG_CTRL1]);
+
/* ensure above writes are completed before re-enabling PHY */
wmb();
@@ -1228,7 +1240,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
* qusb_phy_disable_chirp is not required if soc version is
* mentioned and is not base version.
*/
- if (qphy->soc_min_rev == 0)
+ if (!qphy->soc_min_rev)
qphy->phy.disable_chirp = qusb_phy_disable_chirp;
qphy->phy.start_port_reset = qusb_phy_enable_ext_pulldown;
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c5cdddc..d89714b 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -32,7 +32,6 @@
#include <linux/of.h>
#include <linux/dma-mapping.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/irqchip/msm-mpm-irq.h>
#include <linux/pm_wakeup.h>
#include <linux/reset.h>
#include <linux/extcon.h>
@@ -135,20 +134,12 @@ enum usb_vdd_value {
* for msm_otg driver.
* @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as
* "do not overwrite default value at this address".
- * @vbus_power: VBUS power on/off routine.It should return result
- * as success(zero value) or failure(non-zero value).
* @power_budget: VBUS power budget in mA (0 will be treated as 500mA).
* @mode: Supported mode (OTG/peripheral/host).
* @otg_control: OTG switch controlled by user/Id pin
* @default_mode: Default operational mode. Applicable only if
* OTG switch is controller by user.
* @pmic_id_irq: IRQ number assigned for PMIC USB ID line.
- * @mpm_otgsessvld_int: MPM wakeup pin assigned for OTG SESSVLD
- * interrupt. Used when .otg_control == OTG_PHY_CONTROL.
- * @mpm_dpshv_int: MPM wakeup pin assigned for DP SHV interrupt.
- * Used during host bus suspend.
- * @mpm_dmshv_int: MPM wakeup pin assigned for DM SHV interrupt.
- * Used during host bus suspend.
* @disable_reset_on_disconnect: perform USB PHY and LINK reset
* on USB cable disconnection.
* @pnoc_errata_fix: workaround needed for PNOC hardware bug that
@@ -187,23 +178,17 @@ enum usb_vdd_value {
* @bool enable_streaming: Indicates whether streaming to be enabled by default.
* @bool enable_axi_prefetch: Indicates whether AXI Prefetch interface is used
for improving data performance.
- * @bool enable_sdp_typec_current_limit: Indicates whether type-c current for
- sdp charger to be limited.
* @usbeth_reset_gpio: Gpio used for external usb-to-eth reset.
*/
struct msm_otg_platform_data {
int *phy_init_seq;
int phy_init_sz;
- int (*vbus_power)(bool on);
unsigned int power_budget;
enum usb_mode_type mode;
enum otg_control_type otg_control;
enum usb_mode_type default_mode;
enum msm_usb_phy_type phy_type;
int pmic_id_irq;
- unsigned int mpm_otgsessvld_int;
- unsigned int mpm_dpshv_int;
- unsigned int mpm_dmshv_int;
bool disable_reset_on_disconnect;
bool pnoc_errata_fix;
bool enable_lpm_on_dev_suspend;
@@ -227,51 +212,14 @@ struct msm_otg_platform_data {
bool emulation;
bool enable_streaming;
bool enable_axi_prefetch;
- bool enable_sdp_typec_current_limit;
bool vbus_low_as_hostmode;
};
-#define USB_CHG_BLOCK_ULPI 1
-
-#define USB_REQUEST_5V 1
-#define USB_REQUEST_9V 2
-/**
- * struct msm_usb_chg_info - MSM USB charger block details.
- * @chg_block_type: The type of charger block. QSCRATCH/ULPI.
- * @page_offset: USB charger register base may not be aligned to
- * PAGE_SIZE. The kernel driver aligns the base
- * address and use it for memory mapping. This
- * page_offset is used by user space to calaculate
- * the corret charger register base address.
- * @length: The length of the charger register address space.
- */
-struct msm_usb_chg_info {
- uint32_t chg_block_type;
- __kernel_off_t page_offset;
- size_t length;
-};
-
-/* Get the MSM USB charger block information */
-#define MSM_USB_EXT_CHG_INFO _IOW('M', 0, struct msm_usb_chg_info)
-
-/* Vote against USB hardware low power mode */
-#define MSM_USB_EXT_CHG_BLOCK_LPM _IOW('M', 1, int)
-
-/* To tell kernel about voltage being voted */
-#define MSM_USB_EXT_CHG_VOLTAGE_INFO _IOW('M', 2, int)
-
-/* To tell kernel about voltage request result */
-#define MSM_USB_EXT_CHG_RESULT _IOW('M', 3, int)
-
-/* To tell kernel whether charger connected is external charger or not */
-#define MSM_USB_EXT_CHG_TYPE _IOW('M', 4, int)
-
#define MSM_USB_BASE (motg->regs)
#define MSM_USB_PHY_CSR_BASE (motg->phy_csr_regs)
#define DRIVER_NAME "msm_otg"
-#define CHG_RECHECK_DELAY (jiffies + msecs_to_jiffies(2000))
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */
#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */
@@ -283,25 +231,11 @@ struct msm_usb_chg_info {
#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */
#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */
-#define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */
-#define USB_PHY_VDD_DIG_VOL_MIN 1045000 /* uV */
-#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
-
-#define USB_SUSPEND_DELAY_TIME (500 * HZ/1000) /* 500 msec */
-
#define USB_DEFAULT_SYSTEM_CLOCK 80000000 /* 80 MHz */
#define PM_QOS_SAMPLE_SEC 2
#define PM_QOS_THRESHOLD 400
-#define MICRO_5V 5000000
-#define MICRO_9V 9000000
-
-#define SDP_CURRENT_UA 500000
-#define CDP_CURRENT_UA 1500000
-#define DCP_CURRENT_UA 1500000
-#define HVDCP_CURRENT_UA 3000000
-
enum msm_otg_phy_reg_mode {
USB_PHY_REG_OFF,
USB_PHY_REG_ON,
@@ -321,27 +255,11 @@ module_param(lpm_disconnect_thresh, uint, 0644);
MODULE_PARM_DESC(lpm_disconnect_thresh,
"Delay before entering LPM on USB disconnect");
-static bool floated_charger_enable;
-module_param(floated_charger_enable, bool, 0644);
-MODULE_PARM_DESC(floated_charger_enable,
- "Whether to enable floated charger");
-
/* by default debugging is enabled */
static unsigned int enable_dbg_log = 1;
module_param(enable_dbg_log, uint, 0644);
MODULE_PARM_DESC(enable_dbg_log, "Debug buffer events");
-/* Max current to be drawn for HVDCP charger */
-static int hvdcp_max_current = IDEV_HVDCP_CHG_MAX;
-module_param(hvdcp_max_current, int, 0644);
-MODULE_PARM_DESC(hvdcp_max_current, "max current drawn for HVDCP charger");
-
-/* Max current to be drawn for DCP charger */
-static int dcp_max_current = IDEV_CHG_MAX;
-module_param(dcp_max_current, int, 0644);
-MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
-
-static DECLARE_COMPLETION(pmic_vbus_init);
static struct msm_otg *the_msm_otg;
static bool debug_bus_voting_enabled;
@@ -1403,8 +1321,7 @@ static int msm_otg_suspend(struct msm_otg *motg)
struct usb_bus *bus = phy->otg->host;
struct msm_otg_platform_data *pdata = motg->pdata;
int cnt;
- bool host_bus_suspend, device_bus_suspend, dcp, prop_charger;
- bool floated_charger, sm_work_busy;
+ bool host_bus_suspend, device_bus_suspend, sm_work_busy;
u32 cmd_val;
u32 portsc, config2;
u32 func_ctrl;
@@ -1431,13 +1348,6 @@ static int msm_otg_suspend(struct msm_otg *motg)
if (host_bus_suspend)
msm_otg_perf_vote_update(motg, false);
- /*
- * Allow putting PHY into SIDDQ with wall charger connected in
- * case of external charger detection.
- */
- dcp = (motg->chg_type == USB_DCP_CHARGER) && !motg->is_ext_chg_dcp;
- prop_charger = motg->chg_type == USB_NONCOMPLIANT_CHARGER;
- floated_charger = motg->chg_type == USB_FLOATED_CHARGER;
/* !BSV, but its handling is in progress by otg sm_work */
sm_work_busy = !test_bit(B_SESS_VLD, &motg->inputs) &&
@@ -1460,17 +1370,15 @@ static int msm_otg_suspend(struct msm_otg *motg)
/*
* Abort suspend when,
- * 1. charging detection in progress due to cable plug-in
- * 2. host mode activation in progress due to Micro-A cable insertion
- * 3. !BSV, but its handling is in progress by otg sm_work
+ * 1. host mode activation in progress due to Micro-A cable insertion
+ * 2. !BSV, but its handling is in progress by otg sm_work
* Don't abort suspend in case of dcp detected by PMIC
*/
- if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
- !dcp && !motg->is_ext_chg_dcp && !prop_charger &&
- !floated_charger) || sm_work_busy) {
+ if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend) ||
+ sm_work_busy) {
msm_otg_dbg_log_event(phy, "LPM ENTER ABORTED",
- motg->inputs, motg->chg_type);
+ motg->inputs, 0);
enable_irq(motg->irq);
if (motg->phy_irq)
enable_irq(motg->phy_irq);
@@ -1614,7 +1522,7 @@ static int msm_otg_suspend(struct msm_otg *motg)
motg->host_bus_suspend = host_bus_suspend;
motg->device_bus_suspend = device_bus_suspend;
- if (motg->caps & ALLOW_PHY_RETENTION && !device_bus_suspend && !dcp &&
+ if (motg->caps & ALLOW_PHY_RETENTION && !device_bus_suspend &&
(!host_bus_suspend || (motg->caps &
ALLOW_BUS_SUSPEND_WITHOUT_REWORK) ||
((motg->caps & ALLOW_HOST_PHY_RETENTION)
@@ -1626,8 +1534,7 @@ static int msm_otg_suspend(struct msm_otg *motg)
msm_otg_enter_phy_retention(motg);
motg->lpm_flags |= PHY_RETENTIONED;
}
- } else if (device_bus_suspend && !dcp &&
- (pdata->mpm_dpshv_int || pdata->mpm_dmshv_int)) {
+ } else if (device_bus_suspend) {
/* DP DM HV interrupts are used for bus resume from XO off */
msm_otg_enable_phy_hv_int(motg);
if (motg->caps & ALLOW_PHY_RETENTION && pdata->vddmin_gpio) {
@@ -1673,11 +1580,11 @@ static int msm_otg_suspend(struct msm_otg *motg)
}
if (motg->caps & ALLOW_PHY_POWER_COLLAPSE &&
- !host_bus_suspend && !dcp && !device_bus_suspend) {
+ !host_bus_suspend && !device_bus_suspend) {
msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
motg->lpm_flags |= PHY_PWR_COLLAPSED;
} else if (motg->caps & ALLOW_PHY_REGULATORS_LPM &&
- !host_bus_suspend && !device_bus_suspend && !dcp) {
+ !host_bus_suspend && !device_bus_suspend) {
msm_hsusb_ldo_enable(motg, USB_PHY_REG_LPM_ON);
motg->lpm_flags |= PHY_REGULATORS_LPM;
}
@@ -1700,15 +1607,6 @@ static int msm_otg_suspend(struct msm_otg *motg)
enable_irq_wake(motg->pdata->pmic_id_irq);
if (motg->ext_id_irq)
enable_irq_wake(motg->ext_id_irq);
- if (pdata->otg_control == OTG_PHY_CONTROL &&
- pdata->mpm_otgsessvld_int)
- msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 1);
- if ((host_bus_suspend || device_bus_suspend) &&
- pdata->mpm_dpshv_int)
- msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 1);
- if ((host_bus_suspend || device_bus_suspend) &&
- pdata->mpm_dmshv_int)
- msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 1);
}
if (bus)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
@@ -1875,15 +1773,6 @@ static int msm_otg_resume(struct msm_otg *motg)
disable_irq_wake(motg->pdata->pmic_id_irq);
if (motg->ext_id_irq)
disable_irq_wake(motg->ext_id_irq);
- if (pdata->otg_control == OTG_PHY_CONTROL &&
- pdata->mpm_otgsessvld_int)
- msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 0);
- if ((motg->host_bus_suspend || motg->device_bus_suspend) &&
- pdata->mpm_dpshv_int)
- msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 0);
- if ((motg->host_bus_suspend || motg->device_bus_suspend) &&
- pdata->mpm_dmshv_int)
- msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 0);
}
if (bus)
set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
@@ -1919,59 +1808,24 @@ static int msm_otg_resume(struct msm_otg *motg)
}
#endif
-static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+static void msm_otg_notify_chg_current(struct msm_otg *motg, unsigned int mA)
{
- if (!psy) {
- pr_err("No USB power supply registered!\n");
- return;
- }
-
- motg->host_mode = host_mode;
- power_supply_changed(psy);
-}
-
-static int msm_otg_notify_chg_type(struct msm_otg *motg)
-{
- static int charger_type;
- union power_supply_propval pval = {0};
-
- /*
- * TODO
- * Unify OTG driver charger types and power supply charger types
- */
- if (charger_type == motg->chg_type)
- return 0;
-
- if (motg->chg_type == USB_SDP_CHARGER)
- charger_type = POWER_SUPPLY_TYPE_USB;
- else if (motg->chg_type == USB_CDP_CHARGER)
- charger_type = POWER_SUPPLY_TYPE_USB_CDP;
- else if (motg->chg_type == USB_DCP_CHARGER ||
- motg->chg_type == USB_NONCOMPLIANT_CHARGER ||
- motg->chg_type == USB_FLOATED_CHARGER)
- charger_type = POWER_SUPPLY_TYPE_USB_DCP;
- else
- charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
-
- if (!psy) {
- pr_err("No USB power supply registered!\n");
- return -EINVAL;
- }
-
- pr_debug("setting usb power supply type %d\n", charger_type);
- msm_otg_dbg_log_event(&motg->phy, "SET USB PWR SUPPLY TYPE",
- motg->chg_type, charger_type);
- pval.intval = charger_type;
- power_supply_set_property(psy, POWER_SUPPLY_PROP_TYPE, &pval);
- return 0;
-}
-
-static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned int mA)
-{
+ struct usb_gadget *g = motg->phy.otg->gadget;
union power_supply_propval pval = {0};
bool enable;
int limit;
+ if (g && g->is_a_peripheral)
+ return;
+
+ dev_dbg(motg->phy.dev, "Requested curr from USB = %u\n", mA);
+
+ if (motg->cur_power == mA)
+ return;
+
+ dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
+ msm_otg_dbg_log_event(&motg->phy, "AVAIL CURR FROM USB", mA, 0);
+
if (!psy) {
dev_dbg(motg->phy.dev, "no usb power supply registered\n");
goto psy_error;
@@ -1997,82 +1851,12 @@ static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned int mA)
goto psy_error;
pval.intval = limit;
- if (power_supply_set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+ if (power_supply_set_property(psy, POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
&pval))
goto psy_error;
- power_supply_changed(psy);
- return 0;
-
psy_error:
dev_dbg(motg->phy.dev, "power supply error when setting property\n");
- return -ENXIO;
-}
-
-static void msm_otg_set_online_status(struct msm_otg *motg)
-{
- union power_supply_propval pval = {0};
-
- if (!psy) {
- dev_dbg(motg->phy.dev, "no usb power supply registered\n");
- return;
- }
-
- /* Set power supply online status to false */
- pval.intval = false;
- if (power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval))
- dev_dbg(motg->phy.dev, "error setting power supply property\n");
-}
-
-static void msm_otg_notify_charger(struct msm_otg *motg, unsigned int mA)
-{
- struct usb_gadget *g = motg->phy.otg->gadget;
- struct msm_otg_platform_data *pdata = motg->pdata;
-
- if (g && g->is_a_peripheral)
- return;
-
- dev_dbg(motg->phy.dev, "Requested curr from USB = %u, max-type-c:%u\n",
- mA, motg->typec_current_max);
- /* Save bc1.2 max_curr if type-c charger later moves to diff mode */
- motg->bc1p2_current_max = mA;
-
- /*
- * Limit type-c charger current to 500 for SDP charger to avoid more
- * current drawn than 500 with Hosts that don't support type C due to
- * non compliant type-c to standard A cables.
- */
- if (pdata->enable_sdp_typec_current_limit &&
- (motg->chg_type == USB_SDP_CHARGER) &&
- motg->typec_current_max > 500)
- motg->typec_current_max = 500;
-
- /* Override mA if type-c charger used (use hvdcp/bc1.2 if it is 500) */
- if (motg->typec_current_max > 500 && mA < motg->typec_current_max)
- mA = motg->typec_current_max;
-
- if (msm_otg_notify_chg_type(motg))
- dev_err(motg->phy.dev,
- "Failed notifying %d charger type to PMIC\n",
- motg->chg_type);
-
- /*
- * This condition will be true when usb cable is disconnected
- * during bootup before enumeration. Check charger type also
- * to avoid clearing online flag in case of valid charger.
- */
- if (motg->online && motg->cur_power == 0 && mA == 0 &&
- (motg->chg_type == USB_INVALID_CHARGER))
- msm_otg_set_online_status(motg);
-
- if (motg->cur_power == mA)
- return;
-
- dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
- msm_otg_dbg_log_event(&motg->phy, "AVAIL CURR FROM USB",
- mA, motg->chg_type);
-
- msm_otg_notify_power_supply(motg, mA);
motg->cur_power = mA;
}
@@ -2084,12 +1868,8 @@ static int msm_otg_set_power(struct usb_phy *phy, unsigned int mA)
/*
* Gadget driver uses set_power method to notify about the
* available current based on suspend/configured states.
- *
- * IDEV_CHG can be drawn irrespective of suspend/un-configured
- * states when CDP/ACA is connected.
*/
- if (motg->chg_type == USB_SDP_CHARGER)
- msm_otg_notify_charger(motg, mA);
+ msm_otg_notify_chg_current(motg, mA);
return 0;
}
@@ -2234,13 +2014,6 @@ static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
if (vbus_is_on == on)
return;
- if (motg->pdata->vbus_power) {
- ret = motg->pdata->vbus_power(on);
- if (!ret)
- vbus_is_on = on;
- return;
- }
-
if (!vbus_otg) {
pr_err("vbus_otg is NULL.");
return;
@@ -2253,7 +2026,6 @@ static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
* current from the source.
*/
if (on) {
- msm_otg_notify_host_mode(motg, on);
ret = regulator_enable(vbus_otg);
if (ret) {
pr_err("unable to enable vbus_otg\n");
@@ -2266,7 +2038,6 @@ static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
pr_err("unable to disable vbus_otg\n");
return;
}
- msm_otg_notify_host_mode(motg, on);
vbus_is_on = false;
}
}
@@ -2285,7 +2056,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
return -ENODEV;
}
- if (!motg->pdata->vbus_power && host) {
+ if (host) {
vbus_otg = devm_regulator_get(motg->phy.dev, "vbus_otg");
if (IS_ERR(vbus_otg)) {
msm_otg_dbg_log_event(&motg->phy,
@@ -2294,9 +2065,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
pr_err("Unable to get vbus_otg\n");
return PTR_ERR(vbus_otg);
}
- }
-
- if (!host) {
+ } else {
if (otg->state == OTG_STATE_A_HOST) {
msm_otg_start_host(otg, 0);
otg->host = NULL;
@@ -2501,357 +2270,6 @@ static bool msm_otg_read_phy_id_state(struct msm_otg *motg)
return true;
}
-static void msm_otg_chg_check_timer_func(unsigned long data)
-{
- struct msm_otg *motg = (struct msm_otg *) data;
- struct usb_otg *otg = motg->phy.otg;
-
- if (atomic_read(&motg->in_lpm) ||
- !test_bit(B_SESS_VLD, &motg->inputs) ||
- otg->state != OTG_STATE_B_PERIPHERAL ||
- otg->gadget->speed != USB_SPEED_UNKNOWN) {
- dev_dbg(otg->usb_phy->dev, "Nothing to do in chg_check_timer\n");
- return;
- }
-
- if ((readl_relaxed(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
- dev_dbg(otg->usb_phy->dev, "DCP is detected as SDP\n");
- msm_otg_dbg_log_event(&motg->phy, "DCP IS DETECTED AS SDP",
- otg->state, 0);
- set_bit(B_FALSE_SDP, &motg->inputs);
- queue_work(motg->otg_wq, &motg->sm_work);
- }
-}
-
-static bool msm_chg_check_secondary_det(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- u32 chg_det;
- bool ret = false;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- chg_det = ulpi_read(phy, 0x87);
- ret = chg_det & 1;
- break;
- default:
- break;
- }
- return ret;
-}
-
-static void msm_chg_enable_secondary_det(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- /*
- * Configure DM as current source, DP as current sink
- * and enable battery charging comparators.
- */
- ulpi_write(phy, 0x8, 0x85);
- ulpi_write(phy, 0x2, 0x85);
- ulpi_write(phy, 0x1, 0x85);
- break;
- default:
- break;
- }
-}
-
-static bool msm_chg_check_primary_det(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- u32 chg_det;
- bool ret = false;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- chg_det = ulpi_read(phy, 0x87);
- ret = chg_det & 1;
- /* Turn off VDP_SRC */
- ulpi_write(phy, 0x3, 0x86);
- msleep(20);
- break;
- default:
- break;
- }
- return ret;
-}
-
-static void msm_chg_enable_primary_det(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- /*
- * Configure DP as current source, DM as current sink
- * and enable battery charging comparators.
- */
- ulpi_write(phy, 0x2, 0x85);
- ulpi_write(phy, 0x1, 0x85);
- break;
- default:
- break;
- }
-}
-
-static bool msm_chg_check_dcd(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- u32 line_state;
- bool ret = false;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- line_state = ulpi_read(phy, 0x87);
- ret = line_state & 2;
- break;
- default:
- break;
- }
- return ret;
-}
-
-static void msm_chg_disable_dcd(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- ulpi_write(phy, 0x10, 0x86);
- break;
- case SNPS_FEMTO_PHY:
- ulpi_write(phy, 0x10, 0x86);
- /*
- * Disable the Rdm_down after
- * the DCD is completed.
- */
- ulpi_write(phy, 0x04, 0x0C);
- break;
- default:
- break;
- }
-}
-
-static void msm_chg_enable_dcd(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- /* Data contact detection enable */
- ulpi_write(phy, 0x10, 0x85);
- break;
- case SNPS_FEMTO_PHY:
- /*
- * Idp_src and Rdm_down are de-coupled
- * on Femto PHY. If Idp_src alone is
- * enabled, DCD timeout is observed with
- * wall charger. But a genuine DCD timeout
- * may be incorrectly interpreted. Also
- * BC1.2 compliance testers expect Rdm_down
- * to enabled during DCD. Enable Rdm_down
- * explicitly before enabling the DCD.
- */
- ulpi_write(phy, 0x04, 0x0B);
- ulpi_write(phy, 0x10, 0x85);
- break;
- default:
- break;
- }
-}
-
-static void msm_chg_block_on(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- u32 func_ctrl;
-
- /* put the controller in non-driving mode */
- func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
- func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
- func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
- ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- /* disable DP and DM pull down resistors */
- ulpi_write(phy, 0x6, 0xC);
- /* Clear charger detecting control bits */
- ulpi_write(phy, 0x1F, 0x86);
- /* Clear alt interrupt latch and enable bits */
- ulpi_write(phy, 0x1F, 0x92);
- ulpi_write(phy, 0x1F, 0x95);
- udelay(100);
- break;
- default:
- break;
- }
-}
-
-static void msm_chg_block_off(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- u32 func_ctrl;
-
- switch (motg->pdata->phy_type) {
- case SNPS_PICO_PHY:
- case SNPS_FEMTO_PHY:
- /* Clear charger detecting control bits */
- ulpi_write(phy, 0x3F, 0x86);
- /* Clear alt interrupt latch and enable bits */
- ulpi_write(phy, 0x1F, 0x92);
- ulpi_write(phy, 0x1F, 0x95);
- /* re-enable DP and DM pull down resistors */
- ulpi_write(phy, 0x6, 0xB);
- break;
- default:
- break;
- }
-
- /* put the controller in normal mode */
- func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
- func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
- func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
- ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
-}
-
-static const char *chg_to_string(enum usb_chg_type chg_type)
-{
- switch (chg_type) {
- case USB_SDP_CHARGER: return "USB_SDP_CHARGER";
- case USB_DCP_CHARGER: return "USB_DCP_CHARGER";
- case USB_CDP_CHARGER: return "USB_CDP_CHARGER";
- case USB_NONCOMPLIANT_CHARGER: return "USB_NONCOMPLIANT_CHARGER";
- case USB_FLOATED_CHARGER: return "USB_FLOATED_CHARGER";
- default: return "INVALID_CHARGER";
- }
-}
-
-#define MSM_CHG_DCD_TIMEOUT (750 * HZ/1000) /* 750 msec */
-#define MSM_CHG_DCD_POLL_TIME (50 * HZ/1000) /* 50 msec */
-#define MSM_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */
-#define MSM_CHG_SECONDARY_DET_TIME (50 * HZ/1000) /* TVDMSRC_ON */
-static void msm_chg_detect_work(struct work_struct *w)
-{
- struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
- struct usb_phy *phy = &motg->phy;
- bool is_dcd = false, tmout, vout;
- static bool dcd;
- u32 line_state, dm_vlgc;
- unsigned long delay;
-
- dev_dbg(phy->dev, "chg detection work\n");
- msm_otg_dbg_log_event(phy, "CHG DETECTION WORK",
- motg->chg_state, get_pm_runtime_counter(phy->dev));
-
- switch (motg->chg_state) {
- case USB_CHG_STATE_UNDEFINED:
- case USB_CHG_STATE_IN_PROGRESS:
- msm_chg_block_on(motg);
- msm_chg_enable_dcd(motg);
- motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
- motg->dcd_time = 0;
- delay = MSM_CHG_DCD_POLL_TIME;
- break;
- case USB_CHG_STATE_WAIT_FOR_DCD:
- is_dcd = msm_chg_check_dcd(motg);
- motg->dcd_time += MSM_CHG_DCD_POLL_TIME;
- tmout = motg->dcd_time >= MSM_CHG_DCD_TIMEOUT;
- if (is_dcd || tmout) {
- if (is_dcd)
- dcd = true;
- else
- dcd = false;
- msm_chg_disable_dcd(motg);
- msm_chg_enable_primary_det(motg);
- delay = MSM_CHG_PRIMARY_DET_TIME;
- motg->chg_state = USB_CHG_STATE_DCD_DONE;
- } else {
- delay = MSM_CHG_DCD_POLL_TIME;
- }
- break;
- case USB_CHG_STATE_DCD_DONE:
- vout = msm_chg_check_primary_det(motg);
- line_state = readl_relaxed(USB_PORTSC) & PORTSC_LS;
- dm_vlgc = line_state & PORTSC_LS_DM;
- if (vout && !dm_vlgc) { /* VDAT_REF < DM < VLGC */
- if (line_state) { /* DP > VLGC */
- motg->chg_type = USB_NONCOMPLIANT_CHARGER;
- motg->chg_state = USB_CHG_STATE_DETECTED;
- delay = 0;
- } else {
- msm_chg_enable_secondary_det(motg);
- delay = MSM_CHG_SECONDARY_DET_TIME;
- motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
- }
- } else { /* DM < VDAT_REF || DM > VLGC */
- if (line_state) /* DP > VLGC or/and DM > VLGC */
- motg->chg_type = USB_NONCOMPLIANT_CHARGER;
- else if (!dcd && floated_charger_enable)
- motg->chg_type = USB_FLOATED_CHARGER;
- else
- motg->chg_type = USB_SDP_CHARGER;
-
- motg->chg_state = USB_CHG_STATE_DETECTED;
- delay = 0;
- goto state_detected;
- }
- break;
- case USB_CHG_STATE_PRIMARY_DONE:
- vout = msm_chg_check_secondary_det(motg);
- if (vout)
- motg->chg_type = USB_DCP_CHARGER;
- else
- motg->chg_type = USB_CDP_CHARGER;
- motg->chg_state = USB_CHG_STATE_SECONDARY_DONE;
- /* fall through */
- case USB_CHG_STATE_SECONDARY_DONE:
- motg->chg_state = USB_CHG_STATE_DETECTED;
- case USB_CHG_STATE_DETECTED:
-state_detected:
- /*
- * Notify the charger type to power supply
- * owner as soon as we determine the charger.
- */
- if (motg->chg_type == USB_DCP_CHARGER && motg->ext_chg_opened) {
- init_completion(&motg->ext_chg_wait);
- motg->ext_chg_active = DEFAULT;
- }
- msm_otg_notify_chg_type(motg);
- msm_chg_block_off(motg);
-
- /* Enable VDP_SRC in case of DCP charger */
- if (motg->chg_type == USB_DCP_CHARGER)
- ulpi_write(phy, 0x2, 0x85);
-
- dev_dbg(phy->dev, "chg_type = %s\n",
- chg_to_string(motg->chg_type));
- msm_otg_dbg_log_event(phy, "CHG WORK PUT: CHG_TYPE",
- motg->chg_type, get_pm_runtime_counter(phy->dev));
- /* to match _get from sm_work before starting chg_det_work */
- pm_runtime_mark_last_busy(phy->dev);
- pm_runtime_put_autosuspend(phy->dev);
-
- queue_work(motg->otg_wq, &motg->sm_work);
- return;
- default:
- return;
- }
-
- msm_otg_dbg_log_event(phy, "CHG WORK: QUEUE", motg->chg_type, delay);
- queue_delayed_work(motg->otg_wq, &motg->chg_work, delay);
-}
-
-#define VBUS_INIT_TIMEOUT msecs_to_jiffies(5000)
-
/*
* We support OTG, Peripheral only and Host only configurations. In case
* of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
@@ -2863,7 +2281,6 @@ static void msm_otg_init_sm(struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata = motg->pdata;
u32 otgsc = readl_relaxed(USB_OTGSC);
- int ret;
switch (pdata->mode) {
case USB_OTG:
@@ -2903,21 +2320,6 @@ static void msm_otg_init_sm(struct msm_otg *motg)
else
clear_bit(ID, &motg->inputs);
}
- /*
- * VBUS initial state is reported after PMIC
- * driver initialization. Wait for it.
- */
- ret = wait_for_completion_timeout(&pmic_vbus_init,
- VBUS_INIT_TIMEOUT);
- if (!ret) {
- dev_dbg(motg->phy.dev, "%s: timeout waiting for PMIC VBUS\n",
- __func__);
- msm_otg_dbg_log_event(&motg->phy,
- "PMIC VBUS WAIT TMOUT", motg->inputs,
- motg->phy.otg->state);
- clear_bit(B_SESS_VLD, &motg->inputs);
- pmic_vbus_init.done = 1;
- }
}
break;
case USB_HOST:
@@ -2930,22 +2332,6 @@ static void msm_otg_init_sm(struct msm_otg *motg)
set_bit(B_SESS_VLD, &motg->inputs);
else
clear_bit(B_SESS_VLD, &motg->inputs);
- } else if (pdata->otg_control == OTG_PMIC_CONTROL) {
- /*
- * VBUS initial state is reported after PMIC
- * driver initialization. Wait for it.
- */
- ret = wait_for_completion_timeout(&pmic_vbus_init,
- VBUS_INIT_TIMEOUT);
- if (!ret) {
- dev_dbg(motg->phy.dev, "%s: timeout waiting for PMIC VBUS\n",
- __func__);
- msm_otg_dbg_log_event(&motg->phy,
- "PMIC VBUS WAIT TMOUT", motg->inputs,
- motg->phy.otg->state);
- clear_bit(B_SESS_VLD, &motg->inputs);
- pmic_vbus_init.done = 1;
- }
} else if (pdata->otg_control == OTG_USER_CONTROL) {
set_bit(ID, &motg->inputs);
set_bit(B_SESS_VLD, &motg->inputs);
@@ -2960,53 +2346,12 @@ static void msm_otg_init_sm(struct msm_otg *motg)
USB_ID_GROUND;
}
-static void msm_otg_wait_for_ext_chg_done(struct msm_otg *motg)
-{
- struct usb_phy *phy = &motg->phy;
- unsigned long t;
-
- /*
- * Defer next cable connect event till external charger
- * detection is completed.
- */
-
- if (motg->ext_chg_active == ACTIVE) {
-
-do_wait:
- pr_debug("before msm_otg ext chg wait\n");
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: WAIT", 0, 0);
-
- t = wait_for_completion_timeout(&motg->ext_chg_wait,
- msecs_to_jiffies(3000));
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: DONE", t, 0);
-
- if (!t)
- pr_err("msm_otg ext chg wait timeout\n");
- else if (motg->ext_chg_active == ACTIVE)
- goto do_wait;
- else
- pr_debug("msm_otg ext chg wait done\n");
- }
-
- if (motg->ext_chg_opened) {
- if (phy->flags & ENABLE_DP_MANUAL_PULLUP) {
- ulpi_write(phy, ULPI_MISC_A_VBUSVLDEXT |
- ULPI_MISC_A_VBUSVLDEXTSEL,
- ULPI_CLR(ULPI_MISC_A));
- }
- /* clear charging register bits */
- ulpi_write(phy, 0x3F, 0x86);
- /* re-enable DP and DM pull-down resistors*/
- ulpi_write(phy, 0x6, 0xB);
- }
-}
-
static void msm_otg_sm_work(struct work_struct *w)
{
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
struct usb_otg *otg = motg->phy.otg;
struct device *dev = otg->usb_phy->dev;
- bool work = 0, dcp;
+ bool work = 0;
int ret;
pr_debug("%s work\n", usb_otg_state_string(otg->state));
@@ -3054,6 +2399,12 @@ static void msm_otg_sm_work(struct work_struct *w)
pr_debug("!id\n");
msm_otg_dbg_log_event(&motg->phy, "!ID",
motg->inputs, otg->state);
+ if (!otg->host) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "SM WORK: Host Not Set",
+ otg->state, motg->inputs);
+ break;
+ }
msm_otg_start_host(otg, 1);
otg->state = OTG_STATE_A_HOST;
@@ -3061,106 +2412,32 @@ static void msm_otg_sm_work(struct work_struct *w)
pr_debug("b_sess_vld\n");
msm_otg_dbg_log_event(&motg->phy, "B_SESS_VLD",
motg->inputs, otg->state);
- switch (motg->chg_state) {
- case USB_CHG_STATE_UNDEFINED:
- /* put at the end of chg_det or disconnect */
- pm_runtime_get_sync(otg->usb_phy->dev);
- msm_otg_dbg_log_event(&motg->phy, "PM CHG GET",
- get_pm_runtime_counter(dev), 0);
- motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
- msm_chg_detect_work(&motg->chg_work.work);
+ if (!otg->gadget) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "SM WORK: Gadget Not Set",
+ otg->state, motg->inputs);
break;
- case USB_CHG_STATE_DETECTED:
- switch (motg->chg_type) {
- case USB_DCP_CHARGER:
- /* fall through */
- case USB_NONCOMPLIANT_CHARGER:
- msm_otg_notify_charger(motg,
- dcp_max_current);
- if (!motg->is_ext_chg_dcp)
- otg->state =
- OTG_STATE_B_CHARGER;
- break;
- case USB_FLOATED_CHARGER:
- msm_otg_notify_charger(motg,
- IDEV_CHG_MAX);
- otg->state = OTG_STATE_B_CHARGER;
- break;
- case USB_CDP_CHARGER:
- msm_otg_notify_charger(motg,
- IDEV_CHG_MAX);
- /* fall through */
- case USB_SDP_CHARGER:
- pm_runtime_get_sync(otg->usb_phy->dev);
- msm_otg_start_peripheral(otg, 1);
- otg->state =
- OTG_STATE_B_PERIPHERAL;
- mod_timer(&motg->chg_check_timer,
- CHG_RECHECK_DELAY);
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- } else {
- pr_debug("chg_work cancel");
- msm_otg_dbg_log_event(&motg->phy, "CHG_WORK CANCEL",
- motg->inputs, otg->state);
- del_timer_sync(&motg->chg_check_timer);
- clear_bit(B_FALSE_SDP, &motg->inputs);
- cancel_delayed_work_sync(&motg->chg_work);
- /*
- * Find out whether chg_w couldn't start or finished.
- * In both the cases, runtime ref_count vote is missing
- */
- if (motg->chg_state == USB_CHG_STATE_UNDEFINED ||
- motg->chg_state == USB_CHG_STATE_DETECTED) {
- msm_otg_dbg_log_event(&motg->phy, "RT !CHG GET",
- get_pm_runtime_counter(otg->usb_phy->dev), 0);
- pm_runtime_get_sync(dev);
}
- dcp = (motg->chg_type == USB_DCP_CHARGER);
- motg->chg_state = USB_CHG_STATE_UNDEFINED;
- motg->chg_type = USB_INVALID_CHARGER;
- msm_otg_notify_charger(motg, 0);
- if (dcp) {
- if (motg->ext_chg_active == DEFAULT)
- motg->ext_chg_active = INACTIVE;
- msm_otg_wait_for_ext_chg_done(motg);
- /* Turn off VDP_SRC */
- ulpi_write(otg->usb_phy, 0x2, 0x86);
- }
- msm_chg_block_off(motg);
- msm_otg_dbg_log_event(&motg->phy, "RT: CHG A PUT",
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_start_peripheral(otg, 1);
+ otg->state = OTG_STATE_B_PERIPHERAL;
+ } else {
+ pr_debug("Cable disconnected\n");
+ msm_otg_dbg_log_event(&motg->phy, "RT: Cable DISC",
get_pm_runtime_counter(otg->usb_phy->dev), 0);
- /* Delay used only if autosuspend enabled */
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
+
+ msm_otg_notify_chg_current(motg, 0);
}
break;
case OTG_STATE_B_PERIPHERAL:
- if (test_bit(B_SESS_VLD, &motg->inputs) &&
- test_bit(B_FALSE_SDP, &motg->inputs)) {
- pr_debug("B_FALSE_SDP\n");
- msm_otg_start_peripheral(otg, 0);
- motg->chg_type = USB_DCP_CHARGER;
- clear_bit(B_FALSE_SDP, &motg->inputs);
- otg->state = OTG_STATE_B_IDLE;
- msm_otg_dbg_log_event(&motg->phy, "B_FALSE_SDP PUT",
- get_pm_runtime_counter(dev), motg->inputs);
- pm_runtime_put_sync(dev);
- /* schedule work to update charging current */
- work = 1;
- } else if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ if (!test_bit(B_SESS_VLD, &motg->inputs)) {
msm_otg_start_peripheral(otg, 0);
msm_otg_dbg_log_event(&motg->phy, "RT PM: B_PERI A PUT",
get_pm_runtime_counter(dev), 0);
/* _put for _get done on cable connect in B_IDLE */
- pm_runtime_put_noidle(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
/* Schedule work to finish cable disconnect processing*/
otg->state = OTG_STATE_B_IDLE;
work = 1;
@@ -3190,17 +2467,6 @@ static void msm_otg_sm_work(struct work_struct *w)
pm_runtime_get_sync(dev);
}
break;
-
- case OTG_STATE_B_CHARGER:
- if (test_bit(B_SESS_VLD, &motg->inputs)) {
- pr_debug("BSV set again\n");
- msm_otg_dbg_log_event(&motg->phy, "BSV SET AGAIN",
- motg->inputs, otg->state);
- } else if (!test_bit(B_SESS_VLD, &motg->inputs)) {
- otg->state = OTG_STATE_B_IDLE;
- work = 1;
- }
- break;
case OTG_STATE_A_HOST:
if (test_bit(ID, &motg->inputs)) {
msm_otg_start_host(otg, 0);
@@ -3282,7 +2548,6 @@ static irqreturn_t msm_otg_irq(int irq, void *data)
static void msm_otg_set_vbus_state(int online)
{
struct msm_otg *motg = the_msm_otg;
- static bool init;
motg->vbus_state = online;
@@ -3290,67 +2555,19 @@ static void msm_otg_set_vbus_state(int online)
return;
if (online) {
- pr_debug("PMIC: BSV set\n");
- msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV SET",
- init, motg->inputs);
- if (test_and_set_bit(B_SESS_VLD, &motg->inputs) && init)
+ pr_debug("EXTCON: BSV set\n");
+ msm_otg_dbg_log_event(&motg->phy, "EXTCON: BSV SET",
+ motg->inputs, 0);
+ if (test_and_set_bit(B_SESS_VLD, &motg->inputs))
return;
} else {
- pr_debug("PMIC: BSV clear\n");
- msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV CLEAR",
- init, motg->inputs);
- motg->is_ext_chg_dcp = false;
- if (!test_and_clear_bit(B_SESS_VLD, &motg->inputs) && init)
+ pr_debug("EXTCON: BSV clear\n");
+ msm_otg_dbg_log_event(&motg->phy, "EXTCON: BSV CLEAR",
+ motg->inputs, 0);
+ if (!test_and_clear_bit(B_SESS_VLD, &motg->inputs))
return;
}
- /* do not queue state m/c work if id is grounded */
- if (!test_bit(ID, &motg->inputs) &&
- !motg->pdata->vbus_low_as_hostmode) {
- /*
- * state machine work waits for initial VBUS
- * completion in UNDEFINED state. Process
- * the initial VBUS event in ID_GND state.
- */
- if (init)
- return;
- }
-
- if (!init) {
- init = true;
- if (pmic_vbus_init.done &&
- test_bit(B_SESS_VLD, &motg->inputs)) {
- pr_debug("PMIC: BSV came late\n");
- msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV CAME LATE",
- init, motg->inputs);
- goto out;
- }
-
- if (motg->pdata->vbus_low_as_hostmode &&
- !test_bit(B_SESS_VLD, &motg->inputs)) {
- motg->id_state = USB_ID_GROUND;
- clear_bit(ID, &motg->inputs);
- }
- complete(&pmic_vbus_init);
- pr_debug("PMIC: BSV init complete\n");
- msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV INIT COMPLETE",
- init, motg->inputs);
- return;
- }
-
-out:
- if (motg->is_ext_chg_dcp) {
- if (test_bit(B_SESS_VLD, &motg->inputs)) {
- msm_otg_notify_charger(motg, IDEV_CHG_MAX);
- } else {
- motg->is_ext_chg_dcp = false;
- motg->chg_state = USB_CHG_STATE_UNDEFINED;
- motg->chg_type = USB_INVALID_CHARGER;
- msm_otg_notify_charger(motg, 0);
- }
- return;
- }
-
msm_otg_dbg_log_event(&motg->phy, "CHECK VBUS EVENT DURING SUSPEND",
atomic_read(&motg->pm_suspended),
motg->sm_work_pending);
@@ -3498,11 +2715,11 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
goto out;
}
- if (!strncmp(buf, "host", 4)) {
+ if (!strcmp(buf, "host")) {
req_mode = USB_HOST;
- } else if (!strncmp(buf, "peripheral", 10)) {
+ } else if (!strcmp(buf, "peripheral")) {
req_mode = USB_PERIPHERAL;
- } else if (!strncmp(buf, "none", 4)) {
+ } else if (!strcmp(buf, "none")) {
req_mode = USB_NONE;
} else {
status = -EINVAL;
@@ -3584,26 +2801,6 @@ const struct file_operations msm_otg_state_fops = {
.release = single_release,
};
-static int msm_otg_show_chg_type(struct seq_file *s, void *unused)
-{
- struct msm_otg *motg = s->private;
-
- seq_printf(s, "%s\n", chg_to_string(motg->chg_type));
- return 0;
-}
-
-static int msm_otg_chg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, msm_otg_show_chg_type, inode->i_private);
-}
-
-const struct file_operations msm_otg_chg_fops = {
- .open = msm_otg_chg_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int msm_otg_bus_show(struct seq_file *s, void *unused)
{
if (debug_bus_voting_enabled)
@@ -3631,7 +2828,7 @@ static ssize_t msm_otg_bus_write(struct file *file, const char __user *ubuf,
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
- if (!strncmp(buf, "enable", 6)) {
+ if (!strcmp(buf, "enable")) {
/* Do not vote here. Let OTG statemachine decide when to vote */
debug_bus_voting_enabled = true;
} else {
@@ -3783,14 +2980,6 @@ static int msm_otg_debugfs_init(struct msm_otg *motg)
}
}
- msm_otg_dentry = debugfs_create_file("chg_type", 0444, msm_otg_dbg_root,
- motg, &msm_otg_chg_fops);
-
- if (!msm_otg_dentry) {
- debugfs_remove_recursive(msm_otg_dbg_root);
- return -ENODEV;
- }
-
msm_otg_dentry = debugfs_create_file("bus_voting", 0644,
msm_otg_dbg_root, motg, &msm_otg_bus_fops);
@@ -3934,6 +3123,7 @@ static struct platform_device *msm_otg_add_pdev(
goto error;
}
+ arch_setup_dma_ops(&pdev->dev, 0, DMA_BIT_MASK(32), NULL, false);
retval = platform_device_add(pdev);
if (retval)
goto error;
@@ -3956,9 +3146,9 @@ static int msm_otg_setup_devices(struct platform_device *ofdev,
if (!init) {
if (gadget_pdev) {
- platform_device_unregister(gadget_pdev);
device_remove_file(&gadget_pdev->dev,
&dev_attr_perf_mode);
+ platform_device_unregister(gadget_pdev);
}
if (host_pdev)
platform_device_unregister(host_pdev);
@@ -3997,216 +3187,6 @@ static int msm_otg_setup_devices(struct platform_device *ofdev,
return retval;
}
-static int msm_otg_ext_chg_open(struct inode *inode, struct file *file)
-{
- struct msm_otg *motg = the_msm_otg;
-
- pr_debug("msm_otg ext chg open\n");
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: OPEN",
- motg->inputs, motg->phy.otg->state);
-
- motg->ext_chg_opened = true;
- file->private_data = (void *)motg;
- return 0;
-}
-
-static long
-msm_otg_ext_chg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct msm_otg *motg = file->private_data;
- struct msm_usb_chg_info info = {0};
- int ret = 0, val;
-
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: IOCTL", cmd, 0);
- switch (cmd) {
- case MSM_USB_EXT_CHG_INFO:
- info.chg_block_type = USB_CHG_BLOCK_ULPI;
- info.page_offset = motg->io_res->start & ~PAGE_MASK;
- /* mmap() works on PAGE granularity */
- info.length = PAGE_SIZE;
-
- if (copy_to_user((void __user *)arg, &info, sizeof(info))) {
- pr_err("%s: copy to user failed\n\n", __func__);
- ret = -EFAULT;
- }
- break;
- case MSM_USB_EXT_CHG_BLOCK_LPM:
- if (get_user(val, (int __user *)arg)) {
- pr_err("%s: get_user failed\n\n", __func__);
- ret = -EFAULT;
- break;
- }
- pr_debug("%s: LPM block request %d\n", __func__, val);
- msm_otg_dbg_log_event(&motg->phy, "LPM BLOCK REQ", val, 0);
- if (val) { /* block LPM */
- if (motg->chg_type == USB_DCP_CHARGER) {
- motg->ext_chg_active = ACTIVE;
- msm_otg_dbg_log_event(&motg->phy,
- "PM RUNTIME: EXT_CHG GET",
- get_pm_runtime_counter(motg->phy.dev), 0);
- pm_runtime_get_sync(motg->phy.dev);
- } else {
- motg->ext_chg_active = INACTIVE;
- complete(&motg->ext_chg_wait);
- ret = -ENODEV;
- }
- } else {
- motg->ext_chg_active = INACTIVE;
- complete(&motg->ext_chg_wait);
- /*
- * If usb cable is disconnected and then userspace
- * calls ioctl to unblock low power mode, make sure
- * otg_sm work for usb disconnect is processed first
- * followed by decrementing the PM usage counters.
- */
- flush_work(&motg->sm_work);
- msm_otg_dbg_log_event(&motg->phy,
- "PM RUNTIME: EXT_CHG PUT",
- get_pm_runtime_counter(motg->phy.dev), 0);
- pm_runtime_put_sync(motg->phy.dev);
- }
- break;
- case MSM_USB_EXT_CHG_VOLTAGE_INFO:
- if (get_user(val, (int __user *)arg)) {
- pr_err("%s: get_user failed\n\n", __func__);
- ret = -EFAULT;
- break;
- }
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
-
- if (val == USB_REQUEST_5V)
- pr_debug("%s:voting 5V voltage request\n", __func__);
- else if (val == USB_REQUEST_9V)
- pr_debug("%s:voting 9V voltage request\n", __func__);
- break;
- case MSM_USB_EXT_CHG_RESULT:
- if (get_user(val, (int __user *)arg)) {
- pr_err("%s: get_user failed\n\n", __func__);
- ret = -EFAULT;
- break;
- }
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
-
- if (!val)
- pr_debug("%s:voltage request successful\n", __func__);
- else
- pr_debug("%s:voltage request failed\n", __func__);
- break;
- case MSM_USB_EXT_CHG_TYPE:
- if (get_user(val, (int __user *)arg)) {
- pr_err("%s: get_user failed\n\n", __func__);
- ret = -EFAULT;
- break;
- }
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
-
- if (val)
- pr_debug("%s:charger is external charger\n", __func__);
- else
- pr_debug("%s:charger is not ext charger\n", __func__);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int msm_otg_ext_chg_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct msm_otg *motg = file->private_data;
- unsigned long vsize = vma->vm_end - vma->vm_start;
- int ret;
-
- if (vma->vm_pgoff || vsize > PAGE_SIZE)
- return -EINVAL;
-
- vma->vm_pgoff = __phys_to_pfn(motg->io_res->start);
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
- vsize, vma->vm_page_prot);
- if (ret < 0) {
- pr_err("%s: failed with return val %d\n", __func__, ret);
- return ret;
- }
-
- return 0;
-}
-
-static int msm_otg_ext_chg_release(struct inode *inode, struct file *file)
-{
- struct msm_otg *motg = file->private_data;
-
- pr_debug("msm_otg ext chg release\n");
- msm_otg_dbg_log_event(&motg->phy, "EXT CHG: RELEASE",
- motg->inputs, motg->phy.otg->state);
-
- motg->ext_chg_opened = false;
-
- return 0;
-}
-
-static const struct file_operations msm_otg_ext_chg_fops = {
- .owner = THIS_MODULE,
- .open = msm_otg_ext_chg_open,
- .unlocked_ioctl = msm_otg_ext_chg_ioctl,
- .mmap = msm_otg_ext_chg_mmap,
- .release = msm_otg_ext_chg_release,
-};
-
-static int msm_otg_setup_ext_chg_cdev(struct msm_otg *motg)
-{
- int ret;
-
- if (motg->pdata->enable_sec_phy || motg->pdata->mode == USB_HOST ||
- motg->pdata->otg_control != OTG_PMIC_CONTROL) {
- pr_debug("usb ext chg is not supported by msm otg\n");
- return -ENODEV;
- }
-
- ret = alloc_chrdev_region(&motg->ext_chg_dev, 0, 1, "usb_ext_chg");
- if (ret < 0) {
- pr_err("Fail to allocate usb ext char dev region\n");
- return ret;
- }
- motg->ext_chg_class = class_create(THIS_MODULE, "msm_ext_chg");
- if (ret < 0) {
- pr_err("Fail to create usb ext chg class\n");
- goto unreg_chrdev;
- }
- cdev_init(&motg->ext_chg_cdev, &msm_otg_ext_chg_fops);
- motg->ext_chg_cdev.owner = THIS_MODULE;
-
- ret = cdev_add(&motg->ext_chg_cdev, motg->ext_chg_dev, 1);
- if (ret < 0) {
- pr_err("Fail to add usb ext chg cdev\n");
- goto destroy_class;
- }
- motg->ext_chg_device = device_create(motg->ext_chg_class,
- NULL, motg->ext_chg_dev, NULL,
- "usb_ext_chg");
- if (IS_ERR(motg->ext_chg_device)) {
- pr_err("Fail to create usb ext chg device\n");
- ret = PTR_ERR(motg->ext_chg_device);
- motg->ext_chg_device = NULL;
- goto del_cdev;
- }
-
- init_completion(&motg->ext_chg_wait);
- pr_debug("msm otg ext chg cdev setup success\n");
- return 0;
-
-del_cdev:
- cdev_del(&motg->ext_chg_cdev);
-destroy_class:
- class_destroy(motg->ext_chg_class);
-unreg_chrdev:
- unregister_chrdev_region(motg->ext_chg_dev, 1);
-
- return ret;
-}
-
static ssize_t dpdm_pulldown_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -4241,14 +3221,7 @@ static DEVICE_ATTR(dpdm_pulldown_enable, 0644,
static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
- struct msm_otg *motg = container_of(nb, struct msm_otg, vbus_nb);
-
- if (event)
- set_bit(B_SESS_VLD, &motg->inputs);
- else
- clear_bit(B_SESS_VLD, &motg->inputs);
-
- queue_work(motg->otg_wq, &motg->sm_work);
+ msm_otg_set_vbus_state(!!event);
return NOTIFY_DONE;
}
@@ -4259,11 +3232,11 @@ static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
struct msm_otg *motg = container_of(nb, struct msm_otg, id_nb);
if (event)
- clear_bit(ID, &motg->inputs);
+ motg->id_state = USB_ID_GROUND;
else
- set_bit(ID, &motg->inputs);
+ motg->id_state = USB_ID_FLOAT;
- queue_work(motg->otg_wq, &motg->sm_work);
+ msm_id_status_w(&motg->id_status_work.work);
return NOTIFY_DONE;
}
@@ -4368,10 +3341,6 @@ struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
of_property_read_u32(node, "qcom,hsusb-log2-itc",
&pdata->log2_itc);
- of_property_read_u32(node, "qcom,hsusb-otg-mpm-dpsehv-int",
- &pdata->mpm_dpshv_int);
- of_property_read_u32(node, "qcom,hsusb-otg-mpm-dmsehv-int",
- &pdata->mpm_dmshv_int);
pdata->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
if (pdata->pmic_id_irq < 0)
pdata->pmic_id_irq = 0;
@@ -4421,8 +3390,6 @@ struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
pdata->enable_axi_prefetch = of_property_read_bool(node,
"qcom,axi-prefetch-enable");
- pdata->enable_sdp_typec_current_limit = of_property_read_bool(node,
- "qcom,enable-sdp-typec-current-limit");
pdata->vbus_low_as_hostmode = of_property_read_bool(node,
"qcom,vbus-low-as-hostmode");
return pdata;
@@ -4866,11 +3833,8 @@ static int msm_otg_probe(struct platform_device *pdev)
motg->id_state = USB_ID_FLOAT;
set_bit(ID, &motg->inputs);
INIT_WORK(&motg->sm_work, msm_otg_sm_work);
- INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
INIT_DELAYED_WORK(&motg->id_status_work, msm_id_status_w);
INIT_DELAYED_WORK(&motg->perf_vote_work, msm_otg_perf_vote_work);
- setup_timer(&motg->chg_check_timer, msm_otg_chg_check_timer_func,
- (unsigned long) motg);
motg->otg_wq = alloc_ordered_workqueue("k_otg", 0);
if (!motg->otg_wq) {
pr_err("%s: Unable to create workqueue otg_wq\n",
@@ -4878,7 +3842,7 @@ static int msm_otg_probe(struct platform_device *pdev)
goto disable_core_clk;
}
- ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
+ ret = devm_request_irq(&pdev->dev, motg->irq, msm_otg_irq, IRQF_SHARED,
"msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed\n");
@@ -4903,30 +3867,23 @@ static int msm_otg_probe(struct platform_device *pdev)
udelay(200);
writeb_relaxed(0x0, USB2_PHY_USB_PHY_IRQ_CMD);
- ret = request_irq(motg->phy_irq, msm_otg_phy_irq_handler,
- IRQF_TRIGGER_RISING, "msm_otg_phy_irq", motg);
+ ret = devm_request_irq(&pdev->dev, motg->phy_irq,
+ msm_otg_phy_irq_handler, IRQF_TRIGGER_RISING,
+ "msm_otg_phy_irq", motg);
if (ret < 0) {
dev_err(&pdev->dev, "phy_irq request fail %d\n", ret);
- goto free_irq;
+ goto destroy_wq;
}
}
- ret = request_irq(motg->async_irq, msm_otg_irq,
+ ret = devm_request_irq(&pdev->dev, motg->async_irq, msm_otg_irq,
IRQF_TRIGGER_RISING, "msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
- goto free_phy_irq;
+ goto destroy_wq;
}
disable_irq(motg->async_irq);
- if (pdata->otg_control == OTG_PHY_CONTROL && pdata->mpm_otgsessvld_int)
- msm_mpm_enable_pin(pdata->mpm_otgsessvld_int, 1);
-
- if (pdata->mpm_dpshv_int)
- msm_mpm_enable_pin(pdata->mpm_dpshv_int, 1);
- if (pdata->mpm_dmshv_int)
- msm_mpm_enable_pin(pdata->mpm_dmshv_int, 1);
-
phy->init = msm_otg_reset;
phy->set_power = msm_otg_set_power;
phy->set_suspend = msm_otg_set_suspend;
@@ -4946,7 +3903,7 @@ static int msm_otg_probe(struct platform_device *pdev)
ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
if (ret) {
dev_err(&pdev->dev, "usb_add_phy failed\n");
- goto free_async_irq;
+ goto destroy_wq;
}
ret = usb_phy_regulator_init(motg);
@@ -4961,12 +3918,12 @@ static int msm_otg_probe(struct platform_device *pdev)
if (gpio_is_valid(motg->pdata->usb_id_gpio)) {
/* usb_id_gpio request */
- ret = gpio_request(motg->pdata->usb_id_gpio,
- "USB_ID_GPIO");
+ ret = devm_gpio_request(&pdev->dev,
+ motg->pdata->usb_id_gpio,
+ "USB_ID_GPIO");
if (ret < 0) {
dev_err(&pdev->dev, "gpio req failed for id\n");
- motg->pdata->usb_id_gpio = 0;
- goto remove_phy;
+ goto phy_reg_deinit;
}
/*
@@ -4983,7 +3940,7 @@ static int msm_otg_probe(struct platform_device *pdev)
"qcom,hub-reset-gpio");
if (ret < 0) {
dev_err(&pdev->dev, "gpio req failed for hub reset\n");
- goto remove_phy;
+ goto phy_reg_deinit;
}
gpio_direction_output(
motg->pdata->hub_reset_gpio, 1);
@@ -4995,7 +3952,7 @@ static int msm_otg_probe(struct platform_device *pdev)
"qcom,sw-sel-gpio");
if (ret < 0) {
dev_err(&pdev->dev, "gpio req failed for switch sel\n");
- goto remove_phy;
+ goto phy_reg_deinit;
}
if (gpio_get_value(motg->pdata->usb_id_gpio))
gpio_direction_input(
@@ -5015,14 +3972,14 @@ static int msm_otg_probe(struct platform_device *pdev)
}
if (id_irq) {
- ret = request_irq(id_irq,
+ ret = devm_request_irq(&pdev->dev, id_irq,
msm_id_irq,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed for ID\n");
- goto remove_phy;
+ goto phy_reg_deinit;
}
} else {
/* PMIC does USB ID detection and notifies through
@@ -5049,8 +4006,7 @@ static int msm_otg_probe(struct platform_device *pdev)
motg->pdata->enable_phy_id_pullup)
motg->caps = ALLOW_PHY_RETENTION | ALLOW_PHY_REGULATORS_LPM;
- if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
- motg->caps |= ALLOW_HOST_PHY_RETENTION;
+ motg->caps |= ALLOW_HOST_PHY_RETENTION;
device_create_file(&pdev->dev, &dev_attr_dpdm_pulldown_enable);
@@ -5079,10 +4035,6 @@ static int msm_otg_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(&pdev->dev);
}
- ret = msm_otg_setup_ext_chg_cdev(motg);
- if (ret)
- dev_dbg(&pdev->dev, "fail to setup cdev\n");
-
if (pdev->dev.of_node) {
ret = msm_otg_setup_devices(pdev, pdata->mode, true);
if (ret) {
@@ -5103,21 +4055,13 @@ static int msm_otg_probe(struct platform_device *pdev)
if (ret)
goto put_psy;
- if (motg->extcon_vbus) {
- ret = extcon_get_cable_state_(motg->extcon_vbus, EXTCON_USB);
- if (ret)
- set_bit(B_SESS_VLD, &motg->inputs);
- else
- clear_bit(B_SESS_VLD, &motg->inputs);
- }
+ if (motg->extcon_vbus && extcon_get_cable_state_(motg->extcon_vbus,
+ EXTCON_USB))
+ msm_otg_vbus_notifier(&motg->vbus_nb, true, motg->extcon_vbus);
- if (motg->extcon_id) {
- ret = extcon_get_cable_state_(motg->extcon_id, EXTCON_USB_HOST);
- if (ret)
- clear_bit(ID, &motg->inputs);
- else
- set_bit(ID, &motg->inputs);
- }
+ if (motg->extcon_id && extcon_get_cable_state_(motg->extcon_id,
+ EXTCON_USB_HOST))
+ msm_otg_id_notifier(&motg->id_nb, true, motg->extcon_id);
if (gpio_is_valid(motg->pdata->hub_reset_gpio)) {
ret = devm_gpio_request(&pdev->dev,
@@ -5164,21 +4108,12 @@ static int msm_otg_probe(struct platform_device *pdev)
if (pdev->dev.of_node)
msm_otg_setup_devices(pdev, motg->pdata->mode, false);
remove_cdev:
- if (!motg->ext_chg_device) {
- device_destroy(motg->ext_chg_class, motg->ext_chg_dev);
- cdev_del(&motg->ext_chg_cdev);
- class_destroy(motg->ext_chg_class);
- unregister_chrdev_region(motg->ext_chg_dev, 1);
- }
+ pm_runtime_disable(&pdev->dev);
+ device_remove_file(&pdev->dev, &dev_attr_dpdm_pulldown_enable);
+phy_reg_deinit:
+ devm_regulator_unregister(motg->phy.dev, motg->dpdm_rdev);
remove_phy:
usb_remove_phy(&motg->phy);
-free_async_irq:
- free_irq(motg->async_irq, motg);
-free_phy_irq:
- if (motg->phy_irq)
- free_irq(motg->phy_irq, motg);
-free_irq:
- free_irq(motg->irq, motg);
destroy_wq:
destroy_workqueue(motg->otg_wq);
disable_core_clk:
@@ -5244,19 +4179,11 @@ static int msm_otg_remove(struct platform_device *pdev)
extcon_unregister_notifier(motg->extcon_vbus, EXTCON_USB,
&motg->vbus_nb);
- if (!motg->ext_chg_device) {
- device_destroy(motg->ext_chg_class, motg->ext_chg_dev);
- cdev_del(&motg->ext_chg_cdev);
- class_destroy(motg->ext_chg_class);
- unregister_chrdev_region(motg->ext_chg_dev, 1);
- }
-
if (pdev->dev.of_node)
msm_otg_setup_devices(pdev, motg->pdata->mode, false);
if (psy)
power_supply_put(psy);
msm_otg_debugfs_cleanup();
- cancel_delayed_work_sync(&motg->chg_work);
cancel_delayed_work_sync(&motg->id_status_work);
cancel_delayed_work_sync(&motg->perf_vote_work);
msm_otg_perf_vote_update(motg, false);
@@ -5268,24 +4195,9 @@ static int msm_otg_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
- if (motg->phy_irq)
- free_irq(motg->phy_irq, motg);
- if (motg->pdata->pmic_id_irq)
- free_irq(motg->pdata->pmic_id_irq, motg);
usb_remove_phy(phy);
- free_irq(motg->irq, motg);
- if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
- device_remove_file(&pdev->dev,
- &dev_attr_dpdm_pulldown_enable);
- if (motg->pdata->otg_control == OTG_PHY_CONTROL &&
- motg->pdata->mpm_otgsessvld_int)
- msm_mpm_enable_pin(motg->pdata->mpm_otgsessvld_int, 0);
-
- if (motg->pdata->mpm_dpshv_int)
- msm_mpm_enable_pin(motg->pdata->mpm_dpshv_int, 0);
- if (motg->pdata->mpm_dmshv_int)
- msm_mpm_enable_pin(motg->pdata->mpm_dmshv_int, 0);
+ device_remove_file(&pdev->dev, &dev_attr_dpdm_pulldown_enable);
/*
* Put PHY in low power mode.
@@ -5351,29 +4263,11 @@ static int msm_otg_runtime_idle(struct device *dev)
struct usb_phy *phy = &motg->phy;
dev_dbg(dev, "OTG runtime idle\n");
- msm_otg_dbg_log_event(phy, "RUNTIME IDLE",
- phy->otg->state, motg->ext_chg_active);
+ msm_otg_dbg_log_event(phy, "RUNTIME IDLE", phy->otg->state, 0);
if (phy->otg->state == OTG_STATE_UNDEFINED)
return -EAGAIN;
- if (motg->ext_chg_active == DEFAULT) {
- dev_dbg(dev, "Deferring LPM\n");
- /*
- * Charger detection may happen in user space.
- * Delay entering LPM by 3 sec. Otherwise we
- * have to exit LPM when user space begins
- * charger detection.
- *
- * This timer will be canceled when user space
- * votes against LPM by incrementing PM usage
- * counter. We enter low power mode when
- * PM usage counter is decremented.
- */
- pm_schedule_suspend(dev, 3000);
- return -EAGAIN;
- }
-
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 44f53a7..29f6c1a 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -517,11 +517,12 @@ int mdss_mdp_get_plane_sizes(struct mdss_mdp_format_params *fmt, u32 w, u32 h,
if (ps == NULL)
return -EINVAL;
+ memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
+
if ((w > MAX_IMG_WIDTH) || (h > MAX_IMG_HEIGHT))
return -ERANGE;
bpp = fmt->bpp;
- memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
if (mdss_mdp_is_ubwc_format(fmt)) {
rc = mdss_mdp_get_ubwc_plane_size(fmt, w, h, ps);
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index fffaad4..0b3b223 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -32,23 +32,20 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
ci->data->under_android = pi->data->under_android;
ci->data->under_cache = pi->data->under_cache;
ci->data->under_obb = pi->data->under_obb;
- set_top(ci, pi->top_data);
}
/* helper function for derived state */
void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
- uid_t uid, bool under_android,
- struct sdcardfs_inode_data *top)
+ uid_t uid)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
info->data->perm = perm;
info->data->userid = userid;
info->data->d_uid = uid;
- info->data->under_android = under_android;
+ info->data->under_android = false;
info->data->under_cache = false;
info->data->under_obb = false;
- set_top(info, top);
}
/* While renaming, there is a point where we want the path from dentry,
@@ -58,8 +55,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
const struct qstr *name)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
- struct sdcardfs_inode_data *parent_data =
- SDCARDFS_I(d_inode(parent))->data;
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
+ struct sdcardfs_inode_data *parent_data = parent_info->data;
appid_t appid;
unsigned long user_num;
int err;
@@ -80,13 +77,15 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
inherit_derived_state(d_inode(parent), d_inode(dentry));
/* Files don't get special labels */
- if (!S_ISDIR(d_inode(dentry)->i_mode))
+ if (!S_ISDIR(d_inode(dentry)->i_mode)) {
+ set_top(info, parent_info);
return;
+ }
/* Derive custom permissions based on parent and current node */
switch (parent_data->perm) {
case PERM_INHERIT:
case PERM_ANDROID_PACKAGE_CACHE:
- /* Already inherited above */
+ set_top(info, parent_info);
break;
case PERM_PRE_ROOT:
/* Legacy internal layout places users at top level */
@@ -96,7 +95,6 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
info->data->userid = 0;
else
info->data->userid = user_num;
- set_top(info, info->data);
break;
case PERM_ROOT:
/* Assume masked off by default. */
@@ -104,24 +102,24 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
/* App-specific directories inside; let anyone traverse */
info->data->perm = PERM_ANDROID;
info->data->under_android = true;
- set_top(info, info->data);
+ } else {
+ set_top(info, parent_info);
}
break;
case PERM_ANDROID:
if (qstr_case_eq(name, &q_data)) {
/* App-specific directories inside; let anyone traverse */
info->data->perm = PERM_ANDROID_DATA;
- set_top(info, info->data);
} else if (qstr_case_eq(name, &q_obb)) {
/* App-specific directories inside; let anyone traverse */
info->data->perm = PERM_ANDROID_OBB;
info->data->under_obb = true;
- set_top(info, info->data);
/* Single OBB directory is always shared */
} else if (qstr_case_eq(name, &q_media)) {
/* App-specific directories inside; let anyone traverse */
info->data->perm = PERM_ANDROID_MEDIA;
- set_top(info, info->data);
+ } else {
+ set_top(info, parent_info);
}
break;
case PERM_ANDROID_OBB:
@@ -132,13 +130,13 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
if (appid != 0 && !is_excluded(name->name, parent_data->userid))
info->data->d_uid =
multiuser_get_uid(parent_data->userid, appid);
- set_top(info, info->data);
break;
case PERM_ANDROID_PACKAGE:
if (qstr_case_eq(name, &q_cache)) {
info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
info->data->under_cache = true;
}
+ set_top(info, parent_info);
break;
}
}
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 8ed0ea1..137d876 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -821,8 +821,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
return err;
}
-static int sdcardfs_fillattr(struct vfsmount *mnt,
- struct inode *inode, struct kstat *stat)
+static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode,
+ struct kstat *lower_stat, struct kstat *stat)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
struct sdcardfs_inode_data *top = top_data_get(info);
@@ -837,12 +837,12 @@ static int sdcardfs_fillattr(struct vfsmount *mnt,
stat->uid = make_kuid(&init_user_ns, top->d_uid);
stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top));
stat->rdev = inode->i_rdev;
- stat->size = i_size_read(inode);
- stat->atime = inode->i_atime;
- stat->mtime = inode->i_mtime;
- stat->ctime = inode->i_ctime;
- stat->blksize = (1 << inode->i_blkbits);
- stat->blocks = inode->i_blocks;
+ stat->size = lower_stat->size;
+ stat->atime = lower_stat->atime;
+ stat->mtime = lower_stat->mtime;
+ stat->ctime = lower_stat->ctime;
+ stat->blksize = lower_stat->blksize;
+ stat->blocks = lower_stat->blocks;
data_put(top);
return 0;
}
@@ -868,8 +868,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
goto out;
sdcardfs_copy_and_fix_attrs(d_inode(dentry),
d_inode(lower_path.dentry));
- err = sdcardfs_fillattr(mnt, d_inode(dentry), stat);
- stat->blocks = lower_stat.blocks;
+ err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat);
out:
sdcardfs_put_lower_path(dentry, &lower_path);
return err;
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 0a2b516..37d4864 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -334,13 +334,11 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
mutex_lock(&sdcardfs_super_list_lock);
if (sb_info->options.multiuser) {
setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT,
- sb_info->options.fs_user_id, AID_ROOT,
- false, SDCARDFS_I(d_inode(sb->s_root))->data);
+ sb_info->options.fs_user_id, AID_ROOT);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
} else {
setup_derived_state(d_inode(sb->s_root), PERM_ROOT,
- sb_info->options.fs_user_id, AID_ROOT,
- false, SDCARDFS_I(d_inode(sb->s_root))->data);
+ sb_info->options.fs_user_id, AID_ROOT);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fixup_tmp_permissions(d_inode(sb->s_root));
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index d1d8bab..eda8e7a 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -201,6 +201,7 @@ struct sdcardfs_inode_info {
struct sdcardfs_inode_data *data;
/* top folder for ownership */
+ spinlock_t top_lock;
struct sdcardfs_inode_data *top_data;
struct inode vfs_inode;
@@ -379,7 +380,12 @@ static inline struct sdcardfs_inode_data *data_get(
static inline struct sdcardfs_inode_data *top_data_get(
struct sdcardfs_inode_info *info)
{
- return data_get(info->top_data);
+ struct sdcardfs_inode_data *top_data;
+
+ spin_lock(&info->top_lock);
+ top_data = data_get(info->top_data);
+ spin_unlock(&info->top_lock);
+ return top_data;
}
extern void data_release(struct kref *ref);
@@ -401,15 +407,20 @@ static inline void release_own_data(struct sdcardfs_inode_info *info)
}
static inline void set_top(struct sdcardfs_inode_info *info,
- struct sdcardfs_inode_data *top)
+ struct sdcardfs_inode_info *top_owner)
{
- struct sdcardfs_inode_data *old_top = info->top_data;
+ struct sdcardfs_inode_data *old_top;
+ struct sdcardfs_inode_data *new_top = NULL;
- if (top)
- data_get(top);
- info->top_data = top;
+ if (top_owner)
+ new_top = top_data_get(top_owner);
+
+ spin_lock(&info->top_lock);
+ old_top = info->top_data;
+ info->top_data = new_top;
if (old_top)
data_put(old_top);
+ spin_unlock(&info->top_lock);
}
static inline int get_gid(struct vfsmount *mnt,
@@ -513,8 +524,7 @@ struct limit_search {
};
extern void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android,
- struct sdcardfs_inode_data *top);
+ userid_t userid, uid_t uid);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index b89947d..72d89b9 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -215,6 +215,9 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
i->data = d;
kref_init(&d->refcount);
+ i->top_data = d;
+ spin_lock_init(&i->top_lock);
+ kref_get(&d->refcount);
i->vfs_inode.i_version = 1;
return &i->vfs_inode;
diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h
index 80a95d9..e8afdba 100644
--- a/include/dt-bindings/clock/msm-clocks-8952.h
+++ b/include/dt-bindings/clock/msm-clocks-8952.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2014-2018, 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
@@ -317,7 +318,6 @@
#define clk_ln_bb_clk 0x3ab0b36d
#define clk_ln_bb_a_clk 0xc7257ea8
-
/* clock_debug controlled clocks */
#define clk_gcc_debug_mux 0x8121ac15
#define clk_rpm_debug_mux 0x25cd1f3a
@@ -341,4 +341,11 @@
#define clk_audio_pmi_clk 0xb7ba2274
#define clk_audio_lpass_mclk 0x575ec22b
+/* GCC block resets */
+#define GCC_CAMSS_MICRO_BCR 0
+#define GCC_USB_FS_BCR 1
+#define GCC_USB_HS_BCR 2
+#define GCC_USB2_HS_PHY_ONLY_BCR 3
+#define GCC_QUSB2_PHY_BCR 4
+
#endif
diff --git a/include/dt-bindings/clock/msm-clocks-hwio-8952.h b/include/dt-bindings/clock/msm-clocks-hwio-8952.h
new file mode 100644
index 0000000..888c2c7
--- /dev/null
+++ b/include/dt-bindings/clock/msm-clocks-hwio-8952.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2014-2016, 2018, 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_CLOCKS_8952_HWIO_H
+#define __MSM_CLOCKS_8952_HWIO_H
+
+#define GPLL0_MODE 0x21000
+#define GPLL0_STATUS 0x2101C
+#define GPLL6_STATUS 0x3701C
+#define GPLL3_MODE 0x22000
+#define GPLL4_MODE 0x24000
+#define GPLL4_STATUS 0x24024
+#define GX_DOMAIN_MISC 0x5B00C
+#define SYS_MM_NOC_AXI_CBCR 0x3D008
+#define BIMC_GFX_CBCR 0x59034
+#define MSS_CFG_AHB_CBCR 0x49000
+#define MSS_Q6_BIMC_AXI_CBCR 0x49004
+#define USB_HS_BCR 0x41000
+#define USB_HS_SYSTEM_CBCR 0x41004
+#define USB_HS_AHB_CBCR 0x41008
+#define USB_HS_PHY_CFG_AHB_CBCR 0x41030
+#define USB_HS_SYSTEM_CMD_RCGR 0x41010
+#define USB2A_PHY_SLEEP_CBCR 0x4102C
+#define USB_FS_SYSTEM_CBCR 0x3F004
+#define USB_FS_AHB_CBCR 0x3F008
+#define USB_FS_IC_CBCR 0x3F030
+#define USB_FS_SYSTEM_CMD_RCGR 0x3F010
+#define USB_FS_IC_CMD_RCGR 0x3F034
+#define USB2_HS_PHY_ONLY_BCR 0x41034
+#define QUSB2_PHY_BCR 0x4103C
+#define SDCC1_APPS_CMD_RCGR 0x42004
+#define SDCC1_APPS_CBCR 0x42018
+#define SDCC1_AHB_CBCR 0x4201C
+#define SDCC1_ICE_CORE_CMD_RCGR 0x5D000
+#define SDCC1_ICE_CORE_CBCR 0x5D014
+#define SDCC2_APPS_CMD_RCGR 0x43004
+#define SDCC2_APPS_CBCR 0x43018
+#define SDCC2_AHB_CBCR 0x4301C
+#define BLSP1_AHB_CBCR 0x01008
+#define BLSP1_QUP1_SPI_APPS_CBCR 0x02004
+#define BLSP1_QUP1_I2C_APPS_CBCR 0x02008
+#define BLSP1_QUP1_I2C_APPS_CMD_RCGR 0x0200C
+#define BLSP1_QUP2_I2C_APPS_CMD_RCGR 0x03000
+#define BLSP1_QUP3_I2C_APPS_CMD_RCGR 0x04000
+#define BLSP1_QUP4_I2C_APPS_CMD_RCGR 0x05000
+#define BLSP1_QUP1_SPI_APPS_CMD_RCGR 0x02024
+#define BLSP1_UART1_APPS_CBCR 0x0203C
+#define BLSP1_UART1_APPS_CMD_RCGR 0x02044
+#define BLSP1_QUP2_SPI_APPS_CBCR 0x0300C
+#define BLSP1_QUP2_I2C_APPS_CBCR 0x03010
+#define BLSP1_QUP2_SPI_APPS_CMD_RCGR 0x03014
+#define BLSP1_UART2_APPS_CBCR 0x0302C
+#define BLSP1_UART2_APPS_CMD_RCGR 0x03034
+#define BLSP1_QUP3_SPI_APPS_CBCR 0x0401C
+#define BLSP1_QUP3_I2C_APPS_CBCR 0x04020
+#define BLSP1_QUP3_SPI_APPS_CMD_RCGR 0x04024
+#define BLSP1_QUP4_SPI_APPS_CBCR 0x0501C
+#define BLSP1_QUP4_I2C_APPS_CBCR 0x05020
+#define BLSP1_QUP4_SPI_APPS_CMD_RCGR 0x05024
+#define BLSP2_AHB_CBCR 0x0B008
+#define BLSP2_QUP1_SPI_APPS_CBCR 0x0C004
+#define BLSP2_QUP1_I2C_APPS_CBCR 0x0C008
+#define BLSP2_QUP1_I2C_APPS_CMD_RCGR 0x0C00C
+#define BLSP2_QUP2_I2C_APPS_CMD_RCGR 0x0D000
+#define BLSP2_QUP3_I2C_APPS_CMD_RCGR 0x0F000
+#define BLSP2_QUP4_I2C_APPS_CMD_RCGR 0x18000
+#define BLSP2_QUP1_SPI_APPS_CMD_RCGR 0x0C024
+#define BLSP2_UART1_APPS_CBCR 0x0C03C
+#define BLSP2_UART1_APPS_CMD_RCGR 0x0C044
+#define BLSP2_QUP2_SPI_APPS_CBCR 0x0D00C
+#define BLSP2_QUP2_I2C_APPS_CBCR 0x0D010
+#define BLSP2_QUP2_SPI_APPS_CMD_RCGR 0x0D014
+#define BLSP2_UART2_APPS_CBCR 0x0D02C
+#define BLSP2_UART2_APPS_CMD_RCGR 0x0D034
+#define BLSP2_QUP3_SPI_APPS_CBCR 0x0F01C
+#define BLSP2_QUP3_I2C_APPS_CBCR 0x0F020
+#define BLSP2_QUP3_SPI_APPS_CMD_RCGR 0x0F024
+#define BLSP2_QUP4_SPI_APPS_CBCR 0x1801C
+#define BLSP2_QUP4_I2C_APPS_CBCR 0x18020
+#define BLSP2_QUP4_SPI_APPS_CMD_RCGR 0x18024
+#define PDM_AHB_CBCR 0x44004
+#define PDM2_CBCR 0x4400C
+#define PDM2_CMD_RCGR 0x44010
+#define PRNG_AHB_CBCR 0x13004
+#define BOOT_ROM_AHB_CBCR 0x1300C
+#define CRYPTO_CMD_RCGR 0x16004
+#define CRYPTO_CBCR 0x1601C
+#define CRYPTO_AXI_CBCR 0x16020
+#define CRYPTO_AHB_CBCR 0x16024
+#define GCC_XO_DIV4_CBCR 0x30034
+#define APSS_AHB_CMD_RCGR 0x46000
+#define GCC_PLLTEST_PAD_CFG 0x7400C
+#define GFX_TBU_CBCR 0x12010
+#define VENUS_TBU_CBCR 0x12014
+#define APSS_TCU_CBCR 0x12018
+#define MDP_TBU_CBCR 0x1201C
+#define GFX_TCU_CBCR 0x12020
+#define JPEG_TBU_CBCR 0x12034
+#define SMMU_CFG_CBCR 0x12038
+#define QDSS_DAP_CBCR 0x29084
+#define VFE_TBU_CBCR 0x1203C
+#define VFE1_TBU_CBCR 0x12090
+#define CPP_TBU_CBCR 0x12040
+#define APCS_GPLL_ENA_VOTE 0x45000
+#define APCS_CLOCK_BRANCH_ENA_VOTE 0x45004
+#define APCS_SMMU_CLOCK_BRANCH_ENA_VOTE 0x4500C
+#define GCC_DEBUG_CLK_CTL 0x74000
+#define CLOCK_FRQ_MEASURE_CTL 0x74004
+#define CLOCK_FRQ_MEASURE_STATUS 0x74008
+#define GP1_CBCR 0x08000
+#define GP1_CMD_RCGR 0x08004
+#define GP1_CFG_RCGR 0x08008
+#define GP2_CBCR 0x09000
+#define GP2_CMD_RCGR 0x09004
+#define GP3_CBCR 0x0A000
+#define GP3_CMD_RCGR 0x0A004
+#define VCODEC0_CMD_RCGR 0x4C000
+#define VENUS0_VCODEC0_CBCR 0x4C01C
+#define VENUS0_CORE0_VCODEC0_CBCR 0x4C02C
+#define VENUS0_CORE1_VCODEC0_CBCR 0x4C034
+#define VENUS0_AHB_CBCR 0x4C020
+#define VENUS0_AXI_CBCR 0x4C024
+#define PCLK0_CMD_RCGR 0x4D000
+#define MDP_CMD_RCGR 0x4D014
+#define VSYNC_CMD_RCGR 0x4D02C
+#define BYTE0_CMD_RCGR 0x4D044
+#define ESC0_CMD_RCGR 0x4D05C
+#define MDSS_AHB_CBCR 0x4D07C
+#define MDSS_AXI_CBCR 0x4D080
+#define MDSS_PCLK0_CBCR 0x4D084
+#define MDSS_MDP_CBCR 0x4D088
+#define MDSS_VSYNC_CBCR 0x4D090
+#define MDSS_BYTE0_CBCR 0x4D094
+#define MDSS_ESC0_CBCR 0x4D098
+#define CSI0PHYTIMER_CMD_RCGR 0x4E000
+#define CAMSS_CSI0PHYTIMER_CBCR 0x4E01C
+#define CSI0_CMD_RCGR 0x4E020
+#define CAMSS_CSI0_CBCR 0x4E03C
+#define CAMSS_CSI0_AHB_CBCR 0x4E040
+#define CAMSS_CSI0PHY_CBCR 0x4E048
+#define CAMSS_CSI0RDI_CBCR 0x4E050
+#define CAMSS_CSI0PIX_CBCR 0x4E058
+#define CSI1PHYTIMER_CMD_RCGR 0x4F000
+#define CSI1_CMD_RCGR 0x4F020
+#define CAMSS_CSI1_CBCR 0x4F03C
+#define CAMSS_CSI1PHYTIMER_CBCR 0x4F01C
+#define CAMSS_CSI1_AHB_CBCR 0x4F040
+#define CAMSS_CSI1PHY_CBCR 0x4F048
+#define CAMSS_CSI1RDI_CBCR 0x4F050
+#define CAMSS_CSI1PIX_CBCR 0x4F058
+#define CSI2_CMD_RCGR 0x3C020
+#define CAMSS_CSI2_CBCR 0x3C03C
+#define CAMSS_CSI2_AHB_CBCR 0x3C040
+#define CAMSS_CSI2PHY_CBCR 0x3C048
+#define CAMSS_CSI2RDI_CBCR 0x3C050
+#define CAMSS_CSI2PIX_CBCR 0x3C058
+#define CAMSS_ISPIF_AHB_CBCR 0x50004
+#define CCI_CMD_RCGR 0x51000
+#define CAMSS_CCI_CBCR 0x51018
+#define CAMSS_CCI_AHB_CBCR 0x5101C
+#define MCLK0_CMD_RCGR 0x52000
+#define CAMSS_MCLK0_CBCR 0x52018
+#define MCLK1_CMD_RCGR 0x53000
+#define CAMSS_MCLK1_CBCR 0x53018
+#define MCLK2_CMD_RCGR 0x5C000
+#define CAMSS_MCLK2_CBCR 0x5C018
+#define MM_GP0_CMD_RCGR 0x54000
+#define CAMSS_GP0_CBCR 0x54018
+#define MM_GP1_CMD_RCGR 0x55000
+#define CAMSS_GP1_CBCR 0x55018
+#define CAMSS_TOP_AHB_CBCR 0x5A014
+#define CAMSS_AHB_CBCR 0x56004
+#define CAMSS_MICRO_AHB_CBCR 0x5600C
+#define CAMSS_MICRO_BCR 0x56008
+#define JPEG0_CMD_RCGR 0x57000
+#define CAMSS_JPEG0_CBCR 0x57020
+#define CAMSS_JPEG_AHB_CBCR 0x57024
+#define CAMSS_JPEG_AXI_CBCR 0x57028
+#define VFE0_CMD_RCGR 0x58000
+#define CPP_CMD_RCGR 0x58018
+#define CAMSS_VFE0_CBCR 0x58038
+#define CAMSS_CPP_CBCR 0x5803C
+#define CAMSS_CPP_AHB_CBCR 0x58040
+#define CAMSS_VFE_AHB_CBCR 0x58044
+#define CAMSS_VFE_AXI_CBCR 0x58048
+#define CAMSS_CSI_VFE0_CBCR 0x58050
+#define VFE1_CMD_RCGR 0x58054
+#define CAMSS_VFE1_CBCR 0x5805C
+#define CAMSS_VFE1_AHB_CBCR 0x58060
+#define CAMSS_CPP_AXI_CBCR 0x58064
+#define CAMSS_VFE1_AXI_CBCR 0x58068
+#define CAMSS_CSI_VFE1_CBCR 0x58074
+#define GFX3D_CMD_RCGR 0x59000
+#define OXILI_GFX3D_CBCR 0x59020
+#define OXILI_GMEM_CBCR 0x59024
+#define OXILI_AHB_CBCR 0x59028
+#define OXILI_TIMER_CBCR 0x59040
+#define OXILI_AON_CBCR 0x5904C
+#define CAMSS_TOP_AHB_CMD_RCGR 0x5A000
+#define BIMC_GPU_CBCR 0x59030
+#define GTCU_AHB_CBCR 0x12044
+#define IPA_TBU_CBCR 0x120A0
+#define SYSTEM_MM_NOC_CMD_RCGR 0x3D000
+#define USB_FS_BCR 0x3F000
+
+#define APCS_CLOCK_SLEEP_ENA_VOTE 0x45008
+#define BYTE1_CMD_RCGR 0x4D0B0
+#define ESC1_CMD_RCGR 0x4D0A8
+#define PCLK1_CMD_RCGR 0x4D0B8
+#define MDSS_BYTE1_CBCR 0x4D0A0
+#define MDSS_ESC1_CBCR 0x4D09C
+#define MDSS_PCLK1_CBCR 0x4D0A4
+#define DCC_CBCR 0x77004
+
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_BUS_CLK_TYPE 0x316b6c63
+#define RPM_MEM_CLK_TYPE 0x326b6c63
+#define RPM_IPA_CLK_TYPE 0x617069
+#define RPM_SMD_KEY_ENABLE 0x62616E45
+
+#define CXO_CLK_SRC_ID 0x0
+#define QDSS_CLK_ID 0x1
+
+#define PNOC_CLK_ID 0x0
+#define SNOC_CLK_ID 0x1
+#define SYSMMNOC_CLK_ID 0x2
+#define BIMC_CLK_ID 0x0
+#define BIMC_GPU_CLK_ID 0x2
+#define IPA_CLK_ID 0x0
+
+#define BUS_SCALING 0x2
+
+/* XO clock */
+#define BB_CLK1_ID 0x1
+#define BB_CLK2_ID 0x2
+#define RF_CLK2_ID 0x5
+#define LN_BB_CLK_ID 0x8
+#define DIV_CLK1_ID 0xb
+#define DIV_CLK2_ID 0xc
+
+#define APCS_CCI_PLL_MODE 0x00000
+#define APCS_CCI_PLL_L_VAL 0x00004
+#define APCS_CCI_PLL_M_VAL 0x00008
+#define APCS_CCI_PLL_N_VAL 0x0000C
+#define APCS_CCI_PLL_USER_CTL 0x00010
+#define APCS_CCI_PLL_CONFIG_CTL 0x00014
+#define APCS_CCI_PLL_STATUS 0x0001C
+
+#define APCS_C0_PLL_MODE 0x00000
+#define APCS_C0_PLL_L_VAL 0x00004
+#define APCS_C0_PLL_M_VAL 0x00008
+#define APCS_C0_PLL_N_VAL 0x0000C
+#define APCS_C0_PLL_USER_CTL 0x00010
+#define APCS_C0_PLL_CONFIG_CTL 0x00014
+#define APCS_C0_PLL_STATUS 0x0001C
+
+#define APCS_C1_PLL_MODE 0x00000
+#define APCS_C1_PLL_L_VAL 0x00004
+#define APCS_C1_PLL_M_VAL 0x00008
+#define APCS_C1_PLL_N_VAL 0x0000C
+#define APCS_C1_PLL_USER_CTL 0x00010
+#define APCS_C1_PLL_CONFIG_CTL 0x00014
+#define APCS_C1_PLL_STATUS 0x0001C
+
+
+#define CLKFLAG_WAKEUP_CYCLES 0x0
+#define CLKFLAG_SLEEP_CYCLES 0x0
+
+/* Mux source select values */
+#define xo_source_val 0
+#define xo_a_source_val 0
+#define gpll0_source_val 1
+#define gpll3_source_val 2
+#define gpll0_out_main_source_val 1 /* sdcc1_ice_core */
+/* cci_clk_src and usb_fs_system_clk_src */
+#define gpll0_out_aux_source_val 2
+#define gpll4_source_val 2 /* sdcc1_apss_clk_src */
+#define gpll4_out_source_val 3 /* sdcc1_apss_clk_src */
+#define gpll6_source_val 2 /* mclk0_2_clk_src */
+#define gpll6_aux_source_val 3 /* gfx3d_clk_src */
+#define gpll6_out_main_source_val 1 /* usb_fs_ic_clk_src */
+#define dsi0_phypll_source_val 1
+#define dsi0_0phypll_source_val 1 /* byte0_clk & pclk0_clk */
+#define dsi0_1phypll_source_val 3 /* byte1_clk & pclk1_clk */
+#define dsi1_0phypll_source_val 3 /* byte0_clk & pclk0_clk */
+#define dsi1_1phypll_source_val 1 /* byte1_clk & pclk1_clk */
+
+
+#define F(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_source_val), \
+ }
+
+#define F_SLEW(f, s_f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_freq = (s_f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_source_val), \
+ }
+
+#define F_APCS_PLL(f, l, m, n, pre_div, post_div, vco) \
+ { \
+ .freq_hz = (f), \
+ .l_val = (l), \
+ .m_val = (m), \
+ .n_val = (n), \
+ .pre_div_val = BVAL(12, 12, (pre_div)), \
+ .post_div_val = BVAL(9, 8, (post_div)), \
+ .vco_val = BVAL(29, 28, (vco)), \
+ }
+
+#define VDD_DIG_FMAX_MAP1(l1, f1) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+# define OVERRIDE_FMAX1(clkname, l1, f1) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1)
+
+# define OVERRIDE_FMAX2(clkname, l1, f1, l2, f2) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1); \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l2] = (f2)
+
+#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+# define OVERRIDE_FMAX3(clkname, l1, f1, l2, f2, l3, f3) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l2] = (f2);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l3] = (f3)
+
+
+# define OVERRIDE_FMAX4(clkname, l1, f1, l2, f2, l3, f3, l4, f4) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l2] = (f2);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l3] = (f3);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l4] = (f4)
+
+#define VDD_DIG_FMAX_MAP5(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1),\
+ [VDD_DIG_##l2] = (f2),\
+ [VDD_DIG_##l3] = (f3),\
+ [VDD_DIG_##l4] = (f4),\
+ [VDD_DIG_##l5] = (f5),\
+ },\
+ .num_fmax = VDD_DIG_NUM
+
+#define OVERRIDE_FMAX5(clkname, l1, f1, l2, f2, l3, f3, l4, f4, l5, f5) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l2] = (f2);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l3] = (f3);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l4] = (f4);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l5] = (f5)
+
+#define OVERRIDE_FMAX6(clkname, \
+ l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \
+ clkname##_clk_src.c.fmax[VDD_DIG_##l1] = (f1);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l2] = (f2);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l3] = (f3);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l4] = (f4);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l5] = (f5);\
+ clkname##_clk_src.c.fmax[VDD_DIG_##l6] = (f6)
+
+#define OVERRIDE_FTABLE(clkname, ftable, name) \
+ clkname##_clk_src.freq_tbl = ftable##_##name
+
+enum vdd_dig_levels {
+ VDD_DIG_NONE,
+ VDD_DIG_LOWER,
+ VDD_DIG_LOW,
+ VDD_DIG_NOMINAL,
+ VDD_DIG_NOM_PLUS,
+ VDD_DIG_HIGH,
+ VDD_DIG_SUPER_TUR,
+ VDD_DIG_NUM
+};
+
+enum vdd_dig_levels_8917 {
+ VDD_DIG_NONE_8917,
+ VDD_DIG_LOWER_8917,
+ VDD_DIG_LOW_8917,
+ VDD_DIG_NOMINAL_8917,
+ VDD_DIG_NOM_PLUS_8917,
+ VDD_DIG_HIGH_8917,
+ VDD_DIG_NUM_8917
+};
+
+enum vdd_hf_pll_levels_8917 {
+ VDD_HF_PLL_OFF_8917,
+ VDD_HF_PLL_SVS_8917,
+ VDD_HF_PLL_NOM_8917,
+ VDD_HF_PLL_TUR_8917,
+ VDD_HF_PLL_NUM_8917,
+};
+
+static int vdd_corner[] = {
+ RPM_REGULATOR_LEVEL_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_SVS */
+ RPM_REGULATOR_LEVEL_SVS_PLUS, /* VDD_DIG_SVS_PLUS */
+ RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOM */
+ RPM_REGULATOR_LEVEL_NOM_PLUS, /* VDD_DIG_NOM_PLUS */
+ RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_TURBO */
+ RPM_REGULATOR_LEVEL_BINNING, /* VDD_DIG_SUPER_TUR */
+};
+#endif
diff --git a/include/dt-bindings/clock/msm-cpu-clocks-8939.h b/include/dt-bindings/clock/msm-cpu-clocks-8939.h
new file mode 100644
index 0000000..cb16dca
--- /dev/null
+++ b/include/dt-bindings/clock/msm-cpu-clocks-8939.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014-2015, 2018, 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_CLOCK_CPU_8939_H
+#define __MSM_CLOCK_CPU_8939_H
+
+#define clk_a53ssmux_lc 0x71a9377b
+#define clk_a53_lc_clk 0xc69f0878
+#define clk_a53ssmux_bc 0xb5983c42
+#define clk_a53_bc_clk 0xcf28e63a
+#define clk_a53ssmux_cci 0x15560bd5
+#define clk_cci_clk 0x96854074
+#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 21dd697..43d801e 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -275,6 +275,9 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PARALLEL_FCC_MAX,
POWER_SUPPLY_PROP_MIN_ICL,
POWER_SUPPLY_PROP_MOISTURE_DETECTED,
+ POWER_SUPPLY_PROP_BATT_PROFILE_VERSION,
+ POWER_SUPPLY_PROP_BATT_FULL_CURRENT,
+ POWER_SUPPLY_PROP_RECHARGE_SOC,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index c3e980e..18b738c 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -44,31 +44,6 @@ enum usb_noc_mode {
};
/**
- * Different states involved in USB charger detection.
- *
- * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
- * process is not yet started.
- * USB_CHG_STATE_IN_PROGRESS Charger detection in progress
- * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
- * USB_CHG_STATE_DCD_DONE Data pin contact is detected.
- * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
- * between SDP and DCP/CDP).
- * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
- * between DCP and CDP).
- * USB_CHG_STATE_DETECTED USB charger type is determined.
- *
- */
-enum usb_chg_state {
- USB_CHG_STATE_UNDEFINED = 0,
- USB_CHG_STATE_IN_PROGRESS,
- USB_CHG_STATE_WAIT_FOR_DCD,
- USB_CHG_STATE_DCD_DONE,
- USB_CHG_STATE_PRIMARY_DONE,
- USB_CHG_STATE_SECONDARY_DONE,
- USB_CHG_STATE_DETECTED,
-};
-
-/**
* USB charger types
*
* USB_INVALID_CHARGER Invalid USB charger.
@@ -151,17 +126,11 @@ enum usb_id_state {
* @async_int: IRQ line on which ASYNC interrupt arrived in LPM.
* @cur_power: The amount of mA available from downstream port.
* @otg_wq: Strict order otg workqueue for OTG works (SM/ID/SUSPEND).
- * @chg_work: Charger detection work.
- * @chg_state: The state of charger detection process.
* @chg_type: The type of charger attached.
* @bus_perf_client: Bus performance client handle to request BUS bandwidth
* @host_bus_suspend: indicates host bus suspend or not.
* @device_bus_suspend: indicates device bus suspend or not.
* @bus_clks_enabled: indicates pcnoc/snoc/bimc clocks are on or not.
- * @chg_check_timer: The timer used to implement the workaround to detect
- * very slow plug in of wall charger.
- * @bc1p2_current_max: Max charging current allowed as per bc1.2 chg detection
- * @typec_current_max: Max charging current allowed as per type-c chg detection
* @is_ext_chg_dcp: To indicate whether charger detected by external entity
SMB hardware is DCP charger or not.
* @ext_id_irq: IRQ for ID interrupt.
@@ -210,7 +179,6 @@ struct msm_otg {
#define ID 0
#define B_SESS_VLD 1
#define A_BUS_SUSPEND 14
-#define B_FALSE_SDP 18
unsigned long inputs;
struct work_struct sm_work;
bool sm_work_pending;
@@ -222,9 +190,7 @@ struct msm_otg {
int async_int;
unsigned int cur_power;
struct workqueue_struct *otg_wq;
- struct delayed_work chg_work;
struct delayed_work id_status_work;
- enum usb_chg_state chg_state;
enum usb_chg_type chg_type;
unsigned int dcd_time;
unsigned long caps;
@@ -232,7 +198,6 @@ struct msm_otg {
bool host_bus_suspend;
bool device_bus_suspend;
bool bus_clks_enabled;
- struct timer_list chg_check_timer;
/*
* Allowing PHY power collpase turns off the HSUSB 3.3v and 1.8v
* analog regulators while going to low power mode.
@@ -282,17 +247,8 @@ struct msm_otg {
#define PHY_REGULATORS_LPM BIT(4)
int reset_counter;
unsigned int online;
- unsigned int host_mode;
- unsigned int bc1p2_current_max;
- unsigned int typec_current_max;
dev_t ext_chg_dev;
- struct cdev ext_chg_cdev;
- struct class *ext_chg_class;
- struct device *ext_chg_device;
- bool ext_chg_opened;
- enum usb_ext_chg_status ext_chg_active;
- struct completion ext_chg_wait;
struct pinctrl *phy_pinctrl;
bool is_ext_chg_dcp;
struct qpnp_vadc_chip *vadc_dev;
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
new file mode 100644
index 0000000..2c8b651
--- /dev/null
+++ b/include/media/msm_cam_sensor.h
@@ -0,0 +1,294 @@
+#ifndef __LINUX_MSM_CAM_SENSOR_H
+#define __LINUX_MSM_CAM_SENSOR_H
+
+#include <uapi/media/msm_cam_sensor.h>
+#include <uapi/media/msm_camsensor_sdk.h>
+
+#include <linux/compat.h>
+
+#ifdef CONFIG_COMPAT
+
+struct msm_sensor_power_setting32 {
+ enum msm_sensor_power_seq_type_t seq_type;
+ uint16_t seq_val;
+ compat_uint_t config_val;
+ uint16_t delay;
+ compat_uptr_t data[10];
+};
+
+struct msm_sensor_power_setting_array32 {
+ struct msm_sensor_power_setting32 power_setting_a[MAX_POWER_CONFIG];
+ compat_uptr_t power_setting;
+ uint16_t size;
+ struct msm_sensor_power_setting32
+ power_down_setting_a[MAX_POWER_CONFIG];
+ compat_uptr_t power_down_setting;
+ uint16_t size_down;
+};
+
+struct msm_camera_sensor_slave_info32 {
+ char sensor_name[32];
+ char eeprom_name[32];
+ char actuator_name[32];
+ char ois_name[32];
+ char flash_name[32];
+ enum msm_sensor_camera_id_t camera_id;
+ uint16_t slave_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ struct msm_sensor_id_info_t sensor_id_info;
+ struct msm_sensor_power_setting_array32 power_setting_array;
+ uint8_t is_init_params_valid;
+ struct msm_sensor_init_params sensor_init_params;
+ enum msm_sensor_output_format_t output_format;
+ uint8_t bypass_video_node_creation;
+};
+
+struct msm_camera_csid_lut_params32 {
+ uint8_t num_cid;
+ struct msm_camera_csid_vc_cfg vc_cfg_a[MAX_CID];
+ compat_uptr_t vc_cfg[MAX_CID];
+};
+
+struct msm_camera_csid_params32 {
+ uint8_t lane_cnt;
+ uint16_t lane_assign;
+ uint8_t phy_sel;
+ uint32_t csi_clk;
+ struct msm_camera_csid_lut_params32 lut_params;
+ uint8_t csi_3p_sel;
+};
+
+struct msm_camera_csi2_params32 {
+ struct msm_camera_csid_params32 csid_params;
+ struct msm_camera_csiphy_params csiphy_params;
+ uint8_t csi_clk_scale_enable;
+};
+
+struct csid_cfg_data32 {
+ enum csid_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ compat_uptr_t csid_params;
+ compat_uptr_t csid_testmode_params;
+ } cfg;
+};
+
+struct msm_ir_led_cfg_data_t32 {
+ enum msm_ir_led_cfg_type_t cfg_type;
+ int32_t pwm_duty_on_ns;
+ int32_t pwm_period_ns;
+};
+
+struct msm_ir_cut_cfg_data_t32 {
+ enum msm_ir_cut_cfg_type_t cfg_type;
+};
+
+struct msm_laser_led_cfg_data_t32 {
+ enum msm_laser_led_cfg_type_t cfg_type;
+ compat_uptr_t setting;
+ compat_uptr_t debug_reg;
+ uint32_t debug_reg_size;
+ uint16_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+};
+
+struct eeprom_read_t32 {
+ compat_uptr_t dbuffer;
+ uint32_t num_bytes;
+};
+
+struct eeprom_write_t32 {
+ compat_uptr_t dbuffer;
+ uint32_t num_bytes;
+};
+
+struct msm_eeprom_info_t32 {
+ compat_uptr_t power_setting_array;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ compat_uptr_t mem_map_array;
+};
+
+struct msm_eeprom_cfg_data32 {
+ enum eeprom_cfg_type_t cfgtype;
+ uint8_t is_supported;
+ union {
+ char eeprom_name[MAX_SENSOR_NAME];
+ struct eeprom_get_t get_data;
+ struct eeprom_read_t32 read_data;
+ struct eeprom_write_t32 write_data;
+ struct msm_eeprom_info_t32 eeprom_info;
+ } cfg;
+};
+
+struct msm_camera_i2c_seq_reg_setting32 {
+ compat_uptr_t reg_setting;
+ uint16_t size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint16_t delay;
+};
+
+struct msm_camera_i2c_reg_setting32 {
+ compat_uptr_t 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_array_write_config32 {
+ struct msm_camera_i2c_reg_setting32 conf_array;
+ uint16_t slave_addr;
+};
+
+struct msm_actuator_tuning_params_t32 {
+ int16_t initial_code;
+ uint16_t pwd_step;
+ uint16_t region_size;
+ uint32_t total_steps;
+ compat_uptr_t region_params;
+};
+
+struct msm_actuator_params_t32 {
+ enum actuator_type act_type;
+ uint8_t reg_tbl_size;
+ uint16_t data_size;
+ uint16_t init_setting_size;
+ uint32_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum msm_camera_i2c_reg_addr_type i2c_addr_type;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ compat_uptr_t reg_tbl_params;
+ compat_uptr_t init_settings;
+ struct park_lens_data_t park_lens;
+};
+
+struct msm_actuator_set_info_t32 {
+ struct msm_actuator_params_t32 actuator_params;
+ struct msm_actuator_tuning_params_t32 af_tuning_params;
+};
+
+struct sensor_init_cfg_data32 {
+ enum msm_sensor_init_cfg_type_t cfgtype;
+ struct msm_sensor_info_t probed_info;
+ char entity_name[MAX_SENSOR_NAME];
+ union {
+ compat_uptr_t setting;
+ } cfg;
+};
+
+struct msm_actuator_move_params_t32 {
+ int8_t dir;
+ int8_t sign_dir;
+ int16_t dest_step_pos;
+ int32_t num_steps;
+ uint16_t curr_lens_pos;
+ compat_uptr_t ringing_params;
+};
+
+struct msm_actuator_cfg_data32 {
+ int cfgtype;
+ uint8_t is_af_supported;
+ union {
+ struct msm_actuator_move_params_t32 move;
+ struct msm_actuator_set_info_t32 set_info;
+ struct msm_actuator_get_info_t get_info;
+ struct msm_actuator_set_position_t setpos;
+ enum af_camera_name cam_name;
+ } cfg;
+};
+
+struct csiphy_cfg_data32 {
+ enum csiphy_cfg_type_t cfgtype;
+ union {
+ compat_uptr_t csiphy_params;
+ compat_uptr_t csi_lane_params;
+ } cfg;
+};
+
+struct sensorb_cfg_data32 {
+ int cfgtype;
+ union {
+ struct msm_sensor_info_t sensor_info;
+ struct msm_sensor_init_params sensor_init_params;
+ compat_uptr_t setting;
+ struct msm_sensor_i2c_sync_params sensor_i2c_sync_params;
+ } cfg;
+};
+
+struct msm_ois_params_t32 {
+ uint16_t data_size;
+ uint16_t setting_size;
+ uint32_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum msm_camera_i2c_reg_addr_type i2c_addr_type;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ compat_uptr_t settings;
+};
+
+struct msm_ois_set_info_t32 {
+ struct msm_ois_params_t32 ois_params;
+};
+
+struct msm_ois_cfg_data32 {
+ int cfgtype;
+ union {
+ struct msm_ois_set_info_t32 set_info;
+ compat_uptr_t settings;
+ } cfg;
+};
+
+struct msm_flash_init_info_t32 {
+ enum msm_flash_driver_type flash_driver_type;
+ uint32_t slave_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ compat_uptr_t power_setting_array;
+ compat_uptr_t settings;
+};
+
+struct msm_flash_cfg_data_t32 {
+ enum msm_flash_cfg_type_t cfg_type;
+ int32_t flash_current[MAX_LED_TRIGGERS];
+ int32_t flash_duration[MAX_LED_TRIGGERS];
+ union {
+ compat_uptr_t flash_init_info;
+ compat_uptr_t settings;
+ } cfg;
+};
+
+#define VIDIOC_MSM_ACTUATOR_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_actuator_cfg_data32)
+
+#define VIDIOC_MSM_SENSOR_INIT_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct sensor_init_cfg_data32)
+
+#define VIDIOC_MSM_CSIPHY_IO_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csiphy_cfg_data32)
+
+#define VIDIOC_MSM_SENSOR_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data32)
+
+#define VIDIOC_MSM_EEPROM_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_eeprom_cfg_data32)
+
+#define VIDIOC_MSM_OIS_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_ois_cfg_data32)
+
+#define VIDIOC_MSM_CSID_IO_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csid_cfg_data32)
+
+#define VIDIOC_MSM_FLASH_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_flash_cfg_data_t32)
+
+#define VIDIOC_MSM_IR_LED_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ir_led_cfg_data_t32)
+
+#define VIDIOC_MSM_IR_CUT_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t32)
+
+#define VIDIOC_MSM_LASER_LED_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_laser_led_cfg_data_t32)
+
+#endif
+
+#endif
diff --git a/include/media/msm_fd.h b/include/media/msm_fd.h
new file mode 100644
index 0000000..81eaee1
--- /dev/null
+++ b/include/media/msm_fd.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2014-2018, 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_FD__
+#define __MSM_FD__
+
+#include <uapi/media/msm_fd.h>
+#include <linux/compat.h>
+
+#ifdef CONFIG_COMPAT
+/*
+ * struct msm_fd_result32 - Compat structure contain detected faces result.
+ * @frame_id: Frame id of requested result.
+ * @face_cnt: Number of result faces, driver can modify this value (to smaller)
+ * @face_data: Pointer to array of face data structures.
+ * Array size should not be smaller then face_cnt.
+ */
+struct msm_fd_result32 {
+ __u32 frame_id;
+ __u32 face_cnt;
+ compat_uptr_t face_data;
+};
+
+/* MSM FD compat private ioctl ID */
+#define VIDIOC_MSM_FD_GET_RESULT32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_fd_result32)
+#endif
+
+#endif /* __MSM_FD__ */
diff --git a/include/media/msmb_camera.h b/include/media/msmb_camera.h
new file mode 100644
index 0000000..eae2d01
--- /dev/null
+++ b/include/media/msmb_camera.h
@@ -0,0 +1,25 @@
+#ifndef __LINUX_MSMB_CAMERA_H
+#define __LINUX_MSMB_CAMERA_H
+
+#include <uapi/media/msmb_camera.h>
+
+#ifdef CONFIG_COMPAT
+#define MSM_CAM_V4L2_IOCTL_NOTIFY32 \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 30, struct v4l2_event32)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_META32 \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 31, struct v4l2_event32)
+
+#define MSM_CAM_V4L2_IOCTL_CMD_ACK32 \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 32, struct v4l2_event32)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR32 \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 33, struct v4l2_event32)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG32 \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 34, struct v4l2_event32)
+
+#endif
+
+#endif
+
diff --git a/include/media/msmb_generic_buf_mgr.h b/include/media/msmb_generic_buf_mgr.h
new file mode 100644
index 0000000..b3e1a02
--- /dev/null
+++ b/include/media/msmb_generic_buf_mgr.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2013-2016, 2018, 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 __MEDIA_MSMB_GENERIC_BUF_MGR_H__
+#define __MEDIA_MSMB_GENERIC_BUF_MGR_H__
+
+#include <uapi/media/msmb_generic_buf_mgr.h>
+#include <linux/compat.h>
+
+struct v4l2_subdev *msm_buf_mngr_get_subdev(void);
+
+#ifdef CONFIG_COMPAT
+
+struct msm_buf_mngr_info32_t {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t frame_id;
+ struct compat_timeval timestamp;
+ uint32_t index;
+ uint32_t reserved;
+ enum msm_camera_buf_mngr_buf_type type;
+ struct msm_camera_user_buf_cont_t user_buf;
+};
+
+#define VIDIOC_MSM_BUF_MNGR_GET_BUF32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 33, struct msm_buf_mngr_info32_t)
+
+#define VIDIOC_MSM_BUF_MNGR_PUT_BUF32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 34, struct msm_buf_mngr_info32_t)
+
+#define VIDIOC_MSM_BUF_MNGR_BUF_DONE32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 35, struct msm_buf_mngr_info32_t)
+
+#define VIDIOC_MSM_BUF_MNGR_FLUSH32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 39, struct msm_buf_mngr_info32_t)
+
+#endif
+
+#endif
+
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
new file mode 100644
index 0000000..6f5da29
--- /dev/null
+++ b/include/media/msmb_isp.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2014-2016, 2018, 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 __MSMB_ISP__
+#define __MSMB_ISP__
+
+#include <uapi/media/msmb_isp.h>
+
+#ifdef CONFIG_COMPAT
+struct msm_isp_event_data32 {
+ struct compat_timeval timestamp;
+ struct compat_timeval mono_timestamp;
+ uint32_t frame_id;
+ union {
+ struct msm_isp_stats_event stats;
+ struct msm_isp_buf_event buf_done;
+ struct msm_isp_fetch_eng_event fetch_done;
+ struct msm_isp_error_info error_info;
+ struct msm_isp_output_info output_info;
+ struct msm_isp_sof_info sof_info;
+ } u;
+};
+#endif
+
+#endif
+
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
new file mode 100644
index 0000000..1e677f9
--- /dev/null
+++ b/include/media/msmb_pproc.h
@@ -0,0 +1,155 @@
+#ifndef __MSMB_PPROC_H
+#define __MSMB_PPROC_H
+
+#include <uapi/media/msmb_pproc.h>
+
+#include <linux/compat.h>
+
+#define MSM_OUTPUT_BUF_CNT 8
+
+#ifdef CONFIG_COMPAT
+struct msm_cpp_frame_info32_t {
+ int32_t frame_id;
+ struct compat_timeval timestamp;
+ uint32_t inst_id;
+ uint32_t identity;
+ uint32_t client_id;
+ enum msm_cpp_frame_type frame_type;
+ uint32_t num_strips;
+ uint32_t msg_len;
+ compat_uint_t cpp_cmd_msg;
+ int src_fd;
+ int dst_fd;
+ struct compat_timeval in_time, out_time;
+ compat_caddr_t cookie;
+ compat_int_t status;
+ int32_t duplicate_output;
+ uint32_t duplicate_identity;
+ uint32_t feature_mask;
+ uint8_t we_disable;
+ struct msm_cpp_buffer_info_t input_buffer_info;
+ struct msm_cpp_buffer_info_t output_buffer_info[MSM_OUTPUT_BUF_CNT];
+ struct msm_cpp_buffer_info_t duplicate_buffer_info;
+ struct msm_cpp_buffer_info_t tnr_scratch_buffer_info[2];
+ uint32_t reserved;
+ uint8_t partial_frame_indicator;
+ /* the followings are used only for partial_frame type
+ * and is only used for offline frame processing and
+ * only if payload big enough and need to be split into partial_frame
+ * if first_payload, kernel acquires output buffer
+ * first payload must have the last stripe
+ * buffer addresses from 0 to last_stripe_index are updated.
+ * kernel updates payload with msg_len and stripe_info
+ * kernel sends top level, plane level, then only stripes
+ * starting with first_stripe_index and
+ * ends with last_stripe_index
+ * kernel then sends trailing flag at frame done,
+ * if last payload, kernel queues the output buffer to HAL
+ */
+ uint8_t first_payload;
+ uint8_t last_payload;
+ uint32_t first_stripe_index;
+ uint32_t last_stripe_index;
+ uint32_t stripe_info_offset;
+ uint32_t stripe_info;
+ struct msm_cpp_batch_info_t batch_info;
+};
+
+struct msm_cpp_clock_settings32_t {
+ compat_long_t clock_rate;
+ uint64_t avg;
+ uint64_t inst;
+};
+
+struct msm_cpp_stream_buff_info32_t {
+ uint32_t identity;
+ uint32_t num_buffs;
+ compat_caddr_t buffer_info;
+};
+
+struct msm_pproc_queue_buf_info32_t {
+ struct msm_buf_mngr_info32_t buff_mgr_info;
+ uint8_t is_buf_dirty;
+};
+
+struct cpp_hw_info_32_t {
+ uint32_t cpp_hw_version;
+ uint32_t cpp_hw_caps;
+ compat_long_t freq_tbl[MAX_FREQ_TBL];
+ uint32_t freq_tbl_count;
+};
+
+
+#define VIDIOC_MSM_CPP_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_GET_EVENTPAYLOAD32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_GET_INST_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_LOAD_FIRMWARE32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_GET_HW_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_FLUSH_QUEUE32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_TRANSACTION_SETUP32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_GET_EVENTPAYLOAD32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_GET_INST_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_QUEUE_BUF32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_SET_CLOCK32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_POP_STREAM_BUFFER32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 17, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_IOMMU_ATTACH32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 18, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_IOMMU_DETACH32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 19, struct msm_camera_v4l2_ioctl32_t)
+
+#define VIDIOC_MSM_CPP_DELETE_STREAM_BUFF32\
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 20, struct msm_camera_v4l2_ioctl32_t)
+
+struct msm_camera_v4l2_ioctl32_t {
+ uint32_t id;
+ uint32_t len;
+ int32_t trans_code;
+ compat_caddr_t ioctl_ptr;
+};
+#endif
+
+#endif
+
diff --git a/include/net/sock.h b/include/net/sock.h
index badd144..31309b3 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2286,4 +2286,15 @@ extern int sysctl_optmem_max;
extern __u32 sysctl_wmem_default;
extern __u32 sysctl_rmem_default;
+/* SOCKEV Notifier Events */
+#define SOCKEV_SOCKET 0x00
+#define SOCKEV_BIND 0x01
+#define SOCKEV_LISTEN 0x02
+#define SOCKEV_ACCEPT 0x03
+#define SOCKEV_CONNECT 0x04
+#define SOCKEV_SHUTDOWN 0x05
+
+int sockev_register_notify(struct notifier_block *nb);
+int sockev_unregister_notify(struct notifier_block *nb);
+
#endif /* _SOCK_H */
diff --git a/include/soc/qcom/bam_dmux.h b/include/soc/qcom/bam_dmux.h
new file mode 100644
index 0000000..142b8e1
--- /dev/null
+++ b/include/soc/qcom/bam_dmux.h
@@ -0,0 +1,138 @@
+/* Copyright (c) 2011-2012, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+#ifndef _BAM_DMUX_H
+#define _BAM_DMUX_H
+
+#define BAM_DMUX_CH_NAME_MAX_LEN 20
+
+enum {
+ BAM_DMUX_DATA_RMNET_0,
+ BAM_DMUX_DATA_RMNET_1,
+ BAM_DMUX_DATA_RMNET_2,
+ BAM_DMUX_DATA_RMNET_3,
+ BAM_DMUX_DATA_RMNET_4,
+ BAM_DMUX_DATA_RMNET_5,
+ BAM_DMUX_DATA_RMNET_6,
+ BAM_DMUX_DATA_RMNET_7,
+ BAM_DMUX_USB_RMNET_0,
+ BAM_DMUX_RESERVED_0, /* 9..11 are reserved*/
+ BAM_DMUX_RESERVED_1,
+ BAM_DMUX_RESERVED_2,
+ BAM_DMUX_DATA_REV_RMNET_0,
+ BAM_DMUX_DATA_REV_RMNET_1,
+ BAM_DMUX_DATA_REV_RMNET_2,
+ BAM_DMUX_DATA_REV_RMNET_3,
+ BAM_DMUX_DATA_REV_RMNET_4,
+ BAM_DMUX_DATA_REV_RMNET_5,
+ BAM_DMUX_DATA_REV_RMNET_6,
+ BAM_DMUX_DATA_REV_RMNET_7,
+ BAM_DMUX_DATA_REV_RMNET_8,
+ BAM_DMUX_USB_DPL,
+ BAM_DMUX_NUM_CHANNELS
+};
+
+/* event type enum */
+enum {
+ BAM_DMUX_RECEIVE, /* data is struct sk_buff */
+ BAM_DMUX_WRITE_DONE, /* data is struct sk_buff */
+ BAM_DMUX_UL_CONNECTED, /* data is null */
+ BAM_DMUX_UL_DISCONNECTED, /*data is null */
+ BAM_DMUX_TRANSMIT_SIZE, /* data is maximum negotiated transmit MTU */
+};
+
+/*
+ * Open a bam_dmux logical channel
+ * id - the logical channel to open
+ * priv - private data pointer to be passed to the notify callback
+ * notify - event callback function
+ * priv - private data pointer passed to msm_bam_dmux_open()
+ * event_type - type of event
+ * data - data relevant to event. May not be valid. See event_type
+ * enum for valid cases.
+ */
+#ifdef CONFIG_MSM_BAM_DMUX
+int msm_bam_dmux_open(uint32_t id, void *priv,
+ void (*notify)(void *priv, int event_type,
+ unsigned long data));
+
+int msm_bam_dmux_close(uint32_t id);
+
+int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb);
+
+int msm_bam_dmux_kickoff_ul_wakeup(void);
+
+int msm_bam_dmux_ul_power_vote(void);
+
+int msm_bam_dmux_ul_power_unvote(void);
+
+int msm_bam_dmux_is_ch_full(uint32_t id);
+
+int msm_bam_dmux_is_ch_low(uint32_t id);
+
+int msm_bam_dmux_reg_notify(void *priv,
+ void (*notify)(void *priv, int event_type,
+ unsigned long data));
+#else
+static inline int msm_bam_dmux_open(uint32_t id, void *priv,
+ void (*notify)(void *priv, int event_type,
+ unsigned long data))
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_close(uint32_t id)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_kickoff_ul_wakeup(void)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_ul_power_vote(void)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_ul_power_unvote(void)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_is_ch_full(uint32_t id)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_is_ch_low(uint32_t id)
+{
+ return -ENODEV;
+}
+
+static inline int msm_bam_dmux_reg_notify(void *priv,
+ void (*notify)(void *priv, int event_type,
+ unsigned long data))
+{
+ return -ENODEV;
+}
+#endif
+#endif /* _BAM_DMUX_H */
diff --git a/include/soc/qcom/camera2.h b/include/soc/qcom/camera2.h
new file mode 100644
index 0000000..c529aff
--- /dev/null
+++ b/include/soc/qcom/camera2.h
@@ -0,0 +1,227 @@
+/* Copyright (c) 2011-2016, 2018, 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 <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+
+#define MAX_SPECIAL_SUPPORT_SIZE 10
+
+enum msm_camera_device_type_t {
+ MSM_CAMERA_I2C_DEVICE,
+ MSM_CAMERA_PLATFORM_DEVICE,
+ MSM_CAMERA_SPI_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;
+ uint16_t sensor_id_mask;
+};
+
+struct msm_cam_clk_info {
+ const char *clk_name;
+ long clk_rate;
+ uint32_t delay;
+};
+
+struct msm_pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+ bool use_pinctrl;
+};
+
+struct msm_cam_clk_setting {
+ struct msm_cam_clk_info *clk_info;
+ uint16_t num_clk_info;
+ uint8_t enable;
+};
+
+struct v4l2_subdev_info {
+ uint32_t code;
+ enum v4l2_colorspace colorspace;
+ uint16_t fmt;
+ uint16_t order;
+};
+
+struct msm_camera_gpio_num_info {
+ uint16_t gpio_num[SENSOR_GPIO_MAX];
+ uint8_t valid[SENSOR_GPIO_MAX];
+};
+
+struct msm_camera_gpio_conf {
+ void *cam_gpiomux_conf_tbl;
+ uint8_t cam_gpiomux_conf_tbl_size;
+ struct gpio *cam_gpio_common_tbl;
+ uint8_t cam_gpio_common_tbl_size;
+ struct gpio *cam_gpio_req_tbl;
+ uint8_t cam_gpio_req_tbl_size;
+ uint32_t gpio_no_mux;
+ uint32_t *camera_off_table;
+ uint8_t camera_off_table_size;
+ uint32_t *camera_on_table;
+ uint8_t camera_on_table_size;
+ struct msm_camera_gpio_num_info *gpio_num_info;
+};
+
+struct msm_camera_power_ctrl_t {
+ struct device *dev;
+ struct msm_sensor_power_setting *power_setting;
+ uint16_t power_setting_size;
+ struct msm_sensor_power_setting *power_down_setting;
+ uint16_t power_down_setting_size;
+ struct msm_camera_gpio_conf *gpio_conf;
+ struct camera_vreg_t *cam_vreg;
+ int num_vreg;
+ struct msm_camera_i2c_conf *i2c_conf;
+ struct clk **clk_ptr;
+ struct msm_cam_clk_info *clk_info;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+ size_t clk_info_size;
+};
+
+enum msm_camera_actuator_name {
+ MSM_ACTUATOR_MAIN_CAM_0,
+ MSM_ACTUATOR_MAIN_CAM_1,
+ MSM_ACTUATOR_MAIN_CAM_2,
+ MSM_ACTUATOR_MAIN_CAM_3,
+ MSM_ACTUATOR_MAIN_CAM_4,
+ MSM_ACTUATOR_MAIN_CAM_5,
+ MSM_ACTUATOR_WEB_CAM_0,
+ MSM_ACTUATOR_WEB_CAM_1,
+ MSM_ACTUATOR_WEB_CAM_2,
+};
+
+struct msm_actuator_info {
+ struct i2c_board_info const *board_info;
+ enum msm_camera_actuator_name cam_name;
+ int bus_id;
+ int vcm_pwd;
+ int vcm_enable;
+};
+enum msm_camera_i2c_mux_mode {
+ MODE_R,
+ MODE_L,
+ MODE_DUAL
+};
+
+struct msm_camera_i2c_conf {
+ uint8_t use_i2c_mux;
+ struct platform_device *mux_dev;
+ enum msm_camera_i2c_mux_mode i2c_mux_mode;
+};
+
+struct msm_camera_sensor_board_info {
+ const char *sensor_name;
+ const char *eeprom_name;
+ const char *actuator_name;
+ const char *ois_name;
+ const char *flash_name;
+ const char *special_support_sensors[MAX_SPECIAL_SUPPORT_SIZE];
+ int32_t special_support_size;
+ struct msm_camera_slave_info *slave_info;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ struct msm_camera_sensor_strobe_flash_data *strobe_flash_data;
+ struct msm_actuator_info *actuator_info;
+ struct msm_sensor_info_t *sensor_info;
+ const char *misc_regulator;
+ struct msm_camera_power_ctrl_t power_info;
+ struct msm_camera_sensor_slave_info *cam_slave_info;
+};
+
+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;
+};
+
+struct msm_camera_i2c_conf_array {
+ struct msm_camera_i2c_reg_conf *conf;
+ uint16_t size;
+ uint16_t delay;
+ enum msm_camera_i2c_data_type data_type;
+};
+
+struct eeprom_map_t {
+ uint32_t valid_size;
+ uint32_t addr;
+ uint32_t addr_t;
+ uint32_t data;
+ uint32_t data_t;
+ uint32_t delay;
+};
+
+struct eeprom_slave_add_t {
+ uint32_t addr;
+};
+
+struct msm_eeprom_memory_map_t {
+ struct eeprom_map_t page;
+ struct eeprom_map_t pageen;
+ struct eeprom_map_t poll;
+ struct eeprom_map_t mem;
+ struct eeprom_slave_add_t saddr;
+};
+
+struct msm_eeprom_memory_block_t {
+ struct msm_eeprom_memory_map_t *map;
+ uint32_t num_map; /* number of map blocks */
+ uint8_t *mapdata;
+ uint32_t num_data; /* size of total mapdata */
+};
+
+struct msm_eeprom_cmm_t {
+ uint32_t cmm_support;
+ uint32_t cmm_compression;
+ uint32_t cmm_offset;
+ uint32_t cmm_size;
+};
+
+struct msm_eeprom_board_info {
+ const char *eeprom_name;
+ uint16_t i2c_slaveaddr;
+ struct msm_camera_power_ctrl_t power_info;
+ struct msm_eeprom_cmm_t cmm_data;
+ enum i2c_freq_mode_t i2c_freq_mode;
+};
+
+#endif
diff --git a/include/soc/qcom/cx_ipeak.h b/include/soc/qcom/cx_ipeak.h
new file mode 100644
index 0000000..01fdcb4
--- /dev/null
+++ b/include/soc/qcom/cx_ipeak.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2016-2018, 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 __SOC_COM_CX_IPEAK_H
+#define __SOC_COM_CX_IPEAK_H
+
+struct device_node;
+struct cx_ipeak_client;
+
+#ifndef CONFIG_QCOM_CX_IPEAK
+
+static inline struct cx_ipeak_client *cx_ipeak_register(
+ struct device_node *dev_node,
+ const char *client_name)
+{
+ return NULL;
+}
+
+static inline void cx_ipeak_unregister(struct cx_ipeak_client *client)
+{
+}
+
+static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client,
+ bool vote)
+{
+ return 0;
+}
+#else
+
+struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node,
+ const char *client_name);
+void cx_ipeak_unregister(struct cx_ipeak_client *client);
+int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, bool vote);
+
+#endif
+
+#endif /*__SOC_COM_CX_IPEAK_H*/
diff --git a/include/soc/qcom/lpm_levels.h b/include/soc/qcom/lpm_levels.h
new file mode 100644
index 0000000..3665caf
--- /dev/null
+++ b/include/soc/qcom/lpm_levels.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2018, 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 __SOC_QCOM_LPM_LEVEL_H__
+#define __SOC_QCOM_LPM_LEVEL_H__
+
+struct system_pm_ops {
+ int (*enter)(struct cpumask *mask);
+ void (*exit)(void);
+ int (*update_wakeup)(bool);
+ bool (*sleep_allowed)(void);
+};
+
+uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops);
+#endif
diff --git a/include/soc/qcom/system_pm.h b/include/soc/qcom/system_pm.h
deleted file mode 100644
index 028c729..0000000
--- a/include/soc/qcom/system_pm.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * 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 __SOC_QCOM_SYS_PM_H__
-#define __SOC_QCOM_SYS_PM_H__
-
-#ifdef CONFIG_QTI_SYSTEM_PM
-int system_sleep_enter(void);
-
-void system_sleep_exit(void);
-
-bool system_sleep_allowed(void);
-
-int system_sleep_update_wakeup(void);
-#else
-static inline int system_sleep_enter(void)
-{ return -ENODEV; }
-
-static inline void system_sleep_exit(void)
-{ }
-
-static inline bool system_sleep_allowed(void)
-{ return false; }
-
-static inline int system_sleep_update_wakeup(void)
-{ return -ENODEV; }
-
-#endif /* CONFIG_QTI_SYSTEM_PM */
-
-#endif /* __SOC_QCOM_SYS_PM_H__ */
diff --git a/include/trace/events/mpm.h b/include/trace/events/mpm.h
new file mode 100644
index 0000000..433fdbf
--- /dev/null
+++ b/include/trace/events/mpm.h
@@ -0,0 +1,62 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mpm
+
+#if !defined(_TRACE_MPM_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MPM_H_
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(mpm_wakeup_enable_irqs,
+
+ TP_PROTO(uint32_t index, uint32_t irqs),
+
+ TP_ARGS(index, irqs),
+
+ TP_STRUCT__entry(
+ __field(uint32_t, index)
+ __field(uint32_t, irqs)
+ ),
+
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->irqs = irqs;
+ ),
+
+ TP_printk("index:%u wakeup_capable_irqs:0x%x",
+ __entry->index, __entry->irqs)
+);
+
+TRACE_EVENT(mpm_wakeup_pending_irqs,
+
+ TP_PROTO(uint32_t index, uint32_t irqs),
+
+ TP_ARGS(index, irqs),
+
+ TP_STRUCT__entry(
+ __field(uint32_t, index)
+ __field(uint32_t, irqs)
+ ),
+
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->irqs = irqs;
+ ),
+
+ TP_printk("index:%u wakeup_irqs:0x%x", __entry->index, __entry->irqs)
+);
+
+#endif
+#define TRACE_INCLUDE_FILE mpm
+#include <trace/define_trace.h>
diff --git a/include/trace/events/msm_cam.h b/include/trace/events/msm_cam.h
new file mode 100644
index 0000000..3fc1a29
--- /dev/null
+++ b/include/trace/events/msm_cam.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2016, 2018, 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.
+ *
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_cam
+
+#if !defined(_TRACE_MSM_VFE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_VFE_H
+
+#include "msm_isp.h"
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#define STRING_LEN 80
+
+
+TRACE_EVENT(msm_cam_string,
+ TP_PROTO(const char *str),
+ TP_ARGS(str),
+ TP_STRUCT__entry(
+ __array(char, str, STRING_LEN)
+ ),
+ TP_fast_assign(
+ strlcpy(__entry->str, str, STRING_LEN);
+ ),
+ TP_printk("msm_cam: %s", __entry->str)
+);
+
+TRACE_EVENT(msm_cam_tasklet_debug_dump,
+ TP_PROTO(struct msm_vfe_irq_debug_info tasklet_state),
+ TP_ARGS(tasklet_state),
+ TP_STRUCT__entry(
+ __field(unsigned int, vfe_id)
+ __field(unsigned int, core_id)
+ __field(unsigned int, irq_status0)
+ __field(unsigned int, irq_status1)
+ __field(unsigned int, ping_pong_status)
+ __field(long, tv_sec)
+ __field(long, tv_usec)
+ ),
+ TP_fast_assign(
+ __entry->vfe_id = tasklet_state.vfe_id;
+ __entry->irq_status0 =
+ tasklet_state.irq_status0[tasklet_state.vfe_id];
+ __entry->irq_status1 =
+ tasklet_state.irq_status1[tasklet_state.vfe_id];
+ __entry->core_id = tasklet_state.core_id;
+ __entry->ping_pong_status =
+ tasklet_state.ping_pong_status[tasklet_state.vfe_id];
+ __entry->tv_sec =
+ tasklet_state.ts.buf_time.tv_sec;
+ __entry->tv_usec =
+ tasklet_state.ts.buf_time.tv_usec;
+ ),
+ TP_printk("vfe_id %d, core %d, irq_st0 0x%x, irq_st1 0x%x\n"
+ "pi_po_st 0x%x, time %ld:%ld",
+ __entry->vfe_id,
+ __entry->core_id,
+ __entry->irq_status0,
+ __entry->irq_status1,
+ __entry->ping_pong_status,
+ __entry->tv_sec,
+ __entry->tv_usec
+ )
+);
+
+TRACE_EVENT(msm_cam_ping_pong_debug_dump,
+ TP_PROTO(struct msm_vfe_irq_debug_info ping_pong_state),
+ TP_ARGS(ping_pong_state),
+ TP_STRUCT__entry(
+ __field(unsigned int, curr_vfe_id)
+ __field(unsigned int, curr_irq_status0)
+ __field(unsigned int, curr_irq_status1)
+ __field(unsigned int, curr_ping_pong_status)
+ __field(unsigned int, othr_vfe_id)
+ __field(unsigned int, othr_irq_status0)
+ __field(unsigned int, othr_irq_status1)
+ __field(unsigned int, othr_ping_pong_status)
+ __field(long, othr_tv_sec)
+ __field(long, othr_tv_usec)
+ __field(unsigned int, core_id)
+ ),
+ TP_fast_assign(
+ __entry->curr_vfe_id =
+ ping_pong_state.vfe_id;
+ __entry->curr_irq_status0 =
+ ping_pong_state.irq_status0[ping_pong_state.vfe_id];
+ __entry->curr_irq_status1 =
+ ping_pong_state.irq_status1[ping_pong_state.vfe_id];
+ __entry->curr_ping_pong_status =
+ ping_pong_state.
+ ping_pong_status[ping_pong_state.vfe_id];
+ __entry->othr_vfe_id =
+ !ping_pong_state.vfe_id;
+ __entry->othr_irq_status0 =
+ ping_pong_state.irq_status0[!ping_pong_state.vfe_id];
+ __entry->othr_irq_status1 =
+ ping_pong_state.irq_status1[!ping_pong_state.vfe_id];
+ __entry->othr_ping_pong_status =
+ ping_pong_state.
+ ping_pong_status[!ping_pong_state.vfe_id];
+ __entry->othr_tv_sec =
+ ping_pong_state.ts.buf_time.tv_sec;
+ __entry->othr_tv_usec =
+ ping_pong_state.ts.buf_time.tv_usec;
+ __entry->core_id = ping_pong_state.core_id;
+ ),
+ TP_printk("vfe_id %d, irq_st0 0x%x, irq_st1 0x%x, pi_po_st 0x%x\n"
+ "other vfe_id %d, irq_st0 0x%x, irq_st1 0x%x\n"
+ "pi_po_st 0x%x, time %ld:%ld core %d",
+ __entry->curr_vfe_id,
+ __entry->curr_irq_status0,
+ __entry->curr_irq_status1,
+ __entry->curr_ping_pong_status,
+ __entry->othr_vfe_id,
+ __entry->othr_irq_status0,
+ __entry->othr_irq_status1,
+ __entry->othr_ping_pong_status,
+ __entry->othr_tv_sec,
+ __entry->othr_tv_usec,
+ __entry->core_id
+ )
+);
+
+#endif /* _MSM_CAM_TRACE_H */
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index fcf84e3..38f710e 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -421,4 +421,19 @@ struct drm_msm_dither {
__u32 matrix[DITHER_MATRIX_SZ];
};
+/**
+ * struct drm_msm_pa_dither - dspp dither feature structure
+ * @flags: for customizing operations
+ * @strength: dither strength
+ * @offset_en: offset enable bit
+ * @matrix: dither data matrix
+ */
+#define DRM_MSM_PA_DITHER
+struct drm_msm_pa_dither {
+ __u64 flags;
+ __u32 strength;
+ __u32 offset_en;
+ __u32 matrix[DITHER_MATRIX_SZ];
+};
+
#endif /* _MSM_DRM_PP_H_ */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index ea727f2..f8e92fbc 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -367,6 +367,8 @@
header-y += qbt1000.h
header-y += qcedev.h
header-y += qcota.h
+header-y += qg.h
+header-y += qg-profile.h
header-y += qnx4_fs.h
header-y += qnxtypes.h
header-y += qrng.h
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 39b041e..c1ea699 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -684,6 +684,14 @@
#define BTN_TRIGGER_HAPPY39 0x2e6
#define BTN_TRIGGER_HAPPY40 0x2e7
+/* Custom fingerprint gestures keys */
+#define KEY_FP_GESTURE_UP 0x2e8
+#define KEY_FP_GESTURE_DOWN 0x2e9
+#define KEY_FP_GESTURE_LEFT 0x2ea
+#define KEY_FP_GESTURE_RIGHT 0x2eb
+#define KEY_FP_GESTURE_LONG_PRESS 0x2ec
+#define KEY_FP_GESTURE_TAP 0x2ed
+
/* We avoid low common keys in module aliases so they don't get huge. */
#define KEY_MIN_INTERESTING KEY_MUTE
#define KEY_MAX 0x2ff
diff --git a/include/uapi/linux/qbt1000.h b/include/uapi/linux/qbt1000.h
index a4f0dca..0012d23 100644
--- a/include/uapi/linux/qbt1000.h
+++ b/include/uapi/linux/qbt1000.h
@@ -20,6 +20,7 @@ enum qbt1000_commands {
QBT1000_SET_FINGER_DETECT_KEY = 103,
QBT1000_CONFIGURE_POWER_KEY = 104
};
+#define QBT1000_ENABLE_GESTURES 105
/*
* enum qbt1000_fw_event -
@@ -79,6 +80,15 @@ struct qbt1000_erie_event {
};
/*
+ * struct qbt1000_enable_gestures -
+ * used to configure whether gestures detection is enabled or disabled
+ * @enable - if non-zero, gestures detection is enabled
+ */
+struct qbt1000_enable_gestures {
+ unsigned int enable;
+};
+
+/*
* struct qbt1000_set_finger_detect_key -
* used to configure the input key which is sent on finger down/up event
* @key_code - Key code to send on finger down/up. 0 disables sending key events
diff --git a/include/uapi/linux/qg-profile.h b/include/uapi/linux/qg-profile.h
new file mode 100644
index 0000000..bffddbb
--- /dev/null
+++ b/include/uapi/linux/qg-profile.h
@@ -0,0 +1,66 @@
+#ifndef __QG_PROFILE_H__
+#define __QG_PROFILE_H__
+
+#include <linux/ioctl.h>
+
+/**
+ * enum profile_table - Table index for battery profile data
+ */
+enum profile_table {
+ TABLE_SOC_OCV1,
+ TABLE_SOC_OCV2,
+ TABLE_FCC1,
+ TABLE_FCC2,
+ TABLE_Z1,
+ TABLE_Z2,
+ TABLE_Z3,
+ TABLE_Z4,
+ TABLE_Z5,
+ TABLE_Z6,
+ TABLE_Y1,
+ TABLE_Y2,
+ TABLE_Y3,
+ TABLE_Y4,
+ TABLE_Y5,
+ TABLE_Y6,
+ TABLE_MAX,
+};
+
+/**
+ * struct battery_params - Battery profile data to be exchanged
+ * @soc: SOC (state of charge) of the battery
+ * @ocv_uv: OCV (open circuit voltage) of the battery
+ * @batt_temp: Battery temperature in deci-degree
+ * @var: 'X' axis param for interpolation
+ * @table_index:Table index to be used for interpolation
+ */
+struct battery_params {
+ int soc;
+ int ocv_uv;
+ int fcc_mah;
+ int slope;
+ int var;
+ int batt_temp;
+ int table_index;
+};
+
+/* Profile MIN / MAX values */
+#define QG_MIN_SOC 0
+#define QG_MAX_SOC 10000
+#define QG_MIN_OCV_UV 3000000
+#define QG_MAX_OCV_UV 5000000
+#define QG_MIN_VAR 0
+#define QG_MAX_VAR 65535
+#define QG_MIN_FCC_MAH 100
+#define QG_MAX_FCC_MAH 16000
+#define QG_MIN_SLOPE 1
+#define QG_MAX_SLOPE 50000
+
+/* IOCTLs to query battery profile data */
+#define BPIOCXSOC _IOWR('B', 0x01, struct battery_params) /* SOC */
+#define BPIOCXOCV _IOWR('B', 0x02, struct battery_params) /* OCV */
+#define BPIOCXFCC _IOWR('B', 0x03, struct battery_params) /* FCC */
+#define BPIOCXSLOPE _IOWR('B', 0x04, struct battery_params) /* Slope */
+#define BPIOCXVAR _IOWR('B', 0x05, struct battery_params) /* All-other */
+
+#endif /* __QG_PROFILE_H__ */
diff --git a/include/uapi/linux/qg.h b/include/uapi/linux/qg.h
new file mode 100644
index 0000000..c0b2b6e
--- /dev/null
+++ b/include/uapi/linux/qg.h
@@ -0,0 +1,50 @@
+#ifndef __QG_H__
+#define __QG_H__
+
+#define MAX_FIFO_LENGTH 16
+
+enum qg {
+ QG_SOC,
+ QG_OCV_UV,
+ QG_RBAT_MOHM,
+ QG_PON_OCV_UV,
+ QG_GOOD_OCV_UV,
+ QG_ESR,
+ QG_CHARGE_COUNTER,
+ QG_FIFO_TIME_DELTA,
+ QG_RESERVED_1,
+ QG_RESERVED_2,
+ QG_RESERVED_3,
+ QG_RESERVED_4,
+ QG_RESERVED_5,
+ QG_RESERVED_6,
+ QG_RESERVED_7,
+ QG_RESERVED_8,
+ QG_RESERVED_9,
+ QG_RESERVED_10,
+ QG_MAX,
+};
+
+struct fifo_data {
+ unsigned int v;
+ unsigned int i;
+ unsigned int count;
+ unsigned int interval;
+};
+
+struct qg_param {
+ unsigned int data;
+ bool valid;
+};
+
+struct qg_kernel_data {
+ unsigned int fifo_length;
+ struct fifo_data fifo[MAX_FIFO_LENGTH];
+ struct qg_param param[QG_MAX];
+};
+
+struct qg_user_data {
+ struct qg_param param[QG_MAX];
+};
+
+#endif
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 7e72600..0b5ff0f 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -790,6 +790,9 @@ enum v4l2_mpeg_vidc_extradata {
#define V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO \
V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO
V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO = 31,
+#define V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP \
+ V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP
+ V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP = 32,
};
#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE \
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index d8a954e..8a2a315 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2,7 +2,7 @@
* Video for Linux Two header file
*
* Copyright (C) 1999-2012 the contributors
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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 as published by
@@ -594,6 +594,14 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */
#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */
#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */
+#define V4L2_PIX_FMT_SBGGR10DPCM6 v4l2_fourcc('b', 'B', 'A', '6')
+#define V4L2_PIX_FMT_SGBRG10DPCM6 v4l2_fourcc('b', 'G', 'A', '6')
+#define V4L2_PIX_FMT_SGRBG10DPCM6 v4l2_fourcc('B', 'D', '1', '6')
+#define V4L2_PIX_FMT_SRGGB10DPCM6 v4l2_fourcc('b', 'R', 'A', '6')
+#define V4L2_PIX_FMT_SBGGRPLAIN16 v4l2_fourcc('B', 'G', '1', '6')
+#define V4L2_PIX_FMT_SGBRGPLAIN16 v4l2_fourcc('G', 'B', '1', '6')
+#define V4L2_PIX_FMT_SGRBGPLAIN16 v4l2_fourcc('G', 'R', '1', '6')
+#define V4L2_PIX_FMT_SRGGBPLAIN16 v4l2_fourcc('R', 'G', '1', '6')
/* compressed formats */
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
index 1e087a1..1aa2a19 100644
--- a/include/uapi/media/Kbuild
+++ b/include/uapi/media/Kbuild
@@ -15,3 +15,16 @@
header-y += radio-iris.h
header-y += radio-iris-commands.h
header-y += cam_lrme.h
+header-y += msm_cam_sensor.h
+header-y += msm_camera.h
+header-y += msm_camsensor_sdk.h
+header-y += msm_fd.h
+header-y += msm_isp.h
+header-y += msm_jpeg.h
+header-y += msm_jpeg_dma.h
+header-y += msm_media_info.h
+header-y += msmb_camera.h
+header-y += msmb_generic_buf_mgr.h
+header-y += msmb_isp.h
+header-y += msmb_ispif.h
+header-y += msmb_pproc.h
\ No newline at end of file
diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h
new file mode 100644
index 0000000..bd5b906
--- /dev/null
+++ b/include/uapi/media/msm_cam_sensor.h
@@ -0,0 +1,635 @@
+#ifndef __UAPI_LINUX_MSM_CAM_SENSOR_H
+#define __UAPI_LINUX_MSM_CAM_SENSOR_H
+
+#include <linux/v4l2-mediabus.h>
+#include <media/msm_camsensor_sdk.h>
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+#define I2C_SEQ_REG_SETTING_MAX 5
+
+#define MSM_SENSOR_MCLK_8HZ 8000000
+#define MSM_SENSOR_MCLK_16HZ 16000000
+#define MSM_SENSOR_MCLK_24HZ 24000000
+
+#define MAX_SENSOR_NAME 32
+#define MAX_ACTUATOR_AF_TOTAL_STEPS 1024
+
+#define MAX_OIS_MOD_NAME_SIZE 32
+#define MAX_OIS_NAME_SIZE 32
+#define MAX_OIS_REG_SETTINGS 800
+
+#define MOVE_NEAR 0
+#define MOVE_FAR 1
+
+#define MSM_ACTUATOR_MOVE_SIGNED_FAR -1
+#define MSM_ACTUATOR_MOVE_SIGNED_NEAR 1
+
+#define MAX_ACTUATOR_REGION 5
+
+#define MAX_EEPROM_NAME 32
+
+#define MAX_AF_ITERATIONS 3
+#define MAX_NUMBER_OF_STEPS 47
+#define MAX_REGULATOR 5
+
+/*msm_flash_query_data_t query types*/
+#define FLASH_QUERY_CURRENT 1
+
+#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
+#define MSM_V4L2_PIX_FMT_META10 v4l2_fourcc('M', 'E', '1', '0') /* META10 */
+#define MSM_V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4')
+ /* 14 BGBG.. GRGR.. */
+#define MSM_V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4')
+ /* 14 GBGB.. RGRG.. */
+#define MSM_V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('B', 'A', '1', '4')
+ /* 14 GRGR.. BGBG.. */
+#define MSM_V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4')
+ /* 14 RGRG.. GBGB.. */
+
+enum flash_type {
+ LED_FLASH = 1,
+ STROBE_FLASH,
+ GPIO_FLASH
+};
+
+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 msm_camera_stream_type_t {
+ MSM_CAMERA_STREAM_PREVIEW,
+ MSM_CAMERA_STREAM_SNAPSHOT,
+ MSM_CAMERA_STREAM_VIDEO,
+ MSM_CAMERA_STREAM_INVALID,
+};
+
+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_CSID,
+ SUB_MODULE_CSID_3D,
+ SUB_MODULE_CSIPHY,
+ SUB_MODULE_CSIPHY_3D,
+ SUB_MODULE_OIS,
+ SUB_MODULE_EXT,
+ SUB_MODULE_IR_LED,
+ SUB_MODULE_IR_CUT,
+ SUB_MODULE_LASER_LED,
+ SUB_MODULE_MAX,
+};
+
+enum {
+ MSM_CAMERA_EFFECT_MODE_OFF,
+ MSM_CAMERA_EFFECT_MODE_MONO,
+ MSM_CAMERA_EFFECT_MODE_NEGATIVE,
+ MSM_CAMERA_EFFECT_MODE_SOLARIZE,
+ MSM_CAMERA_EFFECT_MODE_SEPIA,
+ MSM_CAMERA_EFFECT_MODE_POSTERIZE,
+ MSM_CAMERA_EFFECT_MODE_WHITEBOARD,
+ MSM_CAMERA_EFFECT_MODE_BLACKBOARD,
+ MSM_CAMERA_EFFECT_MODE_AQUA,
+ MSM_CAMERA_EFFECT_MODE_EMBOSS,
+ MSM_CAMERA_EFFECT_MODE_SKETCH,
+ MSM_CAMERA_EFFECT_MODE_NEON,
+ MSM_CAMERA_EFFECT_MODE_MAX
+};
+
+enum {
+ MSM_CAMERA_WB_MODE_AUTO,
+ MSM_CAMERA_WB_MODE_CUSTOM,
+ MSM_CAMERA_WB_MODE_INCANDESCENT,
+ MSM_CAMERA_WB_MODE_FLUORESCENT,
+ MSM_CAMERA_WB_MODE_WARM_FLUORESCENT,
+ MSM_CAMERA_WB_MODE_DAYLIGHT,
+ MSM_CAMERA_WB_MODE_CLOUDY_DAYLIGHT,
+ MSM_CAMERA_WB_MODE_TWILIGHT,
+ MSM_CAMERA_WB_MODE_SHADE,
+ MSM_CAMERA_WB_MODE_OFF,
+ MSM_CAMERA_WB_MODE_MAX
+};
+
+enum {
+ MSM_CAMERA_SCENE_MODE_OFF,
+ MSM_CAMERA_SCENE_MODE_AUTO,
+ MSM_CAMERA_SCENE_MODE_LANDSCAPE,
+ MSM_CAMERA_SCENE_MODE_SNOW,
+ MSM_CAMERA_SCENE_MODE_BEACH,
+ MSM_CAMERA_SCENE_MODE_SUNSET,
+ MSM_CAMERA_SCENE_MODE_NIGHT,
+ MSM_CAMERA_SCENE_MODE_PORTRAIT,
+ MSM_CAMERA_SCENE_MODE_BACKLIGHT,
+ MSM_CAMERA_SCENE_MODE_SPORTS,
+ MSM_CAMERA_SCENE_MODE_ANTISHAKE,
+ MSM_CAMERA_SCENE_MODE_FLOWERS,
+ MSM_CAMERA_SCENE_MODE_CANDLELIGHT,
+ MSM_CAMERA_SCENE_MODE_FIREWORKS,
+ MSM_CAMERA_SCENE_MODE_PARTY,
+ MSM_CAMERA_SCENE_MODE_NIGHT_PORTRAIT,
+ MSM_CAMERA_SCENE_MODE_THEATRE,
+ MSM_CAMERA_SCENE_MODE_ACTION,
+ MSM_CAMERA_SCENE_MODE_AR,
+ MSM_CAMERA_SCENE_MODE_FACE_PRIORITY,
+ MSM_CAMERA_SCENE_MODE_BARCODE,
+ MSM_CAMERA_SCENE_MODE_HDR,
+ MSM_CAMERA_SCENE_MODE_MAX
+};
+
+enum csid_cfg_type_t {
+ CSID_INIT,
+ CSID_CFG,
+ CSID_TESTMODE_CFG,
+ CSID_RELEASE,
+};
+
+enum csiphy_cfg_type_t {
+ CSIPHY_INIT,
+ CSIPHY_CFG,
+ CSIPHY_RELEASE,
+};
+
+enum camera_vreg_type {
+ VREG_TYPE_DEFAULT,
+ VREG_TYPE_CUSTOM,
+};
+
+enum sensor_af_t {
+ SENSOR_AF_FOCUSSED,
+ SENSOR_AF_NOT_FOCUSSED,
+};
+
+enum cci_i2c_master_t {
+ MASTER_0,
+ MASTER_1,
+ MASTER_MAX,
+};
+
+struct msm_camera_i2c_array_write_config {
+ struct msm_camera_i2c_reg_setting conf_array;
+ uint16_t slave_addr;
+};
+
+struct msm_camera_i2c_read_config {
+ uint16_t slave_addr;
+ uint16_t reg_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t data;
+};
+
+struct msm_camera_csi2_params {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csiphy_params csiphy_params;
+ uint8_t csi_clk_scale_enable;
+};
+
+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;
+ int8_t csid_core[2];
+ uint8_t csi_phy_sel;
+};
+
+struct msm_sensor_info_t {
+ char sensor_name[MAX_SENSOR_NAME];
+ uint32_t session_id;
+ int32_t subdev_id[SUB_MODULE_MAX];
+ int32_t subdev_intf[SUB_MODULE_MAX];
+ uint8_t is_mount_angle_valid;
+ uint32_t sensor_mount_angle;
+ int modes_supported;
+ enum camb_position_t position;
+};
+
+struct camera_vreg_t {
+ const char *reg_name;
+ int min_voltage;
+ int max_voltage;
+ int op_mode;
+ uint32_t delay;
+ const char *custom_vreg_name;
+ enum camera_vreg_type type;
+};
+
+struct sensorb_cfg_data {
+ int cfgtype;
+ union {
+ struct msm_sensor_info_t sensor_info;
+ struct msm_sensor_init_params sensor_init_params;
+ void *setting;
+ struct msm_sensor_i2c_sync_params sensor_i2c_sync_params;
+ } cfg;
+};
+
+struct csid_cfg_data {
+ enum csid_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ struct msm_camera_csid_params *csid_params;
+ struct msm_camera_csid_testmode_parms *csid_testmode_params;
+ } cfg;
+};
+
+struct csiphy_cfg_data {
+ enum csiphy_cfg_type_t cfgtype;
+ union {
+ struct msm_camera_csiphy_params __user *csiphy_params;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ } cfg;
+};
+
+enum eeprom_cfg_type_t {
+ CFG_EEPROM_GET_INFO,
+ CFG_EEPROM_GET_CAL_DATA,
+ CFG_EEPROM_READ_CAL_DATA,
+ CFG_EEPROM_WRITE_DATA,
+ CFG_EEPROM_GET_MM_INFO,
+ CFG_EEPROM_INIT,
+};
+
+struct eeprom_get_t {
+ uint32_t num_bytes;
+};
+
+struct eeprom_read_t {
+ uint8_t *dbuffer;
+ uint32_t num_bytes;
+};
+
+struct eeprom_write_t {
+ uint8_t *dbuffer;
+ uint32_t num_bytes;
+};
+
+struct eeprom_get_cmm_t {
+ uint32_t cmm_support;
+ uint32_t cmm_compression;
+ uint32_t cmm_size;
+};
+
+struct msm_eeprom_info_t {
+ struct msm_sensor_power_setting_array *power_setting_array;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ struct msm_eeprom_memory_map_array *mem_map_array;
+};
+
+struct msm_ir_led_cfg_data_t {
+ enum msm_ir_led_cfg_type_t cfg_type;
+ int32_t pwm_duty_on_ns;
+ int32_t pwm_period_ns;
+};
+
+struct msm_ir_cut_cfg_data_t {
+ enum msm_ir_cut_cfg_type_t cfg_type;
+};
+
+struct msm_laser_led_cfg_data_t {
+ enum msm_laser_led_cfg_type_t cfg_type;
+ void __user *setting;
+ void __user *debug_reg;
+ uint32_t debug_reg_size;
+ uint16_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+};
+
+struct msm_eeprom_cfg_data {
+ enum eeprom_cfg_type_t cfgtype;
+ uint8_t is_supported;
+ union {
+ char eeprom_name[MAX_EEPROM_NAME];
+ struct eeprom_get_t get_data;
+ struct eeprom_read_t read_data;
+ struct eeprom_write_t write_data;
+ struct eeprom_get_cmm_t get_cmm_data;
+ struct msm_eeprom_info_t eeprom_info;
+ } cfg;
+};
+
+enum msm_sensor_cfg_type_t {
+ CFG_SET_SLAVE_INFO,
+ CFG_SLAVE_READ_I2C,
+ CFG_WRITE_I2C_ARRAY,
+ CFG_SLAVE_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,
+ CFG_SET_INIT_SETTING,
+ CFG_SET_RESOLUTION,
+ CFG_SET_STOP_STREAM,
+ CFG_SET_START_STREAM,
+ CFG_SET_SATURATION,
+ CFG_SET_CONTRAST,
+ CFG_SET_SHARPNESS,
+ CFG_SET_ISO,
+ CFG_SET_EXPOSURE_COMPENSATION,
+ CFG_SET_ANTIBANDING,
+ CFG_SET_BESTSHOT_MODE,
+ CFG_SET_EFFECT,
+ CFG_SET_WHITE_BALANCE,
+ CFG_SET_AUTOFOCUS,
+ CFG_CANCEL_AUTOFOCUS,
+ CFG_SET_STREAM_TYPE,
+ CFG_SET_I2C_SYNC_PARAM,
+ CFG_WRITE_I2C_ARRAY_ASYNC,
+ CFG_WRITE_I2C_ARRAY_SYNC,
+ CFG_WRITE_I2C_ARRAY_SYNC_BLOCK,
+};
+
+enum msm_actuator_cfg_type_t {
+ CFG_GET_ACTUATOR_INFO,
+ CFG_SET_ACTUATOR_INFO,
+ CFG_SET_DEFAULT_FOCUS,
+ CFG_MOVE_FOCUS,
+ CFG_SET_POSITION,
+ CFG_ACTUATOR_POWERDOWN,
+ CFG_ACTUATOR_POWERUP,
+ CFG_ACTUATOR_INIT,
+};
+
+struct msm_ois_opcode {
+ uint32_t prog;
+ uint32_t coeff;
+ uint32_t pheripheral;
+ uint32_t memory;
+};
+
+enum msm_ois_cfg_type_t {
+ CFG_OIS_INIT,
+ CFG_OIS_POWERDOWN,
+ CFG_OIS_POWERUP,
+ CFG_OIS_CONTROL,
+ CFG_OIS_I2C_WRITE_SEQ_TABLE,
+};
+
+enum msm_ois_cfg_download_type_t {
+ CFG_OIS_DOWNLOAD,
+ CFG_OIS_DATA_CONFIG,
+};
+
+enum msm_ois_i2c_operation {
+ MSM_OIS_WRITE = 0,
+ MSM_OIS_POLL,
+ MSM_OIS_READ,
+};
+#define MSM_OIS_READ MSM_OIS_READ
+
+struct reg_settings_ois_t {
+ uint16_t reg_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint32_t reg_data;
+ enum msm_camera_i2c_data_type data_type;
+ enum msm_ois_i2c_operation i2c_operation;
+ uint32_t delay;
+};
+
+struct msm_ois_params_t {
+ uint16_t data_size;
+ uint16_t setting_size;
+ uint32_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum msm_camera_i2c_reg_addr_type i2c_addr_type;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ struct reg_settings_ois_t __user *settings;
+};
+
+struct msm_ois_set_info_t {
+ struct msm_ois_params_t ois_params;
+};
+
+struct msm_actuator_move_params_t {
+ int8_t dir;
+ int8_t sign_dir;
+ int16_t dest_step_pos;
+ int32_t num_steps;
+ uint16_t curr_lens_pos;
+ struct damping_params_t __user *ringing_params;
+};
+
+struct msm_actuator_tuning_params_t {
+ int16_t initial_code;
+ uint16_t pwd_step;
+ uint16_t region_size;
+ uint32_t total_steps;
+ struct region_params_t __user *region_params;
+};
+
+struct park_lens_data_t {
+ uint32_t damping_step;
+ uint32_t damping_delay;
+ uint32_t hw_params;
+ uint32_t max_step;
+};
+
+struct msm_actuator_params_t {
+ enum actuator_type act_type;
+ uint8_t reg_tbl_size;
+ uint16_t data_size;
+ uint16_t init_setting_size;
+ uint32_t i2c_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ enum msm_camera_i2c_reg_addr_type i2c_addr_type;
+ enum msm_camera_i2c_data_type i2c_data_type;
+ struct msm_actuator_reg_params_t __user *reg_tbl_params;
+ struct reg_settings_t __user *init_settings;
+ struct park_lens_data_t park_lens;
+};
+
+struct msm_actuator_set_info_t {
+ struct msm_actuator_params_t actuator_params;
+ struct msm_actuator_tuning_params_t af_tuning_params;
+};
+
+struct msm_actuator_get_info_t {
+ uint32_t focal_length_num;
+ uint32_t focal_length_den;
+ uint32_t f_number_num;
+ uint32_t f_number_den;
+ uint32_t f_pix_num;
+ uint32_t f_pix_den;
+ uint32_t total_f_dist_num;
+ uint32_t total_f_dist_den;
+ uint32_t hor_view_angle_num;
+ uint32_t hor_view_angle_den;
+ uint32_t ver_view_angle_num;
+ uint32_t ver_view_angle_den;
+};
+
+enum af_camera_name {
+ ACTUATOR_MAIN_CAM_0,
+ ACTUATOR_MAIN_CAM_1,
+ ACTUATOR_MAIN_CAM_2,
+ ACTUATOR_MAIN_CAM_3,
+ ACTUATOR_MAIN_CAM_4,
+ ACTUATOR_MAIN_CAM_5,
+ ACTUATOR_WEB_CAM_0,
+ ACTUATOR_WEB_CAM_1,
+ ACTUATOR_WEB_CAM_2,
+};
+
+struct msm_ois_slave_info {
+ char ois_name[MAX_OIS_NAME_SIZE];
+ uint32_t i2c_addr;
+ struct msm_ois_opcode opcode;
+};
+struct msm_ois_cfg_data {
+ int cfgtype;
+ union {
+ struct msm_ois_set_info_t set_info;
+ struct msm_camera_i2c_seq_reg_setting *settings;
+ } cfg;
+};
+
+struct msm_ois_cfg_download_data {
+ int cfgtype;
+ struct msm_ois_slave_info slave_info;
+};
+
+struct msm_actuator_set_position_t {
+ uint16_t number_of_steps;
+ uint32_t hw_params;
+ uint16_t pos[MAX_NUMBER_OF_STEPS];
+ uint16_t delay[MAX_NUMBER_OF_STEPS];
+};
+
+struct msm_actuator_cfg_data {
+ int cfgtype;
+ uint8_t is_af_supported;
+ union {
+ struct msm_actuator_move_params_t move;
+ struct msm_actuator_set_info_t set_info;
+ struct msm_actuator_get_info_t get_info;
+ struct msm_actuator_set_position_t setpos;
+ enum af_camera_name cam_name;
+ } cfg;
+};
+
+enum msm_camera_led_config_t {
+ MSM_CAMERA_LED_OFF,
+ MSM_CAMERA_LED_LOW,
+ MSM_CAMERA_LED_HIGH,
+ MSM_CAMERA_LED_INIT,
+ MSM_CAMERA_LED_RELEASE,
+};
+
+struct msm_camera_led_cfg_t {
+ enum msm_camera_led_config_t cfgtype;
+ int32_t torch_current[MAX_LED_TRIGGERS];
+ int32_t flash_current[MAX_LED_TRIGGERS];
+ int32_t flash_duration[MAX_LED_TRIGGERS];
+};
+
+struct msm_flash_init_info_t {
+ enum msm_flash_driver_type flash_driver_type;
+ uint32_t slave_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ struct msm_sensor_power_setting_array __user *power_setting_array;
+ struct msm_camera_i2c_reg_setting_array __user *settings;
+};
+
+struct msm_flash_cfg_data_t {
+ enum msm_flash_cfg_type_t cfg_type;
+ int32_t flash_current[MAX_LED_TRIGGERS];
+ int32_t flash_duration[MAX_LED_TRIGGERS];
+ union {
+ struct msm_flash_init_info_t *flash_init_info;
+ struct msm_camera_i2c_reg_setting_array __user *settings;
+ } cfg;
+};
+
+struct msm_flash_query_data_t {
+ int32_t flags;
+ int32_t query_type;
+ int32_t max_avail_curr;
+};
+
+/* sensor init structures and enums */
+enum msm_sensor_init_cfg_type_t {
+ CFG_SINIT_PROBE,
+ CFG_SINIT_PROBE_DONE,
+ CFG_SINIT_PROBE_WAIT_DONE,
+};
+
+struct sensor_init_cfg_data {
+ enum msm_sensor_init_cfg_type_t cfgtype;
+ struct msm_sensor_info_t probed_info;
+ char entity_name[MAX_SENSOR_NAME];
+ union {
+ void *setting;
+ } cfg;
+};
+
+#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 csiphy_cfg_data)
+
+#define VIDIOC_MSM_CSID_IO_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csid_cfg_data)
+
+#define VIDIOC_MSM_ACTUATOR_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_actuator_cfg_data)
+
+#define VIDIOC_MSM_FLASH_LED_DATA_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct msm_camera_led_cfg_t)
+
+#define VIDIOC_MSM_EEPROM_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_eeprom_cfg_data)
+
+#define VIDIOC_MSM_SENSOR_GET_AF_STATUS \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, uint32_t)
+
+#define VIDIOC_MSM_SENSOR_INIT_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct sensor_init_cfg_data)
+
+#define VIDIOC_MSM_OIS_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_ois_cfg_data)
+
+#define VIDIOC_MSM_FLASH_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_flash_cfg_data_t)
+
+#define VIDIOC_MSM_OIS_CFG_DOWNLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ois_cfg_download_data)
+
+#define VIDIOC_MSM_FLASH_QUERY_DATA \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_flash_query_data_t)
+
+#define VIDIOC_MSM_IR_LED_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_led_cfg_data_t)
+
+#define VIDIOC_MSM_IR_CUT_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t)
+
+#define VIDIOC_MSM_LASER_LED_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_laser_led_cfg_data_t)
+
+#endif
+
diff --git a/include/uapi/media/msm_camera.h b/include/uapi/media/msm_camera.h
new file mode 100644
index 0000000..d282586
--- /dev/null
+++ b/include/uapi/media/msm_camera.h
@@ -0,0 +1,2229 @@
+/* Copyright (c) 2009-2012, 2014-2016, 2018, 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 __UAPI_MSM_CAMERA_H
+#define __UAPI_MSM_CAMERA_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <linux/msm_ion.h>
+
+#define BIT(nr) (1UL << (nr))
+
+#define MSM_CAM_IOCTL_MAGIC 'm'
+
+#define MAX_SERVER_PAYLOAD_LENGTH 8192
+
+#define MSM_CAM_IOCTL_GET_SENSOR_INFO \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 1, struct msm_camsensor_info *)
+
+#define MSM_CAM_IOCTL_REGISTER_PMEM \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 2, struct msm_pmem_info *)
+
+#define MSM_CAM_IOCTL_UNREGISTER_PMEM \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 3, unsigned int)
+
+#define MSM_CAM_IOCTL_CTRL_COMMAND \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 4, struct msm_ctrl_cmd *)
+
+#define MSM_CAM_IOCTL_CONFIG_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 5, struct msm_camera_vfe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_GET_STATS \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 6, struct msm_camera_stats_event_ctrl *)
+
+#define MSM_CAM_IOCTL_GETFRAME \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 7, struct msm_camera_get_frame *)
+
+#define MSM_CAM_IOCTL_ENABLE_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 8, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_CTRL_CMD_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 9, struct camera_cmd *)
+
+#define MSM_CAM_IOCTL_CONFIG_CMD \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 10, struct camera_cmd *)
+
+#define MSM_CAM_IOCTL_DISABLE_VFE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 11, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_PAD_REG_RESET2 \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 12, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_VFE_APPS_RESET \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 13, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 14, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_RELEASE_STATS_BUFFER \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 15, struct msm_stats_buf *)
+
+#define MSM_CAM_IOCTL_AXI_CONFIG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 16, struct msm_camera_vfe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_GET_PICTURE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 17, struct msm_frame *)
+
+#define MSM_CAM_IOCTL_SET_CROP \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 18, struct crop_info *)
+
+#define MSM_CAM_IOCTL_PICT_PP \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 19, uint8_t *)
+
+#define MSM_CAM_IOCTL_PICT_PP_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 20, struct msm_snapshot_pp_status *)
+
+#define MSM_CAM_IOCTL_SENSOR_IO_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 21, struct sensor_cfg_data *)
+
+#define MSM_CAM_IOCTL_FLASH_LED_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 22, unsigned int *)
+
+#define MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME \
+ _IO(MSM_CAM_IOCTL_MAGIC, 23)
+
+#define MSM_CAM_IOCTL_CTRL_COMMAND_2 \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *)
+
+#define MSM_CAM_IOCTL_AF_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 25, struct msm_ctrl_cmt_t *)
+
+#define MSM_CAM_IOCTL_AF_CTRL_DONE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *)
+
+#define MSM_CAM_IOCTL_CONFIG_VPE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_camera_vpe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_AXI_VPE_CONFIG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 28, struct msm_camera_vpe_cfg_cmd *)
+
+#define MSM_CAM_IOCTL_STROBE_FLASH_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 29, uint32_t *)
+
+#define MSM_CAM_IOCTL_STROBE_FLASH_CHARGE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 30, uint32_t *)
+
+#define MSM_CAM_IOCTL_STROBE_FLASH_RELEASE \
+ _IO(MSM_CAM_IOCTL_MAGIC, 31)
+
+#define MSM_CAM_IOCTL_FLASH_CTRL \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 32, struct flash_ctrl_data *)
+
+#define MSM_CAM_IOCTL_ERROR_CONFIG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 33, uint32_t *)
+
+#define MSM_CAM_IOCTL_ABORT_CAPTURE \
+ _IO(MSM_CAM_IOCTL_MAGIC, 34)
+
+#define MSM_CAM_IOCTL_SET_FD_ROI \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 35, struct fd_roi_info *)
+
+#define MSM_CAM_IOCTL_GET_CAMERA_INFO \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 36, struct msm_camera_info *)
+
+#define MSM_CAM_IOCTL_UNBLOCK_POLL_PIC_FRAME \
+ _IO(MSM_CAM_IOCTL_MAGIC, 37)
+
+#define MSM_CAM_IOCTL_RELEASE_PIC_BUFFER \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 38, struct camera_enable_cmd *)
+
+#define MSM_CAM_IOCTL_PUT_ST_FRAME \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 39, struct msm_camera_st_frame *)
+
+#define MSM_CAM_IOCTL_V4L2_EVT_NOTIFY \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 40, struct v4l2_event_and_payload)
+
+#define MSM_CAM_IOCTL_SET_MEM_MAP_INFO \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 41, struct msm_mem_map_info *)
+
+#define MSM_CAM_IOCTL_ACTUATOR_IO_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 42, struct msm_actuator_cfg_data *)
+
+#define MSM_CAM_IOCTL_MCTL_POST_PROC \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 43, struct msm_mctl_post_proc_cmd *)
+
+#define MSM_CAM_IOCTL_RESERVE_FREE_FRAME \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 44, struct msm_cam_evt_divert_frame *)
+
+#define MSM_CAM_IOCTL_RELEASE_FREE_FRAME \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 45, struct msm_cam_evt_divert_frame *)
+
+#define MSM_CAM_IOCTL_PICT_PP_DIVERT_DONE \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 46, struct msm_pp_frame *)
+
+#define MSM_CAM_IOCTL_SENSOR_V4l2_S_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 47, struct v4l2_control)
+
+#define MSM_CAM_IOCTL_SENSOR_V4l2_QUERY_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 48, struct v4l2_queryctrl)
+
+#define MSM_CAM_IOCTL_GET_KERNEL_SYSTEM_TIME \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 49, struct timeval *)
+
+#define MSM_CAM_IOCTL_SET_VFE_OUTPUT_TYPE \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 50, uint32_t *)
+
+#define MSM_CAM_IOCTL_MCTL_DIVERT_DONE \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 51, struct msm_cam_evt_divert_frame *)
+
+#define MSM_CAM_IOCTL_GET_ACTUATOR_INFO \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 52, struct msm_actuator_cfg_data *)
+
+#define MSM_CAM_IOCTL_EEPROM_IO_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 53, struct msm_eeprom_cfg_data *)
+
+#define MSM_CAM_IOCTL_ISPIF_IO_CFG \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 54, struct ispif_cfg_data *)
+
+#define MSM_CAM_IOCTL_STATS_REQBUF \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 55, struct msm_stats_reqbuf *)
+
+#define MSM_CAM_IOCTL_STATS_ENQUEUEBUF \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 56, struct msm_stats_buf_info *)
+
+#define MSM_CAM_IOCTL_STATS_FLUSH_BUFQ \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 57, struct msm_stats_flush_bufq *)
+
+#define MSM_CAM_IOCTL_SET_MCTL_SDEV \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 58, struct msm_mctl_set_sdev_data *)
+
+#define MSM_CAM_IOCTL_UNSET_MCTL_SDEV \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 59, struct msm_mctl_set_sdev_data *)
+
+#define MSM_CAM_IOCTL_GET_INST_HANDLE \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 60, uint32_t *)
+
+#define MSM_CAM_IOCTL_STATS_UNREG_BUF \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 61, struct msm_stats_flush_bufq *)
+
+#define MSM_CAM_IOCTL_CSIC_IO_CFG \
+ _IOWR(MSM_CAM_IOCTL_MAGIC, 62, struct csic_cfg_data *)
+
+#define MSM_CAM_IOCTL_CSID_IO_CFG \
+ _IOWR(MSM_CAM_IOCTL_MAGIC, 63, struct csid_cfg_data *)
+
+#define MSM_CAM_IOCTL_CSIPHY_IO_CFG \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 64, struct csiphy_cfg_data *)
+
+#define MSM_CAM_IOCTL_OEM \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 65, struct sensor_cfg_data *)
+
+#define MSM_CAM_IOCTL_AXI_INIT \
+ _IOWR(MSM_CAM_IOCTL_MAGIC, 66, uint8_t *)
+
+#define MSM_CAM_IOCTL_AXI_RELEASE \
+ _IO(MSM_CAM_IOCTL_MAGIC, 67)
+
+struct v4l2_event_and_payload {
+ struct v4l2_event evt;
+ uint32_t payload_length;
+ uint32_t transaction_id;
+ void *payload;
+};
+
+struct msm_stats_reqbuf {
+ int num_buf; /* how many buffers requested */
+ int stats_type; /* stats type */
+};
+
+struct msm_stats_flush_bufq {
+ int stats_type; /* enum msm_stats_enum_type */
+};
+
+struct msm_mctl_pp_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+struct msm_mctl_post_proc_cmd {
+ int32_t type;
+ struct msm_mctl_pp_cmd cmd;
+};
+
+#define MSM_CAMERA_LED_OFF 0
+#define MSM_CAMERA_LED_LOW 1
+#define MSM_CAMERA_LED_HIGH 2
+#define MSM_CAMERA_LED_INIT 3
+#define MSM_CAMERA_LED_RELEASE 4
+
+#define MSM_CAMERA_STROBE_FLASH_NONE 0
+#define MSM_CAMERA_STROBE_FLASH_XENON 1
+
+#define MSM_MAX_CAMERA_SENSORS 5
+#define MAX_SENSOR_NAME 32
+#define MAX_CAM_NAME_SIZE 32
+#define MAX_ACT_MOD_NAME_SIZE 32
+#define MAX_ACT_NAME_SIZE 32
+#define NUM_ACTUATOR_DIR 2
+#define MAX_ACTUATOR_SCENARIO 8
+#define MAX_ACTUATOR_REGION 5
+#define MAX_ACTUATOR_INIT_SET 12
+#define MAX_ACTUATOR_TYPE_SIZE 32
+#define MAX_ACTUATOR_REG_TBL_SIZE 8
+
+
+#define MSM_MAX_CAMERA_CONFIGS 2
+
+#define PP_SNAP 0x01
+#define PP_RAW_SNAP ((0x01)<<1)
+#define PP_PREV ((0x01)<<2)
+#define PP_THUMB ((0x01)<<3)
+#define PP_MASK (PP_SNAP|PP_RAW_SNAP|PP_PREV|PP_THUMB)
+
+#define MSM_CAM_CTRL_CMD_DONE 0
+#define MSM_CAM_SENSOR_VFE_CMD 1
+
+/* Should be same as VIDEO_MAX_PLANES in videodev2.h */
+#define MAX_PLANES 8
+
+/*****************************************************
+ * structure
+ *****************************************************/
+
+/* define five type of structures for userspace <==> kernel
+ * space communication:
+ * command 1 - 2 are from userspace ==> kernel
+ * command 3 - 4 are from kernel ==> userspace
+ *
+ * 1. control command: control command(from control thread),
+ * control status (from config thread);
+ */
+struct msm_ctrl_cmd {
+ uint16_t type;
+ uint16_t length;
+ void *value;
+ uint16_t status;
+ uint32_t timeout_ms;
+ int resp_fd; /* FIXME: to be used by the kernel, pass-through for now */
+ int vnode_id; /* video dev id. Can we overload resp_fd? */
+ int queue_idx;
+ uint32_t evt_id;
+ uint32_t stream_type; /* used to pass value to qcamera server */
+ int config_ident; /*used as identifier for config node*/
+};
+
+struct msm_cam_evt_msg {
+ unsigned short type; /* 1 == event (RPC), 0 == message (adsp) */
+ unsigned short msg_id;
+ unsigned int len; /* size in, number of bytes out */
+ uint32_t frame_id;
+ void *data;
+ struct timespec timestamp;
+};
+
+struct msm_pp_frame_sp {
+ /* phy addr of the buffer */
+ unsigned long phy_addr;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+ /* buffer length */
+ uint32_t length;
+ int32_t fd;
+ uint32_t addr_offset;
+ /* mapped addr */
+ unsigned long vaddr;
+};
+
+struct msm_pp_frame_mp {
+ /* phy addr of the plane */
+ unsigned long phy_addr;
+ /* offset of plane data */
+ uint32_t data_offset;
+ /* plane length */
+ uint32_t length;
+ int32_t fd;
+ uint32_t addr_offset;
+ /* mapped addr */
+ unsigned long vaddr;
+};
+
+struct msm_pp_frame {
+ uint32_t handle; /* stores vb cookie */
+ uint32_t frame_id;
+ unsigned short buf_idx;
+ int path;
+ unsigned short image_type;
+ unsigned short num_planes; /* 1 for sp */
+ struct timeval timestamp;
+ union {
+ struct msm_pp_frame_sp sp;
+ struct msm_pp_frame_mp mp[MAX_PLANES];
+ };
+ int node_type;
+ uint32_t inst_handle;
+};
+
+struct msm_pp_crop {
+ uint32_t src_x;
+ uint32_t src_y;
+ uint32_t src_w;
+ uint32_t src_h;
+ uint32_t dst_x;
+ uint32_t dst_y;
+ uint32_t dst_w;
+ uint32_t dst_h;
+ uint8_t update_flag;
+};
+
+struct msm_mctl_pp_frame_cmd {
+ uint32_t cookie;
+ uint8_t vpe_output_action;
+ struct msm_pp_frame src_frame;
+ struct msm_pp_frame dest_frame;
+ struct msm_pp_crop crop;
+ int path;
+};
+
+struct msm_cam_evt_divert_frame {
+ unsigned short image_mode;
+ unsigned short op_mode;
+ unsigned short inst_idx;
+ unsigned short node_idx;
+ struct msm_pp_frame frame;
+ int do_pp;
+};
+
+struct msm_mctl_pp_cmd_ack_event {
+ uint32_t cmd; /* VPE_CMD_ZOOM? */
+ int status; /* 0 done, < 0 err */
+ uint32_t cookie; /* daemon's cookie */
+};
+
+struct msm_mctl_pp_event_info {
+ int32_t event;
+ union {
+ struct msm_mctl_pp_cmd_ack_event ack;
+ };
+};
+
+struct msm_isp_event_ctrl {
+ unsigned short resptype;
+ union {
+ struct msm_cam_evt_msg isp_msg;
+ struct msm_ctrl_cmd ctrl;
+ struct msm_cam_evt_divert_frame div_frame;
+ struct msm_mctl_pp_event_info pp_event_info;
+ } isp_data;
+};
+
+#define MSM_CAM_RESP_CTRL 0
+#define MSM_CAM_RESP_STAT_EVT_MSG 1
+#define MSM_CAM_RESP_STEREO_OP_1 2
+#define MSM_CAM_RESP_STEREO_OP_2 3
+#define MSM_CAM_RESP_V4L2 4
+#define MSM_CAM_RESP_DIV_FRAME_EVT_MSG 5
+#define MSM_CAM_RESP_DONE_EVENT 6
+#define MSM_CAM_RESP_MCTL_PP_EVENT 7
+#define MSM_CAM_RESP_MAX 8
+
+#define MSM_CAM_APP_NOTIFY_EVENT 0
+#define MSM_CAM_APP_NOTIFY_ERROR_EVENT 1
+
+/* this one is used to send ctrl/status up to config thread */
+
+struct msm_stats_event_ctrl {
+ /* 0 - ctrl_cmd from control thread,
+ * 1 - stats/event kernel,
+ * 2 - V4L control or read request
+ */
+ int resptype;
+ int timeout_ms;
+ struct msm_ctrl_cmd ctrl_cmd;
+ /* struct vfe_event_t stats_event; */
+ struct msm_cam_evt_msg stats_event;
+};
+
+/* 2. config command: config command(from config thread); */
+struct msm_camera_cfg_cmd {
+ /* what to config:
+ * 1 - sensor config, 2 - vfe config
+ */
+ uint16_t cfg_type;
+
+ /* sensor config type */
+ uint16_t cmd_type;
+ uint16_t queue;
+ uint16_t length;
+ void *value;
+};
+
+#define CMD_GENERAL 0
+#define CMD_AXI_CFG_OUT1 1
+#define CMD_AXI_CFG_SNAP_O1_AND_O2 2
+#define CMD_AXI_CFG_OUT2 3
+#define CMD_PICT_T_AXI_CFG 4
+#define CMD_PICT_M_AXI_CFG 5
+#define CMD_RAW_PICT_AXI_CFG 6
+
+#define CMD_FRAME_BUF_RELEASE 7
+#define CMD_PREV_BUF_CFG 8
+#define CMD_SNAP_BUF_RELEASE 9
+#define CMD_SNAP_BUF_CFG 10
+#define CMD_STATS_DISABLE 11
+#define CMD_STATS_AEC_AWB_ENABLE 12
+#define CMD_STATS_AF_ENABLE 13
+#define CMD_STATS_AEC_ENABLE 14
+#define CMD_STATS_AWB_ENABLE 15
+#define CMD_STATS_ENABLE 16
+
+#define CMD_STATS_AXI_CFG 17
+#define CMD_STATS_AEC_AXI_CFG 18
+#define CMD_STATS_AF_AXI_CFG 19
+#define CMD_STATS_AWB_AXI_CFG 20
+#define CMD_STATS_RS_AXI_CFG 21
+#define CMD_STATS_CS_AXI_CFG 22
+#define CMD_STATS_IHIST_AXI_CFG 23
+#define CMD_STATS_SKIN_AXI_CFG 24
+
+#define CMD_STATS_BUF_RELEASE 25
+#define CMD_STATS_AEC_BUF_RELEASE 26
+#define CMD_STATS_AF_BUF_RELEASE 27
+#define CMD_STATS_AWB_BUF_RELEASE 28
+#define CMD_STATS_RS_BUF_RELEASE 29
+#define CMD_STATS_CS_BUF_RELEASE 30
+#define CMD_STATS_IHIST_BUF_RELEASE 31
+#define CMD_STATS_SKIN_BUF_RELEASE 32
+
+#define UPDATE_STATS_INVALID 33
+#define CMD_AXI_CFG_SNAP_GEMINI 34
+#define CMD_AXI_CFG_SNAP 35
+#define CMD_AXI_CFG_PREVIEW 36
+#define CMD_AXI_CFG_VIDEO 37
+
+#define CMD_STATS_IHIST_ENABLE 38
+#define CMD_STATS_RS_ENABLE 39
+#define CMD_STATS_CS_ENABLE 40
+#define CMD_VPE 41
+#define CMD_AXI_CFG_VPE 42
+#define CMD_AXI_CFG_ZSL 43
+#define CMD_AXI_CFG_SNAP_VPE 44
+#define CMD_AXI_CFG_SNAP_THUMB_VPE 45
+
+#define CMD_CONFIG_PING_ADDR 46
+#define CMD_CONFIG_PONG_ADDR 47
+#define CMD_CONFIG_FREE_BUF_ADDR 48
+#define CMD_AXI_CFG_ZSL_ALL_CHNLS 49
+#define CMD_AXI_CFG_VIDEO_ALL_CHNLS 50
+#define CMD_VFE_BUFFER_RELEASE 51
+#define CMD_VFE_PROCESS_IRQ 52
+#define CMD_STATS_BG_ENABLE 53
+#define CMD_STATS_BF_ENABLE 54
+#define CMD_STATS_BHIST_ENABLE 55
+#define CMD_STATS_BG_BUF_RELEASE 56
+#define CMD_STATS_BF_BUF_RELEASE 57
+#define CMD_STATS_BHIST_BUF_RELEASE 58
+#define CMD_VFE_PIX_SOF_COUNT_UPDATE 59
+#define CMD_VFE_COUNT_PIX_SOF_ENABLE 60
+#define CMD_STATS_BE_ENABLE 61
+#define CMD_STATS_BE_BUF_RELEASE 62
+
+#define CMD_AXI_CFG_PRIM BIT(8)
+#define CMD_AXI_CFG_PRIM_ALL_CHNLS BIT(9)
+#define CMD_AXI_CFG_SEC BIT(10)
+#define CMD_AXI_CFG_SEC_ALL_CHNLS BIT(11)
+#define CMD_AXI_CFG_TERT1 BIT(12)
+#define CMD_AXI_CFG_TERT2 BIT(13)
+
+#define CMD_AXI_START 0xE1
+#define CMD_AXI_STOP 0xE2
+#define CMD_AXI_RESET 0xE3
+#define CMD_AXI_ABORT 0xE4
+
+
+
+#define AXI_CMD_PREVIEW BIT(0)
+#define AXI_CMD_CAPTURE BIT(1)
+#define AXI_CMD_RECORD BIT(2)
+#define AXI_CMD_ZSL BIT(3)
+#define AXI_CMD_RAW_CAPTURE BIT(4)
+#define AXI_CMD_LIVESHOT BIT(5)
+
+/* vfe config command: config command(from config thread)*/
+struct msm_vfe_cfg_cmd {
+ int cmd_type;
+ uint16_t length;
+ void *value;
+};
+
+struct msm_vpe_cfg_cmd {
+ int cmd_type;
+ uint16_t length;
+ void *value;
+};
+
+#define MAX_CAMERA_ENABLE_NAME_LEN 32
+struct camera_enable_cmd {
+ char name[MAX_CAMERA_ENABLE_NAME_LEN];
+};
+
+#define MSM_PMEM_OUTPUT1 0
+#define MSM_PMEM_OUTPUT2 1
+#define MSM_PMEM_OUTPUT1_OUTPUT2 2
+#define MSM_PMEM_THUMBNAIL 3
+#define MSM_PMEM_MAINIMG 4
+#define MSM_PMEM_RAW_MAINIMG 5
+#define MSM_PMEM_AEC_AWB 6
+#define MSM_PMEM_AF 7
+#define MSM_PMEM_AEC 8
+#define MSM_PMEM_AWB 9
+#define MSM_PMEM_RS 10
+#define MSM_PMEM_CS 11
+#define MSM_PMEM_IHIST 12
+#define MSM_PMEM_SKIN 13
+#define MSM_PMEM_VIDEO 14
+#define MSM_PMEM_PREVIEW 15
+#define MSM_PMEM_VIDEO_VPE 16
+#define MSM_PMEM_C2D 17
+#define MSM_PMEM_MAINIMG_VPE 18
+#define MSM_PMEM_THUMBNAIL_VPE 19
+#define MSM_PMEM_BAYER_GRID 20
+#define MSM_PMEM_BAYER_FOCUS 21
+#define MSM_PMEM_BAYER_HIST 22
+#define MSM_PMEM_BAYER_EXPOSURE 23
+#define MSM_PMEM_MAX 24
+
+#define STAT_AEAW 0
+#define STAT_AEC 1
+#define STAT_AF 2
+#define STAT_AWB 3
+#define STAT_RS 4
+#define STAT_CS 5
+#define STAT_IHIST 6
+#define STAT_SKIN 7
+#define STAT_BG 8
+#define STAT_BF 9
+#define STAT_BE 10
+#define STAT_BHIST 11
+#define STAT_MAX 12
+
+#define FRAME_PREVIEW_OUTPUT1 0
+#define FRAME_PREVIEW_OUTPUT2 1
+#define FRAME_SNAPSHOT 2
+#define FRAME_THUMBNAIL 3
+#define FRAME_RAW_SNAPSHOT 4
+#define FRAME_MAX 5
+
+enum msm_stats_enum_type {
+ MSM_STATS_TYPE_AEC, /* legacy based AEC */
+ MSM_STATS_TYPE_AF, /* legacy based AF */
+ MSM_STATS_TYPE_AWB, /* legacy based AWB */
+ MSM_STATS_TYPE_RS, /* legacy based RS */
+ MSM_STATS_TYPE_CS, /* legacy based CS */
+ MSM_STATS_TYPE_IHIST, /* legacy based HIST */
+ MSM_STATS_TYPE_SKIN, /* legacy based SKIN */
+ MSM_STATS_TYPE_BG, /* Bayer Grids */
+ MSM_STATS_TYPE_BF, /* Bayer Focus */
+ MSM_STATS_TYPE_BE, /* Bayer Exposure*/
+ MSM_STATS_TYPE_BHIST, /* Bayer Hist */
+ MSM_STATS_TYPE_AE_AW, /* legacy stats for vfe 2.x*/
+ MSM_STATS_TYPE_COMP, /* Composite stats */
+ MSM_STATS_TYPE_MAX /* MAX */
+};
+
+struct msm_stats_buf_info {
+ int type; /* msm_stats_enum_type */
+ int fd;
+ void *vaddr;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+ uint32_t planar0_off;
+ uint32_t planar1_off;
+ uint32_t planar2_off;
+ uint8_t active;
+ int buf_idx;
+};
+
+struct msm_pmem_info {
+ int type;
+ int fd;
+ void *vaddr;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+ uint32_t planar0_off;
+ uint32_t planar1_off;
+ uint32_t planar2_off;
+ uint8_t active;
+};
+
+struct outputCfg {
+ uint32_t height;
+ uint32_t width;
+
+ uint32_t window_height_firstline;
+ uint32_t window_height_lastline;
+};
+
+#define VIDEO_NODE 0
+#define MCTL_NODE 1
+
+#define OUTPUT_1 0
+#define OUTPUT_2 1
+#define OUTPUT_1_AND_2 2 /* snapshot only */
+#define OUTPUT_1_AND_3 3 /* video */
+#define CAMIF_TO_AXI_VIA_OUTPUT_2 4
+#define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 5
+#define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6
+#define OUTPUT_1_2_AND_3 7
+#define OUTPUT_ALL_CHNLS 8
+#define OUTPUT_VIDEO_ALL_CHNLS 9
+#define OUTPUT_ZSL_ALL_CHNLS 10
+#define LAST_AXI_OUTPUT_MODE_ENUM OUTPUT_ZSL_ALL_CHNLS
+
+#define OUTPUT_PRIM BIT(8)
+#define OUTPUT_PRIM_ALL_CHNLS BIT(9)
+#define OUTPUT_SEC BIT(10)
+#define OUTPUT_SEC_ALL_CHNLS BIT(11)
+#define OUTPUT_TERT1 BIT(12)
+#define OUTPUT_TERT2 BIT(13)
+
+
+
+#define MSM_FRAME_PREV_1 0
+#define MSM_FRAME_PREV_2 1
+#define MSM_FRAME_ENC 2
+
+#define OUTPUT_TYPE_P BIT(0)
+#define OUTPUT_TYPE_T BIT(1)
+#define OUTPUT_TYPE_S BIT(2)
+#define OUTPUT_TYPE_V BIT(3)
+#define OUTPUT_TYPE_L BIT(4)
+#define OUTPUT_TYPE_ST_L BIT(5)
+#define OUTPUT_TYPE_ST_R BIT(6)
+#define OUTPUT_TYPE_ST_D BIT(7)
+#define OUTPUT_TYPE_R BIT(8)
+#define OUTPUT_TYPE_R1 BIT(9)
+#define OUTPUT_TYPE_SAEC BIT(10)
+#define OUTPUT_TYPE_SAFC BIT(11)
+#define OUTPUT_TYPE_SAWB BIT(12)
+#define OUTPUT_TYPE_IHST BIT(13)
+#define OUTPUT_TYPE_CSTA BIT(14)
+
+struct fd_roi_info {
+ void *info;
+ int info_len;
+};
+
+struct msm_mem_map_info {
+ uint32_t cookie;
+ uint32_t length;
+ uint32_t mem_type;
+};
+
+#define MSM_MEM_MMAP 0
+#define MSM_MEM_USERPTR 1
+#define MSM_PLANE_MAX 8
+#define MSM_PLANE_Y 0
+#define MSM_PLANE_UV 1
+
+struct msm_frame {
+ struct timespec ts;
+ int path;
+ int type;
+ unsigned long buffer;
+ uint32_t phy_offset;
+ uint32_t y_off;
+ uint32_t cbcr_off;
+ uint32_t planar0_off;
+ uint32_t planar1_off;
+ uint32_t planar2_off;
+ int fd;
+
+ void *cropinfo;
+ int croplen;
+ uint32_t error_code;
+ struct fd_roi_info roi_info;
+ uint32_t frame_id;
+ int stcam_quality_ind;
+ uint32_t stcam_conv_value;
+
+ struct ion_allocation_data ion_alloc;
+ struct ion_fd_data fd_data;
+ int ion_dev_fd;
+};
+
+enum msm_st_frame_packing {
+ SIDE_BY_SIDE_HALF,
+ SIDE_BY_SIDE_FULL,
+ TOP_DOWN_HALF,
+ TOP_DOWN_FULL,
+};
+
+struct msm_st_crop {
+ uint32_t in_w;
+ uint32_t in_h;
+ uint32_t out_w;
+ uint32_t out_h;
+};
+
+struct msm_st_half {
+ uint32_t buf_p0_off;
+ uint32_t buf_p1_off;
+ uint32_t buf_p0_stride;
+ uint32_t buf_p1_stride;
+ uint32_t pix_x_off;
+ uint32_t pix_y_off;
+ struct msm_st_crop stCropInfo;
+};
+
+struct msm_st_frame {
+ struct msm_frame buf_info;
+ int type;
+ enum msm_st_frame_packing packing;
+ struct msm_st_half L;
+ struct msm_st_half R;
+ int frame_id;
+};
+
+#define MSM_CAMERA_ERR_MASK (0xFFFFFFFF & 1)
+
+struct stats_buff {
+ unsigned long buff;
+ int fd;
+};
+
+struct msm_stats_buf {
+ uint8_t awb_ymin;
+ struct stats_buff aec;
+ struct stats_buff awb;
+ struct stats_buff af;
+ struct stats_buff be;
+ struct stats_buff ihist;
+ struct stats_buff rs;
+ struct stats_buff cs;
+ struct stats_buff skin;
+ int type;
+ uint32_t status_bits;
+ unsigned long buffer;
+ int fd;
+ int length;
+ struct ion_handle *handle;
+ uint32_t frame_id;
+ int buf_idx;
+};
+#define MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT 0
+/* video capture mode in VIDIOC_S_PARM */
+#define MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+1)
+/* extendedmode for video recording in VIDIOC_S_PARM */
+#define MSM_V4L2_EXT_CAPTURE_MODE_VIDEO \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+2)
+/* extendedmode for the full size main image in VIDIOC_S_PARM */
+#define MSM_V4L2_EXT_CAPTURE_MODE_MAIN (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+3)
+/* extendedmode for the thumb nail image in VIDIOC_S_PARM */
+#define MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+4)
+/* ISP_PIX_OUTPUT1: no pp, directly send output1 buf to user */
+#define MSM_V4L2_EXT_CAPTURE_MODE_ISP_PIX_OUTPUT1 \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+5)
+/* ISP_PIX_OUTPUT2: no pp, directly send output2 buf to user */
+#define MSM_V4L2_EXT_CAPTURE_MODE_ISP_PIX_OUTPUT2 \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+6)
+/* raw image type */
+#define MSM_V4L2_EXT_CAPTURE_MODE_RAW \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+7)
+/* RDI dump */
+#define MSM_V4L2_EXT_CAPTURE_MODE_RDI \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+8)
+/* RDI dump 1 */
+#define MSM_V4L2_EXT_CAPTURE_MODE_RDI1 \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+9)
+/* RDI dump 2 */
+#define MSM_V4L2_EXT_CAPTURE_MODE_RDI2 \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+10)
+#define MSM_V4L2_EXT_CAPTURE_MODE_AEC \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+11)
+#define MSM_V4L2_EXT_CAPTURE_MODE_AWB \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+12)
+#define MSM_V4L2_EXT_CAPTURE_MODE_AF \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+13)
+#define MSM_V4L2_EXT_CAPTURE_MODE_IHIST \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+14)
+#define MSM_V4L2_EXT_CAPTURE_MODE_CS \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+15)
+#define MSM_V4L2_EXT_CAPTURE_MODE_RS \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+16)
+#define MSM_V4L2_EXT_CAPTURE_MODE_CSTA \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+17)
+#define MSM_V4L2_EXT_CAPTURE_MODE_V2X_LIVESHOT \
+ (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+18)
+#define MSM_V4L2_EXT_CAPTURE_MODE_MAX (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+19)
+
+
+#define MSM_V4L2_PID_MOTION_ISO V4L2_CID_PRIVATE_BASE
+#define MSM_V4L2_PID_EFFECT (V4L2_CID_PRIVATE_BASE+1)
+#define MSM_V4L2_PID_HJR (V4L2_CID_PRIVATE_BASE+2)
+#define MSM_V4L2_PID_LED_MODE (V4L2_CID_PRIVATE_BASE+3)
+#define MSM_V4L2_PID_PREP_SNAPSHOT (V4L2_CID_PRIVATE_BASE+4)
+#define MSM_V4L2_PID_EXP_METERING (V4L2_CID_PRIVATE_BASE+5)
+#define MSM_V4L2_PID_ISO (V4L2_CID_PRIVATE_BASE+6)
+#define MSM_V4L2_PID_CAM_MODE (V4L2_CID_PRIVATE_BASE+7)
+#define MSM_V4L2_PID_LUMA_ADAPTATION (V4L2_CID_PRIVATE_BASE+8)
+#define MSM_V4L2_PID_BEST_SHOT (V4L2_CID_PRIVATE_BASE+9)
+#define MSM_V4L2_PID_FOCUS_MODE (V4L2_CID_PRIVATE_BASE+10)
+#define MSM_V4L2_PID_BL_DETECTION (V4L2_CID_PRIVATE_BASE+11)
+#define MSM_V4L2_PID_SNOW_DETECTION (V4L2_CID_PRIVATE_BASE+12)
+#define MSM_V4L2_PID_CTRL_CMD (V4L2_CID_PRIVATE_BASE+13)
+#define MSM_V4L2_PID_EVT_SUB_INFO (V4L2_CID_PRIVATE_BASE+14)
+#define MSM_V4L2_PID_STROBE_FLASH (V4L2_CID_PRIVATE_BASE+15)
+#define MSM_V4L2_PID_INST_HANDLE (V4L2_CID_PRIVATE_BASE+16)
+#define MSM_V4L2_PID_MMAP_INST (V4L2_CID_PRIVATE_BASE+17)
+#define MSM_V4L2_PID_PP_PLANE_INFO (V4L2_CID_PRIVATE_BASE+18)
+#define MSM_V4L2_PID_MAX MSM_V4L2_PID_PP_PLANE_INFO
+
+/* camera operation mode for video recording - two frame output queues */
+#define MSM_V4L2_CAM_OP_DEFAULT 0
+/* camera operation mode for video recording - two frame output queues */
+#define MSM_V4L2_CAM_OP_PREVIEW (MSM_V4L2_CAM_OP_DEFAULT+1)
+/* camera operation mode for video recording - two frame output queues */
+#define MSM_V4L2_CAM_OP_VIDEO (MSM_V4L2_CAM_OP_DEFAULT+2)
+/* camera operation mode for standard shapshot - two frame output queues */
+#define MSM_V4L2_CAM_OP_CAPTURE (MSM_V4L2_CAM_OP_DEFAULT+3)
+/* camera operation mode for zsl shapshot - three output queues */
+#define MSM_V4L2_CAM_OP_ZSL (MSM_V4L2_CAM_OP_DEFAULT+4)
+/* camera operation mode for raw snapshot - one frame output queue */
+#define MSM_V4L2_CAM_OP_RAW (MSM_V4L2_CAM_OP_DEFAULT+5)
+/* camera operation mode for jpeg snapshot - one frame output queue */
+#define MSM_V4L2_CAM_OP_JPEG_CAPTURE (MSM_V4L2_CAM_OP_DEFAULT+6)
+
+
+#define MSM_V4L2_VID_CAP_TYPE 0
+#define MSM_V4L2_STREAM_ON 1
+#define MSM_V4L2_STREAM_OFF 2
+#define MSM_V4L2_SNAPSHOT 3
+#define MSM_V4L2_QUERY_CTRL 4
+#define MSM_V4L2_GET_CTRL 5
+#define MSM_V4L2_SET_CTRL 6
+#define MSM_V4L2_QUERY 7
+#define MSM_V4L2_GET_CROP 8
+#define MSM_V4L2_SET_CROP 9
+#define MSM_V4L2_OPEN 10
+#define MSM_V4L2_CLOSE 11
+#define MSM_V4L2_SET_CTRL_CMD 12
+#define MSM_V4L2_EVT_SUB_MASK 13
+#define MSM_V4L2_PRIVATE_CMD 14
+#define MSM_V4L2_MAX 15
+#define V4L2_CAMERA_EXIT 43
+
+struct crop_info {
+ void *info;
+ int len;
+};
+
+struct msm_postproc {
+ int ftnum;
+ struct msm_frame fthumnail;
+ int fmnum;
+ struct msm_frame fmain;
+};
+
+struct msm_snapshot_pp_status {
+ void *status;
+};
+
+#define CFG_SET_MODE 0
+#define CFG_SET_EFFECT 1
+#define CFG_START 2
+#define CFG_PWR_UP 3
+#define CFG_PWR_DOWN 4
+#define CFG_WRITE_EXPOSURE_GAIN 5
+#define CFG_SET_DEFAULT_FOCUS 6
+#define CFG_MOVE_FOCUS 7
+#define CFG_REGISTER_TO_REAL_GAIN 8
+#define CFG_REAL_TO_REGISTER_GAIN 9
+#define CFG_SET_FPS 10
+#define CFG_SET_PICT_FPS 11
+#define CFG_SET_BRIGHTNESS 12
+#define CFG_SET_CONTRAST 13
+#define CFG_SET_ZOOM 14
+#define CFG_SET_EXPOSURE_MODE 15
+#define CFG_SET_WB 16
+#define CFG_SET_ANTIBANDING 17
+#define CFG_SET_EXP_GAIN 18
+#define CFG_SET_PICT_EXP_GAIN 19
+#define CFG_SET_LENS_SHADING 20
+#define CFG_GET_PICT_FPS 21
+#define CFG_GET_PREV_L_PF 22
+#define CFG_GET_PREV_P_PL 23
+#define CFG_GET_PICT_L_PF 24
+#define CFG_GET_PICT_P_PL 25
+#define CFG_GET_AF_MAX_STEPS 26
+#define CFG_GET_PICT_MAX_EXP_LC 27
+#define CFG_SEND_WB_INFO 28
+#define CFG_SENSOR_INIT 29
+#define CFG_GET_3D_CALI_DATA 30
+#define CFG_GET_CALIB_DATA 31
+#define CFG_GET_OUTPUT_INFO 32
+#define CFG_GET_EEPROM_INFO 33
+#define CFG_GET_EEPROM_DATA 34
+#define CFG_SET_ACTUATOR_INFO 35
+#define CFG_GET_ACTUATOR_INFO 36
+/* TBD: QRD */
+#define CFG_SET_SATURATION 37
+#define CFG_SET_SHARPNESS 38
+#define CFG_SET_TOUCHAEC 39
+#define CFG_SET_AUTO_FOCUS 40
+#define CFG_SET_AUTOFLASH 41
+#define CFG_SET_EXPOSURE_COMPENSATION 42
+#define CFG_SET_ISO 43
+#define CFG_START_STREAM 44
+#define CFG_STOP_STREAM 45
+#define CFG_GET_CSI_PARAMS 46
+#define CFG_POWER_UP 47
+#define CFG_POWER_DOWN 48
+#define CFG_WRITE_I2C_ARRAY 49
+#define CFG_READ_I2C_ARRAY 50
+#define CFG_PCLK_CHANGE 51
+#define CFG_CONFIG_VREG_ARRAY 52
+#define CFG_CONFIG_CLK_ARRAY 53
+#define CFG_GPIO_OP 54
+#define CFG_MAX 55
+
+
+#define MOVE_NEAR 0
+#define MOVE_FAR 1
+
+#define SENSOR_PREVIEW_MODE 0
+#define SENSOR_SNAPSHOT_MODE 1
+#define SENSOR_RAW_SNAPSHOT_MODE 2
+#define SENSOR_HFR_60FPS_MODE 3
+#define SENSOR_HFR_90FPS_MODE 4
+#define SENSOR_HFR_120FPS_MODE 5
+
+#define SENSOR_QTR_SIZE 0
+#define SENSOR_FULL_SIZE 1
+#define SENSOR_QVGA_SIZE 2
+#define SENSOR_INVALID_SIZE 3
+
+#define CAMERA_EFFECT_OFF 0
+#define CAMERA_EFFECT_MONO 1
+#define CAMERA_EFFECT_NEGATIVE 2
+#define CAMERA_EFFECT_SOLARIZE 3
+#define CAMERA_EFFECT_SEPIA 4
+#define CAMERA_EFFECT_POSTERIZE 5
+#define CAMERA_EFFECT_WHITEBOARD 6
+#define CAMERA_EFFECT_BLACKBOARD 7
+#define CAMERA_EFFECT_AQUA 8
+#define CAMERA_EFFECT_EMBOSS 9
+#define CAMERA_EFFECT_SKETCH 10
+#define CAMERA_EFFECT_NEON 11
+#define CAMERA_EFFECT_FADED 12
+#define CAMERA_EFFECT_VINTAGECOOL 13
+#define CAMERA_EFFECT_VINTAGEWARM 14
+#define CAMERA_EFFECT_ACCENT_BLUE 15
+#define CAMERA_EFFECT_ACCENT_GREEN 16
+#define CAMERA_EFFECT_ACCENT_ORANGE 17
+#define CAMERA_EFFECT_MAX 18
+
+/* QRD */
+#define CAMERA_EFFECT_BW 10
+#define CAMERA_EFFECT_BLUISH 12
+#define CAMERA_EFFECT_REDDISH 13
+#define CAMERA_EFFECT_GREENISH 14
+
+/* QRD */
+#define CAMERA_ANTIBANDING_OFF 0
+#define CAMERA_ANTIBANDING_50HZ 2
+#define CAMERA_ANTIBANDING_60HZ 1
+#define CAMERA_ANTIBANDING_AUTO 3
+
+#define CAMERA_CONTRAST_LV0 0
+#define CAMERA_CONTRAST_LV1 1
+#define CAMERA_CONTRAST_LV2 2
+#define CAMERA_CONTRAST_LV3 3
+#define CAMERA_CONTRAST_LV4 4
+#define CAMERA_CONTRAST_LV5 5
+#define CAMERA_CONTRAST_LV6 6
+#define CAMERA_CONTRAST_LV7 7
+#define CAMERA_CONTRAST_LV8 8
+#define CAMERA_CONTRAST_LV9 9
+
+#define CAMERA_BRIGHTNESS_LV0 0
+#define CAMERA_BRIGHTNESS_LV1 1
+#define CAMERA_BRIGHTNESS_LV2 2
+#define CAMERA_BRIGHTNESS_LV3 3
+#define CAMERA_BRIGHTNESS_LV4 4
+#define CAMERA_BRIGHTNESS_LV5 5
+#define CAMERA_BRIGHTNESS_LV6 6
+#define CAMERA_BRIGHTNESS_LV7 7
+#define CAMERA_BRIGHTNESS_LV8 8
+
+
+#define CAMERA_SATURATION_LV0 0
+#define CAMERA_SATURATION_LV1 1
+#define CAMERA_SATURATION_LV2 2
+#define CAMERA_SATURATION_LV3 3
+#define CAMERA_SATURATION_LV4 4
+#define CAMERA_SATURATION_LV5 5
+#define CAMERA_SATURATION_LV6 6
+#define CAMERA_SATURATION_LV7 7
+#define CAMERA_SATURATION_LV8 8
+
+#define CAMERA_SHARPNESS_LV0 0
+#define CAMERA_SHARPNESS_LV1 3
+#define CAMERA_SHARPNESS_LV2 6
+#define CAMERA_SHARPNESS_LV3 9
+#define CAMERA_SHARPNESS_LV4 12
+#define CAMERA_SHARPNESS_LV5 15
+#define CAMERA_SHARPNESS_LV6 18
+#define CAMERA_SHARPNESS_LV7 21
+#define CAMERA_SHARPNESS_LV8 24
+#define CAMERA_SHARPNESS_LV9 27
+#define CAMERA_SHARPNESS_LV10 30
+
+#define CAMERA_SETAE_AVERAGE 0
+#define CAMERA_SETAE_CENWEIGHT 1
+
+#define CAMERA_WB_AUTO 1 /* This list must match aeecamera.h */
+#define CAMERA_WB_CUSTOM 2
+#define CAMERA_WB_INCANDESCENT 3
+#define CAMERA_WB_FLUORESCENT 4
+#define CAMERA_WB_DAYLIGHT 5
+#define CAMERA_WB_CLOUDY_DAYLIGHT 6
+#define CAMERA_WB_TWILIGHT 7
+#define CAMERA_WB_SHADE 8
+
+#define CAMERA_EXPOSURE_COMPENSATION_LV0 12
+#define CAMERA_EXPOSURE_COMPENSATION_LV1 6
+#define CAMERA_EXPOSURE_COMPENSATION_LV2 0
+#define CAMERA_EXPOSURE_COMPENSATION_LV3 -6
+#define CAMERA_EXPOSURE_COMPENSATION_LV4 -12
+
+enum msm_v4l2_saturation_level {
+ MSM_V4L2_SATURATION_L0,
+ MSM_V4L2_SATURATION_L1,
+ MSM_V4L2_SATURATION_L2,
+ MSM_V4L2_SATURATION_L3,
+ MSM_V4L2_SATURATION_L4,
+ MSM_V4L2_SATURATION_L5,
+ MSM_V4L2_SATURATION_L6,
+ MSM_V4L2_SATURATION_L7,
+ MSM_V4L2_SATURATION_L8,
+ MSM_V4L2_SATURATION_L9,
+ MSM_V4L2_SATURATION_L10,
+};
+
+enum msm_v4l2_contrast_level {
+ MSM_V4L2_CONTRAST_L0,
+ MSM_V4L2_CONTRAST_L1,
+ MSM_V4L2_CONTRAST_L2,
+ MSM_V4L2_CONTRAST_L3,
+ MSM_V4L2_CONTRAST_L4,
+ MSM_V4L2_CONTRAST_L5,
+ MSM_V4L2_CONTRAST_L6,
+ MSM_V4L2_CONTRAST_L7,
+ MSM_V4L2_CONTRAST_L8,
+ MSM_V4L2_CONTRAST_L9,
+ MSM_V4L2_CONTRAST_L10,
+};
+
+
+enum msm_v4l2_exposure_level {
+ MSM_V4L2_EXPOSURE_N2,
+ MSM_V4L2_EXPOSURE_N1,
+ MSM_V4L2_EXPOSURE_D,
+ MSM_V4L2_EXPOSURE_P1,
+ MSM_V4L2_EXPOSURE_P2,
+};
+
+enum msm_v4l2_sharpness_level {
+ MSM_V4L2_SHARPNESS_L0,
+ MSM_V4L2_SHARPNESS_L1,
+ MSM_V4L2_SHARPNESS_L2,
+ MSM_V4L2_SHARPNESS_L3,
+ MSM_V4L2_SHARPNESS_L4,
+ MSM_V4L2_SHARPNESS_L5,
+ MSM_V4L2_SHARPNESS_L6,
+};
+
+enum msm_v4l2_expo_metering_mode {
+ MSM_V4L2_EXP_FRAME_AVERAGE,
+ MSM_V4L2_EXP_CENTER_WEIGHTED,
+ MSM_V4L2_EXP_SPOT_METERING,
+};
+
+enum msm_v4l2_iso_mode {
+ MSM_V4L2_ISO_AUTO = 0,
+ MSM_V4L2_ISO_DEBLUR,
+ MSM_V4L2_ISO_100,
+ MSM_V4L2_ISO_200,
+ MSM_V4L2_ISO_400,
+ MSM_V4L2_ISO_800,
+ MSM_V4L2_ISO_1600,
+};
+
+enum msm_v4l2_wb_mode {
+ MSM_V4L2_WB_OFF,
+ MSM_V4L2_WB_AUTO,
+ MSM_V4L2_WB_CUSTOM,
+ MSM_V4L2_WB_INCANDESCENT,
+ MSM_V4L2_WB_FLUORESCENT,
+ MSM_V4L2_WB_DAYLIGHT,
+ MSM_V4L2_WB_CLOUDY_DAYLIGHT,
+};
+
+enum msm_v4l2_special_effect {
+ MSM_V4L2_EFFECT_OFF,
+ MSM_V4L2_EFFECT_MONO,
+ MSM_V4L2_EFFECT_NEGATIVE,
+ MSM_V4L2_EFFECT_SOLARIZE,
+ MSM_V4L2_EFFECT_SEPIA,
+ MSM_V4L2_EFFECT_POSTERAIZE,
+ MSM_V4L2_EFFECT_WHITEBOARD,
+ MSM_V4L2_EFFECT_BLACKBOARD,
+ MSM_V4L2_EFFECT_AQUA,
+ MSM_V4L2_EFFECT_EMBOSS,
+ MSM_V4L2_EFFECT_SKETCH,
+ MSM_V4L2_EFFECT_NEON,
+ MSM_V4L2_EFFECT_MAX,
+};
+
+enum msm_v4l2_power_line_frequency {
+ MSM_V4L2_POWER_LINE_OFF,
+ MSM_V4L2_POWER_LINE_60HZ,
+ MSM_V4L2_POWER_LINE_50HZ,
+ MSM_V4L2_POWER_LINE_AUTO,
+};
+
+#define CAMERA_ISO_TYPE_AUTO 0
+#define CAMEAR_ISO_TYPE_HJR 1
+#define CAMEAR_ISO_TYPE_100 2
+#define CAMERA_ISO_TYPE_200 3
+#define CAMERA_ISO_TYPE_400 4
+#define CAMEAR_ISO_TYPE_800 5
+#define CAMERA_ISO_TYPE_1600 6
+
+struct sensor_pict_fps {
+ uint16_t prevfps;
+ uint16_t pictfps;
+};
+
+struct exp_gain_cfg {
+ uint16_t gain;
+ uint32_t line;
+};
+
+struct focus_cfg {
+ int32_t steps;
+ int dir;
+};
+
+struct fps_cfg {
+ uint16_t f_mult;
+ uint16_t fps_div;
+ uint32_t pict_fps_div;
+};
+struct wb_info_cfg {
+ uint16_t red_gain;
+ uint16_t green_gain;
+ uint16_t blue_gain;
+};
+struct sensor_3d_exp_cfg {
+ uint16_t gain;
+ uint32_t line;
+ uint16_t r_gain;
+ uint16_t b_gain;
+ uint16_t gr_gain;
+ uint16_t gb_gain;
+ uint16_t gain_adjust;
+};
+struct sensor_3d_cali_data_t {
+ unsigned char left_p_matrix[3][4][8];
+ unsigned char right_p_matrix[3][4][8];
+ unsigned char square_len[8];
+ unsigned char focal_len[8];
+ unsigned char pixel_pitch[8];
+ uint16_t left_r;
+ uint16_t left_b;
+ uint16_t left_gb;
+ uint16_t left_af_far;
+ uint16_t left_af_mid;
+ uint16_t left_af_short;
+ uint16_t left_af_5um;
+ uint16_t left_af_50up;
+ uint16_t left_af_50down;
+ uint16_t right_r;
+ uint16_t right_b;
+ uint16_t right_gb;
+ uint16_t right_af_far;
+ uint16_t right_af_mid;
+ uint16_t right_af_short;
+ uint16_t right_af_5um;
+ uint16_t right_af_50up;
+ uint16_t right_af_50down;
+};
+struct sensor_init_cfg {
+ uint8_t prev_res;
+ uint8_t pict_res;
+};
+
+struct sensor_calib_data {
+ /* Color Related Measurements */
+ uint16_t r_over_g;
+ uint16_t b_over_g;
+ uint16_t gr_over_gb;
+
+ /* Lens Related Measurements */
+ uint16_t macro_2_inf;
+ uint16_t inf_2_macro;
+ uint16_t stroke_amt;
+ uint16_t af_pos_1m;
+ uint16_t af_pos_inf;
+};
+
+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,
+};
+
+struct msm_sensor_output_info_t {
+ uint16_t x_output;
+ uint16_t y_output;
+ uint16_t line_length_pclk;
+ uint16_t frame_length_lines;
+ uint32_t vt_pixel_clk;
+ uint32_t op_pixel_clk;
+ uint16_t binning_factor;
+};
+
+struct sensor_output_info_t {
+ struct msm_sensor_output_info_t *output_info;
+ uint16_t num_info;
+};
+
+struct msm_sensor_exp_gain_info_t {
+ uint16_t coarse_int_time_addr;
+ uint16_t global_gain_addr;
+ uint16_t vert_offset;
+};
+
+struct msm_sensor_output_reg_addr_t {
+ uint16_t x_output;
+ uint16_t y_output;
+ uint16_t line_length_pclk;
+ uint16_t frame_length_lines;
+};
+
+struct sensor_driver_params_type {
+ struct msm_camera_i2c_reg_setting *init_settings;
+ uint16_t init_settings_size;
+ struct msm_camera_i2c_reg_setting *mode_settings;
+ uint16_t mode_settings_size;
+ struct msm_sensor_output_reg_addr_t *sensor_output_reg_addr;
+ struct msm_camera_i2c_reg_setting *start_settings;
+ struct msm_camera_i2c_reg_setting *stop_settings;
+ struct msm_camera_i2c_reg_setting *groupon_settings;
+ struct msm_camera_i2c_reg_setting *groupoff_settings;
+ struct msm_sensor_exp_gain_info_t *sensor_exp_gain_info;
+ struct msm_sensor_output_info_t *output_info;
+};
+
+struct mirror_flip {
+ int32_t x_mirror;
+ int32_t y_flip;
+};
+
+struct cord {
+ uint32_t x;
+ uint32_t y;
+};
+
+struct msm_eeprom_data_t {
+ void *eeprom_data;
+ uint16_t index;
+};
+
+struct msm_camera_csid_vc_cfg {
+ uint8_t cid;
+ uint8_t dt;
+ uint8_t decode_format;
+};
+
+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_camera_csid_lut_params {
+ uint8_t num_cid;
+ struct msm_camera_csid_vc_cfg *vc_cfg;
+};
+
+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;
+ uint8_t csid_core;
+};
+
+struct msm_camera_csi2_params {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csiphy_params csiphy_params;
+};
+
+enum msm_camera_csi_data_format {
+ CSI_8BIT,
+ CSI_10BIT,
+ CSI_12BIT,
+};
+
+struct msm_camera_csi_params {
+ enum msm_camera_csi_data_format data_format;
+ uint8_t lane_cnt;
+ uint8_t lane_assign;
+ uint8_t settle_cnt;
+ uint8_t dpcm_scheme;
+};
+
+enum csic_cfg_type_t {
+ CSIC_INIT,
+ CSIC_CFG,
+};
+
+struct csic_cfg_data {
+ enum csic_cfg_type_t cfgtype;
+ struct msm_camera_csi_params *csic_params;
+};
+
+enum csid_cfg_type_t {
+ CSID_INIT,
+ CSID_CFG,
+};
+
+struct csid_cfg_data {
+ enum csid_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ struct msm_camera_csid_params *csid_params;
+ } cfg;
+};
+
+enum csiphy_cfg_type_t {
+ CSIPHY_INIT,
+ CSIPHY_CFG,
+};
+
+struct csiphy_cfg_data {
+ enum csiphy_cfg_type_t cfgtype;
+ struct msm_camera_csiphy_params *csiphy_params;
+};
+
+#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 ISPIF_STREAM(intf, action, vfe) (((intf)<<ISPIF_S_STREAM_SHIFT)+\
+ (action)+((vfe)<<ISPIF_VFE_INTF_SHIFT))
+#define ISPIF_ON_FRAME_BOUNDARY (0x01 << 0)
+#define ISPIF_OFF_FRAME_BOUNDARY (0x01 << 1)
+#define ISPIF_OFF_IMMEDIATELY (0x01 << 2)
+#define ISPIF_S_STREAM_SHIFT 4
+#define ISPIF_VFE_INTF_SHIFT 12
+
+#define PIX_0 (0x01 << 0)
+#define RDI_0 (0x01 << 1)
+#define PIX_1 (0x01 << 2)
+#define RDI_1 (0x01 << 3)
+#define RDI_2 (0x01 << 4)
+
+enum msm_ispif_vfe_intf {
+ VFE0,
+ VFE1,
+ VFE_MAX,
+};
+
+enum msm_ispif_intftype {
+ PIX0,
+ RDI0,
+ PIX1,
+ RDI1,
+ RDI2,
+ INTF_MAX,
+};
+
+enum msm_ispif_vc {
+ VC0,
+ VC1,
+ VC2,
+ VC3,
+};
+
+enum msm_ispif_cid {
+ CID0,
+ CID1,
+ CID2,
+ CID3,
+ CID4,
+ CID5,
+ CID6,
+ CID7,
+ CID8,
+ CID9,
+ CID10,
+ CID11,
+ CID12,
+ CID13,
+ CID14,
+ CID15,
+};
+
+struct msm_ispif_params {
+ uint8_t intftype;
+ uint16_t cid_mask;
+ uint8_t csid;
+ uint8_t vfe_intf;
+};
+
+struct msm_ispif_params_list {
+ uint32_t len;
+ struct msm_ispif_params params[4];
+};
+
+enum ispif_cfg_type_t {
+ ISPIF_INIT,
+ ISPIF_SET_CFG,
+ ISPIF_SET_ON_FRAME_BOUNDARY,
+ ISPIF_SET_OFF_FRAME_BOUNDARY,
+ ISPIF_SET_OFF_IMMEDIATELY,
+ ISPIF_RELEASE,
+};
+
+struct ispif_cfg_data {
+ enum ispif_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ int cmd;
+ struct msm_ispif_params_list ispif_params;
+ } cfg;
+};
+
+enum msm_camera_i2c_reg_addr_type {
+ MSM_CAMERA_I2C_BYTE_ADDR = 1,
+ MSM_CAMERA_I2C_WORD_ADDR,
+ MSM_CAMERA_I2C_3B_ADDR,
+ MSM_CAMERA_I2C_DWORD_ADDR,
+};
+#define MSM_CAMERA_I2C_DWORD_ADDR MSM_CAMERA_I2C_DWORD_ADDR
+
+struct msm_camera_i2c_reg_array {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+};
+
+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,
+};
+
+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;
+};
+
+enum oem_setting_type {
+ I2C_READ = 1,
+ I2C_WRITE,
+ GPIO_OP,
+ EEPROM_READ,
+ VREG_SET,
+ CLK_SET,
+};
+
+struct sensor_oem_setting {
+ enum oem_setting_type type;
+ void *data;
+};
+
+enum camera_vreg_type {
+ REG_LDO,
+ REG_VS,
+ 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;
+ int min_voltage;
+ int max_voltage;
+ int op_mode;
+ uint32_t delay;
+};
+
+struct msm_camera_vreg_setting {
+ struct camera_vreg_t *cam_vreg;
+ uint16_t num_vreg;
+ uint8_t enable;
+};
+
+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 sensor_cfg_data {
+ int cfgtype;
+ int mode;
+ int rs;
+ uint8_t max_steps;
+
+ union {
+ int8_t effect;
+ uint8_t lens_shading;
+ uint16_t prevl_pf;
+ uint16_t prevp_pl;
+ uint16_t pictl_pf;
+ uint16_t pictp_pl;
+ uint32_t pict_max_exp_lc;
+ uint16_t p_fps;
+ uint8_t iso_type;
+ struct sensor_init_cfg init_info;
+ struct sensor_pict_fps gfps;
+ struct exp_gain_cfg exp_gain;
+ struct focus_cfg focus;
+ struct fps_cfg fps;
+ struct wb_info_cfg wb_info;
+ struct sensor_3d_exp_cfg sensor_3d_exp;
+ struct sensor_calib_data calib_info;
+ struct sensor_output_info_t output_info;
+ struct msm_eeprom_data_t eeprom_data;
+ struct csi_lane_params_t csi_lane_params;
+ /* QRD */
+ uint16_t antibanding;
+ uint8_t contrast;
+ uint8_t saturation;
+ uint8_t sharpness;
+ int8_t brightness;
+ int ae_mode;
+ uint8_t wb_val;
+ int8_t exp_compensation;
+ uint32_t pclk;
+ struct cord aec_cord;
+ int is_autoflash;
+ struct mirror_flip mirror_flip;
+ void *setting;
+ } cfg;
+};
+
+enum gpio_operation_type {
+ GPIO_REQUEST,
+ GPIO_FREE,
+ GPIO_SET_DIRECTION_OUTPUT,
+ GPIO_SET_DIRECTION_INPUT,
+ GPIO_GET_VALUE,
+ GPIO_SET_VALUE,
+};
+
+struct msm_cam_gpio_operation {
+ enum gpio_operation_type op_type;
+ unsigned int address;
+ int value;
+ const char *tag;
+};
+
+struct damping_params_t {
+ uint32_t damping_step;
+ uint32_t damping_delay;
+ uint32_t hw_params;
+};
+
+enum actuator_type {
+ ACTUATOR_VCM,
+ ACTUATOR_PIEZO,
+ ACTUATOR_HVCM,
+ ACTUATOR_BIVCM,
+};
+
+enum msm_actuator_data_type {
+ MSM_ACTUATOR_BYTE_DATA = 1,
+ MSM_ACTUATOR_WORD_DATA,
+};
+
+enum msm_actuator_addr_type {
+ MSM_ACTUATOR_BYTE_ADDR = 1,
+ MSM_ACTUATOR_WORD_ADDR,
+};
+
+enum msm_actuator_write_type {
+ MSM_ACTUATOR_WRITE_HW_DAMP,
+ MSM_ACTUATOR_WRITE_DAC,
+ MSM_ACTUATOR_WRITE,
+ MSM_ACTUATOR_WRITE_DIR_REG,
+ MSM_ACTUATOR_POLL,
+ MSM_ACTUATOR_READ_WRITE,
+};
+
+struct msm_actuator_reg_params_t {
+ enum msm_actuator_write_type reg_write_type;
+ uint32_t hw_mask;
+ uint16_t reg_addr;
+ uint16_t hw_shift;
+ uint16_t data_type;
+ uint16_t addr_type;
+ uint16_t reg_data;
+ uint16_t delay;
+};
+
+struct reg_settings_t {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+};
+
+struct region_params_t {
+ /* [0] = ForwardDirection Macro boundary
+ * [1] = ReverseDirection Inf boundary
+ */
+ uint16_t step_bound[2];
+ uint16_t code_per_step;
+};
+
+struct msm_actuator_move_params_t {
+ int8_t dir;
+ int8_t sign_dir;
+ int16_t dest_step_pos;
+ int32_t num_steps;
+ struct damping_params_t __user *ringing_params;
+};
+
+struct msm_actuator_tuning_params_t {
+ int16_t initial_code;
+ uint16_t pwd_step;
+ uint16_t region_size;
+ uint32_t total_steps;
+ struct region_params_t __user *region_params;
+};
+
+struct msm_actuator_params_t {
+ enum actuator_type act_type;
+ uint8_t reg_tbl_size;
+ uint16_t data_size;
+ uint16_t init_setting_size;
+ uint32_t i2c_addr;
+ enum msm_actuator_addr_type i2c_addr_type;
+ enum msm_actuator_data_type i2c_data_type;
+ struct msm_actuator_reg_params_t __user *reg_tbl_params;
+ struct reg_settings_t __user *init_settings;
+};
+
+struct msm_actuator_set_info_t {
+ struct msm_actuator_params_t actuator_params;
+ struct msm_actuator_tuning_params_t af_tuning_params;
+};
+
+struct msm_actuator_get_info_t {
+ uint32_t focal_length_num;
+ uint32_t focal_length_den;
+ uint32_t f_number_num;
+ uint32_t f_number_den;
+ uint32_t f_pix_num;
+ uint32_t f_pix_den;
+ uint32_t total_f_dist_num;
+ uint32_t total_f_dist_den;
+ uint32_t hor_view_angle_num;
+ uint32_t hor_view_angle_den;
+ uint32_t ver_view_angle_num;
+ uint32_t ver_view_angle_den;
+};
+
+enum af_camera_name {
+ ACTUATOR_MAIN_CAM_0,
+ ACTUATOR_MAIN_CAM_1,
+ ACTUATOR_MAIN_CAM_2,
+ ACTUATOR_MAIN_CAM_3,
+ ACTUATOR_MAIN_CAM_4,
+ ACTUATOR_MAIN_CAM_5,
+ ACTUATOR_WEB_CAM_0,
+ ACTUATOR_WEB_CAM_1,
+ ACTUATOR_WEB_CAM_2,
+};
+
+struct msm_actuator_cfg_data {
+ int cfgtype;
+ uint8_t is_af_supported;
+ union {
+ struct msm_actuator_move_params_t move;
+ struct msm_actuator_set_info_t set_info;
+ struct msm_actuator_get_info_t get_info;
+ enum af_camera_name cam_name;
+ } cfg;
+};
+
+struct msm_eeprom_support {
+ uint16_t is_supported;
+ uint16_t size;
+ uint16_t index;
+ uint16_t qvalue;
+};
+
+struct msm_calib_wb {
+ uint16_t r_over_g;
+ uint16_t b_over_g;
+ uint16_t gr_over_gb;
+};
+
+struct msm_calib_af {
+ uint16_t macro_dac;
+ uint16_t inf_dac;
+ uint16_t start_dac;
+};
+
+struct msm_calib_lsc {
+ uint16_t r_gain[221];
+ uint16_t b_gain[221];
+ uint16_t gr_gain[221];
+ uint16_t gb_gain[221];
+};
+
+struct pixel_t {
+ int x;
+ int y;
+};
+
+struct msm_calib_dpc {
+ uint16_t validcount;
+ struct pixel_t snapshot_coord[128];
+ struct pixel_t preview_coord[128];
+ struct pixel_t video_coord[128];
+};
+
+struct msm_calib_raw {
+ uint8_t *data;
+ uint32_t size;
+};
+
+struct msm_camera_eeprom_info_t {
+ struct msm_eeprom_support af;
+ struct msm_eeprom_support wb;
+ struct msm_eeprom_support lsc;
+ struct msm_eeprom_support dpc;
+ struct msm_eeprom_support raw;
+};
+
+struct msm_eeprom_cfg_data {
+ int cfgtype;
+ uint8_t is_eeprom_supported;
+ union {
+ struct msm_eeprom_data_t get_data;
+ struct msm_camera_eeprom_info_t get_info;
+ } cfg;
+};
+
+struct sensor_large_data {
+ int cfgtype;
+ union {
+ struct sensor_3d_cali_data_t sensor_3d_cali_data;
+ } data;
+};
+
+enum sensor_type_t {
+ BAYER,
+ YUV,
+ JPEG_SOC,
+};
+
+enum flash_type {
+ LED_FLASH,
+ STROBE_FLASH,
+};
+
+enum strobe_flash_ctrl_type {
+ STROBE_FLASH_CTRL_INIT,
+ STROBE_FLASH_CTRL_CHARGE,
+ STROBE_FLASH_CTRL_RELEASE
+};
+
+struct strobe_flash_ctrl_data {
+ enum strobe_flash_ctrl_type type;
+ int charge_en;
+};
+
+struct msm_camera_info {
+ int num_cameras;
+ uint8_t has_3d_support[MSM_MAX_CAMERA_SENSORS];
+ uint8_t is_internal_cam[MSM_MAX_CAMERA_SENSORS];
+ uint32_t s_mount_angle[MSM_MAX_CAMERA_SENSORS];
+ const char *video_dev_name[MSM_MAX_CAMERA_SENSORS];
+ enum sensor_type_t sensor_type[MSM_MAX_CAMERA_SENSORS];
+};
+
+struct msm_cam_config_dev_info {
+ int num_config_nodes;
+ const char *config_dev_name[MSM_MAX_CAMERA_CONFIGS];
+ int config_dev_id[MSM_MAX_CAMERA_CONFIGS];
+};
+
+struct msm_mctl_node_info {
+ int num_mctl_nodes;
+ const char *mctl_node_name[MSM_MAX_CAMERA_SENSORS];
+};
+
+struct flash_ctrl_data {
+ int flashtype;
+ union {
+ int led_state;
+ struct strobe_flash_ctrl_data strobe_ctrl;
+ } ctrl_data;
+};
+
+#define GET_NAME 0
+#define GET_PREVIEW_LINE_PER_FRAME 1
+#define GET_PREVIEW_PIXELS_PER_LINE 2
+#define GET_SNAPSHOT_LINE_PER_FRAME 3
+#define GET_SNAPSHOT_PIXELS_PER_LINE 4
+#define GET_SNAPSHOT_FPS 5
+#define GET_SNAPSHOT_MAX_EP_LINE_CNT 6
+
+struct msm_camsensor_info {
+ char name[MAX_SENSOR_NAME];
+ uint8_t flash_enabled;
+ uint8_t strobe_flash_enabled;
+ uint8_t actuator_enabled;
+ uint8_t ispif_supported;
+ int8_t total_steps;
+ uint8_t support_3d;
+ enum flash_type flashtype;
+ enum sensor_type_t sensor_type;
+ uint32_t pxlcode; /* enum v4l2_mbus_pixelcode */
+ uint32_t camera_type; /* msm_camera_type */
+ int mount_angle;
+ uint32_t max_width;
+ uint32_t max_height;
+};
+
+#define V4L2_SINGLE_PLANE 0
+#define V4L2_MULTI_PLANE_Y 0
+#define V4L2_MULTI_PLANE_CBCR 1
+#define V4L2_MULTI_PLANE_CB 1
+#define V4L2_MULTI_PLANE_CR 2
+
+struct plane_data {
+ int plane_id;
+ uint32_t offset;
+ unsigned long size;
+};
+
+struct img_plane_info {
+ uint32_t width;
+ uint32_t height;
+ uint32_t pixelformat;
+ uint8_t buffer_type; /*Single/Multi planar*/
+ uint8_t output_port;
+ uint32_t ext_mode;
+ uint8_t num_planes;
+ struct plane_data plane[MAX_PLANES];
+ uint32_t sp_y_offset;
+ uint32_t inst_handle;
+};
+
+#define QCAMERA_NAME "qcamera"
+#define QCAMERA_SERVER_NAME "qcamera_server"
+#define QCAMERA_DEVICE_GROUP_ID 1
+#define QCAMERA_VNODE_GROUP_ID 2
+
+enum msm_cam_subdev_type {
+ CSIPHY_DEV,
+ CSID_DEV,
+ CSIC_DEV,
+ ISPIF_DEV,
+ VFE_DEV,
+ AXI_DEV,
+ VPE_DEV,
+ SENSOR_DEV,
+ ACTUATOR_DEV,
+ EEPROM_DEV,
+ GESTURE_DEV,
+ IRQ_ROUTER_DEV,
+ CPP_DEV,
+ CCI_DEV,
+ FLASH_DEV,
+};
+
+struct msm_mctl_set_sdev_data {
+ uint32_t revision;
+ enum msm_cam_subdev_type sdev_type;
+};
+
+#define MSM_CAM_V4L2_IOCTL_GET_CAMERA_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_GET_CONFIG_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_GET_MCTL_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_CTRL_CMD_DONE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_GET_EVENT_PAYLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_IOCTL_SEND_EVENT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct v4l2_event)
+
+#define MSM_CAM_V4L2_IOCTL_CFG_VPE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct msm_vpe_cfg_cmd)
+
+#define MSM_CAM_V4L2_IOCTL_PRIVATE_S_CTRL \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_PRIVATE_G_CTRL \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct msm_camera_v4l2_ioctl_t)
+
+#define MSM_CAM_V4L2_IOCTL_PRIVATE_GENERAL \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 10, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_INIT \
+ _IO('V', BASE_VIDIOC_PRIVATE + 15)
+
+#define VIDIOC_MSM_VPE_RELEASE \
+ _IO('V', BASE_VIDIOC_PRIVATE + 16)
+
+#define VIDIOC_MSM_VPE_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 17, struct msm_mctl_pp_params *)
+
+#define VIDIOC_MSM_AXI_INIT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 18, uint8_t *)
+
+#define VIDIOC_MSM_AXI_RELEASE \
+ _IO('V', BASE_VIDIOC_PRIVATE + 19)
+
+#define VIDIOC_MSM_AXI_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 20, void *)
+
+#define VIDIOC_MSM_AXI_IRQ \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 21, void *)
+
+#define VIDIOC_MSM_AXI_BUF_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 22, void *)
+
+#define VIDIOC_MSM_AXI_RDI_COUNT_UPDATE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 23, void *)
+
+#define VIDIOC_MSM_VFE_INIT \
+ _IO('V', BASE_VIDIOC_PRIVATE + 24)
+
+#define VIDIOC_MSM_VFE_RELEASE \
+ _IO('V', BASE_VIDIOC_PRIVATE + 25)
+
+struct msm_camera_v4l2_ioctl_t {
+ uint32_t id;
+ uint32_t len;
+ uint32_t trans_code;
+ void __user *ioctl_ptr;
+};
+
+struct msm_camera_vfe_params_t {
+ uint32_t operation_mode;
+ uint32_t capture_count;
+ uint8_t skip_reset;
+ uint8_t stop_immediately;
+ uint16_t port_info;
+ uint32_t inst_handle;
+ uint16_t cmd_type;
+};
+
+enum msm_camss_irq_idx {
+ CAMERA_SS_IRQ_0,
+ CAMERA_SS_IRQ_1,
+ CAMERA_SS_IRQ_2,
+ CAMERA_SS_IRQ_3,
+ CAMERA_SS_IRQ_4,
+ CAMERA_SS_IRQ_5,
+ CAMERA_SS_IRQ_6,
+ CAMERA_SS_IRQ_7,
+ CAMERA_SS_IRQ_8,
+ CAMERA_SS_IRQ_9,
+ CAMERA_SS_IRQ_10,
+ CAMERA_SS_IRQ_11,
+ CAMERA_SS_IRQ_12,
+ CAMERA_SS_IRQ_MAX
+};
+
+enum msm_cam_hw_idx {
+ MSM_CAM_HW_MICRO,
+ MSM_CAM_HW_CCI,
+ MSM_CAM_HW_CSI0,
+ MSM_CAM_HW_CSI1,
+ MSM_CAM_HW_CSI2,
+ MSM_CAM_HW_CSI3,
+ MSM_CAM_HW_ISPIF,
+ MSM_CAM_HW_CPP,
+ MSM_CAM_HW_VFE0,
+ MSM_CAM_HW_VFE1,
+ MSM_CAM_HW_JPEG0,
+ MSM_CAM_HW_JPEG1,
+ MSM_CAM_HW_JPEG2,
+ MSM_CAM_HW_MAX
+};
+
+struct msm_camera_irq_cfg {
+ /* Bit mask of all the camera hardwares that needs to
+ * be composited into a single IRQ to the MSM.
+ * Current usage: (may be updated based on hw changes)
+ * Bits 31:13 - Reserved.
+ * Bits 12:0
+ * 12 - MSM_CAM_HW_JPEG2
+ * 11 - MSM_CAM_HW_JPEG1
+ * 10 - MSM_CAM_HW_JPEG0
+ * 9 - MSM_CAM_HW_VFE1
+ * 8 - MSM_CAM_HW_VFE0
+ * 7 - MSM_CAM_HW_CPP
+ * 6 - MSM_CAM_HW_ISPIF
+ * 5 - MSM_CAM_HW_CSI3
+ * 4 - MSM_CAM_HW_CSI2
+ * 3 - MSM_CAM_HW_CSI1
+ * 2 - MSM_CAM_HW_CSI0
+ * 1 - MSM_CAM_HW_CCI
+ * 0 - MSM_CAM_HW_MICRO
+ */
+ uint32_t cam_hw_mask;
+ uint8_t irq_idx;
+ uint8_t num_hwcore;
+};
+
+#define MSM_IRQROUTER_CFG_COMPIRQ \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, void __user *)
+
+#define MAX_NUM_CPP_STRIPS 8
+
+enum msm_cpp_frame_type {
+ MSM_CPP_OFFLINE_FRAME,
+ MSM_CPP_REALTIME_FRAME,
+};
+
+struct msm_cpp_frame_info_t {
+ int32_t frame_id;
+ uint32_t inst_id;
+ uint32_t client_id;
+ enum msm_cpp_frame_type frame_type;
+ uint32_t num_strips;
+};
+
+struct msm_ver_num_info {
+ uint32_t main;
+ uint32_t minor;
+ uint32_t rev;
+};
+
+#define VIDIOC_MSM_CPP_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_EVENTPAYLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_INST_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct msm_camera_v4l2_ioctl_t)
+
+#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)
+
+/* Instance Handle - inst_handle
+ * Data bundle containing the information about where
+ * to get a buffer for a particular camera instance.
+ * This is a bitmask containing the following data:
+ * Buffer Handle Bitmask:
+ * ------------------------------------
+ * Bits : Purpose
+ * ------------------------------------
+ * 31 : is Dev ID valid?
+ * 30 - 24 : Dev ID.
+ * 23 : is Image mode valid?
+ * 22 - 16 : Image mode.
+ * 15 : is MCTL PP inst idx valid?
+ * 14 - 8 : MCTL PP inst idx.
+ * 7 : is Video inst idx valid?
+ * 6 - 0 : Video inst idx.
+ */
+#define CLR_DEVID_MODE(handle) (handle &= 0x00FFFFFF)
+#define SET_DEVID_MODE(handle, data) \
+ (handle |= ((0x1 << 31) | ((data & 0x7F) << 24)))
+#define GET_DEVID_MODE(handle) \
+ ((handle & 0x80000000) ? ((handle & 0x7F000000) >> 24) : 0xFF)
+
+#define CLR_IMG_MODE(handle) (handle &= 0xFF00FFFF)
+#define SET_IMG_MODE(handle, data) \
+ (handle |= ((0x1 << 23) | ((data & 0x7F) << 16)))
+#define GET_IMG_MODE(handle) \
+ ((handle & 0x800000) ? ((handle & 0x7F0000) >> 16) : 0xFF)
+
+#define CLR_MCTLPP_INST_IDX(handle) (handle &= 0xFFFF00FF)
+#define SET_MCTLPP_INST_IDX(handle, data) \
+ (handle |= ((0x1 << 15) | ((data & 0x7F) << 8)))
+#define GET_MCTLPP_INST_IDX(handle) \
+ ((handle & 0x8000) ? ((handle & 0x7F00) >> 8) : 0xFF)
+
+#define CLR_VIDEO_INST_IDX(handle) (handle &= 0xFFFFFF00)
+#define GET_VIDEO_INST_IDX(handle) \
+ ((handle & 0x80) ? (handle & 0x7F) : 0xFF)
+#define SET_VIDEO_INST_IDX(handle, data) \
+ (handle |= (0x1 << 7) | (data & 0x7F))
+#endif
diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h
new file mode 100644
index 0000000..5266c02
--- /dev/null
+++ b/include/uapi/media/msm_camsensor_sdk.h
@@ -0,0 +1,431 @@
+#ifndef __UAPI_LINUX_MSM_CAMSENSOR_SDK_H
+#define __UAPI_LINUX_MSM_CAMSENSOR_SDK_H
+
+#include <linux/videodev2.h>
+
+#define KVERSION 0x1
+
+#define MAX_POWER_CONFIG 12
+#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_12BIT 3
+#define CSI_DECODE_DPCM_10_6_10 4
+#define CSI_DECODE_DPCM_10_8_10 5
+#define MAX_CID 16
+#define I2C_SEQ_REG_DATA_MAX 1024
+#define I2C_REG_DATA_MAX (8*1024)
+
+#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
+#define MSM_V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4')
+ /* 14 BGBG.. GRGR.. */
+#define MSM_V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4')
+ /* 14 GBGB.. RGRG.. */
+#define MSM_V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('B', 'A', '1', '4')
+ /* 14 GRGR.. BGBG.. */
+#define MSM_V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4')
+ /* 14 RGRG.. GBGB.. */
+
+#define MAX_ACTUATOR_REG_TBL_SIZE 8
+#define MAX_ACTUATOR_REGION 5
+#define NUM_ACTUATOR_DIR 2
+#define MAX_ACTUATOR_SCENARIO 8
+#define MAX_ACT_MOD_NAME_SIZE 32
+#define MAX_ACT_NAME_SIZE 32
+#define MAX_ACTUATOR_INIT_SET 120
+#define MAX_I2C_REG_SET 12
+
+#define MAX_LED_TRIGGERS 3
+
+#define MSM_EEPROM_MEMORY_MAP_MAX_SIZE 80
+#define MSM_EEPROM_MAX_MEM_MAP_CNT 8
+
+#define MSM_SENSOR_BYPASS_VIDEO_NODE 1
+
+enum msm_sensor_camera_id_t {
+ CAMERA_0,
+ CAMERA_1,
+ CAMERA_2,
+ CAMERA_3,
+ MAX_CAMERAS,
+};
+
+enum i2c_freq_mode_t {
+ I2C_STANDARD_MODE,
+ I2C_FAST_MODE,
+ I2C_CUSTOM_MODE,
+ I2C_FAST_PLUS_MODE,
+ I2C_MAX_MODES,
+};
+
+enum camb_position_t {
+ BACK_CAMERA_B,
+ FRONT_CAMERA_B,
+ AUX_CAMERA_B = 0x100,
+ INVALID_CAMERA_B,
+};
+
+enum msm_sensor_power_seq_type_t {
+ SENSOR_CLK,
+ SENSOR_GPIO,
+ SENSOR_VREG,
+ SENSOR_I2C_MUX,
+ SENSOR_I2C,
+};
+
+enum msm_camera_i2c_reg_addr_type {
+ MSM_CAMERA_I2C_BYTE_ADDR = 1,
+ MSM_CAMERA_I2C_WORD_ADDR,
+ MSM_CAMERA_I2C_3B_ADDR,
+ MSM_CAMERA_I2C_DWORD_ADDR,
+ MSM_CAMERA_I2C_ADDR_TYPE_MAX,
+};
+#define MSM_CAMERA_I2C_DWORD_ADDR MSM_CAMERA_I2C_DWORD_ADDR
+
+enum msm_camera_i2c_data_type {
+ MSM_CAMERA_I2C_BYTE_DATA = 1,
+ MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_DWORD_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,
+ MSM_CAMERA_I2C_DATA_TYPE_MAX,
+};
+
+enum msm_sensor_power_seq_gpio_t {
+ SENSOR_GPIO_RESET,
+ SENSOR_GPIO_STANDBY,
+ SENSOR_GPIO_AF_PWDM,
+ SENSOR_GPIO_VIO,
+ SENSOR_GPIO_VANA,
+ SENSOR_GPIO_VDIG,
+ SENSOR_GPIO_VAF,
+ SENSOR_GPIO_FL_EN,
+ SENSOR_GPIO_FL_NOW,
+ SENSOR_GPIO_FL_RESET,
+ SENSOR_GPIO_CUSTOM1,
+ SENSOR_GPIO_CUSTOM2,
+ SENSOR_GPIO_CUSTOM3,
+ SENSOR_GPIO_MAX,
+};
+#define SENSOR_GPIO_CUSTOM3 SENSOR_GPIO_CUSTOM3
+
+enum msm_ir_cut_filter_gpio_t {
+ IR_CUT_FILTER_GPIO_P = 0,
+ IR_CUT_FILTER_GPIO_M,
+ IR_CUT_FILTER_GPIO_MAX,
+};
+#define IR_CUT_FILTER_GPIO_P IR_CUT_FILTER_GPIO_P
+#define IR_CUT_FILTER_GPIO_M IR_CUT_FILTER_GPIO_M
+#define R_CUT_FILTER_GPIO_MAX IR_CUT_FILTER_GPIO_MAX
+
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_V_CUSTOM1,
+ CAM_V_CUSTOM2,
+ CAM_VREG_MAX,
+};
+
+enum msm_sensor_clk_type_t {
+ SENSOR_CAM_MCLK,
+ SENSOR_CAM_CLK,
+ SENSOR_CAM_CLK_MAX,
+};
+
+enum camerab_mode_t {
+ CAMERA_MODE_2D_B = (1<<0),
+ CAMERA_MODE_3D_B = (1<<1),
+ CAMERA_MODE_INVALID = (1<<2),
+};
+
+enum msm_actuator_data_type {
+ MSM_ACTUATOR_BYTE_DATA = 1,
+ MSM_ACTUATOR_WORD_DATA,
+};
+
+enum msm_actuator_addr_type {
+ MSM_ACTUATOR_BYTE_ADDR = 1,
+ MSM_ACTUATOR_WORD_ADDR,
+};
+
+enum msm_actuator_write_type {
+ MSM_ACTUATOR_WRITE_HW_DAMP,
+ MSM_ACTUATOR_WRITE_DAC,
+ MSM_ACTUATOR_WRITE,
+ MSM_ACTUATOR_WRITE_DIR_REG,
+ MSM_ACTUATOR_POLL,
+ MSM_ACTUATOR_READ_WRITE,
+};
+
+enum msm_actuator_i2c_operation {
+ MSM_ACT_WRITE = 0,
+ MSM_ACT_POLL,
+};
+
+enum actuator_type {
+ ACTUATOR_VCM,
+ ACTUATOR_PIEZO,
+ ACTUATOR_HVCM,
+ ACTUATOR_BIVCM,
+};
+
+enum msm_flash_driver_type {
+ FLASH_DRIVER_PMIC,
+ FLASH_DRIVER_I2C,
+ FLASH_DRIVER_GPIO,
+ FLASH_DRIVER_DEFAULT
+};
+
+enum msm_flash_cfg_type_t {
+ CFG_FLASH_INIT,
+ CFG_FLASH_RELEASE,
+ CFG_FLASH_OFF,
+ CFG_FLASH_LOW,
+ CFG_FLASH_HIGH,
+};
+
+enum msm_ir_led_cfg_type_t {
+ CFG_IR_LED_INIT = 0,
+ CFG_IR_LED_RELEASE,
+ CFG_IR_LED_OFF,
+ CFG_IR_LED_ON,
+};
+#define CFG_IR_LED_INIT CFG_IR_LED_INIT
+#define CFG_IR_LED_RELEASE CFG_IR_LED_RELEASE
+#define CFG_IR_LED_OFF CFG_IR_LED_OFF
+#define CFG_IR_LED_ON CFG_IR_LED_ON
+
+enum msm_laser_led_cfg_type_t {
+ CFG_LASER_LED_INIT,
+ CFG_LASER_LED_CONTROL,
+};
+#define CFG_LASER_LED_INIT CFG_LASER_LED_INIT
+#define CFG_LASER_LED_CONTROL CFG_LASER_LED_CONTROL
+
+enum msm_ir_cut_cfg_type_t {
+ CFG_IR_CUT_INIT = 0,
+ CFG_IR_CUT_RELEASE,
+ CFG_IR_CUT_OFF,
+ CFG_IR_CUT_ON,
+};
+#define CFG_IR_CUT_INIT CFG_IR_CUT_INIT
+#define CFG_IR_CUT_RELEASE CFG_IR_CUT_RELEASE
+#define CFG_IR_CUT_OFF CFG_IR_CUT_OFF
+#define CFG_IR_CUT_ON CFG_IR_CUT_ON
+
+enum msm_sensor_output_format_t {
+ MSM_SENSOR_BAYER,
+ MSM_SENSOR_YCBCR,
+ MSM_SENSOR_META,
+};
+
+struct msm_sensor_power_setting {
+ enum msm_sensor_power_seq_type_t seq_type;
+ unsigned short seq_val;
+ long config_val;
+ unsigned short delay;
+ void *data[10];
+};
+
+struct msm_sensor_power_setting_array {
+ struct msm_sensor_power_setting power_setting_a[MAX_POWER_CONFIG];
+ struct msm_sensor_power_setting *power_setting;
+ unsigned short size;
+ struct msm_sensor_power_setting power_down_setting_a[MAX_POWER_CONFIG];
+ struct msm_sensor_power_setting *power_down_setting;
+ unsigned short size_down;
+};
+
+enum msm_camera_i2c_operation {
+ MSM_CAM_WRITE = 0,
+ MSM_CAM_POLL,
+ MSM_CAM_READ,
+};
+
+struct msm_sensor_i2c_sync_params {
+ unsigned int cid;
+ int csid;
+ unsigned short line;
+ unsigned short delay;
+};
+
+struct msm_camera_reg_settings_t {
+ uint16_t reg_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint16_t reg_data;
+ enum msm_camera_i2c_data_type data_type;
+ enum msm_camera_i2c_operation i2c_operation;
+ uint16_t delay;
+};
+
+struct msm_eeprom_mem_map_t {
+ int slave_addr;
+ struct msm_camera_reg_settings_t
+ mem_settings[MSM_EEPROM_MEMORY_MAP_MAX_SIZE];
+ int memory_map_size;
+};
+
+struct msm_eeprom_memory_map_array {
+ struct msm_eeprom_mem_map_t memory_map[MSM_EEPROM_MAX_MEM_MAP_CNT];
+ uint32_t msm_size_of_max_mappings;
+};
+
+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 */
+ unsigned int sensor_mount_angle;
+};
+
+struct msm_sensor_id_info_t {
+ unsigned short sensor_id_reg_addr;
+ unsigned short sensor_id;
+ unsigned short sensor_id_mask;
+};
+
+struct msm_camera_sensor_slave_info {
+ char sensor_name[32];
+ char eeprom_name[32];
+ char actuator_name[32];
+ char ois_name[32];
+ char flash_name[32];
+ enum msm_sensor_camera_id_t camera_id;
+ unsigned short slave_addr;
+ enum i2c_freq_mode_t i2c_freq_mode;
+ 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;
+ unsigned char is_init_params_valid;
+ struct msm_sensor_init_params sensor_init_params;
+ enum msm_sensor_output_format_t output_format;
+ uint8_t bypass_video_node_creation;
+};
+
+struct msm_camera_i2c_reg_array {
+ unsigned short reg_addr;
+ unsigned short reg_data;
+ unsigned int delay;
+};
+
+struct msm_camera_i2c_reg_setting {
+ struct msm_camera_i2c_reg_array *reg_setting;
+ unsigned short size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ unsigned short delay;
+};
+
+struct msm_camera_csid_vc_cfg {
+ unsigned char cid;
+ unsigned char dt;
+ unsigned char decode_format;
+};
+
+struct msm_camera_csid_lut_params {
+ unsigned char num_cid;
+ struct msm_camera_csid_vc_cfg vc_cfg_a[MAX_CID];
+ struct msm_camera_csid_vc_cfg *vc_cfg[MAX_CID];
+};
+
+struct msm_camera_csid_params {
+ unsigned char lane_cnt;
+ unsigned short lane_assign;
+ unsigned char phy_sel;
+ unsigned int csi_clk;
+ struct msm_camera_csid_lut_params lut_params;
+ unsigned char csi_3p_sel;
+};
+
+struct msm_camera_csid_testmode_parms {
+ unsigned int num_bytes_per_line;
+ unsigned int num_lines;
+ unsigned int h_blanking_count;
+ unsigned int v_blanking_count;
+ unsigned int payload_mode;
+};
+
+struct msm_camera_csiphy_params {
+ unsigned char lane_cnt;
+ unsigned char settle_cnt;
+ unsigned short lane_mask;
+ unsigned char combo_mode;
+ unsigned char csid_core;
+ unsigned int csiphy_clk;
+ unsigned char csi_3phase;
+};
+
+struct msm_camera_i2c_seq_reg_array {
+ unsigned short reg_addr;
+ unsigned char reg_data[I2C_SEQ_REG_DATA_MAX];
+ unsigned short reg_data_size;
+};
+
+struct msm_camera_i2c_seq_reg_setting {
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ unsigned short size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ unsigned short delay;
+};
+
+struct msm_actuator_reg_params_t {
+ enum msm_actuator_write_type reg_write_type;
+ unsigned int hw_mask;
+ unsigned short reg_addr;
+ unsigned short hw_shift;
+ unsigned short data_shift;
+ unsigned short data_type;
+ unsigned short addr_type;
+ unsigned short reg_data;
+ unsigned short delay;
+};
+
+
+struct damping_params_t {
+ unsigned int damping_step;
+ unsigned int damping_delay;
+ unsigned int hw_params;
+};
+
+struct region_params_t {
+ /* [0] = ForwardDirection Macro boundary
+ * [1] = ReverseDirection Inf boundary
+ */
+ unsigned short step_bound[2];
+ unsigned short code_per_step;
+ /* qvalue for converting float type numbers to integer format */
+ unsigned int qvalue;
+};
+
+struct reg_settings_t {
+ unsigned short reg_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ unsigned short reg_data;
+ enum msm_camera_i2c_data_type data_type;
+ enum msm_actuator_i2c_operation i2c_operation;
+ unsigned int delay;
+};
+
+struct msm_camera_i2c_reg_setting_array {
+ struct msm_camera_i2c_reg_array reg_setting_a[MAX_I2C_REG_SET];
+ unsigned short size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ unsigned short delay;
+};
+
+#endif
diff --git a/include/uapi/media/msm_fd.h b/include/uapi/media/msm_fd.h
new file mode 100644
index 0000000..eb3bd78
--- /dev/null
+++ b/include/uapi/media/msm_fd.h
@@ -0,0 +1,76 @@
+#ifndef __UAPI_MSM_FD__
+#define __UAPI_MSM_FD__
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+
+/*
+ * struct msm_fd_event - Structure contain event info.
+ * @buf_index: Buffer index.
+ * @frame_id: Frame id.
+ * @face_cnt: Detected faces.
+ */
+struct msm_fd_event {
+ __u32 buf_index;
+ __u32 frame_id;
+ __u32 face_cnt;
+};
+
+/*
+ * enum msm_fd_pose - Face pose.
+ */
+enum msm_fd_pose {
+ MSM_FD_POSE_FRONT,
+ MSM_FD_POSE_RIGHT_DIAGONAL,
+ MSM_FD_POSE_RIGHT,
+ MSM_FD_POSE_LEFT_DIAGONAL,
+ MSM_FD_POSE_LEFT,
+};
+
+/*
+ * struct msm_fd_face_data - Structure contain detected face data.
+ * @pose: refer to enum msm_fd_pose.
+ * @angle: Face angle
+ * @confidence: Face confidence level.
+ * @reserved: Reserved data for future use.
+ * @face: Face rectangle.
+ */
+struct msm_fd_face_data {
+ __u32 pose;
+ __u32 angle;
+ __u32 confidence;
+ __u32 reserved;
+ struct v4l2_rect face;
+};
+
+/*
+ * struct msm_fd_result - Structure contain detected faces result.
+ * @frame_id: Frame id of requested result.
+ * @face_cnt: Number of result faces, driver can modify this value (to smaller)
+ * @face_data: Pointer to array of face data structures.
+ * Array size should not be smaller then face_cnt.
+ */
+struct msm_fd_result {
+ __u32 frame_id;
+ __u32 face_cnt;
+ struct msm_fd_face_data __user *face_data;
+};
+
+/* MSM FD private ioctl ID */
+#define VIDIOC_MSM_FD_GET_RESULT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_fd_result)
+
+/* MSM FD event ID */
+#define MSM_EVENT_FD (V4L2_EVENT_PRIVATE_START)
+
+/* MSM FD control ID's */
+#define V4L2_CID_FD_SPEED (V4L2_CID_PRIVATE_BASE)
+#define V4L2_CID_FD_FACE_ANGLE (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_FD_MIN_FACE_SIZE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_FD_FACE_DIRECTION (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_FD_DETECTION_THRESHOLD (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_FD_WORK_MEMORY_SIZE (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_FD_WORK_MEMORY_FD (V4L2_CID_PRIVATE_BASE + 6)
+
+#endif
+
diff --git a/include/uapi/media/msm_isp.h b/include/uapi/media/msm_isp.h
new file mode 100644
index 0000000..90d87c2
--- /dev/null
+++ b/include/uapi/media/msm_isp.h
@@ -0,0 +1,344 @@
+#ifndef __UAPI_MSM_ISP_H__
+#define __UAPI_MSM_ISP_H__
+
+#define BIT(nr) (1UL << (nr))
+
+/* ISP message IDs */
+#define MSG_ID_RESET_ACK 0
+#define MSG_ID_START_ACK 1
+#define MSG_ID_STOP_ACK 2
+#define MSG_ID_UPDATE_ACK 3
+#define MSG_ID_OUTPUT_P 4
+#define MSG_ID_OUTPUT_T 5
+#define MSG_ID_OUTPUT_S 6
+#define MSG_ID_OUTPUT_V 7
+#define MSG_ID_SNAPSHOT_DONE 8
+#define MSG_ID_STATS_AEC 9
+#define MSG_ID_STATS_AF 10
+#define MSG_ID_STATS_AWB 11
+#define MSG_ID_STATS_RS 12
+#define MSG_ID_STATS_CS 13
+#define MSG_ID_STATS_IHIST 14
+#define MSG_ID_STATS_SKIN 15
+#define MSG_ID_EPOCH1 16
+#define MSG_ID_EPOCH2 17
+#define MSG_ID_SYNC_TIMER0_DONE 18
+#define MSG_ID_SYNC_TIMER1_DONE 19
+#define MSG_ID_SYNC_TIMER2_DONE 20
+#define MSG_ID_ASYNC_TIMER0_DONE 21
+#define MSG_ID_ASYNC_TIMER1_DONE 22
+#define MSG_ID_ASYNC_TIMER2_DONE 23
+#define MSG_ID_ASYNC_TIMER3_DONE 24
+#define MSG_ID_AE_OVERFLOW 25
+#define MSG_ID_AF_OVERFLOW 26
+#define MSG_ID_AWB_OVERFLOW 27
+#define MSG_ID_RS_OVERFLOW 28
+#define MSG_ID_CS_OVERFLOW 29
+#define MSG_ID_IHIST_OVERFLOW 30
+#define MSG_ID_SKIN_OVERFLOW 31
+#define MSG_ID_AXI_ERROR 32
+#define MSG_ID_CAMIF_OVERFLOW 33
+#define MSG_ID_VIOLATION 34
+#define MSG_ID_CAMIF_ERROR 35
+#define MSG_ID_BUS_OVERFLOW 36
+#define MSG_ID_SOF_ACK 37
+#define MSG_ID_STOP_REC_ACK 38
+#define MSG_ID_STATS_AWB_AEC 39
+#define MSG_ID_OUTPUT_PRIMARY 40
+#define MSG_ID_OUTPUT_SECONDARY 41
+#define MSG_ID_STATS_COMPOSITE 42
+#define MSG_ID_OUTPUT_TERTIARY1 43
+#define MSG_ID_STOP_LS_ACK 44
+#define MSG_ID_OUTPUT_TERTIARY2 45
+#define MSG_ID_STATS_BG 46
+#define MSG_ID_STATS_BF 47
+#define MSG_ID_STATS_BHIST 48
+#define MSG_ID_RDI0_UPDATE_ACK 49
+#define MSG_ID_RDI1_UPDATE_ACK 50
+#define MSG_ID_RDI2_UPDATE_ACK 51
+#define MSG_ID_PIX0_UPDATE_ACK 52
+#define MSG_ID_PREV_STOP_ACK 53
+#define MSG_ID_STATS_BE 54
+
+
+/* ISP command IDs */
+#define VFE_CMD_DUMMY_0 0
+#define VFE_CMD_SET_CLK 1
+#define VFE_CMD_RESET 2
+#define VFE_CMD_START 3
+#define VFE_CMD_TEST_GEN_START 4
+#define VFE_CMD_OPERATION_CFG 5
+#define VFE_CMD_AXI_OUT_CFG 6
+#define VFE_CMD_CAMIF_CFG 7
+#define VFE_CMD_AXI_INPUT_CFG 8
+#define VFE_CMD_BLACK_LEVEL_CFG 9
+#define VFE_CMD_MESH_ROLL_OFF_CFG 10
+#define VFE_CMD_DEMUX_CFG 11
+#define VFE_CMD_FOV_CFG 12
+#define VFE_CMD_MAIN_SCALER_CFG 13
+#define VFE_CMD_WB_CFG 14
+#define VFE_CMD_COLOR_COR_CFG 15
+#define VFE_CMD_RGB_G_CFG 16
+#define VFE_CMD_LA_CFG 17
+#define VFE_CMD_CHROMA_EN_CFG 18
+#define VFE_CMD_CHROMA_SUP_CFG 19
+#define VFE_CMD_MCE_CFG 20
+#define VFE_CMD_SK_ENHAN_CFG 21
+#define VFE_CMD_ASF_CFG 22
+#define VFE_CMD_S2Y_CFG 23
+#define VFE_CMD_S2CbCr_CFG 24
+#define VFE_CMD_CHROMA_SUBS_CFG 25
+#define VFE_CMD_OUT_CLAMP_CFG 26
+#define VFE_CMD_FRAME_SKIP_CFG 27
+#define VFE_CMD_DUMMY_1 28
+#define VFE_CMD_DUMMY_2 29
+#define VFE_CMD_DUMMY_3 30
+#define VFE_CMD_UPDATE 31
+#define VFE_CMD_BL_LVL_UPDATE 32
+#define VFE_CMD_DEMUX_UPDATE 33
+#define VFE_CMD_FOV_UPDATE 34
+#define VFE_CMD_MAIN_SCALER_UPDATE 35
+#define VFE_CMD_WB_UPDATE 36
+#define VFE_CMD_COLOR_COR_UPDATE 37
+#define VFE_CMD_RGB_G_UPDATE 38
+#define VFE_CMD_LA_UPDATE 39
+#define VFE_CMD_CHROMA_EN_UPDATE 40
+#define VFE_CMD_CHROMA_SUP_UPDATE 41
+#define VFE_CMD_MCE_UPDATE 42
+#define VFE_CMD_SK_ENHAN_UPDATE 43
+#define VFE_CMD_S2CbCr_UPDATE 44
+#define VFE_CMD_S2Y_UPDATE 45
+#define VFE_CMD_ASF_UPDATE 46
+#define VFE_CMD_FRAME_SKIP_UPDATE 47
+#define VFE_CMD_CAMIF_FRAME_UPDATE 48
+#define VFE_CMD_STATS_AF_UPDATE 49
+#define VFE_CMD_STATS_AE_UPDATE 50
+#define VFE_CMD_STATS_AWB_UPDATE 51
+#define VFE_CMD_STATS_RS_UPDATE 52
+#define VFE_CMD_STATS_CS_UPDATE 53
+#define VFE_CMD_STATS_SKIN_UPDATE 54
+#define VFE_CMD_STATS_IHIST_UPDATE 55
+#define VFE_CMD_DUMMY_4 56
+#define VFE_CMD_EPOCH1_ACK 57
+#define VFE_CMD_EPOCH2_ACK 58
+#define VFE_CMD_START_RECORDING 59
+#define VFE_CMD_STOP_RECORDING 60
+#define VFE_CMD_DUMMY_5 61
+#define VFE_CMD_DUMMY_6 62
+#define VFE_CMD_CAPTURE 63
+#define VFE_CMD_DUMMY_7 64
+#define VFE_CMD_STOP 65
+#define VFE_CMD_GET_HW_VERSION 66
+#define VFE_CMD_GET_FRAME_SKIP_COUNTS 67
+#define VFE_CMD_OUTPUT1_BUFFER_ENQ 68
+#define VFE_CMD_OUTPUT2_BUFFER_ENQ 69
+#define VFE_CMD_OUTPUT3_BUFFER_ENQ 70
+#define VFE_CMD_JPEG_OUT_BUF_ENQ 71
+#define VFE_CMD_RAW_OUT_BUF_ENQ 72
+#define VFE_CMD_RAW_IN_BUF_ENQ 73
+#define VFE_CMD_STATS_AF_ENQ 74
+#define VFE_CMD_STATS_AE_ENQ 75
+#define VFE_CMD_STATS_AWB_ENQ 76
+#define VFE_CMD_STATS_RS_ENQ 77
+#define VFE_CMD_STATS_CS_ENQ 78
+#define VFE_CMD_STATS_SKIN_ENQ 79
+#define VFE_CMD_STATS_IHIST_ENQ 80
+#define VFE_CMD_DUMMY_8 81
+#define VFE_CMD_JPEG_ENC_CFG 82
+#define VFE_CMD_DUMMY_9 83
+#define VFE_CMD_STATS_AF_START 84
+#define VFE_CMD_STATS_AF_STOP 85
+#define VFE_CMD_STATS_AE_START 86
+#define VFE_CMD_STATS_AE_STOP 87
+#define VFE_CMD_STATS_AWB_START 88
+#define VFE_CMD_STATS_AWB_STOP 89
+#define VFE_CMD_STATS_RS_START 90
+#define VFE_CMD_STATS_RS_STOP 91
+#define VFE_CMD_STATS_CS_START 92
+#define VFE_CMD_STATS_CS_STOP 93
+#define VFE_CMD_STATS_SKIN_START 94
+#define VFE_CMD_STATS_SKIN_STOP 95
+#define VFE_CMD_STATS_IHIST_START 96
+#define VFE_CMD_STATS_IHIST_STOP 97
+#define VFE_CMD_DUMMY_10 98
+#define VFE_CMD_SYNC_TIMER_SETTING 99
+#define VFE_CMD_ASYNC_TIMER_SETTING 100
+#define VFE_CMD_LIVESHOT 101
+#define VFE_CMD_LA_SETUP 102
+#define VFE_CMD_LINEARIZATION_CFG 103
+#define VFE_CMD_DEMOSAICV3 104
+#define VFE_CMD_DEMOSAICV3_ABCC_CFG 105
+#define VFE_CMD_DEMOSAICV3_DBCC_CFG 106
+#define VFE_CMD_DEMOSAICV3_DBPC_CFG 107
+#define VFE_CMD_DEMOSAICV3_ABF_CFG 108
+#define VFE_CMD_DEMOSAICV3_ABCC_UPDATE 109
+#define VFE_CMD_DEMOSAICV3_DBCC_UPDATE 110
+#define VFE_CMD_DEMOSAICV3_DBPC_UPDATE 111
+#define VFE_CMD_XBAR_CFG 112
+#define VFE_CMD_MODULE_CFG 113
+#define VFE_CMD_ZSL 114
+#define VFE_CMD_LINEARIZATION_UPDATE 115
+#define VFE_CMD_DEMOSAICV3_ABF_UPDATE 116
+#define VFE_CMD_CLF_CFG 117
+#define VFE_CMD_CLF_LUMA_UPDATE 118
+#define VFE_CMD_CLF_CHROMA_UPDATE 119
+#define VFE_CMD_PCA_ROLL_OFF_CFG 120
+#define VFE_CMD_PCA_ROLL_OFF_UPDATE 121
+#define VFE_CMD_GET_REG_DUMP 122
+#define VFE_CMD_GET_LINEARIZATON_TABLE 123
+#define VFE_CMD_GET_MESH_ROLLOFF_TABLE 124
+#define VFE_CMD_GET_PCA_ROLLOFF_TABLE 125
+#define VFE_CMD_GET_RGB_G_TABLE 126
+#define VFE_CMD_GET_LA_TABLE 127
+#define VFE_CMD_DEMOSAICV3_UPDATE 128
+#define VFE_CMD_ACTIVE_REGION_CFG 129
+#define VFE_CMD_COLOR_PROCESSING_CONFIG 130
+#define VFE_CMD_STATS_WB_AEC_CONFIG 131
+#define VFE_CMD_STATS_WB_AEC_UPDATE 132
+#define VFE_CMD_Y_GAMMA_CONFIG 133
+#define VFE_CMD_SCALE_OUTPUT1_CONFIG 134
+#define VFE_CMD_SCALE_OUTPUT2_CONFIG 135
+#define VFE_CMD_CAPTURE_RAW 136
+#define VFE_CMD_STOP_LIVESHOT 137
+#define VFE_CMD_RECONFIG_VFE 138
+#define VFE_CMD_STATS_REQBUF 139
+#define VFE_CMD_STATS_ENQUEUEBUF 140
+#define VFE_CMD_STATS_FLUSH_BUFQ 141
+#define VFE_CMD_STATS_UNREGBUF 142
+#define VFE_CMD_STATS_BG_START 143
+#define VFE_CMD_STATS_BG_STOP 144
+#define VFE_CMD_STATS_BF_START 145
+#define VFE_CMD_STATS_BF_STOP 146
+#define VFE_CMD_STATS_BHIST_START 147
+#define VFE_CMD_STATS_BHIST_STOP 148
+#define VFE_CMD_RESET_2 149
+#define VFE_CMD_FOV_ENC_CFG 150
+#define VFE_CMD_FOV_VIEW_CFG 151
+#define VFE_CMD_FOV_ENC_UPDATE 152
+#define VFE_CMD_FOV_VIEW_UPDATE 153
+#define VFE_CMD_SCALER_ENC_CFG 154
+#define VFE_CMD_SCALER_VIEW_CFG 155
+#define VFE_CMD_SCALER_ENC_UPDATE 156
+#define VFE_CMD_SCALER_VIEW_UPDATE 157
+#define VFE_CMD_COLORXFORM_ENC_CFG 158
+#define VFE_CMD_COLORXFORM_VIEW_CFG 159
+#define VFE_CMD_COLORXFORM_ENC_UPDATE 160
+#define VFE_CMD_COLORXFORM_VIEW_UPDATE 161
+#define VFE_CMD_TEST_GEN_CFG 162
+#define VFE_CMD_STATS_BE_START 163
+#define VFE_CMD_STATS_BE_STOP 164
+struct msm_isp_cmd {
+ int32_t id;
+ uint16_t length;
+ void *value;
+};
+
+#define VPE_CMD_DUMMY_0 0
+#define VPE_CMD_INIT 1
+#define VPE_CMD_DEINIT 2
+#define VPE_CMD_ENABLE 3
+#define VPE_CMD_DISABLE 4
+#define VPE_CMD_RESET 5
+#define VPE_CMD_FLUSH 6
+#define VPE_CMD_OPERATION_MODE_CFG 7
+#define VPE_CMD_INPUT_PLANE_CFG 8
+#define VPE_CMD_OUTPUT_PLANE_CFG 9
+#define VPE_CMD_INPUT_PLANE_UPDATE 10
+#define VPE_CMD_SCALE_CFG_TYPE 11
+#define VPE_CMD_ZOOM 13
+#define VPE_CMD_MAX 14
+
+#define MSM_PP_CMD_TYPE_NOT_USED 0 /* not used */
+#define MSM_PP_CMD_TYPE_VPE 1 /* VPE cmd */
+#define MSM_PP_CMD_TYPE_MCTL 2 /* MCTL cmd */
+
+#define MCTL_CMD_DUMMY_0 0 /* not used */
+#define MCTL_CMD_GET_FRAME_BUFFER 1 /* reserve a free frame buffer */
+#define MCTL_CMD_PUT_FRAME_BUFFER 2 /* return the free frame buffer */
+#define MCTL_CMD_DIVERT_FRAME_PP_PATH 3 /* divert frame for pp */
+
+/* event typese sending to MCTL PP module */
+#define MCTL_PP_EVENT_NOTUSED 0
+#define MCTL_PP_EVENT_CMD_ACK 1
+
+#define VPE_OPERATION_MODE_CFG_LEN 4
+#define VPE_INPUT_PLANE_CFG_LEN 24
+#define VPE_OUTPUT_PLANE_CFG_LEN 20
+#define VPE_INPUT_PLANE_UPDATE_LEN 12
+#define VPE_SCALER_CONFIG_LEN 260
+#define VPE_DIS_OFFSET_CFG_LEN 12
+
+
+#define CAPTURE_WIDTH 1280
+#define IMEM_Y_SIZE (CAPTURE_WIDTH*16)
+#define IMEM_CBCR_SIZE (CAPTURE_WIDTH*8)
+
+#define IMEM_Y_PING_OFFSET 0x2E000000
+#define IMEM_CBCR_PING_OFFSET (IMEM_Y_PING_OFFSET + IMEM_Y_SIZE)
+
+#define IMEM_Y_PONG_OFFSET (IMEM_CBCR_PING_OFFSET + IMEM_CBCR_SIZE)
+#define IMEM_CBCR_PONG_OFFSET (IMEM_Y_PONG_OFFSET + IMEM_Y_SIZE)
+
+
+struct msm_vpe_op_mode_cfg {
+ uint8_t op_mode_cfg[VPE_OPERATION_MODE_CFG_LEN];
+};
+
+struct msm_vpe_input_plane_cfg {
+ uint8_t input_plane_cfg[VPE_INPUT_PLANE_CFG_LEN];
+};
+
+struct msm_vpe_output_plane_cfg {
+ uint8_t output_plane_cfg[VPE_OUTPUT_PLANE_CFG_LEN];
+};
+
+struct msm_vpe_input_plane_update_cfg {
+ uint8_t input_plane_update_cfg[VPE_INPUT_PLANE_UPDATE_LEN];
+};
+
+struct msm_vpe_scaler_cfg {
+ uint8_t scaler_cfg[VPE_SCALER_CONFIG_LEN];
+};
+
+struct msm_vpe_flush_frame_buffer {
+ uint32_t src_buf_handle;
+ uint32_t dest_buf_handle;
+ int path;
+};
+
+struct msm_mctl_pp_frame_buffer {
+ uint32_t buf_handle;
+ int path;
+};
+struct msm_mctl_pp_divert_pp {
+ int path;
+ int enable;
+};
+struct msm_vpe_clock_rate {
+ uint32_t rate;
+};
+
+#define MSM_MCTL_PP_VPE_FRAME_ACK (1<<0)
+#define MSM_MCTL_PP_VPE_FRAME_TO_APP (1<<1)
+
+#define VFE_OUTPUTS_MAIN_AND_PREVIEW BIT(0)
+#define VFE_OUTPUTS_MAIN_AND_VIDEO BIT(1)
+#define VFE_OUTPUTS_MAIN_AND_THUMB BIT(2)
+#define VFE_OUTPUTS_THUMB_AND_MAIN BIT(3)
+#define VFE_OUTPUTS_PREVIEW_AND_VIDEO BIT(4)
+#define VFE_OUTPUTS_VIDEO_AND_PREVIEW BIT(5)
+#define VFE_OUTPUTS_PREVIEW BIT(6)
+#define VFE_OUTPUTS_VIDEO BIT(7)
+#define VFE_OUTPUTS_RAW BIT(8)
+#define VFE_OUTPUTS_JPEG_AND_THUMB BIT(9)
+#define VFE_OUTPUTS_THUMB_AND_JPEG BIT(10)
+#define VFE_OUTPUTS_RDI0 BIT(11)
+#define VFE_OUTPUTS_RDI1 BIT(12)
+
+struct msm_frame_info {
+ uint32_t inst_handle;
+ uint32_t path;
+};
+
+#endif /*__UAPI_MSM_ISP_H__*/
+
diff --git a/include/uapi/media/msm_jpeg.h b/include/uapi/media/msm_jpeg.h
new file mode 100644
index 0000000..897a180
--- /dev/null
+++ b/include/uapi/media/msm_jpeg.h
@@ -0,0 +1,125 @@
+#ifndef __UAPI_LINUX_MSM_JPEG_H
+#define __UAPI_LINUX_MSM_JPEG_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define OUTPUT_H2V1 0
+#define OUTPUT_H2V2 1
+#define OUTPUT_BYTE 6
+
+#define MSM_JPEG_IOCTL_MAGIC 'g'
+
+#define MSM_JPEG_IOCTL_GET_HW_VERSION \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 1, struct msm_jpeg_hw_cmd)
+
+#define MSM_JPEG_IOCTL_RESET \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 2, struct msm_jpeg_ctrl_cmd)
+
+#define MSM_JPEG_IOCTL_STOP \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 3, struct msm_jpeg_hw_cmds)
+
+#define MSM_JPEG_IOCTL_START \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 4, struct msm_jpeg_hw_cmds)
+
+#define MSM_JPEG_IOCTL_INPUT_BUF_ENQUEUE \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 5, struct msm_jpeg_buf)
+
+#define MSM_JPEG_IOCTL_INPUT_GET \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 6, struct msm_jpeg_buf)
+
+#define MSM_JPEG_IOCTL_INPUT_GET_UNBLOCK \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 7, int)
+
+#define MSM_JPEG_IOCTL_OUTPUT_BUF_ENQUEUE \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 8, struct msm_jpeg_buf)
+
+#define MSM_JPEG_IOCTL_OUTPUT_GET \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 9, struct msm_jpeg_buf)
+
+#define MSM_JPEG_IOCTL_OUTPUT_GET_UNBLOCK \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 10, int)
+
+#define MSM_JPEG_IOCTL_EVT_GET \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 11, struct msm_jpeg_ctrl_cmd)
+
+#define MSM_JPEG_IOCTL_EVT_GET_UNBLOCK \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 12, int)
+
+#define MSM_JPEG_IOCTL_HW_CMD \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 13, struct msm_jpeg_hw_cmd)
+
+#define MSM_JPEG_IOCTL_HW_CMDS \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 14, struct msm_jpeg_hw_cmds)
+
+#define MSM_JPEG_IOCTL_TEST_DUMP_REGION \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 15, unsigned long)
+
+#define MSM_JPEG_IOCTL_SET_CLK_RATE \
+ _IOW(MSM_JPEG_IOCTL_MAGIC, 16, unsigned int)
+
+#define MSM_JPEG_MODE_REALTIME_ENCODE 0
+#define MSM_JPEG_MODE_OFFLINE_ENCODE 1
+#define MSM_JPEG_MODE_REALTIME_ROTATION 2
+#define MSM_JPEG_MODE_OFFLINE_ROTATION 3
+
+struct msm_jpeg_ctrl_cmd {
+ uint32_t type;
+ uint32_t len;
+ void *value;
+};
+
+#define MSM_JPEG_EVT_RESET 0
+#define MSM_JPEG_EVT_SESSION_DONE 1
+#define MSM_JPEG_EVT_ERR 2
+
+struct msm_jpeg_buf {
+ uint32_t type;
+ int fd;
+
+ void *vaddr;
+
+ uint32_t y_off;
+ uint32_t y_len;
+ uint32_t framedone_len;
+
+ uint32_t cbcr_off;
+ uint32_t cbcr_len;
+
+ uint32_t num_of_mcu_rows;
+ uint32_t offset;
+ uint32_t pln2_off;
+ uint32_t pln2_len;
+};
+
+#define MSM_JPEG_HW_CMD_TYPE_READ 0
+#define MSM_JPEG_HW_CMD_TYPE_WRITE 1
+#define MSM_JPEG_HW_CMD_TYPE_WRITE_OR 2
+#define MSM_JPEG_HW_CMD_TYPE_UWAIT 3
+#define MSM_JPEG_HW_CMD_TYPE_MWAIT 4
+#define MSM_JPEG_HW_CMD_TYPE_MDELAY 5
+#define MSM_JPEG_HW_CMD_TYPE_UDELAY 6
+struct msm_jpeg_hw_cmd {
+
+ uint32_t type:4;
+
+ /* n microseconds of timeout for WAIT */
+ /* n microseconds of time for DELAY */
+ /* repeat n times for READ/WRITE */
+ /* max is 0xFFF, 4095 */
+ uint32_t n:12;
+ uint32_t offset:16;
+ uint32_t mask;
+ union {
+ uint32_t data; /* for single READ/WRITE/WAIT, n = 1 */
+ uint32_t *pdata; /* for multiple READ/WRITE/WAIT, n > 1 */
+ };
+};
+
+struct msm_jpeg_hw_cmds {
+ uint32_t m; /* number of elements in the hw_cmd array */
+ struct msm_jpeg_hw_cmd hw_cmd[1];
+};
+
+#endif
+
diff --git a/include/uapi/media/msm_jpeg_dma.h b/include/uapi/media/msm_jpeg_dma.h
new file mode 100644
index 0000000..a3eab9f
--- /dev/null
+++ b/include/uapi/media/msm_jpeg_dma.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2015-2016, 2018, 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 __UAPI_MSM_JPEG_DMA__
+#define __UAPI_MSM_JPEG_DMA__
+
+#include <linux/videodev2.h>
+
+/* msm jpeg dma control ID's */
+#define V4L2_CID_JPEG_DMA_SPEED (V4L2_CID_PRIVATE_BASE)
+#define V4L2_CID_JPEG_DMA_MAX_DOWN_SCALE (V4L2_CID_PRIVATE_BASE + 1)
+
+/* msm_jpeg_dma_buf */
+struct msm_jpeg_dma_buff {
+ int32_t fd;
+ uint32_t offset;
+};
+
+#endif /* __UAPI_MSM_JPEG_DMA__ */
diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h
new file mode 100644
index 0000000..58aff97
--- /dev/null
+++ b/include/uapi/media/msmb_camera.h
@@ -0,0 +1,230 @@
+#ifndef __UAPI_LINUX_MSMB_CAMERA_H
+#define __UAPI_LINUX_MSMB_CAMERA_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define MSM_CAM_LOGSYNC_FILE_NAME "logsync"
+#define MSM_CAM_LOGSYNC_FILE_BASEDIR "camera"
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 30, struct msm_v4l2_event_data)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_META \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 31, struct msm_v4l2_event_data)
+
+#define MSM_CAM_V4L2_IOCTL_CMD_ACK \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 32, struct msm_v4l2_event_data)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 33, struct msm_v4l2_event_data)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 34, struct msm_v4l2_event_data)
+
+#define MSM_CAM_V4L2_IOCTL_DAEMON_DISABLED \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 35, struct msm_v4l2_event_data)
+
+#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_CAMERA_SUBDEV_BUF_MNGR 13
+#define MSM_CAMERA_SUBDEV_SENSOR_INIT 14
+#define MSM_CAMERA_SUBDEV_OIS 15
+#define MSM_CAMERA_SUBDEV_FLASH 16
+#define MSM_CAMERA_SUBDEV_IR_LED 17
+#define MSM_CAMERA_SUBDEV_IR_CUT 18
+#define MSM_CAMERA_SUBDEV_EXT 19
+#define MSM_CAMERA_SUBDEV_TOF 20
+#define MSM_CAMERA_SUBDEV_LASER_LED 21
+#define MSM_MAX_CAMERA_SENSORS 5
+
+/* The below macro is defined to put an upper limit on maximum
+ * number of buffer requested per stream. In case of extremely
+ * large value for number of buffer due to data structure corruption
+ * we return error to avoid integer overflow. Group processing
+ * can have max of 9 groups of 8 bufs each. This value may be
+ * configured in future
+ */
+#define MSM_CAMERA_MAX_STREAM_BUF 72
+
+/* Max batch size of processing */
+#define MSM_CAMERA_MAX_USER_BUFF_CNT 16
+
+/* 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)
+#define MSM_CAMERA_PRIV_G_SESSION_ID (V4L2_CID_PRIVATE_BASE + 14)
+#define MSM_CAMERA_PRIV_CMD_MAX 20
+
+/* data.status - success */
+#define MSM_CAMERA_CMD_SUCCESS 0x00000001
+#define MSM_CAMERA_BUF_MAP_SUCCESS 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)
+#define MSM_CAMERA_ERR_DEVICE_BUSY (MSM_CAMERA_ERR_EVT_BASE + 3)
+
+/* 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 arg_value;
+ /*word 8*/
+ unsigned int ret_value;
+ /*word 9*/
+ unsigned int v4l2_event_type;
+ /*word 10*/
+ unsigned int v4l2_event_id;
+ /*word 11*/
+ unsigned int handle;
+ /*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')
+
+enum smmu_attach_mode {
+ NON_SECURE_MODE = 0x01,
+ SECURE_MODE = 0x02,
+ MAX_PROTECTION_MODE = 0x03,
+};
+
+struct msm_camera_smmu_attach_type {
+ enum smmu_attach_mode attach;
+};
+
+struct msm_camera_user_buf_cont_t {
+ unsigned int buf_cnt;
+ unsigned int buf_idx[MSM_CAMERA_MAX_USER_BUFF_CNT];
+};
+
+struct msm_camera_return_buf {
+ __u32 index;
+ __u32 reserved;
+};
+
+#define MSM_CAMERA_PRIV_IOCTL_ID_BASE 0
+#define MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF 1
+
+struct msm_camera_private_ioctl_arg {
+ __u32 id;
+ __u32 size;
+ __u32 result;
+ __u32 reserved;
+ __u64 ioctl_ptr;
+};
+
+#define VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_private_ioctl_arg)
+
+#endif
+
diff --git a/include/uapi/media/msmb_generic_buf_mgr.h b/include/uapi/media/msmb_generic_buf_mgr.h
new file mode 100644
index 0000000..2961cae
--- /dev/null
+++ b/include/uapi/media/msmb_generic_buf_mgr.h
@@ -0,0 +1,66 @@
+#ifndef __UAPI_MEDIA_MSMB_GENERIC_BUF_MGR_H__
+#define __UAPI_MEDIA_MSMB_GENERIC_BUF_MGR_H__
+
+#include <media/msmb_camera.h>
+
+enum msm_camera_buf_mngr_cmd {
+ MSM_CAMERA_BUF_MNGR_CONT_MAP,
+ MSM_CAMERA_BUF_MNGR_CONT_UNMAP,
+ MSM_CAMERA_BUF_MNGR_CONT_MAX,
+};
+
+enum msm_camera_buf_mngr_buf_type {
+ MSM_CAMERA_BUF_MNGR_BUF_PLANAR,
+ MSM_CAMERA_BUF_MNGR_BUF_USER,
+ MSM_CAMERA_BUF_MNGR_BUF_INVALID,
+};
+
+struct msm_buf_mngr_info {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t frame_id;
+ struct timeval timestamp;
+ uint32_t index;
+ uint32_t reserved;
+ enum msm_camera_buf_mngr_buf_type type;
+ struct msm_camera_user_buf_cont_t user_buf;
+};
+
+struct msm_buf_mngr_main_cont_info {
+ uint32_t session_id;
+ uint32_t stream_id;
+ enum msm_camera_buf_mngr_cmd cmd;
+ uint32_t cnt;
+ int32_t cont_fd;
+};
+
+#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_BASE 0
+#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX 1
+
+#define VIDIOC_MSM_BUF_MNGR_GET_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 33, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_PUT_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 34, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_BUF_DONE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 35, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_CONT_CMD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 36, struct msm_buf_mngr_main_cont_info)
+
+#define VIDIOC_MSM_BUF_MNGR_INIT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 37, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_DEINIT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 38, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_FLUSH \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 39, struct msm_buf_mngr_info)
+
+#define VIDIOC_MSM_BUF_MNGR_IOCTL_CMD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 40, \
+ struct msm_camera_private_ioctl_arg)
+
+#endif
+
diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h
new file mode 100644
index 0000000..2b10748
--- /dev/null
+++ b/include/uapi/media/msmb_isp.h
@@ -0,0 +1,1042 @@
+#ifndef __UAPI_MSMB_ISP__
+#define __UAPI_MSMB_ISP__
+
+#include <linux/videodev2.h>
+#include <media/msmb_camera.h>
+
+#define MAX_PLANES_PER_STREAM 3
+#define MAX_NUM_STREAM 7
+
+#define ISP_VERSION_48 48
+#define ISP_VERSION_47 47
+#define ISP_VERSION_46 46
+#define ISP_VERSION_44 44
+#define ISP_VERSION_40 40
+#define ISP_VERSION_32 32
+#define ISP_NATIVE_BUF_BIT (0x10000 << 0)
+#define ISP0_BIT (0x10000 << 1)
+#define ISP1_BIT (0x10000 << 2)
+#define ISP_META_CHANNEL_BIT (0x10000 << 3)
+#define ISP_SCRATCH_BUF_BIT (0x10000 << 4)
+#define ISP_OFFLINE_STATS_BIT (0x10000 << 5)
+#define ISP_SVHDR_IN_BIT (0x10000 << 6) /* RDI hw stream for SVHDR */
+#define ISP_SVHDR_OUT_BIT (0x10000 << 7) /* SVHDR output bufq stream*/
+
+#define ISP_STATS_STREAM_BIT 0x80000000
+
+#define VFE_HW_LIMIT 1
+
+struct msm_vfe_cfg_cmd_list;
+
+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,
+ PIX_VIDEO,
+ CAMIF_RAW,
+ IDEAL_RAW,
+ RDI_INTF_0,
+ RDI_INTF_1,
+ RDI_INTF_2,
+ VFE_AXI_SRC_MAX
+};
+
+enum msm_vfe_frame_skip_pattern {
+ NO_SKIP,
+ EVERY_2FRAME,
+ EVERY_3FRAME,
+ EVERY_4FRAME,
+ EVERY_5FRAME,
+ EVERY_6FRAME,
+ EVERY_7FRAME,
+ EVERY_8FRAME,
+ EVERY_16FRAME,
+ EVERY_32FRAME,
+ SKIP_ALL,
+ SKIP_RANGE,
+ MAX_SKIP,
+};
+
+/*
+ * Define an unused period. When this period is set it means that the stream is
+ * stopped(i.e the pattern is 0). We don't track the current pattern, just the
+ * period defines what the pattern is, if period is this then pattern is 0 else
+ * pattern is 1
+ */
+#define MSM_VFE_STREAM_STOP_PERIOD 15
+
+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_BF_SCALE, /* Bayer Focus scale */
+ MSM_ISP_STATS_HDR_BE, /* HDR Bayer Exposure */
+ MSM_ISP_STATS_HDR_BHIST, /* HDR Bayer Hist */
+ MSM_ISP_STATS_AEC_BG, /* AEC BG */
+ MSM_ISP_STATS_MAX /* MAX */
+};
+
+/*
+ * @stats_type_mask: Stats type mask (enum msm_isp_stats_type).
+ * @stream_src_mask: Stream src mask (enum msm_vfe_axi_stream_src)
+ * @skip_mode: skip pattern, if skip mode is range only then min/max is used
+ * @min_frame_id: minimum frame id (valid only if skip_mode = RANGE)
+ * @max_frame_id: maximum frame id (valid only if skip_mode = RANGE)
+ */
+struct msm_isp_sw_framskip {
+ uint32_t stats_type_mask;
+ uint32_t stream_src_mask;
+ enum msm_vfe_frame_skip_pattern skip_mode;
+ uint32_t min_frame_id;
+ uint32_t max_frame_id;
+};
+
+enum msm_vfe_testgen_color_pattern {
+ COLOR_BAR_8_COLOR,
+ UNICOLOR_WHITE,
+ UNICOLOR_YELLOW,
+ UNICOLOR_CYAN,
+ UNICOLOR_GREEN,
+ UNICOLOR_MAGENTA,
+ UNICOLOR_RED,
+ UNICOLOR_BLUE,
+ UNICOLOR_BLACK,
+ MAX_COLOR,
+};
+
+enum msm_vfe_camif_input {
+ CAMIF_DISABLED,
+ CAMIF_PAD_REG_INPUT,
+ CAMIF_MIDDI_INPUT,
+ CAMIF_MIPI_INPUT,
+};
+
+struct msm_vfe_fetch_engine_cfg {
+ uint32_t input_format;
+ uint32_t buf_width;
+ uint32_t buf_height;
+ uint32_t fetch_width;
+ uint32_t fetch_height;
+ uint32_t x_offset;
+ uint32_t y_offset;
+ uint32_t buf_stride;
+};
+
+enum msm_vfe_camif_output_format {
+ CAMIF_QCOM_RAW,
+ CAMIF_MIPI_RAW,
+ CAMIF_PLAIN_8,
+ CAMIF_PLAIN_16,
+ CAMIF_MAX_FORMAT,
+};
+
+/*
+ * Camif output general configuration
+ */
+struct msm_vfe_camif_subsample_cfg {
+ uint32_t irq_subsample_period;
+ uint32_t irq_subsample_pattern;
+ uint32_t sof_counter_step;
+ uint32_t pixel_skip;
+ uint32_t line_skip;
+ uint32_t first_line;
+ uint32_t last_line;
+ uint32_t first_pixel;
+ uint32_t last_pixel;
+ enum msm_vfe_camif_output_format output_format;
+};
+
+/*
+ * Camif frame and window configuration
+ */
+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;
+ uint32_t is_split;
+ enum msm_vfe_camif_input camif_input;
+ struct msm_vfe_camif_subsample_cfg subsample_cfg;
+};
+
+struct msm_vfe_testgen_cfg {
+ uint32_t lines_per_frame;
+ uint32_t pixels_per_line;
+ uint32_t v_blank;
+ uint32_t h_blank;
+ enum ISP_START_PIXEL_PATTERN pixel_bayer_pattern;
+ uint32_t rotate_period;
+ enum msm_vfe_testgen_color_pattern color_bar_pattern;
+ uint32_t burst_num_frame;
+};
+
+enum msm_vfe_inputmux {
+ CAMIF,
+ TESTGEN,
+ EXTERNAL_READ,
+};
+
+enum msm_vfe_stats_composite_group {
+ STATS_COMPOSITE_GRP_NONE,
+ STATS_COMPOSITE_GRP_1,
+ STATS_COMPOSITE_GRP_2,
+ STATS_COMPOSITE_GRP_MAX,
+};
+
+enum msm_vfe_hvx_streaming_cmd {
+ HVX_DISABLE,
+ HVX_ONE_WAY,
+ HVX_ROUND_TRIP
+};
+
+struct msm_vfe_pix_cfg {
+ struct msm_vfe_camif_cfg camif_cfg;
+ struct msm_vfe_testgen_cfg testgen_cfg;
+ struct msm_vfe_fetch_engine_cfg fetch_engine_cfg;
+ enum msm_vfe_inputmux input_mux;
+ enum ISP_START_PIXEL_PATTERN pixel_pattern;
+ uint32_t input_format;
+ enum msm_vfe_hvx_streaming_cmd hvx_cmd;
+ uint32_t is_split;
+};
+
+struct msm_vfe_rdi_cfg {
+ uint8_t cid;
+ uint8_t frame_based;
+};
+
+struct msm_vfe_input_cfg {
+ union {
+ struct msm_vfe_pix_cfg pix_cfg;
+ struct msm_vfe_rdi_cfg rdi_cfg;
+ } d;
+ enum msm_vfe_input_src input_src;
+ uint32_t input_pix_clk;
+};
+
+struct msm_vfe_fetch_eng_start {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t buf_idx;
+ uint8_t offline_mode;
+ uint32_t fd;
+ uint32_t buf_addr;
+ uint32_t frame_id;
+};
+
+enum msm_vfe_fetch_eng_pass {
+ OFFLINE_FIRST_PASS,
+ OFFLINE_SECOND_PASS,
+ OFFLINE_MAX_PASS,
+};
+
+struct msm_vfe_fetch_eng_multi_pass_start {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t buf_idx;
+ uint8_t offline_mode;
+ uint32_t fd;
+ uint32_t buf_addr;
+ uint32_t frame_id;
+ uint32_t output_buf_idx;
+ uint32_t input_buf_offset;
+ enum msm_vfe_fetch_eng_pass offline_pass;
+ uint32_t output_stream_id;
+};
+
+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*/
+ uint32_t plane_addr_offset;
+ uint8_t csid_src; /*RDI 0-2*/
+ uint8_t rdi_cid;/*CID 1-16*/
+};
+
+enum msm_stream_rdi_input_type {
+ MSM_CAMERA_RDI_MIN,
+ MSM_CAMERA_RDI_PDAF,
+ MSM_CAMERA_RDI_MAX,
+};
+
+struct msm_vfe_axi_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t vt_enable;
+ 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;
+ uint32_t controllable_output;
+ uint32_t burst_len;
+ /* Flag indicating memory input stream */
+ enum msm_stream_rdi_input_type rdi_input_type;
+};
+
+struct msm_vfe_axi_stream_release_cmd {
+ uint32_t stream_handle;
+};
+
+enum msm_vfe_axi_stream_cmd {
+ STOP_STREAM,
+ START_STREAM,
+ STOP_IMMEDIATELY,
+};
+
+struct msm_vfe_axi_stream_cfg_cmd {
+ uint8_t num_streams;
+ uint32_t stream_handle[VFE_AXI_SRC_MAX];
+ enum msm_vfe_axi_stream_cmd cmd;
+ uint8_t sync_frame_id_src;
+};
+
+enum msm_vfe_axi_stream_update_type {
+ ENABLE_STREAM_BUF_DIVERT,
+ DISABLE_STREAM_BUF_DIVERT,
+ UPDATE_STREAM_FRAMEDROP_PATTERN,
+ UPDATE_STREAM_STATS_FRAMEDROP_PATTERN,
+ UPDATE_STREAM_AXI_CONFIG,
+ UPDATE_STREAM_REQUEST_FRAMES,
+ UPDATE_STREAM_ADD_BUFQ,
+ UPDATE_STREAM_REMOVE_BUFQ,
+ UPDATE_STREAM_SW_FRAME_DROP,
+ UPDATE_STREAM_REQUEST_FRAMES_VER2,
+ UPDATE_STREAM_OFFLINE_AXI_CONFIG,
+};
+#define UPDATE_STREAM_REQUEST_FRAMES_VER2 UPDATE_STREAM_REQUEST_FRAMES_VER2
+
+enum msm_vfe_iommu_type {
+ IOMMU_ATTACH,
+ IOMMU_DETACH,
+};
+
+enum msm_vfe_buff_queue_id {
+ VFE_BUF_QUEUE_DEFAULT,
+ VFE_BUF_QUEUE_SHARED,
+ VFE_BUF_QUEUE_MAX,
+};
+
+struct msm_vfe_axi_stream_cfg_update_info {
+ uint32_t stream_handle;
+ uint32_t output_format;
+ uint32_t user_stream_id;
+ uint32_t frame_id;
+ enum msm_vfe_frame_skip_pattern skip_pattern;
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
+ struct msm_isp_sw_framskip sw_skip_info;
+};
+
+struct msm_vfe_axi_stream_cfg_update_info_req_frm {
+ uint32_t stream_handle;
+ uint32_t user_stream_id;
+ uint32_t frame_id;
+ uint32_t buf_index;
+};
+
+struct msm_vfe_axi_halt_cmd {
+ uint32_t stop_camif;
+ uint32_t overflow_detected;
+ uint32_t blocking_halt;
+};
+
+struct msm_vfe_axi_reset_cmd {
+ uint32_t blocking;
+ uint32_t frame_id;
+};
+
+struct msm_vfe_axi_restart_cmd {
+ uint32_t enable_camif;
+};
+
+struct msm_vfe_axi_stream_update_cmd {
+ uint32_t num_streams;
+ enum msm_vfe_axi_stream_update_type update_type;
+ /*
+ * For backward compatibility, ensure 1st member of any struct
+ * in union below is uint32_t stream_handle.
+ */
+ union {
+ struct msm_vfe_axi_stream_cfg_update_info
+ update_info[MSM_ISP_STATS_MAX];
+ struct msm_vfe_axi_stream_cfg_update_info_req_frm req_frm_ver2;
+ };
+};
+
+struct msm_vfe_smmu_attach_cmd {
+ uint32_t security_mode;
+ uint32_t iommu_attach_mode;
+};
+
+struct msm_vfe_stats_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ enum msm_isp_stats_type stats_type;
+ uint32_t composite_flag;
+ uint32_t framedrop_pattern;
+ uint32_t init_frame_drop; /*MAX 31 Frames*/
+ uint32_t irq_subsample_pattern;
+ uint32_t buffer_offset;
+ 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;
+ uint32_t stats_burst_len;
+};
+
+enum msm_vfe_reg_cfg_type {
+ VFE_WRITE,
+ VFE_WRITE_MB,
+ VFE_READ,
+ VFE_CFG_MASK,
+ VFE_WRITE_DMI_16BIT,
+ VFE_WRITE_DMI_32BIT,
+ VFE_WRITE_DMI_64BIT,
+ VFE_READ_DMI_16BIT,
+ VFE_READ_DMI_32BIT,
+ VFE_READ_DMI_64BIT,
+ GET_MAX_CLK_RATE,
+ GET_CLK_RATES,
+ GET_ISP_ID,
+ VFE_HW_UPDATE_LOCK,
+ VFE_HW_UPDATE_UNLOCK,
+ SET_WM_UB_SIZE,
+ SET_UB_POLICY,
+ GET_VFE_HW_LIMIT,
+};
+
+struct msm_vfe_cfg_cmd2 {
+ uint16_t num_cfg;
+ uint16_t cmd_len;
+ void __user *cfg_data;
+ void __user *cfg_cmd;
+};
+
+struct msm_vfe_cfg_cmd_list {
+ struct msm_vfe_cfg_cmd2 cfg_cmd;
+ struct msm_vfe_cfg_cmd_list *next;
+ uint32_t next_size;
+};
+
+struct msm_vfe_reg_rw_info {
+ uint32_t reg_offset;
+ uint32_t cmd_data_offset;
+ uint32_t len;
+};
+
+struct msm_vfe_reg_mask_info {
+ uint32_t reg_offset;
+ uint32_t mask;
+ uint32_t val;
+};
+
+struct msm_vfe_reg_dmi_info {
+ uint32_t hi_tbl_offset; /*Optional*/
+ uint32_t lo_tbl_offset; /*Required*/
+ uint32_t len;
+};
+
+struct msm_vfe_reg_cfg_cmd {
+ union {
+ struct msm_vfe_reg_rw_info rw_info;
+ struct msm_vfe_reg_mask_info mask_info;
+ struct msm_vfe_reg_dmi_info dmi_info;
+ } u;
+
+ enum msm_vfe_reg_cfg_type cmd_type;
+};
+
+enum vfe_sd_type {
+ VFE_SD_0 = 0,
+ VFE_SD_1,
+ VFE_SD_COMMON,
+ VFE_SD_MAX,
+};
+
+/* When you change the value below, check for the sof event_data size.
+ * V4l2 limits payload to 64 bytes
+ */
+#define MS_NUM_SLAVE_MAX 1
+
+/* Usecases when 2 HW need to be related or synced */
+enum msm_vfe_dual_hw_type {
+ DUAL_NONE = 0,
+ DUAL_HW_VFE_SPLIT = 1,
+ DUAL_HW_MASTER_SLAVE = 2,
+};
+
+/* Type for 2 INTF when used in Master-Slave mode */
+enum msm_vfe_dual_hw_ms_type {
+ MS_TYPE_NONE,
+ MS_TYPE_MASTER,
+ MS_TYPE_SLAVE,
+};
+
+struct msm_isp_set_dual_hw_ms_cmd {
+ uint8_t num_src;
+ /* Each session can be only one type but multiple intf if YUV cam */
+ enum msm_vfe_dual_hw_ms_type dual_hw_ms_type;
+ /* Primary intf is mostly associated with preview.
+ * This primary intf SOF frame_id and timestamp is tracked
+ * and used to calculate delta
+ */
+ enum msm_vfe_input_src primary_intf;
+ /* input_src array indicates other input INTF that may be Master/Slave.
+ * For these additional intf, frame_id and timestamp are not saved.
+ * However, if these are slaves then they will still get their
+ * frame_id from Master
+ */
+ enum msm_vfe_input_src input_src[VFE_SRC_MAX];
+ uint32_t sof_delta_threshold; /* In milliseconds. Sent for Master */
+};
+
+enum msm_isp_buf_type {
+ ISP_PRIVATE_BUF,
+ ISP_SHARE_BUF,
+ MAX_ISP_BUF_TYPE,
+};
+
+struct msm_isp_unmap_buf_req {
+ uint32_t fd;
+};
+
+struct msm_isp_buf_request {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint8_t num_buf;
+ uint32_t handle;
+ enum msm_isp_buf_type buf_type;
+};
+
+struct msm_isp_buf_request_ver2 {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint8_t num_buf;
+ uint32_t handle;
+ enum msm_isp_buf_type buf_type;
+ enum smmu_attach_mode security_mode;
+ uint32_t reserved[4];
+};
+
+struct msm_isp_qbuf_plane {
+ uint32_t addr;
+ uint32_t offset;
+ uint32_t length;
+};
+
+struct msm_isp_qbuf_buffer {
+ struct msm_isp_qbuf_plane planes[MAX_PLANES_PER_STREAM];
+ uint32_t num_planes;
+};
+
+struct msm_isp_qbuf_info {
+ uint32_t handle;
+ int32_t buf_idx;
+ /*Only used for prepare buffer*/
+ struct msm_isp_qbuf_buffer buffer;
+ /*Only used for diverted buffer*/
+ uint32_t dirty_buf;
+};
+
+struct msm_isp_clk_rates {
+ uint32_t svs_rate;
+ uint32_t nominal_rate;
+ uint32_t high_rate;
+};
+
+struct msm_vfe_axi_src_state {
+ enum msm_vfe_input_src input_src;
+ uint32_t src_active;
+ uint32_t src_frame_id;
+};
+
+enum msm_isp_event_mask_index {
+ ISP_EVENT_MASK_INDEX_STATS_NOTIFY = 0,
+ ISP_EVENT_MASK_INDEX_ERROR = 1,
+ ISP_EVENT_MASK_INDEX_IOMMU_P_FAULT = 2,
+ ISP_EVENT_MASK_INDEX_STREAM_UPDATE_DONE = 3,
+ ISP_EVENT_MASK_INDEX_REG_UPDATE = 4,
+ ISP_EVENT_MASK_INDEX_SOF = 5,
+ ISP_EVENT_MASK_INDEX_BUF_DIVERT = 6,
+ ISP_EVENT_MASK_INDEX_COMP_STATS_NOTIFY = 7,
+ ISP_EVENT_MASK_INDEX_MASK_FE_READ_DONE = 8,
+ ISP_EVENT_MASK_INDEX_BUF_DONE = 9,
+ ISP_EVENT_MASK_INDEX_REG_UPDATE_MISSING = 10,
+ ISP_EVENT_MASK_INDEX_PING_PONG_MISMATCH = 11,
+ ISP_EVENT_MASK_INDEX_BUF_FATAL_ERROR = 12,
+};
+
+
+#define ISP_EVENT_SUBS_MASK_NONE 0
+
+#define ISP_EVENT_SUBS_MASK_STATS_NOTIFY \
+ (1 << ISP_EVENT_MASK_INDEX_STATS_NOTIFY)
+
+#define ISP_EVENT_SUBS_MASK_ERROR \
+ (1 << ISP_EVENT_MASK_INDEX_ERROR)
+
+#define ISP_EVENT_SUBS_MASK_IOMMU_P_FAULT \
+ (1 << ISP_EVENT_MASK_INDEX_IOMMU_P_FAULT)
+
+#define ISP_EVENT_SUBS_MASK_STREAM_UPDATE_DONE \
+ (1 << ISP_EVENT_MASK_INDEX_STREAM_UPDATE_DONE)
+
+#define ISP_EVENT_SUBS_MASK_REG_UPDATE \
+ (1 << ISP_EVENT_MASK_INDEX_REG_UPDATE)
+
+#define ISP_EVENT_SUBS_MASK_SOF \
+ (1 << ISP_EVENT_MASK_INDEX_SOF)
+
+#define ISP_EVENT_SUBS_MASK_BUF_DIVERT \
+ (1 << ISP_EVENT_MASK_INDEX_BUF_DIVERT)
+
+#define ISP_EVENT_SUBS_MASK_COMP_STATS_NOTIFY \
+ (1 << ISP_EVENT_MASK_INDEX_COMP_STATS_NOTIFY)
+
+#define ISP_EVENT_SUBS_MASK_FE_READ_DONE \
+ (1 << ISP_EVENT_MASK_INDEX_MASK_FE_READ_DONE)
+
+#define ISP_EVENT_SUBS_MASK_BUF_DONE \
+ (1 << ISP_EVENT_MASK_INDEX_BUF_DONE)
+
+#define ISP_EVENT_SUBS_MASK_REG_UPDATE_MISSING \
+ (1 << ISP_EVENT_MASK_INDEX_REG_UPDATE_MISSING)
+
+#define ISP_EVENT_SUBS_MASK_PING_PONG_MISMATCH \
+ (1 << ISP_EVENT_MASK_INDEX_PING_PONG_MISMATCH)
+
+#define ISP_EVENT_SUBS_MASK_BUF_FATAL_ERROR \
+ (1 << ISP_EVENT_MASK_INDEX_BUF_FATAL_ERROR)
+
+enum msm_isp_event_idx {
+ ISP_REG_UPDATE = 0,
+ ISP_EPOCH_0 = 1,
+ ISP_EPOCH_1 = 2,
+ ISP_START_ACK = 3,
+ ISP_STOP_ACK = 4,
+ ISP_IRQ_VIOLATION = 5,
+ ISP_STATS_OVERFLOW = 6,
+ ISP_BUF_DONE = 7,
+ ISP_FE_RD_DONE = 8,
+ ISP_IOMMU_P_FAULT = 9,
+ ISP_ERROR = 10,
+ ISP_HW_FATAL_ERROR = 11,
+ ISP_PING_PONG_MISMATCH = 12,
+ ISP_REG_UPDATE_MISSING = 13,
+ ISP_BUF_FATAL_ERROR = 14,
+ ISP_EVENT_MAX = 15
+};
+
+#define ISP_EVENT_OFFSET 8
+#define ISP_EVENT_BASE (V4L2_EVENT_PRIVATE_START)
+#define ISP_BUF_EVENT_BASE (ISP_EVENT_BASE + (1 << ISP_EVENT_OFFSET))
+#define ISP_STATS_EVENT_BASE (ISP_EVENT_BASE + (2 << ISP_EVENT_OFFSET))
+#define ISP_CAMIF_EVENT_BASE (ISP_EVENT_BASE + (3 << ISP_EVENT_OFFSET))
+#define ISP_STREAM_EVENT_BASE (ISP_EVENT_BASE + (4 << ISP_EVENT_OFFSET))
+#define ISP_EVENT_REG_UPDATE (ISP_EVENT_BASE + ISP_REG_UPDATE)
+#define ISP_EVENT_EPOCH_0 (ISP_EVENT_BASE + ISP_EPOCH_0)
+#define ISP_EVENT_EPOCH_1 (ISP_EVENT_BASE + ISP_EPOCH_1)
+#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_STATS_OVERFLOW (ISP_EVENT_BASE + ISP_STATS_OVERFLOW)
+#define ISP_EVENT_ERROR (ISP_EVENT_BASE + ISP_ERROR)
+#define ISP_EVENT_SOF (ISP_CAMIF_EVENT_BASE)
+#define ISP_EVENT_EOF (ISP_CAMIF_EVENT_BASE + 1)
+#define ISP_EVENT_BUF_DONE (ISP_EVENT_BASE + ISP_BUF_DONE)
+#define ISP_EVENT_BUF_DIVERT (ISP_BUF_EVENT_BASE)
+#define ISP_EVENT_STATS_NOTIFY (ISP_STATS_EVENT_BASE)
+#define ISP_EVENT_COMP_STATS_NOTIFY (ISP_EVENT_STATS_NOTIFY + MSM_ISP_STATS_MAX)
+#define ISP_EVENT_FE_READ_DONE (ISP_EVENT_BASE + ISP_FE_RD_DONE)
+#define ISP_EVENT_IOMMU_P_FAULT (ISP_EVENT_BASE + ISP_IOMMU_P_FAULT)
+#define ISP_EVENT_HW_FATAL_ERROR (ISP_EVENT_BASE + ISP_HW_FATAL_ERROR)
+#define ISP_EVENT_PING_PONG_MISMATCH (ISP_EVENT_BASE + ISP_PING_PONG_MISMATCH)
+#define ISP_EVENT_REG_UPDATE_MISSING (ISP_EVENT_BASE + ISP_REG_UPDATE_MISSING)
+#define ISP_EVENT_BUF_FATAL_ERROR (ISP_EVENT_BASE + ISP_BUF_FATAL_ERROR)
+#define ISP_EVENT_STREAM_UPDATE_DONE (ISP_STREAM_EVENT_BASE)
+
+/* 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;
+ uint32_t output_format;
+ int8_t buf_idx;
+};
+struct msm_isp_fetch_eng_event {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+ uint32_t fd;
+ int8_t buf_idx;
+ int8_t offline_mode;
+};
+struct msm_isp_stats_event {
+ uint32_t stats_mask; /* 4 bytes */
+ uint8_t stats_buf_idxs[MSM_ISP_STATS_MAX]; /* 11 bytes */
+ uint8_t pd_stats_idx;
+};
+
+struct msm_isp_stream_ack {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+};
+
+enum msm_vfe_error_type {
+ ISP_ERROR_NONE,
+ ISP_ERROR_CAMIF,
+ ISP_ERROR_BUS_OVERFLOW,
+ ISP_ERROR_RETURN_EMPTY_BUFFER,
+ ISP_ERROR_FRAME_ID_MISMATCH,
+ ISP_ERROR_MAX,
+};
+
+struct msm_isp_error_info {
+ enum msm_vfe_error_type err_type;
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t stream_id_mask;
+};
+
+/* This structure reports delta between master and slave */
+struct msm_isp_ms_delta_info {
+ uint8_t num_delta_info;
+ uint32_t delta[MS_NUM_SLAVE_MAX];
+};
+
+/* This is sent in EPOCH irq */
+struct msm_isp_output_info {
+ uint8_t regs_not_updated;
+ /* mask with bufq_handle for regs not updated or return empty */
+ uint16_t output_err_mask;
+ /* mask with stream_idx for get_buf failed */
+ uint8_t stream_framedrop_mask;
+ /* mask with stats stream_idx for get_buf failed */
+ uint16_t stats_framedrop_mask;
+ /* delta between master and slave */
+};
+
+/* This structure is piggybacked with SOF event */
+struct msm_isp_sof_info {
+ uint8_t regs_not_updated;
+ /* mask with bufq_handle for regs not updated */
+ uint16_t reg_update_fail_mask;
+ /* mask with bufq_handle for get_buf failed */
+ uint32_t stream_get_buf_fail_mask;
+ /* mask with stats stream_idx for get_buf failed */
+ uint16_t stats_get_buf_fail_mask;
+ /* delta between master and slave */
+ struct msm_isp_ms_delta_info ms_delta_info;
+ /*
+ * mask with AXI_SRC in paused state. In PAUSED
+ * state there is no Buffer output. So this mask is used
+ * to report drop.
+ */
+ uint16_t axi_updating_mask;
+ /* extended mask with bufq_handle for regs not updated */
+ uint32_t reg_update_fail_mask_ext;
+};
+#define AXI_UPDATING_MASK 1
+#define REG_UPDATE_FAIL_MASK_EXT 1
+
+struct msm_isp_event_data {
+ /*Wall clock except for buffer divert events
+ *which use monotonic clock
+ */
+ struct timeval timestamp;
+ /* Monotonic timestamp since bootup */
+ struct timeval mono_timestamp;
+ uint32_t frame_id;
+ union {
+ /* Sent for Stats_Done event */
+ struct msm_isp_stats_event stats;
+ /* Sent for Buf_Divert event */
+ struct msm_isp_buf_event buf_done;
+ /* Sent for offline fetch done event */
+ struct msm_isp_fetch_eng_event fetch_done;
+ /* Sent for Error_Event */
+ struct msm_isp_error_info error_info;
+ /*
+ * This struct needs to be removed once
+ * userspace switches to sof_info
+ */
+ struct msm_isp_output_info output_info;
+ /* Sent for SOF event */
+ struct msm_isp_sof_info sof_info;
+ } u; /* union can have max 52 bytes */
+};
+
+enum msm_vfe_ahb_clk_vote {
+ MSM_ISP_CAMERA_AHB_SVS_VOTE = 1,
+ MSM_ISP_CAMERA_AHB_TURBO_VOTE = 2,
+ MSM_ISP_CAMERA_AHB_NOMINAL_VOTE = 3,
+ MSM_ISP_CAMERA_AHB_SUSPEND_VOTE = 4,
+};
+
+struct msm_isp_ahb_clk_cfg {
+ uint32_t vote;
+ uint32_t reserved[2];
+};
+
+enum msm_vfe_dual_cam_sync_mode {
+ MSM_ISP_DUAL_CAM_ASYNC,
+ MSM_ISP_DUAL_CAM_SYNC,
+};
+
+struct msm_isp_dual_hw_master_slave_sync {
+ uint32_t sync_mode;
+ uint32_t reserved[2];
+};
+
+struct msm_vfe_dual_lpm_mode {
+ enum msm_vfe_axi_stream_src stream_src[VFE_AXI_SRC_MAX];
+ uint32_t num_src;
+ uint32_t lpm_mode;
+};
+#define V4L2_PIX_FMT_QBGGR8 v4l2_fourcc('Q', 'B', 'G', '8')
+#define V4L2_PIX_FMT_QGBRG8 v4l2_fourcc('Q', 'G', 'B', '8')
+#define V4L2_PIX_FMT_QGRBG8 v4l2_fourcc('Q', 'G', 'R', '8')
+#define V4L2_PIX_FMT_QRGGB8 v4l2_fourcc('Q', 'R', 'G', '8')
+#define V4L2_PIX_FMT_QBGGR10 v4l2_fourcc('Q', 'B', 'G', '0')
+#define V4L2_PIX_FMT_QGBRG10 v4l2_fourcc('Q', 'G', 'B', '0')
+#define V4L2_PIX_FMT_QGRBG10 v4l2_fourcc('Q', 'G', 'R', '0')
+#define V4L2_PIX_FMT_QRGGB10 v4l2_fourcc('Q', 'R', 'G', '0')
+#define V4L2_PIX_FMT_QBGGR12 v4l2_fourcc('Q', 'B', 'G', '2')
+#define V4L2_PIX_FMT_QGBRG12 v4l2_fourcc('Q', 'G', 'B', '2')
+#define V4L2_PIX_FMT_QGRBG12 v4l2_fourcc('Q', 'G', 'R', '2')
+#define V4L2_PIX_FMT_QRGGB12 v4l2_fourcc('Q', 'R', 'G', '2')
+#define V4L2_PIX_FMT_QBGGR14 v4l2_fourcc('Q', 'B', 'G', '4')
+#define V4L2_PIX_FMT_QGBRG14 v4l2_fourcc('Q', 'G', 'B', '4')
+#define V4L2_PIX_FMT_QGRBG14 v4l2_fourcc('Q', 'G', 'R', '4')
+#define V4L2_PIX_FMT_QRGGB14 v4l2_fourcc('Q', 'R', 'G', '4')
+#define V4L2_PIX_FMT_P16BGGR10 v4l2_fourcc('P', 'B', 'G', '0')
+#define V4L2_PIX_FMT_P16GBRG10 v4l2_fourcc('P', 'G', 'B', '0')
+#define V4L2_PIX_FMT_P16GRBG10 v4l2_fourcc('P', 'G', 'R', '0')
+#define V4L2_PIX_FMT_P16RGGB10 v4l2_fourcc('P', 'R', 'G', '0')
+#define V4L2_PIX_FMT_NV14 v4l2_fourcc('N', 'V', '1', '4')
+#define V4L2_PIX_FMT_NV41 v4l2_fourcc('N', 'V', '4', '1')
+#define V4L2_PIX_FMT_META v4l2_fourcc('Q', 'M', 'E', 'T')
+#define V4L2_PIX_FMT_META10 v4l2_fourcc('Q', 'M', '1', '0')
+#define V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4') /* 14 BGBG.GRGR.*/
+#define V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4') /* 14 GBGB.RGRG.*/
+#define V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('B', 'A', '1', '4') /* 14 GRGR.BGBG.*/
+#define V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4') /* 14 RGRG.GBGB.*/
+
+enum msm_isp_ioctl_cmd_code {
+ MSM_VFE_REG_CFG = BASE_VIDIOC_PRIVATE,
+ MSM_ISP_REQUEST_BUF,
+ MSM_ISP_ENQUEUE_BUF,
+ MSM_ISP_RELEASE_BUF,
+ MSM_ISP_REQUEST_STREAM,
+ MSM_ISP_CFG_STREAM,
+ MSM_ISP_RELEASE_STREAM,
+ MSM_ISP_INPUT_CFG,
+ MSM_ISP_SET_SRC_STATE,
+ MSM_ISP_REQUEST_STATS_STREAM,
+ MSM_ISP_CFG_STATS_STREAM,
+ MSM_ISP_RELEASE_STATS_STREAM,
+ MSM_ISP_REG_UPDATE_CMD,
+ MSM_ISP_UPDATE_STREAM,
+ MSM_VFE_REG_LIST_CFG,
+ MSM_ISP_SMMU_ATTACH,
+ MSM_ISP_UPDATE_STATS_STREAM,
+ MSM_ISP_AXI_HALT,
+ MSM_ISP_AXI_RESET,
+ MSM_ISP_AXI_RESTART,
+ MSM_ISP_FETCH_ENG_START,
+ MSM_ISP_DEQUEUE_BUF,
+ MSM_ISP_SET_DUAL_HW_MASTER_SLAVE,
+ MSM_ISP_MAP_BUF_START_FE,
+ MSM_ISP_UNMAP_BUF,
+ MSM_ISP_AHB_CLK_CFG,
+ MSM_ISP_DUAL_HW_MASTER_SLAVE_SYNC,
+ MSM_ISP_FETCH_ENG_MULTI_PASS_START,
+ MSM_ISP_MAP_BUF_START_MULTI_PASS_FE,
+ MSM_ISP_REQUEST_BUF_VER2,
+ MSM_ISP_DUAL_HW_LPM_MODE,
+};
+
+#define VIDIOC_MSM_VFE_REG_CFG \
+ _IOWR('V', MSM_VFE_REG_CFG, \
+ struct msm_vfe_cfg_cmd2)
+
+#define VIDIOC_MSM_ISP_REQUEST_BUF \
+ _IOWR('V', MSM_ISP_REQUEST_BUF, \
+ struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_ENQUEUE_BUF \
+ _IOWR('V', MSM_ISP_ENQUEUE_BUF, \
+ struct msm_isp_qbuf_info)
+
+#define VIDIOC_MSM_ISP_RELEASE_BUF \
+ _IOWR('V', MSM_ISP_RELEASE_BUF, \
+ struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_REQUEST_STREAM \
+ _IOWR('V', MSM_ISP_REQUEST_STREAM, \
+ struct msm_vfe_axi_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STREAM \
+ _IOWR('V', MSM_ISP_CFG_STREAM, \
+ struct msm_vfe_axi_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STREAM \
+ _IOWR('V', MSM_ISP_RELEASE_STREAM, \
+ struct msm_vfe_axi_stream_release_cmd)
+
+#define VIDIOC_MSM_ISP_INPUT_CFG \
+ _IOWR('V', MSM_ISP_INPUT_CFG, \
+ struct msm_vfe_input_cfg)
+
+#define VIDIOC_MSM_ISP_SET_SRC_STATE \
+ _IOWR('V', MSM_ISP_SET_SRC_STATE, \
+ struct msm_vfe_axi_src_state)
+
+#define VIDIOC_MSM_ISP_REQUEST_STATS_STREAM \
+ _IOWR('V', MSM_ISP_REQUEST_STATS_STREAM, \
+ struct msm_vfe_stats_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STATS_STREAM \
+ _IOWR('V', MSM_ISP_CFG_STATS_STREAM, \
+ struct msm_vfe_stats_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STATS_STREAM \
+ _IOWR('V', MSM_ISP_RELEASE_STATS_STREAM, \
+ struct msm_vfe_stats_stream_release_cmd)
+
+#define VIDIOC_MSM_ISP_REG_UPDATE_CMD \
+ _IOWR('V', MSM_ISP_REG_UPDATE_CMD, \
+ enum msm_vfe_input_src)
+
+#define VIDIOC_MSM_ISP_UPDATE_STREAM \
+ _IOWR('V', MSM_ISP_UPDATE_STREAM, \
+ struct msm_vfe_axi_stream_update_cmd)
+
+#define VIDIOC_MSM_VFE_REG_LIST_CFG \
+ _IOWR('V', MSM_VFE_REG_LIST_CFG, \
+ struct msm_vfe_cfg_cmd_list)
+
+#define VIDIOC_MSM_ISP_SMMU_ATTACH \
+ _IOWR('V', MSM_ISP_SMMU_ATTACH, \
+ struct msm_vfe_smmu_attach_cmd)
+
+#define VIDIOC_MSM_ISP_UPDATE_STATS_STREAM \
+ _IOWR('V', MSM_ISP_UPDATE_STATS_STREAM, \
+ struct msm_vfe_axi_stream_update_cmd)
+
+#define VIDIOC_MSM_ISP_AXI_HALT \
+ _IOWR('V', MSM_ISP_AXI_HALT, \
+ struct msm_vfe_axi_halt_cmd)
+
+#define VIDIOC_MSM_ISP_AXI_RESET \
+ _IOWR('V', MSM_ISP_AXI_RESET, \
+ struct msm_vfe_axi_reset_cmd)
+
+#define VIDIOC_MSM_ISP_AXI_RESTART \
+ _IOWR('V', MSM_ISP_AXI_RESTART, \
+ struct msm_vfe_axi_restart_cmd)
+
+#define VIDIOC_MSM_ISP_FETCH_ENG_START \
+ _IOWR('V', MSM_ISP_FETCH_ENG_START, \
+ struct msm_vfe_fetch_eng_start)
+
+#define VIDIOC_MSM_ISP_DEQUEUE_BUF \
+ _IOWR('V', MSM_ISP_DEQUEUE_BUF, \
+ struct msm_isp_qbuf_info)
+
+#define VIDIOC_MSM_ISP_SET_DUAL_HW_MASTER_SLAVE \
+ _IOWR('V', MSM_ISP_SET_DUAL_HW_MASTER_SLAVE, \
+ struct msm_isp_set_dual_hw_ms_cmd)
+
+#define VIDIOC_MSM_ISP_MAP_BUF_START_FE \
+ _IOWR('V', MSM_ISP_MAP_BUF_START_FE, \
+ struct msm_vfe_fetch_eng_start)
+
+#define VIDIOC_MSM_ISP_UNMAP_BUF \
+ _IOWR('V', MSM_ISP_UNMAP_BUF, \
+ struct msm_isp_unmap_buf_req)
+
+#define VIDIOC_MSM_ISP_AHB_CLK_CFG \
+ _IOWR('V', MSM_ISP_AHB_CLK_CFG, struct msm_isp_ahb_clk_cfg)
+
+#define VIDIOC_MSM_ISP_DUAL_HW_MASTER_SLAVE_SYNC \
+ _IOWR('V', MSM_ISP_DUAL_HW_MASTER_SLAVE_SYNC, \
+ struct msm_isp_dual_hw_master_slave_sync)
+
+#define VIDIOC_MSM_ISP_FETCH_ENG_MULTI_PASS_START \
+ _IOWR('V', MSM_ISP_FETCH_ENG_MULTI_PASS_START, \
+ struct msm_vfe_fetch_eng_multi_pass_start)
+
+#define VIDIOC_MSM_ISP_MAP_BUF_START_MULTI_PASS_FE \
+ _IOWR('V', MSM_ISP_MAP_BUF_START_MULTI_PASS_FE, \
+ struct msm_vfe_fetch_eng_multi_pass_start)
+
+#define VIDIOC_MSM_ISP_REQUEST_BUF_VER2 \
+ _IOWR('V', MSM_ISP_REQUEST_BUF_VER2, struct msm_isp_buf_request_ver2)
+
+#define VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE \
+ _IOWR('V', MSM_ISP_DUAL_HW_LPM_MODE, \
+ struct msm_vfe_dual_lpm_mode)
+
+#endif /* __MSMB_ISP__ */
diff --git a/include/uapi/media/msmb_ispif.h b/include/uapi/media/msmb_ispif.h
new file mode 100644
index 0000000..d532037
--- /dev/null
+++ b/include/uapi/media/msmb_ispif.h
@@ -0,0 +1,179 @@
+#ifndef UAPI_MSMB_ISPIF_H
+#define UAPI_MSMB_ISPIF_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/videodev2.h>
+
+#define CSID_VERSION_V20 0x02000011
+#define CSID_VERSION_V22 0x02001000
+#define CSID_VERSION_V30 0x30000000
+#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 MAX_PARAM_ENTRIES (INTF_MAX * 2)
+#define MAX_CID_CH 8
+#define MAX_CID_CH_PARAM_ENTRY 3
+
+#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
+};
+
+enum msm_ispif_pixel_odd_even {
+ PIX_EVEN,
+ PIX_ODD
+};
+
+enum msm_ispif_pixel_pack_mode {
+ PACK_BYTE,
+ PACK_PLAIN_PACK,
+ PACK_NV_P8,
+ PACK_NV_P16
+};
+
+struct msm_ispif_pack_cfg {
+ int pixel_swap_en;
+ enum msm_ispif_pixel_odd_even even_odd_sel;
+ enum msm_ispif_pixel_pack_mode pack_mode;
+};
+
+struct msm_ispif_params_entry {
+ enum msm_ispif_vfe_intf vfe_intf;
+ enum msm_ispif_intftype intftype;
+ int num_cids;
+ enum msm_ispif_cid cids[MAX_CID_CH_PARAM_ENTRY];
+ enum msm_ispif_csid csid;
+ int crop_enable;
+ uint16_t crop_start_pixel;
+ uint16_t crop_end_pixel;
+};
+
+struct msm_ispif_right_param_entry {
+ enum msm_ispif_cid cids[MAX_CID_CH_PARAM_ENTRY];
+ enum msm_ispif_csid csid;
+};
+
+struct msm_ispif_param_data_ext {
+ uint32_t num;
+ struct msm_ispif_params_entry entries[MAX_PARAM_ENTRIES];
+ struct msm_ispif_pack_cfg pack_cfg[CID_MAX];
+ struct msm_ispif_right_param_entry right_entries[MAX_PARAM_ENTRIES];
+ uint32_t stereo_enable;
+ uint16_t line_width[VFE_MAX];
+};
+
+struct msm_ispif_param_data {
+ uint32_t num;
+ struct msm_ispif_params_entry entries[MAX_PARAM_ENTRIES];
+};
+
+struct msm_isp_info {
+ uint32_t max_resolution;
+ uint32_t id;
+ uint32_t ver;
+};
+
+struct msm_ispif_vfe_info {
+ int num_vfe;
+ struct msm_isp_info info[VFE_MAX];
+};
+
+enum ispif_cfg_type_t {
+ ISPIF_CLK_ENABLE,
+ ISPIF_CLK_DISABLE,
+ ISPIF_INIT,
+ ISPIF_CFG,
+ ISPIF_START_FRAME_BOUNDARY,
+ ISPIF_RESTART_FRAME_BOUNDARY,
+ ISPIF_STOP_FRAME_BOUNDARY,
+ ISPIF_STOP_IMMEDIATELY,
+ ISPIF_RELEASE,
+ ISPIF_ENABLE_REG_DUMP,
+ ISPIF_SET_VFE_INFO,
+ ISPIF_CFG2,
+ ISPIF_CFG_STEREO,
+};
+
+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_vfe_info vfe_info; /* ISPIF_SET_VFE_INFO */
+ struct msm_ispif_param_data params; /* CFG, START, STOP */
+ };
+};
+
+struct ispif_cfg_data_ext {
+ enum ispif_cfg_type_t cfg_type;
+ void __user *data;
+ uint32_t size;
+};
+
+#define ISPIF_RDI_PACK_MODE_SUPPORT 1
+
+#define ISPIF_3D_SUPPORT 1
+
+#define ISPIF_LINE_WIDTH_SUPPORT 1
+
+#define VIDIOC_MSM_ISPIF_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
+
+#define VIDIOC_MSM_ISPIF_CFG_EXT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct ispif_cfg_data_ext)
+
+#endif
+
diff --git a/include/uapi/media/msmb_pproc.h b/include/uapi/media/msmb_pproc.h
new file mode 100644
index 0000000..8f45457
--- /dev/null
+++ b/include/uapi/media/msmb_pproc.h
@@ -0,0 +1,255 @@
+#ifndef __UAPI_MSMB_PPROC_H
+#define __UAPI_MSMB_PPROC_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <media/msmb_generic_buf_mgr.h>
+
+/* Should be same as VIDEO_MAX_PLANES in videodev2.h */
+#define MAX_PLANES VIDEO_MAX_PLANES
+/* PARTIAL_FRAME_STRIPE_COUNT must be even */
+#define PARTIAL_FRAME_STRIPE_COUNT 4
+
+#define MAX_NUM_CPP_STRIPS 8
+#define MSM_CPP_MAX_NUM_PLANES 3
+#define MSM_CPP_MIN_FRAME_LENGTH 13
+#define MSM_CPP_MAX_FRAME_LENGTH 4096
+#define MSM_CPP_MAX_FW_NAME_LEN 32
+#define MAX_FREQ_TBL 10
+#define MSM_OUTPUT_BUF_CNT 8
+
+enum msm_cpp_frame_type {
+ MSM_CPP_OFFLINE_FRAME,
+ MSM_CPP_REALTIME_FRAME,
+};
+
+enum msm_vpe_frame_type {
+ MSM_VPE_OFFLINE_FRAME,
+ MSM_VPE_REALTIME_FRAME,
+};
+
+struct msm_cpp_buffer_info_t {
+ int32_t fd;
+ uint32_t index;
+ uint32_t offset;
+ uint8_t native_buff;
+ uint8_t processed_divert;
+ uint32_t identity;
+};
+
+struct msm_cpp_stream_buff_info_t {
+ uint32_t identity;
+ uint32_t num_buffs;
+ struct msm_cpp_buffer_info_t *buffer_info;
+};
+
+enum msm_cpp_batch_mode_t {
+ BATCH_MODE_NONE,
+ BATCH_MODE_VIDEO,
+ BATCH_MODE_PREVIEW
+};
+
+struct msm_cpp_batch_info_t {
+ enum msm_cpp_batch_mode_t batch_mode;
+ uint32_t batch_size;
+ uint32_t intra_plane_offset[MAX_PLANES];
+ uint32_t pick_preview_idx;
+ uint32_t cont_idx;
+};
+
+struct msm_cpp_frame_info_t {
+ int32_t frame_id;
+ struct timeval timestamp;
+ uint32_t inst_id;
+ uint32_t identity;
+ uint32_t client_id;
+ enum msm_cpp_frame_type frame_type;
+ uint32_t num_strips;
+ uint32_t msg_len;
+ uint32_t *cpp_cmd_msg;
+ int src_fd;
+ int dst_fd;
+ struct timeval in_time, out_time;
+ void __user *cookie;
+ int32_t *status;
+ int32_t duplicate_output;
+ uint32_t duplicate_identity;
+ uint32_t feature_mask;
+ uint8_t we_disable;
+ struct msm_cpp_buffer_info_t input_buffer_info;
+ struct msm_cpp_buffer_info_t output_buffer_info[MSM_OUTPUT_BUF_CNT];
+ struct msm_cpp_buffer_info_t duplicate_buffer_info;
+ struct msm_cpp_buffer_info_t tnr_scratch_buffer_info[2];
+ uint32_t reserved;
+ uint8_t partial_frame_indicator;
+ /* the followings are used only for partial_frame type
+ * and is only used for offline frame processing and
+ * only if payload big enough and need to be split into partial_frame
+ * if first_payload, kernel acquires output buffer
+ * first payload must have the last stripe
+ * buffer addresses from 0 to last_stripe_index are updated.
+ * kernel updates payload with msg_len and stripe_info
+ * kernel sends top level, plane level, then only stripes
+ * starting with first_stripe_index and
+ * ends with last_stripe_index
+ * kernel then sends trailing flag at frame done,
+ * if last payload, kernel queues the output buffer to HAL
+ */
+ uint8_t first_payload;
+ uint8_t last_payload;
+ uint32_t first_stripe_index;
+ uint32_t last_stripe_index;
+ uint32_t stripe_info_offset;
+ uint32_t stripe_info;
+ struct msm_cpp_batch_info_t batch_info;
+};
+
+struct msm_cpp_pop_stream_info_t {
+ int32_t frame_id;
+ uint32_t identity;
+};
+
+struct cpp_hw_info {
+ uint32_t cpp_hw_version;
+ uint32_t cpp_hw_caps;
+ unsigned long freq_tbl[MAX_FREQ_TBL];
+ uint32_t freq_tbl_count;
+};
+
+struct msm_vpe_frame_strip_info {
+ uint32_t src_w;
+ uint32_t src_h;
+ uint32_t dst_w;
+ uint32_t dst_h;
+ uint32_t src_x;
+ uint32_t src_y;
+ uint32_t phase_step_x;
+ uint32_t phase_step_y;
+ uint32_t phase_init_x;
+ uint32_t phase_init_y;
+};
+
+struct msm_vpe_buffer_info_t {
+ int32_t fd;
+ uint32_t index;
+ uint32_t offset;
+ uint8_t native_buff;
+ uint8_t processed_divert;
+};
+
+struct msm_vpe_stream_buff_info_t {
+ uint32_t identity;
+ uint32_t num_buffs;
+ struct msm_vpe_buffer_info_t *buffer_info;
+};
+
+struct msm_vpe_frame_info_t {
+ int32_t frame_id;
+ struct timeval timestamp;
+ uint32_t inst_id;
+ uint32_t identity;
+ uint32_t client_id;
+ enum msm_vpe_frame_type frame_type;
+ struct msm_vpe_frame_strip_info strip_info;
+ unsigned long src_fd;
+ unsigned long dst_fd;
+ struct ion_handle *src_ion_handle;
+ struct ion_handle *dest_ion_handle;
+ unsigned long src_phyaddr;
+ unsigned long dest_phyaddr;
+ unsigned long src_chroma_plane_offset;
+ unsigned long dest_chroma_plane_offset;
+ struct timeval in_time, out_time;
+ void *cookie;
+
+ struct msm_vpe_buffer_info_t input_buffer_info;
+ struct msm_vpe_buffer_info_t output_buffer_info;
+};
+
+struct msm_pproc_queue_buf_info {
+ struct msm_buf_mngr_info buff_mgr_info;
+ uint8_t is_buf_dirty;
+};
+
+struct msm_cpp_clock_settings_t {
+ unsigned long clock_rate;
+ uint64_t avg;
+ uint64_t inst;
+};
+
+#define VIDIOC_MSM_CPP_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_EVENTPAYLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_INST_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_LOAD_FIRMWARE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_HW_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_FLUSH_QUEUE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_DEQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_TRANSACTION_SETUP \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_GET_EVENTPAYLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_GET_INST_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_ENQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_QUEUE_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_SET_CLOCK \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_POP_STREAM_BUFFER \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 17, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_IOMMU_ATTACH \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 18, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_IOMMU_DETACH \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 19, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_DELETE_STREAM_BUFF\
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 20, struct msm_camera_v4l2_ioctl_t)
+
+
+#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)
+#define V4L2_EVENT_VPE_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 1)
+
+struct msm_camera_v4l2_ioctl_t {
+ uint32_t id;
+ size_t len;
+ int32_t trans_code;
+ void __user *ioctl_ptr;
+};
+
+#endif
+
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 4a60459..d3ea11f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -3288,6 +3288,46 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
return NULL;
}
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER
+static inline bool
+should_compact_lmk_retry(struct alloc_context *ac, int order, int alloc_flags)
+{
+ struct zone *zone;
+ struct zoneref *z;
+
+ /* Let costly order requests check for compaction progress */
+ if (order > PAGE_ALLOC_COSTLY_ORDER)
+ return false;
+
+ /*
+ * For (0 < order < PAGE_ALLOC_COSTLY_ORDER) allow the shrinkers
+ * to run and free up memory. Do not let these allocations fail
+ * if shrinkers can free up memory. This is similar to
+ * should_compact_retry implementation for !CONFIG_COMPACTION.
+ */
+ for_each_zone_zonelist_nodemask(zone, z, ac->zonelist,
+ ac->high_zoneidx, ac->nodemask) {
+ unsigned long available;
+
+ available = zone_reclaimable_pages(zone);
+ available +=
+ zone_page_state_snapshot(zone, NR_FREE_PAGES);
+
+ if (__zone_watermark_ok(zone, 0, min_wmark_pages(zone),
+ ac_classzone_idx(ac), alloc_flags, available))
+ return true;
+ }
+
+ return false;
+}
+#else
+static inline bool
+should_compact_lmk_retry(struct alloc_context *ac, int order, int alloc_flags)
+{
+ return false;
+}
+#endif
+
static inline bool
should_compact_retry(struct alloc_context *ac, int order, int alloc_flags,
enum compact_result compact_result,
@@ -3300,6 +3340,9 @@ should_compact_retry(struct alloc_context *ac, int order, int alloc_flags,
if (!order)
return false;
+ if (should_compact_lmk_retry(ac, order, alloc_flags))
+ return true;
+
if (compaction_made_progress(compact_result))
(*compaction_retries)++;
@@ -3537,7 +3580,8 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* their order will become available due to high fragmentation so
* always increment the no progress counter for them
*/
- if (did_some_progress && order <= PAGE_ALLOC_COSTLY_ORDER)
+ if ((did_some_progress && order <= PAGE_ALLOC_COSTLY_ORDER) ||
+ IS_ENABLED(CONFIG_ANDROID_LOW_MEMORY_KILLER))
*no_progress_loops = 0;
else
(*no_progress_loops)++;
@@ -3815,7 +3859,8 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
* implementation of the compaction depends on the sufficient amount
* of free memory (see __compaction_suitable)
*/
- if (did_some_progress > 0 &&
+ if ((did_some_progress > 0 ||
+ IS_ENABLED(CONFIG_ANDROID_LOW_MEMORY_KILLER)) &&
should_compact_retry(ac, order, alloc_flags,
compact_result, &compact_priority,
&compaction_retries))
diff --git a/mm/vmscan.c b/mm/vmscan.c
index bb18b47..2740973 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -212,7 +212,8 @@ unsigned long zone_reclaimable_pages(struct zone *zone)
nr = zone_page_state_snapshot(zone, NR_ZONE_INACTIVE_FILE) +
zone_page_state_snapshot(zone, NR_ZONE_ACTIVE_FILE);
- if (get_nr_swap_pages() > 0)
+ if (get_nr_swap_pages() > 0
+ || IS_ENABLED(CONFIG_ANDROID_LOW_MEMORY_KILLER))
nr += zone_page_state_snapshot(zone, NR_ZONE_INACTIVE_ANON) +
zone_page_state_snapshot(zone, NR_ZONE_ACTIVE_ANON);
diff --git a/net/socket.c b/net/socket.c
index bd9679f..539755b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -117,6 +117,8 @@ unsigned int sysctl_net_busy_poll __read_mostly;
static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to);
static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from);
+static BLOCKING_NOTIFIER_HEAD(sockev_notifier_list);
+
static int sock_mmap(struct file *file, struct vm_area_struct *vma);
static int sock_close(struct inode *inode, struct file *file);
@@ -171,6 +173,14 @@ static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;
static DEFINE_PER_CPU(int, sockets_in_use);
/*
+ * Socket Event framework helpers
+ */
+static void sockev_notify(unsigned long event, struct socket *sk)
+{
+ blocking_notifier_call_chain(&sockev_notifier_list, event, sk);
+}
+
+/**
* Support routines.
* Move socket addresses back and forth across the kernel/user
* divide and look after the messy bits.
@@ -1255,6 +1265,9 @@ SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
if (retval < 0)
goto out;
+ if (retval == 0)
+ sockev_notify(SOCKEV_SOCKET, sock);
+
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;
@@ -1400,6 +1413,8 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
&address, addrlen);
}
fput_light(sock->file, fput_needed);
+ if (!err)
+ sockev_notify(SOCKEV_BIND, sock);
}
return err;
}
@@ -1427,6 +1442,8 @@ SYSCALL_DEFINE2(listen, int, fd, int, backlog)
err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed);
+ if (!err)
+ sockev_notify(SOCKEV_LISTEN, sock);
}
return err;
}
@@ -1513,7 +1530,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
fd_install(newfd, newfile);
err = newfd;
-
+ if (!err)
+ sockev_notify(SOCKEV_ACCEPT, sock);
out_put:
fput_light(sock->file, fput_needed);
out:
@@ -1563,6 +1581,8 @@ SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
sock->file->f_flags);
+ if (!err)
+ sockev_notify(SOCKEV_CONNECT, sock);
out_put:
fput_light(sock->file, fput_needed);
out:
@@ -1823,6 +1843,7 @@ SYSCALL_DEFINE2(shutdown, int, fd, int, how)
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
+ sockev_notify(SOCKEV_SHUTDOWN, sock);
err = security_socket_shutdown(sock, how);
if (!err)
err = sock->ops->shutdown(sock, how);
@@ -3340,3 +3361,15 @@ int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how)
return sock->ops->shutdown(sock, how);
}
EXPORT_SYMBOL(kernel_sock_shutdown);
+
+int sockev_register_notify(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&sockev_notifier_list, nb);
+}
+EXPORT_SYMBOL(sockev_register_notify);
+
+int sockev_unregister_notify(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&sockev_notifier_list, nb);
+}
+EXPORT_SYMBOL(sockev_unregister_notify);
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index e2cebf15..1c5b36d 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -173,6 +173,9 @@ enum usb_qmi_audio_format {
USB_QMI_PCM_FORMAT_U32_BE,
};
+static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
+ size_t iova_size, size_t mapped_iova_size);
+
static enum usb_audio_device_speed_enum_v01
get_speed_info(enum usb_device_speed udev_speed)
{
@@ -279,11 +282,14 @@ static unsigned long uaudio_get_iova(unsigned long *curr_iova,
}
static unsigned long uaudio_iommu_map(enum mem_type mtype, phys_addr_t pa,
- size_t size)
+ size_t size, struct sg_table *sgt)
{
- unsigned long va = 0;
+ unsigned long va_sg, va = 0;
bool map = true;
- int ret;
+ int i, ret;
+ size_t sg_len, total_len = 0;
+ struct scatterlist *sg;
+ phys_addr_t pa_sg;
switch (mtype) {
case MEM_EVENT_RING:
@@ -306,18 +312,48 @@ static unsigned long uaudio_iommu_map(enum mem_type mtype, phys_addr_t pa,
pr_err("%s: unknown mem type %d\n", __func__, mtype);
}
- if (!va)
- map = false;
-
- if (!map)
+ if (!va || !map)
goto done;
- pr_debug("%s: map pa %pa to iova %lu for memtype %d\n", __func__, &pa,
- va, mtype);
+ if (!sgt)
+ goto skip_sgt_map;
+
+ va_sg = va;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ sg_len = PAGE_ALIGN(sg->offset + sg->length);
+ pa_sg = page_to_phys(sg_page(sg));
+ ret = iommu_map(uaudio_qdev->domain, va_sg, pa_sg, sg_len,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO);
+ if (ret) {
+ pr_err("%s:mapping failed ret%d\n", __func__, ret);
+ pr_err("memtype:%d, pa:%pK iova:%lu sg_len:%zu\n",
+ mtype, &pa_sg, va_sg, sg_len);
+ uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
+ va = 0;
+ goto done;
+ }
+ pr_debug("%s:memtype %d:map pa:%pK to iova:%lu len:%zu\n",
+ __func__, mtype, &pa_sg, va_sg, sg_len);
+ va_sg += sg_len;
+ total_len += sg_len;
+ }
+
+ if (size != total_len) {
+ pr_err("%s: iova size %zu != mapped iova size %zu\n", __func__,
+ size, total_len);
+ uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
+ va = 0;
+ }
+ return va;
+
+skip_sgt_map:
+ pr_debug("%s:memtype:%d map pa:%pK to iova %lu size:%zu\n", __func__,
+ mtype, &pa, va, size);
+
ret = iommu_map(uaudio_qdev->domain, va, pa, size,
IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO);
if (ret)
- pr_err("%s:failed to map pa:%pa iova:%lu memtype:%d ret:%d\n",
+ pr_err("%s:failed to map pa:%pK iova:%lu memtype:%d ret:%d\n",
__func__, &pa, va, mtype, ret);
done:
return va;
@@ -361,12 +397,12 @@ static void uaudio_put_iova(unsigned long va, size_t size, struct list_head
}
static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
- size_t size)
+ size_t iova_size, size_t mapped_iova_size)
{
size_t umap_size;
bool unmap = true;
- if (!va || !size)
+ if (!va || !iova_size)
return;
switch (mtype) {
@@ -378,11 +414,11 @@ static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
break;
case MEM_XFER_RING:
- uaudio_put_iova(va, size, &uaudio_qdev->xfer_ring_list,
+ uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_ring_list,
&uaudio_qdev->xfer_ring_iova_size);
break;
case MEM_XFER_BUF:
- uaudio_put_iova(va, size, &uaudio_qdev->xfer_buf_list,
+ uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_buf_list,
&uaudio_qdev->xfer_buf_iova_size);
break;
default:
@@ -390,15 +426,16 @@ static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
unmap = false;
}
- if (!unmap)
+ if (!unmap || !mapped_iova_size)
return;
- pr_debug("%s: unmap iova %lu for memtype %d\n", __func__, va, mtype);
+ pr_debug("%s:memtype %d: unmap iova %lu size %zu\n", __func__, mtype,
+ va, mapped_iova_size);
- umap_size = iommu_unmap(uaudio_qdev->domain, va, size);
- if (umap_size != size)
- pr_err("%s: unmapped size %zu for iova %lu\n", __func__,
- umap_size, va);
+ umap_size = iommu_unmap(uaudio_qdev->domain, va, mapped_iova_size);
+ if (umap_size != mapped_iova_size)
+ pr_err("%s:unmapped size %zu for iova %lu of mapped size %zu\n",
+ __func__, umap_size, va, mapped_iova_size);
}
static int prepare_qmi_response(struct snd_usb_substream *subs,
@@ -418,12 +455,11 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
void *hdr_ptr;
u8 *xfer_buf;
unsigned int data_ep_pipe = 0, sync_ep_pipe = 0;
- u32 len, mult, remainder, xfer_buf_len, sg_len, i, total_len = 0;
- unsigned long va, va_sg, tr_data_va = 0, tr_sync_va = 0;
+ u32 len, mult, remainder, xfer_buf_len;
+ unsigned long va, tr_data_va = 0, tr_sync_va = 0;
phys_addr_t xhci_pa, xfer_buf_pa, tr_data_pa = 0, tr_sync_pa = 0;
dma_addr_t dma;
struct sg_table sgt;
- struct scatterlist *sg;
iface = usb_ifnum_to_if(subs->dev, subs->interface);
if (!iface) {
@@ -593,7 +629,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
goto err;
}
- va = uaudio_iommu_map(MEM_EVENT_RING, xhci_pa, PAGE_SIZE);
+ va = uaudio_iommu_map(MEM_EVENT_RING, xhci_pa, PAGE_SIZE, NULL);
if (!va)
goto err;
@@ -610,7 +646,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
resp->speed_info_valid = 1;
/* data transfer ring */
- va = uaudio_iommu_map(MEM_XFER_RING, tr_data_pa, PAGE_SIZE);
+ va = uaudio_iommu_map(MEM_XFER_RING, tr_data_pa, PAGE_SIZE, NULL);
if (!va)
goto unmap_er;
@@ -624,7 +660,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
goto skip_sync;
xhci_pa = resp->xhci_mem_info.tr_sync.pa;
- va = uaudio_iommu_map(MEM_XFER_RING, tr_sync_pa, PAGE_SIZE);
+ va = uaudio_iommu_map(MEM_XFER_RING, tr_sync_pa, PAGE_SIZE, NULL);
if (!va)
goto unmap_data;
@@ -655,20 +691,9 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
dma_get_sgtable(subs->dev->bus->sysdev, &sgt, xfer_buf, xfer_buf_pa,
len);
-
- va = 0;
- for_each_sg(sgt.sgl, sg, sgt.nents, i) {
- sg_len = PAGE_ALIGN(sg->offset + sg->length);
- va_sg = uaudio_iommu_map(MEM_XFER_BUF,
- page_to_phys(sg_page(sg)), sg_len);
- if (!va_sg)
- goto unmap_xfer_buf;
-
- if (!va)
- va = va_sg;
-
- total_len += sg_len;
- }
+ va = uaudio_iommu_map(MEM_XFER_BUF, xfer_buf_pa, len, &sgt);
+ if (!va)
+ goto unmap_sync;
resp->xhci_mem_info.xfer_buff.pa = xfer_buf_pa;
resp->xhci_mem_info.xfer_buff.size = len;
@@ -690,7 +715,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
uadev[card_num].num_intf, GFP_KERNEL);
if (!uadev[card_num].info) {
ret = -ENOMEM;
- goto unmap_xfer_buf;
+ goto unmap_sync;
}
uadev[card_num].udev = subs->dev;
atomic_set(&uadev[card_num].in_use, 1);
@@ -722,16 +747,13 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
return 0;
-unmap_xfer_buf:
- if (va)
- uaudio_iommu_unmap(MEM_XFER_BUF, va, total_len);
unmap_sync:
usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
- uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE);
+ uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE, PAGE_SIZE);
unmap_data:
- uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE);
+ uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE, PAGE_SIZE);
unmap_er:
- uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
err:
return ret;
}
@@ -760,17 +782,17 @@ static void uaudio_dev_intf_cleanup(struct usb_device *udev,
}
uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
- info->data_xfer_ring_size);
+ info->data_xfer_ring_size, info->data_xfer_ring_size);
info->data_xfer_ring_va = 0;
info->data_xfer_ring_size = 0;
uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
- info->sync_xfer_ring_size);
+ info->sync_xfer_ring_size, info->sync_xfer_ring_size);
info->sync_xfer_ring_va = 0;
info->sync_xfer_ring_size = 0;
uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_va,
- info->xfer_buf_size);
+ info->xfer_buf_size, info->xfer_buf_size);
info->xfer_buf_va = 0;
usb_free_coherent(udev, info->xfer_buf_size,
@@ -805,7 +827,8 @@ static void uaudio_dev_cleanup(struct uaudio_dev *dev)
/* all audio devices are disconnected */
if (!uaudio_qdev->card_slot) {
- uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
+ PAGE_SIZE);
usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
pr_debug("%s: all audio devices disconnected\n", __func__);
}
@@ -881,7 +904,8 @@ static void uaudio_dev_release(struct kref *kref)
/* all audio devices are disconnected */
if (!uaudio_qdev->card_slot) {
usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
- uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
+ PAGE_SIZE);
pr_debug("%s: all audio devices disconnected\n", __func__);
}