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\n", 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\n", 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'\n",
        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\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.设置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\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_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\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;
}

复制代码

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\n",
                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\n");
        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\"\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;
}

复制代码

### 回答1: alsa-driver-1.2.7.tar.bz2 是一个音频驱动程序的压缩包。Alsa是Advanced Linux Sound Architecture(高级Linux音频架构)的缩写。它是一个开源的音频驱动程序,为Linux操作系统提供音频功能。该驱动程序被设计用来支持各种声音卡、音频控制器和其他音频设备。 alsa-driver-1.2.7.tar.bz2 是Alsa驱动程序的源代码压缩包。通过将其解压缩,可以获取驱动程序的源代码文件。 要安装alsa-driver-1.2.7.tar.bz2,首先需要在Linux系统中安装必要的编译工具和开发库。然后,您可以使用终端进入解压缩后的文件夹,并运行一系列的命令以编译和安装该驱动程序。 安装成功后,您可以配置和管理硬件设备的音频设置,如音量控制、声道配置和输入输出设备的选择。Alsa驱动程序提供了用于控制和操作音频功能的命令行工具和应用程序接口(API),允许开发者和系统管理员对音频设备进行配置和调整。 总之,alsa-driver-1.2.7.tar.bz2 是一个允许在Linux操作系统上实现音频功能的驱动程序源代码。通过安装和配置该驱动程序,您可以享受到高质量的音频体验,并根据需要进行各种音频设置和调整。 ### 回答2: alsa-driver-1.2.7.tar.bz2是一个用于Linux操作系统的音频驱动程序。ALSA代表高级Linux声音体系结构(Advanced Linux Sound Architecture),它是Linux操作系统中最常用的音频架构之一。 该驱动程序文件是.tar.bz2格式的压缩文件。.tar表示该文件是一个压缩的归档文件,而.bz2表示该文件使用bzip2压缩算法进行压缩。 该驱动程序的版本号为1.2.7,表示这是alsa-driver的第1.2.7个稳定版本。每个新版本通常包含修复漏洞、改进性能、增加新功能等更新。 安装alsa-driver-1.2.7.tar.bz2需要进行以下步骤: 1. 解压文件:使用tar命令解压.tar.bz2文件,例如使用命令tar -xjf alsa-driver-1.2.7.tar.bz2。 2. 进入解压后的目录:cd alsa-driver-1.2.7。 3. 配置驱动程序:运行./configure命令,该命令会检查系统环境并配置驱动程序。 4. 编译驱动程序:运行make命令,该命令会编译驱动程序。 5. 安装驱动程序:运行make install命令,该命令会将驱动程序安装到系统中。 6. 配置系统:根据具体需要,可能需要进行一些额外的配置,例如修改配置文件或加载驱动程序。 安装完成后,alsa-driver-1.2.7将提供音频驱动程序,使得Linux系统能够正常支持音频设备。这将使用户能够播放音乐、观看视频、进行语音通话等各种音频相关的操作。 总之,alsa-driver-1.2.7.tar.bz2是一个用于Linux操作系统的音频驱动程序文件,安装它可以使得系统支持音频设备,并提供各种音频功能。 ### 回答3: alsa-driver-1.2.7.tar.bz2是一个压缩文件,其中包含了ALSA(Advanced Linux Sound Architecture)音频驱动的源代码和相关文件。它是用于Linux操作系统的音频驱动程序。 ALSA是Linux内核中的一种声音处理架构,旨在提供细致、稳定且高质量的音频处理和音频设备驱动。alsa-driver-1.2.7.tar.bz2是ALSA音频驱动的一个版本。 该压缩文件中的源代码可以用于编译和安装ALSA音频驱动程序。首先,需要解压缩该文件。然后,通过进入解压缩后的目录,使用特定的命令和选项进行编译和安装。编译和安装成功后,系统将具有新的ALSA音频驱动程序,以支持各种音频设备。 ALSA音频驱动是Linux系统中实现音频功能的关键组件之一。它负责与硬件设备进行通信,并提供音频输入和输出的功能。安装最新版本的ALSA驱动可以提升音频系统的性能和稳定性,同时支持更多的音频设备和功能。 总而言之,alsa-driver-1.2.7.tar.bz2是一个压缩文件,其中包含了ALSA音频驱动的源代码和相关文件,可用于编译和安装ALSA音频驱动程序,以提供高质量的音频功能和支持各种音频设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值