我對linux理解之alsa一
阿新 • • 發佈:2019-01-06
------------------------------------------
本文系本站原創,歡迎轉載!
------------------------------------------
我們以imx51為平臺,去分析alsa的架構。
有兩個檔案跟平臺具體相關的:
一個是跟cpu的音訊介面相關的:sound/soc/imx/imx-3stack-wm8994.c;
另一個是跟codec晶片有關的:sound/soc/codecs/wm8994.c
我們先看imx-3stack-wm8994.c中的初始化:
static int __init imx_3stack_init(void)
{
int ret;
ret = platform_driver_register(&imx_3stack_wm8994_audio_driver);//註冊audio介面驅動
if (ret)
return -ENOMEM;
imx_3stack_snd_device = platform_device_alloc("soc-audio", 2);//名字是soc-audio
if (!imx_3stack_snd_device)
return -ENOMEM;
platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);//設定data
imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
ret = platform_device_add(imx_3stack_snd_device);//將與soc_core中的platform匹配
if (ret)
platform_device_put(imx_3stack_snd_device);
return ret;
}
這裡面主要有兩個工作:
1,platform_driver_register(&imx_3stack_wm8994_audio_driver);//註冊audio介面驅動
2,platform_device_add(imx_3stack_snd_device);//將與soc_core中的platform匹配
我們先分析第一個工作,platform_driver_register(&imx_3stack_wm8994_audio_driver):
imx_3stack_wm8994_audio_driver定義:
static struct platform_driver imx_3stack_wm8994_audio_driver = {
.probe = imx_3stack_wm8994_probe,
.remove = imx_3stack_wm8994_remove,
.driver = {
.name = "imx-3stack-wm8994",
},
};
對應它的device:
static struct platform_device mxc_wm8994_device = {
.name = "imx-3stack-wm8994",
};
mxc_register_device(&mxc_wm8994_device, &wm8994_data);//設定了mxc_wm8994_device的data了
wm8994_data定義:
static struct mxc_audio_platform_data wm8994_data = {
.ssi_num = 1,
.src_port = 2,
.ext_port = 4,
.hp_irq = IOMUX_TO_IRQ_V3(F101_HEADSET_DET),
.vdda_reg = "VGEN3",
.vddd_reg = "VIOHI",
.vdda = 1800000,
.vddd = 2775000,
.sysclk =24000000,
.hp_status = wm8994_headset_det_status,
.amp_enable = mxc_wm8994_amp_enable,
.init = mxc_wm8994_plat_init,
.finit = mxc_wm8994_plat_finit,
};
這樣我們確認系統中device跟imx_3stack_wm8994_audio_driver對應,那下面會執行probe函式:
imx_3stack_wm8994_probe:
static int __devinit imx_3stack_wm8994_probe(struct platform_device *pdev)
{
struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
struct imx_3stack_priv *priv = &card_priv;
struct snd_soc_dai *wm8994_cpu_dai;
int ret = 0;
priv->pdev = pdev;
imx_3stack_init_dam(plat->src_port, plat->ext_port);//初始化ssi和dai口子
if (plat->src_port == 2)//由wm8994_data定義知道src_port=2
wm8994_cpu_dai = imx_ssi_dai[2];
else
wm8994_cpu_dai = imx_ssi_dai[0];
imx_3stack_dai.cpu_dai = wm8994_cpu_dai;//設定cpu的音訊介面
ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);//建立耳機的屬性檔案
if (ret < 0) {
pr_err("%s:failed to create driver_attr_headphone\n", __func__);
goto sysfs_err;
}
if (plat->init && plat->init())//如果plat的init定義,則執行
goto err_plat_init;
if (plat->hp_status())//根據耳機的初始狀態設定中斷
ret = request_irq(plat->hp_irq,
imx_headphone_detect_handler,
IRQ_TYPE_EDGE_RISING, pdev->name, priv);
else
ret = request_irq(plat->hp_irq,
imx_headphone_detect_handler,
IRQ_TYPE_EDGE_FALLING, pdev->name, priv);
if (ret < 0) {
pr_err("%s: request irq failed\n", __func__);
goto err_card_reg;
}
priv->sysclk = plat->sysclk;
/* The WM8994 has an internal reset that is deasserted 8 SYS_MCLK
cycles after all power rails have been brought up. After this time
communication can start */
wm8994_jack_func = 1;
wm8994_spk_func = 1;//預設是speaker功能
wm8994_line_in_func = 0;
wm8994_modem1_func = 0;
wm8994_modem2_func = 0;
wm8994_psdev = kzalloc(sizeof(struct switch_dev), GFP_KERNEL);//申請一個switch_dev空間
if (wm8994_psdev == NULL) {
ret = -ENOMEM;
goto err_card_reg;
}
wm8994_psdev->name = "h2w";
wm8994_psdev->print_name = h2w_print_name;
ret = switch_dev_register(wm8994_psdev);//註冊一個switch_dev
if (ret < 0) {
pr_err("%s:failed to register switch device\n", __func__);
goto err_switchdev;
}
return 0;
err_switchdev:
kfree(wm8994_psdev);
err_card_reg:
if (plat->finit)
plat->finit();
err_plat_init:
driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
sysfs_err:
return ret;
}
imx_3stack_init的第一個工作就結束了,下面我們看下第二個工作:platform_device_add(imx_3stack_snd_device),我們由
imx_3stack_snd_device組成過程知道,它的名字是"soc-audio",data是imx_3stack_snd_devdata。這個data是重中之重。
我們看下它的定義過程:
static struct snd_soc_device imx_3stack_snd_devdata = {
.card = &snd_soc_card_imx_3stack,//1
.codec_dev = &soc_codec_dev_wm8994,//2
};
(1)card定義如下:
static struct snd_soc_card snd_soc_card_imx_3stack = {
.name = "imx-3stack",
.platform = &imx_soc_platform,//1-1
.dai_link = &imx_3stack_dai,//連線了cpu的dai和codec的dai,1-2
.num_links = 1,
.remove = imx_3stack_card_remove,
};
(1-1)
struct snd_soc_platform imx_soc_platform = {//主要是pcm的處理
.name = "imx-audio",
.pcm_ops = &imx_pcm_ops,//1-1-2
.pcm_new = imx_pcm_new,
.pcm_free = imx_pcm_free_dma_buffers,
};
(1-1-2)
struct snd_pcm_ops imx_pcm_ops = {//pcm的ops
.open = imx_pcm_open,
.close = imx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = imx_pcm_hw_params,
.hw_free = imx_pcm_hw_free,
.prepare = imx_pcm_prepare,
.trigger = imx_pcm_trigger,
.pointer = imx_pcm_pointer,
.mmap = imx_pcm_mmap,
};
(1-2)
static struct snd_soc_dai_link imx_3stack_dai = {//連線codec和cpu
.name = "WM8994",
.stream_name = "WM8994",
.codec_dai = &wm8994_dai,//1-2-1
.init = imx_3stack_wm8994_init,
.ops = &imx_3stack_ops,//1-2-2
};
在第一個工作中的probe函式裡有這樣的語句imx_3stack_dai.cpu_dai = wm8994_cpu_dai,它定義了dai_link的cpu_dai,我們放到1-3中分析。
(1-2-1)
struct snd_soc_dai wm8994_dai = {
.name = "WM8994",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = WM8994_RATES,
.formats = WM8994_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8994_RATES,
.formats = WM8994_FORMATS,
},
.ops = &wm8994_ops,//1-2-1-1
.symmetric_rates = 1,
};
(1-2-1-1),
struct snd_soc_dai_ops wm8994_ops = {
.prepare = wm8994_pcm_prepare,
.startup = wm8994_pcm_startup,
.trigger = wm8994_pcm_trigger,
.hw_free = wm8994_pcm_free,
.shutdown = wm8994_pcm_shutdown,
.hw_params = wm8994_pcm_hw_params,
.digital_mute = wm8994_digital_mute,
.set_fmt = wm8994_set_dai_fmt,
// .set_pll = wm8994_set_fll,
.set_sysclk = wm8994_set_dai_sysclk,
};
(1-2-2)
static struct snd_soc_ops imx_3stack_ops = {
.startup = imx_3stack_startup,
.shutdown = imx_3stack_shutdown,
.hw_params = imx_3stack_audio_hw_params,
};
(1-3)
imx_3stack_dai.cpu_dai = wm8994_cpu_dai,從第一個工作中分析知道它對應 imx_ssi_dai[2]。
這個imx_ssi_dai在sound/soc/imx/imx-ssi.c的probe函式裡有賦值。
(2).codec_dev = &soc_codec_dev_wm8994:
定義在sound/soc/codecs/wm8994.c中:
struct snd_soc_codec_device soc_codec_dev_wm8994 = {
.probe = wm8994_probe,
.remove = wm8994_remove,
.suspend = wm8994_suspend,
.resume = wm8994_resume,
};
第2個工作platform_device_add(imx_3stack_snd_device)執行的時候它會尋找與自己名字相同的driver去匹配,然後去執行它們的probe探測函式。那麼它對應的driver在哪呢?
我們通過名字"soc-audio"可以在sound/soc/soc-core.c中找到對應的驅動定義:
/* ASoC platform driver */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
},
.probe = soc_probe,
.remove = soc_remove,
.suspend = soc_suspend,
.resume = soc_resume,
};
soc_probe:
static int soc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);//得到pdev的data,即imx_3stack_snd_devdata
struct snd_soc_card *card = socdev->card;//從soc_device中得到card
/* Bodge while we push things out of socdev */
card->socdev = socdev;//在card中儲存soc_device
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);//註冊card,新增到card列表裡面
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}
我們看snd_soc_register_card(card):
static int snd_soc_register_card(struct snd_soc_card *card)
{
if (!card->name || !card->dev)
return -EINVAL;
INIT_LIST_HEAD(&card->list);
card->instantiated = 0;
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);
return 0;
}
我們從函式定義中知道snd_soc_register_card主要就是將card新增到card_list中,然後執行snd_soc_instantiate_cards():
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list) //對card列表中的每個音效卡
snd_soc_instantiate_card(card);//執行card初始化動作
}
對card_list中每一個card都執行snd_soc_instantiate_card(card):
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct platform_device *pdev = container_of(card->dev,//對應soc_probe中的pdev->dev
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;//從soc_probe函式中分析知道對應soc_codec_dev_wm8994
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
if (card->instantiated)//已經初始化則退出
return;
found = 0;
list_for_each_entry(platform, &platform_list, list)//對platform列表中的每個platform
if (card->platform == platform) {//對應imx_soc_platform,檢查是否能找到,代表是否已經註冊
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return;
}
ac97 = 0;
for (i = 0; i < card->num_links; i++) {//num_links=1
found = 0;
list_for_each_entry(dai, &dai_list, list) {
if (card->dai_link[i].cpu_dai == dai) {//檢查cpu dai是否註冊,對應imx_ssi_dai[2]
found = 1;
break;
}
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].cpu_dai->name);
return;
}
if (card->dai_link[i].cpu_dai->ac97_control)//i.mx51沒定義
ac97 = 1;
}
/* If we have AC97 in the system then don't wait for the
* codec. This will need revisiting if we have to handle
* systems with mixed AC97 and non-AC97 parts. Only check for
* DAIs currently; we can't do this per link since some AC97
* codecs have non-AC97 DAIs.
*/
if (!ac97)
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list)
if (card->dai_link[i].codec_dai == dai) {//檢查codec dai,對應wm8994_dai
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].codec_dai->name);
return;
}
}
/* Note that we do not current check for codec components */
dev_dbg(card->dev, "All components present, instantiating\n");
/* Found everything, bring it up */
if (card->probe) {//在i.mx51中沒有定義
ret = card->probe(pdev);//執行音效卡的probe函式
if (ret < 0)
return;
}
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->probe) {//這個有定義,在imx-ssi中,對應了imx_ssi_probe
ret = cpu_dai->probe(pdev, cpu_dai);//執行probe函式
if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);//對應wm8994_probe
if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {//這裡platform對應imx_soc_platform,沒有定義probe
ret = platform->probe(pdev);
if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
card->instantiated = 1;//已經初始化
return;
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
for (i--; i >= 0; i--) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
if (card->remove)
card->remove(pdev);
}
在這個函式中我們接觸到platform_list和dai_list,這些list是asla中特有的。我們下面探尋一下他們形成的過程。
platform_list:
我們在sound/soc/imx/imx-pcm.c中找到imx_soc_platform定義:
struct snd_soc_platform imx_soc_platform = {
.name = "imx-audio",
.pcm_ops = &imx_pcm_ops,
.pcm_new = imx_pcm_new,
.pcm_free = imx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(imx_soc_platform);
在初始化函式裡註冊了:
static int __init imx_pcm_init(void)
{
return snd_soc_register_platform(&imx_soc_platform);//註冊了soc_platform,新增到platform列表裡
}
我們看下snd_soc_register_platform(&imx_soc_platform):
int snd_soc_register_platform(struct snd_soc_platform *platform)
{
if (!platform->name)
return -EINVAL;
INIT_LIST_HEAD(&platform->list);
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);//新增到platform列表裡面,這個platform_list是屬於soc子系統的
snd_soc_instantiate_cards();//初始化音效卡,上面已分析
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
return 0;
}
將imx_soc_platform新增到了platform_list裡面了。
dai_list:
我們在sound/soc/imx/imx-ssi.c中找到imx_ssi_dai賦值過程。首先看它的初始化
static int __init imx_ssi_init(void)
{
return platform_driver_register(&imx_ssi_driver);
}
static struct platform_driver imx_ssi_driver = {
.probe = imx_ssi_dev_probe,
.remove = __devexit_p(imx_ssi_dev_remove),
.driver = {
.name = "mxc_ssi",
},
};
我們可以找到它的device定義:
struct platform_device mxc_ssi2_device = {
.name = "mxc_ssi",
.id = 1,
.num_resources = ARRAY_SIZE(ssi2_resources),
.resource = ssi2_resources,
};
這樣它們匹配後將會執行probe函式:
static int imx_ssi_dev_probe(struct platform_device *pdev)
{
int fifo0_channel = pdev->id * 2;
struct snd_soc_dai *dai;
struct imx_ssi *priv;
int fifo, channel;
struct resource *res;
int ret;
BUG_ON(fifo0_channel >= MAX_SSI_CHANNELS);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
priv = kzalloc(sizeof(struct imx_ssi), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Each SSI block has 2 fifos which share the same
private data (struct imx_ssi) */
priv->baseaddr = res->start;
priv->ioaddr = ioremap(res->start, 0x5C);
priv->irq = platform_get_irq(pdev, 0);
priv->ssi_clk = clk_get(&pdev->dev, "ssi_clk");
priv->pdev = pdev;
for (fifo = 0; fifo < 2; fifo++) {
channel = (pdev->id * 2) + fifo;
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);//申請snd_soc_dai空間
if (IS_ERR(dai)) {
ret = -ENOMEM;
goto DAI_ERR;
}
dai->name = kasprintf(GFP_KERNEL, "imx-ssi-%d-%d",
pdev->id + 1, fifo);
if (IS_ERR(dai->name)) {
kfree(dai);
ret = -ENOMEM;
goto DAI_ERR;
}
//一些列初始化
dai->probe = imx_ssi_probe;
dai->suspend = imx_ssi_suspend;
dai->remove = imx_ssi_remove;
dai->resume = imx_ssi_resume;
dai->playback.channels_min = 1;
dai->playback.channels_max = 2;
dai->playback.rates = IMX_SSI_RATES;
dai->playback.formats = IMX_SSI_FORMATS;
dai->capture.channels_min = 1;
dai->capture.channels_max = 2;
dai->capture.rates = IMX_SSI_RATES;
dai->capture.formats = IMX_SSI_FORMATS;
dai->ops = &imx_ssi_dai_ops;
dai->private_data = priv;
dai->id = channel;
imx_ssi_dai[channel] = dai;//賦值給imx_ssi_dai,由上面知道,最後賦值給了imx_3stack_dai.cpu_dai,這就是cpu_dai的由來過程
ret = snd_soc_register_dai(dai);//註冊dai
if (ret < 0) {
kfree(dai->name);
kfree(dai);
goto DAI_ERR;
}
}
return 0;
DAI_ERR:
if (fifo == 1) {
dai = imx_ssi_dai[fifo0_channel];
snd_soc_unregister_dai(dai);
kfree(dai->name);
kfree(dai);
}
clk_put(priv->ssi_clk);
iounmap(priv->ioaddr);
kfree(priv);
return ret;
}
我們看下snd_soc_register_dai(dai):
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
if (!dai->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
if (!dai->ops)
dai->ops = &null_dai_ops;
INIT_LIST_HEAD(&dai->list);//初始化dai列表
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);//新增到dai列表裡面
snd_soc_instantiate_cards();//初始化,這個上面以分析
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
我們看到cpu_dai新增到了alsa的dai_list裡了。
到此為止我們主要分析了cpu端的音訊初始化工作,下面我們看下codec端的初始化工作:
static const struct i2c_device_id wm8994_id[] = {
{"wm8994_i2c", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, wm8994_id);
static struct i2c_driver wm8994_i2c_driver = {
.driver = {
.name = "wm8994_i2c",
.owner = THIS_MODULE,
},
.probe = wm8994_i2c_probe,
.remove = wm8994_i2c_remove,
.id_table = wm8994_id,
};
static int __init wm8994_modinit(void)
{
int ret;
ret = i2c_add_driver(&wm8994_i2c_driver);
if (ret != 0)
pr_err("WM8994: Unable to register I2C driver: %d\n", ret);
return ret;
}
在device定義裡有:
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
{
.type = "wm8994_i2c",
.addr = 0x1a,
.platform_data = &wm8994_data,
},
......
}
i2c驅動和裝置匹配成功後將執行probe函式:
static int wm8994_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8994_priv *wm8994;
struct snd_soc_codec *codec;//編解碼器結構
struct mxc_audio_platform_data *plat = client->dev.platform_data;
struct regulator *reg;
int ret = 0;
if (wm8994_codec) {
dev_err(&client->dev,
"Multiple WM8994 devices not supported\n");
return -ENOMEM;
}
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL);
if (wm8994 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = wm8994;//賦值codec的私有資料
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);//dapm,動態音訊電源管理
INIT_LIST_HEAD(&codec->dapm_paths);
i2c_set_clientdata(client, codec);//將codec賦值為client私有資料
codec->control_data = client;//將client賦值給codec的control_data
if (plat->vddio_reg) {
reg = regulator_get(&client->dev, plat->vddio_reg);
if (IS_ERR(reg))
goto err_reg_vddio;
wm8994->reg_vddio = reg;
}
if (plat->vdda_reg) {
reg = regulator_get(&client->dev, plat->vdda_reg);
if (IS_ERR(reg))
goto err_reg_vdda;
wm8994->reg_vdda = reg;
}
if (plat->vddd_reg) {
reg = regulator_get(&client->dev, plat->vddd_reg);
if (IS_ERR(reg))
goto err_reg_vddd;
wm8994->reg_vddd = reg;
}
if (wm8994->reg_vdda) {
ret = regulator_set_voltage(wm8994->reg_vdda,
plat->vdda, plat->vdda);
regulator_enable(wm8994->reg_vdda);
}
if (wm8994->reg_vddio) {
regulator_set_voltage(wm8994->reg_vddio,
plat->vddio, plat->vddio);
regulator_enable(wm8994->reg_vddio);
}
if (wm8994->reg_vddd) {
regulator_set_voltage(wm8994->reg_vddd,
plat->vddd, plat->vddd);
regulator_enable(wm8994->reg_vddd);
}
if (plat->amp_enable)
plat->amp_enable(1);
mdelay(10);
wm8994_write(codec, WM8994_SOFTWARE_RESET, 0xFFFF);
mdelay(50);
wm8994->cur_mode = WM8994_MODE_NORMAL;
wm8994->old_mode = MODE_END;
wm8994->playback = 0;
wm8994->capture = 0;
wm8994->start_count = 0;
wm8994->revision = wm8994_hw_read(codec, WM8994_SOFTWARE_RESET);
printk("WM8994 revision %x\n", wm8994->revision);
if (wm8994->revision == 0)
goto err_codec_reg;
//codec初始化工作
codec->dev = &client->dev;
codec->name = "WM8994";
codec->owner = THIS_MODULE;
codec->read = wm8994_read;
codec->write = wm8994_write;
codec->hw_write = (hw_write_t)i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8994_set_bias_level;
codec->dai = &wm8994_dai;
codec->num_dai = 1;
codec->reg_cache_size = sizeof(wm8994_regs_default);
codec->reg_cache_step = 2;
codec->reg_cache = (void *)&wm8994_regs_default;
wm8994_codec = codec;
wm8994_dai.dev = &client->dev;
ret = snd_soc_register_codec(codec);//新增到codec列表,然後初始化音效卡
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err_codec_reg;
}
ret = snd_soc_register_dai(&wm8994_dai);//新增到dai列表裡,然後執行初始化音效卡函式
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
goto err_codec_reg;
}
return 0;
err_codec_reg:
if (wm8994->reg_vddd)
regulator_put(wm8994->reg_vddd);
err_reg_vddd:
if (wm8994->reg_vdda)
regulator_put(wm8994->reg_vdda);
err_reg_vdda:
if (wm8994->reg_vddio)
regulator_put(wm8994->reg_vddio);
err_reg_vddio:
kfree(wm8994);
kfree(codec);
return ret;
}
我們看下snd_soc_register_codec(codec):
int snd_soc_register_codec(struct snd_soc_codec *codec)
{
int i;
if (!codec->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!codec->dev)
printk(KERN_WARNING "No device for codec %s\n", codec->name);
INIT_LIST_HEAD(&codec->list);//初始化codec列表
for (i = 0; i < codec->num_dai; i++) {
fixup_codec_formats(&codec->dai[i].playback);
fixup_codec_formats(&codec->dai[i].capture);
}
mutex_lock(&client_mutex);
list_add(&codec->list, &codec_list);//新增到codec列表裡面
snd_soc_instantiate_cards();//初始化音效卡,上面已做分析
mutex_unlock(&client_mutex);
pr_debug("Registered codec '%s'\n", codec->name);
return 0;
}
我們再看下snd_soc_register_dai(&wm8994_dai):
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
if (!dai->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
if (!dai->ops)
dai->ops = &null_dai_ops;
INIT_LIST_HEAD(&dai->list);//初始化dai列表
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);//新增到dai列表裡面
snd_soc_instantiate_cards();//初始化,上面已做分析
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
我們看到codec的dai也新增到dai_list中了。
至此為止,cpu和codec的初始化工作都基本完成,下面才會真正執行上面分析的snd_soc_instantiate_cards(),我們再貼一遍,以便分析:
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list) //對card列表中的每個音效卡
snd_soc_instantiate_card(card);//執行card初始化動作
}
對card_list中每一個card都執行snd_soc_instantiate_card(card):
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct platform_device *pdev = container_of(card->dev,//對應soc_probe中的pdev->dev
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;//從soc_probe函式中分析知道對應soc_codec_dev_wm8994
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
if (card->instantiated)//已經初始化則退出
return;
found = 0;
list_for_each_entry(platform, &platform_list, list)//對platform列表中的每個platform
if (card->platform == platform) {//對應imx_soc_platform,檢查是否能找到,代表是否已經註冊
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return;
}
ac97 = 0;
for (i = 0; i < card->num_links; i++) {//num_links=1
found = 0;
list_for_each_entry(dai, &dai_list, list) {
if (card->dai_link[i].cpu_dai == dai) {//檢查cpu dai是否註冊,對應imx_ssi_dai[2]
found = 1;
break;
}
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].cpu_dai->name);
return;
}
if (card->dai_link[i].cpu_dai->ac97_control)//i.mx51沒定義
ac97 = 1;
}
/* If we have AC97 in the system then don't wait for the
* codec. This will need revisiting if we have to handle
* systems with mixed AC97 and non-AC97 parts. Only check for
* DAIs currently; we can't do this per link since some AC97
* codecs have non-AC97 DAIs.
*/
if (!ac97)
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list)
if (card->dai_link[i].codec_dai == dai) {//檢查codec dai,對應wm8994_dai
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].codec_dai->name);
return;
}
}
/* Note that we do not current check for codec components */
dev_dbg(card->dev, "All components present, instantiating\n");
/* Found everything, bring it up */
if (card->probe) {//在i.mx51中沒有定義
ret = card->probe(pdev);//執行音效卡的probe函式
if (ret < 0)
return;
}
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->probe) {//這個有定義,在imx-ssi中,對應了imx_ssi_probe
ret = cpu_dai->probe(pdev, cpu_dai);//執行probe函式
if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);//對應wm8994_probe
if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {//這裡platform對應imx_soc_platform,沒有定義probe
ret = platform->probe(pdev);
if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
card->instantiated = 1;//已經初始化
return;
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
for (i--; i >= 0; i--) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
if (card->remove)
card->remove(pdev);
}
在都進行了platform_list和dai_list註冊後,會執行cpu_dai->probe和codec_dev->probe,它們分別對應imx_ssi_probe和wm8994_probe。我們先看imx_ssi_probe:
static int imx_ssi_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
{
struct imx_ssi *priv = (struct imx_ssi *)dai->private_data;
if (priv->irq >= 0) {
if (request_irq(priv->irq, imx_ssi_irq, IRQF_SHARED,
pdev->name, priv)) {
printk(KERN_ERR "%s: failure requesting irq for %s\n",
__func__, pdev->name);
return -EBUSY;
}
}
return 0;
}
我們看到主要註冊了ssi的中斷函式。
wm8994_probe見下一節。
我們以imx51為平臺,去分析alsa的架構。
有兩個檔案跟平臺具體相關的:
一個是跟cpu的音訊介面相關的:sound/soc/imx/imx-3stack-wm8994.c;
另一個是跟codec晶片有關的:sound/soc/codecs/wm8994.c
我們先看imx-3stack-wm8994.c中的初始化:
static int __init imx_3stack_init(void)
{
int ret;
ret = platform_driver_register(&imx_3stack_wm8994_audio_driver);//註冊audio介面驅動
if (ret)
return -ENOMEM;
imx_3stack_snd_device = platform_device_alloc("soc-audio", 2);//名字是soc-audio
if (!imx_3stack_snd_device)
return -ENOMEM;
platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);//設定data
imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
ret = platform_device_add(imx_3stack_snd_device);//將與soc_core中的platform匹配
if (ret)
platform_device_put(imx_3stack_snd_device);
return ret;
}
這裡面主要有兩個工作:
1,platform_driver_register(&imx_3stack_wm8994_audio_driver);//註冊audio介面驅動
2,platform_device_add(imx_3stack_snd_device);//將與soc_core中的platform匹配
我們先分析第一個工作,platform_driver_register(&imx_3stack_wm8994_audio_driver):
imx_3stack_wm8994_audio_driver定義:
static struct platform_driver imx_3stack_wm8994_audio_driver = {
.probe = imx_3stack_wm8994_probe,
.remove = imx_3stack_wm8994_remove,
.driver = {
.name = "imx-3stack-wm8994",
},
};
對應它的device:
static struct platform_device mxc_wm8994_device = {
.name = "imx-3stack-wm8994",
};
mxc_register_device(&mxc_wm8994_device, &wm8994_data);//設定了mxc_wm8994_device的data了
wm8994_data定義:
static struct mxc_audio_platform_data wm8994_data = {
.ssi_num = 1,
.src_port = 2,
.ext_port = 4,
.hp_irq = IOMUX_TO_IRQ_V3(F101_HEADSET_DET),
.vdda_reg = "VGEN3",
.vddd_reg = "VIOHI",
.vdda = 1800000,
.vddd = 2775000,
.sysclk =24000000,
.hp_status = wm8994_headset_det_status,
.amp_enable = mxc_wm8994_amp_enable,
.init = mxc_wm8994_plat_init,
.finit = mxc_wm8994_plat_finit,
};
這樣我們確認系統中device跟imx_3stack_wm8994_audio_driver對應,那下面會執行probe函式:
imx_3stack_wm8994_probe:
static int __devinit imx_3stack_wm8994_probe(struct platform_device *pdev)
{
struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
struct imx_3stack_priv *priv = &card_priv;
struct snd_soc_dai *wm8994_cpu_dai;
int ret = 0;
priv->pdev = pdev;
imx_3stack_init_dam(plat->src_port, plat->ext_port);//初始化ssi和dai口子
if (plat->src_port == 2)//由wm8994_data定義知道src_port=2
wm8994_cpu_dai = imx_ssi_dai[2];
else
wm8994_cpu_dai = imx_ssi_dai[0];
imx_3stack_dai.cpu_dai = wm8994_cpu_dai;//設定cpu的音訊介面
ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);//建立耳機的屬性檔案
if (ret < 0) {
pr_err("%s:failed to create driver_attr_headphone\n", __func__);
goto sysfs_err;
}
if (plat->init && plat->init())//如果plat的init定義,則執行
goto err_plat_init;
if (plat->hp_status())//根據耳機的初始狀態設定中斷
ret = request_irq(plat->hp_irq,
imx_headphone_detect_handler,
IRQ_TYPE_EDGE_RISING, pdev->name, priv);
else
ret = request_irq(plat->hp_irq,
imx_headphone_detect_handler,
IRQ_TYPE_EDGE_FALLING, pdev->name, priv);
if (ret < 0) {
pr_err("%s: request irq failed\n", __func__);
goto err_card_reg;
}
priv->sysclk = plat->sysclk;
/* The WM8994 has an internal reset that is deasserted 8 SYS_MCLK
cycles after all power rails have been brought up. After this time
communication can start */
wm8994_jack_func = 1;
wm8994_spk_func = 1;//預設是speaker功能
wm8994_line_in_func = 0;
wm8994_modem1_func = 0;
wm8994_modem2_func = 0;
wm8994_psdev = kzalloc(sizeof(struct switch_dev), GFP_KERNEL);//申請一個switch_dev空間
if (wm8994_psdev == NULL) {
ret = -ENOMEM;
goto err_card_reg;
}
wm8994_psdev->name = "h2w";
wm8994_psdev->print_name = h2w_print_name;
ret = switch_dev_register(wm8994_psdev);//註冊一個switch_dev
if (ret < 0) {
pr_err("%s:failed to register switch device\n", __func__);
goto err_switchdev;
}
return 0;
err_switchdev:
kfree(wm8994_psdev);
err_card_reg:
if (plat->finit)
plat->finit();
err_plat_init:
driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
sysfs_err:
return ret;
}
imx_3stack_init的第一個工作就結束了,下面我們看下第二個工作:platform_device_add(imx_3stack_snd_device),我們由
imx_3stack_snd_device組成過程知道,它的名字是"soc-audio",data是imx_3stack_snd_devdata。這個data是重中之重。
我們看下它的定義過程:
static struct snd_soc_device imx_3stack_snd_devdata = {
.card = &snd_soc_card_imx_3stack,//1
.codec_dev = &soc_codec_dev_wm8994,//2
};
(1)card定義如下:
static struct snd_soc_card snd_soc_card_imx_3stack = {
.name = "imx-3stack",
.platform = &imx_soc_platform,//1-1
.dai_link = &imx_3stack_dai,//連線了cpu的dai和codec的dai,1-2
.num_links = 1,
.remove = imx_3stack_card_remove,
};
(1-1)
struct snd_soc_platform imx_soc_platform = {//主要是pcm的處理
.name = "imx-audio",
.pcm_ops = &imx_pcm_ops,//1-1-2
.pcm_new = imx_pcm_new,
.pcm_free = imx_pcm_free_dma_buffers,
};
(1-1-2)
struct snd_pcm_ops imx_pcm_ops = {//pcm的ops
.open = imx_pcm_open,
.close = imx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = imx_pcm_hw_params,
.hw_free = imx_pcm_hw_free,
.prepare = imx_pcm_prepare,
.trigger = imx_pcm_trigger,
.pointer = imx_pcm_pointer,
.mmap = imx_pcm_mmap,
};
(1-2)
static struct snd_soc_dai_link imx_3stack_dai = {//連線codec和cpu
.name = "WM8994",
.stream_name = "WM8994",
.codec_dai = &wm8994_dai,//1-2-1
.init = imx_3stack_wm8994_init,
.ops = &imx_3stack_ops,//1-2-2
};
在第一個工作中的probe函式裡有這樣的語句imx_3stack_dai.cpu_dai = wm8994_cpu_dai,它定義了dai_link的cpu_dai,我們放到1-3中分析。
(1-2-1)
struct snd_soc_dai wm8994_dai = {
.name = "WM8994",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = WM8994_RATES,
.formats = WM8994_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8994_RATES,
.formats = WM8994_FORMATS,
},
.ops = &wm8994_ops,//1-2-1-1
.symmetric_rates = 1,
};
(1-2-1-1),
struct snd_soc_dai_ops wm8994_ops = {
.prepare = wm8994_pcm_prepare,
.startup = wm8994_pcm_startup,
.trigger = wm8994_pcm_trigger,
.hw_free = wm8994_pcm_free,
.shutdown = wm8994_pcm_shutdown,
.hw_params = wm8994_pcm_hw_params,
.digital_mute = wm8994_digital_mute,
.set_fmt = wm8994_set_dai_fmt,
// .set_pll = wm8994_set_fll,
.set_sysclk = wm8994_set_dai_sysclk,
};
(1-2-2)
static struct snd_soc_ops imx_3stack_ops = {
.startup = imx_3stack_startup,
.shutdown = imx_3stack_shutdown,
.hw_params = imx_3stack_audio_hw_params,
};
(1-3)
imx_3stack_dai.cpu_dai = wm8994_cpu_dai,從第一個工作中分析知道它對應 imx_ssi_dai[2]。
這個imx_ssi_dai在sound/soc/imx/imx-ssi.c的probe函式裡有賦值。
(2).codec_dev = &soc_codec_dev_wm8994:
定義在sound/soc/codecs/wm8994.c中:
struct snd_soc_codec_device soc_codec_dev_wm8994 = {
.probe = wm8994_probe,
.remove = wm8994_remove,
.suspend = wm8994_suspend,
.resume = wm8994_resume,
};
第2個工作platform_device_add(imx_3stack_snd_device)執行的時候它會尋找與自己名字相同的driver去匹配,然後去執行它們的probe探測函式。那麼它對應的driver在哪呢?
我們通過名字"soc-audio"可以在sound/soc/soc-core.c中找到對應的驅動定義:
/* ASoC platform driver */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
},
.probe = soc_probe,
.remove = soc_remove,
.suspend = soc_suspend,
.resume = soc_resume,
};
soc_probe:
static int soc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);//得到pdev的data,即imx_3stack_snd_devdata
struct snd_soc_card *card = socdev->card;//從soc_device中得到card
/* Bodge while we push things out of socdev */
card->socdev = socdev;//在card中儲存soc_device
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);//註冊card,新增到card列表裡面
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}
我們看snd_soc_register_card(card):
static int snd_soc_register_card(struct snd_soc_card *card)
{
if (!card->name || !card->dev)
return -EINVAL;
INIT_LIST_HEAD(&card->list);
card->instantiated = 0;
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);
return 0;
}
我們從函式定義中知道snd_soc_register_card主要就是將card新增到card_list中,然後執行snd_soc_instantiate_cards():
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list) //對card列表中的每個音效卡
snd_soc_instantiate_card(card);//執行card初始化動作
}
對card_list中每一個card都執行snd_soc_instantiate_card(card):
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct platform_device *pdev = container_of(card->dev,//對應soc_probe中的pdev->dev
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;//從soc_probe函式中分析知道對應soc_codec_dev_wm8994
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
if (card->instantiated)//已經初始化則退出
return;
found = 0;
list_for_each_entry(platform, &platform_list, list)//對platform列表中的每個platform
if (card->platform == platform) {//對應imx_soc_platform,檢查是否能找到,代表是否已經註冊
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return;
}
ac97 = 0;
for (i = 0; i < card->num_links; i++) {//num_links=1
found = 0;
list_for_each_entry(dai, &dai_list, list) {
if (card->dai_link[i].cpu_dai == dai) {//檢查cpu dai是否註冊,對應imx_ssi_dai[2]
found = 1;
break;
}
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].cpu_dai->name);
return;
}
if (card->dai_link[i].cpu_dai->ac97_control)//i.mx51沒定義
ac97 = 1;
}
/* If we have AC97 in the system then don't wait for the
* codec. This will need revisiting if we have to handle
* systems with mixed AC97 and non-AC97 parts. Only check for
* DAIs currently; we can't do this per link since some AC97
* codecs have non-AC97 DAIs.
*/
if (!ac97)
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list)
if (card->dai_link[i].codec_dai == dai) {//檢查codec dai,對應wm8994_dai
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].codec_dai->name);
return;
}
}
/* Note that we do not current check for codec components */
dev_dbg(card->dev, "All components present, instantiating\n");
/* Found everything, bring it up */
if (card->probe) {//在i.mx51中沒有定義
ret = card->probe(pdev);//執行音效卡的probe函式
if (ret < 0)
return;
}
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->probe) {//這個有定義,在imx-ssi中,對應了imx_ssi_probe
ret = cpu_dai->probe(pdev, cpu_dai);//執行probe函式
if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);//對應wm8994_probe
if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {//這裡platform對應imx_soc_platform,沒有定義probe
ret = platform->probe(pdev);
if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
card->instantiated = 1;//已經初始化
return;
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
for (i--; i >= 0; i--) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
if (card->remove)
card->remove(pdev);
}
在這個函式中我們接觸到platform_list和dai_list,這些list是asla中特有的。我們下面探尋一下他們形成的過程。
platform_list:
我們在sound/soc/imx/imx-pcm.c中找到imx_soc_platform定義:
struct snd_soc_platform imx_soc_platform = {
.name = "imx-audio",
.pcm_ops = &imx_pcm_ops,
.pcm_new = imx_pcm_new,
.pcm_free = imx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(imx_soc_platform);
在初始化函式裡註冊了:
static int __init imx_pcm_init(void)
{
return snd_soc_register_platform(&imx_soc_platform);//註冊了soc_platform,新增到platform列表裡
}
我們看下snd_soc_register_platform(&imx_soc_platform):
int snd_soc_register_platform(struct snd_soc_platform *platform)
{
if (!platform->name)
return -EINVAL;
INIT_LIST_HEAD(&platform->list);
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);//新增到platform列表裡面,這個platform_list是屬於soc子系統的
snd_soc_instantiate_cards();//初始化音效卡,上面已分析
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
return 0;
}
將imx_soc_platform新增到了platform_list裡面了。
dai_list:
我們在sound/soc/imx/imx-ssi.c中找到imx_ssi_dai賦值過程。首先看它的初始化
static int __init imx_ssi_init(void)
{
return platform_driver_register(&imx_ssi_driver);
}
static struct platform_driver imx_ssi_driver = {
.probe = imx_ssi_dev_probe,
.remove = __devexit_p(imx_ssi_dev_remove),
.driver = {
.name = "mxc_ssi",
},
};
我們可以找到它的device定義:
struct platform_device mxc_ssi2_device = {
.name = "mxc_ssi",
.id = 1,
.num_resources = ARRAY_SIZE(ssi2_resources),
.resource = ssi2_resources,
};
這樣它們匹配後將會執行probe函式:
static int imx_ssi_dev_probe(struct platform_device *pdev)
{
int fifo0_channel = pdev->id * 2;
struct snd_soc_dai *dai;
struct imx_ssi *priv;
int fifo, channel;
struct resource *res;
int ret;
BUG_ON(fifo0_channel >= MAX_SSI_CHANNELS);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
priv = kzalloc(sizeof(struct imx_ssi), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Each SSI block has 2 fifos which share the same
private data (struct imx_ssi) */
priv->baseaddr = res->start;
priv->ioaddr = ioremap(res->start, 0x5C);
priv->irq = platform_get_irq(pdev, 0);
priv->ssi_clk = clk_get(&pdev->dev, "ssi_clk");
priv->pdev = pdev;
for (fifo = 0; fifo < 2; fifo++) {
channel = (pdev->id * 2) + fifo;
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);//申請snd_soc_dai空間
if (IS_ERR(dai)) {
ret = -ENOMEM;
goto DAI_ERR;
}
dai->name = kasprintf(GFP_KERNEL, "imx-ssi-%d-%d",
pdev->id + 1, fifo);
if (IS_ERR(dai->name)) {
kfree(dai);
ret = -ENOMEM;
goto DAI_ERR;
}
//一些列初始化
dai->probe = imx_ssi_probe;
dai->suspend = imx_ssi_suspend;
dai->remove = imx_ssi_remove;
dai->resume = imx_ssi_resume;
dai->playback.channels_min = 1;
dai->playback.channels_max = 2;
dai->playback.rates = IMX_SSI_RATES;
dai->playback.formats = IMX_SSI_FORMATS;
dai->capture.channels_min = 1;
dai->capture.channels_max = 2;
dai->capture.rates = IMX_SSI_RATES;
dai->capture.formats = IMX_SSI_FORMATS;
dai->ops = &imx_ssi_dai_ops;
dai->private_data = priv;
dai->id = channel;
imx_ssi_dai[channel] = dai;//賦值給imx_ssi_dai,由上面知道,最後賦值給了imx_3stack_dai.cpu_dai,這就是cpu_dai的由來過程
ret = snd_soc_register_dai(dai);//註冊dai
if (ret < 0) {
kfree(dai->name);
kfree(dai);
goto DAI_ERR;
}
}
return 0;
DAI_ERR:
if (fifo == 1) {
dai = imx_ssi_dai[fifo0_channel];
snd_soc_unregister_dai(dai);
kfree(dai->name);
kfree(dai);
}
clk_put(priv->ssi_clk);
iounmap(priv->ioaddr);
kfree(priv);
return ret;
}
我們看下snd_soc_register_dai(dai):
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
if (!dai->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
if (!dai->ops)
dai->ops = &null_dai_ops;
INIT_LIST_HEAD(&dai->list);//初始化dai列表
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);//新增到dai列表裡面
snd_soc_instantiate_cards();//初始化,這個上面以分析
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
我們看到cpu_dai新增到了alsa的dai_list裡了。
到此為止我們主要分析了cpu端的音訊初始化工作,下面我們看下codec端的初始化工作:
static const struct i2c_device_id wm8994_id[] = {
{"wm8994_i2c", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, wm8994_id);
static struct i2c_driver wm8994_i2c_driver = {
.driver = {
.name = "wm8994_i2c",
.owner = THIS_MODULE,
},
.probe = wm8994_i2c_probe,
.remove = wm8994_i2c_remove,
.id_table = wm8994_id,
};
static int __init wm8994_modinit(void)
{
int ret;
ret = i2c_add_driver(&wm8994_i2c_driver);
if (ret != 0)
pr_err("WM8994: Unable to register I2C driver: %d\n", ret);
return ret;
}
在device定義裡有:
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
{
.type = "wm8994_i2c",
.addr = 0x1a,
.platform_data = &wm8994_data,
},
......
}
i2c驅動和裝置匹配成功後將執行probe函式:
static int wm8994_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8994_priv *wm8994;
struct snd_soc_codec *codec;//編解碼器結構
struct mxc_audio_platform_data *plat = client->dev.platform_data;
struct regulator *reg;
int ret = 0;
if (wm8994_codec) {
dev_err(&client->dev,
"Multiple WM8994 devices not supported\n");
return -ENOMEM;
}
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL);
if (wm8994 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = wm8994;//賦值codec的私有資料
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);//dapm,動態音訊電源管理
INIT_LIST_HEAD(&codec->dapm_paths);
i2c_set_clientdata(client, codec);//將codec賦值為client私有資料
codec->control_data = client;//將client賦值給codec的control_data
if (plat->vddio_reg) {
reg = regulator_get(&client->dev, plat->vddio_reg);
if (IS_ERR(reg))
goto err_reg_vddio;
wm8994->reg_vddio = reg;
}
if (plat->vdda_reg) {
reg = regulator_get(&client->dev, plat->vdda_reg);
if (IS_ERR(reg))
goto err_reg_vdda;
wm8994->reg_vdda = reg;
}
if (plat->vddd_reg) {
reg = regulator_get(&client->dev, plat->vddd_reg);
if (IS_ERR(reg))
goto err_reg_vddd;
wm8994->reg_vddd = reg;
}
if (wm8994->reg_vdda) {
ret = regulator_set_voltage(wm8994->reg_vdda,
plat->vdda, plat->vdda);
regulator_enable(wm8994->reg_vdda);
}
if (wm8994->reg_vddio) {
regulator_set_voltage(wm8994->reg_vddio,
plat->vddio, plat->vddio);
regulator_enable(wm8994->reg_vddio);
}
if (wm8994->reg_vddd) {
regulator_set_voltage(wm8994->reg_vddd,
plat->vddd, plat->vddd);
regulator_enable(wm8994->reg_vddd);
}
if (plat->amp_enable)
plat->amp_enable(1);
mdelay(10);
wm8994_write(codec, WM8994_SOFTWARE_RESET, 0xFFFF);
mdelay(50);
wm8994->cur_mode = WM8994_MODE_NORMAL;
wm8994->old_mode = MODE_END;
wm8994->playback = 0;
wm8994->capture = 0;
wm8994->start_count = 0;
wm8994->revision = wm8994_hw_read(codec, WM8994_SOFTWARE_RESET);
printk("WM8994 revision %x\n", wm8994->revision);
if (wm8994->revision == 0)
goto err_codec_reg;
//codec初始化工作
codec->dev = &client->dev;
codec->name = "WM8994";
codec->owner = THIS_MODULE;
codec->read = wm8994_read;
codec->write = wm8994_write;
codec->hw_write = (hw_write_t)i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8994_set_bias_level;
codec->dai = &wm8994_dai;
codec->num_dai = 1;
codec->reg_cache_size = sizeof(wm8994_regs_default);
codec->reg_cache_step = 2;
codec->reg_cache = (void *)&wm8994_regs_default;
wm8994_codec = codec;
wm8994_dai.dev = &client->dev;
ret = snd_soc_register_codec(codec);//新增到codec列表,然後初始化音效卡
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err_codec_reg;
}
ret = snd_soc_register_dai(&wm8994_dai);//新增到dai列表裡,然後執行初始化音效卡函式
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
goto err_codec_reg;
}
return 0;
err_codec_reg:
if (wm8994->reg_vddd)
regulator_put(wm8994->reg_vddd);
err_reg_vddd:
if (wm8994->reg_vdda)
regulator_put(wm8994->reg_vdda);
err_reg_vdda:
if (wm8994->reg_vddio)
regulator_put(wm8994->reg_vddio);
err_reg_vddio:
kfree(wm8994);
kfree(codec);
return ret;
}
我們看下snd_soc_register_codec(codec):
int snd_soc_register_codec(struct snd_soc_codec *codec)
{
int i;
if (!codec->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!codec->dev)
printk(KERN_WARNING "No device for codec %s\n", codec->name);
INIT_LIST_HEAD(&codec->list);//初始化codec列表
for (i = 0; i < codec->num_dai; i++) {
fixup_codec_formats(&codec->dai[i].playback);
fixup_codec_formats(&codec->dai[i].capture);
}
mutex_lock(&client_mutex);
list_add(&codec->list, &codec_list);//新增到codec列表裡面
snd_soc_instantiate_cards();//初始化音效卡,上面已做分析
mutex_unlock(&client_mutex);
pr_debug("Registered codec '%s'\n", codec->name);
return 0;
}
我們再看下snd_soc_register_dai(&wm8994_dai):
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
if (!dai->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
if (!dai->ops)
dai->ops = &null_dai_ops;
INIT_LIST_HEAD(&dai->list);//初始化dai列表
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);//新增到dai列表裡面
snd_soc_instantiate_cards();//初始化,上面已做分析
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
我們看到codec的dai也新增到dai_list中了。
至此為止,cpu和codec的初始化工作都基本完成,下面才會真正執行上面分析的snd_soc_instantiate_cards(),我們再貼一遍,以便分析:
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list) //對card列表中的每個音效卡
snd_soc_instantiate_card(card);//執行card初始化動作
}
對card_list中每一個card都執行snd_soc_instantiate_card(card):
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct platform_device *pdev = container_of(card->dev,//對應soc_probe中的pdev->dev
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;//從soc_probe函式中分析知道對應soc_codec_dev_wm8994
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
if (card->instantiated)//已經初始化則退出
return;
found = 0;
list_for_each_entry(platform, &platform_list, list)//對platform列表中的每個platform
if (card->platform == platform) {//對應imx_soc_platform,檢查是否能找到,代表是否已經註冊
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return;
}
ac97 = 0;
for (i = 0; i < card->num_links; i++) {//num_links=1
found = 0;
list_for_each_entry(dai, &dai_list, list) {
if (card->dai_link[i].cpu_dai == dai) {//檢查cpu dai是否註冊,對應imx_ssi_dai[2]
found = 1;
break;
}
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].cpu_dai->name);
return;
}
if (card->dai_link[i].cpu_dai->ac97_control)//i.mx51沒定義
ac97 = 1;
}
/* If we have AC97 in the system then don't wait for the
* codec. This will need revisiting if we have to handle
* systems with mixed AC97 and non-AC97 parts. Only check for
* DAIs currently; we can't do this per link since some AC97
* codecs have non-AC97 DAIs.
*/
if (!ac97)
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list)
if (card->dai_link[i].codec_dai == dai) {//檢查codec dai,對應wm8994_dai
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].codec_dai->name);
return;
}
}
/* Note that we do not current check for codec components */
dev_dbg(card->dev, "All components present, instantiating\n");
/* Found everything, bring it up */
if (card->probe) {//在i.mx51中沒有定義
ret = card->probe(pdev);//執行音效卡的probe函式
if (ret < 0)
return;
}
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->probe) {//這個有定義,在imx-ssi中,對應了imx_ssi_probe
ret = cpu_dai->probe(pdev, cpu_dai);//執行probe函式
if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);//對應wm8994_probe
if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {//這裡platform對應imx_soc_platform,沒有定義probe
ret = platform->probe(pdev);
if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
card->instantiated = 1;//已經初始化
return;
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
for (i--; i >= 0; i--) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
if (card->remove)
card->remove(pdev);
}
在都進行了platform_list和dai_list註冊後,會執行cpu_dai->probe和codec_dev->probe,它們分別對應imx_ssi_probe和wm8994_probe。我們先看imx_ssi_probe:
static int imx_ssi_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
{
struct imx_ssi *priv = (struct imx_ssi *)dai->private_data;
if (priv->irq >= 0) {
if (request_irq(priv->irq, imx_ssi_irq, IRQF_SHARED,
pdev->name, priv)) {
printk(KERN_ERR "%s: failure requesting irq for %s\n",
__func__, pdev->name);
return -EBUSY;
}
}
return 0;
}
我們看到主要註冊了ssi的中斷函式。
wm8994_probe見下一節。