OpenHarmony音频详解--ADM驱动框架介绍(4)

本文介绍了音频设备初始化过程,涉及AudioCardInit函数,通过解析HCS配置文件获取声卡驱动服务名,如codec、dai、dma,并通过AudioBindDaiLink进行绑定,初始化链表和硬件设备,最后将声卡注册到相应设备列表中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前文我们讲到了AudioCardInit函数,这个函数是初始化函数,首先我们看一下源码

static int32_t AudioCardInit(struct HdfDeviceObject *device, struct AudioHost *audioHost)
{
    int32_t ret;
    struct AudioCard *audioCard = NULL;
    if (device == NULL || audioHost == NULL) {
        ADM_LOG_ERR("device or audioHost is NULL.");
        return HDF_FAILURE;
    }

    audioCard = (struct AudioCard *)OsalMemCalloc(sizeof(*audioCard));
    if (audioCard == NULL) {
        ADM_LOG_ERR("Malloc audioCard fail!");
        return HDF_FAILURE;
    }
    audioHost->priv = audioCard;
    /* Get node information through HCS file */
    ret = AudioFillConfigData(device, &(audioCard->configData));
    if (ret != HDF_SUCCESS) {
        ADM_LOG_ERR("AudioFillConfigData fail ret=%d", ret);
        return HDF_ERR_IO;
    }

    audioCard->device = device;
    audioCard->standbyMode = AUDIO_SAPM_TURN_STANDBY_LATER;

    /* Bind specific codec、platform and dai device */
    ret = AudioBindDaiLink(audioCard, &(audioCard->configData));
    if (ret != HDF_SUCCESS) {
        ADM_LOG_ERR("AudioBindDaiLink fail ret=%d", ret);
        return HDF_FAILURE;
    }
    if (audioCard->rtd == NULL || (!audioCard->rtd->complete)) {
        ADM_LOG_ERR("AudioBindDaiLink fail!");
        return HDF_ERR_IO;
    }

    /* Initialize controls list */
    DListHeadInit(&audioCard->controls);
    DListHeadInit(&audioCard->components);
    DListHeadInit(&audioCard->paths);
    DListHeadInit(&audioCard->sapmDirty);
    /* Initialize hardware device */
    ret = AudioInitDaiLink(audioCard);
    if (ret != HDF_SUCCESS) {
        ADM_LOG_ERR("AudioInitDaiLink fail ret=%d", ret);
        return HDF_ERR_IO;
    }

    /* sound card added to list */
    DListInsertHead(&audioCard->list, &cardManager);

    return HDF_SUCCESS;
}

首先我们看一下这个函数AudioFillConfigData,这个函数就是解析声卡配置文件audio_config.hcs,解析此声卡包含的驱动服务名称,包括codec、dai、dma服务代码如下。然后调用AudioBindDaiLink根据找到驱动服务名称找到相应的驱动服务和声卡进行绑定,接着是一些链表的初始化,这些链表包括控制链表、电源管理的组件链表和声卡的通路链表等,最后调用AudioInitDaiLink将声卡对应的codec、dai、dma驱动进行初始化,最后再将此声卡插入到声卡链表中。

    serviceRet = drsOps->GetString(node, "serviceName", &(configData->cardServiceName), 0);
    codecRet = drsOps->GetString(node, "codecName", &(configData->codecName), 0);
    platformRet = drsOps->GetString(node, "platformName", &(configData->platformName), 0);
    cpuRet = drsOps->GetString(node, "cpuDaiName", &(configData->cpuDaiName), 0);
    codeDaiRet = drsOps->GetString(node, "codecDaiName", &(configData->codecDaiName), 0);
    dspRet = drsOps->GetString(node, "dspName", &(configData->dspName), 0);
    dspDaiRet = drsOps->GetString(node, "dspDaiName", &(configData->dspDaiName), 0);

然后我们看一下AudioBindDaiLink函数,这个函数的作用就是将codec、dai、dma驱动和声卡绑定,上面那个函数找到了配置文件中的声卡对应的驱动名称这里做个绑定,源码如下:

