编辑
2025-01-20
记录知识
0

我们根据dayu200的开发板的研究,分析了dayu200的音频是通过ADM的方案实现,如下是具体分析过程

一、声卡组成情况

dayu200的声卡接入方式如下:

image.png

二、代码位置

针对左侧,需要实现dma传输的驱动,路径如下:

device/board/hihope/rk3568/audio_drivers/soc/src/rk3568_dma_adapter.c

对于I2S1,需要实现I2S1的驱动功能,路径如下:

device/board/hihope/rk3568/audio_drivers/dai/src/rk3568_dai_adapter.c

对于RK809,需要实现RK809的驱动功能,路径如下:

device/board/hihope/rk3568/audio_drivers/codec/rk809_codec/src/rk809_codec_adapter.c

对于hdf的ADM框架,代码如下(以Linux路径为例):

out/kernel/src_tmp/linux-5.10/drivers/hdf/framework/model/audio

对于匹配驱动,使用hcs的方式,代码如下:

vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs

三、具体联系

根据上面的代码仓库描述,我们需要知道如下:

  • ADM是openharmony实现的hdf driver
  • 厂商可以在device board里面实现自己的audio driver
  • hdf driver的匹配通过hcs的规则来实现

所以根据上述的四条,可以如下解析:

3.1 ADM hdf driver

/* HdfDriverEntry definitions */ struct HdfDriverEntry g_audioDriverEntry = { .moduleVersion = 1, .moduleName = "HDF_AUDIO", .Bind = AudioDriverBind, .Init = AudioDriverInit, .Release = AudioDriverRelease, }; HDF_INIT(g_audioDriverEntry); /* HdfDriverEntry definitions */ struct HdfDriverEntry g_audioStreamEntry = { .moduleVersion = 1, .moduleName = "HDF_AUDIO_STREAM", .Bind = AudioStreamBind, .Init = AudioStreamInit, .Release = AudioStreamRelease, }; HDF_INIT(g_audioStreamEntry); /* HdfDriverEntry definitions */ struct HdfDriverEntry g_audioControlEntry = { .moduleVersion = 1, .moduleName = "HDF_AUDIO_CONTROL", .Bind = AudioControlBind, .Init = AudioControlInit, .Release = AudioControlRelease, }; HDF_INIT(g_audioControlEntry);

可以知道,ADM需要至少注册三个必要类型的驱动,一个是audio_host,一个是audio_control_dispatch,一个是audio_stream_dispatch。对于的hcs如下:

device_primary :: deviceNode { policy = 2; priority = 60; preload = 0; permission = 0666; moduleName = "HDF_AUDIO"; deviceMatchAttr = "hdf_audio_driver_0"; serviceName = "hdf_audio_codec_primary_dev0"; } device0 :: deviceNode { policy = 2; priority = 80; preload = 0; permission = 0666; moduleName = "HDF_AUDIO_STREAM"; serviceName = "hdf_audio_render"; } device1 :: deviceNode { policy = 2; priority = 80; preload = 0; permission = 0666; moduleName = "HDF_AUDIO_STREAM"; serviceName = "hdf_audio_capture"; } device0 :: deviceNode { policy = 2; priority = 80; preload = 0; permission = 0666; moduleName = "HDF_AUDIO_CONTROL"; serviceName = "hdf_audio_control"; }

这样,在系统开机的时候,会通过hdf device manager驱动管理和注册,调用这些驱动的init函数

3.2 vendor audio hdf driver

对于厂商实现的audio_driver,这里主要是dma,i2s和codec,如下

/* HdfDriverEntry definitions */ struct HdfDriverEntry g_platformDriverEntry = { .moduleVersion = 1, .moduleName = "DMA_RK3568", .Bind = PlatformDriverBind, .Init = PlatformDriverInit, .Release = PlatformDriverRelease, }; HDF_INIT(g_platformDriverEntry); /* HdfDriverEntry definitions */ struct HdfDriverEntry g_daiDriverEntry = { .moduleVersion = 1, .moduleName = "DAI_RK3568", .Bind = DaiDriverBind, .Init = DaiDriverInit, .Release = DaiDriverRelease, }; HDF_INIT(g_daiDriverEntry); /* HdfDriverEntry definitions */ struct HdfDriverEntry g_Rk809DriverEntry = { .moduleVersion = 1, .moduleName = "CODEC_RK809", .Bind = Rk809DriverBind, .Init = Rk809DriverInit, .Release = RK809DriverRelease, }; HDF_INIT(g_Rk809DriverEntry);

