beeugene
16 просмотров0 комментариев0

IMX519 на Repka Pi 5: камера не поддерживается. Пересобираем ядро «по-жёсткому»

TL;DR. Подключил к Repka Pi 5 (RK3588) Arducam IMX519 16MP через CSI — а её ядро не поддерживает: нет ни драйвера, ни device-tree оверлея под этот сенсор. Эпопея растянулась на три акта и шестнадцать пересборок ядра. Первый акт — портирование драйвера и overlay: сенсор появился в медиа-графе, но кадра не давал. Второй — интеграция с rkaiq и ложные следы (split-pipeline, rkisp-unite). Третий — настоящая kernel-отладка с pr_info в драйвере: нашёл и починил шесть багов в RKCIF и imx519, каждый блокировавший свой этап (error 515 → kernel oops → EINVAL → ENOMEM → EPIPE → stream ON). Pipeline полностью активируется, DPHY включается на 987 Мбит/с — но последний слой (физика MIPI) пока открыт. Под катом — весь путь с реальными командами, логами и откатом.

Задача и зачем #

Была цель — получить поток с конкретной камеры Arducam 16MP IMX519 (без автофокуса) именно на Repka Pi 5 через штатный CSI-разъём. Это не самый ходовой выбор, поэтому я сразу готовился к тому, что «из коробки» не заведётся. Но «не из коробки» — это одно, а «пересборка ядра» — совсем другое. До последнего надеялся обойтись малой кровью.

Компонент Модель / значение
Платформа Repka Pi 5 (Rockchip RK3588)
ОС Repka OS (Ubuntu 25.10), ядро 6.1.115.1-repka-pi5
Камера Arducam IMX519 16MP (без автофокуса, fixed-focus)
Интерфейс MIPI-CSI (штатный 22-pin разъём Repka Pi 5)
Шлейф AWM 20624, 22-pin, 0.5 мм pitch (под RP5/Repka Pi 5)
Цель видеопоток с сенсора через V4L2

Шлейфы и разъёмы: куда и как вставлять #

Прежде чем лезть в драйверы и логи, разберёмся с физикой — тут легко наступить на грабли ещё до включения. В комплекте с Arducam IMX519 идёт два шлейфа, и они для разных плат. Перепутать — и камера вообще не определится.

💡 Что такое FPC-шлейф? Это гибкий плоский кабель (Flexible Printed Circuit). На одной его стороне — медные «пятаки» (контакты), вторая сторона — гладкая изоляция. Разъём на плате прижимает шлейф контактами к своим «усикам», и от ориентации шлейфа зависит, попадёт ли сигнал на нужные пины. Вставить шлейф «не той стороной» — частая причина «камера не работает».

Два разъёма камер на Raspberry/Repka: 15-pin и 22-pin #

Стандарт MIPI-CSI на платах этого семейства существует в двух физических вариантах:

Параметр 15-pin (старый) 22-pin (новый)
Шаг контактов (pitch) 1.0 мм 0.5 мм (плотнее)
Линии данных MIPI 2 (Lane 0, Lane 1) 4 (Lane 0–3) — выше пропускная способность
Питание камере 3.3 В 3.3 В
Где применяется Raspberry Pi 3 / 4 / B+ Raspberry Pi 5, Pi Zero, Compute Module IO, Repka Pi 5

Repka Pi 5 конструктивно повторяет разъём Raspberry Pi 5 — это 22-pin, 0.5 мм pitch. Поэтому для неё подходит только «новый» шлейф. Старый 15-pin сюда физически не воткнёшь без адаптера.

💡 Что такое pitch? Это расстояние между центрами соседних контактов. 0.5 мм у 22-pin против 1.0 мм у 15-pin — значит, шлейф с 22 контактами по ширине примерно такой же, как 15-пиновый, но контакты вдвое плотнее. Отсюда и хрупкость: 0.5-мм шлейф легче повредить и сложнее вставить ровно.

Какой шлейф для какой платы #

В комплекте с Arducam обычно два шлейфа, и их легко перепутать:

Шлейф Маркировка Для какой платы
UC-236 (старый, 15-pin, 1.0 мм) Raspberry Pi 4 и раньше
AWM 20624 (новый, 22-pin, 0.5 мм) Raspberry Pi 5 / Repka Pi 5 ← наш

⚠️ На Repka Pi 4 разъёма CSI под этот стандарт НЕТ. Шлейф UC-236 предназначен для Raspberry Pi 4 (и ранних моделей). Если у вас именно Repka Pi 5 — берите шлейф с маркировкой AWM 20624 (или любой 22-pin / 0.5 мм pitch). UC-236 туда не подойдёт ни физически, ни по назначению.

Как вставлять шлейф в разъём CSI Repka Pi 5 #

Это самый капризный момент. У шлейфа AWM 20624 контакты («оголённая часть с пятаками») только с одной стороны. Правильная ориентация для разъёма CSI на Repka Pi 5:

                ┌─────────────────────────────┐
   процессор ←  │ контакты (пятаки) ВВЕРХ/к CPU │
                │ изоляция (гладкая) ВНИЗ       │
                └─────────────────────────────┘
                              ▲
                   сторона БЕЗ контактов направлена к замку разъёма

Простыми словами:

  • Оголённая часть с контактами (пятаками) смотрит в сторону процессора (внутрь платы).
  • Сторона, где контактов нет (гладкая изоляция), направлена в сторону замка разъёма (наружу).

⚠️ Перевёрнутый шлейф = «камеры нет». Это самая частая аппаратная ошибка. На Raspberry Pi 5 (и на Repka Pi 5, повторяющей её разводку) «контакты вверх» — это неправильно. При перевёрнутом шлейфе сенсор не получит нужные сигналы (CLK/DATA повиснут в воздухе или уйдут не на те пины), и I²C-сканер вообще никого не найдёт. Так что если i2cdetect показывает пустую шину — прежде чем винить драйвер, проверьте ориентацию шлейфа.

💡 Как вставлять. Сначала аккуратно потяните защёлку разъёма вверх (она откидывается на пару миллиметров, не дёргайте силой — 0.5-мм разъём хрупкий). Вставьте шлейф до упора, ровно, без перекоса. Затем опустите защёлку, чтобы она прижала шлейф. Если шлейф вставлен криво — часть контактов не дотянется, и поведение будет непредсказуемым.

В моём случае именно шлейф AWM 20624 оказался нужным — его и вставил в разъём CSI по ориентации «контактами к процессору». После этого I²C-скан (о нём ниже) сразу увидел сенсор, и стало ясно, что физика подключена правильно, а проблема — в софте.

Симптом: камеры нет нигде #

Подключил шлейф, загрузился. Первое, что проверяю всегда, — появились ли устройства:

ls /dev/video* /dev/media*
/dev/video-dec0
/dev/video-enc0

Только два узла, и оба — это аппаратные видео-кодировщик/декодировщик RK3588 (video-enc0, video-dec0). К камере они не имеют отношения. Ни /dev/video0, ни /dev/media0. Камера вообще не видна.

💡 Что такое /dev/video0 и /dev/media0?
В Linux оборудование камеры выставляется через два слоя. /dev/video0 — это «видеоузел», из которого можно читать кадры (через V4L2). /dev/media0 — это узел «медиа-контроллера»: он описывает весь конвейер (сенсор → PHY → CIF → ISP) как граф связей. Нет ни того, ни другого — ядро даже не знает про камеру.

Прежде чем паниковать, полез проверить физику — отвечает ли сенсор на шине I²C. Это подскажет, вставлен ли шлейф правильно:

