В Surface 2 две камеры, обе на шине I2C3:
| Задняя | Фронтальная | |
|---|---|---|
| Сенсор | OmniVision OV5693, 5 Мп | onsemi AR0330, 3,4 Мп |
| I²C | 0x36 | 0x10 |
| Порт | CSI-A, 2 линии | CSI-B, блок CIL-C, 2 линии, RAW10 |
| Тактовый сигнал | 24 МГц | 24 МГц |
На старте не было ничего: драйвера VI/CSI для Tegra 114 в Linux нет, ov5693 работал только через ACPI на 19,2 МГц, а драйвера AR0330 в основной ветке нет вовсе.
Как читается топология
Блок топологии камер лежит прямо в ACPI, в \_SB.GFXC.TCBB. Из него видно число камер и сенсоров, а строка CsiC x2 у фронтальной камеры позже оказалась главной подсказкой. Тактовый сигнал берётся из vi_sensor (408/17 = 24 МГц) с вентилем csus на пине cam_mclk_pcc0 в функции vi_alt3.
Драйвер видеовхода
Основой стала серия Tegra 20/30, регистры сверены с исходниками NVIDIA для ядра 3.4 и пользовательской библиотекой камеры из L4T R17. Сенсор OV5693 получил привязку к device tree и настройку PLL под 24 МГц, а AR0330 перенесён с драйвера Rockchip под GPL-2.0 на современный API.
Задняя камера дала первые кадры относительно быстро. Настоящее изображение появилось в тот же день.
Загадка фронтальной камеры
Сенсор отвечал по I²C, инициализировался, тестовый шаблон включался, но до пиксельного парсера не долетало ни одного пакета. Мы перебрали всё очевидное:
- регистр
MIPI_PAD_CTRL: на T114 его нет; - мягкий сброс и инициализацию по алгоритму Rockchip;
- маски калибровки
0x180и0x60: калибровка проходит, данных нет; - GPIO блока камер: один из них оказался подсветкой экрана, другой прерыванием.
Ответ нашёлся в пользовательской библиотеке NVIDIA libnvmm_camera.so. На Tegra 114 порт CSI-B устроен не так, как на Tegra 30: он берёт линии из нового блока CIL-C, а не из CIL-B. Блок CIL-B здесь лишь верхняя половина четырёхлинейного CSI-A.
/*
* CSI-A port = PPA fed by CIL-A (brick A; x4 = CIL-A + CIL-B)
* CSI-B port = PPB fed by CIL-C (brick C; x4 = CIL-C + CIL-D) <-- not CIL-B!
* CSI-E port = PPB fed by CIL-E
* PHY_CIL_COMMAND (0x868): A [1:0], B [17:16], C [21:20], D [25:24], E [29:28]
* PHY_CILx_CONTROL0: A 0x86c, B 0x870, C 0x8f8, D 0x8fc, E 0x900
* pad config (calibration RMW): A 0x894, B 0x89c, C 0x928, D 0x930, E 0x938
*/Но и этого мало. UEFI оставляет пады CIL-C выключенными: регистр 0x928 читается как 0x00020007, где младшие три бита выключают две линии данных и линию тактирования. После очистки этих битов кадры пошли без единой ошибки.
/*
* Bits [2:0] of the C/D/E pad config look like per-lane power-downs
* (CIL-C read 0x00020007 after UEFI = 2 data + clock lane, CIL-E 0x5 = 1 data
* + clock lane). NVIDIA x4 writes 0x928 = 0x00020000 (bits cleared).
*/
static unsigned int ppb_pad_clr = 0x7;Ещё одна ловушка: калибровку нужно запускать с маской 0x004 (только CSIC). С маской 0x00c (C и D) она зависает по таймауту, потому что пады CSID в двухлинейном режиме остаются выключенными.
Ориентация и цвет
Кадр получался перевёрнутым. Переворот через регистр сенсора 0x3820 ломал поток CSI, поэтому мы переворачиваем кадр на стороне VI: VI_OUTPUT_V_DIRECTION, бит 20, запись начинается с последней строки. Чтобы порядок BGGR сохранился, у сенсора задаётся смещение на одну строку. Камеры смонтированы по-разному, поэтому параметр отдельный на каждый канал:
static bool t114_vflip(struct tegra_vi_channel *chan)
{
return chan->portnos[0] == 1 ? out_vflip_ch1 : out_vflip;
}Аппаратного ISP в Linux нет, поэтому сырой Bayer обрабатывает программный ISP из libcamera. Чтобы убрать зелень и синеву, мы подобрали матрицу цветокоррекции.
Как камеры попадают в приложения
libcamera умеет работать с простыми конвейерами, ему нужно лишь объявить новое устройство:
static const SimplePipelineInfo supportedDevices[] = {
{ "qcom-camss", {}, true },
{ "sun6i-csi", {}, false },
{ "tegra-video", {}, true },
};Драйвер должен работать в режиме media controller: tegra-video mc_api=1. Сверху находится своя служба, которая создаёт узлы PipeWire и включает камеру только по запросу. Для старых программ есть v4l2loopback:
| Устройство | Что это |
|---|---|
/dev/video20 | Задняя камера, 720p YUYV 30 fps |
/dev/video21 | Задняя камера, 1080p NV12 |
/dev/video22 | Фронтальная камера, 1152×648 |
Проверено в GNOME Snapshot и Chromium. Простаивающая камера не тратит процессор, а через 3 секунды после ухода последнего клиента выключается.
Ограничения
- Весь дебайеринг на процессоре: 1080p занимает примерно 1,3 ядра.
- Одновременно работает только одна камера.
- Нужен
cma=384M, иначе при потоке не хватает непрерывной памяти. - Запись видео ограничена, потому что нет аппаратного кодировщика.