瀏覽代碼

disp: msm: dsi: snapshot of dsi from 4.14 to 4.19

This change is a snapshot of dsi files taken of 4.14
as of commit 764f7c2 (Merge remote-tracking branch
'quic/dev/msm-4.14-display' into msm-4.14)

Change-Id: I8361a844c35a4450f7800964a8da2741676fd6c7
Signed-off-by: Satya Rama Aditya Pinapala <[email protected]>
Satya Rama Aditya Pinapala 6 年之前
父節點
當前提交
edef6ae040

+ 2 - 0
msm/dsi/dsi_catalog.c

@@ -64,6 +64,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
 	ctrl->ops.set_continuous_clk = dsi_ctrl_hw_cmn_set_continuous_clk;
 	ctrl->ops.wait4dynamic_refresh_done =
 		dsi_ctrl_hw_cmn_wait4dynamic_refresh_done;
+	ctrl->ops.hs_req_sel = dsi_ctrl_hw_cmn_hs_req_sel;
 
 	switch (version) {
 	case DSI_CTRL_VERSION_1_4:
@@ -261,6 +262,7 @@ static void dsi_catalog_phy_4_0_init(struct dsi_phy_hw *phy)
 		dsi_phy_hw_v4_0_dyn_refresh_helper;
 	phy->ops.dyn_refresh_ops.cache_phy_timings =
 		dsi_phy_hw_v4_0_cache_phy_timings;
+	phy->ops.set_continuous_clk = dsi_phy_hw_v4_0_set_continuous_clk;
 }
 
 /**

+ 2 - 0
msm/dsi/dsi_catalog.h

@@ -115,6 +115,7 @@ int dsi_phy_hw_timing_val_v4_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
 int dsi_phy_hw_v4_0_lane_reset(struct dsi_phy_hw *phy);
 void dsi_phy_hw_v4_0_toggle_resync_fifo(struct dsi_phy_hw *phy);
 void dsi_phy_hw_v4_0_reset_clk_en_sel(struct dsi_phy_hw *phy);
+void dsi_phy_hw_v4_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable);
 
 /* DSI controller common ops */
 u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
@@ -239,6 +240,7 @@ void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable,
 		enum dsi_clk_gate_type clk_selection);
 
 void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable);
+void dsi_ctrl_hw_cmn_hs_req_sel(struct dsi_ctrl_hw *ctrl, bool sel_phy);
 
 /* dynamic refresh specific functions */
 void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset);

+ 6 - 14
msm/dsi/dsi_clk_manager.c

@@ -1272,23 +1272,15 @@ static int dsi_display_link_clk_force_update(void *client)
 		goto error;
 	}
 
-	rc = dsi_display_link_clk_disable(l_clks,
-			(DSI_LINK_LP_CLK | DSI_LINK_HS_CLK),
-			mngr->dsi_ctrl_count, mngr->master_ndx);
-	if (rc) {
-		pr_err("%s, failed to stop link clk, rc = %d\n",
-			__func__, rc);
+	rc = dsi_clk_update_link_clk_state(mngr, l_clks, (DSI_LINK_LP_CLK |
+				DSI_LINK_HS_CLK), DSI_CLK_OFF, false);
+	if (rc)
 		goto error;
-	}
 
-	rc = dsi_display_link_clk_enable(l_clks,
-			(DSI_LINK_LP_CLK | DSI_LINK_HS_CLK),
-			mngr->dsi_ctrl_count, mngr->master_ndx);
-	if (rc) {
-		pr_err("%s, failed to start link clk rc= %d\n",
-			__func__, rc);
+	rc = dsi_clk_update_link_clk_state(mngr, l_clks, (DSI_LINK_LP_CLK |
+				DSI_LINK_HS_CLK), DSI_CLK_ON, true);
+	if (rc)
 		goto error;
-	}
 
 error:
 	mutex_unlock(&mngr->clk_mutex);

+ 58 - 27
msm/dsi/dsi_ctrl.c

@@ -34,22 +34,6 @@
 
 #define TICKS_IN_MICRO_SECOND    1000000
 
-/**
- * enum dsi_ctrl_driver_ops - controller driver ops
- */
-enum dsi_ctrl_driver_ops {
-	DSI_CTRL_OP_POWER_STATE_CHANGE,
-	DSI_CTRL_OP_CMD_ENGINE,
-	DSI_CTRL_OP_VID_ENGINE,
-	DSI_CTRL_OP_HOST_ENGINE,
-	DSI_CTRL_OP_CMD_TX,
-	DSI_CTRL_OP_HOST_INIT,
-	DSI_CTRL_OP_TPG,
-	DSI_CTRL_OP_PHY_SW_RESET,
-	DSI_CTRL_OP_ASYNC_TIMING,
-	DSI_CTRL_OP_MAX
-};
-
 struct dsi_ctrl_list_item {
 	struct dsi_ctrl *ctrl;
 	struct list_head list;
@@ -833,6 +817,7 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl,
 	u64 h_period, v_period, bit_rate, pclk_rate, bit_rate_per_lane,
 	    byte_clk_rate;
 	struct dsi_host_common_cfg *host_cfg = &config->common_config;
+	struct dsi_split_link_config *split_link = &host_cfg->split_link;
 	struct dsi_mode_info *timing = &config->video_timing;
 	u64 dsi_transfer_time_us = mode->priv_info->dsi_transfer_time_us;
 	u64 min_dsi_clk_hz = mode->priv_info->min_dsi_clk_hz;
@@ -850,6 +835,9 @@ static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl,
 	if (host_cfg->data_lanes & DSI_DATA_LANE_3)
 		num_of_lanes++;
 
+	if (split_link->split_link_enabled)
+		num_of_lanes = split_link->lanes_per_sublink;
+
 	config->common_config.num_data_lanes = num_of_lanes;
 	config->common_config.bpp = bpp;
 
@@ -943,6 +931,7 @@ static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl,
 	int rc = 0;
 	u8 *buf = NULL;
 	u32 len, i;
+	u8 cmd_type = 0;
 
 	len = packet->size;
 	len += 0x3; len &= ~0x03; /* Align to 32 bits */
@@ -965,7 +954,11 @@ static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl,
 
 
 	/* send embedded BTA for read commands */
-	if ((buf[2] & 0x3f) == MIPI_DSI_DCS_READ)
+	cmd_type = buf[2] & 0x3f;
+	if ((cmd_type == MIPI_DSI_DCS_READ) ||
+			(cmd_type == MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM) ||
+			(cmd_type == MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM) ||
+			(cmd_type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM))
 		buf[3] |= BIT(5);
 
 	*buffer = buf;
@@ -1509,7 +1502,7 @@ static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl,
 	cmd = buff[0];
 	switch (cmd) {
 	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
-		pr_err("Rx ACK_ERROR\n");
+		pr_err("Rx ACK_ERROR 0x%x\n", cmd);
 		rc = 0;
 		break;
 	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
@@ -1525,7 +1518,7 @@ static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl,
 		rc = dsi_parse_long_read_resp(msg, buff);
 		break;
 	default:
-		pr_warn("Invalid response\n");
+		pr_warn("Invalid response: 0x%x\n", cmd);
 		rc = 0;
 	}
 
@@ -1601,6 +1594,18 @@ static int dsi_disable_ulps(struct dsi_ctrl *dsi_ctrl)
 	return rc;
 }
 
