【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

系列文章:

  1. 【Android SDM660开机流程】- UEFI XBL 代码流程分析
  2. 【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序
  3. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析
  4. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程
  5. 【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析

在前面充电流程中,我们看到了显示BMP图片这一块,本文来分析下BMP图片的显示流程。

显示图片的驱动GUUID为 gEfiGraphicsOutputProtocolGuid ,它定义在

# amss/BOOT.XF.1.4/boot_images/MdePkg/MdePkg.dec

  ## Include/Protocol/GraphicsOutput.h
  gEfiGraphicsOutputProtocolGuid = { 0x9042A9DE, 0x23DC, 0x4A38, { 0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A }}

# amss/BOOT.XF.1.4/boot_images/MdePkg/Include/Protocol/GraphicsOutput.h
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }
typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL;

其对应的模块为:amss/BOOT.XF.1.4/boot_images/QcomPkg/Drivers/DisplayDxe

# amss/BOOT.XF.1.4/boot_images/QcomPkg/Sdm660Pkg/LA/Sdm660Pkg.fdf
  # Display/MDP DXE driver
  INF QcomPkg/Drivers/DisplayDxe/DisplayDxe.inf

# amss/BOOT.XF.1.4/boot_images/QcomPkg/Sdm660Pkg/LA/Sdm660Pkg_Core.dsc
  # Display DXE Driver
  QcomPkg/Drivers/DisplayDxe/DisplayDxe.inf

# amss/BOOT.XF.1.4/boot_images/QcomPkg/Sdm660Pkg/LA/Apriori.fdf.inc
  INF QcomPkg/Drivers/DisplayDxe/DisplayDxe.inf

1. GraphicsOutput.h

gEfiGraphicsOutputProtocolGuid所对应的结构体为:_EFI_GRAPHICS_OUTPUT_PROTOCOL

# amss/BOOT.XF.1.4/boot_images/MdePkg/Include/Protocol/GraphicsOutput.h

struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
  EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE  QueryMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE    SetMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT         Blt;
  /// Pointer to EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE data.
  EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE        *Mode;
};
extern EFI_GUID gEfiGraphicsOutputProtocolGuid;

2. 显示驱动初化 DisplayDxe.c

DisplayDxe/DisplayDxe.inf 中我们可以知道,入口函数为:DisplayDxeInitialize
其主要工作流程为:

  1. 注册回调函数,当退出UEFI是,会调用 DisplayDxeExitBootServicesEvent 函数,释放相关的资源
  2. 创建回调函数,当需要显示是,发送这个时间,就会调用 UIActiveEventCallBack,给屏幕上电。
  3. 创建回调函数,收到这个事件是,会调用回调函数UIIdleEventCallBack,给屏幕下电,关闭显示
  4. 初始化ABL上下文,获得所有支持的屏
  5. 显示屏初始化
  6. 如果配置了MDP_DISPLAY_PRIMARY,则上电,检测屏幕是否存在,配置屏幕显示模式,使能主屏显示prop属性
  7. 如果配置了MDP_DISPLAY_EXTERNAL ,同样开始上电检测、配置屏幕显示模式
  8. 初始化显示 pix format 相关信息
  9. 注册显示屏相关的GUUID 服务
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Drivers/DisplayDxe/DisplayDxe.c

