rk27xx: fix busy-wait asm that modifies an input-only operand

delay_nop() in lcd-rk27generic.c and udelay() in system-rk27xx.c both
count a register down with subs, but pass it as an input operand only:

    asm volatile ("1: subs %[n], %[n], #1\n bne 1b" : : [n] "r" (cycles));

That tells GCC the register is unchanged afterwards, so it is free to
load a constant once and reuse the register for every later call with the
same argument. Current GCC does exactly that in lcd_display_init():

    ldr   r4, =10000        @ first delay_nop(10000)
    subs  r4, r4, #1        @ ... counts r4 down to 0
    bl    lcd_write_reg
    subs  r4, r4, #1        @ next delay_nop(10000): r4 not reloaded,
                            @ 0 - 1 wraps, loop runs 2^32 times

At 4 cycles per iteration and 200 MHz that is 85.9 s per wrapped call.
Nine calls wrap, so lcd_init() took 9 x 85.9 = 773 s. Measured on a
generic rk2705 with a tick timestamp either side of lcd_display_init():
77320 ticks at HZ=100, i.e. 773.2 s. With this fix it is 9 ticks, clear
loop included.

That is why lcd_init() looked like a hang on current toolchains while it
worked when the port was written. It also explains why no LCDC clock,
divider, gating or strobe-timing change had any effect: the time was
never spent in the LCD controller.

udelay() happens to work today because its count is computed at run time
on each call, but it has the same undefined behaviour and gets the same
fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iecdd6cca0701c35bce3427f359a9a638b21291b5
This commit is contained in:
Marcin Bukat 2026-09-23 23:44:02 +02:00
parent 3c09d4570f
commit 4dcb5bae9d
2 changed files with 21 additions and 31 deletions

View file

@ -26,33 +26,22 @@
#include "spfd5420a.h"
#include "lcdif-rk27xx.h"
/* TODO: convert to udelay() */
static inline void delay_nop(int cycles)
{
asm volatile ("1: subs %[n], %[n], #1 \n\t"
" bne 1b"
:
: [n] "r" (cycles));
}
/* not tested */
static void lcd_sleep(bool sleep)
{
if (sleep)
{
/* enter sleep mode */
lcd_write_reg(DISPLAY_CTRL1, 0x0170);
delay_nop(50);
udelay(5);
lcd_write_reg(DISPLAY_CTRL1, 0x0000);
delay_nop(50);
udelay(5);
lcd_write_reg(PWR_CTRL1, 0x14B4);
}
else
{
/* return to normal operation */
lcd_write_reg(PWR_CTRL1, 0x14B0);
delay_nop(50);
udelay(5);
lcd_write_reg(DISPLAY_CTRL1, 0x0173);
}
@ -64,9 +53,9 @@ void lcd_display_init(void)
unsigned int x, y;
lcd_write_reg(RESET, 0x0001);
delay_nop(10000);
udelay(200);
lcd_write_reg(RESET, 0x0000);
delay_nop(10000);
udelay(200);
lcd_write_reg(IF_ENDIAN, 0x0000); /* order of receiving data */
lcd_write_reg(DRIVER_OUT_CTRL, 0x0000);
lcd_write_reg(ENTRY_MODE, 0x1038);
@ -88,10 +77,10 @@ void lcd_display_init(void)
lcd_write_reg(PART2_END, 0x0000);
lcd_write_reg(PANEL_IF_CTRL1, 0x0011);
delay_nop(10000);
udelay(200);
lcd_write_reg(PANEL_IF_CTRL2, 0x0202);
lcd_write_reg(PANEL_IF_CTRL3, 0x0300);
delay_nop(10000);
udelay(200);
lcd_write_reg(PANEL_IF_CTRL4, 0x021E);
lcd_write_reg(PANEL_IF_CTRL5, 0x0202);
lcd_write_reg(PANEL_IF_CTRL6, 0x0100);
@ -119,20 +108,20 @@ void lcd_display_init(void)
lcd_write_reg(DISPLAY_CTRL1, 0x0001);
lcd_write_reg(PWR_CTRL6, 0x0001);
lcd_write_reg(PWR_CTRL7, 0x0060);
delay_nop(50000);
udelay(1000);
lcd_write_reg(PWR_CTRL1, 0x16B0);
delay_nop(10000);
udelay(200);
lcd_write_reg(PWR_CTRL2, 0x0147);
delay_nop(10000);
udelay(200);
lcd_write_reg(PWR_CTRL3, 0x0117);
delay_nop(10000);
udelay(200);
lcd_write_reg(PWR_CTRL4, 0x2F00);
delay_nop(50000);
udelay(1000);
lcd_write_reg(VCOM_HV2, 0x0000); /* src 0x0090 */
delay_nop(10000);
udelay(200);
lcd_write_reg(VCOM_HV1, 0x0008); /* src 0x000A */
lcd_write_reg(PWR_CTRL3, 0x01BE);
delay_nop(10000);
udelay(200);
/* addresses setup */
lcd_write_reg(WINDOW_H_START, 0x0000);
@ -144,11 +133,11 @@ void lcd_display_init(void)
/* display on */
lcd_write_reg(DISPLAY_CTRL1, 0x0021);
delay_nop(40000);
udelay(800);
lcd_write_reg(DISPLAY_CTRL1, 0x0061);
delay_nop(100000);
udelay(2000);
lcd_write_reg(DISPLAY_CTRL1, 0x0173);
delay_nop(300000);
udelay(6000);
/* clear screen */

View file

@ -185,13 +185,14 @@ void udelay(unsigned usecs)
cycles_per_usec = (CPUFREQ_NORMAL + 999999) / 1000000;
}
delay = (usecs * cycles_per_usec) / 5;
delay = (usecs * cycles_per_usec) / 4;
asm volatile(
"1: subs %0, %0, #1 \n" /* 1 cycle */
" nop \n" /* 1 cycle */
" bne 1b \n" /* 3 cycles */
: : "r"(delay)
: "+r"(delay) /* modified by the loop: in/out */
:
: "cc" /* flags clobbered */
);
}