Commit graph

12400 commits

Author SHA1 Message Date
Marcin Bukat
9ce3fca4cb rk27xx: no whole-cache invalidate at run time
commit_discard_idcache() invalidated both cache ways at run time, at
every codec and plugin load and every USB connect. With the cache on,
the CPU crashed in the poll loop when that loop started a cache line of
its own; with the cache off around it, since 0e1952a4d1, code fetched
again afterwards could come back wrong. On a Samsung YP-CP3 a build
with a few changes elsewhere took an undefined instruction exception
in the USB interrupt handler at every boot into USB mode, always at
the same instruction, wherever the linker put it. The same build with
only the invalidate at USB connect replaced by a nop booted and
worked.

The original firmware invalidates the ways only once, at power-on with
the cache off, as crt0.S does, and after that only single lines. None
is needed at run time: the cache is unified and write-through, so what
the CPU writes, code included, is in memory and in any cached copy, and
every DMA into memory - SD reads, USB receives, recording - discards
the lines of its own buffer first. NAND is read by the CPU. So
commit_discard_idcache(), and commit_discard_dcache() with it, now do
nothing.

Tested on a YP-CP3, with the build that crashed: it boots into USB
mode, copies files with matching checksums, plays several formats,
runs plugins (fft with playback, bubbles), and records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I14301a0c05736e28e53a3365c838f4a4a73052b3
2026-10-07 08:20:44 +02:00
Marcin Bukat
361913fdd2 YP-CP3: make the WM8750 the I2S master
The WM8750 frames its ADC on ADCLRC and its DAC on DACLRC. As the I2S
slave both are inputs, and the rk27xx has a single LRCK, which on the
YP-CP3 evidently does not reach ADCLRC: with the rk27xx as the master,
the recording DMA got exact zeros on most visits to the recording
screen and real samples only by chance, sometimes changing partway
through a visit. That stays so with the codec PLL no longer glitching
the I2S clocks (previous commit); with the codec as the master it does
not happen.

The codec now drives BCLK and both LRCKs itself, and the rk27xx I2S
transmitter and receiver are slaves. The codec's MCLK still comes from
the rk27xx codec PLL at 256 fs, so sample rates stay exact. The
original firmware runs the codec as master too, but off a fixed 12 MHz
in USB mode.

RK27XX_I2S_MCLK says the rk27xx makes the codec's MCLK, apart from
CODEC_SLAVE, which also makes it the I2S master. The YP-CP3 drops
CODEC_SLAVE for RK27XX_I2S_MCLK, and the WM8750 driver sets its master
bit as it does for any codec that is not a slave.

Tested on a YP-CP3: playback, FM radio, the recording screen's peak
meter on every visit, recording from the microphone and from FM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0dc93d3b2c45fddf61649702a931d11806e78cbf
2026-10-05 22:35:12 +02:00
Marcin Bukat
ce968e7da1 rk27xx: don't reprogram the codec PLL for an unchanged rate
Every start of recording applies the sample rate again, mostly
unchanged, and set_codec_freq() reprogrammed the codec PLL each time.
That glitches MCLK and every I2S clock made of it, right before the
recording receiver is reset and starts on them. Whether it then kept
its framing came down to code timing: on the Samsung YP-CP3 the
recording screen's peak meter showed one channel empty and the other
saturated on some visits, and builds with debug code added never did.

The PLL is now left alone when it already runs the rate asked for. On a
real change the lock bit, which may still show the old lock at first,
is polled only after the 0.3 ms the datasheet gives for locking, with a
timeout of at least a full tick, and the clocks get 1 ms more to settle
before anything starts on them.

Tested on a YP-CP3, together with the codec as I2S master: the peak
meter on every one of many visits to the recording screen, with the
microphone and FM, and at 22 kHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2267e17c5bc7d8044715e762dbb6b92f45af4dcc
2026-10-05 22:35:12 +02:00
Marcin Bukat
f61d379824 YP-CP3: simulator
- uisimulator/bitmaps/UI-samsungypcp3.bmp: the YP-CP3 from the front,
  614x324, the 400x240 screen at 40,37. Dithered to RGB565: the
  simulator converts its background to the 16-bit LCD format, which
  turned the case's gradients into bands.
- sim-ui-defines.h: its window and screen position
- buttonmap/samsung-ypcp3.c: the YP-R0's keyboard layout - the
  YP-CP3 shares its keypad - and click areas for the joystick, Back and
  Menu below it, and User (Rec) and Power on the top edge above them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I9146fd54400d85636a97509993b393bff8b1ab78
2026-10-02 16:42:34 +02:00
Marcin Bukat
69af17695d rk27xx: keep the LCD bus timing at the boosted clock
The LCD controller's CSn/WEn/RDn strobes count bus clocks, set once for
1-4-1 clocks: a 120 ns write cycle at the 50 MHz bus clock, but 60 ns
when the CPU is boosted and the bus runs at 100 MHz. That is too fast
for the Samsung YP-CP3's panel: with the CPU boosted it showed stray
pixels, and partial updates left tearing behind moving things - the
boot logo too, as the firmware boosts before lcd_init().

Double the clocks while boosted, 2-8-2, which keeps the write cycle at
120 ns: set_cpu_frequency() switches them before raising the clock and
after lowering it, and lcd init picks them for the clock it runs at -
the bootloader stays at crt0's CPUFREQ_MAX.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I482073c16af39c6b1856a0e18d19a747c78975f0
2026-10-02 10:30:58 +02:00
Marcin Bukat
3fa27091f3 rk27xx: wait for the LCD DMA transfer to end
lcd_update_rect() waited on the channel's CTL_L LLP_DST_EN bit, which
the last descriptor has clear: it clears when the last block is
loaded, not when it is done. The update returned with that line still
being read, and the next one reprogrammed the window and the channel
under it.

Wait for the channel to disable itself, which it does after its last
block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icf9314fbe40b8ea53a7f5f24ea11513e87a54cf6
2026-10-02 10:30:58 +02:00
Marcin Bukat
5c5dac2dbc rk27xx: clip LCD updates to the screen
lcd_update_rect() took the rect as given. One reaching past the screen
set a GRAM window off the panel, so the update showed nothing, and
built one DMA descriptor per line into scr_llp[LCD_HEIGHT] - past its
end for a rect taller than what is left of the screen. What follows
scr_llp in memory is the PCM driver's locks and then all_queues, the
kernel's queue list: a later broadcast posted to garbage.

invadrox asks for such a rect every frame. On a Samsung YP-CP3 nothing
of its playfield moved - aliens, bombs, the ship - and powering off
afterwards took a data abort in queue_post() from interrupt context.

