drm/komeda Arm 显示驱动¶
drm/komeda 驱动程序支持 Arm 显示处理器 D71 及更高版本的产品,本文档简要概述了驱动程序设计:它的工作原理以及为什么要这样设计。
D71 类似显示 IP 概述¶
从 D71 开始,Arm 显示 IP 开始采用灵活的模块化架构。显示管道由多个独立且功能性的管道阶段组成,这些阶段称为组件,每个组件都具有一些特定功能,可以为流动的管道像素数据提供特定的处理。
典型的 D71 组件
图层¶
图层是第一个管道阶段,它为下一阶段准备像素数据。它从内存中获取像素,如果它是 AFBC,则解码它,旋转源图像,解包或将 YUV 像素转换为设备内部 RGB 像素,然后根据需要调整像素的 color_space。
缩放器¶
顾名思义,缩放器负责缩放,D71 还支持通过缩放器进行图像增强。缩放器的使用非常灵活,可以连接到图层输出以进行图层缩放,或者连接到合成器并缩放整个显示帧,然后将输出数据馈送到 wb_layer,然后将其写入内存。
合成器 (compiz)¶
合成器将多个图层或像素数据流混合成一个单独的显示帧。其输出帧可以馈送到后图像处理器以在监视器上显示,或馈送到 wb_layer 并同时写入内存。用户还可以在合成器和 wb_layer 之间插入一个缩放器,以首先缩小显示帧,然后写入内存。
回写图层 (wb_layer)¶
回写图层执行与图层相反的操作,它连接到 compiz 并将合成结果写入内存。
后图像处理器 (improc)¶
后图像处理器调整帧数据,如伽玛和色彩空间,以满足监视器的要求。
时序控制器 (timing_ctrlr)¶
显示管道的最后阶段,时序控制器不是用于像素处理,而是仅用于控制显示时序。
合并器¶
与图层相比,D71 缩放器大多只有一半的水平输入/输出能力,例如,如果图层支持 4K 输入大小,则缩放器在同一时间只能支持 2K 输入/输出。为了实现完整的帧缩放,D71 引入了图层分割,它将整个图像分割成两个半部分,并将它们馈送到两个图层 A 和 B,并独立进行缩放。缩放后,需要将结果馈送到合并器以将两个部分图像合并在一起,然后将合并后的结果输出到 compiz。
分割器¶
与图层分割类似,但分割器用于回写,它将 compiz 结果分割成两个部分,然后将它们馈送到两个缩放器。
D71 管道的可能用法¶
受益于模块化架构,D71 管道可以轻松调整以适应不同的用途。D71 有两个管道,它们支持两种工作模式
双显示模式 两个管道独立且分开地工作以驱动两个显示输出。
单显示模式 两个管道协同工作以仅驱动一个显示输出。
在这种模式下,pipeline_B 不独立工作,而是将其合成结果输出到 pipeline_A,其像素时序也来自 pipeline_A.timing_ctrlr。pipeline_B 的工作方式就像 pipeline_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}
anonymous
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:当输入是 layer 时为 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
只有在 active_inputs 中设置了 BIT(i) 时,特定的 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 是需要单独禁用的组件,并且此概念还引入了两阶段的概念。阶段 1:用于禁用通用组件。阶段 2:用于禁用 standalong_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
硬件主引擎 clk
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 layerout
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 资源的子管道。
因此,对于 komeda,我们将 KMS crtc/plane/connector 视为管道和组件的用户,并且在任何时候,管道/组件只能由一个用户使用。管道/组件将被视为 DRM-KMS 的私有对象;状态也将由 drm_atomic_state 管理。
如何将 plane 映射到图层(输入)管道¶
Komeda 有多个图层输入管道,请参见:- 单管道数据流 - 启用从属的双管道
最简单的方法是将 plane 绑定到固定的图层管道,但考虑到 komeda 的功能
图层分割,请参见 图层(输入)管道
图层_分割是一个非常复杂的功能,它将一个大图像分成两个部分,并分别由两个图层和两个缩放器处理。但是,它在图像中间分割后会导入边缘问题或效果。为了避免此类问题,它需要复杂的分割计算和一些特殊的图层和缩放器配置。我们最好将此类硬件相关复杂性隐藏到用户模式。
从属管道,请参见 启用从属的双管道
由于 compiz 组件不输出 alpha 值,因此从属管道只能用于底层组合。komeda 驱动程序希望向用户隐藏此限制。做到这一点的方法是根据 plane_state->zpos 选择合适的图层。
因此,对于 komeda,KMS-plane 不表示固定的 komeda 图层管道,而是具有相同功能的多个图层。Komeda 将选择一个或多个图层以满足一个 KMS-plane 的要求。
使组件/管道成为 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
和 user 添加到 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
layer
表示此 plane 的可用图层输入管道。
注意:该图层不是针对特定图层,而是指示一组具有相同功能的图层。
-
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 从属 plane 掩码
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 plane
struct drm_atomic_state *state
plane 状态对象
返回
成功返回零,失败返回 -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。