mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
@ 2026-09-28 16:22 Maxime Ripard
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
                   ` (8 more replies)
  0 siblings, 9 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Hi,

Panels in general, and MIPI-DSI panels in particular, are pretty
difficult to support and require pretty much a panel driver for each
panel produced. Most of them are pretty simple, and require an opaque
initialization sequence that is usually poorly documented.

This creates a tension between OEMs and distros because OEMs will
typically get a new panel to react to a sourcing issue during
production, and thus need some swift turnaround between getting their
new panel and it being operational in the OS. Distributions on the other
hand can take years to ship a kernel with that new panel driver.

To solve this, I followed the example of HID-BPF and wrote a panel
driver that will rely on BPF programs to perform the panel
initialization. That way, we can ship the programs separately from the
kernel, and with a different lifecycle. If this driver is accepted, the
plan is to have a userspace component started by udev to identify and
load the right BPF program for the panels found on the device.

This driver is fully functional and works with both 5" and 7" Touch
Display 2 panels for the RaspberryPi. However, it breaks away from the
typical panel driver in multiple ways:

- BPF programs can only be loaded by userspace. This leaves us with two
  choices: 

  * We prevent the driver from loading until the script itself is
    loaded. This has the side effect of preventing any other output to
    be used until the initramfs is ran at the earliest, and possibly
    ever if the loader isn't installed for example.

  * Or we probe the driver all the time, but only report it as connected
    once a program has been registered. This is somewhat unconventional,
    but allows the other outputs to be functional, *and* allows the user
    to force the output if their panel doesn't require any
    initialization or during debugging. I chose this solution.

- It's not a panel driver, but a bridge one, which is also pretty
  unconventional. This is required because panel drivers don't have
  access to a detect callback that is required for the above, but I also
  think that the recent work from Luca blurs the line from panels and
  bridges and we'll end up going that road anyway.

Let me know what you think,
Maxime

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
Maxime Ripard (6):
      dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
      drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences
      drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header
      drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program
      drm/panel: dsi-bpf: Add Raspberry Pi 5-inch panel BPF program
      [DO NOT MERGE] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays

 .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++
 arch/arm64/boot/dts/broadcom/Makefile              |   8 +
 .../bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso         |  22 +
 .../bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso         |  22 +
 .../dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi  |  65 +++
 drivers/gpu/drm/panel/Kconfig                      |   3 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/bpf/Kconfig                  |  28 ++
 drivers/gpu/drm/panel/bpf/Makefile                 |  10 +
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c    | 452 +++++++++++++++++++++
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c  | 278 +++++++++++++
 drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c | 242 +++++++++++
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c   |   4 +
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h   | 246 +++++++++++
 drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h     | 264 ++++++++++++
 drivers/gpu/drm/panel/bpf/progs/Makefile           |  93 +++++
 .../panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c   | 266 ++++++++++++
 .../panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c   | 271 ++++++++++++
 .../gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h   |  95 +++++
 19 files changed, 2554 insertions(+)
---
base-commit: 6e375de99d0c420169481fcd36064177ab55b09d
change-id: 20260928-drm-mipi-dsi-panel-ebpf-d78b72a9c47f

Best regards,
-- 
Maxime Ripard <mripard@kernel.org>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 19:16   ` Neil Armstrong
                     ` (2 more replies)
  2026-09-28 16:22 ` [PATCH 2/6] drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences Maxime Ripard
                   ` (7 subsequent siblings)
  8 siblings, 3 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Most MIPI-DSI panel drivers follow an identical pattern: acquire
regulators and GPIOs, perform a reset pulse with specific timing,
send a vendor-supplied sequence of DSI commands, then enable the
display. The only truly panel-specific part is the init sequence
and power-on/off timing.

The panel-mipi-dsi-bpf driver replaces per-panel kernel modules
with a single generic driver whose panel-specific behavior is
provided by BPF programs loaded from userspace at runtime,
following the HID-BPF model. This enables new panel support
without kernel patches.

Panel DT nodes use a two-entry compatible with the panel-specific
string first and "panel-mipi-dsi-bpf" as fallback. The generic
driver matches on the fallback, while the first compatible is used
to identify which BPF program to load.

Six normalized optional regulator supplies cover ~95% of existing
MIPI-DSI panels: vcc (IC core), iovcc (I/O interface), avdd/avee
(positive/negative analog), and elvdd/elvss (OLED EL driver).

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++++++++++++++
 1 file changed, 184 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml b/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml
new file mode 100644
index 000000000000..9a20a21cf70e
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml
@@ -0,0 +1,184 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/panel-mipi-dsi-bpf.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic MIPI-DSI panel with BPF init sequences
+
+maintainers:
+  - Maxime Ripard <mripard@kernel.org>
+
+description: |
+  A generic MIPI-DSI panel driver where the panel-specific power sequencing
+  and DSI init commands are provided by BPF programs.
+
+  Panel DT nodes use a fallback compatible so the generic driver matches on
+  "panel-mipi-dsi-bpf" while the first compatible identifies the specific
+  panel for BPF program matching.
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+    items:
+      - description: Panel-specific compatible string
+      - const: panel-mipi-dsi-bpf
+
+  reg:
+    maxItems: 1
+    description: DSI virtual channel
+
+  backlight: true
+  enable-gpios: true
+  height-mm: true
+  port: true
+  reset-gpios: true
+  rotation: true
+  width-mm: true
+
+  vcc-supply:
+    description: IC core supply, typically 2.8-3.3V (charge pump input)
+
+  iovcc-supply:
+    description: I/O interface supply, typically 1.8V (MIPI logic level)
+
+  avdd-supply:
+    description: Positive analog supply for source/gate driver, typically +5V
+
+  avee-supply:
+    description: Negative analog supply for source/gate driver, typically -5V
+
+  elvdd-supply:
+    description: OLED EL positive supply
+
+  elvss-supply:
+    description: OLED EL negative supply
+
+  dsi-lanes:
+    description: Number of DSI data lanes
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [1, 2, 3, 4]
+    default: 4
+
+  mode-video:
+    type: boolean
+    description: Use DSI video mode (as opposed to command mode)
+
+  mode-video-burst:
+    type: boolean
+    description: Use DSI video burst mode
+
+  mode-lpm:
+    type: boolean
+    description: Use low-power mode for DSI commands
+
+  mode-no-eot:
+    type: boolean
+    description: Disable end-of-transmission packet
+
+  clock-non-continuous:
+    type: boolean
+    description: DSI clock is non-continuous
+
+  hs-rate:
+    description: Maximum lane frequency for high speed mode in hertz
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+  lp-rate:
+    description: Maximum lane frequency for low power mode in hertz
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+  panel-timing: true
+
+required:
+  - compatible
+  - reg
+  - panel-timing
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    dsi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        panel@0 {
+            compatible = "elida,kd35t133", "panel-mipi-dsi-bpf";
+            reg = <0>;
+            backlight = <&backlight>;
+            vcc-supply = <&vcc3v3_lcd>;
+            iovcc-supply = <&vcc_1v8>;
+            reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
+            dsi-lanes = <1>;
+            mode-video;
+            mode-video-burst;
+            mode-lpm;
+
+            width-mm = <42>;
+            height-mm = <82>;
+
+            panel-timing {
+                clock-frequency = <17000000>;
+                hactive = <320>;
+                vactive = <480>;
+                hfront-porch = <20>;
+                hsync-len = <4>;
+                hback-porch = <20>;
+                vfront-porch = <2>;
+                vsync-len = <1>;
+                vback-porch = <2>;
+            };
+
+            port {
+                panel_in: endpoint {
+                    remote-endpoint = <&dsi_out>;
+                };
+            };
+        };
+    };
+
+  - |
+    /* OLED panel using avdd/avee and elvdd/elvss supplies */
+
+    dsi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        panel@0 {
+            compatible = "boe,bf060y8m-aj0", "panel-mipi-dsi-bpf";
+            reg = <0>;
+            vcc-supply = <&vreg_l14a>;
+            iovcc-supply = <&vreg_l12a>;
+            elvdd-supply = <&vreg_oled_pos>;
+            elvss-supply = <&vreg_oled_neg>;
+            reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
+            dsi-lanes = <4>;
+            mode-video;
+
+            panel-timing {
+                clock-frequency = <157000000>;
+                hactive = <1080>;
+                vactive = <2160>;
+                hfront-porch = <36>;
+                hsync-len = <4>;
+                hback-porch = <36>;
+                vfront-porch = <4>;
+                vsync-len = <4>;
+                vback-porch = <4>;
+            };
+
+            port {
+                oled_in: endpoint {
+                    remote-endpoint = <&dsi0_out>;
+                };
+            };
+        };
+    };
+
+...

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 2/6] drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 16:22 ` [PATCH 3/6] drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header Maxime Ripard
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Most MIPI-DSI panel drivers reimplement the same boilerplate with
only the init sequence and power-on/off timing varying. However,
each new panel still requires a kernel driver, creating a tension
between OEMs and distributions. OEMs need to support new panels
swiftly to accommodate sourcing and production issues, while
distributions can take years to ship a new kernel.

Replace per-panel modules with a single generic driver whose
panel-specific behavior is provided by BPF programs loaded from
userspace, following the HID-BPF model (drivers/hid/bpf/).

The driver registers a drm_bridge that remains disconnected until
a BPF program attaches, at which point the connector appears and
the display pipeline comes up. This also allows users to force-
enable the pipeline when it makes sense for their setup.

Sleepable kfuncs are exposed to control regulators, GPIOs, and
send and receive DSI data. The rest of the boilerplate is handled
by the driver directly.

Sysfs attributes expose the link configuration so that a
userspace BPF loader can match programs to panels without parsing
the device tree.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 drivers/gpu/drm/panel/Kconfig                      |   3 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/bpf/Kconfig                  |  28 ++
 drivers/gpu/drm/panel/bpf/Makefile                 |  10 +
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c    | 452 +++++++++++++++++++++
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c  | 278 +++++++++++++
 drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c | 242 +++++++++++
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c   |   4 +
 .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h   | 246 +++++++++++
 drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h     | 264 ++++++++++++
 include/drm/drm_panel_dsi_bpf.h                    |  50 +++
 11 files changed, 1578 insertions(+)

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 747f47347521..d885fc4a8eb7 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -1438,6 +1438,9 @@ config DRM_PANEL_XINPENG_XPP055C272
 	depends on BACKLIGHT_CLASS_DEVICE
 	help
 	  Say Y here if you want to enable support for the Xinpeng
 	  XPP055C272 controller for 720x1280 LCD panels with MIPI/RGB/SPI
 	  system interfaces.
+
+source "drivers/gpu/drm/panel/bpf/Kconfig"
+
 endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f2c9c80a218f..729394961101 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -8,10 +8,11 @@ obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o
 obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
 obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o
 obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o
 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o
 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
+obj-$(CONFIG_DRM_PANEL_BPF) += bpf/
 obj-$(CONFIG_DRM_PANEL_CHIPONE_ICNA35XX) += panel-chipone-icna35xx.o
 obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o
 obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
 obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