Clip the rect to the screen, as other targets do, and do nothing if
nothing is left. Every rk27xx screen is a multiple of 4 pixels each
way, so aligning the clipped rect to 4 cannot take it past the edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I1f8bf865251859cd223e208a807c9a9da76a0ba0
2026-10-02 10:30:58 +02:00
Marcin Bukat
5249466cc4 rk27xx: leave the NAND program running until the next access
Every program waited out its tPROG, about 0.8 ms on the Samsung
YP-CP3, before returning. Over USB mass storage that wait comes before
the status of each write command goes back to the host, and so before
the host sends the next one: nothing else runs during it.

Return once the program is started, WP# still lifted, and finish it -
wait, check the status, restore WP# - at the next chip access, or at
flash_sync(), which ftl_sync() calls. A program's result then arrives
with the next flash call; no caller checks flash_program()'s. A copy
still reports its own programs, the last one included, as the FTL
moves the data elsewhere when one fails.

On the YP-CP3 the NAND wrote at 3.64 MB/s; now 4.37 MB/s, against
4.35 MB/s in the original firmware, every read verified, also after a
power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I8de01f8be052e5d3ff06f550c614865fb1946f05
2026-10-01 23:47:51 +02:00
Marcin Bukat
8eb47f474b rk27xx: copy a page of every plane at a time
copy_sectors() copied one destination raw page at a time, so on a
two-plane part every page of a copy took two programs and two tPROG.
Its buffer already holds a page of every plane: copy that much at once,
and flash_program() programs the planes together.

Copies are most of the programs when the FTL closes blocks that random
writes left part written. Over USB mass storage on a Samsung YP-CP3 a
stress test that ran 66792 one-plane programs ran 692 now, with 43958
two-plane ones, and its program time fell from 85 s to 59 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icc60d250ecc75c83c2d817efb7678333bffe3137
2026-10-01 23:47:41 +02:00
Marcin Bukat
21d5dacdb8 rk27xx: overlap NAND read transfers with the slot copies
Every sector of a read was transferred from the chip into a controller
slot and then copied out of it, the next transfer starting only after
the copy. Start it before: it goes into the next slot, not the one
being copied.

On the Samsung YP-CP3, same test: reads at 8.25 MB/s, the original
firmware's speed, every read verified, also after a power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I53f77ea3c00bb449d772357e39602cca9c5cdba3
2026-10-01 23:47:41 +02:00
Marcin Bukat
8dcf743951 rk27xx: set the NAND bus timing from the access time
flash_init() sets FMWAIT to 0x1081, as the OF's FlashInit() does, and
nothing changed it after. But the OF, and the Samsung YP-CP3's NAND
bootloader, follow it with FlashTimingCfg(), at every bus clock
change: from chip 0's access time and the bus clock it derives a
timing that, for the YP-CP3's 25 ns Samsung part at 100 MHz, is 0x60
- by the RK28 controller's register layout, under half the bus cycles
per byte.

Do the same once chips are detected, for 100 MHz: the AHB runs at
CPUFREQ_MAX / 2 or slower, where the value only gains margin.

Over USB mass storage on the YP-CP3 the NAND read at 5.70 MB/s and
wrote at 3.26 MB/s; now 6.83 and 3.65 MB/s, every read verified, also
after a power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I356214c5d10e65952abc3334f5fae5deb2e02cff
2026-10-01 23:47:41 +02:00
Marcin Bukat
9605950788 rk27xx: re-enable the UDC interrupt at every connect
usb_drv_exit() masks the UDC interrupt in the interrupt controller at
every disconnect, but only usb_init_device(), once at boot, unmasked
it. After the first unplug the UDC raised no more interrupts: on the
next plug the charging icon showed - plug detection polls VBUS_STS -
but the host's reset and requests went unanswered, so the device never
enumerated and the USB screen never came up.

Unmask it in usb_drv_init(), which runs at every connect, so that it
pairs with the mask in usb_drv_exit(). The interrupt is now masked
while USB is off, at boot too; nothing needs it then.

Tested on a Samsung YP-CP3: it enumerates at every replug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iab7b7e93c140990c39b0e6dfae23ffcc829f6ab2
2026-10-01 21:54:02 +02:00
Marcin Bukat
ae6c5eb8a8 rk27xx: overlap NAND write staging with the transfer
Every sector of a write waited for the previous sector's transfer to
the chip before it was copied into a controller slot, so the copy and
the transfer never ran together. The slot the copy goes to is not the
one in transfer: copy first and wait only before the BCH engine and
the transfer restart.

On the Samsung YP-CP3, same test: 3.26 MB/s, every read verified, also
after a power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I41f43658916891a2ded74bbe21b6970f7cc78068
2026-10-01 21:36:03 +02:00
Marcin Bukat
cc7d8d07b2 rk27xx: program both planes of a NAND page at once
On a two-plane part an FTL page spans the same page of two blocks, one
in each plane, and flash_program() programmed them one after the other:
two program busy times (tPROG) per page. The original firmware's
FlashProgEnhanced() programs both with one two-plane program - 80h, the
first page, 11h, a wait of tDBSY, 81h, the second page, 10h - so the
planes share one tPROG.

Do the same where the runs of a write cover the same page of both
planes, on parts that take 81h for the second page. The original
firmware sends 80h there on Toshiba and Micron parts, which address
the planes differently too; those still program a page at a time.
enum vendor_t moves to nand-target.h for the check.

Over USB mass storage on a Samsung YP-CP3 the NAND wrote at 2.22 MB/s,
against 4.35 MB/s in the original firmware; now 2.86 MB/s, every read
verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2f40e2ec426cf787b4ce452cbd0d0eb4b702e168
2026-10-01 21:36:03 +02:00
Marcin Bukat
7e9d621f29 rk27xx: read each run of NAND sectors with one page latch
flash_read() latched the page afresh for every sector. The controller
streams a page from its first sector, so each sector also cost a
transfer of every sector before it in the page: reading an 8-sector
page sector by sector took 8 array loads and 36 sector transfers
instead of 1 and 8. Over USB mass storage on a Samsung YP-CP3 the
NAND read at 1.87 MB/s, against 8.25 MB/s in the original firmware.

Read each run of sectors that lie consecutively in one raw page with a
single latch. On a two-plane part a run of FTL sectors stays in one
page until it moves on to the other plane.

