Linux ALSA驱动:Machine驱动(五)

Linux ALSA架构:Machine驱动(五)

一、Machine驱动简介

ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用之前的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。

二、Machine驱动

ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等,Machine驱动路径为以下所示:

#ifdef CONFIG_OF
static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
    {.compatible = "mediatek,mt2701-cs42448-machine",},
    {}
};
#endif

static struct platform_driver mt2701_cs42448_machine = {
    .driver = {
        .name = "mt2701-cs42448",
           #ifdef CONFIG_OF
           .of_match_table = mt2701_cs42448_machine_dt_match,
           #endif
    },
    .probe = mt2701_cs42448_machine_probe,
};

在设备树文件中有注册名为 “mediatek,mt2701-wm8960-machine” 的 platform device。

sound:sound {
        compatible = "mediatek,mt2701-cs42448-machine";
        mediatek,platform = <&afe>;
        /* CS42448 Machine name */
        audio-routing =
        "Line Out Jack", "AOUT1L",
        "Line Out Jack", "AOUT1R",
        "Line Out Jack", "AOUT2L",
        "Line Out Jack", "AOUT2R",
        "Line Out Jack", "AOUT3L",
        "Line Out Jack", "AOUT3R",
        "Line Out Jack", "AOUT4L",
        "Line Out Jack", "AOUT4R",
        "AIN1L", "AMIC",
        "AIN1R", "AMIC",
        "AIN2L", "Tuner In",
        "AIN2R", "Tuner In",
        "AIN3L", "Satellite Tuner In",
        "AIN3R", "Satellite Tuner In",
        "AIN3L", "AUX In",
        "AIN3R", "AUX In";
        mediatek,audio-codec = <&cs42448>;
        mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
        pinctrl-names = "default";
        pinctrl-0 = <&aud_pins_default>;
        i2s1-in-sel-gpio1 = <&pio 53 0>;
        i2s1-in-sel-gpio2 = <&pio 54 0>;
        status = "okay";
    };

当 platform driver & platform device 匹配之后则会调用 platform_driver 下的 probe() 函数。

static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
{
    struct snd_soc_card *card = &mt2701_cs42448_soc_card;
    int ret;
    int i;
    struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
    /* 申请私有数据结构体大小 */
    struct mt2701_cs42448_private *priv =
        devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
                 GFP_KERNEL);
    struct device *dev = &pdev->dev;
    struct snd_soc_dai_link *dai_link;

    if (!priv)
        return -ENOMEM;
    /* 从设备树中获取platform接口信息 */
    platform_node = of_parse_phandle(pdev->dev.of_node,
                     "mediatek,platform", 0);
    if (!platform_node) {
        dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
        return -EINVAL;
    }
    for_each_card_prelinks(card, i, dai_link) {
        if (dai_link->platforms->name)
            continue;
        dai_link->platforms->of_node = platform_node;
    }

    card->dev = dev;
    /* 从设备树中获取codec接口信息 */
    codec_node = of_parse_phandle(pdev->dev.of_node,
                      "mediatek,audio-codec", 0);
    if (!codec_node) {
        dev_err(&pdev->dev,
            "Property 'audio-codec' missing or invalid\n");
        return -EINVAL;
    }
    for_each_card_prelinks(card, i, dai_link) {
        if (dai_link->codecs->name)
            continue;
        dai_link->codecs->of_node = codec_node;
    }
    /* 从设备树中获取codec-bt接口信息 */
    codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
                         "mediatek,audio-codec-bt-mrg", 0);
    if (!codec_node_bt_mrg) {
        dev_err(&pdev->dev,
            "Property 'audio-codec-bt-mrg' missing or invalid\n");
        return -EINVAL;
    }
    mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codecs->of_node
                            = codec_node_bt_mrg;
    /* 设置Audio的路由信息 */
    ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
    if (ret) {
        dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
        return ret;
    }

    priv->i2s1_in_mux_gpio_sel_1 =
        of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
    if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
        ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
                    "i2s1_in_mux_gpio_sel_1");
        if (ret)
            dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
                 __func__, ret);
        gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
    }

    priv->i2s1_in_mux_gpio_sel_2 =
        of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
    if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
        ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
                    "i2s1_in_mux_gpio_sel_2");
        if (ret)
            dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
                 __func__, ret);
        gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
    }
    /* 设置私有数据 */
    snd_soc_card_set_drvdata(card, priv);
    /* 注册声卡 */
    ret = devm_snd_soc_register_card(&pdev->dev, card);

    return ret;
}

注册的 card 为mt2701_cs42448_soc_card,定义如下:

static struct snd_soc_card mt2701_cs42448_soc_card = {
    .name = "mt2701-cs42448",
    .owner = THIS_MODULE,
    .dai_link = mt2701_cs42448_dai_links,
    .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
    .controls = mt2701_cs42448_controls,
    .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
    .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
    .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
};

三、devm_snd_soc_register_card函数

调用devm_snd_soc_register_card函数注册声卡。

int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
{
    struct snd_soc_card **ptr;
    int ret;

    ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL);
    if (!ptr)
        return -ENOMEM;

    ret = snd_soc_register_card(card);
    if (ret == 0) {
        *ptr = card;
        devres_add(dev, ptr);
    } else {
        devres_free(ptr);
    }

    return ret;
}

接着调用snd_soc_register_card函数对card进行初始化。

int snd_soc_register_card(struct snd_soc_card *card)
{
    if (!card->name || !card->dev)
        return -EINVAL;

    dev_set_drvdata(card->dev, card);

    INIT_LIST_HEAD(&card->widgets);
    INIT_LIST_HEAD(&card->paths);
    INIT_LIST_HEAD(&card->dapm_list);
    INIT_LIST_HEAD(&card->aux_comp_list);
    INIT_LIST_HEAD(&card->component_dev_list);
    INIT_LIST_HEAD(&card->list);
    INIT_LIST_HEAD(&card->rtd_list);
    INIT_LIST_HEAD(&card->dapm_dirty);
    INIT_LIST_HEAD(&card->dobj_list);

    card->instantiated = 0;
    mutex_init(&card->mutex);
    mutex_init(&card->dapm_mutex);
    mutex_init(&card->pcm_mutex);
    spin_lock_init(&card->dpcm_lock);

    return snd_soc_bind_card(card);
}

在snd_soc_bind_card函数中实现注册相关的大部分。

static int snd_soc_bind_card(struct snd_soc_card *card)
{
    struct snd_soc_pcm_runtime *rtd;
    struct snd_soc_component *component;
    struct snd_soc_dai_link *dai_link;
    int ret, i;

    mutex_lock(&client_mutex);
    mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
    /* 初始化dapm,把dapm加入到card->dapm_list链表中 */
    snd_soc_dapm_init(&card->dapm, card, NULL);

    /* check whether any platform is ignore machine FE and using topology */
    soc_check_tplg_fes(card);

    /* bind aux_devs too */
    ret = soc_bind_aux_dev(card);
    if (ret < 0)
        goto probe_end;
    /**/
    /* add predefined DAI links to the list */
    card->num_rtd = 0;
    for_each_card_prelinks(card, i, dai_link) {
        ret = snd_soc_add_pcm_runtime(card, dai_link);
        if (ret < 0)
            goto probe_end;
    }

    /* 创建声卡实例,可详见声卡及设备章节 */
    ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
            card->owner, 0, &card->snd_card);
    if (ret < 0) {
        dev_err(card->dev,
            "ASoC: can't create sound card for card %s: %d\n",
            card->name, ret);
        goto probe_end;
    }

    soc_init_card_debugfs(card);

    soc_resume_init(card);
    /* Create card new widgets */
    ret = snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
                    card->num_dapm_widgets);
    if (ret < 0)
        goto probe_end;

    ret = snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
                    card->num_of_dapm_widgets);
    if (ret < 0)
        goto probe_end;

    /* initialise the sound card only once */
    ret = snd_soc_card_probe(card);
    if (ret < 0)
        goto probe_end;

    /* probe all components used by DAI links on this card */
    ret = soc_probe_link_components(card);
    if (ret < 0) {
        dev_err(card->dev,
            "ASoC: failed to instantiate card %d\n", ret);
        goto probe_end;
    }

    /* probe auxiliary components */
    ret = soc_probe_aux_devices(card);
    if (ret < 0) {
        dev_err(card->dev,
            "ASoC: failed to probe aux component %d\n", ret);
        goto probe_end;
    }

    /* probe all DAI links on this card */
    ret = soc_probe_link_dais(card);
    if (ret < 0) {
        dev_err(card->dev,
            "ASoC: failed to instantiate card %d\n", ret);
        goto probe_end;
    }

    for_each_card_rtds(card, rtd) {
        ret = soc_init_pcm_runtime(card, rtd);
        if (ret < 0)
            goto probe_end;
    }
    /* 调用 card dapm、route 相关操作 */
    snd_soc_dapm_link_dai_widgets(card);
    snd_soc_dapm_connect_dai_link_widgets(card);

    ret = snd_soc_add_card_controls(card, card->controls,
                    card->num_controls);
    if (ret < 0)
        goto probe_end;

    ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
                      card->num_dapm_routes);
    if (ret < 0) {
        if (card->disable_route_checks) {
            dev_info(card->dev,
                 "%s: disable_route_checks set, ignoring errors on add_routes\n",
                 __func__);
        } else {
            dev_err(card->dev,
                 "%s: snd_soc_dapm_add_routes failed: %d\n",
                 __func__, ret);
            goto probe_end;
        }
    }

    ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
                      card->num_of_dapm_routes);
    if (ret < 0)
        goto probe_end;

    /* try to set some sane longname if DMI is available */
    snd_soc_set_dmi_name(card, NULL);

    soc_setup_card_name(card->snd_card->shortname,
                card->name, NULL, 0);
    soc_setup_card_name(card->snd_card->longname,
                card->long_name, card->name, 0);
    soc_setup_card_name(card->snd_card->driver,
                card->driver_name, card->name, 1);

    if (card->components) {
        /* the current implementation of snd_component_add() accepts */
        /* multiple components in the string separated by space, */
        /* but the string collision (identical string) check might */
        /* not work correctly */
        ret = snd_component_add(card->snd_card, card->components);
        if (ret < 0) {
            dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n",
                card->name, ret);
            goto probe_end;
        }
    }

    ret = snd_soc_card_late_probe(card);
    if (ret < 0)
        goto probe_end;

    snd_soc_dapm_new_widgets(card);
    /* 注册声卡,可详见声卡及设备 */
    ret = snd_card_register(card->snd_card);
    if (ret < 0) {
        dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
                ret);
        goto probe_end;
    }

    card->instantiated = 1;
    dapm_mark_endpoints_dirty(card);
    snd_soc_dapm_sync(&card->dapm);

    /* deactivate pins to sleep state */
    for_each_card_components(card, component)
        if (!snd_soc_component_active(component))
            pinctrl_pm_select_sleep_state(component->dev);

probe_end:
    if (ret < 0)
        soc_cleanup_card_resources(card);

    mutex_unlock(&card->mutex);
    mutex_unlock(&client_mutex);

    return ret;
}
3.1 对dai_link操作

首先为每个dai_link调用snd_soc_add_pcm_runtime完成dai_link的初始化。

int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
                struct snd_soc_dai_link *dai_link)
{
    struct snd_soc_pcm_runtime *rtd;
    struct snd_soc_dai_link_component *codec, *platform, *cpu;
    struct snd_soc_component *component;
    int i, ret;

    lockdep_assert_held(&client_mutex);

    /* 若card中add_dai_link函数被定义,执行card->add_dai_link操作 */
    ret = snd_soc_card_add_dai_link(card, dai_link);
    if (ret < 0)
        return ret;

    if (dai_link->ignore)
        return 0;

    dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);

    ret = soc_dai_link_sanity_check(card, dai_link);
    if (ret < 0)
        return ret;
    /* 为该dai_link分配一个snd_soc_pcm_runtime实例rtd,加入到card->rtd_list */
    rtd = soc_new_pcm_runtime(card, dai_link);
    if (!rtd)
        return -ENOMEM;
    /* Find cpu_dai form cpu*/
    for_each_link_cpus(dai_link, i, cpu) {
        asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu);
        if (!asoc_rtd_to_cpu(rtd, i)) {
            dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
                 cpu->dai_name);
            goto _err_defer;
        }
        snd_soc_rtd_add_component(rtd, asoc_rtd_to_cpu(rtd, i)->component);
    }

    /* Find CODEC from registered CODECs */
    for_each_link_codecs(dai_link, i, codec) {
        asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec);
        if (!asoc_rtd_to_codec(rtd, i)) {
            dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
                 codec->dai_name);
            goto _err_defer;
        }
        snd_soc_rtd_add_component(rtd, asoc_rtd_to_codec(rtd, i)->component);
    }

    /* Find PLATFORM from registered PLATFORMs */
    for_each_link_platforms(dai_link, i, platform) {
        for_each_component(component) {
            if (!snd_soc_is_matching_component(platform, component))
                continue;
            snd_soc_rtd_add_component(rtd, component);
        }
    }

    return 0;

_err_defer:
    snd_soc_remove_pcm_runtime(card, rtd);
    return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime);

函数会为每个dai_link 分配一个 snd_soc_pcm_runtime 实例 rtd,在前面小节中已经介绍 platform & codec 驱动最终都会创建相应的 component 实例并插入到全局链表 component_list 中,此处则会根据 dai_link 参数匹配相应的 platform & code。根据 name 从 component中找到匹配的 dai_link,并将该 dai_link 对应的 component 插入到 rtd->component_list 链表中。

接着调用soc_probe_link_dais函数对每个dai_link驱动完成porbe。

static int soc_probe_link_dais(struct snd_soc_card *card)
{
    struct snd_soc_pcm_runtime *rtd;
    int order, ret;

    for_each_comp_order(order) {
        for_each_card_rtds(card, rtd) {

            dev_dbg(card->dev,
                "ASoC: probe %s dai link %d late %d\n",
                card->name, rtd->num, order);

            /* probe all rtd connected DAIs in good order */
            ret = snd_soc_pcm_dai_probe(rtd, order);
            if (ret)
                return ret;
        }
    }

    return 0;
}

最后,调用soc_init_pcm_runtime函数对rtd创建PCM设备。

static int soc_init_pcm_runtime(struct snd_soc_card *card,
                struct snd_soc_pcm_runtime *rtd)
{
    struct snd_soc_dai_link *dai_link = rtd->dai_link;
    struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    struct snd_soc_component *component;
    int ret, num, i;

    /* set default power off timeout */
    rtd->pmdown_time = pmdown_time;

    /* do machine specific initialization */
    ret = snd_soc_link_init(rtd);
    if (ret < 0)
        return ret;

    if (dai_link->dai_fmt) {
        ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
        if (ret)
            return ret;
    }

    /* add DPCM sysfs entries */
    soc_dpcm_debugfs_add(rtd);

    num = rtd->num;

    /*
     * most drivers will register their PCMs using DAI link ordering but
     * topology based drivers can use the DAI link id field to set PCM
     * device number and then use rtd + a base offset of the BEs.
     */
    for_each_rtd_components(rtd, i, component) {
        if (!component->driver->use_dai_pcm_id)
            continue;

        if (rtd->dai_link->no_pcm)
            num += component->driver->be_pcm_base;
        else
            num = rtd->dai_link->id;
    }

    /* create compress_device if possible */
    ret = snd_soc_dai_compress_new(cpu_dai, rtd, num);
    if (ret != -ENOTSUPP) {
        if (ret < 0)
            dev_err(card->dev, "ASoC: can't create compress %s\n",
                dai_link->stream_name);
        return ret;
    }

    /* create the pcm */
    ret = soc_new_pcm(rtd, num);
    if (ret < 0) {
        dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
            dai_link->stream_name, ret);
        return ret;
    }

    return snd_soc_pcm_dai_new(rtd);
}

用了soc_new_pcm()函数用于创建标准alsa驱动的pcm逻辑设备。

int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
    struct snd_soc_dai *codec_dai;
    struct snd_soc_dai *cpu_dai;
    struct snd_soc_component *component;
    struct snd_pcm *pcm;
    char new_name[64];
    int ret = 0, playback = 0, capture = 0;
    int stream;
    int i;

    if (rtd->dai_link->dynamic && rtd->num_cpus > 1) {
        dev_err(rtd->dev,
            "DPCM doesn't support Multi CPU for Front-Ends yet\n");
        return -EINVAL;
    }

    if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
        if (rtd->dai_link->dpcm_playback) {
            stream = SNDRV_PCM_STREAM_PLAYBACK;

            for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
                if (snd_soc_dai_stream_valid(cpu_dai, stream)) {
                    playback = 1;
                    break;
                }
            }

            if (!playback) {
                dev_err(rtd->card->dev,
                    "No CPU DAIs support playback for stream %s\n",
                    rtd->dai_link->stream_name);
                return -EINVAL;
            }
        }
        if (rtd->dai_link->dpcm_capture) {
            stream = SNDRV_PCM_STREAM_CAPTURE;

            for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
                if (snd_soc_dai_stream_valid(cpu_dai, stream)) {
                    capture = 1;
                    break;
                }
            }

            if (!capture) {
                dev_err(rtd->card->dev,
                    "No CPU DAIs support capture for stream %s\n",
                    rtd->dai_link->stream_name);
                return -EINVAL;
            }
        }
    } else {
        /* Adapt stream for codec2codec links */
        int cpu_capture = rtd->dai_link->params ?
            SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
        int cpu_playback = rtd->dai_link->params ?
            SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;

        for_each_rtd_codec_dais(rtd, i, codec_dai) {
            if (rtd->num_cpus == 1) {
                cpu_dai = asoc_rtd_to_cpu(rtd, 0);
            } else if (rtd->num_cpus == rtd->num_codecs) {
                cpu_dai = asoc_rtd_to_cpu(rtd, i);
            } else {
                dev_err(rtd->card->dev,
                    "N cpus to M codecs link is not supported yet\n");
                return -EINVAL;
            }

            if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
                snd_soc_dai_stream_valid(cpu_dai,   cpu_playback))
                playback = 1;
            if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
                snd_soc_dai_stream_valid(cpu_dai,   cpu_capture))
                capture = 1;
        }
    }

    if (rtd->dai_link->playback_only) {
        playback = 1;
        capture = 0;
    }

    if (rtd->dai_link->capture_only) {
        playback = 0;
        capture = 1;
    }

    /* PCM设备创建 */
    if (rtd->dai_link->params) {
        snprintf(new_name, sizeof(new_name), "codec2codec(%s)",
             rtd->dai_link->stream_name);

        ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
                       playback, capture, &pcm);
    } else if (rtd->dai_link->no_pcm) {
        snprintf(new_name, sizeof(new_name), "(%s)",
            rtd->dai_link->stream_name);

        ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
                playback, capture, &pcm);
    } else {
        if (rtd->dai_link->dynamic)
            snprintf(new_name, sizeof(new_name), "%s (*)",
                rtd->dai_link->stream_name);
        else
            snprintf(new_name, sizeof(new_name), "%s %s-%d",
                rtd->dai_link->stream_name,
                (rtd->num_codecs > 1) ?
                "multicodec" : asoc_rtd_to_codec(rtd, 0)->name, num);

        ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
            capture, &pcm);
    }
    if (ret < 0) {
        dev_err(rtd->card->dev, "ASoC: can't create pcm %s for dailink %s: %d\n",
            new_name, rtd->dai_link->name, ret);
        return ret;
    }
    dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);

    /* DAPM dai link stream work */
    if (rtd->dai_link->params)
        rtd->close_delayed_work_func = codec2codec_close_delayed_work;
    else
        rtd->close_delayed_work_func = snd_soc_close_delayed_work;

    pcm->nonatomic = rtd->dai_link->nonatomic;
    rtd->pcm = pcm;
    pcm->private_data = rtd;

    if (rtd->dai_link->no_pcm || rtd->dai_link->params) {
        if (playback)
            pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
        if (capture)
            pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
        goto out;
    }

    /* ASoC PCM operations */
    if (rtd->dai_link->dynamic) {
        rtd->ops.open        = dpcm_fe_dai_open;
        rtd->ops.hw_params    = dpcm_fe_dai_hw_params;
        rtd->ops.prepare    = dpcm_fe_dai_prepare;
        rtd->ops.trigger    = dpcm_fe_dai_trigger;
        rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
        rtd->ops.close        = dpcm_fe_dai_close;
        rtd->ops.pointer    = soc_pcm_pointer;
    } else {
        rtd->ops.open        = soc_pcm_open;
        rtd->ops.hw_params    = soc_pcm_hw_params;
        rtd->ops.prepare    = soc_pcm_prepare;
        rtd->ops.trigger    = soc_pcm_trigger;
        rtd->ops.hw_free    = soc_pcm_hw_free;
        rtd->ops.close        = soc_pcm_close;
        rtd->ops.pointer    = soc_pcm_pointer;
    }

    for_each_rtd_components(rtd, i, component) {
        const struct snd_soc_component_driver *drv = component->driver;

        if (drv->ioctl)
            rtd->ops.ioctl        = snd_soc_pcm_component_ioctl;
        if (drv->sync_stop)
            rtd->ops.sync_stop    = snd_soc_pcm_component_sync_stop;
        if (drv->copy_user)
            rtd->ops.copy_user    = snd_soc_pcm_component_copy_user;
        if (drv->page)
            rtd->ops.page        = snd_soc_pcm_component_page;
        if (drv->mmap)
            rtd->ops.mmap        = snd_soc_pcm_component_mmap;
    }

    if (playback)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);

    if (capture)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);

    ret = snd_soc_pcm_component_new(rtd);
    if (ret < 0) {
        dev_err(rtd->dev, "ASoC: pcm %s constructor failed for dailink %s: %d\n",
            new_name, rtd->dai_link->name, ret);
        return ret;
    }

    pcm->no_device_suspend = true;
out:
    dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n",
        (rtd->num_codecs > 1) ? "multicodec" : asoc_rtd_to_codec(rtd, 0)->name,
        (rtd->num_cpus > 1)   ? "multicpu"   : asoc_rtd_to_cpu(rtd, 0)->name);
    return ret;
}
3.2 对component操作

遍历 card->rtd_list 中的所有 rtd 调用 soc_probe_link_components() 函数,函数定义如下:

static int soc_probe_link_components(struct snd_soc_card *card)
{
    struct snd_soc_component *component;
    struct snd_soc_pcm_runtime *rtd;
    int i, ret, order;
    /* 遍历该 rtd->component_list, 为所有用到的 components 调用 soc_probe_component() 函数 */
    for_each_comp_order(order) {
        for_each_card_rtds(card, rtd) {
            for_each_rtd_components(rtd, i, component) {
                if (component->driver->probe_order != order)
                    continue;

                ret = soc_probe_component(card, component);
                if (ret < 0)
                    return ret;
            }
        }
    }

    return 0;
}

该函数会遍历该 rtd->component_list, 为所有用到的 components 调用 soc_probe_component() 函数,定义如下:

static int soc_probe_component(struct snd_soc_card *card,
                   struct snd_soc_component *component)
{
    struct snd_soc_dapm_context *dapm =
        snd_soc_component_get_dapm(component);  /* 获得dapm结构体 */
    struct snd_soc_dai *dai;
    int probed = 0;
    int ret;