+static void dsi_ctrl_enable_error_interrupts(struct dsi_ctrl *dsi_ctrl)
+{
+	if (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE &&
+			!dsi_ctrl->host_config.u.video_engine.bllp_lp11_en &&
+			!dsi_ctrl->host_config.u.video_engine.eof_bllp_lp11_en)
+		dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw,
+				0xFF00A0);
+	else
+		dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw,
+				0xFF00E0);
+}
+
 static int dsi_ctrl_drv_state_init(struct dsi_ctrl *dsi_ctrl)
 {
 	int rc = 0;
@@ -1730,6 +1735,9 @@ static int dsi_ctrl_dts_parse(struct dsi_ctrl *dsi_ctrl,
 	dsi_ctrl->null_insertion_enabled = of_property_read_bool(of_node,
 					"qcom,null-insertion-enabled");
 
+	dsi_ctrl->split_link_supported = of_property_read_bool(of_node,
+					"qcom,split-link-supported");
+
 	rc = of_property_read_u32(of_node, "frame-threshold-time-us",
 			&frame_threshold_time_us);
 	if (rc) {
@@ -2217,7 +2225,7 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
 	}
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
+	dsi_ctrl_enable_error_interrupts(dsi_ctrl);
 	dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
@@ -2340,6 +2348,12 @@ static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
 	if (error & 0x3000E00)
 		pr_err("dsi PHY contention error: 0x%lx\n", error);
 
+	/* ignore TX timeout if blpp_lp11 is disabled */
+	if (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE &&
+			!dsi_ctrl->host_config.u.video_engine.bllp_lp11_en &&
+			!dsi_ctrl->host_config.u.video_engine.eof_bllp_lp11_en)
+		error &= ~DSI_HS_TX_TIMEOUT;
+
 	/* TX timeout error */
 	if (error & 0xE0) {
 		if (error & 0xA0) {
@@ -2637,27 +2651,34 @@ int dsi_ctrl_host_timing_update(struct dsi_ctrl *dsi_ctrl)
 }
 
 /**
- * dsi_ctrl_update_host_init_state() - Update the host initialization state.
+ * dsi_ctrl_update_host_state() - Update the host initialization state.
  * @dsi_ctrl:        DSI controller handle.
+ * @op:			ctrl driver ops
  * @enable:        boolean signifying host state.
  *
- * Update the host initialization status only while exiting from ulps during
- * suspend state.
+ * Update the host status only while exiting from ulps during suspend state.
  *
  * Return: error code.
  */
-int dsi_ctrl_update_host_init_state(struct dsi_ctrl *dsi_ctrl, bool enable)
+int dsi_ctrl_update_host_state(struct dsi_ctrl *dsi_ctrl,
+				enum dsi_ctrl_driver_ops op, bool enable)
 {
 	int rc = 0;
 	u32 state = enable ? 0x1 : 0x0;
 
-	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, state);
+	if (!dsi_ctrl)
+		return rc;
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	rc = dsi_ctrl_check_state(dsi_ctrl, op, state);
 	if (rc) {
 		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
 		       dsi_ctrl->cell_index, rc);
+		mutex_unlock(&dsi_ctrl->ctrl_lock);
 		return rc;
 	}
-	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, state);
+
+	dsi_ctrl_update_state(dsi_ctrl, op, state);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
 }
 
@@ -2720,7 +2741,7 @@ int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool is_splash_enabled)
 	}
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
+	dsi_ctrl_enable_error_interrupts(dsi_ctrl);
 
 	pr_debug("[DSI_%d]Host initialization complete, continuous splash status:%d\n",
 		dsi_ctrl->cell_index, is_splash_enabled);
@@ -2749,6 +2770,16 @@ void dsi_ctrl_isr_configure(struct dsi_ctrl *dsi_ctrl, bool enable)
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 }
 
+void dsi_ctrl_hs_req_sel(struct dsi_ctrl *dsi_ctrl, bool sel_phy)
+{
+	if (!dsi_ctrl)
+		return;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	dsi_ctrl->hw.ops.hs_req_sel(&dsi_ctrl->hw, sel_phy);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+}
+
 void dsi_ctrl_set_continuous_clk(struct dsi_ctrl *dsi_ctrl, bool enable)
 {
 	if (!dsi_ctrl)

+ 29 - 2
msm/dsi/dsi_ctrl.h

@@ -75,6 +75,22 @@ enum dsi_engine_state {
 	DSI_CTRL_ENGINE_MAX,
 };
 
+/**
+ * enum dsi_ctrl_driver_ops - controller driver ops
+ */
+enum dsi_ctrl_driver_ops {
+	DSI_CTRL_OP_POWER_STATE_CHANGE,
+	DSI_CTRL_OP_CMD_ENGINE,
+	DSI_CTRL_OP_VID_ENGINE,
+	DSI_CTRL_OP_HOST_ENGINE,
+	DSI_CTRL_OP_CMD_TX,
+	DSI_CTRL_OP_HOST_INIT,
+	DSI_CTRL_OP_TPG,
+	DSI_CTRL_OP_PHY_SW_RESET,
+	DSI_CTRL_OP_ASYNC_TIMING,
+	DSI_CTRL_OP_MAX
+};
+
 /**
  * struct dsi_ctrl_power_info - digital and analog power supplies for dsi host
  * @digital:  Digital power supply required to turn on DSI controller hardware.
@@ -212,6 +228,7 @@ struct dsi_ctrl_interrupts {
  * @null_insertion_enabled:  A boolean property to allow dsi controller to
  *                           insert null packet.
  * @modeupdated:	  Boolean to send new roi if mode is updated.
+ * @split_link_supported: Boolean to check if hw supports split link.
  */
 struct dsi_ctrl {
 	struct platform_device *pdev;
@@ -267,6 +284,7 @@ struct dsi_ctrl {
 	bool phy_isolation_enabled;
 	bool null_insertion_enabled;
 	bool modeupdated;
+	bool split_link_supported;
 };
 
 /**
@@ -786,15 +804,24 @@ int dsi_ctrl_get_host_engine_init_state(struct dsi_ctrl *dsi_ctrl,
  */
 int dsi_ctrl_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl *dsi_ctrl);
 /**
- * dsi_ctrl_update_host_init_state() - Set the host initialization state
+ * dsi_ctrl_update_host_state() - Set the host state
  */
-int dsi_ctrl_update_host_init_state(struct dsi_ctrl *dsi_ctrl, bool en);
+int dsi_ctrl_update_host_state(struct dsi_ctrl *dsi_ctrl,
+					enum dsi_ctrl_driver_ops op, bool en);
 
 /**
  * dsi_ctrl_pixel_format_to_bpp() - returns number of bits per pxl
  */
 int dsi_ctrl_pixel_format_to_bpp(enum dsi_pixel_format dst_format);
 
+/**
+ * dsi_ctrl_hs_req_sel() - API to enable continuous clk support through phy
+ * @dsi_ctrl:			DSI controller handle.
+ * @sel_phy:			Boolean to control whether to select phy or
+ *				controller
+ */
+void dsi_ctrl_hs_req_sel(struct dsi_ctrl *dsi_ctrl, bool sel_phy);
+
 /**
  * dsi_ctrl_set_continuous_clk() - API to set/unset force clock lane HS request.
  * @dsi_ctrl:                      DSI controller handle.

+ 6 - 0
msm/dsi/dsi_ctrl_hw.h

@@ -823,6 +823,12 @@ struct dsi_ctrl_hw_ops {
 	 * @ctrl:         Pointer to the controller host hardware.
 	 */
 	int (*wait4dynamic_refresh_done)(struct dsi_ctrl_hw *ctrl);
+	/**
+	 * hw.ops.hs_req_sel() - enable continuous clk support through phy
+	 * @ctrl:	Pointer to the controller host hardware.
+	 * @sel_phy:	Bool to control whether to select phy or controller
+	 */
+	void (*hs_req_sel)(struct dsi_ctrl_hw *ctrl, bool sel_phy);
 };
 
 /*

+ 48 - 0
msm/dsi/dsi_ctrl_hw_cmn.c

@@ -19,6 +19,8 @@
 #define DSI_CTRL_DYNAMIC_FORCE_ON         (0x23F|BIT(8)|BIT(9)|BIT(11)|BIT(21))
 #define DSI_CTRL_CMD_MISR_ENABLE          BIT(28)
 #define DSI_CTRL_VIDEO_MISR_ENABLE        BIT(16)
+#define DSI_CTRL_DMA_LINK_SEL             (BIT(12)|BIT(13))
+#define DSI_CTRL_MDP0_LINK_SEL            (BIT(20)|BIT(22))
 
 /* Unsupported formats default to RGB888 */
 static const u8 cmd_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
