olragon

Hộp TV 2016, Linux mainline, và năm ngày gom bằng chứng

🌿 buddingupdated 2026-10-14#linux#phan-cung#tv-box#mainline#dich-nguoc#ghi-chep

Ghi chép thực địa, viết khi hộp vẫn còn nằm trên bàn thợ. Mỗi kết luận dưới đây đều có số đo, dòng log hoặc ảnh chụp màn hình đi kèm. Chỗ nào chỉ là suy luận, mình nói rõ.

English: A 2016 TV box, mainline Linux, and five days of evidence.

Bo mạch chính Q10 Pro: mạch tím, Hi3798CV200 dưới tản nhiệt đen, module WiFi AP6255 bên dưới
Bệnh nhân: bo v2.2, Hi3798CV200 (4 nhân Cortex-A53) dưới tản nhiệt, RAM 2 GB, WiFi/BT AP6255. Tem số serial đã làm mờ.

Cái hộp, và câu hỏi

HiMedia Q10 Pro là hộp Android TV "audiophile" đời 2016: HiSilicon Hi3798CV200 bốn nhân, RAM 2 GB, khay ổ SATA 3.5", đồng hồ VFD 16 đoạn ở mặt trước, ra âm thanh RCA + quang + HDMI. Firmware cuối cùng là Android 7 trên kernel 4.4 của hãng.

Câu hỏi đầu tiên: cố lên Android mới hơn, hay chuyển hẳn sang Linux? Hoá ra câu trả lời nằm ở cấu trúc, chẳng phải chuyện chịu khó:

Vậy là chọn Linux mainline, và chấp nhận mọi driver media phải port lại hoặc tự viết.

Khởi động mù: dùng watchdog làm kênh báo tin

Hộp này chẳng có cổng serial, và hai ngày đầu cũng chẳng có hình nào lên TV. Kernel treo và kernel chạy ngon trông y hệt nhau: TV đen, ping không trả lời.

Cái cứu mình là watchdog SP805 có sẵn trong chip. Mỗi mốc trong giai đoạn khởi động sớm lại hẹn giờ watchdog với một thời hạn khác nhau, nên đo thời gian tới lúc hộp tự reset là biết nó đã chạy tới mốc nào. Qua bảy lần nạp và 18 mốc:

chu kỳ resetmốc cuốichứng minh điều gì
~19 sstextđã vào được kernel chính
134 s__create_page_tablesxong toàn bộ đoạn trước MMU
194 s__mmap_switchedMMU bật, chạy bằng địa chỉ ảo
216 skernel_init_freeablemọi initcall đã chạy; sắp exec /init

Những cú vấp, cú nào cũng để lại bài học dùng được chỗ khác:

Mạng lên sau khi sửa hai chi tiết trong device tree. Bộ reset của PHY Ethernet bị build thành module (=m), trong khi initramfs chẳng nạp module nào, nên PHY chưa từng được reset. Còn bus MDIO chỉ quét con trực tiếp, nên node PHY phải nằm ngay dưới MAC.

Giờ chạy được gì

Tất cả đo trên hộp, kernel 6.18.55 với chuỗi patch của mình (các bản build k14 tới k25). Ảnh màn hình đọc thẳng từ bộ đệm quét của bộ điều khiển hiển thị trong RAM, tức đúng thứ cổng HDMI đang xuất ra.