EFI_STATUS EFIAPI DisplayDxeInitialize(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
  // 1. 注册回调函数,当退出UEFI是,会调用 DisplayDxeExitBootServicesEvent 函数,释放相关的资源
  /* Register for an ExitBootServicesEvent */
  gBS->CreateEvent(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, DisplayDxeExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
  /* Register for BlockIoRefreshEvent */
  gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, BlockIoCallback, NULL, &gBlockIoRefreshGuid, &gBlockIoRefreshEvt);
  
  // 2. 创建回调函数,当需要显示是,发送这个时间,就会调用 UIActiveEventCallBack,给屏幕上电。
  /* Create and Register for UI_Active Event*/
  gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, UIActiveEventCallBack, NULL, &UIActiveEventGuid, &gModeInfo.UIActiveEvent);
  
  // 3. 创建回调函数,收到这个事件是,会调用回调函数UIIdleEventCallBack,给屏幕下电,关闭显示
  /* Create and Register for UI_Idle Event*/
  gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, UIIdleEventCallBack, NULL, &UIIdleEventGuid, &gModeInfo.UIIdleEvent);

  // 4. 初始化ABL上下文,获得所有支持的屏
  // Initialize Apps BootLoader (ABL) interface
  // It checks for panel override, if any, set by ABL and sets up parameters which will be used by MDPInit
  MDPSetProperty(MDP_DISPLAY_PRIMARY, MDP_DISPLAY_PROPERTY_ABL_INTERFACE_INIT, NULL);
  ==========>
  		Display_ABL_Initialize();
  // 5. 显示屏初始化
  // Initialize the MDP 
  MDPInit(&sInitParam, 0x0);
  //
  // Main Display //
  //
        // 6. 如果配置了MDP_DISPLAY_PRIMARY
        // If the primary is supported initialize it
        if (TRUE == sInitParam.aSupportedDisplays[MDP_DISPLAY_PRIMARY])
        {
           // 6.1 上电
            MDPPower(MDP_DISPLAY_PRIMARY, &sPowerParams, 0x0);
           // 6.2 检测默认配置的屏幕是否存在
               // Default reporting of primary display
               if (MDP_STATUS_OK != MDPDetect(MDP_DISPLAY_PRIMARY, &sDetectParams, 0x0))
               {
                  eStatus = EFI_DEVICE_ERROR;
               }
               else if (TRUE == sDetectParams.bDisplayDetected)
               { 
                  // 6.3 配置屏幕显示模式
                  eStatus = DisplayDxeSelectMode(MDP_DISPLAY_PRIMARY, &sDetectParams);

                  // 6.4 使能主屏显示prop属性 Set the primary display to on 
                  MDPSetProperty(MDP_DISPLAY_PRIMARY, MDP_DISPLAY_PROPERTY_POWER_STATE, &sDisplayProp)
               }
            }
        }    

		 // 7. 拓展屏初始化,如果存在 MDP_DISPLAY_EXTERNAL 对应的屏幕,则开始上电检测、配置模式
        //
        // External Display //
        //
        // If the external is supported initialize it,
        if ((EFI_SUCCESS == eStatus) && 
            (TRUE == sInitParam.aSupportedDisplays[MDP_DISPLAY_EXTERNAL]))
            {
                MDPPower(MDP_DISPLAY_EXTERNAL, &sPowerParams, 0x0);
                
                   // Default reporting of External display
                   if ((MDP_STATUS_OK != MDPDetect(MDP_DISPLAY_EXTERNAL, &sDetectParams, 0x0)) || 
                       (FALSE == sDetectParams.bDisplayDetected))
                   {
                      // External display not detected, turn off and continue on
                      sPowerParams.bPowerOn = FALSE;
                
                      if (MDP_STATUS_OK != MDPPower(MDP_DISPLAY_EXTERNAL, &sPowerParams, 0x0))
                      {
                        DEBUG ((EFI_D_INFO, "DisplayDxe: External panel power up failed!\n"));   
                      }
                   }
                   else
                   {
                      eStatus = DisplayDxeSelectMode(MDP_DISPLAY_EXTERNAL,&sDetectParams);
                
                      // Set the external display to on 
                      if (MDP_STATUS_OK != MDPSetProperty(MDP_DISPLAY_EXTERNAL, MDP_DISPLAY_PROPERTY_POWER_STATE, &sDisplayProp))
                      {
                        eStatus = EFI_DEVICE_ERROR; 
                      }
                   }
            }
        }
         
        // If we not have detected a valid mode on both primary and external display report error
        if ((0 == gModeInfo.uNumModes[MDP_DISPLAY_PRIMARY]) && (0 == gModeInfo.uNumModes[MDP_DISPLAY_EXTERNAL]))
        {
           eStatus = EFI_DEVICE_ERROR;
        }
        else
        {
           // 8. 初始化显示format相关信息。
           // Default at some dummy mode
           gModeInfo.sCurrentModeInfo.Version                       = GRAPHICS_OUTPUT_PROTOCOL_REVISION;
           gModeInfo.sCurrentModeInfo.PixelFormat                   = DISPLAYDXE_DEFAULT_PIXEL_FORMAT;
           gModeInfo.sCurrentModeInfo.HorizontalResolution          = 0;
           gModeInfo.sCurrentModeInfo.VerticalResolution            = 0;
           gModeInfo.sCurrentModeInfo.PixelInformation.RedMask      = DISPLAYDXE_RED_MASK;
           gModeInfo.sCurrentModeInfo.PixelInformation.GreenMask    = DISPLAYDXE_GREEN_MASK;
           gModeInfo.sCurrentModeInfo.PixelInformation.BlueMask     = DISPLAYDXE_BLUE_MASK;
           gModeInfo.sCurrentModeInfo.PixelInformation.ReservedMask = DISPLAYDXE_ALPHA_MASK;
           gModeInfo.sCurrentModeInfo.PixelsPerScanLine             = 0;
           
           // Setup the protocol information, set the current mode to an invalid mode forcing a set mode
           gModeInfo.sProtocolInfo.MaxMode    = 1;
           gModeInfo.sProtocolInfo.Mode       = (UINT32)-1;
           gModeInfo.sProtocolInfo.SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
           gModeInfo.sProtocolInfo.Info       = &gModeInfo.sCurrentModeInfo;

			// 9. 注册显示屏相关的GUUID 服务
           // Install display protocols only if panel is detected.
           // Make a new handle with EFI Graphics Protocol
           gBS->InstallMultipleProtocolInterfaces (&hUEFIDisplayHandle, 
           						&gEfiDevicePathProtocolGuid, &DisplayDevicePath,
                                &sOutputGUID,
                                &gDisplayDxeOutputProtocol,
                                &gQcomDisplayPwrCtrlProtocolGuid,
                                &gDisplayPwrCtrlProtocolImplementation,
                                &gEfiDisplayPowerStateProtocolGuid,
                                &gDisplayPwrProtocolImplementation,
                                &gQcomDisplayABLProtocolGuid,
                                &gQcomDisplayABLProtocolImplementation,NULL);
  return eStatus;
}

2.1 获得所有支持的屏 Display_ABL_Initialize()
  1. 获得全局变量 Display_ABLContextType gsABLContext
  2. 清空结构体 gsABLContext
  3. 获取所有支持的 display panels 列表
  4. 检查 ABL中是否有配置覆盖
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Library/MDPLib/DisplayABLInterface.c
void Display_ABL_Initialize(void)
{ 
  // 1. 获得全局变量 Display_ABLContextType gsABLContext
  Display_ABLContextType *pABLContext = GET_ABL_CONTEXT();
  MDP_OSAL_DELAYMS(100);
  // 2. 清空结构体 gsABLContext
  MDP_OSAL_MEMZERO(pABLContext, sizeof(Display_ABLContextType));
  
  // 3. 获取所有支持的 display panels 列表
  /* get list of supported panels & build DT info array */
  CheckTargetPanelSupport();
  
  // 4. 检查 ABL中是否有配置覆盖 /* Check panel override */
  CheckPanelOverride();
}