@@ -26,6 +28,38 @@ static const u8 cmd_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
 static const u8 video_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
 	0x0, 0x1, 0x2, 0x3, 0x3, 0x3, 0x3 };
 
+/**
+ * dsi_split_link_setup() - setup dsi split link configurations
+ * @ctrl:             Pointer to the controller host hardware.
+ * @cfg:              DSI host configuration that is common to both video and
+ *                    command modes.
+ */
+static void dsi_split_link_setup(struct dsi_ctrl_hw *ctrl,
+				struct dsi_host_common_cfg *cfg)
+{
+	u32 reg;
+
+	if (!cfg->split_link.split_link_enabled)
+		return;
+
+	reg = DSI_R32(ctrl, DSI_SPLIT_LINK);
+
+	/* DMA_LINK_SEL */
+	reg &= ~(0x7 << 12);
+	reg |= DSI_CTRL_DMA_LINK_SEL;
+
+	/* MDP0_LINK_SEL */
+	reg &= ~(0x7 << 20);
+	reg |= DSI_CTRL_MDP0_LINK_SEL;
+
+	/* EN */
+	reg |= 0x1;
+
+	/* DSI_SPLIT_LINK */
+	DSI_W32(ctrl, DSI_SPLIT_LINK, reg);
+	wmb(); /* make sure split link is asserted */
+}
+
 /**
  * dsi_setup_trigger_controls() - setup dsi trigger configurations
  * @ctrl:             Pointer to the controller host hardware.
@@ -57,6 +91,7 @@ void dsi_ctrl_hw_cmn_host_setup(struct dsi_ctrl_hw *ctrl,
 	u32 reg_value = 0;
 
 	dsi_setup_trigger_controls(ctrl, cfg);
+	dsi_split_link_setup(ctrl, cfg);
 
 	/* Setup clocking timing controls */
 	reg_value = ((cfg->t_clk_post & 0x3F) << 8);
@@ -1533,6 +1568,19 @@ int dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle(struct dsi_ctrl_hw *ctrl)
 	return rc;
 }
 
+void dsi_ctrl_hw_cmn_hs_req_sel(struct dsi_ctrl_hw *ctrl, bool sel_phy)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, DSI_LANE_CTRL);
+	if (sel_phy)
+		reg &= ~BIT(24);
+	else
+		reg |= BIT(24);
+	DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+	wmb(); /* make sure request is set */
+}
+
 void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable)
 {
 	u32 reg = 0;

+ 1 - 0
msm/dsi/dsi_ctrl_reg.h

@@ -145,6 +145,7 @@
 #define DSI_SECURE_DISPLAY_BLOCK_COMMAND_COLOR     (0x02D0)
 #define DSI_SECURE_DISPLAY_BLOCK_VIDEO_COLOR       (0x02D4)
 #define DSI_LOGICAL_LANE_SWAP_CTRL                 (0x0310)
+#define DSI_SPLIT_LINK                             (0x0330)
 
 
 #endif /* _DSI_CTRL_REG_H_ */

+ 151 - 26
msm/dsi/dsi_display.c

@@ -78,6 +78,11 @@ static int dsi_display_config_clk_gating(struct dsi_display *display,
 		return -EINVAL;
 	}
 
+	if (display->panel->host_config.force_hs_clk_lane) {
+		pr_debug("no dsi clock gating for continuous clock mode\n");
+		return 0;
+	}
+
 	mctrl = &display->ctrl[display->clk_master_idx];
 	if (!mctrl) {
 		pr_err("Invalid controller\n");
@@ -586,8 +591,11 @@ static bool dsi_display_validate_reg_read(struct dsi_panel *panel)
 	for (j = 0; j < config->groups; ++j) {
 		for (i = 0; i < len; ++i) {
 			if (config->return_buf[i] !=
-				config->status_value[group + i])
+				config->status_value[group + i]) {
+				DRM_ERROR("mismatch: 0x%x\n",
+						config->return_buf[i]);
 				break;
+			}
 		}
 
 		if (i == len)
@@ -828,6 +836,11 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 	if (te_check_override && gpio_is_valid(dsi_display->disp_te_gpio))
 		status_mode = ESD_MODE_PANEL_TE;
 
+	if (status_mode == ESD_MODE_PANEL_TE) {
+		rc = dsi_display_status_check_te(dsi_display);
+		goto exit;
+	}
+
 	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
 		DSI_ALL_CLKS, DSI_CLK_ON);
 
@@ -843,23 +856,25 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 	} else if (status_mode == ESD_MODE_PANEL_TE) {
 		rc = dsi_display_status_check_te(dsi_display);
 	} else {
-		pr_warn("unsupported check status mode\n");
+		pr_warn("Unsupported check status mode: %d\n", status_mode);
 		panel->esd_config.esd_enabled = false;
 	}
 
-	/* Unmask error interrupts */
+	/* Unmask error interrupts if check passed*/
 	if (rc > 0) {
 		dsi_display_set_ctrl_esd_check_flag(dsi_display, false);
 		dsi_display_mask_ctrl_error_interrupts(dsi_display, mask,
 							false);
-	} else {
-		/* Handle Panel failures during display disable sequence */
-		atomic_set(&panel->esd_recovery_pending, 1);
 	}
 
 	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
 		DSI_ALL_CLKS, DSI_CLK_OFF);
 
+exit:
+	/* Handle Panel failures during display disable sequence */
+	if (rc <=0)
+		atomic_set(&panel->esd_recovery_pending, 1);
+
 release_panel_lock:
 	dsi_panel_release_panel_lock(panel);
 	SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
@@ -1620,12 +1635,25 @@ static int dsi_display_debugfs_deinit(struct dsi_display *display)
 static void adjust_timing_by_ctrl_count(const struct dsi_display *display,
 					struct dsi_display_mode *mode)
 {
-	mode->timing.h_active /= display->ctrl_count;
-	mode->timing.h_front_porch /= display->ctrl_count;
-	mode->timing.h_sync_width /= display->ctrl_count;
-	mode->timing.h_back_porch /= display->ctrl_count;
-	mode->timing.h_skew /= display->ctrl_count;
-	mode->pixel_clk_khz /= display->ctrl_count;
+	struct dsi_host_common_cfg *host = &display->panel->host_config;
+	bool is_split_link = host->split_link.split_link_enabled;
+	u32 sublinks_count = host->split_link.num_sublinks;
+
+	if (is_split_link && sublinks_count > 1) {
+		mode->timing.h_active /= sublinks_count;
+		mode->timing.h_front_porch /= sublinks_count;
+		mode->timing.h_sync_width /= sublinks_count;
+		mode->timing.h_back_porch /= sublinks_count;
+		mode->timing.h_skew /= sublinks_count;
+		mode->pixel_clk_khz /= sublinks_count;
+	} else {
+		mode->timing.h_active /= display->ctrl_count;
+		mode->timing.h_front_porch /= display->ctrl_count;
+		mode->timing.h_sync_width /= display->ctrl_count;
+		mode->timing.h_back_porch /= display->ctrl_count;
+		mode->timing.h_skew /= display->ctrl_count;
+		mode->pixel_clk_khz /= display->ctrl_count;
+	}
 }
 
 static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
@@ -2366,7 +2394,9 @@ static int dsi_display_ctrl_init(struct dsi_display *display)
 	} else {
 		display_for_each_ctrl(i, display) {
 			ctrl = &display->ctrl[i];
-			rc = dsi_ctrl_update_host_init_state(ctrl->ctrl, true);
+			rc = dsi_ctrl_update_host_state(ctrl->ctrl,
+							DSI_CTRL_OP_HOST_INIT,
+							true);
 			if (rc)
 				pr_debug("host init update failed rc=%d\n", rc);
 		}
@@ -2450,6 +2480,25 @@ static int dsi_display_ctrl_host_disable(struct dsi_display *display)
 	struct dsi_display_ctrl *m_ctrl, *ctrl;
 
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	/*
+	 * For platforms where ULPS is controlled by DSI controller block,
+	 * do not disable dsi controller block if lanes are to be
+	 * kept in ULPS during suspend. So just update the SW state
+	 * and return early.
+	 */
+	if (display->panel->ulps_suspend_enabled &&
+			!m_ctrl->phy->hw.ops.ulps_ops.ulps_request) {
+		display_for_each_ctrl(i, display) {
+			ctrl = &display->ctrl[i];
+			rc = dsi_ctrl_update_host_state(ctrl->ctrl,
+					DSI_CTRL_OP_HOST_ENGINE,
+					false);
+			if (rc)
+				pr_debug("host state update failed %d\n", rc);
+		}
+		return rc;
+	}
+
 	display_for_each_ctrl(i, display) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl || (ctrl == m_ctrl))
@@ -4609,6 +4658,42 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display)
 	return rc;
 }
 
+static int dsi_display_validate_split_link(struct dsi_display *display)
+{
+	int i, rc = 0;
+	struct dsi_display_ctrl *ctrl;
+	struct dsi_host_common_cfg *host = &display->panel->host_config;
+
+	if (!host->split_link.split_link_enabled)
+		return 0;
+
+	if (display->panel->panel_mode == DSI_OP_CMD_MODE) {
+		pr_err("[%s] split link is not supported in command mode\n",
+			display->name);
+		rc = -ENOTSUPP;
+		goto error;
+	}
+
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl->split_link_supported) {
+			pr_err("[%s] split link is not supported by hw\n",
+				display->name);
+			rc = -ENOTSUPP;
+			goto error;
+		}
+
+		set_bit(DSI_PHY_SPLIT_LINK, ctrl->phy->hw.feature_map);
+	}
+
+	pr_debug("Split link is enabled\n");
+	return 0;
+
+error:
+	host->split_link.split_link_enabled = false;
+	return rc;
+}
+
 /**
  * dsi_display_bind - bind dsi device with controlling device
  * @dev:        Pointer to base of platform device
@@ -4649,8 +4734,28 @@ static int dsi_display_bind(struct device *dev,
 
 	if (!display->fw)
 		display->name = display->panel_node->name;
+
+	/* defer bind if ext bridge driver is not loaded */
+	if (display->panel && display->panel->host_config.ext_bridge_mode) {
+		for (i = 0; i < display->ext_bridge_cnt; i++) {
+			if (!of_drm_find_bridge(
+					display->ext_bridge[i].node_of)) {
+				pr_debug("defer for bridge[%d] %s\n", i,
+				  display->ext_bridge[i].node_of->full_name);
+				return -EPROBE_DEFER;
+			}
+		}
+	}
+
 	mutex_lock(&display->display_lock);
 
