bo_free() (munmap+close) fixes the per-tile mmap leak in rkt_npu_matmul; add
rocket_munmap_bo to librocket. rkt_matmul rejects K>8192 (int8 K-limit). Keeps
the canonical primitive in sync with the ggml-rocket backend copy in rk-llama.cpp.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
Adds optional out_scales[N] (per-output-channel requant scale) to
rkt_npu_matmul; per N-tile it uses the tile's channel scale (exact per-channel
with tile_n=1). This is what gemma4's per-channel weight quantization needs.
Verified on NPU: per-channel test (out_scales[oc]=1/(oc+1)) -> row 144 160 176
192 208 224 240 255 matches CPU exactly. All 5 gemmtest cases pass.
Next U6: ggml glue (Q8_0 weights -> per-oc int8 + per-oc out_scale array;
F32 activations -> int8 + activation scale; call this primitive).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extracts the verified 2D-tiling matmul into a clean library function:
rkt_npu_matmul(fd, X, Wc, bias, M,N,K, zero-points, scales, tile_m,tile_n, Y)
-> tiles via rkt_gemm_plan, runs each single-op tile (scaled builder + bias
BO + INT64_MAX prep_bo), assembles Y. This is the function a ggml MUL_MAT
backend calls. gemmtest.c is now a test driver: small / large-M(4096) /
2D-tiled(2048x256) / gemma-scale(K=2048) all PASS on the NPU.
Next (U6): fork rk-llama.cpp ggml-rknpu2.cpp, replace rknn_matmul_run
(line 777) with rkt_npu_matmul, map ggml Q8_0 tensors -> our int8 X/Wc +
per-tensor scales, wire buffer-type + supports_op(MUL_MAT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>