[PATCH RFC 5/9] drm/bridge: tc358762: add SPI master support to control panels
From: Andreas Kemnade
Date: Tue Sep 22 2026 - 02:23:58 EST
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 <andreas@xxxxxxxxxxxx>
---
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