drm/komeda Arm 显示驱动程序

drm/komeda 驱动程序支持 Arm 显示处理器 D71 及更高版本的产品,本文档简要概述了驱动程序的设计:它的工作原理以及为什么这样设计。

D71 类似显示 IP 概述

从 D71 开始,Arm 显示 IP 开始采用灵活且模块化的架构。显示流水线由多个独立的功能流水线阶段组成,称为组件,每个组件都有一些特定的功能,可以对流动的流水线像素数据进行特定的处理。

典型的 D71 组件

层 (Layer)

层是第一个流水线阶段,它为下一阶段准备像素数据。它从内存中获取像素,如果它是 AFBC 则解码,旋转源图像,解包或将 YUV 像素转换为设备内部的 RGB 像素,然后根据需要调整像素的 color_space。

缩放器 (Scaler)

顾名思义,缩放器负责缩放,D71 还支持通过缩放器进行图像增强。缩放器的使用非常灵活,可以连接到层的输出以进行图层缩放,或连接到合成器并缩放整个显示帧,然后将输出数据馈送到 wb_layer,然后将其写入内存。

合成器 (compiz)

合成器将多个层或像素数据流混合成一个单独的显示帧。其输出帧可以馈送到后图像处理器以在监视器上显示,或馈送到 wb_layer 并同时写入内存。用户还可以在合成器和 wb_layer 之间插入一个缩放器,先缩小显示帧,然后再写入内存。

回写层 (wb_layer)

回写层执行与层相反的操作,它连接到 compiz 并将合成结果写入内存。

后图像处理器 (improc)

后图像处理器调整帧数据(如伽马和颜色空间)以满足监视器的要求。

时序控制器 (timing_ctrlr)

显示流水线的最后阶段,时序控制器不是用于像素处理,而仅用于控制显示时序。

合并器 (Merger)

与层相比,D71 缩放器大多只有一半的水平输入/输出功能,例如,如果层支持 4K 输入尺寸,则缩放器只能同时支持 2K 输入/输出。为了实现全帧缩放,D71 引入了图层分割,它将整个图像分割成两个半部分,并将它们馈送到两个层 A 和 B,并独立进行缩放。缩放后,结果需要馈送到合并器以将两个部分图像合并在一起,然后将合并后的结果输出到 compiz。

分割器 (Splitter)

与图层分割类似,但分割器用于回写,它将 compiz 结果分割成两个部分,然后将它们馈送到两个缩放器。

可能的 D71 流水线用法

得益于模块化架构,D71 流水线可以轻松调整以适应不同的用途。D71 有两条流水线,支持两种工作模式

  • 双显示模式:两条流水线独立工作,分别驱动两个显示输出。

  • 单显示模式:两条流水线协同工作,仅驱动一个显示输出。

    在此模式下,流水线 B 不会独立工作,而是将其合成结果输出到流水线 A,并且其像素时序也来自 pipeline_A.timing_ctrlr。流水线 B 的工作方式类似于流水线 A(主)的“从属”。

单流水线数据流

Single pipeline digraph

单流水线数据流

启用从属的双流水线

Slave pipeline digraph

启用从属流水线的数据流

输入和输出的子流水线

根据输入/输出的使用情况,可以将完整的显示流水线轻松地划分为三个子流水线。

层 (输入) 流水线

Layer data digraph

层 (输入) 数据流

Layer Split digraph

图层分割流水线

回写 (输出) 流水线

writeback digraph

回写 (输出) 数据流

split writeback digraph

回写 (输出) 分割数据流

显示输出流水线

display digraph

显示输出数据流

在以下章节中,我们将看到这三个子流水线将分别由 KMS-plane/wb_conn/crtc 处理。

Komeda 资源抽象

struct komeda_pipeline/component

为了充分利用和轻松访问/配置硬件,驱动程序端也使用类似的架构:流水线/组件来描述硬件功能和特性,一个特定的组件包括两个部分

  • 数据流控制。

  • 特定组件的功能和特性。