Menu khởi động trên TV: Q10 Pro, chọn phiên — Desktop, Kodi, Android 13, Android 7 gốc
Menu khởi động trên TV, bấm bằng remote hoặc phím cảm ứng mặt trước.
bộ phậntrạng tháibằng chứng
4 nhân CPUchạynhân phụ được thả qua TEE (SC_GEN1); đường SRAM của hãng không ghi được từ vùng non-secure
Khởi động lạichạyreset bằng SP805 với LOAD 0x100: bản sao của HiSilicon không bao giờ kích từ 0. Lên lại sau 65 s
DVFS CPU + devfreq GPUchạyđiện áp CPU 840–1150 mV, GPU 200–400 MHz
Ethernet, WiFi (AP6255)chạyWiFi cần GPIO4_6 (WL_REG_ON) và chân SDIO 1.8 V
USB 3 bên hôngchạyđo được 91.7 MB/s SuperSpeed
Remote IR + phím cảm ứngchạybo cảm ứng gửi mã NEC vào chính đầu thu IR; kernel gốc chưa từng quét phím
Đồng hồ VFD (TM1629A)chạyfont 6 ô 16 đoạn canh theo ảnh chụp; đọc đúng chữ "HIMEDI"
Hiển thị (driver DRM cho VDP)chạynhận lại chế độ 1080p60 của bootloader; plane đồ hoạ G0 + plane video NV12 V0
GPU (Mali-T720, Panfrost)chạykmscube 59.9 fps, glmark2 90 điểm trên KMS, 0 lỗi GPU
Giải mã video phần cứngchạyH.264, HEVC (8/10-bit, 4K), VP9 khớp từng bit; MPEG-2 trong khoảng 66–68 dB
Âm thanh (tự viết từ đầu)chạyRCA, quang, HDMI và DAC USB; nghe tận tai cả
HDMI-CECphát chạychiều nhận chưa thử: ở đây chẳng có TV hỗ trợ CEC
SATAchập chờnxem phần "Còn dở" bên dưới

Giải mã video phần cứng: từ một màu xanh lè tới khớp từng bit

Bộ giải mã (VDH) và bộ hậu xử lý (VPSS) lấy từ bản port 6.12 của cộng đồng, và phải sửa thật sự mới chạy được trên 6.18 bản 32-bit. Lần giải mã đầu "thành công": 300 khung, không lỗi, khung nào cũng xanh lè một màu.

Một khung hình toàn màu xanh lá
Trước: VDH có chạy, ngắt có kích, nhưng bộ đệm vẫn toàn số 0 NV12. Xanh lè.

Hai lỗi chồng lên nhau. VDH báo lỗi đúng ở 40 khung có byte chống giả mã khởi đầu (emulation-prevention), sửa bằng cách cho driver tự gỡ chúng. Sau đó khung nào cũng hỏng ở VPSS với post-processing failed, state=0x8d, sửa bằng ghi non-secure và một bộ đệm write-back thật thay cho địa chỉ vật lý 0.

Khung 150 của testsrc2 với mã thời gian 00:00:05.000
Sau: khung 150, đúng y mẫu thử với dòng "00:00:05.000 / 150" in sẵn.

Rồi mình kiểm cho kỹ. md5 của mọi byte giải mã ra trùng khít giữa FFmpeg-phần cứng, GStreamer-phần cứng và FFmpeg-phần mềm (720p 461d0f24e55e…, 1080p 69914be4d1c3…). Phần cứng chẳng phải "gần đúng"; nó chính là đáp án chuẩn.

clip (FFmpeg v4l2request)khungso với phần mềmfps
H.264 1080p602400/2400md5 trùng75
HEVC 1080p 8-bit300/300md5 trùng51
HEVC Main10 1080p300/300md5 trùng (P010)31
HEVC 2160p (4K)150/150md5 trùng15
VP9 1080p300/300md5 trùng52
MPEG-2 1080p / 576i300 / 250PSNR khung thấp nhất 68.4 / 66.1 dB41 / 68

Hai phát hiện về hiệu năng, đáng biết nếu bạn mày mò chip tương tự:

Khối lập phương nhiều màu của kmscube do Mali-T720 vẽ
Panfrost trên Mali-T720, hiển thị qua VDP: kmscube 59.9 fps có vsync.

Còn một chuyện lạ chưa giải: khung giải mã bằng phần mềm đi qua bộ vẽ GL của mpv làm Panfrost báo lỗi job INSTR_INVALID_PC (356 lỗi mỗi clip 720p, lặp lại được). Một lần chạy như vậy đi trước một cú reset không rõ nguyên do. Khung giải mã phần cứng đi qua cùng bộ vẽ thì 0 lỗi. Phía Mesa mình chưa đào.

