×

SBC, Linux та DSI. Як змусити це все працювати разом

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

.init

Перед ким з нас поставала задача підключити TFT панель до одноплатного комп’ютера Firefly-RK3399? Зі мною це сталось. Маючи лише приблизне уявлення, що потрібно робити, я почав шукати статті в інтернеті. Інформацію довелось збирати по частинам: дещо з вікіпедії, дещо з сайту виробника, а дещо, дякуючи Google Translate, — з китайських блогів. Всі ці шматки я зібрав у статтю в тому вигляді, в якому б хотів би їх знайти на початку процесу.

MIPI/DSI

В моєму випадку для підключення панелі до SBC використовувалась шина DSI. DSI, або ж Display Serial Interface, це стандарт MIPI Alliance, що описує високошвидкісну послідовну шину даних та протокол зв’язку між джерелом зображення (хостом) та дисплеєм. На фізичному рівні DSI складається з однієї лінії, що надає тактовий сигнал, та від однієї до чотирьох диференційних пар для передачі даних. Перша пара може працювати як на передачу так і на короткочасний прийом даних. Пари 2-4 працюють лише на передачу.

На обох кінцях шини знаходяться котролери: на стороні хоста, це, зазвичай, блок в SoC, на стороні дисплея — окрема мікросхема. Контроллер хоста може керувати налаштуваннями та поведінкою дисплею надсилаючи команди у вигляді послідовності байт довжиною від 1 до 65535. Перший байт послідовності — ідентифікатор команди, наступні — аргументи, кількість яких залежить від ідентифікатора. Так, наприклад, команда 11h (exit_sleep_mode) не потребує аргументів, 3Ah (set_pixel_format) потребує один аргумент, а 33h (set_scroll_area) — шість.

Команди поділяються на дві групи — User Command Set та Manufacturer Command Set. Ідентифікатори від 00h до AFh включно зарезервовані для User Command Set, поведінка цих команд визначена стандартом DCS (Display Command Set) MIPI Alliance. Решта значень (від B0h до FFh) входять до Manufacturer Command Set і їх семантика визначається виробником периферії. Враховуючи рівень складності сучасних панелей, кількість команд доступних в MCS може бути недостатньою. Щоб обійти це обмеження виробники використовують так звані сторінки команд: один і той же ідентифікатор може мати різну семантику в залежності від того яка сторінка активна в даний момент на стороні дисплея. Виглядає це так:

  • На стороні периферії зберігається стан, номер активної сторінки.
  • Одна з команд з діапазону MCS має фіксовану семантику для всіх станів і використовується для зміни активної сторінки.
  • Одна з сторінок є сторінкою за замовченням. Всі значення в діапазоні UCS мають семантику задану стандартом.
  • Для всіх інших сторінок функціональність кодів від 00h до FFh визначається виробником.