这三个驱动,对于的hcs配置如下

device_primary :: deviceNode { policy = 1; priority = 50; preload = 0; permission = 0666; moduleName = "DAI_RK3568"; serviceName = "dai_service"; deviceMatchAttr = "hdf_dai_driver"; } device_primary :: deviceNode { policy = 1; priority = 50; preload = 0; permission = 0666; moduleName = "CODEC_ES8388"; serviceName = "codec_service_0"; deviceMatchAttr = "hdf_codec_driver_0"; } device_primary :: deviceNode { policy = 1; priority = 50; preload = 0; permission = 0666; moduleName = "DMA_RK3568"; serviceName = "dma_service_0"; deviceMatchAttr = "hdf_dma_driver"; }

这样,在系统开机的时候,会通过hdf device manager驱动管理和注册,调用这些驱动的init函数

3.3 ADM和vendor audio驱动的联系

通过hcs的card_controller可以将其联系起来,如下:

controller_0x120c1000 :: card_controller { match_attr = "hdf_audio_driver_0"; serviceName = "hdf_audio_codec_primary_dev0"; codecName = "codec_service_0"; platformName = "dma_service_0"; cpuDaiName = "dai_service"; codecDaiName = "codec_dai"; dspName = "dsp_service_0"; dspDaiName = "dsp_dai"; }

3.4 dma 的hcs config

对于dma而言,需要dma的相关配置,openharmony目前dma都在代码配置,hcs比较少,如下

