如何编写显示驱动程序

因此,您决定建立一个新面板。在深入了解编码之前,请确保 请回答以下问题,以确保您拥有所需的一切:

  • 如何了解设备及其寄存器的工作原理?

    • 这通常称为“运算理论”。制造商通常会提供 包含寄存器定义的数据表,但这些参考资料可能无法 设备的实际使用方式。
  • 类似开发板是否有现成的驱动程序?

    • 在可行的情况下,通过重构代码并重复使用类似的开发板代码, 修改驱动程序的绑定规则。
  • 该设备是否具有固定的显示屏?

    • 一些显示控制器和面板(输出屏幕)是紧密耦合的。 如果是这种情况,新开发板就需要添加对 GPIO、I2C 和其他控件作为显示驱动程序的一部分。

前提条件

本指南假定您熟悉一个或多个应用的驱动程序开发 操作系统此外还假定您熟悉紫红色 DDK-TL

编程语言

新驱动程序必须使用 C++ 编写。我们计划支持 Rust,但仍高度支持 实验性功能

如果已存在获得相应许可的驱动程序,且 那么可以将其移植到 Fuchsia 而不是 如何用 C++实现新版本请联系 graphics-dev@fuchsia.dev 然后再做出这个决定

使用入门

对于没有 ACPI 或 PCI 总线的平台,修改板 驱动程序。本指南假定 板驱动程序已准备就绪,并且显示驱动程序的代号为 fancy。所有代码 适用于新驱动程序将位于 src/graphics/display/drivers/fancy-display/

首先,请创建:

  • DisplayEngine 的最小实现
  • 一组绑定规则
  • DisplayEngine 和绑定规则的构建配方

将驱动程序添加到 build

  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 = "meta/fancy-display.bind"
  bind_output = "fancy-display.bindbc"
  tests = "meta/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 开发板的一部分,//boards/vim3.gni可以通过添加 将您的司机添加到 _common_bootfs_deps 列表中。

选择要驾驶的设备

现在您已经有了构建配方,可以继续创建绑定了 这些规则,驱动程序管理器会根据这些规则来确定驱动程序是否 都可以与设备搭配使用

  1. src/graphics/display/drivers/fancy-display 中,创建 meta/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,
}

对于 PC 设备,intel-display 绑定规则就是一个很好的示例。对于 固定硬件 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 DisplayEngineRegisterDisplayEngineListener(
      const display_engine_listener_protocol* interface) {}

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

  zx_status_t DisplayEngineReleaseBufferCollection(
      uint64_t collection_id) {
    return ZX_ERR_NOT_SUPPORTED;
  }

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

  void DisplayEngineReleaseImage(image_t* image) {}

  config_check_result_t DisplayEngineCheckConfiguration(
      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 DisplayEngineApplyConfiguration(
      const display_config_t** display_config, size_t display_count) {}

  zx_status_t DisplayEngineSetBufferCollectionConstraints(
      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");

需要显示屏驱动程序才能实现 DisplayEngine 协议,用于公开硬件层并实现 Vsync 通知。显示协调器驱动程序多路复用 系统上所有设备专用的驱动程序和显示驱动程序之间 堆栈客户端,即系统合成器和 Virtcon。

实现提示

驱动程序决定何时以及如何将配置传递给 ApplyConfiguration 生效。为了避免画面撕裂,驾驶员应该 vsync 后立即应用新设置。

大多数设备都会对垂直同步事件造成中断。最简单的方法就是 确保及时生成 Vsync 通知, 为这个中断提供服务即使未显示图片,司机也必须 为每个 vsync 调用 OnDisplayVsync

支持引导加载程序的控制器

如果显示屏在启动时处于活跃状态,例如面板打开,而图片 ,您就可以快速获得驱动程序中的基本功能。已读 引导加载程序日志和/或源代码,以查找:

  • 帧缓冲区的物理地址
  • 用于对
  • 图片的像素尺寸,例如800x600
  • 图片的像素格式,例如RGB888、NV12 或 BGRA8888

然后:

  1. 修改驱动程序,以报告具有格式限制的显示屏。
  2. 记录 image->handle 中任何已导入映像的物理地址。
  3. 调用 ApplyConfig 时,对寄存器重新编程。

如果您还不知道如何观察 vsync,则可以使用 以 60Hz 的频率调用 OnDisplayVsync

以“深色”模式启动的控制器

没有什么办法能调出甚至缺少 基本引导加载程序驱动程序。在大多数情况下,您的路线图将是:

  1. 启动设备。
  2. 初始化时钟。
  3. 探索连接的显示屏。
  4. 针对兼容模式对 PHY 进行编程。
  5. 在 vsync 上编程布局(帧缓冲区添加器等),以避免画面撕裂
  6. Sysmem 集成。