Browse Source

disp: msm: dsi: add support for phy/pll bypass

This change adds support for bypassing hw access in DSI PHY/PLL
drivers which enables the DSI driver to run on emulation
platforms that might be missing those modules.

Change-Id: I3e83155a79d60f2357606746214d776cefabd651
Signed-off-by: Rajkumar Subbiah <[email protected]>
Signed-off-by: Shamika Joshi <[email protected]>
Signed-off-by: Alex Danila <[email protected]>
Rajkumar Subbiah 3 years ago
parent
commit
6d5a850504

+ 7 - 1
msm/dsi/dsi_catalog.c

@@ -111,6 +111,7 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
  * @version:     DSI controller version.
  * @index:       DSI controller instance ID.
  * @phy_isolation_enabled:       DSI controller works isolated from phy.
+ * @phy_pll_bypass:              DSI PHY/PLL drivers bypass HW access.
  * @null_insertion_enabled:      DSI controller inserts null packet.
  *
  * This function setups the catalog information in the dsi_ctrl_hw object.
@@ -119,7 +120,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
  */
 int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 		   enum dsi_ctrl_version version, u32 index,
-		   bool phy_isolation_enabled, bool null_insertion_enabled)
+		   bool phy_isolation_enabled, bool phy_pll_bypass,
+		   bool null_insertion_enabled)
 {
 	int rc = 0;
 
@@ -143,6 +145,7 @@ int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 	case DSI_CTRL_VERSION_2_3:
 	case DSI_CTRL_VERSION_2_4:
 		ctrl->phy_isolation_enabled = phy_isolation_enabled;
+		ctrl->phy_pll_bypass = phy_pll_bypass;
 		dsi_catalog_cmn_init(ctrl, version);
 		break;
 	case DSI_CTRL_VERSION_2_5:
@@ -151,6 +154,7 @@ int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 	case DSI_CTRL_VERSION_2_8:
 		ctrl->widebus_support = true;
 		ctrl->phy_isolation_enabled = phy_isolation_enabled;
+		ctrl->phy_pll_bypass = phy_pll_bypass;
 		dsi_catalog_cmn_init(ctrl, version);
 		break;
 	default:
@@ -324,6 +328,8 @@ int dsi_catalog_phy_pll_setup(struct dsi_phy_hw *phy, u32 pll_ver)
 	if (pll_ver >= DSI_PLL_VERSION_UNKNOWN) {
 		DSI_ERR("Unsupported version: %d\n", pll_ver);
 		return -EOPNOTSUPP;
+	} else if (phy->phy_pll_bypass) {
+		return 0;
 	}
 
 	switch (pll_ver) {

+ 3 - 1
msm/dsi/dsi_catalog.h

@@ -16,6 +16,7 @@
  * @version:     DSI controller version.
  * @index:       DSI controller instance ID.
  * @phy_isolation_enabled:       DSI controller works isolated from phy.
+ * @phy_pll_bypass:              DSI PHY/PLL drivers bypass HW access.
  * @null_insertion_enabled:      DSI controller inserts null packet.
  *
  * This function setups the catalog information in the dsi_ctrl_hw object.
@@ -24,7 +25,8 @@
  */
 int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 		   enum dsi_ctrl_version version, u32 index,
-		   bool phy_isolation_enabled, bool null_insertion_enabled);
+		   bool phy_isolation_enabled, bool phy_pll_bypass,
+		   bool null_insertion_enabled);
 
 /**
  * dsi_catalog_phy_setup() - return catalog info for dsi phy hardware

+ 3 - 0
msm/dsi/dsi_clk.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _DSI_CLK_H_
@@ -209,6 +210,7 @@ typedef int (*pll_toggle_cb)(void *priv, bool prepare);
  * @priv_data                pointer to private data
  * @master_ndx               master DSI controller index
  * @dsi_ctrl_count           number of DSI controllers
+ * @phy_pll_bypass           bypass PLL clock related operations
  */
 struct dsi_clk_info {
 	char name[MAX_STRING_LEN];
@@ -225,6 +227,7 @@ struct dsi_clk_info {
 	void *priv_data;
 	u32 master_ndx;
 	u32 dsi_ctrl_count;
+	bool phy_pll_bypass;
 };
 
 /**

+ 14 - 0
msm/dsi/dsi_clk_manager.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/of.h>
@@ -41,6 +42,7 @@ struct dsi_clk_mngr {
 	pre_clockon_cb pre_clkon_cb;
 
 	bool is_cont_splash_enabled;
+	bool phy_pll_bypass;
 	void *priv_data;
 };
 
@@ -119,6 +121,10 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index)
 	struct dsi_clk_mngr *mngr;
 
 	mngr = c->mngr;
+
+	if (mngr->phy_pll_bypass)
+		return 0;
+
 	rc = clk_set_rate(mngr->link_clks[index].hs_clks.pixel_clk, pixel_clk);
 	if (rc)
 		DSI_ERR("failed to set clk rate for pixel clk, rc=%d\n", rc);
@@ -144,6 +150,10 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk,
 	struct dsi_clk_mngr *mngr;
 
 	mngr = c->mngr;
+
+	if (mngr->phy_pll_bypass)
+		return 0;
+
 	rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_clk, byte_clk);
 	if (rc)
 		DSI_ERR("failed to set clk rate for byte clk, rc=%d\n", rc);
@@ -333,6 +343,9 @@ static int dsi_link_hs_clk_set_rate(struct dsi_link_hs_clk_info *link_hs_clks,
 	if (mngr->is_cont_splash_enabled)
 		return 0;
 
+	if (mngr->phy_pll_bypass)
+		return 0;
+
 	rc = clk_set_rate(link_hs_clks->byte_clk,
 		l_clks->freq.byte_clk_rate);
 	if (rc) {
@@ -1451,6 +1464,7 @@ void *dsi_display_clk_mngr_register(struct dsi_clk_info *info)
 	mngr->phy_config_cb = info->phy_config_cb;
 	mngr->phy_pll_toggle_cb = info->phy_pll_toggle_cb;
 	mngr->priv_data = info->priv_data;
+	mngr->phy_pll_bypass = info->phy_pll_bypass;
 	memcpy(mngr->name, info->name, MAX_STRING_LEN);
 
 error:

+ 4 - 1
msm/dsi/dsi_ctrl.c

@@ -2066,6 +2066,9 @@ static int dsi_ctrl_dts_parse(struct dsi_ctrl *dsi_ctrl,
 	dsi_ctrl->split_link_supported = of_property_read_bool(of_node,
 					"qcom,split-link-supported");
 
+	dsi_ctrl->phy_pll_bypass = of_property_read_bool(of_node,
+					"qcom,dsi-phy-pll-bypass");
+
 	rc = of_property_read_u32(of_node, "frame-threshold-time-us",
 			&frame_threshold_time_us);
 	if (rc) {
@@ -2139,7 +2142,7 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
 
 	rc = dsi_catalog_ctrl_setup(&dsi_ctrl->hw, dsi_ctrl->version,
 		dsi_ctrl->cell_index, dsi_ctrl->phy_isolation_enabled,
-		dsi_ctrl->null_insertion_enabled);
+		dsi_ctrl->phy_pll_bypass, dsi_ctrl->null_insertion_enabled);
 	if (rc) {
 		DSI_CTRL_ERR(dsi_ctrl, "Catalog does not support version (%d)\n",
 		       dsi_ctrl->version);

+ 3 - 0
msm/dsi/dsi_ctrl.h

@@ -234,6 +234,8 @@ struct dsi_ctrl_interrupts {
  *			  next TE for command mode.
  * @phy_isolation_enabled:    A boolean property allows to isolate the phy from
  *                          dsi controller and run only dsi controller.
+ * @phy_pll_bypass:      A boolean property that enables skipping HW access in
+ *                       DSI PHY/PLL drivers for running on emulation platforms.
  * @null_insertion_enabled:  A boolean property to allow dsi controller to
  *                           insert null packet.
  * @modeupdated:	  Boolean to send new roi if mode is updated.
@@ -310,6 +312,7 @@ struct dsi_ctrl {
 	unsigned int error_interrupt_count;
 
 	bool phy_isolation_enabled;
+	bool phy_pll_bypass;
 	bool null_insertion_enabled;
 	bool modeupdated;
 	bool split_link_supported;

+ 3 - 0
msm/dsi/dsi_ctrl_hw.h

@@ -948,6 +948,8 @@ struct dsi_ctrl_hw_ops {
  * @supported_errors:       Number of supported errors.
  * @phy_isolation_enabled:    A boolean property allows to isolate the phy from
  *                          dsi controller and run only dsi controller.
+ * @phy_pll_bypass:         A boolean property that enables skipping HW access in
+ *                          PHY/PLL drivers for running on emulation platforms.
  * @null_insertion_enabled:  A boolean property to allow dsi controller to
  *                           insert null packet.
  * @widebus_support:        48 bit wide data bus is supported.
@@ -973,6 +975,7 @@ struct dsi_ctrl_hw {
 	u64 supported_errors;
 
 	bool phy_isolation_enabled;
+	bool phy_pll_bypass;
 	bool null_insertion_enabled;
 	bool widebus_support;
 	bool reset_trig_ctrl;

+ 6 - 0
msm/dsi/dsi_ctrl_hw_cmn.c

@@ -1440,6 +1440,11 @@ void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
 	else
 		int_ctrl &= ~BIT(25);
 
+	if (ctrl->phy_pll_bypass) {
+		int_ctrl &= ~BIT(25);
+		goto dsi_write;
+	}
+
 	/* Do not clear interrupt status */
 	int_ctrl &= 0xAAEEAAFE;
 
@@ -1499,6 +1504,7 @@ void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
 	if (errors & DSI_INTERLEAVE_OP_CONTENTION)
 		int_mask0 &= ~BIT(8);
 
+dsi_write:
 	DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
 	DSI_W32(ctrl, DSI_ERR_INT_MASK0, int_mask0);
 

+ 20 - 2
msm/dsi/dsi_display.c

@@ -70,6 +70,11 @@ static bool is_sim_panel(struct dsi_display *display)
 			display->panel->panel_ack_disabled);
 }
 
+static bool phy_pll_bypass(struct dsi_display *display)
+{
+	return display->ctrl[display->cmd_master_idx].phy->hw.phy_pll_bypass;
+}
+
 static void dsi_display_mask_ctrl_error_interrupts(struct dsi_display *display,
 			u32 mask, bool enable)
 {
@@ -773,6 +778,9 @@ static int dsi_display_read_status(struct dsi_display_ctrl *ctrl,
 	if (!dsi_ctrl_validate_host_state(ctrl->ctrl))
 		return 1;
 
+	if (phy_pll_bypass(display))
+		return 0;
+
 	config = &(panel->esd_config);
 	lenp = config->status_valid_params ?: config->status_cmds_rlen;
 	count = config->status_cmd.count;
@@ -1067,6 +1075,9 @@ static int dsi_display_cmd_rx(struct dsi_display *display,
 		goto release_panel_lock;
 	}
 
+	if (phy_pll_bypass(display))
+		goto release_panel_lock;
+
 	flags = DSI_CTRL_CMD_READ;
 
 	dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON);
@@ -2810,7 +2821,7 @@ int dsi_display_phy_pll_toggle(void *priv, bool prepare)
 		return -EINVAL;
 	}
 
-	if (is_skip_op_required(display))
+	if (is_skip_op_required(display) || phy_pll_bypass(display))
 		return 0;
 
 	if (prepare)