# Сканируем шины I²C в поисках сенсора (по адресам)
sudo i2cdetect -y 6
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- 0c -- -- --
10: -- -- -- -- -- -- -- -- -- -- 1a -- -- -- -- --
...
50: -- UU -- -- -- -- -- -- 58 -- -- -- -- -- -- --

Отличные новости: на шине I²C-6 кто-то отвечает0x1a (это и есть сам IMX519, его стандартный адрес), 0x50 (EEPROM модуля Arducam). Значит, шлейф сидит правильно, питание на модуль подаётся, физика ок. Проблема — софтверная.

Диагностика: почему ядро не видит камеру #

Шаг 1. Есть ли оверлей под мою камеру? #

Проверяю, какие device-tree overlay'ы (о них ниже) идут в комплекте с Repka OS:

ls /boot/dtb/rockchip/overlay/ | grep -iE "csi|cam|imx|ov"
rk3588-csi-imx219.dtbo   ← есть только под IMX219 (RPi Camera V2)

Под IMX519 оверлея нет. Только под IMX219 — это другая камера (RPi Camera V2), родственная, но не моя.

Лирическое отступление: device tree и overlay простыми словами #

Чтобы ядро узнало про оборудование на ARM-плате, оно читает device tree (DT) — скомпилированное описание всего железа: какие контроллеры есть, по каким адресам, какие пины за что отвечают, какие регуляторы питания подключены. На Repka он лежит в /boot/dtb/rockchip/rk3588-repka-pi5.dtb.

Overlay — это «патч» к device tree. Базовый DT описывает плату, а overlay добавляет к нему, например, описание камеры: «на шине I²C-6 по адресу 0x1a висит сенсор Sony IMX519, подключён к PHY, требует 24 МГц тактовый сигнал». Overlay хранится в отдельном файле .dtbo и применяется загрузчиком U-Boot при старте.

💡 MIPI-CSI — это интерфейс камер. Две линии данных (data lanes) + одна тактовая линия, по которым сенсор вываливает пиксели. На RK3588 сигнал с камеры проходит длинный путь: сенсор → DPHY (приёмник физического уровня) → CIF (захват) → ISP (обработка). Каждый узел в этом пути — отдельный драйвер в ядре, и все они должны быть описаны в DT и связаны между собой «графом связей» (endpoints).

Шаг 2. Драйвер IMX519 есть в ядре? #

# Что есть в конфиге ядра из сенсоров Sony IMX
zcat /proc/config.gz | grep -E "VIDEO_IMX"
CONFIG_VIDEO_IMX219=y   ← IMX219 встроен в ядро
CONFIG_VIDEO_IMX415=y
# CONFIG_VIDEO_IMX519 — такой строки НЕТ

Драйвера IMX519 в ядре нет. Это и понятно: Arducam делает драйвер под Raspberry Pi, в основной поток ядра Linux (mainline) он не влит.

Шаг 3. Конвейер RK3588 включён в ядро? #

А вот это — хорошая новость. Проверяю, что весь путь сенсор→ISP встроен в ядро:

zcat /proc/config.gz | grep -E "ROCKCHIP_ISP|ROCKCHIP_CIF|V4L2_FWNODE|V4L2_ASYNC"
CONFIG_VIDEO_ROCKCHIP_ISP=y    ← ISP встроен
CONFIG_VIDEO_ROCKCHIP_CIF=y    ← CIF встроен
CONFIG_V4L2_FWNODE=y
CONFIG_V4L2_ASYNC=y            ← async-привязка (важно ниже)

Вся инфраструктура MIPI-конвейера RK3588 встроена в ядро. Не хватает только драйвера самого сенсора. То есть задача сводится к: добавить драйвер IMX519 + описать его в device tree.

Сводка после диагностики #

Проверка Результат
/dev/video0, /dev/media0 отсутствуют
Шина I²C (физика) ✅ сенсор отвечает на 0x1a, шлейф ок
Overlay под IMX519 ❌ отсутствует (есть только IMX219)
Драйвер IMX519 в ядре ❌ отсутствует
Конвейер RK3588 (ISP/CIF/DPHY) ✅ встроен в ядро

Вывод: надо портировать драйвер и писать свой overlay. План — в три этапа.

Этап 1. Порт драйвера Arducam под ядро 6.1 #

Arducam держит исходники драйвера в открытом репозитории ArduCAM/IMX519_AK7375. Клонирую:

git clone https://github.com/ArduCAM/IMX519_AK7375.git

Внутри — IMX519/imx519.c (около 2000 строк) и Makefile для out-of-tree сборки. Первая проверка — собирается ли он вообще против ядра Repka:

cd IMX519_AK7375/IMX519
make KDIR=/lib/modules/6.1.115.1-repka-pi5/build

Не собирается. Но — и это главное — все ошибки одного класса: API-drift между ядрами 5.15 и 6.1. Драйвер писался под RPi-ядро ~5.15, а в 6.1 часть типов и функций переименовали. Конкретно:

Что в драйвере (5.15) Что стало в 6.1
v4l2_subdev_pad_config v4l2_subdev_state
v4l2_async_register_subdev_sensor_common v4l2_async_register_subdev_sensor
imx519_remove() возвращает int должен быть void
MEDIA_BUS_FMT_SENSOR_DATA MEDIA_BUS_FMT_METADATA_FIXED
fh->pad fh->state

Всё это механические правки — переименования, без изменения логики. Аккуратно правлю сигнатуры функций (их штук пять) и пересобираю:

make KDIR=/lib/modules/6.1.115.1-repka-pi5/build
# ...
LD [M]  imx519.ko

imx519.ko собрался. Самый рискованный момент — совместимость драйвера с чужим ядром — снят. Это чистый V4L2-драйвер, без завязок на Raspberry Pi.

💡 Out-of-tree vs built-in. Можно грузить драйвер как внешний модуль .ko (out-of-tree) — он подгружается во время работы системы. А можно «встроить» в ядро ещё на этапе компиляции (CONFIG_VIDEO_IMX519=y), и тогда он едет внутри самого Image. Как выяснится дальше, для камер на RK3588 эта разница критична.

Этап 2. Device-tree overlay под конвейер RK3588 #

Драйвер есть, но ядро о нём не знает, пока нет описания в DT. Нужно написать свой overlay. Удобно, что в комплекте Repka есть рабочий rk3588-csi-imx219.dtbo под родственную камеру — его декомпилирую и беру за образец:

dtc -I dtb -O dts /boot/dtb/rockchip/overlay/rk3588-csi-imx219.dtbo

Этот overlay описывает всю цепочку CSI→DPHY→CIF→ISP и активирует каждый узел. Топология для IMX519 идентична (тот же разъём, та же разводка Repka), поэтому копирую её 1-в-1. Меняю только фрагмент сенсора под требования драйвера IMX519 (выясненные чтением imx519.c):

  • compatible = "sony,imx519", reg = <0x1a> (I²C-адрес);
  • тактовый сигнал 24 МГц (без него сенсор молчит);
  • три обязательных питания: VANA (2.8 В), VDIG (1.05 В), VDDL (1.8 В);
  • на endpoint — строго data-lanes = <1 2> и link-frequencies = /bits/ 64 <493500000> (драйвер это валидирует).

Собираю overlay и кладу рядом с другими:

dtc -@ -I dts -O dtb -o rk3588-csi-imx519.dtbo rk3588-csi-imx519.dts
sudo cp rk3588-csi-imx519.dtbo /boot/dtb/rockchip/overlay/

Включаю его в /boot/repkaEnv.txt (бэкап оригинала — обязательно):

