Веткаgrep -rn "davinci-imx586.dtsi" $T | head
301 сообщение · –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сЫ S cam собирается поверх libevent. Ставим и повторяем: sudo apk add libevent-dev cd ~/libcamera && meson setup --wipe build -Dpipelines=simple -Dipas=simple -Dcam=enabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build Если ещё чего-то не хватит — оно назовёт зависимость так же понятно, присылай строку. Или можно вовсе не собирать приложение, нам ведь нужна библиотека: meson setup --wipe build -Dpipelines=simple -Dipas=simple -Dcam=disabled -Dqcam=disabled -Ddocumentation=disabled ninja -C build && sudo ninja -C build install а проверять пакетным cam, подсунув ему свежую библиотеку: LD_LIBRARY_PATH=/usr/local/lib cam -l Второй путь быстрее, потому что не тянет ни libevent, ни остальную обвязку приложения. 💰 22.47₽ · ⬇️ 957.7k / ⬆️ 702 · opus-5 · max · 14сЫ ../src/ipa/libipa/camera_sensor_helper.h:91:42: error: redefinition of 'libcamera::ipa::CameraSensorHelperFactory<libcamera::ipa::CameraSensorHelperImx586> libcamera::ipa::global_CameraSensorHelperImx586Factory' 91 | static CameraSensorHelperFactory<helper> global_##helper##Factory(name); | ^~~~~ ../src/ipa/libipa/camera_sensor_helper.cpp:901:1: note: in expansion of macro 'REGISTER_CAMERA_SENSOR_HELPER' 901 | REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) | ^~~~~~~~~~~~~~~~~~~~~~~~~ ../src/ipa/libipa/camera_sensor_helper.h:91:42: note: 'libcamera::ipa::CameraSensorHelperFactory<libcamera::ipa::CameraSensorHelperImx586> libcamera::ipa::global_CameraSensorHelperImx586Factory' previously declared here 91 | static CameraSensorHelperFactory<helper> global_##helper##Factory(name); | ^~~ ../src/ipa/libipa/camera_sensor_helper.cpp:900:1: note: in expansion of macro 'REGISTER_CAMERA_SENSOR_HELPER' 900 | REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ ninja: subcommand failedS Макрос создаёт глобальную переменную с именем от класса, поэтому дважды один класс зарегистрировать нельзя. Нужен второй, наследник: cd ~/libcamera && python3 - <<'EOF' p='src/ipa/libipa/camera_sensor_helper.cpp' s=open(p).read() s=s.replace('''REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx586) REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586)''', '''REGISTER_CAMERA_SENSOR_HELPER("imx586", CameraSensorHelperImx586) /* The same part, under the name the other compatible gives its entity. */ class CameraSensorHelperImx582 : public CameraSensorHelperImx586 { }; REGISTER_CAMERA_SENSOR_HELPER("imx582", CameraSensorHelperImx582)''') open(p,'w').write(s) print("исправлено") EOF ninja -C build Заодно и по смыслу честнее: два имени — два класса, второй просто наследует первый без изменений. 💰 23.28₽ · ⬇️ 971.2k / ⬆️ 743 · opus-5 · max · 12с
Ы LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/cam -l [1:14:30.339139896] [19899] INFO Camera camera_manager.cpp:340 libcamera v0.0.0+1-244572db-dirty (2026-08-01T19:01:32GMT) [1:14:30.344918075] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 2 on pad 0/0: Not a tty [1:14:30.344997815] [19900] WARN CameraSensor camera_sensor_legacy.cpp:402 'ov8856 13-0036': The PixelArraySize property has been defaulted to 1640x1232 [1:14:30.345040836] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 1 on pad 0/0: Not a tty [1:14:30.345059950] [19900] WARN CameraSensor camera_sensor_legacy.cpp:413 'ov8856 13-0036': The PixelArrayActiveAreas property has been defaulted to (0, 0)/1640x1232 [1:14:30.345080263] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [1:14:30.345103075] [19900] WARN CameraSensor camera_sensor_legacy.cpp:421 'ov8856 13-0036': Failed to retrieve the sensor crop rectangle [1:14:30.345119690] [19900] WARN CameraSensor camera_sensor_legacy.cpp:427 'ov8856 13-0036': The sensor kernel driver needs to be fixed [1:14:30.345137242] [19900] WARN CameraSensor camera_sensor_legacy.cpp:429 'ov8856 13-0036': See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information [1:14:30.345991617] [19900] WARN CameraSensor camera_sensor_legacy.cpp:502 'ov8856 13-0036': No sensor delays found in static properties. Assuming unverified defaults. [1:14:30.891269282] [19900] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8856.yaml' not found for IPA module 'simple', falling back to '/usr/local/share/libcamera/ipa/simple/uncalibrated.yaml' [1:14:30.891483345] [19900] ERROR V4L2 v4l2_subdevice.cpp:1192 'ov8856 13-0036': Unable to get rectangle 0 on pad 0/0: Not a tty [1:14:30.891564855] [19900] WARN CameraSensor camera_sensor_legacy.cpp:881 'ov8856 13-0036': The analogue crop rectangle has been defaulted to the active area size [1:14:30.934827685] [19901] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/local/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 24 [1:14:30.946549981] [19900] INFO Camera camera_manager.cpp:223 Adding camera '/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36' for pipeline handler simple [1:14:30.952784671] [19900] WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'imx586.yaml' not found for IPA module 'simple', falling back to '/usr/local/share/libcamera/ipa/simple/uncalibrated.yaml' [1:14:30.991114218] [19902] INFO IPAProxySoftWorker soft_ipa_proxy_worker.cpp:443 Starting worker for IPA module /usr/local/lib/libcamera/ipa/ipa_soft_simple.so with IPC fd = 29 [1:14:31.002176045] [19900] 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 Патч работает: обе жалобы про imx586 исчезли — и про свойства, и про пересчёт усиления. Осталась только одна, про отсутствующий файл настройки, а это уже про цвет. Снимай при свете: sudo pkill -f wireplumber; sudo pkill -f pipewire LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/cam -c 2 --capture=8 \ -s width=1920,height=1080,pixelformat=RGB888 --file=/tmp/lc_#.rgb convert -size 1920x1080 -depth 8 bgra:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc.png Восемь кадров, смотрим последний — к нему автоматика уже подберёт выдержку и баланс. Что осталось из вывода: imx586.yaml не найден — файл цветовой настройки. Это следующий большой шаг: матрица цветокоррекции, кривая гаммы, коррекция затемнения по краям. Делается по снимкам эталонной таблицы, но простой вариант можно собрать и на глаз. Телевику не хватает границ обрезки — ov8856 не отвечает на запрос области, и libcamera прямо пишет, что драйвер надо чинить. Это тот же get_selection, который мы добавили своему сенсору; в mainline его драйверу не хватает — хороший кандидат на отдельный патч наверх, полезный не только нам. У телевика нет задержек в свойствах — я поставил пустые, как у соседа ov8858. Если понадобится, проставим двойки, это одна строка. 💰 24.21₽ · ⬇️ 973.8k / ⬆️ 1.1k · opus-5 · max · 24сЫ cam0: Capture 8 frames 4532.762104 (0.00 fps) cam0-stream0 seq: 000000 bytesused: 6220800 4532.795425 (30.01 fps) cam0-stream0 seq: 000001 bytesused: 6220800 4532.828749 (30.01 fps) cam0-stream0 seq: 000002 bytesused: 6220800 4532.862080 (30.00 fps) cam0-stream0 seq: 000003 bytesused: 6220800 4532.895395 (30.02 fps) cam0-stream0 seq: 000004 bytesused: 6220800 4532.928715 (30.01 fps) cam0-stream0 seq: 000005 bytesused: 6220800 4532.962034 (30.01 fps) cam0-stream0 seq: 000006 bytesused: 6220800 4532.995354 (30.01 fps) cam0-stream0 seq: 000007 bytesused: 6220800 WARNING: The convert command is deprecated in IMv7, use "magick" instead of "convert" or "magick convert" convert: unexpected end-of-file '/tmp/lc_cam0-stream0-000007.rgb': No such file or directory @ error/bgr.c/ReadBGRImage/247. convert: no images defined `/tmp/lc.png' @ error/deprecate.c/ConvertImageCommand/3375.S Тридцать кадров в секунду, непрерывно — это уже видео, а не одиночные снимки. И размер кадра 6 220 800 это ровно 1920×1080×3, то есть на этот раз формат упакованный, без лишнего байта. Файла нет, скорее всего, из-за имени — посмотри, что получилось: ls -l /tmp/lc_* 2>/dev/null | tail -3 А переводить надо как трёхбайтовый, и с учётом того, что libcamera называет форматы по упакованному значению, порядок в памяти обратный: F=$(ls -t /tmp/lc_*.rgb | head -1); echo $F magick -size 1920x1080 -depth 8 bgr:$F /tmp/lc.png magick -size 1920x1080 -depth 8 rgb:$F /tmp/lc_rgb.png Какая из двух картинок выйдет с правильными цветами (небо синее, а не оранжевое), та и верная — заодно и запомним. Кстати, вычёркиваем ещё пункт из списка «чего не хватает до main»: видео с камеры работает, тридцать кадров при 1920×1080 через программный ISP. 💰 23.85₽ · ⬇️ 975.3k / ⬆️ 1.1k · opus-5 · max · 20сЫ S Файлы на месте, а convert ругался потому, что я велел ему читать четырёхбайтовый формат — он ждал 8 294 400 байт и упёрся в конец файла на 6 220 800. Правильно так: magick -size 1920x1080 -depth 8 bgr:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc_bgr.png magick -size 1920x1080 -depth 8 rgb:/tmp/lc_cam0-stream0-000007.rgb /tmp/lc_rgb.png Смотри обе: у одной цвета будут настоящими, у другой красное с синим переставлены. Заодно интересно, как отработала автоматика — восьмой кадр снят через четверть секунды после начала, выдержка к нему уже должна сойтись. 💰 23.03₽ · ⬇️ 976.6k / ⬆️ 438 · opus-5 · max · 14сЫ S Опиши, что именно — цветной шум, полосы, или картинка узнаётся, но цвета не те? От этого зависит, где искать. Подозрение у меня такое: libcamera на запрос 1920×1080 выбрала наш режим с бинированием четыре на четыре, а при таком сведении порядок цветовых точек может сдвинуться. Драйвер же во всех режимах объявляет один и тот же SRGGB10, и если на самом деле там, скажем, GRBG, картинка получится с переставленными цветами и «грязью» на переходах. Проверяется быстро, нашим же распаковщиком — снимем тот же режим напрямую и переберём четыре варианта: cd ~/lanekit sed -i 's/^W=[0-9]*/W=1920/; s/^H=[0-9]*/H=1080/' capture.sh EXPOSURE=1000 GAIN=400 sh capture.sh for p in rggb bggr grbg gbrg; do ~/raw10preview /tmp/frame.raw /tmp/b_$p.ppm 1920 1080 $p 2 >/dev/null 2>&1 magick /tmp/b_$p.ppm /tmp/b_$p.png done ls -l /tmp/b_*.png Какой из четырёх окажется с натуральными цветами, тот и правильный для этого режима — и тогда я поправлю драйвер, чтобы он объявлял код по режиму, а не один на всех. 💰 23.58₽ · ⬇️ 977.1k / ⬆️ 1.2k · opus-5 · max · 25сЫ S Тогда сначала отделим камеру от обработки — сними тот же режим нашим путём (команду я дал в прошлом сообщении) и посмотри /tmp/b_rggb.png. Если и там мыло, дело не в libcamera. И проверь наводку: мы её ставили на 600, но libcamera про объектив ничего не знает и наверняка оставила линзу там, где она была после её собственных настроек — то есть, скорее всего, на нуле, а это бесконечность. Для комнаты нужно ближе: D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --set-ctrl focus_absolute=600 v4l2-ctl -d "$D" --get-ctrl focus_absolute Потом повтори съёмку через libcamera — она положение линзы не трогает, так что оно останется. Ещё одно подозрение по этому режиму: у вендора он сделан бинированием четыре на четыре с длиной строки 2912, и если при таком сведении сенсор отдаёт кадр иначе, чем мы объявляем, то и получится каша. Наш собственный снимок это как раз и покажет. 💰 23.59₽ · ⬇️ 978.4k / ⬆️ 902 · opus-5 · max · 20сЫ S cd ~/lanekit D=$(media-ctl -d /dev/media0 -e "dw9768 12-000c") v4l2-ctl -d "$D" --set-ctrl focus_absolute=600 # наш путь, тот же режим 1920x1080 sed -i 's/^W=[0-9]*/W=1920/; s/^H=[0-9]*/H=1080/' capture.sh EXPOSURE=1000 GAIN=400 sh capture.sh for p in rggb bggr grbg gbrg; do ~/raw10preview /tmp/frame.raw /tmp/b_$p.ppm 1920 1080 $p 2 >/dev/null 2>&1 magick /tmp/b_$p.ppm /tmp/b_$p.png done # для сравнения — заведомо рабочий режим sed -i 's/^W=[0-9]*/W=4000/; s/^H=[0-9]*/H=3000/' capture.sh EXPOSURE=3006 GAIN=600 sh capture.sh ~/raw10preview /tmp/frame.raw /tmp/ctl.ppm 4000 3000 rggb 4 && magick /tmp/ctl.ppm /tmp/ctl.png ls -l /tmp/b_*.png /tmp/ctl.png Смотри ctl.png — он должен быть таким же нормальным, как раньше. Если он хороший, а все четыре b_*.png мутные, значит режим 1920×1080 у нас поднимается неверно, и я буду разбирать его таблицу отдельно: там бинирование четыре на четыре, длина строки 2912, и вполне может оказаться, что сенсор отдаёт кадр другого размера, чем мы объявляем приёмнику. 💰 23.25₽ · ⬇️ 979.4k / ⬆️ 722 · opus-5 · max · 13сЫ S Покажи, на чём именно споткнулось: sudo dmesg | grep -vE "link_setup|got IRQ" | tail -12 media-ctl -d /dev/media0 -p | grep -A3 "entity 171" | tail -3 v4l2-ctl -d $(media-ctl -d /dev/media0 -e msm_vfe0_video0) --get-fmt-video | tail -4 «Broken pipe» на запуске потока в camss означает несогласованность форматов по цепочке: либо какой-то пад не принял 1920×1080, либо на видеоузле остался прежний размер. Первая команда назовёт причину прямо, вторая покажет, что стоит у сенсора, третья — что на выходе. Подозреваю вот что: в драйвере, который сейчас загружен, предел органа pixel_rate жёстко привязан к первому режиму — я эту оплошность нашёл и поправил, но пересборки после правки ещё не было. Для 1920×1080 значение выходит за предел, установка молча не проходит, и дальше camss считает поток точек неверным. Если так и есть, лечится пересборкой драйвера из прибранного набора — там уже правильно. 💰 23.75₽ · ⬇️ 980.4k / ⬆️ 1.1k · opus-5 · max · 24сЫ sudo dmesg | grep -vE "link_setup|got IRQ" | tail -12 [ 4800.173239] qcom-camss ace0000.camss: VFE:0 HW Version = 1.3.0 [ 4800.173293] csid_reset called [ 4800.173322] qcom-camss ace0000.camss: CSID:0 HW Version = 2.0.0 [ 4800.173347] qcom-camss ace0000.camss: validating link "msm_csid0":1 -> "msm_vfe0_rdi0":0 [ 4800.173353] qcom-camss ace0000.camss: validating stream "msm_csid0":1:0 -> "msm_vfe0_rdi0":0:0 [ 4800.173362] qcom-camss ace0000.camss: validating link "msm_csiphy0":1 -> "msm_csid0":0 [ 4800.173367] qcom-camss ace0000.camss: validating stream "msm_csiphy0":1:0 -> "msm_csid0":0:0 [ 4800.173374] qcom-camss ace0000.camss: validating link "imx586 12-001a":0 -> "msm_csiphy0":0 [ 4800.173379] qcom-camss ace0000.camss: validating stream "imx586 12-001a":0:0 -> "msm_csiphy0":0:0 [ 4800.173385] qcom-camss ace0000.camss: v4l2_subdev_link_validate_default: media bus code does not match (source 0x00003007, sink 0x0000300f) [ 4800.173391] qcom-camss ace0000.camss: v4l2_subdev_link_validate_default: link was "imx586 12-001a":0 -> "msm_csiphy0":0 [ 4800.173399] qcom-camss ace0000.camss: Failed to start media pipeline: -32 media-ctl -d /dev/media0 -p | grep -A3 "entity 171" | tail -3 type V4L2 subdev subtype Sensor flags 0 device node name /dev/v4l-subdev16 pad0: SOURCE v4l2-ctl -d $(media-ctl -d /dev/media0 -e msm_vfe0_video0) --get-fmt-video | tail -4 Quantization : Full Range Plane 0 : Bytes per Line : 2400 Size Image : 2592000