Merge "msm: mhi_dev: Disable/Enable interrupts during suspend/resume"
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 0589165..15f1e93 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -103,6 +103,8 @@
- qcom,sde-dsc-size: A u32 value indicates the address range for each dsc.
- qcom,sde-cdm-size: A u32 value indicates the address range for each cdm.
- qcom,sde-pp-size: A u32 value indicates the address range for each pingpong.
+- qcom,sde-te-source: Array of GPIO sources indicating which pingpong TE is
+ sourced to which panel TE gpio.
- qcom,sde-wb-size: A u32 value indicates the address range for each writeback.
- qcom,sde-len: A u32 entry for SDE address range.
- qcom,sde-intf-max-prefetch-lines: Array of u32 values for max prefetch lines on
@@ -502,6 +504,7 @@
qcom,sde-pp-off = <0x00071000 0x00071800
0x00072000 0x00072800>;
qcom,sde-pp-slave = <0x0 0x0 0x0 0x0>;
+ qcom,sde-te-source = <0x0 0x1 0x0 0x0>;
qcom,sde-cdm-off = <0x0007a200>;
qcom,sde-dsc-off = <0x00081000 0x00081400>;
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
index 1934bc5..2d689d2 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
@@ -113,6 +113,7 @@
- qcom,platform-bklight-en-gpio-invert: Invert the gpio used to enable display back-light
- qcom,panel-mode-gpio: Specifies the GPIO to select video/command/single-port/dual-port
mode of panel through gpio when it supports these modes.
+- qcom,ext-vdd-gpio: Specifies the GPIO to enable external VDD supply.
- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node
Refer to pinctrl-bindings.txt
- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-client.txt b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt
new file mode 100644
index 0000000..0d5fde8
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt
@@ -0,0 +1,27 @@
+Qualcomm Technologies, Inc. mdss-spi-client
+
+mdss-spi-client is for SPI display to send the FB data to SPI master.
+
+Required properties:
+- compatible : should be "qcom,mdss-spi-client"
+- spi-max-frequency : Maximum SPI clocking speed of device in Hz
+
+Optional properties:
+- label: A string used to describe the controller used.
+- spi-cpol : Boolean property indicating device requires inverse
+ clock polarity (CPOL) mode
+- spi-cpha : Empty property indicating device requires shifted
+ clock phase (CPHA) mode
+- spi-cs-high : Empty property indicating device requires
+ chip select active high
+
+Example:
+spi@78b9000 { /* BLSP1 QUP5 */
+ qcom,mdss_spi_client {
+ reg = <0>;
+ compatible = "qcom,mdss-spi-client";
+ label = "MDSS SPI QUP5 CLIENT";
+ spi-max-frequency = <50000000>;
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt
new file mode 100644
index 0000000..d46068f
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt
@@ -0,0 +1,203 @@
+Qualcomm Technologies, Inc. mdss-spi-panel
+
+mdss-spi-panel is a SPI panel device which supports panels that
+are compatible with display serial interface specification.
+
+Required properties:
+- qcom,mdss-spi-panel-controller: Specifies the phandle for the SPI controller that
+ this panel will be mapped to.
+- qcom,mdss-spi-panel-width: Specifies panel width in pixels.
+- qcom,mdss-spi-panel-height: Specifies panel height in pixels.
+- qcom,mdss-spi-bpp: Specifies the panel bits per pixels.
+ 3 = for rgb111
+ 8 = for rgb332
+ 12 = for rgb444
+ 16 = for rgb565
+ 18 = for rgb666
+ 24 = for rgb888
+- qcom,mdss-spi-panel-destination: A string that specifies the destination display for the panel.
+ "display_1" = DISPLAY_1
+ "display_2" = DISPLAY_2
+- qcom,mdss-spi-on-command: A byte stream formed by multiple packets
+ byte 0: wait number of specified ms after command
+ transmitted
+ byte 1: 8 bits length in network byte order
+ byte 3 and beyond: number byte of payload
+- qcom,mdss-spi-off-command: A byte stream formed by multiple packets
+ byte 0: wait number of specified ms after command
+ transmitted
+ byte 1: 8 bits length in network byte order
+ byte 3 and beyond: number byte of payload
+Optional properties:
+- qcom,mdss-spi-panel-name: A string used as a descriptive name of the panel
+- qcom,cont-splash-enabled: Boolean used to enable continuous splash mode.
+ If this property is specified, it is required to
+ to specify the memory reserved for the splash
+ screen using the qcom,memblock-reserve binding
+ for the framebuffer device attached to the panel.
+- qcom,mdss-spi-h-back-porch: Horizontal back porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-h-front-porch: Horizontal front porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-h-pulse-width: Horizontal pulse width.
+ 2 = default value.
+- qcom,mdss-spi-h-sync-skew: Horizontal sync skew value.
+ 0 = default value.
+- qcom,mdss-spi-v-back-porch: Vertical back porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-v-front-porch: Vertical front porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-v-pulse-width: Vertical pulse width.
+ 2 = default value.
+- qcom,mdss-spi-bl-pmic-control-type: A string that specifies the implementation of backlight
+ control for this panel.
+ "bl_ctrl_pwm" = Backlight controlled by PWM gpio.
+ "bl_ctrl_wled" = Backlight controlled by WLED.
+ other: Unknown backlight control. (default)
+- qcom,mdss-spi-bl-min-level: Specifies the min backlight level supported by the panel.
+ 0 = default value.
+- qcom,mdss-spi-bl-max-level: Specifies the max backlight level supported by the panel.
+ 255 = default value.
+- qcom,mdss-spi-panel-framerate: Specifies the frame rate for the panel.
+- qcom,esd-check-enabled: Boolean used to enable ESD recovery feature.
+- qcom,mdss-spi-panel-status-check-mode:Specifies the panel status check method for ESD recovery.
+ "send_init_command" = send init code to recover panel status.
+ "reg_read" = Read register value to check the panel status.
+- qcom,mdss-spi-panel-status-reg:Unsigned 8bits integer value to specifies the value
+ of panel status register address.
+- qcom,mdss-spi-panel-status-read-length:Unsigned 8bits integer value that specifies
+ the expected read-back length of the panel register.
+- qcom,mdss-spi-panel-status-value:An unsigned 8bits integer araray that specifies the
+ values of the panel status register which is used to
+ check the panel status.
+ The size of this array is specified by
+ qcom,mdss-dsi-panel-status-read-length.
+
+Example:
+&mdss_mdp {
+ spi_gc9305_qvga_cmd: qcom,mdss_spi_gc9305_qvga_cmd {
+ qcom,mdss-spi-panel-name = "gc9305 qvga command mode spi panel";
+ qcom,mdss-spi-panel-destination = "display_1";
+ qcom,mdss-spi-panel-controller = <&mdss_spi>;
+ qcom,mdss-spi-panel-framerate = <30>;
+ qcom,mdss-spi-panel-width = <240>;
+ qcom,mdss-spi-panel-height = <320>;
+ qcom,mdss-spi-h-front-porch = <79>;
+ qcom,mdss-spi-h-back-porch = <59>;
+ qcom,mdss-spi-h-pulse-width = <60>;
+ qcom,mdss-spi-v-back-porch = <10>;
+ qcom,mdss-spi-v-front-porch = <7>;
+ qcom,mdss-spi-v-pulse-width = <2>;
+ qcom,mdss-spi-h-left-border = <0>;
+ qcom,mdss-spi-h-right-border = <0>;
+ qcom,mdss-spi-v-top-border = <0>;
+ qcom,mdss-spi-v-bottom-border = <0>;
+ qcom,mdss-spi-bpp = <16>;
+ qcom,mdss-spi-on-command = [00 01 FE
+ 00 01 EF
+ 00 02 36 48
+ 00 02 3A 05
+ 00 02 35 00
+ 00 03 A4 44 44
+ 00 03 A5 42 42
+ 00 03 AA 88 88
+ 00 03 E8 12 40
+ 00 03 E3 01 10
+ 00 02 FF 61
+ 00 02 AC 00
+ 00 03 A6 2A 2A
+ 00 03 A7 2B 2B
+ 00 03 A8 18 18
+ 00 03 A9 2A 2A
+ 00 02 AD 33
+ 00 02 AF 55
+ 00 02 AE 2B
+ 00 05 2A 00 00 00 EF
+ 00 05 2B 00 00 01 3F
+ 00 01 2C
+ 00 07 F0 02 02 00 08 0C 10
+ 00 07 F1 01 00 00 14 1D 0E
+ 00 07 F2 10 09 37 04 04 48
+ 00 07 F3 10 0B 3F 05 05 4E
+ 00 07 F4 0D 19 17 1D 1E 0F
+ 00 07 F5 06 12 13 1A 1B 0F
+ 78 01 11
+ 00 01 29
+ 00 01 2C];
+ qcom,mdss-spi-off-command = [20 01 28
+ 20 01 10];
+ qcom,mdss-spi-bl-min-level = <1>;
+ qcom,mdss-spi-bl-max-level = <4095>;
+ qcom,esd-check-enabled;
+ qcom,mdss-spi-panel-status-check-mode = "reg_read";
+ qcom,mdss-spi-panel-status-reg = /bits/ 8 <0x09>;
+ qcom,mdss-spi-panel-status-read-length = <4>;
+ qcom,mdss-spi-panel-status-value = /bits/ 8 <0x52 0x29 0x83 0x00>;
+ };
+};
+
+mdss-spi-display is a spi interface display which support send frame
+data and command to panel, compatible with SPI interface specification.
+
+Required properties:
+- compatible: This property applies to SPI panels only.
+ compatible = "qcom,mdss-spi-display".
+- vdd-supply: Phandle for vdd regulator device node.
+- vddio-supply: Phandle for vdd-io regulator device node.
+- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the interface is mapped.
+- qcom,mdss-mdp: pHandle that specifies the mdss-mdp device.
+- qcom,panel-supply-entries: A node that lists the elements of the supply used to
+ power the DSI panel. There can be more than one instance
+ of this binding, in which case the entry would be appended
+ with the supply entry index. For a detailed description
+ fields in the supply entry, refer to the qcom,ctrl-supply-entries
+ binding above.
+- qcom,platform-spi-dc-gpio: Pull down this gpio indicate current package is command,
+ Pull up this gpio indicate current package is parameter or pixels.
+
+Optional properties:
+- label:A string used to describe the controller used.
+ -- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+ -- qcom,supply-min-voltage: minimum voltage level (uV)
+ -- qcom,supply-max-voltage: maximum voltage level (uV)
+ -- qcom,supply-enable-load: load drawn (uA) from enabled supply
+ -- qcom,supply-disable-load: load drawn (uA) from disabled supply
+ -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+ -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+ -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+ -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+
+Example:
+ mdss_spi: qcom,mdss_spi {
+ compatible = "qcom,mdss-spi-display";
+ label = "mdss spi panel";
+
+ qcom,mdss-fb-map = <&mdss_fb0>;
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8909_l17>;
+ vddio-supply = <&pm8909_l6>;
+ qcom,platform-spi-dc-gpio = <&msm_gpio 110 0>;
+
+ qcom,panel-supply-entries {
+ #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/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index dae45e1..ced854d 100644
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -67,6 +67,7 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
+CONFIG_ARM_MODULE_PLTS=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index 3c43fa3..40d5cb1 100644
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -70,6 +70,7 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
+CONFIG_ARM_MODULE_PLTS=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
index 4d9c40c..993799b 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
@@ -14,7 +14,7 @@
#include "apq8053-lite-dragon.dtsi"
&mdss_dsi0 {
- qcom,ext_vdd-gpio = <&tlmm 100 0>;
+ qcom,ext-vdd-gpio = <&tlmm 100 0>;
qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
qcom,platform-lane-config = [00 00 ff 0f
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
index 396fd55..1744c90 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
@@ -18,6 +18,41 @@
/delete-node/ himax_ts@48;
};
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_boent51021_1200p_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ vdd-supply = <&pm8953_l10>;
+ vddio-supply = <&pm8953_l6>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,ext-vdd-gpio = <&tlmm 100 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
+
+ qcom,platform-lane-config = [00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 8f];
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+};
+
&eeprom0 {
gpios = <&tlmm 26 0>,
<&tlmm 40 0>,
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi
new file mode 100644
index 0000000..04accd8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi
@@ -0,0 +1,92 @@
+/* Novatek Android Driver Sample Code for Novatek chipset
+ *
+ * Copyright (C) 2015-2018 Novatek Microelectronics Corp.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+&mdss_mdp {
+ dsi_boent51021_1200p_video: qcom,mdss_dsi_boent51021_1200p_video {
+ qcom,mdss-dsi-panel-name =
+ "boent51021 1200p video mode dsi panel";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ 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 = <1200>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <32>;
+ qcom,mdss-dsi-h-pulse-width = <1>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <14>;
+ qcom,mdss-dsi-v-front-porch = <25>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ 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-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 23 01 00 00 01 00 02 8f a5
+ 23 01 00 00 00 00 02 83 00
+ 23 01 00 00 00 00 02 84 00
+ 23 01 00 00 00 00 02 8c 80
+ 23 01 00 00 00 00 02 cd 6c
+ 23 01 00 00 00 00 02 c8 fc
+ 23 01 00 00 00 00 02 97 00
+ 23 01 00 00 00 00 02 8b 10
+ 23 01 00 00 00 00 02 a9 20
+ 23 01 00 00 00 00 02 83 aa
+ 23 01 00 00 00 00 02 84 11
+ 23 01 00 00 00 00 02 a9 4b
+ 23 01 00 00 00 00 02 85 04
+ 23 01 00 00 00 00 02 86 08
+ 23 01 00 00 00 00 02 9c 10
+ 05 01 00 00 00 00 02 11 00
+ 23 01 00 00 00 00 02 8f 00
+ ];
+ qcom,mdss-dsi-off-command = [
+ 23 01 00 00 00 00 02 83 00
+ 23 01 00 00 78 00 02 84 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_lp_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ 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-panel-timings = [
+ f2 3a 28 00 6c 70 2c 3e 2e 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x0e>;
+ qcom,mdss-dsi-t-clk-pre = <0x33>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-pan-physical-width-dimension = <135>;
+ qcom,mdss-pan-physical-height-dimension = <216>;
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm8909.dtsi
index a72fdbf..2b9c1e1 100644
--- a/arch/arm64/boot/dts/qcom/msm8909.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi
@@ -77,7 +77,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu0_slp_sts>;
- qcom,limits-info = <&mitigation_profile0>;
};
CPU1: cpu@1 {
@@ -87,7 +86,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu1_slp_sts>;
- qcom,limits-info = <&mitigation_profile2>;
};
CPU2: cpu@2 {
@@ -97,7 +95,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu2_slp_sts>;
- qcom,limits-info = <&mitigation_profile1>;
};
CPU3: cpu@3 {
@@ -107,7 +104,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu3_slp_sts>;
- qcom,limits-info = <&mitigation_profile2>;
};
};
@@ -559,79 +555,6 @@
};
- qcom,sensor-information {
- compatible = "qcom,sensor-information";
- sensor_information0: qcom,sensor-information-0 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor0";
- qcom,alias-name = "pop_mem";
- };
-
- sensor_information1: qcom,sensor-information-1 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor1";
- };
-
- sensor_information2: qcom,sensor-information-2 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor2";
- };
-
- sensor_information3: qcom,sensor-information-3 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor3";
- };
-
- sensor_information4: qcom,sensor-information-4 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor4";
- };
-
- sensor_information5: qcom,sensor-information-5 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "pa_therm0";
- };
-
- sensor_information6: qcom,sensor-information-6 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "case_therm";
- };
-
- sensor_information7: qcom,sensor-information-7 {
- qcom,sensor-type = "alarm";
- qcom,sensor-name = "pm8909_tz";
- qcom,scaling-factor = <1000>;
- };
-
- sensor_information8: qcom,sensor-information-8 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "xo_therm";
- };
-
- sensor_information9: qcom,sensor-information-9 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "xo_therm_buf";
- };
- };
-
- mitigation_profile0: qcom,limit_info-0 {
- qcom,temperature-sensor = <&sensor_information3>;
- qcom,boot-frequency-mitigate;
- qcom,emergency-frequency-mitigate;
- };
-
- mitigation_profile1: qcom,limit_info-1 {
- qcom,temperature-sensor = <&sensor_information3>;
- qcom,boot-frequency-mitigate;
- qcom,hotplug-mitigation-enable;
- };
-
- mitigation_profile2: qcom,limit_info-2 {
- qcom,temperature-sensor = <&sensor_information4>;
- qcom,boot-frequency-mitigate;
- qcom,hotplug-mitigation-enable;
- };
-
qcom,ipc-spinlock@1905000 {
compatible = "qcom,ipc-spinlock-sfpb";
reg = <0x1905000 0x8000>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
index 7ee30f6..a80b4fe 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
@@ -25,6 +25,7 @@
#include "dsi-panel-lt8912-1080p-video.dtsi"
#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
#include "dsi-panel-hx83100a-800p-video.dtsi"
+#include "dsi-panel-boent51021-1200p-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -169,3 +170,11 @@
1f 1c 05 06 03 03 04 a0
1f 10 05 06 03 03 04 a0];
};
+
+&dsi_boent51021_1200p_video {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 1d 08 0a 06 03 04 a0];
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
index a0b4a22..f5e9489 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -129,15 +129,19 @@
/* Clocks */
clock-names = "core_clk", "iface_clk", "bus_clk",
- "core0_clk", "core0_bus_clk";
+ "core0_clk", "core0_bus_clk", "core1_clk",
+ "core1_bus_clk";
clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
<&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
<&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
<&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
- <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>;
+ <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_AXI_CLK>;
qcom,proxy-clock-names = "core_clk", "iface_clk",
- "bus_clk", "core0_clk", "core0_bus_clk";
- qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
+ "bus_clk", "core0_clk", "core0_bus_clk",
+ "core1_clk", "core1_bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x4 0x4>;
qcom,allowed-clock-rates = <100000000 200000000 330000000
364700000>;
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index bb98179..92e369e 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -73,7 +73,6 @@
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
-CONFIG_ARM64_SW_TTBR0_PAN=y
# CONFIG_ARM64_VHE is not set
CONFIG_RANDOMIZE_BASE=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index fb1c316..fc1eb458 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -76,7 +76,6 @@
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
-CONFIG_ARM64_SW_TTBR0_PAN=y
# CONFIG_ARM64_VHE is not set
CONFIG_RANDOMIZE_BASE=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index f5c24e2..34a3d5f 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2380,7 +2380,6 @@
struct sde_crtc *sde_crtc;
struct sde_kms *sde_kms;
unsigned long flags;
- bool frame_done = false;
bool in_clone_mode = false;
if (!work) {
@@ -2435,10 +2434,6 @@
SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event,
SDE_EVTLOG_FUNC_CASE3);
}
-
- if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
- | SDE_ENCODER_FRAME_EVENT_ERROR))
- frame_done = true;
}
if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE) {
@@ -2459,9 +2454,6 @@
SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
crtc->base.id, ktime_to_ns(fevent->ts));
- if (frame_done)
- complete_all(&sde_crtc->frame_done_comp);
-
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
@@ -3341,10 +3333,10 @@
&cstate->property_state);
}
-static int _sde_crtc_wait_for_frame_done(struct drm_crtc *crtc)
+static int _sde_crtc_flush_event_thread(struct drm_crtc *crtc)
{
struct sde_crtc *sde_crtc;
- int ret, rc = 0, i;
+ int i;
if (!crtc) {
SDE_ERROR("invalid argument\n");
@@ -3368,17 +3360,9 @@
kthread_flush_work(&sde_crtc->frame_events[i].work);
}
- ret = wait_for_completion_timeout(&sde_crtc->frame_done_comp,
- msecs_to_jiffies(SDE_FRAME_DONE_TIMEOUT));
- if (!ret) {
- SDE_ERROR("frame done completion wait timed out, ret:%d\n",
- ret);
- SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FATAL);
- rc = -ETIMEDOUT;
- }
SDE_EVT32_VERBOSE(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
- return rc;
+ return 0;
}
static int _sde_crtc_commit_kickoff_rot(struct drm_crtc *crtc,
@@ -3705,7 +3689,6 @@
struct sde_kms *sde_kms;
struct sde_crtc_state *cstate;
bool is_error, reset_req;
- int ret;
if (!crtc) {
SDE_ERROR("invalid argument\n");
@@ -3764,23 +3747,11 @@
}
sde_crtc->reset_request = reset_req;
- /* wait for frame_event_done completion */
- SDE_ATRACE_BEGIN("wait_for_frame_done_event");
- ret = _sde_crtc_wait_for_frame_done(crtc);
- SDE_ATRACE_END("wait_for_frame_done_event");
+ SDE_ATRACE_BEGIN("flush_event_thread");
+ _sde_crtc_flush_event_thread(crtc);
+ SDE_ATRACE_END("flush_event_thread");
sde_crtc_calc_fps(sde_crtc);
- if (ret) {
- SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
- crtc->base.id,
- atomic_read(&sde_crtc->frame_pending));
-
- is_error = true;
-
- /* force offline rotation mode since the commit has no pipes */
- cstate->sbuf_cfg.rot_op_mode = SDE_CTL_ROT_OP_MODE_OFFLINE;
- }
-
if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
/* acquire bandwidth and other resources */
SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
@@ -3818,7 +3789,6 @@
sde_encoder_kickoff(encoder, false);
}
- reinit_completion(&sde_crtc->frame_done_comp);
SDE_ATRACE_END("crtc_commit");
return;
}
@@ -4219,11 +4189,7 @@
if (cstate->num_ds_enabled)
sde_crtc->ds_reconfig = true;
- /* wait for frame_event_done completion */
- if (_sde_crtc_wait_for_frame_done(crtc))
- SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
- crtc->base.id,
- atomic_read(&sde_crtc->frame_pending));
+ _sde_crtc_flush_event_thread(crtc);
SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
sde_crtc->vblank_requested,
@@ -4238,6 +4204,8 @@
sde_crtc->enabled = false;
if (atomic_read(&sde_crtc->frame_pending)) {
+ SDE_ERROR("crtc%d frame_pending%d\n", crtc->base.id,
+ atomic_read(&sde_crtc->frame_pending));
SDE_EVT32(DRMID(crtc), atomic_read(&sde_crtc->frame_pending),
SDE_EVTLOG_FUNC_CASE2);
sde_core_perf_crtc_release_bw(crtc);
@@ -4731,8 +4699,6 @@
for (i = 1; i < SSPP_MAX; i++) {
if (pipe_staged[i]) {
- sde_plane_clear_multirect(pipe_staged[i]);
-
if (is_sde_plane_virtual(pipe_staged[i]->plane)) {
SDE_ERROR(
"r1 only virt plane:%d not supported\n",
@@ -4740,6 +4706,7 @@
rc = -EINVAL;
goto end;
}
+ sde_plane_clear_multirect(pipe_staged[i]);
}
}
@@ -6097,7 +6064,6 @@
mutex_init(&sde_crtc->rp_lock);
INIT_LIST_HEAD(&sde_crtc->rp_head);
- init_completion(&sde_crtc->frame_done_comp);
sde_crtc->enabled = false;
INIT_LIST_HEAD(&sde_crtc->frame_event_list);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index c02a81e..99177b1 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -190,7 +190,6 @@
* @frame_events : static allocation of in-flight frame events
* @frame_event_list : available frame event list
* @spin_lock : spin lock for frame event, transaction status, etc...
- * @frame_done_comp : for frame_event_done synchronization
* @event_thread : Pointer to event handler thread
* @event_worker : Event worker queue
* @event_cache : Local cache of event worker structures
@@ -262,7 +261,6 @@
struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
struct list_head frame_event_list;
spinlock_t spin_lock;
- struct completion frame_done_comp;
/* for handling internal event thread */
struct sde_crtc_event event_cache[SDE_CRTC_MAX_EVENT_COUNT];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index b54c152..f9a59b0 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1489,12 +1489,13 @@
vsync_cfg.pp_count = sde_enc->num_phys_encs;
vsync_cfg.frame_rate = mode_info.frame_rate;
+ vsync_cfg.vsync_source =
+ sde_enc->cur_master->hw_pp->caps->te_source;
if (is_dummy)
vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_1;
else if (disp_info->is_te_using_watchdog_timer)
vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0;
- else
- vsync_cfg.vsync_source = SDE_VSYNC0_SOURCE_GPIO;
+
vsync_cfg.is_dummy = is_dummy;
hw_mdptop->ops.setup_vsync_source(hw_mdptop, &vsync_cfg);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 475f8d0..baec526 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -249,6 +249,7 @@
DITHER_OFF,
DITHER_LEN,
DITHER_VER,
+ TE_SOURCE,
PP_PROP_MAX,
};
@@ -589,6 +590,7 @@
{DITHER_OFF, "qcom,sde-dither-off", false, PROP_TYPE_U32_ARRAY},
{DITHER_LEN, "qcom,sde-dither-size", false, PROP_TYPE_U32},
{DITHER_VER, "qcom,sde-dither-version", false, PROP_TYPE_U32},
+ {TE_SOURCE, "qcom,sde-te-source", false, PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type dsc_prop[] = {
@@ -2628,6 +2630,10 @@
snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u",
pp->id - PINGPONG_0);
pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0);
+ pp->te_source = PROP_VALUE_ACCESS(prop_value, TE_SOURCE, i);
+ if (!prop_exists[TE_SOURCE] ||
+ pp->te_source > SDE_VSYNC_SOURCE_WD_TIMER_0)
+ pp->te_source = SDE_VSYNC0_SOURCE_GPIO;
sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i);
sblk->te.id = SDE_PINGPONG_TE;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 65919e9..52bdc78 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -653,6 +653,7 @@
*/
struct sde_pingpong_cfg {
SDE_HW_BLK_INFO;
+ u32 te_source;
const struct sde_pingpong_sub_blks *sblk;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 2e46599..dc55ad5 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -3449,6 +3449,25 @@
src_w, src_h);
return -EINVAL;
}
+
+ /*
+ * SSPP fetch , unpack output and QSEED3 input lines need
+ * to match for Y plane
+ */
+ if (i == 0 &&
+ (sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG) &
+ BIT(SDE_DRM_DEINTERLACE)) &&
+ ((pstate->scaler3_cfg.src_height[i] != (src_h/2)) ||
+ (pstate->pixel_ext.roi_h[i] != (src_h/2)))) {
+ SDE_ERROR_PLANE(psde,
+ "de-interlace fail roi[%d] %d/%d, src %dx%d, src %dx%d\n",
+ i, pstate->pixel_ext.roi_w[i],
+ pstate->pixel_ext.roi_h[i],
+ pstate->scaler3_cfg.src_width[i],
+ pstate->scaler3_cfg.src_height[i],
+ src_w, src_h);
+ return -EINVAL;
+ }
}
pstate->scaler_check_state = SDE_PLANE_SCLCHECK_SCALER_V2;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 40c9862..d77ea21 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2361,6 +2361,17 @@
return 0;
}
+static bool is_heic_encode_session(struct msm_vidc_inst *inst)
+{
+ if (inst->session_type == MSM_VIDC_ENCODER &&
+ (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) ==
+ HAL_VIDEO_CODEC_HEVC) &&
+ (inst->img_grid_dimension > 0))
+ return true;
+ else
+ return false;
+}
+
static bool is_eos_buffer(struct msm_vidc_inst *inst, u32 device_addr)
{
struct eos_buf *temp, *next;
@@ -2406,10 +2417,7 @@
}
empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
- if (inst->session_type == MSM_VIDC_ENCODER &&
- (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) ==
- HAL_VIDEO_CODEC_HEVC) &&
- (inst->img_grid_dimension > 0) &&
+ if (is_heic_encode_session(inst) &&
(empty_buf_done->input_tag < inst->tinfo.count - 1)) {
dprintk(VIDC_DBG, "Wait for last tile. Current tile no: %d\n",
empty_buf_done->input_tag);
@@ -3154,10 +3162,11 @@
static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
{
struct msm_vidc_inst *temp;
+ int op_rate = 0;
dprintk(VIDC_ERR, "Running instances:\n");
- dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s\n",
- "type", "w", "h", "fps", "prop");
+ dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%6s|%4s\n",
+ "type", "w", "h", "fps", "opr", "prop");
mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
@@ -3171,13 +3180,21 @@
if (msm_comm_turbo_session(temp))
strlcat(properties, "T", sizeof(properties));
- dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4s\n",
+ if (is_realtime_session(temp))
+ strlcat(properties, "R", sizeof(properties));
+
+ if (temp->clk_data.operating_rate)
+ op_rate = temp->clk_data.operating_rate >> 16;
+ else
+ op_rate = temp->prop.fps;
+
+ dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%6d|%4s\n",
temp->session_type,
max(temp->prop.width[CAPTURE_PORT],
temp->prop.width[OUTPUT_PORT]),
max(temp->prop.height[CAPTURE_PORT],
temp->prop.height[OUTPUT_PORT]),
- temp->prop.fps, properties);
+ temp->prop.fps, op_rate, properties);
}
}
mutex_unlock(&core->lock);
@@ -4425,10 +4442,7 @@
for (c = 0; c < etbs.count; ++c) {
struct vidc_frame_data *frame_data = &etbs.data[c];
- if (inst->session_type == MSM_VIDC_ENCODER &&
- get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc)
- == HAL_VIDEO_CODEC_HEVC &&
- (inst->img_grid_dimension > 0)) {
+ if (is_heic_encode_session(inst)) {
rc = msm_comm_qbuf_heic_tiles(inst, frame_data);
if (rc) {
dprintk(VIDC_ERR,
@@ -5506,6 +5520,11 @@
u32 input_height, input_width, output_height, output_width;
u32 rotation;
+ if (is_heic_encode_session(inst)) {
+ dprintk(VIDC_DBG, "Skip downscale check for HEIC\n");
+ return 0;
+ }
+
input_height = inst->prop.height[OUTPUT_PORT];
input_width = inst->prop.width[OUTPUT_PORT];
output_height = inst->prop.height[CAPTURE_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 530fe3a..1dc6723 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -25,6 +25,7 @@
enum clock_properties {
CLOCK_PROP_HAS_SCALING = 1 << 0,
CLOCK_PROP_HAS_MEM_RETENTION = 1 << 1,
+ CLOCK_PROP_DISABLE_MEMCORE_ONLY = 1 << 2,
};
#define PERF_GOV "performance"
@@ -666,6 +667,11 @@
else
vc->has_mem_retention = false;
+ if (clock_props[c] & CLOCK_PROP_DISABLE_MEMCORE_ONLY)
+ vc->disable_memcore_only = true;
+ else
+ vc->disable_memcore_only = false;
+
dprintk(VIDC_DBG, "Found clock %s: scale-able = %s\n", vc->name,
vc->count ? "yes" : "no");
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 23e33fe..06d8fa5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -1,4 +1,4 @@
-/* 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
@@ -90,6 +90,7 @@
u32 count;
bool has_scaling;
bool has_mem_retention;
+ bool disable_memcore_only;
};
struct clock_set {
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index ade04e3..d2e56f3 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1097,6 +1097,8 @@
int rc = 0;
venus_hfi_for_each_clock(device, cl) {
+ if (cl->disable_memcore_only)
+ continue;
if (cl->has_scaling) {/* has_scaling */
device->clk_freq = freq;
rc = clk_set_rate(cl->clk, freq);
@@ -3362,6 +3364,8 @@
}
venus_hfi_for_each_clock_reverse(device, cl) {
+ if (cl->disable_memcore_only)
+ continue;
dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n",
cl->name);
rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
@@ -3391,6 +3395,11 @@
}
venus_hfi_for_each_clock(device, cl) {
+ /* MEM CORE is ON by default. Unset it for unused clocks*/
+ if (cl->disable_memcore_only) {
+ clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM);
+ continue;
+ }
/*
* For the clocks we control, set the rate prior to preparing
* them. Since we don't really have a load at this point, scale
@@ -3428,6 +3437,8 @@
fail_clk_enable:
venus_hfi_for_each_clock_reverse_continue(device, cl, c) {
+ if (cl->disable_memcore_only)
+ continue;
dprintk(VIDC_ERR, "Clock: %s disable and unprepare\n",
cl->name);
clk_disable_unprepare(cl->clk);
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 3ab93fe..cd76a09 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -335,6 +335,9 @@
POWER_SUPPLY_ATTR(battery_info),
POWER_SUPPLY_ATTR(battery_info_id),
POWER_SUPPLY_ATTR(enable_jeita_detection),
+ POWER_SUPPLY_ATTR(esr_actual),
+ POWER_SUPPLY_ATTR(esr_nominal),
+ POWER_SUPPLY_ATTR(soh),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index e834b8e..f21b2a8 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -60,6 +60,7 @@
bool cl_feedback_on;
bool esr_disable;
bool esr_discharge_enable;
+ bool qg_ext_sense;
};
struct qg_esr_data {
@@ -120,6 +121,10 @@
int charge_type;
int chg_iterm_ma;
int next_wakeup_ms;
+ int esr_actual;
+ int esr_nominal;
+ int soh;
+ int soc_reporting_ready;
u32 fifo_done_count;
u32 wa_flags;
u32 seq_no;
@@ -151,11 +156,18 @@
struct ttf *ttf;
};
+struct ocv_all {
+ u32 ocv_uv;
+ u32 ocv_raw;
+ char ocv_type[20];
+};
+
enum ocv_type {
S7_PON_OCV,
S3_GOOD_OCV,
S3_LAST_OCV,
SDAM_PON_OCV,
+ PON_OCV_MAX,
};
enum debug_mask {
diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h
index 88572ca..e0f400d 100644
--- a/drivers/power/supply/qcom/qg-reg.h
+++ b/drivers/power/supply/qcom/qg-reg.h
@@ -17,6 +17,7 @@
#define QG_TYPE 0x0D
#define QG_STATUS1_REG 0x08
+#define QG_OK_BIT BIT(7)
#define BATTERY_PRESENT_BIT BIT(0)
#define ESR_MEAS_DONE_BIT BIT(4)
@@ -32,6 +33,7 @@
#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 BATTERY_MISSING_INT_RT_STS_BIT BIT(0)
#define QG_INT_LATCHED_STS_REG 0x18
#define FIFO_UPDATE_DONE_INT_LAT_STS_BIT BIT(3)
@@ -64,6 +66,12 @@
#define QG_S3_ENTRY_IBAT_THRESHOLD_REG 0x5E
#define QG_S3_EXIT_IBAT_THRESHOLD_REG 0x5F
+#define QG_S5_OCV_VALIDATE_MEAS_CTL1_REG 0x60
+#define ALLOW_S5_BIT BIT(7)
+
+#define QG_S7_PON_OCV_MEAS_CTL1_REG 0x64
+#define ADC_CONV_DLY_MASK GENMASK(3, 0)
+
#define QG_ESR_MEAS_TRIG_REG 0x68
#define HW_ESR_MEAS_START_BIT BIT(0)
@@ -105,6 +113,7 @@
#define QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET 0x6E /* 4-byte 0x6E-0x71 */
#define QG_SDAM_ESR_CHARGE_SF_OFFSET 0x72 /* 2-byte 0x72-0x73 */
#define QG_SDAM_ESR_DISCHARGE_SF_OFFSET 0x74 /* 2-byte 0x74-0x75 */
+#define QG_SDAM_MAX_OFFSET 0xA4
/* Below offset is used by PBS */
#define QG_SDAM_PON_OCV_OFFSET 0xBC /* 2-byte 0xBC-0xBD */
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index a8a7826..99e0f33 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -25,10 +25,12 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/pmic-voter.h>
#include <linux/qpnp/qpnp-adc.h>
#include <uapi/linux/qg.h>
+#include <uapi/linux/qg-profile.h>
#include "fg-alg.h"
#include "qg-sdam.h"
#include "qg-core.h"
@@ -685,6 +687,7 @@
rc = qg_sdam_read(SDAM_ESR_CHARGE_SF, &data);
if (!rc && data) {
+ data = CAP(QG_ESR_SF_MIN, QG_ESR_SF_MAX, data);
chip->kdata.param[QG_ESR_CHARGE_SF].data = data;
chip->kdata.param[QG_ESR_CHARGE_SF].valid = true;
qg_dbg(chip, QG_DEBUG_ESR,
@@ -695,6 +698,7 @@
rc = qg_sdam_read(SDAM_ESR_DISCHARGE_SF, &data);
if (!rc && data) {
+ data = CAP(QG_ESR_SF_MIN, QG_ESR_SF_MAX, data);
chip->kdata.param[QG_ESR_DISCHARGE_SF].data = data;
chip->kdata.param[QG_ESR_DISCHARGE_SF].valid = true;
qg_dbg(chip, QG_DEBUG_ESR,
@@ -1002,6 +1006,7 @@
qg_dbg(chip, QG_DEBUG_ESR, "ESR_SW=%d during %s\n",
chip->esr_avg, is_charging ? "CHARGE" : "DISCHARGE");
qg_retrieve_esr_params(chip);
+ chip->esr_actual = chip->esr_avg;
}
return 0;
@@ -1052,23 +1057,22 @@
if (chip->udata.param[QG_ESR].valid)
chip->esr_last = chip->udata.param[QG_ESR].data;
+ if (chip->esr_actual != -EINVAL && chip->udata.param[QG_ESR].valid) {
+ chip->esr_nominal = chip->udata.param[QG_ESR].data;
+ if (chip->qg_psy)
+ power_supply_changed(chip->qg_psy);
+ }
+
if (!chip->dt.esr_disable)
qg_store_esr_params(chip);
qg_dbg(chip, QG_DEBUG_STATUS, "udata update: batt_soc=%d cc_soc=%d full_soc=%d qg_esr=%d\n",
- chip->batt_soc, chip->cc_soc, chip->full_soc, chip->esr_last);
+ (chip->batt_soc != INT_MIN) ? chip->batt_soc : -EINVAL,
+ (chip->cc_soc != INT_MIN) ? chip->cc_soc : -EINVAL,
+ chip->full_soc, chip->esr_last);
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)
{
@@ -1154,6 +1158,11 @@
pr_err("Failed to read RT status, rc=%d\n", rc);
goto done;
}
+ /* ignore VBAT low if battery is missing */
+ if ((status & BATTERY_MISSING_INT_RT_STS_BIT) ||
+ chip->battery_missing)
+ goto done;
+
chip->vbat_low = !!(status & VBAT_LOW_INT_RT_STS_BIT);
rc = process_rt_fifo_data(chip, chip->vbat_low, false);
@@ -1170,8 +1179,20 @@
{
struct qpnp_qg *chip = data;
u32 ocv_uv = 0;
+ int rc;
+ u8 status = 0;
qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+
+ 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);
+
+ /* ignore VBAT empty if battery is missing */
+ if ((status & BATTERY_MISSING_INT_RT_STS_BIT) ||
+ chip->battery_missing)
+ return IRQ_HANDLED;
+
pr_warn("VBATT EMPTY SOC = 0\n");
chip->catch_up_soc = 0;
@@ -1232,7 +1253,6 @@
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",
@@ -1256,11 +1276,9 @@
},
[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,
},
};
@@ -1691,6 +1709,17 @@
chip->cl->learned_cap_uah = pval->intval;
mutex_unlock(&chip->cl->lock);
break;
+ case POWER_SUPPLY_PROP_SOH:
+ chip->soh = pval->intval;
+ qg_dbg(chip, QG_DEBUG_STATUS, "SOH update: SOH=%d esr_actual=%d esr_nominal=%d\n",
+ chip->soh, chip->esr_actual, chip->esr_nominal);
+ break;
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ chip->esr_actual = pval->intval;
+ break;
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ chip->esr_nominal = pval->intval;
+ break;
default:
break;
}
@@ -1737,6 +1766,9 @@
case POWER_SUPPLY_PROP_RESISTANCE_NOW:
pval->intval = chip->esr_last;
break;
+ case POWER_SUPPLY_PROP_SOC_REPORTING_READY:
+ pval->intval = chip->soc_reporting_ready;
+ break;
case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE:
pval->intval = chip->dt.rbat_conn_mohm;
break;
@@ -1785,6 +1817,17 @@
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
rc = ttf_get_time_to_empty(chip->ttf, &pval->intval);
break;
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ pval->intval = (chip->esr_actual == -EINVAL) ? -EINVAL :
+ (chip->esr_actual * 1000);
+ break;
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ pval->intval = (chip->esr_nominal == -EINVAL) ? -EINVAL :
+ (chip->esr_nominal * 1000);
+ break;
+ case POWER_SUPPLY_PROP_SOH:
+ pval->intval = chip->soh;
+ break;
default:
pr_debug("Unsupported property %d\n", psp);
break;
@@ -1798,6 +1841,9 @@
{
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ case POWER_SUPPLY_PROP_SOH:
return 1;
default:
break;
@@ -1815,6 +1861,7 @@
POWER_SUPPLY_PROP_RESISTANCE,
POWER_SUPPLY_PROP_RESISTANCE_ID,
POWER_SUPPLY_PROP_RESISTANCE_NOW,
+ POWER_SUPPLY_PROP_SOC_REPORTING_READY,
POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
POWER_SUPPLY_PROP_DEBUG_BATTERY,
POWER_SUPPLY_PROP_BATTERY_TYPE,
@@ -1828,6 +1875,9 @@
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_ESR_ACTUAL,
+ POWER_SUPPLY_PROP_ESR_NOMINAL,
+ POWER_SUPPLY_PROP_SOH,
};
static const struct power_supply_desc qg_psy_desc = {
@@ -1914,6 +1964,7 @@
{
int rc;
bool parallel_enabled = is_parallel_enabled(chip);
+ bool update_smb = false;
if (parallel_enabled == chip->parallel_enabled)
return 0;
@@ -1924,7 +1975,14 @@
mutex_lock(&chip->data_lock);
- rc = process_rt_fifo_data(chip, false, true);
+ /*
+ * Parallel charger uses the same external sense, hence do not
+ * enable SMB sensing if PMI632 is configured for external sense.
+ */
+ if (!chip->dt.qg_ext_sense)
+ update_smb = true;
+
+ rc = process_rt_fifo_data(chip, false, update_smb);
if (rc < 0)
pr_err("Failed to process RT FIFO data, rc=%d\n", rc);
@@ -1949,6 +2007,110 @@
return 0;
}
+static int qg_handle_battery_removal(struct qpnp_qg *chip)
+{
+ int rc, length = QG_SDAM_MAX_OFFSET - QG_SDAM_VALID_OFFSET;
+ u8 *data;
+
+ /* clear SDAM */
+ data = kcalloc(length, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ rc = qg_sdam_multibyte_write(QG_SDAM_VALID_OFFSET, data, length);
+ if (rc < 0)
+ pr_err("Failed to clear SDAM rc=%d\n", rc);
+
+ return rc;
+}
+
+#define MAX_QG_OK_RETRIES 20
+static int qg_handle_battery_insertion(struct qpnp_qg *chip)
+{
+ int rc, count = 0;
+ u32 ocv_uv = 0, ocv_raw = 0;
+ u8 reg = 0;
+
+ do {
+ rc = qg_read(chip, chip->qg_base + QG_STATUS1_REG, ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+
+ if (reg & QG_OK_BIT)
+ break;
+
+ msleep(200);
+ count++;
+ } while (count < MAX_QG_OK_RETRIES);
+
+ if (count == MAX_QG_OK_RETRIES) {
+ qg_dbg(chip, QG_DEBUG_STATUS, "QG_OK not set!\n");
+ return 0;
+ }
+
+ /* read S7 PON OCV */
+ rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, S7_PON_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read PON OCV rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "S7_OCV on battery insertion = %duV\n", ocv_uv);
+
+ chip->kdata.param[QG_GOOD_OCV_UV].data = ocv_uv;
+ chip->kdata.param[QG_GOOD_OCV_UV].valid = true;
+ /* clear all the userspace data */
+ chip->kdata.param[QG_CLEAR_LEARNT_DATA].data = 1;
+ chip->kdata.param[QG_CLEAR_LEARNT_DATA].valid = true;
+
+ vote(chip->awake_votable, GOOD_OCV_VOTER, true, 0);
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+
+ return 0;
+}
+
+static int qg_battery_status_update(struct qpnp_qg *chip)
+{
+ int rc;
+ union power_supply_propval prop = {0, };
+
+ if (!is_batt_available(chip))
+ return 0;
+
+ mutex_lock(&chip->data_lock);
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &prop);
+ if (rc < 0) {
+ pr_err("Failed to get battery-present, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (chip->battery_missing && prop.intval) {
+ pr_warn("Battery inserted!\n");
+ rc = qg_handle_battery_insertion(chip);
+ if (rc < 0)
+ pr_err("Failed in battery-insertion rc=%d\n", rc);
+ } else if (!chip->battery_missing && !prop.intval) {
+ pr_warn("Battery removed!\n");
+ rc = qg_handle_battery_removal(chip);
+ if (rc < 0)
+ pr_err("Failed in battery-removal rc=%d\n", rc);
+ }
+
+ chip->battery_missing = !prop.intval;
+
+done:
+ mutex_unlock(&chip->data_lock);
+ return rc;
+}
+
+
static void qg_status_change_work(struct work_struct *work)
{
struct qpnp_qg *chip = container_of(work,
@@ -1961,6 +2123,10 @@
goto out;
}
+ rc = qg_battery_status_update(chip);
+ if (rc < 0)
+ pr_err("Failed to process battery status update rc=%d\n", rc);
+
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
if (rc < 0)
@@ -2380,12 +2546,21 @@
return 0;
}
+
+static struct ocv_all ocv[] = {
+ [S7_PON_OCV] = { 0, 0, "S7_PON_OCV"},
+ [S3_GOOD_OCV] = { 0, 0, "S3_GOOD_OCV"},
+ [S3_LAST_OCV] = { 0, 0, "S3_LAST_OCV"},
+ [SDAM_PON_OCV] = { 0, 0, "SDAM_PON_OCV"},
+};
+
+#define S7_ERROR_MARGIN_UV 20000
static int qg_determine_pon_soc(struct qpnp_qg *chip)
{
- int rc = 0, batt_temp = 0;
+ int rc = 0, batt_temp = 0, i;
bool use_pon_ocv = true;
unsigned long rtc_sec = 0;
- u32 ocv_uv = 0, ocv_raw = 0, soc = 0, shutdown[SDAM_MAX] = {0};
+ u32 ocv_uv = 0, soc = 0, shutdown[SDAM_MAX] = {0};
char ocv_type[20] = "NONE";
if (!chip->profile_loaded) {
@@ -2434,33 +2609,47 @@
goto done;
}
- /*
- * Read S3_LAST_OCV, if S3_LAST_OCV is invalid,
- * read the SDAM_PON_OCV
- * if SDAM is not-set, use S7_PON_OCV.
- */
- strlcpy(ocv_type, "S3_LAST_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, S3_LAST_OCV);
- if (rc < 0)
- goto done;
-
- if (ocv_raw == FIFO_V_RESET_VAL) {
- /* S3_LAST_OCV is invalid */
- strlcpy(ocv_type, "SDAM_PON_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, SDAM_PON_OCV);
+ /* read all OCVs */
+ for (i = S7_PON_OCV; i < PON_OCV_MAX; i++) {
+ rc = qg_read_ocv(chip, &ocv[i].ocv_uv,
+ &ocv[i].ocv_raw, i);
if (rc < 0)
- goto done;
+ pr_err("Failed to read %s OCV rc=%d\n",
+ ocv[i].ocv_type, rc);
+ else
+ qg_dbg(chip, QG_DEBUG_PON, "%s OCV=%d\n",
+ ocv[i].ocv_type, ocv[i].ocv_uv);
+ }
- if (!ocv_uv) {
- /* SDAM_PON_OCV is not set */
+ if (ocv[S3_LAST_OCV].ocv_raw == FIFO_V_RESET_VAL) {
+ if (!ocv[SDAM_PON_OCV].ocv_uv) {
strlcpy(ocv_type, "S7_PON_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw,
- S7_PON_OCV);
- if (rc < 0)
- goto done;
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else if (ocv[SDAM_PON_OCV].ocv_uv <=
+ ocv[S7_PON_OCV].ocv_uv) {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else if (!shutdown[SDAM_VALID] &&
+ ((ocv[SDAM_PON_OCV].ocv_uv -
+ ocv[S7_PON_OCV].ocv_uv) >
+ S7_ERROR_MARGIN_UV)) {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else {
+ strlcpy(ocv_type, "SDAM_PON_SOC", 20);
+ ocv_uv = ocv[SDAM_PON_OCV].ocv_uv;
+ }
+ } else {
+ if (ocv[S3_LAST_OCV].ocv_uv >= ocv[S7_PON_OCV].ocv_uv) {
+ strlcpy(ocv_type, "S3_LAST_SOC", 20);
+ ocv_uv = ocv[S3_LAST_OCV].ocv_uv;
+ } else {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
}
}
+ ocv_uv = CAP(QG_MIN_OCV_UV, QG_MAX_OCV_UV, ocv_uv);
rc = lookup_soc_ocv(&soc, ocv_uv, batt_temp, false);
if (rc < 0) {
pr_err("Failed to lookup SOC@PON rc=%d\n", rc);
@@ -2493,6 +2682,9 @@
pr_info("using %s @ PON ocv_uv=%duV soc=%d\n",
ocv_type, ocv_uv, chip->msoc);
+ /* SOC reporting is now ready */
+ chip->soc_reporting_ready = 1;
+
return 0;
}
@@ -2515,6 +2707,7 @@
return 0;
}
+#define ADC_CONV_DLY_512MS 0xA
static int qg_hw_init(struct qpnp_qg *chip)
{
int rc, temp;
@@ -2695,6 +2888,22 @@
return rc;
}
+ /* disable S5 */
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_S5_OCV_VALIDATE_MEAS_CTL1_REG,
+ ALLOW_S5_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to disable S5 rc=%d\n", rc);
+
+ /* change PON OCV time to 512ms */
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_S7_PON_OCV_MEAS_CTL1_REG,
+ ADC_CONV_DLY_MASK,
+ ADC_CONV_DLY_512MS);
+ if (rc < 0)
+ pr_err("Failed to reconfigure S7-delay rc=%d\n", rc);
+
+
return 0;
}
@@ -3117,6 +3326,8 @@
else
chip->dt.esr_disable_soc = temp * 100;
+ chip->dt.qg_ext_sense = of_property_read_bool(node, "qcom,qg-ext-sns");
+
/* Capacity learning params*/
if (!chip->dt.cl_disable) {
chip->dt.cl_feedback_on = of_property_read_bool(node,
@@ -3176,9 +3387,9 @@
chip->cl->dt.min_start_soc, chip->cl->dt.max_start_soc,
chip->cl->dt.min_temp, chip->cl->dt.max_temp);
}
- qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d\n",
+ qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d ext-sns=%d\n",
chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv,
- chip->dt.delta_soc);
+ chip->dt.delta_soc, chip->dt.qg_ext_sense);
return 0;
}
@@ -3407,6 +3618,9 @@
chip->cc_soc = INT_MIN;
chip->full_soc = QG_SOC_FULL;
chip->chg_iterm_ma = INT_MIN;
+ chip->soh = -EINVAL;
+ chip->esr_actual = -EINVAL;
+ chip->esr_nominal = -EINVAL;
rc = qg_alg_init(chip);
if (rc < 0) {
diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig
index e8f902b..a5e0662 100644
--- a/drivers/video/fbdev/msm/Kconfig
+++ b/drivers/video/fbdev/msm/Kconfig
@@ -82,6 +82,16 @@
MHL (Mobile High-Definition Link) technology
uses USB connector to output HDMI content
+config FB_MSM_MDSS_SPI_PANEL
+ depends on FB_MSM_MDSS
+ bool "Support SPI panel feature"
+ default n
+ ---help---
+ The MDSS SPI Panel provides support for transmittimg SPI signals of
+ MDSS frame buffer data to connected panel. Limited by SPI rate, the
+ current max fps only reach to 27 fps, and limited by MDP hardware
+ architecture only supply on MDP3
+
config FB_MSM_MDSS_MHL3
depends on FB_MSM_MDSS_HDMI_PANEL
bool "MHL3 SII8620 Support"
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index 81d4953..26a940c 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -46,6 +46,11 @@
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
+ifeq ($(CONFIG_SPI_QUP), y)
+obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_client.o
+obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_panel.o
+endif
+
ifneq ($(CONFIG_FB_MSM_MDSS_MDP3), y)
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o
diff --git a/drivers/video/fbdev/msm/dsi_status_v2.c b/drivers/video/fbdev/msm/dsi_status_v2.c
index 35b0984..87720cf 100644
--- a/drivers/video/fbdev/msm/dsi_status_v2.c
+++ b/drivers/video/fbdev/msm/dsi_status_v2.c
@@ -18,6 +18,7 @@
#include "mdss_dsi.h"
#include "mdp3_ctrl.h"
+#include "mdss_spi_panel.h"
/*
* mdp3_check_te_status() - Check the status of panel for TE based ESD.
@@ -165,3 +166,79 @@
mdss_fb_report_panel_dead(pdsi_status->mfd);
}
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL)
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval)
+{
+ struct dsi_status_data *pdsi_status = NULL;
+ struct mdss_panel_data *pdata = NULL;
+ struct spi_panel_data *ctrl_pdata = NULL;
+ struct mdp3_session_data *mdp3_session = NULL;
+ int ret = 0;
+
+ pdsi_status = container_of(to_delayed_work(work),
+ struct dsi_status_data, check_status);
+
+ if (!pdsi_status || !(pdsi_status->mfd)) {
+ pr_err("%s: mfd not available\n", __func__);
+ return;
+ }
+
+ pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev);
+ if (!pdata) {
+ pr_err("%s: panel data not available\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data, panel_data);
+ if (!ctrl_pdata || !ctrl_pdata->check_status) {
+ pr_err("%s: Dsi ctrl or status_check callback not available\n",
+ __func__);
+ return;
+ }
+
+ mdp3_session = pdsi_status->mfd->mdp.private1;
+ if (!mdp3_session) {
+ pr_err("%s: Display is off\n", __func__);
+ return;
+ }
+
+ if (mdp3_session->in_splash_screen) {
+ schedule_delayed_work(&pdsi_status->check_status,
+ msecs_to_jiffies(interval));
+ pr_debug("%s: cont splash is on\n", __func__);
+ return;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+ if (!mdp3_session->status) {
+ pr_debug("%s: display off already\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+ return;
+ }
+
+ if (!ret)
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ else
+ pr_err("%s:wait_for_dma_done error\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+
+ if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) {
+ if (ret > 0) {
+ schedule_delayed_work(&pdsi_status->check_status,
+ msecs_to_jiffies(interval));
+ } else {
+ char *envp[2] = {"PANEL_ALIVE=0", NULL};
+
+ pdata->panel_info.panel_dead = true;
+ ret = kobject_uevent_env(
+ &pdsi_status->mfd->fbi->dev->kobj, KOBJ_CHANGE, envp);
+ pr_err("%s:panel has gone bad, sending uevent - %s\n",
+ __func__, envp[0]);
+ }
+ }
+}
+#else
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval)
+{
+}
+#endif
diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c
index c9db88e..54e9919 100644
--- a/drivers/video/fbdev/msm/mdp3.c
+++ b/drivers/video/fbdev/msm/mdp3.c
@@ -56,6 +56,7 @@
#include "mdss_debug.h"
#include "mdss_smmu.h"
#include "mdss.h"
+#include "mdss_spi_panel.h"
#ifndef EXPORT_COMPAT
#define EXPORT_COMPAT(x)
@@ -109,6 +110,7 @@
static struct mdss_panel_intf pan_types[] = {
{"dsi", MDSS_PANEL_INTF_DSI},
+ {"spi", MDSS_PANEL_INTF_SPI},
};
static char mdss_mdp3_panel[MDSS_MAX_PANEL_LEN];
@@ -127,6 +129,13 @@
},
};
+#ifndef CONFIG_FB_MSM_MDSS_SPI_PANEL
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level)
+{
+
+}
+#endif
+
static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
{
int i = 0;
@@ -1350,7 +1359,11 @@
client == MDP3_CLIENT_DMA_P)
mdss_smmu_unmap_dma_buf(data->tab_clone,
dom, dir, data->srcp_dma_buf);
- else
+ else if (client == MDP3_CLIENT_SPI) {
+ ion_unmap_kernel(iclient, data->srcp_ihdl);
+ ion_free(iclient, data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ } else
mdss_smmu_unmap_dma_buf(data->srcp_table,
dom, dir, data->srcp_dma_buf);
data->mapped = false;
@@ -1416,7 +1429,13 @@
data->srcp_dma_buf = NULL;
return ret;
}
-
+ data->srcp_ihdl = ion_import_dma_buf(iclient,
+ data->srcp_dma_buf);
+ if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
+ pr_err("error on ion_import_fd\n");
+ data->srcp_ihdl = NULL;
+ return -EIO;
+ }
data->srcp_attachment =
mdss_smmu_dma_buf_attach(data->srcp_dma_buf,
&mdp3_res->pdev->dev, dom);
@@ -1449,6 +1468,25 @@
data->tab_clone, dom,
&data->addr, &data->len,
DMA_BIDIRECTIONAL);
+ } else if (client == MDP3_CLIENT_SPI) {
+ void *vaddr;
+
+ if (ion_handle_get_size(iclient,
+ data->srcp_ihdl,
+ (size_t *)&data->len) < 0) {
+ pr_err("get size failed\n");
+ return -EINVAL;
+ }
+ vaddr = ion_map_kernel(iclient,
+ data->srcp_ihdl);
+ if (IS_ERR_OR_NULL(vaddr)) {
+ pr_err("Mapping failed\n");
+ mdp3_put_img(data, client);
+ return -EINVAL;
+ }
+ data->addr = (dma_addr_t) vaddr;
+ data->len -= img->offset;
+ return 0;
} else {
ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
data->srcp_table, dom, &data->addr,
@@ -1741,6 +1779,8 @@
if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
status = MDP3_REG_READ(MDP3_REG_DSI_VIDEO_EN);
rc = status & 0x1;
+ } else if (pdata->panel_info.type == SPI_PANEL) {
+ rc = is_spi_panel_continuous_splash_on(pdata);
} else {
status = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG);
status &= 0x180000;
@@ -1777,7 +1817,11 @@
mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate,
MDP3_CLIENT_DMA_P);
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
+ /*DMA not used on SPI interface, remove DMA bus voting*/
+ if (panel_info->type == SPI_PANEL)
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
+ else
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
bus_handle->restore_ab[MDP3_CLIENT_DMA_P] = ab;
bus_handle->restore_ib[MDP3_CLIENT_DMA_P] = ib;
@@ -1795,6 +1839,8 @@
if (panel_info->type == MIPI_VIDEO_PANEL)
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_VIDEO].active = 1;
+ else if (panel_info->type == SPI_PANEL)
+ mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_SPI_CMD].active = 1;
else
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1;
@@ -2485,6 +2531,9 @@
mdp3_res->mdss_util->mdp_probe_done = true;
pr_debug("%s: END\n", __func__);
+ if (mdp3_res->pan_cfg.pan_intf == MDSS_PANEL_INTF_SPI)
+ mdp3_interface.check_dsi_status = mdp3_check_spi_panel_status;
+
probe_done:
if (IS_ERR_VALUE(rc))
kfree(mdp3_res->mdp3_hw.irq_info);
diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h
index 7132dee..6b56052 100644
--- a/drivers/video/fbdev/msm/mdp3.h
+++ b/drivers/video/fbdev/msm/mdp3.h
@@ -84,6 +84,7 @@
MDP3_CLIENT_DSI = 1,
MDP3_CLIENT_PPP,
MDP3_CLIENT_IOMMU,
+ MDP3_CLIENT_SPI,
MDP3_CLIENT_MAX,
};
@@ -208,6 +209,8 @@
bool solid_fill_vote_en;
struct list_head reg_bus_clist;
struct mutex reg_bus_lock;
+ int bklt_level;
+ int bklt_update;
bool twm_en;
u32 max_bw;
@@ -267,6 +270,7 @@
void mdp3_enable_regulator(int enable);
void mdp3_check_dsi_ctrl_status(struct work_struct *work,
uint32_t interval);
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval);
int mdp3_dynamic_clock_gating_ctrl(int enable);
int mdp3_footswitch_ctrl(int enable);
int mdp3_qos_remapper_setup(struct mdss_panel_data *panel);
@@ -279,6 +283,8 @@
void mdp3_clear_irq(u32 interrupt_mask);
int mdp3_enable_panic_ctrl(void);
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level);
+
int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit);
int mdp3_layer_atomic_validate(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 50b232a..c976c0e 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -23,11 +23,13 @@
#include <linux/dma-buf.h>
#include <linux/pm_runtime.h>
#include <linux/iommu.h>
+#include <linux/msm_ion.h>
#include "mdp3_ctrl.h"
#include "mdp3.h"
#include "mdp3_ppp.h"
#include "mdss_smmu.h"
+#include "mdss_spi_panel.h"
#include "mdss_sync.h"
#define VSYNC_EXPIRE_TICK 4
@@ -72,7 +74,7 @@
bufq->pop_idx = 0;
}
-void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
+void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client)
{
int count = bufq->count;
@@ -83,7 +85,7 @@
struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
- mdp3_put_img(data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(data, client);
}
bufq->count = 0;
bufq->push_idx = 0;
@@ -122,6 +124,18 @@
return bufq->count;
}
+int mdp3_get_ion_client(struct msm_fb_data_type *mfd)
+{
+ int intf_type;
+
+ intf_type = mdp3_ctrl_get_intf_type(mfd);
+
+ if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD)
+ return MDP3_CLIENT_SPI;
+ else
+ return MDP3_CLIENT_DMA_P;
+}
+
void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses,
struct notifier_block *notifier)
{
@@ -308,8 +322,11 @@
struct mdp3_notification vsync_client;
struct mdp3_notification *arg = NULL;
bool mod_vsync_timer = false;
+ int intf_type;
pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
+
+ intf_type = mdp3_ctrl_get_intf_type(mfd);
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
!mdp3_session->intf)
@@ -347,18 +364,25 @@
}
}
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
- mdp3_clk_enable(0, 0);
+ if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
+ mdp3_spi_vsync_enable(mdp3_session->panel, arg);
+ } else {
+ mdp3_clk_enable(1, 0);
+ mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
+ mdp3_clk_enable(0, 0);
+ }
/*
* Need to fake vsync whenever dsi interface is not
* active or when dsi clocks are currently off
*/
- if (mod_vsync_timer) {
+ if (mod_vsync_timer && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) {
mod_timer(&mdp3_session->vsync_timer,
jiffies + msecs_to_jiffies(mdp3_session->vsync_period));
- } else if (!enable) {
+ } else if (enable && !mdp3_session->clk_on) {
+ mdp3_ctrl_reset_countdown(mdp3_session, mfd);
+ mdp3_ctrl_clk_enable(mfd, 1);
+ } else if (!enable && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) {
del_timer(&mdp3_session->vsync_timer);
}
@@ -597,14 +621,32 @@
static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status)
{
int rc = 0;
+ u32 vtotal = 0;
+ int frame_rate = DEFAULT_FRAME_RATE;
if (status) {
+ struct mdss_panel_info *panel_info = mfd->panel_info;
u64 ab = 0;
u64 ib = 0;
+ frame_rate = mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
mdp3_calc_dma_res(mfd->panel_info, NULL, &ab, &ib,
ppp_bpp(mfd->fb_imgType));
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
+ vtotal = panel_info->yres + panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width;
+ ab = panel_info->xres * vtotal * ppp_bpp(mfd->fb_imgType);
+ ab *= frame_rate;
+ ib = ab;
+
+ /*DMA not used on SPI interface, remove DMA bus voting*/
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD)
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
+ else
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P,
+ ab, ib);
} else {
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
}
@@ -653,6 +695,9 @@
case LCDC_PANEL:
type = MDP3_DMA_OUTPUT_SEL_LCDC;
break;
+ case SPI_PANEL:
+ type = MDP3_DMA_OUTPUT_SEL_SPI_CMD;
+ break;
default:
type = MDP3_DMA_OUTPUT_SEL_MAX;
}
@@ -717,7 +762,8 @@
cfg.type = mdp3_ctrl_get_intf_type(mfd);
if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ cfg.type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
video->hsync_period = hsync_period;
video->hsync_pulse_width = h_pulse_width;
video->vsync_period = vsync_period;
@@ -765,7 +811,7 @@
struct fb_var_screeninfo *var;
struct mdp3_dma_output_config outputConfig;
struct mdp3_dma_source sourceConfig;
- int frame_rate = mfd->panel_info->mipi.frame_rate;
+ int frame_rate = DEFAULT_FRAME_RATE;
int vbp, vfp, vspw;
int vtotal, vporch;
struct mdp3_notification dma_done_callback;
@@ -774,6 +820,7 @@
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
vbp = panel_info->lcdc.v_back_porch;
vfp = panel_info->lcdc.v_front_porch;
vspw = panel_info->lcdc.v_pulse_width;
@@ -814,7 +861,7 @@
sourceConfig.stride = fix->line_length;
}
- te.frame_rate = panel_info->mipi.frame_rate;
+ te.frame_rate = frame_rate;
te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode;
te.tear_check_en = panel_info->te.tear_check_en;
te.sync_cfg_height = panel_info->te.sync_cfg_height;
@@ -1003,6 +1050,7 @@
static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
{
int rc = 0;
+ int client = 0;
bool intf_stopped = true;
struct mdp3_session_data *mdp3_session;
struct mdss_panel_data *panel;
@@ -1156,10 +1204,11 @@
pr_err("%s: pm_runtime_put failed (rc %d)\n",
__func__, rc);
}
- mdp3_bufq_deinit(&mdp3_session->bufq_out);
+ client = mdp3_get_ion_client(mfd);
+ mdp3_bufq_deinit(&mdp3_session->bufq_out, client);
if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
}
}
@@ -1189,6 +1238,10 @@
mdp3_ctrl_clk_enable(mdp3_session->mfd, 0);
}
off_error:
+ if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) {
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
+ }
MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__);
mutex_unlock(&mdp3_session->lock);
/* Release the last reference to the runtime device */
@@ -1336,14 +1389,16 @@
struct fb_info *fbi = mfd->fbi;
struct fb_fix_screeninfo *fix;
int format;
+ int client;
fix = &fbi->fix;
format = mdp3_ctrl_get_source_format(mfd->fb_imgType);
mutex_lock(&mdp3_session->lock);
+ client = mdp3_get_ion_client(mfd);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
} else {
rc = -EINVAL;
}
@@ -1362,18 +1417,20 @@
struct msmfb_data *img = &req->data;
struct mdp3_img_data data;
struct mdp3_dma *dma = mdp3_session->dma;
+ int client;
+ client = mdp3_get_ion_client(mfd);
memset(&data, 0, sizeof(struct mdp3_img_data));
- if (mfd->panel.type == MIPI_CMD_PANEL)
+ if (mfd->panel.type == MIPI_CMD_PANEL || client == MDP3_CLIENT_SPI)
is_panel_type_cmd = true;
if (is_panel_type_cmd) {
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
+ rc = mdp3_iommu_enable(client);
if (rc) {
pr_err("fail to enable iommu\n");
return rc;
}
}
- rc = mdp3_get_img(img, &data, MDP3_CLIENT_DMA_P);
+ rc = mdp3_get_img(img, &data, client);
if (rc) {
pr_err("fail to get overlay buffer\n");
goto err;
@@ -1383,14 +1440,14 @@
pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n",
data.len, (dma->source_config.stride *
dma->source_config.height));
- mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(&data, client);
rc = -EINVAL;
goto err;
}
rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
if (rc) {
pr_err("fail to queue the overlay buffer, buffer drop\n");
- mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(&data, client);
goto err;
}
rc = 0;
@@ -1449,8 +1506,11 @@
struct mdp3_img_data *data;
struct mdss_panel_info *panel_info;
int rc = 0;
+ int client;
static bool splash_done;
struct mdss_panel_data *panel;
+ int frame_rate = DEFAULT_FRAME_RATE;
+ int stride;
if (!mfd || !mfd->mdp.private1)
return -EINVAL;
@@ -1460,6 +1520,9 @@
if (!mdp3_session || !mdp3_session->dma)
return -EINVAL;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
+ client = mdp3_get_ion_client(mfd);
+
if (mdp3_bufq_count(&mdp3_session->bufq_in) == 0) {
pr_debug("no buffer in queue yet\n");
return -EPERM;
@@ -1510,7 +1573,17 @@
if (data) {
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
mdp3_ctrl_clk_enable(mfd, 1);
- if (mdp3_session->dma->update_src_cfg &&
+ stride = mdp3_session->dma->source_config.stride;
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD){
+ mdp3_session->intf->active = false;
+ msm_ion_do_cache_op(mdp3_res->ion_client,
+ data->srcp_ihdl, (void *)(int)data->addr,
+ data->len, ION_IOC_INV_CACHES);
+ rc = mdss_spi_panel_kickoff(mdp3_session->panel,
+ (void *)(int)data->addr, (int)data->len,
+ stride);
+ } else if (mdp3_session->dma->update_src_cfg &&
panel_info->partial_update_enabled) {
panel->panel_info.roi.x = mdp3_session->dma->roi.x;
panel->panel_info.roi.y = mdp3_session->dma->roi.y;
@@ -1530,7 +1603,9 @@
MDP_NOTIFY_FRAME_TIMEOUT);
} else {
if (mdp3_ctrl_get_intf_type(mfd) ==
- MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
mdp3_ctrl_notify(mdp3_session,
MDP_NOTIFY_FRAME_DONE);
}
@@ -1545,16 +1620,16 @@
mdp3_release_splash_memory(mfd);
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
if (data)
- mdp3_put_img(data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(data, client);
}
if (mdp3_session->first_commit) {
/*wait to ensure frame is sent to panel*/
if (panel_info->mipi.post_init_delay)
- msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
+ msleep(((1000 / frame_rate) + 1) *
panel_info->mipi.post_init_delay);
else
- msleep(1000 / panel_info->mipi.frame_rate);
+ msleep((1000 / frame_rate) + 1);
mdp3_session->first_commit = false;
if (panel)
rc |= panel->event_handler(panel,
@@ -1569,6 +1644,12 @@
mdp3_session->esd_recovery = false;
}
+ /*Update backlight only if its changed*/
+ if (mdp3_res->bklt_level && mdp3_res->bklt_update) {
+ mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level);
+ mdp3_res->bklt_update = false;
+ }
+
/* start vsync tick countdown for cmd mode if vsync isn't enabled */
if (mfd->panel.type == MIPI_CMD_PANEL && !mdp3_session->vsync_enabled)
mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0);
@@ -1695,17 +1776,18 @@
}
panel = mdp3_session->panel;
- if (mdp3_session->first_commit) {
- /*wait to ensure frame is sent to panel*/
- if (panel_info->mipi.post_init_delay)
- msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
- panel_info->mipi.post_init_delay);
- else
- msleep(1000 / panel_info->mipi.frame_rate);
- mdp3_session->first_commit = false;
- if (panel)
- panel->event_handler(panel, MDSS_EVENT_POST_PANEL_ON,
- NULL);
+ if (mdp3_ctrl_get_intf_type(mfd) != MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
+ if (mdp3_session->first_commit) {
+ if (panel_info->mipi.init_delay)
+ msleep(((1000 / panel_info->mipi.frame_rate)
+ + 1) * panel_info->mipi.post_init_delay);
+ else
+ msleep(1000 / panel_info->mipi.frame_rate);
+ mdp3_session->first_commit = false;
+ if (panel)
+ panel->event_handler(panel,
+ MDSS_EVENT_POST_PANEL_ON, NULL);
+ }
}
mdp3_session->vsync_before_commit = 0;
@@ -1716,6 +1798,11 @@
mdp3_session->esd_recovery = false;
}
+ /*Update backlight only if its changed*/
+ if (mdp3_res->bklt_level && mdp3_res->bklt_update) {
+ mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level);
+ mdp3_res->bklt_update = false;
+ }
pan_error:
mutex_unlock(&mdp3_session->lock);
@@ -1756,7 +1843,8 @@
switch (metadata->op) {
case metadata_op_frame_rate:
metadata->data.panel_frame_rate =
- mfd->panel_info->mipi.frame_rate;
+ mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
break;
case metadata_op_get_caps:
metadata->data.caps.mdp_rev = 305;
@@ -2893,6 +2981,7 @@
struct msm_mdp_interface *mdp3_interface = &mfd->mdp;
struct mdp3_session_data *mdp3_session = NULL;
u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
+ int frame_rate = DEFAULT_FRAME_RATE;
int rc;
int splash_mismatch = 0;
struct sched_param sched = { .sched_priority = 16 };
@@ -2902,6 +2991,8 @@
if (rc)
splash_mismatch = 1;
+ frame_rate = mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
mdp3_interface->on_fnc = mdp3_ctrl_on;
mdp3_interface->off_fnc = mdp3_ctrl_off;
mdp3_interface->do_histogram = NULL;
@@ -2980,10 +3071,11 @@
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
mdp3_session->vsync_timer.data = (u32)mdp3_session;
- mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
+ mdp3_session->vsync_period = 1000 / frame_rate;
mfd->mdp.private1 = mdp3_session;
init_completion(&mdp3_session->dma_completion);
- if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)
mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.h b/drivers/video/fbdev/msm/mdp3_ctrl.h
index b7b667b..5193af1 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.h
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.h
@@ -84,12 +84,13 @@
struct work_struct retire_work;
};
-void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq);
+void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client);
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
int mdp3_bufq_push(struct mdp3_buffer_queue *bufq,
struct mdp3_img_data *data);
int mdp3_ctrl_get_source_format(u32 imgType);
int mdp3_ctrl_get_pack_pattern(u32 imgType);
int mdp3_ctrl_reset(struct msm_fb_data_type *mfd);
+int mdp3_get_ion_client(struct msm_fb_data_type *mfd);
#endif /* MDP3_CTRL_H */
diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c
index 71fcbf9..b223c87 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.c
+++ b/drivers/video/fbdev/msm/mdp3_dma.c
@@ -114,7 +114,8 @@
}
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
mdp3_irq_enable(MDP3_INTR_LCDC_START_OF_FRAME);
} else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
@@ -150,7 +151,8 @@
}
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
mdp3_irq_disable(MDP3_INTR_LCDC_START_OF_FRAME);
} else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
@@ -1268,6 +1270,22 @@
return 0;
}
+static int spi_cmd_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ return 0;
+}
+
+static int spi_cmd_start(struct mdp3_intf *intf)
+{
+ intf->active = true;
+ return 0;
+}
+
+static int spi_cmd_stop(struct mdp3_intf *intf)
+{
+ intf->active = false;
+ return 0;
+}
int mdp3_intf_init(struct mdp3_intf *intf)
{
switch (intf->cfg.type) {
@@ -1286,6 +1304,11 @@
intf->start = dsi_cmd_start;
intf->stop = dsi_cmd_stop;
break;
+ case MDP3_DMA_OUTPUT_SEL_SPI_CMD:
+ intf->config = spi_cmd_config;
+ intf->start = spi_cmd_start;
+ intf->stop = spi_cmd_stop;
+ break;
default:
return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdp3_dma.h b/drivers/video/fbdev/msm/mdp3_dma.h
index 24caedb9..ec327b6 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.h
+++ b/drivers/video/fbdev/msm/mdp3_dma.h
@@ -47,6 +47,7 @@
MDP3_DMA_OUTPUT_SEL_DSI_CMD,
MDP3_DMA_OUTPUT_SEL_LCDC,
MDP3_DMA_OUTPUT_SEL_DSI_VIDEO,
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD,
MDP3_DMA_OUTPUT_SEL_MAX
};
diff --git a/drivers/video/fbdev/msm/mdp3_layer.c b/drivers/video/fbdev/msm/mdp3_layer.c
index 0078466..98b5c19 100644
--- a/drivers/video/fbdev/msm/mdp3_layer.c
+++ b/drivers/video/fbdev/msm/mdp3_layer.c
@@ -197,6 +197,7 @@
struct msmfb_data img;
bool is_panel_type_cmd = false;
struct mdp3_img_data data;
+ int intf_type;
int rc = 0;
layer = &input_layer[0];
@@ -208,23 +209,24 @@
goto err;
}
+ intf_type = mdp3_get_ion_client(mfd);
memset(&img, 0, sizeof(img));
img.memory_id = buffer->planes[0].fd;
img.offset = buffer->planes[0].offset;
memset(&data, 0, sizeof(struct mdp3_img_data));
- if (mfd->panel.type == MIPI_CMD_PANEL)
+ if (mfd->panel.type == MIPI_CMD_PANEL || intf_type == MDP3_CLIENT_SPI)
is_panel_type_cmd = true;
if (is_panel_type_cmd) {
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
+ rc = mdp3_iommu_enable(intf_type);
if (rc) {
pr_err("fail to enable iommu\n");
return rc;
}
}
- rc = mdp3_get_img(&img, &data, MDP3_CLIENT_DMA_P);
+ rc = mdp3_get_img(&img, &data, intf_type);
if (rc) {
pr_err("fail to get overlay buffer\n");
goto err;
@@ -260,7 +262,7 @@
struct mdp3_session_data *mdp3_session;
struct mdp3_dma *dma;
int layer_count = commit->input_layer_cnt;
- int stride, format;
+ int stride, format, client;
/* Handle NULL commit */
if (!layer_count) {
@@ -273,7 +275,8 @@
mutex_lock(&mdp3_session->lock);
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ client = mdp3_get_ion_client(mfd);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
layer_list = commit->input_layers;
layer = &layer_list[0];
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 3391059..5459510 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -542,6 +542,7 @@
{
struct mdss_panel_info *panel_info = mfd->panel_info;
int i, lcount = 0;
+ int frame_rate = DEFAULT_FRAME_RATE;
struct mdp_blit_req *req;
struct bpp_info bpp;
u64 old_solid_fill_pixel = 0;
@@ -556,6 +557,7 @@
ATRACE_BEGIN(__func__);
lcount = lreq->count;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
if (lcount == 0) {
pr_err("Blit with request count 0, continue to recover!!!\n");
ATRACE_END(__func__);
@@ -583,11 +585,11 @@
is_blit_optimization_possible(lreq, i);
req = &(lreq->req_list[i]);
- if (req->fps > 0 && req->fps <= panel_info->mipi.frame_rate) {
+ if (req->fps > 0 && req->fps <= frame_rate) {
if (fps == 0)
fps = req->fps;
else
- fps = panel_info->mipi.frame_rate;
+ fps = frame_rate;
}
mdp3_get_bpp_info(req->src.format, &bpp);
@@ -645,7 +647,7 @@
}
if (fps == 0)
- fps = panel_info->mipi.frame_rate;
+ fps = frame_rate;
if (lreq->req_list[0].flags & MDP_SOLID_FILL) {
honest_ppp_ab = ppp_res.solid_fill_byte * 4;
diff --git a/drivers/video/fbdev/msm/mdp3_ppp_data.c b/drivers/video/fbdev/msm/mdp3_ppp_data.c
index ac88d9b..dd2cce0 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp_data.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp_data.c
@@ -89,8 +89,8 @@
};
const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = {
- [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
- [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
[MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
[MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
[MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 816eed0..b4e4bdd 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -383,6 +383,14 @@
ret = 0;
}
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ ret = gpio_direction_output(
+ ctrl_pdata->vdd_ext_gpio, 0);
+ if (ret)
+ pr_err("%s: unable to set dir for vdd gpio\n",
+ __func__);
+ }
+
if (mdss_dsi_pinctrl_set_state(ctrl_pdata, false))
pr_debug("reset disable: pinctrl not enabled\n");
@@ -410,6 +418,15 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ ret = gpio_direction_output(
+ ctrl_pdata->vdd_ext_gpio, 1);
+ usleep_range(3000, 4000); /* h/w recommended delay */
+ if (ret)
+ pr_err("%s: unable to set dir for vdd gpio\n",
+ __func__);
+ }
+
ret = msm_mdss_enable_vreg(
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 1);
@@ -3263,6 +3280,7 @@
struct mdss_util_intf *util;
static int te_irq_registered;
struct mdss_panel_data *pdata;
+ struct mdss_panel_cfg *pan_cfg = NULL;
if (!pdev || !pdev->dev.of_node) {
pr_err("%s: pdev not found for DSI controller\n", __func__);
@@ -3295,6 +3313,14 @@
return -ENODEV;
}
+ pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_SPI);
+ if (IS_ERR(pan_cfg)) {
+ return PTR_ERR(pan_cfg);
+ } else if (pan_cfg) {
+ pr_debug("%s: SPI is primary\n", __func__);
+ return -ENODEV;
+ }
+
ctrl_pdata->mdss_util = util;
atomic_set(&ctrl_pdata->te_irq_ready, 0);
@@ -4183,6 +4209,11 @@
of_property_read_bool(ctrl_pdev->dev.of_node,
"qcom,platform-bklight-en-gpio-invert");
+ ctrl_pdata->vdd_ext_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,ext-vdd-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->vdd_ext_gpio))
+ pr_info("%s: ext vdd gpio not specified\n", __func__);
+
ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
"qcom,platform-reset-gpio", 0);
if (!gpio_is_valid(ctrl_pdata->rst_gpio))
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 2ff2951..5d2d677 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -435,6 +435,7 @@
int rst_gpio;
int disp_en_gpio;
int bklt_en_gpio;
+ int vdd_ext_gpio;
int mode_gpio;
int intf_mux_gpio;
bool bklt_en_gpio_invert;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 85ed42e..b94280d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -326,6 +326,15 @@
goto bklt_en_gpio_err;
}
}
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ rc = gpio_request(ctrl_pdata->vdd_ext_gpio,
+ "vdd_enable");
+ if (rc) {
+ pr_err("request vdd enable gpio failed, rc=%d\n",
+ rc);
+ goto vdd_en_gpio_err;
+ }
+ }
if (gpio_is_valid(ctrl_pdata->mode_gpio)) {
rc = gpio_request(ctrl_pdata->mode_gpio, "panel_mode");
if (rc) {
@@ -337,6 +346,9 @@
return rc;
mode_gpio_err:
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio))
+ gpio_free(ctrl_pdata->vdd_ext_gpio);
+vdd_en_gpio_err:
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio))
gpio_free(ctrl_pdata->bklt_en_gpio);
bklt_en_gpio_err:
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index c5af962..326768d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -146,15 +146,18 @@
return NOTIFY_DONE;
mfd = evdata->info->par;
- ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev),
+ if (mfd->panel_info->type == SPI_PANEL) {
+ pinfo = mfd->panel_info;
+ } else {
+ ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev),
struct mdss_dsi_ctrl_pdata, panel_data);
- if (!ctrl_pdata) {
- pr_err("%s: DSI ctrl not available\n", __func__);
- return NOTIFY_BAD;
+ if (!ctrl_pdata) {
+ pr_err("%s: DSI ctrl not available\n", __func__);
+ return NOTIFY_BAD;
+ }
+
+ pinfo = &ctrl_pdata->panel_data.panel_info;
}
-
- pinfo = &ctrl_pdata->panel_data.panel_info;
-
if ((!(pinfo->esd_check_enabled) &&
dsi_status_disable) ||
(dsi_status_disable == DSI_STATUS_CHECK_DISABLE)) {
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 72f651e..edeecb4 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -345,6 +345,9 @@
case EDP_PANEL:
ret = snprintf(buf, PAGE_SIZE, "edp panel\n");
break;
+ case SPI_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "spi panel\n");
+ break;
default:
ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
break;
@@ -1214,6 +1217,7 @@
struct mdss_panel_data *pdata;
struct fb_info *fbi;
int rc;
+ const char *data;
if (fbi_list_index >= MAX_FBI_LIST)
return -ENOMEM;
@@ -1262,6 +1266,21 @@
mfd->pdev = pdev;
+ if (mfd->panel.type == SPI_PANEL)
+ mfd->fb_imgType = MDP_RGB_565;
+ if (mfd->panel.type == MIPI_VIDEO_PANEL || mfd->panel.type ==
+ MIPI_CMD_PANEL || mfd->panel.type == SPI_PANEL){
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,mdss-fb-format", &data);
+ if (!rc) {
+ if (!strcmp(data, "rgb888"))
+ mfd->fb_imgType = MDP_RGB_888;
+ else if (!strcmp(data, "rgb565"))
+ mfd->fb_imgType = MDP_RGB_565;
+ else
+ mfd->fb_imgType = MDP_RGBA_8888;
+ }
+ }
mfd->split_fb_left = mfd->split_fb_right = 0;
mdss_fb_set_split_mode(mfd, pdata);
@@ -1374,6 +1393,7 @@
mfd->mdp_sync_pt_data.threshold = 1;
mfd->mdp_sync_pt_data.retire_threshold = 0;
break;
+ case SPI_PANEL:
case MIPI_CMD_PANEL:
mfd->mdp_sync_pt_data.threshold = 1;
mfd->mdp_sync_pt_data.retire_threshold = 1;
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index aa90d5f..314dc0e 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -60,6 +60,7 @@
#define WRITEBACK_PANEL 10 /* Wifi display */
#define LVDS_PANEL 11 /* LVDS */
#define EDP_PANEL 12 /* LVDS */
+#define SPI_PANEL 13
#define DSC_PPS_LEN 128
@@ -108,6 +109,7 @@
MDSS_PANEL_INTF_DSI,
MDSS_PANEL_INTF_EDP,
MDSS_PANEL_INTF_HDMI,
+ MDSS_PANEL_INTF_SPI,
};
enum {
@@ -118,6 +120,12 @@
};
enum {
+ MDSS_PANEL_BLANK_BLANK = 0,
+ MDSS_PANEL_BLANK_UNBLANK,
+ MDSS_PANEL_BLANK_LOW_POWER,
+};
+
+enum {
MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0,
MDSS_PANEL_LOW_PERSIST_MODE_ON,
};
@@ -430,6 +438,10 @@
char frame_rate; /* fps */
};
+struct spi_panel_info {
+ char frame_rate;
+};
+
/**
* struct dynamic_fps_data - defines dynamic fps related data
* @hfp: horizontal front porch
@@ -677,6 +689,7 @@
u32 partial_update_roi_merge;
struct ion_handle *splash_ihdl;
int panel_power_state;
+ int blank_state;
int compression_mode;
uint32_t panel_dead;
@@ -736,6 +749,7 @@
struct lcd_panel_info lcdc;
struct fbc_panel_info fbc;
struct mipi_panel_info mipi;
+ struct spi_panel_info spi;
struct lvds_panel_info lvds;
struct edp_panel_info edp;
@@ -875,6 +889,9 @@
frame_rate = panel_info->lcdc.frame_rate;
break;
}
+ case SPI_PANEL:
+ frame_rate = panel_info->spi.frame_rate;
+ break;
default:
pixel_total = (panel_info->lcdc.h_back_porch +
panel_info->lcdc.h_front_porch +
diff --git a/drivers/video/fbdev/msm/mdss_spi_client.c b/drivers/video/fbdev/msm/mdss_spi_client.c
new file mode 100644
index 0000000..541e382
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_client.c
@@ -0,0 +1,198 @@
+/* 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 <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/qpnp/pin.h>
+#include <linux/delay.h>
+
+#include "mdss_spi_client.h"
+
+#define MAX_READ_SPEED_HZ 9600000
+#define SPI_PANEL_COMMAND_LEN 1
+static struct spi_device *mdss_spi_client;
+
+int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len)
+{
+ int rc = 0;
+ u32 max_speed_hz;
+ u8 memory_write_reg = 0x2c;
+ u8 empty_pack[] = {0x29, 0x29, 0x29};
+ struct spi_transfer t[4] = {
+ [0] = {
+ .tx_buf = ®_addr,
+ .len = 1,
+ },
+ [1] = {
+ .rx_buf = data,
+ .len = len,
+ },
+ [2] = {
+ .tx_buf = &empty_pack,
+ .len = 3,
+ },
+ [3] = {
+ .tx_buf = &memory_write_reg,
+ .len = 1,
+ }
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+ max_speed_hz = mdss_spi_client->max_speed_hz;
+ mdss_spi_client->max_speed_hz = MAX_READ_SPEED_HZ;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[2], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+ spi_message_init(&m);
+ spi_message_add_tail(&t[3], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+ mdss_spi_client->max_speed_hz = max_speed_hz;
+
+ return rc;
+}
+
+int mdss_spi_tx_command(const void *buf)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = SPI_PANEL_COMMAND_LEN,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+int mdss_spi_tx_parameter(const void *buf, size_t len)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+int mdss_spi_tx_pixel(const void *buf, size_t len)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 16;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+static int mdss_spi_client_probe(struct spi_device *spidev)
+{
+ int irq;
+ int cs;
+ int cpha, cpol, cs_high;
+ u32 max_speed;
+ struct device_node *np;
+
+ irq = spidev->irq;
+ cs = spidev->chip_select;
+ cpha = (spidev->mode & SPI_CPHA) ? 1:0;
+ cpol = (spidev->mode & SPI_CPOL) ? 1:0;
+ cs_high = (spidev->mode & SPI_CS_HIGH) ? 1:0;
+ max_speed = spidev->max_speed_hz;
+ np = spidev->dev.of_node;
+ pr_debug("cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x] Max_speed[%d]\n",
+ cs, cpha, cpol, cs_high, max_speed);
+ mdss_spi_client = spidev;
+
+ return 0;
+}
+
+
+static const struct of_device_id mdss_spi_dt_match[] = {
+ { .compatible = "qcom,mdss-spi-client" },
+ {},
+};
+
+static struct spi_driver mdss_spi_client_driver = {
+ .probe = mdss_spi_client_probe,
+ .driver = {
+ .name = "mdss-spi-client",
+ .owner = THIS_MODULE,
+ .of_match_table = mdss_spi_dt_match,
+ },
+};
+
+static int __init mdss_spi_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&mdss_spi_client_driver);
+
+ return 0;
+}
+module_init(mdss_spi_init);
+
+static void __exit mdss_spi_exit(void)
+{
+ spi_unregister_driver(&mdss_spi_client_driver);
+}
+module_exit(mdss_spi_exit);
+
diff --git a/drivers/video/fbdev/msm/mdss_spi_client.h b/drivers/video/fbdev/msm/mdss_spi_client.h
new file mode 100644
index 0000000..2d15625
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_client.h
@@ -0,0 +1,20 @@
+/* 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 __MDSS_SPI_CLINET_H__
+#define __MDSS_SPI_CLINET_H__
+
+int mdss_spi_tx_command(const void *buf);
+int mdss_spi_tx_parameter(const void *buf, size_t len);
+int mdss_spi_tx_pixel(const void *buf, size_t len);
+int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len);
+#endif /* End of __MDSS_SPI_CLINET_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_spi_panel.c b/drivers/video/fbdev/msm/mdss_spi_panel.c
new file mode 100644
index 0000000..86a1c1c
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_panel.c
@@ -0,0 +1,1713 @@
+/* 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 <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/qpnp/pin.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/leds.h>
+#include <linux/qpnp/pwm.h>
+#include <linux/of_device.h>
+
+#include "mdss.h"
+#include "mdss_panel.h"
+#include "mdss_spi_panel.h"
+#include "mdss_spi_client.h"
+#include "mdp3.h"
+
+DEFINE_LED_TRIGGER(bl_led_trigger);
+static int mdss_spi_panel_reset(struct mdss_panel_data *pdata, int enable)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+ struct mdss_panel_info *pinfo = NULL;
+ int i, rc = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (!gpio_is_valid(ctrl_pdata->rst_gpio)) {
+ pr_debug("%s:%d, reset line not configured\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio)) {
+ pr_debug("%s:%d, dc line not configured\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ pinfo = &(ctrl_pdata->panel_data.panel_info);
+
+ if (enable) {
+ rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n");
+ if (rc) {
+ pr_err("display reset gpio request failed\n");
+ return rc;
+ }
+
+ rc = gpio_request(ctrl_pdata->disp_dc_gpio, "disp_dc");
+ if (rc) {
+ pr_err("display dc gpio request failed\n");
+ return rc;
+ }
+
+ if (!pinfo->cont_splash_enabled) {
+ for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) {
+ gpio_direction_output((ctrl_pdata->rst_gpio),
+ pdata->panel_info.rst_seq[i]);
+ if (pdata->panel_info.rst_seq[++i])
+ usleep_range(pinfo->rst_seq[i] * 1000,
+ pinfo->rst_seq[i] * 1000);
+ }
+ }
+
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ pr_debug("%s: Panel Not properly turned OFF\n",
+ __func__);
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ pr_err("%s: Reset panel done\n", __func__);
+ }
+ } else {
+ gpio_direction_output((ctrl_pdata->rst_gpio), 0);
+ gpio_free(ctrl_pdata->rst_gpio);
+
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0);
+ gpio_free(ctrl_pdata->disp_dc_gpio);
+ }
+ return rc;
+}
+
+
+static int mdss_spi_panel_pinctrl_set_state(
+ struct spi_panel_data *ctrl_pdata,
+ bool active)
+{
+ struct pinctrl_state *pin_state;
+ int rc = -EFAULT;
+
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl))
+ return PTR_ERR(ctrl_pdata->pin_res.pinctrl);
+
+ pin_state = active ? ctrl_pdata->pin_res.gpio_state_active
+ : ctrl_pdata->pin_res.gpio_state_suspend;
+ if (!IS_ERR_OR_NULL(pin_state)) {
+ rc = pinctrl_select_state(ctrl_pdata->pin_res.pinctrl,
+ pin_state);
+ if (rc)
+ pr_err("%s: can not set %s pins\n", __func__,
+ active ? MDSS_PINCTRL_STATE_DEFAULT
+ : MDSS_PINCTRL_STATE_SLEEP);
+ } else {
+ pr_err("%s: invalid '%s' pinstate\n", __func__,
+ active ? MDSS_PINCTRL_STATE_DEFAULT
+ : MDSS_PINCTRL_STATE_SLEEP);
+ }
+ return rc;
+}
+
+
+static int mdss_spi_panel_pinctrl_init(struct platform_device *pdev)
+{
+ struct spi_panel_data *ctrl_pdata;
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ ctrl_pdata->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl)) {
+ pr_err("%s: failed to get pinctrl\n", __func__);
+ return PTR_ERR(ctrl_pdata->pin_res.pinctrl);
+ }
+
+ ctrl_pdata->pin_res.gpio_state_active
+ = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl,
+ MDSS_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_active))
+ pr_warn("%s: can not get default pinstate\n", __func__);
+
+ ctrl_pdata->pin_res.gpio_state_suspend
+ = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl,
+ MDSS_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_suspend))
+ pr_warn("%s: can not get sleep pinstate\n", __func__);
+
+ return 0;
+}
+
+
+static int mdss_spi_panel_power_on(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+ ret = msm_mdss_enable_vreg(
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 1);
+ if (ret) {
+ pr_err("%s: failed to enable vregs for %s\n",
+ __func__, "PANEL_PM");
+ }
+
+ /*
+ * If continuous splash screen feature is enabled, then we need to
+ * request all the GPIOs that have already been configured in the
+ * bootloader. This needs to be done irresepective of whether
+ * the lp11_init flag is set or not.
+ */
+ if (pdata->panel_info.cont_splash_enabled) {
+ if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true))
+ pr_debug("reset enable: pinctrl not enabled\n");
+
+ ret = mdss_spi_panel_reset(pdata, 1);
+ if (ret)
+ pr_err("%s: Panel reset failed. rc=%d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+
+static int mdss_spi_panel_power_off(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ ret = mdss_spi_panel_reset(pdata, 0);
+ if (ret) {
+ pr_warn("%s: Panel reset failed. rc=%d\n", __func__, ret);
+ ret = 0;
+ }
+
+ if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, false))
+ pr_warn("reset disable: pinctrl not enabled\n");
+
+ ret = msm_mdss_enable_vreg(
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 0);
+ if (ret)
+ pr_err("%s: failed to disable vregs for %s\n",
+ __func__, "PANEL_PM");
+
+end:
+ return ret;
+}
+
+
+static int mdss_spi_panel_power_ctrl(struct mdss_panel_data *pdata,
+ int power_state)
+{
+ int ret;
+ struct mdss_panel_info *pinfo;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo = &pdata->panel_info;
+ pr_debug("%s: cur_power_state=%d req_power_state=%d\n", __func__,
+ pinfo->panel_power_state, power_state);
+
+ if (pinfo->panel_power_state == power_state) {
+ pr_debug("%s: no change needed\n", __func__);
+ return 0;
+ }
+
+ switch (power_state) {
+ case MDSS_PANEL_POWER_OFF:
+ ret = mdss_spi_panel_power_off(pdata);
+ break;
+ case MDSS_PANEL_POWER_ON:
+ ret = mdss_spi_panel_power_on(pdata);
+ break;
+ default:
+ pr_err("%s: unknown panel power state requested (%d)\n",
+ __func__, power_state);
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ pinfo->panel_power_state = power_state;
+
+ return ret;
+}
+
+static int mdss_spi_panel_unblank(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) {
+ ret = ctrl_pdata->on(pdata);
+ if (ret) {
+ pr_err("%s: unable to initialize the panel\n",
+ __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
+ }
+
+ return ret;
+}
+
+static int mdss_spi_panel_blank(struct mdss_panel_data *pdata, int power_state)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ ret = ctrl_pdata->off(pdata);
+ if (ret) {
+ pr_err("%s: Panel OFF failed\n", __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ }
+
+ return ret;
+}
+
+
+static int mdss_spi_panel_event_handler(struct mdss_panel_data *pdata,
+ int event, void *arg)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+ int power_state;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ switch (event) {
+ case MDSS_EVENT_LINK_READY:
+ rc = mdss_spi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON);
+ if (rc) {
+ pr_err("%s:Panel power on failed. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true);
+ mdss_spi_panel_reset(pdata, 1);
+ break;
+ case MDSS_EVENT_UNBLANK:
+ rc = mdss_spi_panel_unblank(pdata);
+ break;
+ case MDSS_EVENT_PANEL_ON:
+ ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE;
+ break;
+ case MDSS_EVENT_BLANK:
+ power_state = (int) (unsigned long) arg;
+ break;
+ case MDSS_EVENT_PANEL_OFF:
+ power_state = (int) (unsigned long) arg;
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
+ rc = mdss_spi_panel_blank(pdata, power_state);
+ rc = mdss_spi_panel_power_ctrl(pdata, power_state);
+ break;
+ default:
+ pr_debug("%s: unhandled event=%d\n", __func__, event);
+ break;
+ }
+ pr_debug("%s-:event=%d, rc=%d\n", __func__, event, rc);
+ return rc;
+}
+
+int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata)
+{
+ int i = 0, voltage = 0;
+ struct mdss_vreg *vreg;
+ int num_vreg;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+ vreg = ctrl_pdata->panel_power_data.vreg_config;
+ num_vreg = ctrl_pdata->panel_power_data.num_vreg;
+
+ for (i = 0; i < num_vreg; i++) {
+ if (regulator_is_enabled(vreg[i].vreg) <= 0)
+ return false;
+ voltage = regulator_get_voltage(vreg[i].vreg);
+ if (!(voltage >= vreg[i].min_voltage &&
+ voltage <= vreg[i].max_voltage))
+ return false;
+ }
+
+ return true;
+}
+
+static void enable_spi_panel_te_irq(struct spi_panel_data *ctrl_pdata,
+ bool enable)
+{
+ static bool is_enabled = true;
+
+ if (is_enabled == enable)
+ return;
+
+ if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) {
+ pr_err("%s:%d,SPI panel TE GPIO not configured\n",
+ __func__, __LINE__);
+ return;
+ }
+
+ if (enable)
+ enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
+ else
+ disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
+
+ is_enabled = enable;
+}
+
+int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int dma_stride)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+ char *tx_buf;
+ int rc = 0;
+ int panel_yres;
+ int panel_xres;
+ int padding_length = 0;
+ int actual_stride = 0;
+ int byte_per_pixel = 0;
+ int scan_count = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ tx_buf = ctrl_pdata->tx_buf;
+ panel_xres = ctrl_pdata->panel_data.panel_info.xres;
+ panel_yres = ctrl_pdata->panel_data.panel_info.yres;
+
+ byte_per_pixel = ctrl_pdata->panel_data.panel_info.bpp / 8;
+ actual_stride = panel_xres * byte_per_pixel;
+ padding_length = dma_stride - actual_stride;
+
+ /* remove the padding and copy to continuous buffer */
+ while (scan_count < panel_yres) {
+ memcpy((tx_buf + scan_count * actual_stride),
+ (buf + scan_count * (actual_stride + padding_length)),
+ actual_stride);
+ scan_count++;
+ }
+
+ enable_spi_panel_te_irq(ctrl_pdata, true);
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ reinit_completion(&ctrl_pdata->spi_panel_te);
+
+ rc = wait_for_completion_timeout(&ctrl_pdata->spi_panel_te,
+ msecs_to_jiffies(SPI_PANEL_TE_TIMEOUT));
+
+ if (rc == 0)
+ pr_err("wait panel TE time out\n");
+
+ rc = mdss_spi_tx_pixel(tx_buf, ctrl_pdata->byte_pre_frame);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return rc;
+}
+
+static int mdss_spi_read_panel_data(struct mdss_panel_data *pdata,
+ u8 reg_addr, u8 *data, u8 len)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0);
+ rc = mdss_spi_read_data(reg_addr, data, len);
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 1);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return rc;
+}
+
+static int mdss_spi_panel_on(struct mdss_panel_data *pdata)
+{
+ struct spi_panel_data *ctrl = NULL;
+ struct mdss_panel_info *pinfo;
+ int i;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+ pinfo = &pdata->panel_info;
+ ctrl = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ for (i = 0; i < ctrl->on_cmds.cmd_cnt; i++) {
+ /* pull down dc gpio indicate this is command */
+ gpio_direction_output(ctrl->disp_dc_gpio, 0);
+ mdss_spi_tx_command(ctrl->on_cmds.cmds[i].command);
+ gpio_direction_output((ctrl->disp_dc_gpio), 1);
+
+ if (ctrl->on_cmds.cmds[i].dchdr.dlen > 1) {
+ mdss_spi_tx_parameter(ctrl->on_cmds.cmds[i].parameter,
+ ctrl->on_cmds.cmds[i].dchdr.dlen-1);
+ }
+ if (ctrl->on_cmds.cmds[i].dchdr.wait != 0)
+ msleep(ctrl->on_cmds.cmds[i].dchdr.wait);
+ }
+
+ pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
+
+ pr_debug("%s:-\n", __func__);
+
+ return 0;
+}
+
+
+static int mdss_spi_panel_off(struct mdss_panel_data *pdata)
+{
+ struct spi_panel_data *ctrl = NULL;
+ struct mdss_panel_info *pinfo;
+ int i;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo = &pdata->panel_info;
+ ctrl = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ for (i = 0; i < ctrl->off_cmds.cmd_cnt; i++) {
+ /* pull down dc gpio indicate this is command */
+ gpio_direction_output(ctrl->disp_dc_gpio, 0);
+ mdss_spi_tx_command(ctrl->off_cmds.cmds[i].command);
+ gpio_direction_output((ctrl->disp_dc_gpio), 1);
+
+ if (ctrl->off_cmds.cmds[i].dchdr.dlen > 1) {
+ mdss_spi_tx_parameter(ctrl->off_cmds.cmds[i].parameter,
+ ctrl->off_cmds.cmds[i].dchdr.dlen-1);
+ }
+
+ if (ctrl->off_cmds.cmds[i].dchdr.wait != 0)
+ msleep(ctrl->off_cmds.cmds[i].dchdr.wait);
+ }
+
+ pinfo->blank_state = MDSS_PANEL_BLANK_BLANK;
+
+ pr_debug("%s:-\n", __func__);
+ return 0;
+}
+
+static void mdss_spi_put_dt_vreg_data(struct device *dev,
+ struct mdss_module_power *module_power)
+{
+ if (!module_power) {
+ pr_err("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (module_power->vreg_config) {
+ devm_kfree(dev, module_power->vreg_config);
+ module_power->vreg_config = NULL;
+ }
+ module_power->num_vreg = 0;
+}
+
+
+static int mdss_spi_get_panel_vreg_data(struct device *dev,
+ struct mdss_module_power *mp)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_node = NULL;
+ struct device_node *supply_root_node = NULL;
+
+ if (!dev || !mp) {
+ pr_err("%s: invalid input\n", __func__);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ of_node = dev->of_node;
+
+ mp->num_vreg = 0;
+
+ supply_root_node = of_get_child_by_name(of_node,
+ "qcom,panel-supply-entries");
+
+ for_each_available_child_of_node(supply_root_node, supply_node) {
+ mp->num_vreg++;
+ }
+ if (mp->num_vreg == 0) {
+ pr_debug("%s: no vreg\n", __func__);
+ goto novreg;
+ } else {
+ pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg);
+ }
+
+ mp->vreg_config = kcalloc(mp->num_vreg, sizeof(struct mdss_vreg),
+ GFP_KERNEL);
+
+ if (mp->vreg_config != NULL) {
+ for_each_available_child_of_node(supply_root_node,
+ supply_node) {
+ const char *st = NULL;
+ /* vreg-name */
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("%s: error reading name. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ snprintf(mp->vreg_config[i].vreg_name,
+ ARRAY_SIZE((mp->vreg_config[i].vreg_name)),
+ "%s", st);
+ /* vreg-min-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err("%s: error reading min volt. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].min_voltage = tmp;
+
+ /* vreg-max-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err("%s: error reading max volt. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].max_voltage = tmp;
+
+ /* enable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err("%s: error read enable load. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp;
+
+ /* disable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err("%s: error read disable load. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp;
+
+ /* pre-sleep */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-on-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read pre on value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].pre_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-off-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read pre off value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].pre_off_sleep = tmp;
+ }
+
+ /* post-sleep */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-on-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read post on value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].post_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-off-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read post off value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].post_off_sleep = tmp;
+ }
+
+ ++i;
+ }
+ }
+ return rc;
+error:
+ kfree(mp->vreg_config);
+ mp->vreg_config = NULL;
+
+novreg:
+ mp->num_vreg = 0;
+
+ return rc;
+
+}
+
+static int mdss_spi_panel_parse_cmds(struct device_node *np,
+ struct spi_panel_cmds *pcmds, char *cmd_key)
+{
+ const char *data;
+ int blen = 0, len;
+ char *buf, *bp;
+ struct spi_ctrl_hdr *dchdr;
+ int i, cnt;
+
+ data = of_get_property(np, cmd_key, &blen);
+ if (!data) {
+ pr_err("%s: failed, key=%s\n", __func__, cmd_key);
+ return -ENOMEM;
+ }
+
+ buf = kcalloc(blen, sizeof(char), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, data, blen);
+
+ /* scan dcs commands */
+ bp = buf;
+ len = blen;
+ cnt = 0;
+ while (len >= sizeof(*dchdr)) {
+ dchdr = (struct spi_ctrl_hdr *)bp;
+ if (dchdr->dlen > len) {
+ pr_err("%s: dtsi parse error, len=%d",
+ __func__, dchdr->dlen);
+ goto exit_free;
+ }
+ bp += sizeof(*dchdr);
+ len -= sizeof(*dchdr);
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ cnt++;
+ }
+
+ if (len != 0) {
+ pr_err("%s: dcs_cmd=%x len=%d error",
+ __func__, buf[0], len);
+ goto exit_free;
+ }
+
+ pcmds->cmds = kcalloc(cnt, sizeof(struct spi_cmd_desc),
+ GFP_KERNEL);
+ if (!pcmds->cmds)
+ goto exit_free;
+
+ pcmds->cmd_cnt = cnt;
+ pcmds->buf = buf;
+ pcmds->blen = blen;
+
+ bp = buf;
+ len = blen;
+ for (i = 0; i < cnt; i++) {
+ dchdr = (struct spi_ctrl_hdr *)bp;
+ len -= sizeof(*dchdr);
+ bp += sizeof(*dchdr);
+ pcmds->cmds[i].dchdr = *dchdr;
+ pcmds->cmds[i].command = bp;
+ pcmds->cmds[i].parameter = bp + sizeof(char);
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ }
+
+ pr_debug("%s: dcs_cmd=%x, len=%d, cmd_cnt=%d\n", __func__,
+ pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt);
+
+ return 0;
+
+exit_free:
+ kfree(buf);
+ return -ENOMEM;
+}
+static int mdss_spi_panel_parse_reset_seq(struct device_node *np,
+ u32 rst_seq[MDSS_SPI_RST_SEQ_LEN], u32 *rst_len,
+ const char *name)
+{
+ int num = 0, i;
+ int rc;
+ struct property *data;
+ u32 tmp[MDSS_SPI_RST_SEQ_LEN];
+
+ *rst_len = 0;
+ data = of_find_property(np, name, &num);
+ num /= sizeof(u32);
+ if (!data || !num || num > MDSS_SPI_RST_SEQ_LEN || num % 2) {
+ pr_err("%s:%d, error reading %s, length found = %d\n",
+ __func__, __LINE__, name, num);
+ } else {
+ rc = of_property_read_u32_array(np, name, tmp, num);
+ if (rc)
+ pr_err("%s:%d, error reading %s, rc = %d\n",
+ __func__, __LINE__, name, rc);
+ else {
+ for (i = 0; i < num; ++i)
+ rst_seq[i] = tmp[i];
+ *rst_len = num;
+ }
+ }
+ return 0;
+}
+
+static bool mdss_send_panel_cmd_for_esd(struct spi_panel_data *ctrl_pdata)
+{
+
+ if (ctrl_pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return false;
+ }
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ mdss_spi_panel_on(&ctrl_pdata->panel_data);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return true;
+}
+
+static bool mdss_spi_reg_status_check(struct spi_panel_data *ctrl_pdata)
+{
+ int ret = 0;
+ int i = 0;
+
+ if (ctrl_pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return false;
+ }
+
+ pr_debug("%s: Checking Register status\n", __func__);
+
+ ret = mdss_spi_read_panel_data(&ctrl_pdata->panel_data,
+ ctrl_pdata->panel_status_reg,
+ ctrl_pdata->act_status_value,
+ ctrl_pdata->status_cmds_rlen);
+ if (ret < 0) {
+ pr_err("%s: Read status register returned error\n", __func__);
+ } else {
+ for (i = 0; i < ctrl_pdata->status_cmds_rlen; i++) {
+ pr_debug("act_value[%d] = %x, exp_value[%d] = %x\n",
+ i, ctrl_pdata->act_status_value[i],
+ i, ctrl_pdata->exp_status_value[i]);
+ if (ctrl_pdata->act_status_value[i] !=
+ ctrl_pdata->exp_status_value[i])
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void mdss_spi_parse_esd_params(struct device_node *np,
+ struct spi_panel_data *ctrl)
+{
+ u32 tmp;
+ int rc;
+ struct property *data;
+ const char *string;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+
+ pinfo->esd_check_enabled = of_property_read_bool(np,
+ "qcom,esd-check-enabled");
+
+ if (!pinfo->esd_check_enabled)
+ return;
+
+ ctrl->status_mode = SPI_ESD_MAX;
+
+ rc = of_property_read_string(np,
+ "qcom,mdss-spi-panel-status-check-mode", &string);
+ if (!rc) {
+ if (!strcmp(string, "reg_read")) {
+ ctrl->status_mode = SPI_ESD_REG;
+ ctrl->check_status =
+ mdss_spi_reg_status_check;
+ } else if (!strcmp(string, "send_init_command")) {
+ ctrl->status_mode = SPI_SEND_PANEL_COMMAND;
+ ctrl->check_status =
+ mdss_send_panel_cmd_for_esd;
+ return;
+ } else {
+ pr_err("No valid panel-status-check-mode string\n");
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+ }
+
+ rc = of_property_read_u8(np, "qcom,mdss-spi-panel-status-reg",
+ &ctrl->panel_status_reg);
+ if (rc) {
+ pr_warn("%s:%d, Read status reg failed, disable ESD check\n",
+ __func__, __LINE__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-status-read-length",
+ &tmp);
+ if (rc) {
+ pr_warn("%s:%d, Read reg length failed, disable ESD check\n",
+ __func__, __LINE__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ ctrl->status_cmds_rlen = (!rc ? tmp : 1);
+
+ ctrl->exp_status_value = kzalloc(sizeof(u8) *
+ (ctrl->status_cmds_rlen + 1), GFP_KERNEL);
+ ctrl->act_status_value = kzalloc(sizeof(u8) *
+ (ctrl->status_cmds_rlen + 1), GFP_KERNEL);
+
+ if (!ctrl->exp_status_value || !ctrl->act_status_value) {
+ pr_err("%s: Error allocating memory for status buffer\n",
+ __func__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ data = of_find_property(np, "qcom,mdss-spi-panel-status-value", &tmp);
+ tmp /= sizeof(u8);
+ if (!data || (tmp != ctrl->status_cmds_rlen)) {
+ pr_err("%s: Panel status values not found\n", __func__);
+ pinfo->esd_check_enabled = false;
+ memset(ctrl->exp_status_value, 0, ctrl->status_cmds_rlen);
+ } else {
+ rc = of_property_read_u8_array(np,
+ "qcom,mdss-spi-panel-status-value",
+ ctrl->exp_status_value, tmp);
+ if (rc) {
+ pr_err("%s: Error reading panel status values\n",
+ __func__);
+ pinfo->esd_check_enabled = false;
+ memset(ctrl->exp_status_value, 0,
+ ctrl->status_cmds_rlen);
+ }
+ }
+}
+
+static int mdss_spi_panel_parse_dt(struct device_node *np,
+ struct spi_panel_data *ctrl_pdata)
+{
+ u32 tmp;
+ int rc;
+ const char *data;
+ struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
+
+ pinfo->cont_splash_enabled = of_property_read_bool(np,
+ "qcom,cont-splash-enabled");
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-width", &tmp);
+ if (rc) {
+ pr_err("%s: panel width not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->xres = (!rc ? tmp : 240);
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-height", &tmp);
+ if (rc) {
+ pr_err("%s:panel height not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->yres = (!rc ? tmp : 320);
+
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-physical-width-dimension", &tmp);
+ pinfo->physical_width = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-physical-height-dimension", &tmp);
+ pinfo->physical_height = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-framerate", &tmp);
+ pinfo->spi.frame_rate = (!rc ? tmp : 30);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-front-porch", &tmp);
+ pinfo->lcdc.h_front_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-back-porch", &tmp);
+ pinfo->lcdc.h_back_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-pulse-width", &tmp);
+ pinfo->lcdc.h_pulse_width = (!rc ? tmp : 2);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-sync-skew", &tmp);
+ pinfo->lcdc.hsync_skew = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-back-porch", &tmp);
+ pinfo->lcdc.v_back_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-front-porch", &tmp);
+ pinfo->lcdc.v_front_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-pulse-width", &tmp);
+ pinfo->lcdc.v_pulse_width = (!rc ? tmp : 2);
+
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bpp", &tmp);
+ if (rc) {
+ pr_err("%s: bpp not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->bpp = (!rc ? tmp : 16);
+
+ pinfo->pdest = DISPLAY_1;
+
+ ctrl_pdata->bklt_ctrl = SPI_UNKNOWN_CTRL;
+ data = of_get_property(np, "qcom,mdss-spi-bl-pmic-control-type", NULL);
+ if (data) {
+ if (!strcmp(data, "bl_ctrl_wled")) {
+ led_trigger_register_simple("bkl-trigger",
+ &bl_led_trigger);
+ pr_debug("%s: SUCCESS-> WLED TRIGGER register\n",
+ __func__);
+ ctrl_pdata->bklt_ctrl = SPI_BL_WLED;
+ } else if (!strcmp(data, "bl_gpio_pulse")) {
+ led_trigger_register_simple("gpio-bklt-trigger",
+ &bl_led_trigger);
+ pr_debug("%s: SUCCESS-> GPIO PULSE TRIGGER register\n",
+ __func__);
+ ctrl_pdata->bklt_ctrl = SPI_BL_WLED;
+ } else if (!strcmp(data, "bl_ctrl_pwm")) {
+ ctrl_pdata->bklt_ctrl = SPI_BL_PWM;
+ ctrl_pdata->pwm_pmi = of_property_read_bool(np,
+ "qcom,mdss-spi-bl-pwm-pmi");
+ rc = of_property_read_u32(np,
+ "qcom,mdss-spi-bl-pmic-pwm-frequency", &tmp);
+ if (rc) {
+ pr_err("%s: Error, panel pwm_period\n",
+ __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata->pwm_period = tmp;
+ if (ctrl_pdata->pwm_pmi) {
+ ctrl_pdata->pwm_bl = of_pwm_get(np, NULL);
+ if (IS_ERR(ctrl_pdata->pwm_bl)) {
+ pr_err("%s: Error, pwm device\n",
+ __func__);
+ ctrl_pdata->pwm_bl = NULL;
+ return -EINVAL;
+ }
+ } else {
+ rc = of_property_read_u32(np,
+ "qcom,mdss-spi-bl-pmic-bank-select",
+ &tmp);
+ if (rc) {
+ pr_err("%s: Error, lpg channel\n",
+ __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata->pwm_lpg_chan = tmp;
+ tmp = of_get_named_gpio(np,
+ "qcom,mdss-spi-pwm-gpio", 0);
+ ctrl_pdata->pwm_pmic_gpio = tmp;
+ pr_debug("%s: Configured PWM bklt ctrl\n",
+ __func__);
+ }
+ }
+ }
+ rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp);
+ pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bl-min-level", &tmp);
+ pinfo->bl_min = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bl-max-level", &tmp);
+ pinfo->bl_max = (!rc ? tmp : 255);
+ ctrl_pdata->bklt_max = pinfo->bl_max;
+
+
+ mdss_spi_panel_parse_reset_seq(np, pinfo->rst_seq,
+ &(pinfo->rst_seq_len),
+ "qcom,mdss-spi-reset-sequence");
+
+ mdss_spi_panel_parse_cmds(np, &ctrl_pdata->on_cmds,
+ "qcom,mdss-spi-on-command");
+
+ mdss_spi_panel_parse_cmds(np, &ctrl_pdata->off_cmds,
+ "qcom,mdss-spi-off-command");
+
+ mdss_spi_parse_esd_params(np, ctrl_pdata);
+
+
+ return 0;
+}
+
+static void mdss_spi_panel_pwm_cfg(struct spi_panel_data *ctrl)
+{
+ if (ctrl->pwm_pmi)
+ return;
+
+ ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt");
+ if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) {
+ pr_err("%s: Error: lpg_chan=%d pwm request failed",
+ __func__, ctrl->pwm_lpg_chan);
+ }
+ ctrl->pwm_enabled = 0;
+}
+
+static void mdss_spi_panel_bklt_pwm(struct spi_panel_data *ctrl, int level)
+{
+ int ret;
+ u32 duty;
+ u32 period_ns;
+
+ if (ctrl->pwm_bl == NULL) {
+ pr_err("%s: no PWM\n", __func__);
+ return;
+ }
+
+ if (level == 0) {
+ if (ctrl->pwm_enabled) {
+ ret = pwm_config_us(ctrl->pwm_bl, level,
+ ctrl->pwm_period);
+ if (ret)
+ pr_err("%s: pwm_config_us() failed err=%d.\n",
+ __func__, ret);
+ pwm_disable(ctrl->pwm_bl);
+ }
+ ctrl->pwm_enabled = 0;
+ return;
+ }
+
+ duty = level * ctrl->pwm_period;
+ duty /= ctrl->bklt_max;
+
+ pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n",
+ __func__, ctrl->bklt_ctrl, ctrl->pwm_period,
+ ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan);
+
+ if (ctrl->pwm_period >= USEC_PER_SEC) {
+ ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period);
+ if (ret) {
+ pr_err("%s: pwm_config_us() failed err=%d\n",
+ __func__, ret);
+ return;
+ }
+ } else {
+ period_ns = ctrl->pwm_period * NSEC_PER_USEC;
+ ret = pwm_config(ctrl->pwm_bl,
+ level * period_ns / ctrl->bklt_max,
+ period_ns);
+ if (ret) {
+ pr_err("%s: pwm_config() failed err=%d\n",
+ __func__, ret);
+ return;
+ }
+ }
+
+ if (!ctrl->pwm_enabled) {
+ ret = pwm_enable(ctrl->pwm_bl);
+ if (ret)
+ pr_err("%s: pwm_enable() failed err=%d\n", __func__,
+ ret);
+ ctrl->pwm_enabled = 1;
+ }
+}
+
+static void mdss_spi_panel_bl_ctrl(struct mdss_panel_data *pdata,
+ u32 bl_level)
+{
+ if (bl_level) {
+ mdp3_res->bklt_level = bl_level;
+ mdp3_res->bklt_update = true;
+ } else {
+ mdss_spi_panel_bl_ctrl_update(pdata, bl_level);
+ }
+}
+
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP)
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata,
+ u32 bl_level)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0))
+ bl_level = pdata->panel_info.bl_min;
+
+ switch (ctrl_pdata->bklt_ctrl) {
+ case SPI_BL_WLED:
+ led_trigger_event(bl_led_trigger, bl_level);
+ break;
+ case SPI_BL_PWM:
+ mdss_spi_panel_bklt_pwm(ctrl_pdata, bl_level);
+ break;
+ default:
+ pr_err("%s: Unknown bl_ctrl configuration %d\n",
+ __func__, ctrl_pdata->bklt_ctrl);
+ break;
+ }
+}
+#endif
+
+static int mdss_spi_panel_init(struct device_node *node,
+ struct spi_panel_data *ctrl_pdata,
+ bool cmd_cfg_cont_splash)
+{
+ int rc = 0;
+ static const char *panel_name;
+ struct mdss_panel_info *pinfo;
+
+ if (!node || !ctrl_pdata) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -ENODEV;
+ }
+
+ pinfo = &ctrl_pdata->panel_data.panel_info;
+
+ pr_debug("%s:%d\n", __func__, __LINE__);
+ pinfo->panel_name[0] = '\0';
+ panel_name = of_get_property(node, "qcom,mdss-spi-panel-name", NULL);
+ if (!panel_name) {
+ pr_info("%s:%d, Panel name not specified\n",
+ __func__, __LINE__);
+ } else {
+ pr_debug("%s: Panel Name = %s\n", __func__, panel_name);
+ strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN);
+ }
+ rc = mdss_spi_panel_parse_dt(node, ctrl_pdata);
+ if (rc) {
+ pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__);
+ return rc;
+ }
+
+ ctrl_pdata->byte_pre_frame = pinfo->xres * pinfo->yres * pinfo->bpp/8;
+
+ ctrl_pdata->tx_buf = kmalloc(ctrl_pdata->byte_pre_frame, GFP_KERNEL);
+
+ if (!cmd_cfg_cont_splash)
+ pinfo->cont_splash_enabled = false;
+
+ pr_info("%s: Continuous splash %s\n", __func__,
+ pinfo->cont_splash_enabled ? "enabled" : "disabled");
+
+ pinfo->dynamic_switch_pending = false;
+ pinfo->is_lpm_mode = false;
+ pinfo->esd_rdy = false;
+
+ ctrl_pdata->on = mdss_spi_panel_on;
+ ctrl_pdata->off = mdss_spi_panel_off;
+ ctrl_pdata->panel_data.set_backlight = mdss_spi_panel_bl_ctrl;
+
+ return 0;
+}
+
+static int mdss_spi_get_panel_cfg(char *panel_cfg,
+ struct spi_panel_data *ctrl_pdata)
+{
+ int rc;
+ struct mdss_panel_cfg *pan_cfg = NULL;
+
+ if (!ctrl_pdata)
+ return MDSS_PANEL_INTF_INVALID;
+
+ pan_cfg = ctrl_pdata->mdss_util->panel_intf_type(MDSS_PANEL_INTF_SPI);
+ if (IS_ERR(pan_cfg)) {
+ return PTR_ERR(pan_cfg);
+ } else if (!pan_cfg) {
+ panel_cfg[0] = 0;
+ return 0;
+ }
+
+ pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__,
+ pan_cfg->arg_cfg);
+ ctrl_pdata->panel_data.panel_info.is_prim_panel = true;
+ rc = strlcpy(panel_cfg, pan_cfg->arg_cfg,
+ sizeof(pan_cfg->arg_cfg));
+ return rc;
+}
+
+static int mdss_spi_panel_regulator_init(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (!pdev) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ if (!ctrl_pdata) {
+ pr_err("%s: invalid driver data\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_mdss_config_vreg(&pdev->dev,
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 1);
+ if (rc)
+ pr_err("%s: failed to init vregs for %s\n",
+ __func__, "PANEL_PM");
+
+ return rc;
+
+}
+
+static irqreturn_t spi_panel_te_handler(int irq, void *data)
+{
+ struct spi_panel_data *ctrl_pdata = (struct spi_panel_data *)data;
+ static int count = 2;
+
+ if (!ctrl_pdata) {
+ pr_err("%s: SPI display not available\n", __func__);
+ return IRQ_HANDLED;
+ }
+ complete(&ctrl_pdata->spi_panel_te);
+
+ if (ctrl_pdata->vsync_client.handler && !(--count)) {
+ ctrl_pdata->vsync_client.handler(ctrl_pdata->vsync_client.arg);
+ count = 2;
+ }
+
+ return IRQ_HANDLED;
+}
+
+void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client)
+{
+ int updated = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (vsync_client) {
+ if (ctrl_pdata->vsync_client.handler != vsync_client->handler) {
+ ctrl_pdata->vsync_client = *vsync_client;
+ updated = 1;
+ }
+ } else {
+ if (ctrl_pdata->vsync_client.handler) {
+ ctrl_pdata->vsync_client.handler = NULL;
+ ctrl_pdata->vsync_client.arg = NULL;
+ updated = 1;
+ }
+ }
+
+ if (updated) {
+ if (vsync_client && vsync_client->handler)
+ enable_spi_panel_te_irq(ctrl_pdata, true);
+ else
+ enable_spi_panel_te_irq(ctrl_pdata, false);
+ }
+}
+
+static struct device_node *mdss_spi_pref_prim_panel(
+ struct platform_device *pdev)
+{
+ struct device_node *spi_pan_node = NULL;
+
+ pr_debug("%s:%d: Select primary panel from dt\n",
+ __func__, __LINE__);
+ spi_pan_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,spi-pref-prim-pan", 0);
+ if (!spi_pan_node)
+ pr_err("%s:can't find panel phandle\n", __func__);
+
+ return spi_pan_node;
+}
+
+static int spi_panel_device_register(struct device_node *pan_node,
+ struct spi_panel_data *ctrl_pdata)
+{
+ int rc;
+ struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
+ struct device_node *spi_ctrl_np = NULL;
+ struct platform_device *ctrl_pdev = NULL;
+
+ pinfo->type = SPI_PANEL;
+
+ spi_ctrl_np = of_parse_phandle(pan_node,
+ "qcom,mdss-spi-panel-controller", 0);
+ if (!spi_ctrl_np) {
+ pr_err("%s: SPI controller node not initialized\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ ctrl_pdev = of_find_device_by_node(spi_ctrl_np);
+ if (!ctrl_pdev) {
+ of_node_put(spi_ctrl_np);
+ pr_err("%s: SPI controller node not find\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ rc = mdss_spi_panel_regulator_init(ctrl_pdev);
+ if (rc) {
+ pr_err("%s: failed to init regulator, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo,
+ FPS_RESOLUTION_HZ);
+ pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo);
+
+ ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-te-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->disp_te_gpio))
+ pr_err("%s:%d, TE gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->disp_dc_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-spi-dc-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio))
+ pr_err("%s:%d, SPI DC gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-reset-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->rst_gpio))
+ pr_err("%s:%d, reset gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler;
+
+ if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM)
+ mdss_spi_panel_pwm_cfg(ctrl_pdata);
+
+ ctrl_pdata->ctrl_state = CTRL_STATE_UNKNOWN;
+
+ if (pinfo->cont_splash_enabled) {
+ rc = mdss_spi_panel_power_ctrl(&(ctrl_pdata->panel_data),
+ MDSS_PANEL_POWER_ON);
+ if (rc) {
+ pr_err("%s: Panel power on failed\n", __func__);
+ return rc;
+ }
+ if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM)
+ ctrl_pdata->pwm_enabled = 1;
+ pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
+ ctrl_pdata->ctrl_state |=
+ (CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
+ } else {
+ pinfo->panel_power_state = MDSS_PANEL_POWER_OFF;
+ }
+
+ rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data));
+ if (rc) {
+ pr_err("%s: unable to register SPI panel\n", __func__);
+ return rc;
+ }
+
+ pr_debug("%s: Panel data initialized\n", __func__);
+ return 0;
+}
+
+
+/**
+ * mdss_spi_find_panel_of_node(): find device node of spi panel
+ * @pdev: platform_device of the spi ctrl node
+ * @panel_cfg: string containing intf specific config data
+ *
+ * Function finds the panel device node using the interface
+ * specific configuration data. This configuration data is
+ * could be derived from the result of bootloader's GCDB
+ * panel detection mechanism. If such config data doesn't
+ * exist then this panel returns the default panel configured
+ * in the device tree.
+ *
+ * returns pointer to panel node on success, NULL on error.
+ */
+static struct device_node *mdss_spi_find_panel_of_node(
+ struct platform_device *pdev, char *panel_cfg)
+{
+ int len, i;
+ int ctrl_id = pdev->id - 1;
+ char panel_name[MDSS_MAX_PANEL_LEN] = "";
+ char ctrl_id_stream[3] = "0:";
+ char *stream = NULL, *pan = NULL;
+ struct device_node *spi_pan_node = NULL, *mdss_node = NULL;
+
+ len = strlen(panel_cfg);
+ if (!len) {
+ /* no panel cfg chg, parse dt */
+ pr_err("%s:%d: no cmd line cfg present\n",
+ __func__, __LINE__);
+ goto end;
+ } else {
+ if (ctrl_id == 1)
+ strlcpy(ctrl_id_stream, "1:", 3);
+
+ stream = strnstr(panel_cfg, ctrl_id_stream, len);
+ if (!stream) {
+ pr_err("controller config is not present\n");
+ goto end;
+ }
+ stream += 2;
+
+ pan = strnchr(stream, strlen(stream), ':');
+ if (!pan) {
+ strlcpy(panel_name, stream, MDSS_MAX_PANEL_LEN);
+ } else {
+ for (i = 0; (stream + i) < pan; i++)
+ panel_name[i] = *(stream + i);
+ panel_name[i] = 0;
+ }
+
+ pr_debug("%s:%d:%s:%s\n", __func__, __LINE__,
+ panel_cfg, panel_name);
+
+ mdss_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,mdss-mdp", 0);
+ if (!mdss_node) {
+ pr_err("%s: %d: mdss_node null\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ spi_pan_node = of_find_node_by_name(mdss_node,
+ panel_name);
+ if (!spi_pan_node) {
+ pr_err("%s: invalid pan node, selecting prim panel\n",
+ __func__);
+ goto end;
+ }
+ return spi_pan_node;
+ }
+end:
+ if (strcmp(panel_name, NONE_PANEL))
+ spi_pan_node = mdss_spi_pref_prim_panel(pdev);
+ of_node_put(mdss_node);
+ return spi_pan_node;
+}
+
+
+static int mdss_spi_panel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata;
+ struct mdss_panel_cfg *pan_cfg = NULL;
+ struct device_node *spi_pan_node = NULL;
+ bool cmd_cfg_cont_splash = true;
+ char panel_cfg[MDSS_MAX_PANEL_LEN];
+ struct mdss_util_intf *util;
+ const char *ctrl_name;
+
+ util = mdss_get_util_intf();
+ if (util == NULL) {
+ pr_err("Failed to get mdss utility functions\n");
+ return -ENODEV;
+ }
+
+ if (!util->mdp_probe_done) {
+ pr_err("%s: MDP not probed yet\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ if (!pdev->dev.of_node) {
+ pr_err("SPI driver only supports device tree probe\n");
+ return -ENOTSUPP;
+ }
+
+ pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_DSI);
+ if (IS_ERR(pan_cfg)) {
+ pr_err("%s: return MDSS_PANEL_INTF_DSI\n", __func__);
+ return PTR_ERR(pan_cfg);
+ } else if (pan_cfg) {
+ pr_err("%s: DSI is primary\n", __func__);
+ return -ENODEV;
+ }
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ if (!ctrl_pdata) {
+ ctrl_pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct spi_panel_data),
+ GFP_KERNEL);
+ if (!ctrl_pdata) {
+ pr_err("%s: FAILED: cannot alloc spi panel\n",
+ __func__);
+ rc = -ENOMEM;
+ goto error_no_mem;
+ }
+ platform_set_drvdata(pdev, ctrl_pdata);
+ }
+
+ ctrl_pdata->mdss_util = util;
+
+ ctrl_name = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!ctrl_name)
+ pr_info("%s:%d, Ctrl name not specified\n",
+ __func__, __LINE__);
+ else
+ pr_debug("%s: Ctrl name = %s\n",
+ __func__, ctrl_name);
+
+
+ rc = of_platform_populate(pdev->dev.of_node,
+ NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ goto error_no_mem;
+ }
+
+ rc = mdss_spi_panel_pinctrl_init(pdev);
+ if (rc)
+ pr_warn("%s: failed to get pin resources\n", __func__);
+
+ rc = mdss_spi_get_panel_vreg_data(&pdev->dev,
+ &ctrl_pdata->panel_power_data);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to get panel vreg data, rc=%d\n",
+ __func__, rc);
+ goto error_vreg;
+ }
+
+ /* SPI panels can be different between controllers */
+ rc = mdss_spi_get_panel_cfg(panel_cfg, ctrl_pdata);
+ if (!rc)
+ /* spi panel cfg not present */
+ pr_warn("%s:%d:spi specific cfg not present\n",
+ __func__, __LINE__);
+
+ /* find panel device node */
+ spi_pan_node = mdss_spi_find_panel_of_node(pdev, panel_cfg);
+ if (!spi_pan_node) {
+ pr_err("%s: can't find panel node %s\n", __func__, panel_cfg);
+ goto error_pan_node;
+ }
+
+ cmd_cfg_cont_splash = true;
+
+ rc = mdss_spi_panel_init(spi_pan_node, ctrl_pdata, cmd_cfg_cont_splash);
+ if (rc) {
+ pr_err("%s: spi panel init failed\n", __func__);
+ goto error_pan_node;
+ }
+
+ rc = spi_panel_device_register(spi_pan_node, ctrl_pdata);
+ if (rc) {
+ pr_err("%s: spi panel dev reg failed\n", __func__);
+ goto error_pan_node;
+ }
+
+ ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler;
+
+
+ init_completion(&ctrl_pdata->spi_panel_te);
+ mutex_init(&ctrl_pdata->spi_tx_mutex);
+
+ rc = devm_request_irq(&pdev->dev,
+ gpio_to_irq(ctrl_pdata->disp_te_gpio),
+ spi_panel_te_handler, IRQF_TRIGGER_RISING,
+ "TE_GPIO", ctrl_pdata);
+ if (rc) {
+ pr_err("TE request_irq failed.\n");
+ return rc;
+ }
+
+ pr_debug("%s: spi panel initialized\n", __func__);
+ return 0;
+
+error_pan_node:
+ of_node_put(spi_pan_node);
+error_vreg:
+ mdss_spi_put_dt_vreg_data(&pdev->dev,
+ &ctrl_pdata->panel_power_data);
+error_no_mem:
+ devm_kfree(&pdev->dev, ctrl_pdata);
+ return rc;
+}
+
+
+static const struct of_device_id mdss_spi_panel_match[] = {
+ { .compatible = "qcom,mdss-spi-display" },
+ {},
+};
+
+static struct platform_driver this_driver = {
+ .probe = mdss_spi_panel_probe,
+ .driver = {
+ .name = "spi_panel",
+ .owner = THIS_MODULE,
+ .of_match_table = mdss_spi_panel_match,
+ },
+};
+
+static int __init mdss_spi_display_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&this_driver);
+ return 0;
+}
+module_init(mdss_spi_display_init);
+
+MODULE_DEVICE_TABLE(of, mdss_spi_panel_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/fbdev/msm/mdss_spi_panel.h b/drivers/video/fbdev/msm/mdss_spi_panel.h
new file mode 100644
index 0000000..80b7ea8
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_panel.h
@@ -0,0 +1,148 @@
+/* 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 __MDSS_SPI_PANEL_H__
+#define __MDSS_SPI_PANEL_H__
+
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP)
+#include <linux/list.h>
+#include <linux/mdss_io_util.h>
+#include <linux/irqreturn.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
+
+#include "mdss_panel.h"
+#include "mdp3_dma.h"
+
+#define MDSS_MAX_BL_BRIGHTNESS 255
+
+#define MDSS_SPI_RST_SEQ_LEN 10
+
+#define NONE_PANEL "none"
+
+#define CTRL_STATE_UNKNOWN 0x00
+#define CTRL_STATE_PANEL_INIT BIT(0)
+#define CTRL_STATE_MDP_ACTIVE BIT(1)
+
+#define MDSS_PINCTRL_STATE_DEFAULT "mdss_default"
+#define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep"
+#define SPI_PANEL_TE_TIMEOUT 400
+
+enum spi_panel_data_type {
+ panel_cmd,
+ panel_parameter,
+ panel_pixel,
+ UNKNOWN_FORMAT,
+};
+
+enum spi_panel_bl_ctrl {
+ SPI_BL_PWM,
+ SPI_BL_WLED,
+ SPI_BL_DCS_CMD,
+ SPI_UNKNOWN_CTRL,
+};
+
+struct spi_pinctrl_res {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+};
+#define SPI_PANEL_DST_FORMAT_RGB565 0
+
+struct spi_ctrl_hdr {
+ char wait; /* ms */
+ char dlen; /* 8 bits */
+};
+
+struct spi_cmd_desc {
+ struct spi_ctrl_hdr dchdr;
+ char *command;
+ char *parameter;
+};
+
+struct spi_panel_cmds {
+ char *buf;
+ int blen;
+ struct spi_cmd_desc *cmds;
+ int cmd_cnt;
+};
+
+enum spi_panel_status_mode {
+ SPI_ESD_REG,
+ SPI_SEND_PANEL_COMMAND,
+ SPI_ESD_MAX,
+};
+
+
+struct spi_panel_data {
+ struct mdss_panel_data panel_data;
+ struct mdss_util_intf *mdss_util;
+ struct spi_pinctrl_res pin_res;
+ struct mdss_module_power panel_power_data;
+ struct completion spi_panel_te;
+ struct mdp3_notification vsync_client;
+ unsigned int vsync_status;
+ int byte_pre_frame;
+ char *tx_buf;
+ u8 ctrl_state;
+ int disp_te_gpio;
+ int rst_gpio;
+ int disp_dc_gpio; /* command or data */
+ struct spi_panel_cmds on_cmds;
+ struct spi_panel_cmds off_cmds;
+ bool (*check_status)(struct spi_panel_data *pdata);
+ int (*on)(struct mdss_panel_data *pdata);
+ int (*off)(struct mdss_panel_data *pdata);
+ struct mutex spi_tx_mutex;
+ struct pwm_device *pwm_bl;
+ int bklt_ctrl; /* backlight ctrl */
+ bool pwm_pmi;
+ int pwm_period;
+ int pwm_pmic_gpio;
+ int pwm_lpg_chan;
+ int pwm_enabled;
+ int bklt_max;
+ int status_mode;
+ u32 status_cmds_rlen;
+ u8 panel_status_reg;
+ u8 *exp_status_value;
+ u8 *act_status_value;
+ unsigned char *return_buf;
+};
+
+int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int stride);
+int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata);
+void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client);
+void mdp3_check_spi_panel_status(struct work_struct *work,
+ uint32_t interval);
+
+#else
+static inline int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int stride){
+ return 0;
+}
+static inline int is_spi_panel_continuous_splash_on(
+ struct mdss_panel_data *pdata)
+{
+ return 0;
+}
+static inline int mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client){
+ return 0;
+}
+
+#endif/* End of CONFIG_FB_MSM_MDSS_SPI_PANEL && ONFIG_SPI_QUP */
+
+#endif /* End of __MDSS_SPI_PANEL_H__ */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 78c2a9f..ad6d53d 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -286,6 +286,9 @@
POWER_SUPPLY_PROP_BATTERY_INFO,
POWER_SUPPLY_PROP_BATTERY_INFO_ID,
POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
+ POWER_SUPPLY_PROP_ESR_ACTUAL,
+ POWER_SUPPLY_PROP_ESR_NOMINAL,
+ POWER_SUPPLY_PROP_SOH,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/uapi/linux/qg-profile.h b/include/uapi/linux/qg-profile.h
index bffddbb..0230b32 100644
--- a/include/uapi/linux/qg-profile.h
+++ b/include/uapi/linux/qg-profile.h
@@ -55,6 +55,8 @@
#define QG_MAX_FCC_MAH 16000
#define QG_MIN_SLOPE 1
#define QG_MAX_SLOPE 50000
+#define QG_ESR_SF_MIN 5000
+#define QG_ESR_SF_MAX 20000
/* IOCTLs to query battery profile data */
#define BPIOCXSOC _IOWR('B', 0x01, struct battery_params) /* SOC */
diff --git a/include/uapi/linux/qg.h b/include/uapi/linux/qg.h
index 2194e1f..40882a7 100644
--- a/include/uapi/linux/qg.h
+++ b/include/uapi/linux/qg.h
@@ -19,7 +19,7 @@
QG_ESR_CHARGE_SF,
QG_ESR_DISCHARGE_SF,
QG_FULL_SOC,
- QG_RESERVED_8,
+ QG_CLEAR_LEARNT_DATA,
QG_RESERVED_9,
QG_RESERVED_10,
QG_MAX,
@@ -32,6 +32,7 @@
#define QG_ESR_CHARGE_SF QG_ESR_CHARGE_SF
#define QG_ESR_DISCHARGE_SF QG_ESR_DISCHARGE_SF
#define QG_FULL_SOC QG_FULL_SOC
+#define QG_CLEAR_LEARNT_DATA QG_CLEAR_LEARNT_DATA
struct fifo_data {
unsigned int v;
diff --git a/scripts/build-all.py b/scripts/build-all.py
index bd468cd..4a60ebc 100755
--- a/scripts/build-all.py
+++ b/scripts/build-all.py
@@ -59,12 +59,8 @@
def check_kernel():
"""Ensure that PWD is a kernel directory"""
- have_defconfig = any([
- os.path.isfile('arch/arm64/configs/msm_defconfig'),
- os.path.isfile('arch/arm64/configs/sdm845_defconfig')])
-
- if not all([os.path.isfile('MAINTAINERS'), have_defconfig]):
- fail("This doesn't seem to be an MSM kernel dir")
+ if not os.path.isfile('MAINTAINERS'):
+ fail("This doesn't seem to be a kernel dir")
def check_build():
"""Ensure that the build directory is present."""