一、MTK KMS分析
mtk_drm_kms_init 函數分析
mtk_drm_kms_init 是 MediaTek DRM 驅動程序中的一個靜態函數(static int mtk_drm_kms_init(struct drm_device *drm)),位于 mtk_drm_drv.c 文件中。該函數的主要作用是初始化 DRM 設備的 Kernel Mode Setting (KMS) 子系統,包括配置顯示模式、綁定硬件組件、創建 CRTC(Controller)、設置 DMA 參數、初始化各種輔助結構和工作隊列等。它是驅動從硬件抽象層過渡到 DRM 框架的核心入口點,通常在組件綁定(component_bind_all)后調用,確保顯示管道(display pipeline)準備就緒,支持用戶空間的圖形渲染和模式設置。
函數的輸入是一個已分配的 drm_device 結構(包含 MediaTek 特定的私有數據 mtk_drm_private)。輸出為 0(成功)或負錯誤碼(失敗)。整個函數邏輯嚴謹,包含多個錯誤處理路徑(使用 goto 跳轉清理資源)。下面我基于提供的代碼,按步驟詳細分析其執行流程、關鍵操作和潛在邏輯。
1. 準備階段:獲取私有數據和檢查依賴
- struct mtk_drm_private *private = drm->dev_private;
- 從 drm_device 中提取 MediaTek 特定的私有數據結構 mtk_drm_private,其中包含設備樹節點、寄存器映射、helper_opt 等信息(這些在 mtk_drm_probe 中已初始化)。
- 檢查 M4U (Memory Management Unit) 支持:
- if (mtk_drm_helper_get_opt(private->helper_opt, MTK_DRM_OPT_USE_M4U)) { ... }
- 如果啟用 M4U(IOMMU 相關的內存管理選項),檢查 IOMMU 是否就緒(iommu_present(&platform_bus_type))。
- 如果 IOMMU 未準備好,返回 -EPROBE_DEFER,延遲驅動探測(probe),等待 IOMMU 驅動加載。這確保了 DMA 內存分配的安全性和連續性(防止內存碎片)。
- if (mtk_drm_helper_get_opt(private->helper_opt, MTK_DRM_OPT_USE_M4U)) { ... }
- 日志記錄:DDPINFO("%s+\n", __func__); – 輸出調試信息,表示函數開始。
2. 初始化 DRM Mode Config
- drm_mode_config_init(drm);
- 初始化 DRM 的模式配置結構(drm_mode_config),設置默認的模式設置回調(如 atomic_check、atomic_commit),啟用原子模式設置支持。
- 設置默認分辨率限制:
- drm->mode_config.min_width = 1; drm->mode_config.min_height = 1;
- 最小分辨率設置為 1x1(默認最小值,避免無效模式)。
- drm->mode_config.max_width = 4096; drm->mode_config.max_height = 4096;
- 最大分辨率設置為 4096x4096(默認值,用于檢查幀緩沖區大小限制,在 drm_mode_addfb 中使用)。
- drm->mode_config.min_width = 1; drm->mode_config.min_height = 1;
- 設置模式配置函數:drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
- 關聯 MediaTek 特定的模式配置函數(未在代碼中顯示,但可能是自定義的頁面翻轉、屬性處理等)。
3. 綁定組件和初始化 VBlank
- ret = component_bind_all(drm->dev, drm);
- 綁定所有顯示組件(通過 mtk_drm_ops 中的 bind 函數),這些組件在 mtk_drm_probe 中已添加到 match 列表中(如 OVL、RDMA、DSI 等)。
- 如果綁定失敗,跳轉到 err_config_cleanup 清理 mode config。
- ret = drm_vblank_init(drm, MAX_CRTC);
- 初始化 VBlank(垂直同步)支持,最多支持 MAX_CRTC 個 CRTC(通常為 3 或 4,根據 SoC)。
- VBlank 用于同步幀渲染和顯示,避免撕裂(tearing)。失敗則跳轉到 err_component_unbind 解綁組件。
4. 創建 CRTC(顯示控制器)
- ret = mtk_drm_crtc_create(drm, private->data->main_path_data);
- 創建主路徑 CRTC(OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 等)。
- main_path_data 來自 SoC 特定的數據(如 mt6885_mmsys_driver_data)。
- 根據設備樹屬性和階段(disp_helper_get_stage())創建擴展路徑:
- 如果是正常階段(DISP_HELPER_STAGE_NORMAL):
- 檢查 "enable_ext_alter_path" 屬性,如果啟用,使用 ext_alter_path_data 創建擴展路徑 CRTC(OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0)。
- 否則,使用 ext_path_data。
- 創建第三個 CRTC(third_path_data 或變體,根據 "condition-num" 屬性調整路徑,如是否包含 TDSHP)。
- 創建第四個 CRTC(如果啟用 "enable_secondary_path" 或 "enable_discrete_path",使用 fourth_path_data_secondary 或 fourth_path_data_discrete)。
- 如果是正常階段(DISP_HELPER_STAGE_NORMAL):
- 這些 CRTC 代表不同的顯示管道,支持多屏輸出(如主屏、擴展屏)。
- 失敗則跳轉到 err_component_unbind。
- 注釋:/*TODO: Need to check path rule*/ – 表示路徑規則檢查待實現,可能涉及硬件兼容性。
5. 設置 DMA 和內存分配
- 選擇 OVL 設備作為 DMA 分配設備:
- 從主路徑或擴展路徑的第一個組件節點獲取 OVL 設備(private->comp_node[...])。
- private->dma_dev = &pdev->dev;
- 使用 OVL 設備進行所有 DMA 內存分配,確保連續 IOVA(IO Virtual Address)用于 PRIME 緩沖區導入。
- 配置 DMA 參數:
- 如果 dma_dev->dma_parms 未分配,動態分配(devm_kzalloc)。
- ret = dma_set_max_seg_size(dma_dev, (unsigned int)DMA_BIT_MASK(32));
- 設置 DMA 段最大大小為 32-bit(4GB),確保 PRIME 緩沖區導入時的連續性。
- 失敗則清理并返回。
- drm_mode_config_reset(drm);
- 重置模式配置到默認狀態。