+	rc = dsi_display_validate_split_link(display);
+	if (rc) {
+		pr_err("[%s] split link validation failed, rc=%d\n",
+						 display->name, rc);
+		goto error;
+	}
+
 	rc = dsi_display_debugfs_init(display);
 	if (rc) {
 		pr_err("[%s] debugfs init failed, rc=%d\n", display->name, rc);
@@ -5273,7 +5378,7 @@ static struct dsi_display_ext_bridge *dsi_display_ext_get_bridge(
 		sde_conn = to_sde_connector(conn_iter);
 		if (sde_conn->encoder == bridge->encoder) {
 			display = sde_conn->display;
-			for (i = 0; i < display->ctrl_count; i++) {
+			display_for_each_ctrl(i, display) {
 				if (display->ext_bridge[i].bridge == bridge)
 					return &display->ext_bridge[i];
 			}
@@ -5440,6 +5545,9 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
 	struct drm_bridge *prev_bridge = bridge;
 	int rc = 0, i;
 
+	if (display->panel && !display->panel->host_config.ext_bridge_mode)
+		return 0;
+
 	for (i = 0; i < display->ext_bridge_cnt; i++) {
 		struct dsi_display_ext_bridge *ext_bridge_info =
 				&display->ext_bridge[i];
@@ -5750,8 +5858,10 @@ int dsi_display_get_modes(struct dsi_display *display,
 {
 	struct dsi_dfps_capabilities dfps_caps;
 	struct dsi_display_ctrl *ctrl;
+	struct dsi_host_common_cfg *host = &display->panel->host_config;
+	bool is_split_link;
 	u32 num_dfps_rates, panel_mode_count, total_mode_count;
-	u32 mode_idx, array_idx = 0;
+	u32 sublinks_count, mode_idx, array_idx = 0;
 	struct dsi_dyn_clk_caps *dyn_clk_caps;
 	int i, start, end, rc = -EINVAL;
 
@@ -5827,15 +5937,25 @@ int dsi_display_get_modes(struct dsi_display *display,
 				panel_mode.timing.dsi_transfer_time_us;
 		}
 
-		panel_mode.timing.h_active *= display->ctrl_count;
-		panel_mode.timing.h_front_porch *= display->ctrl_count;
-		panel_mode.timing.h_sync_width *= display->ctrl_count;
-		panel_mode.timing.h_back_porch *= display->ctrl_count;
-		panel_mode.timing.h_skew *= display->ctrl_count;
-		panel_mode.pixel_clk_khz *= display->ctrl_count;
+		is_split_link = host->split_link.split_link_enabled;
+		sublinks_count = host->split_link.num_sublinks;
+		if (is_split_link && sublinks_count > 1) {
+			panel_mode.timing.h_active *= sublinks_count;
+			panel_mode.timing.h_front_porch *= sublinks_count;
+			panel_mode.timing.h_sync_width *= sublinks_count;
+			panel_mode.timing.h_back_porch *= sublinks_count;
+			panel_mode.timing.h_skew *= sublinks_count;
+			panel_mode.pixel_clk_khz *= sublinks_count;
+		} else {
+			panel_mode.timing.h_active *= display->ctrl_count;
+			panel_mode.timing.h_front_porch *= display->ctrl_count;
+			panel_mode.timing.h_sync_width *= display->ctrl_count;
+			panel_mode.timing.h_back_porch *= display->ctrl_count;
+			panel_mode.timing.h_skew *= display->ctrl_count;
+			panel_mode.pixel_clk_khz *= display->ctrl_count;
+		}
 
 		start = array_idx;
-
 		for (i = 0; i < num_dfps_rates; i++) {
 			struct dsi_display_mode *sub_mode =
 					&display->modes[array_idx];
@@ -5886,6 +6006,7 @@ int dsi_display_get_panel_vfp(void *dsi_display,
 	u32 count, refresh_rate = 0;
 	struct dsi_dfps_capabilities dfps_caps;
 	struct dsi_display *display = (struct dsi_display *)dsi_display;
+	struct dsi_host_common_cfg *host;
 
 	if (!display)
 		return -EINVAL;
@@ -5909,7 +6030,11 @@ int dsi_display_get_panel_vfp(void *dsi_display,
 		return -EINVAL;
 	}
 
-	h_active *= display->ctrl_count;
+	host = &display->panel->host_config;
+	if (host->split_link.split_link_enabled)
+		h_active *= host->split_link.num_sublinks;
+	else
+		h_active *= display->ctrl_count;
 
 	for (i = 0; i < count; i++) {
 		struct dsi_display_mode *m = &display->modes[i];
@@ -6574,7 +6699,8 @@ int dsi_display_prepare(struct dsi_display *display)
 		if (display->is_cont_splash_enabled &&
 		    display->config.panel_mode == DSI_OP_VIDEO_MODE) {
 			pr_err("DMS not supported on first frame\n");
-			return -EINVAL;
+			rc = -EINVAL;
+			goto error;
 		}
 
 		/* update dsi ctrl for new mode */
@@ -6771,8 +6897,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable)
 
 	mutex_lock(&display->display_lock);
 
-	for (i = 0; i < display->ctrl_count; i++) {
-
+	display_for_each_ctrl(i, display) {
 		if (enable) {
 			/* send the commands to enable qsync */
 			rc = dsi_panel_send_qsync_on_dcs(display->panel, i);

+ 171 - 37
msm/dsi/dsi_panel.c

@@ -8,6 +8,7 @@
 #include <linux/slab.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
+#include <linux/pwm.h>
 #include <video/mipi_display.h>
 
 #include "dsi_panel.h"
@@ -587,17 +588,16 @@ error:
 static int dsi_panel_wled_register(struct dsi_panel *panel,
 		struct dsi_backlight_config *bl)
 {
-	int rc = 0;
 	struct backlight_device *bd;
 
 	bd = backlight_device_get_by_type(BACKLIGHT_RAW);
 	if (!bd) {
 		pr_err("[%s] fail raw backlight register\n", panel->name);
-		rc = -EINVAL;
+		return -EPROBE_DEFER;
 	}
 
 	bl->raw_bd = bd;
-	return rc;
+	return 0;
 }
 
 static int dsi_panel_update_backlight(struct dsi_panel *panel,
@@ -620,6 +620,57 @@ static int dsi_panel_update_backlight(struct dsi_panel *panel,
 	return rc;
 }
 
+static int dsi_panel_update_pwm_backlight(struct dsi_panel *panel,
+	u32 bl_lvl)
+{
+	int rc = 0;
+	u32 duty = 0;
+	u32 period_ns = 0;
+	struct dsi_backlight_config *bl;
+
+	if (!panel) {
+		pr_err("Invalid Params\n");
+		return -EINVAL;
+	}
+
+	bl = &panel->bl_config;
+	if (!bl->pwm_bl) {
+		pr_err("pwm device not found\n");
+		return -EINVAL;
+	}
+
+	period_ns = bl->pwm_period_usecs * NSEC_PER_USEC;
+	duty = bl_lvl * period_ns;
+	duty /= bl->bl_max_level;
+
+	rc = pwm_config(bl->pwm_bl, duty, period_ns);
+	if (rc) {
+		pr_err("[%s] failed to change pwm config, rc=\n", panel->name,
+			rc);
+		goto error;
+	}
+
+	if (bl_lvl == 0 && bl->pwm_enabled) {
+		pwm_disable(bl->pwm_bl);
+		bl->pwm_enabled = false;
+		return 0;
+	}
+
+	if (!bl->pwm_enabled) {
+		rc = pwm_enable(bl->pwm_bl);
+		if (rc) {
+			pr_err("[%s] failed to enable pwm, rc=\n", panel->name,
+				rc);
+			goto error;
+		}
+
+		bl->pwm_enabled = true;
+	}
+
+error:
+	return rc;
+}
+
 int dsi_panel_set_backlight(struct dsi_panel *panel, u32 bl_lvl)
 {
 	int rc = 0;
@@ -638,6 +689,9 @@ int dsi_panel_set_backlight(struct dsi_panel *panel, u32 bl_lvl)
 		break;
 	case DSI_BACKLIGHT_EXTERNAL:
 		break;
+	case DSI_BACKLIGHT_PWM:
+		rc = dsi_panel_update_pwm_backlight(panel, bl_lvl);
+		break;
 	default:
 		pr_err("Backlight type(%d) not supported\n", bl->type);
 		rc = -ENOTSUPP;
@@ -662,6 +716,7 @@ static u32 dsi_panel_get_brightness(struct dsi_backlight_config *bl)
 		break;
 	case DSI_BACKLIGHT_DCS:
 	case DSI_BACKLIGHT_EXTERNAL:
+	case DSI_BACKLIGHT_PWM:
 	default:
 		/*
 		 * Ideally, we should read the backlight level from the
@@ -681,6 +736,22 @@ void dsi_panel_bl_handoff(struct dsi_panel *panel)
 	bl->bl_level = dsi_panel_get_brightness(bl);
 }
 
+static int dsi_panel_pwm_register(struct dsi_panel *panel)
+{
+	int rc = 0;
+	struct dsi_backlight_config *bl = &panel->bl_config;
+
+	bl->pwm_bl = devm_of_pwm_get(panel->parent, panel->panel_of_node, NULL);
+	if (IS_ERR_OR_NULL(bl->pwm_bl)) {
+		rc = PTR_ERR(bl->pwm_bl);
+		pr_err("[%s] failed to request pwm, rc=%d\n", panel->name,
+			rc);
+		return rc;
+	}
+
+	return 0;
+}
+
 static int dsi_panel_bl_register(struct dsi_panel *panel)
 {
 	int rc = 0;
@@ -697,6 +768,9 @@ static int dsi_panel_bl_register(struct dsi_panel *panel)
 		break;
 	case DSI_BACKLIGHT_EXTERNAL:
 		break;
+	case DSI_BACKLIGHT_PWM:
+		rc = dsi_panel_pwm_register(panel);
+		break;
 	default:
 		pr_err("Backlight type(%d) not supported\n", bl->type);
 		rc = -ENOTSUPP;
@@ -707,6 +781,13 @@ error:
 	return rc;
 }
 
+static void dsi_panel_pwm_unregister(struct dsi_panel *panel)
+{
+	struct dsi_backlight_config *bl = &panel->bl_config;
+
+	devm_pwm_put(panel->parent, bl->pwm_bl);
+}
+
 static int dsi_panel_bl_unregister(struct dsi_panel *panel)
 {
 	int rc = 0;
@@ -722,6 +803,9 @@ static int dsi_panel_bl_unregister(struct dsi_panel *panel)
 		break;
 	case DSI_BACKLIGHT_EXTERNAL:
 		break;
+	case DSI_BACKLIGHT_PWM:
+		dsi_panel_pwm_unregister(panel);
+		break;
 	default:
 		pr_err("Backlight type(%d) not supported\n", bl->type);
 		rc = -ENOTSUPP;
@@ -1085,6 +1169,44 @@ static int dsi_panel_parse_misc_host_config(struct dsi_host_common_cfg *host,
 	return 0;
 }
 
+static void dsi_panel_parse_split_link_config(struct dsi_host_common_cfg *host,
+					struct dsi_parser_utils *utils,
+					const char *name)
+{
+	int rc = 0;
+	u32 val = 0;
+	bool supported = false;
+	struct dsi_split_link_config *split_link = &host->split_link;
+
+	supported = utils->read_bool(utils->data, "qcom,split-link-enabled");
+
+	if (!supported) {
+		pr_debug("[%s] Split link is not supported\n", name);
+		split_link->split_link_enabled = false;
+		return;
+	}
+
+	rc = utils->read_u32(utils->data, "qcom,sublinks-count", &val);
+	if (rc || val < 1) {
+		pr_debug("[%s] Using default sublinks count\n", name);
+		split_link->num_sublinks = 2;
+	} else {
+		split_link->num_sublinks = val;
+	}
+
+	rc = utils->read_u32(utils->data, "qcom,lanes-per-sublink", &val);
+	if (rc || val < 1) {
+		pr_debug("[%s] Using default lanes per sublink\n", name);
+		split_link->lanes_per_sublink = 2;
+	} else {
+		split_link->lanes_per_sublink = val;
+	}
+
+	pr_debug("[%s] Split link is supported %d-%d\n", name,
+		split_link->num_sublinks, split_link->lanes_per_sublink);
+	split_link->split_link_enabled = true;
+}
+
 static int dsi_panel_parse_host_config(struct dsi_panel *panel)
 {
 	int rc = 0;
@@ -1130,6 +1252,9 @@ static int dsi_panel_parse_host_config(struct dsi_panel *panel)
 		goto error;
 	}
 
+	dsi_panel_parse_split_link_config(&panel->host_config, utils,
+						panel->name);
+
 error:
 	return rc;
 }
@@ -2021,34 +2146,14 @@ static int dsi_panel_parse_bl_pwm_config(struct dsi_panel *panel)
 	struct dsi_backlight_config *config = &panel->bl_config;
 	struct dsi_parser_utils *utils = &panel->utils;
 
-	rc = utils->read_u32(utils->data, "qcom,dsi-bl-pmic-bank-select",
+	rc = utils->read_u32(utils->data, "qcom,bl-pmic-pwm-period-usecs",
 				  &val);
 	if (rc) {
-		pr_err("bl-pmic-bank-select is not defined, rc=%d\n", rc);
-		goto error;
-	}
-	config->pwm_pmic_bank = val;
-
-	rc = utils->read_u32(utils->data, "qcom,dsi-bl-pmic-pwm-frequency",
-				  &val);
-	if (rc) {
-		pr_err("bl-pmic-bank-select is not defined, rc=%d\n", rc);
+		pr_err("bl-pmic-pwm-period-usecs is not defined, rc=%d\n", rc);
 		goto error;
 	}
 	config->pwm_period_usecs = val;
 
-	config->pwm_pmi_control = utils->read_bool(utils->data,
-						"qcom,mdss-dsi-bl-pwm-pmi");
-
-	config->pwm_gpio = utils->get_named_gpio(utils->data,
-					     "qcom,mdss-dsi-pwm-gpio",
-					     0);
-	if (!gpio_is_valid(config->pwm_gpio)) {
-		pr_err("pwm gpio is invalid\n");
-		rc = -EINVAL;
-		goto error;
-	}
-
 error:
 	return rc;
 }
@@ -2139,9 +2244,17 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel)
 					      "qcom,platform-bklight-en-gpio",
 					      0);
 	if (!gpio_is_valid(panel->bl_config.en_gpio)) {
-		pr_debug("[%s] failed get bklt gpio, rc=%d\n", panel->name, rc);
-		rc = 0;
-		goto error;
+		if (panel->bl_config.en_gpio == -EPROBE_DEFER) {
+			pr_debug("[%s] failed to get bklt gpio, rc=%d\n",
+					panel->name, rc);
+			rc = -EPROBE_DEFER;
+			goto error;
+		} else {
+			pr_debug("[%s] failed to get bklt gpio, rc=%d\n",
+					 panel->name, rc);
+			rc = 0;
+			goto error;
+		}
 	}
 
 error:
@@ -2341,12 +2454,16 @@ int dsi_dsc_populate_static_param(struct msm_display_dsc_info *dsc)
 
 
 static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode,
-				struct dsi_parser_utils *utils)
+		struct dsi_parser_utils *utils)
 {
 	const char *data;
 	u32 len, i;
 	int rc = 0;
 	struct dsi_display_mode_priv_info *priv_info;
+	struct dsi_mode_info *timing = NULL;
+
+	if (!mode || !mode->priv_info)
+		return -EINVAL;
 
 	priv_info = mode->priv_info;
 
@@ -2366,9 +2483,11 @@ static int dsi_panel_parse_phy_timing(struct dsi_display_mode *mode,
 		priv_info->phy_timing_len = len;
 	}
 
-	mode->pixel_clk_khz = (DSI_H_TOTAL_DSC(&mode->timing) *
-			DSI_V_TOTAL(&mode->timing) *
-			mode->timing.refresh_rate) / 1000;
+	timing = &mode->timing;
+
+	mode->pixel_clk_khz = (DSI_H_TOTAL(&mode->timing) *
+							DSI_V_TOTAL(&mode->timing) *
+							mode->timing.refresh_rate) / 1000;
 	return rc;
 }
 
@@ -3111,9 +3230,11 @@ struct dsi_panel *dsi_panel_get(struct device *parent,
 		pr_err("failed to parse power config, rc=%d\n", rc);
 
 	rc = dsi_panel_parse_bl_config(panel);
-	if (rc)
+	if (rc) {
 		pr_err("failed to parse backlight config, rc=%d\n", rc);
-
+		if (rc == -EPROBE_DEFER)
+			goto error;
+	}
 
 	rc = dsi_panel_parse_misc_features(panel);
 	if (rc)
@@ -3637,10 +3758,14 @@ int dsi_panel_set_lp1(struct dsi_panel *panel)
 	}
 
 	mutex_lock(&panel->panel_lock);
+	if (!panel->panel_initialized)
+		goto exit;
+
 	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_LP1);
 	if (rc)
 		pr_err("[%s] failed to send DSI_CMD_SET_LP1 cmd, rc=%d\n",
 		       panel->name, rc);
+exit:
 	mutex_unlock(&panel->panel_lock);
 	return rc;
 }
@@ -3655,10 +3780,14 @@ int dsi_panel_set_lp2(struct dsi_panel *panel)
 	}
 
 	mutex_lock(&panel->panel_lock);
+	if (!panel->panel_initialized)
+		goto exit;
+
 	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_LP2);
 	if (rc)
 		pr_err("[%s] failed to send DSI_CMD_SET_LP2 cmd, rc=%d\n",
 		       panel->name, rc);
+exit:
 	mutex_unlock(&panel->panel_lock);
 	return rc;
 }
@@ -3673,10 +3802,14 @@ int dsi_panel_set_nolp(struct dsi_panel *panel)
 	}
 
 	mutex_lock(&panel->panel_lock);
+	if (!panel->panel_initialized)
+		goto exit;
+
 	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_NOLP);
 	if (rc)
 		pr_err("[%s] failed to send DSI_CMD_SET_NOLP cmd, rc=%d\n",
 		       panel->name, rc);
+exit:
 	mutex_unlock(&panel->panel_lock);
 	return rc;
 }
@@ -3868,6 +4001,7 @@ int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx,
 	mutex_unlock(&panel->panel_lock);
 
 	dsi_panel_destroy_cmd_packets(set);
+	dsi_panel_dealloc_cmd_packets(set);
 
 	return rc;
 }
@@ -4004,11 +4138,11 @@ int dsi_panel_enable(struct dsi_panel *panel)
 	mutex_lock(&panel->panel_lock);
 
 	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_ON);
-	if (rc) {
+	if (rc)
 		pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n",
 		       panel->name, rc);
-	}
-	panel->panel_initialized = true;
+	else
+		panel->panel_initialized = true;
 	mutex_unlock(&panel->panel_lock);
 	return rc;
 }

+ 2 - 3
msm/dsi/dsi_panel.h

@@ -102,10 +102,9 @@ struct dsi_backlight_config {
 
 	int en_gpio;
 	/* PWM params */
-	bool pwm_pmi_control;
-	u32 pwm_pmic_bank;
+	struct pwm_device *pwm_bl;
+	bool pwm_enabled;
 	u32 pwm_period_usecs;
-	int pwm_gpio;
 
 	/* WLED params */
 	struct led_trigger *wled;

+ 21 - 1
msm/dsi/dsi_phy.c

@@ -1139,7 +1139,6 @@ int dsi_phy_conv_logical_to_phy_lane(
 	return i;
 }
 
-
 /**
  * dsi_phy_config_dynamic_refresh() - Configure dynamic refresh registers
  * @phy:	DSI PHY handle
@@ -1239,6 +1238,27 @@ void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy)
 	mutex_unlock(&phy->phy_lock);
 }
 
+/**
+ * dsi_phy_set_continuous_clk() - set/unset force clock lane HS request
+ * @phy:	DSI PHY handle
+ * @enable:	variable to control continuous clock
+ */
+void dsi_phy_set_continuous_clk(struct msm_dsi_phy *phy, bool enable)
+{
+	if (!phy)
+		return;
+
+	mutex_lock(&phy->phy_lock);
+
+	if (phy->hw.ops.set_continuous_clk)
+		phy->hw.ops.set_continuous_clk(&phy->hw, enable);
+	else
+		pr_warn("set_continuous_clk ops not present\n");
+
+	mutex_unlock(&phy->phy_lock);
+
+}
+
 void dsi_phy_drv_register(void)
 {
 	platform_driver_register(&dsi_phy_platform_driver);

+ 9 - 1
msm/dsi/dsi_phy.h

@@ -330,5 +330,13 @@ void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy);
  * @size:	   Number of phy lane settings.
  */
 int dsi_phy_dyn_refresh_cache_phy_timings(struct msm_dsi_phy *phy,
-					  u32 *dst, u32 size);
+
+		u32 *dst, u32 size);
+/**
+ * dsi_phy_set_continuous_clk() - API to set/unset force clock lane HS request.
+ * @phy:	DSI PHY Handle.
+ * @enable:	variable to control continuous clock.
+ */
+void dsi_phy_set_continuous_clk(struct msm_dsi_phy *phy, bool enable);
+
 #endif /* _DSI_PHY_H_ */

+ 9 - 0
msm/dsi/dsi_phy_hw.h

@@ -40,11 +40,13 @@ enum dsi_phy_version {
  * enum dsi_phy_hw_features - features supported by DSI PHY hardware
  * @DSI_PHY_DPHY:        Supports DPHY
  * @DSI_PHY_CPHY:        Supports CPHY
+ * @DSI_PHY_SPLIT_LINK:  Supports Split Link
  * @DSI_PHY_MAX_FEATURES:
  */
 enum dsi_phy_hw_features {
 	DSI_PHY_DPHY,
 	DSI_PHY_CPHY,
+	DSI_PHY_SPLIT_LINK,
 	DSI_PHY_MAX_FEATURES
 };
 
@@ -303,6 +305,13 @@ struct dsi_phy_hw_ops {
 	 */
 	void (*reset_clk_en_sel)(struct dsi_phy_hw *phy);
 
+	/**
+	 * set_continuous_clk() - Set continuous clock
+	 * @phy:	Pointer to DSI PHY hardware object
+	 * @enable:	Bool to control continuous clock request.
+	 */
+	void (*set_continuous_clk)(struct dsi_phy_hw *phy, bool enable);
+
 	void *timing_ops;
 	struct phy_ulps_config_ops ulps_ops;
 	struct phy_dyn_refresh_ops dyn_refresh_ops;

+ 75 - 40
msm/dsi/dsi_phy_hw_v2_0.c

@@ -34,23 +34,17 @@
 #define DSIPHY_CMN_REGULATOR_CAL_STATUS1          0x0068
 #define DSI_MDP_ULPS_CLAMP_ENABLE_OFF             0x0054
 
-/* n = 0..3 for data lanes and n = 4 for clock lane */
-#define DSIPHY_DLNX_CFG0(n)                     (0x100 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG1(n)                     (0x104 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG2(n)                     (0x108 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG3(n)                     (0x10C + ((n) * 0x80))
+/* n = 0..3 for data lanes and n = 4 for clock lane
+ * t for count per lane
+ */
+#define DSIPHY_DLNX_CFG(n, t) \
+			(0x100 + ((t) * 0x04) + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL(n, t) \
+			(0x118 + ((t) * 0x04) + ((n) * 0x80))
+#define DSIPHY_DLNX_STRENGTH_CTRL(n, t) \
+			(0x138 + ((t) * 0x04) + ((n) * 0x80))
 #define DSIPHY_DLNX_TEST_DATAPATH(n)            (0x110 + ((n) * 0x80))
 #define DSIPHY_DLNX_TEST_STR(n)                 (0x114 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_4(n)            (0x118 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_5(n)            (0x11C + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_6(n)            (0x120 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_7(n)            (0x124 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_8(n)            (0x128 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_9(n)            (0x12C + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_10(n)           (0x130 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_11(n)           (0x134 + ((n) * 0x80))
-#define DSIPHY_DLNX_STRENGTH_CTRL_0(n)          (0x138 + ((n) * 0x80))
-#define DSIPHY_DLNX_STRENGTH_CTRL_1(n)          (0x13C + ((n) * 0x80))
 #define DSIPHY_DLNX_BIST_POLY(n)                (0x140 + ((n) * 0x80))
 #define DSIPHY_DLNX_BIST_SEED0(n)               (0x144 + ((n) * 0x80))
 #define DSIPHY_DLNX_BIST_SEED1(n)               (0x148 + ((n) * 0x80))
@@ -80,10 +74,15 @@ void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy,
 				      struct dsi_phy_per_lane_cfgs *reg_cfg)
 {
 	int i;
+	bool is_split_link = test_bit(DSI_PHY_SPLIT_LINK, phy->feature_map);
 
 	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
 		DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), reg_cfg->lane[i][0]);
 
+	if (is_split_link)
+		DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(DSI_LOGICAL_CLOCK_LANE+1),
+				reg_cfg->lane[DSI_LOGICAL_CLOCK_LANE][0]);
+
 	/* make sure all values are written to hardware */
 	wmb();
 
@@ -108,35 +107,55 @@ void dsi_phy_hw_v2_0_regulator_disable(struct dsi_phy_hw *phy)
 void dsi_phy_hw_v2_0_enable(struct dsi_phy_hw *phy,
 			    struct dsi_phy_cfg *cfg)
 {
-	int i;
+	int i, j;
+	struct dsi_phy_per_lane_cfgs *lanecfg = &cfg->lanecfg;
 	struct dsi_phy_per_lane_cfgs *timing = &cfg->timing;
+	struct dsi_phy_per_lane_cfgs *strength = &cfg->strength;
 	u32 data;
+	bool is_split_link = test_bit(DSI_PHY_SPLIT_LINK, phy->feature_map);
 
 	DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
 
 	DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, 0x1);
 	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
-
-		DSI_W32(phy, DSIPHY_DLNX_CFG0(i), cfg->lanecfg.lane[i][0]);
-		DSI_W32(phy, DSIPHY_DLNX_CFG1(i), cfg->lanecfg.lane[i][1]);
-		DSI_W32(phy, DSIPHY_DLNX_CFG2(i), cfg->lanecfg.lane[i][2]);
-		DSI_W32(phy, DSIPHY_DLNX_CFG3(i), cfg->lanecfg.lane[i][3]);
+		for (j = 0; j < lanecfg->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_CFG(i, j),
+				lanecfg->lane[i][j]);
 
 		DSI_W32(phy, DSIPHY_DLNX_TEST_STR(i), 0x88);
 
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_4(i), timing->lane[i][0]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_5(i), timing->lane[i][1]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_6(i), timing->lane[i][2]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_7(i), timing->lane[i][3]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_8(i), timing->lane[i][4]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_9(i), timing->lane[i][5]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_10(i), timing->lane[i][6]);
-		DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_11(i), timing->lane[i][7]);
-
-		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
-			cfg->strength.lane[i][0]);
-		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
-			cfg->strength.lane[i][1]);
+		for (j = 0; j < timing->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL(i, j),
+				timing->lane[i][j]);
+
+		for (j = 0; j < strength->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL(i, j),
+				strength->lane[i][j]);
+	}
+
+	if (is_split_link) {
+		i = DSI_LOGICAL_CLOCK_LANE;
+
+		for (j = 0; j < lanecfg->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_CFG(i+1, j),
+				lanecfg->lane[i][j]);
+
+		DSI_W32(phy, DSIPHY_DLNX_TEST_STR(i+1), 0x0);
+		DSI_W32(phy, DSIPHY_DLNX_TEST_DATAPATH(i+1), 0x88);
+
+		for (j = 0; j < timing->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL(i+1, j),
+				timing->lane[i][j]);
+
+		for (j = 0; j < strength->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL(i+1, j),
+				strength->lane[i][j]);
+
+		/* enable split link for cmn clk cfg1 */
+		data = DSI_R32(phy, DSIPHY_CMN_CLK_CFG1);
+		data |= BIT(1);
+		DSI_W32(phy, DSIPHY_CMN_CLK_CFG1, data);
+
 	}
 
 	/* make sure all values are written to hardware before enabling phy */
@@ -193,13 +212,20 @@ void dsi_phy_hw_v2_0_disable(struct dsi_phy_hw *phy,
  */
 void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg)
 {
-	int i = 0;
+	int i = 0, j;
+	struct dsi_phy_per_lane_cfgs *strength = &cfg->strength;
+	bool is_split_link = test_bit(DSI_PHY_SPLIT_LINK, phy->feature_map);
 
 	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
-		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
-			cfg->strength.lane[i][0]);
-		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
-			cfg->strength.lane[i][1]);
+		for (j = 0; j < strength->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL(i, j),
+				strength->lane[i][j]);
+	}
+	if (is_split_link) {
+		i = DSI_LOGICAL_CLOCK_LANE;
+		for (j = 0; j < strength->count_per_lane; j++)
+			DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL(i+1, j),
+				strength->lane[i][j]);
 	}
 	wmb(); /* make sure write happens */
 	pr_debug("[DSI_%d]Phy enabled out of idle screen\n", phy->index);
@@ -213,14 +239,23 @@ void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg)
 void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy)
 {
 	int i = 0;
+	bool is_split_link = test_bit(DSI_PHY_SPLIT_LINK, phy->feature_map);
 
 	DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
 	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
 		DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), 0x1c);
+	if (is_split_link)
+		DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(DSI_LOGICAL_CLOCK_LANE+1),
+									0x1c);
+
 	DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
 
 	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
-		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i), 0x0);
+		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL(i, 1), 0x0);
+	if (is_split_link)
+		DSI_W32(phy,
+		DSIPHY_DLNX_STRENGTH_CTRL(DSI_LOGICAL_CLOCK_LANE+1, 1), 0x0);
+
 	wmb(); /* make sure write happens */
 	pr_debug("[DSI_%d]Phy disabled during idle screen\n", phy->index);
 }