diff --git a/drivers/gpu/drm/panel/bpf/Kconfig b/drivers/gpu/drm/panel/bpf/Kconfig
new file mode 100644
index 000000000000..2331e9fc26cc
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_PANEL_BPF
+	tristate "eBPF-backed panels"
+	depends on DRM && DRM_PANEL
+	depends on BPF_SYSCALL
+	help
+	  TODO
+
+menu "eBPF Display Panels"
+	depends on DRM_PANEL_BPF
+
+config DRM_PANEL_BPF_MIPI_DSI
+	tristate "Generic BPF-based MIPI-DSI panel"
+	depends on BACKLIGHT_CLASS_DEVICE
+	depends on BPF_SYSCALL
+	depends on DRM_MIPI_DSI
+	depends on OF
+	select VIDEOMODE_HELPERS
+	help
+	  Generic MIPI-DSI panel driver that delegates panel-specific
+	  power sequencing and DSI init commands to BPF programs loaded
+	  from userspace.
+
+	  Enable this if you want to support MIPI-DSI panels without
+	  panel-specific kernel drivers.
+
+endmenu
diff --git a/drivers/gpu/drm/panel/bpf/Makefile b/drivers/gpu/drm/panel/bpf/Makefile
new file mode 100644
index 000000000000..07c68ca2eb69
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_PANEL_BPF_MIPI_DSI) += panel-bpf-mipi-dsi.o
+
+panel-bpf-mipi-dsi-y := panel-bpf-mipi-dsi-core.o \
+			 panel-bpf-mipi-dsi-kfuncs.o \
+			 panel-bpf-mipi-dsi-ops.o \
+			 panel-bpf-mipi-dsi-trace.o
+
+CFLAGS_panel-bpf-mipi-dsi-trace.o := -I$(src)
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c
new file mode 100644
index 000000000000..585f3cc5fda9
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic MIPI-DSI panel driver with BPF-based init sequences
+ *
+ * Copyright (C) 2026
+ *
+ * A single generic MIPI-DSI panel driver where the panel-specific
+ * power sequencing and DSI init commands are provided by BPF
+ * struct_ops programs loaded from userspace, following the HID-BPF
+ * model (drivers/hid/bpf/).
+ *
+ * This file handles DT resource acquisition, drm_bridge registration,
+ * and dispatching bridge callbacks to BPF programs. Panel DT nodes
+ * use a fallback compatible:
+ *
+ *   compatible = "elida,kd35t133", "panel-mipi-dsi-bpf";
+ *
+ * The driver matches on "panel-mipi-dsi-bpf". The panel's OF node
+ * path is stored as panel_id for BPF matching. New panels only need
+ * a DT overlay and a BPF program — no kernel driver.
+ *
+ * See panel-bpf-mipi-dsi-ops.c for the struct_ops binding logic and
+ * panel-bpf-mipi-dsi-kfuncs.c for the kfunc interface.
+ */
+
+#include <linux/backlight.h>
+#include <linux/bpf.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+
+#include "panel-bpf-mipi-dsi.h"
+#include "panel-bpf-mipi-dsi-trace.h"
+
+static LIST_HEAD(panel_bpf_mipi_dsi_list);
+DEFINE_MUTEX(panel_bpf_mipi_dsi_list_lock);
+
+struct panel_bpf_mipi_dsi_entry {
+	struct list_head		list;
+	struct panel_bpf_mipi_dsi	*panel;
+};
+
+static void panel_bpf_mipi_dsi_list_cleanup(void *data)
+{
+	struct panel_bpf_mipi_dsi_entry *entry = data;
+
+	scoped_guard(mutex, &panel_bpf_mipi_dsi_list_lock)
+		list_del(&entry->list);
+}
+
+int panel_bpf_mipi_dsi_list_add(struct panel_bpf_mipi_dsi *panel)
+{
+	struct device *dev = &panel->dsi->dev;
+	struct panel_bpf_mipi_dsi_entry *entry;
+
+	entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->panel = panel;
+
+	scoped_guard(mutex, &panel_bpf_mipi_dsi_list_lock)
+		list_add_tail(&entry->list, &panel_bpf_mipi_dsi_list);
+
+	return devm_add_action_or_reset(dev, panel_bpf_mipi_dsi_list_cleanup,
+					entry);
+}
+
+struct panel_bpf_mipi_dsi *
+panel_bpf_mipi_dsi_find_panel_unlocked(const char *panel_id)
+{
+	struct panel_bpf_mipi_dsi_entry *entry;
+
+	lockdep_assert_held(&panel_bpf_mipi_dsi_list_lock);
+
+	list_for_each_entry(entry, &panel_bpf_mipi_dsi_list, list)
+		if (!strcmp(entry->panel->panel_id, panel_id))
+			return entry->panel;
+
+	return NULL;
+}
+
+#define call_bpf_op(p, op, c)						\
+	({								\
+		struct panel_bpf_mipi_dsi *__bpf_panel = (p);		\
+		int __result = 0;					\
+									\
+		lockdep_assert_held(&__bpf_panel->bpf_lock);		\
+									\
+		if (__bpf_panel->bpf_ops && __bpf_panel->bpf_ops->op) {	\
+			trace_panel_bpf_mipi_dsi_callback(__bpf_panel->panel_id, #op); \
+			__result = __bpf_panel->bpf_ops->op(c);		\
+			trace_panel_bpf_mipi_dsi_callback_done(__bpf_panel->panel_id, #op, \
+							       __result); \
+		}							\
+									\
+		__result;						\
+	})
+
+static void panel_bpf_mipi_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+							struct drm_atomic_commit *commit)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	struct panel_bpf_mipi_dsi_ctx ctx = { .bridge = bridge };
+
+	scoped_guard(mutex, &panel->bpf_lock)
+		call_bpf_op(panel, panel_prepare, &ctx);
+}
+
+static void panel_bpf_mipi_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
+						    struct drm_atomic_commit *commit)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	struct panel_bpf_mipi_dsi_ctx ctx = { .bridge = bridge };
+
+	scoped_guard(mutex, &panel->bpf_lock)
+		call_bpf_op(panel, panel_enable, &ctx);
+
+	backlight_enable(panel->backlight);
+}
+
+static void panel_bpf_mipi_dsi_bridge_atomic_disable(struct drm_bridge *bridge,
+						     struct drm_atomic_commit *commit)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	struct panel_bpf_mipi_dsi_ctx ctx = { .bridge = bridge };
+
+	backlight_disable(panel->backlight);
+
+	scoped_guard(mutex, &panel->bpf_lock)
+		call_bpf_op(panel, panel_disable, &ctx);
+}
+
+static void panel_bpf_mipi_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge,
+							  struct drm_atomic_commit *commit)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	struct panel_bpf_mipi_dsi_ctx ctx = { .bridge = bridge };
+
+	scoped_guard(mutex, &panel->bpf_lock)
+		call_bpf_op(panel, panel_unprepare, &ctx);
+}
+
+static int panel_bpf_mipi_dsi_bridge_get_modes(struct drm_bridge *bridge,
+					       struct drm_connector *connector)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	struct drm_display_mode *mode;
+	struct videomode vm;
+
+	videomode_from_timing(&panel->dt, &vm);
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode)
+		return -ENOMEM;
+
+	drm_display_mode_from_videomode(&vm, mode);
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	connector->display_info.width_mm = panel->width_mm;
+	connector->display_info.height_mm = panel->height_mm;
+	drm_connector_set_panel_orientation(connector, panel->orientation);
+
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+/*
+ * Report connected only when a BPF program is attached. This lets
+ * userspace see the connector come up when the BPF loader runs.
+ */
+static enum drm_connector_status
+panel_bpf_mipi_dsi_bridge_detect(struct drm_bridge *bridge,
+				 struct drm_connector *connector)
+{
+	struct panel_bpf_mipi_dsi *panel = drm_bridge_to_bpf_panel(bridge);
+	enum drm_connector_status status;
+
+	scoped_guard(mutex, &panel->bpf_lock)
+		status = panel->bpf_ops ? connector_status_connected :
+			connector_status_disconnected;
+
+	return status;
+}
+
+static int panel_bpf_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
+					    struct drm_encoder *encoder,
+					    enum drm_bridge_attach_flags flags)
+{
+	struct drm_connector *connector;
+
+	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+		return 0;
+
+	connector = drm_bridge_connector_init(bridge->dev, encoder);
+	if (IS_ERR(connector))
+		return PTR_ERR(connector);
+
+	return drm_connector_attach_encoder(connector, encoder);
+}
+
+static const struct drm_bridge_funcs panel_bpf_mipi_dsi_bridge_funcs = {
+	.attach			= panel_bpf_mipi_dsi_bridge_attach,
+	.atomic_create_state	= drm_atomic_helper_bridge_create_state,
+	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state,
+	.detect			= panel_bpf_mipi_dsi_bridge_detect,
+	.atomic_pre_enable	= panel_bpf_mipi_dsi_bridge_atomic_pre_enable,
+	.atomic_enable		= panel_bpf_mipi_dsi_bridge_atomic_enable,
+	.atomic_disable		= panel_bpf_mipi_dsi_bridge_atomic_disable,
+	.atomic_post_disable	= panel_bpf_mipi_dsi_bridge_atomic_post_disable,
+	.get_modes		= panel_bpf_mipi_dsi_bridge_get_modes,
+};
+
+/*
+ * Sysfs attributes exposing the DSI link configuration. These mirror
+ * the DT properties and let userspace (e.g. the BPF loader) read
+ * back the panel's link parameters without parsing the device tree.
+ */
+static const char * const pixel_format_names[] = {
+	[MIPI_DSI_FMT_RGB888]		= "rgb888",
+	[MIPI_DSI_FMT_RGB666]		= "rgb666",
+	[MIPI_DSI_FMT_RGB666_PACKED]	= "rgb666-packed",
+	[MIPI_DSI_FMT_RGB565]		= "rgb565",
+	[MIPI_DSI_FMT_RGB101010]	= "rgb101010",
+};
+
+static ssize_t pixel_format_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+	if (dsi->format >= ARRAY_SIZE(pixel_format_names))
+		return sysfs_emit(buf, "unknown(%u)\n", dsi->format);
+
+	return sysfs_emit(buf, "%s\n", pixel_format_names[dsi->format]);
+}
+static DEVICE_ATTR_RO(pixel_format);
+
+static ssize_t lanes_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+	return sysfs_emit(buf, "%u\n", dsi->lanes);
+}
+static DEVICE_ATTR_RO(lanes);
+
+static ssize_t mode_flags_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+	return sysfs_emit(buf, "0x%lx\n", dsi->mode_flags);
+}
+static DEVICE_ATTR_RO(mode_flags);
+
+static ssize_t hs_rate_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+	return sysfs_emit(buf, "%lu\n", dsi->hs_rate);
+}
+static DEVICE_ATTR_RO(hs_rate);
+
+static ssize_t lp_rate_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+	return sysfs_emit(buf, "%lu\n", dsi->lp_rate);
+}
+static DEVICE_ATTR_RO(lp_rate);
+
+static struct attribute *panel_bpf_mipi_dsi_attrs[] = {
+	&dev_attr_pixel_format.attr,
+	&dev_attr_lanes.attr,
+	&dev_attr_mode_flags.attr,
+	&dev_attr_hs_rate.attr,
+	&dev_attr_lp_rate.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(panel_bpf_mipi_dsi);
+
+static const char * const panel_bpf_mipi_dsi_supply_names[] = {
+	[PANEL_BPF_MIPI_DSI_SUPPLY_VCC]		= "vcc",
+	[PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC]	= "iovcc",
+	[PANEL_BPF_MIPI_DSI_SUPPLY_AVDD]	= "avdd",
+	[PANEL_BPF_MIPI_DSI_SUPPLY_AVEE]	= "avee",
+	[PANEL_BPF_MIPI_DSI_SUPPLY_ELVDD]	= "elvdd",
+	[PANEL_BPF_MIPI_DSI_SUPPLY_ELVSS]	= "elvss",
+};
+
+static int panel_bpf_mipi_dsi_parse_regulators(struct panel_bpf_mipi_dsi *panel)
+{
+	int i;
+
+	for (i = 0; i < PANEL_BPF_MIPI_DSI_SUPPLY_COUNT; i++)
+		panel->supplies[i].supply = panel_bpf_mipi_dsi_supply_names[i];
+
+	return devm_regulator_bulk_get(&panel->dsi->dev, PANEL_BPF_MIPI_DSI_SUPPLY_COUNT,
+				       panel->supplies);
+}
+
+static int panel_bpf_mipi_dsi_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct panel_bpf_mipi_dsi *panel;
+	u32 val;
+	int ret;
+
+	panel = devm_drm_bridge_alloc(dev, struct panel_bpf_mipi_dsi, bridge,
+				      &panel_bpf_mipi_dsi_bridge_funcs);
+	if (IS_ERR(panel))
+		return PTR_ERR(panel);
+
+	panel->dsi = dsi;
+	mutex_init(&panel->bpf_lock);
+
+	panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_RESET] =
+		devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_RESET]))
+		return dev_err_probe(dev,
+				     PTR_ERR(panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_RESET]),
+				     "Failed to get reset GPIO\n");
+
+	panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_ENABLE] =
+		devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_ENABLE]))
+		return dev_err_probe(dev,
+				     PTR_ERR(panel->gpios[PANEL_BPF_MIPI_DSI_GPIO_ENABLE]),
+				     "Failed to get enable GPIO\n");
+
+	ret = panel_bpf_mipi_dsi_parse_regulators(panel);
+	if (ret)
+		return ret;
+
+	ret = drm_of_get_panel_orientation(dev->of_node, &panel->orientation);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to get orientation\n");
+
+	ret = of_get_display_timing(dev->of_node, "panel-timing", &panel->dt);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to get panel-timing\n");
+
+	of_property_read_u32(dev->of_node, "width-mm", &panel->width_mm);
+	of_property_read_u32(dev->of_node, "height-mm", &panel->height_mm);
+
+	panel->backlight = devm_of_find_backlight(dev);
+	if (IS_ERR(panel->backlight))
+		return dev_err_probe(dev, PTR_ERR(panel->backlight),
+				     "Failed to get backlight\n");
+
+	if (!of_property_read_u32(dev->of_node, "dsi-lanes", &val))
+		dsi->lanes = val;
+	else
+		dsi->lanes = 4;
+
+	dsi->format = MIPI_DSI_FMT_RGB888;
+
+	if (of_property_read_bool(dev->of_node, "mode-video"))
+		dsi->mode_flags |= MIPI_DSI_MODE_VIDEO;
+	if (of_property_read_bool(dev->of_node, "mode-video-burst"))
+		dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
+	if (of_property_read_bool(dev->of_node, "mode-lpm"))
+		dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+	if (of_property_read_bool(dev->of_node, "mode-no-eot"))
+		dsi->mode_flags |= MIPI_DSI_MODE_NO_EOT_PACKET;
+	if (of_property_read_bool(dev->of_node, "clock-non-continuous"))
+		dsi->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	if (!of_property_read_u32(dev->of_node, "hs-rate", &val))
+		dsi->hs_rate = val;
+	if (!of_property_read_u32(dev->of_node, "lp-rate", &val))
+		dsi->lp_rate = val;
+
+	mipi_dsi_set_drvdata(dsi, panel);
+
+	panel->bridge.type = DRM_MODE_CONNECTOR_DSI;
+	panel->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
+	panel->bridge.of_node = dev->of_node;
+	panel->bridge.pre_enable_prev_first = true;
+
+	snprintf(panel->panel_id, sizeof(panel->panel_id), "%pOF",
+		 dev->of_node);
+
+	ret = panel_bpf_mipi_dsi_list_add(panel);
+	if (ret)
+		return ret;
+
+	ret = devm_drm_bridge_add(dev, &panel->bridge);
+	if (ret)
+		return ret;
+
+	ret = devm_mipi_dsi_attach(dev, dsi);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
+
+	return 0;
+}
+
+static const struct of_device_id panel_bpf_mipi_dsi_of_match[] = {
+	{ .compatible = "panel-mipi-dsi-bpf" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, panel_bpf_mipi_dsi_of_match);
+
+static struct mipi_dsi_driver panel_bpf_mipi_dsi_driver = {
+	.driver = {
+		.name		= "panel-bpf-mipi-dsi",
+		.of_match_table	= panel_bpf_mipi_dsi_of_match,
+		.dev_groups	= panel_bpf_mipi_dsi_groups,
+	},
+	.probe	= panel_bpf_mipi_dsi_probe,
+};
+
+static int __init panel_bpf_mipi_dsi_init(void)
+{
+	int ret;
+
+	ret = panel_bpf_mipi_dsi_register_struct_ops();
+	if (ret)
+		return ret;
+
+	ret = panel_bpf_mipi_dsi_register_kfuncs();
+	if (ret)
+		return ret;
+
+	return mipi_dsi_driver_register(&panel_bpf_mipi_dsi_driver);
+}
+module_init(panel_bpf_mipi_dsi_init);
+
+static void __exit panel_bpf_mipi_dsi_exit(void)
+{
+	mipi_dsi_driver_unregister(&panel_bpf_mipi_dsi_driver);
+}
+module_exit(panel_bpf_mipi_dsi_exit);
+
+MODULE_DESCRIPTION("Generic MIPI-DSI panel driver with BPF init sequences");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c
new file mode 100644
index 000000000000..d44c61382c77
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/btf_ids.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+
+#include "panel-bpf-mipi-dsi.h"
+#include "panel-bpf-mipi-dsi-trace.h"
+
+static const char * const panel_bpf_mipi_dsi_gpio_names[] = {
+	[PANEL_BPF_MIPI_DSI_GPIO_ENABLE]	= "enable",
+	[PANEL_BPF_MIPI_DSI_GPIO_RESET]		= "reset",
+};
+
+__bpf_kfunc_start_defs();
+
+/**
+ * panel_bpf_mipi_dsi_regulator_enable_and_wait - Enable a supply and wait
+ * @ctx: Panel context passed to the BPF callback
+ * @supply: Which supply to enable (VCC, IOVCC, AVDD, ...)
+ * @settle_ms: Milliseconds to sleep after enabling
+ *
+ * Enable the regulator identified by @supply and optionally sleep for
+ * @settle_ms to let the voltage stabilize.
+ *
+ * If the regulator was not specified in the DT, this function is a
+ * no-op.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+__bpf_kfunc int panel_bpf_mipi_dsi_regulator_enable_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+							     enum panel_bpf_mipi_dsi_supply supply,
+							     u32 settle_ms)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+	int ret;
+
+	if (supply >= PANEL_BPF_MIPI_DSI_SUPPLY_COUNT)
+		return -EINVAL;
+
+	trace_panel_bpf_mipi_dsi_regulator_enable_and_wait(panel->panel_id,
+							   panel->supplies[supply].supply,
+							   settle_ms);
+
+	ret = regulator_enable(panel->supplies[supply].consumer);
+
+	if (settle_ms)
+		msleep(settle_ms);
+
+	return ret;
+}
+
+/**
+ * panel_bpf_mipi_dsi_regulator_disable - Disable a supply
+ * @ctx: Panel context passed to the BPF callback
+ * @supply: Which supply to disable (VCC, IOVCC, AVDD, ...)
+ *
+ * Disable the regulator identified by @supply. Typically called
+ * from panel_unprepare after the panel has been put to sleep.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+__bpf_kfunc int panel_bpf_mipi_dsi_regulator_disable(struct panel_bpf_mipi_dsi_ctx *ctx,
+						     enum panel_bpf_mipi_dsi_supply supply)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+
+	if (supply >= PANEL_BPF_MIPI_DSI_SUPPLY_COUNT)
+		return -EINVAL;
+
+	trace_panel_bpf_mipi_dsi_regulator_disable(panel->panel_id,
+						   panel->supplies[supply].supply);
+
+	return regulator_disable(panel->supplies[supply].consumer);
+}
+
+/**
+ * panel_bpf_mipi_dsi_gpio_cycle_and_wait - Pulse a GPIO high then low
+ * @ctx: Panel context passed to the BPF callback
+ * @gpio: Which GPIO to cycle (RESET, ENABLE)
+ * @assert_ms: Milliseconds to hold the GPIO asserted
+ * @settle_ms: Milliseconds to sleep after deasserting
+ *
+ * Assert @gpio (set to 1), hold for @assert_ms, deassert (set to 0),
+ * then sleep for @settle_ms. This is the standard reset pulse
+ * sequence used by most MIPI-DSI panels.
+ *
+ * If the GPIO was not specified in the DT, this function is a no-op.
+ */
+__bpf_kfunc void panel_bpf_mipi_dsi_gpio_cycle_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+							enum panel_bpf_mipi_dsi_gpio gpio,
+							u32 assert_ms, u32 settle_ms)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+
+	if (gpio >= PANEL_BPF_MIPI_DSI_GPIO_COUNT)
+		return;
+
+	trace_panel_bpf_mipi_dsi_gpio_cycle_and_wait(panel->panel_id,
+						     panel_bpf_mipi_dsi_gpio_names[gpio],
+						     assert_ms, settle_ms);
+
+	gpiod_set_value_cansleep(panel->gpios[gpio], 1);
+
+	if (assert_ms)
+		msleep(assert_ms);
+
+	gpiod_set_value_cansleep(panel->gpios[gpio], 0);
+
+	if (settle_ms)
+		msleep(settle_ms);
+}
+
+/**
+ * panel_bpf_mipi_dsi_gpio_enable - Assert a GPIO
+ * @ctx: Panel context passed to the BPF callback
+ * @gpio: Which GPIO to assert (RESET, ENABLE)
+ *
+ * Set @gpio to its active state (logical 1). Typically used in
+ * panel_unprepare to hold reset asserted while the panel is off.
+ */
+__bpf_kfunc void panel_bpf_mipi_dsi_gpio_enable(struct panel_bpf_mipi_dsi_ctx *ctx,
+						 enum panel_bpf_mipi_dsi_gpio gpio)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+
+	if (gpio >= PANEL_BPF_MIPI_DSI_GPIO_COUNT)
+		return;
+
+	trace_panel_bpf_mipi_dsi_gpio_enable(panel->panel_id,
+					     panel_bpf_mipi_dsi_gpio_names[gpio]);
+
+	gpiod_set_value_cansleep(panel->gpios[gpio], 1);
+}
+
+/**
+ * panel_bpf_mipi_dsi_gpio_disable - Deassert a GPIO
+ * @ctx: Panel context passed to the BPF callback
+ * @gpio: Which GPIO to deassert (RESET, ENABLE)
+ *
+ * Set @gpio to its inactive state (logical 0).
+ */
+__bpf_kfunc void panel_bpf_mipi_dsi_gpio_disable(struct panel_bpf_mipi_dsi_ctx *ctx,
+						 enum panel_bpf_mipi_dsi_gpio gpio)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+
+	if (gpio >= PANEL_BPF_MIPI_DSI_GPIO_COUNT)
+		return;
+
+	trace_panel_bpf_mipi_dsi_gpio_disable(panel->panel_id,
+					      panel_bpf_mipi_dsi_gpio_names[gpio]);
+
+	gpiod_set_value_cansleep(panel->gpios[gpio], 0);
+}
+
+/**
+ * panel_bpf_mipi_dsi_dcs_write_and_wait - Send a DCS command and wait
+ * @ctx: Panel context passed to the BPF callback
+ * @cmd: DCS command byte
+ * @data__nullable: Payload bytes following the command, or NULL
+ * @data__nullable__sz: Size of @data__nullable in bytes
+ * @settle_ms: Milliseconds to sleep after sending
+ *
+ * Send a MIPI DCS command with optional payload over the DSI link, with
+ * a post-send sleep of @settle_ms. Typically used for commands like
+ * MIPI_DCS_EXIT_SLEEP_MODE.
+ *
+ * Return: Number of bytes written on success, negative errno on
+ * failure.
+ */
+__bpf_kfunc int panel_bpf_mipi_dsi_dcs_write_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+						      u8 cmd, const u8 *data__nullable,
+						      u32 data__nullable__sz,
+						      u32 settle_ms)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+	int ret;
+
+	trace_panel_bpf_mipi_dsi_dcs_write_and_wait(panel->panel_id, cmd, data__nullable,
+						    data__nullable__sz, settle_ms);
+
+	ret = mipi_dsi_dcs_write(panel->dsi, cmd, data__nullable,
+				 data__nullable__sz);
+
+	if (settle_ms)
+		msleep(settle_ms);
+
+	return ret;
+}
+
+/**
+ * panel_bpf_mipi_dsi_generic_write_and_wait - Send a generic DSI write and wait
+ * @ctx: Panel context passed to the BPF callback
+ * @data: Payload bytes to send
+ * @data__sz: Size of @data in bytes
+ * @settle_ms: Milliseconds to sleep after sending
+ *
+ * Send a generic (non-DCS) MIPI DSI write over the DSI link, then
+ * sleep for @settle_ms. Used for vendor-specific commands that do
+ * not follow the DCS command format.
+ *
+ * Return: Number of bytes written on success, negative errno on
+ * failure.
+ */
+__bpf_kfunc int panel_bpf_mipi_dsi_generic_write_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+							  const u8 *data, u32 data__sz,
+							  u32 settle_ms)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+	int ret;
+
+	trace_panel_bpf_mipi_dsi_generic_write_and_wait(panel->panel_id,
+							data, data__sz,
+							settle_ms);
+
+	ret = mipi_dsi_generic_write(panel->dsi, data, data__sz);
+	if (settle_ms)
+		msleep(settle_ms);
+
+	return ret;
+}
+
+/**
+ * panel_bpf_mipi_dsi_dcs_read - Read a DCS register
+ * @ctx: Panel context passed to the BPF callback
+ * @cmd: DCS command byte to read
+ * @data: Buffer to store the response
+ * @data__sz: Size of @data in bytes
+ *
+ * Send a DCS read command and store the response in @data. Useful for
+ * reading panel identification registers or status during init.
+ *
+ * Return: Number of bytes read on success, negative errno on failure.
+ */
+__bpf_kfunc int panel_bpf_mipi_dsi_dcs_read(struct panel_bpf_mipi_dsi_ctx *ctx,
+					    u8 cmd, u8 *data, u32 data__sz)
+{
+	struct panel_bpf_mipi_dsi *panel = bpf_ctx_to_bpf_panel(ctx);
+	int ret;
+
+	trace_panel_bpf_mipi_dsi_dcs_read(panel->panel_id, cmd, data__sz);
+
+	ret = mipi_dsi_dcs_read(panel->dsi, cmd, data, data__sz);
+
+	trace_panel_bpf_mipi_dsi_dcs_read_done(panel->panel_id, cmd, data__sz, ret);
+
+	return ret;
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(panel_bpf_mipi_dsi_kfunc_ids)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_regulator_enable_and_wait, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_regulator_disable, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_gpio_cycle_and_wait, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_gpio_enable, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_gpio_disable, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_dcs_write_and_wait, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_generic_write_and_wait, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, panel_bpf_mipi_dsi_dcs_read, KF_SLEEPABLE)
+BTF_KFUNCS_END(panel_bpf_mipi_dsi_kfunc_ids)
+
+static const struct btf_kfunc_id_set panel_bpf_mipi_dsi_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set   = &panel_bpf_mipi_dsi_kfunc_ids,
+};
+
+int panel_bpf_mipi_dsi_register_kfuncs(void)
+{
+	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
+					 &panel_bpf_mipi_dsi_kfunc_set);
+}
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c
new file mode 100644
index 000000000000..345b9aadaae7
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPF struct_ops for the generic MIPI-DSI panel driver
+ *
+ * Implements the struct_ops callbacks that let BPF programs provide
+ * drm_panel_dsi_bpf_ops.
+ */
+
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/string.h>
+
+#include <drm/drm_mipi_dsi.h>
+
+#include "panel-bpf-mipi-dsi.h"
+#include "panel-bpf-mipi-dsi-trace.h"
+
+/* Required by bpf_struct_ops_desc_init(), which calls it unconditionally. */
+static int panel_bpf_mipi_dsi_bpf_init(struct btf *btf)
+{
+	return 0;
+}
+
+/*
+ * Copy non-function fields from the userspace struct_ops map into the
+ * kernel instance. The BPF verifier rejects non-zero values in fields
+ * that are not explicitly handled here, so every data field must have
+ * a case. Return 1 to indicate the field was consumed.
+ */
+static int panel_bpf_mipi_dsi_bpf_init_member(const struct btf_type *t,
+					      const struct btf_member *member,
+					      void *kdata, const void *udata)
+{
+	const struct drm_panel_dsi_bpf_ops *uops =
+		(const struct drm_panel_dsi_bpf_ops *)udata;
+	struct drm_panel_dsi_bpf_ops *kops =
+		(struct drm_panel_dsi_bpf_ops *)kdata;
+	u32 moff;
+
+	moff = __btf_member_bit_offset(t, member) / 8;
+
+	switch (moff) {
+	case offsetof(struct drm_panel_dsi_bpf_ops, panel_id):
+		memcpy(kops->panel_id, uops->panel_id,
+		       sizeof(kops->panel_id));
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, compatible):
+		memcpy(kops->compatible, uops->compatible,
+		       sizeof(kops->compatible));
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, format):
+		kops->format = uops->format;
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, lanes):
+		kops->lanes = uops->lanes;
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, mode_flags):
+		kops->mode_flags = uops->mode_flags;
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, hs_rate):
+		kops->hs_rate = uops->hs_rate;
+		return 1;
+	case offsetof(struct drm_panel_dsi_bpf_ops, lp_rate):
+		kops->lp_rate = uops->lp_rate;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Validate that the DSI link parameters compiled into the BPF program
+ * match what the DT describes. Rejects mismatches early so a BPF
+ * program built for a different panel variant cannot silently attach.
+ */
+static int panel_bpf_mipi_dsi_bpf_check_config(struct panel_bpf_mipi_dsi *panel,
+					       struct drm_panel_dsi_bpf_ops *ops)
+{
+	struct mipi_dsi_device *dsi = panel->dsi;
+	struct device *dev = &dsi->dev;
+
+	if (!of_device_is_compatible(dev->of_node, ops->compatible)) {
+		dev_err(dev,
+			"BPF compatible \"%s\" doesn't match panel\n",
+			ops->compatible);
+		return -EINVAL;
+	}
+
+	if (ops->format != dsi->format) {
+		dev_err(dev,
+			"BPF pixel format %u doesn't match DT format %u\n",
+			ops->format, dsi->format);
+		return -EINVAL;
+	}
+
+	if (ops->lanes != dsi->lanes) {
+		dev_err(dev,
+			"BPF lanes %u doesn't match DT lanes %u\n",
+			ops->lanes, dsi->lanes);
+		return -EINVAL;
+	}
+
+	if (ops->mode_flags != dsi->mode_flags) {
+		dev_err(dev,
+			"BPF mode_flags 0x%lx doesn't match DT mode_flags 0x%lx\n",
+			ops->mode_flags, dsi->mode_flags);
+		return -EINVAL;
+	}
+
+	if (ops->hs_rate != dsi->hs_rate) {
+		dev_err(dev,
+			"BPF hs_rate %lu doesn't match DT hs_rate %lu\n",
+			ops->hs_rate, dsi->hs_rate);
+		return -EINVAL;
+	}
+
+	if (ops->lp_rate != dsi->lp_rate) {
+		dev_err(dev,
+			"BPF lp_rate %lu doesn't match DT lp_rate %lu\n",
+			ops->lp_rate, dsi->lp_rate);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Bind a BPF program to its target panel. Called when the struct_ops
+ * link is activated. Returns -ENODEV if no panel matches, -EBUSY if
+ * a program is already attached.
+ */
+static int panel_bpf_mipi_dsi_bpf_reg(void *kdata, struct bpf_link *link)
+{
+	struct drm_panel_dsi_bpf_ops *ops = kdata;
+	struct panel_bpf_mipi_dsi *panel;
+	int ret;
+
+	trace_panel_bpf_mipi_dsi_reg(ops->panel_id);
+
+	guard(mutex)(&panel_bpf_mipi_dsi_list_lock);
+
+	panel = panel_bpf_mipi_dsi_find_panel_unlocked(ops->panel_id);
+	if (!panel)
+		return -ENODEV;
+
+	guard(mutex)(&panel->bpf_lock);
+
+	if (panel->bpf_ops)
+		return -EBUSY;
+
+	ret = panel_bpf_mipi_dsi_bpf_check_config(panel, ops);
+	if (ret)
+		return ret;
+
+	ops->bridge = &panel->bridge;
+	panel->bpf_ops = ops;
+
+	return 0;
+}
+
+/*
+ * Detach a BPF program from its panel. The bridge stays registered so
+ * the display pipeline is not torn down — callbacks become no-ops
+ * until a new program attaches.
+ */
+static void panel_bpf_mipi_dsi_bpf_unreg(void *kdata, struct bpf_link *link)
+{
+	struct drm_panel_dsi_bpf_ops *ops = kdata;
+	struct panel_bpf_mipi_dsi *panel;
+
+	trace_panel_bpf_mipi_dsi_unreg(ops->panel_id);
+
+	if (!ops->bridge)
+		return;
+
+	panel = drm_bridge_to_bpf_panel(ops->bridge);
+
+	scoped_guard(mutex, &panel->bpf_lock) {
+		panel->bpf_ops = NULL;
+		ops->bridge = NULL;
+	}
+}
+
+static bool panel_bpf_mipi_dsi_verifier_is_valid_access(int off, int size,
+							enum bpf_access_type type,
+							const struct bpf_prog *prog,
+							struct bpf_insn_access_aux *info)
+{
+	return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
+}
+
+static const struct bpf_verifier_ops panel_bpf_mipi_dsi_bpf_verifier_ops = {
+	.get_func_proto		= bpf_base_func_proto,
+	.is_valid_access	= panel_bpf_mipi_dsi_verifier_is_valid_access,
+};
+
+/* CFI stubs — no-op targets for indirect call validation (CONFIG_CFI_CLANG). */
+static int panel_bpf_mipi_dsi_cfi_stub_prepare(struct panel_bpf_mipi_dsi_ctx *ctx)
+{
+	return 0;
+}
+
+static int panel_bpf_mipi_dsi_cfi_stub_unprepare(struct panel_bpf_mipi_dsi_ctx *ctx)
+{
+	return 0;
+}
+
+static int panel_bpf_mipi_dsi_cfi_stub_enable(struct panel_bpf_mipi_dsi_ctx *ctx)
+{
+	return 0;
+}
+
+static int panel_bpf_mipi_dsi_cfi_stub_disable(struct panel_bpf_mipi_dsi_ctx *ctx)
+{
+	return 0;
+}
+
+static struct drm_panel_dsi_bpf_ops panel_bpf_mipi_dsi_bpf_struct_ops_cfi_stubs = {
+	.panel_prepare		= panel_bpf_mipi_dsi_cfi_stub_prepare,
+	.panel_unprepare	= panel_bpf_mipi_dsi_cfi_stub_unprepare,
+	.panel_enable		= panel_bpf_mipi_dsi_cfi_stub_enable,
+	.panel_disable		= panel_bpf_mipi_dsi_cfi_stub_disable,
+};
+
+static struct bpf_struct_ops panel_bpf_mipi_dsi_bpf_struct_ops = {
+	.verifier_ops	= &panel_bpf_mipi_dsi_bpf_verifier_ops,
+	.init		= panel_bpf_mipi_dsi_bpf_init,
+	.init_member	= panel_bpf_mipi_dsi_bpf_init_member,
+	.reg		= panel_bpf_mipi_dsi_bpf_reg,
+	.unreg		= panel_bpf_mipi_dsi_bpf_unreg,
+	.name		= "drm_panel_dsi_bpf_ops",
+	.cfi_stubs	= &panel_bpf_mipi_dsi_bpf_struct_ops_cfi_stubs,
+	.owner		= THIS_MODULE,
+};
+
+int panel_bpf_mipi_dsi_register_struct_ops(void)
+{
+	return register_bpf_struct_ops(&panel_bpf_mipi_dsi_bpf_struct_ops,
+				       drm_panel_dsi_bpf_ops);
+}
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c
new file mode 100644
index 000000000000..0822494b0be1
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define CREATE_TRACE_POINTS
+#include "panel-bpf-mipi-dsi-trace.h"
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h
new file mode 100644
index 000000000000..5a64ba4509a5
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#if !defined(_PANEL_BPF_MIPI_DSI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _PANEL_BPF_MIPI_DSI_TRACE_H_
+
+#include <linux/tracepoint.h>
+#include <linux/types.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM panel_bpf
+#define TRACE_INCLUDE_FILE panel-bpf-mipi-dsi-trace
+
+TRACE_EVENT(panel_bpf_mipi_dsi_callback,
+	    TP_PROTO(const char *panel, const char *name),
+	    TP_ARGS(panel, name),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+	    ),
+	    TP_printk("panel=%s callback=%s",
+		      __get_str(panel), __get_str(name))
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_callback_done,
+	    TP_PROTO(const char *panel, const char *name, int ret),
+	    TP_ARGS(panel, name, ret),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+		__field(int, ret)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+		__entry->ret = ret;
+	    ),
+	    TP_printk("panel=%s callback=%s ret=%d",
+		      __get_str(panel), __get_str(name), __entry->ret)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_reg,
+	    TP_PROTO(const char *panel),
+	    TP_ARGS(panel),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+	    ),
+	    TP_printk("panel=%s", __get_str(panel))
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_unreg,
+	    TP_PROTO(const char *panel),
+	    TP_ARGS(panel),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+	    ),
+	    TP_printk("panel=%s", __get_str(panel))
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_dcs_write_and_wait,
+	    TP_PROTO(const char *panel, u8 cmd, const u8 *data, u32 len,
+		     int ret),
+	    TP_ARGS(panel, cmd, data, len, ret),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__field(u8, cmd)
+		__field(u32, len)
+		__field(int, ret)
+		__array(u8, data, 8)
+	    ),
+	    TP_fast_assign(
+		unsigned int n = min_t(u32, len, 8);
+
+		__assign_str(panel);
+		__entry->cmd = cmd;
+		__entry->len = len;
+		__entry->ret = ret;
+		memset(__entry->data, 0, 8);
+		if (n && data)
+			memcpy(__entry->data, data, n);
+	    ),
+	    TP_printk("panel=%s cmd=0x%02x len=%u data=%s ret=%d",
+		      __get_str(panel), __entry->cmd, __entry->len,
+		      __print_hex(__entry->data, min_t(u32, __entry->len, 8)),
+		      __entry->ret)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_generic_write_and_wait,
+	    TP_PROTO(const char *panel, const u8 *data, u32 len, int ret),
+	    TP_ARGS(panel, data, len, ret),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__field(u32, len)
+		__field(int, ret)
+		__array(u8, data, 8)
+	    ),
+	    TP_fast_assign(
+		unsigned int n = min_t(u32, len, 8);
+
+		__assign_str(panel);
+		__entry->len = len;
+		__entry->ret = ret;
+		memset(__entry->data, 0, 8);
+		if (n && data)
+			memcpy(__entry->data, data, n);
+	    ),
+	    TP_printk("panel=%s len=%u data=%s ret=%d",
+		      __get_str(panel), __entry->len,
+		      __print_hex(__entry->data, min_t(u32, __entry->len, 8)),
+		      __entry->ret)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_dcs_read,
+	    TP_PROTO(const char *panel, u8 cmd, u32 len),
+	    TP_ARGS(panel, cmd, len),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__field(u8, cmd)
+		__field(u32, len)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__entry->cmd = cmd;
+		__entry->len = len;
+	    ),
+	    TP_printk("panel=%s cmd=0x%02x len=%u",
+		      __get_str(panel), __entry->cmd, __entry->len)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_dcs_read_done,
+	    TP_PROTO(const char *panel, u8 cmd, u32 len, int ret),
+	    TP_ARGS(panel, cmd, len, ret),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__field(u8, cmd)
+		__field(u32, len)
+		__field(int, ret)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__entry->cmd = cmd;
+		__entry->len = len;
+		__entry->ret = ret;
+	    ),
+	    TP_printk("panel=%s cmd=0x%02x len=%u ret=%d",
+		      __get_str(panel), __entry->cmd, __entry->len,
+		      __entry->ret)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_gpio_cycle_and_wait,
+	    TP_PROTO(const char *panel, const char *name, u32 assert_ms, u32 wait_ms),
+	    TP_ARGS(panel, name, assert_ms, wait_ms),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+		__field(u32, assert_ms)
+		__field(u32, wait_ms)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+		__entry->assert_ms = assert_ms;
+		__entry->wait_ms = wait_ms;
+	    ),
+	    TP_printk("panel=%s gpio=%s assert_ms=%u wait_ms=%u",
+		      __get_str(panel), __get_str(name),
+		      __entry->assert_ms, __entry->wait_ms)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_gpio_enable,
+	    TP_PROTO(const char *panel, const char *name),
+	    TP_ARGS(panel, name),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+	    ),
+	    TP_printk("panel=%s gpio=%s",
+		      __get_str(panel), __get_str(name))
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_gpio_disable,
+	    TP_PROTO(const char *panel, const char *name),
+	    TP_ARGS(panel, name),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+	    ),
+	    TP_printk("panel=%s gpio=%s",
+		      __get_str(panel), __get_str(name))
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_regulator_enable_and_wait,
+	    TP_PROTO(const char *panel, const char *name, u32 wait_ms),
+	    TP_ARGS(panel, name, wait_ms),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+		__field(u32, wait_ms)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+		__entry->wait_ms = wait_ms;
+	    ),
+	    TP_printk("panel=%s supply=%s wait_ms=%u",
+		      __get_str(panel), __get_str(name),
+		      __entry->wait_ms)
+);
+
+TRACE_EVENT(panel_bpf_mipi_dsi_regulator_disable,
+	    TP_PROTO(const char *panel, const char *name),
+	    TP_ARGS(panel, name),
+	    TP_STRUCT__entry(
+		__string(panel, panel)
+		__string(name, name)
+	    ),
+	    TP_fast_assign(
+		__assign_str(panel);
+		__assign_str(name);
+	    ),
+	    TP_printk("panel=%s supply=%s",
+		      __get_str(panel), __get_str(name))
+);
+
+#endif /* _PANEL_BPF_MIPI_DSI_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/panel/bpf
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h
new file mode 100644
index 000000000000..5e51e2883fa4
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PANEL_BPF_MIPI_DSI_H__
+#define __PANEL_BPF_MIPI_DSI_H__
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/display_timing.h>
+
+struct mipi_dsi_device;
+
+#define PANEL_BPF_MIPI_DSI_ID_LEN		128
+#define PANEL_BPF_MIPI_DSI_COMPATIBLE_LEN	64
+
+enum panel_bpf_mipi_dsi_gpio {
+	PANEL_BPF_MIPI_DSI_GPIO_ENABLE,
+	PANEL_BPF_MIPI_DSI_GPIO_RESET,
+	PANEL_BPF_MIPI_DSI_GPIO_COUNT,
+};
+
+enum panel_bpf_mipi_dsi_supply {
+	PANEL_BPF_MIPI_DSI_SUPPLY_VCC,
+	PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC,
+	PANEL_BPF_MIPI_DSI_SUPPLY_AVDD,
+	PANEL_BPF_MIPI_DSI_SUPPLY_AVEE,
+	PANEL_BPF_MIPI_DSI_SUPPLY_ELVDD,
+	PANEL_BPF_MIPI_DSI_SUPPLY_ELVSS,
+	PANEL_BPF_MIPI_DSI_SUPPLY_COUNT,
+};
+
+/**
+ * struct panel_bpf_mipi_dsi - Per-panel instance state
+ */
+struct panel_bpf_mipi_dsi {
+	/**
+	 * @bridge:
+	 *
+	 * The drm_bridge registered with DRM
+	 */
+	struct drm_bridge		bridge;
+
+	/**
+	 * @dsi:
+	 *
+	 * Our parent MIPI-DSI device
+	 */
+	struct mipi_dsi_device		*dsi;
+
+	/**
+	 * @panel_id:
+	 *
+	 * Full OF node path, used for BPF matching and tracing
+	 */
+	char				panel_id[PANEL_BPF_MIPI_DSI_ID_LEN];
+
+	/**
+	 * @gpios:
+	 *
+	 * Optional GPIOs indexed by &enum panel_bpf_mipi_dsi_gpio
+	 */
+	struct gpio_desc		*gpios[PANEL_BPF_MIPI_DSI_GPIO_COUNT];
+
+	/**
+	 * @supplies:
+	 *
+	 * Regulators indexed by &enum panel_bpf_mipi_dsi_supply
+	 */
+	struct regulator_bulk_data	supplies[PANEL_BPF_MIPI_DSI_SUPPLY_COUNT];
+
+	/**
+	 * @backlight:
+	 *
+	 * Optional backlight device
+	 */
+	struct backlight_device		*backlight;
+
+	/**
+	 * @dt:
+	 *
+	 * Display timing parsed from the panel-timing node
+	 */
+	struct display_timing		dt;
+
+	/**
+	 * @width_mm:
+	 *
+	 * Physical panel width in millimeters
+	 */
+	u32				width_mm;
+
+	/**
+	 * @height_mm:
+	 *
+	 * Physical panel height in millimeters
+	 */
+	u32				height_mm;
+
+	/**
+	 * @orientation:
+	 *
+	 * Panel orientation
+	 */
+	enum drm_panel_orientation	orientation;
+
+	/**
+	 * @bpf_ops:
+	 *
+	 * Currently attached BPF struct_ops, or NULL if none attached.
+	 * Protected by @bpf_lock.
+	 */
+	struct drm_panel_dsi_bpf_ops	*bpf_ops;
+
+	/**
+	 * @bpf_lock: Protects @bpf_ops against concurrent attach/detach/use
+	 */
+	struct mutex			bpf_lock;
+};
+
+#define drm_bridge_to_bpf_panel(b)					\
+	container_of_const(b, struct panel_bpf_mipi_dsi, bridge)
+
+int panel_bpf_mipi_dsi_list_add(struct panel_bpf_mipi_dsi *panel);
+struct panel_bpf_mipi_dsi *panel_bpf_mipi_dsi_find_panel_unlocked(const char *panel_id);
+
+extern struct mutex panel_bpf_mipi_dsi_list_lock;
+
+/**
+ * struct panel_bpf_mipi_dsi_ctx - Context passed to BPF panel programs
+ */
+struct panel_bpf_mipi_dsi_ctx {
+	/**
+	 * @bridge:
+	 *
+	 * The drm_bridge this callback operates on (private)
+	 */
+	struct drm_bridge *bridge;
+};
+
+#define bpf_ctx_to_bpf_panel(c)			\
+	drm_bridge_to_bpf_panel((c)->bridge)
+
+/**
+ * struct drm_panel_dsi_bpf_ops - BPF struct_ops for MIPI-DSI panels
+ */
+struct drm_panel_dsi_bpf_ops {
+	/**
+	 * @bridge:
+	 *
+	 * TODO.
+	 *
+	 * Private, used for internal book-keeping.
+	 */
+	struct drm_bridge	*bridge;
+
+	/**
+	 * @panel_id:
+	 *
+	 * Target panel OF node path for instance matching. Set by the
+	 * userspace loader before registration. Immutable after.
+	 */
+	char			panel_id[PANEL_BPF_MIPI_DSI_ID_LEN];
+
+	/**
+	 * @compatible:
+	 *
+	 * DT compatible string this program supports. Set at compile
+	 * time. The kernel does not use this for matching but is
+	 * metadata for the userspace loader, which uses it to decide
+	 * which file to load for a given panel's compatible list.
+	 */
+	char			compatible[PANEL_BPF_MIPI_DSI_COMPATIBLE_LEN];
+
+	/**
+	 * @format:
+	 *
+	 * MIPI DSI pixel format for the panel link.
+	 */
+	enum mipi_dsi_pixel_format format;
+
+	/**
+	 * @lanes:
+	 *
+	 * Number of DSI data lanes.
+	 */
+	u32			lanes;
+
+	/**
+	 * @mode_flags:
+	 *
+	 * DSI mode flags (MIPI_DSI_MODE_VIDEO, etc).
+	 */
+	unsigned long		mode_flags;
+
+	/**
+	 * @hs_rate:
+	 *
+	 * Maximum lane frequency for high speed mode in hertz.
+	 */
+	unsigned long		hs_rate;
+
+	/**
+	 * @lp_rate:
+	 *
+	 * Maximum lane frequency for low power mode in hertz.
+	 */
+	unsigned long		lp_rate;
+
+	/**
+	 * @panel_prepare:
+	 *
+	 * Optional. Called after the DSI host has been pre-enabled. The
+	 * DSI link is up in LP (Low Power) mode, so DSI commands can be
+	 * sent via the dcs_write and generic_write kfuncs.
+	 *
+	 * This is where BPF programs should enable regulators, cycle
+	 * the reset GPIO, and send the panel init sequence. Most panels
+	 * only need this callback. Sleepable.
+	 */
+	int (*panel_prepare)(struct panel_bpf_mipi_dsi_ctx *ctx);
+
+	/**
+	 * @panel_enable:
+	 *
+	 * Optional. Called after the DSI host has switched the link to
+	 * HS (High Speed) mode and started the video stream. For panels
+	 * that need DSI commands sent after the video stream is active.
+	 * Sleepable.
+	 */
+	int (*panel_enable)(struct panel_bpf_mipi_dsi_ctx *ctx);
+
+	/**
+	 * @panel_disable:
+	 *
+	 * Optional. Called while the DSI link is still active in HS
+	 * mode and the video stream is still running. For panels that
+	 * need DSI commands sent before the video stream stops.
+	 * Sleepable.
+	 */
+	int (*panel_disable)(struct panel_bpf_mipi_dsi_ctx *ctx);
+
+	/**
+	 * @panel_unprepare:
+	 *
+	 * Optional. Called after the DSI host has stopped the video
+	 * stream and torn down the HS link. The link may still be in LP
+	 * mode at this point, allowing shutdown commands (display off,
+	 * enter sleep) to be sent.
+	 *
+	 * This is where BPF programs should send shutdown commands,
+	 * assert reset, and disable regulators. Sleepable.
+	 */
+	int (*panel_unprepare)(struct panel_bpf_mipi_dsi_ctx *ctx);
+};
+
+int panel_bpf_mipi_dsi_register_kfuncs(void);
+int panel_bpf_mipi_dsi_register_struct_ops(void);
+
+#endif /* __PANEL_BPF_MIPI_DSI_H__ */
diff --git a/include/drm/drm_panel_dsi_bpf.h b/include/drm/drm_panel_dsi_bpf.h
new file mode 100644
index 000000000000..02d4707fd0a0
--- /dev/null
+++ b/include/drm/drm_panel_dsi_bpf.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DRM_PANEL_DSI_BPF_H__
+#define __DRM_PANEL_DSI_BPF_H__
+
+#include <linux/bpf.h>
+
+struct mipi_dsi_device;
+struct drm_panel;
+
+#define DSI_BPF_PANEL_ID_LEN	64
+
+/**
+ * struct dsi_bpf_ctx - Context passed to BPF panel programs
+ * @panel: The drm_panel this callback operates on (private)
+ */
+struct dsi_bpf_ctx {
+	struct drm_panel *panel;
+};
+
+/**
+ * struct drm_panel_dsi_bpf_ops - BPF struct_ops for MIPI-DSI panels
+ * @panel_id: Device identifier for matching. On DT systems this holds
+ *	the panel's compatible string. Firmware-agnostic to allow future
+ *	ACPI support. Written before load, immutable after.
+ * @panel_prepare: Called to power on the panel and send init commands.
+ *	Must enable regulators, toggle GPIOs, and send the DSI init
+ *	sequence. Sleepable.
+ * @panel_unprepare: Called to power off the panel. Must send shutdown
+ *	commands, assert reset, and disable regulators. Sleepable.
+ * @panel_enable: Optional. Called after video stream starts, for panels
+ *	that need post-video-start DSI commands. Sleepable.
+ * @panel_disable: Optional. Called before video stream stops. Sleepable.
+ * @set_brightness: Optional. Called to set backlight brightness via DSI
+ *	commands. Sleepable.
+ */
+struct drm_panel_dsi_bpf_ops {
+	char			panel_id[DSI_BPF_PANEL_ID_LEN];
+
+	/* private: internal bookkeeping */
+	struct drm_panel	*panel;
+
+	/* public: */
+	int (*panel_prepare)(struct dsi_bpf_ctx *ctx);
+	int (*panel_unprepare)(struct dsi_bpf_ctx *ctx);
+	int (*panel_enable)(struct dsi_bpf_ctx *ctx);
+	int (*panel_disable)(struct dsi_bpf_ctx *ctx);
+	int (*set_brightness)(struct dsi_bpf_ctx *ctx, u32 brightness);
+};
+
+#endif /* __DRM_PANEL_DSI_BPF_H__ */

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 3/6] drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
  2026-09-28 16:22 ` [PATCH 2/6] drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 16:22 ` [PATCH 4/6] drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program Maxime Ripard
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Add the BPF-side header panel-bpf-mipi-dsi.h and a standalone
Makefile for building panel BPF programs. The header provides
section name macros (PANEL_BPF_MIPI_DSI_PREPARE, etc.), the
PANEL_BPF_MIPI_DSI_OPS() struct_ops declaration macro, extern
declarations for all the kfuncs, and some convenience macros.

