Вот и объяснение: pipewire отдаёт две «Built-in Back Camera», и по умолчанию выбрана 56 — почти наверняка телевик, который кадров не даёт. Плюс рядом торчат пять сырых узлов camss как v4l2-устройства, в которые лезть вообще нельзя, там формат надо настраивать руками.
Посмотри, кто есть кто, и переключи умолчание:
wpctl inspect 56 | grep -iE "node.name|device.name|object.path|api\."
wpctl inspect 58 | grep -iE "node.name|device.name|object.path|api\."
Тот, у которого в пути camera@1a — широкая. Ставим его по умолчанию:
wpctl set-default 58 # подставь нужный номер
А проще всего снова убрать телевик с глаз и перезапустить pipewire, тогда выбирать будет не из чего:
echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind
systemctl --user restart pipewire wireplumber
wpctl status | grep -A4 "Sources"
После этого запускай приложение.
💰 7.70₽ · ⬇️ 265.2k / ⬆️ 920 · opus-5 · max · 16с
ВеткаВот и объяснение: pipewire отдаёт две «Built-in Back Camera», и по умолчанию выбрана 56 — почти наверняка телевик, котор…
254 сообщения · –Ы wpctl inspect 56 | grep -iE "node.name|device.name|object.path|api\." api.libcamera.location = "back" api.libcamera.path = "/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" api.libcamera.rotation = "180" device.name = "libcamera_device./base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" factory.name = "api.libcamera.source" * node.name = "libcamera_input._base_soc_0_cci_ac4a000_i2c-bus_1_camera_36" * object.path = "libcamera:/base/soc@0/cci@ac4a000/i2c-bus@1/camera@36" wpctl inspect 58 | grep -iE "node.name|device.name|object.path|api\." api.libcamera.location = "back" api.libcamera.path = "/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a" api.libcamera.rotation = "180" device.name = "libcamera_device./base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a" factory.name = "api.libcamera.source" * node.name = "libcamera_input._base_soc_0_cci_ac4a000_i2c-bus_0_camera_1a" * object.path = "libcamera:/base/soc@0/cci@ac4a000/i2c-bus@0/camera@1a"S Как и думал: 56 — телевик (camera@36), 58 — широкая (camera@1a), а по умолчанию стоит именно телевик. Переключаем: wpctl set-default 58 wpctl status | grep -A4 "Sources" Звёздочка должна переехать на 58. И запускай приложение. Если оно всё равно упрётся (некоторые программы сами перебирают устройства и берут первое, игнорируя умолчание) — тогда убираем телевик совсем: echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user restart pipewire wireplumber 💰 7.01₽ · ⬇️ 266.7k / ⬆️ 339 · opus-5 · max · 8сЫ S Тогда добьём телевик и посмотрим, какие форматы вообще предлагает широкая — подозреваю, что приложение просит YUV, а программный ISP отдаёт только RGB, и переговоры о формате рушатся. echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user restart pipewire wireplumber sleep 2 wpctl status | grep -A4 "Sources" И список форматов узла (номер возьми из свежего wpctl status, он мог поменяться): pw-cli enum-params 58 EnumFormat 2>/dev/null | grep -E "format|Video|size|framerate" | head -30 Если там окажутся только RGB/BGR/ARGB, а привычных NV12/YUY2 нет — вот и причина. Тогда варианта два: включить в pipewire промежуточное преобразование или добавить вывод в NV12 в сам программный ISP (он это умеет в свежих версиях, вопрос сборки). 💰 7.46₽ · ⬇️ 267.1k / ⬆️ 1.1k · opus-5 · max · 19сЫ S Занят — pipewire держит камеру открытой. Останови его и повтори: systemctl --user stop pipewire wireplumber pipewire.socket 2>/dev/null pkill -f wireplumber; pkill -f pipewire; sleep 1 echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user start pipewire wireplumber Но это второстепенно. Гораздо важнее список форматов — без него мы гадаем. Номер узла возьми свежий: wpctl status | grep -A4 Sources pw-cli enum-params <номер> EnumFormat 2>&1 | grep -E "format:|size:|Video" | head -30 💰 7.38₽ · ⬇️ 268.3k / ⬆️ 721 · opus-5 · max · 13сЫ S Так и есть — pipewire завис, скорее всего именно на телевике: он открыл камеру, ждёт кадры, а их нет. Это, кстати, объясняет и ошибку приложения: оно спрашивает у зависшего сервиса. Бей жёстко: sudo pkill -9 -f pipewire; sudo pkill -9 -f wireplumber; sleep 1 echo 13-0036 | sudo tee /sys/bus/i2c/drivers/ov8856/unbind systemctl --user start pipewire wireplumber 2>/dev/null || (pipewire & wireplumber &) sleep 2; wpctl status | grep -A4 Sources Должна остаться одна камера. И сразу запускай приложение — подозреваю, что теперь оно заработает без всяких форматных плясок. 💰 7.11₽ · ⬇️ 269.1k / ⬆️ 646 · opus-5 · max · 13сЫ S Ошибка не «занято», а именно ввод-вывод — значит дело не в pipewire. Проверим, как оно вообще привязано, и попробуем через модуль: ls -l /sys/bus/i2c/devices/13-0036/driver 2>/dev/null ls /sys/bus/i2c/drivers/ | grep -i ov lsmod | grep -i ov8856 Если это модуль — выгружаем целиком, так надёжнее: sudo rmmod ov8856 && dmesg | tail -5 А если он вкомпилирован в ядро, останется путь через дерево: дописать телевику status = "disabled", пересобрать dtb и перезагрузиться. Тогда он исчезнет и из libcamera, и из pipewire до тех пор, пока мы им не займёмся всерьёз. 💰 7.67₽ · ⬇️ 269.8k / ⬆️ 1.1k · opus-5 · max · 23сЫ S Ясно: модуль держит camss, потому что телевик прописан в его графе. Выгрузить camss тоже нельзя — тогда он будет ждать пропавший сенсор и не поднимет вообще ни одной камеры. Значит убираем телевик из дерева. Это одна пересборка dtb и перезагрузка, зато уходит и зависание pipewire, и ошибка приложения: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Отключённый узел v4l2 пропускает молча, так что camss спокойно поднимется с одной камерой. После перезагрузки проверь: cam -l | grep -E "^[0-9]:" Должна остаться одна строка. Дальше приложение — думаю, заведётся сразу. Когда займёмся телевиком, вернём одной строкой: заменить disabled на okay и пересобрать dtb. 💰 8.56₽ · ⬇️ 271.1k / ⬆️ 1.7k · opus-5 · max · 30сЫ S Переменные потерялись — восстановим: F=$(find / -name "sm7150-xiaomi-davinci.dtsi" -not -path "/proc/*" 2>/dev/null | head -1) K=${F%/arch/*} echo "$K"; echo "$F" И дальше как было: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync 💰 7.40₽ · ⬇️ 272.8k / ⬆️ 456 · opus-5 · max · 10сЫ Ы S Тогда просто прямыми путями: K=/home/user/linux-7.2 F=$K/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi ls -l $F && tail -5 $F Если файл на месте — дописываем и собираем: cat >> $F <<'EOF' /* временно, пока телевик не отдаёт кадры: он вешает pipewire и приложения */ &cci0_i2c1 { camera@36 { status = "disabled"; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync 💰 7.02₽ · ⬇️ 273.4k / ⬆️ 566 · opus-5 · max · 10сЫ S Сбрасывает на диск всё, что ядро ещё держит в памяти. Запись файла на самом деле сначала попадает в кеш страниц, а на флеш уезжает потом, когда ядру удобно — обычно в пределах секунд. Нам это важно ровно перед перезагрузкой: если выключить телефон сразу после dtbs_install, есть шанс, что новый dtb физически не долетел, и на диске останется обрезанный файл. Загрузчик такой прочитает и не запустится — а чинить это придётся уже из рекавери. Пара секунд на команду против такого сценария — выгодный обмен. По той же причине я его пишу и после установки ядра. 💰 6.80₽ · ⬇️ 274.0k / ⬆️ 328 · opus-5 · max · 10сЫ S Хороший сдвиг — значит формат больше не спотыкается, теперь просто не находит устройство. Проверим, что видно на трёх уровнях: cam -l | grep -E "^[0-9]:" wpctl status | grep -A5 Sources systemctl --user status pipewire wireplumber --no-pager 2>/dev/null | grep -E "Active|Loaded" | head -4 Если cam камеру видит, а pipewire нет — просто перезапусти сеанс медиасервера: systemctl --user restart pipewire wireplumber sleep 2; wpctl status | grep -A5 Sources Подозреваю, что после перезагрузки wireplumber успел прочитать граф раньше, чем camss закончил регистрацию, и остался ни с чем. 💰 7.04₽ · ⬇️ 274.5k / ⬆️ 552 · opus-5 · max · 11сЫ S Хуже — теперь camss вообще не видит ни одного сенсора. Значит зацепило и широкую. Смотрим, что происходило при загрузке: dmesg | grep -iE "imx586|dw976|ov8856|camss|cci" | tail -30 Заодно проверим, что в собранном дереве получилось на самом деле: dtc -I fs -O dts /proc/device-tree/soc@0/cci@ac4a000 2>/dev/null | grep -A3 -E "camera@1a|camera@36" | head -20 Подозреваю одно из двух: либо мой блок приписался не туда и отключил не тот узел, либо сенсор в этот раз не поднялся по другой причине — например, объектив снова не успел, а он у нас в зависимостях. 💰 7.45₽ · ⬇️ 275.3k / ⬆️ 844 · opus-5 · max · 16сЫ sudo dmesg | grep -iE "imx586|dw976|ov8856|camss|cci" | tail -30 [ 0.643109] platform ace0000.camss: Adding to iommu group 8 [ 12.161971] calling qcom_cci_driver_init+0x0/0xff8 [i2c_qcom_cci] @ 683 [ 12.218138] /soc@0/camss@ace0000: Fixed dependency cycle(s) with /soc@0/cci@ac4a000/i2c-bus@0/camera@1a [ 12.218239] /soc@0/cci@ac4a000/i2c-bus@0/camera@1a: Fixed dependency cycle(s) with /soc@0/camss@ace0000 [ 12.245726] probe of ac4a000.cci returned 0 after 79230 usecs [ 12.261155] probe of ac4b000.cci returned 0 after 15397 usecs [ 12.277401] initcall qcom_cci_driver_init+0x0/0xff8 [i2c_qcom_cci] returned 0 after 2585 usecs [ 12.451174] calling qcom_camss_driver_init+0x0/0xff8 [qcom_camss] @ 663 [ 12.467864] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@0/endpoint of port 0: 4 lanes, first 0 [ 12.472200] qcom-camss ace0000.camss: parsed endpoint /soc@0/camss@ace0000/ports/port@3/endpoint of port 3: 2 lanes, first 0 [ 12.498440] probe of ace0000.camss returned 0 after 45022 usecs [ 12.502731] initcall qcom_camss_driver_init+0x0/0xff8 [qcom_camss] returned 0 after 34539 usecs [ 12.676818] calling dw9768_i2c_driver_init+0x0/0xff8 [dw9768] @ 665 [ 12.705469] initcall dw9768_i2c_driver_init+0x0/0xff8 [dw9768] returned 0 after 2480 usecs [ 12.724098] calling imx586_i2c_driver_init+0x0/0xff8 [imx586] @ 681 [ 12.737141] imx586 12-001a: IMX582 detected [ 12.742400] imx586 12-001a: signalling mode default 0x02 [ 12.762162] imx586 12-001a: lane mode default 0x03 [ 12.790466] initcall imx586_i2c_driver_init+0x0/0xff8 [imx586] returned 0 after 61465 usecsS Вот и объяснение: сенсор поднялся нормально («IMX582 detected»), но camss всё равно ждёт обе камеры — в логе видно, что он разобрал и порт 0, и порт 3. Отключённый сенсор к порту 3 больше не привязывается, ожидание не завершается никогда, и граф так и не публикуется. Отсюда «No sensor found». Значит убирать надо не только сенсор, но и вход, к которому он подключён: cat >> $F <<'EOF' /* вместе с отключённым телевиком убираем и его вход в camss, иначе приёмник ждёт сенсор, которого нет, и граф не публикуется */ &camss { ports { port@3 { /delete-node/ endpoint; }; }; }; EOF cd $K && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync После перезагрузки в логе должна остаться одна строка parsed endpoint ... port 0, а cam -l показать одну камеру. Если предпочитаешь наоборот — вернуть телевик и не трогать дерево, скажи, тогда просто уберём оба добавленных блока и спрячем лишний узел правилом wireplumber, без перезагрузки. 💰 9.01₽ · ⬇️ 277.0k / ⬆️ 1.8k · opus-5 · max · 31сЫ S Понял, больше без переменных. cat >> /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi <<'EOF' /* вместе с отключённым телевиком убираем и его вход в camss, иначе приёмник ждёт сенсор, которого нет, и граф не публикуется */ &camss { ports { port@3 { /delete-node/ endpoint; }; }; }; EOF cd /home/user/linux-7.2 && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync После перезагрузки: sudo dmesg | grep "parsed endpoint" cam -l | grep -E "^[0-9]:" Ждём одну строку про порт 0 и одну камеру в списке. 💰 7.54₽ · ⬇️ 278.9k / ⬆️ 425 · opus-5 · max · 7сЫ arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi:195.24-201.6: ERROR (phandle_references): /soc@0/cci@ac4a000/i2c-bus@1/camera@36/port/endpoint: Reference to non-existent node or label "csiphy3_ep" arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi:195.24-201.6: ERROR (phandle_references): /soc@0/cci@ac4a000/i2c-bus@1/camera@36/port/endpoint: Reference to non-existent node or label "csiphy3_ep" ERROR: Input tree has errors, aborting (use -f to force output) ERROR: Input tree has errors, aborting (use -f to force output) make[3]: *** [scripts/Makefile.dtbs:140: arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci-visionox.dtb] Error 2 make[3]: *** Waiting for unfinished jobs.... make[3]: *** [scripts/Makefile.dtbs:140: arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci-samsung.dtb] Error 2 make[2]: *** [scripts/Makefile.build:549: arch/arm64/boot/dts/qcom] Error 2 make[1]: *** [/home/user/linux-7.2/Makefile:1629: dtbs] Error 2 make: *** [Makefile:248: __sub-make] Error 2S Логично: я удалил вход, а сенсор телевика на него ссылается. Раз он нам сейчас не нужен — удалим его целиком, тогда и ссылка исчезнет: cat >> /home/user/linux-7.2/arch/arm64/boot/dts/qcom/sm7150-xiaomi-davinci.dtsi <<'EOF' /* сенсор телевика ссылается на удалённый вход, поэтому убираем и его */ &cci0_i2c1 { /delete-node/ camera@36; }; EOF cd /home/user/linux-7.2 && make dtbs -j8 && sudo make dtbs_install INSTALL_DTBS_PATH=/boot/dtbs && sync Когда вернёмся к телевику, просто снесём эти три добавленных блока в конце файла — они идут подряд и помечены комментариями. 💰 7.75₽ · ⬇️ 279.9k / ⬆️ 911 · opus-5 · max · 15сЫ S Значит телевик был ни при чём — ошибка от самой широкой. Надо понять, каким путём приложение вообще ходит в камеру: ldd /usr/bin/plasma-camera | grep -iE "camera|pipewire|gstreamer|multimedia|Qt6Multimedia" ls /usr/lib/qt6/plugins/multimedia/ 2>/dev/null Подозрение такое: plasma-camera построен на QtMultimedia, а у неё на Linux по умолчанию бэкенд ffmpeg, который умеет только сырой V4L2. Он видит наши узлы camss (/dev/video0 и компанию), пытается открыть их как обычную веб-камеру — и получает отказ, потому что там нужен предварительно настроенный граф и упакованный raw10, а не готовый YUV. Если так, то приложение к нашей камере просто не подходит, и лечится это не настройкой, а другим потребителем: qcam из нашей сборки, Megapixels, или Qt с бэкендом gstreamer плюс libcamerasrc. Но сначала посмотрим на вывод — гадать не будем. 💰 8.17₽ · ⬇️ 280.8k / ⬆️ 1.3k · opus-5 · max · 22сЫ ldd /usr/bin/plasma-camera | grep -iE "camera|pipewire|gstreamer|multimedia|Qt6Multimedia" libQt6Multimedia.so.6 => /lib/libQt6Multimedia.so.6 (0xffff9ff38000) libcamera.so.0.7 => /lib/libcamera.so.0.7 (0xffff9d82a000) libcamera-base.so.0.7 => /lib/libcamera-base.so.0.7 (0xffff9fc71000) ls /usr/lib/qt6/plugins/multimedia/ 2>/dev/null libffmpegmediaplugin.so libgstreamermediaplugin.so в /lib/ если чо наша libcameraS Тогда смотрим, что именно оно просит. Включим логи QtMultimedia: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 QT_LOGGING_RULES="qt.multimedia*=true" plasma-camera 2>&1 | tail -30 И заодно проверим, кто там тянет libcamera — сама Qt или приложение: ldd /lib/libQt6Multimedia.so.6 | grep -i camera ldd /usr/lib/qt6/plugins/multimedia/libffmpegmediaplugin.so | grep -i camera Есть ещё второй бэкенд, gstreamer — его можно попробовать прямо так: QT_MEDIA_BACKEND=gstreamer plasma-camera 2>&1 | tail -20 Ключевой вопрос — открывает ли оно камеру через libcamera или лезет напрямую в /dev/video0. Во втором случае оно обречено: наши узлы отдают упакованный raw10 и требуют настроенного графа, обычная камера так себя не ведёт. 💰 9.36₽ · ⬇️ 282.4k / ⬆️ 2.1k · opus-5 · max · 32сЫ S Тогда спросим саму libcamera — она логирует независимо от Qt: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 LIBCAMERA_LOG_LEVELS=*:DEBUG plasma-camera 2>&1 | grep -iE "validate|configur|adjust|pixelformat|stream" | tail -40 Там будет видно, какой формат и размер оно запрашивает и на чём проверка говорит «нет». И для сравнения — заведомо рабочий потребитель на Qt, если он собрался: ls -l /home/user/libcamera/build/src/apps/qcam/qcam 2>/dev/null && \ /home/user/libcamera/build/src/apps/qcam/qcam 2>&1 | tail -5 Если qcam покажет живую картинку — значит виновата логика запроса в plasma-camera, и чинить надо её, а не камеру. 💰 8.20₽ · ⬇️ 284.7k / ⬆️ 736 · opus-5 · max · 13сЫ S А, понял, в чём фокус: tail ничего не покажет, пока программа не завершится, а она висит с открытым окном. Пиши в файл: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 LIBCAMERA_LOG_LEVELS=*:DEBUG plasma-camera > /tmp/pc.log 2>&1 & sleep 8 kill %1 wc -l /tmp/pc.log grep -iE "validate|configur|adjust|pixelformat|Invalid" /tmp/pc.log | tail -30 Если файл окажется пустым и после этого — значит приложение вообще не доходит до libcamera, и тогда смотрим просто первые строки: head -30 /tmp/pc.log 💰 7.49₽ · ⬇️ 285.4k / ⬆️ 650 · opus-5 · max · 10сЫ S Вот и причина, целиком видно в логе: Picked 8000x6000 ... for max processed stream size 7992x6000 Failed to open /dev/dma_heap/system: No such file or directory Using /dev/udmabuf Failed to create dma buf for frame-0: Invalid argument Приложение не задаёт размер, libcamera по умолчанию берёт максимум — 7992×6000 в ABGR8888, это 191 МБ на кадр. Куч dma-heap в ядре нет, поэтому libcamera выделяет через udmabuf, а у того по умолчанию стоит потолок в 64 МБ на буфер. Отсюда Invalid argument и «конфигурация недопустима». Потолок правится на лету: cat /sys/module/udmabuf/parameters/size_limit_mb echo 1024 | sudo tee /sys/module/udmabuf/parameters/size_limit_mb И запускай приложение снова. Оно должно завестись, но учти: полный кадр через программный демозаик — это порядка секунды на кадр, видоискатель будет ползти. Зато мы убедимся, что причина именно эта. А правильное лечение — включить в ядре кучи dma-buf, тогда libcamera возьмёт нормальный аллокатор: CONFIG_DMABUF_HEAPS=y CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMABUF_HEAPS_CMA=y Это в следующую пересборку ядра, вместе с патчем get_selection для телевика. 💰 13.59₽ · ⬇️ 296.6k / ⬆️ 1.9k · opus-5 · max · 33сЫ S Всё сходится: это не фильтр, а квадро-байер. В полном разрешении сенсор отдаёт пиксели группами 2×2 одного цвета — вместо привычного шахматного узора получается «крупная клетка», и обычный демозаик libcamera раскладывает её в зелёное с точками. Мы это уже видели в своих кадрах: полный кадр надо разбирать шаблоном quad, а не rggb. Проще говоря, режим 8000×6000 годится только как сырьё для нашего инструмента, а для просмотра нужен любой другой — они все с бинингом и дают честный байер. Плюс задержка сразу упадёт: 1080p через программный демозаик это 24 мс на кадр против почти секунды сейчас. Загляни в настройки приложения — там должен быть выбор разрешения, судя по логу оно знает про все четыре режима (1920×1080, 4000×2250, 4000×3000, 8000×6000). Поставь 1920×1080. Если селектора там нет, сделаем иначе: добавлю в драйвер параметр модуля, скрывающий полноразмерный режим от перечисления, и тогда libcamera будет брать максимумом 4000×3000. Наши собственные скрипты полный кадр всё равно снимут, они ходят напрямую. 💰 9.31₽ · ⬇️ 298.8k / ⬆️ 1.5k · opus-5 · max · 28сЫ S Понял — значит выбор разрешения у приложения не предусмотрен вообще, оно берёт умолчание libcamera, а умолчание там — «самый большой размер». В коде рядом с этим местом даже висит комментарий разработчиков «надо бы придумать способ выбирать по-умному». Придумаем. Правка на три десятка строк в нашей сборке: для видоискателя брать не максимум, а самый большой размер в пределах разумного. python3 - <<'PY' p = "/home/user/libcamera/src/libcamera/pipeline/simple/simple.cpp" s = open(p).read() anchor = """\t\tcfg.pixelFormat = formats.begin()->first; \t\tcfg.size = formats.begin()->second[0].max; """ add = """ \t\t/* \t\t * Taking the largest size is a poor default for a software ISP: \t\t * on this board it selects a 48 megapixel quad bayer mode that no \t\t * debayer can interpret and that costs about a second a frame. \t\t * Prefer the largest size that stays sane for the role. \t\t */ \t\tif (role != StreamRole::Raw) { \t\t\tconst Size limit = role == StreamRole::StillCapture \t\t\t\t\t ? Size(4096, 3072) : Size(1920, 1080); \t\t\tSize best; \t\t\tfor (const SizeRange &range : formats.begin()->second) { \t\t\t\tconst Size &sz = range.max; \t\t\t\tif (sz.width <= limit.width && sz.height <= limit.height && \t\t\t\t sz.width * sz.height > best.width * best.height) \t\t\t\t\tbest = sz; \t\t\t} \t\t\tif (!best.isNull()) \t\t\t\tcfg.size = best; \t\t} """ assert anchor in s, "не нашёл место вставки" open(p, "w").write(s.replace(anchor, anchor + add, 1)) print("вставлено") PY cd /home/user/libcamera && ninja -C build && sudo ninja -C build install Пересборка тут только одного файла, минуты две. После неё видоискатель пойдёт в 1920×1080 — это 24 мс на кадр вместо секунды, и телефон перестанет греться. Съёмка фото останется в 4000×3000, а полноразмерный квадро-байер будет доступен только нашим скриптам, которые умеют его разбирать правильно. 💰 28.10₽ · ⬇️ 905.8k / ⬆️ 5.2k · opus-5 · max · 81сЫ S Согласен, это лечение симптома в чужом коде. Тогда так. Сначала проверим, что вообще доступно как потребитель: ls /home/user/libcamera/build/src/apps/qcam/qcam 2>/dev/null && echo "qcam собран" apk search -q megapixels 2>/dev/null apk search -q snapshot 2>/dev/null | head -5 qcam — родной просмотрщик libcamera, он принимает те же ключи потока, что и cam, то есть разрешение задаётся явно: XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 \ /home/user/libcamera/build/src/apps/qcam/qcam -c 1 -s width=1920,height=1080,pixelformat=RGB888 Это сразу даст живой видоискатель без всяких патчей — годится как проверка и как рабочий инструмент на сегодня. А правильный потребитель для телефона — Megapixels: он специально сделан под такие камеры, умеет выбирать режим для превью и отдельный для снимка, и настраивается файлом описания устройства. Как раз наш случай: превью в 1920×1080, снимок в 4000×3000, а полноразмерный квадро-байер вообще не трогаем. Если он есть в репозитории, поставим и напишем ему описание для давинчи — заодно это готовый кусок для порта в pmOS. plasma-camera в этой роли слабоват: он не даёт выбрать разрешение вообще, что для телефона с четырьмя режимами странно. Но если хочешь, потом заведём им запрос — правка там небольшая. 💰 9.94₽ · ⬇️ 307.0k / ⬆️ 1.3k · opus-5 · max · 23сЫ S Значит qcam не собрался — для него нужны заголовки Qt6, которых при сборке не было. Покажи, что нашлось в репозитории: apk search -q megapixels; echo "---"; apk search -q camera | head -20 Если megapixels там есть — ставим его, это самый подходящий вариант. Если нет, вторым кандидатом идёт snapshot от GNOME (ходит через pipewire), а третьим — досборка qcam: пакеты qt6-qtbase-dev и qt6-qttools-dev, потом meson configure build -Dqcam=enabled и пересборка. 💰 7.89₽ · ⬇️ 308.3k / ⬆️ 333 · opus-5 · max · 9сЫ S Ему нужен файл описания устройства — сам он камеру не ищет, а берёт готовое описание по совместимости платы. Посмотрим, что за версия и куда класть: apk info -v megapixels apk info -L megapixels | grep -iE "config|\.ini|share" | head -20 tr '\0' '\n' < /proc/device-tree/compatible ls /usr/share/megapixels/config/ 2>/dev/null | head По этим четырём выводам я напишу описание для давинчи: превью 1920×1080, снимок 4000×3000, поворот 180, объектив через focus_absolute. Заодно это ровно тот файл, который потом уйдёт в pmOS вместе с портом. 💰 8.11₽ · ⬇️ 308.7k / ⬆️ 889 · opus-5 · max · 15сЫ Ссылка
нажмите — покажемqcom-sm7150:~$ apk info -v megapixels megapixels: GTK+4 camera app for mobile devices megapixels: https://gitlab.com/megapixels-org/Megapixels megapixels: 2389 KiB qcom-sm7150:~$ apk info -L megapixels | grep -iE "config|\.ini|share" | head -20 usr/share/applications/me.gapixels.Megapixels.desktop usr/share/glib-2.0/schemas/me.gapixels.Megapixels.gschema.xml usr/share/icons/hicolor/scalable/apps/me.gapixels.Megapixels.svg usr/share/megapixels/movie.sh usr/share/megapixels/postprocess.sh usr/share/megapixels/config/pine64,pinephone,front.dcp usr/share/megapixels/config/pine64,pinephone,rear.dcp usr/share/metainfo/me.gapixels.Megapixels.metainfo.xml qcom-sm7150:~$ tr '\0' '\n' < /proc/device-tree/compatible xiaomi,davinci-samsung xiaomi,davinci qcom,sm7150 qcom-sm7150:~$ ls /usr/share/megapixels/config/ 2>/dev/null | head google,b4s4-sdm670.conf pine64,pinephone,front.dcp pine64,pinephone,rear.dcp pine64,pinephone-pro.conf pine64,pinephone.conf pine64,pinetab.conf purism,librem5.conf samsung,midas.conf xiaomi,daisy.conf xiaomi,markw.confS Формат .conf, ключ — совместимость платы, значит нам нужен /usr/share/megapixels/config/xiaomi,davinci.conf. Покажи готовый пример, чтобы я написал наш точно по формату: cat "/usr/share/megapixels/config/xiaomi,daisy.conf" echo "==========" head -40 "/usr/share/megapixels/config/pine64,pinephone.conf" Дальше я соберу описание для давинчи: превью 1920×1080, снимок 4000×3000, поворот 180, объектив на focus_absolute — и телевик впишем сразу, чтобы потом не возвращаться. 💰 8.18₽ · ⬇️ 310.2k / ⬆️ 494 · opus-5 · max · 11сЫ Version = 1; Make: "Xiaomi"; Model: "Mi A2 Lite"; Front: { SensorDriver: "ov5675"; BridgeDriver: "qcom-camss"; Modes: ( { Width: 2592; Height: 1944; Rate: 30; Format: "GRBG10p"; Rotate: 270; Mirror: true; Pipeline: ( {Type: "Link", From: "msm_csiphy2", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_ispif0", ToPad: 0}, {Type: "Link", From: "msm_ispif0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "ov5675"}, {Type: "Mode", Entity: "msm_csiphy2"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_ispif0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); }, { Width: 1296; Height: 972; Rate: 30; Format: "GRBG10p"; Rotate: 270; Mirror: true; Pipeline: ( {Type: "Link", From: "msm_csiphy2", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_ispif0", ToPad: 0}, {Type: "Link", From: "msm_ispif0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "ov5675"}, {Type: "Mode", Entity: "msm_csiphy2"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_ispif0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); } ); }; Version = 1; Make: "PINE64"; Model: "PinePhone"; Rear: { SensorDriver: "ov5640"; BridgeDriver: "sun6i-csi"; FlashPath: "/sys/class/leds/white:flash"; IsoMin: 100; IsoMax: 64000; Modes: ( { Width: 2592; Height: 1944; Rate: 15; Format: "BGGR8"; Rotate: 270; FocalLength: 3.33; FNumber: 3.0; Pipeline: ( {Type: "Link", From: "ov5640", FromPad: 0, To: "sun6i-csi-bridge", ToPad: 0}, {Type: "Mode", Entity: "ov5640"}, {Type: "Mode", Entity: "sun6i-csi-bridge"} ); }, { Width: 1280; Height: 720; Rate: 30; Format: "BGGR8"; Rotate: 270; FocalLength: 3.33; FNumber: 3.0; Pipeline: ( {Type: "Link", From: "ov5640", FromPad: 0, To: "sun6i-csi-bridge", ToPad: 0}, {Type: "Mode", Entity: "ov5640"}, {Type: "Mode", Entity: "sun6i-csi-bridge"}S Формат понятен — Megapixels сам рулит графом через media-ctl, libcamera ему не нужна. Значит пишем описание нашей камеры: sudo tee "/usr/share/megapixels/config/xiaomi,davinci.conf" >/dev/null <<'EOF' Version = 1; Make: "Xiaomi"; Model: "Mi 9T"; Rear: { SensorDriver: "imx586"; BridgeDriver: "qcom-camss"; IsoMin: 100; IsoMax: 6400; Modes: ( { Width: 1920; Height: 1080; Rate: 30; Format: "RGGB10p"; Rotate: 180; FocalLength: 4.74; FNumber: 1.75; Pipeline: ( {Type: "Link", From: "msm_csiphy0", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "imx586"}, {Type: "Mode", Entity: "msm_csiphy0"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); }, { Width: 4000; Height: 3000; Rate: 30; Format: "RGGB10p"; Rotate: 180; FocalLength: 4.74; FNumber: 1.75; Pipeline: ( {Type: "Link", From: "msm_csiphy0", FromPad: 1, To: "msm_csid0", ToPad: 0}, {Type: "Link", From: "msm_csid0", FromPad: 1, To: "msm_vfe0_rdi0", ToPad: 0}, {Type: "Mode", Entity: "imx586"}, {Type: "Mode", Entity: "msm_csiphy0"}, {Type: "Mode", Entity: "msm_csid0"}, {Type: "Mode", Entity: "msm_vfe0_rdi0"} ); } ); }; EOF Полноразмерный режим я сюда сознательно не вписал — он квадро-байерный, и обычным демозаиком из него получается та самая зелёная сетка. Запускай. Если снова скажет, что камеры нет, посмотрим, по какой строке совместимости он ищет: megapixels 2>&1 | head -20 💰 10.54₽ · ⬇️ 312.0k / ⬆️ 2.4k · opus-5 · max · 35сЫ S Нужен его вывод — он при падении обычно пишет причину: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 megapixels > /tmp/mp.log 2>&1; echo "код выхода: $?"; tail -30 /tmp/mp.log Скорее всего одно из трёх: не понравился формат RGGB10p (тогда он ждёт другое написание), не нашёл сущность imx586 по короткому имени, или споткнулся на моих ссылках в конвейере. В логе это будет прямым текстом. 💰 8.74₽ · ⬇️ 314.5k / ⬆️ 537 · opus-5 · max · 11сЫ megapixels Found postprocessor script at /usr/share/megapixels/movie.sh [libmegapixels] Driver rejected pixfmt try: 0x3007 MPCamera: VIDIOC_STREAMON error 32, Broken pipe Initialized OpenGL ES 3.2 Found calibration file at .config MPCamera: VIDIOC_DQBUF error 22, Invalid argument Segmentation fault (core dumped)S Ключевая строка — Driver rejected pixfmt try: 0x3007. Это код SBGGR10_1X10, то есть он просит у сенсора байер «синий первый», а драйвер сейчас отдаёт «красный первый» (0x300f). Дальше по цепочке всё рушится: поток не стартует, буфер не приходит, и на этом он падает уже своим багом. Скорее всего он сам разворачивает порядок из-за Rotate: 180 — поворот на 180 градусов действительно меняет RGGB на BGGR. Проверим и подстроимся. Сначала убедись, что флипы сброшены (их могла оставить libcamera): SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl horizontal_flip,vertical_flip Если оба нули — меняем строку формата на противоположную: sudo sed -i 's/"RGGB10p"/"BGGR10p"/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" megapixels > /tmp/mp.log 2>&1; echo "код: $?"; grep -iE "pixfmt|STREAMON|DQBUF|error" /tmp/mp.log | head Если наоборот, флипы окажутся единицами — тогда порядок в конфиге правильный, а сбросить надо их: v4l2-ctl -d $SD --set-ctrl horizontal_flip=0,vertical_flip=0 💰 10.12₽ · ⬇️ 315.4k / ⬆️ 2.3k · opus-5 · max · 33сЫ S Вот и объяснение: флипы стоят в единицах, их оставила libcamera. С ними драйвер честно рапортует «синий первый», а всё остальное в конвейере ждёт «красный первый» — отсюда и отказ, и Broken pipe на старте потока. Сбрасываем флипы и возвращаем конфиг как был: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --set-ctrl horizontal_flip=0,vertical_flip=0 sudo sed -i 's/"BGGR10p"/"RGGB10p"/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" v4l2-ctl -d $SD --get-ctrl horizontal_flip,vertical_flip И запускай: export XDG_RUNTIME_DIR=/run/user/$(id -u) WAYLAND_DISPLAY=wayland-0 megapixels > /tmp/mp.log 2>&1; echo "код: $?"; grep -iE "pixfmt|STREAMON|DQBUF|error|calib" /tmp/mp.log | head Только учти: стоит запустить libcamera-приложение — и флипы вернутся, потому что она честно отрабатывает поворот модуля из дерева. Если megapixels взлетит, я допишу ему в конфиг постоянный сброс через правило udev, чтобы они не мешали друг другу. 💰 9.84₽ · ⬇️ 317.8k / ⬆️ 1.5k · opus-5 · max · 25сЫ S Так и должно быть: у вендора для задней широкой записан roll = 90, а в дереве у нас стоит rotation = <180> — это наследство форка, и оно неверно. Отсюда и расхождение. Сейчас подгони отображение: sudo sed -i 's/Rotate: 180;/Rotate: 270;/g' "/usr/share/megapixels/config/xiaomi,davinci.conf" megapixels Если перевернётся в другую сторону — поменяй на 90. А заодно это объясняет и конфликт с флипами. Сейчас в дереве написано «модуль повёрнут на 180», и libcamera честно пытается это исправить, включая оба флипа — из-за чего меняется порядок байера и ломается megapixels. Если поставить настоящие 90 градусов, libcamera флипами компенсировать уже не сможет (поворот на прямой угол так не исправляется) и просто сообщит ориентацию приложению. Флипы останутся нулевыми, и оба потребителя перестанут мешать друг другу. Правку в дерево внесу в следующий заход — она в одну строку, вместе с включением куч dma-buf в ядре. 💰 17.62₽ · ⬇️ 639.7k / ⬆️ 1.9k · opus-5 · max · 38сЫ S Скорее всего он выставляет выдержку и усиление сам, по своим умолчаниям. Посмотри, что он реально ставит, пока окно открыто: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain Для сравнения: на нормально освещённой комнате мы снимали с выдержкой около 3000 и усилением 960 в режиме 4000×3000, а в 1080p потолок выдержки примерно 1240. И загляни в интерфейс — у Megapixels есть панель с ISO и выдержкой, там же обычно переключатель автоматики. Если он стоит в ручном режиме с минимальными значениями, всё сходится. Если же окажется, что значения выставлены нормальные, а картинка всё равно тёмная — тогда дело в его собственной обработке: он берёт уровень чёрного и гамму из файла калибровки, а нашего у него нет, и он мог подставить чужой. 💰 8.67₽ · ⬇️ 321.7k / ⬆️ 764 · opus-5 · max · 16сЫ S Покажи, что реально стоит в драйвере, пока окно открыто — из интерфейса не видно, в каких единицах он меряет: SD=$(media-ctl -d /dev/media0 -e "imx586 12-001a") v4l2-ctl -d $SD --get-ctrl exposure,analogue_gain,vertical_blanking media-ctl -d /dev/media0 -p | grep -A3 "imx586" | grep fmt Смысл вот в чём: у нашего сенсора потолок выдержки зависит от режима — в 1920×1080 это около 1240 строк, в 4000×3000 около 3030. Число 360 не совпадает ни с одним, значит либо Megapixels сам ограничивает выдержку кадровой частотой из конфига (мы написали 30, а режим умеет 60), либо он показывает не строки, а что-то своё. Если окажется первое — просто поднимем предел, увеличив вертикальный интервал через тот же конфиг или сняв ограничение частоты. 💰 8.95₽ · ⬇️ 322.6k / ⬆️ 1.2k · opus-5 · max · 22сЫ S Значит сенсор уже на пределе: выдержка 1080 строк при потолке 1240, усиление максимальное 960. Кстати, это и объясняет «EXP лимит 360» — он показывает десятые доли миллисекунды, 1240 строк это как раз 36 мс. То есть темнит не сенсор, а обработка самого Megapixels. У него превью — это быстрый демозаик на видеокарте с собственной тоновой кривой, и он часто заметно темнее итогового снимка. Проверь по-настоящему: нажми затвор и посмотри результат. ls -lt ~/Pictures/ 2>/dev/null | head Файл там появляется после отработки postprocess.sh — это уже полноценная обработка, с уровнем чёрного и балансом. И заодно посмотрим, что за калибровку он нашёл, а то в логе была подозрительная строка: grep -i calib /tmp/mp.log ls -la ~/.config/megapixels* ~/.local/share/megapixels 2>/dev/null | head Если он подобрал чужой профиль (там лежат только пайнфоновские), цвет и яркость будут считаться по не тем коэффициентам — тогда просто уберём его, и он вернётся к нейтральной обработке. 💰 9.49₽ · ⬇️ 323.8k / ⬆️ 1.5k · opus-5 · max · 27сЫ S Ссылка
нажмите — покажемЭто известная бага, и её уже починили. В libmegapixels 0.2.1 есть ровно наш случай: библиотека стала брать длину строки из драйвера, потому что «не все драйверы используют минимальное дополнение для данной ширины и режима». Наш camss как раз такой — он выравнивает строку до 16 байт, и на 4000 пикселях получается 5008 вместо 5000. Отсюда и проверка, которая падает. Проверь версию: apk info -v libmegapixels megapixels 2>/dev/null Если меньше 0.2.1 — собери свежую, библиотека маленькая: cd ~ && git clone https://gitlab.com/megapixels-org/libmegapixels cd libmegapixels && meson setup build --prefix=/usr --libdir=lib --buildtype=release && ninja -C build && sudo ninja -C build install После этого снимок в 4000×3000 должен сохраниться нормально — а заодно и превью подтянется, потому что оно считает то же самое. Источники: заметки к выпуску 0.2.1, описание формата конфига, как добавлять железо в libmegapixels. 💰 32.41₽ · ⬇️ 654.1k / ⬆️ 1.8k · opus-5 · max · 54сЫ S Ы S Ы S Ы S Синева — это уже не наш конвейер, а обработка Megapixels. Он считает цвет по профилю калибровки (.dcp), а для нашего сенсора его нет — в комплекте лежат только пайнфоновские. Без профиля он берёт нейтральную матрицу, и сырой отклик сенсора уходит в синь ровно так же, как уходил в libcamera до того, как мы положили ей матрицу. Лечится тем же способом, только в его формате: снимок цветовой мишени при дневном свете, потом dcamprof считает профиль, кладём как xiaomi,davinci,rear.dcp рядом с конфигом. Это полчаса работы, но нужна мишень — сгодится и распечатанная, если печать не совсем дикая. И отдельно про кнопку съёмки. Я посмотрел исходник, проверка там до сих пор такая: assert(bytesused == (libmegapixels_mode_width_to_bytes(format, width) + libmegapixels_mode_width_to_padding(format, width)) * height); То есть приложение само вычисляет длину строки вместо того, чтобы взять её у драйвера — а camss выравнивает строку до 16 байт, и на ширине 4000 получается 5008 вместо 5000. Библиотеку это уже умеет учитывать, а приложение нет. Если хочешь снимать в 4000×3000, я сделаю патч на пару строк: брать bytesperline из VIDIOC_G_FMT. Заодно и в апстрим отправим — баг общий для всех камер на camss, не только нашей. 💰 9.62₽ · ⬇️ 333.6k / ⬆️ 1.7k · opus-5 · max · 34сЫ S Запомнил, патч на bytesperline — за мной. По синеве: сначала выясним, есть ли у него баланс белого вообще, или он просто гонит сырые каналы как есть. grep -iE "awb|balance|white|gain" /tmp/mp.log | head -10 И глянь в интерфейсе — рядом с ISO и выдержкой должен быть либо переключатель баланса, либо выбор освещения (солнце/лампа/авто). Если автоматики нет, померяем коэффициенты сами, мишень для этого не нужна. Снимем лист белой бумаги нашим путём и посчитаем, во сколько раз каналы отличаются: W=1920 H=1080 EXPOSURE=600 GAIN=200 sh ~/lanekit/capture.sh /tmp/wb.raw По этому кадру я посчитаю усиления красного и синего относительно зелёного — и дальше либо пропишем их в профиль для Megapixels, либо, что интереснее, выставим прямо в сенсор: у IMX582 есть отдельные цифровые усиления на каждый цвет (регистры 0x0210–0x0217), и их можно выставить драйвером через стандартные ручки red_balance и blue_balance. Тогда баланс будет правильным для любого потребителя, включая тех, кто про баланс вообще не думает. 💰 9.72₽ · ⬇️ 335.3k / ⬆️ 1.3k · opus-5 · max · 27сЫ S Могу — и это правильный ход, числа те же самые. Только формат другой: libcamera ест матрицу текстом, а Megapixels — профиль .dcp, это двоичный формат от Adobe с матрицей XYZ→камера. Пересчёт из нашей матрицы туда честный, я его сделаю. Сейчас посмотрю, что именно он ищет и в каком виде — в логе была строка «Found calibration file at .config», хочу понять, что он там нашёл. Да, и это самый быстрый путь: числа те же, только упаковать надо в двоичный профиль. Я разобрал их формат — это урезанный TIFF, где нужны всего два поля: матрица XYZ→камера и код освещения. Сгенерирую сам. Но одну вещь мне надо померить у тебя: точку белого. Матрица в libcamera работает поверх уже выровненных каналов, а профиль должен знать, насколько сенсор изначально «зелёный» — без этого пересчёт будет вслепую. Сними лист белой бумаги во весь кадр при том свете, где снимаешь, и посчитай средние по каналам: W=1920 H=1080 EXPOSURE=800 GAIN=300 sh ~/lanekit/capture.sh /tmp/wb.raw python3 - <<'PY' W,H,S=1920,1080,2400 d=open('/tmp/wb.raw','rb').read() def row(y): off=y*S; out=[] for i in range(0,W*5//4,5): b=d[off+i:off+i+5]; lo=b[4] out += [(b[0]<<2)|(lo&3),(b[1]<<2)|((lo>>2)&3),(b[2]<<2)|((lo>>4)&3),(b[3]<<2)|((lo>>6)&3)] return out s=[0,0,0,0]; n=0 for y in range(H//2-200,H//2+200,2): a=row(y); b=row(y+1) for x in range(W//2-200,W//2+200,2): s[0]+=a[x]; s[1]+=a[x+1]; s[2]+=b[x]; s[3]+=b[x+1]; n+=1 print("R,Gr,Gb,B =", [round(v/n-64,1) for v in s]) PY Пришли эти четыре числа. Хорошо, если они выйдут в районе 400–700: тогда и не пересвечено, и шума мало. Если больше 900 — убавь выдержку, если меньше 150 — прибавь. 💰 63.65₽ · ⬇️ 2.38M / ⬆️ 6.0k · opus-5 · max · 97сЫ S Слишком темно — значения 5–10 единиц из 1023, это почти шум, по такому балансу считать нельзя. Нужен кадр, где бумага светится хотя бы на треть шкалы. Возьми режим 4000×3000, там выдержка втрое длиннее, и посвети на лист лампой или поднеси к окну: W=4000 H=3000 EXPOSURE=3000 GAIN=960 sh ~/lanekit/capture.sh /tmp/wb.raw python3 - <<'PY' W,H,S=4000,3000,5008 d=open('/tmp/wb.raw','rb').read() def row(y): off=y*S; out=[] for i in range(0,W*5//4,5): b=d[off+i:off+i+5]; lo=b[4] out += [(b[0]<<2)|(lo&3),(b[1]<<2)|((lo>>2)&3),(b[2]<<2)|((lo>>4)&3),(b[3]<<2)|((lo>>6)&3)] return out s=[0,0,0,0]; n=0 for y in range(H//2-300,H//2+300,2): a=row(y); b=row(y+1) for x in range(W//2-300,W//2+300,2): s[0]+=a[x]; s[1]+=a[x+1]; s[2]+=b[x]; s[3]+=b[x+1]; n+=1 print("R,Gr,Gb,B =", [round(v/n-64,1) for v in s]) PY Целюсь в 300–700 по зелёному. И важно: свет должен быть один — либо только лампа, либо только окно, иначе точка белого получится смешанной и профиль будет врать в обе стороны. 💰 9.77₽ · ⬇️ 345.3k / ⬆️ 1.1k · opus-5 · max · 18сЫ S Отлично, теперь есть точка белого: зелёный 374, красный 238, синий 176. То есть сенсор, как и положено, сильно «зелёный» — нейтраль по нему (0.636, 1, 0.470), а выравнивающие усиления 1.57 и 2.13. Собираю профиль из этих чисел и нашей матрицы: Готово. Клади рядом с конфигом — имя он строит из совместимости платы и названия камеры: sudo cp ~/Downloads/davinci_rear.dcp "/usr/share/megapixels/config/xiaomi,davinci,rear.dcp" megapixels 2>&1 | grep -i calib Должна появиться строка «Found calibration file at /usr/share/megapixels/config/xiaomi,davinci,rear.dcp». Что внутри: матрица XYZ→камера, посчитанная из нашей imx586.yaml и твоего замера, плюс прямая матрица для рендера и код освещения D65. Проверка сошлась — белая точка профиля приходит ровно в D50, как требует формат. Одно уточнение: я пометил профиль дневным светом. Если ты снимал лист под лампой накаливания, а не у окна, скажи — пересчитаю по тёплой матрице и помечу как лампу, иначе он будет слегка желтить на дневном и уводить в синь под лампой. 💰 40.43₽ · ⬇️ 1.40M / ⬆️ 5.3k · opus-5 · max · 80сЫ S Держи командой: sudo mkdir -p /usr/share/megapixels/config base64 -d > /tmp/davinci_rear.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAFQAAAPjGAgAyAAAApAAA ABTHCgAJAAAA1gAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgCOLwAAECcAALjx//8QJwAAbvr//xAn AADG8v//ECcAAOAsAAAQJwAANQYAABAnAABg////ECcAAK4EAAAQJwAAIA0AABAnAABNaSA5VCBJ TVg1ODIgKG1lYXN1cmVkIG5ldXRyYWwsIGdlbmVyaWMgc29ueSBjY20pAJIXAAAQJwAAIwwAABAn AAD1AQAAECcAAF8HAAAQJwAA/CYAABAnAAC1+P//ECcAAOL+//8QJwAAcfb//xAnAADpKgAAECcA AA== EOF sudo cp /tmp/davinci_rear.dcp "/usr/share/megapixels/config/xiaomi,davinci,rear.dcp" megapixels 2>&1 | grep -i calib Ждём строку «Found calibration file at /usr/share/megapixels/config/xiaomi,davinci,rear.dcp» и нормальный цвет в превью. 💰 17.13₽ · ⬇️ 705.5k / ⬆️ 744 · opus-5 · max · 13сЫ S Нашёл причину, и это опять их баг. Смотри цикл поиска: static const char *paths[] = { "config/%s,%s.dcp", ... }; for (const char *fmt = paths[0]; fmt; fmt++) { Он идёт не по массиву путей, а по символам первой строки: fmt++ сдвигает указатель внутри "config/%s,%s.dcp". Дальше он гуляет по соседним строкам в памяти, натыкается на что-то вроде .config, проверяет access() — а это существующий каталог, проверка проходит — и радостно объявляет его файлом калибровки. Наш профиль в /usr/share при этом не смотрится никогда. Зато первая проверка, до цикла, написана правильно. Ей и воспользуемся: mkdir -p ~/.config/megapixels/config for m in "xiaomi,davinci" "xiaomi,davinci-samsung"; do for s in rear Rear; do cp /tmp/davinci_rear.dcp ~/.config/megapixels/config/"$m,$s.dcp" done done ls ~/.config/megapixels/config/ megapixels 2>&1 | grep -i calib Теперь в строке должен быть полный путь до нашего файла. Четыре копии — потому что неизвестно, по какой строке совместимости он ищет (в дереве их две) и в каком регистре передаёт имя камеры; лишние потом уберём. Этот цикл, кстати, добавляю в список на патч вместе с bytesperline — там обе правки по одной строке. 💰 19.77₽ · ⬇️ 708.6k / ⬆️ 2.7k · opus-5 · max · 44сЫ S Значит первая, правильная проверка всё равно промахнулась, и он опять свалился в кривой цикл. Посмотрим, куда именно легли файлы и что за окружение: echo "HOME=$HOME XDG_CONFIG_HOME=$XDG_CONFIG_HOME" ls -la ~/.config/megapixels/config/ Он ищет файл по шаблону $XDG_CONFIG_HOME/megapixels/config/<совместимость>,<имя камеры>.dcp, причём имя камеры берёт из нашего же конфига — то есть буквально из строки Rear:. Если имена не совпали, проверка падает, и дальше он уходит гулять по памяти. На всякий случай накидаем ещё вариантов имени: cd ~/.config/megapixels/config/ for m in "xiaomi,davinci" "xiaomi,davinci-samsung"; do for s in rear Rear REAR imx586; do cp -n /tmp/davinci_rear.dcp "$m,$s.dcp"; done done ls megapixels 2>&1 | grep -i calib Если и это не поможет, зайдём с другой стороны: запустим из пустого каталога, где нет .config, — тогда кривой цикл ничего не найдёт и напечатает «No calibration found <имя>», а нам только это имя и нужно. 💰 20.22₽ · ⬇️ 715.3k / ⬆️ 2.4k · opus-5 · max · 40сЫ S Тогда путь у него получается /home/user/.config/megapixels/config/… — то, что нам надо. Покажи, что там реально лежит: ls -la /home/user/.config/megapixels/config/ И параллельно вытащим точное имя камеры, которое он подставляет. Запусти из пустого каталога — тогда мусорные относительные пути не сработают и он честно напечатает, чего не нашёл: mkdir -p /tmp/empty && cd /tmp/empty && megapixels 2>&1 | grep -iE "calib" ; cd ~ Ожидаю строку вида «No calibration found rear» — вот это слово и есть недостающая часть имени файла. 💰 9.55₽ · ⬇️ 360.3k / ⬆️ 518 · opus-5 · max · 11сЫ ls -la /home/user/.config/megapixels/config/ total 24 drwxr-sr-x 2 user root 4096 Aug 2 11:26 . drwxr-sr-x 3 user root 4096 Aug 2 11:26 .. -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci,Rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci,rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci-samsung,Rear.dcp -rw-r--r-- 1 user root 286 Aug 2 11:26 xiaomi,davinci-samsung,rear.dcpS Файлы на месте, значит он ищет по другому имени. Точное имя он печатает сам, когда ничего не находит — но у тебя в домашнем каталоге лежит .config, и кривой цикл цепляется за него раньше. Поэтому запусти из пустого каталога: mkdir -p /tmp/empty && cd /tmp/empty && megapixels 2>&1 | grep -i calib; cd ~ Там будет строка «No calibration found <имя>» — пришли её, и я скажу, как назвать файл. Ожидаю либо Rear, либо imx586, либо, если он взял камеру не из нашего конфига, а сам, — что-нибудь вроде Qualcomm Camera Subsystem. 💰 27.66₽ · ⬇️ 1.09M / ⬆️ 2.1k · opus-5 · max · 36сЫ S Он находит корень / — то есть кривой цикл срабатывает всегда, а правильная проверка перед ним не проходит. Посмотрим, какой шаблон пути реально вкомпилирован в твою сборку (она может отличаться от того master, что я читаю): grep -ao "[^\"]*megapixels/config[^\"]*" /usr/bin/megapixels | sort -u | head grep -ao "No calibration found[^\"]*" /usr/bin/megapixels | head -3 И заодно проверим, как он вообще назвал камеру — библиотека умеет говорить подробно: MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | grep -iE "config|camera|name" | head -15 Одно из двух: либо шаблон в твоей версии другой (например, без имени камеры), либо камера называется не так, как мы думаем. Обе вещи видно прямо в этих выводах. 💰 10.04₽ · ⬇️ 364.0k / ⬆️ 1.0k · opus-5 · max · 18сЫ S Тогда ищем шаблоны профиля напрямую: grep -ao "[^\"]*\.dcp" /usr/bin/megapixels | sort -u | head Заодно интересно, что скажет отладочный лог библиотеки — из него будет видно, какой конфиг подхватился и как названа камера: MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | head -20 💰 9.10₽ · ⬇️ 365.2k / ⬆️ 340 · opus-5 · max · 7сЫ MEGAPIXELS_LOGLEVEL=debug megapixels 2>&1 | head -20 Found postprocessor script at /usr/share/megapixels/movie.sh Initialized OpenGL ES 3.2 Found calibration file at / Using shader /org/postmarketos/Megapixels/debayer.vert and /org/postmarketos/Megapixels/debayer_packed.frag Exposure limit: 324 Dropping frame Dropping frame Dropping frame L/Expose + 828 Gain + 4 Dropping frame Dropping frame Gain + 8 Dropping frame Gain + 12 Dropping frame Gain + 16 Dropping frame Dropping frame Gain + 20 grep -ao "[^\"]*\.dcp" /usr/bin/megapixels | sort -u | head config/%s,%s.dcpS Вот и разгадка: в твоей сборке зашит один шаблон, и тот относительный — config/%s,%s.dcp. То есть профиль он ищет не в ~/.config и не в /usr/share, а в подкаталоге config рядом с текущим рабочим каталогом. Строк с абсолютными путями в бинарнике просто нет. Значит кладём туда: mkdir -p ~/config for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_rear.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels 2>&1 | grep -i calib Запускать обязательно из домашнего каталога — иначе относительный путь не совпадёт. Должно напечатать «Found calibration file at config/xiaomi,davinci-samsung,Rear.dcp». Если вместо этого снова /, значит имя камеры другое — тогда переберём: положим ещё imx586 и Qualcomm Camera Subsystem. Кстати, к списку патчей добавляется третий пункт: искать профиль в нормальных местах, а не в текущем каталоге. Итого правок в Megapixels три, все маленькие. 💰 10.05₽ · ⬇️ 365.9k / ⬆️ 1.4k · opus-5 · max · 22сЫ S Верно, моя ошибка: я пометил профиль дневным светом, а замер у тебя был под тёплой лампой. Матрицу тоже надо брать тёплую. Пересчитываю: Пересчитал под лампу — тёплая матрица и код освещения «лампа накаливания» вместо дневного: base64 -d > /tmp/davinci_rear_A.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAEQAAAPjGAgAqAAAApAAA ABTHCgAJAAAAzgAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgAqMgAAECcAAMvv//8QJwAA7Pn//xAn AABr9v//ECcAAH4rAAAQJwAATAQAABAnAABe////ECcAABIGAAAQJwAA2wsAABAnAABNaSA5VCBJ TVg1ODIgKHR1bmdzdGVuLCBtZWFzdXJlZCBuZXV0cmFsKQDgFQAAECcAALMMAAAQJwAAGAMAABAn AABIBQAAECcAAI4nAAAQJwAAOvr//xAnAADo/v//ECcAAMjx//8QJwAAjC8AABAnAAA= EOF for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_rear_A.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels Но по-хорошему нужен второй замер. Профиль формата DCP умеет держать две точки — тёплую и дневную, а приложение интерполирует между ними по сцене. Сейчас там только одна, поэтому при другом свете он всё равно будет промахиваться. Так что как будет светло — повтори тот же замер у окна, при дневном свете и без ламп: W=4000 H=3000 EXPOSURE=1500 GAIN=400 sh ~/lanekit/capture.sh /tmp/wb_day.raw и прогони тот же питоновский счётчик. С двумя наборами чисел соберу полноценный профиль, и цвет станет правильным при любом освещении. 💰 21.75₽ · ⬇️ 737.3k / ⬆️ 3.0k · opus-5 · max · 43сЫ S Разобрался, почему подстановка «правильной по спецификации» матрицы даёт то красноту, то синеву. У Megapixels баланс белого считается так: avg_r = r*matrix1[0] + g*matrix1[1] + b*matrix1[2]; // и так три раза new_r = g / r; new_b = g / b; // серый мир То есть он берёт ColorMatrix1 из профиля и умножает на неё сырые каналы, хотя по формату эта матрица описывает обратное направление, XYZ→камера. Поэтому чем «правильнее» я её считаю, тем сильнее уезжает баланс — она работает у него не как матрица цвета, а как коэффициенты смешивания в автобалансе. Значит правильный ответ на твой вопрос — да, надо просто положить туда нашу матрицу из libcamera как есть. Тогда серый мир будет считаться в пространстве, близком к sRGB, ровно как в libcamera, где картинка тебе и нравилась: base64 -d > /tmp/davinci_ccm.dcp <<'EOF' PLACEHOLDER EOF Секунду, соберу файл. Готово. Кладём: base64 -d > /tmp/davinci_ccm.dcp <<'EOF' SUlSQwgAAAAFABTGAgASAAAASgAAACHGCgAJAAAAXAAAAFrGAwABAAAAFQAAAPjGAgAoAAAApAAA ABTHCgAJAAAAzAAAAAAAAABYaWFvbWkgTWkgOVQgcmVhcgCAPgAAECcAALDr//8QJwAA4Pz//xAn AAAE9///ECcAAIA+AAAQJwAAjPH//xAnAABw/v//ECcAAITq//8QJwAAHD4AABAnAABNaSA5VCBJ TVg1ODIgKGxpYmNhbWVyYSBjY20gYXMgbWF0cml4MSkAkhcAABAnAAAjDAAAECcAAPUBAAAQJwAA XwcAABAnAAD8JgAAECcAALX4//8QJwAA4v7//xAnAABx9v//ECcAAOkqAAAQJwAA EOF for m in "xiaomi,davinci-samsung" "xiaomi,davinci"; do for s in Rear rear; do cp /tmp/davinci_ccm.dcp ~/config/"$m,$s.dcp"; done done cd ~ && megapixels Внутри — та самая матрица из imx586.yaml, строка в строку, плюс честная прямая матрица для сохранения в DNG. Строки матрицы суммируются в единицу, поэтому их автобаланс теперь будет считать серый мир в почти-sRGB, как это делает libcamera. Если цвет совпадёт с тем, что тебе нравилось — значит гипотеза верна, и заодно у нас на руках готовый баг-репорт: у них в автобалансе используется матрица не того направления. 💰 55.24₽ · ⬇️ 1.87M / ⬆️ 7.8k · opus-5 · max · 122сЫ S Это уже не профиль, а их автобаланс. Смотри, что он делает: считает средний цвет кадра и тянет его к серому, причём гайны применяет к сырым каналам, а среднее считает после матрицы — то есть с обратной связью. На тёплой комнате со стенами и деревом «серый мир» уводит картинку в синь, а на мониторе (он синий) — резко в красноту. Твоё наблюдение это ровно и описывает, лучше любого лога. Проверить просто: найди в интерфейсе переключатель баланса белого и выключи автоматику. Тогда останутся коэффициенты из профиля — то есть твой же замер по белому листу, — и цвет должен встать правильно для этого света. Если переключателя нет, то дальше в этой ветке ловить нечего: у них наивный серый мир без учёта освещения, и это чинится только патчем. У нас к ним и так уже накопилось три штуки: 1. длина строки берётся расчётом вместо bytesperline — из-за этого падает съёмка на 4000×3000; 2. цикл поиска профиля бежит по символам строки вместо массива путей — поэтому он «находит» каталог /; 3. матрица ColorMatrix1 используется в автобалансе не в том направлении. Все три маленькие. Предлагаю на этом megapixels отложить: снимать у нас уже есть чем, а патчи оформим спокойно, вместе с камерными правками в ядро. 💰 10.45₽ · ⬇️ 380.9k / ⬆️ 1.4k · opus-5 · max · 29сЫ S Значит всё сходится: цвет и матрица правильные, а гуляет именно автобаланс. На нейтральной стене серому миру не с чем ошибаться, вот он и попадает в точку. По уму лечится тем, что делают взрослые ISP: балансом не по среднему, а по оценке освещения — считать корреляцию с кривой Планка и выбирать ближайшую точку белого из профиля. У нас для этого как раз есть готовая заготовка — профиль с двумя источниками, тёплым и дневным. Не хватает только второго замера, дневного. Так что предлагаю так: как будет светло, сделаешь тот же снимок белого листа у окна, и я соберу профиль с двумя точками. А параллельно оформлю три патча к Megapixels — они простые и полезны всем, кто снимает через camss. Что дальше сейчас — вернёмся к камере в ядре (там висят кучи dma-buf, поворот 90 вместо 180 и телевик), или на сегодня хватит? 💰 9.77₽ · ⬇️ 382.3k / ⬆️ 497 · opus-5 · max · 13сЫ S Сделал. Правка в пяти местах: везде, где приложение вычисляет длину строки из ширины, теперь берётся то, что сказал драйвер — mode->stride, который libmegapixels уже хранит. Заодно чинится и превью на 4000×3000, там та же арифметика. Ставим зависимости и берём исходник: sudo apk add git meson ninja-build gcc musl-dev gtk4.0-dev glib-dev \ libmegapixels-dev libdng-dev mesa-dev cd ~ && git clone https://gitlab.com/megapixels-org/Megapixels.git && cd Megapixels Кладём патч и применяем: cat > /tmp/mp_stride.py <<'PYEOF' #!/usr/bin/env python3 import sys, os root = sys.argv[1] if len(sys.argv) > 1 else "." def edit(path, pairs): p = os.path.join(root, path); s = open(p).read() for old, new in pairs: if new in s and old not in s: print(" уже применено:", path); continue if old not in s: print(" НЕ НАЙДЕНО в", path); sys.exit(1) s = s.replace(old, new, 1) open(p, "w").write(s); print(" правлено:", path) edit("src/gles2_debayer.h", [( """ uint32_t src_width, uint32_t src_height,""", """ uint32_t src_width, uint32_t src_stride, uint32_t src_height,""")]) edit("src/gles2_debayer.c", [( """ const uint32_t src_width, const uint32_t src_height,""", """ const uint32_t src_width, const uint32_t src_stride, const uint32_t src_height,"""), (""" GLuint padding_bytes = libmegapixels_mode_width_to_padding(self->format, src_width);""", """ GLuint padding_bytes = src_stride > row_length ? src_stride - row_length : libmegapixels_mode_width_to_padding(self->format, src_width);"""), ]) edit("src/process_pipeline.c", [( """static void init_gl(MPPipeline *pipeline, GdkSurface **surface)""", """/* * The length of one line as the driver reports it. Computing it from the * width only works when the driver pads lines the minimum amount; camss for * instance aligns them to sixteen bytes, so at 4000 pixels a line is 5008 * bytes and not 5000. libmegapixels keeps what the driver said in stride. */ static uint32_t mode_stride(const libmegapixels_mode *mode) { if (mode->stride) return mode->stride; return libmegapixels_mode_width_to_bytes(mode->format, mode->width) + libmegapixels_mode_width_to_padding(mode->format, mode->width); } static void init_gl(MPPipeline *pipeline, GdkSurface **surface)"""), (""" libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) + libmegapixels_mode_width_to_padding(state_proc.mode->format, state_proc.mode->width), state_proc.mode->height,""", """ mode_stride(state_proc.mode), state_proc.mode->height,"""), (""" (libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) * state_proc.mode->height));""", """ (mode_stride(state_proc.mode) * state_proc.mode->height));"""), (""" size_t size = (libmegapixels_mode_width_to_bytes(state_proc.mode->format, state_proc.mode->width) +Ы S Логично. Файл выше, дальше так: sudo apk add git meson ninja-build gcc musl-dev gtk4.0-dev glib-dev \ libmegapixels-dev libdng-dev mesa-dev cd ~ && git clone https://gitlab.com/megapixels-org/Megapixels.git python3 ~/Downloads/patch.py ~/Megapixels cd ~/Megapixels && meson setup build --prefix=/usr --buildtype=release && ninja -C build && sudo ninja -C build install cd ~ && megapixels Скрипт идемпотентный: если запустить дважды, скажет «уже применено» и ничего не испортит. Если какое-то место не найдётся — остановится и назовёт файл, значит master уехал и я поправлю. 💰 19.57₽ · ⬇️ 794.6k / ⬆️ 476 · opus-5 · max · 15сЫ S Скорее всего это не звук как таковой, а pipewire — мы его сегодня несколько раз убивали через kill -9, а он обслуживает и звук, и камеру разом. Плюс мы подменили системную libcamera своей сборкой, а pipewire её грузит своим модулем: если он на ней падает, вместе с камерой уезжает и аудио. Проверь: systemctl --user status pipewire wireplumber --no-pager 2>&1 | grep -E "Active|Main PID|error" | head wpctl status 2>&1 | head -25 И попробуй просто перезапустить: systemctl --user restart pipewire wireplumber sleep 2; wpctl status | head -20 Если после перезапуска он снова падает, посмотрим лог: journalctl --user -u pipewire -n 40 --no-pager | tail -20 Там будет видно, роняет ли его модуль камеры. Если да — просто отключим ему модуль libcamera, звук от этого не пострадает, а камерой мы и так ходим напрямую. 💰 10.09₽ · ⬇️ 397.7k / ⬆️ 895 · opus-5 · max · 16сЫ S Это уже не pipewire — карты нет на уровне ядра. Смотрим, что с ней: dmesg | grep -iE "soundwire|swr|lpass|wcd|q6|snd" | tail -30 ls /sys/class/sound/ 2>/dev/null lsmod | grep -iE "snd|soundwire|wcd|q6" | head И проверь, на месте ли то, чем мы её лечили в прошлый раз: ls -l /etc/udev/rules.d/ | grep -iE "swr|sound" Подозрение такое: сегодня мы дважды пересобирали и ставили dtb, и если установка положила другой набор файлов, загрузчик мог взять дерево от другой панели — у давинчи их две, samsung и visionox, и звуковая часть в них описана по-разному. Проверим, что в живом дереве: ls /proc/device-tree/ | head -20 tr -d '\0' < /proc/device-tree/model; echo ls /boot/dtbs/qcom/ 2>/dev/null | grep davinci 💰 10.65₽ · ⬇️ 398.7k / ⬆️ 1.1k · opus-5 · max · 20сЫ qcom-sm7150:~$ sudo dmesg | grep -iE "soundwire|swr|lpass|wcd|q6|snd" | tail -30 qcom-sm7150:~$ ls /sys/class/sound/ 2>/dev/null timer lsmod | grep -iE "snd|soundwire|wcd|q6" | head snd_soc_sm8250 12288 0 snd_soc_qcom_offload_utils 12288 1 snd_soc_sm8250 snd_soc_qcom_sdw 12288 1 snd_soc_sm8250 snd_soc_qcom_common 12288 1 snd_soc_sm8250 soc_usb 16384 1 snd_soc_qcom_offload_utils snd_usb_audio 475136 1 soc_usb snd_hwdep 20480 1 snd_usb_audio snd_usbmidi_lib 45056 1 snd_usb_audio snd_ump 28672 1 snd_usb_audio snd_rawmidi 45056 2 snd_usbmidi_lib,snd_umpS Модули кодека и SoundWire вообще не загружены — есть только машинный драйвер snd_soc_sm8250 без пользователей. То есть карта не собралась, потому что не привязались её части. dmesg пустой, потому что буфер вытеснили наши камерные логи. Смотри журнал загрузки целиком: journalctl -k -b | grep -iE "wcd|swr|soundwire|lpass|q6afe|q6asm|adsp" | tail -30 И проверь, на месте ли узлы в живом дереве: ls /proc/device-tree/soc@0/ | grep -iE "lpass|swr|audio|remoteproc" ls /proc/device-tree/ | grep -iE "sound|swr" Меня интересует одно: пропали ли сами узлы (тогда виноват dtb, который мы сегодня трижды пересобирали) или узлы на месте, но драйверы не привязались (тогда дело в модулях или в порядке загрузки, и это та же болячка, что мы лечили правилом udev). 💰 11.09₽ · ⬇️ 400.2k / ⬆️ 1.3k · opus-5 · max · 30сЫ Aug 02 10:40:34 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 6 usecs Aug 02 10:40:34 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 9 usecs Aug 02 10:40:34 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 5 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 12 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 7 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 5346 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 7 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 5 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 4 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 4 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 4 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 3 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 2 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 1 usecs Aug 02 10:40:35 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 1 usecs Aug 02 10:40:37 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 5 usecs Aug 02 10:40:37 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 5 usecs Aug 02 10:40:38 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 7 usecs Aug 02 10:40:38 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 7 usecs Aug 02 10:40:38 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 5 usecs Aug 02 10:40:38 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 2 usecs Aug 02 10:40:39 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 8 usecs Aug 02 10:40:39 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 6 usecs Aug 02 10:40:50 qcom-sm7150 kernel: probe of 62ed0000.soundwire-controller returned -517 after 10 usecs Aug 02 10:40:50 qcom-sm7150 kernel: probe of 62ef0000.soundwire-controller returned -517 after 4 usecs Aug 02 10:40:50 qcom-sm7150 kernel: platform 62ed0000.soundwire-controller: deferred probe pending: platform: supplier 62ec0000.txmacro not ready Aug 02 10:40:50 qcom-sm7150 kernel: platform 62ee0000.rxmacro: deferred probe pending: platform: wait for supplier /soc@0/pinctrl@62b52000/rx-swr-active-state Aug 02 10:40:50 qcom-sm7150 kernel: platform 62ef0000.soundwire-controller: deferred probe pending: platform: supplier 62ee0000.rxmacro not ready Aug 02 10:40:50 qcom-sm7150 kernel: platform 62ec0000.txmacro: deferred probe pending: platform: wait for supplier /soc@0/pinctrl@62b52000/tx-swr-active-state Aug 02 10:40:50 qcom-sm7150 kernel: platform audio-codec: deferred probe pending: platform: wait for supplier /soc@0/pinctrl@62b52000/wcd9375-reset-active-state ls /proc/device-tree/soc@0/ | grep -iE "lpass|swr|audio|remoteproc" remoteproc@4080000 remoteproc@62400000 remoteproc@8300000 ls /proc/device-tree/ | grep -iE "sound|swr" soundS Всё ясно: звук встал в бесконечное ожидание пинконтрола. Смотри последние строки — rxmacro ждёт rx-swr-active-state, txmacro ждёт tx-swr-active-state, кодек ждёт wcd9375-reset-active-state, и все три состояния живут в pinctrl@62b52000. Это LPASS LPI — ровно тот драйвер, который мы чинили патчем про мультиплексор SoundWire. То есть провайдер этих состояний не поднялся. Проверь: uname -r lsmod | grep -i pinctrl journalctl -k -b | grep -iE "lpi|62b52000" | tail -15 ls -l /boot/vmlinux* /boot/dtbs/qcom/*davinci* 2>/dev/null Главное, что хочу исключить: не подменился ли у тебя кернел. Мы сегодня ставили пакеты через apk, и если он заодно обновил ядро из репозитория, то на диске оказалось чужое — без нашего патча к LPI и без камерных правок. Даты файлов в /boot это сразу покажут. 💰 12.42₽ · ⬇️ 403.4k / ⬆️ 1.8k · opus-5 · max · 30сЫ S Хорошо, значит дело в самом модуле. Проверь и попробуй загрузить руками: lsmod | grep -i lpi ls /lib/modules/$(uname -r)/kernel/drivers/pinctrl/qcom/ | grep -i lpi sudo modprobe pinctrl-sm7150-lpass-lpi 2>&1 | tail -3 sleep 2; ls /sys/class/sound/; aplay -l 2>&1 | head -5 Если после загрузки модуля карта появится — значит он просто не подхватился автоматически, и лечится это записью в /etc/modules-load.d/. Если модуля нет в каталоге вообще — значит при последней установке модулей он не доехал, и надо будет доставить его из дерева сборки. 💰 10.52₽ · ⬇️ 405.2k / ⬆️ 560 · opus-5 · max · 11с