On the YP-CP3, same test: 5.64 MB/s, every read verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I9b08e56f4429ae432ccd2d2e23b9b34c18e56e97
2026-10-01 21:06:30 +02:00
Marcin Bukat
17db971b66 rk27xx: leave the SDRAM mode and timings as the boot left them
Firmware builds reprogram the SDRAM controller twice. system_init()
sets burst 8, T_RP = T_RCD = 1 and the refresh, with CAS 3 since
a6538abd16 - CAS 2 on HM60X/HM801. And set_sdram_timing(), on every CPU
frequency change, rewrites the mode: CAS 2 whenever the AHB runs at
100 MHz or below, which is every clock this code sets.

On a generic rk2705 the firmware hangs in system_init() on those writes;
with them skipped it boots. Doing the same writes from IRAM, so that
nothing touches the SDRAM while the controller reprograms the chip,
hangs the same way: it is the settings themselves. The board's SDRAM
is an Elpida EDS1216AATA-75 (16 MB), a 133 MHz part at CAS 3 whose
minimum clock period at CAS 2 is 10 ns - exactly the 100 MHz it runs
at here, with no margin, next to minimal T_RP/T_RCD.

On a Samsung YP-CP3 the firmware boots but corrupts memory at random
once the clock first changes: data aborts on valid addresses in
unrelated code, undefined instruction exceptions on valid instructions,
glitches in the boot logo, crashes on USB plug and unplug. A memory
test over 15 MB passes with the boot's setup (CAS 3, burst 1,
T_RP = T_RCD = 2) and with system_init()'s values alike - it never
changes the clock - and skipping only the system_init() writes is not
enough, as set_sdram_timing() still selects CAS 2. With both removed
the YP-CP3 runs, passes a USB mass storage stress test and survives
USB unplug.

