Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Adam Azuddin" <azuddinadam@gmail.com>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v5 2/2] drm/tiny: Add RAiO RA8875 display controller driver
Date: Fri, 30 Oct 2026 09:09:57 +0000	[thread overview]
Message-ID: <sashiko-outbox-190739@kernel.org> (raw)
In-Reply-To: <20261030083737.47188-3-azuddinadam@gmail.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] drm/tiny: ra8875: heap overflow in ra8875_clear_screen() via unvalidated mode
- [High] drm/tiny: ra8875: plane updates run before CRTC enable and screen clear
- [High] drm/tiny: ra8875: miscalculation of HNDR and VNDR back porch in ra8875_hw_init()
- [High] drm/tiny: ra8875: missing drm_dev_enter() guard in ra8875_crtc_atomic_enable()
- [Medium] drm/tiny: ra8875: missing vdd-supply regulator acquisition and enable
- [Medium] drm/tiny: ra8875: missing drm_gem_fb_begin_cpu_access() in ra8875_fb_dirty()
- [Medium] drm/tiny/ra8875: unaligned u16 stores in ra8875_fb_dirty() via drm_fb_swab()
- [Low] drm/tiny: ra8875: missing MMU dependency in Kconfig for DRM_RA8875
--

commit 7ff969ff52235eb3411700feffd794a3e46ceb5a
Author: Adam Azuddin <azuddinadam@gmail.com>

drm/tiny: Add RAiO RA8875 display controller driver

This commit adds a DRM tiny driver for the RAiO RA8875 SPI-connected TFT
display controller supporting resolutions up to 800x480 with GEM shmem
buffer management.

> diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
> index f0e72d4b6a470..2f1c49e31dd60 100644
> --- a/drivers/gpu/drm/tiny/Kconfig
> +++ b/drivers/gpu/drm/tiny/Kconfig
> @@ -98,6 +98,19 @@ config DRM_PIXPAPER
>  
>  	  If M is selected, the module will be built as pixpaper.ko.
>  
> +config DRM_RA8875
> +	tristate "DRM support for RA8875 display panels"
> +	depends on DRM && SPI && OF
> +	select DRM_CLIENT_SELECTION
> +	select DRM_KMS_HELPER
> +	select DRM_GEM_SHMEM_HELPER
> +	select VIDEOMODE_HELPERS

[Severity: Low]
In drivers/gpu/drm/tiny/Kconfig, DRM_RA8875 selects DRM_GEM_SHMEM_HELPER,
which depends on MMU in drivers/gpu/drm/Kconfig.

Without a dependency on MMU, enabling DRM_RA8875 on a !CONFIG_MMU target
triggers an unmet direct dependency warning and fails to build in
drm_gem_shmem_helper.c.

Should DRM_RA8875 depend on MMU as well, matching the other shmem-based
drivers in drivers/gpu/drm/tiny/Kconfig?

[ ... ]