controller_0x120c1010 :: dma_controller { match_attr = "hdf_dma_driver"; serviceName = "dma_service_0"; idInfo { chipName = "/i2s@fe410000"; chipIdRegister = 0xfe410000; chipIdSize = 0x1000; } regConfig { /* reg: register address rreg: register address shift: shift bits rshift: rshift bits min: min value max: max value mask: mask of value invert: enum InvertVal 0-uninvert 1-invert value: value reg, rreg, shift, rshift, min, max, mask, invert value */ daiStartupSeqConfig = [ 0x00, 0x00, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x0, //Transmit Operation Init ]; } }

3.5 dai 的hcs config

对于i2s而言,我们需要配置i2s1的基本信息,并且填充startup/hwparam/trigger的寄存器配置,如下

daiStartupSeqConfig = [ 0x00, 0x00, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x7200000f, //Transmit Operation Init 0x04, 0x04, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x01c8000f, //Receive Operation Init 0x08, 0x08, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x00001f1f, //Clock Generation Init 0x10, 0x10, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x001f0000, //DMA Control Init 0x14, 0x14, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x01f00000, //Interrupt Control Init 0x1C, 0x1C, 0, 0, 0, 0x3, 0x3, 0, 0, //XFER Init 0x30, 0x30, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x00003eff, //TDM Transmit Init 0x34, 0x34, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x00003eff, //TDM Receive Init 0x38, 0x38, 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0x00000707 //Clock Divider Init ]; daiParamsSeqConfig = [ 0x08, 0x08, 8, 8, 0x1F, 0xFF, 0xFF, 0, 0x0, // I2S_CKR_RSD 0x08, 0x08, 0, 0, 0x1F, 0xFF, 0xFF, 0, 0x0, // I2S_CKR_TSD 0x38, 0x38, 8, 8, 0x00, 0xFF, 0xFF, 0, 0x0, // I2S_CLKDIV_RX_MDIV 0x38, 0x38, 0, 0, 0x00, 0xFF, 0xFF, 0, 0x0, // I2S_CLKDIV_TX_MDIV 0x08, 0x08, 27, 27, 0x0, 0x1, 0x1, 0, 0x0, // I2S_CKR_MSS 0x08, 0x08, 26, 26, 0x0, 0x1, 0x1, 0, 0x0, // I2S_CKR_CKP 0x08, 0x08, 25, 25, 0x0, 0x1, 0x1, 0, 0x0, // I2S_CKR_RLP 0x08, 0x08, 24, 24, 0x0, 0x1, 0x1, 0, 0x0, // I2S_CKR_TLP ]; daiTriggerSeqConfig = [ 0x10, 0x10, 24, 24, 0x0, 0x1, 0x1, 0, 0x1, // I2S_DMACR_RDE 0x10, 0x10, 8, 8, 0x0, 0x1, 0x1, 0, 0x1, // I2S_DMACR_TDE 0x14, 0x14, 17, 17, 0x0, 0x1, 0x1, 0, 0x0, // I2S_INTCR_RXOIE 0x14, 0x14, 16, 16, 0x0, 0x1, 0x1, 0, 0x0, // I2S_INTCR_RXFIE 0x14, 0x14, 1, 1, 0x0, 0x1, 0x1, 0, 0x0, // I2S_INTCR_TXUIE 0x14, 0x14, 0, 0, 0x0, 0x1, 0x1, 0, 0x0 // I2S_INTCR_TXEIE ];

3.6 codec的hcs config

对于codec,这里是rk809,我们需要配置rk809的基本信息,如startup/hwparam/trigger,sapm寄存器,init寄存器,播放和录制默认配置,如下

controller_0x120c1030 :: codec_controller { match_attr = "hdf_codec_driver_0"; serviceName = "codec_service_0"; codecDaiName = "codec_dai"; hwInfo = [ /* Playback/Captrue, formats, rates, rate_min, rate_max, channels_min, channels_max, buffer_bytes_max, period_bytes_min, period_bytes_max, periods_min, periods_max */ 1, 0xF, 0xFF, 8000, 96000, 1, 2, 1, 2, 3, 4, 5, 2, 0xF, 0xFF, 8000, 96000, 1, 2, 1, 2, 3, 4, 5, ]; regConfig { /* reg: register address rreg: register address shift: shift bits rshift: rshift bits min: min value max: max value mask: mask of value invert: enum InvertVal 0-uninvert 1-invert value: value */ /* reg, value */ initSeqConfig = [ 0x13, 0xf4, 0x15, 0xff, 0x17, 0x40, 0x18, 0xc8, 0x1e, 0x00, 0x27, 0x3f, 0x29, 0x99, 0x2f, 0x03, 0x30, 0x06, 0x35, 0x02, 0x38, 0x10, 0x3c, 0x0F, 0x3d, 0x80, 0x3e, 0x0f, 0x3f, 0x11, 0x40, 0xa5, 0x41, 0x77, 0x42, 0x04, 0x43, 0x58, 0x44, 0x2d, 0x45, 0x0c, 0x46, 0xa5, 0x47, 0x00, 0x48, 0x00, 0x4b, 0x0f, 0x4c, 0x20, 0x4e, 0x0f, 0x4f, 0x00, ]; controlsConfig = [ /*array index, iface, mixer/mux, enable,*/ 0, 2, 0, 1, 1, 2, 0, 1, 2, 2, 0, 1, 3, 2, 0, 1, 4, 2, 0, 1, 5, 2, 0, 1, 8, 2, 0, 1, 9, 2, 0, 1, ]; /* reg, rreg, shift, rshift, min, max, mask, invert, value */ ctrlParamsSeqConfig = [ 0x31, 0x32, 0, 0, 0x00, 0xFF, 0xFF, 1, 0x00, // DACL/R Playback Volume 0x1a, 0x1b, 0, 0, 0x00, 0xFF, 0xFF, 1, 0x00, // ADCL/R Capture Volume 0x38, 0x38, 0, 0, 0x0, 0x1, 0x1, 0, 0x0, // DAC Playback Mute 0x27, 0x27, 6, 6, 0x0, 0x1, 0x1, 0, 0x0, // ADCL/R Capture Mute 0x29, 0x29, 4, 4, 0x0, 0xF, 0xF, 0, 0x9, // Mic Left Gain 0x29, 0x29, 0, 0, 0x0, 0xF, 0xF, 0, 0x9, // Mic Right Gain 0x4a, 0x4a, 2, 2, 0x0, 0x2, 0x3, 0, 0x0, // Render Channel Mode 0x4d, 0x4d, 2, 2, 0x0, 0x2, 0x3, 0, 0x0, // Captrue Channel Mode ]; /* reg, rreg, shift, rshift, min, max, mask, invert, value */ daiParamsSeqConfig = [ 0x45, 0x45, 0, 0, 0x0, 0xFF, 0xFF, 0, 0x0C, // PLL_PREDIV_BIT 0x35, 0x35, 0, 0, 0x0, 0x7, 0x7, 0, 0x2, // DAC_Sample_rate 0x1e, 0x1e, 0, 0, 0x0, 0x7, 0x7, 0, 0x2, // ADC_Sample_rate 0x4e, 0x4e, 0, 0, 0x0, 0x17, 0x1F, 0, 0x0F, // TX_datawidth 0x4b, 0x4b, 0, 0, 0x0, 0x17, 0x1F, 0, 0x0F, // RX_datawidth 0x15, 0x15, 0x0, 0x0, 0x0, 0xf, 0xf, 0, 0x0, // rx clk enable 0x15, 0x15, 0x4, 0x4, 0x0, 0xf, 0xf, 0, 0x0, // tx clk enable ]; ctrlSapmParamsSeqConfig = [ 0x27, 0x27, 5, 5, 0x00, 0x1, 0x1, 1, 0x00, //LPGA MIC -- connect MIC1 0x27, 0x27, 4, 4, 0x00, 0x1, 0x1, 1, 0x00, //RPGA MIC -- connect MIC2 0x2F, 0x2F, 2, 2, 0x00, 0x1, 0x1, 1, 0x00, //Speaker1 Switch -- connect speaker 0x2F, 0x2F, 1, 1, 0x00, 0x1, 0x1, 1, 0x00, //Headphone1 Switch -- connect hpl 0x2F, 0x2F, 0, 0, 0x00, 0x1, 0x1, 1, 0x00, //Headphone2 Switch -- connect hpr ]; /* reg is 0xFFFF: component has no sapm register bit sapmType, compNameIndex, reg, mask, shift, invert, kcontrolNews, kcontrolsNum */ sapmComponent = [ 10, 0, 0x18, 0x1, 7, 1, 0, 0, //ADCL 10, 1, 0x18, 0x1, 6, 1, 0, 0, //ADCR 11, 32, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //DAC1 11, 33, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //DAC2 11, 34, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //DAC3 6, 52, 0xFFFF, 0xFFFF, 0, 0, 3, 1, //SPKL PGA 6, 54, 0xFFFF, 0xFFFF, 0, 0, 4, 1, //HPL PGA 6, 55, 0xFFFF, 0xFFFF, 0, 0, 5, 1, //HPR PGA 15, 6, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //SPK 14, 10, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //HPL 14, 11, 0xFFFF, 0xFFFF, 0, 0, 0, 0, //HPR 6, 4, 0xFFFF, 0xFFFF, 6, 0, 1, 1, //LPGA 6, 5, 0xFFFF, 0xFFFF, 6, 0, 2, 1, //RPGA 13, 40, 0xFFFF, 0xFFFF, 6, 0, 0, 0, //MIC1 13, 41, 0x4d, 0x1, 1, 0, 0, 0 //MIC2 ]; /*array index, iface, mixer/mux, enable*/ sapmConfig = [ 0, 2, 0, 1, 1, 2, 0, 1, 24, 2, 0, 1, 28, 2, 0, 1, 29, 2, 0, 1 ]; } }

至此,dayu200的基本音频方案ADM的情况已经梳理清楚

编辑
2025-01-20
记录知识
0

openharmony有多种音频调试方案,这里介绍和解读一下openharmony的音频方案。

一、参考文档

根据openharmony在社区分享的文章《audio适配方案》,我们可以知道对于openharmony系统适配音频有四种方案,分别如下:

  • adm驱动适配
  • alsalib适配标准alsa
  • hdi to hidl对接
  • hdi对接vender hal
    对于上面四种方案,我相信大部分人第一次看到会有点犯懵,所以这里主要解释一下这四种方案的具体意思。从而理解openharmony是如何处理音频的。这四种方案的图示如下:

image.png

二、ADM适配方法

ADM的全称是audio driver mode,这是openharmony系统自身的一套音频框架,它仿照alsa的基本概念做了改造和裁剪,其基本思想还是基于alsa。所以如下

image.png 对比于alsa,如下

image.png 所以我们可以知道

ADM的的实现如下:

platform-----dai-----codec

这里platform实现了平台dma/cpu的驱动,dai实现了i2s的驱动,codec实现了具体声卡的驱动

alsa的实现如下:

platform-----dai-----codec

这里platform实现了平台dma/cpu的驱动,dai实现了i2s的驱动,codec实现了具体声卡的驱动

可以发现ADM的本质和ASoC完全相同,但如果我不解释,光看图,或许以为ADM与ASoC不太一致。

其主要原因是openharmony的框架图中,故意模糊了概念,而ASoC的框架更清晰能够一眼看懂

至于其他的,也大同小异

  • alsa的machine对比于ADM的card manager

  • alsa的dapm对于于ADM的sapm

  • alsa的controls对比ADM的control dispatch

  • alsa的pcm interface对比于ADM的stream dispatch

2.1 ADM我们需要做什么

根据上面的分析,我们可以知道ADM是改造的ALSA,所以对于ADM的开发,我们需要遵循ADM的框架,编写HDF driver,实现Codec驱动即可

三、alsa适配方法

对于alsa而言,驱动可以完全使用标准linux的ASoC框架,上层则配置使能alsalib即可,对于openharmony,根据其系统的设计,我们还需要适配openharmony的上层支撑,这里是supportlib的实现。

所以关键点有三个,如下:

3.1 内核走标准alsa

CONFIG_SOUND=y CONFIG_SND=y # CONFIG_DRIVERS_HDF_AUDIO is not set # CONFIG_DRIVERS_HDF_AUDIO_RK3568 is not set

3.2 内核调用库走alsalib

通过配置项drivers_peripheral_audio_feature_alsa_lib来控制代码的执行逻辑,如果是drivers_peripheral_audio_feature_alsa_lib=true,则默认使用alsalib的libasound库

具体代码路径如下:

drivers/peripheral/audio/hdi_service

3.3 系统调用库走alsa的实现

根据上面的方法,已经可以确保系统的音频走alsalib的libasound的了,但是openharmony系统需要能够使用音频,需要对音频的api做一下重实现,这里已经完成了,就是supportlibs,代码仓库位置如下:

drivers/peripheral/audio/supportlibs

这里文件情况如下:

adm_adapter alsa_adapter interfaces

可以知道,对于ADM,默认走adm_adapter,而对于alsa我们走的是alsa_adapter,其中interfaces对上层的抽象,提供了上层需要的api接口

对于上层需要的接口,简单介绍如下:

image.png 至此,openharmony走alsa的理论路径如下(自下向上):

ASoC---->libasound---->supportlibs(alsa_adapter)--→openharmony api

四、hdi to hidl方案

如果不太熟悉android的同学,我们在提到hidl,会不太清楚,再者,在《audio适配方案》中, 里面描述的是HDIL,这个东西没有任何的解释,所以我认为这是华为的人编写文档的时候的一个书写错误,习以为常就好啦。

这里提到的hidl是借鉴了安卓的思路,安卓在对于应用层之间,使用aidl,在对于设备抽象层之间,使用hidl。对于安卓,其实现方式如下(自上到下):

SystemApp--->Android Framework ---> HIDL Binder--->HIDL Service Manager----> HAL

所以openharmony也在借鉴安卓的思路,实现自己的hidl,故我们可以看到如下言论

image.png

对于openharmony的idl,对于文档如下:

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/IDL/idl-guidelines.md

目前实现的是应用和服务之间的通信方案,对比于安卓的aidl。

所以可以通过查找代码,根据audio的实现可以知道,如果需要实现hidl,则需要实现hidl的服务,类比于安卓的hidl service manager。该仓库应该在

drivers/peripheral/codec/hal/idl_service

但是目前看来,没有良好的实现和落地,还需要进一步发展。此方案不容易落地。未来可能是openharmony努力的目标。

五、hdi对接vendor hal方案

此方案的意图在于OEM自行根据HDI的接口自己实现,哪有一个操作系统说我提供上层api,下层自己实现的说法,故不现实。

根据此说法,应该实现路径如下(自低到上):

alsa/自己实现audio框架(openharmony不干)--->自己实现vendor hal(openharmony不干)--->vendor hal对接hdi api(openharmony上层api)

如果底层使用alsa,则第三章的方案中的alsalib和supportlibs需要自行实现成vendor hal,所以这属于一个异想天开的方案,当前阶段不必理会。

编辑
2025-01-20
记录知识
0

我们基于RK3568在众达板卡上开发了Openharmony,相关仓库通过repo管理,故代码可以直接repo拉取和编译,下文说明拉取方法和编译步骤

一、仓库地址

Openharmony的代码仓库地址在:https://gitlab2.kylin.com/sh-product-Embedded/openharmony,这些仓库包含我们基于Openharmony的修改内容

二、repo获取仓库

拉取命令如下

repo init -u https://gitlab2.kylin.com/sh-product-Embedded/openharmony/manifest.git -b OpenHarmony-4.0-Release -g ohos:standard --no-repo-verify

拉取时,如果出现如下错误,则可以两个方向排除

image.png

2.1 服务器上网认证

服务器需要认证,建议找服务器的owner私下聊天沟通一下,认证网络后,才能拉到gitee/openharmony的仓库代码

2.2 gitlab2证书异常

这种情况下是应该https/http拉取代码的ca证书不信任导致,这种情况需要设置宏如下:

export GIT_SSL_NO_VERIFY=1

三、同步代码

在repo init之后,就可以开始同步代码,如下命令

repo sync -c repo forall -c 'git lfs pull'

注意repo拉取git lfs可能因为网络问题出错,如果失败可以多尝试几次

代码同步之后可以发现如下几个目录正常即可

device/board/allgo/ device/board/allgo/mbrc3568a/kernel/linux_sdk/ vendor/allgo/

四、下载prebuilts

代码拉取完成后,需要拉取prebuilts如下:

build/prebuilts_download.sh

五、编译

编译之前需要添加白名单如下:

cd $sdk_path/build patch -p1 < device/board/allgo/mbrc3568a/build_whitelist_for_mbrc3568a.patch

然后执行编译脚本如下

./build.sh --product-name mbrc3568a --ccache --no-prebuilt-sdk

六、镜像位置

编译完成之后,镜像位置在:

out/mbrc3568a/packages/phone/images/
编辑
2025-01-20
记录知识
0

在编译Openharmony的时候,经常出现类似如下错误:exceptions.ohos_exception.OHOSException: subsystem name config incorrect in '/root/tf/new_openharmony/vendor/hihope/dayu210/ohos.build', build file subsystem name is product_hihope,configured subsystem name is product_dayu210。这是因为白名单设置问题导致,所以需要修改白名单,如下是思路

一、相关文件

对于白名单文件,涉及到的是compile_standard_whitelist.json,此文件在build仓库目录下。

二、"bundle_subsystem_error"

"bundle_subsystem_error"是用于检测是否存在子系统,在hb/services/loader.py中将其获取到bundle_subsystem_allow_list

bundle_subsystem_allow_list = compile_standard_allow_info.get("bundle_subsystem_error", [])

在hb/util/loader/load_ohos_build.py中会根据build_files来遍历subsystem名字,如果不匹配则报错。逻辑如下

if _subsystem_name != self._subsystem_name: is_allow = False for file_path in self._bundle_subsystem_allow_list: if _build_file.endswith(file_path): is_allow = True break if is_allow: print("warning: subsystem name config incorrect in '{}', build file subsystem name is {}," "configured subsystem name is {}.".format( _build_file, _subsystem_name, self._subsystem_name)) else: raise OHOSException("subsystem name config incorrect in '{}', build file subsystem name is {}," "configured subsystem name is {}.".format( _build_file, _subsystem_name, self._subsystem_name), 2014)

针对此,我们需要将我们的3588仓库添加到compile_standard_whitelist.json中,如下

+ "device/board/hihope/dayu210/ohos.build", + "vendor/hihope/dayu210/ohos.build"

三、"subsystem_components"

这里涉及子系统和组件的白名单,如果需要编译相关的组件,则需要添加,我这里添加如下:

@@ -32,6 +45,15 @@ "//device/soc/rockchip/rk3568/hardware/omx_il/component/video/enc:libomxvpu_enc", "//device/soc/rockchip/rk3568/hardware/omx_il/core:libOMX_Core", "//device/soc/rockchip/rk3568/hardware/omx_il/libOMXPlugin:libOMX_Pluginhw", + "//device/soc/rockchip/rk3568/hardware/npu:libnpu_vdi_impl", + "//device/soc/rockchip/rk3588/hardware/codec:libcodec_oem_interface", + "//device/soc/rockchip/rk3588/hardware/display:display_composer_vendor", + "//device/soc/rockchip/rk3588/hardware/display:display_gfx", + "//device/soc/rockchip/rk3588/hardware/display:libdisplay_buffer_vdi_impl", + "//device/soc/rockchip/rk3588/hardware/display:libdisplay_buffer_vendor", + "//device/soc/rockchip/rk3588/hardware/display:libdisplay_composer_vdi_impl", + "//device/soc/rockchip/rk3588/hardware/display:libhigbm_vendor", + "//device/soc/rockchip/rk3588/hardware/mpp/mpp-develop/mpp/legacy:rockchip_vpu",

out/rk3588/build_configs/parts_info/part_subsystem.json中的内容会在编译的时候解析,如果不匹配则按照如下逻辑出错

for component in components_name: if component['component'] in list(subsystem_compoents_whitelist_info.keys()): continue overrided_components_name = '{}_{}'.format(component['component'], 'override') if component['component'] in list(part_subsystem_component_info.keys()) \ or overrided_components_name in list(part_subsystem_component_info.keys()): if subsystem_name in list(part_subsystem_component_info.values()): continue if subsystem_name == component['component']: continue name = subsystem_name message = "find subsystem {} failed, please check it in {}.".format(subsystem_name, config_path) else: name = component['component'] message = "find component {} failed, please check it in {}.".format(component['component'], config_path) if name in subsystem_components_list: print(f"Warning: {message}") else: raise Exception(message)

这里涉及两方面,一个是vendor/hihope/dayu210/config.json,这是Openharmony产品需要打开的功能配置,会生成part_subsystem.json

另一个是仓库本身的bundle.json,这里会声明此仓库属于哪个subsystem和哪个component,所以如果不匹配就会出现

3.1 "gn_part_or_subsystem_error"

"gn_part_or_subsystem_error"用来判断build_configs/parts_info/part_subsystem.json的subsystems name和gn的BUILD.gn的subsystem是否对应,如果不对应,则按照下面逻辑报错

if subsystems_name is None or subsystems_name == '' or subsystems_name != args.subsystem_name: message = f"subsystem name or part name is incorrect, " \ f"target is {args.target_path}, subsystem name is {args.subsystem_name}, " \ f"part name is {args.part_name}" if args.target_path in bundle_file_allow_list: print(f"[0/0] warning: {message}") else: raise Exception(message)

3.2 "deps_added_external_part_module"

"deps_added_external_part_module"是依赖模块的白名单检测,对应BUILD.gn的deps,如果依赖存在异常,则如下逻辑报错

for dep in args.deps: dep_path = get_path_from_label(dep) if dep_path.find('third_party/rust/crates') != -1: continue if dep_path.find('third_party') != -1: dep_part = get_dep_part(dep_path, third_party_info) tips_info = "{} depend part {}, need set part deps {} info to".format( args.target_path, dep, dep_part) check_third_party_deps(args, dep_part, parts_deps_info, tips_info, third_deps_allow_list) continue match_flag = False for pattern in part_pattern: if dep_path.startswith(pattern): match_flag = True break if match_flag is False: message = "deps validation part_name: '{}', target: '{}', dep: '{}' failed!!!".format( args.part_name, args.target_path, dep) if args.target_path in deps_allow_list: print(f"[0/0] WARNING:{message}") else: raise Exception(message)

3.3 "external_deps_added_self_part_module"

"external_deps_added_self_part_module"是外部依赖的白名单检测,对应BUILD.gn的external_deps,如果依赖存在异常,则如下逻辑报错

if external_part_name == args.part_name: message = "{} in target {} is dependency within part {}, Need to used deps".format( external_part_name, args.target_path, args.part_name) if args.target_path in added_self_part_allow_list: print(f"[0/0] WARNING: {message}") return else: raise Exception(message)

3.4 "external_deps_bundle_not_add"

"external_deps_bundle_not_add"同上一致,但是他会按照如下逻辑报错

part_deps_info = parts_deps_info.get(args.part_name) if not part_deps_info: _warning_info = "{} {}.".format(_tips_info, args.part_name) elif not part_deps_info.get('components') or \ not external_part_name in part_deps_info.get('components'): _warning_info = "{} {}.".format(_tips_info, part_deps_info.get('build_config_file')) else: _warning_info = "" if _warning_info != "": if args.target_path in bundle_not_add_allow_list: print(f"[0/0] WARNING: {_warning_info}") else: raise Exception(_warning_info)

3.5 "third_deps_bundle_not_add"

"third_deps_bundle_not_add"是第三方依赖白名单,如果白名单没有第三方依赖库,则按照如下逻辑报错

def check_third_party_deps(args, dep_part, parts_deps_info, _tips_info, third_deps_allow_list): """check whether the three-party dependency is in the part declaration""" if args.part_name == dep_part: return part_deps_info = parts_deps_info.get(args.part_name) if not part_deps_info: _warning_info = f"{_tips_info} {args.part_name}." elif not part_deps_info.get('third_party') or \ not dep_part in part_deps_info.get('third_party'): _warning_info = f"{_tips_info} {part_deps_info.get('build_config_file')}." else: _warning_info = "" if _warning_info != "": if args.target_path in third_deps_allow_list: print(f"[0/0] WARNING: {_warning_info}") else: raise Exception(_warning_info) return

四、总结

至此,我们可以知道,如果我们需要添加新的平台,我们需要将其仓库都添加到白名单中。这样Openharmony才能正常编译

五、参考链接

https://forums.openharmony.cn/forum.php?mod=viewthread&tid=1864&extra=

编辑
2025-01-20
记录知识
0

Openharmony4.0的版本在内核上不支持rk8602的regulator驱动,这就导致两路cpu大核无法正常上电,npu无法正常上电,因为rk860x给cpu和npu提供了真实的regulator。

一、设备树

在rk3588s上,我们可以通过设备树知道cpub0和cpub1以及npu的供电情况如下:

&rknpu { rknpu-supply = <&vdd_npu_s0>; mem-supply = <&vdd_npu_mem_s0>; }; &cpu_b0 { cpu-supply = <&vdd_cpu_big0_s0>; mem-supply = <&vdd_cpu_big0_mem_s0>; }; &cpu_b2 { cpu-supply = <&vdd_cpu_big1_s0>; mem-supply = <&vdd_cpu_big1_mem_s0>; };

对于此,我们可以知道npu和cpu上都需要两路实际的电源供应,对于驱动,则需要如下获取regulator的引用节点

rknpu_supply = devm_regulator_get(&pdev->dev, "rknpu"); cpu_supply = devm_regulator_get(&pdev->dev, "cpu"); mem_supply = devm_regulator_get(&pdev->dev, "mem");

然后再通过上电时机将此路电源打开

ret = regulator_enable(rknpu_supply); ret = regulator_enable(cpu_supply); ret = regulator_enable(mem_supply);

基于此,我们可以知道,对于rk3588s而言,这类regulartor是核心的上电要求,所以我们不能忽略,如下可以找到regulator的设备树定义

vdd_cpu_big0_s0: vdd_cpu_big0_mem_s0: rk8602@42 { compatible = "rockchip,rk8602"; reg = <0x42>; vin-supply = <&vcc5v0_sys>; regulator-compatible = "rk860x-reg"; regulator-name = "vdd_cpu_big0_s0"; regulator-min-microvolt = <550000>; regulator-max-microvolt = <1050000>; regulator-ramp-delay = <2300>; rockchip,suspend-voltage-selector = <1>; regulator-boot-on; regulator-always-on; regulator-state-mem { regulator-off-in-suspend; }; }; vdd_cpu_big1_s0: vdd_cpu_big1_mem_s0: rk8603@43 { compatible = "rockchip,rk8603"; reg = <0x43>; vin-supply = <&vcc5v0_sys>; regulator-compatible = "rk860x-reg"; regulator-name = "vdd_cpu_big1_s0"; regulator-min-microvolt = <550000>; regulator-max-microvolt = <1050000>; regulator-ramp-delay = <2300>; rockchip,suspend-voltage-selector = <1>; regulator-boot-on; regulator-always-on; regulator-state-mem { regulator-off-in-suspend; }; }; vdd_npu_s0: vdd_npu_mem_s0: rk8602@42 { compatible = "rockchip,rk8602"; reg = <0x42>; vin-supply = <&vcc5v0_sys>; regulator-compatible = "rk860x-reg"; regulator-name = "vdd_npu_s0"; regulator-min-microvolt = <550000>; regulator-max-microvolt = <950000>; regulator-ramp-delay = <2300>; rockchip,suspend-voltage-selector = <1>; regulator-boot-on; regulator-always-on; regulator-state-mem { regulator-off-in-suspend; }; };

二、驱动

根据上述可以知道,我们必须要使能这个regulator电,并且此电的设备树节点是rk8602,rk8603.故需要移植驱动

针对此,可以找到驱动文件drivers/regulator/rk860x-regulator.c,其相关的compatible如下

static const struct of_device_id rk860x_dt_ids[] = { { .compatible = "rockchip,rk8600", .data = (void *)RK860X_CHIP_ID_00 }, { .compatible = "rockchip,rk8601", .data = (void *)RK860X_CHIP_ID_01 }, { .compatible = "rockchip,rk8602", .data = (void *)RK860X_CHIP_ID_02 }, { .compatible = "rockchip,rk8603", .data = (void *)RK860X_CHIP_ID_03 }, { } };

这里可以知道,一份驱动对应了多个compatible,可以实现多个节点的不同配置。这样我们可以确定移植此驱动即可。

对于vendor/drivers/regulator/Makefile 添加如下

obj-$(CONFIG_REGULATOR_RK860X) += rk860x-regulator.o

对于vendor/drivers/regulator/Kconfig 添加如下

config REGULATOR_RK860X tristate "Rockchip RK860X Regulator" depends on I2C select REGMAP_I2C help This driver supports Rockchip RK860X Digitally Programmable Buck Regulator. The RK860X is a step-down switching voltage regulator that delivers a digitally programmable output from an input voltage supply of 2.5V to 5.5V. The output voltage is programmed through an I2C interface.

对于arch/arm64/configs/rockchip_linux_defconfig添加如下

CONFIG_REGULATOR_RK860X=y

至此,两路cpu大核和npu的电即可正常输出