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) >