The Makefile follows the same pattern as drivers/hid/bpf/progs/.
It generates vmlinux.h from BTF, bootstraps bpftool and libbpf
from the kernel tools/ directory, then compiles every *.bpf.c
file into a stripped .bpf.o object. The resulting objects are
userspace artifacts loaded at runtime.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 drivers/gpu/drm/panel/bpf/progs/Makefile           | 93 +++++++++++++++++++++
 .../gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h   | 95 ++++++++++++++++++++++
 include/drm/drm_panel_dsi_bpf.h                    | 50 ------------
 3 files changed, 188 insertions(+), 50 deletions(-)

diff --git a/drivers/gpu/drm/panel/bpf/progs/Makefile b/drivers/gpu/drm/panel/bpf/progs/Makefile
new file mode 100644
index 000000000000..74190ee618ab
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/progs/Makefile
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: GPL-2.0
+OUTPUT := .output
+abs_out := $(abspath $(OUTPUT))
+
+CLANG ?= clang
+LLC ?= llc
+LLVM_STRIP ?= llvm-strip
+
+TOOLS_PATH := $(abspath ../../../../../../tools)
+BPFTOOL_SRC := $(TOOLS_PATH)/bpf/bpftool
+BPFTOOL_OUTPUT := $(abs_out)/bpftool
+DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)/bootstrap/bpftool
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+
+LIBBPF_SRC := $(TOOLS_PATH)/lib/bpf
+LIBBPF_OUTPUT := $(abs_out)/libbpf
+LIBBPF_DESTDIR := $(LIBBPF_OUTPUT)
+LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)/include
+BPFOBJ := $(LIBBPF_OUTPUT)/libbpf.a
+
+INCLUDES := -I$(OUTPUT) -I$(LIBBPF_INCLUDE) -I$(TOOLS_PATH)/include/uapi
+CFLAGS := -g -Wall
+
+VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)				\
+		     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)	\
+		     ../../../../../../vmlinux				\
+		     /sys/kernel/btf/vmlinux				\
+		     /boot/vmlinux-$(shell uname -r)
+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+ifeq ($(VMLINUX_BTF),)
+$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
+endif
+
+ifeq ($(V),1)
+Q =
+msg =
+else
+Q = @
+msg = @printf '  %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
+MAKEFLAGS += --no-print-directory
+submake_extras := feature_display=0
+endif
+
+.DELETE_ON_ERROR:
+
+.PHONY: all clean
+
+SOURCES = $(wildcard *.bpf.c)
+TARGETS = $(SOURCES:.bpf.c=.bpf.o)
+
+all: $(TARGETS)
+
+clean:
+	$(call msg,CLEAN)
+	$(Q)rm -rf $(OUTPUT) $(TARGETS)
+
+%.bpf.o: %.bpf.c vmlinux.h $(BPFOBJ) | $(OUTPUT)
+	$(call msg,BPF,$@)
+	$(Q)$(CLANG) -g -O2 --target=bpf -Wall -Werror $(INCLUDES)		\
+		     -Wno-microsoft-anon-tag					\
+		     -fms-extensions						\
+		     -c $(filter %.c,$^) -o $@ &&				\
+	$(LLVM_STRIP) -g $@
+
+vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
+ifeq ($(VMLINUX_H),)
+	$(call msg,GEN,,$@)
+	$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
+else
+	$(call msg,CP,,$@)
+	$(Q)cp "$(VMLINUX_H)" $@
+endif
+
+$(OUTPUT) $(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT):
+	$(call msg,MKDIR,$@)
+	$(Q)mkdir -p $@
+
+$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUTPUT)
+	$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC)			       \
+		    OUTPUT=$(abspath $(dir $@))/ prefix=		       \
+		    DESTDIR=$(LIBBPF_DESTDIR) $(abspath $@) install_headers
+
+ifeq ($(CROSS_COMPILE),)
+$(DEFAULT_BPFTOOL): $(BPFOBJ) | $(BPFTOOL_OUTPUT)
+	$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC)			       \
+		    OUTPUT=$(BPFTOOL_OUTPUT)/				       \
+		    LIBBPF_BOOTSTRAP_OUTPUT=$(LIBBPF_OUTPUT)/		       \
+		    LIBBPF_BOOTSTRAP_DESTDIR=$(LIBBPF_DESTDIR)/ bootstrap
+else
+$(DEFAULT_BPFTOOL): | $(BPFTOOL_OUTPUT)
+	$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC)			       \
+		    OUTPUT=$(BPFTOOL_OUTPUT)/ bootstrap
+endif
diff --git a/drivers/gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h b/drivers/gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h
new file mode 100644
index 000000000000..e2b03afef2c8
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef ____PANEL_BPF_MIPI_DSI_BPF__H
+#define ____PANEL_BPF_MIPI_DSI_BPF__H
+
+#define PANEL_BPF_MIPI_DSI_PREPARE	"struct_ops.s/panel_prepare"
+#define PANEL_BPF_MIPI_DSI_UNPREPARE	"struct_ops.s/panel_unprepare"
+#define PANEL_BPF_MIPI_DSI_ENABLE	"struct_ops.s/panel_enable"
+#define PANEL_BPF_MIPI_DSI_DISABLE	"struct_ops.s/panel_disable"
+
+#define PANEL_BPF_MIPI_DSI_OPS(name) SEC(".struct_ops") \
+	struct drm_panel_dsi_bpf_ops name
+
+/* Mode flags — kernel #defines not exported through BTF */
+#define MIPI_DSI_MODE_VIDEO		(1 << 0)
+#define MIPI_DSI_MODE_VIDEO_BURST	(1 << 1)
+#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
+#define MIPI_DSI_MODE_NO_EOT_PACKET	(1 << 9)
+#define MIPI_DSI_CLOCK_NON_CONTINUOUS	(1 << 10)
+#define MIPI_DSI_MODE_LPM		(1 << 11)
+
+extern int panel_bpf_mipi_dsi_regulator_enable_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+							enum panel_bpf_mipi_dsi_supply supply,
+							__u32 settle_ms) __ksym;
+extern int panel_bpf_mipi_dsi_regulator_disable(struct panel_bpf_mipi_dsi_ctx *ctx,
+						enum panel_bpf_mipi_dsi_supply supply) __ksym;
+extern void panel_bpf_mipi_dsi_gpio_cycle_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+						   enum panel_bpf_mipi_dsi_gpio gpio,
+						   __u32 assert_ms,
+						   __u32 settle_ms) __ksym;
+extern void panel_bpf_mipi_dsi_gpio_enable(struct panel_bpf_mipi_dsi_ctx *ctx,
+					   enum panel_bpf_mipi_dsi_gpio gpio) __ksym;
+extern void panel_bpf_mipi_dsi_gpio_disable(struct panel_bpf_mipi_dsi_ctx *ctx,
+					   enum panel_bpf_mipi_dsi_gpio gpio) __ksym;
+extern int panel_bpf_mipi_dsi_dcs_write_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+						  __u8 cmd, const __u8 *data,
+						  __u32 data__sz,
+						  __u32 settle_ms) __ksym;
+extern int panel_bpf_mipi_dsi_generic_write_and_wait(struct panel_bpf_mipi_dsi_ctx *ctx,
+						     const __u8 *data,
+						     __u32 data__sz,
+						     __u32 settle_ms) __ksym;
+extern int panel_bpf_mipi_dsi_dcs_read(struct panel_bpf_mipi_dsi_ctx *ctx,
+				       __u8 cmd, __u8 *data,
+				       __u32 data__sz) __ksym;
+
+/*
+ * Convenience macros
+ *
+ * These wrap the _and_wait kfuncs with settle_ms = 0 for the common
+ * case where no post-operation delay is needed.
+ */
+
+/* Enable a supply without a post-enable delay. */
+#define panel_bpf_mipi_dsi_regulator_enable(ctx, supply)			\
+	panel_bpf_mipi_dsi_regulator_enable_and_wait((ctx), (supply), 0)
+
+/* Send a DCS command without a post-send delay. */
+#define panel_bpf_mipi_dsi_dcs_write(ctx, cmd, data, data__sz)		\
+	panel_bpf_mipi_dsi_dcs_write_and_wait((ctx), (cmd), (data), (data__sz), 0)
+
+/* Send a generic DSI write without a post-send delay. */
+#define panel_bpf_mipi_dsi_generic_write(ctx, data, data__sz)		\
+	panel_bpf_mipi_dsi_generic_write_and_wait((ctx), (data), (data__sz), 0)
+
+/* Send a DCS command with a single byte payload. */
+#define panel_bpf_mipi_dsi_dcs_write_byte(ctx, cmd, val)		\
+	do {								\
+		const __u8 _v = (val);					\
+		panel_bpf_mipi_dsi_dcs_write((ctx), (cmd), &_v, 1);	\
+	} while (0)
+
+/*
+ * Standard DCS command helpers
+ *
+ * exit_sleep_mode and enter_sleep_mode include the 120ms delay
+ * required by the MIPI DCS specification before the next command.
+ */
+
+/* Send MIPI_DCS_EXIT_SLEEP_MODE with a 120ms settling delay. */
+#define panel_bpf_mipi_dsi_exit_sleep_mode(ctx)				\
+	panel_bpf_mipi_dsi_dcs_write_and_wait((ctx), MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0, 120)
+
+/* Send MIPI_DCS_ENTER_SLEEP_MODE with a 120ms settling delay. */
+#define panel_bpf_mipi_dsi_enter_sleep_mode(ctx)			\
+	panel_bpf_mipi_dsi_dcs_write_and_wait((ctx), MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0, 120)
+
+/* Send MIPI_DCS_SET_DISPLAY_ON. */
+#define panel_bpf_mipi_dsi_set_display_on(ctx)				\
+	panel_bpf_mipi_dsi_dcs_write((ctx), MIPI_DCS_SET_DISPLAY_ON, NULL, 0)
+
+/* Send MIPI_DCS_SET_DISPLAY_OFF. */
+#define panel_bpf_mipi_dsi_set_display_off(ctx)				\
+	panel_bpf_mipi_dsi_dcs_write((ctx), MIPI_DCS_SET_DISPLAY_OFF, NULL, 0)
+
+#endif /* ____PANEL_BPF_MIPI_DSI_BPF__H */
diff --git a/include/drm/drm_panel_dsi_bpf.h b/include/drm/drm_panel_dsi_bpf.h
deleted file mode 100644
index 02d4707fd0a0..000000000000
--- a/include/drm/drm_panel_dsi_bpf.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __DRM_PANEL_DSI_BPF_H__
-#define __DRM_PANEL_DSI_BPF_H__
-
-#include <linux/bpf.h>
-
-struct mipi_dsi_device;
-struct drm_panel;
-
-#define DSI_BPF_PANEL_ID_LEN	64
-
-/**
- * struct dsi_bpf_ctx - Context passed to BPF panel programs
- * @panel: The drm_panel this callback operates on (private)
- */
-struct dsi_bpf_ctx {
-	struct drm_panel *panel;
-};
-
-/**
- * struct drm_panel_dsi_bpf_ops - BPF struct_ops for MIPI-DSI panels
- * @panel_id: Device identifier for matching. On DT systems this holds
- *	the panel's compatible string. Firmware-agnostic to allow future
- *	ACPI support. Written before load, immutable after.
- * @panel_prepare: Called to power on the panel and send init commands.
- *	Must enable regulators, toggle GPIOs, and send the DSI init
- *	sequence. Sleepable.
- * @panel_unprepare: Called to power off the panel. Must send shutdown
- *	commands, assert reset, and disable regulators. Sleepable.
- * @panel_enable: Optional. Called after video stream starts, for panels
- *	that need post-video-start DSI commands. Sleepable.
- * @panel_disable: Optional. Called before video stream stops. Sleepable.
- * @set_brightness: Optional. Called to set backlight brightness via DSI
- *	commands. Sleepable.
- */
-struct drm_panel_dsi_bpf_ops {
-	char			panel_id[DSI_BPF_PANEL_ID_LEN];
-
-	/* private: internal bookkeeping */
-	struct drm_panel	*panel;
-
-	/* public: */
-	int (*panel_prepare)(struct dsi_bpf_ctx *ctx);
-	int (*panel_unprepare)(struct dsi_bpf_ctx *ctx);
-	int (*panel_enable)(struct dsi_bpf_ctx *ctx);
-	int (*panel_disable)(struct dsi_bpf_ctx *ctx);
-	int (*set_brightness)(struct dsi_bpf_ctx *ctx, u32 brightness);
-};
-
-#endif /* __DRM_PANEL_DSI_BPF_H__ */

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 4/6] drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (2 preceding siblings ...)
  2026-09-28 16:22 ` [PATCH 3/6] drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 16:22 ` [PATCH 5/6] drm/panel: dsi-bpf: Add Raspberry Pi 5-inch " Maxime Ripard
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Translate the raspberrypi,dsi-7inch initialization sequence from
drivers/gpu/drm/panel/panel-ilitek-ili9881c.c into a BPF program.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 .../panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c   | 266 +++++++++++++++++++++
 1 file changed, 266 insertions(+)