Âm thanh: driver viết từ công thức thanh ghi

Mainline chẳng có driver cho khối âm thanh (AIAO). Mình rút công thức cấp thanh ghi từ SDK của hãng rồi viết một driver ALSA nhỏ: một vòng đệm cho cả ba cổng cùng đọc, con trỏ đọc được hrtimer hỏi định kỳ, nên chẳng cần ngắt, cũng chẳng cần node device tree. Bài thử âm đo được 48.592 và 48.593 khung/giây trên hai cổng (mục tiêu 48.000, cửa sổ đo 1 s cộng phụ phí tiến trình). Rồi tới phần quan trọng: tai nghe thấy tiếng ở RCA, rồi ở HDMI (N=6144, CTS đo được 148.351), và cổng quang sáng đỏ.

Bên trên là một "chế độ nhạc" biến hộp thành máy phát nhạc qua mạng: MPD, AirPlay 2, Spotify Connect, UPnP/OpenHome, xuất bit-perfect ra DAC USB (đo passthrough 16/48, 0 lần hụt đệm). Kèm thêm chọn ngõ ra, bật tắt từng cổng và chỉnh lệch trái/phải, từ remote, dòng lệnh hay trình duyệt trên điện thoại.

Màn hình chờ của chế độ nhạc: Ready, phát từ AirPlay, Spotify Connect hay app UPnP, hoặc mở thư viện trên trình duyệt; ngõ ra Onboard (HDMI / RCA / quang); âm lượng 8
Chế độ nhạc chờ sẵn trên TV: AirPlay, Spotify Connect, UPnP hoặc thư viện trên web. Địa chỉ mạng LAN đã được làm mờ.
Màn hình đang phát: 01 Tone 96k, FLAC 24/96 ra onboard, chuyển về 48 kHz
Dòng chất lượng nói thật: đường onboard chỉ chạy 48 kHz, nên 24/96 hiện rõ là đã chuyển mẫu.

Một hộp, mấy cuộc đời

Kernel trên USB khởi động giữ nguyên làm đường lui. Nó kexec sang kernel mới nhất trong lúc watchdog đã hẹn giờ, kèm bộ đếm lỗi, nên một kernel hỏng chỉ tốn một lần reset, chẳng bao giờ làm chết hộp. Rồi menu trên TV cho chọn phiên:

Desktop LXQt với menu Start kiểu Windows, có Chromium, Kodi, Android 13
Desktop: LXQt với menu Start quen tay, tăng tốc GPU qua glamor (293 MB, CPU gần như rảnh).
Màn hình chính Android 13, 1:04, thứ Hai 12 tháng 10
Android 13 (LineageOS 20 trong Waydroid), toàn màn hình trên GPU Mali: lên launcher sau 100 s khi chạy từ ổ SATA, dùng 724 MB, tạm dừng/tiếp tục trong ~1 s.
Màn hình chào Kodi 21.2 Omega
Kodi 21.2 trên GBM + GLES, giải mã bằng V4L2-request.

Để Android 13 lên được là cả một chuỗi lỗi câm, sửa lần lượt từng cái:

Còn dở

Nói thật cả những chỗ hổng:

Những lần mình sai, giữ lại có chủ ý

Mấy cái này tốn thời gian thật, nên mình giữ trong ghi chép:

Giờ thì sao

Năm ngày, giờ nó là một hộp Linux hiền lành. Lên menu TV trong ~90 s, giải mã video phần cứng khớp từng bit, phát nhạc ra DAC, chạy desktop, Kodi, Chromium và Android 13, và vẫn hiện giờ trên chiếc VFD cũ. Lỗi vật lý cuối cùng đang chờ một sợi dây mới.

Chăm trồng bởi Ba rồng & AI nữa: hạt giống (cái hộp, những câu hỏi, những lần "chắc chưa?") là của Ba rồng; những đêm chăm dài thì chia nhau.