a) 注册codec 用到的函数:
这个函数用于注册codec, 他通过 list_add(&codec->list, &codec_list);
把codec加入到codec_list 里面。所有的codec将保存在codec_list。
/**
* snd_soc_register_codec - Register a codec with the ASoC core
*
* @codec: codec to register
*/
int snd_soc_register_codec(struct device *dev,
struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai)
b) 注册dia 用到的函数:
这两个函数将注册 dai driver.并且把 新创建的dai 加入到dai_list里面。
/**
* snd_soc_register_dai - Register a DAI with the ASoC core
*
* @dai: DAI to register
*/
int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
/**
* snd_soc_register_dais - Register multiple DAIs with the ASoC core
*
* @dai: Array of DAIs to register
* @count: Number of DAIs
*/
int snd_soc_register_dais(struct device *dev,
struct snd_soc_dai_driver *dai_drv, size_t count)
int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_dai *dai;
dev_dbg(dev, "dai register %s\n", dev_name(dev));
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
if (dai == NULL)
return -ENOMEM;
/* create DAI component name */
dai->name = fmt_single_name(dev, &dai->id);
if (dai->name == NULL) {
kfree(dai);
return -ENOMEM;
}
dai->dev = dev;
dai->driver = dai_drv;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
c) 注册平台驱动:
通过这个函数注册平台驱动。
/**
* snd_soc_register_platform - Register a platform with the ASoC core
*
* @platform: platform to register
*/
int snd_soc_register_platform(struct device *dev,
struct snd_soc_platform_driver *platform_drv)
d) 所有的dia_link 都以dai_link->name 为设备名称, 创建在
/sys/devices/platform/soc-audio 下面。
bash-3.2# pwd
/sys/devices/platform/soc-audio
bash-3.2# ls
uevent
modalias
subsystem
power
driver
SDP4430 Media
SDP4430 Media Capture
SDP4430 Voice
SDP4430 Tones Playback
SDP4430 Vibra Playback
SDP4430 MODEM
SDP4430 Media LP
hdmi
Legacy McBSP
Legacy McPDM
Legacy DMIC
(Backend) PDM-DL1
(Backend) PDM-UL1
(Backend) PDM-DL2
(Backend) PDM-VIB
(Backend) BT-VX
(Backend) FM-EXT
(Backend) MODEM-EXT
(Backend) WM8960
(Backend) DMIC0
(Backend) DMIC1
(Backend) DMIC2
(Backend) VXREC
sound
microamps_requested_av-switch
1) omap mcbsp platform driver 分析:
a) 设备的定义
/* macro for building platform_device for McBSP ports */
#define OMAP_MCBSP_PLATFORM_DEVICE(port_nr) \
static struct platform_device omap_mcbsp##port_nr = { \
.name = "omap-mcbsp-dai", \
.id = OMAP_MCBSP##port_nr, \
}
OMAP_MCBSP_PLATFORM_DEVICE(1);
OMAP_MCBSP_PLATFORM_DEVICE(2);
OMAP_MCBSP_PLATFORM_DEVICE(3);
OMAP_MCBSP_PLATFORM_DEVICE(4);
OMAP_MCBSP_PLATFORM_DEVICE(5);
/× 对于omap4, 这册了4个 mcbsp设备 ×/
static void omap_init_audio(void)
{
platform_device_register(&omap_mcbsp1);
platform_device_register(&omap_mcbsp2);
if (cpu_is_omap243x() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
platform_device_register(&omap_mcbsp3);
platform_device_register(&omap_mcbsp4);
}
if (cpu_is_omap243x() || cpu_is_omap34xx())
platform_device_register(&omap_mcbsp5);
platform_device_register(&omap_pcm);
}
b) mcbsp设备的驱动
static __devinit int asoc_mcbsp_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &omap_mcbsp_dai);
}
static struct snd_soc_dai_driver omap_mcbsp_dai =
{
.probe = mcbsp_dai_probe,
.playback = {
.channels_min = 1,
.channels_max = 16,
.rates = OMAP_MCBSP_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.channels_min = 1,
.channels_max = 16,
.rates = OMAP_MCBSP_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &mcbsp_dai_ops,
};
int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_dai *dai;
dev_dbg(dev, "dai register %s\n", dev_name(dev));
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); /× 分配一个dai */
if (dai == NULL)
return -ENOMEM;
/* create DAI component name */
dai->name = fmt_single_name(dev, &dai->id); /* 获取dai 的名称 ×/
if (dai->name == NULL) {
kfree(dai);
return -ENOMEM;
}
dai->dev = dev; /× 填充设备 ×/
dai->driver = dai_drv; /× 填充驱动 ×/
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
HDMI platform driver 分析:
HDMI设备的定义:
static struct platform_device sdp4430_hdmi_audio_device = {
.name = "hdmi-dai",
.id = -1,
};
HDMI 设备的音频驱动
static __devinit int omap_hdmi_probe(struct platform_device *pdev)
{
struct hdmi_notifier *notifier = &hdmi_data.notifier;
notifier->hpd_notifier = hdmi_hpd_notifier;
notifier->pwrchange_notifier = hdmi_pwrchange_notifier;
notifier->private_data = &hdmi_data;
hdmi_lib_init();
hdmi_add_notifier(notifier);
return snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
}
static struct snd_soc_dai_driver omap_hdmi_dai = {
.playback = {
.channels_min = 2,
/* currently we support only stereo HDMI */
.channels_max = 2,
.rates = OMAP_HDMI_RATES,
.formats = OMAP_HDMI_FORMATS,
},
.ops = &omap_hdmi_dai_ops,
};
C) abe dia 的注册:
设备定义:
static struct platform_device omap_abe_dai = {
.name = "omap-abe-dai",
.id = -1,
};
/× 注册abe dai */
static int __devinit omap_abe_probe(struct platform_device *pdev)
{
return snd_soc_register_dais(&pdev->dev, omap_abe_dai,
ARRAY_SIZE(omap_abe_dai));
}
static struct snd_soc_dai_driver omap_abe_dai[] = {
{ /* Multimedia Playback and Capture */
.name = "MultiMedia1",
.probe = omap_abe_dai_probe,
.remove = omap_abe_dai_remove,
.playback = {
.stream_name = "MultiMedia1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = OMAP_ABE_FORMATS,
},
.capture = {
.stream_name = "MultiMedia1 Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
},
{ /* Multimedia Capture */
.name = "MultiMedia2",
.capture = {
.stream_name = "MultiMedia2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
},
{ /* Voice Playback and Capture */
.name = "Voice",
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = OMAP_ABE_FORMATS,
},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
},
{ /* Tones Playback */
.name = "Tones",
.playback = {
.stream_name = "Tones Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
},
{ /* Vibra */
.name = "Vibra",
.playback = {
.stream_name = "Vibra Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
},
{ /* MODEM Voice Playback and Capture */
.name = "MODEM",
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = OMAP_ABE_FORMATS | SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = OMAP_ABE_FORMATS | SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &omap_abe_dai_ops,
},
{ /* Low Power HiFi Playback */
.name = "MultiMedia1 LP",
.playback = {
.stream_name = "MultiMedia1 LP Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.formats = OMAP_ABE_FORMATS | SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &omap_abe_dai_ops,
},
};
D) dmic dia 注册
设备:
static void omap_init_dmic(void)
{
struct omap_hwmod *oh;
struct omap_device *od;
oh = omap_hwmod_lookup("dmic");
if (!oh) {
printk(KERN_ERR "Could not look up dmic hw_mod\n");
return;
}
od = omap_device_build("omap-dmic-dai", -1, oh, NULL, 0,
omap_dmic_latency,
ARRAY_SIZE(omap_dmic_latency), 0);
if (IS_ERR(od))
printk(KERN_ERR "Could not build omap_device for omap-dmic-dai\n");
}
dai;
static struct snd_soc_dai_driver omap_dmic_dai[] = {
{
.name = "omap-dmic-dai-0",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_dai_ops,
},
{
.name = "omap-dmic-dai-1",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_abe_dai_ops,
},
{
.name = "omap-dmic-dai-2",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_abe_dai_ops,
},
#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP) || \
defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
{
.name = "omap-dmic-abe-dai-0",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_abe_dai_ops,
},
{
.name = "omap-dmic-abe-dai-1",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_abe_dai_ops,
},
{
.name = "omap-dmic-abe-dai-2",
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_DMIC_RATES,
.formats = OMAP_DMIC_FORMATS,
},
.ops = &omap_dmic_abe_dai_ops,
},
#endif
};
e) mcpdm dai
设备
static void omap_init_mcpdm(void)
{
struct omap_hwmod *oh;
struct omap_device *od;
struct omap_mcpdm_platform_data *pdata;
oh = omap_hwmod_lookup("omap-mcpdm-dai");
if (!oh)
printk(KERN_ERR "Could not look up mcpdm hw_mod\n");
pdata = kzalloc(sizeof(struct omap_mcpdm_platform_data), GFP_KERNEL);
if (!pdata) {
printk(KERN_ERR "Could not allocate platform data\n");
return;
}
pdata->device_enable = omap_device_enable;
pdata->device_idle = omap_device_idle;
pdata->device_shutdown = omap_device_shutdown;
od = omap_device_build("omap-mcpdm-dai", -1, oh, pdata,
sizeof(struct omap_mcpdm_platform_data),
omap_mcpdm_latency,
ARRAY_SIZE(omap_mcpdm_latency), 0);
if (od <= 0)
printk(KERN_ERR "Could not build omap_device for omap-mcpdm-dai\n");
}
probe:
static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
{
struct omap_mcpdm *mcpdm;
struct omap_hwmod *oh;
int ret = 0;
oh = omap_hwmod_lookup("omap-mcpdm-dai");
if (oh == NULL) {
dev_err(&pdev->dev, "no hwmod device found\n");
return -ENODEV;
}
mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
if (!mcpdm)
return -ENOMEM;
platform_set_drvdata(pdev, mcpdm);
mcpdm->downlink = &omap_mcpdm_links[0];
mcpdm->uplink = &omap_mcpdm_links[1];
mutex_init(&mcpdm->mutex);
mcpdm->free = 1;
mcpdm->io_base = omap_hwmod_get_mpu_rt_va(oh);
if (!mcpdm->io_base) {
ret = -ENOMEM;
goto err;
}
mcpdm->irq = platform_get_irq(pdev, 0);
if (mcpdm->irq < 0) {
ret = mcpdm->irq;
goto err;
}
ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
0, "McPDM", mcpdm);
if (ret) {
dev_err(mcpdm->dev, "Request for McPDM IRQ failed: %d\n", ret);
goto err;
}
pm_runtime_enable(&pdev->dev);
mcpdm->dev = &pdev->dev;
/* TODO: values will be different per device, read from FS */
mcpdm->dl1_offset = 0x1F;
mcpdm->dl2_offset = 0x1F;
wake_lock_init(&mcpdm->wake_lock, WAKE_LOCK_SUSPEND, "mcpdm");
INIT_DELAYED_WORK(&mcpdm->delayed_work, playback_work);
#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
INIT_DELAYED_WORK(&mcpdm->delayed_abe_work, playback_abe_work);
#endif
ret = snd_soc_register_dais(&pdev->dev, omap_mcpdm_dai,
ARRAY_SIZE(omap_mcpdm_dai));
if (ret < 0)
goto dai_err;
return 0;
dai_err:
wake_lock_destroy(&mcpdm->wake_lock);
free_irq(mcpdm->irq, mcpdm);
err:
kfree(mcpdm);
return ret;
}
Dai:
static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
{
.name = "mcpdm-dl1",
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-dl2",
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-vib",
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-ul1",
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_abe_dai_ops,
},
#endif
{
.name = "mcpdm-dl",
.playback = {
.channels_min = 1,
.channels_max = 4,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_dai_ops,
},
{
.name = "mcpdm-ul",
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
.ops = &omap_mcpdm_dai_ops,
}, };
F) VXREC
设备:
static struct platform_device omap_abe_vxrec = {
.name = "omap-abe-vxrec-dai",
.id = -1,
};
static inline void omap_init_abe(void)
{
platform_device_register(&codec_dmic0);
platform_device_register(&codec_dmic1);
platform_device_register(&codec_dmic2);
platform_device_register(&omap_abe_dai);
platform_device_register(&omap_abe_vxrec);
}
DAI:
static struct snd_soc_dai_driver omap_vxrec_dai[] = {
{
.name = "omap-abe-vxrec-dai",
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
},
},
};
G) dump dai
这些在sdp4430.c中注册
static struct snd_soc_dai_driver dai[] = {
{
.name = "Bluetooth",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
/* TODO: make this a separate FM CODEC driver or DUMMY */
{
.name = "FM Digital",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = "HDMI",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
};
声卡主驱动的加载:
声卡设备的创建:
sdp4430_snd_device = platform_device_alloc("soc-audio", -1);
if (!sdp4430_snd_device) {
printk(KERN_ERR "Platform device allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(sdp4430_snd_device, &snd_soc_sdp4430);
ret = platform_device_add(sdp4430_snd_device);
if (ret)
goto plat_err;
驱动的数据:
/* Audio machine driver */
static struct snd_soc_card snd_soc_sdp4430 = {
.name = "SDP4430",
.long_name = "TI OMAP4 SDP4430 Board",
.dai_link = sdp4430_dai,
.num_links = ARRAY_SIZE(sdp4430_dai),
};
声卡的驱动程序:定义在文件soc-core.c中,soc_probe 完成声卡的probe
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
int ret = 0;
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
INIT_LIST_HEAD(&card->dai_dev_list);
INIT_LIST_HEAD(&card->codec_dev_list);
INIT_LIST_HEAD(&card->platform_dev_list);
ret = snd_soc_register_card(card); /* 注册一个声卡 ×/
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}
注册声卡函数解析:
/**
* snd_soc_register_card - Register a card with the ASoC core
*
* @card: Card to register
*
* Note that currently this is an internal only function: it will be
* exposed to machine drivers after further backporting of ASoC v2
* registration APIs.
*/
static int snd_soc_register_card(struct snd_soc_card *card)
{
int i, ret = 0;
if (!card->name || !card->dev)
return -EINVAL;
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links,
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
for (i = 0; i < card->num_links; i++) {
/* create virtual CODEC for dynamic links */
dev_dbg(card->dev, "DAI create runtime %s\n", card->dai_link[i].name);
card->rtd[i].dai_link = &card->dai_link[i];
if (card->rtd[i].dai_link->dynamic) {
card->rtd[i].dai_link->codec_name = "null-codec";
card->rtd[i].dai_link->codec_dai_name = "null-codec-dai";
ret = snd_soc_register_codec(card->dev, &null_codec_drv,
&null_codec_dai_drv, 1);
if (ret < 0) {
printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n",
__func__, ret);
goto out;
}
continue;
}
if (card->rtd[i].dai_link->no_codec) {
card->rtd[i].dai_link->codec_name = "null-codec";
ret = snd_soc_register_codec(card->dev, &null_codec_drv,
&null_codec_dai_drv, 1);
if (ret < 0) {
printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n",
__func__, ret);
goto out;
}
continue;
}
}
INIT_LIST_HEAD(&card->list);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
mutex_lock(&client_mutex);
list_add(&card->list, &card_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
out:
return ret;
}
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct platform_device *pdev = to_platform_device(card->dev);
int ret, i;
mutex_lock(&card->mutex);
if (card->instantiated) {
mutex_unlock(&card->mutex);
return;
}
/* bind DAIs */
for (i = 0; i < card->num_links; i++)
soc_bind_dai_link(card, i);
/* bind completed ? */
if (card->num_rtd != card->num_links) {
mutex_unlock(&card->mutex);
return;
}
/* card bind complete so register a sound card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create sound card for card %s\n",
card->name);
mutex_unlock(&card->mutex);
return;
}
card->snd_card->dev = card->dev;
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(pdev);
if (ret < 0)
goto card_probe_error;
}
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_dai_link(card, i);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to instanciate card %s\n", card->name);
goto probe_dai_err;
}
}
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->long_name);
snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
"%s", card->name);
ret = snd_card_register(card->snd_card);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
goto probe_dai_err;
}
#ifdef CONFIG_SND_SOC_AC97_BUS
/* register any AC97 codecs */
for (i = 0; i < card->num_rtd; i++) {
ret = soc_register_ac97_dai_link(&card->rtd[i]);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name);
goto probe_dai_err;
}
}
#endif
card->instantiated = 1;
mutex_unlock(&card->mutex);
return;
probe_dai_err:
for (i = 0; i < card->num_links; i++)
soc_remove_dai_link(card, i);
card_probe_error:
if (card->remove)
card->remove(pdev);
snd_card_free(card->snd_card);
mutex_unlock(&card->mutex);
}
/×此函数主要是获取rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai 这四个参数 ×/
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai, *cpu_dai;
if (rtd->complete)
return 1;
dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
/* do we already have the CPU DAI for this link ? */
if (rtd->cpu_dai) {
goto find_codec;
}
/* no, then find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) {
if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) {
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
rtd->cpu_dai = cpu_dai;
goto find_codec;
}
}
dev_dbg(card->dev, "CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
find_codec:
/* do we already have the CODEC for this link ? */
if (rtd->codec) {
goto find_platform;
}
/* no, then find CODEC from registered CODECs*/
list_for_each_entry(codec, &codec_list, list) {
if (!strcmp(codec->name, dai_link->codec_name)) {
rtd->codec = codec;
if (!try_module_get(codec->dev->driver->owner))
return -ENODEV;
/* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/
list_for_each_entry(codec_dai, &dai_list, list) {
if ((codec->dev == codec_dai->dev || codec->driver == &null_codec_drv) &&
!strcmp(codec_dai->name, dai_link->codec_dai_name)) {
rtd->codec_dai = codec_dai;
goto find_platform;
}
}
dev_dbg(card->dev, "CODEC DAI %s not registered\n",
dai_link->codec_dai_name);
goto find_platform;
}
}
dev_dbg(card->dev, "CODEC %s not registered\n",
dai_link->codec_name);
find_platform:
/* do we already have the CODEC DAI for this link ? */
if (rtd->platform) {
goto out;
}
/* no, then find CPU DAI from registered DAIs*/
list_for_each_entry(platform, &platform_list, list) {
if (!strcmp(platform->name, dai_link->platform_name)) {
if (!try_module_get(platform->dev->driver->owner))
return -ENODEV;
rtd->platform = platform;
goto out;
}
}
dev_dbg(card->dev, "platform %s not registered\n",
dai_link->platform_name);
return 0;
out:
/* mark rtd as complete if we found all 4 of our client devices */
if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
rtd->complete = 1;
card->num_rtd++;
}
return 1;
}
static int soc_probe_dai_link(struct snd_soc_card *card, int num)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
int ret;
dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
/* config components */
platform->snd_card = codec->snd_card = card->snd_card;
codec_dai->codec = codec;
codec->card = card;
platform->card = card;
cpu_dai->platform = platform;
rtd->card = card;
rtd->dev.parent = card->dev;
codec_dai->card = card;
cpu_dai->card = card;
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */
if (!cpu_dai->probed) {
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",
cpu_dai->name);
return ret;
}
}
/* Make sure all DAPM widgets are instantiated */
snd_soc_dapm_new_widgets(platform->dapm);
cpu_dai->probed = 1;
/* mark cpu_dai as probed and add to card cpu_dai list */
list_add(&cpu_dai->card_list, &card->dai_dev_list);
}
/* probe the CODEC */
if (!codec->probed) {
if (codec->driver->probe) {
ret = codec->driver->probe(codec);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe CODEC %s\n",
codec->name);
return ret;
}
}
soc_init_codec_debugfs(codec);
/* mark codec as probed and add to card codec list */
codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list);
}
/* probe the platform */
if (!platform->probed) {
if (platform->driver->probe) {
ret = platform->driver->probe(platform);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe platform %s\n",
platform->name);
return ret;
}
}
soc_init_platform_debugfs(platform);
/* mark platform as probed and add to card platform list */
platform->probed = 1;
list_add(&platform->card_list, &card->platform_dev_list);
}
/* probe the CODEC DAI */
if (!codec_dai->probed) {
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",
codec_dai->name);
return ret;
}
}
/* mark cpu_dai as probed and add to card cpu_dai list */
codec_dai->probed = 1;
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
mutex_init(&rtd->pcm_mutex);
/* now that all clients have probed, initialise the DAI link */
if (dai_link->init) {
ret = dai_link->init(rtd);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name);
return ret;
}
}
/* Make sure all DAPM widgets are instantiated */
snd_soc_dapm_new_widgets(codec->dapm);
snd_soc_dapm_sync(codec->dapm);
/* register the rtd device */
rtd->dev.init_name = rtd->dai_link->stream_name;
rtd->dev.release = rtd_release;
rtd->dev.init_name = dai_link->name;
ret = device_register(&rtd->dev);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register DAI runtime device %d\n", ret);
return ret;
}
rtd->dev_registered = 1;
ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time);
if (ret < 0)
printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");
/* add DAPM sysfs entries for this codec */
ret = snd_soc_dapm_sys_add(&rtd->dev);
if (ret < 0)
printk(KERN_WARNING "asoc: failed to add codec dapm sysfs entries\n");
/* add codec sysfs entries */
ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);
if (ret < 0)
printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
/* create the pcm */
ret = soc_new_pcm(rtd, num);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);
return ret;
}
/* add platform data for AC97 devices */
if (rtd->codec_dai->driver->ac97_control)
snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);
return 0;
}
根据dai link 创建pcm 设备, 有多少个links就创建多少个设备
/* create a new pcm */
static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm_substream *substream[2];
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
if (rtd->dai_link->dynamic) {
if (rtd->dai_link->fe_playback_channels)
playback = 1;
if (rtd->dai_link->fe_capture_channels)
capture = 1;
} else {
if (codec_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min)
capture = 1;
}
dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
ret = snd_pcm_new(rtd->card->snd_card, new_name,
num, playback, capture, &pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
return ret;
}
fe_aif 的寻找,
比如 对于 link 1, cpu_dai_naem 是“MultiMedia1" ,
{
.name = "SDP4430 Media",
.stream_name = "Multimedia",
/* ABE components - MM-UL & MM_DL */
.cpu_dai_name = "MultiMedia1",
.platform_name = "omap-pcm-audio",
.dynamic = 1, /* BE is dynamic */
.supported_be = mm1_be,
.num_be = ARRAY_SIZE(mm1_be),
.fe_playback_channels = 2,
.fe_capture_channels = 8,
.no_host_mode = SND_SOC_DAI_LINK_OPT_HOST,
},
rtd->pcm = pcm;
pcm->private_data = rtd;
substream[SNDRV_PCM_STREAM_PLAYBACK] =
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream[SNDRV_PCM_STREAM_CAPTURE] =
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (rtd->dai_link->no_pcm) {
if (playback)
substream[SNDRV_PCM_STREAM_PLAYBACK]->private_data = rtd;
if (capture)
substream[SNDRV_PCM_STREAM_CAPTURE]->private_data = rtd;
goto out;
}
/* setup any hostless PCMs - i.e. no host IO is performed */
if (rtd->dai_link->no_host_mode) {
if (substream[SNDRV_PCM_STREAM_PLAYBACK]) {
substream[SNDRV_PCM_STREAM_PLAYBACK]->hw_no_buffer = 1;
snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_PLAYBACK],
&no_host_hardware);
}
if (substream[SNDRV_PCM_STREAM_CAPTURE]) {
substream[SNDRV_PCM_STREAM_CAPTURE]->hw_no_buffer = 1;
snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_CAPTURE],
&no_host_hardware);
}
}
/* ASoC PCM operations */
rtd->ops.open = snd_soc_pcm_open;
rtd->ops.hw_params = snd_soc_pcm_hw_params;
rtd->ops.prepare = snd_soc_pcm_prepare;
rtd->ops.trigger = snd_soc_pcm_trigger;
rtd->ops.hw_free = snd_soc_pcm_hw_free;
rtd->ops.close = snd_soc_pcm_close;
rtd->ops.pointer = snd_soc_pcm_pointer;
rtd->ops.ioctl = snd_soc_pcm_ioctl;
rtd->ops.ack = platform->driver->ops->ack;
rtd->ops.copy = platform->driver->ops->copy;
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
if (!platform->driver->pcm_new)
goto out;
ret = platform->driver->pcm_new(rtd);
if (ret < 0) {
printk(KERN_ERR "asoc: platform pcm constructor failed\n");
return ret;
}
out:
pcm->private_free = platform->driver->pcm_free;
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
* startup for the cpu DAI, platform, machine and codec DAI.
*/
int snd_soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
int ret = 0;
mutex_lock(&rtd->pcm_mutex);
/* Work out backend DAI's if we are a frontend */
if (rtd->dai_link->dynamic) {
ret = snd_soc_get_backend_dais(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: no valid backend routes for PCM: %s\n",
dev_name(&rtd->dev));
goto out;
}
}
/* Are we the backend and already enabled */
if (rtd->dai_link->no_pcm) {
if (rtd->fe_clients == 0) {
dev_err(&rtd->dev, "operations not permitted on backend DAI\n");
ret = -ENODEV;
goto out;
}
if (rtd->be_active++)
goto no_pcm;
}
if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
snd_soc_set_runtime_hwparams(substream, &no_host_hardware);
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
goto out;
}
}
if (platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
goto platform_err;
}
}
if (codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",
codec_dai->name);
goto codec_dai_err;
}
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
goto machine_err;
}
}
if (rtd->dai_link->no_pcm)
goto no_pcm;
/* Check that the codec and cpu DAI's are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
max(codec_dai_drv->playback.rate_min,
cpu_dai_drv->playback.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->playback.rate_max,
cpu_dai_drv->playback.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->playback.channels_min,
cpu_dai_drv->playback.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->playback.channels_max,
cpu_dai_drv->playback.channels_max);
runtime->hw.formats =
codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
runtime->hw.rates =
codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
if (codec_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->playback.rates;
if (cpu_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->playback.rates;
} else {
runtime->hw.rate_min =
max(codec_dai_drv->capture.rate_min,
cpu_dai_drv->capture.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->capture.rate_max,
cpu_dai_drv->capture.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->capture.channels_min,
cpu_dai_drv->capture.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->capture.channels_max,
cpu_dai_drv->capture.channels_max);
runtime->hw.formats =
codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
runtime->hw.rates =
codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
if (codec_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->capture.rates;
if (cpu_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->capture.rates;
}
snd_pcm_limit_hw_rates(runtime);
if (!runtime->hw.rates) {
printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active || codec_dai->active) {
ret = soc_pcm_apply_symmetry(substream);
if (ret != 0)
goto config_err;
}
pr_debug("asoc: %s <-> %s info:\n",
codec_dai->name, cpu_dai->name);
pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
no_pcm:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
codec_dai->playback_active++;
} else {
cpu_dai->capture_active++;
codec_dai->capture_active++;
}
cpu_dai->active++;
codec_dai->active++;
rtd->codec->active++;
mutex_unlock(&rtd->pcm_mutex);
return 0;
config_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
machine_err:
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
if (platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
if (rtd->dai_link->dynamic)
snd_soc_put_backend_dais(substream);
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
int snd_soc_get_backend_dais(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
int i, num;
const char *fe_aif = NULL, *be_aif;
enum snd_soc_dapm_type fe_type, be_type;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
fe_type = snd_soc_dapm_aif_in;
be_type = snd_soc_dapm_aif_out;
} else {
fe_type = snd_soc_dapm_aif_out;
be_type = snd_soc_dapm_aif_in;
}
/* search card for valid frontend steams */
for (i = 0; i < card->num_links; i++) {
/* check for frontend */
if (card->rtd[i].dai_link->dynamic)
continue;
fe_aif = snd_soc_dapm_get_aif(card->rtd[i].platform->dapm,
cpu_dai->driver->name, fe_type);
}
if (fe_aif == NULL) {
dev_err(&rtd->dev, "no frontend widgets for stream %s\n",
cpu_dai->driver->name);
return 0;
} else
dev_dbg(&rtd->dev, "got fe %s\n", fe_aif);
/* search card for valid backends */
for (i = 0; i < card->num_links; i++) {
/* check for frontend */
if (card->rtd[i].dai_link->dynamic)
continue;
/* backends must belong to this frontend */
if (card->rtd[i].dai_link->no_pcm) {
/* 在frontend支持的所有的 backend中找be_aif, 如果找到则返回widgets 的名称 ×/
if (!is_be_supported(rtd, card->rtd[i].dai_link->name))
continue;
be_aif = snd_soc_dapm_get_aif(card->rtd[i].platform->dapm,
card->rtd[i].dai_link->stream_name, be_type);
if (be_aif == NULL) {
dev_dbg(&rtd->dev, "no backend widget for stream %s\n",
card->rtd[i].dai_link->stream_name);
continue;
}
dev_dbg(&rtd->dev, "got be %s for stream %s\n", be_aif,
card->rtd[i].dai_link->stream_name);
/* check for valid path */
/× 在两个aif widgets中找到一条有效的路径 ×/
num = snd_soc_scenario_set_path(card->rtd[i].platform->dapm,
fe_aif, be_aif, substream->stream);
/* add backend if we have space */
if (num > 0) {
if (rtd->num_be[substream->stream] == SND_SOC_MAX_BE)
dev_dbg(&rtd->dev, "no more backends permitted\n");
else {
dev_dbg(&rtd->dev, "** active path for %s to %s\n", fe_aif, be_aif);
rtd->be_rtd[rtd->num_be[substream->stream]++][substream->stream] = &card->rtd[i];
card->rtd[i].fe_clients++;
}
}
}
}
return rtd->num_be[substream->stream] ? rtd->num_be[substream->stream] : -EINVAL;
}
fe_aif 的获取,主要是根据cpu dai driver 的name 和widgets 的sname 做对比, 如果widgets 的sname 包含了
cpu dai driver 的name且类型一致则找到 fe_aif
const char *snd_soc_dapm_get_aif(struct snd_soc_dapm_context *dapm,
const char *stream_name, enum snd_soc_dapm_type type)
{
struct snd_soc_dapm_widget *w;
list_for_each_entry(w, &dapm->widgets, list) {
if (!w->sname)
continue;
if (w->id == type && strstr(w->sname, stream_name))
return w->name;
}
return NULL;
}