ALSA driver---register CPU DAI


參考:

https://elixir.bootlin.com/linux/v4.9.218/source/sound/soc/soc-core.c#L3034

https://blog.csdn.net/DroidPhone/article/details/7316061

 

CPU DAI通過調用snd_soc_register_component進行注冊。

在snd_soc_register_component函數內主要創建和分配snd_soc_component結構體的內存,將snd_soc_component_driver 和snd_soc_dai_driver注冊到創建的component里,將創建的component加到全局列表component_list中,方便后續注冊Machine driver時調用到。

int snd_soc_register_component(struct device *dev,
                   const struct snd_soc_component_driver *cmpnt_drv,
                   struct snd_soc_dai_driver *dai_drv,
                   int num_dai)
{
    struct snd_soc_component *cmpnt;
    int ret;

    cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);
    if (!cmpnt) {
        dev_err(dev, "ASoC: Failed to allocate memory\n");
        return -ENOMEM;
    }

    ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);
    if (ret)
        goto err_free;

    cmpnt->ignore_pmdown_time = true;
    cmpnt->registered_as_component = true;

    ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);
    if (ret < 0) {
        dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret);
        goto err_cleanup;
    }

    snd_soc_component_add(cmpnt);
    return 0;

err_cleanup:
    snd_soc_component_cleanup(cmpnt);
err_free:
    kfree(cmpnt);
    return ret;
}

1.首先分配snd_soc_component的內存。

2.調用snd_soc_component_initiallize來初始化component, 主要使用snd_soc_component_driver來初始化component。

snd_soc_component_driver結構體如下,主要包含controls, dapm_widget, dapm_routes,在CPU DAI中,dapm主要描述了FE DAI和BE DAI是如何link的。

/* component interface */
struct snd_soc_component_driver {
    const char *name;

    /* Default control and setup, added after probe() is run */
    const struct snd_kcontrol_new *controls;
    unsigned int num_controls;
    const struct snd_soc_dapm_widget *dapm_widgets;
    unsigned int num_dapm_widgets;
    const struct snd_soc_dapm_route *dapm_routes;
    unsigned int num_dapm_routes;

    int (*probe)(struct snd_soc_component *);
    void (*remove)(struct snd_soc_component *);

    /* DT */
    int (*of_xlate_dai_name)(struct snd_soc_component *component,
                 struct of_phandle_args *args,
                 const char **dai_name);
    void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
        int subseq);
    int (*stream_event)(struct snd_soc_component *, int event);

    /* probe ordering - for components with runtime dependencies */
    int probe_order;
    int remove_order;
};

具體的初始化過程如下:將component driver賦值給component的driver成員,使用component driver dapm_widget和dapm_route賦值給component相應的成員,初始化component的dai_list.

static int snd_soc_component_initialize(struct snd_soc_component *component,
    const struct snd_soc_component_driver *driver, struct device *dev)
{
    struct snd_soc_dapm_context *dapm;

    component->name = fmt_single_name(dev, &component->id);
    if (!component->name) {
        dev_err(dev, "ASoC: Failed to allocate name\n");
        return -ENOMEM;
    }

    component->dev = dev;
    component->driver = driver;
    component->probe = component->driver->probe;
    component->remove = component->driver->remove;

    dapm = &component->dapm;
    dapm->dev = dev;
    dapm->component = component;
    dapm->bias_level = SND_SOC_BIAS_OFF;
    dapm->idle_bias_off = true;
    if (driver->seq_notifier)
        dapm->seq_notifier = snd_soc_component_seq_notifier;
    if (driver->stream_event)
        dapm->stream_event = snd_soc_component_stream_event;

    component->controls = driver->controls;
    component->num_controls = driver->num_controls;
    component->dapm_widgets = driver->dapm_widgets;
    component->num_dapm_widgets = driver->num_dapm_widgets;
    component->dapm_routes = driver->dapm_routes;
    component->num_dapm_routes = driver->num_dapm_routes;

    INIT_LIST_HEAD(&component->dai_list);
    mutex_init(&component->io_mutex);

    return 0;
}

3.調用snd_soc_register_dais來注冊CPU DAI

static int snd_soc_register_dais(struct snd_soc_component *component,
    struct snd_soc_dai_driver *dai_drv, size_t count,
    bool legacy_dai_naming)
{
    struct device *dev = component->dev;
    struct snd_soc_dai *dai;
    unsigned int i;
    int ret;

    dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);

    component->dai_drv = dai_drv;

    for (i = 0; i < count; i++) {

        dai = soc_add_dai(component, dai_drv + i,
                count == 1 && legacy_dai_naming);
        if (dai == NULL) {
            ret = -ENOMEM;
            goto err;
        }
    }

    return 0;

err:
    snd_soc_unregister_dais(component);

    return ret;
}

在snd_soc_register_dais函數內遍歷snd_soc_dai_driver 列表,通過soc_add_dai將每個dai都添加到component的dai_list。

snd_soc_dai_driver的結構體如下:

struct snd_soc_dai_driver {
    /* DAI description */
    const char *name;
    unsigned int id;
    unsigned int base;
    struct snd_soc_dobj dobj;

    /* DAI driver callbacks */
    int (*probe)(struct snd_soc_dai *dai);
    int (*remove)(struct snd_soc_dai *dai);
    int (*suspend)(struct snd_soc_dai *dai);
    int (*resume)(struct snd_soc_dai *dai);
    /* compress dai */
    int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
    /* DAI is also used for the control bus */
    bool bus_control;

    /* ops */
    const struct snd_soc_dai_ops *ops;

    /* DAI capabilities */
    struct snd_soc_pcm_stream capture;
    struct snd_soc_pcm_stream playback;
    unsigned int symmetric_rates:1;
    unsigned int symmetric_channels:1;
    unsigned int symmetric_samplebits:1;

    /* probe ordering - for components with runtime dependencies */
    int probe_order;
    int remove_order;
};

snd_soc_dai_driver  結構體關鍵成員如下:

probe、remove  回調函數,分別在聲卡加載和卸載時被調用;
suspend、resume  電源管理回調函數;
ops  指向snd_soc_dai_ops結構,用於配置和控制該dai;
playback  snd_soc_pcm_stream結構,用於指出該dai支持的聲道數,碼率,數據格式等能力;
capture  snd_soc_pcm_stream結構,用於指出該dai支持的聲道數,碼率,數據格式等能力;

其中snd_soc_pcm_stream類型的playback 和capture,在soc_pcm_open()中調用soc_pcm_init_runtime_hw()來檢查cpu_dai和codec_dai的支持能力。
其中ops包含的回調函數如下:

struct snd_soc_dai_ops {
    /*
     * DAI clocking configuration, all optional.
     * Called by soc_card drivers, normally in their hw_params.
     */
    int (*set_sysclk)(struct snd_soc_dai *dai,
        int clk_id, unsigned int freq, int dir);
    int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
        unsigned int freq_in, unsigned int freq_out);
    int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
    int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);

    /*
     * DAI format configuration
     * Called by soc_card drivers, normally in their hw_params.
     */
    int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
    int (*xlate_tdm_slot_mask)(unsigned int slots,
        unsigned int *tx_mask, unsigned int *rx_mask);
    int (*set_tdm_slot)(struct snd_soc_dai *dai,
        unsigned int tx_mask, unsigned int rx_mask,
        int slots, int slot_width);
    int (*set_channel_map)(struct snd_soc_dai *dai,
        unsigned int tx_num, unsigned int *tx_slot,
        unsigned int rx_num, unsigned int *rx_slot);
    int (*set_tristate)(struct snd_soc_dai *dai, int tristate);

    /*
     * DAI digital mute - optional.
     * Called by soc-core to minimise any pops.
     */
    int (*digital_mute)(struct snd_soc_dai *dai, int mute);
    int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);

    /*
     * ALSA PCM audio operations - all optional.
     * Called by soc-core during audio PCM operations.
     */
    int (*startup)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    void (*shutdown)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    int (*hw_params)(struct snd_pcm_substream *,
        struct snd_pcm_hw_params *, struct snd_soc_dai *);
    int (*hw_free)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    int (*prepare)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
    /*
     * NOTE: Commands passed to the trigger function are not necessarily
     * compatible with the current state of the dai. For example this
     * sequence of commands is possible: START STOP STOP.
     * So do not unconditionally use refcounting functions in the trigger
     * function, e.g. clk_enable/disable.
     */
    int (*trigger)(struct snd_pcm_substream *, int,
        struct snd_soc_dai *);
    int (*bespoke_trigger)(struct snd_pcm_substream *, int,
        struct snd_soc_dai *);
    /*
     * For hardware based FIFO caused delay reporting.
     * Optional.
     */
    snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
        struct snd_soc_dai *);
};