> diff --git a/drivers/gpu/drm/tiny/ra8875.c b/drivers/gpu/drm/tiny/ra8875.c
> new file mode 100644
> index 0000000000000..937bb7fdbd910
> --- /dev/null
> +++ b/drivers/gpu/drm/tiny/ra8875.c
[ ... ]
> +static int ra8875_hw_init(struct ra8875_device *ra8875,
> +			  struct drm_display_mode *mode)
> +{
[ ... ]
> +	/* Horizontal timing */
> +	hdwr = (mode->hdisplay / 8) - 1;
> +	hndr = ((mode->htotal - mode->hdisplay) / 8) - 1;
> +	hstr = ((mode->hsync_start - mode->hdisplay) / 8) - 1;
> +	hpwr = ((mode->hsync_end - mode->hsync_start) / 8) - 1;
> +
> +	ret = ra8875_write_reg(ra8875, RA8875_HDWR, hdwr);
> +	if (ret)
> +		return ret;
> +	ret = ra8875_write_reg(ra8875, RA8875_HNDFTR, RA8875_HNDFTR_HNDFT(0));
> +	if (ret)
> +		return ret;
> +	ret = ra8875_write_reg(ra8875, RA8875_HNDR, hndr);
> +	if (ret)
> +		return ret;
> +	ret = ra8875_write_reg(ra8875, RA8875_HSTR, hstr);
> +	if (ret)
> +		return ret;
> +	ret = ra8875_write_reg(ra8875, RA8875_HPWR, hpwr);
> +	if (ret)
> +		return ret;
> +
> +	/* Vertical timing */
> +	vdhr = mode->vdisplay - 1;
> +	vndr = mode->vtotal - mode->vdisplay - 1;
> +	vstr = mode->vsync_start - mode->vdisplay - 1;
> +	vpwr = mode->vsync_end - mode->vsync_start - 1;

[Severity: High]
In drivers/gpu/drm/tiny/ra8875.c, are hndr and vndr in ra8875_hw_init()
being computed from the total blanking interval rather than the back porch?

In drm_display_mode_from_videomode():

    dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
    dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
    dmode->htotal = dmode->hsync_end + vm->hback_porch;

Since HSTR (and VSTR0/VSTR1) already program the front porch and HPWR (and
VPWR) program the sync pulse width, RA8875_HNDR and RA8875_VNDR0/RA8875_VNDR1
program the horizontal and vertical non-display (back porch) periods.

Using mode->htotal - mode->hdisplay and mode->vtotal - mode->vdisplay counts
the front porch and sync pulse width a second time. Should these be computed
from mode->htotal - mode->hsync_end and mode->vtotal - mode->vsync_end
instead?

[ ... ]

> +static int ra8875_clear_screen(struct ra8875_device *ra8875,
> +			       struct drm_display_mode *mode)
> +{
[ ... ]
> +	memset(txbuf, 0, 1 + rows_per_chunk * width * 2);
> +	txbuf[0] = RA8875_DATAWRITE;
> +
> +	for (y = 0; y < height; y += rows) {
> +		rows = min(rows_per_chunk, height - y);
> +		ret = ra8875_write_pixels(ra8875, 1 + rows * width * 2);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ra8875_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
> +{
> +	struct ra8875_device *ra8875 =
> +		container_of(crtc->dev, struct ra8875_device, drm);
> +	struct drm_crtc_state *crtc_state =
> +		drm_atomic_get_new_crtc_state(state, crtc);
> +	struct drm_display_mode *mode = &crtc_state->mode;
> +	int ret;
> +
> +	ret = ra8875_hw_init(ra8875, mode);
> +	if (ret) {
> +		drm_err(&ra8875->drm, "Failed to initialize hardware\n");
> +		return;
> +	}
> +
> +	ret = ra8875_clear_screen(ra8875, mode);
> +	if (ret) {
> +		drm_err(&ra8875->drm, "Failed to clear screen\n");
> +		return;
> +	}
> +}

[Severity: High]
In drivers/gpu/drm/tiny/ra8875.c, doesn't the atomic commit order cause
ra8875_crtc_atomic_enable() to wipe out the initial plane update on every
modeset?

In ra8875_probe(), ra8875->rst_gpio is requested with GPIOD_OUT_HIGH and
drm->mode_config.helper_private is left unset. When drm_atomic_helper_commit()
runs a modeset (including the initial fbdev modeset from drm_client_setup()),
it uses the default drm_atomic_helper_commit_tail(), which executes:

    drm_atomic_helper_commit_tail()
      drm_atomic_helper_commit_planes(dev, state, 0)
        ra8875_plane_atomic_update()
          ra8875_fb_dirty()
      drm_atomic_helper_commit_modeset_enables(dev, state)
        ra8875_crtc_atomic_enable()
          ra8875_hw_init()
          ra8875_clear_screen()

As a result, ra8875_plane_atomic_update() writes the framebuffer while the
chip is still held in reset, and then ra8875_crtc_atomic_enable() resets the
hardware and zeroes the entire display memory in ra8875_clear_screen().

In addition, ra8875_plane_atomic_check() passes can_update_disabled = true to
drm_atomic_helper_check_plane_state(), and ra8875_plane_atomic_update() does
not check whether the CRTC is active.

Should drm->mode_config.helper_private be configured with
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm (so CRTC enable runs
before plane updates, and plane updates are skipped when the CRTC is
inactive), and can_update_disabled be set to false in
ra8875_plane_atomic_check()?

[Severity: High]
Also in ra8875_crtc_atomic_enable() in drivers/gpu/drm/tiny/ra8875.c, can a
modeset racing with device removal in ra8875_remove() cause a use-after-free
on ra8875->txbuf and ra8875->rst_gpio?

In ra8875_probe(), ra8875->txbuf and ra8875->rst_gpio are allocated with
devm_kzalloc(&spi->dev, ...) and devm_gpiod_get_optional(&spi->dev, ...), so
devres frees them as soon as ra8875_remove() returns, even if a DRM file
descriptor remains open.

Unlike ra8875_plane_atomic_update(), ra8875_crtc_atomic_enable() does not
guard its hardware and buffer accesses with drm_dev_enter() and
drm_dev_exit(). If an atomic commit enters ra8875_crtc_atomic_enable()
concurrently with or after drm_dev_unplug() in ra8875_remove(),
drm_dev_unplug() will not synchronize with it and ra8875_clear_screen() can
memset() and SPI-transfer from the freed ra8875->txbuf buffer.

Should ra8875_crtc_atomic_enable() (and ra8875_crtc_atomic_disable()) be
guarded with drm_dev_enter() and drm_dev_exit()?

[ ... ]

> +static void ra8875_fb_dirty(struct ra8875_device *ra8875, struct iosys_map *src,
> +			    struct drm_framebuffer *fb, struct drm_rect *rect)
> +{
[ ... ]
> +	for (int y = rect->y1; y < rect->y2; y += rows) {
> +		struct drm_rect chunk;
> +
> +		rows = min(RA8875_TXBUF_ROWS, rect->y2 - y);
> +		chunk = DRM_RECT_INIT(rect->x1, y, width, rows);
> +
> +		iosys_map_set_vaddr(&dst, ra8875->txbuf + 1);
> +		drm_fb_swab(&dst, NULL, src, fb, &chunk, false, &fmtcnv_state);
> +
> +		if (ra8875_write_pixels(ra8875, 1 + rows * width * 2))
> +			goto err;
> +	}

[Severity: Medium]
In ra8875_fb_dirty() in drivers/gpu/drm/tiny/ra8875.c, drm_fb_swab() reads
from the shadow-mapped framebuffer with the CPU, but
drm_gem_begin_shadow_fb_access() only vmaps the buffer without calling
dma_buf_begin_cpu_access().

When displaying an imported PRIME DMA-BUF on a non-cache-coherent system, can
omitting drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) and
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE) around the CPU read result in
stale cache reads and display corruption?

[Severity: Medium]
Also in ra8875_fb_dirty() in drivers/gpu/drm/tiny/ra8875.c, doesn't passing
ra8875->txbuf + 1 as the destination buffer to drm_fb_swab() perform
unaligned 16-bit stores on every pixel?

In ra8875_probe(), ra8875->txbuf is allocated with devm_kzalloc(), which is
even-aligned, making ra8875->txbuf + 1 an odd address. For DRM_FORMAT_RGB565,
drm_fb_swab() calls drm_fb_swab16_line(), which casts the destination pointer
directly to u16 * and writes *dbuf16++ = swab16(*sbuf16++) without
put_unaligned().

Can this trigger alignment faults on architectures requiring natural
alignment as well as UBSAN alignment warnings?

[ ... ]

> +static enum drm_mode_status ra8875_mode_valid(struct drm_crtc *crtc,
> +					      const struct drm_display_mode *mode)
> +{
> +	if (mode->hdisplay % 8 != 0 || mode->hsync_start % 8 != 0 ||
> +	    mode->hsync_end % 8 != 0 || mode->htotal % 8 != 0) {
> +		drm_dbg_kms(crtc->dev,
> +		   "mode timings must be 8-pixel aligned (hdisplay=%d hsync_start=%d hsync_end=%d htotal=%d)\n",
> +			    mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal);
> +		return MODE_H_ILLEGAL;
> +	}
> +	return MODE_OK;
> +}

[Severity: Critical]
As reported by Sashiko on v4 [1], can a userspace-supplied display mode
overflow ra8875->txbuf in ra8875_clear_screen()?

In ra8875_probe() in drivers/gpu/drm/tiny/ra8875.c, ra8875->txbuf is
allocated for a fixed maximum width of RA8875_MAX_WIDTH (800):

    buf_size = 1 + RA8875_TXBUF_ROWS * RA8875_MAX_WIDTH * 2;
    ra8875->txbuf = devm_kzalloc(&spi->dev, buf_size, GFP_KERNEL);

While drm->mode_config.max_width restricts framebuffer sizes, a userspace
DRM master can submit a custom CRTC mode via KMS (for example with the
primary plane disabled). Because ra8875_mode_valid() only checks that the
horizontal timings are multiples of 8 and does not call
drm_crtc_helper_mode_valid_fixed(crtc, mode, &ra8875->mode) or bound
mode->hdisplay to RA8875_MAX_WIDTH, committing a mode with mode->hdisplay >
800 causes ra8875_crtc_atomic_enable() -> ra8875_clear_screen() to memset()
and SPI-write 1 + 16 * mode->hdisplay * 2 bytes past the end of
ra8875->txbuf.

Additionally, neither ra8875_probe() nor ra8875_mode_valid() verifies that
the horizontal and vertical front porch, sync pulse, and back porch lengths
are strictly positive, so a zero-length porch or sync pulse will underflow
when subtracting 1 in ra8875_hw_init().

Should ra8875_mode_valid() validate the mode with
drm_crtc_helper_mode_valid_fixed() and verify that all porch and sync pulse
intervals are non-zero?

[ ... ]

> +static int ra8875_probe(struct spi_device *spi)
> +{
[ ... ]
> +	ra8875->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(ra8875->rst_gpio))
> +		return dev_err_probe(dev, PTR_ERR(ra8875->rst_gpio),
> +			     "failed to get reset GPIO\n");

[Severity: Medium]
In ra8875_probe() in drivers/gpu/drm/tiny/ra8875.c, the driver requests the
optional reset GPIO, but never acquires or enables the vdd-supply regulator
that is marked as required in
Documentation/devicetree/bindings/display/panel/raio,ra8875.yaml.

If vdd-supply is backed by a switchable regulator that is disabled at boot or
turned off by the regulator core as unused, won't the RA8875 remain unpowered
when ra8875_hw_init() attempts SPI transfers? Should ra8875_probe() enable
the vdd supply (for example via devm_regulator_get_enable(dev, "vdd"))?

[1] https://lore.kernel.org/r/20261012064518.3497285-3-azuddinadam@gmail.com

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261030083737.47188-1-azuddinadam@gmail.com?part=2

      reply	other threads:[~2026-10-30  9:09 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30  8:37 [PATCH v5 0/2] drm/tiny: Add RAiO RA8875 display controller driver support Adam Azuddin
2026-10-30  8:37 ` [PATCH v5 1/2] dt-bindings: display: panel: Add RAiO RA8875 display controller Adam Azuddin
2026-10-30  8:37 ` [PATCH v5 2/2] drm/tiny: Add RAiO RA8875 display controller driver Adam Azuddin
2026-10-30  9:09   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-190739@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=azuddinadam@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox