Меню
Linux на Microsoft Surface 2

Камеры на Tegra 114

Драйвер VI/CSI с нуля, порт AR0330 и почему фронтальная камера не присылала данные.

В Surface 2 две камеры, обе на шине I2C3:

ЗадняяФронтальная
СенсорOmniVision OV5693, 5 Мпonsemi AR0330, 3,4 Мп
I²C0x360x10
Порт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, иначе при потоке не хватает непрерывной памяти.
  • Запись видео ограничена, потому что нет аппаратного кодировщика.