diff --git a/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c b/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c
new file mode 100644
index 000000000000..1f9ff7f2fd3c
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPF panel program for Raspberry Pi 5-inch MIPI-DSI panel (ILI9881C)
+ *
+ * Translated from drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+ * (rpi_5inch_init[] / rpi_5inch_desc)
+ */
+
+#include "vmlinux.h"
+#include "panel-bpf-mipi-dsi.h"
+#include <bpf/bpf_tracing.h>
+
+#define PAGE(p) do {						\
+	const __u8 _d[] = { 0x98, 0x81, (p) };			\
+	panel_bpf_mipi_dsi_dcs_write(pctx, 0xff, _d, sizeof(_d));\
+} while (0)
+
+#define CMD(c, d) panel_bpf_mipi_dsi_dcs_write_byte(pctx, (c), (d))
+
+SEC(PANEL_BPF_MIPI_DSI_PREPARE)
+int BPF_PROG(panel_prepare, struct panel_bpf_mipi_dsi_ctx *pctx)
+{
+	int ret;
+
+	ret = panel_bpf_mipi_dsi_regulator_enable_and_wait(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC, 5);
+	if (ret)
+		return ret;
+
+	ret = panel_bpf_mipi_dsi_regulator_enable_and_wait(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_VCC, 5);
+	if (ret)
+		return ret;
+
+	panel_bpf_mipi_dsi_gpio_cycle_and_wait(pctx, PANEL_BPF_MIPI_DSI_GPIO_RESET, 20, 20);
+
+	/* Page 3 */
+	PAGE(3);
+	CMD(0x01, 0x00);
+	CMD(0x02, 0x00);
+	CMD(0x03, 0x73);
+	CMD(0x04, 0x73);
+	CMD(0x05, 0x00);
+	CMD(0x06, 0x06);
+	CMD(0x07, 0x02);
+	CMD(0x08, 0x00);
+	CMD(0x09, 0x01);
+	CMD(0x0a, 0x01);
+	CMD(0x0b, 0x01);
+	CMD(0x0c, 0x01);
+	CMD(0x0d, 0x01);
+	CMD(0x0e, 0x01);
+	CMD(0x0f, 0x01);
+	CMD(0x10, 0x01);
+	CMD(0x11, 0x00);
+	CMD(0x12, 0x00);
+	CMD(0x13, 0x01);
+	CMD(0x14, 0x00);
+	CMD(0x15, 0x00);
+	CMD(0x16, 0x00);
+	CMD(0x17, 0x00);
+	CMD(0x18, 0x00);
+	CMD(0x19, 0x00);
+	CMD(0x1a, 0x00);
+	CMD(0x1b, 0x00);
+	CMD(0x1c, 0x00);
+	CMD(0x1d, 0x00);
+	CMD(0x1e, 0xc0);
+	CMD(0x1f, 0x80);
+	CMD(0x20, 0x04);
+	CMD(0x21, 0x03);
+	CMD(0x22, 0x00);
+	CMD(0x23, 0x00);
+	CMD(0x24, 0x00);
+	CMD(0x25, 0x00);
+	CMD(0x26, 0x00);
+	CMD(0x27, 0x00);
+	CMD(0x28, 0x33);
+	CMD(0x29, 0x03);
+	CMD(0x2a, 0x00);
+	CMD(0x2b, 0x00);
+	CMD(0x2c, 0x00);
+	CMD(0x2d, 0x00);
+	CMD(0x2e, 0x00);
+	CMD(0x2f, 0x00);
+	CMD(0x30, 0x00);
+	CMD(0x31, 0x00);
+	CMD(0x32, 0x00);
+	CMD(0x33, 0x00);
+	CMD(0x34, 0x03);
+	CMD(0x35, 0x00);
+	CMD(0x36, 0x03);
+	CMD(0x37, 0x00);
+	CMD(0x38, 0x00);
+	CMD(0x39, 0x00);
+	CMD(0x3a, 0x00);
+	CMD(0x3b, 0x00);
+	CMD(0x3c, 0x00);
+	CMD(0x3d, 0x00);
+	CMD(0x3e, 0x00);
+	CMD(0x3f, 0x00);
+	CMD(0x40, 0x00);
+	CMD(0x41, 0x00);
+	CMD(0x42, 0x00);
+	CMD(0x43, 0x00);
+	CMD(0x44, 0x00);
+	CMD(0x50, 0x01);
+	CMD(0x51, 0x23);
+	CMD(0x52, 0x45);
+	CMD(0x53, 0x67);
+	CMD(0x54, 0x89);
+	CMD(0x55, 0xab);
+	CMD(0x56, 0x01);
+	CMD(0x57, 0x23);
+	CMD(0x58, 0x45);
+	CMD(0x59, 0x67);
+	CMD(0x5a, 0x89);
+	CMD(0x5b, 0xab);
+	CMD(0x5c, 0xcd);
+	CMD(0x5d, 0xef);
+	CMD(0x5e, 0x10);
+	CMD(0x5f, 0x09);
+	CMD(0x60, 0x08);
+	CMD(0x61, 0x0f);
+	CMD(0x62, 0x0e);
+	CMD(0x63, 0x0d);
+	CMD(0x64, 0x0c);
+	CMD(0x65, 0x02);
+	CMD(0x66, 0x02);
+	CMD(0x67, 0x02);
+	CMD(0x68, 0x02);
+	CMD(0x69, 0x02);
+	CMD(0x6a, 0x02);
+	CMD(0x6b, 0x02);
+	CMD(0x6c, 0x02);
+	CMD(0x6d, 0x02);
+	CMD(0x6e, 0x02);
+	CMD(0x6f, 0x02);
+	CMD(0x70, 0x02);
+	CMD(0x71, 0x06);
+	CMD(0x72, 0x07);
+	CMD(0x73, 0x02);
+	CMD(0x74, 0x02);
+	CMD(0x75, 0x06);
+	CMD(0x76, 0x07);
+	CMD(0x77, 0x0e);
+	CMD(0x78, 0x0f);
+	CMD(0x79, 0x0c);
+	CMD(0x7a, 0x0d);
+	CMD(0x7b, 0x02);
+	CMD(0x7c, 0x02);
+	CMD(0x7d, 0x02);
+	CMD(0x7e, 0x02);
+	CMD(0x7f, 0x02);
+	CMD(0x80, 0x02);
+	CMD(0x81, 0x02);
+	CMD(0x82, 0x02);
+	CMD(0x83, 0x02);
+	CMD(0x84, 0x02);
+	CMD(0x85, 0x02);
+	CMD(0x86, 0x02);
+	CMD(0x87, 0x09);
+	CMD(0x88, 0x08);
+	CMD(0x89, 0x02);
+	CMD(0x8a, 0x02);
+
+	/* Page 4 */
+	PAGE(4);
+	CMD(0x6c, 0x15);
+	CMD(0x6e, 0x2a);
+	CMD(0x6f, 0x57);
+	CMD(0x3a, 0xa4);
+	CMD(0x8d, 0x1a);
+	CMD(0x87, 0xba);
+	CMD(0x26, 0x76);
+	CMD(0xb2, 0xd1);
+
+	/* Page 1 */
+	PAGE(1);
+	CMD(0x22, 0x0a);
+	CMD(0x31, 0x00);
+	CMD(0x53, 0x35);
+	CMD(0x55, 0x50);
+	CMD(0x50, 0xaf);
+	CMD(0x51, 0xaf);
+	CMD(0x60, 0x14);
+	CMD(0xa0, 0x08);
+	CMD(0xa1, 0x1d);
+	CMD(0xa2, 0x2c);
+	CMD(0xa3, 0x14);
+	CMD(0xa4, 0x19);
+	CMD(0xa5, 0x2e);
+	CMD(0xa6, 0x22);
+	CMD(0xa7, 0x23);
+	CMD(0xa8, 0x97);
+	CMD(0xa9, 0x1e);
+	CMD(0xaa, 0x29);
+	CMD(0xab, 0x7b);
+	CMD(0xac, 0x18);
+	CMD(0xad, 0x17);
+	CMD(0xae, 0x4b);
+	CMD(0xaf, 0x1f);
+	CMD(0xb0, 0x27);
+	CMD(0xb1, 0x52);
+	CMD(0xb2, 0x63);
+	CMD(0xb3, 0x39);
+	CMD(0xc0, 0x08);
+	CMD(0xc1, 0x1d);
+	CMD(0xc2, 0x2c);
+	CMD(0xc3, 0x14);
+	CMD(0xc4, 0x19);
+	CMD(0xc5, 0x2e);
+	CMD(0xc6, 0x22);
+	CMD(0xc7, 0x23);
+	CMD(0xc8, 0x97);
+	CMD(0xc9, 0x1e);
+	CMD(0xca, 0x29);
+	CMD(0xcb, 0x7b);
+	CMD(0xcc, 0x18);
+	CMD(0xcd, 0x17);
+	CMD(0xce, 0x4b);
+	CMD(0xcf, 0x1f);
+	CMD(0xd0, 0x27);
+	CMD(0xd1, 0x52);
+	CMD(0xd2, 0x63);
+	CMD(0xd3, 0x39);
+
+	/* Switch to page 0 and finalize */
+	PAGE(0);
+
+	/* set_tear_on with VBLANK mode (0x00) */
+	CMD(0x35, 0x00);
+
+	ret = panel_bpf_mipi_dsi_exit_sleep_mode(pctx);
+	if (ret)
+		return ret;
+
+	ret = panel_bpf_mipi_dsi_set_display_on(pctx);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+SEC(PANEL_BPF_MIPI_DSI_UNPREPARE)
+int BPF_PROG(panel_unprepare, struct panel_bpf_mipi_dsi_ctx *pctx)
+{
+	panel_bpf_mipi_dsi_set_display_off(pctx);
+	panel_bpf_mipi_dsi_enter_sleep_mode(pctx);
+
+	panel_bpf_mipi_dsi_regulator_disable(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_VCC);
+	panel_bpf_mipi_dsi_regulator_disable(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC);
+	panel_bpf_mipi_dsi_gpio_enable(pctx, PANEL_BPF_MIPI_DSI_GPIO_RESET);
+
+	return 0;
+}
+
+PANEL_BPF_MIPI_DSI_OPS(raspberrypi_dsi_5inch) = {
+	.panel_id		= "/soc/dsi@7e700000/panel@0",
+	.compatible		= "raspberrypi,dsi-5inch",
+	.format			= MIPI_DSI_FMT_RGB888,
+	.lanes			= 2,
+	.mode_flags		= MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+	.panel_prepare		= (void *)panel_prepare,
+	.panel_unprepare	= (void *)panel_unprepare,
+};
+
+char _license[] SEC("license") = "GPL";

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 5/6] drm/panel: dsi-bpf: Add Raspberry Pi 5-inch panel BPF program
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (3 preceding siblings ...)
  2026-09-28 16:22 ` [PATCH 4/6] drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 16:22 ` [PATCH DO NOT MERGE 6/6] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays Maxime Ripard
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Translate the raspberrypi,dsi-5inch initialization sequence from
drivers/gpu/drm/panel/panel-ilitek-ili9881c.c into a BPF program.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 .../panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c   | 271 +++++++++++++++++++++
 1 file changed, 271 insertions(+)

diff --git a/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c b/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c
new file mode 100644
index 000000000000..2055828de2f4
--- /dev/null
+++ b/drivers/gpu/drm/panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPF panel program for Raspberry Pi 7-inch MIPI-DSI panel (ILI9881C)
+ *
+ * Translated from drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+ * (rpi_7inch_init[] / rpi_7inch_desc)
+ */
+
+#include "vmlinux.h"
+#include "panel-bpf-mipi-dsi.h"
+#include <bpf/bpf_tracing.h>
+
+#define PAGE(p) do {						\
+	const __u8 _d[] = { 0x98, 0x81, (p) };			\
+	panel_bpf_mipi_dsi_dcs_write(pctx, 0xff, _d, sizeof(_d));\
+} while (0)
+
+#define CMD(c, d) panel_bpf_mipi_dsi_dcs_write_byte(pctx, (c), (d))
+
+SEC(PANEL_BPF_MIPI_DSI_PREPARE)
+int BPF_PROG(panel_prepare, struct panel_bpf_mipi_dsi_ctx *pctx)
+{
+	int ret;
+
+	ret = panel_bpf_mipi_dsi_regulator_enable_and_wait(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC, 5);
+	if (ret)
+		return ret;
+
+	ret = panel_bpf_mipi_dsi_regulator_enable_and_wait(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_VCC, 5);
+	if (ret)
+		return ret;
+
+	panel_bpf_mipi_dsi_gpio_cycle_and_wait(pctx, PANEL_BPF_MIPI_DSI_GPIO_RESET, 20, 20);
+
+	/* Page 3 */
+	PAGE(3);
+	CMD(0x01, 0x00);
+	CMD(0x02, 0x00);
+	CMD(0x03, 0x73);
+	CMD(0x04, 0x00);
+	CMD(0x05, 0x00);
+	CMD(0x06, 0x0a);
+	CMD(0x07, 0x00);
+	CMD(0x08, 0x00);
+	CMD(0x09, 0x61);
+	CMD(0x0a, 0x00);
+	CMD(0x0b, 0x00);
+	CMD(0x0c, 0x01);
+	CMD(0x0d, 0x00);
+	CMD(0x0e, 0x00);
+	CMD(0x0f, 0x61);
+	CMD(0x10, 0x61);
+	CMD(0x11, 0x00);
+	CMD(0x12, 0x00);
+	CMD(0x13, 0x00);
+	CMD(0x14, 0x00);
+	CMD(0x15, 0x00);
+	CMD(0x16, 0x00);
+	CMD(0x17, 0x00);
+	CMD(0x18, 0x00);
+	CMD(0x19, 0x00);
+	CMD(0x1a, 0x00);
+	CMD(0x1b, 0x00);
+	CMD(0x1c, 0x00);
+	CMD(0x1d, 0x00);
+	CMD(0x1e, 0x40);
+	CMD(0x1f, 0x80);
+	CMD(0x20, 0x06);
+	CMD(0x21, 0x01);
+	CMD(0x22, 0x00);
+	CMD(0x23, 0x00);
+	CMD(0x24, 0x00);
+	CMD(0x25, 0x00);
+	CMD(0x26, 0x00);
+	CMD(0x27, 0x00);
+	CMD(0x28, 0x33);
+	CMD(0x29, 0x03);
+	CMD(0x2a, 0x00);
+	CMD(0x2b, 0x00);
+	CMD(0x2c, 0x00);
+	CMD(0x2d, 0x00);
+	CMD(0x2e, 0x00);
+	CMD(0x2f, 0x00);
+	CMD(0x30, 0x00);
+	CMD(0x31, 0x00);
+	CMD(0x32, 0x00);
+	CMD(0x33, 0x00);
+	CMD(0x34, 0x04);
+	CMD(0x35, 0x00);
+	CMD(0x36, 0x00);
+	CMD(0x37, 0x00);
+	CMD(0x38, 0x3c);
+	CMD(0x39, 0x00);
+	CMD(0x3a, 0x00);
+	CMD(0x3b, 0x00);
+	CMD(0x3c, 0x00);
+	CMD(0x3d, 0x00);
+	CMD(0x3e, 0x00);
+	CMD(0x3f, 0x00);
+	CMD(0x40, 0x00);
+	CMD(0x41, 0x00);
+	CMD(0x42, 0x00);
+	CMD(0x43, 0x00);
+	CMD(0x44, 0x00);
+	CMD(0x50, 0x10);
+	CMD(0x51, 0x32);
+	CMD(0x52, 0x54);
+	CMD(0x53, 0x76);
+	CMD(0x54, 0x98);
+	CMD(0x55, 0xba);
+	CMD(0x56, 0x10);
+	CMD(0x57, 0x32);
+	CMD(0x58, 0x54);
+	CMD(0x59, 0x76);
+	CMD(0x5a, 0x98);
+	CMD(0x5b, 0xba);
+	CMD(0x5c, 0xdc);
+	CMD(0x5d, 0xfe);
+	CMD(0x5e, 0x00);
+	CMD(0x5f, 0x0e);
+	CMD(0x60, 0x0f);
+	CMD(0x61, 0x0c);
+	CMD(0x62, 0x0d);
+	CMD(0x63, 0x06);
+	CMD(0x64, 0x07);
+	CMD(0x65, 0x02);
+	CMD(0x66, 0x02);
+	CMD(0x67, 0x02);
+	CMD(0x68, 0x02);
+	CMD(0x69, 0x01);
+	CMD(0x6a, 0x00);
+	CMD(0x6b, 0x02);
+	CMD(0x6c, 0x15);
+	CMD(0x6d, 0x14);
+	CMD(0x6e, 0x02);
+	CMD(0x6f, 0x02);
+	CMD(0x70, 0x02);
+	CMD(0x71, 0x02);
+	CMD(0x72, 0x02);
+	CMD(0x73, 0x02);
+	CMD(0x74, 0x02);
+	CMD(0x75, 0x0e);
+	CMD(0x76, 0x0f);
+	CMD(0x77, 0x0c);
+	CMD(0x78, 0x0d);
+	CMD(0x79, 0x06);
+	CMD(0x7a, 0x07);
+	CMD(0x7b, 0x02);
+	CMD(0x7c, 0x02);
+	CMD(0x7d, 0x02);
+	CMD(0x7e, 0x02);
+	CMD(0x7f, 0x01);
+	CMD(0x80, 0x00);
+	CMD(0x81, 0x02);
+	CMD(0x82, 0x14);
+	CMD(0x83, 0x15);
+	CMD(0x84, 0x02);
+	CMD(0x85, 0x02);
+	CMD(0x86, 0x02);
+	CMD(0x87, 0x02);
+	CMD(0x88, 0x02);
+	CMD(0x89, 0x02);
+	CMD(0x8a, 0x02);
+
+	/* Page 4 */
+	PAGE(4);
+	CMD(0x6c, 0x15);
+	CMD(0x6e, 0x2a);
+	CMD(0x6f, 0x33);
+	CMD(0x3b, 0x98);
+	CMD(0x3a, 0x94);
+	CMD(0x8d, 0x14);
+	CMD(0x87, 0xba);
+	CMD(0x26, 0x76);
+	CMD(0xb2, 0xd1);
+	CMD(0xb5, 0x06);
+	CMD(0x38, 0x01);
+	CMD(0x39, 0x00);
+
+	/* Page 1 */
+	PAGE(1);
+	CMD(0x22, 0x0a);
+	CMD(0x31, 0x00);
+	CMD(0x53, 0x7d);
+	CMD(0x55, 0x8f);
+	CMD(0x40, 0x33);
+	CMD(0x50, 0x96);
+	CMD(0x51, 0x96);
+	CMD(0x60, 0x23);
+	CMD(0xa0, 0x08);
+	CMD(0xa1, 0x1d);
+	CMD(0xa2, 0x2a);
+	CMD(0xa3, 0x10);
+	CMD(0xa4, 0x15);
+	CMD(0xa5, 0x28);
+	CMD(0xa6, 0x1c);
+	CMD(0xa7, 0x1d);
+	CMD(0xa8, 0x7e);
+	CMD(0xa9, 0x1d);
+	CMD(0xaa, 0x29);
+	CMD(0xab, 0x6b);
+	CMD(0xac, 0x1a);
+	CMD(0xad, 0x18);
+	CMD(0xae, 0x4b);
+	CMD(0xaf, 0x20);
+	CMD(0xb0, 0x27);
+	CMD(0xb1, 0x50);
+	CMD(0xb2, 0x64);
+	CMD(0xb3, 0x39);
+	CMD(0xc0, 0x08);
+	CMD(0xc1, 0x1d);
+	CMD(0xc2, 0x2a);
+	CMD(0xc3, 0x10);
+	CMD(0xc4, 0x15);
+	CMD(0xc5, 0x28);
+	CMD(0xc6, 0x1c);
+	CMD(0xc7, 0x1d);
+	CMD(0xc8, 0x7e);
+	CMD(0xc9, 0x1d);
+	CMD(0xca, 0x29);
+	CMD(0xcb, 0x6b);
+	CMD(0xcc, 0x1a);
+	CMD(0xcd, 0x18);
+	CMD(0xce, 0x4b);
+	CMD(0xcf, 0x20);
+	CMD(0xd0, 0x27);
+	CMD(0xd1, 0x50);
+	CMD(0xd2, 0x64);
+	CMD(0xd3, 0x39);
+
+	/* Switch to page 0 and finalize */
+	PAGE(0);
+
+	/* set_tear_on with VBLANK mode (0x00) */
+	CMD(0x35, 0x00);
+
+	ret = panel_bpf_mipi_dsi_exit_sleep_mode(pctx);
+	if (ret)
+		return ret;
+
+	ret = panel_bpf_mipi_dsi_set_display_on(pctx);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+SEC(PANEL_BPF_MIPI_DSI_UNPREPARE)
+int BPF_PROG(panel_unprepare, struct panel_bpf_mipi_dsi_ctx *pctx)
+{
+	panel_bpf_mipi_dsi_set_display_off(pctx);
+	panel_bpf_mipi_dsi_enter_sleep_mode(pctx);
+
+	panel_bpf_mipi_dsi_regulator_disable(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_VCC);
+	panel_bpf_mipi_dsi_regulator_disable(pctx, PANEL_BPF_MIPI_DSI_SUPPLY_IOVCC);
+	panel_bpf_mipi_dsi_gpio_enable(pctx, PANEL_BPF_MIPI_DSI_GPIO_RESET);
+
+	return 0;
+}
+
+PANEL_BPF_MIPI_DSI_OPS(raspberrypi_dsi_7inch) = {
+	.panel_id		= "/soc/dsi@7e700000/panel@0",
+	.compatible		= "raspberrypi,dsi-7inch",
+	.format			= MIPI_DSI_FMT_RGB888,
+	.lanes			= 2,
+	.mode_flags		= MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+	.panel_prepare		= (void *)panel_prepare,
+	.panel_unprepare	= (void *)panel_unprepare,
+};
+
+char _license[] SEC("license") = "GPL";

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH DO NOT MERGE 6/6] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (4 preceding siblings ...)
  2026-09-28 16:22 ` [PATCH 5/6] drm/panel: dsi-bpf: Add Raspberry Pi 5-inch " Maxime Ripard
@ 2026-09-28 16:22 ` Maxime Ripard
  2026-09-28 16:37 ` [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Laurent Pinchart
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-28 16:22 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

Add device tree overlays for the Raspberry Pi 5-inch and 7-inch
MIPI-DSI panels (ILI9881C) on the Raspberry Pi 4 Model B.

Both panels share the same I2C-connected display MCU at 0x45 on
i2c0_1, which provides GPIO and PWM for reset and backlight
control. The common parts are in a shared dtsi. Each panel overlay
includes it and adds only the panel-specific compatible string, physical
dimensions, and timing.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
 arch/arm64/boot/dts/broadcom/Makefile              |  8 +++
 .../bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso         | 22 ++++++++
 .../bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso         | 22 ++++++++
 .../dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi  | 65 ++++++++++++++++++++++
 4 files changed, 117 insertions(+)

diff --git a/arch/arm64/boot/dts/broadcom/Makefile b/arch/arm64/boot/dts/broadcom/Makefile
index 01ecfa304184..d717dc8c4071 100644
--- a/arch/arm64/boot/dts/broadcom/Makefile
+++ b/arch/arm64/boot/dts/broadcom/Makefile
@@ -13,8 +13,16 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-400.dtb \
 			      bcm2837-rpi-3-b.dtb \
 			      bcm2837-rpi-3-b-plus.dtb \
 			      bcm2837-rpi-cm3-io3.dtb \
 			      bcm2837-rpi-zero-2-w.dtb
 
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-4-b-dsi-ili9881-5inch.dtb
+bcm2711-rpi-4-b-dsi-ili9881-5inch-dtbs := bcm2711-rpi-4-b.dtb \
+	bcm2711-rpi-4-b-dsi-ili9881-5inch.dtbo
+
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-4-b-dsi-ili9881-7inch.dtb
+bcm2711-rpi-4-b-dsi-ili9881-7inch-dtbs := bcm2711-rpi-4-b.dtb \
+	bcm2711-rpi-4-b-dsi-ili9881-7inch.dtbo
+
 subdir-y	+= bcmbca
 subdir-y	+= northstar2
 subdir-y	+= stingray
diff --git a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso
new file mode 100644
index 000000000000..99aac1105ac6
--- /dev/null
+++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+#include "bcm2711-rpi-4-b-dsi-ili9881.dtsi"
+
+&panel {
+	compatible = "raspberrypi,dsi-5inch", "panel-mipi-dsi-bpf";
+	width-mm = <62>;
+	height-mm = <110>;
+
+	panel-timing {
+		clock-frequency = <83333000>;
+		hactive = <720>;
+		vactive = <1280>;
+		hfront-porch = <110>;
+		hsync-len = <12>;
+		hback-porch = <95>;
+		vfront-porch = <100>;
+		vsync-len = <2>;
+		vback-porch = <100>;
+	};
+};
diff --git a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso
new file mode 100644
index 000000000000..809d9b354b89
--- /dev/null
+++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+#include "bcm2711-rpi-4-b-dsi-ili9881.dtsi"
+
+&panel {
+	compatible = "raspberrypi,dsi-7inch", "panel-mipi-dsi-bpf";
+	width-mm = <90>;
+	height-mm = <151>;
+
+	panel-timing {
+		clock-frequency = <83330000>;
+		hactive = <720>;
+		vactive = <1280>;
+		hfront-porch = <239>;
+		hsync-len = <33>;
+		hback-porch = <50>;
+		vfront-porch = <20>;
+		vsync-len = <2>;
+		vback-porch = <30>;
+	};
+};
diff --git a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi
new file mode 100644
index 000000000000..bd08e22581a3
--- /dev/null
+++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common DSI infrastructure for Raspberry Pi ILI9881C panels
+ * (backlight, i2c mux, display MCU, DSI1 port wiring)
+ *
+ * Each panel overlay includes this and extends &panel with its own
+ * compatible, dimensions, and panel-timing.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+
+&{/} {
+	panel_backlight: panel_backlight {
+		compatible = "pwm-backlight";
+		brightness-levels = <0 31>;
+		num-interpolated-steps = <31>;
+		default-brightness-level = <15>;
+		pwms = <&display_mcu 0 200000 0>;
+	};
+};
+
+&i2c0 {
+	status = "okay";
+};
+
+&i2c0mux {
+	status = "okay";
+};
+
+&i2c0_1 {
+	status = "okay";
+
+	display_mcu: display_mcu@45 {
+		compatible = "raspberrypi,touchscreen-panel-regulator-v2";
+		reg = <0x45>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		#pwm-cells = <3>;
+	};
+};
+
+&dsi1 {
+	status = "okay";
+
+	panel: panel@0 {
+		reg = <0>;
+		reset-gpios = <&display_mcu 0 GPIO_ACTIVE_LOW>;
+		backlight = <&panel_backlight>;
+		dsi-lanes = <2>;
+		mode-video;
+		mode-lpm;
+
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&dsi1_out>;
+			};
+		};
+	};
+
+	port {
+		dsi1_out: endpoint {
+			remote-endpoint = <&panel_in>;
+		};
+	};
+};

-- 
2.55.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (5 preceding siblings ...)
  2026-09-28 16:22 ` [PATCH DO NOT MERGE 6/6] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays Maxime Ripard
@ 2026-09-28 16:37 ` Laurent Pinchart
  2026-09-28 17:31   ` Benjamin Tissoires
  2026-09-29  6:54   ` Maxime Ripard
  2026-09-28 16:39 ` Neil Armstrong
  2026-09-29  9:03 ` Jani Nikula
  8 siblings, 2 replies; 27+ messages in thread
From: Laurent Pinchart @ 2026-09-28 16:37 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Benjamin Tissoires

Hi Maxime,

On Mon, Sep 28, 2026 at 06:22:00PM +0200, Maxime Ripard wrote:
> Hi,
> 
> Panels in general, and MIPI-DSI panels in particular, are pretty
> difficult to support and require pretty much a panel driver for each
> panel produced. Most of them are pretty simple, and require an opaque
> initialization sequence that is usually poorly documented.
> 
> This creates a tension between OEMs and distros because OEMs will
> typically get a new panel to react to a sourcing issue during
> production, and thus need some swift turnaround between getting their
> new panel and it being operational in the OS. Distributions on the other
> hand can take years to ship a kernel with that new panel driver.
> 
> To solve this, I followed the example of HID-BPF and wrote a panel
> driver that will rely on BPF programs to perform the panel
> initialization. That way, we can ship the programs separately from the
> kernel, and with a different lifecycle. If this driver is accepted, the
> plan is to have a userspace component started by udev to identify and
> load the right BPF program for the panels found on the device.

Interesting idea.

How would that work for devices that want early display support ? In
particular, how does it interact with your recent work on "fastboot"
support (with the kernel drivers taking over a running display
configured by the boot loader) ?

I'm also wondering about the incentives for vendors to upstream the
code. How will we avoid having a proliferation of out-of-tree drivers ?

> This driver is fully functional and works with both 5" and 7" Touch
> Display 2 panels for the RaspberryPi. However, it breaks away from the
> typical panel driver in multiple ways:
> 
> - BPF programs can only be loaded by userspace. This leaves us with two
>   choices: 
> 
>   * We prevent the driver from loading until the script itself is
>     loaded. This has the side effect of preventing any other output to
>     be used until the initramfs is ran at the earliest, and possibly
>     ever if the loader isn't installed for example.
> 
>   * Or we probe the driver all the time, but only report it as connected
>     once a program has been registered. This is somewhat unconventional,
>     but allows the other outputs to be functional, *and* allows the user
>     to force the output if their panel doesn't require any
>     initialization or during debugging. I chose this solution.
> 
> - It's not a panel driver, but a bridge one, which is also pretty
>   unconventional. This is required because panel drivers don't have
>   access to a detect callback that is required for the above, but I also
>   think that the recent work from Luca blurs the line from panels and
>   bridges and we'll end up going that road anyway.
> 
> Let me know what you think,
> Maxime
> 
> Signed-off-by: Maxime Ripard <mripard@kernel.org>
> ---
> Maxime Ripard (6):
>       dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
>       drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences
>       drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header
>       drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program
>       drm/panel: dsi-bpf: Add Raspberry Pi 5-inch panel BPF program
>       [DO NOT MERGE] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays
> 
>  .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++
>  arch/arm64/boot/dts/broadcom/Makefile              |   8 +
>  .../bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso         |  22 +
>  .../bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso         |  22 +
>  .../dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi  |  65 +++
>  drivers/gpu/drm/panel/Kconfig                      |   3 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/bpf/Kconfig                  |  28 ++
>  drivers/gpu/drm/panel/bpf/Makefile                 |  10 +
>  .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c    | 452 +++++++++++++++++++++
>  .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c  | 278 +++++++++++++
>  drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c | 242 +++++++++++
>  .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c   |   4 +
>  .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h   | 246 +++++++++++
>  drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h     | 264 ++++++++++++
>  drivers/gpu/drm/panel/bpf/progs/Makefile           |  93 +++++
>  .../panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c   | 266 ++++++++++++
>  .../panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c   | 271 ++++++++++++
>  .../gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h   |  95 +++++
>  19 files changed, 2554 insertions(+)
> ---
> base-commit: 6e375de99d0c420169481fcd36064177ab55b09d
> change-id: 20260928-drm-mipi-dsi-panel-ebpf-d78b72a9c47f

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (6 preceding siblings ...)
  2026-09-28 16:37 ` [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Laurent Pinchart
@ 2026-09-28 16:39 ` Neil Armstrong
  2026-09-28 17:24   ` Benjamin Tissoires
  2026-09-29  7:13   ` Maxime Ripard
  2026-09-29  9:03 ` Jani Nikula
  8 siblings, 2 replies; 27+ messages in thread
From: Neil Armstrong @ 2026-09-28 16:39 UTC (permalink / raw)
  To: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Robert Foss, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Luca Ceresoli, Albert Esteve, Dave Stevenson,
	Javier Martinez Canillas, dri-devel, devicetree, linux-kernel,
	bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Benjamin Tissoires

Hi,

On 9/28/26 18:22, Maxime Ripard wrote:
> Hi,
> 
> Panels in general, and MIPI-DSI panels in particular, are pretty
> difficult to support and require pretty much a panel driver for each
> panel produced. Most of them are pretty simple, and require an opaque
> initialization sequence that is usually poorly documented.
> 
> This creates a tension between OEMs and distros because OEMs will
> typically get a new panel to react to a sourcing issue during
> production, and thus need some swift turnaround between getting their
> new panel and it being operational in the OS. Distributions on the other
> hand can take years to ship a kernel with that new panel driver.
> 
> To solve this, I followed the example of HID-BPF and wrote a panel
> driver that will rely on BPF programs to perform the panel
> initialization. That way, we can ship the programs separately from the
> kernel, and with a different lifecycle. If this driver is accepted, the
> plan is to have a userspace component started by udev to identify and
> load the right BPF program for the panels found on the device.

This is kind of late for serious applications except if we manage to
solve the bootloader to Linux display engine transition.

> 
> This driver is fully functional and works with both 5" and 7" Touch
> Display 2 panels for the RaspberryPi. However, it breaks away from the
> typical panel driver in multiple ways:
> 
> - BPF programs can only be loaded by userspace. This leaves us with two
>    choices:
> 
>    * We prevent the driver from loading until the script itself is
>      loaded. This has the side effect of preventing any other output to
>      be used until the initramfs is ran at the earliest, and possibly
>      ever if the loader isn't installed for example.

This adds a dependency on user-space behavior and if somehow the
initramfs doesn't load for a reason we won't have a way to display
an error.

> 
>    * Or we probe the driver all the time, but only report it as connected
>      once a program has been registered. This is somewhat unconventional,
>      but allows the other outputs to be functional, *and* allows the user
>      to force the output if their panel doesn't require any
>      initialization or during debugging. I chose this solution.

Both options are not really great...

> 
> - It's not a panel driver, but a bridge one, which is also pretty
>    unconventional. This is required because panel drivers don't have
>    access to a detect callback that is required for the above, but I also
>    think that the recent work from Luca blurs the line from panels and
>    bridges and we'll end up going that road anyway.

On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
the panel and the DDIC. So being a bridge is fine, but in a general way we lack
a proper way to describe the display/panel/monitor independently of the DDIC.

At first glance it's a nice driver, but moving the timings into a blob moves something
into possible proprietary binaries with possible closed licence and distribution
restriction so it's a downgrade for the same of bringing up a panel faster.

I'll review further and comment.

> 
> Let me know what you think,
> Maxime
> 
> Signed-off-by: Maxime Ripard <mripard@kernel.org>
> ---
> Maxime Ripard (6):
>        dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
>        drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences
>        drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header
>        drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program
>        drm/panel: dsi-bpf: Add Raspberry Pi 5-inch panel BPF program
>        [DO NOT MERGE] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays
> 
>   .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++
>   arch/arm64/boot/dts/broadcom/Makefile              |   8 +
>   .../bcm2711-rpi-4-b-dsi-ili9881-5inch.dtso         |  22 +
>   .../bcm2711-rpi-4-b-dsi-ili9881-7inch.dtso         |  22 +
>   .../dts/broadcom/bcm2711-rpi-4-b-dsi-ili9881.dtsi  |  65 +++
>   drivers/gpu/drm/panel/Kconfig                      |   3 +
>   drivers/gpu/drm/panel/Makefile                     |   1 +
>   drivers/gpu/drm/panel/bpf/Kconfig                  |  28 ++
>   drivers/gpu/drm/panel/bpf/Makefile                 |  10 +
>   .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-core.c    | 452 +++++++++++++++++++++
>   .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-kfuncs.c  | 278 +++++++++++++
>   drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi-ops.c | 242 +++++++++++
>   .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.c   |   4 +
>   .../gpu/drm/panel/bpf/panel-bpf-mipi-dsi-trace.h   | 246 +++++++++++
>   drivers/gpu/drm/panel/bpf/panel-bpf-mipi-dsi.h     | 264 ++++++++++++
>   drivers/gpu/drm/panel/bpf/progs/Makefile           |  93 +++++
>   .../panel/bpf/progs/Raspberrypi__dsi-5inch.bpf.c   | 266 ++++++++++++
>   .../panel/bpf/progs/Raspberrypi__dsi-7inch.bpf.c   | 271 ++++++++++++
>   .../gpu/drm/panel/bpf/progs/panel-bpf-mipi-dsi.h   |  95 +++++
>   19 files changed, 2554 insertions(+)
> ---
> base-commit: 6e375de99d0c420169481fcd36064177ab55b09d
> change-id: 20260928-drm-mipi-dsi-panel-ebpf-d78b72a9c47f
> 
> Best regards,


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:39 ` Neil Armstrong
@ 2026-09-28 17:24   ` Benjamin Tissoires
  2026-09-28 19:20     ` Neil Armstrong
  2026-09-29  7:13   ` Maxime Ripard
  1 sibling, 1 reply; 27+ messages in thread
From: Benjamin Tissoires @ 2026-09-28 17:24 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On Sep 28 2026, Neil Armstrong wrote:
> Hi,
> 
> On 9/28/26 18:22, Maxime Ripard wrote:
> > Hi,
> > 
> > Panels in general, and MIPI-DSI panels in particular, are pretty
> > difficult to support and require pretty much a panel driver for each
> > panel produced. Most of them are pretty simple, and require an opaque
> > initialization sequence that is usually poorly documented.
> > 
> > This creates a tension between OEMs and distros because OEMs will
> > typically get a new panel to react to a sourcing issue during
> > production, and thus need some swift turnaround between getting their
> > new panel and it being operational in the OS. Distributions on the other
> > hand can take years to ship a kernel with that new panel driver.
> > 
> > To solve this, I followed the example of HID-BPF and wrote a panel
> > driver that will rely on BPF programs to perform the panel
> > initialization. That way, we can ship the programs separately from the
> > kernel, and with a different lifecycle. If this driver is accepted, the
> > plan is to have a userspace component started by udev to identify and
> > load the right BPF program for the panels found on the device.
> 
> This is kind of late for serious applications except if we manage to
> solve the bootloader to Linux display engine transition.
> 
> > 
> > This driver is fully functional and works with both 5" and 7" Touch
> > Display 2 panels for the RaspberryPi. However, it breaks away from the
> > typical panel driver in multiple ways:
> > 
> > - BPF programs can only be loaded by userspace. This leaves us with two
> >    choices:
> > 
> >    * We prevent the driver from loading until the script itself is
> >      loaded. This has the side effect of preventing any other output to
> >      be used until the initramfs is ran at the earliest, and possibly
> >      ever if the loader isn't installed for example.
> 
> This adds a dependency on user-space behavior and if somehow the
> initramfs doesn't load for a reason we won't have a way to display
> an error.
> 
> > 
> >    * Or we probe the driver all the time, but only report it as connected
> >      once a program has been registered. This is somewhat unconventional,
> >      but allows the other outputs to be functional, *and* allows the user
> >      to force the output if their panel doesn't require any
> >      initialization or during debugging. I chose this solution.
> 
> Both options are not really great...
> 
> > 
> > - It's not a panel driver, but a bridge one, which is also pretty
> >    unconventional. This is required because panel drivers don't have
> >    access to a detect callback that is required for the above, but I also
> >    think that the recent work from Luca blurs the line from panels and
> >    bridges and we'll end up going that road anyway.
> 
> On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
> the panel and the DDIC. So being a bridge is fine, but in a general way we lack
> a proper way to describe the display/panel/monitor independently of the DDIC.
> 
> At first glance it's a nice driver, but moving the timings into a blob moves something
> into possible proprietary binaries with possible closed licence and distribution
> restriction so it's a downgrade for the same of bringing up a panel faster.

Quick answer on this, because I had the very same questions regarding
HID-BPF:
- in BPF, you can require (and by default it does) that only GPL
	compatible BPF programs are loaded, closing the argument of "closed
	licence and distribution restriction"
- also, a BPF program can be disassembled much easier than a binary
	blob, and I remember Alexei showing me an example where you get almost
	the source code from the BPF object in just one pass.

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:37 ` [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Laurent Pinchart
@ 2026-09-28 17:31   ` Benjamin Tissoires
  2026-09-28 18:12     ` Laurent Pinchart
  2026-09-29  6:54   ` Maxime Ripard
  1 sibling, 1 reply; 27+ messages in thread
From: Benjamin Tissoires @ 2026-09-28 17:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Maxime Ripard, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel

On Sep 28 2026, Laurent Pinchart wrote:
> Hi Maxime,
> 
> On Mon, Sep 28, 2026 at 06:22:00PM +0200, Maxime Ripard wrote:
> > Hi,
> > 
> > Panels in general, and MIPI-DSI panels in particular, are pretty
> > difficult to support and require pretty much a panel driver for each
> > panel produced. Most of them are pretty simple, and require an opaque
> > initialization sequence that is usually poorly documented.
> > 
> > This creates a tension between OEMs and distros because OEMs will
> > typically get a new panel to react to a sourcing issue during
> > production, and thus need some swift turnaround between getting their
> > new panel and it being operational in the OS. Distributions on the other
> > hand can take years to ship a kernel with that new panel driver.
> > 
> > To solve this, I followed the example of HID-BPF and wrote a panel
> > driver that will rely on BPF programs to perform the panel
> > initialization. That way, we can ship the programs separately from the
> > kernel, and with a different lifecycle. If this driver is accepted, the
> > plan is to have a userspace component started by udev to identify and
> > load the right BPF program for the panels found on the device.
> 
> Interesting idea.
> 
> How would that work for devices that want early display support ? In

Brain fart... Include a bpf VM in the bootloader? :)