因此,驱动程序定义了一个通用头文件 struct komeda_component 来描述数据流控制,并且所有特定组件都是此基本结构的子类。

struct komeda_component

定义:

struct komeda_component {
    struct drm_private_obj obj;
    struct komeda_pipeline *pipeline;
    char name[32];
    u32 __iomem *reg;
    u32 id;
    u32 hw_id;
    u8 max_active_inputs;
    u8 max_active_outputs;
    u32 supported_inputs;
    u32 supported_outputs;
    const struct komeda_component_funcs *funcs;
};

成员

obj

将组件视为私有对象

pipeline

此组件所属的 komeda 流水线

name

组件名称

reg

组件寄存器基址,由芯片初始化,仅供芯片使用

id

组件 ID

hw_id

组件硬件 ID,由芯片初始化,仅供芯片使用

max_active_inputs

max_active_outputs:

可以同时激活的最大输入/输出数量。注意:该数字不是 supported_inputssupported_outputs 的位数,但可能小于它,因为组件可能不支持同时启用所有 supported_inputs/输出。

max_active_outputs

最大输出数量

supported_inputs

supported_outputs:

支持的输入/输出的 BIT(component->id) 的位掩码,描述了组件如何链接到流水线的可能性。

supported_outputs

支持的输出组件 ID 的位掩码

funcs

访问硬件的芯片功能

描述

struct komeda_component 描述了如何将组件链接到显示流水线的数据流功能。所有指定的组件都是此结构的子类。

struct komeda_component_output

定义:

struct komeda_component_output {
    struct komeda_component *component;
    u8 output_port;
};

成员

component

指示数据来自哪个组件

output_port

komeda_component_output.component 的输出端口

描述

一个组件有多个输出,如果想知道数据来自哪里,仅知道组件是不够的,我们仍然需要知道它的输出端口

struct komeda_component_state

定义:

struct komeda_component_state {
    struct drm_private_state obj;
    struct komeda_component *component;
    union {
        struct drm_crtc *crtc;
        struct drm_plane *plane;
        struct drm_connector *wb_conn;
        void *binding_user;
    };
    u16 active_inputs;
    u16 changed_active_inputs;
    u16 affected_inputs;
    struct komeda_component_output inputs[KOMEDA_COMPONENT_N_INPUTS];
};

成员

obj

通过 drm_atomic_state 跟踪 component_state

component

指向组件的反向指针

{unnamed_union}

匿名

crtc

用户 crtc 的反向指针

plane

用户 plane 的反向指针

wb_conn

用户 wb_connector 的反向指针

binding_user

当前绑定的用户,用户可以是 crtcplanewb_conn,其有效性由 componentinputs 决定

  • 层:其用户始终是 plane。

  • compiz/improc/timing_ctrlr:用户是 crtc。

  • wb_layer:wb_conn;

  • scaler:当输入是层时为 plane,当输入是 compiz 时为 wb_conn。

active_inputs

active_inputs 是 inputs 索引的位掩码

  • active_inputs = changed_active_inputs | unchanged_active_inputs

  • affected_inputs = old->active_inputs | new->active_inputs;

  • disabling_inputs = affected_inputs ^ active_inputs;

  • changed_inputs = disabling_inputs | changed_active_inputs;

注意:changed_inputs 不包括所有 active_input,而只包括 changed_active_inputs,并且此位掩码可以在芯片级用于脏更新。

changed_active_inputs

已更改的 active_inputs 的位掩码

affected_inputs

受影响的 inputs 的位掩码

inputs

仅当 BIT(i) 在 active_inputs 中设置时,特定的 inputs[i] 才有效,否则 inputs[i] 未定义。

描述

component_state 是组件的数据流配置,并且它是所有特定 component_state(如 komeda_layer_statekomeda_scaler_state)的超类。

struct komeda_pipeline

定义:

struct komeda_pipeline {
    struct drm_private_obj obj;
    struct komeda_dev *mdev;
    struct clk *pxlclk;
    int id;
    u32 avail_comps;
    u32 standalone_disabled_comps;
    int n_layers;
    struct komeda_layer *layers[KOMEDA_PIPELINE_MAX_LAYERS];
    int n_scalers;
    struct komeda_scaler *scalers[KOMEDA_PIPELINE_MAX_SCALERS];
    struct komeda_compiz *compiz;
    struct komeda_splitter *splitter;
    struct komeda_merger *merger;
    struct komeda_layer  *wb_layer;
    struct komeda_improc *improc;
    struct komeda_timing_ctrlr *ctrlr;
    const struct komeda_pipeline_funcs *funcs;
    struct device_node *of_node;
    struct device_node *of_output_port;
    struct device_node *of_output_links[2];
    bool dual_link;
};

成员

obj

将流水线链接为 drm_atomic_state 的私有对象

mdev

父 komeda_dev

pxlclk

像素时钟

id

流水线 ID

avail_comps

流水线的可用组件掩码

standalone_disabled_comps

当禁用流水线时,一些组件不能与其他组件一起禁用,而是需要单独禁用。 standalone_disabled_comps 是需要单独禁用的组件,这个概念也引入了两阶段的概念。第一阶段:用于禁用通用组件。第二阶段:用于禁用 standalone_disabled_comps。

n_layers

layers 上的层数

layers

流水线层

n_scalers

scalers 上的缩放器数量

scalers

流水线缩放器

compiz

合成器

splitter

用于将 compiz 输出拆分为两个半数据流

merger

merger

wb_layer

回写层

improc

后图像处理器

ctrlr

时序控制器

funcs

芯片私有流水线功能

of_node

流水线 dt 节点

of_output_port

流水线输出端口

of_output_links

输出连接器设备节点

dual_link

如果 of_output_links[0] 和 [1] 都有效,则为 true

描述

表示完整的显示流水线并包含所有功能组件。

struct komeda_pipeline_state

定义:

struct komeda_pipeline_state {
    struct drm_private_state obj;
    struct komeda_pipeline *pipe;
    struct drm_crtc *crtc;
    u32 active_comps;
};

成员

obj

通过 drm_atomic_state 跟踪 pipeline_state

pipe

指向流水线的后向指针

crtc

当前绑定的 crtc

active_comps

位掩码 - 活动组件的 BIT(component->id)

注意

与流水线不同,pipeline_state 不会收集任何 component_state 到其中。这是因为所有组件都将由 drm_atomic_state 管理。

资源发现和初始化

流水线和组件用于描述如何处理像素数据。我们仍然需要一个 @struct komeda_dev 来描述设备的整体视图以及设备的控制能力。

我们有 &komeda_dev、&komeda_pipeline 和 &komeda_component。现在用流水线填充设备。由于 komeda 不仅适用于 D71,也适用于以后的产品,当然我们最好在不同的产品之间尽可能多地共享。为了实现这一点,将 komeda 设备分为两层:CORE 和 CHIP。

  • CORE:用于通用功能和能力处理。

  • CHIP:用于寄存器编程和硬件特定功能(限制)处理。

CORE 可以通过三个芯片函数结构访问 CHIP

struct komeda_dev_funcs

定义:

struct komeda_dev_funcs {
    void (*init_format_table)(struct komeda_dev *mdev);
    int (*enum_resources)(struct komeda_dev *mdev);
    void (*cleanup)(struct komeda_dev *mdev);
    int (*connect_iommu)(struct komeda_dev *mdev);
    int (*disconnect_iommu)(struct komeda_dev *mdev);
    irqreturn_t (*irq_handler)(struct komeda_dev *mdev, struct komeda_events *events);
    int (*enable_irq)(struct komeda_dev *mdev);
    int (*disable_irq)(struct komeda_dev *mdev);
    void (*on_off_vblank)(struct komeda_dev *mdev, int master_pipe, bool on);
    void (*dump_register)(struct komeda_dev *mdev, struct seq_file *seq);
    int (*change_opmode)(struct komeda_dev *mdev, int new_mode);
    void (*flush)(struct komeda_dev *mdev, int master_pipe, u32 active_pipes);
};