工作時鍾配置函數  通常由machine驅動調用:

set_sysclk  設置dai的主時鍾;
set_pll  設置PLL參數;
set_clkdiv  設置分頻系數;

dai的格式配置函數  通常由machine驅動調用:
set_fmt   設置dai的格式;
set_tdm_slot  如果dai支持時分復用,用於設置時分復用的slot;
set_channel_map 聲道的時分復用映射設置;
set_tristate  設置dai引腳的狀態,當與其他dai並聯使用同一引腳時需要使用該回調;

標准的snd_soc_ops回調  通常由soc-core在進行PCM操作時調用:

startup
shutdown
hw_params
hw_free
prepare
trigger
抗pop,pop聲  由soc-core調用:

digital_mute 

以下這些api通常被machine驅動使用,machine驅動在他的snd_pcm_ops字段中的hw_params回調中使用這些api:

snd_soc_dai_set_fmt()  實際上會調用snd_soc_dai_ops或者codec driver中的set_fmt回調;
snd_soc_dai_set_pll() 實際上會調用snd_soc_dai_ops或者codec driver中的set_pll回調;
snd_soc_dai_set_sysclk()  實際上會調用snd_soc_dai_ops或者codec driver中的set_sysclk回調;
snd_soc_dai_set_clkdiv()  實際上會調用snd_soc_dai_ops或者codec driver中的set_clkdiv回調;

在snd_add_dai函數中,創建snd_soc_dai結構體dai,並使用dai_drv對snd_soc_dai進行初始化,最后將創建的dai添加到component->dai_list中。在此函數中還會將dai_drv的name copy給dai的name,后續Machine driver就是通過這個name來找到dai的。

/* Create a DAI and add it to the component's DAI list */
static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
    struct snd_soc_dai_driver *dai_drv,
    bool legacy_dai_naming)
{
    struct device *dev = component->dev;
    struct snd_soc_dai *dai;

    dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));

    dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
    if (dai == NULL)
        return NULL;

    /*
     * Back in the old days when we still had component-less DAIs,
     * instead of having a static name, component-less DAIs would
     * inherit the name of the parent device so it is possible to
     * register multiple instances of the DAI. We still need to keep
     * the same naming style even though those DAIs are not
     * component-less anymore.
     */
    if (legacy_dai_naming &&
       (dai_drv->id == 0 || dai_drv->name == NULL)) {
        dai->name = fmt_single_name(dev, &dai->id);
    } else {
        dai->name = fmt_multiple_name(dev, dai_drv);
        if (dai_drv->id)
            dai->id = dai_drv->id;
        else
            dai->id = component->num_dai;
    }
    if (dai->name == NULL) {
        kfree(dai);
        return NULL;
    }

    dai->component = component;
    dai->dev = dev;
    dai->driver = dai_drv;
    if (!dai->driver->ops)
        dai->driver->ops = &null_dai_ops;

    list_add(&dai->list, &component->dai_list);
    component->num_dai++;

    dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
    return dai;
}

至此,component driver包含的dapm_widget和dapm_route已經添加到component里面,CPU DAI也創建完成,並添加到component的dai_list里面。

4.調用snd_soc_component_add將component加到全局列表component_list.

static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
    if (!component->write && !component->read) {
        if (!component->regmap)
            component->regmap = dev_get_regmap(component->dev, NULL);
        if (component->regmap)
            snd_soc_component_setup_regmap(component);
    }

    list_add(&component->list, &component_list);
    INIT_LIST_HEAD(&component->dobj_list);
}

