Android音頻驅動-ASOC之Machine

/* SoC card */
struct snd_soc_card {
    const char *name;
    const char *long_name;
    const char *driver_name;
    struct device *dev;
    struct snd_card *snd_card;
    struct module *owner;

    struct mutex mutex;
    struct mutex dapm_mutex;

    bool instantiated;

    int (*probe)(struct snd_soc_card *card);
    int (*late_probe)(struct snd_soc_card *card);
    int (*remove)(struct snd_soc_card *card);

    /* the pre and post PM functions are used to do any PM work before and
     * after the codec and DAI's do any PM work. */
    int (*suspend_pre)(struct snd_soc_card *card);
    int (*suspend_post)(struct snd_soc_card *card);
    int (*resume_pre)(struct snd_soc_card *card);
    int (*resume_post)(struct snd_soc_card *card);

    /* callbacks */
    int (*set_bias_level)(struct snd_soc_card *,
                  struct snd_soc_dapm_context *dapm,
                  enum snd_soc_bias_level level);
    int (*set_bias_level_post)(struct snd_soc_card *,
                   struct snd_soc_dapm_context *dapm,
                   enum snd_soc_bias_level level);

    long pmdown_time;

    /* CPU <--> Codec DAI links  */
    struct snd_soc_dai_link *dai_link;
    int num_links;
    struct snd_soc_pcm_runtime *rtd;
    int num_rtd;

    /* optional codec specific configuration */
    struct snd_soc_codec_conf *codec_conf;
    int num_configs;

    /*
     * optional auxiliary devices such as amplifiers or codecs with DAI
     * link unused
     */
    struct snd_soc_aux_dev *aux_dev;
    int num_aux_devs;
    struct snd_soc_pcm_runtime *rtd_aux;
    int num_aux_rtd;

    const struct snd_kcontrol_new *controls;
    int num_controls;

    /*
     * Card-specific routes and widgets.
     */
    const struct snd_soc_dapm_widget *dapm_widgets;
    int num_dapm_widgets;
    const struct snd_soc_dapm_route *dapm_routes;
    int num_dapm_routes;
    bool fully_routed;

    struct work_struct deferred_resume_work;

    /* lists of probed devices belonging to this card */
    struct list_head codec_dev_list;

    struct list_head widgets;
    struct list_head paths;
    struct list_head dapm_list;
    struct list_head dapm_dirty;

    /* Generic DAPM context for the card */
    struct snd_soc_dapm_context dapm;
    struct snd_soc_dapm_stats dapm_stats;
    struct snd_soc_dapm_update *update;

#ifdef CONFIG_DEBUG_FS
    struct dentry *debugfs_card_root;
    struct dentry *debugfs_pop_time;
#endif
    u32 pop_time;

    void *drvdata;
};
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_pcm_runtime {
    struct device *dev;
    struct snd_soc_card *card;
    struct snd_soc_dai_link *dai_link;
    struct mutex pcm_mutex;
    enum snd_soc_pcm_subclass pcm_subclass;
    struct snd_pcm_ops ops;

    unsigned int dev_registered:1;

    /* Dynamic PCM BE runtime data */
    struct snd_soc_dpcm_runtime dpcm[2];
    int fe_compr;

    long pmdown_time;
    unsigned char pop_wait:1;

    /* runtime devices */
    struct snd_pcm *pcm;
    struct snd_compr *compr;
    struct snd_soc_codec *codec;
    struct snd_soc_platform *platform;
    struct snd_soc_dai *codec_dai;
    struct snd_soc_dai *cpu_dai;
    struct snd_soc_component *component; /* Only valid for AUX dev rtds */

    struct snd_soc_dai **codec_dais;
    unsigned int num_codecs;

    struct delayed_work delayed_work;
#ifdef CONFIG_DEBUG_FS
    struct dentry *debugfs_dpcm_root;
    struct dentry *debugfs_dpcm_state;
#endif
};

mt_soc_machine.c

/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt_soc_dai_common[] = {
    /* FrontEnd DAI Links */
    {
     .name = "I2S0DL1OUTPUT",
     .stream_name = MT_SOC_I2SDL1_STREAM_NAME,//匹配pcm id
     .cpu_dai_name = MT_SOC_I2S0DL1_NAME,//匹配cpu dai driver
     .platform_name = MT_SOC_I2S0DL1_PCM,//匹配platform driver
     .codec_dai_name = MT_SOC_CODEC_I2S0TXDAI_NAME,//匹配codec dai driver
     .codec_name = MT_SOC_CODEC_NAME,//匹配codec driver
     .init = mt_soc_audio_init,
     .ops = &mt_machine_audio_ops,
     },
         ......
}
static struct snd_soc_card snd_soc_card_mt = {
    .name = "mt-snd-card",//聲卡設備
    .dai_link = mt_soc_dai_common,
    .num_links = ARRAY_SIZE(mt_soc_dai_common),
    .controls = mt_soc_controls,
    .num_controls = ARRAY_SIZE(mt_soc_controls),
};
static int __init mt_soc_snd_init(void)
{
    int ret;
    struct snd_soc_card *card = &snd_soc_card_mt;
    mt_snd_device = platform_device_alloc("soc-audio", -1);
    platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);
    ret = platform_device_add(mt_snd_device);
    return 0;
}

註冊聲卡驅動soc-core.c

static int __init snd_soc_init(void)
{
    snd_soc_util_init();

    return platform_driver_register(&soc_driver);
}

/* ASoC platform driver */
static struct platform_driver soc_driver = {
    .driver     = {
        .name       = "soc-audio",//聲卡驅動
        .owner      = THIS_MODULE,
        .pm     = &snd_soc_pm_ops,
    },
    .probe      = soc_probe,
    .remove     = soc_remove,
};
static int soc_probe(struct platform_device *pdev)
{
    struct snd_soc_card *card = platform_get_drvdata(pdev);//獲取聲卡
    card->dev = &pdev->dev;
    return snd_soc_register_card(card);
}
int snd_soc_register_card(struct snd_soc_card *card)
{
    int i, j, ret;
    for (i = 0; i < card->num_links; i++) {
        struct snd_soc_dai_link *link = &card->dai_link[i];
        ret = snd_soc_init_multicodec(card, link);//初始化dai_link管理的codec,獲得codec name和codec dai name
    }
    ......
    dev_set_drvdata(card->dev, card);//保存聲卡
    snd_soc_initialize_card_lists(card);

    card->rtd = devm_kzalloc(card->dev,
                 sizeof(struct snd_soc_pcm_runtime) *
                 (card->num_links + card->num_aux_devs),
                 GFP_KERNEL);//初始化runtime數組對象,和dai links數量對應

    card->num_rtd = 0;
    card->rtd_aux = &card->rtd[card->num_links];

    for (i = 0; i < card->num_links; i++) {
        card->rtd[i].card = card;
        card->rtd[i].dai_link = &card->dai_link[i];//每個runtime對應一個dai link對象
        card->rtd[i].codec_dais = devm_kzalloc(card->dev,
                    sizeof(struct snd_soc_dai *) *
                    (card->rtd[i].dai_link->num_codecs),
                    GFP_KERNEL);//初始化runtime的codec dai鏈表
    }

    for (i = 0; i < card->num_aux_devs; i++)
        card->rtd_aux[i].card = card;

    INIT_LIST_HEAD(&card->dapm_dirty);
    card->instantiated = 0;
    mutex_init(&card->mutex);
    mutex_init(&card->dapm_mutex);

    ret = snd_soc_instantiate_card(card);

    /* deactivate pins to sleep state */
    for (i = 0; i < card->num_rtd; i++) {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int j;

        for (j = 0; j < rtd->num_codecs; j++) {
            struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
            if (!codec_dai->active)
                pinctrl_pm_select_sleep_state(codec_dai->dev);
        }

        if (!cpu_dai->active)
            pinctrl_pm_select_sleep_state(cpu_dai->dev);
    }

    return ret;
}

static int snd_soc_init_multicodec(struct snd_soc_card *card,
                   struct snd_soc_dai_link *dai_link)
{
    /* Legacy codec/codec_dai link is a single entry in multicodec */
    if (dai_link->codec_name || dai_link->codec_of_node ||
        dai_link->codec_dai_name) {
        dai_link->num_codecs = 1;
        dai_link->codecs = devm_kzalloc(card->dev,
                sizeof(struct snd_soc_dai_link_component),
                GFP_KERNEL);//初始化codecs數組,其實只有一個對象
        dai_link->codecs[0].name = dai_link->codec_name;//用來匹配codec
        dai_link->codecs[0].of_node = dai_link->codec_of_node;
        dai_link->codecs[0].dai_name = dai_link->codec_dai_name;//用來匹配codec dai
    }
    return 0;
}
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
    struct snd_soc_codec *codec;
    struct snd_soc_dai_link *dai_link;
    int ret, i, order, dai_fmt;

    mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);

    /* bind DAIs */
    for (i = 0; i < card->num_links; i++) {
        ret = soc_bind_dai_link(card, i);//card->rtd對象綁定codec dai、codec、cpu dai、platform
    }

    /* bind aux_devs too */
    for (i = 0; i < card->num_aux_devs; i++) {
        ret = soc_bind_aux_dev(card, i);
    }

    /* initialize the register cache for each available codec */
    list_for_each_entry(codec, &codec_list, list) {
        if (codec->cache_init)
            continue;
        ret = snd_soc_init_codec_cache(codec);
    }

    /* card bind complete so register a sound card */
    ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
            card->owner, 0, &card->snd_card);

    card->dapm.bias_level = SND_SOC_BIAS_OFF;
    card->dapm.dev = card->dev;
    card->dapm.card = card;
    list_add(&card->dapm.list, &card->dapm_list);

    if (card->dapm_widgets)
        snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
                      card->num_dapm_widgets);

    /* initialise the sound card only once */
    if (card->probe) {
        ret = card->probe(card);
    }

    /* probe all components used by DAI links on this card */
    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
            order++) {
        for (i = 0; i < card->num_links; i++) {
            ret = soc_probe_link_components(card, i, order);
        }
    }

    /* probe all DAI links on this card */
    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
            order++) {
        for (i = 0; i < card->num_links; i++) {
            ret = soc_probe_link_dais(card, i, order);
        }
    }

    for (i = 0; i < card->num_aux_devs; i++) {
        ret = soc_probe_aux_dev(card, i);
    }

    snd_soc_dapm_link_dai_widgets(card);
    snd_soc_dapm_connect_dai_link_widgets(card);

    if (card->controls)
        snd_soc_add_card_controls(card, card->controls, card->num_controls);

    if (card->dapm_routes)
        snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
                    card->num_dapm_routes);

    for (i = 0; i < card->num_links; i++) {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
        dai_link = &card->dai_link[i];
        dai_fmt = dai_link->dai_fmt;

        if (dai_fmt) {
            struct snd_soc_dai **codec_dais = rtd->codec_dais;
            int j;

            for (j = 0; j < rtd->num_codecs; j++) {
                struct snd_soc_dai *codec_dai = codec_dais[j];

                ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
            }
        }

        /* If this is a regular CPU link there will be a platform */
        if (dai_fmt && (dai_link->platform_name || dai_link->platform_of_node)) {
            ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,dai_fmt);
        } else if (dai_fmt) {
            /* Flip the polarity for the "CPU" end */
            dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
            switch (dai_link->dai_fmt &
                SND_SOC_DAIFMT_MASTER_MASK) {
            case SND_SOC_DAIFMT_CBM_CFM:
                dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
                break;
            case SND_SOC_DAIFMT_CBM_CFS:
                dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
                break;
            case SND_SOC_DAIFMT_CBS_CFM:
                dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
                break;
            case SND_SOC_DAIFMT_CBS_CFS:
                dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
                break;
            }

            ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
                          dai_fmt);
        }
    }

    snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
         "%s", card->name);
    snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
         "%s", card->long_name ? card->long_name : card->name);
    snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
         "%s", card->driver_name ? card->driver_name : card->name);
    for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
        switch (card->snd_card->driver[i]) {
        case '_':
        case '-':
        case '\0':
            break;
        default:
            if (!isalnum(card->snd_card->driver[i]))
                card->snd_card->driver[i] = '_';
            break;
        }
    }

    if (card->late_probe) {
        ret = card->late_probe(card);
    }

    if (card->fully_routed)
        snd_soc_dapm_auto_nc_pins(card);

    snd_soc_dapm_new_widgets(card);

    ret = snd_card_register(card->snd_card);
    card->instantiated = 1;
    snd_soc_dapm_sync(&card->dapm);
    mutex_unlock(&card->mutex);

    return 0;
}
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
    struct snd_soc_dai_link *dai_link = &card->dai_link[num];
    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
    struct snd_soc_dai_link_component *codecs = dai_link->codecs;
    struct snd_soc_dai_link_component cpu_dai_component;
    struct snd_soc_dai **codec_dais = rtd->codec_dais;
    struct snd_soc_platform *platform;
    const char *platform_name;
    int i;

    cpu_dai_component.name = dai_link->cpu_name;
    cpu_dai_component.of_node = dai_link->cpu_of_node;
    cpu_dai_component.dai_name = dai_link->cpu_dai_name;//machine的cup dai name
    rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);//保存和machine匹配的cpu dai driver

    rtd->num_codecs = dai_link->num_codecs;

    /* Find CODEC from registered CODECs */
    for (i = 0; i < rtd->num_codecs; i++) {
        codec_dais[i] = snd_soc_find_dai(&codecs[i]);
    }

    /* Single codec links expect codec and codec_dai in runtime data */
    rtd->codec_dai = codec_dais[0];//保存和machine匹配的codec dai driver
    rtd->codec = rtd->codec_dai->codec;//保存和machine匹配的codec driver

    /* if there's no platform we match on the empty platform */
    platform_name = dai_link->platform_name;
    if (!platform_name && !dai_link->platform_of_node)
        platform_name = "snd-soc-dummy";

    /* find one from the set of registered platforms */
    list_for_each_entry(platform, &platform_list, list) {
        if (dai_link->platform_of_node) {
            if (platform->dev->of_node !=
                dai_link->platform_of_node)
                continue;
        } else {
            if (strcmp(platform->component.name, platform_name))
                continue;
        }

        rtd->platform = platform;//保存和machine匹配的platform driver
    }

    card->num_rtd++;

    return 0;
}
static struct snd_soc_dai *snd_soc_find_dai(
    const struct snd_soc_dai_link_component *dlc)
{
    struct snd_soc_component *component;
    struct snd_soc_dai *dai;

    /* Find CPU DAI from registered DAIs*/
    list_for_each_entry(component, &component_list, list) {
        if (dlc->of_node && component->dev->of_node != dlc->of_node)
            continue;
        if (dlc->name && strcmp(component->name, dlc->name))
            continue;
        list_for_each_entry(dai, &component->dai_list, list) {
            if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
                continue;

            return dai;
        }
    }

    return NULL;
}
發佈了40 篇原創文章 · 獲贊 16 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章