Linux ALSA声卡驱动之四:Codec 以及Codec

您所在的位置:网站首页 声卡驱动有什么区别和作用 Linux ALSA声卡驱动之四:Codec 以及Codec

Linux ALSA声卡驱动之四:Codec 以及Codec

2024-04-05 03:15| 来源: 网络整理| 查看: 265

ALSA声卡驱动:            1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介             2.Linux ALSA声卡驱动之二:Platform             3. Linux ALSA声卡驱动之三:Platform之Cpu_dai             4. Linux ALSA声卡驱动之四:Codec 以及Codec_dai             5.Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册             6.Linux ALSA声卡驱动之六:PCM的注册流程             7.Linux ALSA声卡驱动之七:录音(Capture) 调用流程         一.  Codec简介

    

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

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

 

二、 Codec和codec_dai 的注册:snd_soc_register_codec

        在第一章(Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介)以及介绍了platfrom可以细分platform和cpu_dai,codec可以细分codec和codec_dai ,platform和cpu_dai都有各自的驱动注册。由此猜想codec和codec_dai也是由各自的驱动注册吧,但实际却不是这样,mtk平台codec和codec_dai两部分代码都由一个驱动注册 mtk-soc-codec-63xx.c  snd_soc_register_codec,其实如果拆分出来也是可以。

2.1 snd_soc_register_codec函数时序图

  

  从上述的时序图不难看出,通过snd_soc_register_codec , snd_soc_register_dais  , snd_soc_component_add_unlocked , list_add(&codec->list, &codec_list)这几个函数的转变,最终会把相应的component->list添加到component_list链表 ,codec->list添加到codec_list链表。下面我们看具体的代码细节

  2.2 snd_soc_register_codec 函数相关结构体图

    结合下图我们可以比较清晰的知道各个结构体之间的关系:

结构体snd_soc_dai   通过*driver 维护snd_soc_codec_driver  ,*driver就是mtk_6357_dai_codecs。snd_soc_dai的初始化也是通过mtk_6357_dai_codecs。 通过list_add(&dai->list, &component->dai_list),把自己添加到snd_soc_component中 snd_soc_component 通过mtk_6357_dai_codecs初始化赋值,并且*dai_drv指向mtk_6357_dai_codecs  ,*driver维护snd_soc_component_driver结构体,dai_list链表头, 下面挂载snd_soc_dai:snd_soc_dai->list   list_add(&dai->list, &component->dai_list)。通过list_add(&component->list, &component_list) 把snd_soc_component添加到全局链表component_list中

 

 

