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(主)的“从属”。
单流水线数据流¶
启用从属的双流水线¶
输入和输出的子流水线¶
根据输入/输出的使用情况,可以将完整的显示流水线轻松地划分为三个子流水线。
层 (输入) 流水线¶
回写 (输出) 流水线¶
显示输出流水线¶
在以下章节中,我们将看到这三个子流水线将分别由 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_inputs 或 supported_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
描述
一个组件有多个输出,如果想知道数据来自哪里,仅知道组件是不够的,我们仍然需要知道它的输出端口
-
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
当前绑定的用户,用户可以是 crtc、plane 或 wb_conn,其有效性由 component 和 inputs 决定
层:其用户始终是 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_state、komeda_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_pipeline_funcs
struct komeda_component_funcs
-
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
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
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 资源的一个子流水线。
Plane:Layer(输入)流水线
Wb_connector:Writeback(输出)流水线
Crtc:显示输出流水线
因此,对于 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_component
、komeda_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
图层
表示此平面可用的图层输入管道。
注意:该图层不是针对特定的图层,而是表示具有相同功能的一组图层。
-
struct komeda_plane_state¶
定义:
struct komeda_plane_state {
struct drm_plane_state base;
struct list_head zlist_node;
u8 layer_split : 1;
};
成员
base
zlist_node
zorder 列表节点
layer_split
开启/关闭 layer_split
描述
plane_state 可以拆分为两个数据流(左/右),并由两个图层 komeda_plane.layer
和 komeda_plane.layer
.right 处理
-
struct komeda_wb_connector¶
定义:
struct komeda_wb_connector {
struct drm_writeback_connector base;
struct komeda_layer *wb_layer;
};
成员
base
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
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
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。