    if (!strcmp(component->name, "snd-soc-dummy"))
        return 0;

    if (component->card) {
        if (component->card != card) {
            dev_err(component->dev,
                "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n",
                card->name, component->card->name);
            return -ENODEV;
        }
        return 0;
    }

    ret = snd_soc_component_module_get_when_probe(component);
    if (ret < 0)
        return ret;

    component->card = card;
    soc_set_name_prefix(card, component);

    soc_init_component_debugfs(component);

    snd_soc_dapm_init(dapm, card, component);
    /* 创建 component->driver->dai_widgets */
    ret = snd_soc_dapm_new_controls(dapm,
                    component->driver->dapm_widgets,
                    component->driver->num_dapm_widgets);

    if (ret != 0) {
        dev_err(component->dev,
            "Failed to create new controls %d\n", ret);
        goto err_probe;
    }
    /* 遍历 component->dai_list 创建 dai widgets */
    for_each_component_dais(component, dai) {
        ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
        if (ret != 0) {
            dev_err(component->dev,
                "Failed to create DAI widgets %d\n", ret);
            goto err_probe;
        }
    }
    /* 最终调用 component->driver->probe() 函数 */
    ret = snd_soc_component_probe(component);
    if (ret < 0) {
        dev_err(component->dev,
            "ASoC: failed to probe component %d\n", ret);
        goto err_probe;
    }
    WARN(dapm->idle_bias_off &&
         dapm->bias_level != SND_SOC_BIAS_OFF,
         "codec %s can not start from non-off bias with idle_bias_off==1\n",
         component->name);
    probed = 1;

    /*
     * machine specific init
     * see
     *    snd_soc_component_set_aux()
     */
    ret = snd_soc_component_init(component);
    if (ret < 0)
        goto err_probe;
    /* 创建 component->driver->controls & dapm_routes */
    ret = snd_soc_add_component_controls(component,
                         component->driver->controls,
                         component->driver->num_controls);
    if (ret < 0)
        goto err_probe;

    ret = snd_soc_dapm_add_routes(dapm,
                      component->driver->dapm_routes,
                      component->driver->num_dapm_routes);
    if (ret < 0) {
        if (card->disable_route_checks) {
            dev_info(card->dev,
                 "%s: disable_route_checks set, ignoring errors on add_routes\n",
                 __func__);
        } else {
            dev_err(card->dev,
                "%s: snd_soc_dapm_add_routes failed: %d\n",
                __func__, ret);
            goto err_probe;
        }
    }

    /* 将该 dapm text(即 component dapm text) 插入到 card->dapm_list 中 */
    list_add(&component->card_list, &card->component_dev_list);

err_probe:
    if (ret < 0)
        soc_remove_component(component, probed);

    return ret;
}

该函数主要是实现对匹配的 platform/codec component 涉及的 DAPM、kcontrol 相关操作(下一章节介绍)。

3.3 对card_control操作

snd_soc_add_card_controls会调用到snd_soc_add_controls函数来创建Control设备。

static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
    const struct snd_kcontrol_new *controls, int num_controls,
    const char *prefix, void *data)
{
    int err, i;

    for (i = 0; i < num_controls; i++) {
        const struct snd_kcontrol_new *control = &controls[i];

        err = snd_ctl_add(card, snd_soc_cnew(control, data,
                             control->name, prefix));
        if (err < 0) {
            dev_err(dev, "ASoC: Failed to add %s: %d\n",
                control->name, err);
            return err;
        }
    }

    return 0;
}

snd_soc_cnew会调用到snd_ctl_new1,详见前面章节。

3.4 对dapm操作

调用 card dapm、route 相关操作(详细见后面章节)

    snd_soc_dapm_init(&card->dapm, card, NULL);
    ret = snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
                    card->num_dapm_widgets);

    snd_soc_dapm_link_dai_widgets(card);
    snd_soc_dapm_connect_dai_link_widgets(card);    

    ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
                      card->num_dapm_routes);
    snd_soc_dapm_new_widgets(card);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值