sudo cp /boot/repkaEnv.txt /boot/repkaEnv.txt.bak
# правим строку overlays:
#   overlays=panthor-gpu csi-imx519

Перезагрузка — и…

Этап 3. Сенсор опознался, но графа нет (главный сюрприз) #

После ребута проверяю логи:

dmesg | grep -i imx519
imx519 6-001a: Device found is imx519    ← СЕНСОР ОПОЗНАН!

Chip ID прочитан (регистр 0x0016 вернул 0x0519), питание, тактовый сигнал и link-frequency — корректны. Но…

media-ctl -d /dev/media0 -p | grep imx519
# (пусто — imx519 в медиа-графе отсутствует)

Сенсор опознан, а в медиа-графе его нет. RKCIF (захват) при этом непрерывно ругается:

rkcif-mipi-lvds2: rkcif_update_sensor_info: stream[0] get remote terminal sensor failed!
get_remote_sensor: video pad[0] is null

Копаю глубже через debugfs:

cat /sys/kernel/debug/v4l2-async/pending_async_subdevices
imx519 6-001a:
rockchip-csi2-dphy0:
rkisp0-vir0:
rockchip-mipi-csi2:
rkcif-mipi-lvds2:

Все пять узлов конвейера висят в pending — каждый зарегистрировался как async-subdev, но ни один notifier их не «связал». Это ключевое открытие.

Лирическое отступление: v4l2-async и race condition #

Камерный конвейер состоит из независимых драйверов (сенсор, PHY, CIF, ISP), которые probe'ятся в непредсказуемом порядке. Чтобы они «нашли» друг друга, в V4L2 есть механизм async-binding: каждый узел регистрируется как «кандидат» (subdev), и хотя бы один из них создаёт «слушателя» (notifier), который связывает кандидатов по графу связей в device tree.

Проблема в том, что notifier не ждёт вечно. Если на момент завершения notifier'а нужный сенсор ещё не зарегистрировался — связывание не происходит, и граф остаётся разорванным. Сенсор потом подгрузится, но его уже некому «подобрать».

Так и случилось. Таймлайн из dmesg:

12.63s — rkcif notifier COMPLETED (решил, что сенсоров больше не будет)
13.17s — imx519 наконец зарегистрировался (опоздал на полсекунды)

Ложные следы, на которые я чуть не повёлся #

След 1: «Догрузить модуль раньше через initramfs». Добавил imx519 в /etc/initramfs-tools/modules, пересобрал initramfs. Модуль действительно стал грузиться раньше (с 17.9 с до 13.1 с) — но gap всё равно остался, notifier закрывался чуть раньше. Помогло, но не до конца.

След 2: «Перепривязать RKCIF через sysfs». В /sys/bus/platform/drivers/rkcif/ есть bind/unbind. Попробовал отбиндить и забиндить RKCIF заново, чтобы он переслушал notifier, когда сенсор уже на месте. Результат — kernel oops (крах RKCIF). Медиа-граф не рассчитан на повторный probe при существующих сущностях. Этот путь отпал.

После этих граблей стало ясно: лечить надо корень — порядок probe — а не симптомы.

Этап 4. Пересборка ядра с встроенным IMX519 #

Единственное надёжное решение: сделать драйвер built-in (CONFIG_VIDEO_IMX519=y), а не модулем. Тогда probe сенсора пойдёт синхронно с RKCIF в одном цикле, и async-binding сработает штатно. Но для этого нужно пересобрать ядро целиком.

Шаг 1. Исходники нужной версии #

Полные исходники лежат у производителя на GitFlic. Нужен именно репозиторий под RK3588 — npo_rbs/repka-os_linux-rockchip (не путать с repka-os_kernel, это ядро под Repka Pi 4 на Allwinner). Проверяю версию:

git clone --depth 1 --branch master \
  https://gitflic.ru/project/npo_rbs/repka-os_linux-rockchip.git
cd repka-os_linux-rockchip
grep -E "^(VERSION|PATCHLEVEL|SUBLEVEL|EXTRAVERSION)" Makefile
VERSION = 6
PATCHLEVEL = 1
SUBLEVEL = 115
EXTRAVERSION = .1

Точное совпадение с установленным ядром 6.1.115.1-repka-pi5.

Шаг 2. Встраиваю драйвер в дерево #

Три правки:

# 1. Исходник — в дерево
cp imx519.c drivers/media/i2c/imx519.c

# 2. Запись в Kconfig (рядом с imx219)
cat >> drivers/media/i2c/Kconfig <<'EOF'
config VIDEO_IMX519
	tristate "Sony IMX519 sensor support"
	depends on I2C && VIDEO_DEV
	select MEDIA_CONTROLLER
	select VIDEO_V4L2_SUBDEV_API
	select V4L2_FWNODE
	help
	  Sony IMX519 camera (Arducam 16MP).
EOF

# 3. Запись в Makefile
echo 'obj-$(CONFIG_VIDEO_IMX519) += imx519.o' >> drivers/media/i2c/Makefile

Шаг 3. Конфиг от текущей системы #

Беру готовый .config от установленного ядра — чтобы собрать с теми же опциями, что работают сейчас:

cp /boot/config-6.1.115.1-repka-pi5 .config
# Включаю IMX519 как built-in
sed -i '/^CONFIG_VIDEO_IMX219=y$/a CONFIG_VIDEO_IMX519=y' .config
make ARCH=arm64 oldconfig   # нормализовать конфиг

Шаг 4. Сборка #

make ARCH=arm64 -j8 Image dtbs modules

⚠️ Место и время. Полное дерево ядра — ~2 ГБ, сборка съедает ещё ~4 ГБ. На 8 ядрах RK3588 сборка идёт ~15–20 минут. На 16 ГБ microSD я не уместился — пришлось перенести систему на 64 ГБ eMMC. Учитывайте объём носителя заранее.

⚠️ Охлаждение обязательно. При сборке грузятся все 8 ядер на 100% подряд, минут 15. С активным охлаждением (вентилятор) температура процессора доходит до ~70 °C — терпимо. А вот без вентилятора RK3588 быстро уходит в троттлинг: сбрасывает частоты, сборка растягивается в разы, а при перегреве ядра плата может и зависнуть на ровном месте. Так что собирайте ядро только с обдувом — это не рекомендация, а необходимое условие. Если у платы только радиатор без вентилятора — снимите корпус, поставьте внешний кулер, или собирайте короткими заходами с паузами на остывание.

Сборка падала дважды, и оба раза по моей вине:

  • certs/extract-cert.c: openssl/bio.h: Нет такого файла — не хватало libssl-dev. Лечится apt install libssl-dev.
  • fixdep: error opening file ... No such fileгонка при параллельной сборке: я запустил две пересекающиеся фоновые сборки в одном дереве, и они повредили .o-файлы друг друга. Лечится make clean и одной чистой сборкой через setsid.

Проверка, что драйвер действительно встроен:

grep imx519 drivers/media/i2c/built-in.a   # imx519.o присутствует → built-in ✓
ls -lh arch/arm64/boot/Image               # ядро собрано

Этап 5. Установка ядра (с откатом) #

Это самая рискованная часть — есть шанс, что новое ядро не загрузится. Поэтому сначала бэкапы.

⚠️ Перед установкой ядра — обязательно снимите резервные копии. Если новое ядро не встанет, плата может не загрузиться, и откатить можно будет только с другой системы (сняв eMMC/SD и подмонтировав на ПК) или через serial-консоль. Ниже — что именно бэкапить и как возвращать.