2.3 snd_soc_register_codec函数实现细节    2.3.1 mtk_mt6357_codec_dev_probe函数 static int mtk_mt6357_codec_dev_probe(struct platform_device *pdev) { pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); if (pdev->dev.dma_mask == NULL) pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; if (pdev->dev.of_node) { dev_set_name(&pdev->dev, "%s", MT_SOC_CODEC_NAME); /* check if use hp depop flow */ //读取use_hp_depop_flow属性值 of_property_read_u32(pdev->dev.of_node, "use_hp_depop_flow", &mUseHpDepopFlow); pr_debug("%s(), use_hp_depop_flow = %d\n", __func__, mUseHpDepopFlow); } else { pr_debug("%s(), pdev->dev.of_node = NULL!!!\n", __func__); } pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); return snd_soc_register_codec(&pdev->dev, &soc_mtk_codec, mtk_6357_dai_codecs, ARRAY_SIZE(mtk_6357_dai_codecs)); } soc_mtk_codec  这个结构体我们暂时不分析,其中不难看出,主要是对probe  remove  read write 函数的驱动层实现 static struct snd_soc_codec_driver soc_mtk_codec = { .probe = mt6357_codec_probe, .remove = mt6357_codec_remove, .read = mt6357_read, .write = mt6357_write, }; mtk_6357_dai_codecs  static struct snd_soc_dai_driver mtk_6357_dai_codecs[] = { { .name = MT_SOC_CODEC_TXDAI_NAME, .ops = &mt6323_aif1_dai_ops, .playback = { .stream_name = MT_SOC_DL1_STREAM_NAME, .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SND_SOC_ADV_MT_FMTS, }, }, { .name = MT_SOC_CODEC_RXDAI_NAME, .ops = &mt6323_aif1_dai_ops, .capture = { .stream_name = MT_SOC_UL1_STREAM_NAME, .channels_min = 1, .channels_max = 2, .rates = SOC_HIGH_USE_RATE, .formats = SND_SOC_ADV_MT_FMTS, }, }, { .name = MT_SOC_CODEC_TDMRX_DAI_NAME, .ops = &mt6323_aif1_dai_ops, .capture = { .stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME, .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE), }, }, { .name = MT_SOC_CODEC_I2S0TXDAI_NAME, .ops = &mt6323_aif1_dai_ops, .playback = { .stream_name = MT_SOC_I2SDL1_STREAM_NAME, .channels_min = 1, .channels_max = 2, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SND_SOC_ADV_MT_FMTS, }, }, ........

 

如上代码也就是对snd_soc_dai_driver结构体赋值,取得mtk_6357_dai_codecs数组,这个跟上一章节(Linux ALSA声卡驱动之三:Platform之Cpu_dai),mtk_dai_stub_dai 数组获取一样。下面是snd_soc_dai_driver 和snd_soc_pcm_stream  两个结构体的构成。

 

snd_soc_dai_driver struct snd_soc_dai_driver { /* DAI description */ const char *name; unsigned int id; unsigned int base; /* 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;//一个struct snd_soc_dai_ops类型的结构, 该dai的操作函数集 /* 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_pcm_stream struct snd_soc_pcm_stream { const char *stream_name; u64 formats; //这个格式定义在pcm.h SNDRV_PCM_FMTBITxxxx /* SNDRV_PCM_FMTBIT_* */ unsigned int rates; //采样率 我们常说16k 48k /* SNDRV_PCM_RATE_* */ unsigned int rate_min; //最小采样率 /* min rate */ unsigned int rate_max; //最大采样率 /* max rate */ unsigned int channels_min; //最小通道数,单声道1/* min channels */ unsigned int channels_max; //最大通道数 立体声道2/* max channels */ unsigned int sig_bits; //位数 8 还是16 /* number of bits of content */ }

 

 

2.3.1 snd_soc_register_codec codec_drv的值赋值到codec->componentcodec_drv和dai_drv都挂载到 codec->component->dai_list,期间还会生出snd_soc_dai也会挂载到 codec->component codec->list 挂载到全局变量codec_list 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; // //codec_drv的值赋值到codec->component if (codec_drv->controls) { codec->component.controls = codec_drv->controls; codec->component.num_controls = codec_drv->num_controls; } if (codec_drv->dapm_widgets) { codec->component.dapm_widgets = codec_drv->dapm_widgets; codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets; } if (codec_drv->dapm_routes) { codec->component.dapm_routes = codec_drv->dapm_routes; codec->component.num_dapm_routes = codec_drv->num_dapm_routes; } 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); } //依据dai_drv 创建snd_soc_dai 并且添加到 codec->component->dai_list 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; } //从component.dai_list中取出dai,并且codec赋值到dai->codec , list_for_each_entry(dai, &codec->component.dai_list, list) dai->codec = codec; mutex_lock(&client_mutex); //codec->component->list添加到component_list ,至此cpu dai 的component 和codec dai 的component都添加到component_list snd_soc_component_add_unlocked(&codec->component); //codec->list添加到codec_list中 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; } 2.3.2 snd_soc_register_dais 初始化snd_soc_component,并赋值。dai_drv for循环逐一取出snd_soc_dai_driver 创建新的snd_soc_dai 并且挂在component->dai_list 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); //snd_soc_dai_driver 添加到snd_soc_component component->dai_drv = dai_drv; component->num_dai = count; for (i = 0; i < count; i++) { dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); if (dai == NULL) { ret = -ENOMEM; goto err; } /* * 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. */ //snd_soc_dai_driver name 赋值到snd_soc_dai name 并且赋值 id if (count == 1 && legacy_dai_naming && (dai_drv[i].id == 0 || dai_drv[i].name == NULL)) { dai->name = fmt_single_name(dev, &dai->id); } else { dai->name = fmt_multiple_name(dev, &dai_drv[i]); if (dai_drv[i].id) dai->id = dai_drv[i].id; else dai->id = i; } if (dai->name == NULL) { kfree(dai); ret = -ENOMEM; goto err; } // 创建新的snd_soc_dai 并且挂在component->dai_list dai->component = component; dai->dev = dev; dai->driver = &dai_drv[i]; //ops 由dai_drv赋值的, 可能是cpu dai 就是:mtk_dai_stub_dai ,codec dai:mtk_6357_dai_codecs if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; list_add(&dai->list, &component->dai_list); dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); } return 0; err: snd_soc_unregister_dais(component); return ret; }   2.3.3 struct snd_soc_dai    由核心层内部创建和维护, 用于代表一个dai. 可以是cpu_dai或codec_dai struct snd_soc_dai { const char *name;//该dai的名称, ASoC核心层靠name来区分不同的dai. int id;//应该是核心层自动分配的一个id号 struct device *dev; /* driver ops */ struct snd_soc_dai_driver *driver;//指向下属的snd_soc_dai_driver, 该结构体一般由底层驱动实现 /* DAI runtime info */ unsigned int capture_active:1; /* stream is in use */ unsigned int playback_active:1; /* stream is in use */ unsigned int symmetric_rates:1; unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; unsigned int active; unsigned char probed:1; struct snd_soc_dapm_widget *playback_widget; struct snd_soc_dapm_widget *capture_widget; /* DAI DMA data */ void *playback_dma_data; void *capture_dma_data; /* Symmetry data - only valid if symmetry is being enforced */ unsigned int rate; unsigned int channels; unsigned int sample_bits; /* parent platform/codec */ struct snd_soc_codec *codec; struct snd_soc_component *component; /* CODEC TDM slot masks and params (for fixup) */ unsigned int tx_mask; unsigned int rx_mask; struct list_head list;//用于把自己挂载到component->dai_list下. list_add(&dai->list, &component->dai_list) };

 

2.3.4 struct snd_soc_component   当底层驱动注册platform、codec+codec dai、cpu dai时, 核心层都会创建一个对应的snd_soc_component,并且会挂到component_list 链表 struct snd_soc_component { const char *name; //这个跟device_driver->name 和snd_soc_component_driver->id有关, int id; const char *name_prefix; struct device *dev; struct snd_soc_card *card; unsigned int active; unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int registered_as_component:1;//在snd_soc_register_component时修改为1 struct list_head list;//用于把自己挂载到全局链表component_list下 ,component_list 实在soc-core 中保持的全局变量 struct snd_soc_dai_driver *dai_drv;//dai 有可能是cpu dai 也有可能是 codec dai 是一个数组 int num_dai;//dai 的数量 const struct snd_soc_component_driver *driver;//指向下属的snd_soc_component_driver, 该结构体一般由底层平台驱动实现 struct list_head dai_list;//链表头, 挂接snd_soc_dai->list list_add(&dai->list, &component->dai_list) int (*read)(struct snd_soc_component *, unsigned int, unsigned int *); int (*write)(struct snd_soc_component *, unsigned int, unsigned int); struct regmap *regmap; int val_bytes; struct mutex io_mutex; /* attached dynamic objects */ struct list_head dobj_list; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; #endif /* * DO NOT use any of the fields below in drivers, they are temporary and * are going to be removed again soon. If you use them in driver code the * driver will be marked as BROKEN when these fields are removed. */ /* Don't use these, use snd_soc_component_get_dapm() */ struct snd_soc_dapm_context dapm; 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; struct snd_soc_codec *codec; int (*probe)(struct snd_soc_component *); void (*remove)(struct snd_soc_component *); #ifdef CONFIG_DEBUG_FS void (*init_debugfs)(struct snd_soc_component *component); const char *debugfs_prefix; #endif };

 

2.3.5 struct snd_soc_component_driver  底层驱动需要填充该结构体, 然后向ASoC核心层注册 struct snd_soc_component_driver { //ASoC核心层用名字区分不同的snd_soc_component_driver. //注意这个name与snd_soc_component->name不是同一个. 这里的name由驱动编写者填入, 而component->name由系统自动生成. const char *name; /* Default control and setup, added after probe() is run */ const struct snd_kcontrol_new *controls; unsigned int num_controls; //dapm_widgets、dapm_routes : 与dapm相关, dapm其实是对kcontrol做了一层封装 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; }; 2.3.6 struct snd_soc_codec   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 }; 总结:

     梳理完,你会发现,其实跟前面的platform  cpu_dai一样都是初始化结构体snd_soc_component 和snd_soc_codec  并且把这两个结构体挂到component_list 和code_list中。以供soc_bind_dai_link绑定cpu_dai 、codec_dai 、codec、platform使用。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3