[PATCH v2 3/3] fbdev: pxafb: initialize IRQ completions before requesting IRQ

From: Matthias Goergens

Date: Mon Nov 02 2026 - 03:08:13 EST


pxafb_handle_irq() completes command_done when LCSR has LCSR_CMD_INT
set, and an overlay's branch_done when LCSR1 has its branch status bit
set. Probe requests the IRQ before it initialises either: command_done
in pxafb_smart_init(), where commit 07df1c4fea14 ("[ARM] pxafb: small
cleanup of the smart panel code") moved it, and branch_done in the
overlay setup, since the overlays were added. If one of those status
bits is already set when the handler is installed, for example because
the bootloader left a smart panel or an overlay running, complete()
runs on an uninitialised completion.

Initialise all completions in a helper called from pxafb_init_fbinfo(),
before the IRQ is requested, and drop the later initialisation.

With the completions initialised early, such an interrupt instead
leaves a completion count with no waiter, which would let a later wait
return before its transaction has finished. So reinitialise
command_done and branch_done just before starting the transactions they
wait for.

I have no PXA hardware. In a test build on ARM QEMU that raises the
LCD interrupt early through the real handler, the unpatched driver
crashes in complete() and the patched one does not. After an early
command interrupt, a later smart-panel flush returned success at once
without the reinitialisation, and timed out with it, as it should when
no further interrupt arrives.

Sashiko, the AI review bot, pointed out the early interrupt while
reviewing a pxafb cleanup.

Link: https://lore.kernel.org/all/20260722081427.413EF1F000E9@xxxxxxxxxxxxxxx/
Fixes: 07df1c4fea14 ("[ARM] pxafb: small cleanup of the smart panel code")
Fixes: 198fc108ee4c ("[ARM] pxafb: add support for overlay1 and overlay2 as framebuffer devices")
Signed-off-by: Matthias Goergens <matthias.goergens@xxxxxxxxx>
---
drivers/video/fbdev/pxafb.c | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c
index b889a17e7304..afdcb14f4b42 100644
--- a/drivers/video/fbdev/pxafb.c
+++ b/drivers/video/fbdev/pxafb.c
@@ -642,6 +642,7 @@ static void overlay1fb_disable(struct pxafb_layer *ofb)

lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1));
lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1));
+ reinit_completion(&ofb->branch_done);
lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3);

if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)
@@ -705,6 +706,7 @@ static void overlay2fb_disable(struct pxafb_layer *ofb)

lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2));
lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2));
+ reinit_completion(&ofb->branch_done);
lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3);
lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3);
lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3);
@@ -894,7 +896,6 @@ static void init_pxafb_overlay(struct pxafb_info *fbi, struct pxafb_layer *ofb,
ofb->ops = &ofb_ops[id];
ofb->usage = 0;
ofb->fbi = fbi;
- init_completion(&ofb->branch_done);
}

static inline int pxafb_overlay_supported(void)
@@ -1170,6 +1171,7 @@ int pxafb_smart_flush(struct fb_info *info)
lcd_writel(fbi, FDADR6, fbi->fdadr[6]);

/* begin sending */
+ reinit_completion(&fbi->command_done);
lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);

if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) {
@@ -1281,9 +1283,6 @@ static int pxafb_smart_init(struct pxafb_info *fbi)
fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
fbi->n_smart_cmds = 0;

- init_completion(&fbi->command_done);
- init_completion(&fbi->refresh_done);
-
if (!fbi->inf->smart_update)
return -EINVAL;

@@ -1779,6 +1778,23 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi,
fbi->video_mem_size = video_mem_size;
}

+static void pxafb_init_completions(struct pxafb_info *fbi)
+{
+#ifdef CONFIG_FB_PXA_OVERLAY
+ int i;
+#endif
+
+ init_completion(&fbi->disable_done);
+#ifdef CONFIG_FB_PXA_SMARTPANEL
+ init_completion(&fbi->command_done);
+ init_completion(&fbi->refresh_done);
+#endif
+#ifdef CONFIG_FB_PXA_OVERLAY
+ for (i = 0; i < 2; i++)
+ init_completion(&fbi->overlay[i].branch_done);
+#endif
+}
+
static struct pxafb_info *pxafb_init_fbinfo(struct device *dev,
struct pxafb_mach_info *inf)
{
@@ -1835,7 +1851,7 @@ static struct pxafb_info *pxafb_init_fbinfo(struct device *dev,
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_WORK(&fbi->task, pxafb_task);
mutex_init(&fbi->ctrlr_lock);
- init_completion(&fbi->disable_done);
+ pxafb_init_completions(fbi);

return fbi;
}
--
2.56.0