> particular, how does it interact with your recent work on "fastboot"
> support (with the kernel drivers taking over a running display
> configured by the boot loader) ?
> 
> I'm also wondering about the incentives for vendors to upstream the
> code. How will we avoid having a proliferation of out-of-tree drivers ?

My 2 cents here from the HID-BPF point of view:
- as mentioned in my reply to Neil, the kernel enforce GPL licensed BPF
	only
- so vendors have to publish their code somewhere
- in HID-BPF, I decided to provide an "official" list of HID-BPF sources
	as part of the kernel (see drivers/hid/bpf/progs). I try to sync them
	with the userspace tree when time permits

So basically, distributions can say "we ship only BPF objects compiled
from the kernel tree". It doesn't mean they have to be in a released
kernel already to remove the friction, but in the process of being
released could be sufficient.

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 17:31   ` Benjamin Tissoires
@ 2026-09-28 18:12     ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2026-09-28 18:12 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Maxime Ripard, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel

On Mon, Sep 28, 2026 at 07:31:59PM +0200, Benjamin Tissoires wrote:
> On Sep 28 2026, Laurent Pinchart wrote:
> > On Mon, Sep 28, 2026 at 06:22:00PM +0200, Maxime Ripard wrote:
> > > Hi,
> > > 
> > > Panels in general, and MIPI-DSI panels in particular, are pretty
> > > difficult to support and require pretty much a panel driver for each
> > > panel produced. Most of them are pretty simple, and require an opaque
> > > initialization sequence that is usually poorly documented.
> > > 
> > > This creates a tension between OEMs and distros because OEMs will
> > > typically get a new panel to react to a sourcing issue during
> > > production, and thus need some swift turnaround between getting their
> > > new panel and it being operational in the OS. Distributions on the other
> > > hand can take years to ship a kernel with that new panel driver.
> > > 
> > > To solve this, I followed the example of HID-BPF and wrote a panel
> > > driver that will rely on BPF programs to perform the panel
> > > initialization. That way, we can ship the programs separately from the
> > > kernel, and with a different lifecycle. If this driver is accepted, the
> > > plan is to have a userspace component started by udev to identify and
> > > load the right BPF program for the panels found on the device.
> > 
> > Interesting idea.
> > 
> > How would that work for devices that want early display support ? In
> 
> Brain fart... Include a bpf VM in the bootloader? :)

The boot loader can have its own panel driver, that's not an issue. What
I'm wondering is how we will deal with e.g. getting oops traces on the
screen before userspace is available.

> > particular, how does it interact with your recent work on "fastboot"
> > support (with the kernel drivers taking over a running display
> > configured by the boot loader) ?
> > 
> > I'm also wondering about the incentives for vendors to upstream the
> > code. How will we avoid having a proliferation of out-of-tree drivers ?
> 
> My 2 cents here from the HID-BPF point of view:
> - as mentioned in my reply to Neil, the kernel enforce GPL licensed BPF
> 	only

I didn't know that, it's interesting.

> - so vendors have to publish their code somewhere
> - in HID-BPF, I decided to provide an "official" list of HID-BPF sources
> 	as part of the kernel (see drivers/hid/bpf/progs). I try to sync them
> 	with the userspace tree when time permits
> 
> So basically, distributions can say "we ship only BPF objects compiled
> from the kernel tree". It doesn't mean they have to be in a released
> kernel already to remove the friction, but in the process of being
> released could be sufficient.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
@ 2026-09-28 19:16   ` Neil Armstrong
  2026-09-28 20:40   ` Rob Herring
  2026-09-28 20:43   ` Rob Herring (Arm)
  2 siblings, 0 replies; 27+ messages in thread
From: Neil Armstrong @ 2026-09-28 19:16 UTC (permalink / raw)
  To: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Robert Foss, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Luca Ceresoli, Albert Esteve, Dave Stevenson,
	Javier Martinez Canillas, dri-devel, devicetree, linux-kernel,
	bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Benjamin Tissoires

On 9/28/26 18:22, Maxime Ripard wrote:
> Most MIPI-DSI panel drivers follow an identical pattern: acquire
> regulators and GPIOs, perform a reset pulse with specific timing,
> send a vendor-supplied sequence of DSI commands, then enable the
> display. The only truly panel-specific part is the init sequence
> and power-on/off timing.
> 
> The panel-mipi-dsi-bpf driver replaces per-panel kernel modules
> with a single generic driver whose panel-specific behavior is
> provided by BPF programs loaded from userspace at runtime,
> following the HID-BPF model. This enables new panel support
> without kernel patches.
> 
> Panel DT nodes use a two-entry compatible with the panel-specific
> string first and "panel-mipi-dsi-bpf" as fallback. The generic
> driver matches on the fallback, while the first compatible is used
> to identify which BPF program to load.
> 
> Six normalized optional regulator supplies cover ~95% of existing
> MIPI-DSI panels: vcc (IC core), iovcc (I/O interface), avdd/avee
> (positive/negative analog), and elvdd/elvss (OLED EL driver).
> 
> Signed-off-by: Maxime Ripard <mripard@kernel.org>
> ---
>   .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++++++++++++++
>   1 file changed, 184 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml b/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml
> new file mode 100644
> index 000000000000..9a20a21cf70e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.yaml
> @@ -0,0 +1,184 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/panel/panel-mipi-dsi-bpf.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic MIPI-DSI panel with BPF init sequences
> +
> +maintainers:
> +  - Maxime Ripard <mripard@kernel.org>
> +
> +description: |
> +  A generic MIPI-DSI panel driver where the panel-specific power sequencing
> +  and DSI init commands are provided by BPF programs.
> +
> +  Panel DT nodes use a fallback compatible so the generic driver matches on
> +  "panel-mipi-dsi-bpf" while the first compatible identifies the specific
> +  panel for BPF program matching.
> +
> +allOf:
> +  - $ref: panel-common.yaml#
> +
> +properties:
> +  compatible:
> +    items:
> +      - description: Panel-specific compatible string
> +      - const: panel-mipi-dsi-bpf

I don't see how this can be a valid hardware description, bfp is a software
implementation and has nothing to do in the bindings.

Neil

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 17:24   ` Benjamin Tissoires
@ 2026-09-28 19:20     ` Neil Armstrong
  2026-09-28 19:48       ` Benjamin Tissoires
  2026-09-29  7:27       ` Maxime Ripard
  0 siblings, 2 replies; 27+ messages in thread
From: Neil Armstrong @ 2026-09-28 19:20 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On 9/28/26 19:24, Benjamin Tissoires wrote:
> On Sep 28 2026, Neil Armstrong wrote:
>> Hi,
>>
>> On 9/28/26 18:22, Maxime Ripard wrote:
>>> Hi,
>>>
>>> Panels in general, and MIPI-DSI panels in particular, are pretty
>>> difficult to support and require pretty much a panel driver for each
>>> panel produced. Most of them are pretty simple, and require an opaque
>>> initialization sequence that is usually poorly documented.
>>>
>>> This creates a tension between OEMs and distros because OEMs will
>>> typically get a new panel to react to a sourcing issue during
>>> production, and thus need some swift turnaround between getting their
>>> new panel and it being operational in the OS. Distributions on the other
>>> hand can take years to ship a kernel with that new panel driver.
>>>
>>> To solve this, I followed the example of HID-BPF and wrote a panel
>>> driver that will rely on BPF programs to perform the panel
>>> initialization. That way, we can ship the programs separately from the
>>> kernel, and with a different lifecycle. If this driver is accepted, the
>>> plan is to have a userspace component started by udev to identify and
>>> load the right BPF program for the panels found on the device.
>>
>> This is kind of late for serious applications except if we manage to
>> solve the bootloader to Linux display engine transition.
>>
>>>
>>> This driver is fully functional and works with both 5" and 7" Touch
>>> Display 2 panels for the RaspberryPi. However, it breaks away from the
>>> typical panel driver in multiple ways:
>>>
>>> - BPF programs can only be loaded by userspace. This leaves us with two
>>>     choices:
>>>
>>>     * We prevent the driver from loading until the script itself is
>>>       loaded. This has the side effect of preventing any other output to
>>>       be used until the initramfs is ran at the earliest, and possibly
>>>       ever if the loader isn't installed for example.
>>
>> This adds a dependency on user-space behavior and if somehow the
>> initramfs doesn't load for a reason we won't have a way to display
>> an error.
>>
>>>
>>>     * Or we probe the driver all the time, but only report it as connected
>>>       once a program has been registered. This is somewhat unconventional,
>>>       but allows the other outputs to be functional, *and* allows the user
>>>       to force the output if their panel doesn't require any
>>>       initialization or during debugging. I chose this solution.
>>
>> Both options are not really great...
>>
>>>
>>> - It's not a panel driver, but a bridge one, which is also pretty
>>>     unconventional. This is required because panel drivers don't have
>>>     access to a detect callback that is required for the above, but I also
>>>     think that the recent work from Luca blurs the line from panels and
>>>     bridges and we'll end up going that road anyway.
>>
>> On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
>> the panel and the DDIC. So being a bridge is fine, but in a general way we lack
>> a proper way to describe the display/panel/monitor independently of the DDIC.
>>
>> At first glance it's a nice driver, but moving the timings into a blob moves something
>> into possible proprietary binaries with possible closed licence and distribution
>> restriction so it's a downgrade for the same of bringing up a panel faster.
> 
> Quick answer on this, because I had the very same questions regarding
> HID-BPF:
> - in BPF, you can require (and by default it does) that only GPL
> 	compatible BPF programs are loaded, closing the argument of "closed
> 	licence and distribution restriction"
> - also, a BPF program can be disassembled much easier than a binary
> 	blob, and I remember Alexei showing me an example where you get almost
> 	the source code from the BPF object in just one pass.

Right, it "solve" one of my question, but doesn't really solve the issue
of vendors providing "GPL" bpf programs with source available "somewhere".

Another big issue is the API, I don't want to keep the current API as-is,
we plan to support more advanced panel features and use try to use the
atomic states to support rate switching for example, and I'm not confident
it's a good idea since there's no "simple" and "forever valid" API
to initialize panels...

Neil

> 
> Cheers,
> Benjamin


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 19:20     ` Neil Armstrong
@ 2026-09-28 19:48       ` Benjamin Tissoires
  2026-09-28 20:36         ` Neil Armstrong
  2026-09-29  7:27       ` Maxime Ripard
  1 sibling, 1 reply; 27+ messages in thread
From: Benjamin Tissoires @ 2026-09-28 19:48 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On Sep 28 2026, Neil Armstrong wrote:
> On 9/28/26 19:24, Benjamin Tissoires wrote:
> > On Sep 28 2026, Neil Armstrong wrote:
> > > Hi,
> > > 
> > > On 9/28/26 18:22, Maxime Ripard wrote:
> > > > Hi,
> > > > 
> > > > Panels in general, and MIPI-DSI panels in particular, are pretty
> > > > difficult to support and require pretty much a panel driver for each
> > > > panel produced. Most of them are pretty simple, and require an opaque
> > > > initialization sequence that is usually poorly documented.
> > > > 
> > > > This creates a tension between OEMs and distros because OEMs will
> > > > typically get a new panel to react to a sourcing issue during
> > > > production, and thus need some swift turnaround between getting their
> > > > new panel and it being operational in the OS. Distributions on the other
> > > > hand can take years to ship a kernel with that new panel driver.
> > > > 
> > > > To solve this, I followed the example of HID-BPF and wrote a panel
> > > > driver that will rely on BPF programs to perform the panel
> > > > initialization. That way, we can ship the programs separately from the
> > > > kernel, and with a different lifecycle. If this driver is accepted, the
> > > > plan is to have a userspace component started by udev to identify and
> > > > load the right BPF program for the panels found on the device.
> > > 
> > > This is kind of late for serious applications except if we manage to
> > > solve the bootloader to Linux display engine transition.
> > > 
> > > > 
> > > > This driver is fully functional and works with both 5" and 7" Touch
> > > > Display 2 panels for the RaspberryPi. However, it breaks away from the
> > > > typical panel driver in multiple ways:
> > > > 
> > > > - BPF programs can only be loaded by userspace. This leaves us with two
> > > >     choices:
> > > > 
> > > >     * We prevent the driver from loading until the script itself is
> > > >       loaded. This has the side effect of preventing any other output to
> > > >       be used until the initramfs is ran at the earliest, and possibly
> > > >       ever if the loader isn't installed for example.
> > > 
> > > This adds a dependency on user-space behavior and if somehow the
> > > initramfs doesn't load for a reason we won't have a way to display
> > > an error.
> > > 
> > > > 
> > > >     * Or we probe the driver all the time, but only report it as connected
> > > >       once a program has been registered. This is somewhat unconventional,
> > > >       but allows the other outputs to be functional, *and* allows the user
> > > >       to force the output if their panel doesn't require any
> > > >       initialization or during debugging. I chose this solution.
> > > 
> > > Both options are not really great...
> > > 
> > > > 
> > > > - It's not a panel driver, but a bridge one, which is also pretty
> > > >     unconventional. This is required because panel drivers don't have
> > > >     access to a detect callback that is required for the above, but I also
> > > >     think that the recent work from Luca blurs the line from panels and
> > > >     bridges and we'll end up going that road anyway.
> > > 
> > > On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
> > > the panel and the DDIC. So being a bridge is fine, but in a general way we lack
> > > a proper way to describe the display/panel/monitor independently of the DDIC.
> > > 
> > > At first glance it's a nice driver, but moving the timings into a blob moves something
> > > into possible proprietary binaries with possible closed licence and distribution
> > > restriction so it's a downgrade for the same of bringing up a panel faster.
> > 
> > Quick answer on this, because I had the very same questions regarding
> > HID-BPF:
> > - in BPF, you can require (and by default it does) that only GPL
> > 	compatible BPF programs are loaded, closing the argument of "closed
> > 	licence and distribution restriction"
> > - also, a BPF program can be disassembled much easier than a binary
> > 	blob, and I remember Alexei showing me an example where you get almost
> > 	the source code from the BPF object in just one pass.
> 
> Right, it "solve" one of my question, but doesn't really solve the issue
> of vendors providing "GPL" bpf programs with source available "somewhere".
> 
> Another big issue is the API, I don't want to keep the current API as-is,
> we plan to support more advanced panel features and use try to use the
> atomic states to support rate switching for example, and I'm not confident
> it's a good idea since there's no "simple" and "forever valid" API
> to initialize panels...
> 

That's exactly where BPF shines. The simple rule of thumb is: there is
no API stability guaranteed. Basically, thanks to the verifier and CORE
(Compile Once Run Everywhere), you don't need to keep the API stable and
available forever. There are multiple ways of dealing with it, but the
gist is that if a BPF is trying to load an "old" API, it will be
rejected by the verifier.

Then it's a matter of being nice enough in the kernel and provide ways
to deal with those.

To give you a few examples:

- in HID-BPF, in userspace, we keep old versions of APIs in separate
	compiled objects. Each is incremented (by 10 so we have a little bit
	of room in the middle). Then the loader tries first
	0020-device-with-new-api.bpf.o, and if it fails, it tries
	0010-device-with-old-api.bpf.o

	I'm not saying we should do the same here, but that's one idea

- recently, a BPF commit broke the ABI of a function while removing
	implicits: bpf_wq_set_callback_impl() was replaced by
	bpf_wq_set_callback() with a different number of arguments. I simply
	had to add a new function in my header which basically does:

	static inline int
	hid_bpf_wq_set_callback(struct bpf_wq *wq,
	                        int (*callback_fn)(void *, int *, void *),
	                        unsigned int flags)
	{
      if (bpf_ksym_exists(bpf_wq_set_callback))
              return bpf_wq_set_callback(wq, callback_fn, flags);
      if (bpf_ksym_exists(bpf_wq_set_callback_impl))
              return bpf_wq_set_callback_impl(wq, callback_fn, flags, NULL);
  }

  And then I changed the bpf.c to use hid_bpf_wq_set_callback() and the
  bpf is compatible with both APIs

- with CORE, struct fields are relocated on the fly when you load the BPF
 
  So for instance, if my BPF only accesses fields .name, .phys and .id
  in the BPF, I can define:
  struct hid_device {
    char                       name[128];
    char                       phys[64];
    unsigned int               id;
  }

  Then when loading the bpf, the verifier replaces all offset to the
  ones actually used by the running kernel, and the program loads
  transparently, even if you add fields before/after or change the
  fields order in the kernel.

Changing the mindset is the hardest part of it. But once you are making
the shift, it's actually much better to work with. It doesn't mean you
can go yolo. You still need to be careful in your choices knowing the
impact on your users. But the API at version 0 is not frozen and you
don't need to maintain it forever, especially if you control the loader
and the headers used to compile the BPFs.

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 19:48       ` Benjamin Tissoires
@ 2026-09-28 20:36         ` Neil Armstrong
  2026-09-29  7:41           ` Maxime Ripard
  0 siblings, 1 reply; 27+ messages in thread
From: Neil Armstrong @ 2026-09-28 20:36 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Maxime Ripard, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On 9/28/26 21:48, Benjamin Tissoires wrote:
> On Sep 28 2026, Neil Armstrong wrote:
>> On 9/28/26 19:24, Benjamin Tissoires wrote:
>>> On Sep 28 2026, Neil Armstrong wrote:
>>>> Hi,
>>>>
>>>> On 9/28/26 18:22, Maxime Ripard wrote:
>>>>> Hi,
>>>>>
>>>>> Panels in general, and MIPI-DSI panels in particular, are pretty
>>>>> difficult to support and require pretty much a panel driver for each
>>>>> panel produced. Most of them are pretty simple, and require an opaque
>>>>> initialization sequence that is usually poorly documented.
>>>>>
>>>>> This creates a tension between OEMs and distros because OEMs will
>>>>> typically get a new panel to react to a sourcing issue during
>>>>> production, and thus need some swift turnaround between getting their
>>>>> new panel and it being operational in the OS. Distributions on the other
>>>>> hand can take years to ship a kernel with that new panel driver.
>>>>>
>>>>> To solve this, I followed the example of HID-BPF and wrote a panel
>>>>> driver that will rely on BPF programs to perform the panel
>>>>> initialization. That way, we can ship the programs separately from the
>>>>> kernel, and with a different lifecycle. If this driver is accepted, the
>>>>> plan is to have a userspace component started by udev to identify and
>>>>> load the right BPF program for the panels found on the device.
>>>>
>>>> This is kind of late for serious applications except if we manage to
>>>> solve the bootloader to Linux display engine transition.
>>>>
>>>>>
>>>>> This driver is fully functional and works with both 5" and 7" Touch
>>>>> Display 2 panels for the RaspberryPi. However, it breaks away from the
>>>>> typical panel driver in multiple ways:
>>>>>
>>>>> - BPF programs can only be loaded by userspace. This leaves us with two
>>>>>      choices:
>>>>>
>>>>>      * We prevent the driver from loading until the script itself is
>>>>>        loaded. This has the side effect of preventing any other output to
>>>>>        be used until the initramfs is ran at the earliest, and possibly
>>>>>        ever if the loader isn't installed for example.
>>>>
>>>> This adds a dependency on user-space behavior and if somehow the
>>>> initramfs doesn't load for a reason we won't have a way to display
>>>> an error.
>>>>
>>>>>
>>>>>      * Or we probe the driver all the time, but only report it as connected
>>>>>        once a program has been registered. This is somewhat unconventional,
>>>>>        but allows the other outputs to be functional, *and* allows the user
>>>>>        to force the output if their panel doesn't require any
>>>>>        initialization or during debugging. I chose this solution.
>>>>
>>>> Both options are not really great...
>>>>
>>>>>
>>>>> - It's not a panel driver, but a bridge one, which is also pretty
>>>>>      unconventional. This is required because panel drivers don't have
>>>>>      access to a detect callback that is required for the above, but I also
>>>>>      think that the recent work from Luca blurs the line from panels and
>>>>>      bridges and we'll end up going that road anyway.
>>>>
>>>> On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
>>>> the panel and the DDIC. So being a bridge is fine, but in a general way we lack
>>>> a proper way to describe the display/panel/monitor independently of the DDIC.
>>>>
>>>> At first glance it's a nice driver, but moving the timings into a blob moves something
>>>> into possible proprietary binaries with possible closed licence and distribution
>>>> restriction so it's a downgrade for the same of bringing up a panel faster.
>>>
>>> Quick answer on this, because I had the very same questions regarding
>>> HID-BPF:
>>> - in BPF, you can require (and by default it does) that only GPL
>>> 	compatible BPF programs are loaded, closing the argument of "closed
>>> 	licence and distribution restriction"
>>> - also, a BPF program can be disassembled much easier than a binary
>>> 	blob, and I remember Alexei showing me an example where you get almost
>>> 	the source code from the BPF object in just one pass.
>>
>> Right, it "solve" one of my question, but doesn't really solve the issue
>> of vendors providing "GPL" bpf programs with source available "somewhere".
>>
>> Another big issue is the API, I don't want to keep the current API as-is,
>> we plan to support more advanced panel features and use try to use the
>> atomic states to support rate switching for example, and I'm not confident
>> it's a good idea since there's no "simple" and "forever valid" API
>> to initialize panels...
>>
> 
> That's exactly where BPF shines. The simple rule of thumb is: there is
> no API stability guaranteed. Basically, thanks to the verifier and CORE
> (Compile Once Run Everywhere), you don't need to keep the API stable and
> available forever. There are multiple ways of dealing with it, but the
> gist is that if a BPF is trying to load an "old" API, it will be
> rejected by the verifier.
> 
> Then it's a matter of being nice enough in the kernel and provide ways
> to deal with those.
> 
> To give you a few examples:
> 
> - in HID-BPF, in userspace, we keep old versions of APIs in separate
> 	compiled objects. Each is incremented (by 10 so we have a little bit
> 	of room in the middle). Then the loader tries first
> 	0020-device-with-new-api.bpf.o, and if it fails, it tries
> 	0010-device-with-old-api.bpf.o
> 
> 	I'm not saying we should do the same here, but that's one idea
> 
> - recently, a BPF commit broke the ABI of a function while removing
> 	implicits: bpf_wq_set_callback_impl() was replaced by
> 	bpf_wq_set_callback() with a different number of arguments. I simply
> 	had to add a new function in my header which basically does:
> 
> 	static inline int
> 	hid_bpf_wq_set_callback(struct bpf_wq *wq,
> 	                        int (*callback_fn)(void *, int *, void *),
> 	                        unsigned int flags)
> 	{
>        if (bpf_ksym_exists(bpf_wq_set_callback))
>                return bpf_wq_set_callback(wq, callback_fn, flags);
>        if (bpf_ksym_exists(bpf_wq_set_callback_impl))
>                return bpf_wq_set_callback_impl(wq, callback_fn, flags, NULL);
>    }
> 
>    And then I changed the bpf.c to use hid_bpf_wq_set_callback() and the
>    bpf is compatible with both APIs
> 
> - with CORE, struct fields are relocated on the fly when you load the BPF
>   
>    So for instance, if my BPF only accesses fields .name, .phys and .id
>    in the BPF, I can define:
>    struct hid_device {
>      char                       name[128];
>      char                       phys[64];
>      unsigned int               id;
>    }
> 
>    Then when loading the bpf, the verifier replaces all offset to the
>    ones actually used by the running kernel, and the program loads
>    transparently, even if you add fields before/after or change the
>    fields order in the kernel.
> 
> Changing the mindset is the hardest part of it. But once you are making
> the shift, it's actually much better to work with. It doesn't mean you
> can go yolo. You still need to be careful in your choices knowing the
> impact on your users. But the API at version 0 is not frozen and you
> don't need to maintain it forever, especially if you control the loader
> and the headers used to compile the BPFs.

It's really impressive, but there's no way this would become the de-facto
way to program the panels, and if the motivation is to make it simpler
this implementation requires adding bindings and bpf programs which that
are the same as native panel drivers, but won't be able to work until
user-space starts and will require to be in initramfs or rootfs to
have functional display.

Not sure to understand what are the positive features here
except isolating the panel code in a safe bpf program (is this really needed ?).

I mean the idea is cool and bpf is a nice thing, but I'm really not
convinced about it programming panels.

Neil

> 
> Cheers,
> Benjamin


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
  2026-09-28 19:16   ` Neil Armstrong
