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 +#include #include #include #include @@ -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 {