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
