* [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display
@ 2026-09-22 6:16 Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 1/9] dt-bindings: display: panel: Add panel for Epson Moverio BT-200 Andreas Kemnade
` (8 more replies)
0 siblings, 9 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:16 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
This series depends on
https://lore.kernel.org/lkml/20260907-tc358762-fixes-v4-0-e3fc3ab5cd83@ideasonboard.com/.
That series is still v7.2 based, but this needs -next material, to work.
It compiles on -rc.
The Epson Moverio BT200 AR glasses have two displays. Each is behind a
TC358762. As disassembly of that unit is not feasible, not much can be
found out about the display chip itself, so no good compatible name for
the panel can be found. Also I do not have access to a full datasheet
for the bridge, so my knowledge is based to values found in vendor
kernel. So more magic numbers than I wanted.
As the panel needs to be configured via SPI, add support in the bridge
for that, also the initialisation needs to be moved to pre_enable()
to be able to use SPI in the panel driver alse in prepare(). To be able
to properly narrow down any issues, add some read checks. Since two
lanes are in use, the intenal PLL needs to be configured to derive
proper clocks. Check for success there. Configuration of the PLL is
too tailored to the BT200, it needs proper divider calculation
derived from the input clock rate.
I did not find any sane looking value for the input clock producing
the known constant for SYSPLL3.
Issues / Limitations:
- SPI only works if bridge is pre-enabled, probably does not matter
as the main use case is to control the display behind it and
SPI transactions are properly rejected if the bridge is not ready.
- SYSPLL3 tailored to BT200.
- error messages on first read in reinitialisation without reset,
no practical problems seem to be connected to this, will
probably went away in most cases if shared reset is enabled.
- some lines missing in output, so the picture gets around
10% smaller vertially, with the upper 10% repeating at
the button. I have a standalone variant of the panel driver
which does not have that issue. I did not find any significant
differences in omapdss registers per debugfs yet.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
Andreas Kemnade (9):
dt-bindings: display: panel: Add panel for Epson Moverio BT-200
drm: panel: Add panel for Epson Moverio BT200
dt-bindings: display: bridge: tc358762: Add some missing properties
drm/bridge: tc358762: Move initialisation to pre_enable
drm/bridge: tc358762: add SPI master support to control panels
drm/bridge: tc358762: read back ID register
drm/bridge: tc358762: make LPTXTIMECNT configurable
drm/bridge: tc358762: Basic support for 2 lanes
ARM: dts: ti/omap: omap4-embt2ws: Add displays
.../bindings/display/bridge/toshiba,tc358762.yaml | 14 ++
.../bindings/display/panel/epson,panel-bt200.yaml | 48 ++++
arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts | 120 ++++++++++
drivers/gpu/drm/bridge/tc358762.c | 234 ++++++++++++++++---
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-epson-bt200.c | 251 +++++++++++++++++++++
7 files changed, 651 insertions(+), 27 deletions(-)
---
base-commit: ae05b821e09ca387eab7441be8c82fd521f7a42c
change-id: 20260921-b2-panel-via-bridge-ba42974b612f
prerequisite-change-id: 20260326-tc358762-fixes-6f666500da9e:v4
prerequisite-patch-id: df4c4c2ab36f4504f08fd89804c91ecf1bbc5b85
prerequisite-patch-id: d270eb332c88f888f628b2cf751517500024b12c
prerequisite-patch-id: 6050aad2a160d9e85b176e8118f4ac4247de1355
prerequisite-patch-id: a7b3986c944f62156da6b368e6251b29451feaf2
prerequisite-patch-id: eb52b7ed89c1d44539bc7eef61f242dd444d3171
prerequisite-patch-id: e1a953467d847bea6bc9847134c0199e4702423b
prerequisite-patch-id: 538b6e710dcc07bd70eed2795d9e463e91bf0b48
prerequisite-patch-id: 6636bdd386becb3622944e57653e4fd1502527cc
prerequisite-patch-id: 84cf0fc416da9a016ebbd586020cb67b18aa84b7
prerequisite-patch-id: 64ff4472fe4467e9da0a11f69fe30d50db12204d
prerequisite-patch-id: 88939d8af6eef5fea65b7883909856e692c29cfa
prerequisite-patch-id: c6c3d34380425f4ee21411e860726786b56c8236
prerequisite-patch-id: ac1f5d7bc054cf7a0d84cb343f4b1d1e393a7956
Best regards,
--
Andreas Kemnade <andreas@kemnade.info>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 1/9] dt-bindings: display: panel: Add panel for Epson Moverio BT-200
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
@ 2026-09-22 6:16 ` Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200 Andreas Kemnade
` (7 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:16 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
The Epson Moverio BT200 has two DPI panels with SPI control
input in its glasses. The chip is unknown and not feasible to
non-destructively find out more, so just add a more generic
compatible.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
.../bindings/display/panel/epson,panel-bt200.yaml | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/epson,panel-bt200.yaml b/Documentation/devicetree/bindings/display/panel/epson,panel-bt200.yaml
new file mode 100644
index 000000000000..b3ea09d194aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/epson,panel-bt200.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/epson,panel-bt200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Panels of the Epson Moverio BT-200 head unit
+
+maintainers:
+ - Andreas Kemnade <andreas@kemnade.info>
+
+allOf:
+ - $ref: panel-common.yaml#
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: epson,panel-bt200
+
+ reg:
+ const: 0
+
+required:
+ - compatible
+ - port
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel: panel@0 {
+ compatible = "epson,panel-bt200";
+ reg = <0>;
+
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+ };
+ };
+
+...
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 1/9] dt-bindings: display: panel: Add panel for Epson Moverio BT-200 Andreas Kemnade
@ 2026-09-22 6:16 ` Andreas Kemnade
2026-09-22 9:13 ` Uwe Kleine-König
2026-09-22 6:17 ` [PATCH RFC 3/9] dt-bindings: display: bridge: tc358762: Add some missing properties Andreas Kemnade
` (6 subsequent siblings)
8 siblings, 1 reply; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:16 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
Not much is known about the panel, initialisation sequence
and parameters come from the vendor kernel. As it is located
in glasses, it is hard to disassemble to find out more.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
drivers/gpu/drm/panel/Kconfig | 10 ++
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-epson-bt200.c | 251 ++++++++++++++++++++++++++++++
3 files changed, 262 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index cbdf7b8f7f7a..0cb56687294d 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -159,6 +159,16 @@ config DRM_PANEL_ELIDA_KD35T133
KD35T133 controller for 320x480 LCD panels with MIPI-DSI
system interfaces.
+config DRM_PANEL_EPSON_BT200
+ tristate "Epson BT200 panel driver"
+ depends on OF && SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the 960x540
+ pixel panels used in the Epson Moverio BT200. They are used
+ together with the TC358762 DSI-to-DPI bridge. To compile this
+ driver as a module, choose M here.
+
config DRM_PANEL_FEIXIN_K101_IM2BA02
tristate "Feixin K101 IM2BA02 panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 3b523cf37833..921af234d335 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_EDP) += panel-edp.o
obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
+obj-$(CONFIG_DRM_PANEL_EPSON_BT200) += panel-epson-bt200.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
obj-$(CONFIG_DRM_PANEL_FOCALTECH_OTA7290B) += panel-focaltech-ota7290b.o
diff --git a/drivers/gpu/drm/panel/panel-epson-bt200.c b/drivers/gpu/drm/panel/panel-epson-bt200.c
new file mode 100644
index 000000000000..c26a4f8ae15e
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-epson-bt200.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/backlight.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct drm_display_mode default_mode = {
+ .clock = 41600, /* kHz */
+#if 0
+ /* values matching struct omap_video_timings in vendor kernel */
+ .hdisplay = 960,
+ .hsync_start = 960 + 176,
+ .hsync_end = 960 + 176 + 20,
+ .htotal = 960 + 176 + 20 + 86,
+#else
+ /* values from what is writted to LCDCTRL on vendor kernel */
+ .hdisplay = 960,
+ .hsync_start = 960 + 33,
+ .hsync_end = 960 + 33 + 20,
+ .htotal = 960 + 33 + 20 + 86,
+#endif
+ .vdisplay = 540,
+ .vsync_start = 540 + 9,
+ .vsync_end = 540 + 9 + 3,
+ .vtotal = 540 + 9 + 3 + 12,
+ .flags = 0,
+};
+
+struct bt200_panel {
+ struct drm_panel panel;
+ struct spi_device *spi;
+};
+
+static int bt200_panel_write(struct bt200_panel *ctx, u8 addr, u8 data)
+{
+ u8 val[2];
+
+ val[0] = addr;
+ val[1] = data;
+
+ return spi_write(ctx->spi, val, sizeof(val));
+}
+
+static inline struct bt200_panel *panel_to_bt200(struct drm_panel *panel)
+{
+ return container_of(panel, struct bt200_panel, panel);
+}
+
+static int bt200_disable(struct drm_panel *panel)
+{
+ struct bt200_panel *ctx = panel_to_bt200(panel);
+
+ bt200_panel_write(ctx, 0x0A, 0);
+ return 0;
+}
+
+static int bt200_unprepare(struct drm_panel *panel)
+{
+ return 0;
+}
+
+/* found this table in vendor kernel */
+static struct {
+ u8 addr;
+ u8 data;
+} lcddr_init[] = {
+ {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x05, 0x01},
+ {0x07, 0x00}, {0x0A, 0x00}, {0x10, 0x03}, {0x11, 0x44},
+ {0x12, 0x44}, {0x13, 0x55}, {0x14, 0x03}, {0x15, 0x00},
+ {0x16, 0x2A}, {0x17, 0x20}, {0x18, 0x00}, {0x19, 0x10},
+ {0x1A, 0x12}, {0x1B, 0x0E}, {0x1C, 0x0F}, {0x1D, 0x10},
+ {0x1E, 0x0F}, {0x1F, 0x1B}, {0x20, 0x0F}, {0x21, 0x00},
+ {0x22, 0x00}, {0x23, 0x00}, {0x24, 0x00}, {0x28, 0x14},
+ {0x29, 0x19}, {0x2A, 0x17}, {0x2B, 0x2B}, {0x2C, 0x99},
+ {0x2D, 0x13}, {0x2E, 0x2A}, {0x30, 0x0B}, {0x31, 0x00},
+ {0x32, 0x00}, {0x33, 0x01}, {0x34, 0x00}, {0x35, 0x0B},
+ {0x36, 0x04}, {0x37, 0x21}, {0x38, 0x00}, {0x39, 0x46},
+ {0x3A, 0x01}, {0x3B, 0x06}, {0x3C, 0x03}, {0x3D, 0x00},
+ {0x3E, 0x06}, {0x3F, 0x04}, {0x40, 0x00}, {0x41, 0x0D},
+ {0x42, 0x00}, {0x43, 0x2E}, {0x45, 0x08}, {0x46, 0x00},
+ {0x47, 0x01}, {0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x0B},
+ {0x4B, 0x38}, {0x4C, 0x03}, {0x4D, 0x04}, {0x50, 0x0F},
+ {0x51, 0x04}, {0x52, 0x01}, {0x53, 0x0E}, {0x54, 0x11},
+ {0x55, 0x9F}, {0x56, 0x36}, {0x57, 0x00}, {0x58, 0x68},
+ {0x59, 0x01}, {0x5A, 0xE0}, {0x5B, 0x00}, {0x5C, 0x00},
+ {0x5D, 0x10}, {0x5E, 0x36}, {0x5F, 0x36}, {0x60, 0x00},
+ {0x61, 0x04}, {0x62, 0x12}, {0x63, 0x00}, {0x64, 0x80},
+ {0x65, 0x00}, {0x66, 0x80}, {0x67, 0x1C}, {0x68, 0x00},
+ {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00}, {0x6C, 0x00},
+ {0x70, 0x00}, {0x71, 0x55}, {0x72, 0x44}, {0x73, 0x33},
+ {0x74, 0x22}, {0x75, 0x33}, {0x76, 0x22}, {0x77, 0x22},
+ {0x78, 0x33}, {0x79, 0x22}, {0x7A, 0x44}, {0x7B, 0x55},
+ {0x7C, 0x00}, {0x80, 0x00}, {0x81, 0x66}, {0x82, 0x22},
+ {0x83, 0x33}, {0x84, 0x22}, {0x85, 0x33}, {0x86, 0x22},
+ {0x87, 0x22}, {0x88, 0x33}, {0x89, 0x44}, {0x8A, 0x44},
+ {0x8B, 0x44}, {0x8C, 0x00}, {0x90, 0x00}, {0x91, 0x77},
+ {0x92, 0x66}, {0x93, 0x33}, {0x94, 0x11}, {0x95, 0x33},
+ {0x96, 0x22}, {0x97, 0x22}, {0x98, 0x44}, {0x99, 0x44},
+ {0x9A, 0x33}, {0x9B, 0x44}, {0x9C, 0x00}, {0xA0, 0x88},
+ {0xA1, 0x88}, {0xA2, 0x88}, {0xA3, 0x88}, {0xA4, 0x88},
+ {0xA5, 0x88}, {0xA6, 0x88}, {0xA7, 0x88}, {0xA8, 0x88},
+ {0xA9, 0x88}, {0xAA, 0x88}, {0xAB, 0x88}, {0xAC, 0x88},
+ {0xAD, 0x88}, {0xAE, 0x88}, {0xAF, 0x88}, {0xB0, 0x88},
+ {0xB1, 0x88}, {0xB2, 0x88}, {0xB3, 0x88}, {0xB4, 0x88},
+ {0xB5, 0x88}, {0xB6, 0x88}, {0xB7, 0x88}, {0xB8, 0x88},
+ {0xB9, 0x88}, {0xBA, 0x88}, {0xBB, 0xA6}, {0xBC, 0x88},
+ {0xBD, 0x88}, {0xBE, 0x88}, {0xBF, 0x88}, {0xC0, 0x88},
+ {0xC1, 0x88}, {0xC2, 0x88}, {0xC3, 0x88}, {0xC4, 0x88},
+ {0xC5, 0x88}, {0xC6, 0x88}, {0xC7, 0x88}, {0xC8, 0x88},
+ {0xC9, 0x88}, {0xD0, 0x36}, {0xD1, 0x26}, {0xD2, 0x21},
+ {0xD3, 0x1F}, {0xD4, 0x17}, {0xD5, 0x15}, {0xD6, 0x13},
+ {0xD7, 0x10}, {0xD8, 0x08}, {0xD9, 0x08}, {0xDA, 0x18},
+ {0xDB, 0x1D}, {0xDC, 0x1F}, {0xDD, 0x27}, {0xDE, 0x29},
+ {0xDF, 0x2B}, {0xE0, 0x2E}, {0xE1, 0x36}, {0xE6, 0x00},
+ {0xF1, 0x00}, {0xF2, 0x00}, {0xF3, 0x00}
+};
+
+static int init_lcd(struct bt200_panel *ctx)
+{
+ int i;
+ int r;
+
+ r = spi_setup(ctx->spi);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < ARRAY_SIZE(lcddr_init); ++i) {
+ r = bt200_panel_write(ctx, (u16)lcddr_init[i].addr,
+ lcddr_init[i].data);
+ if (r) {
+ dev_err(ctx->panel.dev, "failed to write initial config (write) %d\n", i);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int bt200_prepare(struct drm_panel *panel)
+{
+ int r;
+ struct bt200_panel *ctx = panel_to_bt200(panel);
+
+ r = init_lcd(ctx);
+ if (r)
+ return r;
+
+ msleep(50);
+ return 0;
+}
+
+static int bt200_enable(struct drm_panel *panel)
+{
+ struct bt200_panel *ctx = panel_to_bt200(panel);
+
+ return bt200_panel_write(ctx, 0x0A, 1);
+}
+
+static int bt200_get_modes(struct drm_panel *panel, struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs bt200_panel_funcs = {
+ .disable = bt200_disable,
+ .unprepare = bt200_unprepare,
+ .prepare = bt200_prepare,
+ .enable = bt200_enable,
+ .get_modes = bt200_get_modes,
+};
+
+static int bt200_probe(struct spi_device *spi)
+{
+ struct bt200_panel *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(&spi->dev, struct bt200_panel, panel,
+ &bt200_panel_funcs, DRM_MODE_CONNECTOR_DPI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ spi_set_drvdata(spi, ctx);
+ ctx->spi = spi;
+ ctx->panel.prepare_prev_first = true;
+
+ spi->bits_per_word = 8;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ drm_panel_add(&ctx->panel);
+ return 0;
+}
+
+static void bt200_remove(struct spi_device *dsi)
+{
+ struct bt200_panel *ctx = spi_get_drvdata(dsi);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id bt200_of_match[] = {
+ { .compatible = "epson,panel-bt200", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, bt200_of_match);
+
+static const struct spi_device_id bt200_ids[] = {
+ { "panel-bt200", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, bt200_ids);
+
+static struct spi_driver bt200_driver = {
+ .probe = bt200_probe,
+ .remove = bt200_remove,
+ .id_table = bt200_ids,
+ .driver = {
+ .name = "panel-bt200",
+ .of_match_table = bt200_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_spi_driver(bt200_driver);
+
+MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");
+MODULE_DESCRIPTION("bt200 panel driver");
+MODULE_LICENSE("GPL");
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 3/9] dt-bindings: display: bridge: tc358762: Add some missing properties
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 1/9] dt-bindings: display: panel: Add panel for Epson Moverio BT-200 Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200 Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 4/9] drm/bridge: tc358762: Move initialisation to pre_enable Andreas Kemnade
` (5 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
The TC358762 can act as an SPI master with two CS for controlling DPI
slave devices. Add a spi subnode for that.
Add a property to describe number of DSI data lanes. If two lanes
are used, an external clock is required.
There is the LPTXTIMECNT register which needs to be set to a non-standard
value on Epson Moverio BT-200, add a property for it. I know nothing about
it besides the required value.
---
.../bindings/display/bridge/toshiba,tc358762.yaml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358762.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358762.yaml
index 6c1de0b21722..0e380f2a56e6 100644
--- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358762.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358762.yaml
@@ -44,6 +44,20 @@ properties:
required:
- port@1
+ spi:
+ $ref: /schemas/spi/spi-controller.yaml#
+ unevaluatedProperties: false
+
+ dsi-lanes:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2]
+
+ toshiba,lptxtimecnt:
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ clocks:
+ maxItems: 1
+
required:
- compatible
- reg
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 4/9] drm/bridge: tc358762: Move initialisation to pre_enable
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (2 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 3/9] dt-bindings: display: bridge: tc358762: Add some missing properties Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 5/9] drm/bridge: tc358762: add SPI master support to control panels Andreas Kemnade
` (4 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
To allow panels to use SPI already in the prepare function, have things
already setup here.
Maybe setting LCDCTRL in enable might be an idea?
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
drivers/gpu/drm/bridge/tc358762.c | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index e73be376e33c..ec72e6e92b46 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -167,6 +167,12 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct tc358762 *ctx = bridge_to_tc358762(bridge);
+ struct drm_connector_state *conn_state;
+ struct drm_bridge_state *bridge_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ u32 lcdctrl;
int ret;
ret = regulator_enable(ctx->regulator);
@@ -178,21 +184,6 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
usleep_range(5000, 10000);
}
- ctx->pre_enabled = true;
-}
-
-static void tc358762_enable(struct drm_bridge *bridge,
- struct drm_atomic_commit *state)
-{
- struct tc358762 *ctx = bridge_to_tc358762(bridge);
- struct drm_connector_state *conn_state;
- struct drm_bridge_state *bridge_state;
- struct drm_crtc_state *crtc_state;
- struct drm_connector *connector;
- struct drm_display_mode *mode;
- u32 lcdctrl;
- int ret;
-
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
@@ -269,6 +260,13 @@ static void tc358762_enable(struct drm_bridge *bridge,
ret = tc358762_clear_error(ctx);
if (ret < 0)
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
+
+ ctx->pre_enabled = true;
+}
+
+static void tc358762_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *state)
+{
}
static int tc358762_attach(struct drm_bridge *bridge,
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 5/9] drm/bridge: tc358762: add SPI master support to control panels
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (3 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 4/9] drm/bridge: tc358762: Move initialisation to pre_enable Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 6/9] drm/bridge: tc358762: read back ID register Andreas Kemnade
` (3 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
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@kemnade.info>
---
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
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 6/9] drm/bridge: tc358762: read back ID register
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (4 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 5/9] drm/bridge: tc358762: add SPI master support to control panels Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 7/9] drm/bridge: tc358762: make LPTXTIMECNT configurable Andreas Kemnade
` (2 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
to check if reading works and in preparation for the PLL setup, read back
the ID register value. Also this produces better errors if chip is not
connected. In the Epson Moverio BT200, the DSI busses are on a (probably
not hot-) detachable cable.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
drivers/gpu/drm/bridge/tc358762.c | 40 +++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index 8024e49e2868..1bea89fb03a8 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -50,6 +50,8 @@
#define DSI_LANEENABLE_L0EN BIT(1)
#define DSI_LANEENABLE_L1EN BIT(2)
+#define RDPKTLN 0x0410 /* Packet length */
+
/* LCDC/DPI Registers */
#define LCDCTRL 0x0420 /* Video Path Control */
#define LCDCTRL_MSF BIT(0) /* Magic square in RGB666 */
@@ -97,6 +99,8 @@
#define SYSCTRL_PCLKDIV_DIV_2 2
#define SYSCTRL_PCLKDIV_DIV_3 4
+#define IDREG 0x04A0 /* Chip and Revision ID */
+
#define LPX_PERIOD 3
struct tc358762 {
@@ -119,6 +123,27 @@ static int tc358762_clear_error(struct tc358762 *ctx)
return ret;
}
+static int tc358762_read(struct tc358762 *ctx, u16 addr, u32 *val)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ ssize_t ret;
+ u8 addr_buf[2];
+ u8 buf[4];
+
+ put_unaligned_le16(addr, addr_buf);
+
+ ret = mipi_dsi_generic_read(dsi, addr_buf, 2, buf, sizeof(buf));
+ if (ret < 0) {
+ dev_err(ctx->dev, "gen read failed: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(ctx->dev, "read return %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3]);
+
+ *val = get_unaligned_le32(buf);
+ return ret;
+}
+
static void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
@@ -182,6 +207,7 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
struct drm_display_mode *mode;
u32 lcdctrl;
int ret;
+ u32 id;
ret = regulator_enable(ctx->regulator);
if (ret < 0)
@@ -205,6 +231,8 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
*/
tc358762_write(ctx, LCDCTRL, 0);
+ tc358762_write(ctx, RDPKTLN, 3);
+
tc358762_write(ctx, SYSCTRL,
FIELD_PREP(SYSCTRL_DPIDATA_IO_MASK, SYSCTRL_DPIDATA_IO_4MA) |
FIELD_PREP(SYSCTRL_DPISTB_IO_MASK, SYSCTRL_DPISTB_IO_4MA) |
@@ -283,6 +311,18 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
if (ret < 0)
dev_err(ctx->dev, "error initializing bridge (%d)\n", ret);
+ /*
+ * This generates an ack with error if the device is reinitialized
+ * without reset, ignore the error here, the second read should be
+ * successful.
+ */
+ tc358762_read(ctx, IDREG, &id);
+ ret = tc358762_read(ctx, IDREG, &id);
+ if (ret < 0)
+ dev_err(ctx->dev, "read id failed (%d)\n", ret);
+ else if (id != 0x6200)
+ dev_info(ctx->dev, "unknown chip with id %x\n", id);
+
ctx->pre_enabled = true;
}
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 7/9] drm/bridge: tc358762: make LPTXTIMECNT configurable
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (5 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 6/9] drm/bridge: tc358762: read back ID register Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 8/9] drm/bridge: tc358762: Basic support for 2 lanes Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 9/9] ARM: dts: ti/omap: omap4-embt2ws: Add displays Andreas Kemnade
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
On the Epson Moverio BT-200, reading back registers does not work with the
default value, so make it configurable. Real meaning of this value is not
known from public sources.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
drivers/gpu/drm/bridge/tc358762.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index 1bea89fb03a8..194785bff997 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -15,6 +15,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_graph.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -101,8 +102,6 @@
#define IDREG 0x04A0 /* Chip and Revision ID */
-#define LPX_PERIOD 3
-
struct tc358762 {
struct device *dev;
struct drm_bridge bridge;
@@ -112,6 +111,7 @@ struct tc358762 {
struct spi_controller *spi;
bool pre_enabled;
int error;
+ u32 lpx_period;
bool use_vtg;
};
@@ -246,7 +246,7 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
tc358762_write(ctx, PPI_D0S_ATMR, 0);
tc358762_write(ctx, PPI_D1S_ATMR, 0);
- tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD);
+ tc358762_write(ctx, PPI_LPTXTIMECNT, ctx->lpx_period);
if (ctx->use_vtg) {
struct videomode vm = { 0 };
@@ -367,6 +367,9 @@ static int tc358762_parse_dt(struct tc358762 *ctx)
if (IS_ERR(ctx->reset_gpio))
return PTR_ERR(ctx->reset_gpio);
+ ctx->lpx_period = 3;
+ device_property_read_u32(dev, "toshiba,lptxtimecnt", &ctx->lpx_period);
+
return 0;
}
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 8/9] drm/bridge: tc358762: Basic support for 2 lanes
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (6 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 7/9] drm/bridge: tc358762: make LPTXTIMECNT configurable Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 9/9] ARM: dts: ti/omap: omap4-embt2ws: Add displays Andreas Kemnade
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
Add support for 2 data lanes. Enabling the PLL is required. This are just
the values working for the Epson Moverio BT-200. With the standard mode
flags, the panel mode was deemed unusable, adjust them to have more freedom
in case of 2 data lanes operation.
Nice looking values for the input clock for the PLL were not found.
Setting the required register needs special sequences, entering ULPS in
between is the way to do it in the vendor kernel. It seems that some
stop mode transitions are also enough. This all seems not to work
if LPM is used for writing the register, to temporarily disable that.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
drivers/gpu/drm/bridge/tc358762.c | 87 +++++++++++++++++++++++++++++++++++----
1 file changed, 78 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index 194785bff997..632597fec20d 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -100,6 +100,8 @@
#define SYSCTRL_PCLKDIV_DIV_2 2
#define SYSCTRL_PCLKDIV_DIV_3 4
+#define SYSPLL3 0x0470 /* */
+
#define IDREG 0x04A0 /* Chip and Revision ID */
struct tc358762 {
@@ -183,6 +185,7 @@ static void tc358762_post_disable(struct drm_bridge *bridge,
if (!ctx->pre_enabled)
return;
+ to_mipi_dsi_device(ctx->dev)->mode_flags |= MIPI_DSI_MODE_LPM;
ctx->pre_enabled = false;
/* Turn off the DPI output */
@@ -208,7 +211,9 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
u32 lcdctrl;
int ret;
u32 id;
+ u32 sysctrl;
+ to_mipi_dsi_device(ctx->dev)->mode_flags |= MIPI_DSI_MODE_LPM;
ret = regulator_enable(ctx->regulator);
if (ret < 0)
dev_err(ctx->dev, "error enabling regulators (%d)\n", ret);
@@ -233,15 +238,26 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
tc358762_write(ctx, RDPKTLN, 3);
- tc358762_write(ctx, SYSCTRL,
- FIELD_PREP(SYSCTRL_DPIDATA_IO_MASK, SYSCTRL_DPIDATA_IO_4MA) |
- FIELD_PREP(SYSCTRL_DPISTB_IO_MASK, SYSCTRL_DPISTB_IO_4MA) |
- FIELD_PREP(SYSCTRL_PCLKDIV_MASK, SYSCTRL_PCLKDIV_DIV_3));
+ sysctrl = FIELD_PREP(SYSCTRL_DPIDATA_IO_MASK, SYSCTRL_DPIDATA_IO_4MA) |
+ FIELD_PREP(SYSCTRL_DPISTB_IO_MASK, SYSCTRL_DPISTB_IO_4MA);
+
+ if (to_mipi_dsi_device(ctx->dev)->lanes == 2)
+ sysctrl |= FIELD_PREP(SYSCTRL_PCLKDIV_MASK, SYSCTRL_PCLKDIV_DIV_2);
+ else
+ sysctrl |= FIELD_PREP(SYSCTRL_PCLKDIV_MASK, SYSCTRL_PCLKDIV_DIV_3);
+
+ tc358762_write(ctx, SYSCTRL, sysctrl);
msleep(100);
- tc358762_write(ctx, DSI_LANEENABLE,
- DSI_LANEENABLE_L0EN | DSI_LANEENABLE_CLEN);
+ if (to_mipi_dsi_device(ctx->dev)->lanes == 2) {
+ tc358762_write(ctx, DSI_LANEENABLE,
+ DSI_LANEENABLE_L0EN | DSI_LANEENABLE_CLEN |
+ DSI_LANEENABLE_L1EN);
+ } else {
+ tc358762_write(ctx, DSI_LANEENABLE,
+ DSI_LANEENABLE_L0EN | DSI_LANEENABLE_CLEN);
+ }
tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5);
tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5);
tc358762_write(ctx, PPI_D0S_ATMR, 0);
@@ -329,6 +345,43 @@ static void tc358762_pre_enable(struct drm_bridge *bridge,
static void tc358762_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
+ struct tc358762 *ctx = bridge_to_tc358762(bridge);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+ u32 syspll;
+
+ if (dsi->lanes == 2) {
+ tc358762_read(ctx, SYSPLL3, &syspll);
+ dev_dbg(ctx->dev, "syspll reg: %x\n", syspll);
+ /*
+ * as handling of these registers is bound to certain
+ * conditions, avoid write if already set
+ */
+ if (syspll != 0xB8640000) {
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+ /*
+ * SYSPLL3 seems to get set to 0x47000000 without it.
+ * Also trigger some stop mode transitions
+ * TODO: find some sane value for clock input and add proper dividers
+ */
+ tc358762_write(ctx, SYSPLL3, 0xB8640000);
+ ret = tc358762_clear_error(ctx);
+ if (ret) {
+ dev_err(ctx->dev, "error writing to SYSPLL3: %d\n", ret);
+ return;
+ }
+
+ msleep(20);
+ /*
+ * control read to see if this is wonky, probably also includes
+ * stop mode transitions
+ */
+ ret = tc358762_read(ctx, SYSPLL3, &syspll);
+
+ if ((ret) || (syspll != 0xB8640000))
+ dev_err(ctx->dev, "failed to configure PLL %d\n", ret);
+ }
+ }
}
static int tc358762_attach(struct drm_bridge *bridge,
@@ -355,6 +408,7 @@ static int tc358762_parse_dt(struct tc358762 *ctx)
{
struct drm_bridge *panel_bridge;
struct device *dev = ctx->dev;
+ u32 lanes;
panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
if (IS_ERR(panel_bridge))
@@ -367,6 +421,13 @@ static int tc358762_parse_dt(struct tc358762 *ctx)
if (IS_ERR(ctx->reset_gpio))
return PTR_ERR(ctx->reset_gpio);
+ if (!device_property_read_u32(dev, "dsi-lanes", &lanes)) {
+ if (lanes > 2)
+ return -EINVAL;
+
+ to_mipi_dsi_device(ctx->dev)->lanes = lanes;
+ }
+
ctx->lpx_period = 3;
device_property_read_u32(dev, "toshiba,lptxtimecnt", &ctx->lpx_period);
@@ -459,13 +520,21 @@ static int tc358762_probe(struct mipi_dsi_device *dsi)
*/
dsi->lanes = 1;
dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE;
-
ret = tc358762_parse_dt(ctx);
if (ret < 0)
return ret;
+ if (dsi->lanes == 2) {
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_LPM;
+
+ dsi->hs_rate = 800000000;
+ dsi->lp_rate = 9200000;
+ } else
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE;
+
ret = tc358762_configure_regulators(ctx);
if (ret < 0)
return ret;
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH RFC 9/9] ARM: dts: ti/omap: omap4-embt2ws: Add displays
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
` (7 preceding siblings ...)
2026-09-22 6:17 ` [PATCH RFC 8/9] drm/bridge: tc358762: Basic support for 2 lanes Andreas Kemnade
@ 2026-09-22 6:17 ` Andreas Kemnade
8 siblings, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 6:17 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns
Cc: dri-devel, devicetree, linux-kernel, linux-omap, Andreas Kemnade
Add the displays. They are behind a DSI to DPI bridge. Specifying the
required clock needs some more thought to find a sane rate, so leave it
out for now. Also do not enable the shared reset line yet, to have some
more recovery chances in case of possible display initialisation problems.
There were situations in development where displays suddenly work in a
sane way after several resets.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
---
arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts | 120 ++++++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts b/arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts
index e11d1931c42a..856b92fb1866 100644
--- a/arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts
+++ b/arch/arm/boot/dts/ti/omap/omap4-epson-embt2ws.dts
@@ -120,6 +120,12 @@ lb_v50: regulator-lb-v50 {
enable-active-high;
};
+ unknown_reg: regulator-unknown {
+ compatible = "regulator-fixed";
+ regulator-name = "unknown";
+ regulator-always-on;
+ };
+
wl12xx_pwrseq: wl12xx-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = <&twl 1>;
@@ -140,6 +146,120 @@ wl12xx_vmmc: wl12xx-vmmc {
};
};
+&dss {
+ status = "okay";
+};
+
+&dsi1 {
+ status = "okay";
+
+ bridge@0 {
+ compatible = "toshiba,tc358762";
+ dsi-lanes = <2>;
+ reg = <0>;
+ vddc-supply = <&unknown_reg>;
+ /* reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; */
+ toshiba,lptxtimecnt = <7>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ bridge_in_right: endpoint {
+ remote-endpoint = <&dsi_out_right>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ bridge_out_right: endpoint {
+ remote-endpoint = <&panel_in_right>;
+ };
+ };
+ };
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "epson,panel-bt200";
+ reg = <0>;
+
+ port {
+ panel_in_right: endpoint {
+ remote-endpoint = <&bridge_out_right>;
+ };
+ };
+ };
+ };
+ };
+
+ port {
+ dsi_out_right: endpoint {
+ remote-endpoint = <&bridge_in_right>;
+ lanes = <2 3 4 5 0 1>;
+ };
+ };
+};
+
+&dsi2 {
+ status = "okay";
+
+ bridge@0 {
+ compatible = "toshiba,tc358762";
+ reg = <0>;
+ dsi-lanes = <2>;
+ toshiba,lptxtimecnt = <7>;
+ vddc-supply = <&unknown_reg>;
+ /* reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ bridge_in_left: endpoint {
+ remote-endpoint = <&dsi_out_left>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ bridge_out_left: endpoint {
+ remote-endpoint = <&panel_in_left>;
+ };
+ };
+ };
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ display@0 {
+ compatible = "epson,panel-bt200";
+ reg = <0>;
+
+ port {
+ panel_in_left: endpoint {
+ remote-endpoint = <&bridge_out_left>;
+ };
+ };
+ };
+ };
+ };
+
+ port {
+ dsi_out_left: endpoint {
+ remote-endpoint = <&bridge_in_left>;
+ lanes = <2 3 0 1 4 5>;
+ };
+ };
+};
+
&gpio1 {
pinctrl-names = "default";
pinctrl-0 = <&gpio1_hog_pins &gpio1wk_hog_pins>;
--
2.47.3
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200
2026-09-22 6:16 ` [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200 Andreas Kemnade
@ 2026-09-22 9:13 ` Uwe Kleine-König
2026-09-22 10:18 ` Andreas Kemnade
2026-09-22 15:08 ` Andreas Kemnade
0 siblings, 2 replies; 13+ messages in thread
From: Uwe Kleine-König @ 2026-09-22 9:13 UTC (permalink / raw)
To: Andreas Kemnade
Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns, dri-devel, devicetree, linux-kernel,
linux-omap
[-- Attachment #1: Type: text/plain, Size: 9108 bytes --]
Hallo Andreas,
On Tue, Sep 22, 2026 at 08:16:59AM +0200, Andreas Kemnade wrote:
> diff --git a/drivers/gpu/drm/panel/panel-epson-bt200.c b/drivers/gpu/drm/panel/panel-epson-bt200.c
> new file mode 100644
> index 000000000000..c26a4f8ae15e
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-epson-bt200.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +#include <linux/backlight.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
Please don't include <linux/mod_devicetable.h>. You can rely on
<linux/spi/spi.h> to provide of_device_id and spi_device_id; or if you
prefer full iwyu include <linux/device-id/of.h> and <.../spi.h>.
> +#include <linux/spi/spi.h>
> +
> +#include <drm/drm_connector.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +static const struct drm_display_mode default_mode = {
> + .clock = 41600, /* kHz */
> +#if 0
huh, upstream code isn't supposed to have #if 0 blocks.
> + /* values matching struct omap_video_timings in vendor kernel */
> + .hdisplay = 960,
> + .hsync_start = 960 + 176,
> + .hsync_end = 960 + 176 + 20,
> + .htotal = 960 + 176 + 20 + 86,
> +#else
> + /* values from what is writted to LCDCTRL on vendor kernel */
> + .hdisplay = 960,
> + .hsync_start = 960 + 33,
> + .hsync_end = 960 + 33 + 20,
> + .htotal = 960 + 33 + 20 + 86,
> +#endif
> + .vdisplay = 540,
> + .vsync_start = 540 + 9,
> + .vsync_end = 540 + 9 + 3,
> + .vtotal = 540 + 9 + 3 + 12,
> + .flags = 0,
> +};
> +
> +struct bt200_panel {
> + struct drm_panel panel;
> + struct spi_device *spi;
> +};
> +
> +static int bt200_panel_write(struct bt200_panel *ctx, u8 addr, u8 data)
> +{
> + u8 val[2];
> +
> + val[0] = addr;
> + val[1] = data;
> +
> + return spi_write(ctx->spi, val, sizeof(val));
> +}
> +
> +static inline struct bt200_panel *panel_to_bt200(struct drm_panel *panel)
> +{
> + return container_of(panel, struct bt200_panel, panel);
> +}
> +
> +static int bt200_disable(struct drm_panel *panel)
> +{
> + struct bt200_panel *ctx = panel_to_bt200(panel);
> +
> + bt200_panel_write(ctx, 0x0A, 0);
> + return 0;
> +}
> +
> +static int bt200_unprepare(struct drm_panel *panel)
> +{
> + return 0;
> +}
> +
> +/* found this table in vendor kernel */
> +static struct {
> + u8 addr;
> + u8 data;
> +} lcddr_init[] = {
> + {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x05, 0x01},
> + {0x07, 0x00}, {0x0A, 0x00}, {0x10, 0x03}, {0x11, 0x44},
> + {0x12, 0x44}, {0x13, 0x55}, {0x14, 0x03}, {0x15, 0x00},
> + {0x16, 0x2A}, {0x17, 0x20}, {0x18, 0x00}, {0x19, 0x10},
> + {0x1A, 0x12}, {0x1B, 0x0E}, {0x1C, 0x0F}, {0x1D, 0x10},
> + {0x1E, 0x0F}, {0x1F, 0x1B}, {0x20, 0x0F}, {0x21, 0x00},
> + {0x22, 0x00}, {0x23, 0x00}, {0x24, 0x00}, {0x28, 0x14},
> + {0x29, 0x19}, {0x2A, 0x17}, {0x2B, 0x2B}, {0x2C, 0x99},
> + {0x2D, 0x13}, {0x2E, 0x2A}, {0x30, 0x0B}, {0x31, 0x00},
> + {0x32, 0x00}, {0x33, 0x01}, {0x34, 0x00}, {0x35, 0x0B},
> + {0x36, 0x04}, {0x37, 0x21}, {0x38, 0x00}, {0x39, 0x46},
> + {0x3A, 0x01}, {0x3B, 0x06}, {0x3C, 0x03}, {0x3D, 0x00},
> + {0x3E, 0x06}, {0x3F, 0x04}, {0x40, 0x00}, {0x41, 0x0D},
> + {0x42, 0x00}, {0x43, 0x2E}, {0x45, 0x08}, {0x46, 0x00},
> + {0x47, 0x01}, {0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x0B},
> + {0x4B, 0x38}, {0x4C, 0x03}, {0x4D, 0x04}, {0x50, 0x0F},
> + {0x51, 0x04}, {0x52, 0x01}, {0x53, 0x0E}, {0x54, 0x11},
> + {0x55, 0x9F}, {0x56, 0x36}, {0x57, 0x00}, {0x58, 0x68},
> + {0x59, 0x01}, {0x5A, 0xE0}, {0x5B, 0x00}, {0x5C, 0x00},
> + {0x5D, 0x10}, {0x5E, 0x36}, {0x5F, 0x36}, {0x60, 0x00},
> + {0x61, 0x04}, {0x62, 0x12}, {0x63, 0x00}, {0x64, 0x80},
> + {0x65, 0x00}, {0x66, 0x80}, {0x67, 0x1C}, {0x68, 0x00},
> + {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00}, {0x6C, 0x00},
> + {0x70, 0x00}, {0x71, 0x55}, {0x72, 0x44}, {0x73, 0x33},
> + {0x74, 0x22}, {0x75, 0x33}, {0x76, 0x22}, {0x77, 0x22},
> + {0x78, 0x33}, {0x79, 0x22}, {0x7A, 0x44}, {0x7B, 0x55},
> + {0x7C, 0x00}, {0x80, 0x00}, {0x81, 0x66}, {0x82, 0x22},
> + {0x83, 0x33}, {0x84, 0x22}, {0x85, 0x33}, {0x86, 0x22},
> + {0x87, 0x22}, {0x88, 0x33}, {0x89, 0x44}, {0x8A, 0x44},
> + {0x8B, 0x44}, {0x8C, 0x00}, {0x90, 0x00}, {0x91, 0x77},
> + {0x92, 0x66}, {0x93, 0x33}, {0x94, 0x11}, {0x95, 0x33},
> + {0x96, 0x22}, {0x97, 0x22}, {0x98, 0x44}, {0x99, 0x44},
> + {0x9A, 0x33}, {0x9B, 0x44}, {0x9C, 0x00}, {0xA0, 0x88},
> + {0xA1, 0x88}, {0xA2, 0x88}, {0xA3, 0x88}, {0xA4, 0x88},
> + {0xA5, 0x88}, {0xA6, 0x88}, {0xA7, 0x88}, {0xA8, 0x88},
> + {0xA9, 0x88}, {0xAA, 0x88}, {0xAB, 0x88}, {0xAC, 0x88},
> + {0xAD, 0x88}, {0xAE, 0x88}, {0xAF, 0x88}, {0xB0, 0x88},
> + {0xB1, 0x88}, {0xB2, 0x88}, {0xB3, 0x88}, {0xB4, 0x88},
> + {0xB5, 0x88}, {0xB6, 0x88}, {0xB7, 0x88}, {0xB8, 0x88},
> + {0xB9, 0x88}, {0xBA, 0x88}, {0xBB, 0xA6}, {0xBC, 0x88},
> + {0xBD, 0x88}, {0xBE, 0x88}, {0xBF, 0x88}, {0xC0, 0x88},
> + {0xC1, 0x88}, {0xC2, 0x88}, {0xC3, 0x88}, {0xC4, 0x88},
> + {0xC5, 0x88}, {0xC6, 0x88}, {0xC7, 0x88}, {0xC8, 0x88},
> + {0xC9, 0x88}, {0xD0, 0x36}, {0xD1, 0x26}, {0xD2, 0x21},
> + {0xD3, 0x1F}, {0xD4, 0x17}, {0xD5, 0x15}, {0xD6, 0x13},
> + {0xD7, 0x10}, {0xD8, 0x08}, {0xD9, 0x08}, {0xDA, 0x18},
> + {0xDB, 0x1D}, {0xDC, 0x1F}, {0xDD, 0x27}, {0xDE, 0x29},
> + {0xDF, 0x2B}, {0xE0, 0x2E}, {0xE1, 0x36}, {0xE6, 0x00},
> + {0xF1, 0x00}, {0xF2, 0x00}, {0xF3, 0x00}
Smells non-GPLly. Maybe ask the provider of the vendor kernel for "the
preferred form of the work for making modifications to it".
I guess {0x0A, 0x00} disables the display, so taking over an enabled
display setup by the bootloader doesn't work as is.
> +};
> +
> +static int init_lcd(struct bt200_panel *ctx)
> +{
> + int i;
> + int r;
> +
> + r = spi_setup(ctx->spi);
> + if (r < 0)
> + return r;
> +
> + for (i = 0; i < ARRAY_SIZE(lcddr_init); ++i) {
> + r = bt200_panel_write(ctx, (u16)lcddr_init[i].addr,
> + lcddr_init[i].data);
> + if (r) {
> + dev_err(ctx->panel.dev, "failed to write initial config (write) %d\n", i);
I claim that `i` isn't very interesting here, but maybe mention
lcddr_init[i].addr and/or `r` (using %pe).
> + return r;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int bt200_prepare(struct drm_panel *panel)
> +{
> + int r;
> + struct bt200_panel *ctx = panel_to_bt200(panel);
> +
> + r = init_lcd(ctx);
> + if (r)
> + return r;
> +
> + msleep(50);
> + return 0;
> +}
> +
> +static int bt200_enable(struct drm_panel *panel)
> +{
> + struct bt200_panel *ctx = panel_to_bt200(panel);
> +
> + return bt200_panel_write(ctx, 0x0A, 1);
> +}
> +
> +static int bt200_get_modes(struct drm_panel *panel, struct drm_connector *connector)
> +{
> + struct drm_display_mode *mode;
> +
> + mode = drm_mode_duplicate(connector->dev, &default_mode);
> + if (!mode)
> + return -ENOMEM;
> +
> + drm_mode_set_name(mode);
> + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> + drm_mode_probed_add(connector, mode);
> +
> + return 1;
> +}
> +
> +static const struct drm_panel_funcs bt200_panel_funcs = {
> + .disable = bt200_disable,
> + .unprepare = bt200_unprepare,
> + .prepare = bt200_prepare,
> + .enable = bt200_enable,
> + .get_modes = bt200_get_modes,
> +};
> +
> +static int bt200_probe(struct spi_device *spi)
> +{
> + struct bt200_panel *ctx;
> + int ret;
> +
> + ctx = devm_drm_panel_alloc(&spi->dev, struct bt200_panel, panel,
> + &bt200_panel_funcs, DRM_MODE_CONNECTOR_DPI);
> + if (IS_ERR(ctx))
> + return PTR_ERR(ctx);
> +
> + spi_set_drvdata(spi, ctx);
> + ctx->spi = spi;
> + ctx->panel.prepare_prev_first = true;
> +
> + spi->bits_per_word = 8;
> +
> + ret = drm_panel_of_backlight(&ctx->panel);
> + if (ret)
Error message here? (Use dev_err_probe().)
> + return ret;
> +
> + drm_panel_add(&ctx->panel);
> + return 0;
> +}
> +
> +static void bt200_remove(struct spi_device *dsi)
> +{
> + struct bt200_panel *ctx = spi_get_drvdata(dsi);
> +
> + drm_panel_remove(&ctx->panel);
> +}
> +
> +static const struct of_device_id bt200_of_match[] = {
> + { .compatible = "epson,panel-bt200", },
> + {},
Please use { } here (space between curlys and no trailing comma).
> +};
> +
> +MODULE_DEVICE_TABLE(of, bt200_of_match);
> +
> +static const struct spi_device_id bt200_ids[] = {
> + { "panel-bt200", 0 },
Drop the unused 0.
> + { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(spi, bt200_ids);
> +
> +static struct spi_driver bt200_driver = {
> + .probe = bt200_probe,
> + .remove = bt200_remove,
> + .id_table = bt200_ids,
> + .driver = {
> + .name = "panel-bt200",
> + .of_match_table = bt200_of_match,
> + .suppress_bind_attrs = true,
Why suppress bind attrs?
> + },
> +};
> +
> +module_spi_driver(bt200_driver);
> +
> +MODULE_AUTHOR("Andreas Kemnade <andreas@kemnade.info>");
> +MODULE_DESCRIPTION("bt200 panel driver");
> +MODULE_LICENSE("GPL");
>
> --
> 2.47.3
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200
2026-09-22 9:13 ` Uwe Kleine-König
@ 2026-09-22 10:18 ` Andreas Kemnade
2026-09-22 15:08 ` Andreas Kemnade
1 sibling, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 10:18 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns, dri-devel, devicetree, linux-kernel,
linux-omap
On Tue, 22 Sep 2026 11:13:36 +0200
Uwe Kleine-König <u.kleine-koenig@baylibre.com> wrote:
> Hallo Andreas,
>
> On Tue, Sep 22, 2026 at 08:16:59AM +0200, Andreas Kemnade wrote:
> > diff --git a/drivers/gpu/drm/panel/panel-epson-bt200.c b/drivers/gpu/drm/panel/panel-epson-bt200.c
> > new file mode 100644
> > index 000000000000..c26a4f8ae15e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-epson-bt200.c
> > @@ -0,0 +1,251 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +#include <linux/backlight.h>
> > +#include <linux/errno.h>
> > +#include <linux/delay.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
>
> Please don't include <linux/mod_devicetable.h>. You can rely on
> <linux/spi/spi.h> to provide of_device_id and spi_device_id; or if you
> prefer full iwyu include <linux/device-id/of.h> and <.../spi.h>.
>
> > +#include <linux/spi/spi.h>
> > +
> > +#include <drm/drm_connector.h>
> > +#include <drm/drm_modes.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +
> > +static const struct drm_display_mode default_mode = {
> > + .clock = 41600, /* kHz */
> > +#if 0
>
> huh, upstream code isn't supposed to have #if 0 blocks.
>
correct, this will go away latest in the first non-rfc series.
> > + /* values matching struct omap_video_timings in vendor kernel */
> > + .hdisplay = 960,
> > + .hsync_start = 960 + 176,
> > + .hsync_end = 960 + 176 + 20,
> > + .htotal = 960 + 176 + 20 + 86,
> > +#else
> > + /* values from what is writted to LCDCTRL on vendor kernel */
> > + .hdisplay = 960,
> > + .hsync_start = 960 + 33,
> > + .hsync_end = 960 + 33 + 20,
> > + .htotal = 960 + 33 + 20 + 86,
> > +#endif
> > + .vdisplay = 540,
> > + .vsync_start = 540 + 9,
> > + .vsync_end = 540 + 9 + 3,
> > + .vtotal = 540 + 9 + 3 + 12,
> > + .flags = 0,
> > +};
> > +
> > +struct bt200_panel {
> > + struct drm_panel panel;
> > + struct spi_device *spi;
> > +};
> > +
> > +static int bt200_panel_write(struct bt200_panel *ctx, u8 addr, u8 data)
> > +{
> > + u8 val[2];
> > +
> > + val[0] = addr;
> > + val[1] = data;
> > +
> > + return spi_write(ctx->spi, val, sizeof(val));
> > +}
> > +
> > +static inline struct bt200_panel *panel_to_bt200(struct drm_panel *panel)
> > +{
> > + return container_of(panel, struct bt200_panel, panel);
> > +}
> > +
> > +static int bt200_disable(struct drm_panel *panel)
> > +{
> > + struct bt200_panel *ctx = panel_to_bt200(panel);
> > +
> > + bt200_panel_write(ctx, 0x0A, 0);
> > + return 0;
> > +}
> > +
> > +static int bt200_unprepare(struct drm_panel *panel)
> > +{
> > + return 0;
> > +}
> > +
> > +/* found this table in vendor kernel */
> > +static struct {
> > + u8 addr;
> > + u8 data;
> > +} lcddr_init[] = {
> > + {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x05, 0x01},
> > + {0x07, 0x00}, {0x0A, 0x00}, {0x10, 0x03}, {0x11, 0x44},
> > + {0x12, 0x44}, {0x13, 0x55}, {0x14, 0x03}, {0x15, 0x00},
> > + {0x16, 0x2A}, {0x17, 0x20}, {0x18, 0x00}, {0x19, 0x10},
> > + {0x1A, 0x12}, {0x1B, 0x0E}, {0x1C, 0x0F}, {0x1D, 0x10},
> > + {0x1E, 0x0F}, {0x1F, 0x1B}, {0x20, 0x0F}, {0x21, 0x00},
> > + {0x22, 0x00}, {0x23, 0x00}, {0x24, 0x00}, {0x28, 0x14},
> > + {0x29, 0x19}, {0x2A, 0x17}, {0x2B, 0x2B}, {0x2C, 0x99},
> > + {0x2D, 0x13}, {0x2E, 0x2A}, {0x30, 0x0B}, {0x31, 0x00},
> > + {0x32, 0x00}, {0x33, 0x01}, {0x34, 0x00}, {0x35, 0x0B},
> > + {0x36, 0x04}, {0x37, 0x21}, {0x38, 0x00}, {0x39, 0x46},
> > + {0x3A, 0x01}, {0x3B, 0x06}, {0x3C, 0x03}, {0x3D, 0x00},
> > + {0x3E, 0x06}, {0x3F, 0x04}, {0x40, 0x00}, {0x41, 0x0D},
> > + {0x42, 0x00}, {0x43, 0x2E}, {0x45, 0x08}, {0x46, 0x00},
> > + {0x47, 0x01}, {0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x0B},
> > + {0x4B, 0x38}, {0x4C, 0x03}, {0x4D, 0x04}, {0x50, 0x0F},
> > + {0x51, 0x04}, {0x52, 0x01}, {0x53, 0x0E}, {0x54, 0x11},
> > + {0x55, 0x9F}, {0x56, 0x36}, {0x57, 0x00}, {0x58, 0x68},
> > + {0x59, 0x01}, {0x5A, 0xE0}, {0x5B, 0x00}, {0x5C, 0x00},
> > + {0x5D, 0x10}, {0x5E, 0x36}, {0x5F, 0x36}, {0x60, 0x00},
> > + {0x61, 0x04}, {0x62, 0x12}, {0x63, 0x00}, {0x64, 0x80},
> > + {0x65, 0x00}, {0x66, 0x80}, {0x67, 0x1C}, {0x68, 0x00},
> > + {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00}, {0x6C, 0x00},
> > + {0x70, 0x00}, {0x71, 0x55}, {0x72, 0x44}, {0x73, 0x33},
> > + {0x74, 0x22}, {0x75, 0x33}, {0x76, 0x22}, {0x77, 0x22},
> > + {0x78, 0x33}, {0x79, 0x22}, {0x7A, 0x44}, {0x7B, 0x55},
> > + {0x7C, 0x00}, {0x80, 0x00}, {0x81, 0x66}, {0x82, 0x22},
> > + {0x83, 0x33}, {0x84, 0x22}, {0x85, 0x33}, {0x86, 0x22},
> > + {0x87, 0x22}, {0x88, 0x33}, {0x89, 0x44}, {0x8A, 0x44},
> > + {0x8B, 0x44}, {0x8C, 0x00}, {0x90, 0x00}, {0x91, 0x77},
> > + {0x92, 0x66}, {0x93, 0x33}, {0x94, 0x11}, {0x95, 0x33},
> > + {0x96, 0x22}, {0x97, 0x22}, {0x98, 0x44}, {0x99, 0x44},
> > + {0x9A, 0x33}, {0x9B, 0x44}, {0x9C, 0x00}, {0xA0, 0x88},
> > + {0xA1, 0x88}, {0xA2, 0x88}, {0xA3, 0x88}, {0xA4, 0x88},
> > + {0xA5, 0x88}, {0xA6, 0x88}, {0xA7, 0x88}, {0xA8, 0x88},
> > + {0xA9, 0x88}, {0xAA, 0x88}, {0xAB, 0x88}, {0xAC, 0x88},
> > + {0xAD, 0x88}, {0xAE, 0x88}, {0xAF, 0x88}, {0xB0, 0x88},
> > + {0xB1, 0x88}, {0xB2, 0x88}, {0xB3, 0x88}, {0xB4, 0x88},
> > + {0xB5, 0x88}, {0xB6, 0x88}, {0xB7, 0x88}, {0xB8, 0x88},
> > + {0xB9, 0x88}, {0xBA, 0x88}, {0xBB, 0xA6}, {0xBC, 0x88},
> > + {0xBD, 0x88}, {0xBE, 0x88}, {0xBF, 0x88}, {0xC0, 0x88},
> > + {0xC1, 0x88}, {0xC2, 0x88}, {0xC3, 0x88}, {0xC4, 0x88},
> > + {0xC5, 0x88}, {0xC6, 0x88}, {0xC7, 0x88}, {0xC8, 0x88},
> > + {0xC9, 0x88}, {0xD0, 0x36}, {0xD1, 0x26}, {0xD2, 0x21},
> > + {0xD3, 0x1F}, {0xD4, 0x17}, {0xD5, 0x15}, {0xD6, 0x13},
> > + {0xD7, 0x10}, {0xD8, 0x08}, {0xD9, 0x08}, {0xDA, 0x18},
> > + {0xDB, 0x1D}, {0xDC, 0x1F}, {0xDD, 0x27}, {0xDE, 0x29},
> > + {0xDF, 0x2B}, {0xE0, 0x2E}, {0xE1, 0x36}, {0xE6, 0x00},
> > + {0xF1, 0x00}, {0xF2, 0x00}, {0xF3, 0x00}
>
> Smells non-GPLly. Maybe ask the provider of the vendor kernel for "the
> preferred form of the work for making modifications to it".
>
Well, this are address / data pairs. so probably the more
preferred form would be symbolic names. I did not manage to get u-boot sources
from Epson either. So I think it would be even harder to get anything here.
And since these things are very old, maybe more information
does not even exist anymore.
So how to progress here?
> I guess {0x0A, 0x00} disables the display, so taking over an enabled
> display setup by the bootloader doesn't work as is.
>
The factory bootloader seems only to reset things. It seems not
to initialize anything regarding display.
Until just a few months my preferred way of booting this device
was to boot vendor kernel, reprogram boot order temporarily,
go through more recent u-boot and boot linux. During this procedure,
the bridge setup was left intact. I just read things back to check
if values are already set and happlily reinitialized display itself.
No issue found doing it that way.
[...]
> > +MODULE_DEVICE_TABLE(spi, bt200_ids);
> > +
> > +static struct spi_driver bt200_driver = {
> > + .probe = bt200_probe,
> > + .remove = bt200_remove,
> > + .id_table = bt200_ids,
> > + .driver = {
> > + .name = "panel-bt200",
> > + .of_match_table = bt200_of_match,
> > + .suppress_bind_attrs = true,
>
> Why suppress bind attrs?
>
That was not intentional, maybe I had something in mind that
it is common practice is certain areas, but not for panels.
Regards,
Andreas
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200
2026-09-22 9:13 ` Uwe Kleine-König
2026-09-22 10:18 ` Andreas Kemnade
@ 2026-09-22 15:08 ` Andreas Kemnade
1 sibling, 0 replies; 13+ messages in thread
From: Andreas Kemnade @ 2026-09-22 15:08 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrzej Hajda, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Marek Vasut, Aaro Koskinen, Kevin Hilman, Roger Quadros,
Tony Lindgren, hns, dri-devel, devicetree, linux-kernel,
linux-omap
On Tue, 22 Sep 2026 11:13:36 +0200
Uwe Kleine-König <u.kleine-koenig@baylibre.com> wrote:
> > +/* found this table in vendor kernel */
> > +static struct {
> > + u8 addr;
> > + u8 data;
> > +} lcddr_init[] = {
> > + {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x05, 0x01},
> > + {0x07, 0x00}, {0x0A, 0x00}, {0x10, 0x03}, {0x11, 0x44},
> > + {0x12, 0x44}, {0x13, 0x55}, {0x14, 0x03}, {0x15, 0x00},
> > + {0x16, 0x2A}, {0x17, 0x20}, {0x18, 0x00}, {0x19, 0x10},
> > + {0x1A, 0x12}, {0x1B, 0x0E}, {0x1C, 0x0F}, {0x1D, 0x10},
> > + {0x1E, 0x0F}, {0x1F, 0x1B}, {0x20, 0x0F}, {0x21, 0x00},
> > + {0x22, 0x00}, {0x23, 0x00}, {0x24, 0x00}, {0x28, 0x14},
> > + {0x29, 0x19}, {0x2A, 0x17}, {0x2B, 0x2B}, {0x2C, 0x99},
> > + {0x2D, 0x13}, {0x2E, 0x2A}, {0x30, 0x0B}, {0x31, 0x00},
> > + {0x32, 0x00}, {0x33, 0x01}, {0x34, 0x00}, {0x35, 0x0B},
> > + {0x36, 0x04}, {0x37, 0x21}, {0x38, 0x00}, {0x39, 0x46},
> > + {0x3A, 0x01}, {0x3B, 0x06}, {0x3C, 0x03}, {0x3D, 0x00},
> > + {0x3E, 0x06}, {0x3F, 0x04}, {0x40, 0x00}, {0x41, 0x0D},
> > + {0x42, 0x00}, {0x43, 0x2E}, {0x45, 0x08}, {0x46, 0x00},
> > + {0x47, 0x01}, {0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x0B},
> > + {0x4B, 0x38}, {0x4C, 0x03}, {0x4D, 0x04}, {0x50, 0x0F},
> > + {0x51, 0x04}, {0x52, 0x01}, {0x53, 0x0E}, {0x54, 0x11},
> > + {0x55, 0x9F}, {0x56, 0x36}, {0x57, 0x00}, {0x58, 0x68},
> > + {0x59, 0x01}, {0x5A, 0xE0}, {0x5B, 0x00}, {0x5C, 0x00},
> > + {0x5D, 0x10}, {0x5E, 0x36}, {0x5F, 0x36}, {0x60, 0x00},
> > + {0x61, 0x04}, {0x62, 0x12}, {0x63, 0x00}, {0x64, 0x80},
> > + {0x65, 0x00}, {0x66, 0x80}, {0x67, 0x1C}, {0x68, 0x00},
> > + {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00}, {0x6C, 0x00},
> > + {0x70, 0x00}, {0x71, 0x55}, {0x72, 0x44}, {0x73, 0x33},
> > + {0x74, 0x22}, {0x75, 0x33}, {0x76, 0x22}, {0x77, 0x22},
> > + {0x78, 0x33}, {0x79, 0x22}, {0x7A, 0x44}, {0x7B, 0x55},
> > + {0x7C, 0x00}, {0x80, 0x00}, {0x81, 0x66}, {0x82, 0x22},
> > + {0x83, 0x33}, {0x84, 0x22}, {0x85, 0x33}, {0x86, 0x22},
> > + {0x87, 0x22}, {0x88, 0x33}, {0x89, 0x44}, {0x8A, 0x44},
> > + {0x8B, 0x44}, {0x8C, 0x00}, {0x90, 0x00}, {0x91, 0x77},
> > + {0x92, 0x66}, {0x93, 0x33}, {0x94, 0x11}, {0x95, 0x33},
> > + {0x96, 0x22}, {0x97, 0x22}, {0x98, 0x44}, {0x99, 0x44},
> > + {0x9A, 0x33}, {0x9B, 0x44}, {0x9C, 0x00}, {0xA0, 0x88},
> > + {0xA1, 0x88}, {0xA2, 0x88}, {0xA3, 0x88}, {0xA4, 0x88},
> > + {0xA5, 0x88}, {0xA6, 0x88}, {0xA7, 0x88}, {0xA8, 0x88},
> > + {0xA9, 0x88}, {0xAA, 0x88}, {0xAB, 0x88}, {0xAC, 0x88},
> > + {0xAD, 0x88}, {0xAE, 0x88}, {0xAF, 0x88}, {0xB0, 0x88},
> > + {0xB1, 0x88}, {0xB2, 0x88}, {0xB3, 0x88}, {0xB4, 0x88},
> > + {0xB5, 0x88}, {0xB6, 0x88}, {0xB7, 0x88}, {0xB8, 0x88},
> > + {0xB9, 0x88}, {0xBA, 0x88}, {0xBB, 0xA6}, {0xBC, 0x88},
> > + {0xBD, 0x88}, {0xBE, 0x88}, {0xBF, 0x88}, {0xC0, 0x88},
> > + {0xC1, 0x88}, {0xC2, 0x88}, {0xC3, 0x88}, {0xC4, 0x88},
> > + {0xC5, 0x88}, {0xC6, 0x88}, {0xC7, 0x88}, {0xC8, 0x88},
> > + {0xC9, 0x88}, {0xD0, 0x36}, {0xD1, 0x26}, {0xD2, 0x21},
> > + {0xD3, 0x1F}, {0xD4, 0x17}, {0xD5, 0x15}, {0xD6, 0x13},
> > + {0xD7, 0x10}, {0xD8, 0x08}, {0xD9, 0x08}, {0xDA, 0x18},
> > + {0xDB, 0x1D}, {0xDC, 0x1F}, {0xDD, 0x27}, {0xDE, 0x29},
> > + {0xDF, 0x2B}, {0xE0, 0x2E}, {0xE1, 0x36}, {0xE6, 0x00},
> > + {0xF1, 0x00}, {0xF2, 0x00}, {0xF3, 0x00}
>
> Smells non-GPLly. Maybe ask the provider of the vendor kernel for "the
> preferred form of the work for making modifications to it".
>
hmm, e.g.
panel-novatek-nt36523.c
has similar large init sequences with magic numbers.
So nothing unusual in upstream.
Regards,
Andreas
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-09-22 15:09 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-22 6:16 [PATCH RFC 0/9] drm: panel: Add support for Epson Moverio BT 200 display Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 1/9] dt-bindings: display: panel: Add panel for Epson Moverio BT-200 Andreas Kemnade
2026-09-22 6:16 ` [PATCH RFC 2/9] drm: panel: Add panel for Epson Moverio BT200 Andreas Kemnade
2026-09-22 9:13 ` Uwe Kleine-König
2026-09-22 10:18 ` Andreas Kemnade
2026-09-22 15:08 ` Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 3/9] dt-bindings: display: bridge: tc358762: Add some missing properties Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 4/9] drm/bridge: tc358762: Move initialisation to pre_enable Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 5/9] drm/bridge: tc358762: add SPI master support to control panels Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 6/9] drm/bridge: tc358762: read back ID register Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 7/9] drm/bridge: tc358762: make LPTXTIMECNT configurable Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 8/9] drm/bridge: tc358762: Basic support for 2 lanes Andreas Kemnade
2026-09-22 6:17 ` [PATCH RFC 9/9] ARM: dts: ti/omap: omap4-embt2ws: Add displays Andreas Kemnade
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®