如何編寫顯示驅動程式庫

所以您決定啟動新的主面板。在開始編寫程式之前,請先回答下列問題,確認您具備所有必要功能:

  • 我該如何瞭解裝置及其註冊的運作方式?

    • 這通常稱為「作業理論」。製造商通常會提供規格書,其中含有登錄定義,但這些參考資料可能無法說明裝置的實際使用方式。
  • 是否有其他類似白板的驅動程式庫?

    • 在實際可行情況下,請透過重構該程式碼並修正驅動程式庫的繫結規則,為類似的主面板重複使用程式碼。
  • 裝置是否有固定的螢幕?

    • 部分顯示控制器和麵板 (輸出畫面) 彼此緊耦合。如果這是新主面板的運作,您將需要在顯示驅動程式庫中新增 GPIO、I2C 和其他控制項的支援功能。

必要條件

本指南假設您熟悉一或多個作業系統的驅動程式庫開發。也假設您熟悉 Fuchsia DDK-TL

程式語言

新驅動程式必須以 C++ 編寫。Rust 支援已規劃,但目前仍在高度實驗階段。

如果已有適當授權的驅動程式以 C 編寫,我們建議將其移植至 Fuchsia,而不要在 C++ 中實作新版本。在做出這項決定前,請與 graphics-dev@fuchsia.dev 聯絡。

踏出第一步

如果平台沒有 ACPI 或 PCI 匯流排,第一個步驟就是修改主機板驅動程式。本指南假設主面板驅動程式庫已準備就緒,螢幕驅動程式庫為 fancy。新驅動程式庫的所有程式碼都會存放在 src/graphics/display/drivers/fancy-display/ 中。

首先,請建立:

將驅動程式庫新增至版本

  1. 在名為 BUILD.gn 的檔案中建立建構方案
# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/bind/bind.gni")
import("//build/drivers.gni")

driver_bind_rules("fancy-display-bind") {
  rules = "fancy-display.bind"
  bind_output = "fancy-display.bindbc"
  tests = "bind_tests.json"
  deps = [
    "//src/devices/bind/board_maker_company.platform",
  ]
}

# Factored out so that it can be used in tests.
source_set("common") {
  public_deps = [
    ":fancy-display-bind",
  ]
  sources = [
    "fancy-display.cc",
  ]
}

fuchsia_driver("fancy-display") {
  sources = []
  deps = [
    ":common",
    "//src/devices/lib/driver",
  ]
}
  1. //src/graphics/display/drivers/fancy-display 新增為用於測試產品的面板依附元件。舉例來說,如果您的裝置屬於 Khadas VIM3 主面板,請將驅動程式新增至 _common_bootfs_deps 清單,藉此修改 //boards/vim3.gni

選擇要開車的裝置

現在您已擁有建構方案,接下來可以繼續建立「繫結規則」,讓驅動程式管理器用來判斷裝置是否可以搭配驅動程式使用。

  1. src/graphics/display/drivers/fancy-display 中建立 fancy-display.bind
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

using fuchsia.pci;

fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.PLANK_HW_INC;
accept fuchsia.BIND_PCI_DID {
  // Fancy
  0x0100,
  // Fancy+ series
  0x0120,
  0x0121,
}

如為電腦裝置,則可使用 intel-i915 繫結規則。針對固定硬體 SoC,請參閱 Amlogic 顯示規則

最小驅動程式庫

最後,請新增裸機驅動程式庫,以便在每次成功繫結至裝置時建構新物件。之後,您可以使用規格書 讓裝置執行實際操作。

src/graphics/display/drivers/fancy-display 中建立 fancy-display.cc

#include <ddktl/device.h>
#include <fuchsia/hardware/display/controller/cpp/banjo.h>

namespace fancy_display {

class Device;
using DeviceType = ddk::Device<Device>

// A Device exposes a single display controller for use by the display
// coordinator driver in src/graphics/display/drivers/coordinator.
//
// This object is constructed once for each device that matches this
// driver's bind rules.
class Device : public DeviceType {
 public:
  explicit Device(zx_device_t* parent) : DeviceType(parent) {}

  // If Bind() returns an error, the driver won't claim the device.
  zx_status_t Bind() { return ZX_OK };

  // Functionality needed by the common display driver core.
  void DisplayControllerImplSetDisplayControllerInterface(
      const display_controller_interface_protocol* interface) {}

  zx_status_t DisplayControllerImplImportBufferCollection(
      uint64_t collection_id, zx::channel collection_token) {
    return ZX_ERR_NOT_SUPPORTED;
  }