+ 15 - 6
msm/dsi/dsi_phy_hw_v4_0.c

@@ -182,12 +182,6 @@ static void dsi_phy_hw_v4_0_lane_settings(struct dsi_phy_hw *phy,
 		DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]);
 	}
 
-	if (cfg->force_clk_lane_hs) {
-		u32 reg = DSI_R32(phy, DSIPHY_CMN_LANE_CTRL1);
-
-		reg |= BIT(5) | BIT(6);
-		DSI_W32(phy, DSIPHY_CMN_LANE_CTRL1, reg);
-	}
 }
 
 /**
@@ -692,3 +686,18 @@ int dsi_phy_hw_v4_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings,
 
 	return 0;
 }
+
+void dsi_phy_hw_v4_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(phy, DSIPHY_CMN_LANE_CTRL1);
+
+	if (enable)
+		reg |= BIT(5) | BIT(6);
+	else
+		reg &= ~(BIT(5) | BIT(6));
+
+	DSI_W32(phy, DSIPHY_CMN_LANE_CTRL1, reg);
+	wmb(); /* make sure request is set */
+}

+ 13 - 0
msm/dsi/dsi_pwr.c

@@ -64,6 +64,14 @@ static int dsi_pwr_parse_supply_node(struct dsi_parser_utils *utils,
 		regs->vregs[i].disable_load = tmp;
 
 		/* Optional values */
+		rc = utils->read_u32(node, "qcom,supply-off-min-voltage", &tmp);
+		if (rc) {
+			pr_debug("off-min-voltage not specified\n");
+			rc = 0;
+		} else {
+			regs->vregs[i].off_min_voltage = tmp;
+		}
+
 		rc = utils->read_u32(node, "qcom,supply-pre-on-sleep", &tmp);
 		if (rc) {
 			pr_debug("pre-on-sleep not specified\n");
@@ -158,6 +166,11 @@ static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
 			if (regs->vregs[i].pre_off_sleep)
 				msleep(regs->vregs[i].pre_off_sleep);
 
+			if (regs->vregs[i].off_min_voltage)
+				(void)regulator_set_voltage(regs->vregs[i].vreg,
+						regs->vregs[i].off_min_voltage,
+						regs->vregs[i].max_voltage);
+
 			(void)regulator_set_load(regs->vregs[i].vreg,
 						regs->vregs[i].disable_load);
 			(void)regulator_disable(regs->vregs[i].vreg);

+ 2 - 0
msm/dsi/dsi_pwr.h

@@ -21,6 +21,7 @@ struct dsi_parser_utils;
  * @max_voltage:     Maximum voltage in uV.
  * @enable_load:     Load, in uA, when enabled.
  * @disable_load:    Load, in uA, when disabled.
+ * @off_min_voltage: Minimum voltage in uV when regulator is disabled.
  * @pre_on_sleep:    Sleep, in ms, before enabling the regulator.
  * @post_on_sleep:   Sleep, in ms, after enabling the regulator.
  * @pre_off_sleep:   Sleep, in ms, before disabling the regulator.
@@ -33,6 +34,7 @@ struct dsi_vreg {
 	u32 max_voltage;
 	u32 enable_load;
 	u32 disable_load;
+	u32 off_min_voltage;
 	u32 pre_on_sleep;
 	u32 post_on_sleep;
 	u32 pre_off_sleep;

+ 1 - 1
msm/sde/sde_encoder_phys_cmd.c

@@ -506,7 +506,7 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
 		sde_connector_event_notify(conn, DRM_EVENT_SDE_HW_RECOVERY,
 				sizeof(uint8_t), event);
 	} else if (cmd_enc->pp_timeout_report_cnt) {
-		SDE_DBG_DUMP("panic");
+		SDE_DBG_DUMP("dsi_dbg_bus", "panic");
 	}
 
 	/* request a ctl reset before the next kickoff */

+ 10 - 13
msm/sde/sde_kms.c

@@ -1292,9 +1292,19 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
 			SDE_ERROR("dsi %d connector init failed\n", i);
 			dsi_display_drm_bridge_deinit(display);
 			sde_encoder_destroy(encoder);
+			continue;
 		}
 	}
 
