From: Hugo Villeneuve <hvillene...@dimonoff.com>

Add support for sending MIPI DSI command packets from the host to a
peripheral. This is required for panels that need configuration before
they accept video data.

Based on Renesas Linux kernel v5.10 repos [1].

Link: https://github.com/renesas-rz/rz_linux-cip.git
Cc: Biju Das <biju.das...@bp.renesas.com>
Cc: Chris Brandt <chris.bra...@renesas.com>
Signed-off-by: Hugo Villeneuve <hvillene...@dimonoff.com>
---
 .../gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c    | 174 ++++++++++++++++++
 .../drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h   |  56 ++++++
 2 files changed, 230 insertions(+)

diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c 
b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index dc6ab012cdb69..77d3a31ff8e35 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
@@ -6,6 +6,7 @@
  */
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
@@ -23,9 +24,12 @@
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
+#include <video/mipi_display.h>
 
 #include "rzg2l_mipi_dsi_regs.h"
 
+#define RZG2L_DCS_BUF_SIZE     128 /* Maximum DCS buffer size in external 
memory. */
+
 struct rzg2l_mipi_dsi {
        struct device *dev;
        void __iomem *mmio;
@@ -44,6 +48,10 @@ struct rzg2l_mipi_dsi {
        unsigned int num_data_lanes;
        unsigned int lanes;
        unsigned long mode_flags;
+
+       /* DCS buffer pointers when using external memory. */
+       dma_addr_t dcs_buf_phys;
+       u8 *dcs_buf_virt;
 };
 
 static inline struct rzg2l_mipi_dsi *
@@ -651,9 +659,168 @@ static int rzg2l_mipi_dsi_host_detach(struct 
mipi_dsi_host *host,
        return 0;
 }
 
+static ssize_t rzg2l_mipi_dsi_read_response(struct rzg2l_mipi_dsi *dsi,
+                                           const struct mipi_dsi_msg *msg)
+{
+       u8 *msg_rx = msg->rx_buf;
+       u16 size;
+       u8 datatype;
+       u32 result;
+
+       result = rzg2l_mipi_dsi_link_read(dsi, RXRSS0R);
+       if (result & RXRSS0R_RXPKTDFAIL) {
+               dev_err(dsi->dev, "packet rx data did not save correctly\n");
+               return -EPROTO;
+       }
+
+       if (result & RXRSS0R_RXFAIL) {
+               dev_err(dsi->dev, "packet rx failure\n");
+               return -EPROTO;
+       }
+
+       if (!(result & RXRSS0R_RXSUC))
+               return -EPROTO;
+
+       datatype = FIELD_GET(RXRSS0R_DT, result);
+
+       switch (datatype) {
+       case 0:
+               dev_dbg(dsi->dev, "ACK\n");
+               return 0;
+       case MIPI_DSI_RX_END_OF_TRANSMISSION:
+               dev_dbg(dsi->dev, "EoTp\n");
+               return 0;
+       case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+               dev_dbg(dsi->dev, "Acknowledge and error report: $%02x%02x\n",
+                       (u8)FIELD_GET(RXRSS0R_DATA1, result),
+                       (u8)FIELD_GET(RXRSS0R_DATA0, result));
+               return 0;
+       case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+       case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+               msg_rx[0] = FIELD_GET(RXRSS0R_DATA0, result);
+               return 1;
+       case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+       case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+               msg_rx[0] = FIELD_GET(RXRSS0R_DATA0, result);
+               msg_rx[1] = FIELD_GET(RXRSS0R_DATA1, result);
+               return 2;
+       case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+       case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+               size = FIELD_GET(RXRSS0R_WC, result);
+
+               if (size > msg->rx_len) {
+                       dev_err(dsi->dev, "rx buffer too small");
+                       return -ENOSPC;
+               }
+
+               memcpy(msg_rx, dsi->dcs_buf_virt, size);
+               return size;
+       default:
+               dev_err(dsi->dev, "unhandled response type: %02x\n", datatype);
+               return -EPROTO;
+       }
+}
+
+static ssize_t rzg2l_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+                                           const struct mipi_dsi_msg *msg)
+{
+       struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host);
+       struct mipi_dsi_packet packet;
+       bool need_bta;
+       u32 value;
+       int ret;
+
+       ret = mipi_dsi_create_packet(&packet, msg);
+       if (ret < 0)
+               return ret;
+
+       /* Terminate operation after this descriptor is finished */
+       value = SQCH0DSC0AR_NXACT_TERM;
+
+       if (msg->flags & MIPI_DSI_MSG_REQ_ACK) {
+               need_bta = true; /* Message with explicitly requested ACK */
+               value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_NON_READ);
+       } else if (msg->rx_buf && msg->rx_len > 0) {
+               need_bta = true; /* Read request */
+               value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_READ);
+       } else {
+               need_bta = false;
+               value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_NONE);
+       }
+
+       /* Set transmission speed */
+       if (msg->flags & MIPI_DSI_MSG_USE_LPM)
+               value |= SQCH0DSC0AR_SPD_LOW;
+       else
+               value |= SQCH0DSC0AR_SPD_HIGH;
+
+       /* Write TX packet header */
+       value |= FIELD_PREP(SQCH0DSC0AR_DT, packet.header[0]) |
+               FIELD_PREP(SQCH0DSC0AR_DATA0, packet.header[1]) |
+               FIELD_PREP(SQCH0DSC0AR_DATA1, packet.header[2]);
+
+       if (mipi_dsi_packet_format_is_long(msg->type)) {
+               value |= SQCH0DSC0AR_FMT_LONG;
+
+               if (packet.payload_length > RZG2L_DCS_BUF_SIZE) {
+                       dev_err(dsi->dev, "Packet Tx payload size (%d) too 
large",
+                               (unsigned int)packet.payload_length);
+                       return -ENOSPC;
+               }
+
+               /* Copy TX packet payload data to memory space */
+               memcpy(dsi->dcs_buf_virt, packet.payload, 
packet.payload_length);
+       } else {
+               value |= SQCH0DSC0AR_FMT_SHORT;
+       }
+
+       rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0AR, value);
+
+       /*
+        * Write: specify payload data source location, only used for
+        *        long packet.
+        * Read:  specify payload data storage location of response
+        *        packet. Note: a read packet is always a short packet.
+        *        If the response packet is a short packet or a long packet
+        *        with WC = 0 (no payload), DTSEL is meaningless.
+        */
+       rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0BR, 
SQCH0DSC0BR_DTSEL_MEM_SPACE);
+
+       /*
+        * Set SQCHxSR.AACTFIN bit when descriptor actions are finished.
+        * Read: set Rx result save slot number to 0 (ACTCODE).
+        */
+       rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0CR, SQCH0DSC0CR_FINACT);
+
+       /* Set rx/tx payload data address, only relevant for long packet. */
+       rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0DR, (u32)dsi->dcs_buf_phys);
+
+       /* Start sequence 0 operation */
+       value = rzg2l_mipi_dsi_link_read(dsi, SQCH0SET0R);
+       value |= SQCH0SET0R_START;
+       rzg2l_mipi_dsi_link_write(dsi, SQCH0SET0R, value);
+
+       /* Wait for operation to finish */
+       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read,
+                               value, value & SQCH0SR_ADESFIN,
+                               2000, 20000, false, dsi, SQCH0SR);
+       if (ret == 0) {
+               /* Success: clear status bit */
+               rzg2l_mipi_dsi_link_write(dsi, SQCH0SCR, SQCH0SCR_ADESFIN);
+
+               if (need_bta)
+                       ret = rzg2l_mipi_dsi_read_response(dsi, msg);
+               else
+                       ret = packet.payload_length;
+       }
+
+       return ret;
+}
+
 static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = {
        .attach = rzg2l_mipi_dsi_host_attach,
        .detach = rzg2l_mipi_dsi_host_detach,
+       .transfer = rzg2l_mipi_dsi_host_transfer,
 };
 
 /* 
-----------------------------------------------------------------------------
@@ -771,6 +938,11 @@ static int rzg2l_mipi_dsi_probe(struct platform_device 
*pdev)
        if (ret < 0)
                goto err_pm_disable;
 
+       dsi->dcs_buf_virt = dma_alloc_coherent(dsi->host.dev, 
RZG2L_DCS_BUF_SIZE,
+                                              &dsi->dcs_buf_phys, GFP_KERNEL);
+       if (!dsi->dcs_buf_virt)
+               return -ENOMEM;
+
        return 0;
 
 err_phy:
@@ -785,6 +957,8 @@ static void rzg2l_mipi_dsi_remove(struct platform_device 
*pdev)
 {
        struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev);
 
+       dma_free_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, dsi->dcs_buf_virt,
+                         dsi->dcs_buf_phys);
        mipi_dsi_host_unregister(&dsi->host);
        pm_runtime_disable(&pdev->dev);
 }
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h 
b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
index 1dbc16ec64a4b..33cd669bc74b1 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
@@ -81,6 +81,16 @@
 #define RSTSR_SWRSTLP                  (1 << 1)
 #define RSTSR_SWRSTHS                  (1 << 0)
 
+/* Rx Result Save Slot 0 Register */
+#define RXRSS0R                                0x240
+#define RXRSS0R_RXPKTDFAIL             BIT(28)
+#define RXRSS0R_RXFAIL                 BIT(27)
+#define RXRSS0R_RXSUC                  BIT(25)
+#define RXRSS0R_DT                     GENMASK(21, 16)
+#define RXRSS0R_DATA1                  GENMASK(15, 8)
+#define RXRSS0R_DATA0                  GENMASK(7, 0)
+#define RXRSS0R_WC                     GENMASK(15, 0) /* Word count for long 
packet. */
+
 /* Clock Lane Stop Time Set Register */
 #define CLSTPTSETR                     0x314
 #define CLSTPTSETR_CLKKPT(x)           ((x) << 24)
