之前分析了rk809的hdf驱动,可以知道rk809在Rk809DeviceInit的时候,需要对hcs的配置进行解析和设置,所以这里解析一下rk809的hcs配置
对于openharmony的配置,hcs的配置目录在
vendor/hihope/rk3568/hdf_config/
根据Openharmony audio(二) dayu200的音频方案的解析,已经简单的可以联系上hcs和hdf driver了。这里给出比较详细的说明
这里的重点主要将如下的hcs文件配置
device_info.hcs audio_config.hcs codec_config.hcs dai_config.hcs dma_config.hcs
这里面描述了我们使用的codec的hcs配置,如下
device_primary :: deviceNode { policy = 1; priority = 50; preload = 0; permission = 0666; moduleName = "CODEC_RK809"; serviceName = "codec_service_0"; deviceMatchAttr = "hdf_codec_driver_0"; }
通过上面的配置,可以对应到HDF的驱动文件rk809_codec_adapter.c
Openharmony audio(二) dayu200的音频方案已经提到了这个hcs用来描述ADM框架下的各类设备的名字。匹配的代码如下:
int32_t AudioFillConfigData(const struct HdfDeviceObject *device, struct AudioConfigData *configData) { const struct DeviceResourceNode *node = NULL; struct DeviceResourceIface *drsOps = NULL; ADM_LOG_DEBUG("Entry."); if (device == NULL || configData == NULL) { ADM_LOG_ERR("Input para check error"); return HDF_FAILURE; } node = device->property; if (node == NULL) { ADM_LOG_ERR("drs node is NULL."); return HDF_FAILURE; } drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); if (drsOps == NULL || drsOps->GetString == NULL) { ADM_LOG_ERR("AudioFillConfigData: invalid drs ops fail!"); return HDF_FAILURE; } (void)drsOps->GetString(node, "serviceName", &(configData->cardServiceName), 0); (void)drsOps->GetString(node, "codecName", &(configData->codecName), 0); (void)drsOps->GetString(node, "platformName", &(configData->platformName), 0); (void)drsOps->GetString(node, "cpuDaiName", &(configData->cpuDaiName), 0); (void)drsOps->GetString(node, "codecDaiName", &(configData->codecDaiName), 0); (void)drsOps->GetString(node, "dspName", &(configData->dspName), 0); (void)drsOps->GetString(node, "dspDaiName", &(configData->dspDaiName), 0); ADM_LOG_INFO("cardServiceName = %s", configData->cardServiceName); ADM_LOG_INFO("codecName = %s, codecDaiName = %s", configData->codecName, configData->codecDaiName); ADM_LOG_INFO("platformName = %s, cpuDaiNamei = %s", configData->platformName, configData->cpuDaiName); ADM_LOG_INFO("dspName = %s, dspDaiName = %s", configData->dspName, configData->dspDaiName); return HDF_SUCCESS; }
也就是说,填充了结构体AudioConfigData
struct AudioConfigData { const char *cardServiceName; const char *codecName; const char *platformName; const char *cpuDaiName; const char *codecDaiName; const char *dspName; const char *dspDaiName; };
这个结构体的填充,用作在"HDF_AUDIO"初始化时,通过一系列的Seek函数来找到对应的设备。其调用路径如下:
g_audioDriverEntry--->AudioDriverInit--->AudioCardInit--->AudioFillConfigData/AudioBindDaiLink--->AudioSeekPlatformDevice/AudioSeekCpuDaiDevice/AudioSeekCodecDevice
这里需要留意的是codecName = "codec_service_0"
这里描述就是基于RK809最关键的配置信息,主要如下:
对于hwInfo的数据如下:
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, ];
解析后的作用如下:
static int32_t AudioSetPortInfoConfigStub(const uint64_t *buf, struct AudioPortInfo *configData) { switch (buf[0]) { /* Playback/Captrue */ case PORT_OUT: /* Playback */ return AudioSetPortInfoConfig(buf, &configData->render); case PORT_IN: /* Captrue */ return AudioSetPortInfoConfig(buf, &configData->capture); default: ADM_LOG_ERR("portDirection = %llu element num failed", buf[0]); return HDF_FAILURE; } }
这里用作audio的参数默认配置,主要作用结构体如下:
/* SoC PCM stream information */ struct AudioPcmStream { uint64_t portDirection; /* SNDRV_PCM_FMTBIT_* */ uint64_t formats; /* SNDRV_PCM_RATE_* */ uint64_t rates; /* min rate */ uint64_t rateMin; /* max rate */ uint64_t rateMax; /* min channels */ uint64_t channelsMin; /* max channels */ uint64_t channelsMax; /* max buffer size */ uint64_t bufferBytesMax; /* min period size */ uint64_t periodBytesMin; /* max period size */ uint64_t periodBytesMax; /* min # of periods */ uint64_t periodsMin; /* max # of periods */ uint64_t periodsMax; };
此时如果用户层下发control dispatch,如果是AUDIODRV_CTRL_IOCTRL_ELEM_CARD,则获取这些信息给到用户。具体如下:
static int32_t WritePcmInfoToRspData(struct HdfSBuf *rspData, const struct AudioPcmStream *pcmInfo) { if (rspData == NULL || pcmInfo == NULL) { ADM_LOG_ERR("params rspData or pcmInfo is null."); return HDF_FAILURE; } if (pcmInfo->portDirection != PORT_IN && pcmInfo->portDirection != PORT_OUT) { ADM_LOG_DEBUG("pcmInfo->portDirection nonsupport PORT_IN or PORT_OUT"); return HDF_SUCCESS; } if (!HdfSbufWriteUint8(rspData, (uint8_t)pcmInfo->portDirection)) { ADM_LOG_ERR("Write response data portDirection=%llu failed!", pcmInfo->portDirection); return HDF_FAILURE; } return HDF_SUCCESS; }
寄存器配置在RK809上包含如下:
这里通过内核数组匹配如下:
static char *g_audioRegGroupName[AUDIO_GROUP_MAX] = { "resetSeqConfig", "initSeqConfig", "ctrlParamsSeqConfig", "ctrlParamsMuxSeqConfig", "ctrlSapmParamsSeqConfig", "ctrlSapmMuxParamsSeqConfig", "daiStartupSeqConfig", "daiParamsSeqConfig", "daiTriggerSeqConfig", "controlsConfig", "sapmComponent", "sapmConfig" };
代码解析如下:
switch (index) { case AUDIO_CTRL_CFG_GROUP: case AUDIO_SAPM_CFG_GROUP: ret = ParseAudioCtrlItem(parser, regCfgNode, group); break; case AUDIO_RSET_GROUP: case AUDIO_INIT_GROUP: ret = ParseAudioAddrItem(parser, regCfgNode, group); break; case AUDIO_DAI_PATAM_GROUP: case AUDIO_DAI_TRIGGER_GROUP: case AUDIO_CTRL_PATAM_GROUP: case AUDIO_CTRL_SAPM_PATAM_GROUP: case AUDIO_DAI_STARTUP_PATAM_GROUP: ret = ParseAudioRegItem(parser, regCfgNode, group); break; case AUDIO_CTRL_PATAM_MUX_GROUP: case AUDIO_CTRL_SAPM_PATAM_MUX_GROUP: ret = ParseAudioEnumRegItem(parser, regCfgNode, group); break; case AUDIO_SAPM_COMP_GROUP: ret = ParseAudioSapmItem(parser, regCfgNode, group); break; default: ADM_LOG_ERR("parse audio config index = %u not found!", index); return HDF_FAILURE; }
可以看到不同的类型的reg,按照不同的方式解析。这也就导致我们在修改和填写这些寄存器的时候,需要严格按照代码的方式来修改。
对于初始化寄存器,通过ParseAudioAddrItem解析,将其存放在addrCfgItem内,用作启动codec前的寄存器初始化操作,如下:
static int32_t ParseAudioAddrItem(const struct DeviceResourceIface *parser, const struct DeviceResourceNode *regNode, struct AudioRegCfgGroupNode* group) { int32_t step; int32_t index; uint32_t *buf = NULL; if (parser == NULL || regNode == NULL || group == NULL) { ADM_LOG_ERR("Input para check error."); return HDF_FAILURE; } buf = GetRegArray(parser, regNode, group, AUDIO_ADDR_CFG_INDEX_MAX); if (buf == NULL) { ADM_LOG_ERR("malloc reg array buf failed!"); return HDF_FAILURE; } group->addrCfgItem = (struct AudioAddrConfig*)OsalMemCalloc(group->itemNum * sizeof(*(group->addrCfgItem))); if (group->addrCfgItem == NULL) { OsalMemFree(buf); ADM_LOG_ERR("malloc audio addr config item failed!"); return HDF_ERR_MALLOC_FAIL; } for (index = 0; index < group->itemNum; ++index) { step = AUDIO_ADDR_CFG_INDEX_MAX * index; group->addrCfgItem[index].addr = buf[step + AUDIO_ADDR_CFG_REG_INDEX]; group->addrCfgItem[index].value = buf[step + AUDIO_ADDR_CFG_VALUE_INDEX]; } OsalMemFree(buf); return HDF_SUCCESS; }
此时,当codec初始化的时候,他将根据addrCfgItem来进行i2c的write参照,如下:
int32_t CodecDeviceInitRegConfig(const struct CodecDevice *device) { int32_t ret; uint32_t index; struct AudioAddrConfig *initCfg = NULL; struct AudioRegCfgGroupNode **regCfgGroup = NULL; if (device == NULL || device->devData == NULL || device->devData->Write == NULL) { AUDIO_DRIVER_LOG_ERR("param val is null."); return HDF_FAILURE; } regCfgGroup = device->devData->regCfgGroup; if (regCfgGroup == NULL || regCfgGroup[AUDIO_INIT_GROUP] == NULL) { AUDIO_DRIVER_LOG_ERR("regCfgGroup init group is null."); return HDF_FAILURE; } initCfg = regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem; if (initCfg == NULL) { AUDIO_DRIVER_LOG_ERR("initCfg is NULL."); return HDF_FAILURE; } for (index = 0; index < regCfgGroup[AUDIO_INIT_GROUP]->itemNum; index++) { ret = device->devData->Write(device, initCfg[index].addr, initCfg[index].value); if (ret != HDF_SUCCESS) { AUDIO_DRIVER_LOG_ERR("Write err regAddr: 0x%x.\n", initCfg[index].addr); return HDF_FAILURE; } OsalMSleep(COMM_WAIT_TIMES); } AUDIO_DRIVER_LOG_DEBUG("success."); return HDF_SUCCESS; }
控制配置通过ParseAudioCtrlItem来解析,将其存放在ctrlCfgItem,如下
static int32_t ParseAudioCtrlItem(const struct DeviceResourceIface *parser, const struct DeviceResourceNode *regNode, struct AudioRegCfgGroupNode* group) { int32_t step; int32_t index; uint32_t *buf = NULL; if (parser == NULL || regNode == NULL || group == NULL) { ADM_LOG_ERR("Input para check error"); return HDF_FAILURE; } buf = GetRegArray(parser, regNode, group, AUDIO_CTRL_CFG_INDEX_MAX); if (buf == NULL) { ADM_LOG_ERR("malloc reg array buf failed!"); return HDF_FAILURE; } group->ctrlCfgItem = (struct AudioControlConfig*)OsalMemCalloc(group->itemNum * sizeof(*(group->ctrlCfgItem))); if (group->ctrlCfgItem == NULL) { OsalMemFree(buf); ADM_LOG_ERR("malloc audio ctrl config item failed!"); return HDF_ERR_MALLOC_FAIL; } for (index = 0; index < group->itemNum; ++index) { step = AUDIO_CTRL_CFG_INDEX_MAX * index; group->ctrlCfgItem[index].arrayIndex = buf[step + AUDIO_CTRL_CFG_INDEX_INDEX]; group->ctrlCfgItem[index].iface = buf[step + AUDIO_CTRL_CFG_IFACE_INDEX]; group->ctrlCfgItem[index].type = buf[step + AUDIO_CTRL_CFG_TYPE_INDEX]; group->ctrlCfgItem[index].enable = buf[step + AUDIO_CTRL_CFG_ENABLE_INDEX]; } OsalMemFree(buf); return HDF_SUCCESS; }
无论对于controlsConfig还是sapmConfig,最后遵循kcontrols的设计思想,根据codec的mixer和mux将其存放在如下结构体数据中
codeData->controls
简要代码如下:
# Mixer codeData->controls[index].Get = AudioCodecGetCtrlOps; codeData->controls[index].Set = AudioCodecSetCtrlOps; audioSapmControls[index].Get = AudioCodecSapmGetCtrlOps; audioSapmControls[index].Set = AudioCodecSapmSetCtrlOps; #Muxer codeData->controls[index].Get = AudioCodecGetEnumCtrlOps; codeData->controls[index].Set = AudioCodecSetEnumCtrlOps; audioSapmControls[index].Get = AudioCodecSapmGetEnumCtrlOps; audioSapmControls[index].Set = AudioCodecSapmSetEnumCtrlOps;
这样,在上层调用WRITE时,可以调用ControlHostElemWrite
{AUDIODRV_CTRL_IOCTRL_ELEM_WRITE, ControlHostElemWrite},
在ControlHostElemWrite中,将通过controls调用对应的Set函数,如下:
result = kctrl->Set(kctrl, &elemValue); if (result != HDF_SUCCESS) { ADM_LOG_ERR("Get control value fail result=%d", result); return HDF_FAILURE; }
这些seq通过ParseAudioRegItem来解析,ParseAudioRegItem的实现如下:
static int32_t ParseAudioRegItem(const struct DeviceResourceIface *parser, const struct DeviceResourceNode *regNode, struct AudioRegCfgGroupNode* group) { int32_t step; int32_t index; int32_t *buf = NULL; if (group == NULL || parser == NULL || regNode == NULL) { ADM_LOG_ERR("Input para check error"); return HDF_FAILURE; } buf = GetRegArray(parser, regNode, group, AUDIO_REG_CFG_INDEX_MAX); if (buf == NULL) { ADM_LOG_ERR("malloc reg array buf failed!"); return HDF_FAILURE; } group->regCfgItem = (struct AudioMixerControl*)OsalMemCalloc(group->itemNum * sizeof(*(group->regCfgItem))); if (group->regCfgItem == NULL) { OsalMemFree(buf); ADM_LOG_ERR("malloc audio reg config item failed!"); return HDF_ERR_MALLOC_FAIL; } for (index = 0; index < group->itemNum; ++index) { step = AUDIO_REG_CFG_INDEX_MAX * index; group->regCfgItem[index].reg = buf[step + AUDIO_REG_CFG_REG_INDEX]; group->regCfgItem[index].rreg = buf[step + AUDIO_REG_CFG_RREG_INDEX]; group->regCfgItem[index].shift = buf[step + AUDIO_REG_CFG_SHIFT_INDEX]; group->regCfgItem[index].rshift = buf[step + AUDIO_REG_CFG_RSHIFT_INDEX]; group->regCfgItem[index].min = buf[step + AUDIO_REG_CFG_MIN_INDEX]; group->regCfgItem[index].max = buf[step + AUDIO_REG_CFG_MAX_INDEX]; group->regCfgItem[index].mask = buf[step + AUDIO_REG_CFG_MASK_INDEX]; group->regCfgItem[index].invert = buf[step + AUDIO_REG_CFG_INVERT_INDEX]; group->regCfgItem[index].value = buf[step + AUDIO_REG_CFG_VALUE_INDEX]; } OsalMemFree(buf); return HDF_SUCCESS; }
这里其实解析了Mixer Control配置,数据结构如下:
/* mixer control */ struct AudioMixerControl { uint32_t min; uint32_t max; int32_t platformMax; uint32_t mask; uint32_t reg; uint32_t rreg; /* right sound channel reg */ uint32_t shift; uint32_t rshift; /* right sound channel reg shift */ uint32_t invert; uint32_t value; };
这里其实就是mixer,顾名思义,这是混合的意思,也就说,这里描述的是整个codec用作mixer的寄存器配置。
所以很好理解的是,根据4.2.2中关于Kcontrols的解析,会填充mixer和mux的回调,因为这边只实现了mixer,所以回调都是走的mixer,也就是如下:
AudioCodecSetCtrlOps; AudioCodecSapmSetCtrlOps;
这两个回调的实现大致如下:
AudioUpdateCodecRegBits AudioCodecRegUpdate
说明白点就是,更新ctrlParamsSeqConfig/daiParamsSeqConfig/ctrlSapmParamsSeqConfig
中描述的寄存器中的位。
有必要看看sapmComponent的配置情况如下:
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 ];
component是通过ParseAudioSapmItem实现,其代码如下:
static int32_t ParseAudioSapmItem(const struct DeviceResourceIface *parser, const struct DeviceResourceNode *regNode, struct AudioRegCfgGroupNode* group) { int32_t step; int32_t index; uint32_t *buf = NULL; if (group == NULL || parser == NULL || regNode == NULL) { ADM_LOG_ERR("Input para check error"); return HDF_FAILURE; } buf = GetRegArray(parser, regNode, group, AUDIO_SAPM_COMP_INDEX_MAX); if (buf == NULL) { ADM_LOG_ERR("malloc reg array buf failed!"); return HDF_FAILURE; } group->sapmCompItem = (struct AudioSapmCtrlConfig*)OsalMemCalloc(group->itemNum * sizeof(*(group->sapmCompItem))); if (group->sapmCompItem == NULL) { OsalMemFree(buf); ADM_LOG_ERR("malloc audio reg config item failed!"); return HDF_ERR_MALLOC_FAIL; } for (index = 0; index < group->itemNum; ++index) { step = AUDIO_SAPM_COMP_INDEX_MAX * index; group->sapmCompItem[index].sapmType = buf[step + AUDIO_SAPM_COMP_INDEX_TYPE]; group->sapmCompItem[index].compNameIndex = buf[step + AUDIO_SAPM_COMP_INDEX_NAME]; group->sapmCompItem[index].reg = buf[step + AUDIO_SAPM_COMP_INDEX_REG]; group->sapmCompItem[index].mask = buf[step + AUDIO_SAPM_COMP_INDEX_MASK]; group->sapmCompItem[index].shift = buf[step + AUDIO_SAPM_COMP_INDEX_SHIFT]; group->sapmCompItem[index].invert = buf[step + AUDIO_SAPM_COMP_INDEX_INVERT]; group->sapmCompItem[index].kcontrolNews = buf[step + AUDIO_SAPM_COMP_INDEX_KCTL]; group->sapmCompItem[index].kcontrolsNum = buf[step + AUDIO_SAPM_COMP_INDEX_KCTLNUM]; } OsalMemFree(buf); return HDF_SUCCESS; }
对应的数据结构如下:
struct AudioSapmCtrlConfig { uint8_t sapmType; uint16_t compNameIndex; uint32_t reg; uint32_t mask; uint8_t shift; uint8_t invert; uint32_t kcontrolNews; uint32_t kcontrolsNum; };
这里有意思的是,sapmType和compNameIndex是一堆数字,这个数字需要从代码去找答案,如下:
sapmType值得是组件的功能,如下:
/* sapm widget types */ enum AudioSapmType { AUDIO_SAPM_INPUT = 0, /* 0 input pin */ AUDIO_SAPM_OUTPUT, /* 1 output pin */ AUDIO_SAPM_MUX, /* 2 selects 1 analog signal from many inputs */ AUDIO_SAPM_DEMUX, /* 3 connects the input to one of multiple outputs */ AUDIO_SAPM_VIRT_MUX, /* 4 virtual version of snd_soc_dapm_mux */ AUDIO_SAPM_VALUE_MUX, /* 5 selects 1 analog signal from many inputs */ AUDIO_SAPM_MIXER, /* 6 mixes several analog signals together */ AUDIO_SAPM_MIXER_NAMED_CTRL, /* 7 mixer with named controls */ AUDIO_SAPM_PGA, /* 8 programmable gain/attenuation (volume) */ AUDIO_SAPM_OUT_DRV, /* 9 output driver */ AUDIO_SAPM_ADC, /* 10 analog to digital converter */ AUDIO_SAPM_DAC, /* 11 digital to analog converter */ AUDIO_SAPM_MICBIAS, /* 12 microphone bias (power) */ AUDIO_SAPM_MIC, /* 13 microphone */ AUDIO_SAPM_HP, /* 14 headphones */ AUDIO_SAPM_SPK, /* 15 speaker */ AUDIO_SAPM_LINE, /* 16 line input/output */ AUDIO_SAPM_ANALOG_SWITCH, /* 17 analog switch */ AUDIO_SAPM_VMID, /* 18 codec bias/vmid - to minimise pops */ AUDIO_SAPM_PRE, /* 19 machine specific pre component - exec first */ AUDIO_SAPM_POST, /* 20 machine specific post component - exec last */ AUDIO_SAPM_SUPPLY, /* 21 power/clock supply */ AUDIO_SAPM_REGULATOR_SUPPLY, /* 22 external regulator */ AUDIO_SAPM_CLOCK_SUPPLY, /* 23 external clock */ AUDIO_SAPM_AIF_IN, /* 24 audio interface input */ AUDIO_SAPM_AIF_OUT, /* 25 audio interface output */ AUDIO_SAPM_SIGGEN, /* 26 signal generator */ AUDIO_SAPM_SINK, /* 27 */ };
而compNameIndex是预定义的组件名字,如下:
static char *g_audioSapmCompNameList[AUDIO_SAPM_COMP_NAME_LIST_MAX] = { "ADCL", "ADCR", "DACL", "DACR", // [0], [1] [2], [3] "LPGA", "RPGA", "SPKL", "SPKR", // [4], [5] [6], [7] "MIC", "LOUT", "HPL", "HPR", // [8], [9] [10], [11] "Stereo Mixer", "Line Mix", "Input Mixer", "Speaker Mix", // [12], [13] [14], [15] "Input Mux", "AuxOut Mux", "SPKL Mux", "SPKR Mux", // [16], [17] [18], [19] "AUXOUTL", "AUXOUTR", "LINEINL", "LINEINR", // [20], [21] [22], [23] "AUXINL", "AUXINR", "I2S Mix", "AuxI Mix", // [24], [25] [26], [27] "CaptureL Mix", "CaptureR Mix", "Mono1 Mixer", "Mono2 Mixer", // [28], [29] [30], [31] "DAC1", "DAC2", "DAC3", "DAC4", // [32], [33] [34], [35] "ADC1", "ADC2", "ADC3", "ADC4", // [36], [37] [38], [39] "MIC1", "MIC2", "MIC3", "MIC4", // [40], [41],[42], [43], "SPK1", "SPK2", "SPK3", "SPK4", // [44], [45],[46], [47], "DAC Mix", "DAC Mux", "ADC Mix", "ADC Mux", // [48], [49],[50], [51], "SPKL PGA", "SPKR PGA", "HPL PGA", "HPR PGA", // [52], [53],[54], [55], };
reg/mask/shift/invert好理解,分别是寄存器地址,掩码,偏移量,是否反转 但是kcontrolsNews,没使用,不清楚干什么用的,kcontrolsNum我们大于0即可。如下:
if (sapmComponent->kcontrolsNum > 0) { sapmComponent->kcontrols = OsalMemCalloc(sizeof(struct AudioKcontrol*) * sapmComponent->kcontrolsNum); if (sapmComponent->kcontrols == NULL) { ADM_LOG_ERR("malloc kcontrols fail!"); return HDF_FAILURE; } }
至此rk809下的hcs已经分析差不多了,我们可以发现,华为在设计ADM的时候,很大程度上借鉴了ASoC,但是代码的设计又远没有ASoC的巧妙,很多东西需要强行对应他的代码逻辑,而且一些功能的设计并不完美,我总结如下几个不合适的点。
hwInfo这里我理解应该和alsa的snd_pcm_info_user类似设计,再不济和驱动的snd_pcm_hw_constraint类似设计,原意是想预设可以通过上层调用获取到声卡的支持信息,但实际没使用
对于芯片的初始化过程,有如下两个可能需要留意的:
openharmony实现initSeqConfig直接初始化了寄存器,属于一种十分理想的思维,这就容易导致一些驱动在适配的时候,hdf没有提供一下良好的机制,需要做一些特殊的技巧性动作。
这一点对于linux驱动的reg_cache_default和set_bias_level的作用。
低级的程序员通常喜欢在编写代码的时候将一些含义模糊化,特别是定义一些数字,然后告诉你1代表什么,2代表什么,3代表什么。
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, ];
这里openharmony喜欢这样处理,在controlsConfig中的第一项array index,它代表了如下数组的下标
static char *g_audioDaiControlsList[AUDIO_CTRL_LIST_MAX] = { "Main Playback Volume", "Main Capture Volume", "Playback Mute", "Capture Mute", "Mic Left Gain", "Mic Right Gain", "External Codec Enable", "Internally Codec Enable", "Render Channel Mode", "Captrue Channel Mode" };
这时候controlsConfig的值,这里的0就不是0了,而是"Main Playback Volume",这里的9就不是9了,而是"Captrue Channel Mode"
而我们在阅读优秀的代码的时候,特别是linux代码,通常是以更简单,更让人易懂的方式告诉你,什么是什么,让人更容易开发,例如一个kcontrol的信号值,设置录音的禁音:
SOC_SINGLE("Capture Mute", ES8323_ADCCONTROL7, 2, 1, 0),
这里直接了当的告诉你,控件名"Capture Mute"是通过ES8323_ADCCONTROL7寄存器控制的第2位,其最大值为,不做反转。
通常我们在做配置化的时候,一个dts的配置没有顺序要求,只要描写规范即可,但是hcs就不一样了。openharmony定义了顺序,你修改就存在问题,里面暗藏匹配规则,例如ctrlSapmParamsSeqConfig
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 ];
这里定义了配置先是LPGA MIC/RPGA MIC/Speaker1 Switch/Headphone1 Switch/Headphone2 Switch。如果大家按照dts的思维,应该不会讲究顺序,也就是我把下面三行提到前面,如下可能也许是对的
ctrlSapmParamsSeqConfig = [ 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 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 ];
但是答案是错了,因为ADC代码强行绑定了ctrlSapmParamsSeqConfig需要和sapmConfig关联,这里sapmConfig如下:
/*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 ];
这里的0,1,24,28,29代表的"LPGA MIC Switch"/"RPGA MIC Switch"/"Speaker1 Switch"/"Headphone1 Switch"/"Headphone2 Switch"
然后代码如下:
for (index = 0; index < regCfgGroup[AUDIO_SAPM_CFG_GROUP]->itemNum; index++) { if (sapmCtrlItem[index].type == AUDIO_CONTROL_MIXER) { audioSapmControls[index].iface = sapmCtrlItem[index].iface; audioSapmControls[index].name = g_audioSapmCfgNameList[sapmCtrlItem[index].arrayIndex]; audioSapmControls[index].privateValue = (unsigned long)(uintptr_t)(void*)(&ctlSapmRegCfgItem[index]); audioSapmControls[index].Info = AudioInfoCtrlOps; audioSapmControls[index].Get = AudioCodecSapmGetCtrlOps; audioSapmControls[index].Set = AudioCodecSapmSetCtrlOps; } else if (sapmCtrlItem[index].type == AUDIO_CONTROL_MUX) { audioSapmControls[index].iface = sapmCtrlItem[index].iface; audioSapmControls[index].name = g_audioSapmCfgNameList[sapmCtrlItem[index].arrayIndex]; audioSapmControls[index].privateValue = (unsigned long)(uintptr_t)(void*)(&ctlRegEnumCfgItem[index]); audioSapmControls[index].Info = AudioInfoEnumCtrlOps; audioSapmControls[index].Get = AudioCodecSapmGetEnumCtrlOps; audioSapmControls[index].Set = AudioCodecSapmSetEnumCtrlOps; } }
这里遍历的index是要和sapmCtrlItem[index].arrayIndex完全一致,这就导致ctrlSapmParamsSeqConfig要和sapmConfig顺序一致。
如果不是写这个代码的人本人知道这么做,如果不熟读ADM代码的话,全世界的人都不知道需要这么配。
相比于linux的dts,两个属性的值,通过of的接口,不会存在关联性
还有很多错误,这里就不提了,上面的错误实实在在影响到大家开发,这里提出来为了避免下一个人掉坑