2.1.1 解析屏列表,并配置全局变量 CheckTargetPanelSupport()
  1. 获取gPanelList数组,保存在psPanelDTInfo中,
    从头文件#include "MDPPlatformLib.h"可以看出,调用的是 /MDPPlatformLibBoot/MDPPlatformLib.c 中的代码
  2. 解析屏幕列表
  3. 使用 QcomTokenSpace GUID 配置全局变量: DisplaySupportedPanelCount
  4. 使用 QcomTokenSpace GUID 配置全局变量: DisplaySupportedPanelList
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Library/MDPLib/DisplayABLInterface.c
/****************************************************************************
*   Helper function to query Platform Lib for a list of supported panels and
*   set boot service variables to inform ABL
****************************************************************************/
static void CheckTargetPanelSupport()
{
  MDPPlatformParams       sPlatformParams;
  // 1. 获取gPanelList数组,保存在psPanelDTInfo中,从头文件#include "MDPPlatformLib.h"可以看出,调用的是 /MDPPlatformLibBoot/MDPPlatformLib.c 中的代码
  MDPPlatformConfigure(MDP_DISPLAY_PRIMARY, MDPPLATFORM_CONFIG_GETPANELDTINFO, &sPlatformParams);
  	========>  
  			pPlatformParams->psPanelDTInfo = (PanelInfoType*)&gPanelList;
  	<========
    // 
    CHAR8 *pPanels     = pABLContext->pSupportedPanels;
    CHAR8 *szSeparator = ",";
    uint32 uPanelCount = 0;
    PanelInfoType *psPanelDTInfo = sPlatformParams.psPanelDTInfo;
    /* The received panel list is sorted by panel IDs Go through the panel mapping to lookup names. */
    while (MDPPLATFORM_PANEL_NONE != psPanelDTInfo[uPanelCount].ePanel){
    	if (0 < uPanelCount)
    		LocalAsciiStrnCat(pPanels, PANEL_LIST_STR_LEN_MAX, szSeparator);  /* separator */

		/* append it to string */
        LocalAsciiStrnCat(pPanels, PANEL_LIST_STR_LEN_MAX, (CHAR8*)psPanelDTInfo[uPanelCount].name);
        uPanelCount++;
	}
	/* Save Panel DT info to ABL Context */
	pABLContext->pDTInfoArray = psPanelDTInfo;
	
    // 2. 使用 QcomTokenSpace GUID 配置全局变量: DisplaySupportedPanelCount
    eStatus = MDP_SetBootServiceVariable(DISPVAR_SUPPORTED_PANEL_COUNT, &uPanelCount, sizeof(uPanelCount), 0);
    
    // 3. 使用 QcomTokenSpace GUID 配置全局变量: DisplaySupportedPanelList
    eStatus = MDP_SetBootServiceVariable(DISPVAR_SUPPORTED_PANEL_LIST, pPanels, AsciiStrSize(pPanels), 0);
}

在gPanelList 中保存了当前所支持的所有屏信息列表,从gPanelList 中可以解析出屏相关的配置信息,如下所示:

# amss/BOOT.XF.1.4/boot_images/QcomPkg/Sdm660Pkg/Library/MDPPlatformLib/MDPPlatformLib.c
/* DT info for panels*/
const PanelInfoType gPanelList[] =
{
  /*Supported Panels*/  
   /* Truly HX8399 fhd video */
  PANEL_CREATE_ENTRY("hx8399_truly_fhd_video",     MDPPLATFORM_PANEL_HX8399_TRULY_FHD_VIDEO,      "qcom,mdss_dsi_hx8399_truly_fhd_video",        DISP_INTF_DSI, Truly_HX8399_FHD_video_xmldata,        DISP_TOPOLOGY_CONFIG_0,    PLL_OVERRIDE_NONE, DISP_MODE_SINGLE_DSI,                              DISP_MODE_SINGLE_DSI,                            DISP_MODE_SINGLE_DSI),
   省略一大部分屏信息 
  /*Skip mode only panels, SW render in UEFI*/
  /*Sharp 2k video*/
  PANEL_CREATE_ENTRY("nt35597_wqxga_dualdsi_video", MDPPLATFORM_PANEL_NT35597_WQHD_DUALDSI_VIDEO,   "qcom,mdss_dsi_nt35597_wqxga_video",     DISP_INTF_DSI, NULL,                                  DISP_TOPOLOGY_CONFIG_NONE, PLL_OVERRIDE_NONE, DISP_MODE_DUAL_DSI   | DISP_MODE_SKIP_BOOTLOADER,  DISP_MODE_DUAL_DSI   | DISP_MODE_SKIP_BOOTLOADER,     DISP_MODE_DUAL_DSI   | DISP_MODE_SKIP_BOOTLOADER),
};

2.2 显示屏初始化 MDPInit()
  1. 分配 DSI 和 I2C 相关的缓存内存资源
  2. 检查是否配置了bSWRender 软件渲染,
  3. 如果没有配置,则获取平台信息,是硬件初始化
  4. 检查再ABL中是否有对屏幕配置覆盖,或者是否支持 硬件屏幕,判断条见为 gPanelList 数组内容是否为空
  5. 初始化 MDP HAL,配置 MMU
  6. 如果不支持硬件屏幕,或者屏幕检测失败,则配置为软件渲染
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Library/MDPLib/MDPLib.c
/* This function will perform the basic initialization and detection of the MDP core */
MDP_Status  MDPInit(MDP_InitParamsType *pMDPInitParams, uint32 uFlags)
{
	// 1. 分配 DSI 和 I2C 相关的缓存内存资源
    gpDSIInitSequenceBuffer = (uint8*)MDP_OSAL_CALLOC(MDP_DSI_COMMAND_BUFFER_SIZE);
    gpDSITermSequenceBuffer = (uint8*)MDP_OSAL_CALLOC(MDP_DSI_COMMAND_BUFFER_SIZE);
    gpDSIDscPpsBuffer = (uint8*)MDP_OSAL_CALLOC(MDP_DSI_DSC_PPS_TOTAL_PACKET_SIZE);
    
    gpI2CInitSequenceBuffer = (uint8*)MDP_OSAL_CALLOC(MDP_I2C_COMMAND_BUFFER_SIZE);
    gpI2CTermSequenceBuffer = (uint8*)MDP_OSAL_CALLOC(MDP_I2C_COMMAND_BUFFER_SIZE);
	// 2. 检查是否配置了bSWRender 软件渲染,
    MDPPlatformConfigure(MDP_DISPLAY_PRIMARY, MDPPLATFORM_CONFIG_SW_RENDERER, &sPlatformParams);
 		// 3. 如果没有配置,则获取平台信息,是硬件初始化
        // Start hardware initialization, fall back to SW renderer in this path if key hardware functions fail
        if (FALSE == bSWRender)
        {
          //Get the platform Chip ID and catch in gsMDPHwPrivateInfo
          if (MDP_STATUS_OK == (eStatus = MDPPlatformConfigure(MDP_DISPLAY_PRIMARY, MDPPLATFORM_CONFIG_GETPLATFORMINFO, &sPlatformParams)))
          {
            psMDPHwPrivateInfo->sEFIChipSetId     = sPlatformParams.sPlatformInfo.sEFIChipSetId;
            psMDPHwPrivateInfo->sEFIChipSetFamily = sPlatformParams.sPlatformInfo.sEFIChipSetFamily;
            psMDPHwPrivateInfo->eEFIPlatformInfo  = sPlatformParams.sPlatformInfo.sEFIPlatformType.platform;
          }
          // 4. 检查再ABL中是否有对屏幕配置覆盖,或者是否支持 硬件屏幕,判断条见为 gPanelList 数组内容是否为空
          // Hardware path
          Display_ABL_CheckPanelSkip();
          
          //Set MDSS base address
          MDPPlatformSetMdssBase(sPlatformParams.sPlatformInfo.sEFIChipSetFamily);
          // 5. 初始化 MDP HAL,配置 MMU
          HAL_MDP_Init(NULL, DEAFAULT_MDP_INIT_FLAGS)
          // Initialize the HW private info 
          MDPInitHwPrivateInfo(psMDPHwPrivateInfo);
          HAL_MDP_TrafficCtrl_Init(NULL, 0);
          // Hardware detected
          // - Setup based on the hardware configuration              
          // Populate the input parameters
          pMDPInitParams->uMDPVersionMajor    = psMDPHwPrivateInfo->sMDPVersionInfo.uMajorVersion;
          pMDPInitParams->uMDPVersionMinor    = psMDPHwPrivateInfo->sMDPVersionInfo.uMinorVersion;
          pMDPInitParams->uMDPVersionRevision = psMDPHwPrivateInfo->sMDPVersionInfo.uReleaseVersion;
		  //For continuous splash feature, since frame buffer memory is shared between UEFI
          //and kernel, the MMU context need to be updated to enable sharing
          MDP_SetupMMUSIDs(pMDPInitParams->uMDPVersionMajor,pMDPInitParams->uMDPVersionMinor);

          // Find the supported displays 
          MDP_OSAL_MEMZERO(&pMDPInitParams->aSupportedDisplays, sizeof(pMDPInitParams->aSupportedDisplays));
          pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_PRIMARY]  = TRUE;
          pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_EXTERNAL] = FALSE;
                  
          // Check the external display configuration.
          if (ExtDisp_SupportedByPlatform(&sExtDispAttr))
          {
                gDisplayInfo[MDP_DISPLAY_EXTERNAL].ePhysConnect          = sExtDispAttr.ePhysConnect;                
                pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_EXTERNAL] = TRUE;
          }

          // If DISABLEDISPLAY variable is set, then disable primary display
          // Note: Make sure to check if external display is supported before proceeding
          if(TRUE == pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_EXTERNAL])
          {
                if(TRUE == MDPPlatformGetDisableDisplay())
                {
                  // DISABLEDISPLAY variable is set. Disable primary display
                  pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_PRIMARY]  = FALSE;
                }
           }
        }
		// 6. 如果不支持硬件屏幕,或者屏幕检测失败,则配置为软件渲染
        // Platform is configured for SW renderer, or hardware detection failed
        if (TRUE == bSWRender)
        {
           // Populate the input parameters
           pMDPInitParams->uMDPVersionMajor    = 0;
           pMDPInitParams->uMDPVersionMinor    = 0;
           pMDPInitParams->uMDPVersionRevision = 0;
           
           // Fix the supported displays to just primary
           MDP_OSAL_MEMZERO(&pMDPInitParams->aSupportedDisplays, sizeof(pMDPInitParams->aSupportedDisplays));
           pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_PRIMARY]  = TRUE;
           pMDPInitParams->aSupportedDisplays[MDP_DISPLAY_EXTERNAL] = FALSE;

           // Tell platform layer we are in SW render mode to by pass any hardware configuration
           MDP_OSAL_MEMZERO(&sPlatformParams, sizeof(MDPPlatformParams));
           sPlatformParams.sPlatformInfo.bSWRenderOverrride = TRUE;
           MDPPlatformConfigure(MDP_DISPLAY_PRIMARY, MDPPLATFORM_CONFIG_SW_RENDERER, &sPlatformParams);        
        }
    return eStatus;
}

2.3 上电 MDPPower()

MDPPower() 中的逻辑比较简单,根据pMDPPowerParams->bPowerOn 来决定是上电还是下电。

MDP_Status  MDPPower(MDP_Display_IDType eDisplayId, MDP_PowerParamsType *pMDPPowerParams, uint32 uFlags)
{
    Display_ABL_CheckPanelSkip();
    
    if (TRUE == pMDPPowerParams->bPowerOn){
        // Power up the respective displays
        MDPPlatformConfigure(eDisplayId, MDPPLATFORM_CONFIG_POWERUP, NULL);
    }
    else if (FALSE == pMDPPowerParams->bPowerOn)
    {
        // Power down the respective displays
        MDPPlatformConfigure(eDisplayId, MDPPLATFORM_CONFIG_POWERDOWN, NULL);
    }
    MDP_LOG_FUNC_EXIT("MDPPower()");    
    return eStatus;
}

