diff options
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_dsi.c | 158 |
1 files changed, 92 insertions, 66 deletions
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index ae70f76bfb89..2314c8122992 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -208,79 +208,16 @@ static int mcde_dsi_host_detach(struct mipi_dsi_host *host, (type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \ (type == MIPI_DSI_DCS_READ)) -static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) +static int mcde_dsi_execute_transfer(struct mcde_dsi *d, + const struct mipi_dsi_msg *msg) { - struct mcde_dsi *d = host_to_mcde_dsi(host); const u32 loop_delay_us = 10; /* us */ - const u8 *tx = msg->tx_buf; u32 loop_counter; size_t txlen = msg->tx_len; size_t rxlen = msg->rx_len; + int i; u32 val; int ret; - int i; - - if (txlen > 16) { - dev_err(d->dev, - "dunno how to write more than 16 bytes yet\n"); - return -EIO; - } - if (rxlen > 4) { - dev_err(d->dev, - "dunno how to read more than 4 bytes yet\n"); - return -EIO; - } - - dev_dbg(d->dev, - "message to channel %d, write %zd bytes read %zd bytes\n", - msg->channel, txlen, rxlen); - - /* Command "nature" */ - if (MCDE_DSI_HOST_IS_READ(msg->type)) - /* MCTL_MAIN_DATA_CTL already set up */ - val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ; - else - val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE; - /* - * More than 2 bytes will not fit in a single packet, so it's - * time to set the "long not short" bit. One byte is used by - * the MIPI DCS command leaving just one byte for the payload - * in a short package. - */ - if (mipi_dsi_packet_format_is_long(msg->type)) - val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT; - val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; - val |= txlen << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; - val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; - val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; - writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); - - /* MIPI DCS command is part of the data */ - if (txlen > 0) { - val = 0; - for (i = 0; i < 4 && i < txlen; i++) - val |= tx[i] << (i * 8); - } - writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0); - if (txlen > 4) { - val = 0; - for (i = 0; i < 4 && (i + 4) < txlen; i++) - val |= tx[i + 4] << (i * 8); - writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1); - } - if (txlen > 8) { - val = 0; - for (i = 0; i < 4 && (i + 8) < txlen; i++) - val |= tx[i + 8] << (i * 8); - writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2); - } - if (txlen > 12) { - val = 0; - for (i = 0; i < 4 && (i + 12) < txlen; i++) - val |= tx[i + 12] << (i * 8); - writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3); - } writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); @@ -297,6 +234,7 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, usleep_range(loop_delay_us, (loop_delay_us * 3) / 2); if (!loop_counter) { dev_err(d->dev, "DSI read timeout!\n"); + /* Set exit code and retry */ return -ETIME; } } else { @@ -307,6 +245,7 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, usleep_range(loop_delay_us, (loop_delay_us * 3) / 2); if (!loop_counter) { + /* Set exit code and retry */ dev_err(d->dev, "DSI write timeout!\n"); return -ETIME; } @@ -348,6 +287,93 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, ret = rdsz; } + /* Successful transmission */ + return ret; +} + +static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct mcde_dsi *d = host_to_mcde_dsi(host); + const u8 *tx = msg->tx_buf; + size_t txlen = msg->tx_len; + size_t rxlen = msg->rx_len; + unsigned int retries = 0; + u32 val; + int ret; + int i; + + if (txlen > 16) { + dev_err(d->dev, + "dunno how to write more than 16 bytes yet\n"); + return -EIO; + } + if (rxlen > 4) { + dev_err(d->dev, + "dunno how to read more than 4 bytes yet\n"); + return -EIO; + } + + dev_dbg(d->dev, + "message to channel %d, write %zd bytes read %zd bytes\n", + msg->channel, txlen, rxlen); + + /* Command "nature" */ + if (MCDE_DSI_HOST_IS_READ(msg->type)) + /* MCTL_MAIN_DATA_CTL already set up */ + val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ; + else + val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE; + /* + * More than 2 bytes will not fit in a single packet, so it's + * time to set the "long not short" bit. One byte is used by + * the MIPI DCS command leaving just one byte for the payload + * in a short package. + */ + if (mipi_dsi_packet_format_is_long(msg->type)) + val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT; + val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; + val |= txlen << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; + val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; + val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; + writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); + + /* MIPI DCS command is part of the data */ + if (txlen > 0) { + val = 0; + for (i = 0; i < 4 && i < txlen; i++) + val |= tx[i] << (i * 8); + } + writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0); + if (txlen > 4) { + val = 0; + for (i = 0; i < 4 && (i + 4) < txlen; i++) + val |= tx[i + 4] << (i * 8); + writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1); + } + if (txlen > 8) { + val = 0; + for (i = 0; i < 4 && (i + 8) < txlen; i++) + val |= tx[i + 8] << (i * 8); + writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2); + } + if (txlen > 12) { + val = 0; + for (i = 0; i < 4 && (i + 12) < txlen; i++) + val |= tx[i + 12] << (i * 8); + writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3); + } + + while (retries < 3) { + ret = mcde_dsi_execute_transfer(d, msg); + if (ret >= 0) + break; + retries++; + } + if (ret < 0 && retries) + dev_err(d->dev, "gave up after %d retries\n", retries); + + /* Clear any errors */ writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); |