The boot ROM's and the bootloaders' setup works on every rk27xx target
seen, and nothing before Rockbox changes it (the NAND bootloader's
stage 1 and rk27load's s1 only probe the organisation). The gain
claimed for the tweak was a slight improvement in memory throughput.
So remove it for every rk27xx target, the HM60X/HM801 CAS 2 included,
and have set_sdram_timing() adjust only the refresh to the bus clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icd063367783b0ae6eb80bad67d908269c07e704f
2026-10-01 20:18:10 +02:00
Marcin Bukat
2ce1cdcd8e rk27xx: don't switch SD cards to high-speed mode
Every SD write failed. The card took the write command and went to
receive-data state, but never saw the block: CMD12 still found it there,
and the controller reported a missing CRC status after each block, 20
retries over. Reads worked. Stress-tested over USB mass storage on a
generic rk2705, with a SDHC card, in the normal firmware and in the
bootloader alike.

sd_init_card() switched the card to high-speed mode with CMD6. This host
is an SD 1.01 controller with a card clock of at most 25 MHz, per the
rk27xx datasheet; high speed and CMD6 came with SD 1.10, and a card
switched to it evidently does not take the data this host drives. The
original firmware never switches: after selecting the card it sets the
block length and a 1-bit bus and stays at default speed. A slower card
clock did not help; dropping the switch alone did.

Leave the card at default speed. Tested on a generic rk2705:
ums_stress.py over a 32 MiB window of the card - fill, verify, edge
sizes, a mixed read/write soak - passes at 2.3 MB/s writing and reading,
about 75% of what a 1-bit bus at 25 MHz carries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icfafab0bb460c8c0fda3dea455df9a36622bf764
2026-10-01 20:18:10 +02:00
Marcin Bukat
3b142f45ce rk27xx: fix a -Wundef warning in simulator builds
config.h defines HAVE_STORAGE_FLUSH for the rk27xx Scheme A FTL when
CONFIG_STORAGE has STORAGE_NAND and CONFIG_NAND is NAND_RK27XX. sim.h
undefines CONFIG_NAND but keeps CONFIG_STORAGE, so a simulator for a
NAND target - the iPod nano 2G - evaluated the undefined macro:

  "CONFIG_NAND" is not defined, evaluates to 0 [-Wundef]

Test that it is defined first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Id1e328f08c14d7c5528d29f110fdbb8be68ab2f5
2026-10-01 16:40:13 +02:00
Marcin Bukat
c453f4fa8f rk27xx: name the NAND's FTL scheme in each target's config
CONFIG_RK27XX_FTL selects the FTL a target's NAND uses,
RK27XX_FTL_SCHEME_A or RK27XX_FTL_SCHEME_B. rk27generic and the YP-CP3
are Scheme A, the HM-60x Scheme B. ftl-rk27xx.c mounts the one named;
for Scheme B it maps the drives onto the volumes ID block 1 records:
the system disk from LBA 0, the user volume after the system data
area.

The other rk27xx targets with NAND - HM-801, MA8, MA8C, MA9, MA9C and
iHiFi 760, 770, 770C, 800, 960 - have no confirmed scheme. They drop
the NAND from storage and build only the FTL scheme finder, so users
can report what their device holds and the scheme can then be set.

Only Scheme A flushes at shutdown: Scheme B holds nothing in RAM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I484217b82c6de7316b90b354c012bfbaa263b3dd
2026-10-01 12:15:25 +02:00
Marcin Bukat
e83b7d6dc4 rk27xx: add a finder for the NAND's FTL scheme
Many rk27xx targets have NAND whose format nobody has examined. The
finder reads ID block 1 and the first page of the first 512 blocks and
says which FTL formatted them: Scheme A by its remap-log blocks,
Scheme B by its bad-block table and data headers, another Scheme B
generation by other 0xFxxx tags. The later ID block layout ('RK27' at
0x0a) records the FTL area's BCH strength at 0x1ed - 8 on the HM-601,
14 on the Archos Vision 28 - and the scan reads in that mode.

It is read-only, shown in the debug menu as "View FTL scheme", and
built for targets whose NAND is not storage - none yet.

Run on dumps of an HM-601 it reports Scheme B, a Samsung YP-CP3
Scheme A, and an Archos Vision 28 the other Scheme B generation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0528f9d2a6996089b6488a77b43942f6fd1f2c16
2026-10-01 12:15:25 +02:00
Marcin Bukat
16492de569 rk27xx: add the Scheme B flash translation layer
Scheme B is the self-describing NAND format of the HiFiMAN HM-601 and
similar players: every block carries its logical number and a version
in its metadata, so the mapping is rebuilt by a scan at mount, and
small writes go through a 16-page RAM cache journalled to flash.

ftl-scheme-b.c is a reimplementation from reverse engineering. The
format and the behaviour were worked out by analysing the machine code
of the HM-601's NAND bootloader and of a compiled Rockchip FTL object
from the rk2808 platform, which handles the same format, and checked
against dumps of the media; no source code was used. Where the two
binaries differ the HM-601 is followed: 16-bit versions compared
across wrap, plain 0xF200/0xF100 tags, a copy that stamps one header
on every page. The number of open exchange blocks is configurable - 8
on the HM-601, whose mount recovers no more.

Checked by running the compiled object under qemu over a NAND
simulator, side by side with this code, on a 4 GiB HM-601 dump: the
same state after mount, identical reads of all 3958 logical blocks,
and flash programs and erases identical one for one - over 600 random
writes on each of three seeds and at every power-cut point of three
sweeps, 1435 points - with 0 wrong sectors.

Not built yet: no target selects it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icb935ea78d716c1e51f1453fc3ab3f47f3467027
2026-10-01 12:15:25 +02:00
Marcin Bukat
efde59472e rk27xx: make the BCH strength of the FTL area selectable
The controller has a second ECC mode, BCHCTL bit 13: t=14 instead of
t=8, over the same field and polynomial. Some firmware writes its FTL
area in it - the Archos Vision 28 does - while every boot area seen is
t=8.

flash_set_ecc() selects the mode for reads and copies of the FTL area;
flash_read_raw(), which reads the boot area, stays at t=8. Writing is
refused in t=14 mode: a t=14 sector is a 538-byte record on the media
(3 metadata and 23 parity bytes), the program path addresses 528-byte
records, and programming in that mode has not been tried.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iaf4ce9274b72838b7719232fc49cdce0cceecbac
2026-10-01 12:15:25 +02:00
Marcin Bukat
6bf3eaff41 rk27xx: let an FTL keep its own data in metadata byte 1
The flash layer writes 0x00 into metadata byte 1 of every page it
programs, which is how Scheme A tells a programmed page from an erased
one. Scheme B keeps a 16-bit field in bytes 0-1 of every sector - its
block tags, versions and block numbers - so it needs the byte as
written.

Add flash_set_meta_passthrough() to turn the forcing off, and
flash_copy_meta(), a copy that either keeps each sector's own metadata
or programs a page of it given by the caller: Scheme B's copy stamps
one header on every page it moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I49b2c24d812284af2c50d776f0942b5b1b989c98
2026-10-01 12:15:25 +02:00
Marcin Bukat
0db035d231 rk27generic: expose the NAND as a drive
The NAND holds two volumes, SYS and USER; the split is recorded in the
boot area, so they are separate drives rather than partitions of one.
SYS is hidden: nothing in it is the user's, and deleting or overfilling
it stops the device booting. HAVE_RK27XX_NAND_SYS, documented in the
config and off, brings it back as a drive of its own.

storage.c numbers drives by driver, SD first:

                 default          HAVE_RK27XX_NAND_SYS
   drive 0       SD               SD
   drive 1       NAND USER        NAND SYS
   drive 2                        NAND USER

NUM_DRIVES was 1, which was already wrong for a target whose
CONFIG_STORAGE names both SD and NAND.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ic68921838faef0ff6fd296d43e86ec3a10a091c5
2026-10-01 12:15:25 +02:00
Marcin Bukat
a0c1085f3f rk27xx: add the Scheme A FTL
ftl-rk27xx.c has been four empty stubs since 2010. Fill them in with
the flash translation layer the rk2705/rk2706 original firmware uses,
called Scheme A here to tell it from the log-structured layout of later
firmware. It reads and writes that format exactly as the original
firmware does, so a device keeps working with its original firmware
after Rockbox has written to it.

- ftl-scheme-a.{c,h}: the FTL. The file opens with a description of
  the on-flash format and how the FTL works: the SYS and USER volumes,
  super-blocks and zones, the zone table, the remap log and its mirror,
  the exchange record and the write protocol, power-loss recovery, bad
  blocks. Oddities of the original firmware kept for compatibility are
  marked where they are.
- Parameters that differ between firmware builds - the zone reserve
  base, the system zone offset, the format flag - are recovered from
  the media at mount and checked against its structure; a mount that
  cannot confirm them is read-only. A mount that would have to repair
  the remap log while not allowed to write fails rather than serve
  wrong data.
- ftl-rk27xx.c: the storage glue. It finds the boot area's ID block,
  which records where SYS ends, and mounts the FTL.
- ata-nand-rk27xx.c: a drive per volume. SYS holds the original
  firmware - on a Rockbox device including the BASE.RKW that chainloads
  the bootloader - and nothing of the user's, so it is a drive only when
  the target defines HAVE_RK27XX_NAND_SYS. Capacity comes from the FTL's
  tables, not from raw block geometry.
- config.h: HAVE_STORAGE_FLUSH for the rk27xx NAND. The FTL holds up to
  three part-written pages in RAM; storage_flush() commits them at
  shutdown and ROLO.

Writing is opt-in: without FTL_ALLOW_WRITE the FTL mounts read-only and
never writes the flash, not even a repair the mount could make.

Two bugs of the original firmware are not reproduced. A write starting
before a page held part-written in RAM and running through it left two
buffers holding that page, and the older one was later programmed over
the newer data; such a write now flushes the held page first. And its
bad-block marker took two of its three metadata bytes from the stack,
which can make a retired block look like a remap-log block; the marker
is now written in full.

Tested in a host simulator on NAND images of a Samsung YP-CP3 and a
generic rk2705, against the original firmware's FTL object run under
qemu-arm: identical traces of every read, program and erase, with a
hash of the data each program writes, over mounting and reading, random
writes with every sector verified, a power cut at every flash operation
of a write, and a program or erase failure at every one.

On a generic rk2705: the read-only mount reports the layout and
capacities the original firmware does and every file's MD5 matches; a
write test passes 8192/8192 across a remount and a power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I573d944389cefb456ecf38c6c7b91bebfe0fe377
2026-10-01 12:15:25 +02:00
Marcin Bukat
f428c491d1 rk27xx: keep the NAND controller clocked
system_init() gates the clocks of modules a firmware build does not use,
and the list included the NAND controller's HCLK. Once the NAND is
storage, the first access to the controller - flash_init() writing
FMWAIT at 0x180e8004 - takes a data abort on the unclocked peripheral.

No rk27xx target stored to NAND before, which is why this never showed.
The LCDC clock in the same list stays gated: the firmware draws with it
gated, the MCU interface running from the LCDC HCLK, which is not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I52a4dc9e63fe5b89c85b60dc3ef77d74528dc06f
2026-10-01 10:29:06 +02:00
Marcin Bukat
455b1bcbf8 rk27xx: add the NAND flash layer
flash-rk27xx.c drives the NAND controller and its BCH engine for the
flash translation layer that follows. It knows the chip's geometry and
how to read, program, erase and copy sectors, but not what the sectors
mean.

- Addresses are 512-byte sectors in a linear view of the chip where a
  block is a super-block: on a two-plane part, one physical block from
  each plane, consecutive pages alternating between them. The layer
  maps that view to the chip; the FTL never sees planes.
- Every sector carries 16 spare bytes: 13 of BCH code, generated and
  checked by the hardware, and 3 for the FTL. Byte 1 is written 0x00 on
  every program - the "page programmed" marker the original firmware's
  FTL keys its mount and recovery on.
- The program sequence was read out of the original firmware's own
  machine code. The write kick is the read kick plus FL_WR, and the BCH
  engine needs BCH_WR to encode rather than decode.
- A copy is read through the ECC engine and programmed, never the
  chip's internal data move, which on this MLC part would carry bit
  errors forward.
- Writes are refused until the FTL enables them, and writes into the
  boot area are dropped and reported successful, as the original
  firmware does. The write-protect line is lifted only for the duration
  of each program or erase.
- Failures and timeouts are counted: the FTL can act on few of them.

Only the first chip is handled; every device the FTL has been checked
on has one.

Tested on a generic rk2705 directly, on a free block: a program across
both planes, a single-sector program with metadata, a whole page, a
copy and an erase each read back byte-exact through the controller's
ECC decode, which also shows the code it generated is valid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: If6ffa9f9811172f4f8829c1e68d6de9466a022c8
2026-10-01 10:29:06 +02:00
Marcin Bukat
d8f099f937 rk27xx: restore the missing 0xd5 NAND device code
flash_init() identifies a chip by looking its READ ID device code up in
device_code[] and taking the capacity from device_info[] at the same
index. device_code[] had seven entries and device_info[] eight: 0xd5
(16 Gbit) was dropped when the table was transcribed from the OF. Every
code after the gap picked up the capacity one row up, so a 0xd7
(32 Gbit, 4 GiB) part was sized at 2 GiB.

On a generic rk2705 that halved total_phy_sec to 4194304, and the FTL
looked for its tables in the wrong place and read erased flash. With the
entry restored it reports 8388608, matching the chip and the host-side
dump of the same unit.

The OF's own table, as it appears in its NAND bootloader:

    76 79 f1 da dc d3 d5 d7  00 00 02 00  00 00 04 00 ...

A compile-time check now fails the build if the two tables differ in
length again. Also fixes two register addresses in the same loop that
were missing a digit (0x180E204/0x180E208 for 0x180E8204/0x180E8208);
they are stored for reference only and nothing reads them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ia70c249eea2e44ea34aef52d8ae20860b2b5db9d
2026-10-01 10:28:03 +02:00
Marcin Bukat
c3356ac35f rk27xx: build the raw NAND layer
nand-rk27xx.c held a complete transcription of the OF's NAND handling -
chip detection, geometry derivation, chip select, ECC reads - inside
"#if 0", written as documentation back when the FTL was still unknown.
It has therefore never been compiled.

Enabling it exposed three things nothing had ever caught:

 - flash_init() looks up ManufactureIDTbl[] and DeviceCode[], but the
   tables are named manufacture_id_tbl[] and device_code[]
 - mlc_refresh_row, flash_pend_cmd and flash_read_status_cmd are
   assigned but were never defined
 - memcpy() was used without including string.h

struct flashspec_t moves to nand-target.h, with flash_spec[] and
total_phy_sec declared there, because the FTL's flash primitives need
the geometry flash_init() derives. The "_raw" fields describe one
physical plane and the others the multi-plane view the FTL addresses;
that distinction is load-bearing for the FTL's address mapping, so both
are kept.

flash_read_page() is renamed flash_read_page_raw(). It reads a whole
page unbuffered and without ECC, and the name is needed for the FTL
primitive that does the ECC read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I3eb2d6f41d72b3239a0f9493e49532c242c588fa
2026-10-01 10:28:03 +02:00
Marcin Bukat
53bc6abc97 rk27xx: fill in the NANDC BCH register definitions
BCHCTL bit 13 and the whole of BCHST were marked unknown. Both are
needed by any driver that reads NAND through the controller's ECC
engine: a read has to be able to report an uncorrectable sector, and MLC
parts want a refresh once a sector needs enough correction.

  BCHCTL bit 13   ECC strength, clear = t=8, set = t=14 (both m=13,
                  poly 0x25af). Established by decoding both modes
                  against real media until the stored ECC bytes
                  reproduced.

  BCHST  bit 0    result valid
         bit 2    error - uncorrectable when set together with bit 0
         bits 6:3 number of corrected bit errors

Taken from the rk2705 NAND bootloader's ECC read loop:

    tst r0,#1 ; tst r0,#4     both set -> sector uncorrectable
    lsl r0,#25 ; lsr r0,#28   -> (BCHST >> 3) & 0xf, corrected bits
    cmp r0,#3                 >= 3 triggers a block refresh

The threshold agrees with MlcRefreshHook in the Samsung OF's flash.o, so
two independent firmwares say the same thing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I1e356d5510827b46035f1d7d4a2cc69f4e83f43f
2026-10-01 10:28:03 +02:00
Marcin Bukat
0e1952a4d1 rk27xx: invalidate the cache with the cache controller off
commit_discard_idcache() invalidated both cache ways while running from
cached SDRAM. When the loop polling for the invalidate to finish starts on
a cache line of its own, it is fetched through the ways being invalidated
and the CPU takes a data abort, reported at the loop's branch. Whether it
crashed thus depended on where the linker put the function.

usb_storage calls it on every USB connect. On rk27generic a jpeg change
that grew clip_jpeg_fd by 8 bytes moved the loop onto a new line, and the
firmware crashed as the USB screen came up, with an empty backtrace.

Turn the cache controller off around the invalidate, as crt0.S does at
start-up. The cache is write-through, so no data is lost.

Tested on a generic rk2705 with the function padded so the poll loop starts
a new cache line: without this change it crashes at the first USB connect,
with it the device enumerates as a mass storage device. The normally
linked build works too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I319b811fdef30717999b60132000e70f410001fc
2026-09-30 16:47:48 +02:00
Marcin Bukat
70c546113d rk27xx: bring the hardware-tested UDC fixes to the current USB core
The rk27xx UDC driver was fixed and stress-tested on a generic rk2705
against the USB core of early 2026 - including a port to the control
request API of that time. Meanwhile the core took over the EP0 state
machine (usb_core_setup_received()) and endpoint allocation, and the
driver was converted to both without the fixes. This carries them over.

Transfers and resets (a mass-storage stress test fell off the bus after
10,000 - 27,000 operations without these):
- Transfers are set up with interrupts disabled: the interrupt handler
  advances buf/cnt of the same endpoint for the next packet.
- ep_write()'s wait for TXFULL to clear is bounded by an iteration count.
  It also runs in the interrupt handler, where current_tick never
  advances, so a tick timeout spun forever.
- A bus reset cancels transfers - usb_drv_cancel_all_transfers() was
  empty - instead of re-initialising the completion semaphores, which
  loses a thread blocked on one for good; blocked senders are woken with
  an error and the enabled endpoints NAKed and flushed. The reset handler
  also calls usb_core_bus_reset(), which it never did.
- Blocking sends time out after a second and report the error.
- An ACK with no transfer armed (one cancelled by a reset) is ignored.

Configuration:
- The configuration number is DEV_INFO [11:8]; it was read as bits 10:7,
  bit 7 being DEV_EN, so configuration 1 was reported as 2.
- The UDC completes SET_ADDRESS and SET_CONFIGURATION itself, raising no
  interrupt, so udc_helper() - which reports them from DEV_INFO - also
  runs from a tick task while the device is unconfigured. After a bus
  reset of a configured device the host re-sends SET_CONFIGURATION and
  goes straight to a bulk command that NAKs without interrupting: without
  the tick, the device never came back.

EP0, with the core now running the control state machine:
- Right after connect the UDC reports one SETUP with both registers zero;
  no host sends that, and it is ignored.
- A SETUP clears a stall, and ends - reported to the core as failed -
  any EP0 transfer still in flight, which belongs to a request the host
  abandoned; otherwise the core would wait for it forever. EP0 stall uses
  the EP0 registers, not endpoints[0], a stub without registers.
- Control reads are clipped to wLength and end with a zero length packet
  when a short answer fills whole packets.
- The core arms status stages with no buffer; they land in a dummy one.
- A status OUT arriving while the data IN is still going - the host took
  less than was offered - ends the data stage too.
- At a bus reset EP0 transfers are dropped silently: the core resets its
  own EP0 state.

Tested on a generic rk2705. The transfer, reset and configuration fixes
first against the USB core of early 2026: RAM-disk, NAND and SD stress
tests over USB mass storage, and usbreset recovery. Then the driver as it
is here, on the current core: enumeration, and a mass-storage stress test
of three LUNs at once (RAM disk, NAND, SD) that also passes after an eject
and a cold power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I1b2d03ca8f688f2a0e7b28ead64b7ab050a0a9e8
2026-09-30 15:28:54 +02:00
Marcin Bukat
475b4629b6 rk27xx: attach to the bus only once the USB core is set up
The UDC was only ever connected from the interrupt handler, on CONN_INTR.
That needs a cable-insert edge after the stack is up, and there is none
when the cable is already in - booting with it plugged, or taking the
controller over from the ROM loader or hwstub, which leave it enumerated
as a different device. The device then never enumerated.

Connecting from usb_drv_init() would not do either: usb_core_init() calls
it first, before the class drivers are set up and before the core sets its
own state, so a fast host enumerated against state that was then
overwritten and the descriptor read timed out - depending on timing.

Add usb_drv_connect(), which drops off the bus, resets the PHY and
reconnects, and call it from usb_enable() after usb_core_init() returns.

Tested on a generic rk2705, loaded over hwstub with the cable in: the
device drops off, comes back and enumerates as Rockbox.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I372bd6a49594c00909ada0cc43b046178140e755
2026-09-30 15:23:08 +02:00
Marcin Bukat
79329c8f7c rk27xx: don't return from SD transfers with the lock held
sd_read_sectors() and sd_write_sectors() take sd_mtx and power the
controller, then check the requested range and return -1 on failure -
leaving the mutex held and the controller on. Check the range first.

With no card present numblocks is 0, so every request takes that path.
Rockbox mutexes are recursive for the owning thread, so the first thread
to touch the SD drive keeps working, and every other thread that does
blocks forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I16c2be3bde5c62946fdedaabe115b6c4bc330467
2026-09-30 14:43:00 +02:00
Marcin Bukat
89e9ade855 rk27generic: FM radio screen keys, tuner noted as an RDA5807P
The FM screen had no actions for the rk27xx generic keypad; give it those
its keymap already maps (menu, play, stop, exit) in radio.c.

The board's tuner is an RDA5807P. It keeps being driven as a TEA5767, in
the chip's compatible mode, as the original firmware does: tuning, seek
and the stereo indicator work so, and the RDA mode would bring nothing
here - this variant has no RDS. Say so next to CONFIG_TUNER.

The tuner's audio is on the codec's line input 1: only that line is
powered and mixed in while the radio plays (RK27XX_CODEC_FM_LINE 1). With
line 2 instead the radio is silent.

Tested on the rk27generic board: manual tuning, seek, stereo indicator and
audio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2135ee484705ff0fce6d88e73288d8ed2aec6c2d
2026-09-30 08:07:47 -04:00
Marcin Bukat
19caf27567 rk27xx codec: mix in only the tuner's line, keep the DAC
audiohw_set_monitor() switched the output mixer from the DAC to both line
bypasses: voice and beeps went silent while the radio played, and a line
nobody listened to was mixed in, with its noise. Both line inputs were
also powered from start-up on.

- the target names the line its tuner is on, RK27XX_CODEC_FM_LINE (1 or
  2); both are used where it does not say
- monitoring adds that line's bypass to the DAC instead of replacing it
- the line inputs stay in standby until monitored, and go back after

Only rk27generic uses the internal codec - the other rk27xx targets
have external DACs or codecs.

Tested on rk27generic: the radio plays through the monitored line, key
clicks stay audible over it, and playback is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I177fdf605e4d997159898c868b2186c9584eb19a
2026-09-30 08:07:15 -04:00
Solomon Peachy
bbe37187d8 imx233: Increase bootloader's firmware buffer size to 2MB
Bootloaders with this change are now able to boot rockbox binaries over
1MB.  (1.5MB buffer led to bootloader hanging)

Change-Id: I540bd4146aaa7236df93e74f6f2c69aa32e4a874
2026-09-29 15:15:45 -04:00
Marcin Bukat
3689292b28 YP-CP3: recording from the microphone and FM radio
- config: HAVE_RECORDING, sources microphone and FM, 8-48 kHz
- wm8751.c: the YP-CP3's own audiohw_set_recsrc(). The rk27xx cannot
  send received samples straight back out, so what is heard of an input
  goes through the codec's analog bypass - the input PGA into the output
  mixers: the radio always, the microphone never. As in the original
  firmware, the radio passes the PGA at +12 dB when only listened to,
  and the microphone - mono, on RINPUT2 - gets +13 dB boost and is
  recorded by the right ADC onto both channels (the original firmware's
  noise gate is left out). The HD300's version assumes the microphone on
  INPUT3 and headphones on OUT1, and switches OUT2 - the YP-CP3's
  headphones - off.
- wm8751.c: the playback-only FM monitor added for the YP-CP3 goes; the
  radio is routed by audiohw_set_recsrc() now, as on the HD300
- wm8751.h: DATSEL, which ADC feeds each channel of the output data

Only partly tested on hardware: FM radio still plays, and the recording
screen's peak meter follows the microphone. Not yet tested: a recorded
file played back, recording FM, recording gain range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I3c25f4333f765d98dddc2fd722c111f8493bbe77
2026-09-29 09:52:13 +02:00
Marcin Bukat
a33b0c7360 rk27xx: recording over I2S and HDMA channel 1
pcm-rk27xx.c had no recording at all. Add it for targets with
HAVE_RECORDING: HDMA channel 1 moves the I2S Rx FIFO into the recording
buffer - hdreq 7, fixed source, incrementing destination, 32-bit inc8
slices, the mirror of the playback channel and the setup the Samsung
YP-CP3's original firmware uses. Playback keeps channel 0, so the two can
run together.

- HDMA_ISR holds both channels' masks and flags; playback used to write
  all of it, clearing whatever the other channel had. Each channel now
  updates only its own bits, and INT_HDMA serves each channel's count
  down flag. Both channels start masked and clear.
- Recording cannot mask the shared interrupt, so pcm_rec_lock() defers a
  completed buffer to pcm_rec_unlock() instead.
- In master mode the I2S Rx side runs only while recording: started with
  nothing reading its FIFO it upsets playback. I2S_RXCTL gets the Tx
  frame format, plus bit 24 as the YP-CP3's original firmware sets it.
- audio-rk27xx.c routes inputs through audiohw_set_recsrc() on targets
  that record.

Only partly tested, on a YP-CP3: playback and FM radio still work after
the interrupt changes, and the recording screen's peak meter follows the
microphone, so samples arrive through the DMA. Not yet tested: playing
back a recorded file, recording FM, long recordings, recording while
playing. A known risk: a flag set by the hardware in the few cycles of
the read-modify-write of HDMA_ISR would be lost and stall that channel.
Other rk27xx targets build; their playback was not retested on hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Idc64bce8075d0fc628c29767fc33beda4fff4342
2026-09-29 09:50:20 +02:00
Marcin Bukat
c922ef98c3 YP-CP3: Si4703 FM radio
The YP-CP3's tuner is a Silicon Labs Si470x at I2C address 0x20, an
Si4703 by the RDS the original firmware enables. The firmware drives it
the Si470x way - writes from register 2, reads from 0x0A - starts its
oscillator with TEST1 = 0x8100 and a 500 ms wait before ENABLE, and has
no power or reset line for it.

- config: CONFIG_TUNER SI4700 with RDS, polled (no interrupt line
  needed), and FM radio as an input source
- si4700.c: the YP-CP3 starts the oscillator as the Sansas do
- power-ypcp3.c: tuner power stubs - there is nothing to switch
- the rk27xx tuner I2C glue, and "radio" in the target's configure entry
- wm8751.c: audiohw_set_monitor() for a WM8750 target that plays the
  radio but cannot record (rk27xx has no recording yet). The tuner, on
  LINPUT1/RINPUT1, goes through the input PGA at +12 dB - as in the
  original firmware - into the output mixers, the DAC staying in the mix.
  The existing monitor routing lives in the recording code and switches
  OUT2 off, which is the YP-CP3's headphone output.

Tested on a YP-CP3: stations tune and play at a sane level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ic097fc85dbd7fb71fcc28c97a9d378fa09599e79
2026-09-29 09:47:51 +02:00
Marcin Bukat
a35f344eef YP-CP3: WM8750 audio
The YP-CP3 uses Wolfson WM8750. Headphones are wired on OUT2 and
a headphone amplifier enabled by GPIO F2, active high -
all RE from the original firmware.

The original firmware runs the codec as I2S master in its 12 MHz "USB
mode", fed a fixed 12 MHz MCLK, which puts 44.1 kHz at 44.118. Rockbox
instead makes the rk27xx the master and clocks the codec from the codec
PLL at exactly 256 fs (CODEC_SLAVE, as every other rk27xx target with an
external codec), so the codec's CLOCKING register is its normal-mode
256 fs setting at every rate. 96 kHz is left out: the WM8750 cannot take
it at 256 fs.

- config: HAVE_WM8750, CODEC_SLAVE, rates 8-48 kHz; the WM8750 has
  hardware tone controls, so HAVE_SW_TONE_CONTROLS goes
- ypcp3/wmcodec-ypcp3.c: register writes over the rk27xx I2C driver
- wm8751.c: on the YP-CP3, power on and drive OUT2 instead of OUT1, set
  the volume there, and switch the amplifier with the outputs
- english.lang: the YP-CP3 gets the bass/treble cutoff settings the
  WM8750 brings

Tested on a YP-CP3: playback at 44.1 and 48 kHz on headphones, pitch and
volume correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I870038fb6a21a9c26b025e3df7c220fd925f49f6
2026-09-29 09:34:02 +02:00
Marcin Bukat
f7655e226c Samsung YP-CP3: Seiko S-35390A on I2C as RTC
Found in the original firmware by its command-in-address protocol
and bit-reversed data; the original firmware leaves it
in 12-hour mode, which the driver now handles.

Change-Id: I9f4147d3057aaa6aa498a1750cb4e746d69b1be6
2026-09-29 09:30:23 +02:00
Marcin Bukat
2190a31dac Samsung YP-CP3 define power hold pin
Change-Id: I9fbfa00b37d3f2194df36f808fa01e2a2eab0e59
2026-09-29 09:29:38 +02:00
Marcin Bukat
0dc08e53a9 S-35390A RTC: bring the driver back, handle 12-hour mode
The S-35390A keeps the hour either as 0-23 or - its default after a
power-on reset - as 0-11 plus a p.m. flag, selected by the 12/24 bit of
status register 1. The driver assumed 24-hour mode and never set it: it
masked the p.m. flag off, so on a chip left in 12-hour mode every
afternoon read as morning, and writing an afternoon time stored an
out-of-range hour.

Read the mode in rtc_init() and convert the hour both ways. The chip's
mode is left alone, as another firmware on the same player may depend on
it - the Samsung YP-CP3's original firmware runs it in 12-hour mode and
never touches the bit. If the status register cannot be read, the driver
keeps assuming 24-hour mode, as before.

Upstream removed the driver with its only users, the Meizu M3/M6 and
Samsung YP-S3 ports (1a33d7990a); the Samsung YP-CP3 needs it, so it
comes back here, with RTC_S35390A and its SOURCES entry.

Also pick the I2C header by CONFIG_I2C, so rk27xx targets can use the
driver; i2c_read()/i2c_write() have the same shape there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I54d8c8cfaa1b88237f6c4b08d2a510ec9fce8ec5
2026-09-29 09:20:12 +02:00
Marcin Bukat
8a53c15d1c rk27xx: only list PLL settings for the rates a target has
set_codec_freq()'s table of codec PLL settings names every rate from 8 to
96 kHz by its HW_FREQ_ index, which exists only for rates in the target's
HW_SAMPR_CAPS. Every CODEC_SLAVE target so far has all of them; a codec
without 96 kHz at 256 fs - the WM8750 - leaves HW_FREQ_96 undefined and
the table no longer builds.

Wrap each entry in HW_HAVE_xx_(), as the codec drivers' tables do. For
the existing targets the table is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ifab688ffa6f83c5319d686fcf40fc2b887c75d24
2026-09-29 09:06:36 +02:00
Marcin Bukat
98f162b212 New target: Samsung YP-CP3 (rk27xx)
Scaffolding for a port, starting from the rk27xx generic target: configure
entry 146 (target id 117, model number 132), config/samsungypcp3.h, and
firmware/target/arm/rk27xx/ypcp3/.

- LCD: the rk27xx LCD interface (lcdif-rk27xx.c) with this panel's init
  sequence, from RE. See g#1050. Looks like SPFD5420A 18-bit bus,
  400x240 landscape. It differs from the generic panel's sequence in the
  gamma curve and in not writing VCOM_HV1.

- Backlight: PD4 / PWM0 with the generic board's timing, which the hwstub
  work found to be the same.

- Storage: the microSD slot. Card detect is PC7, active low,
  as on the generic board.

- Keys: the YP-R0's key set - five-way yog, Back, Menu, Rec/User, Power -
  so the target uses SAMSUNG_YPR0_PAD and its keymaps. Eight keys sit on
  two resistor ladders on the LSADC, found in the original firmware and
  measured on a unit:
      channel 1   up 158, down 295, select 435, left 561, right 687
      channel 2   menu 155, back 292, user 431
  each key owning half a step either side of its reading;
  power is GPIO C1, active high.

Builds as firmware and as bootloader. Status 3 (unusable) in builds.pm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iff4984023415f341ca7507af6b5b2b551201e953
2026-09-29 08:53:27 +02:00
Marcin Bukat
d3893ead3a rk27xx: keep interrupt and bulk endpoints in separate groups
USB mass storage writes ran at 0.03 MB/s in the firmware - to the NAND
and to the SD card alike - while the bootloader, with the same driver,
wrote the same NAND at 2 MB/s. The firmware adds a HID interface, and
without it writes ran at full speed.

The UDC's endpoints come in groups of three - bulk OUT, bulk IN,
interrupt IN: 1-3, 4-6 and so on - and allocation handed out the first
free endpoint of each type, so HID's interrupt endpoint 3 landed in the
group of mass storage's bulk endpoints 1 and 2. The host polls it every
16 ms, the idle endpoint NAKs, and each poll costs the group's bulk
traffic: writes advanced about one packet per poll. Polled every 125 us
instead, they all but stopped; moved to endpoint 6, in a group of its
own, they ran at 2.36 MB/s, as without HID.

Never give an interrupt endpoint a group with bulk endpoints in use, or
the other way round, whichever class asks first. Since which endpoints
are available then depends on what is already allocated, the driver
tracks the allocation itself - option 2 of usb_drv.h, as usb-designware
does - with its context in usb-rk27xx.h, which usb_core.c includes before
usb_drv.h. HID keeps working.

Tested on a generic rk2705 with ums_stress.py over USB mass storage to
the SD card: 0.03 MB/s with HID on endpoint 3, 2.36 MB/s with it on
endpoint 6 - the allocation this change produces for mass storage plus
HID. (Measured with the driver's earlier allocator, before the USB core
took allocation over; this carries the same rule into the new scheme.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ib80ed206a3705f2c76fc8457b5c0a54d60a46402
2026-09-28 16:55:21 +02:00
Marcin Bukat
4dcb5bae9d 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
2026-09-28 16:22:13 +02:00
Marcin Bukat
9bcdd30293 usb_storage: flush storage when the host stops or ejects the unit
A host that is done with a mass-storage device - "safely remove", eject,
udisksctl power-off - sends START STOP UNIT with the start bit clear, and
may cut power right after. usb_storage only marked the LUN ejected.

Storage drivers can still hold data in RAM at that point: a flash
translation layer keeps part-written pages until a later write completes
them, and HAVE_STORAGE_FLUSH exists so they can be committed - but only
shutdown and ROLO called it. On a device unplugged after a proper eject
and then losing power, that data was lost although the host had done
everything right.

Call storage_flush() on stop and on eject, on targets that define
HAVE_STORAGE_FLUSH. The SCSI handler runs in the USB thread, as do the
reads and writes, so the flush cannot race them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I5b5811858c75dfb3ee89535ab59f4a19663945df
2026-09-28 08:39:24 +02:00
Solomon Peachy
2745e0b973 FS#14011: Handle usb hosts whose first request is SET_ADDRESS (Anthony Fletcher)
Thanks for the SET_ADDRESS rework. I think it leaves one case uncovered:
a host whose first request is SET_ADDRESS ends up with a device whose
interfaces are all numbered 0.

Two things still depend on the core seeing a request before the address arrives:

  1. usb_core assigns interfaces and endpoints only on the first control
     request it handles in DEFAULT. When SET_ADDRESS comes first, the driver
     completes it and usb_core_set_address() moves the state to ADDRESS. So
     allocate_interfaces_and_endpoints() never runs.
  2. Under USB_DETECT_BY_REQUEST, usb.c enables the class drivers only
     on a USB_TRANSFER_COMPLETION event. The SET_ADDRESS status stage now
     completes inside the driver, so the drivers are still disabled when the
     address arrives.

Change-Id: Ifacb8b07cbdefb0dee3d414c2257a50a08759f3d
2026-09-27 19:36:21 -04:00