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(主) 的“从属”

单管道数据流

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}

anonymous

crtc

用户 crtc 的后向指针

plane

用户 plane 的后向指针

wb_conn

用户 wb_connector 的后向指针

binding_user

当前绑定的用户,用户可以是 crtcplanewb_conn,这由 componentinputs 决定。

  • 图层:其用户始终是 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_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 是需要单独禁用的组件,并且此概念还引入了两阶段的概念。阶段 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_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

硬件主引擎 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

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 映射到图层(输入)管道

Komeda 有多个图层输入管道,请参见:- 单管道数据流 - 启用从属的双管道

最简单的方法是将 plane 绑定到固定的图层管道,但考虑到 komeda 的功能

  • 图层分割,请参见 图层(输入)管道

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

  • 从属管道,请参见 启用从属的双管道

    由于 compiz 组件不输出 alpha 值,因此从属管道只能用于底层组合。komeda 驱动程序希望向用户隐藏此限制。做到这一点的方法是根据 plane_state->zpos 选择合适的图层。

因此,对于 komeda,KMS-plane 不表示固定的 komeda 图层管道,而是具有相同功能的多个图层。Komeda 将选择一个或多个图层以满足一个 KMS-plane 的要求。

使组件/管道成为 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 和 user 添加到 komeda_component_statekomeda_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

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

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 从属 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

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 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。