+	rc = dsi_display_drm_ext_bridge_init(display,
+					encoder, connector);
+	if (rc) {
+		SDE_ERROR("dsi %d ext bridge init failed\n", rc);
+		dsi_display_drm_bridge_deinit(display);
+		sde_encoder_destroy(encoder);
+		sde_connector_destroy(connector);
+	}
+
 	/* wb */
 	for (i = 0; i < sde_kms->wb_display_count &&
 		priv->num_encoders < max_encoders; ++i) {
@@ -2193,7 +2203,6 @@ static void _sde_kms_post_open(struct msm_kms *kms, struct drm_file *file)
 	struct drm_connector *connector = NULL;
 	struct drm_connector_list_iter conn_iter;
 	struct sde_connector *sde_conn = NULL;
-	int i;
 
 	if (!kms) {
 		SDE_ERROR("invalid kms\n");
@@ -2211,18 +2220,6 @@ static void _sde_kms_post_open(struct msm_kms *kms, struct drm_file *file)
 	if (!dev->mode_config.poll_enabled)
 		return;
 
-	/* init external dsi bridge here to make sure ext bridge is probed*/
-	for (i = 0; i < sde_kms->dsi_display_count; ++i) {
-		struct dsi_display *dsi_display;
-
-		dsi_display = sde_kms->dsi_displays[i];
-		if (dsi_display->bridge) {
-			dsi_display_drm_ext_bridge_init(dsi_display,
-				dsi_display->bridge->base.encoder,
-				dsi_display->drm_conn);
-		}
-	}
-
 	mutex_lock(&dev->mode_config.mutex);
 	drm_connector_list_iter_begin(dev, &conn_iter);
 	drm_for_each_connector_iter(connector, &conn_iter) {

+ 2 - 1
msm/sde_dbg.c

@@ -4517,7 +4517,8 @@ void sde_dbg_init_dbg_buses(u32 hwversion)
 	memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
 
 	if (IS_SM8150_TARGET(hwversion) || IS_SM6150_TARGET(hwversion) ||
-				IS_SDMMAGPIE_TARGET(hwversion)) {
+				IS_SDMMAGPIE_TARGET(hwversion) ||
+				IS_SDMTRINKET_TARGET(hwversion)) {
 		dbg->dbgbus_sde.entries = dbg_bus_sde_sm8150;
 		dbg->dbgbus_sde.cmn.entries_size =
 				ARRAY_SIZE(dbg_bus_sde_sm8150);