# Бэкап ядра и DTB
sudo cp /boot/vmlinuz-6.1.115.1-repka-pi5 \
         /boot/vmlinuz-6.1.115.1-repka-pi5.bak-orig
sudo cp /boot/dtb/rockchip/rk3588-repka-pi5.dtb \
         /boot/dtb/rockchip/rk3588-repka-pi5.dtb.bak-orig
# Бэкап модулей
sudo mv /lib/modules/6.1.115.1-repka-pi5 \
         /lib/modules/6.1.115.1-repka-pi5.bak-orig

Установка. Замечу: текущее ядро у Repka — gzip-сжатый Image, поэтому наше (изначально несжатое) сжимаем так же:

gzip -9 -c arch/arm64/boot/Image > /boot/vmlinuz-6.1.115.1-repka-pi5
sudo cp arch/arm64/boot/dts/rockchip/rk3588-repka-pi5.dtb \
        /boot/dtb/rockchip/rk3588-repka-pi5.dtb
sudo make ARCH=arm64 modules_install
sudo depmod -a 6.1.115.1-repka-pi5

Финальная проверка, что imx519 теперь в modules.builtin (значит, встроен в ядро, а не отдельный файл):

grep imx519 /lib/modules/6.1.115.1-repka-pi5/modules.builtin
# kernel/drivers/media/i2c/imx519.ko  ← встроен ✓

После перезагрузки: кажется, заработало #

Самый волнительный момент всей истории — перезагрузка с пересобранным ядром. Запускаю проверку по списку:

uname -r
# 6.1.115.1-repka-pi5   ← наше ядро грузится ✓
dmesg | grep imx519
[ 12.255] platform csi2-dphy0: Fixed dependency cycle(s) with /i2c@fec80000/camera-imx519@1a
[ 12.267] imx519 6-001a: Looking up VANA-supply from device tree
[ 12.267] imx519 6-001a: Looking up VDIG-supply from device tree
[ 12.267] imx519 6-001a: Looking up VDDL-supply from device tree
[ 12.277] imx519 6-001a: Device found is imx519   ← сенсор опознан ✓

Главный признак успеха — теперь смотрю, перестал ли RKCIF ругаться на сенсор:

dmesg | grep -c "get remote terminal sensor failed"
# 0   ← НОЛЬ строк! Раньше их были десятки

Ноль. Async-binding прошёл — сенсор привязался к конвейеру. Это решило проблему порядка загрузки, над которой я бился. Но, как выяснится ниже, это была лишь первая из стен.

Камера в медиа-графе #

Смотрю топологию:

media-ctl -d /dev/media0 -p | grep -i imx519
- entity 63: imx519 6-001a (2 pads, 1 link, 0 routes)
	pad0: Source
		[stream:0 fmt:SRGGB10_1X10/1920x1080 field:none colorspace:srgb ...]
		-> "rockchip-mipi-csi2":0 [ENABLED]   ← линк активен

Сенсор появился как полноправная сущность графа, линк к CSI2 приёмнику ENABLED. Формат SRGGB10_1X10 (10-битный байеровский RAW) propagated через весь путь сенсор→DPHY→CSI2→CIF. А в crop-границах сенсора виден и полный 16-мегапиксельный режим:

crop.bounds:(8,48)/4656x3496   ← доступен полный кадр IMX519

Сенсор живой и управляемый #

Проверяю, что камера не просто «висит» в графе, а реально управляется:

v4l2-ctl -d /dev/v4l-subdev2 --list-ctrls-menus | head
User Controls
              exposure 0x00980911 (int)    : min=20 max=1147 step=1 default=1000 value=1000
       horizontal_flip (bool)   : default=0 value=0
         vertical_flip (bool)   : default=0 value=0
Image Source Controls
       analogue_gain (int)      : min=0 max=960 step=1 default=0 value=0
        red_pixel_value (int)   : min=0 max=4095 ...   ← BLC (коррекция чёрного)

Exposure, gain, повороты, BLC — все controls отвечают. Камера полностью интегрирована в V4L2.

💡 Почему я обрадовался. Из всей эпопеи самым сложным оказалось не «написать драйвер» и не «собрать ядро», а заставить сенсор привязаться к медиа-графу. На RK3588 это требует синхронного порядка probe, который достижим только при built-in драйвере. И вот — сенсор в графе, линки ENABLED, controls живые. Казалось бы, победа. Но, как выяснилось, я праздновал слишком рано.

Ложная победа: камера в графе, но кадра нет #

Обрадовавшись, что сенсор привязался к конвейеру, я пошёл за финальным призом — первым кадром. Ведь есть /dev/video0, /dev/video11 (ISP mainpath), камера в графе... Должно же заработать? Как бы не так.

v4l2-ctl -d /dev/video11 --set-fmt-video=width=1920,height=1080,pixelformat=NV12
v4l2-ctl -d /dev/video11 --stream-mmap --stream-count=1 --stream-to=test.nv12
VIDIOC_STREAMON returned -1 (Operation not permitted)

И пустой файл test.nv12 в 0 байт. Попытки через CIF-узлы (/dev/video0) — error 515. Камера «есть», а картинку не отдаёт. Тут я понял: «сенсор в медиа-графе» — это ещё не «камера работает». Между этими двумя состояниями — целая пропасть, которую я в азарте перепрыгнул мысленно, но не фактически.

💡 В чём была ошибка. Я принял промежуточную веху (async-binding прошёл, сенсор виден в графе) за финальную (поток идёт, кадр ловится). Это классическая ловушка отладки: «всё зеленое в чек-листе» создаёт ложное чувство завершённости. Реальный критерий успеха — не media-ctl показывает сенсор, а файл с пикселями ненулевого размера.

Почему кадра нет: новый слой проблем #

Стал разбираться, и вскрылся совершенно отдельный мир проблем — экосистема Rockchip ISP. Оказалось, что на RK3588 между RAW-сенсором и готовым RGB/YUV стоит ISP (Image Signal Processor — аппаратный процессор обработки изображений), и путь через него не «автоматический», а требует целой связки:

  • rkaiq_3A_server — сервер автоэкспозиции/автофокуса/баланса белого (3A). Без него ISP не знает, как обрабатывать кадр.
  • IQ-файл (/etc/iqfiles/*.json) — калибровка ISP под конкретный сенсор (BLC, AWB, vignetting, цветовая матрица). Без него rkaiq не запустится.
  • Тонкая настройка мульти-device медиа-графа — CIF живёт на /dev/media0, а ISP на /dev/media1, и их надо связать через rawrd-мост.

Пробую запустить rkaiq и сразу натыкаюсь на стену:

rkaiq_3A_server
CAMHW:E:248:parse sensor entity name imx519 6-001a error at 0, please check sensor driver!
CAMHW:E:@get_sensor_caps /dev/v4l-subdev2: Get sensor module info failed
CAMHW:E:get isp or ispp info fail, something gos wrong!

parse sensor entity name imx519 6-001a error — rkaiq не может разобрать имя сенсора! И это при том, что сам сенсор в графе прекрасно виден. Оказывается, rkaiq парсит имя сущности по строго определённому формату m<индекс>_<сторона>_<сенсор>, а наш драйвер оставил стандартное V4L2-имя imx519 6-001a. Плюс rkaiq дёргает специальный ioctl RKMODULE_GET_MODULE_INFO, чтобы получить имя модуля и подобрать IQ-файл — а наш Arducam-драйвер этот ioctl не реализует.

Что выяснилось о рабочем примере #

Чтобы понять, чего не хватает, разобрал рабочий imx219.c из дерева ядра Repka — для него есть и IQ-файл, и rkaiq его привязывает. И увидел три вещи, которых нет в Arducam-версии imx519:

  1. #include <linux/rk-camera-module.h> — заголовок с Rockchip-специфичными структурами.
  2. Чтение rockchip,camera-module-* из device tree (index, facing, name, lens) — это те самые свойства, что я прописал в overlay, но драйвер их не читал.
  3. Формирование имени в формате Rockchip: snprintf(sd->name, ..., "m%02d_%c_%s %s", ...) — даёт m00_b_imx219 6-0010, а не imx219 6-0010.
  4. Обработка ioctl RKMODULE_GET_MODULE_INFO + RKMODULE_GET_HDR_CFG + compat-обёртка для 32-бит.

То есть «работающий» imx219 от Rockchip — это не тот же imx219, что в mainline. Это модифицированная версия с интеграцией в проприетарный стек rkaiq. Наш Arducam imx519 этой интеграции лишён — он «чистый» V4L2-драйвер.

⚠️ Ловушка «у меня есть драйвер». Когда находишь драйвер под свой сенсор (особенно от производителя камеры), легко решить, что этого достаточно. Но на RK3588 драйвер сенсора — только первый слой. Над ним ещё Rockchip-специфичный «клей» (module info, rkaiq-ioctl, IQ-файл), и без него сенсор хоть и виден, но бесполезен для картинки.

Итог ложной победы #

Думал, что достиг На самом деле
«Камера работает» Сенсор виден в графе, но кадр не получается
Драйвер «готов» Драйвер V4L2-готов, но не интегрирован в rkaiq
Конвейер «настроен» Конвейер сенсор→CIF есть, но CIF↔ISP мост не активен
Осталось «только снять кадр» Нужен целый второй акт: доработка драйвера + IQ-файл + rkaiq

Так «финальная» глава превратилась в пролог ко второму акту — интеграции с Rockchip ISP. И вот этот второй акт разворачивается прямо здесь, потому что он оказался не короче первого.

Второй акт: интеграция с rkaiq и стена split-pipeline #

Раз поняв, чего не хватает, я засучил рукава и доработал драйвер по образцу imx219: добавил #include <linux/rk-camera-module.h>, чтение rockchip,camera-module-* из device tree, формирование имени в формате m00_b_imx519 и обработку ioctl RKMODULE_GET_MODULE_INFO. Заодно создал IQ-файл imx519_arducam-imx519_default.json, скопировав его с ближайшего родственника — imx219 (калибровка будет неточной по цветам, но rkaiq должен запуститься).

Пересобрал ядро заново, перезагрузился и проверяю имя сенсора:

media-ctl -d /dev/media0 -p | grep -A1 "CAM_SENSOR\|imx519"
- entity 63: m00_b_imx519 6-001a (2 pads, 1 link, 0 routes)
             type V4L2 subdev subtype Sensor flags 0

Имя изменилось на m00_b_imx519 — rockchip-формат на месте. Запускаю rkaiq с надеждой:

rkaiq_3A_server
Cound not find rkisp dev names, skipped /dev/media0
ERR: Bad media topology for: /dev/media0
CAMHW:E:get isp or ispp info fail, something gos wrong!

Старая ошибка parse sensor entity name error исчезла — rkaiq теперь распознаёт сенсор. Но на её месте выросла новая: Bad media topology. Прогресс есть, но стена просто отодвинулась.

Диагноз: split-pipeline #

Стал разбираться, в чём дело. На RK3588 медиа-инфраструктура оказалась разрезана надвое:

/dev/media0 (rkcif):  сенсор → DPHY → CSI2 → CIF stream-узлы
/dev/media1 (rkisp0): rkisp-isp-subdev ← rawrd0_m ← ... → mainpath

Сенсор и CIF-захват живут на одном media-устройстве, а ISP (процессор обработки) — на другом. rkaiq же перебирает /dev/media%d и для каждого ищет сенсор и ISP вместе. На media0 есть сенсор, но нет ISP; на media1 — наоборот. Ни одно устройство не подходит, отсюда Bad media topology для всех.

При этом сам rkaiq_3A_server за всю историю платы не отработал успешно ни разу — 2102 «Bad topology» и 0 «engine succeed» в логах. То есть это не специфика моего imx519: камерный стек сломан здесь для любого сенсора.

💡 Что такое split-pipeline. На RK3588 есть две аппаратные единицы: CIF (Camera Interface — захват сырого потока с MIPI) и ISP (Image Signal Processor — обработка: дебайеризация, баланс белого, гамма). В простом режиме они работают как отдельные подсистемы и создают два независимых media-device. Связь между ними — через DDR: CIF пишет туда RAW-кадр, а ISP забирает его через узлы rawrd*. Но rkaiq хочет видеть весь путь как единый граф на одном media-устройстве — и в split-режиме этого не получает.

Перепроверил гипотезы (и отбросил две) #

Прежде чем лезть в device tree, проверил версии и попытался обойти rkaiq вручную.

Гипотеза 1: несовпадение версий rkaiq ↔ ядро. Сравнил: rkaiq 5.0x4.1-rk3588 пишет ISP HW ver: 30, в конфиге ядра CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V30=y, rkisp-драйвер v02.09.00. Всё совпадает. Версии — не причина.

Гипотеза 2: получить кадр в обход rkaiq. На Neardi Wiki нашёл рабочий пример захвата с mainpath через v4l2-ctl --stream-mmap=3 --stream-skip=3. Попробовал то же у себя: активировал линк rkisp_rawrd0_m → rkisp-isp-subdev, выставил форматы, запустил захват. Результат — select timeout, файл 0 байт. CIF-узел /dev/video0 и вовсе не открывается (error 515 / EOPNOTSUPP).

Замкнутый круг: ISP (rawrd на media1) читает кадр из DDR, куда его должен записать CIF (на media0). Но CIF не запускается без «terminal subdev» синхронизации с ISP, а ISP не запускается без данных от CIF. Каждая сторона ждёт другую — и никто не начинает.

Решение в исходниках: rkisp-unite #

Интернет по Bad media topology давал мало: issue #254 на ubuntu-rockchip закрыт без решения (автор ушёл на Arch). Зато в исходниках самого ядра Repka нашёл подсказку — в rk3588s.dtsi узел rkisp0_vir0 содержит закомментированную альтернативу:

rkisp0_vir0: rkisp0-vir0 {
    rockchip,hw = <&rkisp0>;          /* одиночный ISP0 → split */
    /*
     * dual isp process image case
     * other rkisp hw and virtual nodes should disabled
     * rockchip,hw = <&rkisp_unite>;    ← объединённый режим */
    */
};

А в rk3588s-tablet-single.dtsi этот режим включён явно: rkisp_unite + rkisp_unite_mmu в okay, rkisp0_vir0 указывает на rkisp_unite, а одиночный rkisp0 — выключен. Это и есть dual-ISP unite mode, при котором два ISP-блока объединяются в единый media-graph. И судя по документации Firefly, топология overlay у меня верная — не хватает только перевести её в unite-режим.

Что меняю в overlay #

Добавил в свой rk3588-csi-imx519.dts три изменения:

Узел Было (split) Стало (unite)
rkisp0 (одиночный ISP0) status = "okay" status = "disabled"
rkisp0_vir0 rockchip,hw = <&rkisp0> rockchip,hw = <&rkisp_unite>
rkisp_unite disabled status = "okay"
rkisp_unite_mmu disabled status = "okay"

