• 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
    ");
            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
    ", 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
    ");
            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
    ", 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
    ", 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'
    ", 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
    ",
                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"
    ",
                    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
    ", 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
    ", ret);
                goto err_probe;
            }
        }
    
        if (component->probe) {
            ret = component->probe(component);
            if (ret < 0) {
                dev_err(component->dev,
                    "ASoC: failed to probe component %d
    ", 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
    ",
                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
    ", 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;
    }
  • 相关阅读:
    泛型约束 where T : class,new()
    在Navicat for MySQL中打开视图时,提示视图没有主键的问题
    转:JQuery实现下拉框的数据加载和联动
    查询每门课程最高分的学生的学号,课程号,成绩
    转:SQL子句的执行顺序
    端口映射
    服务器与个人电脑的区别
    花生壳使用指南
    如何测试本机的公网IP能否被Internet用户访问
    利用ADSL拨号上网方式如何搭建服务器
  • 原文地址:https://www.cnblogs.com/fellow1988/p/12637017.html
Copyright © 2020-2023  润新知