]> www.infradead.org Git - users/hch/misc.git/commitdiff
drm/rcar-du: dsi: Implement DSI command support
authorMarek Vasut <marek.vasut+renesas@mailbox.org>
Sun, 31 Aug 2025 19:04:25 +0000 (21:04 +0200)
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Mon, 8 Sep 2025 06:45:01 +0000 (09:45 +0300)
Implement support for DSI command transfer. Transmission of both Short
Packet and Long Packet is implemented, so is command transmission to
request response from peripheral device and transmission of non-read
command with BTA.

The AXI memory access mode is currently not implemented, each transfer
is performed purely using controller register interface. Short Packet
transfer can transfer up to 2 Bytes of data, Long Packet transfer can
transfer up to 16 Bytes of data.

Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Link: https://lore.kernel.org/r/20250831190507.327848-1-marek.vasut+renesas@mailbox.org
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h

index 952c3efb74da9b58aba55fb8f71325399631ce99..5c73a513f678ecc1b7d088c187cb0c751c049a39 100644 (file)
@@ -937,9 +937,234 @@ static int rcar_mipi_dsi_host_detach(struct mipi_dsi_host *host,
        return 0;
 }
 
+static ssize_t rcar_mipi_dsi_host_tx_transfer(struct mipi_dsi_host *host,
+                                             const struct mipi_dsi_msg *msg,
+                                             bool is_rx_xfer)
+{
+       const bool is_tx_long = mipi_dsi_packet_format_is_long(msg->type);
+       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
+       struct mipi_dsi_packet packet;
+       u8 payload[16] = { 0 };
+       u32 status;
+       int ret;
+
+       ret = mipi_dsi_create_packet(&packet, msg);
+       if (ret)
+               return ret;
+
+       /* Configure LP or HS command transfer. */
+       rcar_mipi_dsi_write(dsi, TXCMSETR, (msg->flags & MIPI_DSI_MSG_USE_LPM) ?
+                                          TXCMSETR_SPDTYP : 0);
+
+       /* Register access mode for RX transfer. */
+       if (is_rx_xfer)
+               rcar_mipi_dsi_write(dsi, RXPSETR, 0);
+
+       /* Do not use IRQ, poll for completion, the completion is quick. */
+       rcar_mipi_dsi_write(dsi, TXCMIER, 0);
+
+       /*
+        * Send the header:
+        * header[0] = Virtual Channel + Data Type
+        * header[1] = Word Count LSB (LP) or first param (SP)
+        * header[2] = Word Count MSB (LP) or second param (SP)
+        */
+       rcar_mipi_dsi_write(dsi, TXCMPHDR,
+                           (is_tx_long ? TXCMPHDR_FMT : 0) |
+                           TXCMPHDR_VC(msg->channel) |
+                           TXCMPHDR_DT(msg->type) |
+                           TXCMPHDR_DATA1(packet.header[2]) |
+                           TXCMPHDR_DATA0(packet.header[1]));
+
+       if (is_tx_long) {
+               memcpy(payload, packet.payload,
+                      min(msg->tx_len, sizeof(payload)));
+
+               rcar_mipi_dsi_write(dsi, TXCMPPD0R,
+                                   (payload[3] << 24) | (payload[2] << 16) |
+                                   (payload[1] << 8) | payload[0]);
+               rcar_mipi_dsi_write(dsi, TXCMPPD1R,
+                                   (payload[7] << 24) | (payload[6] << 16) |
+                                   (payload[5] << 8) | payload[4]);
+               rcar_mipi_dsi_write(dsi, TXCMPPD2R,
+                                   (payload[11] << 24) | (payload[10] << 16) |
+                                   (payload[9] << 8) | payload[8]);
+               rcar_mipi_dsi_write(dsi, TXCMPPD3R,
+                                   (payload[15] << 24) | (payload[14] << 16) |
+                                   (payload[13] << 8) | payload[12]);
+       }
+
+       /* Start the transfer, RX with BTA, TX without BTA. */
+       if (is_rx_xfer) {
+               rcar_mipi_dsi_write(dsi, TXCMCR, TXCMCR_BTAREQ);
+
+               /* Wait until the transmission, BTA, reception completed. */
+               ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                                       (status & RXPSR_BTAREQEND),
+                                       2000, 50000, false, dsi, RXPSR);
+       } else {
+               rcar_mipi_dsi_write(dsi, TXCMCR, TXCMCR_TXREQ);
+
+               /* Wait until the transmission completed. */
+               ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                                       (status & TXCMSR_TXREQEND),
+                                       2000, 50000, false, dsi, TXCMSR);
+       }
+
+       if (ret < 0) {
+               dev_err(dsi->dev, "Command transfer timeout (0x%08x)\n",
+                       status);
+               return ret;
+       }
+
+       return packet.size;
+}
+
+static ssize_t rcar_mipi_dsi_host_rx_transfer(struct mipi_dsi_host *host,
+                                             const struct mipi_dsi_msg *msg)
+{
+       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
+       u8 *rx_buf = (u8 *)(msg->rx_buf);
+       u32 reg, data, status, wc;
+       int i, ret;
+
+       /* RX transfer received data validation and parsing starts here. */
+       reg = rcar_mipi_dsi_read(dsi, TOSR);
+       if (reg & TOSR_TATO) {  /* Turn-Around TimeOut. */
+               /* Clear TATO Turn-Around TimeOut bit. */
+               rcar_mipi_dsi_write(dsi, TOSR, TOSR_TATO);
+               return -ETIMEDOUT;
+       }
+
+       reg = rcar_mipi_dsi_read(dsi, RXPSR);
+
+       if (msg->flags & MIPI_DSI_MSG_REQ_ACK) {
+               /* Transfer with zero-length RX. */
+               if (!(reg & RXPSR_RCVACK)) {
+                       /* No ACK on RX response received. */
+                       return -EINVAL;
+               }
+       } else {
+               /* Transfer with non-zero-length RX. */
+               if (!(reg & RXPSR_RCVRESP)) {
+                       /* No packet header of RX response received. */
+                       return -EINVAL;
+               }
+
+               if (reg & (RXPSR_CRCERR | RXPSR_WCERR | RXPSR_AXIERR | RXPSR_OVRERR)) {
+                       /* Incorrect response payload. */
+                       return -ENODATA;
+               }
+
+               data = rcar_mipi_dsi_read(dsi, RXPHDR);
+               if (data & RXPHDR_FMT) {        /* Long Packet Response. */
+                       /* Read Long Packet Response length from packet header. */
+                       wc = data & 0xffff;
+                       if (wc > msg->rx_len) {
+                               dev_warn(dsi->dev,
+                                        "Long Packet Response longer than RX buffer (%d), limited to %zu Bytes\n",
+                                        wc, msg->rx_len);
+                               wc = msg->rx_len;
+                       }
+
+                       if (wc > 16) {
+                               dev_warn(dsi->dev,
+                                        "Long Packet Response too long (%d), limited to 16 Bytes\n",
+                                        wc);
+                               wc = 16;
+                       }
+
+                       for (i = 0; i < msg->rx_len; i++) {
+                               if (!(i % 4))
+                                       data = rcar_mipi_dsi_read(dsi, RXPPD0R + i);
+
+                               rx_buf[i] = data & 0xff;
+                               data >>= 8;
+                       }
+               } else {        /* Short Packet Response. */
+                       if (msg->rx_len >= 1)
+                               rx_buf[0] = data & 0xff;
+                       if (msg->rx_len >= 2)
+                               rx_buf[1] = (data >> 8) & 0xff;
+                       if (msg->rx_len >= 3) {
+                               dev_warn(dsi->dev,
+                                        "Expected Short Packet Response too long (%zu), limited to 2 Bytes\n",
+                                        msg->rx_len);
+                       }
+               }
+       }
+
+       if (reg & RXPSR_RCVAKE) {
+               /* Acknowledge and Error report received. */
+               return -EFAULT;
+       }
+
+       /* Wait until the bus handover to host processor completed. */
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               !(status & PPIDL0SR_DIR),
+                               2000, 50000, false, dsi, PPIDL0SR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Command RX DIR timeout (0x%08x)\n", status);
+               return ret;
+       }
+
+       /* Wait until the data lane is in LP11 stop state. */
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               status & PPIDL0SR_STPST,
+                               2000, 50000, false, dsi, PPIDL0SR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Command RX STPST timeout (0x%08x)\n", status);
+               return ret;
+       }
+
+       return 0;
+}
+
+static ssize_t rcar_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+                                          const struct mipi_dsi_msg *msg)
+{
+       const bool is_rx_xfer = (msg->flags & MIPI_DSI_MSG_REQ_ACK) || msg->rx_len;
+       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
+       int ret;
+
+       if (msg->tx_len > 16 || msg->rx_len > 16) {
+               /* ToDo: Implement Memory on AXI bus command mode. */
+               dev_warn(dsi->dev,
+                        "Register-based command mode supports only up to 16 Bytes long payload\n");
+               return -EOPNOTSUPP;
+       }
+
+       ret = rcar_mipi_dsi_host_tx_transfer(host, msg, is_rx_xfer);
+
+       /* If TX transfer succeeded and this transfer has RX part. */
+       if (ret >= 0 && is_rx_xfer) {
+               ret = rcar_mipi_dsi_host_rx_transfer(host, msg);
+               if (ret)
+                       return ret;
+
+               ret = msg->rx_len;
+       }
+
+       /*
+        * Wait a bit between commands, otherwise panels based on ILI9881C
+        * TCON may fail to correctly receive all commands sent to them.
+        * Until we can actually test with another DSI device, keep the
+        * delay here, but eventually this delay might have to be moved
+        * into the ILI9881C panel driver.
+        */
+       usleep_range(1000, 2000);
+
+       /* Clear the completion interrupt. */
+       if (!msg->rx_len)
+               rcar_mipi_dsi_write(dsi, TXCMSR, TXCMSR_TXREQEND);
+
+       return ret;
+}
+
 static const struct mipi_dsi_host_ops rcar_mipi_dsi_host_ops = {
        .attach = rcar_mipi_dsi_host_attach,
        .detach = rcar_mipi_dsi_host_detach,
+       .transfer = rcar_mipi_dsi_host_transfer
 };
 
 /* -----------------------------------------------------------------------------
index a54c7eb4113b93c9991ed078b49ecd66552e66b7..76521276e2af8eb8d3d494af4b05d811fb737fc9 100644 (file)
 #define TXSETR                         0x100
 #define TXSETR_LANECNT_MASK            (0x3 << 0)
 
+/*
+ * DSI Command Transfer Registers
+ */
+#define TXCMSETR                       0x110
+#define TXCMSETR_SPDTYP                        (1 << 8)        /* 0:HS 1:LP */
+#define TXCMSETR_LPPDACC               (1 << 0)
+#define TXCMCR                         0x120
+#define TXCMCR_BTATYP                  (1 << 2)
+#define TXCMCR_BTAREQ                  (1 << 1)
+#define TXCMCR_TXREQ                   (1 << 0)
+#define TXCMSR                         0x130
+#define TXCMSR_CLSNERR                 (1 << 18)
+#define TXCMSR_AXIERR                  (1 << 16)
+#define TXCMSR_TXREQEND                        (1 << 0)
+#define TXCMSCR                                0x134
+#define TXCMSCR_CLSNERR                        (1 << 18)
+#define TXCMSCR_AXIERR                 (1 << 16)
+#define TXCMSCR_TXREQEND               (1 << 0)
+#define TXCMIER                                0x138
+#define TXCMIER_CLSNERR                        (1 << 18)
+#define TXCMIER_AXIERR                 (1 << 16)
+#define TXCMIER_TXREQEND               (1 << 0)
+#define TXCMADDRSET0R                  0x140
+#define TXCMPHDR                       0x150
+#define TXCMPHDR_FMT                   (1 << 24)       /* 0:SP 1:LP */
+#define TXCMPHDR_VC(n)                 (((n) & 0x3) << 22)
+#define TXCMPHDR_DT(n)                 (((n) & 0x3f) << 16)
+#define TXCMPHDR_DATA1(n)              (((n) & 0xff) << 8)
+#define TXCMPHDR_DATA0(n)              (((n) & 0xff) << 0)
+#define TXCMPPD0R                      0x160
+#define TXCMPPD1R                      0x164
+#define TXCMPPD2R                      0x168
+#define TXCMPPD3R                      0x16c
+
+#define RXSETR                         0x200
+#define RXSETR_CRCEN                   (((n) & 0xf) << 24)
+#define RXSETR_ECCEN                   (((n) & 0xf) << 16)
+#define RXPSETR                                0x210
+#define RXPSETR_LPPDACC                        (1 << 0)
+#define RXPSR                          0x220
+#define RXPSR_ECCERR1B                 (1 << 28)
+#define RXPSR_UEXTRGERR                        (1 << 25)
+#define RXPSR_RESPTOERR                        (1 << 24)
+#define RXPSR_OVRERR                   (1 << 23)
+#define RXPSR_AXIERR                   (1 << 22)
+#define RXPSR_CRCERR                   (1 << 21)
+#define RXPSR_WCERR                    (1 << 20)
+#define RXPSR_UEXDTERR                 (1 << 19)
+#define RXPSR_UEXPKTERR                        (1 << 18)
+#define RXPSR_ECCERR                   (1 << 17)
+#define RXPSR_MLFERR                   (1 << 16)
+#define RXPSR_RCVACK                   (1 << 14)
+#define RXPSR_RCVEOT                   (1 << 10)
+#define RXPSR_RCVAKE                   (1 << 9)
+#define RXPSR_RCVRESP                  (1 << 8)
+#define RXPSR_BTAREQEND                        (1 << 0)
+#define RXPSCR                         0x224
+#define RXPSCR_ECCERR1B                        (1 << 28)
+#define RXPSCR_UEXTRGERR               (1 << 25)
+#define RXPSCR_RESPTOERR               (1 << 24)
+#define RXPSCR_OVRERR                  (1 << 23)
+#define RXPSCR_AXIERR                  (1 << 22)
+#define RXPSCR_CRCERR                  (1 << 21)
+#define RXPSCR_WCERR                   (1 << 20)
+#define RXPSCR_UEXDTERR                        (1 << 19)
+#define RXPSCR_UEXPKTERR               (1 << 18)
+#define RXPSCR_ECCERR                  (1 << 17)
+#define RXPSCR_MLFERR                  (1 << 16)
+#define RXPSCR_RCVACK                  (1 << 14)
+#define RXPSCR_RCVEOT                  (1 << 10)
+#define RXPSCR_RCVAKE                  (1 << 9)
+#define RXPSCR_RCVRESP                 (1 << 8)
+#define RXPSCR_BTAREQEND               (1 << 0)
+#define RXPIER                         0x228
+#define RXPIER_ECCERR1B                        (1 << 28)
+#define RXPIER_UEXTRGERR               (1 << 25)
+#define RXPIER_RESPTOERR               (1 << 24)
+#define RXPIER_OVRERR                  (1 << 23)
+#define RXPIER_AXIERR                  (1 << 22)
+#define RXPIER_CRCERR                  (1 << 21)
+#define RXPIER_WCERR                   (1 << 20)
+#define RXPIER_UEXDTERR                        (1 << 19)
+#define RXPIER_UEXPKTERR               (1 << 18)
+#define RXPIER_ECCERR                  (1 << 17)
+#define RXPIER_MLFERR                  (1 << 16)
+#define RXPIER_RCVACK                  (1 << 14)
+#define RXPIER_RCVEOT                  (1 << 10)
+#define RXPIER_RCVAKE                  (1 << 9)
+#define RXPIER_RCVRESP                 (1 << 8)
+#define RXPIER_BTAREQEND               (1 << 0)
+#define RXPADDRSET0R                   0x230
+#define RXPSIZESETR                    0x238
+#define RXPSIZESETR_SIZE(n)            (((n) & 0xf) << 3)
+#define RXPHDR                         0x240
+#define RXPHDR_FMT                     (1 << 24)       /* 0:SP 1:LP */
+#define RXPHDR_VC(n)                   (((n) & 0x3) << 22)
+#define RXPHDR_DT(n)                   (((n) & 0x3f) << 16)
+#define RXPHDR_DATA1(n)                        (((n) & 0xff) << 8)
+#define RXPHDR_DATA0(n)                        (((n) & 0xff) << 0)
+#define RXPPD0R                                0x250
+#define RXPPD1R                                0x254
+#define RXPPD2R                                0x258
+#define RXPPD3R                                0x25c
+#define AKEPR                          0x300
+#define AKEPR_VC(n)                    (((n) & 0x3) << 22)
+#define AKEPR_DT(n)                    (((n) & 0x3f) << 16)
+#define AKEPR_ERRRPT(n)                        (((n) & 0xffff) << 0)
+#define RXRESPTOSETR                   0x400
+#define TACR                           0x500
+#define TASR                           0x510
+#define TASCR                          0x514
+#define TAIER                          0x518
+#define TOSR                           0x610
+#define TOSR_TATO                      (1 << 2)
+#define TOSR_LRXHTO                    (1 << 1)
+#define TOSR_HRXTO                     (1 << 0)
+#define TOSCR                          0x614
+#define TOSCR_TATO                     (1 << 2)
+#define TOSCR_LRXHTO                   (1 << 1)
+#define TOSCR_HRXTO                    (1 << 0)
+
 /*
  * Video Mode Register
  */
 #define PPICLSCR_HSTOLP                        (1 << 27)
 #define PPICLSCR_TOHS                  (1 << 26)
 
+#define PPIDL0SR                       0x740
+#define PPIDL0SR_DIR                   (1 << 10)
+#define PPIDL0SR_STPST                 (1 << 6)
+
 #define PPIDLSR                                0x760
 #define PPIDLSR_STPST                  (0xf << 0)