6.1 mtk_plane.c 核心结构
先看代码骨架。MTK的Plane驱动,核心文件就是mtk_plane.c。它主要做了三件事:
- 注册Plane到DRM框架
- 实现
atomic_check和atomic_update回调 - 管理Overlay硬件资源
我贴一段关键的结构体定义:
struct mtk_plane { struct drm_plane base; struct mtk_plane_state *state; struct mtk_ddp_comp *ddp_comp; int idx; bool enabled; };嗯,这里要注意ddp_comp这个指针。它指向的是显示流水线里的具体硬件模块。每个Plane都绑定了一个DDP组件,说白了就是硬件Overlay单元。
6.2 Overlay硬件单元
MTK8678的Overlay单元,我直接说结论:它支持4个图层叠加。每个图层都有独立的寄存器组,用来配置源地址、目标尺寸、格式、透明度等。
我在项目中遇到过一个问题:客户要求同时显示主界面、画中画、悬浮按钮和状态栏。结果发现第四个图层死活出不来。查了半天,原来是某个Overlay单元被其他模块占用了。嗯,这玩意儿是共享资源,用之前得先申请。
Overlay硬件单元的主要能力:
- 支持RGB和YUV格式输入
- 支持1x到4x缩放(硬件scaler)
- 支持逐像素Alpha混合
- 支持全局Alpha(整个图层半透明)
关键寄存器:OVL_CON控制寄存器、OVL_SRC_ADDR源地址、OVL_SRC_SIZE源尺寸、OVL_DST_SIZE目标尺寸、OVL_ALPHA透明度控制。
6.3 格式转换与缩放
格式转换这块,说白了就是硬件帮你做颜色空间转换。比如视频流通常是NV12(YUV),但屏幕是RGB。Overlay硬件内部有个CSC(Color Space Converter)模块,自动帮你转。
我曾经踩过一个坑:用NV12格式播放视频,结果颜色偏绿。查了三天,发现是CSC矩阵配置错了。MTK的CSC矩阵有标准模式和宽色域模式,默认是标准模式,但视频源是BT.2020宽色域。改一下寄存器就好了。
缩放就更直接了。硬件scaler支持双线性插值和最近邻插值。我建议:
- 视频播放用双线性,画质好
- 游戏UI用最近邻,速度快
看一段配置代码:
static void mtk_plane_setup_scaler(struct mtk_plane_state *state) { struct mtk_rect src = state->src; struct mtk_rect dst = state->dst; // 计算缩放比例 u32 hscale = (src.width << 12) / dst.width; u32 vscale = (src.height << 12) / dst.height; // 写入硬件寄存器 writel(hscale, OVL_HSIZE); writel(vscale, OVL_VSIZE); }这里<< 12是定点数表示法,保留小数精度。说白了就是硬件只认整数,但我们要做亚像素精度控制。
6.4 Alpha混合
Alpha混合是Plane的灵魂。没有它,图层就是硬叠,互相遮挡。有了Alpha,才能实现半透明、淡入淡出、阴影效果。
MTK的Overlay支持两种Alpha模式:
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 全局Alpha | 整个图层统一透明度 | 悬浮窗、状态栏 |
| 逐像素Alpha | 每个像素有自己的Alpha值 | 带透明通道的PNG、视频字幕 |
混合公式很简单:
输出 = 前景 * Alpha + 背景 * (1 - Alpha)但硬件实现起来有讲究。我记得有一次做画中画功能,主画面和子画面叠加时,边缘出现黑边。原因是子画面的Alpha通道没有做预乘(Premultiplied Alpha)。
避坑指南:我曾经在配置Alpha混合时,忘记设置OVL_CON寄存器的PREMULTI_EN位。结果半透明区域全是黑色锯齿。解决办法很简单:把源数据做预乘,或者硬件开启预乘模式。
看一段Alpha配置代码:
static void mtk_plane_set_alpha(struct mtk_plane_state *state) { u32 alpha = state->base.alpha; // 0-255 u32 premulti = state->base.pixel_blend_mode; // 配置全局Alpha writel(alpha, OVL_ALPHA); // 配置混合模式 if (premulti == DRM_MODE_BLEND_PREMULTI) set_bit(OVL_CON_PREMULTI_EN, OVL_CON); else clear_bit(OVL_CON_PREMULTI_EN, OVL_CON); }这里DRM_MODE_BLEND_PREMULTI是DRM框架定义的混合模式。说白了就是告诉硬件:你的Alpha是预乘过的,还是没预乘的。搞错了,画面就崩。
6.5 实战经验总结
最后,我分享几个实际调试中的经验:
- 先确认硬件资源:Overlay单元数量有限,用之前检查
mtk_ddp_comp_get是否成功 - 格式转换要匹配:源格式和目标格式的CSC矩阵必须对应,否则颜色失真
- 缩放性能要评估:4K分辨率下做2x缩放,带宽压力很大。我建议用
perf工具监控DDR带宽 - Alpha混合顺序:多个图层叠加时,从底层到顶层依次混合。顺序错了,半透明效果就乱套
警告:千万不要在中断上下文里配置Plane寄存器。我曾经这么干过,结果系统直接死锁。Plane配置必须在atomic_update回调里完成,而且要用spin_lock保护。
好了,Plane驱动这块就聊到这儿。说白了,它就是显示流水线里的“图层管理员”。你只要把源地址、尺寸、格式、Alpha值告诉它,它就能帮你把画面叠到屏幕上。下一章咱们聊聊CRTC驱动,那个更刺激——它管的是整个显示时序。