int32_t AudioBindDaiLink(struct AudioCard *audioCard, const struct AudioConfigData *configData)
{
    if (audioCard == NULL) {
        ADM_LOG_ERR("Input params check error: audioCard is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }
    if (configData == NULL) {
        ADM_LOG_ERR("Input params check error: configData is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }

    audioCard->rtd = (struct AudioRuntimeDeivces *)OsalMemCalloc(sizeof(struct AudioRuntimeDeivces));
    if (audioCard->rtd == NULL) {
        ADM_LOG_ERR("Malloc audioCard->rtd fail!");
        return HDF_ERR_MALLOC_FAIL;
    }

    audioCard->rtd->complete = AUDIO_DAI_LINK_UNCOMPLETE;
    if (AudioSeekPlatformDevice(audioCard->rtd, configData) == HDF_SUCCESS) {
        ADM_LOG_DEBUG("PLATFORM [%s] is registered!", configData->platformName);
    }
    if (AudioSeekCpuDaiDevice(audioCard->rtd, configData) == HDF_SUCCESS) {
        ADM_LOG_DEBUG("CPU DAI [%s] is registered!", configData->cpuDaiName);
    }
    if (AudioSeekCodecDevice(audioCard->rtd, configData) == HDF_SUCCESS) {
        ADM_LOG_DEBUG("CODEC [%s] is registered!", configData->codecName);
    }
    if (AudioSeekDspDevice(audioCard->rtd, configData) == HDF_SUCCESS) {
        ADM_LOG_DEBUG("CODEC [%s] is registered!", configData->dspName);
    }
    audioCard->rtd->complete = AUDIO_DAI_LINK_COMPLETE;
    ADM_LOG_DEBUG("All devices register complete!");

    return HDF_SUCCESS;
}

这块代码可以看到调用了很多seek函数,这些seek函数的功能是在相应的驱动链表中找到此声卡对应的驱动服务,这些驱动服务链表是在系统驱动的时候各个驱动注册进去的。我们就以codec为例看看他是怎么注册的。

下面是一个codec的init函数,在系统启动的时候会调用。

static int32_t Rk809DriverInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct regmap_config codecRegmapCfg = getCodecRegmap();
    struct platform_device *codeDev = GetCodecPlatformDevice();
    struct rk808 *rk808 = NULL;
    if (!codeDev) {
        AUDIO_DEVICE_LOG_ERR("codeDev not ready");
        return HDF_FAILURE;
    }
    g_chip = devm_kzalloc(&codeDev->dev, sizeof(struct Rk809ChipData), GFP_KERNEL);
    if (!g_chip) {
        AUDIO_DEVICE_LOG_ERR("devm_kzalloc for g_chip failed!");
        return HDF_ERR_MALLOC_FAIL;
    }
    g_chip->codec = g_rk809Data;
    g_chip->dai = g_rk809DaiData;
    platform_set_drvdata(codeDev, g_chip);
    g_chip->pdev = codeDev;
    rk808 = dev_get_drvdata(g_chip->pdev->dev.parent);
    if (!rk808) {
        return HDF_FAILURE;
    }

    g_chip->regmap = devm_regmap_init_i2c(rk808->i2c, &codecRegmapCfg);
    if (IS_ERR(g_chip->regmap)) {
        AUDIO_DEVICE_LOG_ERR("failed to allocate regmap: %ld\n", PTR_ERR(g_chip->regmap));
        return HDF_FAILURE;
    }

    if (CodecDaiGetPortConfigInfo(device, &g_rk809DaiData) != HDF_SUCCESS) {
        return HDF_FAILURE;
    }

    if (CodecGetConfigInfo(device, &g_rk809Data) !=  HDF_SUCCESS) {
        return HDF_FAILURE;
    }
    if (CodecSetConfigInfoOfControls(&g_rk809Data,  &g_rk809DaiData) != HDF_SUCCESS) {
        return HDF_FAILURE;
    }
    ret = CodecGetServiceName(device, &(g_rk809Data.drvCodecName));
    if (ret != HDF_SUCCESS) {
        return ret;
    }
    ret = CodecGetDaiName(device,  &(g_rk809DaiData.drvDaiName));
    if (ret != HDF_SUCCESS) {
        return HDF_FAILURE;
    }
    OsalMutexInit(&g_rk809Data.mutex);
    OsalMutexInit(&g_rk809DaiData.mutex);
    ret = AudioRegisterCodec(device, &g_rk809Data, &g_rk809DaiData);
    if (ret != HDF_SUCCESS) {
        return ret;
    }
    return HDF_SUCCESS;
}

这个函数的功能就是做codec数据的初始化,通过解析hcs配置文件解析来我们codec私有HCS文件中的配置信息。最后会调用AudioRegisterCodec函数将此codec注册到codec设备链表中。

我们一起来看一下AudioRegisterCodec源码:

int32_t AudioRegisterCodec(struct HdfDeviceObject *device, struct CodecData *codecData, struct DaiData *daiData)
{
    struct CodecDevice *codec = NULL;
    int32_t ret;

    if (device == NULL) {
        ADM_LOG_ERR("Input params check error: device is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }
    if (codecData == NULL) {
        ADM_LOG_ERR("Input params check error: codecData is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }
    if (daiData == NULL) {
        ADM_LOG_ERR("Input params check error: daiData is NULL.");
        return HDF_ERR_INVALID_OBJECT;
    }

    codec = (struct CodecDevice *)OsalMemCalloc(sizeof(*codec));
    if (codec == NULL) {
        ADM_LOG_ERR("Malloc codec device fail!");
        return HDF_ERR_MALLOC_FAIL;
    }

    codec->devCodecName = codecData->drvCodecName;
    codec->devData = codecData;
    codec->device = device;

    ret = AudioSocRegisterDai(device, daiData);
    if (ret != HDF_SUCCESS) {
        OsalIoUnmap((void *)((uintptr_t)(codec->devData->virtualAddress)));
        OsalMemFree(codec);
        ADM_LOG_ERR("Register dai device fail ret=%d", ret);
        return HDF_ERR_IO;
    }
    DListInsertHead(&codec->list, &codecController);
    ADM_LOG_INFO("Register [%s] success.", codec->devCodecName);

    return HDF_SUCCESS;
}

AudioRegisterCodec这个函数最终会调用DListInsertHead(&codec->list, &codecController);将codec插入到codecController链表中。声卡初始化的时候就会遍历此链表找到相应的codec。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天夏已微凉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值