diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index c7d92cf247cb..94eece45a049 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -289,6 +289,8 @@ DSI1_INT_PR_TO) #define DSI0_STAT 0x2c +# define DSI0_STAT_ERR_CONT_LP1 BIT(6) +# define DSI0_STAT_ERR_CONT_LP0 BIT(5) #define DSI0_HSTX_TO_CNT 0x30 #define DSI0_LPRX_TO_CNT 0x34 #define DSI0_TA_TO_CNT 0x38 @@ -1213,10 +1215,9 @@ static int vc4_dsi_bridge_attach(struct drm_bridge *bridge, &dsi->bridge, flags); } -static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) +static ssize_t vc4_dsi_transfer(struct vc4_dsi *dsi, + const struct mipi_dsi_msg *msg, bool log_error) { - struct vc4_dsi *dsi = host_to_dsi(host); struct drm_device *drm = dsi->bridge.dev; struct mipi_dsi_packet packet; u32 pkth = 0, pktc = 0; @@ -1337,10 +1338,12 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, DSI_PORT_WRITE(TXPKT1C, pktc); if (!wait_for_completion_timeout(&dsi->xfer_completion, - msecs_to_jiffies(1000))) { - dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); - dev_err(&dsi->pdev->dev, "instat: 0x%08x\n", - DSI_PORT_READ(INT_STAT)); + msecs_to_jiffies(500))) { + if (log_error) { + dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); + dev_err(&dsi->pdev->dev, "instat: 0x%08x, stat: 0x%08x\n", + DSI_PORT_READ(INT_STAT), DSI_PORT_READ(INT_STAT)); + } ret = -ETIMEDOUT; } else { ret = dsi->xfer_result; @@ -1383,7 +1386,8 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, return ret; reset_fifo_and_return: - drm_err(drm, "DSI transfer failed, resetting: %d\n", ret); + if (log_error) + drm_err(drm, "DSI transfer failed, resetting: %d\n", ret); DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); udelay(1); @@ -1396,6 +1400,40 @@ reset_fifo_and_return: return ret; } +static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + u32 stat, disp0_ctrl; + int ret; + + ret = vc4_dsi_transfer(dsi, msg, false); + + if (ret == -ETIMEDOUT) { + stat = DSI_PORT_READ(STAT); + disp0_ctrl = DSI_PORT_READ(DISP0_CTRL); + + DSI_PORT_WRITE(STAT, DSI_PORT_BIT(STAT_ERR_CONT_LP1)); + if (!(disp0_ctrl & DSI_DISP0_ENABLE)) { + /* If video mode not enabled, then try recovering by + * enabling it briefly to clear FIFOs and the state. + */ + disp0_ctrl |= DSI_DISP0_ENABLE; + DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl); + msleep(30); + disp0_ctrl &= ~DSI_DISP0_ENABLE; + DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl); + msleep(30); + + ret = vc4_dsi_transfer(dsi, msg, true); + } else { + DRM_ERROR("DSI transfer failed whilst in HS mode stat: 0x%08x\n", + stat); + } + } + return ret; +} + static const struct component_ops vc4_dsi_ops; static int vc4_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device)