  zx_status_t DisplayControllerImplReleaseBufferCollection(
      uint64_t collection_id) {
    return ZX_ERR_NOT_SUPPORTED;
  }

  zx_status_t DisplayControllerImplImportImage(const image_metadata_t* image_metadata,
                                               uint64_t collection_id, uint32_t index,
                                               uint64_t* out_image_handle) {
    return ZX_ERR_NOT_SUPPORTED;
  }

  void DisplayControllerImplReleaseImage(image_t* image) {}

  config_check_result_t DisplayControllerImplCheckConfiguration(
      const display_config_t** display_configs, size_t display_count,
      client_composition_opcode_t* out_client_composition_opcodes_list, size_t client_composition_opcodes_count,
      size_t* out_client_composition_opcodes_actual);

  void DisplayControllerImplApplyConfiguration(
      const display_config_t** display_config, size_t display_count) {}

  void DisplayControllerImplSetEld(
      uint64_t display_id,
      const uint8_t* raw_eld_list,
      size_t raw_eld_count) {}

  zx_status_t DisplayControllerImplSetBufferCollectionConstraints(
      const image_buffer_usage_t* usage, uint64_t collection_id) {
    return ZX_ERR_NOT_SUPPORTED;
  }

};

}  // namespace fancy_display

// Main bind function called from dev manager.
zx_status_t fancy_display_bind(void* ctx, zx_device_t* parent) {
    fbl::AllocChecker alloc_checker;
    auto dev = fbl::make_unique_checked<fancy_display::Device>(
        &alloc_checker, parent);
    if (!alloc_checker.check()) {
        return ZX_ERR_NO_MEMORY;
    }
    auto status = dev->Bind();
    if (status == ZX_OK) {
      // The driver/device manager now owns this memory.
      [[maybe_unused]] auto ptr = dev.release();
    }
    return status;
}

// zx_driver_ops_t is the ABI between driver modules and the device manager.
// This lambda is used so that drivers can be rebuilt without compiler
// warnings if/when new fields are added to the struct.
static zx_driver_ops_t fancy_display_ops = [](){
    zx_driver_ops_t ops;
    ops.version = DRIVER_OPS_VERSION;
    ops.bind = fancy_display_bind;
    return ops;
}();

// ZIRCON_DRIVER marks the compiled driver as compatible with the zircon
// 0.1 driver ABI.
ZIRCON_DRIVER(fancy_display, fancy_display_ops, "zircon", "0.1");

顯示驅動程式必須實作 DisplayControllerImpl 通訊協定,這會公開硬體層並實作 vsync 通知。系統上所有裝置專用驅動程式和顯示驅動程式庫堆疊用戶端 (即系統合成器和 Virtcon) 之間的「螢幕協調器」驅動程式庫多工器。

導入提示

傳遞至 ApplyConfiguration 的設定驅動程式庫決定何時和方式生效。為了避免發生撕裂,驅動程式應在 vsync 後套用新設定。

大多數裝置都會為 vsync 事件產生中斷情形。如要確保即時顯示 vsync 通知,最簡單的方法就是建立另一個執行緒,藉此處理中斷情形。即使未顯示圖片,您的驅動程式庫必須針對每個 vsync 呼叫 OnDisplayVsync

支援系統啟動載入程式的控制器

如果螢幕在啟動時處於啟用狀態 (例如面板開啟且顯示圖片),您就可以快速在驅動程式庫中使用基本功能。讀取系統啟動載入程式記錄和/或原始碼,即可找到:

  • framebuffer 的實際位址
  • 用於規劃該位址的收銀機
  • 圖片的像素尺寸,例如 800x600
  • 圖片的像素格式,例如 RGB888、NV12 或 BGRA8888

然後執行下列操作:

  1. 請修改驅動程式庫,以回報具有格式限制的螢幕。
  2. 錄製 image->handle 中所有已匯入圖片的實際地址。
  3. 呼叫 ApplyConfig 時,重新編寫註冊的程式。

如果您不知道如何觀察 vsync,可以使用在 60Hz 呼叫 OnDisplayVsync 的執行緒中假裝。

啟動「深色」的控制器

即使顯示缺乏基本系統啟動載入程式驅動程式庫的顯示控制器,也沒有辦法開啟。在大多數情況下,您的發展藍圖如下:

  1. 將裝置充電。
  2. 初始化時鐘。
  3. 探索附加的螢幕。
  4. 為相容模式編寫 PHY。
  5. 在 vsync 上規劃版面配置 (影格緩衝區新增器等),以避免撕裂
  6. Sysmem 整合。