* [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode
@ 2026-07-27 8:08 Jun Nie
2026-07-27 8:08 ` [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain Jun Nie
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Neil Armstrong, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree,
Jun Nie, Jonathan Marek
The 2 DSI interfaces may be connected to 2 independent panels in bonded-DSI
mode. Horizontal timing and DSC configuration are adjusted per individual
panel in DSI host side. Support to multiple slice per packet is added for
the device setup to test the usage case. A panel driver is included as an
use case example.
Changes vs v4:
- Add git commit message to clarify dual_panel member of mipi_dsi_device does
not force panel to handle panel topology if panel driver does not intend to
support dual panels.
- Add git commit message to clarify dsc->pic_width does not impact crtc
configuration.
- Clear dsc parameter of dsi host to handle non-DSC case.
- Fix device tree document and pass "make dt_binding_check"
- Remove unrelated platform dts nodes in panel device tree document.
- Fix regulator resource release sequence and device leak on failure.
- Fix possible unalignment of DSC pps buffer usage.
- Link to v4: https://lore.kernel.org/r/20260521-sm8650-7-1-bonded-dsi-v4-0-a4dd5e0850f1@linaro.org
Changes vs v3:
- A panel driver is added as an use case example.
- Move dsc_slice_per_pkt to struct drm_dsc_config.
- Polish commit messages.
- Link to v3: https://lore.kernel.org/all/20250924-dsi-dual-panel-upstream-v3-0-6927284f1098@linaro.org
Changes vs v2:
- Polish commit message to describe usage case and requirements to
panel driver.
- Remove changes in device tree and add dual_panel flag in mipi_dsi_device
to pass information from panel to dsi host.
- Drop the register programming to DSI_VBIF_CTRL, as no issue is seen
in latest test.
- Link to v2: https://lore.kernel.org/r/20250220-dual-dsi-v2-0-6c0038d5a2ef@linaro.org
Change vs v1:
- Add device tree binding for dual panel case in handling frame width for
DSC to avoid breaking existing dual-DSI case.
- Leverage Marijn's patch to configure proper slice per interface in
dsi_update_dsc_timing().
- Polish commit comments.
- Link to v1: https://lore.kernel.org/all/20240829-sm8650-v6-11-hmd-pocf-mdss-quad-upstream-8-v1-0-bdb05b4b5a2e@linaro.org/
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
---
Jun Nie (6):
dt-bindings: display: panel: Modify reset gpio number constrain
drm/msm/dsi: support DSC configurations with slice_per_pkt > 1
drm/mipi-dsi: Add flag to support dual-panel configurations
drm/msm/dsi: Support dual panel use case with single CRTC
dt-bindings: display: Add Synaptics R63455 panel support
drm/panel: Add driver for Synaptics R63455 DSI panel
.../bindings/display/panel/panel-common.yaml | 2 +-
.../bindings/display/panel/synaptics,r63455.yaml | 133 +++++
drivers/gpu/drm/msm/dsi/dsi_host.c | 37 +-
drivers/gpu/drm/panel/Kconfig | 11 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-synaptics-r63455.c | 644 +++++++++++++++++++++
include/drm/display/drm_dsc.h | 7 +
include/drm/drm_mipi_dsi.h | 2 +
8 files changed, 820 insertions(+), 17 deletions(-)
---
base-commit: 6654f8f33aa6229a90d4401519a62a2bf96cb851
change-id: 20260521-sm8650-7-1-bonded-dsi-7a7d5b70f687
Best regards,
--
Jun Nie <jun.nie@linaro.org>
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:18 ` sashiko-bot 2026-07-27 8:08 ` [PATCH v5 2/6] drm/msm/dsi: support DSC configurations with slice_per_pkt > 1 Jun Nie ` (4 subsequent siblings) 5 siblings, 1 reply; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie Some panel support 2 reset gpio, such as Synaptics R63455. So modify the number constrain of gpio to 1 to avoid check failure. Signed-off-by: Jun Nie <jun.nie@linaro.org> --- Documentation/devicetree/bindings/display/panel/panel-common.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.yaml b/Documentation/devicetree/bindings/display/panel/panel-common.yaml index 087415753d606..7c450d2799808 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-common.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-common.yaml @@ -119,7 +119,7 @@ properties: confused with a backlight enable signal. reset-gpios: - maxItems: 1 + minItems: 1 description: Specifier for a GPIO connected to the panel reset control signal. The reset signal is active low and resets the panel internal logic -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain 2026-07-27 8:08 ` [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain Jun Nie @ 2026-07-27 8:18 ` sashiko-bot 0 siblings, 0 replies; 12+ messages in thread From: sashiko-bot @ 2026-07-27 8:18 UTC (permalink / raw) To: Jun Nie; +Cc: robh, dri-devel, conor+dt, devicetree Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The patch relaxes the `reset-gpios` constraint in `panel-common.yaml` to allow multiple GPIOs, but fails to update the global `gpio-consumer-common.yaml` schema, which strictly enforces a maximum of 1 item. - [Medium] Replacing `maxItems: 1` with `minItems: 1` entirely removes the upper bound from the `reset-gpios` property, violating the dt-schema meta-schema requirements for phandle arrays. -- --- Patch [1]: [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain --- commit bef64e9dd2f24e3eff4806992787382fb7261e4b Author: Jun Nie <jun.nie@linaro.org> dt-bindings: display: panel: Modify reset gpio number constrain This patch modifies the constraints for reset-gpios in the display panel bindings to support panels that require two reset lines, such as the Synaptics R63455. It replaces the maximum item constraint with a minimum item constraint. > diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.yaml b/Documentation/devicetree/bindings/display/panel/panel-common.yaml > --- a/Documentation/devicetree/bindings/display/panel/panel-common.yaml > +++ b/Documentation/devicetree/bindings/display/panel/panel-common.yaml [ ... ] > reset-gpios: > - maxItems: 1 > + minItems: 1 [Severity: Medium] Does removing the maxItems constraint violate the dt-schema meta-schema rules? Array properties ending in -gpios generally require maxItems or items to be defined to provide a strict upper bound. Leaving this unbounded could cause the binding to trigger errors when validated against the core meta-schema. [Severity: Medium] Even with this local change, won't panels specifying two reset gpios still fail schema validation due to the global gpio schema? The global schema restricts reset-gpios: Documentation/devicetree/bindings/gpio/gpio-consumer-common.yaml:allOf() else: properties: reset-gpios: maxItems: 1 This implicitly overrides the relaxed constraint in the panel schema. Does gpio-consumer-common.yaml also need an update to allow for multiple reset-gpios in panel nodes? > description: > Specifier for a GPIO connected to the panel reset control signal. > The reset signal is active low and resets the panel internal logic -- Sashiko AI review · https://sashiko.dev/#/patchset/20260727-sm8650-7-1-bonded-dsi-v5-0-c042266b9eeb@linaro.org?part=1 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 2/6] drm/msm/dsi: support DSC configurations with slice_per_pkt > 1 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie 2026-07-27 8:08 ` [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:08 ` [PATCH v5 3/6] drm/mipi-dsi: Add flag to support dual-panel configurations Jun Nie ` (3 subsequent siblings) 5 siblings, 0 replies; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie, Jonathan Marek Some panels require multiple slice to be sent in a single DSC packet. And this feature is a must for specific panels, such as Sharp ls026b3sa06. Add a dsc_slice_per_pkt member into struct drm_dsc_config and support the feature in msm mdss driver. Co-developed-by: Jonathan Marek <jonathan@marek.ca> Signed-off-by: Jonathan Marek <jonathan@marek.ca> Signed-off-by: Jun Nie <jun.nie@linaro.org> --- drivers/gpu/drm/msm/dsi/dsi_host.c | 27 ++++++++++++--------------- include/drm/display/drm_dsc.h | 7 +++++++ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 53ff23f4f68ab..e39938fb0f502 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -166,6 +166,7 @@ struct msm_dsi_host { struct drm_display_mode *mode; struct drm_dsc_config *dsc; + unsigned int dsc_slice_per_pkt; /* connected device info */ unsigned int channel; @@ -938,17 +939,10 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod slice_per_intf = dsc->slice_count; total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; - bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */ + bytes_per_pkt = dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt; eol_byte_num = total_bytes_per_intf % 3; - - /* - * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. - * - * Since the current driver only supports slice_per_pkt = 1, - * pkt_per_line will be equal to slice per intf for now. - */ - pkt_per_line = slice_per_intf; + pkt_per_line = slice_per_intf / msm_host->dsc_slice_per_pkt; if (is_cmd_mode) /* packet data type */ reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); @@ -1104,12 +1098,8 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) else /* * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. - * Currently, the driver only supports default value of slice_per_pkt = 1 - * - * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info - * and adjust DSC math to account for slice_per_pkt. */ - wc = msm_host->dsc->slice_chunk_size + 1; + wc = msm_host->dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | @@ -1719,8 +1709,14 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - if (dsi->dsc) + if (dsi->dsc) { msm_host->dsc = dsi->dsc; + /* for backwards compatibility, assume 1 if not set */ + msm_host->dsc_slice_per_pkt = dsi->dsc->dsc_slice_per_pkt ?: 1; + } else { + msm_host->dsc = NULL; + msm_host->dsc_slice_per_pkt = 0; + } if (msm_host->format == MIPI_DSI_FMT_RGB101010) { if (!msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G, @@ -1757,6 +1753,7 @@ static int dsi_host_detach(struct mipi_dsi_host *host, struct msm_dsi_host *msm_host = to_msm_dsi_host(host); dsi_dev_detach(msm_host->pdev); + msm_host->dsc = NULL; DBG("id=%d", msm_host->id); diff --git a/include/drm/display/drm_dsc.h b/include/drm/display/drm_dsc.h index bbbe7438473d3..c522ab3d71853 100644 --- a/include/drm/display/drm_dsc.h +++ b/include/drm/display/drm_dsc.h @@ -267,6 +267,13 @@ struct drm_dsc_config { * Offset adjustment for second line in Native 4:2:0 mode */ u16 second_line_offset_adj; + + /** + * @dsc_slice_per_pkt: + * Number of DSC slices to be sent in a single packet. This is not + * part of DSC standard, and only used in some DSI panels so far. + */ + unsigned int dsc_slice_per_pkt; }; /** -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v5 3/6] drm/mipi-dsi: Add flag to support dual-panel configurations 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie 2026-07-27 8:08 ` [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain Jun Nie 2026-07-27 8:08 ` [PATCH v5 2/6] drm/msm/dsi: support DSC configurations with slice_per_pkt > 1 Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:08 ` [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC Jun Nie ` (2 subsequent siblings) 5 siblings, 0 replies; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie Some devices treat two independent physical DSI panels as a single logical panel from the CRTC's perspective. However, two separate DSI hosts are still required to drive the panels individually. Introduce a `dual_panel` flag to the `mipi_dsi_device` struct. This allows a panel driver to inform the DSI host that it is part of a dual-panel setup, enabling the host to coordinate both physical displays as one. This change does not force individual panel driver to manage system-level display topology if the driver does not intended to support dual panel topology. Only the driver that set the flag to true need to take care of panel topology. Signed-off-by: Jun Nie <jun.nie@linaro.org> --- include/drm/drm_mipi_dsi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 2ab651a36115d..889ef1421207a 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -169,6 +169,7 @@ struct mipi_dsi_device_info { * @host: DSI host for this peripheral * @dev: driver model device node for this peripheral * @attached: the DSI device has been successfully attached + * @dual_panel: the DSI device is one instance of dual panel * @name: DSI peripheral chip type * @channel: virtual channel assigned to the peripheral * @format: pixel format for video mode @@ -186,6 +187,7 @@ struct mipi_dsi_device { struct mipi_dsi_host *host; struct device dev; bool attached; + bool dual_panel; char name[DSI_DEV_NAME_SIZE]; unsigned int channel; -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie ` (2 preceding siblings ...) 2026-07-27 8:08 ` [PATCH v5 3/6] drm/mipi-dsi: Add flag to support dual-panel configurations Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:25 ` sashiko-bot 2026-07-27 8:08 ` [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support Jun Nie 2026-07-27 8:08 ` [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel Jun Nie 5 siblings, 1 reply; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie Support a hardware configuration where two independent DSI panels are driven by a single, synchronous CRTC. This configuration uses a bonded DSI link to provide a unified vblank for both displays. This allows application software to treat the two displays as a single, wide framebuffer with a synchronized refresh cycle, simplifying rendering logic for side-by-side panel arrangements. At the DSI host level, the frame width for each link must be that of an individual panel. The driver therefore halves the CRTC's horizontal resolution before configuring the DSI host and any DSC encoders, ensuring each panel receives the correct half of the framebuffer. The pic_width is used to calculated DSC parameter for DPU DSC controller together with panel's parameter. While panel driver that support dual panel shall provide slice_width parameter of single panel. This patch only impact DSC configuration, so crtc is not aware of it and not impacted by it. While the DSI panel driver should manage two panels togehter. 1. During probe, the driver finds the sibling dsi host via device tree phandle and register the 2nd panel to get another mipi_dsi_device. 2. Set dual_panel flag on both mipi_dsi_device. 3. Prepare DSC data per requirement from single panel. 4. All DSI commands should be send on every DSI link. 5. Handle power supply for 2 panels in one shot, the same is true to brightness. 6. From the CRTC's perspective, the two panels appear as one wide display. The driver exposes a DRM mode where the horizontal timings (hdisplay, hsync_start, etc.) are doubled, while the vertical timings remain those of a single panel. Because 2 panels are expected to be mounted in left/right position. To maintain synchronization, both DSI links are configured to share a single clock source, with the DSI1 controller using the clock provided to DSI0 as below. &mdss_dsi1 { assigned-clocks = <&dispcc DISP_CC_MDSS_BYTE1_CLK_SRC>, <&dispcc DISP_CC_MDSS_PCLK1_CLK_SRC>; assigned-clock-parents = <&mdss_dsi0_phy 0>, <&mdss_dsi0_phy 1>; } Signed-off-by: Jun Nie <jun.nie@linaro.org> --- drivers/gpu/drm/msm/dsi/dsi_host.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index e39938fb0f502..a3c56c3dc7904 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -186,6 +186,7 @@ struct msm_dsi_host { bool registered; bool power_on; bool enabled; + bool is_dual_panel; int irq; }; @@ -1024,7 +1025,10 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) return; } - dsc->pic_width = mode->hdisplay; + if (msm_host->is_dual_panel) + dsc->pic_width = hdisplay; + else + dsc->pic_width = mode->hdisplay; dsc->pic_height = mode->vdisplay; DBG("Mode %dx%d\n", dsc->pic_width, dsc->pic_height); @@ -1705,6 +1709,7 @@ static int dsi_host_attach(struct mipi_dsi_host *host, if (dsi->lanes > msm_host->num_data_lanes) return -EINVAL; + msm_host->is_dual_panel = dsi->dual_panel; msm_host->channel = dsi->channel; msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; @@ -2600,6 +2605,9 @@ enum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host, if (!msm_host->dsc) return MODE_OK; + if (msm_host->is_dual_panel) + pic_width = mode->hdisplay / 2; + if (pic_width % dsc->slice_width) { pr_err("DSI: pic_width %d has to be multiple of slice %d\n", pic_width, dsc->slice_width); -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC 2026-07-27 8:08 ` [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC Jun Nie @ 2026-07-27 8:25 ` sashiko-bot 0 siblings, 0 replies; 12+ messages in thread From: sashiko-bot @ 2026-07-27 8:25 UTC (permalink / raw) To: Jun Nie; +Cc: dri-devel, robh, conor+dt, devicetree Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Mutating the shared `dsc->pic_width` field in the DSI host driver corrupts the upstream DPU DSC configuration for dual-panel displays. -- --- Patch [4]: [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC --- commit ab7fbd44233bae5d942988068bde384adcd14200 Author: Jun Nie <jun.nie@linaro.org> drm/msm/dsi: Support dual panel use case with single CRTC Support a hardware configuration where two independent DSI panels are driven by a single, synchronous CRTC. This configuration uses a bonded DSI link to provide a unified vblank for both displays. > diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c > index ca9862848a6ab..badddb534b01a 100644 > --- a/drivers/gpu/drm/msm/dsi/dsi_host.c > +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c > @@ -1024,7 +1025,10 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) > return; > } > > - dsc->pic_width = mode->hdisplay; > + if (msm_host->is_dual_panel) > + dsc->pic_width = hdisplay; > + else > + dsc->pic_width = mode->hdisplay; [Severity: High] Does mutating the shared dsc->pic_width here corrupt the upstream DPU DSC configuration for dual-panel displays? In a dual-panel setup, dsi_timing_setup() overwrites the shared dsc->pic_width with hdisplay (which is already halved for bonded DSI). Later in the atomic commit sequence, dpu_encoder_prep_dsc() reads this mutated value: drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c:dpu_encoder_prep_dsc() { ... pic_width = dsc->pic_width; ... enc_ip_w = intf_ip_w / num_dsc; ... } Because dsc->pic_width is already halved here, dividing it again by num_dsc (2) results in each DSC encoder being incorrectly configured to process a quarter of the screen width instead of half. Could this misconfigure the DPU pipeline and lead to display corruption? > dsc->pic_height = mode->vdisplay; > DBG("Mode %dx%d\n", dsc->pic_width, dsc->pic_height); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260727-sm8650-7-1-bonded-dsi-v5-0-c042266b9eeb@linaro.org?part=4 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie ` (3 preceding siblings ...) 2026-07-27 8:08 ` [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:18 ` sashiko-bot 2026-07-27 9:47 ` Rob Herring (Arm) 2026-07-27 8:08 ` [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel Jun Nie 5 siblings, 2 replies; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie Add support for the dual-panel system found in the virtual reality device. This system consists of two physical 2160x2160 panels, each connected via a MIPI DSI interface. The backlight is managed through DSI link. Signed-off-by: Jun Nie <jun.nie@linaro.org> --- .../bindings/display/panel/synaptics,r63455.yaml | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml b/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml new file mode 100644 index 0000000000000..c3bc8df981df7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/synaptics,r63455.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synaptics R63455 based dual 2160x2160 MIPI-DSI Panel + +maintainers: + - Jun Nie <jun.nie@linaro.org> + +description: + Synaptics R63455 is a Virtual Reality Display Driver and VR Bridge, used in + pair in Headset devices. The Virtual Reality Display complex is composed of + two strictly identical display panels, each driven by its own DSI interface + but forms a single virtual display for the human eye perception and thus + requires a strict synchronization of the two display panel content update. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - sharp,ls026b3sa06 + - boe,vs026c4m-n52-6000 + - const: synaptics,r63455 + + reg: + maxItems: 1 + description: DSI virtual channel + + reset-gpios: + maxItems: 2 + description: 2 reset pins for 2 physical panels + + left-pos-supply: + description: Positive 5.7V supply for left panel + + right-pos-supply: + description: Positive 5.7V supply for right panel + + left-neg-supply: + description: Negative 5.7V supply for left panel + + right-neg-supply: + description: Negative 5.7V supply for right panel + + left-backlight-supply: + description: Backlight 21V supply for left panel + + right-backlight-supply: + description: Backlight 21V supply for right panel + + vdda-supply: + description: core 1.8V supply for panels + + ports: + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: DSI input port for primary DSI link + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: DSI input port for secondary DSI link + + required: + - port@0 + - port@1 + +required: + - compatible + - reset-gpios + - left-pos-supply + - left-neg-supply + - right-pos-supply + - right-neg-supply + - left-backlight-supply + - right-backlight-supply + - vdda-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi@ae94000 { + vdda-supply = <&vreg_l3i_1p2>; + status = "okay"; + + qcom,dual-dsi-mode; + qcom,master-dsi; + + panel: panel@0 { + compatible = "sharp,ls026b3sa06", "synaptics,r63455"; + reg = <0>; + + reset-gpios = <&pm8550_gpios 3 GPIO_ACTIVE_HIGH>, + <&pm8550_gpios 11 GPIO_ACTIVE_HIGH>; + + left-pos-supply = <&vpos_left>; + left-neg-supply = <&vneg_left>; + right-pos-supply = <&vpos_right>; + right-neg-supply = <&vneg_right>; + left-backlight-supply = <&backlight_left>; + right-backlight-supply = <&backlight_right>; + + vdda-supply = <&vreg_l12b_1p8>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + panel0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + panel1_in: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; + +... -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support 2026-07-27 8:08 ` [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support Jun Nie @ 2026-07-27 8:18 ` sashiko-bot 2026-07-27 9:47 ` Rob Herring (Arm) 1 sibling, 0 replies; 12+ messages in thread From: sashiko-bot @ 2026-07-27 8:18 UTC (permalink / raw) To: Jun Nie; +Cc: dri-devel, conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Low] Missing #address-cells, #size-cells, and reg in the example's parent DSI node - [Low] Missing schema reference for the ports node - [Low] The 'reg' property is not marked as required -- --- Patch [5]: [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support --- commit 55e4e84f35f079171a4dea5e2741a34a7fac11e9 Author: Jun Nie <jun.nie@linaro.org> dt-bindings: display: Add Synaptics R63455 panel support Add support for the dual-panel system found in the virtual reality device. This system consists of two physical 2160x2160 panels, each connected via a MIPI DSI interface. The backlight is managed through DSI link. > diff --git a/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml b/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml > new file mode 100644 > index 0000000000000..c3bc8df981df7 > --- /dev/null > +++ b/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml [ ... ] > + vdda-supply: > + description: core 1.8V supply for panels > + > + ports: [Severity: Low] Should this ports node include a $ref to /schemas/graph.yaml#/properties/ports? Without it, standard Open Firmware (OF) graph structural checks might be bypassed, allowing invalid port configurations to silently pass schema validation. > + properties: > + port@0: > + $ref: /schemas/graph.yaml#/properties/port > + description: DSI input port for primary DSI link > + > + port@1: > + $ref: /schemas/graph.yaml#/properties/port > + description: DSI input port for secondary DSI link > + > + required: > + - port@0 > + - port@1 > + > +required: > + - compatible > + - reset-gpios [Severity: Low] Does 'reg' need to be added to this required list? Since MIPI DSI panels must specify a virtual channel using the reg property, omitting it here might allow incomplete device trees to pass validation. > + - left-pos-supply > + - left-neg-supply > + - right-pos-supply > + - right-neg-supply > + - left-backlight-supply > + - right-backlight-supply > + - vdda-supply > + > +additionalProperties: false > + > +examples: > + - | > + #include <dt-bindings/gpio/gpio.h> > + > + dsi@ae94000 { [Severity: Low] Will this trigger a DTC warning during dt_binding_check? The parent node has a unit address (dsi@ae94000) but lacks a reg property, and it also lacks #address-cells and #size-cells which are needed since the child node panel@0 has a reg property. > + vdda-supply = <&vreg_l3i_1p2>; > + status = "okay"; > + > + qcom,dual-dsi-mode; > + qcom,master-dsi; > + > + panel: panel@0 { > + compatible = "sharp,ls026b3sa06", "synaptics,r63455"; > + reg = <0>; [ ... ] -- Sashiko AI review · https://sashiko.dev/#/patchset/20260727-sm8650-7-1-bonded-dsi-v5-0-c042266b9eeb@linaro.org?part=5 ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support 2026-07-27 8:08 ` [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support Jun Nie 2026-07-27 8:18 ` sashiko-bot @ 2026-07-27 9:47 ` Rob Herring (Arm) 1 sibling, 0 replies; 12+ messages in thread From: Rob Herring (Arm) @ 2026-07-27 9:47 UTC (permalink / raw) To: Jun Nie Cc: Maarten Lankhorst, Abhinav Kumar, Sean Paul, dri-devel, freedreno, devicetree, Dmitry Baryshkov, Simona Vetter, Jessica Zhang, David Airlie, Thomas Zimmermann, Conor Dooley, Neil Armstrong, linux-kernel, Dmitry Baryshkov, Maxime Ripard, linux-arm-msm, Marijn Suijten, Rob Clark, Krzysztof Kozlowski On Mon, 27 Jul 2026 16:08:44 +0800, Jun Nie wrote: > Add support for the dual-panel system found in the virtual reality device. > This system consists of two physical 2160x2160 panels, each connected via > a MIPI DSI interface. The backlight is managed through DSI link. > > Signed-off-by: Jun Nie <jun.nie@linaro.org> > --- > .../bindings/display/panel/synaptics,r63455.yaml | 133 +++++++++++++++++++++ > 1 file changed, 133 insertions(+) > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: ./Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml:26:9: [warning] wrong indentation: expected 10 but found 8 (indentation) dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/synaptics,r63455.yaml: properties:ports: 'anyOf' conditional failed, one must be fixed: 'type' is a required property '$ref' is a required property hint: node schemas must have a type or $ref from schema $id: http://devicetree.org/meta-schemas/core.yaml Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dts:29.17-27: Warning (reg_format): /example-0/dsi@ae94000/panel@0:reg: property has invalid length (4 bytes) (#address-cells == 2, #size-cells == 1) Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dts:20.21-61.11: Warning (unit_address_vs_reg): /example-0/dsi@ae94000: node has a unit name, but no reg or ranges property Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (pci_device_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (pci_device_bus_num): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (simple_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (i2c_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (spi_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dts:27.28-60.15: Warning (avoid_default_addr_size): /example-0/dsi@ae94000/panel@0: Relying on default #address-cells value Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dts:27.28-60.15: Warning (avoid_default_addr_size): /example-0/dsi@ae94000/panel@0: Relying on default #size-cells value Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: Warning (unique_unit_address_if_enabled): Failed prerequisite 'avoid_default_addr_size' /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: panel@0 (sharp,ls026b3sa06): reset-gpios: [[4294967295, 3, 0], [4294967295, 11, 0]] is too long from schema $id: http://devicetree.org/schemas/display/panel/synaptics,r63455.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/synaptics,r63455.example.dtb: panel@0 (sharp,ls026b3sa06): reset-gpios: [[4294967295, 3, 0], [4294967295, 11, 0]] is too long from schema $id: http://devicetree.org/schemas/gpio/gpio-consumer-common.yaml doc reference errors (make refcheckdocs): See https://patchwork.kernel.org/project/devicetree/patch/20260727-sm8650-7-1-bonded-dsi-v5-5-c042266b9eeb@linaro.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] 12+ messages in thread
* [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie ` (4 preceding siblings ...) 2026-07-27 8:08 ` [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support Jun Nie @ 2026-07-27 8:08 ` Jun Nie 2026-07-27 8:20 ` sashiko-bot 5 siblings, 1 reply; 12+ messages in thread From: Jun Nie @ 2026-07-27 8:08 UTC (permalink / raw) To: Rob Clark, Dmitry Baryshkov, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, devicetree, Jun Nie Add support for the DSI panels used in the virtual reality device. The device features two physical panels, each providing a MIPI DSI interface and a built-in LED backlight and expose a single logical panel to the DRM framework. Signed-off-by: Jun Nie <jun.nie@linaro.org> --- drivers/gpu/drm/panel/Kconfig | 11 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-synaptics-r63455.c | 644 +++++++++++++++++++++++++ 3 files changed, 656 insertions(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7c2d9feb4064e..e5af233d6f5f9 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -1227,6 +1227,17 @@ config DRM_PANEL_SYNAPTICS_TDDI namesake, with varying resolutions and data lanes. They also have a built-in LED backlight and a touch controller. +config DRM_PANEL_SYNAPTICS_R63455 + tristate "Synaptics R63455-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER + help + Say Y if you want to enable support for panels based on the + Synaptics R63455 controller. + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index dc8a05f2c34d9..ee6b41151804c 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SUMMIT) += panel-summit.o obj-$(CONFIG_DRM_PANEL_SYNAPTICS_R63353) += panel-synaptics-r63353.o obj-$(CONFIG_DRM_PANEL_SYNAPTICS_TDDI) += panel-synaptics-tddi.o +obj-$(CONFIG_DRM_PANEL_SYNAPTICS_R63455) += panel-synaptics-r63455.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o diff --git a/drivers/gpu/drm/panel/panel-synaptics-r63455.c b/drivers/gpu/drm/panel/panel-synaptics-r63455.c new file mode 100644 index 0000000000000..e0732a765854a --- /dev/null +++ b/drivers/gpu/drm/panel/panel-synaptics-r63455.c @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026, Linaro Ltd. All rights reserved. + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#define R63455_MF_CMD_ACCESS_PROTECT 0xb0 +#define R63455_SEQ_CTL 0xd6 +#define R63455_DSI_CTL 0xb6 +#define R63455_DISP_MODE 0xb7 +#define R63455_GEN_OUTPIN_SET 0xb9 +#define R63455_DISP_SET1 0xc0 +#define R63455_DISP_SET2 0xf1 +#define R63455_DISP_SET3 0xc6 +#define R63455_DISP_SET3_2 0xcd +#define R63455_DISP_SET4 0xcf +#define R63455_DISP_SET5 0xec +#define R63455_DISP_SET6 0xef +#define R63455_TE_GPIO_CTL 0xbe +#define R63455_PPS_SET 0xe6 + +#define BRIGHTNESS_DEFAULT 256 +#define BRIGHTNESS_MAX_120 367 + +#define VBP 100 +#define VFP 700 +#define VID_VS_DELAY 1112 +#define GPO1_TES1 4068 +#define RTN 59 + +static const char * const vdda_driver[] = { + "vdda", +}; + +static const char * const dual_backlight_driver[] = { + "left-backlight", + "right-backlight", +}; + +static const char * const dual_lcd_bias_pos[] = { + "left-pos", + "right-pos", +}; + +static const char * const dual_lcd_bias_neg[] = { + "left-neg", + "right-neg" +}; + +struct r63455_ctx { + struct device *dev; + struct drm_panel panel; + + struct regulator_bulk_data reg_vdda[ARRAY_SIZE(vdda_driver)]; + struct regulator_bulk_data reg_bl[ARRAY_SIZE(dual_backlight_driver)]; + struct regulator_bulk_data reg_lcd_bias_pos[ARRAY_SIZE(dual_lcd_bias_pos)]; + struct regulator_bulk_data reg_lcd_bias_neg[ARRAY_SIZE(dual_lcd_bias_neg)]; + + struct gpio_descs *reset_gpios; + int (*dsi_init_seq)(struct drm_panel *panel); + struct backlight_device *backlight; + struct mipi_dsi_device *dsi[2]; + struct drm_dsc_config dsc_cfg; +}; + +static inline struct r63455_ctx *panel_to_ctx(struct drm_panel *panel) +{ + return container_of(panel, struct r63455_ctx, panel); +} + +static const struct drm_display_mode modes = { + .name = "4320x2160_120", + .clock = (2160 + 24 + 20 + 20) * (2160 + 404 + 1 + 20) * 120 * 2 / 1000, + .hdisplay = 2160 * 2, + .hsync_start = (2160 + 24) * 2, + .hsync_end = (2160 + 24 + 20) * 2, + .htotal = (2160 + 24 + 20 + 20) * 2, + .vdisplay = 2160, + .vsync_start = 2160 + 404, + .vsync_end = 2160 + 404 + 1, + .vtotal = 2160 + 404 + 1 + 20, + .width_mm = 50 * 2, + .height_mm = 50, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +#define r63455_dsi_write_seq(ctx, dsi_ctx, cmd, seq...) \ + do { \ + u8 d[] = {cmd, seq}; \ + dsi_ctx.dsi = ctx->dsi[0]; \ + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, d, ARRAY_SIZE(d)); \ + dsi_ctx.dsi = ctx->dsi[1]; \ + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +#define r63455_dsi_write_buffer(ctx, dsi_ctx, d) \ + do { \ + dsi_ctx.dsi = ctx->dsi[0]; \ + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, d, ARRAY_SIZE(d)); \ + dsi_ctx.dsi = ctx->dsi[1]; \ + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +/* + * Extract the low byte and high byte of the 16bit number converted + * to the cpu endianness. + */ +#define LE16_BYTE0(val) (le16_to_cpu(val) & 0xff) +#define LE16_BYTE1(val) ((le16_to_cpu(val) >> 8) & 0xff) +#define BE16_BYTE0(val) (be16_to_cpu(val) & 0xff) +#define BE16_BYTE1(val) ((be16_to_cpu(val) >> 8) & 0xff) + +static int r63455_dsi_populate_dsc_params(struct r63455_ctx *ctx) +{ + int ret; + struct drm_dsc_config *dsc = &ctx->dsc_cfg; + + dsc->simple_422 = 0; + dsc->convert_rgb = 1; + dsc->vbr_enable = 0; + + drm_dsc_set_const_params(dsc); + drm_dsc_set_rc_buf_thresh(dsc); + + /* handle only bpp = bpc = 8, pre-SCR panels */ + ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "failed to setup dsc params\n"); + return ret; + } + + dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); + dsc->line_buf_depth = dsc->bits_per_component + 1; + + return drm_dsc_compute_rc_parameters(dsc); +} + +static int r63455_init_dsc_config(struct r63455_ctx *ctx) +{ + ctx->dsc_cfg = (struct drm_dsc_config) { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x2, + .dsc_slice_per_pkt = 4, + .slice_height = 48, + .slice_width = 540, + .slice_count = 4, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + + .pic_width = 2160, + .pic_height = 2160, + }; + + return r63455_dsi_populate_dsc_params(ctx); +} + +static int r63455_panel_on_sharp_ls026b3sa06(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + + u8 cmd[sizeof(struct drm_dsc_picture_parameter_set)]; + u8 pps_cmd[1 + sizeof(struct drm_dsc_picture_parameter_set)]; + + drm_dsc_pps_payload_pack((void *)&cmd[0], &ctx->dsc_cfg); + memcpy((void *)&pps_cmd[1], (void *)&cmd[0], + sizeof(struct drm_dsc_picture_parameter_set)); + pps_cmd[0] = R63455_PPS_SET; + + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_MF_CMD_ACCESS_PROTECT, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_SEQ_CTL, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, + R63455_DSI_CTL, + 0x20, 0x6b, 0x80, 0x06, 0x33, 0x9a, 0x00, 0x1a, + 0x7a); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_MODE, + 0x54, 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_GEN_OUTPIN_SET, + 0xf, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf, 0xb2, 0x00, 0x64); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET3, + 0x08, 0x70, 0x28, 0x48, 0x00, 0x00, 0x13, 0x21, + 0xff, 0x00, 0x0f, 0x01, 0x14, 0x17, 0x00, 0x00, + 0x00, 0x02, 0x40, 0x0C, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x70, 0x08, + 0xD0, 0x02, 0x21, 0x6F, 0x08, 0x5A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET1, + RTN, 0x86, LE16_BYTE0(VBP), LE16_BYTE1(VBP), 0x08, + 0x70, BE16_BYTE0(VFP), BE16_BYTE1(VFP), 0x00, + 0x00, 0x08, 0x3B, 0x00, 0x00, 0x19, 0x01, 0x22); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET3_2, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET4, + 0x8b, 0x00, 0x80, 0x46, 0x61, 0x00, 0x8b); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET5, + BE16_BYTE0(VID_VS_DELAY), + BE16_BYTE1(VID_VS_DELAY), + 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET6, + 0x00, 0x24, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x03, + 0x1D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_TE_GPIO_CTL, + 0x00, 0x6A, 0x02); + r63455_dsi_write_buffer(ctx, dsi_ctx, pps_cmd); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_TEAR_ON, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x40); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_EXIT_SLEEP_MODE); + mipi_dsi_msleep(&dsi_ctx, 170); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_DISPLAY_ON); + mipi_dsi_msleep(&dsi_ctx, 200); + + return dsi_ctx.accum_err; +} + +static int r63455_panel_on_boe_vs026c4m_n52_26000(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + + u8 cmd[sizeof(struct drm_dsc_picture_parameter_set)]; + u8 pps_cmd[1 + sizeof(struct drm_dsc_picture_parameter_set)]; + + drm_dsc_pps_payload_pack((void *)&cmd[0], &ctx->dsc_cfg); + memcpy((void *)&pps_cmd[1], (void *)&cmd[0], + sizeof(struct drm_dsc_picture_parameter_set)); + pps_cmd[0] = R63455_PPS_SET; + + r63455_dsi_write_seq(ctx, dsi_ctx, + R63455_MF_CMD_ACCESS_PROTECT, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_SEQ_CTL, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DSI_CTL, + 0x20, 0x6b, 0x80, 0x06, 0x33, 0x9A, 0x00, + 0x1a, 0x7a); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_MODE, + 0x54, 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_GEN_OUTPIN_SET, + 0xf, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf, 0xb2, + 0xf, 0xb2, 0x00, 0x64); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET3, + 0x0B, 0x70, 0x08, 0x48, 0x00, 0x00, 0x13, 0x01, + 0xFF, 0x00, 0x01, 0x09, 0x16, 0x16, 0x00, 0x00, + 0x00, 0x01, 0x40, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x70, 0x08, + 0xD0, 0x02, 0x21, 0x6F, 0x08, 0x5A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET1, + RTN, 0x86, LE16_BYTE0(VBP), LE16_BYTE1(VBP), 0x08, + 0x70, BE16_BYTE0(VFP), BE16_BYTE1(VFP), 0x00, + 0x00, 0x08, 0x3A, 0x00, 0x00, 0x17, 0x00, 0x17); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET3_2, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET4, + 0x8b, 0x00, 0x80, 0x46, 0x61, 0x00, 0x8b); + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_DISP_SET5, + BE16_BYTE0(VID_VS_DELAY), BE16_BYTE1(VID_VS_DELAY), + 0x00, 0x00, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, + R63455_TE_GPIO_CTL, 0x00, 0x6A, 0x02); + r63455_dsi_write_buffer(ctx, dsi_ctx, pps_cmd); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_TEAR_ON, 0x00); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_EXIT_SLEEP_MODE); + mipi_dsi_msleep(&dsi_ctx, 170); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_DISPLAY_ON); + mipi_dsi_msleep(&dsi_ctx, 200); + + return dsi_ctx.accum_err; +} + +static int r63455_set_brightness(struct r63455_ctx *ctx, u16 brightness, + u16 pulse_offset_rows) +{ + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + u16 gpo1_tew1; + + gpo1_tew1 = brightness > BRIGHTNESS_MAX_120 ? + BRIGHTNESS_MAX_120 : brightness; + + ctx->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM; + ctx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; + + r63455_dsi_write_seq(ctx, dsi_ctx, + R63455_GEN_OUTPIN_SET, + 0x0f, 0xe4, + BE16_BYTE0(gpo1_tew1), + BE16_BYTE1(gpo1_tew1), + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xb2, + BE16_BYTE0(brightness), + BE16_BYTE1(brightness)); + + ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; + ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; + + return dsi_ctx.accum_err; +} + +static int r63455_disable(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + + ctx->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM; + ctx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; + + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_DISPLAY_OFF); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_SET_TEAR_OFF); + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_ENTER_SLEEP_MODE); + + mipi_dsi_msleep(&dsi_ctx, 200); + + if (dsi_ctx.accum_err) + return dsi_ctx.accum_err; + + /* required by hardware to be after MIPI_DCS_ENTER_SLEEP_MODE */ + return backlight_disable(ctx->backlight); +} + +static int r63455_unprepare(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + int ret; + + gpiod_set_value(ctx->reset_gpios->desc[0], 0); + gpiod_set_value(ctx->reset_gpios->desc[1], 0); + + msleep(25); + ret = regulator_bulk_disable(ARRAY_SIZE(ctx->reg_lcd_bias_neg), + ctx->reg_lcd_bias_neg); + if (ret) + dev_err(ctx->dev, "Could not disable bias negative\n"); + + usleep_range(1000, 2000); + ret = regulator_bulk_disable(ARRAY_SIZE(ctx->reg_lcd_bias_pos), + ctx->reg_lcd_bias_pos); + if (ret) + dev_err(ctx->dev, "Could not disable bias positive\n"); + + msleep(20); + + ret = regulator_bulk_disable(ARRAY_SIZE(ctx->reg_vdda), ctx->reg_vdda); + if (ret) + dev_err(ctx->dev, "Could not disable vdda\n"); + + ret = regulator_bulk_disable(ARRAY_SIZE(ctx->reg_bl), ctx->reg_bl); + if (ret) + dev_err(ctx->dev, + "failed to disable regulator backlight: %d\n", ret); + + return ret; +} + +static int r63455_enable(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + int ret; + + ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM; + ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; + + msleep(20); + ret = ctx->dsi_init_seq(panel); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "dsi init failed\n"); + return ret; + } + ctx->backlight->props.pulse_offset_rows = GPO1_TES1; + + ret = backlight_enable(ctx->backlight); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "failed to enable backlight\n"); + return ret; + } + + return ret; +} + +static int r63455_prepare(struct drm_panel *panel) +{ + struct r63455_ctx *ctx = panel_to_ctx(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->reg_bl), ctx->reg_bl); + if (ret) { + dev_err(ctx->dev, "Could not enable backlight\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->reg_vdda), ctx->reg_vdda); + if (ret) { + dev_err(ctx->dev, "Could not enable vdda\n"); + goto bl_off; + } + + usleep_range(1000, 2000); + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->reg_lcd_bias_pos), + ctx->reg_lcd_bias_pos); + if (ret) { + dev_err(ctx->dev, "Could not enable bias positive\n"); + goto vdda_off; + } + usleep_range(1000, 2000); + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->reg_lcd_bias_neg), + ctx->reg_lcd_bias_neg); + if (ret) { + dev_err(ctx->dev, "Could not enable bias negative\n"); + goto bias_off; + } + + msleep(20); + gpiod_set_value(ctx->reset_gpios->desc[0], 1); + gpiod_set_value(ctx->reset_gpios->desc[1], 1); + + return 0; + +bias_off: + regulator_bulk_disable(ARRAY_SIZE(ctx->reg_lcd_bias_pos), + ctx->reg_lcd_bias_pos); +vdda_off: + regulator_bulk_disable(ARRAY_SIZE(ctx->reg_vdda), ctx->reg_vdda); +bl_off: + regulator_bulk_disable(ARRAY_SIZE(ctx->reg_bl), ctx->reg_bl); + return ret; +} + +static int r63455_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + struct r63455_ctx *r63455_ctx = mipi_dsi_get_drvdata(dsi); + + return r63455_set_brightness(r63455_ctx, bl->props.brightness, + bl->props.pulse_offset_rows); +} + +static const struct backlight_ops r63455_bl_ops = { + .update_status = r63455_bl_update_status, +}; + +static int r63455_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + return drm_connector_helper_get_modes_fixed(connector, &modes); +} + +static const struct drm_panel_funcs r63455_drm_funcs = { + .disable = r63455_disable, + .enable = r63455_enable, + .unprepare = r63455_unprepare, + .prepare = r63455_prepare, + .get_modes = r63455_get_modes, +}; + +static int r63455_panel_add(struct r63455_ctx *ctx) +{ + struct device *dev = ctx->dev; + struct backlight_properties bl_props; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(ctx->reg_vdda); i++) + ctx->reg_vdda[i].supply = vdda_driver[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->reg_vdda), + ctx->reg_vdda); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(dual_backlight_driver); i++) + ctx->reg_bl[i].supply = dual_backlight_driver[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->reg_bl), + ctx->reg_bl); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(ctx->reg_lcd_bias_pos); i++) + ctx->reg_lcd_bias_pos[i].supply = dual_lcd_bias_pos[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->reg_lcd_bias_pos), + ctx->reg_lcd_bias_pos); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(ctx->reg_lcd_bias_neg); i++) + ctx->reg_lcd_bias_neg[i].supply = dual_lcd_bias_neg[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->reg_lcd_bias_neg), + ctx->reg_lcd_bias_neg); + if (ret < 0) + return ret; + + ctx->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpios) || ctx->reset_gpios->ndescs != 2) + return PTR_ERR(ctx->reset_gpios); + + memset(&bl_props, 0, sizeof(bl_props)); + bl_props.type = BACKLIGHT_RAW; + bl_props.brightness = BRIGHTNESS_DEFAULT; + bl_props.max_brightness = BRIGHTNESS_MAX_120; + + ctx->backlight = devm_backlight_device_register(dev, dev_name(dev), + dev, ctx->dsi[0], + &r63455_bl_ops, + &bl_props); + if (IS_ERR(ctx->backlight)) { + ret = PTR_ERR(ctx->backlight); + dev_err(dev, "Failed to register backlight (%d)\n", ret); + return ret; + } + + ctx->panel.prepare_prev_first = true; + return devm_drm_panel_add(dev, &ctx->panel); +} + +static int r63455_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct r63455_ctx *ctx; + struct mipi_dsi_device *dsi1_device; + struct device_node *dsi1; + struct mipi_dsi_host *dsi1_host; + struct mipi_dsi_device *dsi_dev; + int ret = 0; + int i; + + const struct mipi_dsi_device_info info = { + .type = "r63455-dsi-panel", + .channel = 0, + .node = NULL, + }; + + ctx = devm_drm_panel_alloc(&dsi->dev, __typeof(*ctx), panel, + &r63455_drm_funcs, DRM_MODE_CONNECTOR_DSI); + + ctx->dsi_init_seq = of_device_get_match_data(dev); + if (!ctx->dsi_init_seq) + return -ENODEV; + + dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi1) { + DRM_DEV_ERROR(dev, "failed to get secondary dsi\n"); + return -ENODEV; + } + + dsi1_host = of_find_mipi_dsi_host_by_node(dsi1); + of_node_put(dsi1); + if (!dsi1_host) { + DRM_DEV_ERROR(dev, "failed to find secondary dsi host\n"); + return -EPROBE_DEFER; + } + + dsi1_device = devm_mipi_dsi_device_register_full(dev, dsi1_host, &info); + if (IS_ERR(dsi1_device)) { + DRM_DEV_ERROR(dev, "failed to create dsi device\n"); + return PTR_ERR(dsi1_device); + } + + ctx->dsi[1] = dsi1_device; + mipi_dsi_set_drvdata(dsi, ctx); + ctx->dev = dev; + ctx->dsi[0] = dsi; + + ret = r63455_init_dsc_config(ctx); + if (ret) + return ret; + + ctx->dsi[0]->dsc = &ctx->dsc_cfg; + ctx->dsi[0]->dual_panel = true; + ctx->dsi[1]->dual_panel = true; + ctx->dsi[1]->dsc = &ctx->dsc_cfg; + + ret = r63455_panel_add(ctx); + if (ret) { + DRM_DEV_ERROR(dev, "failed to add panel %d\n", ret); + return ret; + } + + for (i = 0; i < 2; i++) { + dsi_dev = ctx->dsi[i]; + dsi_dev->lanes = 3; + dsi_dev->format = MIPI_DSI_FMT_RGB888; + dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + ret = devm_mipi_dsi_attach(dev, dsi_dev); + if (ret < 0) { + DRM_DEV_ERROR(dev, "dsi attach failed i = %d\n", i); + return ret; + } + } + + return 0; +} + +static const struct of_device_id r63455_of_match[] = { + { + .compatible = "sharp,ls026b3sa06", + .data = r63455_panel_on_sharp_ls026b3sa06, + }, + { + .compatible = "boe,vs026c4m-n52-6000", + .data = r63455_panel_on_boe_vs026c4m_n52_26000, + }, + { } +}; +MODULE_DEVICE_TABLE(of, r63455_of_match); + +static struct mipi_dsi_driver r63455_driver = { + .driver = { + .name = "panel-synaptics-r63455", + .of_match_table = r63455_of_match, + }, + .probe = r63455_probe, +}; +module_mipi_dsi_driver(r63455_driver); + +MODULE_DESCRIPTION("Synaptics R63455 DSI Panel Driver"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel 2026-07-27 8:08 ` [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel Jun Nie @ 2026-07-27 8:20 ` sashiko-bot 0 siblings, 0 replies; 12+ messages in thread From: sashiko-bot @ 2026-07-27 8:20 UTC (permalink / raw) To: Jun Nie; +Cc: robh, devicetree, dri-devel, conor+dt Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider: - [High] A copy-paste error in the BOE panel initialization sequence duplicates command bytes, corrupting the DSI payload. - [High] Concurrent non-atomic read-modify-write operations on DSI `mode_flags` can corrupt the transmission mode state. - [High] Missing IS_ERR() check on the return value of devm_drm_panel_alloc() leads to an immediate dereference of an error pointer. - [High] The backlight sysfs interface is exposed to userspace before the DSI device is fully configured and attached. - [Medium] The macros LE16_BYTE0/1 and BE16_BYTE0/1 use endianness conversion functions before bitwise masking, causing incorrect byte order on Big Endian architectures. - [Medium] Returning PTR_ERR() on a valid pointer when the GPIO array size is incorrect leaks a kernel heap address and produces a positive error code. - [Medium] The backlight update_status callback blindly sends DCS commands without checking the panel's power state. - [Low] An early return in the panel disable path on DSI command failure skips disabling the backlight. -- --- Patch [6]: [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel --- commit 89f5e4df3241a9188e302ee5028a24a7b0bc9542 Author: Jun Nie <jun.nie@linaro.org> Subject: drm/panel: Add driver for Synaptics R63455 DSI panel This commit adds support for the Synaptics R63455 DSI panel used in virtual reality devices. It exposes a dual-panel configuration as a single logical panel to the DRM framework. > --- a/drivers/gpu/drm/panel/panel-synaptics-r63455.c > +++ b/drivers/gpu/drm/panel/panel-synaptics-r63455.c [ ... ] > +/* > + * Extract the low byte and high byte of the 16bit number converted > + * to the cpu endianness. > + */ > +#define LE16_BYTE0(val) (le16_to_cpu(val) & 0xff) > +#define LE16_BYTE1(val) ((le16_to_cpu(val) >> 8) & 0xff) > +#define BE16_BYTE0(val) (be16_to_cpu(val) & 0xff) > +#define BE16_BYTE1(val) ((be16_to_cpu(val) >> 8) & 0xff) [Severity: Medium] Will this generate the correct byte order on Big Endian architectures? Since be16_to_cpu is a no-op on Big Endian systems, applying the bitwise shift and mask afterward will extract the bytes in Little Endian order rather than Big Endian. This will cause the panel initialization sequence to receive payload bytes in reversed order. Could we directly shift and mask without the endian conversion macros to safely serialize CPU-native integers across all architectures? [ ... ] > +static int r63455_panel_on_boe_vs026c4m_n52_26000(struct drm_panel *panel) > +{ [ ... ] > + r63455_dsi_write_seq(ctx, dsi_ctx, R63455_GEN_OUTPIN_SET, > + 0xf, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0xf, 0xb2, > + 0xf, 0xb2, 0x00, 0x64); [Severity: High] Does this sequence intentionally send the 0xf, 0xb2 bytes twice for the BOE panel initialization? This looks like it could be a copy-paste error that sends 14 bytes instead of the expected 12 bytes. Could this transmit malformed configuration data to the display controller and cause initialization failures? [ ... ] > +static int r63455_set_brightness(struct r63455_ctx *ctx, u16 brightness, > + u16 pulse_offset_rows) > +{ [ ... ] > + ctx->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM; > + ctx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; [Severity: High] Are these bitwise operations on mode_flags safe from race conditions? Since r63455_set_brightness executes under the backlight sysfs lock, it can run concurrently with DRM atomic commit handlers like r63455_enable or r63455_disable which use independent locks. Could a concurrent read-modify-write here corrupt the transmission mode state, such as unintentionally dropping the MIPI_DSI_MODE_VIDEO flag? [ ... ] > +static int r63455_disable(struct drm_panel *panel) > +{ [ ... ] > + r63455_dsi_write_seq(ctx, dsi_ctx, MIPI_DCS_ENTER_SLEEP_MODE); > + > + mipi_dsi_msleep(&dsi_ctx, 200); > + > + if (dsi_ctx.accum_err) > + return dsi_ctx.accum_err; > + > + /* required by hardware to be after MIPI_DCS_ENTER_SLEEP_MODE */ > + return backlight_disable(ctx->backlight); [Severity: Low] Does returning early here leave the backlight subsystem in an active state? If the DSI ENTER_SLEEP_MODE sequence fails, the driver skips calling backlight_disable. While the physical regulators are disabled later in r63455_unprepare, could this leave the backlight framework out of sync? [ ... ] > +static int r63455_bl_update_status(struct backlight_device *bl) > +{ > + struct mipi_dsi_device *dsi = bl_get_data(bl); > + struct r63455_ctx *r63455_ctx = mipi_dsi_get_drvdata(dsi); > + > + return r63455_set_brightness(r63455_ctx, bl->props.brightness, > + bl->props.pulse_offset_rows); > +} [Severity: Medium] Does this function need to verify the panel power state before sending DCS commands? Userspace can modify the brightness via sysfs while the DRM display is disabled. If this occurs, it might trigger MIPI DSI transmissions to a powered-off panel or unclocked DSI host. Could this lead to hardware lockups or DSI timeouts? [ ... ] > +static int r63455_panel_add(struct r63455_ctx *ctx) > +{ [ ... ] > + ctx->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_LOW); > + if (IS_ERR(ctx->reset_gpios) || ctx->reset_gpios->ndescs != 2) > + return PTR_ERR(ctx->reset_gpios); [Severity: Medium] If the GPIO array has a count other than 2, will this return a positive value derived from a valid heap pointer? When devm_gpiod_get_array succeeds but returns an unexpected number of descriptors, ctx->reset_gpios is a valid pointer rather than an error pointer. Could we return an explicit error code like -EINVAL here instead of leaking the pointer address through PTR_ERR? [ ... ] > +static int r63455_probe(struct mipi_dsi_device *dsi) > +{ [ ... ] > + ctx = devm_drm_panel_alloc(&dsi->dev, __typeof(*ctx), panel, > + &r63455_drm_funcs, DRM_MODE_CONNECTOR_DSI); > + > + ctx->dsi_init_seq = of_device_get_match_data(dev); [Severity: High] Should we check the return value of devm_drm_panel_alloc for an error pointer? If memory allocation fails during driver probe, devm_drm_panel_alloc returns ERR_PTR(-ENOMEM). Dereferencing ctx on the very next line to assign dsi_init_seq will result in a kernel panic. [ ... ] > + ret = r63455_panel_add(ctx); > + if (ret) { > + DRM_DEV_ERROR(dev, "failed to add panel %d\n", ret); > + return ret; > + } > + > + for (i = 0; i < 2; i++) { > + dsi_dev = ctx->dsi[i]; > + dsi_dev->lanes = 3; > + dsi_dev->format = MIPI_DSI_FMT_RGB888; > + dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | > + MIPI_DSI_CLOCK_NON_CONTINUOUS; > + ret = devm_mipi_dsi_attach(dev, dsi_dev); [Severity: High] Is it safe to register the backlight device before the DSI host is configured and attached? Calling r63455_panel_add exposes the backlight sysfs interface to userspace. However, the lanes, format, mode_flags, and devm_mipi_dsi_attach are not set up until the loop that follows. If a udev rule writes to the brightness node during this window, could it trigger DSI transmissions on an unattached device and lock up the host? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260727-sm8650-7-1-bonded-dsi-v5-0-c042266b9eeb@linaro.org?part=6 ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-07-27 9:47 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-27 8:08 [PATCH v5 0/6] Add support to 2 panels in bonded-DSI mode Jun Nie 2026-07-27 8:08 ` [PATCH v5 1/6] dt-bindings: display: panel: Modify reset gpio number constrain Jun Nie 2026-07-27 8:18 ` sashiko-bot 2026-07-27 8:08 ` [PATCH v5 2/6] drm/msm/dsi: support DSC configurations with slice_per_pkt > 1 Jun Nie 2026-07-27 8:08 ` [PATCH v5 3/6] drm/mipi-dsi: Add flag to support dual-panel configurations Jun Nie 2026-07-27 8:08 ` [PATCH v5 4/6] drm/msm/dsi: Support dual panel use case with single CRTC Jun Nie 2026-07-27 8:25 ` sashiko-bot 2026-07-27 8:08 ` [PATCH v5 5/6] dt-bindings: display: Add Synaptics R63455 panel support Jun Nie 2026-07-27 8:18 ` sashiko-bot 2026-07-27 9:47 ` Rob Herring (Arm) 2026-07-27 8:08 ` [PATCH v5 6/6] drm/panel: Add driver for Synaptics R63455 DSI panel Jun Nie 2026-07-27 8:20 ` sashiko-bot
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.