После перекомпиляции overlay проверил, что все метки фиксапов (включая новые rkisp_unite, rkisp_unite_mmu) присутствуют в базовом DTB — иначе overlay не применится. Применились, ок.

Ожидание: после перезагрузки rkaiq должен найти сенсор и ISP на одном media-устройстве, Bad media topology исчезнет, и кадр пойдёт. Но это пока гипотеза — проверить можно только ребутом.

💡 Почему я не уверен. Unite-режим официально описан для планшетных конфигураций RK3588 (где он работает). На Repka Pi 5 никто его, судя по всему, не включал — отсюда и сломанный rkaiq «из коробки». Шанс, что unite поднимет единый граф — высокий, но не 100%. Если не поможет — останется последний рычаг: стриминг через libv4l-rkmpp / GStreamer rk-плагин в обход rkaiq. Но это уже третья стена, и о ней — если до неё дойдёт.

Третий акт: kernel-отладка, или шесть багов в чужом драйвере #

Unite оказался ложным следом: он объединяет два ISP-блока для удвоения ширины кадра, а не сливает media-устройства. Сенсор по-прежнему на media0, ISP — на media1, rkaiq так же падает. Тогда я перестал гадать и пошёл по жёсткому пути — добавлять отладочный вывод прямо в ядро и собирать его после каждого изменения. Этот третий акт состоял из шестнадцати пересборок ядра и шести найденных багов, каждый из которых блокировал свой этап. Рассказываю по порядку — это полезно даже если у вас другой сенсор.

Баг №1 и №2: g_frame_interval без фильтра ENOIOCTLCMD #

Сначала добавил pr_info в rkcif_fh_open (open-handler CIF) и увидел точное место отказа:

RKCIF_OPEN: ENTER stream[0]
RKCIF_OPEN: attach_hw ok
RKCIF_OPEN: update_sensor_info FAIL ret=-515

515 = ENOIOCTLCMD — «ioctl не реализован». Оказалось, в rkcif_update_sensor_info (capture.c) CIF вызывает у сенсора g_frame_interval — необязательный video-subdev-op, который наш imx519 не реализует. Соседние проверки корректно фильтруют этот код:

// Правильно (соседи):
ret = v4l2_subdev_call(..., get_mbus_config, ...);
if (ret && ret != -ENOIOCTLCMD) { return ret; }

// Баг (наш случай):
ret = v4l2_subdev_call(..., g_frame_interval, ...);
if (ret) { return ret; }   ← не фильтрует ENOIOCTLCMD → open падает с -515

Починил — добавил фильтр. open() заработал. Но тут же вскрылся второй экземпляр того же бага — в rkcif_set_fmt. Там g_frame_interval тоже без фильтра возвращал -515, из-за чего cif_fmt_out оставался NULL, а потом VIDIOC_REQBUFS делал null-deref → kernel oops → зависание платы (watchdog тормозил перезагрузку). Тот же фикс, и crash ушёл.

💡 Урок. Когда находишь баг «отсутствует фильтр ENOIOCTLCMD» — проверь все вызовы g_frame_interval в файле. У RKCIF их восемь, и два были сломаны одинаково. Лечится одним паттерном.

Баг №3: field = INTERLACED вместо NONE #

После фиксов open и set_fmt заработали, но STREAMON падал с EINVAL в rkcif_sanity_check_fmt. Добавил отладку в rkcif_get_input_fmt и увидел:

RKCIF_FMT: sensor code=0x300f field=1 size=1920x1080

code=0x300f (SRGGB10_1X10) — правильно. Но field=1 (INTERLACED) — неправильно, должно быть 0 (NONE). В таблице in_fmts для SRGGB10_1X10 только field=NONE, поэтому совпадения нет → NULL → EINVAL.

Наш драйвер в imx519_update_image_pad_format устанавливал width/height, но не трогал field — и ядро V4L2 оставляло там мусор (1). Фикс — одна строка:

fmt->format.field = V4L2_FIELD_NONE;

Баг №4: crop bounds больше input #

Sanity прошла, но новая ошибка: crop size is bigger than input. Сенсор в get_selection(CROP_BOUNDS) возвращал полный pixel array (8, 48, 4656, 3496), а CIF копировал это в свой crop и проверял crop.left + crop.width <= input.width. При left=8, width=4656 сумма 4664 > 4656 — превышение на 8 пикселей. На Raspberry Pi unicam это прощает, RKCIF — нет.

Фикс — в get_selection для CROP_BOUNDS возвращать размер текущего mode, а не весь pixel array:

case V4L2_SEL_TGT_CROP_BOUNDS:
    sel->r.left = 0;
    sel->r.top = 0;
    sel->r.width = imx519->mode->width;
    sel->r.height = imx519->mode->height;

Баг №5: dummy buffer размера 0 #

Crop прошёл, но STREAMON упал с ENOMEM. Лог: Failed to allocate the memory for dummy buffer, size 0. CIF пытался создать dummy-буфер (для режима без реального стрима), но размер вычислялся через enum_frame_interval, который наш сенсор не реализует → размер оставался 0.

Обход — выключить dummy через sysfs:

echo 0 > /sys/devices/platform/rkcif-mipi-lvds2/is_use_dummybuf

После dummybuf ошибка сменилась на EPIPE, а в логах:

rockchip-csi2-dphy0: No pixel rate control in subdev

DPHY ищет у сенсора control V4L2_CID_LINK_FREQ (сообщение про «pixel rate» вводит в заблуждение). Я добавил регистрацию LINK_FREQ в imx519_init_controls, но с ошибкой — передал &imx519_ctrl_ops вместо NULL. Из-за этого V4L2 считал control изменяемым, вызывал s_ctrl, который не обрабатывает LINK_FREQ → ctrl(id:0x9f0901,val:0x0) is not handled → stream off.

// Баг:
v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx519_ctrl_ops, V4L2_CID_LINK_FREQ, ...);

// Правильно (как в imx219):
v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, ...);

После фикса not handled исчез, и DPHY наконец включился:

rockchip-csi2-dphy0: dphy0, data_rate_mbps 987
csi2_dphy_s_stream stream on:1, dphy1, ret 0

Прогресс симптомов — вся картина #

Шесть багов, каждый блокировал свой этап. Симптомы эволюционировали так:

Этап Симптом Баг Фикс
open() error 515 g_frame_interval без фильтра ret != -ENOIOCTLCMD
set_fmt kernel oops → hang то же в set_fmt → null deref тот же фикс
sanity_check EINVAL field=INTERLACED field = V4L2_FIELD_NONE
sanity_check EINVAL crop bounds > input bounds = mode size
STREAMON ENOMEM dummy buffer size 0 is_use_dummybuf=0
STREAMON EPIPE нет LINK_FREQ control v4l2_ctrl_new_int_menu(..., NULL, ...)
STREAMON stream ON, кадра нет физический слой MIPI (открытая задача)

Где мы сейчас: сенсор стримит, кадра нет #

После шести фиксов pipeline полностью активируется — но кадр всё ещё 0 байт. Казалось бы, опять стена. Но чтобы понять, где именно обрывается поток, я добавил отладку в сам сенсорный драйвер — в imx519_set_stream и imx519_start_streaming. И получил ответ, который всё расставил по местам:

dmesg | grep -E "SET_STREAM|START_STREAMING"
imx519 6-001a: SET_STREAM: enable=1 streaming=0
imx519 6-001a: SET_STREAM: pm_runtime_get_sync ret=0
imx519 6-001a: START_STREAMING: mode=1920x1080
imx519 6-001a: START_STREAMING: writing common regs (272)
imx519 6-001a: START_STREAMING: common regs OK
imx519 6-001a: START_STREAMING: writing mode regs (73)
imx519 6-001a: START_STREAMING: mode regs OK
imx519 6-001a: START_STREAMING: ctrl setup OK
imx519 6-001a: START_STREAMING: writing MODE_SELECT=STREAMING
imx519 6-001a: START_STREAMING: MODE_SELECT ret=0 (DONE)
imx519 6-001a: SET_STREAM: start_streaming ret=0

Каждый шаг — ret=0. Сенсор полностью инициализируется: 272 общих регистра + 73 регистра режима записаны по I²C без единой ошибки, controls применены, регистр MODE_SELECT переведён в режим стриминга. Команда s_stream(1) дошла до сенсора, и он подтвердил готовность. Софтверная инициализация — идеальна.

DPHY тоже включается корректно:

rockchip-csi2-dphy0: dphy0, data_rate_mbps 987
csi2_dphy_s_stream stream on:1, dphy1, ret 0

987 Мбит/с = 493.5 МГц × 2 (DDR) — это ровно та link frequency, которую мы задали в overlay. DPHY вычислил тайминги из LINK_FREQ control, который мы добавили в баге №6. Всё сходится.

Последняя софтверная попытка: continuous clock #

Я попробовал убрать clock-noncontinuous из device-tree overlay — многие сенсоры требуют именно continuous clock (DPHY постоянно генерирует тактовый сигнал, а не только в момент передачи данных). Перекомпилировал overlay, перезагрузился — без изменений, кадр по-прежнему 0 байт.

Проверка register-tables: может, сенсор настроен неправильно? #

Раз continuous clock не помог, возникло подозрение: а вдруг register-tables драйвера (писавшиеся Arducam для Raspberry Pi) настраивают MIPI-выход сенсора в режим, несовместимый с RK3588 DPHY? Например, неправильное число lanes, или PLL выдаёт не ту частоту.

Проверил. В driver-таблицах для всех режимов:

{0x0114, 0x01},   // CSI_LANE_MODE = 2-lane — правильно для RK3588
{0x0301, 0x06},   // PLL VTPXCK_DIV
{0x0303, 0x04},   // PLL VTSYCK_DIV
{0x0307, 0x40},   // PLL_VT_MPY
// ... и так далее

Регистр 0x0114 (CSI lane mode) = 0x01 — это 2-lane режим, ровно то, что наш overlay и заявляет (data-lanes = <1 2>). PLL-настройки тоже есть и записываются без ошибок (видели в логе START_STREAMING). Поиск по интернету показал: IMX519 datasheet — под NDA Sony, и единственный источник register-tables — это сам Arducam-драйвер. Альтернативных RK3588-специфичных таблиц не существует. При этом те же таблицы работают на Raspberry Pi — значит регистры сами по себе корректны, и MIPI-выход сенсора настроен правильно.

Register-tables не виноваты.

Итог: граница софтвера #

Это и есть та стена, дальше которой софтверная отладка не проходит. Все доказательства говорят одно:

  • ✅ Сенсор инициализирован и стримит (345 регистров, все ret=0)
  • ✅ DPHY включён с правильным таймингом (987 Мбит/с)
  • ✅ Pipeline активирован без единой ошибки
  • ❌ MIPI-данные не доходят от сенсора до CIF

При этом I²C работает (chip-ID читается, регистры пишутся), а MIPI high-speed lanes — нет. Это возможно, потому что I²C и MIPI — разные пары проводов в шлейфе: I²C (SCL/SDA) может быть подключен правильно, а MIPI lanes (CLK±, D0±, D1±) — нет. Или register-tables от Arducam, писавшиеся для bcm2835 (Raspberry Pi), настраивают MIPI-выход сенсора в режим, несовместимый с ожиданиями RK3588 DPHY.

Дальнейшая диагностика требует физического доступа — осциллографа или логического анализатора на MIPI-линиях, чтобы увидеть, есть ли реально high-speed сигнал в момент стриминга. Это уже не та задача, которую решает правка кода.

💡 Главный урок третьего акта. Я дважды ошибался с гипотезами (metadata-pad, rkisp-unite), пока не перестал гадать и не начал инструментировать ядро напрямую. pr_info в нужной функции за одну итерацию даёт больше, чем часы чтения кода и гугления. Когда упёрся в стену — добавь отладочный вывод в каждую проверку функции, пересобери, и последняя напечатанная строка покажет точное место отказа. Это дороже (цикл сборки), но вернее любых догадок. И только когда каждый шаг в логе показывает ret=0, а результата всё равно нет — пора признать, что проблема не в коде, а в физике.

Если новое ядро не загрузилось: откат #

Если после перезагрузки плата не поднимается (не отвечает по сети, нет картинки на HDMI) — скорее всего, ядро встало криво. Откат делается с другой системы: вынимаете накопитель (SD/eMMC), подмонтируете его на ПК или на второй Repka, и возвращаете файлы из бэкапов:

# На другой системе, с подмонтированным загрузочным разделом платы (например, /mnt)
cp /mnt/boot/vmlinuz-6.1.115.1-repka-pi5.bak-orig \
   /mnt/boot/vmlinuz-6.1.115.1-repka-pi5
cp /mnt/boot/dtb/rockchip/rk3588-repka-pi5.dtb.bak-orig \
   /mnt/boot/dtb/rockchip/rk3588-repka-pi5.dtb
# Вернуть модули
rm -rf /mnt/lib/modules/6.1.115.1-repka-pi5
mv /mnt/lib/modules/6.1.115.1-repka-pi5.bak-orig \
   /mnt/lib/modules/6.1.115.1-repka-pi5
# (опционально) выключить overlay, если он мешает
sed -i 's/^overlays=.*/overlays=panthor-gpu/' /mnt/boot/repkaEnv.txt

После возврата файлов плата грузится со старым ядром, как ни в чём не бывало.

💡 Если плата грузится, но камера всё ещё не работает — это уже не «ядро не встало», а софтверная отладка: смотрите dmesg | grep imx519, проверяйте, что overlay применился (ls /proc/device-tree/i2c@fec80000/camera-imx519@1a/), и крутите параметры overlay (регуляторы, тактовый сигнал). На этом этапе откат не нужен — ядро рабочее, просто камера требует доводки.

Если грузится, но отвалилось Wi-Fi/сеть: битый modules.dep #

Это самый коварный сценарий: плата грузится, вы радуетесь, а потом обнаруживаете, что отвалился Wi-Fi, пропал графический логин, или не работает звук. Ядро-то новое, а вот модули — недоустановлены.

Как это происходит #

При пересборке ядра make modules_install копирует сотни .ko-файлов и генерирует modules.dep — индекс, по которому modprobe находит модули. Если в момент установки происходит перезагрузка (watchdog, зависание, случайный ребут) — modules_install обрывается на середине. Файлы .ko скопированы частично, а modules.dep остаётся пустым или обрезанным (153 байта вместо нормальных ~50 КБ).

Результат: modprobe aic8800_fdrv (драйвер Wi-Fi) не находит модуль, потому что depmod не зарегистрировал его в индексе. Wi-Fi не поднимается. То же — с любым другим модулем: звуком, GPU, Bluetooth.

Как диагностировать #

# Размер modules.dep — если около 150 байт, он сломан
ls -l /lib/modules/$(uname -r)/modules.dep
# Должно быть ~50000 байт; если 150 — пересобирайте

# Проверить, знает ли modprobe о нужном модуле
modprobe -c | grep aic8800   # пусто = проблема

