* [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
` (6 subsequent siblings)
7 siblings, 3 replies; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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
` (5 subsequent siblings)
7 siblings, 0 replies; 18+ 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] 18+ 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
` (4 subsequent siblings)
7 siblings, 0 replies; 18+ 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] 18+ 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
` (3 subsequent siblings)
7 siblings, 0 replies; 18+ 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] 18+ 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
` (2 subsequent siblings)
7 siblings, 0 replies; 18+ 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] 18+ 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
2026-09-28 16:39 ` Neil Armstrong
7 siblings, 0 replies; 18+ 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] 18+ 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-28 16:39 ` Neil Armstrong
7 siblings, 1 reply; 18+ 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] 18+ 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
0 siblings, 1 reply; 18+ 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] 18+ 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; 18+ 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] 18+ 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
7 siblings, 1 reply; 18+ 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] 18+ 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
0 siblings, 1 reply; 18+ 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] 18+ 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
0 siblings, 1 reply; 18+ 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] 18+ 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
0 siblings, 1 reply; 18+ 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] 18+ 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
0 siblings, 0 replies; 18+ 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] 18+ messages in thread