zoukankan      html  css  js  c++  java
  • ALSA driver---register codec

    参考:

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

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

    在移动设备中,Codec的作用可以归结为4种,分别是:

    对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号
    对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号
    对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的
    对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等

    codec通过调用snd_soc_register_codec进行注册。

    在snd_soc_register_codec函数内主要创建和分配snd_soc_codec结构体的内存,将snd_soc_codec_driver 赋值给codec->driver,snd_soc_dai_driver注册到codec->component里,将创建的codec->component加到全局列表component_list中,将创建的codec加到全局列表codec_list中,方便后续注册Machine driver时调用到。 

    /**
     * snd_soc_register_codec - Register a codec with the ASoC core
     *
     * @dev: The parent device for this codec
     * @codec_drv: Codec driver
     * @dai_drv: The associated DAI driver
     * @num_dai: Number of DAIs
     */
    int snd_soc_register_codec(struct device *dev,
                   const struct snd_soc_codec_driver *codec_drv,
                   struct snd_soc_dai_driver *dai_drv,
                   int num_dai)
    {
        struct snd_soc_dapm_context *dapm;
        struct snd_soc_codec *codec;
        struct snd_soc_dai *dai;
        int ret, i;
    
        dev_dbg(dev, "codec register %s
    ", dev_name(dev));
    
        codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
        if (codec == NULL)
            return -ENOMEM;
    
        codec->component.codec = codec;
    
        ret = snd_soc_component_initialize(&codec->component,
                &codec_drv->component_driver, dev);
        if (ret)
            goto err_free;
    
        if (codec_drv->probe)
            codec->component.probe = snd_soc_codec_drv_probe;
        if (codec_drv->remove)
            codec->component.remove = snd_soc_codec_drv_remove;
        if (codec_drv->write)
            codec->component.write = snd_soc_codec_drv_write;
        if (codec_drv->read)
            codec->component.read = snd_soc_codec_drv_read;
        codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
    
        dapm = snd_soc_codec_get_dapm(codec);
        dapm->idle_bias_off = codec_drv->idle_bias_off;
        dapm->suspend_bias_off = codec_drv->suspend_bias_off;
        if (codec_drv->seq_notifier)
            dapm->seq_notifier = codec_drv->seq_notifier;
        if (codec_drv->set_bias_level)
            dapm->set_bias_level = snd_soc_codec_set_bias_level;
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->component.val_bytes = codec_drv->reg_word_size;
    
    #ifdef CONFIG_DEBUG_FS
        codec->component.init_debugfs = soc_init_codec_debugfs;
        codec->component.debugfs_prefix = "codec";
    #endif
    
        if (codec_drv->get_regmap)
            codec->component.regmap = codec_drv->get_regmap(dev);
    
        for (i = 0; i < num_dai; i++) {
            fixup_codec_formats(&dai_drv[i].playback);
            fixup_codec_formats(&dai_drv[i].capture);
        }
    
        ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
        if (ret < 0) {
            dev_err(dev, "ASoC: Failed to register DAIs: %d
    ", ret);
            goto err_cleanup;
        }
    
        list_for_each_entry(dai, &codec->component.dai_list, list)
            dai->codec = codec;
    
        mutex_lock(&client_mutex);
        snd_soc_component_add_unlocked(&codec->component);
        list_add(&codec->list, &codec_list);
        mutex_unlock(&client_mutex);
    
        dev_dbg(codec->dev, "ASoC: Registered codec '%s'
    ",
            codec->component.name);
        return 0;
    
    err_cleanup:
        snd_soc_component_cleanup(&codec->component);
    err_free:
        kfree(codec);
        return ret;
    }

    1.首先分配snd_soc_codec的内存。

    /* SoC Audio Codec device */
    struct snd_soc_codec {
        struct device *dev;
        const struct snd_soc_codec_driver *driver;
    
        struct list_head list;
        struct list_head card_list;
    
        /* runtime */
        unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
        unsigned int cache_init:1; /* codec cache has been initialized */
    
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
        hw_write_t hw_write;
        void *reg_cache;
    
        /* component */
        struct snd_soc_component component;
    
    #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_reg;
    #endif
    };

    2.调用snd_soc_component_initiallize来初始化codec->component, 主要使用snd_soc_codec_driver的成员component_driver来初始化component。

    snd_soc_codec_driver结构体如下:

    /* codec driver */
    struct snd_soc_codec_driver {
    
        /* driver ops */
        int (*probe)(struct snd_soc_codec *);
        int (*remove)(struct snd_soc_codec *);
        int (*suspend)(struct snd_soc_codec *);
        int (*resume)(struct snd_soc_codec *);
        struct snd_soc_component_driver component_driver;
    
        /* codec wide operations */
        int (*set_sysclk)(struct snd_soc_codec *codec,
                  int clk_id, int source, unsigned int freq, int dir);
        int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
            unsigned int freq_in, unsigned int freq_out);
    
        /* codec IO */
        struct regmap *(*get_regmap)(struct device *);
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
        unsigned int reg_cache_size;
        short reg_cache_step;
        short reg_word_size;
        const void *reg_cache_default;
    
        /* codec bias level */
        int (*set_bias_level)(struct snd_soc_codec *,
                      enum snd_soc_bias_level level);
        bool idle_bias_off;
        bool suspend_bias_off;
    
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                     enum snd_soc_dapm_type, int);
    
        bool ignore_pmdown_time;  /* Doesn't benefit from pmdown delay */
    };

    snd_soc_component_driver结构体如下,主要包含controls, dapm_widget, dapm_routes,在Codec DAI中,dapm主要描述了codec内部widget是如何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.设置codec->component的probe,remove,write,read函数,即为snd_soc_codec_driver相应的成员函数。

    4.调用snd_soc_register_dais来注册codec 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_add_dai函数中,创建snd_soc_dai结构体dai,并使用dai_drv对snd_soc_dai进行初始化,最后将创建的dai添加到component->dai_list中。在此函数中还会将dai_drv的name copy给dai的name,后续Machine driver就是通过这个name来找到codec 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;
    }

    5.设置codec DAI的成员codec为当前创建的codec。

    6.调用snd_soc_component_add_unlocked将codec->component加到全局列表component,并将创建的codec加到全局列表codec_list.

    在后续注册card时(snd_soc_register_card),snd_soc_instantiate_card中会调用soc_bind_dai_link函数,在此函数中通过dai的name来找到machine driver的某条dai_link上的codec dai,并通过codec dai找到相应的codec, 具体如下:

        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
        
        /* Find CODEC from registered CODECs */
        codec_dais = rtd->codec_dais;
        for (i = 0; i < rtd->num_codecs; i++) {
            codec_dais[i] = snd_soc_find_dai(&codecs[i]);
            if (!codec_dais[i]) {
                dev_err(card->dev, "ASoC: CODEC DAI %s not registered
    ",
                    codecs[i].dai_name);
                goto _err_defer;
            }
        }
    
        /* Single codec links expect codec and codec_dai in runtime data */
        rtd->codec_dai = codec_dais[0];
        rtd->codec = rtd->codec_dai->codec;

    其中dai_link->codecs是在snd_soc_init_multicodec()中创建和初始化的。

    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);
            if (!dai_link->codecs)
                return -ENOMEM;
    
            dai_link->codecs[0].name = dai_link->codec_name;
            dai_link->codecs[0].of_node = dai_link->codec_of_node;
            dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
        }
    
        if (!dai_link->codecs) {
            dev_err(card->dev, "ASoC: DAI link has no CODECs
    ");
            return -EINVAL;
        }
    
        return 0;
    }

    在soc_probe_link_components中会对codec 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函数内,对codec 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;
    }
  • 相关阅读:
    D. Babaei and Birthday Cake--- Codeforces Round #343 (Div. 2)
    Vijos P1389婚礼上的小杉
    AIM Tech Round (Div. 2) C. Graph and String
    HDU 5627Clarke and MST
    bzoj 3332 旧试题
    codeforces 842C Ilya And The Tree
    codesforces 671D Roads in Yusland
    Travelling
    codeforces 606C Sorting Railway Cars
    codeforces 651C Watchmen
  • 原文地址:https://www.cnblogs.com/fellow1988/p/12642017.html
Copyright © 2011-2022 走看看