To allow to use panels with their SPI port connected to the bridge, add
SPI master support. Due to lack of a complete datasheet, mode and speed
are not configurable. It is also unclear how CS behaves.

Signed-off-by: Andreas Kemnade <[email protected]>
---
 drivers/gpu/drm/bridge/tc358762.c | 72 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/tc358762.c 
b/drivers/gpu/drm/bridge/tc358762.c
index ec72e6e92b46..8024e49e2868 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -16,6 +16,9 @@
 #include <linux/module.h>
 #include <linux/of_graph.h>
 #include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/unaligned.h>
 
 #include <video/mipi_display.h>
 #include <video/videomode.h>
@@ -72,7 +75,11 @@
 
 /* SPI Master Registers */
 #define SPICMR                 0x0450
-#define SPITCR                 0x0454
+#define SPI_SEL_CS0            0x0002
+
+#define SPITCR1                        0x0454
+
+#define WCMDQUE                        0x0500
 
 /* System Controller Registers */
 #define SYSCTRL                        0x0464
@@ -98,6 +105,7 @@ struct tc358762 {
        struct regulator *regulator;
        struct drm_bridge *panel_bridge;
        struct gpio_desc *reset_gpio;
+       struct spi_controller *spi;
        bool pre_enabled;
        int error;
        bool use_vtg;
@@ -252,11 +260,25 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
 
        tc358762_write(ctx, LCDCTRL, lcdctrl);
 
+       /*
+        * value just copied from vendor driver, no idea which settings
+        * are configured
+        */
+       if (ctx->spi)
+               tc358762_write(ctx, SPITCR1, 0x00000122);
+
        tc358762_write(ctx, PPI_STARTPPI, PPI_STARTPPI_STARTPPI);
        tc358762_write(ctx, DSI_STARTDSI, DSI_STARTDSI_STARTDSI);
 
        msleep(100);
 
+       /*
+        * no idea when CS is actually asserted, maybe once per handling of
+        * one packet written to WCMDQUE? Maybe just after setting SPI_SEL_CS0
+        */
+       if (ctx->spi)
+               tc358762_write(ctx, SPICMR, SPI_SEL_CS0);
+
        ret = tc358762_clear_error(ctx);
        if (ret < 0)
                dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
@@ -308,6 +330,40 @@ static int tc358762_parse_dt(struct tc358762 *ctx)
        return 0;
 }
 
+static int tc358762_spi_transfer_one(struct spi_controller *ctlr,
+                                    struct spi_device *spi,
+                                    struct spi_transfer *t)
+{
+       struct tc358762 *ctx = spi_controller_get_devdata(ctlr);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+       /*
+        * limits to be determined, just define something which is
+        * enough for current use case.
+        */
+       u8 data[8];
+
+       if (!ctx->pre_enabled)
+               return -ENODEV;
+
+       if (t->len > sizeof(data) - 2)
+               return -EOVERFLOW;
+
+       /*
+        * half duplex is supported by the bridge,
+        * but due to lack of testing possibilities, support only simplex write
+        */
+       if (t->rx_buf)
+               return -EINVAL;
+
+       if (!t->tx_buf)
+               return -EINVAL;
+
+       put_unaligned_le16(WCMDQUE, data);
+       memcpy(data + 2, t->tx_buf, t->len);
+
+       return mipi_dsi_generic_write(dsi, data, t->len + 2);
+}
+
 static int tc358762_configure_regulators(struct tc358762 *ctx)
 {
        ctx->regulator = devm_regulator_get(ctx->dev, "vddc");
@@ -321,6 +377,7 @@ static int tc358762_probe(struct mipi_dsi_device *dsi)
 {
        struct device *dev = &dsi->dev;
        struct tc358762 *ctx;
+       struct device_node *spi_node;
        int ret;
 
        ctx = devm_drm_bridge_alloc(dev, struct tc358762, bridge,
@@ -333,6 +390,19 @@ static int tc358762_probe(struct mipi_dsi_device *dsi)
        ctx->dev = dev;
        ctx->pre_enabled = false;
 
+       if (IS_ENABLED(CONFIG_SPI)) {
+               spi_node = of_get_child_by_name(dev->of_node, "spi");
+               if (spi_node) {
+                       ctx->spi = devm_spi_alloc_host(dev, 0);
+                       spi_controller_set_devdata(ctx->spi, ctx);
+                       ctx->spi->transfer_one = tc358762_spi_transfer_one;
+                       ctx->spi->dev.of_node = spi_node;
+                       ret = devm_spi_register_controller(dev, ctx->spi);
+                       if (ret < 0)
+                               return dev_err_probe(dev, ret, "register spi 
controller  failed\n");
+               }
+       }
+
        /* Always use VTG */
        ctx->use_vtg = true;
 

-- 
2.47.3

Reply via email to