@@ -148,4 +158,50 @@
 #define VICH1HPSETR_HFP(x)             (((x) & 0x1fff) << 16)
 #define VICH1HPSETR_HBP(x)             (((x) & 0x1fff) << 0)
 
+/* Sequence Channel 0 Set 0 Register */
+#define SQCH0SET0R                     0x5c0
+#define SQCH0SET0R_START               BIT(0)
+
+/* Sequence Channel 0 Set 1 Register */
+#define SQCH0SET1R                     0x5c4
+
+/* Sequence Channel 0 Status Register */
+#define SQCH0SR                                0x5d0
+#define SQCH0SR_RUNNING                        BIT(2)
+#define SQCH0SR_ADESFIN                        BIT(8)
+
+/* Sequence Channel 0 Status Clear Register */
+#define SQCH0SCR                       0x5d4
+#define SQCH0SCR_ADESFIN               BIT(8)
+
+/* Sequence Channel 0 Descriptor 0-A Register */
+#define SQCH0DSC0AR                    0x780
+#define SQCH0DSC0AR_NXACT_TERM         0
+#define SQCH0DSC0AR_NXACT_OPER         BIT(28)
+#define SQCH0DSC0AR_BTA                        GENMASK(27, 26)
+#define SQCH0DSC0AR_BTA_NONE           0
+#define SQCH0DSC0AR_BTA_NON_READ       1
+#define SQCH0DSC0AR_BTA_READ           2
+#define SQCH0DSC0AR_BTA_ONLY           3
+#define SQCH0DSC0AR_SPD_HIGH           0
+#define SQCH0DSC0AR_SPD_LOW            BIT(25)
+#define SQCH0DSC0AR_FMT_SHORT          0
+#define SQCH0DSC0AR_FMT_LONG           BIT(24)
+#define SQCH0DSC0AR_DT                 GENMASK(21, 16)
+#define SQCH0DSC0AR_DATA1              GENMASK(15, 8)
+#define SQCH0DSC0AR_DATA0              GENMASK(7, 0)
+
+/* Sequence Channel 0 Descriptor 0-B Register */
+#define SQCH0DSC0BR                    0x784
+#define SQCH0DSC0BR_DTSEL_PAYLOAD_DR   0       /* Use packet payload data 
register */
+#define SQCH0DSC0BR_DTSEL_MEM_SPACE    BIT(24) /* Use external memory */
+
+/* Sequence Channel 0 Descriptor 0-C Register */
+#define SQCH0DSC0CR                    0x788
+#define SQCH0DSC0CR_FINACT             BIT(0)
+#define SQCH0DSC0CR_AUXOP              BIT(22)
+
+/* Sequence Channel 0 Descriptor 0-D Register */
+#define SQCH0DSC0DR                    0x78c
+
 #endif /* __RZG2L_MIPI_DSI_REGS_H__ */
-- 
2.39.5

Reply via email to