Short version (EN): first documented unlock of level 3 register protection on the Nintendo Switch OLED panel (Samsung AMS699VC01), plus a full dump of its vendor registers. The community has only ever used levels 1–2. Includes the exact packet, the one place where it works, a working hekate patch, and the resulting register map.
Панель Nintendo Switch OLED — Samsung AMS699VC01. Её регистры закрыты трёхуровневой защитой. Сообщество, включая hekate, использует только первые два уровня.
Здесь — снятие третьего, подтверждённое обратным чтением, и карта сорока вендорских регистров за ним.
Читается целиком, десять байт — пять групп по 16 бит:
5A 5A | A5 A5 | A5 A5 | A5 00 | A5 00
ур.1 ур.2 ур.3 ? ?
5A — открыто, A5 — закрыто. Первый уровень открыт заводом, второй и третий закрыты.
hekate шлёт пятибайтный пакет, и он достаёт только до второго уровня:
DSI_WR_DATA = 0x539; тип 0x39 (DCS long write), счётчик 5 байт
DSI_WR_DATA = 0x5A5A5AE2; E2 5A 5A 5A
DSI_WR_DATA = 0x5A; пятый байт
DSI_TRIGGER = DSI_TRIGGER_HOST;
Ей больше и не надо — ELVSS живёт на втором уровне.
Для третьего нужно шесть параметров, то есть пакет из семи байт:
DSI_WR_DATA = 0x739; счётчик 7 байт
DSI_WR_DATA = 0x5A5A5AE2; E2 5A 5A 5A
DSI_WR_DATA = 0x005A5A5A; 5A 5A 5A + паддинг
DSI_TRIGGER = DSI_TRIGGER_HOST;
Результат чтения после записи:
до: 5A 5A | A5 A5 | A5 A5 | A5 00 | A5 00
после: 5A 5A | 5A 5A | 5A 5A | A5 00 | A5 00
Возврат — тот же пакет с A5 вместо последних 5A.
Внутри display_init() загрузчика. Больше нигде.
Причина: под HOS бит DSI_CONTROL.HOST_ENABLE сброшен до того, как любой сисмодуль получает управление. Проверено на нулевой секунде до инициализации служб, на 11-й, на 23-й и непрерывной слежкой 30 секунд — он закрыт всегда.
Из меню загрузчика тоже не выйдет: там уже идёт видеопоток, и host-пакеты не доходят.
Единственное окно — момент, когда загрузчик поднимает дисплей и сам разговаривает с панелью.
40 регистров с настоящим содержимым из 176 опрошенных. Полный дамп — data/panel_dump.txt.
Ключевое:
| регистр | содержимое |
|---|---|
0x04 |
50 9B 20 — идентификатор, декодируется в 0x2050 |
0xB1 |
питание и ELVSS |
0xB5, 0xB7, 0xB9 |
таблицы гаммы, RGB-триплеты |
0xBB |
таблица из 11 записей с шагом 10 |
0xBC, 0xBF |
ASCII-строки прошивки панели |
0xC8, 0xC9 |
кривые по 4-байтным группам |
0xE2 |
сам замок |
Регистров кадровой развёртки там НЕТ. 0xF2, 0x60, 0x68 — те, что есть у родственного семейства S6E3FC3 — отдают дежурную однобайтовую отписку даже при открытом третьем уровне.
Подтверждается драйвером родственного контроллера в ядре Linux (panel-samsung-s6e3fc2x01.c): там ни одной команды частоты кадров, режим один, тайминги задаёт хост.
git clone https://github.com/CTCaer/hekate
cd hekate
git apply /путь/к/patch/panel_dump.patch
cp /путь/к/src/panel_dump.* bdk/display/
cp /путь/к/tools/lzpy.py tools/
maketools/lzpy.py — замена host-утилит lz77 и bin2c на Python. Нужна, если на машине нет родного компилятора: формат потока взят из tools/lz/lz.c, обратимость проверена round-trip'ом. Если gcc есть, файл не нужен, а правки Makefile из патча можно откатить.
- Положить
output/hekate.binна карту какpayload.bin. - Выключить консоль полностью.
- Включить, удерживая VOL+. Дамп снимается молча, до появления картинки, около секунды.
- В меню: Console Info → Panel dump (или Tools → Panel dump).
- Отчёт ляжет в корень карты как
panel_dump.txt.
Без VOL+ в панель не уходит ни одного пакета.
- проверка идентификатора до первого пакета: на чужой панели не отправляется ничего;
- замок возвращается на любом пути выхода — между снятием и возвратом в коде нет ни одного
return; - обход ограничен бюджетом времени, чтобы молчащая панель не дала минуты чёрного экрана;
- дежурная отписка отделяется от содержимого по гистограмме: байт считается отпиской, только если встретился не меньше восьми раз.
Дежурный ответ. Панель отвечает одним и тем же однобайтовым значением с одной и той же контрольной суммой на любой регистр, которого у неё нет. Отличить его от содержимого можно только по повторяемости.
Длина ответа. Разбирать надо по типу ответного пакета, а не по тому, сколько байт запросили: 0x21 короткий 1 байт, 0x22 короткий 2, 0x1A/0x1C длинный со счётчиком, 0x02 отчёт об ошибке, 0x84 подтверждение без данных. Запрос 10 байт при коротком ответе легко прочитать как ноль.
Спящая панель. Настройка частоты врёт: докладывала 60 Гц, когда развёртка шла 15 и 34. Судить можно только по замеру кадра.
Износ блока DSI. Порядка 30–50 чтений за загрузку, дальше очередь залипает и чтения идут сплошным FFFFFFFF. Необратимо в пределах загрузки.
DSI_TRIGGER не диагностика. Бит взведён в покое, до всякого пакета. Счётчик очереди передачи общий с видеопотоком и тоже ничего не говорит. Единственное доказательство доставки — ответ панели.
Замок вскрывали ради этого. Ответ: регистра частоты у панели нет, а потолок лежит в её строчном драйвере.
Проверено десять направлений:
| направление | итог |
|---|---|
| подъём пиксельного клока (PLLD) | 66 Гц — срыв |
| пересчёт таймингов D-PHY | все параметры в допуске, срыв остаётся |
| преэмфазис и сила драйвера площадок DSI | эффект есть, стену не двигает |
| ужимание горизонтального гашения | срыв тот же при стоковой скорости линии |
| сохранение времени вертикального гашения | без изменений |
| холодный старт на 90 / 70 / 67.7 Гц | чёрный экран / зелень |
| порядок калибровки MIPI_CAL | отрабатывает верно, после смены клока |
| питание и гамма (ELVSS) | от яркости не зависит |
| заморозка D-PHY | без нашего пересчёта хуже |
| голодание дисплейного контроллера | флагов недобора нет (140 000 опросов за 50 мс) |
Единственная величина, которая росла при всех способах — строк в секунду:
60 Гц: 78 000 строк/с -> 12.82 мкс на строку чисто
65 Гц: 84 500 строк/с -> 11.83 мкс полосы
66 Гц: 85 800 строк/с -> 11.65 мкс срыв
Панель упирается не в частоту кадров и не в объём данных, а в то, как быстро её строчный драйвер зажигает одну строку.
частота = 1 / (число строк × время строки)
1300 строк × 12.82 мкс = 16.7 мс = 60 Гц
Ровно паспортные шестьдесят. Запас — около восьми процентов, и он выбирается на 65 Гц.
Чтобы получить больше, нужно либо строку быстрее (панель не умеет), либо строк меньше (их 1280, это физическое разрешение).
Патч — производная работа от hekate, GPLv2. Авторские уведомления CTCaer и naehrwert сохранены во всех затронутых файлах.
Определения регистров DSI — из hekate. Расшифровка структуры замка и снятие третьего уровня — новое.