Commit graph

39773 commits

Author SHA1 Message Date
Michael Giacomelli
5d8ce62b45 codecs: link libarm_support for its division routines
lib/arm_support/support-arm.S was written to replace libgcc's
division for ARM, and bff5a35c3c (FS#10943, 2010) added it to the
core, the plugin library and the codec library alike.  When
1501df045f (2013) replaced EXTRA_LIBS with explicit lists, plugins
kept it and codecs did not, and they have taken their division from
libgcc since.

With the gcc 4.4 toolchain that cost little: its libgcc had a
routine that used clz.  With gcc 9.5 libgcc has no soft-float ARMv5
variant, so ARMv5 targets get the ARMv4 routine, a shift and
subtract loop of about 130 cycles a division.

Monkey's Audio divides two or three times a sample in its range
decoder and, without codec IRAM, does it in C.  On a Clip+ it is
11% to 21% slower than 3.14 was, with over half of -c1000's decode
in __udivsi3.  MP2 is 4% to 7% slower.

Put libarm_support back, ahead of libgcc.  In perfsim's model of
the Clip+ a division falls to 44 cycles, Monkey's Audio by 38%, 30%
and 22% at -c1000, -c2000 and -c3000 (60.9 to 37.9 MHz at -c1000),
and MP2 by 3% to 7%; nothing else moves by more than 0.6%.  A Clip+
decodes -c1000 with the same checksum as before.

A division by zero in a codec goes to __div0 again, and so to the
firmware's handler, as it did with support-arm.S and with the old
libgcc (3.14's ape.codec calls it).  The gcc 9.5 libgcc returns
from its own stub instead.

Change-Id: I6a9a79ce8e85bca69870349a5c0823f392a578b6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 07:46:37 -04:00
Marcin Bukat
4c78e98461 cabbiev2: 400x240 FM screen, sharing the WPS backdrop
CabbieV2 had an FM screen only for the 160x128 and 128x128 greyscale
targets. Add one for 400x240, laid out like its WPS: station art and
names where the album art and track info are, the frequency in the
progress bar, Scan/Preset, MHz and Stereo/Mono below it, and hold,
battery, volume and signal strength along the bottom.

Both screens share one backdrop, its header bar empty: each draws its
label, NOW PLAYING or FM RADIO, in Helvetica Bold rather than having it
painted into a backdrop of its own. The shuffle and repeat
placeholders, painted into the old WPS backdrop, are images drawn
while those are off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0331184479589cd4953bd78f4ead336f5d9b6fea
2026-10-05 10:03:20 +02:00
Marcin Bukat
b0ad97add8 wpsbuild: ship the fonts a skin loads with %Fl
A theme's own font is converted into the build; fonts its skins load
themselves with %Fl were not, and needed the font package - or the
skin fell back to another font. Convert those as well, the way the
images a skin uses are copied with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ifb698f7417287954130be9b466fe3b2248c5c773
2026-10-05 10:03:20 +02:00
Marcin Bukat
cc362a8ecd buildzip: install wps/ subfolders with a dot in their name
make install skipped every wps/ subfolder whose name has a dot in it:
the test meant for "." and ".." matched a dot anywhere. A theme named
with one lost its images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ic225960349e5d73ae4defa7524fa2cee5066e920
2026-10-05 10:03:20 +02:00
Marcin Bukat
13553bda06 radio: redraw the FM screen after the autoscan question
Entering the FM screen with no presets asks whether to scan for them.
The question and the scan clear the screen, after fms_fix_displays()
had shown the skin's backdrop, and nothing showed it again: the skin
redraws only its viewports, so the backdrop stayed missing everywhere
else - the header bar of CabbieV2's FM screen among it.

Leave the FM screen for the question and the scan and enter it again
after, as for the other screens shown from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I7caadf3ac2a03f2825176084d8969ae6e59af471
2026-10-05 10:03:20 +02:00
Michael Giacomelli
f86be7dc42 test_codec: fix stale results screen and scroll the log
The results were drawn right after backlight_on(), which only queues
a request to the backlight thread. lcd_update() does nothing while the
LCD is off, and the plugin then blocked waiting for a key without
updating again, so the display could keep showing the last progress
line. Refresh the display periodically while waiting for a key.

Also scroll the log up when the screen is full instead of wrapping
around to the top and overwriting old lines, which made the output
hard to read when testing a whole folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 22:46:38 -04:00
Solomon Peachy
ecdeb02dda FS#14005 - Use a 44.1KHz floor when guessing playback frequency
Regression introduced in f87ff3a9b, which made it possible for audio
playback to request a freq under 44.1KHz, instead of treating 44.1 as
a floor (and upsampling)

However, there is a report of 22KHz files playing back distorted on an
imx233 target.

IMO a 44KHz floor is reasonable, but this bug is a symptom of something
deeper.  Perhaps the mp3 codec isn't doing the right thing, or there's
an issue in the pcm mixer somewhere, or the imx233 codec doesn't properly
handle 22KHz?  Further investigation is warranted.

Change-Id: I751ce05f8605de7f90d3eb7b3c98873487df438b
2026-10-04 13:04:56 -04:00
Solomon Peachy
50d8c4a58e FS#14029 - Updated Vietnamese Translation (Chu Khánh Hạnh)
Change-Id: I6f5807d76989375b6318d02fd0e42f8beaa488fa
2026-10-04 13:01:55 -04:00
Michael Giacomelli
0ed3734e5e flac: cope with frames larger than one buffer request
The frame decoder reads from one flat buffer and cannot refill it, but
request_buffer() only guarantees 32KiB of contiguous data (the buffering
guard area). Frames that can be larger than that, such as high
resolution or poorly compressible streams (FLAC decoder testbench file
31), could be handed to the decoder truncated, which read past the end
of the data and lost sync.

When a request returns less than the largest frame the stream can
contain (STREAMINFO max framesize, or a bound from block size, channels
and bit depth) and it is not the end of the file, copy the frame into a
private static buffer and decode from that. Streams whose frames always
fit never touch the buffer and pay one comparison per frame. The buffer
is 64KiB, or sized for the 4608 sample blocks of memory limited targets,
and is left out entirely when MEMORYSIZE is 2MB or less.

Also fail with a codec error, instead of advancing past the data, if a
decoded frame consumed more bytes than were provided.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Change-Id: Ib4cf85ad513f8e096f73d13b4374d072e1745cb2
2026-10-03 18:17:56 -04:00
Michael Giacomelli
e430ecb607 imageviewer/jpegp: don't crash or hang on damaged files
On colour targets the image viewer hands every file its own decoder
rejects to jpegp, including damaged ones, but jpegp barely checks its
input. Corrupt and truncated files crashed or hung it:

- At the end of the file GETC() kept returning stale bytes, so marker
  searches and table reads never ended. Feed EOI markers (FF D9)
  instead, which ends every loop, and stop calling read() there. This
  state is reset in OPEN(): the overlay loader does not clear .bss.
- A file ending before any scan decoded as a blank image. Report it as
  corrupt instead.
- Out of range header values were used as array indexes: Huffman and
  conditioning table IDs, Huffman table sizes, sampling factors, scan
  component counts and spectral selection. Reject them, and frames of
  zero width or with no components.
- Invalid Huffman codes walked past the code length table, run lengths
  wrote past coefficient 63, and huge coefficients indexed past the
  IDCT clamp table. Bound all three.
- An odd DAC segment length never ended its loop.
- The coefficient buffer size could overflow an int.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I49465f38d283e159274d2f381029280cea42a8f1
2026-10-03 08:01:44 -04:00
Solomon Peachy
bef221afd7 checkwps: Also validate 'filetype colours'
Change-Id: I6a3dd1b08afb1037e7315fbf820dada3d108d909
2026-10-02 17:51:31 -04:00
Solomon Peachy
90506b8808 checkwps: Numerous improvements to cfg validation
* Check iconset, remote iconset, and viewer iconset
 * don't crash (or fail) on empty strings
 * when component path is not absolute, search appropriate path
   (eg FONT_DIR, ICON_DIR, WPS_DIR, SBS_DIR)

Change-Id: I256d00d26d79b1d1f09038877cfaaa804123df72
2026-10-02 17:43:43 -04: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
Solomon Peachy
9910b2aebf Revert "FS#14019: Detect and reject outdated binary language files (WIP)"
This reverts commit c0ae6c7cd1.
2026-10-02 10:18:29 -04:00
Solomon Peachy
025284d95a checkwps: '-' is also valid for backdrops
Change-Id: I67c2d871f053afca277c8fc4ad671f9689f5c647
2026-10-02 09:59:46 -04:00
Solomon Peachy
c0ae6c7cd1 FS#14019: Detect and reject outdated binary language files (WIP)
Accomplish this by checksumming the english language input
and (1) including that in binary files and (2) checking the
value matches what was compiled into the firmware image

Not sure if this is the best approach but it works.

Change-Id: I8f79ad1b9d1cdf69e6a085d7b3dd1b5e078af04b
2026-10-02 09:14:52 -04:00
Solomon Peachy
265cf7ac92 FS#14026 - Spanish Translation Update (Jordan Fajardo)
Change-Id: Idccd1d1a48c1c6f28d77782cc73e36b6868faff3
2026-10-02 08:28:55 -04:00
Michael Giacomelli
9936e65e6d imageviewer/jpegp: decode RGB images
jpegp converted every image from YCbCr, so RGB JPEGs showed scrambled
colours. That affects progressive RGB files, and now also baseline RGB
files the jpeg decoder rejects and hands on to jpegp, such as RGB with
the R component sampled 2x2.

Record the JFIF and Adobe APP14 markers, decide the colour space with
the same rule as the other decoders, and skip the YUV conversion for
RGB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: If023eeb612b7f8a891d21fbe0070ab43c5a08f17
2026-10-02 08:24:03 -04:00
Michael Giacomelli
142a6fbb39 jpeg: decode RGB images
Both decoders treated every 3-component image as YCbCr, so RGB JPEGs
(as written by cjpeg -rgb, and by some Adobe software) came out with
wrong colours.

Decide the colour space as libjpeg does: a JFIF marker means YCbCr;
otherwise the transform flag of an Adobe APP14 marker decides (0 is
RGB); otherwise component IDs 'R', 'G', 'B' mean RGB.

Core loader: on colour targets R, G and B are stored in place in the
row buffer and the YUV conversion is skipped. Greyscale builds now
also decode G and B for RGB and combine them into luma per block,
which needs every component to be one block per MCU; other RGB
layouts are rejected there.

Plugin: RGB needs one block per MCU for every component, otherwise it
is rejected (colour targets fall back to jpegp). Colour builds convert
the R, G and B planes to YCbCr in place after decoding, so display and
greyscale view modes are unchanged; greyscale builds combine R, G and
B into luma per block as the core does.

Code size on the e200: core loader +351 bytes, plugin decoder +603
bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ib24a0b7690ca4c00b3ac01b2f511309efeac5975
2026-10-02 08:23:46 -04:00
Solomon Peachy
5dc5c4a06c checkwps: Fix red on monchrome devices
Change-Id: Ib9064fea51eaf9ce4a2095c1f1d599fc193d2a6d
2026-10-02 08:04:24 -04:00
Solomon Peachy
152c348537 FS#14025 Checkwps now validates theme cfg files
This allows fonts, backdrops, and wps/fms/sbs to be checked.

Note that while settings _names_ are validated, the _values_
can not always be checked.  Detectable settings errors
are flagged, but are not considered fatal.

Change-Id: I2a2b7ad94462e983345a1e692eccd6bd57e90eb9
2026-10-02 07:50:19 -04:00
Marcin Bukat
6350d3f7a0 YP-CP3: build plugins
The YP-CP3 shares the YP-R0's keypad and so its plugin keymaps; with
the 400x240 screen handled every plugin builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I516ab057c081e710cbc1cf6eca2b24ee87231244
2026-10-02 10:30:58 +02:00
Marcin Bukat
02f54fc3a8 plugins: 400x240 landscape screens
Seven plugins have no layout or bitmaps for a 400x240 landscape
screen - the Samsung YP-CP3's, which no target building plugins has had
in that orientation. Give each the 320x240 one: the same height, and
centred in the 80 pixels more width wherever a 320x240 background has
to line up with it.

- bubbles, invadrox, rockblox: the 320x240 layout and background,
  centred; the margins are cleared
- sudoku, jewels: the 320x240 bitmaps; their layouts already centre
  themselves or use the width
- superdom: the 320x240 box size and board items - boxes as wide as
  the screen allows make the board taller than it
- wormlet: the sizes of 320x240

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I223154da5da2250ba0f6068106ba9bec242a29ee
2026-10-02 10:30:58 +02:00
Marcin Bukat
d94e41bade invadrox: bracket SCORENUM_Y
Five layouts define SCORENUM_Y as SCORE_Y + (...) unbracketed, so the
playfield update after each frame, PLAYFIELD_Y + 1 - SCORENUM_Y -
FONT_HEIGHT high, added that part rather than subtracting it: on a
240-line screen it ran 25 lines past the bottom. Most LCD drivers clip
it; the rk27xx one did not, and nothing in the playfield moved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icecf7780d81595db9d6e42125fc421cc1cb40339
2026-10-02 10:30:58 +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
Michael Giacomelli
1488b30c62 imageviewer/jpeg: reject damaged files safely
Damaged and truncated JPEGs could make the image viewer read or write
outside its buffers:

- process_markers() trusted segment lengths, and Huffman table symbol
  counts, so a segment running past the end of the file was parsed
  from whatever memory followed it. Check that each marker segment,
  and each Huffman table in it, lies within the file.
- A file without a complete SOS header was decoded from a NULL entropy
  data pointer, as load_image() checked only for DQT and SOF. Require
  SOS as well.
- img_mem() computed the image size in an int, which overflows for a
  large image (a 65535x65535 file came out as 0), so the decode wrote
  far past the buffer. Compute it in 64 bits and saturate.

Found with the jpeg-conformance files of the imazen codec-corpus,
which include truncated files and files from fuzzing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I1e2a9a4346fd1c0e34b0813267c6317dbf704bc7
2026-10-01 19:32:57 -04: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
Vencislav Atanasov
23f1f294b5 Fix case of "iPod" in docs and CLI tools help
Change-Id: I50a6a8f9cac361eee1c36f66570b20b5521b3283
2026-10-01 17:32:24 -04:00
Vencislav Atanasov
41be2016b8 docs: Fix case of "iPod" in area name and history
Change-Id: Ie0f54f98f95a868642e95d957b25aaa651ae9545
2026-10-01 17:32:24 -04:00
Vencislav Atanasov
626643b46d ipodpatcher: Fix case of "iPod" in logs
Functions, structs and variables not modified.

Change-Id: I4c0ea679e3e2225d3d4d0bcbe5db5071e5a18b47
2026-10-01 17:32:24 -04:00
Vencislav Atanasov
e28511a757 rbutilqt: Fix case of "iPod" in logs and translations
Functions, classes and variables not modified.

Change-Id: I252c32c5c22294d73896e6ad6368328f260bdcab
2026-10-01 17:32:24 -04: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
Solomon Peachy
bfab6a2276 Translation Updates
* German (Wilfried Winkler)
 * Italian (Alessio Lenzi)

Change-Id: I85b7936c85f188c09dc3621133ae83d1b70f6187
2026-10-01 15:52:43 -04: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
Michael Giacomelli
5c74d20bce jpeg_load: decode chroma sampled like 1x2 or 2x1 luma
When all three components share 1x2 or 2x1 sampling there is no chroma
subsampling, but each interleaved MCU holds two blocks per component.
The core loader assumed one chroma block per MCU, so these files (the
folder.jpg in the original report) decoded to garbage and have been
rejected since chroma sampling is validated.

Lay out the MCU generically in fix_headers(): each component's H x V
blocks in turn, with a per-block position that places chroma blocks
with the same offsets as luma. The MCU size and decode buffer now come
from the luma sampling in colour builds too, and the chroma IDCT scale
from the luma:chroma sampling ratio, which is unchanged for 1x1
chroma. The unused subsample_x/y fields are removed.

All other layouts decode byte-identically to before at every scale.
The new layouts decode byte-identically to the same image encoded as
4:4:4. Code size drops by 108 bytes on the e200 and struct jpeg by
20 bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I63a894d6609e56f3553a902afd6278ce70e04637
2026-10-01 13:47:38 -04:00
Michael Giacomelli
e32bfaadb1 jpeg: reject layouts the decoders cannot handle
Both JPEG decoders accepted several baseline layouts they cannot decode
and produced garbage without an error:

- chroma with sampling factors other than 1x1 (the MCU layout is chosen
  from luma alone, so any other chroma layout desynchronises)
- files written as more than one scan, where the first scan does not
  hold every component (it was decoded as if it were interleaved)
- scans whose components are not in frame order
- a height of 0 in SOF, to be defined later by a DNL marker

Reject these in process_markers(). The imageviewer then falls back to
the jpegp decoder on colour targets, which handles all of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ia3373f2b934eef6e3f354b4d064faf2d89868050
2026-10-01 13:22:53 -04:00
Michael Giacomelli
0e50ed3c43 jpeg: use the quantization table each component selects
Both JPEG decoders ignored the Tq selector in the frame header and
always dequantized luma with table 0 and chroma with table 1. Files
with a single shared table multiplied chroma by an empty table, and
files with separate Cb and Cr tables used the wrong one for Cr.

imageviewer/jpeg: build one dequantization table per component (3
instead of 2, +256 bytes) from the table it selects. tab_membership is
no longer used and is removed.

Core loader: the raw tables are pre-scaled in place for the IDCT, and
luma and chroma can use different IDCT scales. fix_quant_tables() now
maps each component to a table slot, copying a table that luma and
chroma share at different scales to a slot no component uses (there are
4 slots and at most 3 components, so one is always free), and rewrites
quanttable_select to that slot. No extra memory.

Selectors above 3 are rejected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: If04c61fb0fef95da11d98d9918ea7225d8a440b0
2026-10-01 13:13:40 -04:00
Michael Giacomelli
b897766a7a jpeg: use the Huffman tables the scan header selects
Both JPEG decoders ignored the DC/AC table selectors in the SOS header
and always decoded luma with tables 0 and chroma with tables 1, the
layout libjpeg writes by default. Files where all components share
table 0, or where the slots are assigned differently, decoded to noise.

Look up each component's tables from its selectors instead. Baseline
JPEG only allows tables 0 and 1, which both decoders already hold, so
this needs no extra memory; selectors above 1 are rejected. In the core
loader tab_membership is no longer used and is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ie1ec407ddecf1256fef410b1256b97af412fb194
2026-10-01 13:03:53 -04:00
Michael Giacomelli
c12f6ebacd imageviewer/jpeg: don't read past the end of entropy data
The bit reader refilled from the input buffer without checking its end.
The end-of-data check in the decode loops only runs once per MCU row, so
a stream that desynchronises (or is truncated) read past the end of the
file buffer for the rest of the row. Return zero bytes past the end
instead; the pointer still advances so the per-row check stops the
decode.

Found with AddressSanitizer on a JPEG whose chroma is sampled more
densely than its luma.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iaada1bc3cbf62b18f10fb2377c12d4d5cb9524de
2026-10-01 11:57:40 -04: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
01d18e42a9 rk27xx: fix -Wundef warnings in the FTL scheme finder
The debug menu tests CONFIG_NAND == NAND_RK27XX to include the FTL
scheme finder. Most targets do not define CONFIG_NAND at all, so every
native build but rk27xx's warned twice:

  "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: I82a1e8fa2f09aac1c5f0d746fb6d475e389d12d6
2026-10-01 13:00:41 +02:00