【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程
- 1. GraphicsOutput.h
- 2. 显示驱动初化 DisplayDxe.c
- 3. 显示图片函数分析 DisplayDxe_Blt
系列文章:
- 《【Android SDM660开机流程】- UEFI XBL 代码流程分析》
- 《【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序》
- 《【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析》
- 《【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程》
- 《【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
其主要工作流程为:
- 注册回调函数,当退出UEFI是,会调用
DisplayDxeExitBootServicesEvent
函数,释放相关的资源 - 创建回调函数,当需要显示是,发送这个时间,就会调用
UIActiveEventCallBack
,给屏幕上电。 - 创建回调函数,收到这个事件是,会调用回调函数
UIIdleEventCallBack
,给屏幕下电,关闭显示 - 初始化ABL上下文,获得所有支持的屏
- 显示屏初始化
- 如果配置了
MDP_DISPLAY_PRIMARY
,则上电,检测屏幕是否存在,配置屏幕显示模式,使能主屏显示prop属性 - 如果配置了
MDP_DISPLAY_EXTERNAL
,同样开始上电检测、配置屏幕显示模式 - 初始化显示 pix format 相关信息
- 注册显示屏相关的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()
- 获得全局变量 Display_ABLContextType gsABLContext
- 清空结构体 gsABLContext
- 获取所有支持的 display panels 列表
- 检查 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()
- 获取gPanelList数组,保存在psPanelDTInfo中,
从头文件#include "MDPPlatformLib.h"
可以看出,调用的是/MDPPlatformLibBoot/MDPPlatformLib.c
中的代码 - 解析屏幕列表
- 使用
QcomTokenSpace GUID
配置全局变量:DisplaySupportedPanelCount
- 使用
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()
- 分配 DSI 和 I2C 相关的缓存内存资源
- 检查是否配置了bSWRender 软件渲染,
- 如果没有配置,则获取平台信息,是硬件初始化
- 检查再ABL中是否有对屏幕配置覆盖,或者是否支持 硬件屏幕,判断条见为 gPanelList 数组内容是否为空
- 初始化 MDP HAL,配置 MMU
- 如果不支持硬件屏幕,或者屏幕检测失败,则配置为软件渲染
# 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)
- 获取到屏幕的 xml 信息,解析 xml
- 配置屏相关的参数
- 更新屏幕信息到 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);
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}}
};
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
};
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,
};
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
};
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格式,这样就能直接显示出来了。
- pSrcBuffer 指向图片buffer,pDstBuffer指向 framebuffer的地址。
- 数据从pSrcBuffer 拷贝到pDstBuffer中,宽、高、字节/每像素 赋值
- 拷贝图片数据
# 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;
}
}