# Модуль физически есть, но не находится
find /lib/modules/$(uname -r) -name "aic8800*"

Как починить #

Если плата доступна (Ethernet работает, или через HDMI/клавиатуру) — перегенерируйте индекс и переустановите модули:

# Способ 1: быстрый — только пересобрать индекс (если .ko файлы на месте)
cd /root/linux-rockchip   # там, где дерево исходников
make ARCH=arm64 modules_install   # полная переустановка модулей
depmod -a $(uname -r)            # принудительно пересобрать индекс

# Способ 2: если дерево исходников недоступно, только depmod
depmod -a $(uname -r)
ls -l /lib/modules/$(uname -r)/modules.dep   # проверить размер

# Проверить, что модуль находится теперь
modprobe -c | grep aic8800

# Загрузить вручную
modprobe aic8800_fdrv
ip link show wlan0   # должен появиться интерфейс

Если Ethernet не работает (модуль dwmac тоже отвалился) — подключите монитор и USB-клавиатуру, или выпишите накопитель и сделайте depmod с другой системы, подмонтировав раздел.

⚠️ Мораль. Никогда не перезагружайтесь, пока make modules_install полностью не завершён. Проверяйте, что последняя строка вывода — DEPMOD /lib/modules/..., а размер modules.dep — порядка 50 КБ. Если сомневаетесь — запустите depmod -a $(uname -r) вручную после установки. Это спасёт вас от «отвалившегося Wi-Fi после пересборки ядра».

Что вообще может «отвалиться» #

Модульная подсистема RK3588 на Repka включает десятки драйверов, которые грузятся через modprobe:

Что Модуль Симптом пропажи
Wi-Fi aic8800_fdrv, aic8800_bsp нет wlan0
Звук (HDMI) snd-hdmi нет звука
GPU panthor нет графики
Видео-кодеки mpp_* не работает аппаратное видео
Камера (если модуль) imx519 нет /dev/video*

Если что-то из этого пропало после пересборки — сначала проверьте modules.dep, а не вините ядро.

Итоги #

Параметр Значение
Камера Arducam IMX519 16MP (fixed-focus)
Платформа Repka Pi 5 (RK3588), ядро 6.1.115.1-repka-pi5
Путь пересборка ядра + 6 баг-фиксов в RKCIF/imx519 (через kernel-отладку)
Драйвер портирован 5.15→6.1 + RKMODULE ioctl + rockchip-имя + LINK_FREQ + crop/field фиксы
Overlay собственный rk3588-csi-imx519.dtbo
Физика ✅ сенсор отвечает на I²C, шлейф корректен
Ядро после ребута ✅ грузится, chip ID 0x0519 прочитан
Камера в медиа-графе ✅ entity m00_b_imx519, линки ENABLED
open() / set_fmt / sanity ✅ работают (после фиксов g_frame_interval, field, crop)
Pipeline активация ✅ stream ON, DPHY включён (987 Мбит/с)
Сенсор инициализация ✅ 345 регистров записаны, MODE_SELECT=STREAMING ret=0
Готовый кадр MIPI-данные не доходят — физический слой (осциллограф)
Баг-фиксов в чужом коде 6 (g_frame_interval ×2, field, crop bounds, dummybuf, LINK_FREQ)
Пересборок ядра 16
Бэкапы ядро, DTB, модули, overlay — всё сохранено для отката

Главный неочевидный вывод всей истории — их три, и все болезненные.

Первый: на RK3588 разница между out-of-tree модулем и built-in драйвером — не формальность. Модуль грузится позже завершения async-notifier'а RKCIF, и сенсор «теряется». Лишь встроив драйвер в ядро, удаётся выставить правильный порядок probe.

Второй: «сенсор в медиа-графе» — тоже ещё не победа, и даже pipeline, который активируется — не победа. Каждый снятый софтверный баг открывал следующий, и только дойдя до stream ON без ошибок, стало ясно, что дальше — уже физика MIPI.

Третий, главный: перестань гадать — инструментируй. Я дважды ошибался с гипотезами (metadata-pad, rkisp-unite), пока не начал добавлять pr_info прямо в функции ядра. Одна итерация сборки с отладкой давала точный диагноз, который часы чтения кода не давали. Это дороже (цикл сборки ~5 мин), но вернее любых догадок.

Уроки, которые я вынес #

  1. Сначала физика, потом софт. Проверка I²C-сканером за минуту подтвердила, что шлейф и питание ок, и проблема — в драйверах/DT, а не в «битой камере». Не полезет ничего — начинай с самого нижнего уровня.
  2. Out-of-tree vs built-in — не одно и то же на RK3588. Модуль технически работает (chip ID читается), но не привязывается к медиа-графу из-за race condition с notifier'ом. Если «всё собралось, но графа нет» — копай в сторону порядка probe, и не трать время на перепривязку через sysfs (крахнет RKCIF).
  3. Не запускай параллельные сборки в одном дереве. Две пересекающиеся make в одном дереве объектов дают «гонку» с потерянными .d-файлами и непонятными ошибками fixdep. Одна чистая сборка через setsid — и всё соберётся.
  4. Празднуй только на финальной вехе. Я отметил победу, когда сенсор появился в медиа-графе с ENABLED-линком — и ошибся. На RK3588 «драйвер собрался» → «chip ID прочитался» → «сенсор в графе» → «файл с пикселями ненулевого размера» — это четыре разных уровня, и только последний настоящая победа. Всё остальное — промежуточные ступени, любая из которых может оказаться ложным финишем.

Шпаргалка команд #

# Камера видна на I²C?
sudo i2cdetect -y 6

# Какие overlay камер есть в комплекте
ls /boot/dtb/rockchip/overlay/ | grep -iE "csi|cam|imx"

# Сборка out-of-tree модуля (проверка совместимости)
make KDIR=/lib/modules/$(uname -r)/build

# Компиляция overlay
dtc -@ -I dts -O dtb -o rk3588-csi-imx519.dtbo rk3588-csi-imx519.dts

# Сборка ядра (built-in imx519)
make ARCH=arm64 -j8 Image dtbs modules

# Проверка, что драйвер встроен
grep imx519 drivers/media/i2c/built-in.a

# Логи камеры после загрузки
dmesg | grep -i imx519
# Ключевая проверка — привязался ли сенсор к CIF:
dmesg | grep -c "get remote terminal sensor failed"   # 0 = успех

# Async-привязка (кто ждёт кого; пусто = все связаны)
cat /sys/kernel/debug/v4l2-async/pending_async_subdevices

# Топология медиа-графа + проверка, что imx519 в нём с ENABLED-линком
media-ctl -d /dev/media0 -p | grep -iA2 imx519

# Формат и доступные размеры сенсора
v4l2-ctl -d /dev/v4l-subdev2 --get-subdev-fmt pad=0,stream=0

# Controls сенсора (exposure, gain, flip) — живые?
v4l2-ctl -d /dev/v4l-subdev2 --list-ctrls-menus

# Откат ядра (с другой системы, на подмонтированном разделе /mnt)
cp /mnt/boot/vmlinuz-6.1.115.1-repka-pi5.bak-orig \
   /mnt/boot/vmlinuz-6.1.115.1-repka-pi5
cp /mnt/boot/dtb/rockchip/rk3588-repka-pi5.dtb.bak-orig \
   /mnt/boot/dtb/rockchip/rk3588-repka-pi5.dtb

Ссылки #


0

Комментарии (0)

Для участия в обсуждении Вы должны быть авторизованным пользователем

Еще посты по теме

Наиболее интересные по мнению читателей



Темы

Навигация

ВойтиРегистрация