Команда, яка перемикає сторінки може мати один аргумент, номер сторінки (FF 05 — активувати сторінку #5), або, щоб уникнути випадкового перемкнення через помилки в коді, кілька додаткових байтів «паролю» для підтвердження наміру. FF 93 81 05 — активувати сторінку #5, якщо аргументи 1 та 2 відрізняються від 93 81 — запит буде проігноровано.

Абстрактний приклад використання однакових ідентифікаторів з різних сторінок:

FF 93 81 05 // page 5
11 73       // gewgaw_level := 73h
FF 93 81 00 // page 0
11          // exit_sleep_mode

На транспортному рівні DSI оперує пакетами які бувають двох форматів: короткі та довгі. Пакет в короткому форматі складається лише з заголовка довжиною чотири байти: типа пакета, два байти даних, контрольна сума заголовку.

[DATA ID]  [DATA0] [DATA1] [ECC]

Поле DATA ID визначає тип даних пакету. Для короткого формату їх є декілька, але для цієї статті релевантні лише два: 05h (DCS Short Write, no parameter) та 15h (DCS Short Write, 1 parameter).

Довгий формат складається з трьох частин: заголовку, даних та 16-бітної контрольної суми даних:

[DATA ID] [LEN_LSB] [LEN_MSB] [ECC] [DATA0] ... [DATA1] [CRC_LSB] [CRC_MSB]

Знову ж таки єдиний тип даних цього формату який нас цікавить — 39h (DCS Long Write).

Команда 11 «загорнена» в пакет набуває вигляду 05 11 00 XX, E0 05 стає 15 E0 05 XX, а FF 93 81 05 — 39 04 00 XX FF 93 81 05 YY YY.

Вся ця інформація знадобиться в наступному розділі.

Linux/SoC

Отже у вас є SBC з Linux, панель та кабель, яким панель підключена до SBC. Як змусити це все працювати разом?

Для початку потрібно визначити чи є вже готовий драйвер панелі. Оскільки послідовність команд для запуску та часові параметри відрізняються між різними моделями, то ядро, тим чи іншим чином, має отримати цю інформацію. Один з способів — окремий драйвер на кожну модель, в який вбудовано всі деталі реалізації панелі.

Якщо вам пощастило і ваша модель підтримується ядром, то процес запуску виглядає наступним чином:

  • Додати підтримку DSI контролера в конфігурацію ядра (наприклад CONFIG_ROCKCHIP_DW_MIPI_DSI)
  • Додати підтримку панелі в конфігурацію ядра (наприклад CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04)
  • Модифікувати dts файл SBC:
    • Ввімкнути DSI контролер
    • Додати вузол для панелі
    • Поєднати це все разом

Для офіційного ядра Linux фрагмент dts може вигядати наступним чином:

&mipi_dsi {
    // вмикаємо
    status = "okay";
    clock-master;

    ports {
        mipi_out: port@1 {
            // .. skipped ..
        };
    };

    // вузол панелі
    panel@0 {
        compatible = "kingdisplay,kd097d04";
        power-supply = <&pp3300_s0>;
        reg = <0>;
        backlight = <&backlight>;
        enable-gpios = <&gpio4 25 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&display_rst_l>;

        // магія DRM
        ports {
            // .. skipped ..
        };
    };
};

В якості прикладу можна пошукати dts файли SBC з таким же SoC але з ввімкненим DSI контролером.

На практиці більш ймовірний інший варінт. Оскільки китайські виробники екранів не палають бажанням писати та просувати код в офіційне дерево Linux, то, скоріш за все, замість готового драйвера у вас буде замилений скріншот з діаграмою часових параметрів та загадковий .txt файл вигляду:

REGISTER,FF,03,98,81,03

//GIP_1
REGISTER,01,01,00
...
// Page 0 command
REGISTER,FF,03,98,81,00
REGISTER,35,01,00  // TE On
REGISTER,11,00     // Sleep Out
Delay,120
REGISTER,29,00     //  Display On
Delay,20

Файл загадковий лише в тому випадку, якщо ви до цього ніколи не стикались з DSI. Але, дочитавши до цього місця, ви вже маєте здогадатись, що ця шифрограма — послідовність команд для ініціалізації панелі. Трохи поміркувавши можна дійти висновку, що формат рядку REGISTER має вигляд REGISTER,CMD,NARGS[,ARG1,…​] і REGISTER,35,01,00 перетвориться на 35 00.

Як використати цю послідовність на практиці залежить від того, з яким деревом Linux ви працюєте.

Якщо це офіційне дерево, то наступний крок буде написати власний драйвер панелі взявши за основу вже існуючий. Наприклад ось цей. В залежності від того наскільки ваш екран відрізняється від KD097D04 вам, можливо, доведеться переписати логіку роботи з GPIO, джерелами живлення, змінити часові характеристики, послідовність команд, значення полів lanes, format, mode_flags структури mipi_dsi_device, тощо.

Якщо ж ви використовуєте неофіційне дерево, то можливі інші шляхи. Китаські виробники SoC, так само як виробники екранів, теж не поспішають просувати код в репозитарій Лінуса і віддають перевагу підтримці власної копії, яку вони можуть повністю контролювати і яка може досить суттєво відрізнятись від офіційної.

Так, наприклад, в офіційному ядрі є модуль simple-panel для панелей, які для роботи потребують лише налаштування регулятора живлення та одного-двох сигналів GPIO. Rockchip розширила функціональність цього модуля: додала новий compatible рядок, simple-panel-dsi, і винесла всі налаштування DSI (формат, флаги, послідовності команд) з коду ядра в файл опису дерева приладів. Це дозволяє не писати нові драйвера для кожної моделі, а просто модифікувати dts файл.

Тож якщо у вас (a) SBC на основі SoC виробництва Rockchip (b) дерево Linux від Rockchip, вам потрібно:

  • Додати підтримку DSI контролера в конфігурацію ядра (CONFIG_ROCKCHIP_DW_MIPI_DSI)
  • Додати підтримку simple-panel в конфігурацію ядра (CONFIG_DRM_PANEL_SIMPLE)
  • Модифікувати dts файл SBC:
    • Ввімкнути DSI контролер
    • Додати вузол для панелі з всіма розширеними параметрами
    • Поєднати це все разом

Приклад фрагменту dts:

&edp {
    status = "disabled";
};

&dsi_in_vopl {
    status = "disabled";
};

&dsi_in_vopb {
    status = "okay";
};

&dsi {
    status = "okay";

    panel@0 {
        compatible = "simple-panel-dsi";
        reg = <0>;
        power-supply = <&vcc_lcd>;
        enable-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&lcd_panel_reset>;

        dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST)>;
        dsi,format = <MIPI_DSI_FMT_RGB888>;
        dsi,lanes = <2>;

        panel-init-sequence = [
            // .. skipped ..
            // page0
            39 00 04 FF 98 81 00
            15 00 02 35 00
            15 78 02 11 00          // sleep out, delay 120
            15 14 02 29 00          // display on, delay 20
        ];

        display-timings {
            native-mode = <&timing0>;

            timing0: timing0-1280p {
                // .. skipped ..
            };
        };
    };
};