2.4 解析XML,生成字符串发送给ABL中 MDPDetect(MDP_DISPLAY_PRIMARY, &sDetectParams, 0x0)
  1. 获取到屏幕的 xml 信息,解析 xml
  2. 配置屏相关的参数
  3. 更新屏幕信息到 ABL 中
    拼接字符串,示例如: 0:dsi:0:qcom,mdss_dsi_nt35597_wqxga_video:dsi:1:qcom,mdss_dsi_nt35597_wqxga_video:config0
    将字符串 通过 QcomTokenSpace GUID 保存起来,供 ABL 中使用
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Library/MDPLib/MDPLib.c
MDP_Status  MDPDetect(MDP_Display_IDType eDisplayId, MDP_DetectParamType *pMDPDetectParams, uint32 uFlags )
{
        // Handle each display
        switch (eDisplayId)
        {
        case MDP_DISPLAY_PRIMARY:
		{
        	MDPDetectPanel(eDisplayId, pDisplayInfo);
			=================>
				// 1. 获取到屏幕的 xml 信息,解析 xml
				MDPPlatformConfigure(eDisplayId, MDPPLATFORM_CONFIG_GETPANELCONFIG, &sPlatformParams);
				XML_Parser(sPlatformParams.sPlatformPanel.pPanelXMLConfig, sPlatformParams.sPlatformPanel.uConfigSize, sXmlTagsList, XML_TAGSLIST_LENGTH);
			<==================
            pMDPDetectParams->bDisplayDetected          = pDisplayInfo->bDetected;
            pMDPDetectParams->uSupportedModes           = 1; // Only 1 mode is supported
            pMDPDetectParams->aModeList[0].bInterlaced  = FALSE;
            pMDPDetectParams->aModeList[0].uModeIndex   = 0;
            pMDPDetectParams->aModeList[0].uWidth       = pDisplayInfo->uDisplayWidth;
            pMDPDetectParams->aModeList[0].uHeight      = pDisplayInfo->uDisplayHeight;
            eSelectedPanel                              = pDisplayInfo->eSelectedPanel;
                 
            switch (pDisplayInfo->ePhysConnect)
            {
            case MDP_DISPLAY_CONNECT_PRIMARY_DSI_VIDEO:
            case MDP_DISPLAY_CONNECT_PRIMARY_DSI_CMD:
            case MDP_DISPLAY_CONNECT_SECONDARY_DSI_VIDEO:
            case MDP_DISPLAY_CONNECT_SECONDARY_DSI_CMD:                
            	pMDPDetectParams->aModeList[0].uRefreshRate = pDisplayInfo->uAttrs.sDsi.uRefreshRate;
				/* If DSC is enable setup MDP structures for DSC */
				// 2. 配置屏相关的参数
                MDPSetupDSCProperty(pDisplayInfo);
                break;
            }
        case MDP_DISPLAY_EXTERNAL:
            {
              MDPDetectExtPlugin(pDisplayInfo);
              // DP detected 
              pDisplayInfo->bDetected            = TRUE;
              pDisplayInfo->uNumInterfaces       = MDP_INTERFACE_SINGLE;
              MDPDetectParams->bDisplayDetected = TRUE;
			  // Enumerate all modes
              for (i=0;i<MDP_DISPLAY_MAX_MODES;i++){
                  MDP_OSAL_MEMZERO(&sMode, sizeof(MDP_Panel_AttrType));
                  if (MDP_STATUS_OK == ExtDisp_GetModeInfo(i, &sMode)){
                    pMDPDetectParams->aModeList[i].uModeIndex   = i;
                    pMDPDetectParams->aModeList[i].uWidth       = sMode.uDisplayWidth;
                    pMDPDetectParams->aModeList[i].uHeight      = sMode.uDisplayHeight;
                    pMDPDetectParams->aModeList[i].uRefreshRate = sMode.uRefreshRate;                      
                    pMDPDetectParams->aModeList[i].bInterlaced  = FALSE; 
                    pMDPDetectParams->uSupportedModes++;
                  }
                  }
              }
            }
            break;
        }
    }
    // 3. 更新屏幕信息到 ABL 中
    //Update ABL with selected panel info
    Display_ABL_SetPanelConfiguration(eSelectedPanel);
    ===========>
    	// 3.1 拼接字符串,示例如: 0:dsi:0:qcom,mdss_dsi_nt35597_wqxga_video:dsi:1:qcom,mdss_dsi_nt35597_wqxga_video:config0
    	UpdatePanelConfiguration(eSelected, pConfigStr);
    	// 3.2 将字符串 通过 QcomTokenSpace GUID 保存起来,供 ABL 中使用
    	MDP_SetBootServiceVariable(DISPVAR_PANEL_CONFIGURATION, pConfigStr, AsciiStrLen(pConfigStr)+1, 0)
    <===========
    
    MDP_LOG_FUNC_EXIT("MDPDetect()");    

    return eStatus;
}

2.5 配置屏幕显示模式 DisplayDxeSelectMode()

这个函数没啥好看,就是配置显示模式,最大16种。
每种显示模式对应着不同的分辨率,刷新率等。

# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\DisplayDxe\DisplayDxe.c
/** DisplayDxeSelectMode
  Display DXE Local function to select the mode out of supported modes **/
static EFI_STATUS DisplayDxeSelectMode(MDP_Display_IDType eDisplayId, MDP_DetectParamType  *pDisplayModes)
{
      // Handle each display
      switch (eDisplayId)
      {
         //For Primary only 1 mode is supported, 
        case MDP_DISPLAY_PRIMARY:
            gModeInfo.uSelectedModeIndex[eDisplayId] = 0 ;
        break;
        // Select the largest mode out of the list of supported modes available on the external monitor
        case MDP_DISPLAY_EXTERNAL:
        {
           MDP_ModeInfo *pMaxMode = &pDisplayModes->aModeList[0];
             //Auto Select the largest mode
             // We loop through the list to find the largest mode 
             for (uI = 1; uI < pDisplayModes->uSupportedModes; uI++)
             {
               // Largest Mode
               if ((pMaxMode->uWidth < pDisplayModes->aModeList[uI].uWidth) &&
                   (pMaxMode->uHeight < pDisplayModes->aModeList[uI].uHeight))
               {
                 pMaxMode = &pDisplayModes->aModeList[uI];
                 uLargestModeIndex = uI;
               }
             }
             gModeInfo.uSelectedModeIndex[eDisplayId] = uLargestModeIndex;
           }
           else           
           {
             uint32 uWidth  = uExtModePCD & 0xFFFF;
             uint32 uHeight = (uExtModePCD>>16) & 0xFFFF;

             gModeInfo.uSelectedModeIndex[eDisplayId] = MDP_DISPLAY_MAX_MODES;
             
             // We loop through the list to find the match 
             for (uI = 0; uI < pDisplayModes->uSupportedModes; uI++)
             {
               if ((uWidth  ==  pDisplayModes->aModeList[uI].uWidth) &&
                   (uHeight ==  pDisplayModes->aModeList[uI].uHeight))
               {
                 gModeInfo.uSelectedModeIndex[eDisplayId] = uI;
                 break;
               }
             }
             if (MDP_DISPLAY_MAX_MODES == gModeInfo.uSelectedModeIndex[eDisplayId])
             {
               gModeInfo.uSelectedModeIndex[eDisplayId] = 0;
               DEBUG((EFI_D_INFO, "DisplayDxe: Failed to override the External panel mode!\n"));
             }

           }
        }
        break;
      }

      if (EFI_SUCCESS == Status)
      {
         MDP_PropertiesParamType               sDisplayProp;      
         UINT32                                uResLimitIndex     = (PcdGet32(PcdFrameBufMaxRes) < DISPLAY_FB_RES_MAX)?PcdGet32(PcdFrameBufMaxRes):0;
         EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *pMode              = &gModeInfo.sModeList[eDisplayId][0];
         UINT32                                uSelectedModeIndex = gModeInfo.uSelectedModeIndex[eDisplayId];
        
         // Set the Selected Mode index 
         //(MDPlib checks if it needs dual pipe and for external monitor also sets the active timing information for this mode 
         MDP_OSAL_MEMZERO(&sDisplayProp, sizeof(MDP_PropertiesParamType));
         sDisplayProp.sModeParams.uModeIndex = uSelectedModeIndex;
         MDPSetProperty(eDisplayId, MDP_DISPLAY_PROPERTY_MODE_INFO, &sDisplayProp);
      
         // Update global Mode Information
         pMode->Version                              = GRAPHICS_OUTPUT_PROTOCOL_REVISION;
         pMode->PixelFormat                          = DISPLAYDXE_DEFAULT_PIXEL_FORMAT;
         pMode->HorizontalResolution                 = pDisplayModes->aModeList[uSelectedModeIndex].uWidth;
         pMode->VerticalResolution                   = pDisplayModes->aModeList[uSelectedModeIndex].uHeight;
    
         // Limit the frame buffer allocation as specified by PcdFrameBufMaxRes, frame buffer will be letterboxed in to the larger resolution panel
         if ((pDisplayModes->aModeList[uSelectedModeIndex].uWidth * pDisplayModes->aModeList[uSelectedModeIndex].uHeight) >
             (gFBResLimit[uResLimitIndex].uWidth * gFBResLimit[uResLimitIndex].uHeight))
         {
            // Portrait mode panel 
            if (pDisplayModes->aModeList[uSelectedModeIndex].uHeight > pDisplayModes->aModeList[uSelectedModeIndex].uWidth)
            {
              // Clamp the width to Max height and height to Max Width to maintain the portrait aspect ratio 
              pMode->HorizontalResolution  = gFBResLimit[uResLimitIndex].uHeight;  
              pMode->VerticalResolution    = gFBResLimit[uResLimitIndex].uWidth;
            }
            // Landscape mode panel
            else
            {
              pMode->HorizontalResolution = gFBResLimit[uResLimitIndex].uWidth;
              pMode->VerticalResolution   = gFBResLimit[uResLimitIndex].uHeight;
            }
            DEBUG ((EFI_D_INFO, "DisplayDxe: Frame buffer allocation limited to %dix%d\n", pMode->HorizontalResolution, pMode->VerticalResolution));            
         }
          
         pMode->PixelInformation.RedMask             = DISPLAYDXE_RED_MASK;
         pMode->PixelInformation.GreenMask           = DISPLAYDXE_GREEN_MASK;
         pMode->PixelInformation.BlueMask            = DISPLAYDXE_BLUE_MASK;
         pMode->PixelInformation.ReservedMask        = DISPLAYDXE_ALPHA_MASK;
         pMode->PixelsPerScanLine                    = pMode->HorizontalResolution;
         gModeInfo.uNumModes[eDisplayId]++;
      }
   }

   return Status;
}

2.6 使能主屏显示prop属性MDPSetProperty()
MDPSetProperty(MDP_DISPLAY_PRIMARY, MDP_DISPLAY_PROPERTY_POWER_STATE, &sDisplayProp)

传入的sDisplayProp ,其结构体为:

/* 
 * MDPProperty Parameters
 */
typedef union
{
  uint32                uBacklightLevel;           /* MDP_DISPLAY_PROPERTY_BACKLIGHT */
  bool32                bDisplayPwrState;          /* MDP_DISPLAY_PROPERTY_POWER_STATE */
  bool32                bDisplayDetected;          /* MDP_DISPLAY_PROPERTY_DETECTION_INFO */ 
  MDP_SetModeParamType  sModeParams;               /* MDP_DISPLAY_PROPERTY_MODE_INFO */
} MDP_PropertiesParamType;

包含了 背光等级,电源开关,模式索引等信息。

我们来进函数看下,本次传入的是 MDP_DISPLAY_PROPERTY_POWER_STATE,
可以看出,就是执行了 pDisplayInfo->bDisplayPwrState = pMDPPropertiesParams->bDisplayPwrState; 这句话。

MDP_Status  MDPSetProperty(MDP_Display_IDType eDisplayId, MDP_Display_Property eProperty, MDP_PropertiesParamType *pMDPPropertiesParams)
{
    MDP_Panel_AttrType  *pDisplayInfo = MDP_GET_DISPLAYINFO(eDisplayId);

    switch (eProperty)
    {
    case MDP_DISPLAY_PROPERTY_BACKLIGHT:
      {
        MDPPlatformParams  sPlatformParams;
        // Setup any other display parameters
        
        MDP_OSAL_MEMZERO(&sPlatformParams, sizeof(MDPPlatformParams));

        sPlatformParams.sBacklightConfig.bEnable                        = TRUE;
        sPlatformParams.sBacklightConfig.eBacklightType                 = pDisplayInfo->eBacklightType;
        sPlatformParams.sBacklightConfig.uBacklightCntrl.eBacklightCtrl = pDisplayInfo->uBacklightCntrl.eBacklightCtrl;
        sPlatformParams.sBacklightConfig.uLevel                         = pMDPPropertiesParams->uBacklightLevel;

        if (MDP_STATUS_OK == (eStatus = MDPPlatformConfigure(eDisplayId, MDPPLATFORM_CONFIG_SETBACKLIGHT, &sPlatformParams)))
        {
          // Cache current backlight level
          pDisplayInfo->uBacklightLevel = pMDPPropertiesParams->uBacklightLevel;
        }
      }
      break;

    case MDP_DISPLAY_PROPERTY_FIRMWAREENV:
      {
        eStatus = MDP_SaveFirmwareEnvironmentVariable(eDisplayId);
      }
      break;  

    case MDP_DISPLAY_PROPERTY_MODE_INFO:
      {
         MDP_HwPrivateInfo *psMDPHwPrivateInfo = MDP_GETPRIVATEINFO();
         uint32             ModeIndex          =  pMDPPropertiesParams->sModeParams.uModeIndex;

         // External monitor supports more than one mode , store the mode info for the selected mode
         // For primary only one mode is supported and this information is populated at the detect time
         switch (eDisplayId)
         {
            case MDP_DISPLAY_EXTERNAL:
               eStatus = ExtDisp_GetModeInfo(ModeIndex, pDisplayInfo);
            break;
            default:
            break;   
         }

         // Check if we need Dual pipe for this panel
         if ((NULL != psMDPHwPrivateInfo->pDeviceCaps) &&
             (pDisplayInfo->uDisplayWidth > psMDPHwPrivateInfo->pDeviceCaps->pResolutionCaps->uMaxLayerWidthPx))
         {
             pDisplayInfo->uNumMixers = MDP_DUALPIPE_NUM_MIXERS;
         }
      }
      break;

    case MDP_DISPLAY_PROPERTY_POWER_STATE:
      {
        /* Cache the current display power state information */
        pDisplayInfo->bDisplayPwrState  =  pMDPPropertiesParams->bDisplayPwrState;
        break;
      }
    case MDP_DISPLAY_PROPERTY_ABL_INTERFACE_INIT:
      {
        /* Initialize ABL context which will be used to create panel configuration string for ABL later */
        Display_ABL_Initialize();
        
        break;
      }
  }
  return eStatus;
}

2.7 注册显示相关的服务 InstallMultipleProtocolInterfaces()

在代码中,同时注册了好多个GUUID,接下来,我们一个个来看看:

gBS->InstallMultipleProtocolInterfaces (&hUEFIDisplayHandle, 
           		&gEfiDevicePathProtocolGuid, 			&DisplayDevicePath,
                &sOutputGUID,  							&gDisplayDxeOutputProtocol,
                &gQcomDisplayPwrCtrlProtocolGuid,  		&gDisplayPwrCtrlProtocolImplementation,
                &gEfiDisplayPowerStateProtocolGuid,  	&gDisplayPwrProtocolImplementation,
                &gQcomDisplayABLProtocolGuid, 			&gQcomDisplayABLProtocolImplementation,
                NULL);
  1. gEfiDevicePathProtocolGuid 对应 DisplayDevicePath
# amss/BOOT.XF.1.4/boot_images/MdePkg/Include/Protocol/DevicePath.h
// Device Path protocol
#define EFI_DEVICE_PATH_PROTOCOL_GUID \
  { \
    0x9576e91, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} \
  }

/* DisplayDXE Device path */
static EFI_DISPLAY_DEVICE_PATH DisplayDevicePath = 
{
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID
  },
  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {sizeof (EFI_DEVICE_PATH_PROTOCOL), 0}}
};

  1. sOutputGUID 对应 gDisplayDxeOutputProtocol
# amss/BOOT.XF.1.4/boot_images/MdePkg/Include/Protocol/GraphicsOutput.h
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }

# amss/BOOT.XF.1.4/boot_images/QcomPkg/Drivers/DisplayDxe/DisplayDxe.c
/* Function table pointer to the supported protocol functions */
static EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayDxeOutputProtocol = {
    &DisplayDxe_QueryMode,
    &DisplayDxe_SetMode,
    &DisplayDxe_Blt,
    &gModeInfo.sProtocolInfo
};

  1. gQcomDisplayPwrCtrlProtocolGuid 对应 gDisplayPwrCtrlProtocolImplementation,用于显示的电源控制
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Drivers/DisplayDxe/DisplayPwrCtrlProtocol.c
/**
  Display power UEFI Protocol implementation
 */
EFI_QCOM_DISPLAY_PWR_CTRL_PROTOCOL gDisplayPwrCtrlProtocolImplementation =
{
  DISPLAY_PWR_CTRL_REVISION,
  EFI_DisplayPanelPowerControl,
  EFI_DisplayPanelPowerStatus,
  EFI_DisplayBackLightBrightnessLevelControl,
  EFI_DisplayBackLightBrightnessLevelStatus,
}; 

  1. gEfiDisplayPowerStateProtocolGuid 对应 gDisplayPwrProtocolImplementation,用于获取电源状态
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Drivers/DisplayDxe/DisplayPwrProtocol.c
/**
  Display power UEFI Protocol implementation
 */
EFI_DISPLAY_POWER_PROTOCOL gDisplayPwrProtocolImplementation =
{
  EFI_DISPLAY_POWER_PROTOCOL_REVISION,
  EFI_DisplayPowerSetDisplayPowerState,
  EFI_DisplayPowerGetDisplayPowerState 
}; 

  1. gQcomDisplayABLProtocolGuid 对应 gQcomDisplayABLProtocolImplementation
# amss/BOOT.XF.1.4/boot_images/QcomPkg/Library/MDPLib/DisplayABLInterface.c
EFI_QCOM_DISPLAY_ABL_PROTOCOL  gQcomDisplayABLProtocolImplementation = 
{
  DISPLAY_ABL_REVISION,
  Display_ABL_SetProperty,
  Display_ABL_GetProperty,
  Display_ABL_RenderLogo,   //NOTE: Adding this to have same EFI_QCOM_DISPLAY_UTILS_PROTOCOL protocol APIs as on ABL side. 
  Display_ABL_SetMode,      //NOTE: Adding this to have same EFI_QCOM_DISPLAY_UTILS_PROTOCOL protocol APIs as on ABL side. 
  Display_ABL_SetVariable,
  Display_ABL_GetVariable
};

好,显示驱动实发始化差不多主就这些,对于调试驱动来说,需要配置如下


3. 显示图片函数分析 DisplayDxe_Blt

先来看下传入的参数:

  • This: 调用的模式驱动指针
  • BltBuffer:图片buffer 地址
  • BltOperation : 显示模式,如:EfiBltVideoFill填充,EfiBltVideoToBltBuffer,EfiBltBufferToVideo,EfiBltVideoToVideo
    本次传入的是 EfiBltBufferToVideo,意思是将图片做为video方式显示。
  • SourceX,SourceY: 源 buffer 的 x,y 坐标
  • DestinationX,DestinationY : 目标 buffer的x,y坐标
  • Width, Height 宽高
  • Delta : 一行的大小,值为 width x rgb(3byte)

DisplayDxe_Blt 函数的主要作用就是拷贝图片数据到 framebuffer中,数据为RGB格式,这样就能直接显示出来了。

  1. pSrcBuffer 指向图片buffer,pDstBuffer指向 framebuffer的地址。
  2. 数据从pSrcBuffer 拷贝到pDstBuffer中,宽、高、字节/每像素 赋值
  3. 拷贝图片数据
# amss\BOOT.XF.1.4\boot_images\QcomPkg\Drivers\DisplayDxe\DisplayDxe.c
/**
  Display DXE Protocol Blt function
  
  @param  This         Protocol instance pointer.
  @param  BltBuffer    Buffer containing data to blit into video buffer. This
                       buffer has a size of Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
  @param  BltOperation Operation to perform on BlitBuffer and video memory
  @param  SourceX      X coordinate of source for the BltBuffer.
  @param  SourceY      Y coordinate of source for the BltBuffer.
  @param  DestinationX X coordinate of destination for the BltBuffer.
  @param  DestinationY Y coordinate of destination for the BltBuffer.
  @param  Width        Width of rectangle in BltBuffer in pixels.
  @param  Height       Hight of rectangle in BltBuffer in pixels.
  @param  Delta        OPTIONAL

  @retval EFI_SUCCESS           The Blt operation completed.
  @retval EFI_INVALID_PARAMETER BltOperation is not valid or source/destination parameters are invalid
  @retval EFI_DEVICE_ERROR      A hardware error occured writting to the video buffer.


**/
EFI_STATUS
DisplayDxe_Blt (
  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL            *This,
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL           *BltBuffer,
  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION       BltOperation,
  IN  UINTN                                   SourceX,
  IN  UINTN                                   SourceY,
  IN  UINTN                                   DestinationX,
  IN  UINTN                                   DestinationY,
  IN  UINTN                                   Width,
  IN  UINTN                                   Height,
  IN  UINTN                                   Delta)
{
      case EfiBltBufferToVideo:
        {
          // 1. pSrcBuffer 指向图片buffer,pDstBuffer指向 framebuffer的地址。
          UINT8 *pSrcBuffer = (UINT8*)BltBuffer;
          UINT8 *pDstBuffer = (UINT8*)DISPLAYDXE_PHYSICALADDRESS32(gModeInfo.sProtocolInfo.FrameBufferBase);
          UINTN SrcStride, DstStride, CopyWidth, CopyHeight;

		  // 2. 数据从pSrcBuffer 拷贝到pDstBuffer中,宽、高、字节/每像素 赋值
          CopyWidth  = Width;
          CopyHeight = Height;
          /* Video buffer stride in bytes, consider padding as well */
          DstStride = gModeInfo.sCurrentModeInfo.PixelsPerScanLine * DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL;
          /* Src buffer stride in bytes. Delta is valid when X or Y is not 0 */
          SrcStride = Width * DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL;
        
	      // 3. 拷贝图片数据
          DisplayDxeBltInternal (pSrcBuffer,
                                 pDstBuffer,
                                 SourceX, SourceY, 
                                 CopyWidth,  CopyHeight,
                                 SrcStride,
                                 DestinationX, DestinationY,
                                 DstStride,
                                 DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL);
          
          /* If display frame buffer is cached, need to call cache maintenance function */
          FlushStaleLines(pDstBuffer + (DstStride * DestinationY), (DstStride * CopyHeight));
          eStatus = EFI_SUCCESS;
        }
        break;

      case EfiBltVideoToBltBuffer:
        {  
          UINT8 *pDstBuffer = (UINT8*)BltBuffer;
          UINT8 *pSrcBuffer = (UINT8*)DISPLAYDXE_PHYSICALADDRESS32(gModeInfo.sProtocolInfo.FrameBufferBase);
          UINTN SrcStride, DstStride, CopyWidth, CopyHeight;
          CopyWidth  = Width;
          CopyHeight = Height;
          /* Video buffer stride in bytes, consider padding as well */
          SrcStride = gModeInfo.sCurrentModeInfo.PixelsPerScanLine * DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL;

          /* Buffer stride in bytes. Delta is valid when X or Y is not 0 */
          DstStride = Width * DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL;
          DisplayDxeBltInternal (pSrcBuffer,
                                 pDstBuffer,
                                 SourceX, 
                                 SourceY, 
                                 CopyWidth, 
                                 CopyHeight,
                                 SrcStride,
                                 DestinationX,
                                 DestinationY,
                                 DstStride,
                                 DISPLAYDXE_DEFAULT_BYTES_PER_PIXEL);

          eStatus = EFI_SUCCESS;
        }
        break; 
      }    
    }
    return eStatus;    
}

从 DisplayDxeBltInternal() 中可以看出,它主要作用就是拷贝数据,很好理解

static void DisplayDxeBltInternal (
  UINT8   *pSrc,
  UINT8   *pDst,
  UINTN    uSrcX,
  UINTN    uSrcY,
  UINTN    uSrcWidth,
  UINTN    uSrcHeight,
  UINTN    uSrcStride,
  UINTN    uDstX,
  UINTN    uDstY,
  UINTN    uDstStride,
  UINTN    uBytesPerPixel
  )
{
  UINT32 uI = 0;
  UINT32 uSrcWidthBytes = uSrcWidth * uBytesPerPixel;  
  
  /* move src pointer to start of src rectangle */
  pSrc += (uSrcY * uSrcStride) + (uSrcX * uBytesPerPixel);

  /* move dest pointer to start of dest rectangle */
  pDst += (uDstY * uDstStride) + (uDstX * uBytesPerPixel); 

  /* Blit Operation 
   *
   *  We use MDP_OSAL_MEMCPY which invokes Neon memcpy (kr_memcpy.asm) 
   *  This memcpy supports overlapped src and dst buffers but copying may not be optimal in the overlap case 
   */  
  for (uI = 0; uI < uSrcHeight; ++uI)
  {
    MDP_OSAL_MEMCPY((void*)pDst, (void*)pSrc, uSrcWidthBytes);

    pDst += uDstStride;
    pSrc += uSrcStride;
  }
}
  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"小夜猫&小懒虫&小财迷"的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值