AMOLED без DRM: как текст U-Boot попадает на экран Infinix / How U-Boot text gets onto an AMOLED without DRM

Первое, что я хотел от нового загрузчика, — чтобы он показывал что-нибудь на экране телефона. Не логотип производителя, а нормальный текст: меню, список разделов, приглашение ввести команду. В U-Boot для этого есть подсистема video, есть драйверы панелей, есть vidconsole, который рисует символы. Всё должно работать из коробки.

Не работает. И вот тут выяснилось, что в форке U-Boot под MediaTek нет вообще ничего, что умеет показывать текст на экране этого телефона. Ни DRM-драйвера для MT6789, ни драйвера панели, ни встроенного фреймбуфера, который заработал бы. Ноль.

Писать с нуля? Я не верил. Нашёл обходной путь, который в итоге и стал основным.

Главный трюк: панель уже работает

Вот что важно понять про загрузку на MediaTek. Порядок такой:

Preloader → BL2_EXT → LK → U-Boot

К моменту, когда начинает грузиться U-Boot, панель уже инициализирована — её подняли BL2_EXT и LK. DSI-контроллер тактируется, тайминги выставлены, панели отправлены команды инициализации, она горит.

Это меняет всё. Мне не нужно поднимать панель. Нужно только перенастроить конвейер, который передаёт в неё картинку.

У MediaTek это OVL0 → RDMA0 → DITHER0 → DSI0:

framebuffer в DRAM
      │
      ▼
    OVL0    слой 0: адрес, размер, формат пикселей
      │
      ▼
   RDMA0    читает пиксели из памяти
      │
      ▼
  DITHER0   приводит к формату панели
      │
      ▼
    DSI0    уже настроен LK, шлёт команды
      │
      ▼
  ILI7835F   AMOLED 1080x2400

Формально это DRM-пайплайн. Но драйвера DRM в U-Boot для MediaTek нет, и тащить его из Linux — это месяц работы. Написать свой, который умеет только инициализировать четыре блока и показывать кадр, — это 453 строки.

drivers/video/mediatek/mtk_disp_g99.c. Написал с нуля.

Первый способ: вообще не прикалываться

Самое очевидное решение — не трогать ничего, просто дать U-Boot кадр по адресу simple-framebuffer из device tree. Адрес там есть, 0xfe0c8000, прямо из DT телефона.

Телефон с этого момента не грузится. Synchronous Abort, всё.

Причина тупая, если знать: этот адрес не отображён в MMU U-Boot. Ядро Android отображает, а загрузчик — нет. Любое обращение к кадру, даже из драйвера, уходит в никуда и вызывает исключение синхронной абстракции.

Узел simple-framebuffer из DTS выпилил. Саму область памяти оставил в reserved-memory, но без флага no-map — чтобы U-Boot к ней мог добраться. Это отдельная тонкость: no-map означает «не маппить в MMU», то есть гарантированно недоступно.

Адрес кадра: два числа, не путать

Тут я сам путался какое-то время. В DT резерве кадр начинается с 0xfe0c8000. А в коде драйвера по умолчанию стоит 0xfeabe000. Это не ошибка — это два разных адреса:

Адрес Что это
0xfe0c8000 начало зарезервированной области, размер 31 МБ
0xfeabe000 адрес, который реально поставил LK в OVL0_L0_ADDR

0xfeabe000 лежит внутри того резерва. Драйвер читает OVL0_L0_ADDR и берёт то, что реально настроил LK; если там пусто или 0xffffffff — откатывается на 0xfeabe000. Плюс проверка диапазона: вне 0x40000000…0x2fffffffff — тоже откат.

Это важный принцип: адрес берём у того, кто поднял панель. LK знает, куда оно положило кадр. Мы — нет.

Кэш, который стоил мне полдня

Экран загорался пустым. Текст рисуется в память, vidconsole доволен, а панель показывает старое. Классика.

Проблема в том, что mw.l пишет только в кэш CPU. А OVL читает кадр из DRAM. Данные лежат в кэше, DRAM про них ничего не знает, и на панель уходит мусор — точнее, ничего.

Решение — dc cvac после записи в кадр: сбросить кэш до точки монолитности. В драйвере это делает flush_dcache_range() автоматически. Но если пишешь в кадр руками из консоли U-Boot, нужно сделать самому:

dc cvac 0xfeabe000 0xa3e000

Забыл про это на полдня, потому что драйвер vidconsole обычно сам об этом не напоминает.

Смещение 0xd00, из-за которого чуть не сошёл с ума

Вот это стоит отдельного рассказа, потому что выглядит как глупость, а стоит недели.

У MediaTek DSI-команды лежат в очереди (CMDQ). В большинстве чипов этот буфер по смещению 0x200 от базы DSI. Я так и написал сначала. Экран не заработал.

Открыл дизассемблированный LK, начал искать отправку команд. Нашёл функцию, которая формирует код команды для DSI и кладёт её в очередь. Смотрел на смещения — и увидел 0xd00.

У MT6789 очередь команд по 0xd00. Не 0x200.

Код, который шлёт кадр на панель (DCS Write Memory Start), выглядит так:

#define DSI0_CMDQ         (DSI0_BASE + 0xd00)   /* MT6789: НЕ 0x200 */
#define DSI_CMDQ_WRITE_MEM_START  0x2c3909

Один байт в адресе. Неделя на то, чтобы догадаться, что 0x200 пришёл из головы от другого чипа.

Что ещё пришлось поправить

Ещё три вещи, которые вылезли при отладке:

OVL0 SRC_CON = 0x3. LK оставил активными два слоя. Надо оставить как есть, иначе логотип поверх текста.

OVL0 L0_CON = 0x000001ff. Было 0x1001020 — не подходит для ARGB8888. Меняю на 32 бита и разрешение 1080×2400.

CFG_UPDATE после мультиплексоров. MMSYS пишет через теневые регистры: записал в MMSYS_OVL0_MOUT — а реальное значение сменится только после CFG_UPDATE. Без этого артефакты и двоение текста.

Отдельно: гейты часов в MMSYS снимаются через регистр CLR, а не SET. Это не опечатка в моём коде — так у MediaTek.

Как проверять

После probe() драйвер пишет блок значений в память прямо за кадром — по адресу 0xff4a1400. Читается уже из консоли U-Boot:

md.l 0xff4a1400 20
Смещение Что
+0x00 magic 0xDEADBEEF
+0x04 uc_priv->fb
+0x08 plat->base
+0x0c uc_priv->fb_size

Если magic не тот — драйвер не дошёл до конца probe(), и в консоли будет написано, где остановился. Эта мелочь сэкономила мне несколько часов: не надо гадать, что сломалось.

Что получилось

Текст U-Boot на AMOLED 1080×2400. Меню загрузки на экране телефона, точки, логотип сборки — всё рисуется собственным драйвером на 453 строки. Заработало с v183, окончательно — с v187.

Только текст. Ни картинок, ни графики — vidconsole в UCLASS_VIDEO умеет ровно это.


Код: drivers/video/mediatek/mtk_disp_g99.c ·
разбор дисплея: docs/hardware/display.md ·
общее состояние проекта: README

Дальше — про USB: почему fastboot, консоль и UMS пришлось делать на PIO без DMA, и почему ПК не видел устройство два дня подряд.