@ 2026-09-28 20:40   ` Rob Herring
  2026-09-29  8:39     ` Maxime Ripard
  2026-09-28 20:43   ` Rob Herring (Arm)
  2 siblings, 1 reply; 27+ messages in thread
From: Rob Herring @ 2026-09-28 20:40 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Krzysztof Kozlowski,
	Conor Dooley, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel, Benjamin Tissoires

On Mon, Sep 28, 2026 at 06:22:01PM +0200, Maxime Ripard wrote:
> Most MIPI-DSI panel drivers follow an identical pattern: acquire
> regulators and GPIOs, perform a reset pulse with specific timing,
> send a vendor-supplied sequence of DSI commands, then enable the
> display. The only truly panel-specific part is the init sequence
> and power-on/off timing.
> 
> The panel-mipi-dsi-bpf driver replaces per-panel kernel modules
> with a single generic driver whose panel-specific behavior is
> provided by BPF programs loaded from userspace at runtime,
> following the HID-BPF model. This enables new panel support
> without kernel patches.
> 
> Panel DT nodes use a two-entry compatible with the panel-specific
> string first and "panel-mipi-dsi-bpf" as fallback. The generic
> driver matches on the fallback, while the first compatible is used
> to identify which BPF program to load.

If you need the 1st compatible anyways, what is the point of the second 
one? Also, I assume there is at least some panel supported in the kernel 
you might want to convert to this. That panel would not have the 
fallback (and the DT is fixed).

And I agree with Neil's comment. At least until we start embedding BPF 
into DT directly. ;)

Rob

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
  2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
  2026-09-28 19:16   ` Neil Armstrong
  2026-09-28 20:40   ` Rob Herring
@ 2026-09-28 20:43   ` Rob Herring (Arm)
  2 siblings, 0 replies; 27+ messages in thread
From: Rob Herring (Arm) @ 2026-09-28 20:43 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Florian Fainelli, Jernej Skrabec, Thomas Zimmermann,
	linux-rpi-kernel, Javier Martinez Canillas, Neil Armstrong,
	Andrzej Hajda, Dave Stevenson, Maarten Lankhorst,
	Nick Desaulniers, Krzysztof Kozlowski, dri-devel, linux-kernel,
	Jessica Zhang, Albert Esteve, linux-arm-kernel, Jonas Karlman,
	Bill Wendling, David Airlie, Robert Foss, Laurent Pinchart,
	Broadcom internal kernel review list, devicetree, bpf,
	Benjamin Tissoires, Simona Vetter, Luca Ceresoli, llvm,
	Justin Stitt, Conor Dooley, Nathan Chancellor


On Mon, 28 Sep 2026 18:22:01 +0200, Maxime Ripard wrote:
> Most MIPI-DSI panel drivers follow an identical pattern: acquire
> regulators and GPIOs, perform a reset pulse with specific timing,
> send a vendor-supplied sequence of DSI commands, then enable the
> display. The only truly panel-specific part is the init sequence
> and power-on/off timing.
> 
> The panel-mipi-dsi-bpf driver replaces per-panel kernel modules
> with a single generic driver whose panel-specific behavior is
> provided by BPF programs loaded from userspace at runtime,
> following the HID-BPF model. This enables new panel support
> without kernel patches.
> 
> Panel DT nodes use a two-entry compatible with the panel-specific
> string first and "panel-mipi-dsi-bpf" as fallback. The generic
> driver matches on the fallback, while the first compatible is used
> to identify which BPF program to load.
> 
> Six normalized optional regulator supplies cover ~95% of existing
> MIPI-DSI panels: vcc (IC core), iovcc (I/O interface), avdd/avee
> (positive/negative analog), and elvdd/elvss (OLED EL driver).
> 
> Signed-off-by: Maxime Ripard <mripard@kernel.org>
> ---
>  .../bindings/display/panel/panel-mipi-dsi-bpf.yaml | 184 +++++++++++++++++++++
>  1 file changed, 184 insertions(+)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (elida,kd35t133): 'dsi-lanes', 'height-mm', 'mode-lpm', 'mode-video', 'mode-video-burst', 'panel-timing', 'vcc-supply', 'width-mm' do not match any of the regexes: '^pinctrl-[0-9]+$'
	from schema $id: http://devicetree.org/schemas/display/panel/elida,kd35t133.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (elida,kd35t133): compatible: ['elida,kd35t133', 'panel-mipi-dsi-bpf'] is too long
	from schema $id: http://devicetree.org/schemas/display/panel/elida,kd35t133.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (elida,kd35t133): 'vdd-supply' is a required property
	from schema $id: http://devicetree.org/schemas/display/panel/elida,kd35t133.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (boe,bf060y8m-aj0): 'dsi-lanes', 'iovcc-supply', 'mode-video', 'panel-timing' do not match any of the regexes: '^pinctrl-[0-9]+$'
	from schema $id: http://devicetree.org/schemas/display/panel/boe,bf060y8m-aj0.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (boe,bf060y8m-aj0): compatible: ['boe,bf060y8m-aj0', 'panel-mipi-dsi-bpf'] is too long
	from schema $id: http://devicetree.org/schemas/display/panel/boe,bf060y8m-aj0.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (boe,bf060y8m-aj0): 'vci-supply' is a required property
	from schema $id: http://devicetree.org/schemas/display/panel/boe,bf060y8m-aj0.yaml
Documentation/devicetree/bindings/display/panel/panel-mipi-dsi-bpf.example.dtb: panel@0 (boe,bf060y8m-aj0): 'vddio-supply' is a required property
	from schema $id: http://devicetree.org/schemas/display/panel/boe,bf060y8m-aj0.yaml

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260928-drm-mipi-dsi-panel-ebpf-v1-1-5244926aace4@kernel.org

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:37 ` [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Laurent Pinchart
  2026-09-28 17:31   ` Benjamin Tissoires
@ 2026-09-29  6:54   ` Maxime Ripard
  1 sibling, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-29  6:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Benjamin Tissoires

[-- Attachment #1: Type: text/plain, Size: 3178 bytes --]

Hi Laurent,

On Mon, Sep 28, 2026 at 07:37:11PM +0300, Laurent Pinchart wrote:
> On Mon, Sep 28, 2026 at 06:22:00PM +0200, Maxime Ripard wrote:
> > Panels in general, and MIPI-DSI panels in particular, are pretty
> > difficult to support and require pretty much a panel driver for each
> > panel produced. Most of them are pretty simple, and require an opaque
> > initialization sequence that is usually poorly documented.
> > 
> > This creates a tension between OEMs and distros because OEMs will
> > typically get a new panel to react to a sourcing issue during
> > production, and thus need some swift turnaround between getting their
> > new panel and it being operational in the OS. Distributions on the other
> > hand can take years to ship a kernel with that new panel driver.
> > 
> > To solve this, I followed the example of HID-BPF and wrote a panel
> > driver that will rely on BPF programs to perform the panel
> > initialization. That way, we can ship the programs separately from the
> > kernel, and with a different lifecycle. If this driver is accepted, the
> > plan is to have a userspace component started by udev to identify and
> > load the right BPF program for the panels found on the device.
> 
> Interesting idea.
> 
> How would that work for devices that want early display support ? In
> particular, how does it interact with your recent work on "fastboot"
> support (with the kernel drivers taking over a running display
> configured by the boot loader) ?

That's a very good question, and I haven't really thought about it tbh.
I don't think it would be super hard to implement though. For panels
itself, we don't typically track anything in the state. panel_bridge
however would still need to fill its own bridge state but that only
contains the bus formats and flags which can be infered from the DT.

Another thing we might need for the connector is whether it's active or
not, but panel ICs typically don't tell you that anyway, and you can
always infer it from the encoder / previous bridge state.

And then, for the first kernel modeset, we end up in the same situation
than a regular driver I suppose.

> I'm also wondering about the incentives for vendors to upstream the
> code. How will we avoid having a proliferation of out-of-tree drivers ?

I'm not really concerned about that part, because:

* You can *already* support a panel with a proprietary out-of-tree
  module, and it hasn't really affected us so far

* In fact, it's easier to decompile an hypothetical proprietary BPF
  program than an compiled .ko file (that we wouldn't be able to load
  anyway), so if we really end up there, it would be easy for us to get
  to the source code again.

* And panels are so trivial that I don't really see the importance of
  having support for them in the kernel anyway. It's constant tedious
  work that is barely documented, fragile, and provides very little
  value. And the vast majority of the panels are not supported anyway
  except for some willing OEMs (that are using panel-edp anyway) and
  willing hobbyists/consultants. So we probably wouldn't miss much.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:39 ` Neil Armstrong
  2026-09-28 17:24   ` Benjamin Tissoires
@ 2026-09-29  7:13   ` Maxime Ripard
  2026-09-29  7:46     ` Benjamin Tissoires
  1 sibling, 1 reply; 27+ messages in thread
From: Maxime Ripard @ 2026-09-29  7:13 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Jessica Zhang, David Airlie, Simona Vetter, Maarten Lankhorst,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel, Benjamin Tissoires

[-- Attachment #1: Type: text/plain, Size: 4563 bytes --]

Hi Neil,

On Mon, Sep 28, 2026 at 06:39:58PM +0200, Neil Armstrong wrote:
> On 9/28/26 18:22, Maxime Ripard wrote:
> > Panels in general, and MIPI-DSI panels in particular, are pretty
> > difficult to support and require pretty much a panel driver for each
> > panel produced. Most of them are pretty simple, and require an opaque
> > initialization sequence that is usually poorly documented.
> > 
> > This creates a tension between OEMs and distros because OEMs will
> > typically get a new panel to react to a sourcing issue during
> > production, and thus need some swift turnaround between getting their
> > new panel and it being operational in the OS. Distributions on the other
> > hand can take years to ship a kernel with that new panel driver.
> > 
> > To solve this, I followed the example of HID-BPF and wrote a panel
> > driver that will rely on BPF programs to perform the panel
> > initialization. That way, we can ship the programs separately from the
> > kernel, and with a different lifecycle. If this driver is accepted, the
> > plan is to have a userspace component started by udev to identify and
> > load the right BPF program for the panels found on the device.
> 
> This is kind of late for serious applications except if we manage to
> solve the bootloader to Linux display engine transition.

Virtually all "generic" distributions are shipping the panel as modules
today anyway, because anything else is nothing but impractical. But
maybe you don't consider them serious enough. Also, applications live in
userspace already, so can be ran after this driver would be initialized
anyway.

> > This driver is fully functional and works with both 5" and 7" Touch
> > Display 2 panels for the RaspberryPi. However, it breaks away from the
> > typical panel driver in multiple ways:
> > 
> > - BPF programs can only be loaded by userspace. This leaves us with two
> >    choices:
> > 
> >    * We prevent the driver from loading until the script itself is
> >      loaded. This has the side effect of preventing any other output to
> >      be used until the initramfs is ran at the earliest, and possibly
> >      ever if the loader isn't installed for example.
> 
> This adds a dependency on user-space behavior and if somehow the
> initramfs doesn't load for a reason we won't have a way to display
> an error.

Yes, if an error happens before the DRM driver loads, it won't be shown
on the screen. This is already the case for any panel driver today on
any major !embedded distribution. And with built-in drivers, this can
also happen before or while the DRM driver loads.

The solution is always the same though: load simpledrm first, move to
the proper DRM device once it's functional. It still works with this
solution.

> > 
> >    * Or we probe the driver all the time, but only report it as connected
> >      once a program has been registered. This is somewhat unconventional,
> >      but allows the other outputs to be functional, *and* allows the user
> >      to force the output if their panel doesn't require any
> >      initialization or during debugging. I chose this solution.
> 
> Both options are not really great...

Feel free to make any suggestions

> > 
> > - It's not a panel driver, but a bridge one, which is also pretty
> >    unconventional. This is required because panel drivers don't have
> >    access to a detect callback that is required for the above, but I also
> >    think that the recent work from Luca blurs the line from panels and
> >    bridges and we'll end up going that road anyway.
> 
> On this point, DDIC _are_ bridges,

I have no idea what a DDIC mean.

> but in the current panel API we blur the line between the panel and
> the DDIC. So being a bridge is fine, but in a general way we lack a
> proper way to describe the display/panel/monitor independently of the
> DDIC.
> 
> At first glance it's a nice driver, but moving the timings into a blob

Let's not kid ourselves, it's *already* a blob. We just sugar-coated it
enough that we can be happy and call it GPL.

> moves something into possible proprietary binaries with possible
> closed licence and distribution restriction so it's a downgrade for
> the same of bringing up a panel faster.

We can already make a proprietary, out-of-tree, panel driver today. That
being said, the only license we allow for BPF programs here is GPL, so
if anything it would be less of a concern for this than it would be for
regular panels.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 19:20     ` Neil Armstrong
  2026-09-28 19:48       ` Benjamin Tissoires
@ 2026-09-29  7:27       ` Maxime Ripard
  1 sibling, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-29  7:27 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Benjamin Tissoires, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 5718 bytes --]

On Mon, Sep 28, 2026 at 09:20:12PM +0200, Neil Armstrong wrote:
> On 9/28/26 19:24, Benjamin Tissoires wrote:
> > On Sep 28 2026, Neil Armstrong wrote:
> > > Hi,
> > > 
> > > On 9/28/26 18:22, Maxime Ripard wrote:
> > > > Hi,
> > > > 
> > > > Panels in general, and MIPI-DSI panels in particular, are pretty
> > > > difficult to support and require pretty much a panel driver for each
> > > > panel produced. Most of them are pretty simple, and require an opaque
> > > > initialization sequence that is usually poorly documented.
> > > > 
> > > > This creates a tension between OEMs and distros because OEMs will
> > > > typically get a new panel to react to a sourcing issue during
> > > > production, and thus need some swift turnaround between getting their
> > > > new panel and it being operational in the OS. Distributions on the other
> > > > hand can take years to ship a kernel with that new panel driver.
> > > > 
> > > > To solve this, I followed the example of HID-BPF and wrote a panel
> > > > driver that will rely on BPF programs to perform the panel
> > > > initialization. That way, we can ship the programs separately from the
> > > > kernel, and with a different lifecycle. If this driver is accepted, the
> > > > plan is to have a userspace component started by udev to identify and
> > > > load the right BPF program for the panels found on the device.
> > > 
> > > This is kind of late for serious applications except if we manage to
> > > solve the bootloader to Linux display engine transition.
> > > 
> > > > 
> > > > This driver is fully functional and works with both 5" and 7" Touch
> > > > Display 2 panels for the RaspberryPi. However, it breaks away from the
> > > > typical panel driver in multiple ways:
> > > > 
> > > > - BPF programs can only be loaded by userspace. This leaves us with two
> > > >     choices:
> > > > 
> > > >     * We prevent the driver from loading until the script itself is
> > > >       loaded. This has the side effect of preventing any other output to
> > > >       be used until the initramfs is ran at the earliest, and possibly
> > > >       ever if the loader isn't installed for example.
> > > 
> > > This adds a dependency on user-space behavior and if somehow the
> > > initramfs doesn't load for a reason we won't have a way to display
> > > an error.
> > > 
> > > > 
> > > >     * Or we probe the driver all the time, but only report it as connected
> > > >       once a program has been registered. This is somewhat unconventional,
> > > >       but allows the other outputs to be functional, *and* allows the user
> > > >       to force the output if their panel doesn't require any
> > > >       initialization or during debugging. I chose this solution.
> > > 
> > > Both options are not really great...
> > > 
> > > > 
> > > > - It's not a panel driver, but a bridge one, which is also pretty
> > > >     unconventional. This is required because panel drivers don't have
> > > >     access to a detect callback that is required for the above, but I also
> > > >     think that the recent work from Luca blurs the line from panels and
> > > >     bridges and we'll end up going that road anyway.
> > > 
> > > On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
> > > the panel and the DDIC. So being a bridge is fine, but in a general way we lack
> > > a proper way to describe the display/panel/monitor independently of the DDIC.
> > > 
> > > At first glance it's a nice driver, but moving the timings into a blob moves something
> > > into possible proprietary binaries with possible closed licence and distribution
> > > restriction so it's a downgrade for the same of bringing up a panel faster.
> > 
> > Quick answer on this, because I had the very same questions regarding
> > HID-BPF:
> > - in BPF, you can require (and by default it does) that only GPL
> > 	compatible BPF programs are loaded, closing the argument of "closed
> > 	licence and distribution restriction"
> > - also, a BPF program can be disassembled much easier than a binary
> > 	blob, and I remember Alexei showing me an example where you get almost
> > 	the source code from the BPF object in just one pass.
> 
> Right, it "solve" one of my question, but doesn't really solve the issue
> of vendors providing "GPL" bpf programs with source available "somewhere".

Would you be ok if I was to make a tool to decompile a BPF program into its
source file equivalent?

> Another big issue is the API, I don't want to keep the current API as-is,
> we plan to support more advanced panel features and use try to use the
> atomic states to support rate switching for example, and I'm not confident
> it's a good idea since there's no "simple" and "forever valid" API
> to initialize panels...

So, a couple of things here. First, I really don't think we should
extend the panel API, like at all. But let's discuss that at Plumbers, I
don't think it's very relevant to this discussion anyway.

Second, you don't have to use this driver, like, at all. For anything
more complicated than what this driver can provide, I totally expect to
still merge dedicated panel drivers if it makes sense. I also expect
that this driver would be enough for 90% of our panel drivers and
would allow us to support most of the cruft.

