diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -511,6 +511,14 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
 	if (dsi->slave)
 		dw_mipi_message_config(dsi->slave, msg);
 
+	if (dsi->plat_data->host_ops &&
+	    dsi->plat_data->host_ops->pre_transfer) {
+		ret = dsi->plat_data->host_ops->pre_transfer(
+			dsi->plat_data->priv_data, msg);
+		if (ret)
+			return ret;
+	}
+
 	ret = dw_mipi_dsi_write(dsi, &packet);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/iopoll.h>
 #include <linux/math64.h>
 #include <linux/mfd/syscon.h>
@@ -64,6 +65,14 @@
 #define PHY_STATUS_TIMEOUT_US		10000
 #define CMD_PKT_STATUS_TIMEOUT_US	20000
 
+#define DSI_LPCLK_CTRL			0x94
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+#define DSI_CMD_PKT_STATUS		0x74
+#define GEN_RD_CMD_BUSY			BIT(6)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_CMD_FULL			BIT(1)
+
 #define BYPASS_VCO_RANGE	BIT(7)
 #define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
 #define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
@@ -1088,9 +1098,129 @@ static int dw_mipi_dsi_rockchip_host_detach(void *priv_data,
 	return 0;
 }
 
+static bool dw_mipi_dsi_rockchip_is_dcs(const struct mipi_dsi_msg *msg, u8 *dcs_cmd)
+{
+	const u8 *tx = msg->tx_buf;
+
+	switch (msg->type) {
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_DCS_LONG_WRITE:
+		if (!tx || !msg->tx_len)
+			return false;
+		*dcs_cmd = tx[0];
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int dw_mipi_dsi_rockchip_pre_transfer(void *priv_data,
+					     const struct mipi_dsi_msg *msg)
+{
+	struct dw_mipi_dsi_rockchip *dsi = priv_data;
+	u32 pkt_status = 0, phy_status = 0;
+	u8 dcs_cmd = 0;
+	bool clk_stop_prepared = false;
+	bool clk_stop_recovered = false;
+	unsigned int try;
+	int ret;
+
+	if (msg->type == MIPI_DSI_DCS_READ)
+		return 0;
+
+	if (!dw_mipi_dsi_rockchip_is_dcs(msg, &dcs_cmd))
+		return 0;
+
+	if (!(msg->flags & MIPI_DSI_MSG_USE_LPM))
+		return 0;
+
+	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS, pkt_status,
+				 !(pkt_status & (GEN_CMD_FULL |
+						 GEN_PLD_W_FULL |
+						 GEN_RD_CMD_BUSY)),
+				 1000, CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret) {
+		dev_err(dsi->dev,
+			"uc rockchip dsi pre-transfer timeout: cmd=0x%02x stage=cmd-fifo pkt=0x%08x phy=0x%08x ret=%d\n",
+			dcs_cmd, pkt_status, readl(dsi->base + DSI_PHY_STATUS),
+			ret);
+		return ret;
+	}
+
+	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, phy_status,
+				 phy_status & LOCK, 1000,
+				 PHY_STATUS_TIMEOUT_US);
+	if (ret) {
+		dev_err(dsi->dev,
+			"uc rockchip dsi pre-transfer timeout: cmd=0x%02x stage=phy-lock pkt=0x%08x phy=0x%08x ret=%d\n",
+			dcs_cmd, pkt_status, phy_status, ret);
+		return ret;
+	}
+
+	readl(dsi->base + DSI_INT_ST0);
+	readl(dsi->base + DSI_INT_ST1);
+
+	phy_status = readl(dsi->base + DSI_PHY_STATUS);
+	if (!(phy_status & STOP_STATE_CLK_LANE)) {
+		dsi_write(dsi, DSI_LPCLK_CTRL, 0);
+		usleep_range(10000, 12000);
+		clk_stop_prepared = true;
+		dev_info(dsi->dev,
+			 "uc rockchip dsi pre-transfer prepare clk-stop before dcs: cmd=0x%02x pkt=0x%08x phy=0x%08x lpclk=0x%08x\n",
+			 dcs_cmd, pkt_status, phy_status,
+			 readl(dsi->base + DSI_LPCLK_CTRL));
+	}
+
+	for (try = 1; try <= 3; try++) {
+		ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, phy_status,
+					 phy_status & STOP_STATE_CLK_LANE,
+					 1000, PHY_STATUS_TIMEOUT_US);
+		if (!ret)
+			break;
+
+		if (try == 3)
+			break;
+
+		dev_info(dsi->dev,
+			 "uc rockchip dsi pre-transfer stabilize: cmd=0x%02x stage=clk-stop try=%u pkt=0x%08x phy=0x%08x lpclk=0x%08x ret=%d\n",
+			 dcs_cmd, try, pkt_status, phy_status,
+			 readl(dsi->base + DSI_LPCLK_CTRL), ret);
+
+		clk_stop_recovered = true;
+		dsi_write(dsi, DSI_LPCLK_CTRL, 0);
+		usleep_range(10000, 12000);
+	}
+
+	if (ret) {
+		dev_err(dsi->dev,
+			"uc rockchip dsi pre-transfer timeout: cmd=0x%02x stage=clk-stop tries=3 pkt=0x%08x phy=0x%08x lpclk=0x%08x ret=%d\n",
+			dcs_cmd, pkt_status, phy_status,
+			readl(dsi->base + DSI_LPCLK_CTRL), ret);
+		return ret;
+	}
+
+	if (clk_stop_prepared || clk_stop_recovered) {
+		dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+		if (clk_stop_recovered)
+			dev_info(dsi->dev,
+				 "uc rockchip dsi pre-transfer recovered clk-stop before dcs; continue transfer cmd=0x%02x pkt=0x%08x phy=0x%08x lpclk=0x%08x\n",
+				 dcs_cmd, pkt_status, phy_status,
+				 readl(dsi->base + DSI_LPCLK_CTRL));
+		else
+			dev_info(dsi->dev,
+				 "uc rockchip dsi pre-transfer prepared clk-stop before dcs; continue transfer cmd=0x%02x pkt=0x%08x phy=0x%08x lpclk=0x%08x\n",
+				 dcs_cmd, pkt_status, phy_status,
+				 readl(dsi->base + DSI_LPCLK_CTRL));
+	}
+
+	return 0;
+}
+
 static const struct dw_mipi_dsi_host_ops dw_mipi_dsi_rockchip_host_ops = {
 	.attach = dw_mipi_dsi_rockchip_host_attach,
 	.detach = dw_mipi_dsi_rockchip_host_detach,
+	.pre_transfer = dw_mipi_dsi_rockchip_pre_transfer,
 };
 
 static int dw_mipi_dsi_rockchip_dphy_bind(struct device *dev,
diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
--- a/include/drm/bridge/dw_mipi_dsi.h
+++ b/include/drm/bridge/dw_mipi_dsi.h
@@ -17,6 +17,7 @@
 struct drm_encoder;
 struct dw_mipi_dsi;
 struct mipi_dsi_device;
+struct mipi_dsi_msg;
 struct platform_device;
 
 struct dw_mipi_dsi_dphy_timing {
@@ -44,6 +45,8 @@ struct dw_mipi_dsi_host_ops {
 		      struct mipi_dsi_device *dsi);
 	int (*detach)(void *priv_data,
 		      struct mipi_dsi_device *dsi);
+	int (*pre_transfer)(void *priv_data,
+			    const struct mipi_dsi_msg *msg);
 };
 
 struct dw_mipi_dsi_plat_data {
