Ты был прав, что надавил: никакого сжатия там не было, я поверил своей же неудачной попытке разбора и назвал это сжатием. Энтропия по всему файлу от 1.0 до 2.4 бита на байт — данные лежат открыто, просто устроены иначе, чем я предполагал: каждое значение занимает восемь байт, а «смещение в битах» из схемы на самом деле оказалось номером поля, умноженным на восемь. Плюс значение относится к регистру на три записи дальше по списку.
Что теперь есть, прочитанное из вендорного файла этого самого телефона:
начальная последовательность 408 регистров
8000×6000 строка 9184 кадр 6058 2099 Мбит/с на линию без бинирования
4000×3000 строка 7872 кадр 3054 1145 Мбит/с бинирование 2×2
4000×2250 строка 7872 кадр 2308 1729 Мбит/с 2×2
1920×1080 строка 2912 кадр 1262 1540 Мбит/с 4×4
Заодно оно подтвердило из первоисточника всё, что мы добывали измерением и рассуждением: 0x0114 = 0x03 — четыре линии, записи в 0x0111 нет вовсе — значит заводской D-PHY, 0x0136/0x0137 = 0x13/0x33 — опорная частота 19.2 МГц, а не 24, как в таблицах от MediaTek.
Драйвер переведён на эти таблицы целиком: четыре режима вместо двух самодельных, начальная последовательность вендорная, частота линка выбирается по режиму, ручка числа линий выброшена за ненадобностью. В дереве соответственно 19.2 МГц и список из четырёх частот.
Мои выведенные вручную множители тоже оказались близки к истине — для бинированного режима я считал 1248 Мбит/с, у вендора 1145, — но теперь они не нужны: есть подлинные.
💰 361₽ · ⬇️ 13.82M / ⬆️ 30.1k · opus-5 · max · 473с
ВеткаТы был прав, что надавил: никакого сжатия там не было, я поверил своей же неудачной попытке разбора и назвал это сжатием…
301 сообщение · –Ы S Не полностью. Скажу честно, что теперь есть, чего нет и что вообще ни разу не запускалось. Будет работать (при условии, что новые таблицы поведут себя как вендорные): опознание сенсора, включение питания и такта на 19.2 МГц, четыре режима — 8000×6000, 4000×3000, 4000×2250 и 1920×1080, съёмка сырых кадров через RDI, выдержка, усиление и частота кадров через органы управления, непрерывный поток, а не только одиночный кадр. Плюс наводка на резкость, если привод действительно отзовётся по адресу 0x0c. Не будет работать без дальнейшей работы: Калибровка из EEPROM модуля. Коррекция чувствительности (2304 байта) и коррекция по краям (два раза по 192) сейчас не грузятся, поэтому в полном разрешении коррекция выключена — иначе она работала бы по мусору. На бинированные режимы это не влияет, там её и вендор не включает. Автоматическая наводка. Есть только ручное управление положением линзы; алгоритма, который сам ищет резкость, в ядре нет — это забота libcamera, и он там есть, но его ещё надо будет проверить. Цветовая настройка. Файла настройки для libcamera нет, поэтому картинка будет с честными, но некрасивыми цветами: без правильного баланса, без коррекции затемнения по краям. Фазовый автофокус, вспышка, зум — вообще не трогали. И главное: ни одна из новых таблиц ни разу не запускалась. Всё, что мы сняли, было на таблицах от MediaTek с частотой 24 МГц и одной самодельной настройкой. Сейчас поменялось разом: опорная частота, начальная последовательность, все режимы и все частоты линка. Так что первый запуск — это проверка, а не готовый результат. Отдельно отмечу риск: полный кадр у вендора идёт на 2099 Мбит/с на линию, а мы уже видели, что на 2496 приём сыплется. Если и 2099 окажется многовато, надёжным останется режим 4000×3000 на 1145 — он вдвое ниже той границы, и именно с него я бы начинал проверку. 💰 18.92₽ · ⬇️ 744.0k / ⬆️ 1.7k · opus-5 · max · 38сЫ S Сделаю. Раз формат поддался, в этом же файле должны быть и сведения о EEPROM. Читаю поля описания модуля: Уже кое-что важное: по смещению видно значение 0x3210 — это laneAssign, то есть линии данных идут ноль-один-два-три по порядку. Ровно то, что мы намерили. Читаю всю область описания модуля подряд: Продвинулся. В описании модуля теперь читаются и остальные поля: +3456 0x3210 laneAssign — линии данных 0,1,2,3 по порядку +3464 0x0 isComboMode — не совмещённый режим имена: "imx582_davinci", "davinci_imx582_dw9763_ofilm", "back_pmic_flash" laneAssign = 0x3210 — это прямое подтверждение из вендорного файла того, что мы намерили: логическая линия n читается с физической n. А вот адрес и раскладка EEPROM лежат в отдельном файле, и его имя теперь известно: com.qti.eeprom.davinci_imx582_dw9763_ofilm.bin. Он на телефоне, рядом с теми, что ты уже копировал: ls /vendor/lib/camera/com.qti.eeprom.* 2>/dev/null || \ find / -name "com.qti.eeprom.*davinci*" 2>/dev/null | head Скопируй его так же, как копировал сенсорные — формат я теперь разбираю, оттуда возьмутся и адрес микросхемы, и смещения блоков калибровки, и способ проверки целостности. Пока файла нет, пишу сам механизм. Правильный для mainline способ — объявить EEPROM в дереве обычным at24, а в драйвере читать блок через nvmem и заливать его в регистры сенсора с 0x7F00: eeprom@50 { compatible = "atmel,24c64"; reg = <0x50>; read-only; }; cal = devm_nvmem_cell_get(dev, "qsc"); ... for (i = 0; i < IMX582_QSC_SIZE; i++) cci_write(cci, CCI_REG8(0x7F00 + i), qsc[i], &ret); Так это ложится в общую схему ядра, а не самоделкой, и после отправки наверх не вызовет вопросов. Жду файл, чтобы подставить точные числа. 💰 76.04₽ · ⬇️ 2.99M / ⬆️ 5.2k · opus-5 · max · 91сЫ S Смонтируй только на чтение — это безопасно: ls /dev/disk/by-partlabel/ 2>/dev/null | grep -i vendor || ls /dev/block/by-name/ 2>/dev/null | grep -i vendor sudo mkdir -p /mnt/vendor sudo mount -o ro /dev/disk/by-partlabel/vendor /mnt/vendor 2>/dev/null || \ sudo mount -o ro /dev/block/by-name/vendor /mnt/vendor ls /mnt/vendor/lib/camera/ | grep -i "eeprom\|actuator" | head Нужны два файла: com.qti.eeprom.davinci_imx582_dw9763_ofilm.bin и com.qti.actuator.*davinci*.bin. Первый даст адрес микросхемы памяти и раскладку блоков калибровки, второй — коэффициенты плавного подвода для привода, те самые, которые я взял по умолчанию. Если раздел не монтируется (бывает, если он в другом формате или занят), скажи — есть обходной путь: адрес микросхемы можно найти опросом шины, а раскладку вычислить по содержимому, потому что блок коррекции чувствительности ровно 2304 байта и у него характерный вид. Но это уже гадание по узору, а с файлом будет точно. 💰 19.05₽ · ⬇️ 753.2k / ⬆️ 951 · opus-5 · max · 16сЫ ls /mnt/vendor/lib/camera/ com.qti.sensor.imx582_davinci.so com.qti.sensormodule.davinci_ofilm_imx586.bin com.qti.tuned.davinci_ofilm_ov8856.bin com.qti.sensor.imx586_davinci.so com.qti.sensormodule.davinci_ofilm_ov8856.bin com.qti.tuned.davinci_ofilm_s5k3l6.bin com.qti.sensor.ov8856_davinci.so com.qti.sensormodule.davinci_ofilm_s5k3l6.bin com.qti.tuned.davinci_sunny_s5k3t2.bin com.qti.sensor.s5k3l6_davinci.so com.qti.sensormodule.davinci_sunny_s5k3t2.bin com.qti.tuned.default.bin com.qti.sensor.s5k3t2_davinci.so com.qti.tuned.davinci_luxvisions_imx582.bin components com.qti.sensormodule.davinci_luxvisions_imx582.bin com.qti.tuned.davinci_luxvisions_imx586.bin fdconfigpreview.bin com.qti.sensormodule.davinci_luxvisions_imx586.bin com.qti.tuned.davinci_luxvisions_ov8856.bin fdconfigpreviewlite.bin com.qti.sensormodule.davinci_luxvisions_ov8856.bin com.qti.tuned.davinci_luxvisions_s5k3l6.bin fdconfigvideo.bin com.qti.sensormodule.davinci_luxvisions_s5k3l6.bin com.qti.tuned.davinci_ofilm_imx582.bin fdconfigvideolite.bin com.qti.sensormodule.davinci_ofilm_imx582.bin com.qti.tuned.davinci_ofilm_imx586.binS В этом каталоге их нет, но есть подкаталог components — у CAMX именно там лежат составные части вроде памяти и привода: ls /mnt/vendor/lib/camera/components/ | head -40 Заодно любопытная находка: com.qti.sensor.imx582_davinci.so — это библиотека самого сенсора, в ней у CAMX живут расширения вроде чтения калибровки. Если бинарников памяти не окажется, раскладку можно будет достать из неё, там эти смещения обычно лежат прямо в коде. 💰 18.31₽ · ⬇️ 754.9k / ⬆️ 540 · opus-5 · max · 12сЫ com.almalence.node.sr.so com.altek.node.depurple.so com.qti.camx.chiiqutils.so com.qti.eisv2.so com.qti.eisv3.so com.qti.hvx.addconstant.so com.qti.hvx.binning.so com.qti.node.depth.so com.qti.node.dummyrtb.so com.qti.node.dummysat.so com.qti.node.eisv2.so com.qti.node.eisv3.so com.qti.node.fcv.so com.qti.node.gpu.so com.qti.node.memcpy.so com.qti.node.remosaic.so com.qti.node.stich.so com.qti.node.swregistration.so com.qti.node.watermark.so com.qti.node.xiaomigenderage.so com.qti.stats.aec.so com.qti.stats.af.so com.qti.stats.afd.so com.qti.stats.asd.so com.qti.stats.awb.so com.qti.stats.awbwrapper.so com.qti.stats.haf.so com.qti.stats.localhistogram.so com.qti.stats.pdlib.so com.qti.stats.pdlibsony.so com.qti.stats.pdlibwrapper.so com.qtistatic.stats.aec.so com.qtistatic.stats.af.so com.qtistatic.stats.awb.so com.qtistatic.stats.pdlib.so com.visidon.node.clearshot.so com.visidon.node.sr.so libdepthmapwrapper.so libmmcamera_cac3.soS Файлов памяти нет ни там, ни там — значит калибровку CAMX берёт не из отдельного описания, а внутри библиотеки сенсора. Посмотрим, что в ней: strings -a /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.so | grep -iE "qsc|otp|eeprom|lrc|calib" | head -20 ls -l /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.so Если там мелькнёт QSC или OTP — значит логика чтения зашита в неё, и дальше я скажу, какие константы искать. Есть и вторая возможность, которую стоит проверить сразу: у IMX586 калибровка чувствительности лежит в собственной памяти сенсора, а не в отдельной микросхеме. Читается она через регистры выбора страницы, и тогда никакой EEPROM вообще не нужен — всё делается по той же шине, что и остальное. Проверить это можно так: strings -a /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.so | grep -iE "0x0a0|page|read_otp" | head Если подтвердится, я напишу чтение прямо в драйвере: выбрать страницу, вычитать 2304 байта, залить в регистры с 0x7F00 — и коррекция заработает без всякого дерева и nvmem. 💰 19.27₽ · ⬇️ 756.0k / ⬆️ 1.6k · opus-5 · max · 28сЫ qcom-sm7150:~$ strings -a /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.so | grep -iE "qsc|otp|eeprom|lrc|calib" | head -20 qcom-sm7150:~$ qcom-sm7150:~$ ls -l /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.so -rw-r--r-- 1 root root 5208 Jan 1 2009 /mnt/vendor/lib/camera/com.qti.sensor.imx582_davinci.soS Библиотека — заглушка на пять килобайт, никакой логики в ней нет. И описания памяти на разделе тоже нет ни одного. Это меняет вывод. Смотри: у MediaTek калибровку читают из внешней микросхемы модуля и заливают в сенсор. У davinci такого описания нет вовсе — значит калибровка лежит в собственной памяти сенсора и применяется им самим, когда коррекция включена. Именно это вендорная таблица и делает: 0x3620 = 0x01, 0x3621 = 0x01. То есть моё прежнее решение гасить коррекцию было правильным для таблиц MediaTek (там без внешних коэффициентов она работала бы по мусору), но для вендорных таблиц оно неверно — надо оставлять как у вендора. Переворачиваю: На всякий случай проверь, что описания памяти действительно нет нигде на разделе: find /mnt/vendor -iname "*eeprom*" 2>/dev/null | head Если пусто — вывод окончательный, и делать нам с калибровкой ничего не нужно: она внутри сенсора. Тогда отвечаю на твой вопрос про полноту: из списка недостающего коррекция чувствительности отпадает, потому что она не наша забота. Остаётся: Наводка на резкость — привод объявлен, драйвер выбран, но алгоритма поиска резкости в ядре нет и не должно быть, это делает libcamera. Цветовая настройка — файл настройки для libcamera; делается по готовым кадрам, то есть после первого запуска. И то, чего у нас нет по мелочи: зеркалирование, испытательные картинки, границы обрезки. Это дописывается в драйвер за один присест, но нужно уже для отправки наверх, а не для работы камеры. Так что после сборки основная камера должна давать кадры во всех четырёх режимах и наводиться вручную. Это уже полноценная работа сенсора; всё остальное — надстройки. 💰 55.76₽ · ⬇️ 2.28M / ⬆️ 2.5k · opus-5 · max · 50сЫ S Тогда вопрос закрыт: калибровка внутри сенсора, нам её трогать не нужно. Пока не пускаешь к железу, дописываю то, чего драйверу не хватает для отправки наверх. Смотрю, что уже есть: В драйвере уже есть испытательные картинки, а не хватает трёх вещей: зеркалирования, границ обрезки и свойств из дерева (ориентация, поворот). Делаю по образцу imx219, где зеркалирование правильно меняет порядок цветовых точек: Метки error в этой функции нет — стиль обработки ошибок другой. Правлю под него: Дописал то, чего не хватало драйверу для приличного вида. Зеркалирование. Регистр 0x0101, но с тонкостью: отражение меняет порядок цветовых точек в квадрате, поэтому драйвер теперь сообщает разный код формата в зависимости от того, что включено: static const u32 imx586_mbus_formats[] = { MEDIA_BUS_FMT_SRGGB10_1X10, /* без отражений */ MEDIA_BUS_FMT_SGRBG10_1X10, /* по горизонтали */ MEDIA_BUS_FMT_SGBRG10_1X10, /* по вертикали */ MEDIA_BUS_FMT_SBGGR10_1X10, /* и так, и так */ }; Оба органа управления помечены как меняющие раскладку, поэтому во время потока их не переставить — так же сделано у imx219, откуда я и взял образец. Границы обрезки. Теперь драйвер отвечает на запрос области: все режимы читают матрицу целиком (маленькие — бинированием, а не вырезкой), поэтому прямоугольник один и равен 8000×6000. Свойства из дерева. Ориентация и поворот, которые у нас в дереве прописаны, теперь доходят до пользователя через органы управления, а не пропадают. Вместе с прежним это даёт полный набор: частота линка, поток точек, гашение по горизонтали и вертикали, выдержка, усиление, испытательные картинки, зеркалирование, границы, свойства. Для отправки наверх остаётся только прогнать checkpatch и проверить сборку — а это уже к железу, там же и первый запуск вендорных таблиц. 💰 168₽ · ⬇️ 6.89M / ⬆️ 7.8k · opus-5 · max · 121сЫ S Один изъян в apply.sh нашёл прямо сейчас: он ставит только два модуля, а новый драйвер привода — нет. А без него камера не поднимется вовсе, я про эту зависимость писал. Исправляю и собираю окончательный набор: Ставится так. Сначала подмена файла дерева — имя изменилось, поэтому надо поправить и #include: tar xzf lanekit21.tar.gz && cd lanekit T=~/linux-7.2/arch/arm64/boot/dts/qcom grep -rn "davinci-imx586.dtsi" $T | head Покажи, что найдёт — там будет строка подключения. Дальше: cp davinci-camera.dtsi $T/ sed -i 's|davinci-imx586.dtsi|davinci-camera.dtsi|' $T/<файл, который нашёлся> rm -f $T/davinci-imx586.dtsi sh apply.sh apply.sh правит camss, кладёт драйвер, включает CONFIG_VIDEO_DW9768, собирает модули и дерево, ставит три модуля несжатыми. Если он остановится с жалобой на VIDEO_DW9768 — значит настройка не включилась, скажи мне. Потом дерево и перезагрузка: cd ~/linux-7.2 sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs sync && sudo reboot После загрузки первым делом это: sudo dmesg | grep -iE "imx58|dw9768|camss|csiphy" | head -20 Жду три вещи: IMX582 detected, привязку dw9768 без ошибок и появление медиа-устройства. Если сенсор не опознается — почти наверняка из-за смены опорной частоты на 19.2 МГц, это лечится одной строкой в дереве. Дальше съёмка, и начинать я предлагаю с бинированного режима, он вдвое ниже по скорости линии и потому надёжнее: cd ~/lanekit sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh sh capture.sh Если пойдёт — переключим на полный кадр и проверим наводку через focus.sh. 💰 75.27₽ · ⬇️ 3.09M / ⬆️ 3.4k · opus-5 · max · 59сЫ S Значит прошлый набор не подключал файл, а дописывал его содержимое прямо в дерево. Найдём, куда: T=~/linux-7.2/arch/arm64/boot/dts/qcom grep -ln "camera@1a\|camw_dvdd_1p1" $T/*.dts $T/*.dtsi grep -n "camera@1a\|camw_dvdd_1p1\|Main (wide) camera" $T/sm7150-xiaomi-davinci.dtsi | head Первая команда покажет файл, вторая — с какой строки начинается наш кусок. Дальше я дам точную команду, чтобы вырезать старое и подставить новое: содержимое поменялось сильно (линии, частота, привод, питание), поэтому просто дописать поверх нельзя — получатся два узла camera@1a и сборка встанет. 💰 18.60₽ · ⬇️ 775.4k / ⬆️ 633 · opus-5 · max · 14сЫ grep -ln "camera@1a\|camw_dvdd_1p1" $T/*.dts $T/*.dtsi /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi grep -n "camera@1a\|camw_dvdd_1p1\|Main (wide) camera" $T/sm7150-xiaomi-davinci.dtsi | head 635: * Main (wide) camera of the Xiaomi Mi 9T / Redmi K20 (davinci): Sony IMX586. 653: camw_dvdd_1p1: regulator-camw-dvdd-1p1 { 655: regulator-name = "camw_dvdd_1p1"; 687: camera@1a { 701: dvdd-supply = <&camw_dvdd_1p1>;S Наш кусок дописан в конец sm7150-xiaomi-davinci.dtsi. Сначала посмотрим границу, ничего не меняя: F=$T/sm7150-xiaomi-davinci.dtsi S=$(grep -n "Main (wide) camera" $F | cut -d: -f1) echo "комментарий на строке $S, всего строк: $(wc -l < $F)" sed -n "$((S-4)),$((S+1))p" $F echo "--- конец файла:" tail -4 $F Мне нужно увидеть две вещи: что перед комментарием идёт строка с SPDX-License-Identifier (это начало нашей вставки) и что файл заканчивается нашим же куском, а не чем-то ещё. Если так и есть, замена делается одной командой с сохранением копии: cp $F $F.bak head -n $((S-3)) $F > /tmp/d.new cat ~/lanekit/davinci-camera.dtsi >> /tmp/d.new mv /tmp/d.new $F grep -c "camera@1a" $F # должна быть ровно единица Копия останется в .bak — если что-то пойдёт не так при сборке, вернём одной командой. 💰 19.87₽ · ⬇️ 776.4k / ⬆️ 1.8k · opus-5 · max · 29сЫ qcom-sm7150:~/lanekit$ F=$T/sm7150-xiaomi-davinci.dtsi qcom-sm7150:~/lanekit$ S=$(grep -n "Main (wide) camera" $F | cut -d: -f1) qcom-sm7150:~/lanekit$ echo "комментарий на строке $S, всего строк: $(wc -l < $F)" комментарий на строке 635, всего строк: 734 qcom-sm7150:~/lanekit$ sed -n "$((S-4)),$((S+1))p" $F }; }; // SPDX-License-Identifier: BSD-3-Clause /* * Main (wide) camera of the Xiaomi Mi 9T / Redmi K20 (davinci): Sony IMX586. * qcom-sm7150:~/lanekit$ echo "--- конец файла:" --- конец файла: qcom-sm7150:~/lanekit$ tail -4 $F bias-disable; }; }; };S Граница ровно та, что нужно: строка 633 — начало нашей вставки, файл ею и заканчивается. Значит команда из прошлого сообщения подходит без изменений: cp $F $F.bak head -n $((S-3)) $F > /tmp/d.new cat ~/lanekit/davinci-camera.dtsi >> /tmp/d.new mv /tmp/d.new $F grep -c "camera@1a" $F tail -3 $F Ожидаю единицу от grep и закрывающую скобку в конце. Дальше сборка: cd ~/lanekit && sh apply.sh Он соберёт модули и дерево. На дереве и проверится, всё ли в новом файле правильно: dtc ругается на несуществующие метки и лишние скобки сразу, так что если сборка прошла — значит ссылки на vdda_mipi_csi0_0p9, vreg_l3c_1p23, pm6150l_gpios 11 и остальное разрешились. Потом установка дерева и перезагрузка: cd ~/linux-7.2 sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs sync && sudo reboot 💰 19.31₽ · ⬇️ 778.6k / ⬆️ 774 · opus-5 · max · 14сЫ S Сейчас подниму по записям — у нас всё это записано. По записям, для звонков нужны три вещи, и все три сбрасываются при перезагрузке, если что-то не поднялось само. Проверь по порядку: aplay -l | head -3 amixer -c0 cget name='HPHL Switch' cat /etc/conf.d/q6voiced 2>/dev/null ls -l /usr/local/bin/voicepath.sh Первое — жива ли карта вообще (у нас был случай, когда мастер SoundWire успевал уснуть до пробы кодека, и карта не создавалась). Второе — включён ли порт кодека: без HPHL Switch = 1 звука в ухе не будет никогда, это и был главный дефект. Третье — файл настроек демона, без него он падал на разборе аргументов и крутился по кругу. Если карта на месте, а HPHL Switch в нуле — просто прогони наш набор: sh /usr/local/bin/voicepath.sh Он ставит приёмный тракт целиком: RX_MACRO RX0 MUX=AIF1_PB, RX INT0_1 MIX1 INP0=RX0, RX INT0 DEM MUX=CLSH_DSM_OUT, RX_EAR Mode Switch=1, HPHL Switch=1, RDAC3_MUX=RX1, EAR_RDAC Switch=1, плюс фронтенды RX_CODEC_DMA_RX_0 Voice Mixer CS-Voice и CS-Voice Capture Mixer TX_CODEC_DMA_TX_3. И главное правило оттуда же: микшеры надо выставить до звонка, а не во время. Драйвер читает их при открытии PCM, q6voice_start() висит на .startup, так что если выставить в разговоре — не подействует. Покажи вывод четырёх команд, и я скажу, что именно отвалилось. 💰 56.89₽ · ⬇️ 2.34M / ⬆️ 2.1k · opus-5 · max · 49сЫ Ы S Хорошо. Тогда ставим дерево и перезагружаемся: cd ~/linux-7.2 sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs sync && sudo reboot После загрузки — три проверки подряд: sudo dmesg | grep -iE "imx58|dw9768|camss" | head -20 media-ctl -d /dev/media0 -p | grep -E "entity [0-9]+:" | head aplay -l | head -3 Первая скажет, опознался ли сенсор на новой опорной частоте 19.2 МГц и привязался ли привод. Вторая — собрался ли граф (там должен появиться и dw9768 отдельной сущностью). Третья заодно проверит, что звук пережил смену дерева: мы же меняли только камерную часть, но dtb собрался заново целиком, поэтому лучше убедиться сразу. Потом первый кадр, с бинированного режима: cd ~/lanekit sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh sh capture.sh 💰 18.77₽ · ⬇️ 784.1k / ⬆️ 496 · opus-5 · max · 15сЫ [ 0.655649] platform ace0000.camss: Adding to iommu group 8 [ 12.567469] /soc@0/camss@ace0000: Fixed dependency cycle(s) with /soc@0/cci@ac4a000/i2c-bus@0/camera@1a [ 12.567670] /soc@0/cci@ac4a000/i2c-bus@0/camera@1a: Fixed dependency cycle(s) with /soc@0/camss@ace0000 [ 12.580913] /soc@0/camss@ace0000: Fixed dependency cycle(s) with /soc@0/cci@ac4a000/i2c-bus@1/camera@36 [ 12.580977] /soc@0/cci@ac4a000/i2c-bus@1/camera@36: Fixed dependency cycle(s) with /soc@0/camss@ace0000 [ 12.840134] calling qcom_camss_driver_init+0x0/0xff8 [qcom_camss] @ 623 [ 12.931882] probe of ace0000.camss returned 0 after 87834 usecs [ 12.933334] initcall qcom_camss_driver_init+0x0/0xff8 [qcom_camss] returned 0 after 37974 usecs [ 13.219832] calling imx586_i2c_driver_init+0x0/0xff8 [imx586] @ 624 [ 13.239521] imx586 12-001a: IMX582 detected [ 13.240825] imx586 12-001a: signalling mode default 0x02 [ 13.245512] imx586 12-001a: lane mode default 0x03 [ 13.251058] initcall imx586_i2c_driver_init+0x0/0xff8 [imx586] returned 0 after 15394 usecs qcom-sm7150:~$ media-ctl -d /dev/media0 -p | grep -E "entity [0-9]+:" | head - entity 1: msm_csiphy0 (2 pads, 2 links, 0 routes) - entity 4: msm_csiphy1 (2 pads, 2 links, 0 routes) - entity 7: msm_csiphy2 (2 pads, 2 links, 0 routes) - entity 10: msm_csiphy3 (2 pads, 2 links, 0 routes) - entity 13: msm_csid0 (5 pads, 14 links, 0 routes) - entity 19: msm_csid1 (5 pads, 14 links, 0 routes) - entity 25: msm_vfe0_rdi0 (2 pads, 3 links, 0 routes) - entity 28: msm_vfe0_video0 (1 pad, 1 link) - entity 34: msm_vfe0_rdi1 (2 pads, 3 links, 0 routes) - entity 37: msm_vfe0_video1 (1 pad, 1 link) qcom-sm7150:~$ aplay -l | head -3 **** List of PLAYBACK Hardware Devices **** card 0: X9T [Xiaomi Mi 9T], device 0: MultiMedia1 (*) [] Subdevices: 1/1S Сенсор опознан на новой частоте и с вендорными таблицами — это главное. Но в графе его нет: у msm_csiphy0 всего две связи, то есть сенсор к нему не привязан. Причина, скорее всего, та, о которой я предупреждал: сенсор ждёт привод, а dw9768 в логе не появился вовсе. Проверим: lsmod | grep -E "dw9768|imx586" grep VIDEO_DW9768 ~/linux-7.2/.config ls -l /lib/modules/$(uname -r)/kernel/drivers/media/i2c/dw9768.ko* sudo modprobe dw9768; sleep 1; sudo dmesg | tail -5 media-ctl -d /dev/media0 -p | grep -E "imx586|dw9768" Если после ручной загрузки сенсор появится в графе — значит модуль просто не подхватился автоматически, и лечится это depmod плюс правильным именем файла. Если modprobe скажет, что модуля нет, — значит настройка VIDEO_DW9768 не включилась при сборке, и надо пересобрать модули (дерево при этом трогать не придётся, перезагрузка не нужна). 💰 19.61₽ · ⬇️ 785.9k / ⬆️ 1.0k · opus-5 · max · 18сЫ qcom-sm7150:~$ grep VIDEO_DW9768 ~/linux-7.2/.config CONFIG_VIDEO_DW9768=m qcom-sm7150:~$ ls -l /lib/modules/$(uname -r)/kernel/drivers/media/i2c/dw9768.ko* ls: /lib/modules/7.1.0-sm7150/kernel/drivers/media/i2c/dw9768.ko*: No such file or directory qcom-sm7150:~$ sudo modprobe dw9768; sleep 1; sudo dmesg | tail -5 modprobe: FATAL: Module dw9768 not found in directory /lib/modules/7.1.0-sm7150 [ 43.513522] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 44.515100] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 45.205696] calling rmnet_init+0x0/0xff8 [rmnet] @ 1952 [ 45.205747] initcall rmnet_init+0x0/0xff8 [rmnet] returned 0 after 20 usecs [ 45.516591] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened qcom-sm7150:~$ media-ctl -d /dev/media0 -p | grep -E "imx586|dw9768" - entity 171: imx586 12-001a (1 pad, 0 link, 0 routes) после make modules_install qcom-sm7150:~/linux-7.2$ sudo modprobe dw9768; sleep 1; sudo dmesg | tail -5 [ 45.205747] initcall rmnet_init+0x0/0xff8 [rmnet] returned 0 after 20 usecs [ 45.516591] qcom_q6v5_pas 62400000.remoteproc: Handover signaled, but it already happened [ 190.439959] calling dw9768_i2c_driver_init+0x0/0xff8 [dw9768] @ 9541 [ 190.442923] probe of 12-000c returned 0 after 2828 usecs [ 190.442968] initcall dw9768_i2c_driver_init+0x0/0xff8 [dw9768] returned 0 after 2980 usecs qcom-sm7150:~/linux-7.2$ media-ctl -d /dev/media0 -p | grep -E "imx586|dw9768" <- "imx586 12-001a":0 [ENABLED,IMMUTABLE] - entity 171: imx586 12-001a (1 pad, 1 link, 0 routes) - entity 175: dw9768 12-000c (0 pad, 0 link, 0 routes)S Привод привязался на 12-000c — то есть адрес 0x0c, ровно тот, что я вывел чтением. И сенсор встал в граф. (Мой install_ko промахнулся мимо пути — там ${src#*/} откусывает первый каталог, получалось media/i2c/ вместо drivers/media/i2c/. make modules_install это обошёл, дальше поправлю.) Теперь первый кадр вендорными таблицами, с бинированного режима: cd ~/lanekit sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh sh capture.sh Ожидаемый размер файла — 15 000 000 байт ровно (4000 точек по 10 бит это 5000 байт на строку, умножить на 3000). Если пойдёт, сразу посмотрим на картинку: ./raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 convert /tmp/preview.ppm /tmp/preview.png И заодно проверим наводку — она теперь должна работать: D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d $D --list-ctrls Там должен быть focus_absolute от 0 до 1023. 💰 19.54₽ · ⬇️ 787.9k / ⬆️ 838 · opus-5 · max · 18сЫ S Тогда смотрим, где встало. Включим отчёт приёмника и повторим: echo 1 | sudo tee /sys/module/qcom_camss/parameters/debug_rx_irq sudo dmesg -C sh capture.sh for p in rx_irq_seen rdi0_bytes_ping rdi0_bytes_pong rdi0_sof_ts; do printf '%s = %s\n' $p "$(cat /sys/module/qcom_camss/parameters/$p)" done sudo dmesg | grep -vE "link_setup|got IRQ" | tail -15 Мне нужны три вещи: строка csiphy: link ... settle count ... (для бинированного режима линк должен быть 572400000), значение rx_irq_seen — я его расшифрую по битам, и не появилось ли новых жалоб от camss. По значению rx_irq_seen сразу будет видно, что происходит: нули означают, что сенсор молчит и дело в таблицах, а биты начала и конца передачи с ошибками — что данные идут, но разбираются неверно. 💰 19.29₽ · ⬇️ 788.8k / ⬆️ 1.0k · opus-5 · max · 18сЫ qcom-sm7150:~/lanekit$ sh capture.sh Terminated -rw-r--r-- 1 user user 0 Aug 1 17:09 /tmp/frame.raw qcom-sm7150:~/lanekit$ for p in rx_irq_seen rdi0_bytes_ping rdi0_bytes_pong rdi0_sof_ts; do > printf '%s = %s\n' $p "$(cat /sys/module/qcom_camss/parameters/$p)" > done rx_irq_seen = 229376 rdi0_bytes_ping = 0 rdi0_bytes_pong = 0 rdi0_sof_ts = 0 qcom-sm7150:~/lanekit$ sudo dmesg | grep -vE "link_setup|got IRQ" | tail -15 [ 311.156144] qcom-camss ace0000.camss: validating stream "msm_csid0":1:0 -> "msm_vfe0_rdi0":0:0 [ 311.156159] qcom-camss ace0000.camss: validating link "msm_csiphy0":1 -> "msm_csid0":0 [ 311.156167] qcom-camss ace0000.camss: validating stream "msm_csiphy0":1:0 -> "msm_csid0":0:0 [ 311.156180] qcom-camss ace0000.camss: validating link "imx586 12-001a":0 -> "msm_csiphy0":0 [ 311.156187] qcom-camss ace0000.camss: validating stream "imx586 12-001a":0:0 -> "msm_csiphy0":0:0 [ 311.156203] qcom-camss ace0000.camss: msm_vfe0_video0: format is (4000x3000 pRAA little-endian (0x41415270)/1p field:1), trying (4000x3000 pRAA little-endian (0x41415270)/1p field:1) [ 311.156226] vfe_reserve_wm ret=0 [ 311.156254] qcom-camss ace0000.camss: csiphy: link 572400000 Hz, timer 300000000 Hz, settle count 21 [ 311.156342] qcom-camss ace0000.camss: csiphy: settle high bytes 00 00 00 00 ff [ 311.156352] qcom-camss ace0000.camss: enable streams "imx586 12-001a":0/0x1 [ 311.156363] imx586 12-001a: collect_streams: sub-device "imx586 12-001a" does not support streams [ 331.144798] Writing reset_bits=3f9f to vfe->base=0000000024f51bae with id 0 [ 331.144866] vfe irq handled! [ 331.144958] qcom-camss ace0000.camss: disable streams "imx586 12-001a":0/0x1 [ 331.144984] imx586 12-001a: collect_streams: sub-device "imx586 12-001a" does not support streamsS rx_irq_seen = 0x38000 — это переполнение приёмных очередей на линиях 0, 1 и 2, при полном отсутствии признаков начала и конца передачи. То есть на провода что-то льётся, но приёмник не может поймать начало пакета. Проверяю извлечённую таблицу бинированного режима — не сползла ли она: Таблица бинированного режима извлеклась правильно — там всё согласовано: длина строки 7872, кадра 3054, бинирование 2×2, вырезка 4000×3000, выход 4000×3000, коррекция чувствительности выключена (как и положено для бинированного). Значит дело не в ней. Проверим три вещи разом: sudo cat /sys/kernel/debug/clk/camcc_mclk2_clk/clk_rate sed -i 's/^W=4000/W=8000/; s/^H=3000/H=6000/' capture.sh sh capture.sh cat /sys/module/qcom_camss/parameters/rx_irq_seen Первое — реальная опорная частота: она должна быть 19200000. Если там 24000000, то сенсор считает свой PLL от другой частоты, и всё поедет. Второе и третье — полный кадр вендорной таблицей. Её извлечение я проверял подробнее всего (область считывания, размеры, длины совпали до бита), так что если полный кадр пойдёт, а бинированный нет — виновата именно вторая таблица. Если не пойдёт и он — причина общая, и я буду смотреть начальную последовательность. И сразу после неудачной попытки прочитаем, что реально записалось в сенсор: sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x00 r1 # 0x0100, идёт ли поток sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x14 r1 # 0x0114, число линий sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x36 r1 # 0x0136, опорная частота 💰 43.64₽ · ⬇️ 1.58M / ⬆️ 5.1k · opus-5 · max · 79сЫ sudo cat /sys/kernel/debug/clk/camcc_mclk2_clk/clk_rate 19200000 qcom-sm7150:~/lanekit$ sed -i 's/^W=4000/W=8000/; s/^H=3000/H=6000/' capture.sh qcom-sm7150:~/lanekit$ sh capture.sh Terminated -rw-r--r-- 1 user user 0 Aug 1 17:11 /tmp/frame.raw qcom-sm7150:~/lanekit$ cat /sys/module/qcom_camss/parameters/rx_irq_seen 229376S Опорная частота верна, а оба режима падают одинаково — значит виновата общая часть, то есть начальная последовательность. Проверяю её сверкой с таблицей MediaTek: у одного семейства сенсоров общие настройки должны в основном совпадать. Начальная последовательность извлечена верно: из 91 общего регистра с таблицей MediaTek совпали 83, а все восемь расхождений осмысленные — опорная частота (19.2 против 24), версия регистров и несколько значений в области 0x9xxx, которые у вариантов сенсора и должны отличаться. Будь смещение сбито, разошлось бы всё подряд, а не четыре пары. Значит надо смотреть не таблицы, а сам сенсор: идёт ли у него счётчик кадров. Запусти съёмку в фоне и прочитай его дважды: sh capture.sh > /tmp/cap.log 2>&1 & sleep 4 echo "счётчик кадров:" sudo i2ctransfer -f -y 12 w2@0x1a 0x00 0x05 r1; sleep 1 sudo i2ctransfer -f -y 12 w2@0x1a 0x00 0x05 r1 echo "поток / линии / опорная:" sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x00 r1 sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x14 r1 sudo i2ctransfer -f -y 12 w2@0x1a 0x01 0x36 r1 wait Разные значения счётчика — сенсор выдаёт кадры, и виноват приём. Одинаковые при 0x0100 = 0x01 — сенсор считает, что стримит, но матрица стоит: тогда в таблице режима не хватает чего-то, что вендор пишет отдельно от неё, и я пойду искать это в остальных записях файла (там кроме шести режимов и начальной последовательности было ещё несколько мелких групп, которые я не разбирал). 💰 41.67₽ · ⬇️ 1.60M / ⬆️ 3.2k · opus-5 · max · 52сЫ Ы S Сенсор работает: счётчик кадров идёт (0x27 → 0x38 за секунду, это около семнадцати кадров), поток включён, четыре линии, опорная 19.2 МГц. Значит виноват приём, и дело в тех записях файла, которые я не разобрал — там было ещё несколько мелких групп между режимами и начальной последовательностью: Появились ответы: записи 666–669 — это команды пуска и остановки (0x0100) и удержания параметров (0x0104), а 670–685 — отдельная группа настроек, которую CAMX пишет сверх таблиц. Но главное подозрение другое. Сравню, что писала работавшая таблица MediaTek и не пишет вендорная — там может оказаться настройка передатчика, которую CAMX делает не из этого файла: Есть кандидат. Между режимами и начальной последовательностью лежит группа из шестнадцати регистров, которую CAMX пишет сверх таблиц, а мы её не пишем вовсе: 0x0350=00 0x510C=01 0x510D=02 0x3F71=01 0x4B82=03 0x4B83=01 0x3041=01 0x424A=00 0x424B=00 0x423E=FF 0x423D=FF 0x4B48=00 0x4B49=15 0x4225=00 0x4226=00 0x3040=01 Пара 0x3040/0x3041 у сенсоров этого семейства отвечает за ручную установку временных параметров передатчика — а именно от них зависит, поймает ли приёмник начало пакета. Симптом «данные льются, начала нет» ложится на это идеально. Добавляю группу в драйвер: Перезагрузка не нужна, только модуль: cp ~/imx586.c ~/linux-7.2/drivers/media/i2c/imx586.c cd ~/linux-7.2 && make -j8 modules sudo make modules_install sudo pkill -f wireplumber; sudo pkill -f pipewire sudo modprobe -r qcom_camss; sudo modprobe -r imx586 sudo modprobe imx586; sudo modprobe qcom_camss sleep 2 cd ~/lanekit && sh capture.sh cat /sys/module/qcom_camss/parameters/rx_irq_seen (путь к скачанному imx586.c поправь под то, куда он лёг) Что смотреть: если в rx_irq_seen появятся младшие биты — начало и конец передачи на линиях — значит приёмник наконец поймал поток, и дальше уже вопрос только ошибок. Если останется голое переполнение очередей 0x38000, то у меня следующий подозреваемый — частота такта самого CSIPHY: mainline выбирает 300 МГц, а вендор на этом режиме ставит 384, и запас в четырнадцать процентов может быть недостаточным. Это правится одной строкой в таблице тактов и тоже без перезагрузки. 💰 124₽ · ⬇️ 4.85M / ⬆️ 8.8k · opus-5 · max · 155сЫ S Справедливо. Я и правда сказал «прочитал всё», а потом нашёл в том же файле непрочитанные группы. Разбираю его целиком, по описанию полей: Вот полный перечень. Нечитанного много: streamConfiguration на каждый режим, i2cInfo, powerUpSequence, calibrationInfo, EEPROMName, а также masterSettings, initSettings, deInitSettings. Начинаю с описания потоков — там у CAMX лежит время установления: Описание потока подтверждает извлечённые числа: длина строки 9184, кадра 6058, тип данных 0x2B (RAW10), поток точек 839 520 000 — ровно то, что я считал, — и частота кадров 15.32. А вот важное: полей regSetting в файле шестнадцать, а групп записей я нашёл тринадцать. Значит мой разбор пропускает записи — скорее всего те, где данные не однобайтовые. Проверяю: Теперь видны все девятнадцать блоков, и среди них те, что я пропускал: записи с двухбайтовыми данными по адресу 0x0003 и группа 0x0002…0x0007. Это уже не сенсор — это привод объектива, у DW9763 как раз 0x03 это старший байт ЦАП, а 0x02 — управление режимом. Достаю: Достал последовательность привода — она отвечает на вопрос, который я раньше закрывал догадкой: 0x02 = 0x01, затем 0x00, затем 0x02 (выключение спящего режима, включение плавного подвода) 0x06 = 0x60 → режим подвода 3, делитель 0 0x07 = 0x02 → выдержка подвода 2 Я в дереве поставил режим 2, делитель 1, выдержку 0x20 — по умолчанию из привязки. Вендорные значения другие, поправлю. А теперь важное про ту группу из шестнадцати регистров, которую я добавил в драйвер: надо убедиться, что это не настройки ведущий-ведомый для синхронизации двух камер, иначе я мог сделать хуже. Смотрю, какому полю описания принадлежит каждый блок: Разметка приблизительная, но у описания есть девять полей name — это, скорее всего, и есть подписи групп. Читаю их: Это не подписи групп, а список ключей вспомогательных сведений (dualCamOffset, moduleId, fuseIdOffset и путь к калибровке двух камер в /data). Значит подписей нет, и природу той группы надо выяснять по самим регистрам. Проверяю, встречаются ли они у MediaTek: Проверил: из этих регистров MediaTek знает только 0x0350, и то на чтение. Остальные — из областей фазового автофокуса и статистики, то есть та группа, скорее всего, не про физику передачи, и моя догадка про ручные тайминги была неверной. Она не мешает, но и не поможет. Зато чтение кода приёмника даёт настоящего кандидата. Вот как mainline выбирает частоту такта для CSID: } else if (clock->nfreqs) { clk_set_rate(clock->clk, clock->freq[0]); /* всегда первый элемент */ } а в таблице 7150 первым стоит 300 МГц: .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, .clock_rate = { { 300000000, 384000000, 400000000 }, ... Вендор же выбирает ступень по нагрузке и на полном кадре берёт 400. А поток у нас 2099 Мбит/с на линию × 4 линии = 8.4 Гбит/с, то есть 1.05 ГБ/с — при 300 МГц запас всего четырнадцать процентов. Переполнение приёмных очередей при отсутствии начала пакета выглядит ровно так. Правка местная, без перезагрузки: cd ~/linux-7.2 sed -i 's/\.clock_rate = { { 300000000, 384000000, 400000000 },\n\t\t\t\t{ 0 },/&/' drivers/media/platform/qcom/camss/camss.c python3 - <<'EOF' import re p='drivers/media/platform/qcom/camss/camss.c' s=open(p).read() i=s.index('static const struct camss_subdev_resources csid_res_7150[]') j=s.index('\n};', i) blk=s[i:j].replace('{ 300000000, 384000000, 400000000 }','{ 400000000, 384000000, 300000000 }') s=s[:i]+blk+s[j:] open(p,'w').write(s) print("заменено вхождений:", blk.count('400000000, 384000000, 300000000')) EOF make -j8 modules && sudo make modules_install sudo pkill -f wireplumber; sudo pkill -f pipewire sudo modprobe -r qcom_camss && sudo modprobe qcom_camss cd ~/lanekit && sh capture.shЫ S Обе записи (CSID0 и CSID1) поправились. Дальше по тому же блоку — сборка, замена модуля и съёмка: make -j8 modules && sudo make modules_install sudo pkill -f wireplumber; sudo pkill -f pipewire sudo modprobe -r qcom_camss && sudo modprobe qcom_camss cd ~/lanekit && sh capture.sh cat /sys/module/qcom_camss/parameters/rx_irq_seen И заодно поправлю собственную оплошность в измерителе: rx_irq_seen обнуляется в той же функции, которая вызывается и при остановке потока, поэтому значение, которое мы читаем, относится к разбору конца, а не к самой съёмке. Так что если кадр опять не пойдёт, я перенесу обнуление в начало и цифра станет честной. Сейчас смотрим только на одно: появился ли файл в 60 миллионов байт. 💰 20.93₽ · ⬇️ 831.4k / ⬆️ 483 · opus-5 · max · 12сЫ S Значит дело не в такте CSID. Проверим, что сенсор реально настроен так, как мы думаем — прочитаем у него ключевые регистры прямо во время съёмки: sh capture.sh > /dev/null 2>&1 & sleep 4 for r in 0112 0114 0342 0343 0340 0341 034C 034D 034E 034F 0301 0303 0305 0306 0307 030D 030E 030F 0100; do hi=$(( 0x$r >> 8 )); lo=$(( 0x$r & 0xff )) printf "0x%s = " $r sudo i2ctransfer -f -y 12 w2@0x1a $hi $lo r1 done wait Сверю с вендорной таблицей: там должно быть 0x0112=0A, 0x0114=03, длина строки 23E0, кадра 17AA, выход 1F40×1770, и множители PLL 05 02 03 01 4D и 10 06 D5. Если что-то из этого не совпадёт — значит часть записей не доехала (у Sony часть регистров принимается только в дежурном режиме, до пуска потока), и это объяснит всё: сенсор гонит кадры со старыми настройками, а приёмник ждёт другие. А если совпадёт всё — то сенсор настроен верно, гонит верный поток, и остаётся единственное место, которое мы ещё не смотрели: сама физика приёма на скорости 2099 Мбит/с. Тогда я перенесу проверку на бинированный режим, где скорость вдвое ниже, и мы разделим «не тянет скорость» и «не ловит вообще». 💰 22.33₽ · ⬇️ 831.9k / ⬆️ 3.0k · opus-5 · max · 50сЫ S Все регистры совпали с вендорной таблицей до последнего бита, и поток включён. Значит сенсор настроен правильно и гонит именно то, что мы думаем. Тогда проверяем скорость. Бинированный режим идёт на 1145 Мбит/с на линию — почти ровно та скорость, на которой у нас кадр уже снимался (1248): cd ~/lanekit sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh sh capture.sh Если он пойдёт, а полный кадр нет — значит приём не тянет 2099 Мбит/с, и дальше я буду смотреть настройки физики для высоких скоростей (там у вендора в таблице CSIPHY есть отдельные значения, которые mainline пишет одинаковыми для всех линий — я про них писал, но проверить тогда не удалось). Если и бинированный не пойдёт — значит сломалось что-то общее при переходе на новое дерево, и я буду сравнивать текущее состояние приёмника с тем, что было при удачной съёмке: там у нас в логе оставались и частота линка, и время установления, и маска линий. 💰 21.02₽ · ⬇️ 835.2k / ⬆️ 752 · opus-5 · max · 18сЫ S Тогда сломалось что-то общее, и гадать больше не буду — добавлю печать того, что реально уходит в железо: маску линий у CSIPHY и число с раскладкой у CSID. sh ~/patch-trace.sh ~/linux-7.2/drivers/media/platform/qcom/camss cd ~/linux-7.2 && make -j8 modules && sudo make modules_install sudo pkill -f wireplumber; sudo pkill -f pipewire sudo modprobe -r qcom_camss && sudo modprobe qcom_camss cd ~/lanekit && sudo dmesg -C && sh capture.sh sudo dmesg | grep -E "csiphy:|receiver:|camss" | head -12 Жду две строки: csiphy: lane mask 0xd5 from 4 lanes receiver: lanes 4, assignment 0x3210, phy 0, cfg0 0x... Маска 0xd5 — это такт плюс линии 0…3, раскладка 0x3210 — та самая вендорная. Если увидим что-то другое, значит дерево прочиталось не так, как я думаю, и виновата моя же правка эндпоинта. Если совпадёт — конфигурация приёмника верна, и остаётся физика: тогда включим вендорную таблицу настроек линий, которую я перенёс в драйвер ещё тогда и которую мы так и не проверили: echo 1 | sudo tee /sys/module/qcom_camss/parameters/vendor_lane_regs sh capture.sh 💰 61.66₽ · ⬇️ 2.51M / ⬆️ 3.0k · opus-5 · max · 54сЫ Terminated -rw-r--r-- 1 user user 0 Aug 1 17:31 /tmp/frame.raw [ 1650.880966] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 1650.882027] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x0 [ 1650.882041] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 1651.251253] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x0 [ 1651.295233] qcom-camss ace0000.camss: csid_link_setup: Enabled CSID virtual channels mask 0x1 [ 1651.482528] qcom-camss ace0000.camss: CSIPHY 3PH HW Version = 0x00000000 [ 1651.482864] qcom-camss ace0000.camss: VFE:0 HW Version = 1.3.0 [ 1651.482952] qcom-camss ace0000.camss: CSID:0 HW Version = 2.0.0 [ 1651.482975] qcom-camss ace0000.camss: validating link "msm_csid0":1 -> "msm_vfe0_rdi0":0 [ 1651.482982] qcom-camss ace0000.camss: validating stream "msm_csid0":1:0 -> "msm_vfe0_rdi0":0:0 [ 1651.482991] qcom-camss ace0000.camss: validating link "msm_csiphy0":1 -> "msm_csid0":0 [ 1651.482995] qcom-camss ace0000.camss: validating stream "msm_csiphy0":1:0 -> "msm_csid0":0:0S Вывод обрезан на двенадцатой строке (head -12), а всё интересное идёт дальше. Покажи хвост: sudo dmesg | grep -E "csiphy:|receiver:|enable streams|format is|reserve_wm|Failed" | tail -12 Обрати внимание на одну строку, которая уже видна: CSIPHY 3PH HW Version = 0x00000000. Если этот регистр читается нулём, значит блок физики в момент чтения не запитан или не тактируется — а это уже прямая причина всего остального. Проверь заодно, что там сейчас с тактами: sudo grep -E "csiphy0|cphy_rx|csi0phytimer" /sys/kernel/debug/clk/clk_summary Нужны строки camcc_csiphy0_clk и camcc_cphy_rx_clk_src: важно, включены ли они (счётчик потребителей) и на какой частоте. 💰 20.78₽ · ⬇️ 840.3k / ⬆️ 1.2k · opus-5 · max · 22сЫ [ 1651.483036] qcom-camss ace0000.camss: msm_vfe0_video0: format is (4000x3000 pRAA little-endian (0x41415270)/1p field:1), trying (4000x3000 pRAA little-endian (0x41415270)/1p field:1) [ 1651.483052] vfe_reserve_wm ret=0 [ 1651.483074] qcom-camss ace0000.camss: receiver: lanes 4, assignment 0x4321, phy 0, cfg0 0x00043213 [ 1651.483082] qcom-camss ace0000.camss: csiphy: link 572400000 Hz, timer 300000000 Hz, settle count 21 [ 1651.483088] qcom-camss ace0000.camss: csiphy: lane mask 0xd4 from 4 lanes [ 1651.483170] qcom-camss ace0000.camss: csiphy: settle high bytes 00 00 00 00 ff [ 1651.483176] qcom-camss ace0000.camss: enable streams "imx586 12-001a":0/0x1 sudo grep -E "csiphy0|cphy_rx|csi0phytimer" /sys/kernel/debug/clk/clk_summary camcc_csi0phytimer_clk_src 0 0 0 300000000 0 0 50000 N ace0000.camss csiphy0_timer_src camcc_csi0phytimer_clk 0 0 0 300000000 0 0 50000 N ace0000.camss csiphy0_timer camcc_cphy_rx_clk_src 0 0 0 300000000 0 0 50000 N ace0000.camss cphy_rx_src ace0000.camss cphy_rx_src camcc_ife_lite_cphy_rx_clk 0 0 0 300000000 0 0 50000 N deviceless no_connection_id camcc_ife_1_cphy_rx_clk 0 0 0 300000000 0 0 50000 N ace0000.camss vfe1_cphy_rx camcc_ife_0_cphy_rx_clk 0 0 0 300000000 0 0 50000 N ace0000.camss vfe0_cphy_rx ace0000.camss vfe0_cphy_rx camcc_csiphy0_clk 0 0 0 300000000 0 0 50000 N ace0000.camss csiphy0 qcom-sm7150:~/linuS Вот оно, наконец: receiver: lanes 4, assignment 0x4321 csiphy: lane mask 0xd4 from 4 lanes Раскладка 0x4321 — это позиции 1, 2, 3, 4, а не 0…3. Маска 0xd4 соответствует линиям 1, 2, 3, а четвёртая (позиция 4) вылезла за пределы байта и потерялась. То есть приёмник слушает не те провода — ровно та ошибка, которую мы нашли измерением и, как я думал, исправили в дереве. Проверяю, что я на самом деле отправил: Файл, который я отправил, содержит правильное: со стороны camss <0 1 2 3>, со стороны сенсора <1 2 3 4>. А ядро видит на стороне camss именно сенсорные значения. Значит в загруженном дереве не то, что мы думаем. Посмотрим прямо в него: D=/sys/firmware/devicetree/base xxd $D/soc@0/camss@ace0000/ports/port@0/endpoint/data-lanes xxd $D/soc@0/cci@ac4a000/i2c-bus@0/camera@1a/port/endpoint/data-lanes grep -c "data-lanes" $T/sm7150-xiaomi-davinci.dtsi grep -n "data-lanes" $T/sm7150-xiaomi-davinci.dtsi Первые две команды покажут, что реально загружено (00000000 00000001 00000002 00000003 против 00000001 00000002 00000003 00000004). Последние две — сколько объявлений линий осталось в исходнике: их должно быть ровно три (наша сторона camss, сторона сенсора и телевик). Если больше — значит от старой вставки что-то уцелело и перекрывает нашу. 💰 44.08₽ · ⬇️ 1.69M / ⬆️ 3.9k · opus-5 · max · 66сЫ qcom-sm7150:~/linux-7.2$ D=/sys/firmware/devicetree/base qcom-sm7150:~/linux-7.2$ xxd $D/soc@0/camss@ace0000/ports/port@0/endpoint/data-lanes 00000000: 0000 0000 0000 0001 0000 0002 0000 0003 ................ qcom-sm7150:~/linux-7.2$ xxd $D/soc@0/cci@ac4a000/i2c-bus@0/camera@1a/port/endpoint/data-lanes 00000000: 0000 0001 0000 0002 0000 0003 0000 0004 ................ qcom-sm7150:~/linux-7.2$ grep -c "data-lanes" $T/sm7150-xiaomi-davinci.dtsi grep: /sm7150-xiaomi-davinci.dtsi: No such file or directory qcom-sm7150:~/linux-7.2$ grep -n "data-lanes" $T/sm7150-xiaomi-davinci.dtsi grep: /sm7150-xiaomi-davinci.dtsi: No such file or directoryS Дерево в памяти правильное: у camss 0,1,2,3, у сенсора 1,2,3,4. А драйвер сообщает 0x4321. Значит он читает не тот эндпоинт. Смотрю, что делает разбор в этом форке: Разбор берёт эндпоинты самого camss — значит должен читать 0,1,2,3. Проверим, не перебивает ли их наша же отладочная ручка: cat /sys/module/qcom_camss/parameters/lane_base cat /sys/module/qcom_camss/parameters/lane_count grep -n "lane_base" -A4 ~/linux-7.2/drivers/media/platform/qcom/camss/camss.c | head -24 Подозрение такое: lane_base должен быть -1 (то есть «брать из дерева»), а если там 1, то каждая линия сдвигается на единицу — и мы получаем ровно 0x4321 вместо 0x3210 и маску 0xd4 вместо 0xd5. Это в точности то, что видно в логе. Если так — виноват мой же патч: скорее всего, вставка попала в файл дважды или значение по умолчанию оказалось не тем. Тогда исправление на одну строку и без перезагрузки. 💰 101₽ · ⬇️ 4.24M / ⬆️ 2.8k · opus-5 · max · 61сЫ qcom-sm7150:~/linux-7.2$ cat /sys/module/qcom_camss/parameters/lane_base -1 qcom-sm7150:~/linux-7.2$ cat /sys/module/qcom_camss/parameters/lane_count 0 qcom-sm7150:~/linux-7.2$ grep -n "lane_base" -A4 ~/linux-7.2/drivers/media/platform/qcom/camss/camss.c | head -24 32:static int lane_base = -1; 33:module_param(lane_base, int, 0644); 34:MODULE_PARM_DESC(lane_base, 35- "debug: renumber CSI-2 data lane positions starting at this one"); 36- 37-static int lane_count; 38-module_param(lane_count, int, 0644); -- 5007: if (lane_base >= 0) 5008: lncfg->data[i].pos = lane_base + i; 5009- else 5010- lncfg->data[i].pos = mipi_csi2->data_lanes[i]; 5011- lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; 5012- }S Ручки в исходном положении и код верный — значит разбор читает из дерева именно 1,2,3,4. Но в загруженном дереве у camss стоит 0,1,2,3. Такое возможно, если наш блок попал в исходники дважды, в разные файлы, и в dtb собралась не та копия. Проверь по всем файлам davinci: grep -rn "data-lanes" ~/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci* grep -rln "camera@1a" ~/linux-7.2/arch/arm64/boot/dts/qcom/ ls -la /boot/dtbs/*/qcom/ 2>/dev/null | grep -i davinci Первое покажет все объявления линий и в каком файле. Второе — не осталось ли второй копии узла сенсора. Третье — какие вообще файлы дерева установлены: у davinci их два, под разные матрицы, и если загрузчик берёт не тот, что мы пересобрали, всё сходится. И заодно поставлю печать прямо в разбор, чтобы видеть, что он вычитал: python3 - <<'EOF' p='/home/user/linux-7.2/drivers/media/platform/qcom/camss/camss.c' s=open(p).read() a=""" csd->interface.csiphy_id = vep.base.port;""" if 'endpoint of port' not in s: s=s.replace(a, a+""" dev_info(dev, "endpoint of port %u: %u lanes, positions %u %u %u %u\\n", vep.base.port, mipi_csi2->num_data_lanes, mipi_csi2->data_lanes[0], mipi_csi2->data_lanes[1], mipi_csi2->data_lanes[2], mipi_csi2->data_lanes[3]);""") open(p,'w').write(s) print("печать добавлена") EOF Только учти: строка с mipi_csi2 объявлена ниже по коду, поэтому если сборка ругнётся — просто перенеси печать на несколько строк ниже, после mipi_csi2 = &vep.bus.mipi_csi2;. 💰 23.65₽ · ⬇️ 851.4k / ⬆️ 3.4k · opus-5 · max · 49сЫ S Исходники и загруженное дерево правильные: у camss <0 1 2 3> (строка 704), у сенсора <1 2 3 4> (745), у телевика <0 1> (714). А разбор при этом выдаёт позиции 1…4. Значит он читает не тот узел, и надо это увидеть. Ставим печать с именем узла: python3 - <<'EOF' p='/home/user/linux-7.2/drivers/media/platform/qcom/camss/camss.c' s=open(p).read() a="\tlncfg->clk.pos = mipi_csi2->clock_lane;" if 'parsed endpoint' not in s: s=s.replace(a, """ dev_info(dev, "parsed endpoint %pfw of port %u: %u lanes, first %u\\n", ep, vep.base.port, mipi_csi2->num_data_lanes, mipi_csi2->data_lanes[0]); """+a) open(p,'w').write(s) print("печать добавлена") EOF cd ~/linux-7.2 && make -j8 modules && sudo make modules_install sudo modprobe -r qcom_camss && sudo modprobe qcom_camss sudo dmesg | grep "parsed endpoint" %pfw печатает полный путь узла в дереве, так что сразу станет видно, чей эндпоинт попал в разбор: наш camss/ports/port@0/endpoint или сенсорный. И заодно проверь, нет ли под портом второго узла: ls /sys/firmware/devicetree/base/soc@0/camss@ace0000/ports/port@0/ 💰 24.63₽ · ⬇️ 857.9k / ⬆️ 2.3k · opus-5 · max · 36сЫ S Кто-то держит модуль — почти наверняка недобитый v4l2-ctl от прошлой съёмки: sudo pkill -9 -f v4l2-ctl; sudo pkill -9 -f wireplumber; sudo pkill -9 -f pipewire sleep 1 sudo modprobe -r qcom_camss && sudo modprobe qcom_camss sudo dmesg | grep "parsed endpoint" ls /sys/firmware/devicetree/base/soc@0/camss@ace0000/ports/port@0/ Если и после этого не выгрузится — покажи lsmod | grep camss, посмотрим счётчик ссылок. 💰 20.85₽ · ⬇️ 860.2k / ⬆️ 429 · opus-5 · max · 9сЫ qcom-sm7150:~/linux-7.2$ sudo pkill -9 -f v4l2-ctl; sudo pkill -9 -f wireplumber; sudo pkill -9 -f pipewire Killed Killed Killed qcom-sm7150:~/linux-7.2$ sudo modprobe -r qcom_camss && sudo modprobe qcom_camss modprobe: FATAL: Module qcom_camss is in use. qcom-sm7150:~/linux-7.2$ qcom-sm7150:~/linux-7.2$S Терминал заодно поломался от pkill — он прибил и часть собственного вывода. Проще перезагрузиться, тем более модуль уже установлен и подхватится сам: sync && sudo reboot После загрузки: sudo dmesg | grep -E "parsed endpoint|receiver:|lane mask" ls /sys/firmware/devicetree/base/soc@0/camss@ace0000/ports/port@0/ Строка parsed endpoint ... покажет полный путь узла, который попал в разбор, и число линий с первой позицией. Тогда станет ясно, читает ли драйвер наш эндпоинт camss или сенсорный — а это уже последняя неизвестная в этой цепочке. 💰 20.18₽ · ⬇️ 860.9k / ⬆️ 453 · opus-5 · max · 11сЫ [ 12.601596] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@0/endpoint of port 0: 4 lanes, first 1 [ 12.601606] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@3/endpoint of port 3: 2 lanes, first 1 ls /sys/firmware/devicetree/base/soc@0/camss@ace0000/ports/port@0/ endpoint name regS Вот и разгадка: разбор читает правильный узел (camss/ports/port@0/endpoint), но выдаёт первую позицию 1, хотя в дереве там 0. Значит преобразует их сама v4l2-fwnode. Читаю её: Нашёл. Виноват мой же файл дерева. Вот код разбора: if (have_clk_lane && lanes_used & BIT(clock_lane) && !use_default_lane_mapping) { pr_warn("duplicated lane %u in clock-lanes, using defaults\n", v); use_default_lane_mapping = true; } У нас в эндпоинте стояло clock-lanes = <0> и data-lanes = <0 1 2 3>. Нулевая позиция занята и тактом, и линией данных — разбор считает это противоречием, молча выбрасывает нашу раскладку и берёт стандартную, а она как раз 1, 2, 3, 4. Отсюда и 0x4321, и маска 0xd4. Именно поэтому раньше всё работало через ручки модуля: lane_base=0 обходил разбор стороной. Проверь, что предупреждение есть: sudo dmesg | grep -i "duplicated lane" И правка — убрать clock-lanes из наших эндпоинтов, а у телевика ещё и удалить унаследованный: F=~/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi python3 - <<'EOF' p='/home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi' s=open(p).read() head, tail = s[:s.index('// SPDX-License-Identifier: BSD-3-Clause\n/*\n * Cameras of the Xiaomi')], s[s.index('// SPDX-License-Identifier: BSD-3-Clause\n/*\n * Cameras of the Xiaomi'):] # в нашем блоке убираем такт-линию: она конфликтует с линией данных 0 tail = tail.replace("\t\t\t\tclock-lanes = <0>;\n", "") # у телевика она унаследована из верхней части файла, поэтому удаляем явно tail = tail.replace("""&csiphy3_ep { data-lanes = <0 1>; };""", """&csiphy3_ep { /delete-property/ clock-lanes; data-lanes = <0 1>; };""") open(p,'w').write(head+tail) print("осталось clock-lanes в нашем блоке:", tail.count('clock-lanes')) EOF cd ~/linux-7.2 && make -j8 dtbs && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs sync && sudo reboot После загрузки строка parsed endpoint ... first 0 и маска 0xd5 — и снимаем кадр. 💰 43.59₽ · ⬇️ 1.73M / ⬆️ 3.3k · opus-5 · max · 49сЫ S Предупреждение подтвердило диагноз слово в слово — оно было там всё это время, просто мы на него не смотрели. После загрузки: sudo dmesg | grep -E "parsed endpoint|duplicated lane" cd ~/lanekit && sh capture.sh Жду first 0 и полное отсутствие строки про дублирование. Дальше файл на 15 000 000 байт — сейчас в capture.sh стоит бинированный режим 4000×3000. Если пойдёт, сразу и полный кадр проверим, и наводку. А заодно снимем ту заплату с ручками lane_base/lane_count из camss — она больше не нужна, дерево теперь описывает всё правильно само. 💰 21.29₽ · ⬇️ 866.4k / ⬆️ 486 · opus-5 · max · 13сЫ sudo dmesg | grep -E "parsed endpoint|duplicated lane" [sudo: authenticate] Password: [ 12.081684] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@0/endpoint of port 0: 4 lanes, first 0 [ 12.083753] duplicated lane 0 in clock-lanes, using defaults [ 12.086863] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@3/endpoint of port 3: 2 lanes, first 1S Ы S Кадр снят. 15 024 000 байт — это 4000×3000 с выравниванием строки до 5008 байт, ровно как должно быть. Смотрим картинку и пробуем остальное: ./raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 convert /tmp/preview.ppm /tmp/preview.png sed -i 's/^W=4000/W=8000/; s/^H=3000/H=6000/' capture.sh sh capture.sh # полный кадр, 2099 Мбит/с на линию D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --list-ctrls Три вопроса, на которые это ответит: нормальная ли картинка в бинированном режиме, тянет ли плата полную скорость (60 000 000 байт), и виден ли орган наводки. Если полный кадр не пойдёт — не страшно, у нас есть рабочий 4000×3000 на 30 кадрах, а разница по скорости линии там почти вдвое. 💰 20.73₽ · ⬇️ 868.5k / ⬆️ 700 · opus-5 · max · 13сЫ S Полный кадр, значит, доезжает, но рвётся — полосы это и есть потерянные пакеты. Ровно то, чего я опасался: 2099 Мбит/с на линию для этого приёма многовато. И наводка на месте: focus_absolute от 0 до 1023. Два лечения полного кадра, оба без перезагрузки. Первое — поднять такт самой физики: mainline берёт первую подходящую частоту из списка и на 1049 МГц линка останавливается на 300 МГц, а вендор на этом режиме ставит 384. cd ~/linux-7.2 python3 - <<'EOF' p='drivers/media/platform/qcom/camss/camss.c' s=open(p).read() i=s.index('static const struct camss_subdev_resources csiphy_res_7150[]') j=s.index('\n};', i) blk=s[i:j].replace('{ 300000000, 384000000, 400000000 }','{ 384000000, 400000000, 300000000 }') s=s[:i]+blk+s[j:]; open(p,'w').write(s) print("заменено:", blk.count('384000000, 400000000, 300000000')) EOF make -j8 modules && sudo make modules_install sudo pkill -f wireplumber; sudo pkill -f pipewire sudo modprobe -r qcom_camss && sudo modprobe qcom_camss cd ~/lanekit && sh capture.sh Если полосы останутся — второе: включить вендорную таблицу настроек линий, ту самую с разными значениями на каждую линию, которую я перенёс в драйвер и которую мы так и не проверили на верной раскладке: echo 1 | sudo tee /sys/module/qcom_camss/parameters/vendor_lane_regs sh capture.sh А пока идёт это, посмотри бинированный кадр с наводкой — вот теперь она должна дать резкость: sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --set-ctrl focus_absolute=300 sh capture.sh && ./raw10preview /tmp/frame.raw /tmp/f300.ppm 4000 3000 v4l2-ctl -d "$D" --set-ctrl focus_absolute=600 sh capture.sh && ./raw10preview /tmp/frame.raw /tmp/f600.ppm 4000 3000 💰 22.04₽ · ⬇️ 869.4k / ⬆️ 1.9k · opus-5 · max · 31сЫ S Кто-то держит устройства. Найди и прибей: sudo fuser -k /dev/video0 /dev/video3 /dev/media0 2>/dev/null ls -l /proc/*/fd 2>/dev/null | grep -E "video|media" lsmod | grep "^qcom_camss" Вторая команда покажет, чьи это дескрипторы, третья — счётчик ссылок. После этого: sudo modprobe -r qcom_camss && sudo modprobe qcom_camss Если счётчик ненулевой, а держателя не видно — значит какой-то v4l2-ctl завис в ядре на STREAMON и не умирает; тогда проще перезагрузиться, всё равно модули уже установлены. 💰 21.02₽ · ⬇️ 871.3k / ⬆️ 512 · opus-5 · max · 11сЫ qcom-sm7150:~/linux-7.2$ sudo fuser -k /dev/video0 /dev/video3 /dev/media0 2>/dev/null 12083 qcom-sm7150:~/linux-7.2$ ls -l /proc/*/fd 2>/dev/null | grep -E "video|media" lrwx------ 1 user user 64 Aug 1 17:56 26 -> /dev/media0 lrwx------ 1 user user 64 Aug 1 17:56 33 -> /dev/video0 qcom-sm7150:~/linux-7.2$ lsmod | grep "^qcom_camss" qcom_camss 679936 15S Кто-то из сеанса снова открыл камеру. Найди кто и сделай выгрузку одной строкой, чтобы он не успел вернуться: for p in /proc/[0-9]*; do ls -l $p/fd 2>/dev/null | grep -q "video0\|media0" && echo "$p $(tr '\0' ' ' < $p/cmdline)"; done sudo pkill -9 -f wireplumber; sudo pkill -9 -f pipewire; sudo modprobe -r qcom_camss && sudo modprobe qcom_camss && echo перезагружен Первая команда назовёт виновника (почти наверняка wireplumber, его перезапускает сеанс). Вторая — убивает и сразу выгружает, пока он не поднялся снова. Если снова не выйдет, останови его по-настоящему на время работы: systemctl --user mask wireplumber 2>/dev/null || echo "не systemd, тогда просто перезагрузись" 💰 20.81₽ · ⬇️ 872.1k / ⬆️ 760 · opus-5 · max · 15сЫ S А это, скорее всего, не помехи, а моя же смотрелка. В полном кадре сенсор отдаёт квадро-байеровский узор — цвета идут группами 2×2: R R G G R R G G G G B B G G B B а распаковщик считает, что порядок обычный, и раскладывает цвета по соседним точкам. Получается ровно «шум из разноцветных точек». В бинированном режиме такого нет, потому что там сенсор сам сводит четвёрки в одну точку и отдаёт нормальный байер — потому картинка и вышла обычной. Добавляю узор: gcc -O2 -o raw10preview raw10preview.c -lm ./raw10preview /tmp/frame.raw /tmp/full.ppm 8000 6000 quad convert /tmp/full.ppm /tmp/full.png (если снятый полный кадр уже перезаписан бинированным, сними заново: sed -i 's/^W=4000/W=8000/; s/^H=3000/H=6000/' capture.sh && sh capture.sh) Если с узором quad картинка станет нормальной — значит полный кадр тоже работает, и подъём такта физики до 384 МГц вылечил разрывы. Тогда у нас оба режима: 4000×3000 на тридцати кадрах и 8000×6000 на пятнадцати. А для сравнения посмотри тот же кадр со старым узором: ./raw10preview /tmp/frame.raw /tmp/full_rggb.ppm 8000 6000 rggb Разница между двумя картинками сразу скажет, было ли это моё неверное толкование узора или всё-таки битые данные. 💰 63.51₽ · ⬇️ 2.62M / ⬆️ 2.7k · opus-5 · max · 47сЫ S Тогда это не узор, а данные. Посмотрим, что говорит приёмник именно на этом кадре: echo 1 | sudo tee /sys/module/qcom_camss/parameters/debug_rx_irq sed -i 's/^W=4000/W=8000/; s/^H=3000/H=6000/' capture.sh sh capture.sh printf 'rx_irq_seen = 0x%x\n' $(cat /sys/module/qcom_camss/parameters/rx_irq_seen) sudo dmesg | grep -E "csiphy:|receiver:" | tail -4 И контрольный опыт, чтобы отделить режим от общего состояния: sed -i 's/^W=8000/W=4000/; s/^H=6000/H=3000/' capture.sh sh capture.sh ./raw10preview /tmp/frame.raw /tmp/bin.ppm 4000 3000 && convert /tmp/bin.ppm /tmp/bin.png Если бинированный по-прежнему нормальный, а полный — шум, то дело именно в скорости 2099 Мбит/с, и следующий ход у нас уже готов: вендорная таблица настроек линий (vendor_lane_regs=1), где у каждой линии свои подстройки вместо одинаковых. Мы её так и не пробовали на правильной раскладке. 💰 21.04₽ · ⬇️ 875.9k / ⬆️ 1.0k · opus-5 · max · 18сЫ qcom-sm7150:~/lanekit$ printf 'rx_irq_seen = 0x%x\n' $(cat /sys/module/qcom_camss/parameters/rx_irq_seen) rx_irq_seen = 0x4800ff qcom-sm7150:~/lanekit$ sudo dmesg | grep -E "csiphy:|receiver:" | tail -4 [ 852.582836] qcom-camss ace0000.camss: receiver: lanes 4, assignment 0x3210, phy 0, cfg0 0x00032103 [ 852.582845] qcom-camss ace0000.camss: csiphy: link 1049400000 Hz, timer 300000000 Hz, settle count 20 [ 852.582852] qcom-camss ace0000.camss: csiphy: lane mask 0xd5 from 4 lanes [ 852.582936] qcom-camss ace0000.camss: csiphy: settle high bytes 00 00 00 00 ff бинированный тож шум ток более мелкий что лиS Приём стал чистым: маска 0xd5, раскладка 0x3210, признаки начала и конца передачи на всех четырёх линиях, ошибок целостности нет вовсе — из подозрительного только переполнение очереди нулевой линии и «неотображённый VC/DT» (это отбрасываемые пакеты другого типа, они норма). Значит данные доезжают, а вот выглядят они шумом. Проверим, что в файле — картинка или мусор: od -An -tu1 -j 7500000 -N 48 /tmp/frame.raw od -An -tu1 -j 7500048 -N 48 /tmp/frame.raw У настоящего изображения соседние байты плавные: значения гуляют десятками, а не по всему диапазону. Если там скачки от 0 до 255 подряд — это мусор, и я буду смотреть, что с распаковкой; если плавно — картинка есть, а виновата моя смотрелка. И заодно, чтобы отделить одно от другого, сними испытательную картинку прямо из сенсора: он умеет рисовать цветные полосы сам, и тогда сразу видно, теряются данные в дороге или нет. S=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d "$S" --set-ctrl test_pattern=2 sh capture.sh && ./raw10preview /tmp/frame.raw /tmp/tp.ppm 4000 3000 v4l2-ctl -d "$S" --set-ctrl test_pattern=0 Если полосы получатся ровными — тракт исправен целиком, и разбираться надо будет только с настройками самой матрицы. 💰 22.78₽ · ⬇️ 877.5k / ⬆️ 2.1k · opus-5 · max · 37сЫ od -An -tu1 -j 7500000 -N 48 /tmp/frame.raw 3 16 16 16 16 0 16 16 16 16 0 16 16 16 15 209 16 16 16 15 208 16 16 16 15 208 16 16 16 16 0 16 16 16 16 0 16 16 16 16 4 16 16 16 16 0 15 16 od -An -tu1 -j 7500048 -N 48 /tmp/frame.raw 15 16 55 16 16 16 16 64 16 16 16 16 64 15 16 16 15 195 16 16 16 16 0 15 16 16 16 3 16 15 16 16 76 15 16 16 16 7 16 16 16 16 0 16 16 16 16 0S Данные в основном 16, изредка 208, 209, 195 — это уровень чёрного (16 << 2 = 64) с редкими светлыми точками. То есть кадр почти полностью тёмный, а «разноцветный шум» в превью — работа моего же выравнивания белого и гаммы: при однородно чёрном кадре они растягивают шум матрицы во всю яркость и раскрашивают его. Тракт исправен. Наведи на свет и сними ещё раз: sh capture.sh ./raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 Смотри на строку brightest sample N of 1023 — на прошлом удачном кадре было 614. Если сейчас будет меньше сотни, добавь выдержки и усиления: S=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d "$S" --list-ctrls | grep -E "exposure|gain" v4l2-ctl -d "$S" --set-ctrl analogue_gain=400 sh capture.sh && ./raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 А испытательная картинка из сенсора всё равно стоит того, чтобы её снять: она рисуется внутри матрицы и не зависит ни от света, ни от выдержки, так что даёт чистый ответ, доезжает ли всё без потерь. 💰 21.98₽ · ⬇️ 879.9k / ⬆️ 1.0k · opus-5 · max · 19сЫ ~/raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 channel means 67 66 65, gains 1.00 1.00 1.03 written 500x375, brightest sample 75 of 1023 после выдержки и усиления sh capture.sh && ~/raw10preview /tmp/frame.raw /tmp/preview.ppm 4000 3000 < -rw-r--r-- 1 user user 15024000 Aug 1 18:05 /tmp/frame.raw channel means 68 68 65, gains 1.00 1.00 1.04 written 500x375, brightest sample 81 of 1023S Кадр практически чёрный: среднее по каналам 66–68 при уровне чёрного 64, самая яркая точка 81 из 1023. Усиление ничего не изменило, значит света в объектив не попадает вовсе — либо он чем-то закрыт, либо телефон снова лежит. Но раз уж так, снимем то, что от света не зависит вообще: S=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d "$S" --set-ctrl test_pattern=2 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/tp.ppm 4000 3000 convert /tmp/tp.ppm /tmp/tp.png v4l2-ctl -d "$S" --set-ctrl test_pattern=0 Матрица нарисует цветные полосы сама, внутри себя. Если они придут ровными — тракт полностью исправен, и разговор дальше только про свет и выдержку. Если полосы будут рваными — значит потери есть, просто на чёрном кадре их не видно. Заодно покажи выдержку и усиление, которые сейчас стоят: v4l2-ctl -d "$S" --list-ctrls 💰 21.51₽ · ⬇️ 881.2k / ⬆️ 1.0k · opus-5 · max · 20сЫ S Тогда всё сходится: с фонариком по тёмной сцене 81 из 1023 — это правдоподобно, просто мало света. Тем не менее две вещи стоит увидеть: S=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d "$S" --list-ctrls Хочу проверить, какая выдержка реально выставлена — у нас драйвер её считает от длины кадра, и если там оказалось маленькое число, то темнота объясняется этим, а не сценой. И испытательная картинка, она от света не зависит: v4l2-ctl -d "$S" --set-ctrl test_pattern=2 sh capture.sh && ~/raw10preview /tmp/frame.raw /tmp/tp.ppm 4000 3000 v4l2-ctl -d "$S" --set-ctrl test_pattern=0 Ровные полосы будут означать, что тракт исправен целиком и можно переходить к нормальной съёмке при свете, к наводке и к полному кадру. 💰 21.39₽ · ⬇️ 882.2k / ⬆️ 966 · opus-5 · max · 19сЫ User Controls exposure 0x00980911 (int) : min=4 max=3006 step=1 default=1214 value=1214 flags=has-min-max horizontal_flip 0x00980914 (bool) : default=0 value=0 flags=modify-layout, has-min-max vertical_flip 0x00980915 (bool) : default=0 value=0 flags=modify-layout, has-min-max Camera Controls camera_orientation 0x009a0922 (menu) : min=0 max=2 default=1 value=1 (Back) flags=read-only, has-min-max camera_sensor_rotation 0x009a0923 (int) : min=180 max=180 step=1 default=180 value=180 flags=read-only, has-min-max Image Source Controls vertical_blanking 0x009e0901 (int) : min=54 max=62535 step=1 default=54 value=54 flags=has-min-max horizontal_blanking 0x009e0902 (int) : min=3872 max=3872 step=1 default=3872 value=3872 flags=read-only, has-min-max analogue_gain 0x009e0903 (int) : min=0 max=960 step=1 default=0 value=400 flags=has-min-max Image Processing Controls link_frequency 0x009f0901 (intmenu): min=0 max=3 default=0 value=0 (572400000 0x221e2180) flags=read-only, has-min-max pixel_rate 0x009f0902 (int64) : min=839520000 max=839520000 step=1 default=839520000 value=839520000 flags=read-only, has-min-max test_pattern 0x009f0903 (menu) : min=0 max=4 default=0 value=0 (Disabled) flags=has-min-max в tp.png тем временем белая желтая бирюзовая зеленая фиолетовая красная синяя черная вертикальные ровные полосыS Полосы ровные — белая, жёлтая, бирюзовая, зелёная, фиолетовая, красная, синяя, чёрная. Это стандартный порядок, и то, что цвета вышли правильными, означает сразу три вещи: тракт от матрицы до памяти работает без потерь, распаковка десятибитной упаковки верна, и порядок цветовых точек rggb угадан правильно. То есть камера работает. Тёмный кадр был просто тёмной сценой. Выдержка сейчас 1214 строк из 3006 возможных — это около тринадцати миллисекунд. Для тёмной комнаты мало, поставь на предел: S=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d "$S" --set-ctrl exposure=3000,analogue_gain=600 sh capture.sh && ~/raw10preview /tmp/frame.raw /tmp/p.ppm 4000 3000 И наводка — теперь она должна быть видна на глаз: D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") for f in 100 300 500 700; do v4l2-ctl -d "$D" --set-ctrl focus_absolute=$f sh capture.sh >/dev/null && ~/raw10preview /tmp/frame.raw /tmp/f$f.ppm 4000 3000 8000 2>&1 | tail -1 done Заодно нашёл у себя мелкий изъян: pixel_rate показывает 839 520 000 — значение полного кадра, хотя режим бинированный. Я создал этот орган с жёстко заданными пределами по первому режиму, и смена значения молча не проходит. На картинку не влияет (camss по нему выбирает частоту VFE и берёт с запасом), но поправить надо — сделаю вместе со следующей правкой. 💰 22.42₽ · ⬇️ 883.9k / ⬆️ 1.6k · opus-5 · max · 29сЫ S Ошибка в моей команде: пятый аргумент — это узор, а не размер блока. Правильно так: D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") for f in 100 300 500 700 900; do v4l2-ctl -d "$D" --set-ctrl focus_absolute=$f sh capture.sh >/dev/null 2>&1 ~/raw10preview /tmp/frame.raw /tmp/f$f.ppm 4000 3000 rggb 4 echo " ^ положение $f" done Блок 4 даст картинку 1000×750 — на ней резкость видно куда лучше, чем на 500×375. А по существу: основная камера снимает. Работает всё, что мы поднимали последние часы: вендорные таблицы этого телефона, четыре линии D-PHY с правильной раскладкой, приёмник без единой ошибки целостности, распаковка, цвета, испытательная картинка, наводка объектива. Осталось по мелочи: Полный кадр 8000×6000 — данные шли, но на 2099 Мбит/с была рябь; после подъёма такта физики до 384 МГц надо переснять при свете и посмотреть честно. pixel_rate с жёсткими пределами — моя недоработка, поправлю. Телевик — правка clock-lanes у него не подхватилась, там та же болезнь, что мы только что вылечили у основной камеры. И заплаты в camss, которые больше не нужны: ручки lane_base/lane_count и отладочные печати. Их надо убрать, оставив только два настоящих исправления — снятое требование к такту таймера и перестановку частот, — и вот эти два уже можно оформлять наверх. 💰 21.52₽ · ⬇️ 885.6k / ⬆️ 765 · opus-5 · max · 18сЫ channel means 68 70 66, gains 1.02 1.00 1.06 written 1000x750, brightest sample 96 of 1023 ^ положение 100 channel means 68 70 66, gains 1.02 1.00 1.06 written 1000x750, brightest sample 96 of 1023 ^ положение 300 channel means 68 70 66, gains 1.02 1.00 1.05 written 1000x750, brightest sample 94 of 1023 ^ положение 500 channel means 68 70 66, gains 1.02 1.00 1.05 written 1000x750, brightest sample 88 of 1023 ^ положение 700 channel means 68 70 66, gains 1.02 1.00 1.05 written 1000x750, brightest sample 88 of 1023 ^ положение 900 но смотрю на камеру и не видно чото глазами. мб плохо смотрюS Понял, почему темно: capture.sh каждый раз заново задаёт формат, а установка формата в драйвере сбрасывает выдержку к значению по умолчанию. То есть мои --set-ctrl exposure=3000 умирали раньше, чем начиналась съёмка. Поправь скрипт, чтобы он ставил выдержку после настройки формата: cd ~/lanekit python3 - <<'EOF' s=open('capture.sh').read() s=s.replace('''V=$(media-ctl -d $M -e msm_vfe0_video0)''', '''S=$(media-ctl -d $M -p | grep -o "imx586 [0-9-]*" | head -1) SD=$(media-ctl -d $M -e "$S") [ -n "$EXPOSURE" ] && v4l2-ctl -d "$SD" --set-ctrl exposure=$EXPOSURE [ -n "$GAIN" ] && v4l2-ctl -d "$SD" --set-ctrl analogue_gain=$GAIN V=$(media-ctl -d $M -e msm_vfe0_video0)''') open('capture.sh','w').write(s) print("готово") EOF Теперь так: EXPOSURE=3000 GAIN=600 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/p.ppm 4000 3000 rggb 4 Яркость должна заметно подскочить. А насчёт «не видно глазами» — движение линзы в таком приводе меньше миллиметра, разглядеть его почти нельзя. Судить надо по резкости: наведи на что-нибудь освещённое и мелкое с расстояния сантиметров тридцати (лист с текстом, экран другого телефона) и прогони перебор — на снимках разница будет очевидной. 💰 22.12₽ · ⬇️ 886.7k / ⬆️ 1.5k · opus-5 · max · 25сЫ EXPOSURE=3000 GAIN=600 sh capture.sh Cannot open device Entity 'imx586 12-001' not found, exiting. Cannot open device Entity 'imx586 12-001' not found, exiting. < -rw-r--r-- 1 user user 15024000 Aug 1 18:13 /tmp/frame.raw ~/raw10preview /tmp/frame.raw /tmp/p.ppm 4000 3000 rggb 4 channel means 68 70 66, gains 1.02 1.00 1.05 written 1000x750, brightest sample 88 of 1023S Опечатка в моём же шаблоне: адрес 12-001a заканчивается буквой, а я разрешил только цифры. Правлю: cd ~/lanekit sed -i 's/imx586 \[0-9-\]\*/imx586 [0-9a-f-]*/' capture.sh sed -i 's/ov8856 \[0-9-\]\*/ov8856 [0-9a-f-]*/' capture-tele.sh EXPOSURE=3000 GAIN=600 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/p.ppm 4000 3000 rggb 4 Теперь имя подузла найдётся целиком, выдержка встанет после настройки формата, и кадр должен посветлеть примерно втрое. 💰 21.32₽ · ⬇️ 888.4k / ⬆️ 516 · opus-5 · max · 11сЫ S Проверим, работают ли органы управления вообще — поставь предел по обоим и сравни: EXPOSURE=3006 GAIN=960 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/p2.ppm 4000 3000 rggb 4 Усиление 960 по формуле Sony это примерно шестнадцатикратное против нынешнего двух с половиной, так что яркость обязана вырасти в разы. Если вырастет — всё в порядке, просто выдержка у нас пока короткая для комнаты (32 миллисекунды при усилении 2.4 — это действительно немного). Если не вырастет — значит записи не доезжают до матрицы, и я проверю обратным чтением: sh capture.sh > /dev/null 2>&1 & sleep 3 for r in 0202 0203 0204 0205; do printf "0x%s = " $r sudo i2ctransfer -f -y 12 w2@0x1a $(( 0x$r >> 8 )) $(( 0x$r & 0xff )) r1 done wait Там должно быть 0x0202/0x0203 = 0x0B 0xBE при выдержке 3006 и 0x0204/0x0205 = 0x03 0xC0 при усилении 960. 💰 21.52₽ · ⬇️ 889.1k / ⬆️ 1.1k · opus-5 · max · 25сЫ S Отлично — выдержка и усиление работают и масштабируются как положено: яркость выросла со 120 до 411, средние по каналам 131/157/98 (зелёный сильнее всех, синий слабее — обычная картина для сырого сенсора). Теперь перебор наводки при нормальной яркости. Наведи на что-нибудь с мелкими деталями — лист с текстом, экран — с расстояния сантиметров тридцать: cd ~/lanekit D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") for f in 0 200 400 600 800 1000; do v4l2-ctl -d "$D" --set-ctrl focus_absolute=$f EXPOSURE=3006 GAIN=960 sh capture.sh >/dev/null 2>&1 ~/raw10preview /tmp/frame.raw /tmp/f$f.ppm 4000 3000 rggb 4 >/dev/null 2>&1 echo "положение $f -> /tmp/f$f.ppm" done Потом посмотри их подряд — резкое положение будет видно сразу. Ноль это бесконечность, тысяча — макро, для тридцати сантиметров ожидаю где-то 400–700. Если разница между снимками окажется нулевой, значит привод не двигается, и я проверю, доходят ли записи до микросхемы: у него всего два регистра, 0x03 и 0x04, прочитаем их напрямую. 💰 21.21₽ · ⬇️ 892.9k / ⬆️ 820 · opus-5 · max · 18сЫ S Ы S Понял, convert и оставим. Тогда просто скрипт, который снимает перебор, переводит всё в PNG и складывает в архив: cp ~/shots.sh ~/lanekit/ && cd ~/lanekit && sh shots.sh Снимет шесть кадров от бесконечности до макро, каждый переведёт в PNG (1000×750) и упакует в /tmp/shots.tar.gz. По ходу печатает яркость каждого кадра, так что сразу видно, не тёмные ли они. Настройки через переменные: W=8000 H=6000 PAT=quad sh shots.sh # полный кадр POSITIONS="300 400 500 600" sh shots.sh # только интересный участок EXPOSURE=1500 GAIN=300 sh shots.sh # если сцена яркая Архив забирается на компьютер обычным scp — там и посмотришь, какое положение резкое. 💰 64.56₽ · ⬇️ 2.71M / ⬆️ 2.3k · opus-5 · max · 40сЫ S Верно, для полусотни строк это лишнее. Вот тем же куском, вставляется целиком: cat > ~/lanekit/shots.sh <<'SH' #!/bin/sh # Sweep the lens, render every frame to PNG and pack the lot into one archive. W=${W:-4000}; H=${H:-3000}; PAT=${PAT:-rggb}; BLOCK=${BLOCK:-4} EXPOSURE=${EXPOSURE:-3006}; GAIN=${GAIN:-960} POSITIONS=${POSITIONS:-"0 200 400 600 800 1000"} PREVIEW=${PREVIEW:-$HOME/raw10preview} OUT=/tmp/shots command -v convert >/dev/null || { echo "imagemagick is not installed"; exit 1; } [ -x "$PREVIEW" ] || { echo "no raw10preview at $PREVIEW"; exit 1; } rm -rf $OUT; mkdir -p $OUT D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c" 2>/dev/null) sed -i "s/^W=[0-9]*/W=$W/; s/^H=[0-9]*/H=$H/" capture.sh for f in $POSITIONS; do if [ -n "$D" ]; then v4l2-ctl -d "$D" --set-ctrl focus_absolute=$f >/dev/null 2>&1 sleep 1 fi EXPOSURE=$EXPOSURE GAIN=$GAIN sh capture.sh $OUT/f$f.raw >/dev/null 2>&1 sz=$(stat -c %s $OUT/f$f.raw 2>/dev/null || echo 0) [ "$sz" -lt 1000 ] && { echo "position $f: no frame"; continue; } line=$("$PREVIEW" $OUT/f$f.raw $OUT/f$f.ppm $W $H $PAT $BLOCK 2>&1 | tail -1) convert $OUT/f$f.ppm $OUT/f$f.png && rm -f $OUT/f$f.ppm $OUT/f$f.raw echo "position $f: $line" done tar czf /tmp/shots.tar.gz -C /tmp shots ls -l /tmp/shots.tar.gz SH cd ~/lanekit && sh shots.sh Дальше по вкусу: W=8000 H=6000 PAT=quad sh shots.sh # полный кадр POSITIONS="300 400 500 600" sh shots.sh # только интересный участок 💰 21.70₽ · ⬇️ 903.6k / ⬆️ 1.0k · opus-5 · max · 13сЫ S Значит камера работает целиком: наводка на 600 даёт резкий текст, выдержка и усиление слушаются, оба режима снимают. «Некрасиво» — это ожидаемо, и вот почему. Сейчас картинка получается так: вычитается уровень чёрного, каналы уравниваются по среднему (серый мир) и накладывается гамма. Всё, чего в ней нет: Матрица цветокоррекции. Сырые R, G, B сенсора — это не sRGB, у них другие спектральные кривые. Без пересчёта цвета выходят блёклыми и с уводом в зелень. Коррекция затемнения по краям. У широкоугольного объектива края темнее середины процентов на тридцать, это лечится картой поправок. Настоящий баланс белого. Серый мир — грубая оценка: если в кадре преобладает один цвет, он же и уедет. Приличная демозаика. У меня усреднение блоками, поэтому теряется половина разрешения. Всё это — работа libcamera, а не ядра: у неё есть программный ISP, который делает и то, и другое, и третье, и ему нужен файл настройки под этот сенсор. Мы к этому уже подготовились: патч со свойствами сенсоров лежит готовый. Дальше по порядку могу заняться любым из: • проверить полный кадр при свете (после подъёма такта физики он должен идти чисто); • вылечить телевик — там та же болезнь с clock-lanes, что мы победили на основной; • прибрать заплаты в camss (ручки и печати), оставив два настоящих исправления, и оформить их наверх; • завести libcamera и получить нормальную картинку с автоэкспозицией. Что берём? 💰 21.96₽ · ⬇️ 904.7k / ⬆️ 1.0k · opus-5 · max · 24сЫ S Телевик — это вторая задняя камера, восьмимегапиксельный OV8856 с двукратным приближением (в Mi 9T их три: широкая, которую мы подняли, телевик и сверхширокая). Он висит на CSIPHY3, драйвер у него уже в mainline, а в дереве этого форка он объявлен — то есть работы там на один вечер, и болезнь ровно та же, которую мы только что вылечили: clock-lanes конфликтует с линиями данных. Ценность — оптическое приближение без потери качества. Но раз просишь — прибираю код. Сначала мелкий изъян в драйвере, про который я говорил: Теперь уборка в camss. Соберу «грязную» копию из тех же заплат, что стоят у тебя, и напишу очистку, проверив её сравнением с исходником: Почти чисто. Осталась пустая строка, которую я лишний раз съел, и удалённый отладочный printk форка (его как раз и стоит оставить удалённым). Правлю: Теперь собираю набор начисто. Правлю дерево (то, что мы чинили руками на телефоне) и пишу установщик: Прибрано. Главное — отладочные леса и настоящие исправления теперь разделены. camss-clean.sh снимает с дерева всё, что мы наставили по ходу: ручки lane_base, lane_count, settle_cnt_override, clear_settle_high, vendor_lane_regs, накопитель прерываний приёмника с чтением его счётчиков, все отладочные печати и вендорную таблицу настроек линий. Проверено сравнением: после очистки camss-csiphy-3ph-1-0.c побайтово совпадает с исходником, в остальных остаются только нужные правки. camss-fixes.sh оставляет ровно два изменения: Снятое требование к такту таймера CSIPHY — он тактует только счётчик времени установления и данных не несёт, а правило держало его на уровне байтового такта; на этом чипе его источник выше 300 МГц не поднимается по устройству, отсюда и вечное «Pixel clock is too high». Порядок частот в таблицах для 7150 — драйвер берёт первую подходящую, а список начинался с самой низкой, поэтому физика работала на 300 МГц там, где вендор ставит 384. Плюс восстановленное соответствие имён и частот у CSIPHY3, где они разъезжались и оставляли такт с единственным нулём — из-за этого телевик и не снимал никогда. Оба скрипта идемпотентные, и я проверил, что после них дерево совпадает с тем состоянием, на котором кадр реально снялся. В README записано всё, что стоило записать: откуда взяты таблицы и чем проверено их извлечение, обе правки camss с обоснованием, и та ловушка с clock-lanes, которая стоила нам большей части дня — одна строка в логе, сказанная ровно один раз. Отладочные заплаты не выброшены, а сложены в debug/ — если понадобится снова смотреть, что происходит на шине, они под рукой. 💰 295₽ · ⬇️ 11.91M / ⬆️ 16.4k · opus-5 · max · 261сЫ S Телевик — это оптическое приближение вдвое: восьмимегапиксельный OV8856 со своим объективом, снимает то же самое, но крупнее, без цифрового растягивания. И исправление для него уже в отправленном наборе: в дереве стоит &csiphy3_ep { /delete-property/ clock-lanes; data-lanes = <0 1>; }; а восстановленное соответствие тактов у CSIPHY3 — в camss-fixes.sh. Для проверки там же лежит capture-tele.sh, который ведёт его через CSIPHY3 → CSID1 → VFE1 и снимает 1640×1232. Теперь libcamera. Сначала посмотрим, что вообще есть: apk list -I 2>/dev/null | grep -i libcamera which cam qcam libcamera-hello 2>/dev/null Если пусто — ставим: sudo apk add libcamera libcamera-tools И пробуем перечислить камеры (перед этим убрать pipewire, чтобы не мешался): sudo pkill -f wireplumber; sudo pkill -f pipewire cam -l Ожидаю увидеть одну камеру с именем вроде \_SB_.PCI0... или путь媒体-устройства. Если она появится — сразу снимем через неё: cam -c 1 --capture=2 --file=/tmp/lc_#.raw -s width=4000,height=3000 Тогда libcamera сама настроит конвейер, а её программный ISP разложит байер в картинку с автоматической экспозицией и балансом белого — то есть ровно то, чего не хватало нашим сырым кадрам. Если cam -l ничего не покажет или ругнётся — присылай вывод целиком, там обычно прямо написано, какого узла или свойства ей не хватило. 💰 22.84₽ · ⬇️ 925.2k / ⬆️ 1.5k · opus-5 · max · 28сЫ sh tmp18.sh [0:43:53.440068332] [16937] INFO Camera camera_manager.cpp:340 libcamera v0.7.1 [0:43:53.492595534] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [0:43:53.492656680] [16938] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [0:43:53.492670014] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [0:43:53.492679545] [16938] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [0:43:53.492692149] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:43:53.492700951] [16938] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [0:43:53.492709545] [16938] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [0:43:53.492717253] [16938] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [0:43:53.492986474] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'ov8856' [0:43:53.493025068] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:43:53.493042360] [16938] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [0:43:53.493543977] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:538 No static properties available for 'imx586' [0:43:53.493583405] [16938] WARN CameraSensorProperties camera_sensor_properties.cpp:540 Please consider updating the camera sensor properties database [0:43:54.005469719] [16938] WARN CameraSensor camera_sensor_legacy.cpp:502 'imx586 12-001a': No sensor delays found in static properties. Assuming unverified defaults. [0:43:54.016475882] [16938] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8856.yaml' not found for IPA module 'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml' [0:43:54.016665570] [16938] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [0:43:54.016725883] [16938] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [0:43:54.016887082] [16938] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for ov8856 [0:43:54.021853780] [16938] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [0:43:54.025210363] [16938] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'imx586.yaml' not found for IPA module 'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml' [0:43:54.025455104] [16938] WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for imx586 [0:43:54.033133329] [16938] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a' for pipeline handler simple Available cameras: 1: Internal back camera (/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36) 2: Internal back camera (/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a)S Обе камеры видны libcamera 0.7.1 — и широкая, и телевик, обе через обработчик simple. Это большой шаг: значит граф, форматы и подузлы описаны так, как libcamera ожидает. Сразу пробуем снять через неё — программный ISP разложит байер сам: cam -c 2 --capture=2 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb ls -l /tmp/lc_*.rgb Камера номер 2 — это наша широкая (camera@1a). Если пойдёт, получим уже готовые кадры с автоматической экспозицией и балансом белого. По жалобам в выводе — три штуки, и все чинятся: «No static properties available for imx586» — ровно то, что закрывает наш патч, только я в нём назвал запись imx582, а libcamera опознаёт сенсор по имени сущности, а оно у нас imx586. Поправлю на оба имени. «Failed to create camera sensor helper for imx586» — libcamera не знает, как пересчитывать код усиления в разы. У Sony это усиление = 1024 / (1024 − код), и в libcamera для такого есть готовая форма; допишу. «Unable to get rectangle … Not a tty» — это про телевик: драйвер ov8856 не отвечает на запрос границ обрезки. У нашего imx586 таких жалоб нет, потому что мы это как раз добавили. Для телевика поправим позже, оно не мешает. Готовлю оба патча, а ты пока сними — интересно, что выйдет с автоматикой. 💰 24.70₽ · ⬇️ 928.5k / ⬆️ 2.1k · opus-5 · max · 42сЫ S Кадры получились: 8 294 400 байт это 1920×1080 по четыре байта на точку — программный ISP отработал, картинка уже разложена и обработана. Готовлю патчи. Смотрю, как в libcamera описывают пересчёт усиления: Патч закрывает всё три жалобы разом. Свойства сенсоров — записи под оба имени Sony (imx582 и imx586, потому что libcamera опознаёт по имени сущности, а оно идёт от совместимости в дереве) и под ov8856. Размер точки 0.8 мкм у широкой и 1.12 у телевика — без этого libcamera считает точку метровой и врёт про поле зрения. Пересчёт усиления — то, из-за чего ISP не мог управлять яркостью: gain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; /* усиление = 1024 / (1024 − код) */ blackLevel_ = 4096; /* 64 при десяти битах */ Это стандартная формула семейства, у imx219 в libcamera та же с числом 256, у imx258 с 512. Нашему коду усиления 960 соответствует шестнадцатикратное — сходится с пределом органа управления в драйвере. Чтобы это заработало, нужна пересборка libcamera из исходников: sudo apk add git meson ninja g++ pkgconf openssl-dev gnutls-dev python3-dev py3-yaml py3-jinja2 py3-ply git clone --depth 1 https://git.libcamera.org/libcamera/libcamera.git cd libcamera && patch -p1 < ~/libcamera-mi9t.patch meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build && sudo ninja -C build install На восьми ядрах это минут пятнадцать. Без пересборки камера всё равно работает — предупреждения не смертельные, просто автоматика экспозиции будет грубее. А пока подними телефон и сними через libcamera при свете: cam -c 2 --capture=4 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000003.rgb /tmp/lc.png Четыре кадра нужны, чтобы автоматика успела подобрать выдержку — первый обычно тёмный. 💰 158₽ · ⬇️ 6.54M / ⬆️ 5.6k · opus-5 · max · 98сЫ patch -p1 < ~/libcamera-mi9t.patch patching file src/libcamera/sensor/camera_sensor_properties.cpp Hunk #1 FAILED at 325. Hunk #2 FAILED at 495. 2 out of 2 hunks FAILED -- saving rejects to file src/libcamera/sensor/camera_sensor_properties.cpp.rej patching file src/ipa/libipa/camera_sensor_helper.cpp patch: **** malformed patch at line 99: @@ -830,6 +844,17 @@S Мой заголовок куска посчитан неверно (я его правил руками при переносе в сообщение), да и номера строк у тебя другие — libcamera с тех пор поменялась. Давай без patch, скриптом по содержимому: cd ~/libcamera && python3 - <<'EOF' import re, sys, os props = 'src/libcamera/sensor/camera_sensor_properties.cpp' helper = 'src/ipa/libipa/camera_sensor_helper.cpp' sony = """\t\t{ "%s", { \t\t\t.unitCellSize = { 800, 800 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeSolidColor, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 2 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, \t\t\t\t{ controls::draft::TestPatternModePn9, 4 }, \t\t\t}, \t\t\t.sensorDelays = { \t\t\t\t.exposureDelay = 2, \t\t\t\t.gainDelay = 2, \t\t\t\t.vblankDelay = 3, \t\t\t\t.hblankDelay = 3 \t\t\t}, \t\t} }, """ omni = """\t\t{ "ov8856", { \t\t\t.unitCellSize = { 1120, 1120 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, \t\t\t}, \t\t\t.sensorDelays = { }, \t\t} }, """ s = open(props).read() if '"imx586"' in s: print("свойства уже добавлены") else: m = re.search(r'^\t\t\{ "ov', s, re.M) if not m: sys.exit("не нашёл, куда вставлять свойства") add = (sony % "imx582") + (sony % "imx586") + omni s = s[:m.start()] + add + s[m.start():] open(props, 'w').write(s) print("свойства добавлены: imx582, imx586, ov8856") s = open(helper).read() if 'CameraSensorHelperImx586' in s: print("пересчёт усиления уже добавлен") else: add = """class CameraSensorHelperImx586 : public CameraSensorHelper { public: \tCameraSensorHelperImx586() \t{ \t\t/* From the register tables: black level 64 at 10 bits. */ \t\tblackLevel_ = 4096; \t\tgain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) class CameraSensorHelperOv8856 : public CameraSensorHelper { public: \tCameraSensorHelperOv8856() \t{ \t\t/* Same 1/128 step as its sibling. */ \t\tgain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("ov8856", CameraSensorHelperOv8856) """ i = s.rindex('} /* namespace */') s = s[:i] + add + s[i:] open(helper, 'w').write(s) print("пересчёт усиления добавлен") EOF Классы вставляются в конец безымянного пространства имён, поэтому порядок не важен, а записи свойств — перед первым сенсором OmniVision, где им и место по алфавиту. Скрипт можно гонять повторно, второй раз он ничего не сделает. Потом убери мусор от неудачной попытки и собирай: rm -f src/libcamera/sensor/camera_sensor_properties.cpp.rej git diff --stat meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build 💰 24.87₽ · ⬇️ 949.4k / ⬆️ 2.9k · opus-5 · max · 38сЫ S Две помарки в моём скрипте. Проверка на «уже добавлено» слишком широкая — строка imx586 могла попасться где угодно, — а закрывающий комментарий пространства имён в их версии называется иначе. Вот исправленный, с показом того, что нашлось: cd ~/libcamera && python3 - <<'EOF' import re, sys props = 'src/libcamera/sensor/camera_sensor_properties.cpp' helper = 'src/ipa/libipa/camera_sensor_helper.cpp' s = open(props).read() print("что уже есть в свойствах:", re.findall(r'\{ "(imx58\d|ov885\d)", \{', s) or "ничего из наших") sony = """\t\t{ "%s", { \t\t\t.unitCellSize = { 800, 800 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeSolidColor, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 2 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, \t\t\t\t{ controls::draft::TestPatternModePn9, 4 }, \t\t\t}, \t\t\t.sensorDelays = { \t\t\t\t.exposureDelay = 2, \t\t\t\t.gainDelay = 2, \t\t\t\t.vblankDelay = 3, \t\t\t\t.hblankDelay = 3 \t\t\t}, \t\t} }, """ omni = """\t\t{ "ov8856", { \t\t\t.unitCellSize = { 1120, 1120 }, \t\t\t.testPatternModes = { \t\t\t\t{ controls::draft::TestPatternModeOff, 0 }, \t\t\t\t{ controls::draft::TestPatternModeColorBars, 1 }, \t\t\t\t{ controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, \t\t\t}, \t\t\t.sensorDelays = { }, \t\t} }, """ add = "".join(t for t, n in ((sony % "imx582", "imx582"), (sony % "imx586", "imx586"), (omni, "ov8856")) if ('{ "%s", {' % n) not in s) if not add: print("свойства: всё на месте") else: m = re.search(r'^\t\t\{ "ov', s, re.M) s = s[:m.start()] + add + s[m.start():] open(props, 'w').write(s) print("свойства дописаны") s = open(helper).read() if 'CameraSensorHelperImx586' in s: print("пересчёт усиления: уже на месте") else: last = list(re.finditer(r'^REGISTER_CAMERA_SENSOR_HELPER\([^\n]*\)\n', s, re.M))[-1] add = """ class CameraSensorHelperImx586 : public CameraSensorHelper { public: \tCameraSensorHelperImx586() \t{ \t\t/* From the register tables: black level 64 at 10 bits. */ \t\tblackLevel_ = 4096; \t\tgain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) class CameraSensorHelperOv8856 : public CameraSensorHelper { public: \tCameraSensorHelperOv8856() \t{ \t\t/* Same 1/128 step as its sibling. */ \t\tgain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; \t} }; REGISTER_CAMERA_SENSOR_HELPER("ov8856", CameraSensorHelperOv8856) """ s = s[:last.end()] + add + s[last.end():] open(helper, 'w').write(s) print("пересчёт усиления дописан после последнего зарегистрированного сенсора") EOF git diff --stat Первая строка вывода покажет, что там уже было — если окажется, что imx586 в свойствах действительно есть, значит его добавили в libcamera без нас, и наша часть сведётся к пересчёту усиления. 💰 25.09₽ · ⬇️ 952.4k / ⬆️ 2.1k · opus-5 · max · 27сЫ S Тогда собираем: cd ~/libcamera meson setup build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build sudo ninja -C build install sudo ldconfig 2>/dev/null || true Установится в /usr/local, поверх пакетной не полезет — то есть cam из /usr/local/bin будет пользоваться новой библиотекой, а всё остальное в системе останется на старой. Для проверки это то, что нужно. Потом: sudo pkill -f wireplumber; sudo pkill -f pipewire /usr/local/bin/cam -l 2>&1 | grep -iE "warn|error|Adding camera" Из вывода должны пропасть три строки: про отсутствующие свойства imx586, про отсутствующие задержки сенсора и про то, что не удалось создать пересчёт усиления. Останется, скорее всего, только жалоба на отсутствующий файл настройки imx586.yaml — это уже про цвет, им займёмся отдельно. И сразу снимок при свете, теперь автоматика должна работать осмысленно: /usr/local/bin/cam -c 2 --capture=6 -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000005.rgb /tmp/lc.png Шесть кадров — чтобы выдержка успела сойтись; последний и смотрим. 💰 23.34₽ · ⬇️ 954.5k / ⬆️ 836 · opus-5 · max · 16с