成员

init_format_table

初始化 komeda_dev->format_table,此函数应在 enum_resource 之前调用

enum_resources

供 CHIP 向 CORE 报告或添加流水线和组件资源

cleanup

调用芯片来清理 komeda_dev->chip 数据

connect_iommu

可选,连接到外部 iommu

disconnect_iommu

可选,断开与外部 iommu 的连接

irq_handler

当发生中断时,供 CORE 从 CHIP 获取硬件事件

enable_irq

启用 irq

disable_irq

禁用 irq

on_off_vblank

通知硬件打开/关闭 vblank

dump_register

可选,将寄存器转储到 seq_file

change_opmode

通知硬件切换到新的显示操作模式。

flush

通知硬件刷新或启动更新

描述

由芯片级别提供,并由芯片入口函数 xxx_identify 返回,

struct komeda_dev

定义:

struct komeda_dev {
    struct device *dev;
    u32 __iomem   *reg_base;
    struct komeda_chip_info chip;
    struct komeda_format_caps_table fmt_tbl;
    struct clk *aclk;
    int irq;
    struct mutex lock;
    u32 dpmode;
    int n_pipelines;
    struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];
    const struct komeda_dev_funcs *funcs;
    void *chip_data;
    struct iommu_domain *iommu;
    struct dentry *debugfs_root;
    u16 err_verbosity;
#define KOMEDA_DEV_PRINT_ERR_EVENTS BIT(0);
#define KOMEDA_DEV_PRINT_WARN_EVENTS BIT(1);
#define KOMEDA_DEV_PRINT_INFO_EVENTS BIT(2);
#define KOMEDA_DEV_PRINT_DUMP_STATE_ON_EVENT BIT(8);
#define KOMEDA_DEV_PRINT_DISABLE_RATELIMIT BIT(12);
};

成员

dev

基本设备结构

reg_base

komeda io 空间的基地址

chip

基本芯片信息

fmt_tbl

komeda_dev_funcs->init_format_table 初始化

aclk

硬件主引擎时钟

irq

irq 编号

lock

用于保护 dpmode

dpmode

当前显示模式

n_pipelines

pipelines 中的管道数

pipelines

komeda 流水线

funcs

访问硬件的芯片函数

chip_data

芯片数据将由 komeda_dev_funcs.enum_resources() 添加,并由 komeda_dev_funcs.cleanup() 销毁

iommu

iommu 域

debugfs_root

komeda debugfs 的根目录

err_verbosity

位掩码,用于指示在错误时要打印的额外信息量

有关详细信息,请参见 KOMEDA_DEV_* 宏。低字节包含调试级别类别,高字节包含额外的调试选项。

描述

流水线和组件用于描述如何处理像素数据。komeda_device 用于描述设备的整体视图以及设备的控制能力。

格式处理

struct komeda_format_caps

定义:

struct komeda_format_caps {
    u32 hw_id;
    u32 fourcc;
    u32 supported_layer_types;
    u32 supported_rots;
    u32 supported_afbc_layouts;
    u64 supported_afbc_features;
};

成员

hw_id

硬件格式 ID,硬件特定值。

fourcc

drm fourcc 格式。

supported_layer_types

指示哪个层支持此格式

supported_rots

此格式允许的旋转

supported_afbc_layouts

支持的 afbc 布局

supported_afbc_features

支持的 afbc 功能

描述

komeda_format_caps 用于描述 ARM 显示针对特定格式的特定功能和限制,并且 format_caps 将像 drm_format_info 的扩展一样链接到 komeda_framebuffer 中。

注意

一个 fourcc 可能有两个不同的 format_caps 项目,分别用于 fourcc 和 fourcc+modifier

struct komeda_format_caps_table

format_caps 管理器

定义:

struct komeda_format_caps_table {
    u32 n_formats;
    const struct komeda_format_caps *format_caps;
    bool (*format_mod_supported)(const struct komeda_format_caps *caps, u32 layer_type, u64 modifier, u32 rot);
};

成员

n_formats

format_caps 列表的大小。

format_caps

format_caps 列表。

format_mod_supported

可选。某些硬件可能具有 format_caps 无法描述的特殊要求或限制,此函数为硬件提供了进行进一步硬件特定检查的能力。

struct komeda_fb

使用 komeda 属性扩展 drm_framebuffer

定义:

struct komeda_fb {
    struct drm_framebuffer base;
    const struct komeda_format_caps *format_caps;
    bool is_va;
    u32 aligned_w;
    u32 aligned_h;
    u32 afbc_size;
    u32 offset_payload;
};

成员

base

drm_framebuffer

format_caps

为 komeda 特定信息扩展 drm_format_info

is_va

如果启用了 smmu,则为 true

aligned_w

对齐的帧缓冲区宽度

aligned_h

对齐的帧缓冲区高度

afbc_size

afbc 的最小大小

offset_payload

afbc 主体缓冲区的起始位置

将 komeda_dev 附加到 DRM-KMS

Komeda 通过流水线/组件抽象资源,但是 DRM-KMS 使用 crtc/plane/connector。一个 KMS-obj 不能仅表示一个组件,因为单个 KMS 对象的要求不能仅仅通过单个组件来实现,通常需要多个组件来满足要求。例如,KMS 的设置模式、伽玛、ctm 都针对 CRTC-obj,但是 komeda 需要 compiz、improc 和 timing_ctrlr 协同工作才能满足这些要求。并且 KMS-Plane 可能需要多个 komeda 资源:layer/scaler/compiz。

因此,一个 KMS-Obj 代表 komeda 资源的一个子流水线。

因此,对于 komeda,我们将 KMS crtc/plane/connector 视为流水线和组件的用户,并且在任何时候,一个流水线/组件只能被一个用户使用。并且流水线/组件将被视为 DRM-KMS 的私有对象;状态也将由 drm_atomic_state 管理。

如何将 plane 映射到 Layer(输入)流水线

Komeda 有多个 Layer 输入流水线,请参见:- 单流水线数据流 - 启用 Slave 的双流水线

最简单的方法是将 plane 绑定到固定的 Layer 流水线,但是请考虑 komeda 的能力

  • Layer Split,请参见 Layer(输入)流水线

    Layer_Split 是一个非常复杂的功能,它将一个大图像分成两部分,并由两个层和两个缩放器分别处理。但是它在拆分后的图像中间引入了一个边缘问题或效果。为了避免此类问题,它需要复杂的拆分计算以及对层和缩放器的一些特殊配置。我们最好将这种硬件相关的复杂性隐藏到用户模式。

  • Slave 流水线,请参见 启用 Slave 的双流水线

    由于 compiz 组件不输出 alpha 值,因此 slave 流水线只能用于底部图层合成。komeda 驱动程序希望向用户隐藏此限制。解决此问题的方法是根据 plane_state->zpos 选择合适的图层。

因此,对于 komeda,KMS-plane 不代表固定的 komeda layer 流水线,而是具有相同功能的多个 Layer。Komeda 将选择一个或多个 Layer 来满足一个 KMS-plane 的要求。

使 component/pipeline 成为 drm_private_obj

drm_private_obj 添加到 komeda_componentkomeda_pipeline

struct komeda_component {
    struct drm_private_obj obj;
    ...
}

struct komeda_pipeline {
    struct drm_private_obj obj;
    ...
}

通过 drm_atomic_state 跟踪 component_state/pipeline_state

添加 drm_private_state 和用户到 komeda_component_state, komeda_pipeline_state

struct komeda_component_state {
    struct drm_private_state obj;
    void *binding_user;
    ...
}

struct komeda_pipeline_state {
    struct drm_private_state obj;
    struct drm_crtc *crtc;
    ...
}

komeda 组件验证

Komeda 有多种类型的组件,但验证过程类似,通常包括以下步骤