Finally, the BPF API is flexible. You can extend it later on and old
programs would still work. The verifier would fail only if a program
uses a new function in a kernel that doesn't support it. I was kind of
expecting to put drm_display_mode in there at some point, if the state
makes sense then why not.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 20:36         ` Neil Armstrong
@ 2026-09-29  7:41           ` Maxime Ripard
  2026-09-29  7:55             ` Javier Martinez Canillas
  0 siblings, 1 reply; 27+ messages in thread
From: Maxime Ripard @ 2026-09-29  7:41 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Benjamin Tissoires, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 10073 bytes --]

On Mon, Sep 28, 2026 at 10:36:18PM +0200, Neil Armstrong wrote:
> On 9/28/26 21:48, Benjamin Tissoires wrote:
> > On Sep 28 2026, Neil Armstrong wrote:
> > > On 9/28/26 19:24, Benjamin Tissoires wrote:
> > > > On Sep 28 2026, Neil Armstrong wrote:
> > > > > Hi,
> > > > > 
> > > > > On 9/28/26 18:22, Maxime Ripard wrote:
> > > > > > Hi,
> > > > > > 
> > > > > > Panels in general, and MIPI-DSI panels in particular, are pretty
> > > > > > difficult to support and require pretty much a panel driver for each
> > > > > > panel produced. Most of them are pretty simple, and require an opaque
> > > > > > initialization sequence that is usually poorly documented.
> > > > > > 
> > > > > > This creates a tension between OEMs and distros because OEMs will
> > > > > > typically get a new panel to react to a sourcing issue during
> > > > > > production, and thus need some swift turnaround between getting their
> > > > > > new panel and it being operational in the OS. Distributions on the other
> > > > > > hand can take years to ship a kernel with that new panel driver.
> > > > > > 
> > > > > > To solve this, I followed the example of HID-BPF and wrote a panel
> > > > > > driver that will rely on BPF programs to perform the panel
> > > > > > initialization. That way, we can ship the programs separately from the
> > > > > > kernel, and with a different lifecycle. If this driver is accepted, the
> > > > > > plan is to have a userspace component started by udev to identify and
> > > > > > load the right BPF program for the panels found on the device.
> > > > > 
> > > > > This is kind of late for serious applications except if we manage to
> > > > > solve the bootloader to Linux display engine transition.
> > > > > 
> > > > > > 
> > > > > > This driver is fully functional and works with both 5" and 7" Touch
> > > > > > Display 2 panels for the RaspberryPi. However, it breaks away from the
> > > > > > typical panel driver in multiple ways:
> > > > > > 
> > > > > > - BPF programs can only be loaded by userspace. This leaves us with two
> > > > > >      choices:
> > > > > > 
> > > > > >      * We prevent the driver from loading until the script itself is
> > > > > >        loaded. This has the side effect of preventing any other output to
> > > > > >        be used until the initramfs is ran at the earliest, and possibly
> > > > > >        ever if the loader isn't installed for example.
> > > > > 
> > > > > This adds a dependency on user-space behavior and if somehow the
> > > > > initramfs doesn't load for a reason we won't have a way to display
> > > > > an error.
> > > > > 
> > > > > > 
> > > > > >      * Or we probe the driver all the time, but only report it as connected
> > > > > >        once a program has been registered. This is somewhat unconventional,
> > > > > >        but allows the other outputs to be functional, *and* allows the user
> > > > > >        to force the output if their panel doesn't require any
> > > > > >        initialization or during debugging. I chose this solution.
> > > > > 
> > > > > Both options are not really great...
> > > > > 
> > > > > > 
> > > > > > - It's not a panel driver, but a bridge one, which is also pretty
> > > > > >      unconventional. This is required because panel drivers don't have
> > > > > >      access to a detect callback that is required for the above, but I also
> > > > > >      think that the recent work from Luca blurs the line from panels and
> > > > > >      bridges and we'll end up going that road anyway.
> > > > > 
> > > > > On this point, DDIC _are_ bridges, but in the current panel API we blur the line between
> > > > > the panel and the DDIC. So being a bridge is fine, but in a general way we lack
> > > > > a proper way to describe the display/panel/monitor independently of the DDIC.
> > > > > 
> > > > > At first glance it's a nice driver, but moving the timings into a blob moves something
> > > > > into possible proprietary binaries with possible closed licence and distribution
> > > > > restriction so it's a downgrade for the same of bringing up a panel faster.
> > > > 
> > > > Quick answer on this, because I had the very same questions regarding
> > > > HID-BPF:
> > > > - in BPF, you can require (and by default it does) that only GPL
> > > > 	compatible BPF programs are loaded, closing the argument of "closed
> > > > 	licence and distribution restriction"
> > > > - also, a BPF program can be disassembled much easier than a binary
> > > > 	blob, and I remember Alexei showing me an example where you get almost
> > > > 	the source code from the BPF object in just one pass.
> > > 
> > > Right, it "solve" one of my question, but doesn't really solve the issue
> > > of vendors providing "GPL" bpf programs with source available "somewhere".
> > > 
> > > Another big issue is the API, I don't want to keep the current API as-is,
> > > we plan to support more advanced panel features and use try to use the
> > > atomic states to support rate switching for example, and I'm not confident
> > > it's a good idea since there's no "simple" and "forever valid" API
> > > to initialize panels...
> > > 
> > 
> > That's exactly where BPF shines. The simple rule of thumb is: there is
> > no API stability guaranteed. Basically, thanks to the verifier and CORE
> > (Compile Once Run Everywhere), you don't need to keep the API stable and
> > available forever. There are multiple ways of dealing with it, but the
> > gist is that if a BPF is trying to load an "old" API, it will be
> > rejected by the verifier.
> > 
> > Then it's a matter of being nice enough in the kernel and provide ways
> > to deal with those.
> > 
> > To give you a few examples:
> > 
> > - in HID-BPF, in userspace, we keep old versions of APIs in separate
> > 	compiled objects. Each is incremented (by 10 so we have a little bit
> > 	of room in the middle). Then the loader tries first
> > 	0020-device-with-new-api.bpf.o, and if it fails, it tries
> > 	0010-device-with-old-api.bpf.o
> > 
> > 	I'm not saying we should do the same here, but that's one idea
> > 
> > - recently, a BPF commit broke the ABI of a function while removing
> > 	implicits: bpf_wq_set_callback_impl() was replaced by
> > 	bpf_wq_set_callback() with a different number of arguments. I simply
> > 	had to add a new function in my header which basically does:
> > 
> > 	static inline int
> > 	hid_bpf_wq_set_callback(struct bpf_wq *wq,
> > 	                        int (*callback_fn)(void *, int *, void *),
> > 	                        unsigned int flags)
> > 	{
> >        if (bpf_ksym_exists(bpf_wq_set_callback))
> >                return bpf_wq_set_callback(wq, callback_fn, flags);
> >        if (bpf_ksym_exists(bpf_wq_set_callback_impl))
> >                return bpf_wq_set_callback_impl(wq, callback_fn, flags, NULL);
> >    }
> > 
> >    And then I changed the bpf.c to use hid_bpf_wq_set_callback() and the
> >    bpf is compatible with both APIs
> > 
> > - with CORE, struct fields are relocated on the fly when you load the BPF
> >    So for instance, if my BPF only accesses fields .name, .phys and .id
> >    in the BPF, I can define:
> >    struct hid_device {
> >      char                       name[128];
> >      char                       phys[64];
> >      unsigned int               id;
> >    }
> > 
> >    Then when loading the bpf, the verifier replaces all offset to the
> >    ones actually used by the running kernel, and the program loads
> >    transparently, even if you add fields before/after or change the
> >    fields order in the kernel.
> > 
> > Changing the mindset is the hardest part of it. But once you are making
> > the shift, it's actually much better to work with. It doesn't mean you
> > can go yolo. You still need to be careful in your choices knowing the
> > impact on your users. But the API at version 0 is not frozen and you
> > don't need to maintain it forever, especially if you control the loader
> > and the headers used to compile the BPFs.
> 
> It's really impressive, but there's no way this would become the de-facto
> way to program the panels

Nobody said it would? And like I said in my previous mail, I actually
expect us to keep merging panel drivers. Do I think it should become the
go-to solution for simple panels? yes. But we can always make
exceptions, and for more complex panels we should totally do a more
complex driver. Like HID has been doing.

> and if the motivation is to make it simpler this implementation
> requires adding bindings and bpf programs which that are the same as
> native panel drivers, but won't be able to work until user-space
> starts and will require to be in initramfs or rootfs to have
> functional display.
> 
> Not sure to understand what are the positive features here except
> isolating the panel code in a safe bpf program (is this really needed
> ?).

From the cover letter:

"""
Panels in general, and MIPI-DSI panels in particular, are pretty
difficult to support and require pretty much a panel driver for each
panel produced. Most of them are pretty simple, and require an opaque
initialization sequence that is usually poorly documented.

This creates a tension between OEMs and distros because OEMs will
typically get a new panel to react to a sourcing issue during
production, and thus need some swift turnaround between getting their
new panel and it being operational in the OS. Distributions on the other
hand can take years to ship a kernel with that new panel driver.

To solve this, I followed the example of HID-BPF and wrote a panel
driver that will rely on BPF programs to perform the panel
initialization. That way, we can ship the programs separately from the
kernel, and with a different lifecycle.
"""

You can disagree with the solution, that's fair, we can also discuss on
how to solve this problem, but I'd appreciate it if you weren't claiming
it's all useless.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-29  7:13   ` Maxime Ripard
@ 2026-09-29  7:46     ` Benjamin Tissoires
  0 siblings, 0 replies; 27+ messages in thread
From: Benjamin Tissoires @ 2026-09-29  7:46 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On Sep 29 2026, Maxime Ripard wrote:
> Hi Neil,
> 
> On Mon, Sep 28, 2026 at 06:39:58PM +0200, Neil Armstrong wrote:
> > On 9/28/26 18:22, Maxime Ripard wrote:
> > > Panels in general, and MIPI-DSI panels in particular, are pretty
> > > difficult to support and require pretty much a panel driver for each
> > > panel produced. Most of them are pretty simple, and require an opaque
> > > initialization sequence that is usually poorly documented.
> > > 
> > > This creates a tension between OEMs and distros because OEMs will
> > > typically get a new panel to react to a sourcing issue during
> > > production, and thus need some swift turnaround between getting their
> > > new panel and it being operational in the OS. Distributions on the other
> > > hand can take years to ship a kernel with that new panel driver.
> > > 
> > > To solve this, I followed the example of HID-BPF and wrote a panel
> > > driver that will rely on BPF programs to perform the panel
> > > initialization. That way, we can ship the programs separately from the
> > > kernel, and with a different lifecycle. If this driver is accepted, the
> > > plan is to have a userspace component started by udev to identify and
> > > load the right BPF program for the panels found on the device.
> > 
> > This is kind of late for serious applications except if we manage to
> > solve the bootloader to Linux display engine transition.
> 
> Virtually all "generic" distributions are shipping the panel as modules
> today anyway, because anything else is nothing but impractical. But
> maybe you don't consider them serious enough. Also, applications live in
> userspace already, so can be ran after this driver would be initialized
> anyway.

To add a little bit to what Maxime said, about the "userspace loader".

One thing I initially worked on on HID-BPF was a kernel-side loader.
Because BPF allows you to run the syscalls from the kernel itself,
nothing prevents you to make an extra "loader" module with the bpf.o
embedded in it that would do the loading of the BPF from within the
kernel itself.

On HID-BPF, my userspace loader was too fancy at some point and the
kernel loader started to be too big. Especially because we depend on
"potentially" connected devices, and embedding all the BPFs in the
module started to be way too much.

But for panels and embedded systems, it would totally make sense to have
a Kconfig that cherry picks which BPF needs to be embedded in the
panel-bpf-loader module, and that module gets loaded/included in the
vmlinux directly, and at probe time, it just attaches the struct_ops.

Well, of course this surely can be integrated with some DT definition
somewhere, but I haven't pushed the thinking too much.

Anyway, no userspace/initramfs required in this case!

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-29  7:41           ` Maxime Ripard
@ 2026-09-29  7:55             ` Javier Martinez Canillas
  0 siblings, 0 replies; 27+ messages in thread
From: Javier Martinez Canillas @ 2026-09-29  7:55 UTC (permalink / raw)
  To: Maxime Ripard, Neil Armstrong
  Cc: Benjamin Tissoires, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel

Maxime Ripard <mripard@kernel.org> writes:

> On Mon, Sep 28, 2026 at 10:36:18PM +0200, Neil Armstrong wrote:
>> On 9/28/26 21:48, Benjamin Tissoires wrote:
>> > On Sep 28 2026, Neil Armstrong wrote:
>> > > On 9/28/26 19:24, Benjamin Tissoires wrote:
>> > > > On Sep 28 2026, Neil Armstrong wrote:
>> > > > > Hi,
>> > > > > 
>> > > > > On 9/28/26 18:22, Maxime Ripard wrote:
>> > > > > > Hi,
>> > > > > > 
>> > > > > > Panels in general, and MIPI-DSI panels in particular, are pretty
>> > > > > > difficult to support and require pretty much a panel driver for each
>> > > > > > panel produced. Most of them are pretty simple, and require an opaque
>> > > > > > initialization sequence that is usually poorly documented.
>> > > > > > 
>> > > > > > This creates a tension between OEMs and distros because OEMs will
>> > > > > > typically get a new panel to react to a sourcing issue during
>> > > > > > production, and thus need some swift turnaround between getting their
>> > > > > > new panel and it being operational in the OS. Distributions on the other
>> > > > > > hand can take years to ship a kernel with that new panel driver.
>> > > > > > 
>> > > > > > To solve this, I followed the example of HID-BPF and wrote a panel
>> > > > > > driver that will rely on BPF programs to perform the panel
>> > > > > > initialization. That way, we can ship the programs separately from the
>> > > > > > kernel, and with a different lifecycle. If this driver is accepted, the
>> > > > > > plan is to have a userspace component started by udev to identify and
>> > > > > > load the right BPF program for the panels found on the device.
>> > > > > 
>> > > > > This is kind of late for serious applications except if we manage to
>> > > > > solve the bootloader to Linux display engine transition.
>> > > > > 

Besides what Maxime already mentioned (that most general purpose Linux distributions
built the drivers as modules anyways), it doesn't have to be mutually exclusive.

A simple panel could be supported using this BPF-based driver and then a panel driver
added to the kernel, if is found that some applications need to have it built-in and
earlier in the boot path.

I don't see why this would be any different than HDI-BPF or other BPF-based infra,
such as sched_ext.

-- 
Best regards,

Javier Martinez Canillas
Core Platforms
Red Hat


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding
  2026-09-28 20:40   ` Rob Herring
@ 2026-09-29  8:39     ` Maxime Ripard
  0 siblings, 0 replies; 27+ messages in thread
From: Maxime Ripard @ 2026-09-29  8:39 UTC (permalink / raw)
  To: Rob Herring
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Thomas Zimmermann, Krzysztof Kozlowski,
	Conor Dooley, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel, Benjamin Tissoires

[-- Attachment #1: Type: text/plain, Size: 3848 bytes --]

Hi,

I'll merge the two discussions in that thread.

On Mon, Sep 28, 2026 at 03:40:59PM -0500, Rob Herring wrote:
> On Mon, Sep 28, 2026 at 06:22:01PM +0200, Maxime Ripard wrote:
> > Most MIPI-DSI panel drivers follow an identical pattern: acquire
> > regulators and GPIOs, perform a reset pulse with specific timing,
> > send a vendor-supplied sequence of DSI commands, then enable the
> > display. The only truly panel-specific part is the init sequence
> > and power-on/off timing.
> > 
> > The panel-mipi-dsi-bpf driver replaces per-panel kernel modules
> > with a single generic driver whose panel-specific behavior is
> > provided by BPF programs loaded from userspace at runtime,
> > following the HID-BPF model. This enables new panel support
> > without kernel patches.
> > 
> > Panel DT nodes use a two-entry compatible with the panel-specific
> > string first and "panel-mipi-dsi-bpf" as fallback. The generic
> > driver matches on the fallback, while the first compatible is used
> > to identify which BPF program to load.
> 
> If you need the 1st compatible anyways, what is the point of the
> second one?

The whole point of this driver is that you don't need to modify the
kernel when you add support for a new panel.

> Also, I assume there is at least some panel supported in the kernel
> you might want to convert to this. That panel would not have the
> fallback (and the DT is fixed).

Yeah, that's true. I'd still need to identify the parts though. I guess
using a generic compatible but a specific model would work?

> And I agree with Neil's comment. At least until we start embedding BPF 
> into DT directly. ;)

And from Neil:

> I don't see how this can be a valid hardware description, bfp is a
> software implementation and has nothing to do in the bindings.

I'm quite a bit surprised by that argument though.

We have in the main dt-schema repos bindings like:

https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/options/u-boot.yaml
https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/bootph.yaml
https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/post-init-providers.yaml

Or, in Linux:
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/misc/google,android-pipe.yaml
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/sound/simple-card.yaml

All of them have been reviewed or acked by you, and yet none of them
relate to any hardware description.

panel-mipi-dsi-spi is a generic panel that will load a firmware, and
quite similar to this one. google,android-pipe and qcom,fastrpc don't
attach to anything and will just open a tunnel to userspace, which is
somewhat equivalent but more dramatic than what this driver is doing.
simple-card or its variations will just instantiate a kernel driver from
the DT and is used pretty much everywhere.

I reused the binding from panel-mipi-dsi-spi for this. It was reviewed
by rob, and acked by a panel maintainer, and 4 years ago, so we're way
past the "oh but we didn't know what we were doing back then" argument.

So, let's phrase this differently: what's different about the
description than panel-mipi-dsi-spi, or any other binding already in
tree?

If it's the BPF part, BPF is not Linux-only, and there's hardware with
direct BPF support these days, so it can be considered OS-agnostic and
not an implementation detail.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
                   ` (7 preceding siblings ...)
  2026-09-28 16:39 ` Neil Armstrong
@ 2026-09-29  9:03 ` Jani Nikula
  2026-09-29  9:32   ` Benjamin Tissoires
  8 siblings, 1 reply; 27+ messages in thread
From: Jani Nikula @ 2026-09-29  9:03 UTC (permalink / raw)
  To: Maxime Ripard, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Albert Esteve,
	Dave Stevenson, Javier Martinez Canillas, dri-devel, devicetree,
	linux-kernel, bpf, llvm, linux-rpi-kernel, linux-arm-kernel,
	Maxime Ripard, Benjamin Tissoires

On Mon, 28 Sep 2026, Maxime Ripard <mripard@kernel.org> wrote:
> Panels in general, and MIPI-DSI panels in particular, are pretty
> difficult to support and require pretty much a panel driver for each
> panel produced. Most of them are pretty simple, and require an opaque
> initialization sequence that is usually poorly documented.
>
> This creates a tension between OEMs and distros because OEMs will
> typically get a new panel to react to a sourcing issue during
> production, and thus need some swift turnaround between getting their
> new panel and it being operational in the OS. Distributions on the other
> hand can take years to ship a kernel with that new panel driver.
>
> To solve this, I followed the example of HID-BPF and wrote a panel
> driver that will rely on BPF programs to perform the panel
> initialization. That way, we can ship the programs separately from the
> kernel, and with a different lifecycle. If this driver is accepted, the
> plan is to have a userspace component started by udev to identify and
> load the right BPF program for the panels found on the device.

I'd think for most panels it would suffice to have a declarative
language describing what to do at what points in time. For example, at
poweron, enable this GPIO, wait a little, write this set of DSI
commands, etc. You rarely need actual programming, or even ability to
read-modify-write something.

It could be, say, YAML converted to some binary representation. Maybe
that could be in the DT, or maybe you could override it with the
firmware loader. And obviously any vendor "blob" could be trivially
converted back to YAML and upstreamed.

Where that falls short, you could always have a dedicated driver, or
extend the declarative stuff.

Now, the question is, how much more BPF solves over that, without
requiring a dedicated driver, and is the added complexity worth it?

FWIW, on the Intel platforms that support DSI, all of the
initialization, poweron/poweroff, backlight on/off etc. sequences like
that are stored in the BIOS, defined by the OEM. A plethora of DSI
panels, one driver. Don't look at it for examples how to implement it,
but I think the basic idea is workable.


BR,
Jani.


-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver
  2026-09-29  9:03 ` Jani Nikula
@ 2026-09-29  9:32   ` Benjamin Tissoires
  0 siblings, 0 replies; 27+ messages in thread
From: Benjamin Tissoires @ 2026-09-29  9:32 UTC (permalink / raw)
  To: Jani Nikula
  Cc: Maxime Ripard, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, Florian Fainelli,
	Broadcom internal kernel review list, Andrzej Hajda, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Albert Esteve, Dave Stevenson, Javier Martinez Canillas,
	dri-devel, devicetree, linux-kernel, bpf, llvm, linux-rpi-kernel,
	linux-arm-kernel

On Sep 29 2026, Jani Nikula wrote:
> On Mon, 28 Sep 2026, Maxime Ripard <mripard@kernel.org> wrote:
> > Panels in general, and MIPI-DSI panels in particular, are pretty
> > difficult to support and require pretty much a panel driver for each
> > panel produced. Most of them are pretty simple, and require an opaque
> > initialization sequence that is usually poorly documented.
> >
> > This creates a tension between OEMs and distros because OEMs will
> > typically get a new panel to react to a sourcing issue during
> > production, and thus need some swift turnaround between getting their
> > new panel and it being operational in the OS. Distributions on the other
> > hand can take years to ship a kernel with that new panel driver.
> >
> > To solve this, I followed the example of HID-BPF and wrote a panel
> > driver that will rely on BPF programs to perform the panel
> > initialization. That way, we can ship the programs separately from the
> > kernel, and with a different lifecycle. If this driver is accepted, the
> > plan is to have a userspace component started by udev to identify and
> > load the right BPF program for the panels found on the device.
> 
> I'd think for most panels it would suffice to have a declarative
> language describing what to do at what points in time. For example, at
> poweron, enable this GPIO, wait a little, write this set of DSI
> commands, etc. You rarely need actual programming, or even ability to
> read-modify-write something.

TBH, that's exactly how Maxime presented the idea to me before
submitting this.

> 
> It could be, say, YAML converted to some binary representation. Maybe
> that could be in the DT, or maybe you could override it with the
> firmware loader. And obviously any vendor "blob" could be trivially
> converted back to YAML and upstreamed.

Ouch. This immediately raises the "let's implement a parser in the
kernel" flag, or "let's create a new langage".

That's exactly what BPF is: ou have a source file (in C, rust, whatever)
that is converted to a binary blob that the kernel already knows how to
use and that is safe to load, and we can easily go back to the sources.

> 
> Where that falls short, you could always have a dedicated driver, or
> extend the declarative stuff.

See Maxime's answers: yes, falling back to dedicated driver is still
encouraged.

> 
> Now, the question is, how much more BPF solves over that, without
> requiring a dedicated driver, and is the added complexity worth it?

You'd still need a dedicated driver for your new blob parser, with far
fewer people who had had a look at it than BPF who has been working on
for several years.

The integration of BPF and the kernel is honestly transparent nowaday:
calling a struct_ops BPF function is just a function call away, and from
the bpf calling anything in the kernel is also just a call away.

> 
> FWIW, on the Intel platforms that support DSI, all of the
> initialization, poweron/poweroff, backlight on/off etc. sequences like
> that are stored in the BIOS, defined by the OEM. A plethora of DSI
> panels, one driver. Don't look at it for examples how to implement it,
> but I think the basic idea is workable.

Isn't that what Maxime wants to have? One driver for a plethora of DSI
panels, with the same ability the BIOS has to also do function calls to
set things up?

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2026-09-29  9:33 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 16:22 [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Maxime Ripard
2026-09-28 16:22 ` [PATCH 1/6] dt-bindings: display: Add panel-mipi-dsi-bpf generic panel binding Maxime Ripard
2026-09-28 19:16   ` Neil Armstrong
2026-09-28 20:40   ` Rob Herring
2026-09-29  8:39     ` Maxime Ripard
2026-09-28 20:43   ` Rob Herring (Arm)
2026-09-28 16:22 ` [PATCH 2/6] drm/panel: Add generic MIPI-DSI panel driver with BPF init sequences Maxime Ripard
2026-09-28 16:22 ` [PATCH 3/6] drm/panel: dsi-bpf: Add BPF program build infrastructure and helper header Maxime Ripard
2026-09-28 16:22 ` [PATCH 4/6] drm/panel: dsi-bpf: Add Raspberry Pi 7-inch panel BPF program Maxime Ripard
2026-09-28 16:22 ` [PATCH 5/6] drm/panel: dsi-bpf: Add Raspberry Pi 5-inch " Maxime Ripard
2026-09-28 16:22 ` [PATCH DO NOT MERGE 6/6] arm64: dts: broadcom: Add Raspberry Pi ILI9881C DSI panel overlays Maxime Ripard
2026-09-28 16:37 ` [PATCH 0/6] drm/bridge: Add a BPF-based MIPI-DSI panel driver Laurent Pinchart
2026-09-28 17:31   ` Benjamin Tissoires
2026-09-28 18:12     ` Laurent Pinchart
2026-09-29  6:54   ` Maxime Ripard
2026-09-28 16:39 ` Neil Armstrong
2026-09-28 17:24   ` Benjamin Tissoires
2026-09-28 19:20     ` Neil Armstrong
2026-09-28 19:48       ` Benjamin Tissoires
2026-09-28 20:36         ` Neil Armstrong
2026-09-29  7:41           ` Maxime Ripard
2026-09-29  7:55             ` Javier Martinez Canillas
2026-09-29  7:27       ` Maxime Ripard
2026-09-29  7:13   ` Maxime Ripard
2026-09-29  7:46     ` Benjamin Tissoires
2026-09-29  9:03 ` Jani Nikula
2026-09-29  9:32   ` Benjamin Tissoires

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®