Поля dsi,lanes, dsi,format, dsi,flags вузла panel@0 відповідають полям lanes, format, mode_flags структури mipi_dsi_device. display-timing задає часові параметри панелі в форматі, який описано в Documentation/devicetree/bindings/display/panel/display-timings.yaml.

Формат послідовностей для запуску та зупинки панелі (panel-init-sequence і panel-exit-sequence відповідно) потребує деяких пояснень. Ці поля — послідовності інструкцій для драйвера. Кожна інструкція складається з типу DSI пакету, команди, та необов’язкового часу затримки після надсилання команди. Формат інструкції: [PACKET_TYPE] [DELAY] [LEN] [DATA1] .. [DATA_LEN]. [PACKET_TYPE] залежить від довжини данних (див. інофрмацію про транспортний рівень DSI). Якщо команда складається з одного байта, то використовується тип 05h, двох — 15h, трьох і більше — 39h. В якості ілюстрації спробуємо перевести рядки файла отриманого від постачальника в команду і в інструкцію для panel-init-sequence:
REGISTER,FF,03,98,81,00 відповідає команді FF 98 81 00 і інструкції 39 00 04 FF 98 81 00, REGISTER,E0,01,05 — E0 05 та 15 00 02 E0 05, а комбінація REGISTER,29,00 та Delay,20 — 29 та 05 14 01 29.

.fini

Зазвичай інтерфейс між екраном та SBC не обмежується лише шиною DSI. Живлення екрана може керуватись окремим регулятором, ввімкнення-вимикнення — сигналом GPIO, рівень яскравості — PWM імпульсами. Модифікації dts можуть охопити більше компонентів: тактові генератори, конфігурація pinmux. Всі ці речі не розписані в статті але їх теж потрібно мати на увазі.

👍ПодобаєтьсяСподобалось2
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі