我们基于Openharmony开发,需要针对repo做一个周编译脚本,这样能够在每周固化系统版本。如下是脚本的使用方法
可以通过git获取此仓库如下:
git clone https://gitlab2.kylin.com/sh-product-Embedded/openharmony/weekly_build_script.git
阶段性版本固化
git clone git@gitlab2.kylin.com:sh-product-Embedded/openharmony/weekly_build_script.git -b B01-kylin-ZD-3568
当获取到此仓库之后,仅需要在仓库目录下运行run.sh即可,如下
cd weekly_build_script && ./run.sh
等待大概2小时后,可以获取到update.img,在如下目录:
out/kylin/update-xxxxxxxx.img
此脚本主要完成如下四个动作
其中同步代码和拉取prebuilts可能因为网络而失败,所以是通过while1来循环
而编译openharmony可能会失败,所以重编译次数为5次,如下:
if ((c>5)) ;then break ; fi
Openharmony在RK3588上不是很成熟,SDK编译经常失败,这里记录编译出的问题
此问题是openharmony内的BUILD.gn需要添加exec_script时,需要在core/gn/ohos_exec_script_allowlist.gni声明,针对此问题, 可以如下
diff --git a/core/gn/ohos_exec_script_allowlist.gni b/core/gn/ohos_exec_script_allowlist.gni index 0a986f49..813976a9 100644 --- a/core/gn/ohos_exec_script_allowlist.gni +++ b/core/gn/ohos_exec_script_allowlist.gni @@ -60,6 +60,7 @@ ohos_exec_script_config = { + "//device/board/hihope/dayu210/camera/BUILD.gn",
错误日志如下:
ninja: error: '../../drivers/peripheral/camera/vdi_base/v4l2/src/camera_dump.cpp', needed by 'obj/drivers/peripheral/camera/vdi_base/v4l2/src/camera_host_vdi_impl_1.0/camera_dump.o', missing and no known rule to make it
原因是dayu210的提交和openharmony sdk不匹配,故根据当前版本设置如下:
diff --git a/dayu210/camera/vdi_impl/v4l2/BUILD.gn b/dayu210/camera/vdi_impl/v4l2/BUILD.gn index 55b0018..c892d56 100755 --- a/dayu210/camera/vdi_impl/v4l2/BUILD.gn +++ b/dayu210/camera/vdi_impl/v4l2/BUILD.gn @@ -139,7 +139,7 @@ if (product_name == "rk3568_mini_system") { host_sources = [ "$camera_path/../v4l2/src/camera_device/camera_device_vdi_impl.cpp", - "$camera_path/../v4l2/src/camera_dump.cpp", + "$camera_path/dump/src/camera_dump.cpp" "$camera_path/../v4l2/src/camera_host/camera_host_config.cpp", @@ -158,6 +158,7 @@ if (product_name == "rk3568_mini_system") { ] host_includes = [ + "$camera_path/dump/include", "$camera_path/../../interfaces/include",
错误日志如下:
Exception: //device/soc/rockchip/rk3588/hardware/display:display_composer_vendor depend part //third_party/libdrm:libdrm, need set part deps libdrm info to device_rk3588.
这里根据文章《Openharmony 4.0 SDK移植RK3588之白名单》可以知道是"third_deps_bundle_not_add"设置问题,这里display_composer_vendor需要添加设置一下,如下
diff --git a/compile_standard_whitelist.json b/compile_standard_whitelist.json index ce4ca863..57fe46b5 100644 --- a/compile_standard_whitelist.json +++ b/compile_standard_whitelist.json "third_deps_bundle_not_add": [ "//arkcompiler/toolchain/tooling:libark_ecma_debugger_test", @@ -455,9 +529,13 @@ + "//device/soc/rockchip/rk3588/hardware/display:display_composer_vendor", + "//device/soc/rockchip/rk3588/hardware/display:libdisplay_buffer_vendor", + "//device/soc/rockchip/rk3588/hardware/display:libhigbm_vendor",
此问题是虚函数声明了函数实现,但是继承的类没有实现,所以原因是openharmony4.1的改动在rk3588的hdi上没有更新,所以我们需要更新rk3588的hdi,新增这些虚函数定义,如下:
diff --git a/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.cpp b/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.cpp index 0affeb9..70c7456 100755 --- a/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.cpp +++ b/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.cpp @@ -116,6 +116,36 @@ int32_t DisplayBufferVdiImpl::IsSupportedAlloc(const std::vector<VerifyAllocInfo return HDF_ERR_NOT_SUPPORT; } +int32_t DisplayBufferVdiImpl::RegisterBuffer(const BufferHandle& handle) +{ + DISPLAY_LOGE("%s is not supported", __func__); + return DISPLAY_NOT_SUPPORT; +} + +int32_t DisplayBufferVdiImpl::SetMetadata(const BufferHandle& handle, uint32_t key, const std::vector<uint8_t>& value) +{ + DISPLAY_LOGE("%s is not supported", __func__); + return DISPLAY_NOT_SUPPORT; +} + +int32_t DisplayBufferVdiImpl::GetMetadata(const BufferHandle& handle, uint32_t key, std::vector<uint8_t>& value) +{ + DISPLAY_LOGE("%s is not supported", __func__); + return DISPLAY_NOT_SUPPORT; +} + +int32_t DisplayBufferVdiImpl::ListMetadataKeys(const BufferHandle& handle, std::vector<uint32_t>& keys) +{ + DISPLAY_LOGE("%s is not supported", __func__); + return DISPLAY_NOT_SUPPORT; +} + +int32_t DisplayBufferVdiImpl::EraseMetadataKey(const BufferHandle& handle, uint32_t key) +{ + DISPLAY_LOGE("%s is not supported", __func__); + return DISPLAY_NOT_SUPPORT; +} + diff --git a/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.h b/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.h index 9cb70a4..8e22852 100755 --- a/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.h +++ b/rk3588/hardware/display/src/display_gralloc/display_buffer_vdi_impl.h @@ -38,6 +38,11 @@ public: virtual int32_t InvalidateCache(const BufferHandle& handle) const override; virtual int32_t IsSupportedAlloc(const std::vector<VerifyAllocInfo>& infos, std::vector<bool>& supporteds) const override; + virtual int32_t RegisterBuffer(const BufferHandle& handle) override; + virtual int32_t SetMetadata(const BufferHandle& handle, uint32_t key, const std::vector<uint8_t>& value) override; + virtual int32_t GetMetadata(const BufferHandle& handle, uint32_t key, std::vector<uint8_t>& value) override; + virtual int32_t ListMetadataKeys(const BufferHandle& handle, std::vector<uint32_t>& keys) override; + virtual int32_t EraseMetadataKey(const BufferHandle& handle, uint32_t key) override; }; } // namespace DISPLAY } // namespace HDI
对于openharmony的rk3588代码分支,安装烧录之后发现默认不亮,这里原因是dayu210的提交人员做了一个定制内容修改,我们的代码需要屏蔽掉,从而使得openharmony的hdi能够正确的找到drm的plane。具体如下:
代码位置:device/soc/rockchip/rk3588/hardware/display/src/display_device/
这里面存在两个动态库,如下
libdisplay_composer_vdi_impl.z.so作为composer_service服务的接口层,作为特定芯片的接口向下的调用
ohos_shared_library("libdisplay_composer_vdi_impl") { sources = [ "src/display_device/display_composer_vdi_impl.cpp" ] include_dirs = [ "src/display_device", "${root_path}/drivers/peripheral/display/utils/include", "${root_path}/drivers/interface/display/composer", "${root_path}/drivers/peripheral/display/composer/hdi_service/include", "${root_path}/drivers/interface/display/composer/hdifd_parcelable", ] deps = [ ":display_composer_vendor" ] external_deps = [ "c_utils:utils", "drivers_interface_display:display_composer_idl_headers", "graphic_chipsetsdk:buffer_handle", "hilog:libhilog", "ipc:ipc_single", ] install_enable = true install_images = [ chipset_base_dir ] subsystem_name = "hdf" part_name = "rockchip_products" }
libdisplay_composer_vendor.z.so作为芯片层的显示框架的适配,实际调用libdrm的接口,如下
ohos_shared_library("display_composer_vendor") { sources = [ "src/display_device/drm_connector.cpp", "src/display_device/drm_crtc.cpp", "src/display_device/drm_device.cpp", "src/display_device/drm_display.cpp", "src/display_device/drm_encoder.cpp", "src/display_device/drm_plane.cpp", "src/display_device/drm_vsync_worker.cpp", "src/display_device/hdi_composer.cpp", "src/display_device/hdi_device_interface.cpp", "src/display_device/hdi_display.cpp", "src/display_device/hdi_drm_composition.cpp", "src/display_device/hdi_drm_layer.cpp", "src/display_device/hdi_gfx_composition.cpp", "src/display_device/hdi_layer.cpp", "src/display_device/hdi_netlink_monitor.cpp", "src/display_device/hdi_session.cpp", ] output_name = "display_composer_vendor" include_dirs = [ "src/display_gralloc", "src/display_device", "${root_path}/drivers/peripheral/display/buffer/hdi_service/include", "${root_path}/drivers/peripheral/display/utils/include", "${root_path}/drivers/peripheral/base", "${root_path}/drivers/interface/display/composer", "${root_path}/foundation/communication/ipc/interfaces/innerkits/ipc_core/include", "${root_path}/device/soc/rockchip/rk3588/hardware/rga/include", "${root_path}/drivers/interface/display/composer/hdifd_parcelable", ] deps = [ ":libdisplay_buffer_vdi_impl", "${root_path}/device/soc/rockchip/rk3588/hardware/rga:librga", "${root_path}/third_party/libdrm:libdrm", ] cflags_cc = [ "-Wno-error=unused-function", "-Wno-error=missing-braces", "-Wno-error=#warnings", ] external_deps = [ "c_utils:utils", "drivers_interface_display:display_buffer_idl_headers", "drivers_interface_display:display_composer_idl_headers", "hdf_core:libhdf_utils", "hilog:libhilog", "hitrace:hitrace_meter", ] install_enable = true install_images = [ chipset_base_dir ] subsystem_name = "hdf" part_name = "rockchip_products" }
对于rk3588的代码,我们编译出来发现hdmi不亮,所以需要跟踪这里的代码,这里值得留意的是
src/display_device/drm_device.cpp。
drm_device.cpp作为drm设备的创建动作,是基于图形的最先操作的,例如打开drm设备,查询crtc,plane,encoder,connector,和property。主要如下:
此函数打开card0显卡设备,从而获取drmFd的fd值,后续可作为drmMode函数的参数
std::shared_ptr<HdiDeviceInterface> DrmDevice::Create() { DISPLAY_LOGD(); if (mDrmFd == nullptr) { const std::string name("rockchip"); int drmFd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); // drmOpen(name.c_str(), nullptr); if (drmFd < 0) { DISPLAY_LOGE("drm file:%{public}s open failed %{public}s", name.c_str(), strerror(errno)); return nullptr; } DISPLAY_LOGD("the drm fd is %{public}d", drmFd); mDrmFd = std::make_shared<HdiFd>(drmFd); } if (mInstance == nullptr) { mInstance = std::make_shared<DrmDevice>(); } return mInstance; }
此函数作为drm格式的转换函数,因为openharmony上层的graphic_2d会有默认显示格式,而libdrm底层有可支持的显示格式,对于libdrm而言,可以通过drmModeAddFB2函数传入drm支持的图片格式的format值,对于openharmony而言,是当前系统支持的格式,我们可以如下知道openharmony支持如下图像格式:
typedef enum { PIXEL_FMT_CLUT8 = 0, /**< CLUT8 format */ PIXEL_FMT_CLUT1, /**< CLUT1 format */ PIXEL_FMT_CLUT4, /**< CLUT4 format */ PIXEL_FMT_RGB_565, /**< RGB565 format */ PIXEL_FMT_RGBA_5658, /**< RGBA5658 format */ PIXEL_FMT_RGBX_4444, /**< RGBX4444 format */ PIXEL_FMT_RGBA_4444, /**< RGBA4444 format */ PIXEL_FMT_RGB_444, /**< RGB444 format */ PIXEL_FMT_RGBX_5551, /**< RGBX5551 format */ PIXEL_FMT_RGBA_5551, /**< RGBA5551 format */ PIXEL_FMT_RGB_555, /**< RGB555 format */ PIXEL_FMT_RGBX_8888, /**< RGBX8888 format */ PIXEL_FMT_RGBA_8888, /**< RGBA8888 format */ PIXEL_FMT_RGB_888, /**< RGB888 format */ PIXEL_FMT_BGR_565, /**< BGR565 format */ PIXEL_FMT_BGRX_4444, /**< BGRX4444 format */ PIXEL_FMT_BGRA_4444, /**< BGRA4444 format */ PIXEL_FMT_BGRX_5551, /**< BGRX5551 format */ PIXEL_FMT_BGRA_5551, /**< BGRA5551 format */ PIXEL_FMT_BGRX_8888, /**< BGRX8888 format */ PIXEL_FMT_BGRA_8888, /**< BGRA8888 format */ PIXEL_FMT_YUV_422_I, /**< YUV422 interleaved format */ PIXEL_FMT_YCBCR_422_SP, /**< YCBCR422 semi-planar format */ PIXEL_FMT_YCRCB_422_SP, /**< YCRCB422 semi-planar format */ PIXEL_FMT_YCBCR_420_SP, /**< YCBCR420 semi-planar format */ PIXEL_FMT_YCRCB_420_SP, /**< YCRCB420 semi-planar format */ PIXEL_FMT_YCBCR_422_P, /**< YCBCR422 planar format */ PIXEL_FMT_YCRCB_422_P, /**< YCRCB422 planar format */ PIXEL_FMT_YCBCR_420_P, /**< YCBCR420 planar format */ PIXEL_FMT_YCRCB_420_P, /**< YCRCB420 planar format */ PIXEL_FMT_YUYV_422_PKG, /**< YUYV422 packed format */ PIXEL_FMT_UYVY_422_PKG, /**< UYVY422 packed format */ PIXEL_FMT_YVYU_422_PKG, /**< YVYU422 packed format */ PIXEL_FMT_VYUY_422_PKG, /**< VYUY422 packed format */ PIXEL_FMT_VENDER_MASK = 0X7FFF0000, /**< vendor mask format */ PIXEL_FMT_BUTT = 0X7FFFFFFF /**< Invalid pixel format */ } PixelFormat;
而对于libdrm的格式可以在此文件查到:third_party/libdrm/include/drm/drm_fourcc.h
故代码实现如下:
uint32_t DrmDevice::ConvertToDrmFormat(PixelFormat fmtIn) { static const PixelFormatConvertTbl convertTable[] = { {DRM_FORMAT_XBGR8888, PIXEL_FMT_RGBX_8888}, {DRM_FORMAT_ABGR8888, PIXEL_FMT_RGBA_8888}, {DRM_FORMAT_RGB888, PIXEL_FMT_RGB_888}, {DRM_FORMAT_RGB565, PIXEL_FMT_BGR_565}, {DRM_FORMAT_BGRX4444, PIXEL_FMT_BGRX_4444}, {DRM_FORMAT_BGRA4444, PIXEL_FMT_BGRA_4444}, {DRM_FORMAT_RGBA4444, PIXEL_FMT_RGBA_4444}, {DRM_FORMAT_RGBX4444, PIXEL_FMT_RGBX_4444}, {DRM_FORMAT_BGRX5551, PIXEL_FMT_BGRX_5551}, {DRM_FORMAT_BGRA5551, PIXEL_FMT_BGRA_5551}, {DRM_FORMAT_BGRX8888, PIXEL_FMT_BGRX_8888}, {DRM_FORMAT_ARGB8888, PIXEL_FMT_BGRA_8888}, {DRM_FORMAT_NV12, PIXEL_FMT_YCBCR_420_SP}, {DRM_FORMAT_NV21, PIXEL_FMT_YCRCB_420_SP}, {DRM_FORMAT_YUV420, PIXEL_FMT_YCBCR_420_P}, {DRM_FORMAT_YVU420, PIXEL_FMT_YCRCB_420_P}, {DRM_FORMAT_NV16, PIXEL_FMT_YCBCR_422_SP}, {DRM_FORMAT_NV61, PIXEL_FMT_YCRCB_422_SP}, {DRM_FORMAT_YUV422, PIXEL_FMT_YCBCR_422_P}, {DRM_FORMAT_YVU422, PIXEL_FMT_YCRCB_422_P}, }; uint32_t fmtOut = 0; for (uint32_t i = 0; i < sizeof(convertTable) / sizeof(convertTable[0]); i++) { if (convertTable[i].pixFormat == fmtIn) { fmtOut = convertTable[i].drmFormat; } } DISPLAY_LOGD("fmtIn %{public}d, outFmt %{public}d", fmtIn, fmtOut); return fmtOut; }
此函数通过遍历drm的property来将其保存在变量prop中。其调用流程如下:
drmModeObjectGetProperties--->drmModeGetProperty--->drmModeFreeProperty-→drmModeFreeObjectProperties
通过上述流程,可提取drmmode的如下结构体数据
typedef struct _drmModeProperty { uint32_t prop_id; uint32_t flags; char name[DRM_PROP_NAME_LEN]; int count_values; uint64_t *values; /* store the blob lengths */ int count_enums; struct drm_mode_property_enum *enums; int count_blobs; uint32_t *blob_ids; /* store the blob IDs */ } drmModePropertyRes, *drmModePropertyPtr;
并将其存放在DrmProperty结构体中,如下
struct DrmProperty { uint32_t propId; uint64_t value; uint32_t type; uint32_t flags; std::string name; std::vector<uint64_t> values; std::vector<DrmPropertyEnum> enums; std::vector<uint32_t> blob_ids; };
故,此函数的代码实现如下:
int32_t DrmDevice::GetProperty(uint32_t objId, uint32_t objType, const std::string &name, DrmProperty &prop) { drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(GetDrmFd(), objId, objType); DISPLAY_CHK_RETURN((!props), DISPLAY_FAILURE, DISPLAY_LOGE("can not get properties")); bool found = false; for (uint32_t i = 0; i < props->count_props; i++) { drmModePropertyPtr p = drmModeGetProperty(GetDrmFd(), props->props[i]); if (strcmp(p->name, name.c_str()) == 0) { found = true; prop.propId = p->prop_id; prop.value = props->prop_values[i]; prop.name = p->name; prop.flags = p->flags; for (int i = 0; i < p->count_values; ++i) { prop.values.push_back(p->values[i]); } for (int i = 0; i < p->count_enums; ++i) { prop.enums.push_back(DrmPropertyEnum(&p->enums[i])); } for (int i = 0; i < p->count_blobs; ++i) { prop.blob_ids.push_back(p->blob_ids[i]); } if (prop.flags & DRM_MODE_PROP_RANGE) { prop.type = static_cast<uint32_t>(DrmPropertyType::DRM_PROPERTY_TYPE_INT); } else if (prop.flags & DRM_MODE_PROP_ENUM) { prop.type = static_cast<uint32_t>(DrmPropertyType::DRM_PROPERTY_TYPE_ENUM); } else if (prop.flags & DRM_MODE_PROP_OBJECT) { prop.type = static_cast<uint32_t>(DrmPropertyType::DRM_PROPERTY_TYPE_OBJECT); } else if (prop.flags & DRM_MODE_PROP_BLOB) { prop.type = static_cast<uint32_t>(DrmPropertyType::DRM_PROPERTY_TYPE_BLOB); } else if (prop.flags & DRM_MODE_PROP_BITMASK) { prop.type = static_cast<uint32_t>(DrmPropertyType::DRM_PROPERTY_TYPE_BITMASK); } } drmModeFreeProperty(p); } drmModeFreeObjectProperties(props); return found ? DISPLAY_SUCCESS : DISPLAY_NOT_SUPPORT; }
此函数初始化drm的设置,主要设置并检查此程序是否为第一个drm的gpu渲染程序(drmSetMaster/drmIsMaster)和设置drm默认使用atomic接口(drmSetClientCap),主要实现如下
int32_t DrmDevice::Init() { int ret = drmSetClientCap(GetDrmFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); DISPLAY_CHK_RETURN((ret), DISPLAY_FAILURE, DISPLAY_LOGE("DRM_CLIENT_CAP_UNIVERSAL_PLANES set failed %{public}s", strerror(errno))); ret = drmSetClientCap(GetDrmFd(), DRM_CLIENT_CAP_ATOMIC, 1); DISPLAY_CHK_RETURN((ret), DISPLAY_FAILURE, DISPLAY_LOGE("DRM_CLIENT_CAP_ATOMIC set failed %{public}s", strerror(errno))); ret = drmSetMaster(GetDrmFd()); DISPLAY_CHK_RETURN((ret), DISPLAY_FAILURE, DISPLAY_LOGE("can not set to master errno : %{public}d", errno)); DISPLAY_LOGE("chenyf master"); ret = drmIsMaster(GetDrmFd()); DISPLAY_CHK_RETURN((!ret), DISPLAY_FAILURE, DISPLAY_LOGE("is not master : %{public}d", errno)); return DISPLAY_SUCCESS; }
此函数作为显示器热插拔时候使用drm分配crtc,从而支持插拔connector时可以正常显示。
bool DrmDevice::HandleHotplug(uint32_t dispId, bool plugIn) { uint32_t find = 0; uint32_t connectorId; for (auto &dispConnectorIdMap : dispConnectorIdMaps_) { if (dispConnectorIdMap.first == dispId) { connectorId = dispConnectorIdMap.second; find = 1; break; } } if (find) { for (auto &connectorPair : mConnectors) { auto connector = connectorPair.second; if (connectorId == connector->GetId()) { if (connector->HandleHotplug(mEncoders, mCrtcs, plugIn) == true) { connector->Init(*this); return true; } } } } return false; }
此函数主要用于发现display的crtc,encoder,connector,plane,并触发DrmDisplay的初始化操作。
std::unordered_map<uint32_t, std::shared_ptr<HdiDisplay>> DrmDevice::DiscoveryDisplay() { uint32_t dispId; uint32_t connectorId; dispConnectorIdMaps_.clear(); mDisplays.clear(); drmModeResPtr res = drmModeGetResources(GetDrmFd()); DISPLAY_CHK_RETURN((res == nullptr), mDisplays, DISPLAY_LOGE("can not get drm resource")); // discovery all drm resource FindAllCrtc(res); FindAllEncoder(res); FindAllConnector(res); FindAllPlane(); DISPLAY_LOGD(); // travel all connector for (auto &connectorPair : mConnectors) { auto connector = connectorPair.second; uint32_t crtcId = 0; int32_t ret = connector->PickIdleCrtcId(mEncoders, mCrtcs, crtcId); if (ret != DISPLAY_SUCCESS) { continue; } DISPLAY_LOGD(); auto crtcIter = mCrtcs.find(crtcId); if (crtcIter == mCrtcs.end()) { DISPLAY_LOGE("can not find crtc for the id %{public}d", connector->GetId()); continue; } DISPLAY_LOGD(); auto crtc = crtcIter->second; DISPLAY_LOGD("crtc %{public}p", crtc.get()); // create the display std::shared_ptr<HdiDisplay> display = std::make_shared<DrmDisplay>(connector, crtc, mInstance); DISPLAY_LOGD(); display->Init(); dispId = display->GetId(); connectorId = connector->GetId(); mDisplays.emplace(dispId, std::move(display)); dispConnectorIdMaps_.emplace(dispId, connectorId); } DISPLAY_LOGD("find display size %{public}zd", mDisplays.size()); return mDisplays; }
对于dayu210的仓库代码,修改如下:
diff --git a/rk3588/hardware/display/src/display_device/drm_device.cpp b/rk3588/hardware/display/src/display_device/drm_device.cpp index 18ef587..146dc6b 100755 --- a/rk3588/hardware/display/src/display_device/drm_device.cpp +++ b/rk3588/hardware/display/src/display_device/drm_device.cpp @@ -276,8 +276,6 @@ std::vector<std::shared_ptr<DrmPlane>> DrmDevice::GetDrmPlane(uint32_t pipe, uin { std::vector<std::shared_ptr<DrmPlane>> planes; for (const auto &plane : mPlanes) { - if (plane->GetId() != 105) - continue; if (plane->IsIdle() && ((1 << pipe) & plane->GetPossibleCrtcs()) && (type == plane->GetType())) { planes.push_back(plane); }
可以发现,dayu210的官方提交会固定在plane105上,但是rk3588s的板子的plane不存在105。所以rk3588s的openharmony系统无法正常显示。将其注释即可正常通过DrmDevice::GetDrmPlane获取到rk3588s平台的所有plane。此时openharmony即可正常通过drm hdi正常分配plane来用于显示了。
获取平台所有的plane和crtc以及connector如下命令
# cat /sys/kernel/debug/dri/0/state plane[54]: Esmart0-win0 crtc=video_port0 fb=241 allocated by = IPC_0_764 refcount=2 format=AB24 little-endian (0x34324241) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=3 start=00000000 size=9216000 imported=no crtc-pos=1920x1200+0+0 src-pos=1920.000000x1200.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[76]: Esmart1-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[98]: Esmart2-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[120]: Esmart3-win0 crtc=video_port3 fb=233 allocated by = IPC_0_764 refcount=2 format=AB24 little-endian (0x34324241) modifier=0x0 size=800x1280 layers: size[0]=800x1280 pitch[0]=3200 offset[0]=0 obj[0]: name=0 refcount=3 start=00000000 size=4096000 imported=no crtc-pos=800x1280+0+0 src-pos=800.000000x1280.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[142]: Cluster0-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[156]: Cluster1-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[170]: Cluster2-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range plane[184]: Cluster3-win0 crtc=(null) fb=0 crtc-pos=0x0+0+0 src-pos=0.000000x0.000000+0.000000+0.000000 rotation=1 normalized-zpos=0 color-encoding=ITU-R BT.601 YCbCr color-range=YCbCr limited range crtc[68]: video_port0 enable=1 active=1 self_refresh_active=0 planes_changed=1 mode_changed=0 active_changed=0 connectors_changed=0 color_mgmt_changed=0 plane_mask=1 connector_mask=2 encoder_mask=2 mode: "1920x1200": 60 154000 1920 1968 2000 2080 1200 1203 1209 1235 0x48 0x5 crtc[90]: video_port1 enable=0 active=0 self_refresh_active=0 planes_changed=0 mode_changed=0 active_changed=0 connectors_changed=0 color_mgmt_changed=0 plane_mask=0 connector_mask=0 encoder_mask=0 mode: "": 0 0 0 0 0 0 0 0 0 0 0x0 0x0 crtc[112]: video_port2 enable=0 active=0 self_refresh_active=0 planes_changed=0 mode_changed=0 active_changed=0 connectors_changed=0 color_mgmt_changed=0 plane_mask=0 connector_mask=0 encoder_mask=0 mode: "": 0 0 0 0 0 0 0 0 0 0 0x0 0x0 crtc[134]: video_port3 enable=1 active=1 self_refresh_active=0 planes_changed=1 mode_changed=0 active_changed=0 connectors_changed=0 color_mgmt_changed=0 plane_mask=8 connector_mask=4 encoder_mask=4 mode: "800x1280": 60 72600 800 832 846 872 1280 1360 1368 1388 0x48 0xa connector[203]: Writeback-1 crtc=(null) self_refresh_aware=0 connector[205]: HDMI-A-1 crtc=video_port0 self_refresh_aware=0 connector[219]: DSI-1 crtc=video_port3 self_refresh_aware=0 connector[222]: DP-1 crtc=(null) self_refresh_aware=0
Openharmony在显示drm panel上的实现仅仅实现了dsi接口的显示屏,这就导致了如果客户使用了非dsi的显示,例如edp,hdmi,则没有办法在openharmony上显示。目前来看openharmony基本没有新的补丁合入,所以这一块的工作量得自行完成。本文通过移植simple_panel来实现非dsi的其他驱动的panel实现。
https://gitlab2.kylin.com/sh-product-Embedded/openharmony/drivers_hdf_core
这里面有两笔提交,一笔是simple_panel的c代码提交如下:
https://gitlab2.kylin.com/sh-product-Embedded/openharmony/drivers_hdf_core/-/commit/951647792a9ca86897b3f23f5edd6bf9f82d7aa5
一笔是Makefile和Kconfig使能的提交如下:
https://gitlab2.kylin.com/sh-product-Embedded/openharmony/drivers_hdf_core/-/commit/235164e43ced1c6c8afedcb660b3b52be51b267e
我们知道,内核提供了标准的panel_simple.c的驱动,其文件如下:
drivers/gpu/drm/panel/panel-simple.c
这里我们关注非dsi的实现,如下:
static struct platform_driver panel_simple_platform_driver = { .driver = { .name = "panel-simple", .of_match_table = platform_of_match, }, .probe = panel_simple_platform_probe, .remove = panel_simple_platform_remove, .shutdown = panel_simple_platform_shutdown, };
所以根据内核标准代码,需要实现其基本功能,这里拆分成了panel_simple_common.c和hdf_drm_panel_simple.c
代码写的比较丑陋,但是我对照了基本逻辑,应该是没有大问题的。
这里panel_simple_common.c主要是panel硬件的信息设置,需要对接dts的内容,并提供了panel_simple_loader_protect函数,如下
而hdf_drm_panel_simple.c主要是对drm负责的panel注册,主要是drm_panel_init和drm_panel_add,这里也需要填充drm_panel_init所需要的回调drm_panel_funcs。如下
综上,我们可以知道,这里的只是通过hdf实现了simple_panel的驱动而已。
对于Makefile,这里如下设置
ifeq ($(CONFIG_DRIVERS_HDF_SIMPLE_PANEL), y) obj-$(CONFIG_DRIVERS_HDF_SIMPLE_PANEL) += \ $(DISPLAY_ROOT_DIR)/panel/panel_simple_common.o else obj-$(CONFIG_ARCH_ROCKCHIP) += \ $(DISPLAY_ROOT_DIR)/panel/ili9881_st_5p5.o endif
这里因为实现了两次panel_simple_loader_protect,所以代码是互斥的,所以通过内核配置CONFIG_DRIVERS_HDF_SIMPLE_PANEL将其隔开
根据上面的讲述,我们可以完成hcs的驱动移植,这里通过修改hcs来使得hcs生效,如下:
diff --git a/rk3568/hdf_config/khdf/device_info/device_info.hcs b/rk3568/hdf_config/khdf/device_info/device_info.hcs index 16b6abf..267aa13 100644 --- a/rk3568/hdf_config/khdf/device_info/device_info.hcs +++ b/rk3568/hdf_config/khdf/device_info/device_info.hcs @@ -241,7 +241,7 @@ policy = 0; priority = 197; preload = 0; - moduleName = "HDF_DRMPANEL"; + moduleName = "HDF_DRM_PANEL_SIMPLE"; } } device_hdf_disp :: device { @@ -253,7 +253,7 @@ serviceName = "hdf_disp"; } } - device_hi35xx_disp :: device { + device_hi35xx_disp :: device { device0 :: deviceNode { policy = 0; priority = 195; @@ -284,7 +284,7 @@ policy = 0; priority = 100; preload = 0; - moduleName = "LCD_ILI9881_ST_5P5"; + moduleName = "PANEL_SIMPLE_COMMON"; } } device_pwm_bl :: device {
为什么这样改,因为hcs的设定中,host是唯一的,所以display只能选择"HDF_DRM_PANEL_SIMPLE"或者"HDF_DRMPANEL"
同样的驱动LCD_ILI9881_ST_5P5和PANEL_SIMPLE_COMMON也互斥,所以只能二选一
如果对hcs生效有疑问的,可以查看文章Openharmony hcs文件编译不生效的问题
Openharmony 4.0的代码编译rk3588代码后,出现开机logo图层仍存在,从而导致系统显示闪烁。最后此问题的原因在于drm_plane.cpp和drm_plane.h的图层设置逻辑异常。如下是问题分析
问题现象如下:
# cat /sys/kernel/debug/dri/0/framebuffer framebuffer[232]: allocated by = IPC_1_798 refcount=2 format=AB24 little-endian (0x34324241) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=4 start=00000000 size=9216000 imported=no framebuffer[244]: allocated by = IPC_1_798vop2_win_data refcount=2 format=AB24 little-endian (0x34324241) modifier=0x0 size=800x1280 layers: size[0]=800x1280 pitch[0]=3200 offset[0]=0 obj[0]: name=0 refcount=4 start=00000000 size=4096000 imported=no framebuffer[243]: allocated by = IPC_1_798 refcount=1 format=AB24 little-endian (0x34324241) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=4 start=00000000 size=9216000 imported=no framebuffer[242]: allocated by = IPC_1_798 refcount=1 format=AB24 little-endian (0x34324241) modifier=0x0 size=800x1280 layers: size[0]=800x1280 pitch[0]=3200 offset[0]=0 obj[0]: name=0 refcount=3 start=00000000 size=4096000 imported=no framebuffer[238]: allocated by = [fbcon] refcount=1 format=XR24 little-endian (0x34325258) modifier=0x0 size=1920x1280 layers: size[0]=1920x1280 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=1 start=00000000 size=9830400 imported=no framebuffer[233]: allocated by = kworker/u16:3 refcount=1 format=RG16 little-endian (0x36314752) modifier=0x0 size=500x501 layers: size[0]=500x501 pitch[0]=1000 offset[0]=765952 obj[0]: name=0 refcount=1 start=00000000 size=1269760 imported=no framebuffer[231]: allocated by = kworker/u16:3 refcount=1 format=RG16 little-endian (0x36314752) modifier=0x0 size=500x501 layers: size[0]=500x501 pitch[0]=1000 offset[0]=765952 obj[0]: name=0 refcount=1 start=00000000 size=1269760 imported=no
通过上述信息可以发现,系统起来之后IPC_1_798作为Openharmony的compose进程占用了fb 232/244/243/242
控制台fbcon占用了fb 238。这都是没有问题的,但是有问题的是kworker/u16:3占用了233/231
这里值得注意的是kworker/u16:3就是内核绘制开机logo的线程,因为当前环境上是双屏,hdmi+dsi显示,则这里有两个kworker,fb233在hdmi上fb231在dsi上。
理论上开机之后,fb不应该被开机logo这样的kworker占用。
根据drm的设计,fb是挂在plane下的,也就是说,对于openharmony的drm上层,对这个kworker的plane的设置存在问题。所以应该怀疑rk3588的plane设置存在异常。
关于设备树,可以知道哪些vp使用哪些plane。如下
/* vp0 & vp1 splice for 8K output */ &vp0 { rockchip,plane-mask = <(1 << ROCKCHIP_VOP2_CLUSTER0 | 1 << ROCKCHIP_VOP2_ESMART0)>; rockchip,primary-plane = <ROCKCHIP_VOP2_ESMART0>; assigned-clocks = <&cru ACLK_VOP>; assigned-clock-rates = <800000000>; }; &vp1 { rockchip,plane-mask = <(1 << ROCKCHIP_VOP2_CLUSTER1 | 1 << ROCKCHIP_VOP2_ESMART1)>; rockchip,primary-plane = <ROCKCHIP_VOP2_ESMART1>; }; &vp2 { rockchip,plane-mask = <(1 << ROCKCHIP_VOP2_CLUSTER2 | 1 << ROCKCHIP_VOP2_ESMART2)>; rockchip,primary-plane = <ROCKCHIP_VOP2_ESMART2>; }; &vp3 { rockchip,plane-mask = <(1 << ROCKCHIP_VOP2_CLUSTER3 | 1 << ROCKCHIP_VOP2_ESMART3)>; rockchip,primary-plane = <ROCKCHIP_VOP2_ESMART3>; };
可以知道,这里vp0使用了cluster0/esmart0,vp1使用了cluster1/esmart1, vp2使用了cluster2/esmart2,vp3使用了cluster3/esmart3。
对于内核,在drivers/gpu/drm/rockchip/rockchip_drm_vop2.c的vop2_crtc_create_plane_mask_property对应了宏的关系如下:
static const struct drm_prop_enum_list props[] = { { ROCKCHIP_VOP2_CLUSTER0, "Cluster0" }, { ROCKCHIP_VOP2_CLUSTER1, "Cluster1" }, { ROCKCHIP_VOP2_ESMART0, "Esmart0" }, { ROCKCHIP_VOP2_ESMART1, "Esmart1" }, { ROCKCHIP_VOP2_SMART0, "Smart0" }, { ROCKCHIP_VOP2_SMART1, "Smart1" }, { ROCKCHIP_VOP2_CLUSTER2, "Cluster2" }, { ROCKCHIP_VOP2_CLUSTER3, "Cluster3" }, { ROCKCHIP_VOP2_ESMART2, "Esmart2" }, { ROCKCHIP_VOP2_ESMART3, "Esmart3" }, };
需要留意的宏如下:
#define ROCKCHIP_VOP2_CLUSTER0 0 #define ROCKCHIP_VOP2_CLUSTER1 1 #define ROCKCHIP_VOP2_ESMART0 2 #define ROCKCHIP_VOP2_ESMART1 3 #define ROCKCHIP_VOP2_SMART0 4 #define ROCKCHIP_VOP2_SMART1 5 #define ROCKCHIP_VOP2_CLUSTER2 6 #define ROCKCHIP_VOP2_CLUSTER3 7 #define ROCKCHIP_VOP2_ESMART2 8 #define ROCKCHIP_VOP2_ESMART3 9
随即代码如下:
prop = drm_property_create_bitmask(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, "PLANE_MASK", props, ARRAY_SIZE(props), 0xffffffff); if (!prop) { DRM_DEV_ERROR(vop2->dev, "create plane_mask prop for vp%d failed\n", vp->id); return -ENOMEM; } vp->plane_mask_prop = prop; drm_object_attach_property(&crtc->base, vp->plane_mask_prop, plane_mask);
可以知道,通过drm的ioctl可以读取"PLANE_MASK"来知道plane的图层设置。
根据rk的vop init过程,可以知道,vop的NAME来自vop2_win_data.name字段,则如下:
static const struct vop2_win_data rk3588_vop_win_data[] = { 其对应如下: .name = "Cluster0-win0", .name = "Cluster0-win1", .name = "Cluster1-win0", .name = "Cluster1-win1", .name = "Cluster2-win0", .name = "Cluster2-win1", .name = "Cluster3-win0", .name = "Cluster3-win1", .name = "Esmart0-win0", .name = "Esmart2-win0", .name = "Esmart1-win0", .name = "Esmart3-win0",
这些name都会被注册为drm的name属性,如下
static int vop2_plane_create_name_property(struct vop2 *vop2, struct vop2_win *win) { struct drm_prop_enum_list *props = vop2->plane_name_list; struct drm_property *prop; uint64_t bits = BIT_ULL(win->plane_id); prop = drm_property_create_bitmask(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, "NAME", props, vop2->registered_num_wins, bits); if (!prop) { DRM_DEV_ERROR(vop2->dev, "create Name prop for %s failed\n", win->name); return -ENOMEM; } win->name_prop = prop; drm_object_attach_property(&win->base.base, win->name_prop, bits); return 0; }
针对此,可以知道内核这边rk3588系列如何设置plane,对于rk3588来说,vop在此芯片上存在12个plane,对于esmart来说,默认都是win0,而对于cluster来说,可以选择win0和win1。
对于图层的选择,我们只需要针对不同的plane的bitmask来选择图层,也就是cluster/smart/esmart。具体在哪个win上,可以由应用和内核选择。
对于openharmony的代码,主要体现在:
src/display_device/drm_crtc.cpp src/display_device/drm_plane.cpp src/display_device/drm_plane.h
对于crtc,我们需要知道有哪些planemask,如下:
可以发现,planemask的设置与内核是一致的,我们通过确定planemask来选择对应图层给应用使用
struct PlaneMaskName planeMaskNames[] = { { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER0_MASK, "Cluster0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER1_MASK, "Cluster1" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER2_MASK, "Cluster2" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER3_MASK, "Cluster3" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_MASK, "Esmart0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_MASK, "Esmart1" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART2_MASK, "Esmart2" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART3_MASK, "Esmart3" }, { DrmPlaneType::DRM_PLANE_TYPE_Unknown, "unknown" }, };
如下遍历PLANE_MASK属性,用来与内核同步设置,这里因为上述和内核是一致的,所以遍历后的mPlaneMask和上述PlaneMaskName其实内容一致的。
ret = drmDevice.GetCrtcProperty(*this, "PLANE_MASK", prop); if (ret != DISPLAY_SUCCESS) { DISPLAY_LOGE("Failed to get plane_mask property"); } else { for (int i = 0; i < static_cast<int>(ARRAY_SIZE(planeMaskNames)); i++) { for (auto &drmEnum : prop.enums) { if (!strncmp(drmEnum.name.c_str(), (const char*)planeMaskNames[i].name, strlen(drmEnum.name.c_str())) && (prop.value & (1LL << drmEnum.value)) > 0) { mPlaneMask |= static_cast<int>(planeMaskNames[i].mask); DISPLAY_LOGI("crtc id %{public}d, plane name %{public}s value %{public}llx", GetId(), (const char*)planeMaskNames[i].name, (long long)planeMaskNames[i].mask); } } } }
而对于plane的定义,如下
struct PlaneTypeName planeTypeNames[] = { { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER0_WIN0, "Cluster0-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER0_WIN1, "Cluster0-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER1_WIN0, "Cluster1-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER1_WIN1, "Cluster1-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_WIN0, "Esmart0-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_WIN1, "Esmart0-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_WIN2, "Esmart0-win2" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_WIN3, "Esmart0-win3" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_WIN0, "Esmart1-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_WIN1, "Esmart1-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_WIN2, "Esmart1-win2" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_WIN3, "Esmart1-win3" }, { DrmPlaneType::DRM_PLANE_TYPE_Unknown, "unknown" }, };
这里可以发现,上层使用的plane的12个和内核描述的12个不相符,内核有cluster2和cluster3,且内核的esmart只有win0,所以如下函数在于内核匹配时出现了不一致的情况
ret = drmDevice.GetPlaneProperty(*this, "NAME", prop); DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("cat not get pane crtc prop id")); for (int i = 0; i < static_cast<int>ARRAY_SIZE(planeTypeNames); i++) { uint32_t find_name = 0; for (auto &drmEnum : prop.enums) { if (!strncmp(drmEnum.name.c_str(), (const char*)planeTypeNames[i].name, strlen(planeTypeNames[i].name))) { find_name = (1LL << drmEnum.value); } } if (find_name) { DISPLAY_LOGI("find plane id %{public}d, type %{public}x %{public}s", GetId(), planeTypeNames[i].type, planeTypeNames[i].name); mWinType = planeTypeNames[i].type; mName = planeTypeNames[i].name; break; } }
所以,对于planeTypeNames的描述错误,我们为了和实际硬件完全一致,则修改如下:
struct PlaneTypeName planeTypeNames[] = { { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER0_WIN0, "Cluster0-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER0_WIN1, "Cluster0-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER1_WIN0, "Cluster1-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER1_WIN1, "Cluster1-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER2_WIN0, "Cluster2-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER2_WIN1, "Cluster2-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER3_WIN0, "Cluster3-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_CLUSTER3_WIN1, "Cluster3-win1" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART0_WIN0, "Esmart0-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART1_WIN0, "Esmart1-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART2_WIN0, "Esmart2-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_ESMART3_WIN0, "Esmart3-win0" }, { DrmPlaneType::DRM_PLANE_TYPE_Unknown, "unknown" }, };
至此,还未完成,Openharmony对于Plane type的宏定义将两个概念放在同一个enum中,这导致匹配是计算plane是否在planemask中存在错误。如下代码根据mask选择plane
int32_t HdiDrmComposition::FindPlaneAndApply(drmModeAtomicReqPtr pset) { int32_t ret = 0; for (uint32_t i = 0; i < mCompLayers.size(); i++) { HdiDrmLayer *layer = static_cast<HdiDrmLayer *>(mCompLayers[i]); HdiLayer *hlayer = mCompLayers[i]; for (uint32_t j = 0; j < mPlanes.size(); j++) { auto &drmPlane = mPlanes[j]; if (drmPlane->GetPipe() != 0 && drmPlane->GetPipe() != (1 << mCrtc->GetPipe())) { DISPLAY_LOGI("plane %{public}d used pipe %{public}d crtc pipe %{public}d", drmPlane->GetId(), drmPlane->GetPipe(), mCrtc->GetPipe()); continue; } /* Check whether the plane belond to the crtc */ if (!(static_cast<int>(drmPlane->GetWinType()) & mCrtc->GetPlaneMask())) { continue; } DISPLAY_LOGD("use plane %{public}d WinType %{public}x crtc %{public}d PlaneMask %{public}x", drmPlane->GetId(), drmPlane->GetWinType(), mCrtc->GetId(), mCrtc->GetPlaneMask()); if (drmPlane->GetCrtcId() == mCrtc->GetId() || drmPlane->GetCrtcId() == 0) { ret = ApplyPlane(*layer, *hlayer, *drmPlane, pset); if (ret != DISPLAY_SUCCESS) { DISPLAY_LOGE("apply plane failed"); break; } /* mark the plane is used by crtc */ drmPlane->BindToPipe(1 << mCrtc->GetPipe()); break; } } } return DISPLAY_SUCCESS; }
这里函数留意的是
if (!(static_cast<int>(drmPlane->GetWinType()) & mCrtc->GetPlaneMask())) { continue; }
可以知道,这里检查的GetWinType和GetPlaneMask都是来自于enum class DrmPlaneType,所以这个DrmPlaneType不能瞎设置。我们必须保障其planemask和plane对应,也就是按位相与为true。具体如下:
一个是plane枚举,如下
// Cluster 0 DRM_PLANE_TYPE_CLUSTER0_WIN0 = 1 << 0, DRM_PLANE_TYPE_CLUSTER0_WIN1 = 1 << 1, // Cluster 1 DRM_PLANE_TYPE_CLUSTER1_WIN0 = 1 << 2, DRM_PLANE_TYPE_CLUSTER1_WIN1 = 1 << 3, // Cluster 2 DRM_PLANE_TYPE_CLUSTER2_WIN0 = 1 << 4, DRM_PLANE_TYPE_CLUSTER2_WIN1 = 1 << 5, // Cluster 3 DRM_PLANE_TYPE_CLUSTER3_WIN0 = 1 << 6, DRM_PLANE_TYPE_CLUSTER3_WIN1 = 1 << 7, // Esmart 0 DRM_PLANE_TYPE_ESMART0_WIN0 = 1 << 8, DRM_PLANE_TYPE_ESMART0_WIN1 = 1 << 10, DRM_PLANE_TYPE_ESMART0_WIN2 = 1 << 16, DRM_PLANE_TYPE_ESMART0_WIN3 = 1 << 20, // Esmart 1 DRM_PLANE_TYPE_ESMART1_WIN0 = 1 << 12, DRM_PLANE_TYPE_ESMART1_WIN1 = 1 << 13, DRM_PLANE_TYPE_ESMART1_WIN2 = 1 << 14, DRM_PLANE_TYPE_ESMART1_WIN3 = 1 << 15, // Esmart 2 DRM_PLANE_TYPE_ESMART2_WIN0 = 1 << 9, DRM_PLANE_TYPE_ESMART2_WIN1 = 1 << 17, DRM_PLANE_TYPE_ESMART2_WIN2 = 1 << 18, DRM_PLANE_TYPE_ESMART2_WIN3 = 1 << 19, // Esmart 3 DRM_PLANE_TYPE_ESMART3_WIN0 = 1 << 11, DRM_PLANE_TYPE_ESMART3_WIN1 = 1 << 21, DRM_PLANE_TYPE_ESMART3_WIN2 = 1 << 22, DRM_PLANE_TYPE_ESMART3_WIN3 = 1 << 23,
另一个是planemask的枚举
// Cluster mask DRM_PLANE_TYPE_CLUSTER0_MASK= 0x1, DRM_PLANE_TYPE_CLUSTER1_MASK= 0x2, DRM_PLANE_TYPE_CLUSTER2_MASK= 0x40, DRM_PLANE_TYPE_CLUSTER3_MASK= 0x80, DRM_PLANE_TYPE_CLUSTER_MASK = 0xff, // Esmart mask DRM_PLANE_TYPE_ESMART0_MASK = 0x04, DRM_PLANE_TYPE_ESMART1_MASK = 0x08, DRM_PLANE_TYPE_ESMART2_MASK = 0x100, DRM_PLANE_TYPE_ESMART3_MASK = 0x200, DRM_PLANE_TYPE_ESMART_MASK = 0xffff00, DRM_PLANE_TYPE_Unknown = 0xffffffff,
对于planemask,如上分析可以知道,与内核是一致的。但是对于plane的枚举,我们发现其是bit1的左移设置。但是却发现,cluster0_mask 只代表0x1,但是cluster0_win0是0x1,而cluster_win1是0x2。这导致FindPlaneAndApply只会选择cluster0_win0。
其他宏也有这样的逻辑错误。这里不一一列举。
对于这样的问题,主要原因是plane的枚举设置太随意了。所以我们保持着改动最小代码的前提下,如下设置枚举
enum class DrmPlaneType { // Cluster 0 DRM_PLANE_TYPE_CLUSTER0_WIN0 = 1 << 10 | 0x1, DRM_PLANE_TYPE_CLUSTER0_WIN1 = 1 << 11 | 0x1, // Cluster 1 DRM_PLANE_TYPE_CLUSTER1_WIN0 = 1 << 12 | 0x2, DRM_PLANE_TYPE_CLUSTER1_WIN1 = 1 << 13 | 0x2, // Cluster 2 DRM_PLANE_TYPE_CLUSTER2_WIN0 = 1 << 14 | 0x40, DRM_PLANE_TYPE_CLUSTER2_WIN1 = 1 << 15 | 0x40, // Cluster 3 DRM_PLANE_TYPE_CLUSTER3_WIN0 = 1 << 16 | 0x80, DRM_PLANE_TYPE_CLUSTER3_WIN1 = 1 << 17 | 0x80, // Esmart 0 DRM_PLANE_TYPE_ESMART0_WIN0 = 1 << 18 | 0x04, // Esmart 1 DRM_PLANE_TYPE_ESMART1_WIN0 = 1 << 19 | 0x08, // Esmart 2 DRM_PLANE_TYPE_ESMART2_WIN0 = 1 << 20 | 0x100, // Esmart 3 DRM_PLANE_TYPE_ESMART3_WIN0 = 1 << 21 | 0x200, // Cluster mask DRM_PLANE_TYPE_CLUSTER0_MASK= 0x1, DRM_PLANE_TYPE_CLUSTER1_MASK= 0x2, DRM_PLANE_TYPE_CLUSTER2_MASK= 0x40, DRM_PLANE_TYPE_CLUSTER3_MASK= 0x80, DRM_PLANE_TYPE_CLUSTER_MASK = 0xff, // Esmart mask DRM_PLANE_TYPE_ESMART0_MASK = 0x04, DRM_PLANE_TYPE_ESMART1_MASK = 0x08, DRM_PLANE_TYPE_ESMART2_MASK = 0x100, DRM_PLANE_TYPE_ESMART3_MASK = 0x200, DRM_PLANE_TYPE_ESMART_MASK = 0xffff00, DRM_PLANE_TYPE_Unknown = 0xffffffff, };
对于plane的枚举,我们将其放在bit10-bit21,对于planemask的枚举同内核设置一致,则bit0-bit9。这样子两个位&时不会出现逻辑异常。则如果设置planemask为cluster0,则plane可以选择cluster0_win0和cluster_win1。从而代码能够正常的RemoveUnusePlane和FindPlaneAndApply。
修改后,现象如下:
# cat /sys/kernel/debug/dri/0/framebuffer framebuffer[236]: allocated by = IPC_1_614 refcount=2 format=AB24 little-endian (0x34324241) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=3 start=00000000 size=9216000 imported=no framebuffer[228]: allocated by = IPC_1_614 refcount=1 format=AB24 little-endian (0x34324241) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=3 start=00000000 size=9216000 imported=no framebuffer[233]: allocated by = [fbcon] refcount=1 format=XR24 little-endian (0x34325258) modifier=0x0 size=1920x1200 layers: size[0]=1920x1200 pitch[0]=7680 offset[0]=0 obj[0]: name=0 refcount=1 start=00000000 size=9216000 imported=no
这下图层正常了。问题解决