Hi,

On 31/08/2025 22:04, Marek Vasut wrote:
> 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+rene...@mailbox.org>

Looks good to me, pushing to drm-misc-next.

 Tomi

> ---
> Cc: David Airlie <airl...@gmail.com>
> Cc: Geert Uytterhoeven <geert+rene...@glider.be>
> Cc: Kieran Bingham <kieran.bingham+rene...@ideasonboard.com>
> Cc: Laurent Pinchart <laurent.pinchart+rene...@ideasonboard.com>
> Cc: Maarten Lankhorst <maarten.lankho...@linux.intel.com>
> Cc: Magnus Damm <magnus.d...@gmail.com>
> Cc: Maxime Ripard <mrip...@kernel.org>
> Cc: Simona Vetter <sim...@ffwll.ch>
> Cc: Thomas Zimmermann <tzimmerm...@suse.de>
> Cc: Tomi Valkeinen <tomi.valkeinen+rene...@ideasonboard.com>
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-renesas-...@vger.kernel.org
> ---
> V2: - Use %zu instead of %ld to print size_t
>     - Drop mode from commit message
>     - Increase timeouts to 50ms, which is about 3 frame times
>     - Add comments to timeouts
>     - Drop use of BIT() macros, the driver will be converted to them
>       in separate patch
> ---
>  .../gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c   | 225 ++++++++++++++++++
>  .../drm/renesas/rcar-du/rcar_mipi_dsi_regs.h  | 125 ++++++++++
>  2 files changed, 350 insertions(+)
> 
> diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c 
> b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
> index 952c3efb74da9..5c73a513f678e 100644
> --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
> +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
> @@ -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
>  };
>  
>  /* 
> -----------------------------------------------------------------------------
> diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h 
> b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
> index a54c7eb4113b9..76521276e2af8 100644
> --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
> +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
> @@ -15,6 +15,127 @@
>  #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
>   */
> @@ -100,6 +221,10 @@
>  #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)
>  

Reply via email to