@@ -3254,6 +3265,9 @@ static int dsi_display_broadcast_cmd(struct dsi_display *display, struct dsi_cmd
 	int i;
 	u32 flags = 0;
 
+	if (phy_pll_bypass(display))
+		return 0;
+
 	/*
 	 * 1. Setup commands in FIFO
 	 * 2. Trigger commands
@@ -3335,7 +3349,7 @@ static int dsi_display_phy_sw_reset(struct dsi_display *display)
 	 * ctrl states are updated separately and hence we do
 	 * an early return
 	 */
-	if (is_skip_op_required(display)) {
+	if (is_skip_op_required(display) || phy_pll_bypass(display)) {
 		DSI_DEBUG(
 			"cont splash/trusted vm use case, phy sw reset not required\n");
 		return 0;
@@ -3390,6 +3404,9 @@ int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
 
 	display = to_dsi_display(host);
 
+	if (phy_pll_bypass(display))
+		return 0;
+
 	/* Avoid sending DCS commands when ESD recovery is pending */
 	if (atomic_read(&display->panel->esd_recovery_pending)) {
 		DSI_DEBUG("ESD recovery pending\n");
@@ -5689,6 +5706,7 @@ static int dsi_display_bind(struct device *dev,
 	info.priv_data = display;
 	info.master_ndx = display->clk_master_idx;
 	info.dsi_ctrl_count = display->ctrl_count;
+	info.phy_pll_bypass = phy_pll_bypass(display);
 	snprintf(info.name, MAX_STRING_LEN,
 			"DSI_MNGR-%s", display->name);
 

+ 4 - 1
msm/dsi/dsi_phy.c

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/of_device.h>
@@ -405,6 +405,9 @@ static int dsi_phy_driver_probe(struct platform_device *pdev)
 	if (!dsi_phy->name)
 		dsi_phy->name = DSI_PHY_DEFAULT_LABEL;
 
+	dsi_phy->hw.phy_pll_bypass = of_property_read_bool(pdev->dev.of_node,
+			"qcom,dsi-phy-pll-bypass");
+
 	DSI_PHY_DBG(dsi_phy, "Probing device\n");
 
 	dsi_phy->ver_info = ver_info;

+ 2 - 0
msm/dsi/dsi_phy_hw.h

@@ -392,6 +392,7 @@ struct dsi_phy_hw_ops {
  * @dyn_pll_base:      VA for the DSI dynamic refresh base address.
  * @length:                Length of the DSI dynamic refresh register base map.
  * @index:                 Instance ID of the controller.
+ * @phy_pll_bypass:        DSI PHY bypass
  * @version:               DSI PHY version.
  * @phy_clamp_base:        Base address of phy clamp register map.
  * @feature_map:           Features supported by DSI PHY.
@@ -403,6 +404,7 @@ struct dsi_phy_hw {
 	void __iomem *dyn_pll_base;
 	u32 dyn_refresh_len;
 	u32 index;
+	bool phy_pll_bypass;
 
 	enum dsi_phy_version version;
 	void __iomem *phy_clamp_base;

+ 20 - 6
msm/dsi/dsi_phy_hw_v5_0.c

@@ -119,6 +119,9 @@ static int dsi_phy_hw_v5_0_is_pll_on(struct dsi_phy_hw *phy)
 {
 	u32 data = 0;
 
+	if (phy->phy_pll_bypass)
+		return 0;
+
 	data = DSI_R32(phy, DSIPHY_CMN_PLL_CNTRL);
 	mb(); /*make sure read happened */
 	return (data & BIT(0));
@@ -435,12 +438,14 @@ void dsi_phy_hw_v5_0_enable(struct dsi_phy_hw *phy,
 	DSI_W32(phy, DSIPHY_CMN_GLBL_DIGTOP_SPARE10, 0x1);
 	udelay(500);
 
-	/* wait for REFGEN READY */
-	rc = DSI_READ_POLL_TIMEOUT_ATOMIC(phy, DSIPHY_CMN_PHY_STATUS,
-		status, (status & BIT(0)), delay_us, timeout_us);
-	if (rc) {
-		DSI_PHY_ERR(phy, "Ref gen not ready. Aborting\n");
-		return;
+	if (!phy->phy_pll_bypass) {
+		/* wait for REFGEN READY */
+		rc = DSI_READ_POLL_TIMEOUT_ATOMIC(phy, DSIPHY_CMN_PHY_STATUS,
+			status, (status & BIT(0)), delay_us, timeout_us);
+		if (rc) {
+			DSI_PHY_ERR(phy, "Ref gen not ready. Aborting\n");
+			return;
+		}
 	}
 
 	if (cfg->phy_type == DSI_PHY_TYPE_CPHY)
@@ -459,6 +464,9 @@ void dsi_phy_hw_v5_0_disable(struct dsi_phy_hw *phy,
 {
 	u32 data = 0;
 
+	if (phy->phy_pll_bypass)
+		return;
+
 	if (dsi_phy_hw_v5_0_is_pll_on(phy))
 		DSI_PHY_WARN(phy, "Turning OFF PHY while PLL is on\n");
 
@@ -497,6 +505,9 @@ void dsi_phy_hw_v5_0_reset_clk_en_sel(struct dsi_phy_hw *phy)
 {
 	u32 data = 0;
 
+	if (phy->phy_pll_bypass)
+		return;
+
 	/*Turning off CLK_EN_SEL after retime buffer sync */
 	data = DSI_R32(phy, DSIPHY_CMN_CLK_CFG1);
 	data &= ~BIT(4);
@@ -514,6 +525,9 @@ int dsi_phy_hw_v5_0_wait_for_lane_idle(
 	u32 const timeout_us = 100;
 	bool split_link_enabled = dsi_phy_hw_v5_0_is_split_link_enabled(phy);
 
+	if (phy->phy_pll_bypass)
+		return 0;
+
 	stop_state_mask = BIT(4); /* clock lane */
 	if (split_link_enabled)
 		stop_state_mask |= BIT(5);

+ 5 - 0
msm/dsi/dsi_pll.c

@@ -304,6 +304,8 @@ int dsi_pll_init(struct platform_device *pdev, struct dsi_pll_resource **pll)
 			pll_res->ssc_center = true;
 	}
 
+	pll_res->phy_pll_bypass = of_property_read_bool(pdev->dev.of_node,
+			"qcom,dsi-phy-pll-bypass");
 
 	if (dsi_pll_get_ioresources(pdev, &pll_res->pll_base, "pll_base")) {
 		DSI_PLL_ERR(pll_res, "Unable to remap pll base resources\n");
@@ -335,6 +337,9 @@ int dsi_pll_init(struct platform_device *pdev, struct dsi_pll_resource **pll)
 		return rc;
 	}
 
+	if (pll_res->phy_pll_bypass)
+		return 0;
+
 	rc = dsi_pll_clock_register(pdev, pll_res);
 	if (rc) {
 		DSI_PLL_ERR(pll_res, "clock register failed rc=%d\n", rc);

+ 2 - 0
msm/dsi/dsi_pll.h

@@ -169,6 +169,8 @@ struct dsi_pll_resource {
 	int bpp;
 	int lanes;
 
+	bool phy_pll_bypass;
+
 	/*
 	 * DSI PHY type DPHY/CPHY
 	 */

+ 3 - 0
msm/dsi/dsi_pll_4nm.c

@@ -548,6 +548,9 @@ static int dsi_pll_4nm_lock_status(struct dsi_pll_resource *pll)
 	u32 const delay_us = 100;
 	u32 const timeout_us = 5000;
 
+	if (pll->phy_pll_bypass)
+		return 0;
+
 	rc = DSI_READ_POLL_TIMEOUT_ATOMIC_GEN(pll->pll_base, pll->index, PLL_COMMON_STATUS_ONE,
 				       status,
 				       ((status & BIT(0)) > 0),

+ 3 - 0
msm/sde/sde_kms.c

@@ -4720,6 +4720,9 @@ static int _sde_kms_get_splash_data(struct sde_splash_data *data)
 	int num_displays, num_regions;
 	struct sde_splash_display *splash_display;
 
+	if (of_find_node_with_property(NULL, "qcom,sde-emulated-env"))
+		return 0;
+
 	if (!data)
 		return -EINVAL;