int komeda_xxxx_validate(struct komeda_component_xxx xxx_comp,
            struct komeda_component_output *input_dflow,
            struct drm_plane/crtc/connector *user,
            struct drm_plane/crtc/connector_state, *user_state)
{
     setup 1: check if component is needed, like the scaler is optional depending
              on the user_state; if unneeded, just return, and the caller will
              put the data flow into next stage.
     Setup 2: check user_state with component features and capabilities to see
              if requirements can be met; if not, return fail.
     Setup 3: get component_state from drm_atomic_state, and try set to set
              user to component; fail if component has been assigned to another
              user already.
     Setup 3: configure the component_state, like set its input component,
              convert user_state to component specific state.
     Setup 4: adjust the input_dflow and prepare it for the next stage.
}

komeda_kms 抽象

struct komeda_plane

drm_plane 的 komeda 实例

定义:

struct komeda_plane {
    struct drm_plane base;
    struct komeda_layer *layer;
};

成员

base

drm_plane

图层

表示此平面可用的图层输入管道。

注意:该图层不是针对特定的图层,而是表示具有相同功能的一组图层。

struct komeda_plane_state

定义:

struct komeda_plane_state {
    struct drm_plane_state base;
    struct list_head zlist_node;
    u8 layer_split : 1;
};

成员

base

drm_plane_state

zlist_node

zorder 列表节点

layer_split

开启/关闭 layer_split

描述

plane_state 可以拆分为两个数据流(左/右),并由两个图层 komeda_plane.layerkomeda_plane.layer.right 处理

struct komeda_wb_connector

定义:

struct komeda_wb_connector {
    struct drm_writeback_connector base;
    struct komeda_layer *wb_layer;
};

成员

base

drm_writeback_connector

wb_layer

表示 komeda 相关的回写管道

struct komeda_crtc

定义:

struct komeda_crtc {
    struct drm_crtc base;
    struct komeda_pipeline *master;
    struct komeda_pipeline *slave;
    u32 slave_planes;
    struct komeda_wb_connector *wb_conn;
    struct completion *disable_done;
    struct drm_encoder encoder;
};

成员

base

drm_crtc

master

只有 master 有显示输出

slave

可选

没有自己的显示输出,处理的数据流将合并到 master 中。

slave_planes

komeda 从属平面掩码

wb_conn

komeda 回写连接器

disable_done

此 flip_done 用于跟踪禁用

encoder

管道末端的编码器

struct komeda_crtc_state

定义:

struct komeda_crtc_state {
    struct drm_crtc_state base;
    u32 affected_pipes;
    u32 active_pipes;
    u64 clock_ratio;
    u32 max_slave_zorder;
};

成员

base

drm_crtc_state

affected_pipes

一次显示实例中受影响的管道

active_pipes

一次显示实例中活动的管道

clock_ratio

(aclk << 32)/pxlclk 的比率

max_slave_zorder

从属 zorder 的最大值

komde_kms 函数

int komeda_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)

构建显示输出数据流

参数

struct drm_crtc *crtc

DRM crtc

struct drm_atomic_state *state

crtc 状态对象

描述

crtc_atomic_check 是最终检查阶段,因此除了根据 crtc_state 构建显示数据管道外,还需要释放或禁用未声明的管道资源。

返回

成功返回零,失败返回 -errno

int komeda_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)

构建输入数据流

参数

struct drm_plane *plane

DRM 平面

struct drm_atomic_state *state

平面状态对象

返回

成功返回零,失败返回 -errno

构建 Komeda 作为 Linux 模块驱动程序

现在我们有两个级别的设备

  • komeda_dev:描述真实的显示硬件。

  • komeda_kms_dev:将 komeda_dev 连接到 DRM-KMS。

所有 komeda 操作都由 komeda_dev 或 komeda_kms_dev 提供或操作,模块驱动程序只是一个简单的包装器,将 Linux 命令 (probe/remove/pm) 传递到 komeda_dev 或 komeda_kms_dev。