static void snd_soc_component_add(struct snd_soc_component *component)
{
    mutex_lock(&client_mutex);
    snd_soc_component_add_unlocked(component);
    mutex_unlock(&client_mutex);
}

 

在后續注冊card時(snd_soc_register_card),snd_soc_instantiate_card中會調用soc_bind_dai_link函數,在此函數中通過name來找到machine driver的某條dai_link上的cpu dai,具體如下:

    struct snd_soc_dai_link_component cpu_dai_component;
    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;
    rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
    if (!rtd->cpu_dai) {
        dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
            dai_link->cpu_dai_name);
        goto _err_defer;
    }

通過dai_link的cpu_name和cpu_dai_name來找到相應的cpu dai,具體查找過程在snd_soc_find_dai函數內,先通過cpu_name找到component,然后遍歷component->dai_list,使用cpu_dai_name來匹配dai的name。

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;
    struct device_node *component_of_node;

    lockdep_assert_held(&client_mutex);

    /* Find CPU DAI from registered DAIs*/
    list_for_each_entry(component, &component_list, list) {
        component_of_node = component->dev->of_node;
        if (!component_of_node && component->dev->parent)
            component_of_node = component->dev->parent->of_node;

        if (dlc->of_node && component_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;
}

 在soc_probe_link_components中會對CPU DAI的component進行probe: 

static int soc_probe_link_components(struct snd_soc_card *card,
            struct snd_soc_pcm_runtime *rtd,
                     int order)
{
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_component *component;
    int i, ret;

    /* probe the CPU-side component, if it is a CODEC */
    component = rtd->cpu_dai->component;
    if (component->driver->probe_order == order) {
        ret = soc_probe_component(card, component);
        if (ret < 0)
            return ret;
    }

    /* probe the CODEC-side components */
    for (i = 0; i < rtd->num_codecs; i++) {
        component = rtd->codec_dais[i]->component;
        if (component->driver->probe_order == order) {
            ret = soc_probe_component(card, component);
            if (ret < 0)
                return ret;
        }
    }

    /* probe the platform */
    if (platform->component.driver->probe_order == order) {
        ret = soc_probe_component(card, &platform->component);
        if (ret < 0)
            return ret;
    }

    return 0;
}

在soc_probe_component函數內,對CPU DAI進行probe,主要包括

將component 和dapm context的card賦值。

調用snd_soc_dapm_new_controls創建component的dapm_widget,並將widget加到card->widgets列表中.

調用snd_soc_dapm_new_dai_widgets對component dai_list每一個dai widget, widget的name為dai->driver->playback/capture.name.並將widget加到card->widgets列表中.

調用component的probe函數

調用snd_soc_dapm_add_routes對component的dapm_routes添加dapm path.

將component的dapm context加到card的dapm context列表。

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);
    struct snd_soc_dai *dai;
    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;
    }

    if (!try_module_get(component->dev->driver->owner))
        return -ENODEV;

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

    soc_init_component_debugfs(component);

    if (component->dapm_widgets) {
        ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
            component->num_dapm_widgets);

        if (ret != 0) {
            dev_err(component->dev,
                "Failed to create new controls %d\n", ret);
            goto err_probe;
        }
    }

    list_for_each_entry(dai, &component->dai_list, list) {
        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;
        }
    }

    if (component->probe) {
        ret = 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);
    }

    /* machine specific init */
    if (component->init) {
        ret = component->init(component);
        if (ret < 0) {
            dev_err(component->dev,
                "Failed to do machine specific init %d\n", ret);
            goto err_probe;
        }
    }

    if (component->controls)
        snd_soc_add_component_controls(component, component->controls,
                     component->num_controls);
    if (component->dapm_routes)
        snd_soc_dapm_add_routes(dapm, component->dapm_routes,
                    component->num_dapm_routes);

    list_add(&dapm->list, &card->dapm_list);

    /* This is a HACK and will be removed soon */
    if (component->codec)
        list_add(&component->codec->card_list, &card->codec_dev_list);

    return 0;

err_probe:
    soc_cleanup_component_debugfs(component);
    component->card = NULL;
    module_put(component->dev->driver->owner);

    return ret;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM