Register audio subsystem of beagle board to ASoC framework.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
In this doc, I will introduce how audio subsystem of beagle board is registered into ASoC framework.
Let's start with function omap3beagle_soc_init [sound/soc/omap]
static struct snd_soc_device omap3beagle_snd_devdata = { <--------------------[1]
.card = &snd_soc_omap3beagle,
.codec_dev = &soc_codec_dev_twl4030,
};
static int __init omap3beagle_soc_init(void)
{
int ret;
if (!machine_is_omap3_beagle()) {
pr_debug("Not OMAP3 Beagle!\n");
return -ENODEV;
}
pr_info("OMAP3 Beagle SoC init\n");
omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); <-------------------- Create a platform device named "soc-audio", based on the knowledge of registrations of platform device
and platform driver, a platform driver named "soc-audio" must be created somewhere. In the
implementation of ASoC, the platform driver is defined in [sound/soc/soc-core.c]
if (!omap3beagle_snd_device) {
printk(KERN_ERR "Platform device allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata); <-------------------- Audio subsystem is implemented as an platform device,and basically is kept in dev->driver_data
of struct platform_device "soc-audio", for sake of reference subsequently.
omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev;
*(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */
ret = platform_device_add(omap3beagle_snd_device); <-------------------- We know that the "probe()" function of the platform driver will be executed, with the premise that the
corresponding platform driver has already been registered. So next, let's see what the "probe()" do?
if (ret)
goto err1;
return 0;
err1:
printk(KERN_ERR "Unable to add platform device\n");
platform_device_put(omap3beagle_snd_device);
return ret;
}
/* 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,
};
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev); <-------------------- Obtain infomation of the audio device, which has been stored previously, bookmarked as [1]
struct snd_soc_card *card = socdev->card; <--------------------[2] the pointer variable "card" now is pointed to sound card, which is defined in [1] as well.
/* Bodge while we push things out of socdev */
card->socdev = socdev; <--------------------[3] we can access struct variable socdev by means of struct variable card, which enable mutual asscess between the two.
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev; <-------------------- now, the member variable "dev" of card points to the same struct dev with that of struct platform device "soc-audio".
then, we can obation the infomation of platform device "soc-audio" through the member varible "dev" of card struct.
ret = snd_soc_register_card(card); <-------------------- the entrance of the registeration.***********************
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)
{
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); <-------------------- add the audio card to the link list "card_list"
snd_soc_instantiate_cards(); <-------------------- try to instantiate this card.
mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
return 0;
}
/*
* Attempt to initialise any uninitalised cards. Must be called with
* client_mutex.
*/
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list) <-------------------- traverse the whole "card_list" link list, and attempt to instantiate all the uninstantiated cards,
herein, which means to the current card just added.
snd_soc_instantiate_card(card); <-------------------- Instantiate the card, and this is the crutial part of the ASoC core, let's see it.
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link omap3beagle_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
.codec_dai = &twl4030_dai,
.ops = &omap3beagle_ops,
};
/* Audio machine driver */
static struct snd_soc_card snd_soc_omap3beagle = {
.name = "omap3beagle",
.platform = &omap_soc_platform,
.dai_link = &omap3beagle_dai,
.num_links = 1,
};
/* Audio subsystem */
static struct snd_soc_device omap3beagle_snd_devdata = {
.card = &snd_soc_omap3beagle,
.codec_dev = &soc_codec_dev_twl4030,
};
static void snd_soc_instantiate_card(struct snd_soc_card *card) <----------------------------------------
{
struct platform_device *pdev = container_of(card->dev, <-------------------- obation the infomation of platform device "audio-soc" through card->dev,mentioned above, bookmark[2].
struct platform_device,
dev);
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev; <-------------------- the codec_dev points to soc_codec_dev_twl4030 defined in [sound/soc/codecs/twl4030.c]
see bookmark[3].
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) <-------------------- traverse the whole link list of "platform_list", and try to match correspoing platform driver.
if (card->platform == platform) {
found = 1; <-------------------- if matched, marke "found" flag and jump out.
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return; <-------------------- if not found, terminate this registeration.
}
ac97 = 0;
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list) <-------------------- iterate the whole link list of "dai_list" to match "cpu_dai", BTW, no matter "cpu_dai" or
"codec_dai", they are of the same type "struct snd_soc_dai", and all registered with link list "dai_list".
if (card->dai_link[i].cpu_dai == dai) {
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)
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) { <-------------------- try to match "codec_dai" of the audio link, configured by ".dai_link = &omap3beagle_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"); <-------------------- all the conditions are met, so we come here.
/* Found everything, bring it up */
if (card->probe) {
ret = card->probe(pdev); <-------------------- execute the probe() function of audio card.
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) {
ret = cpu_dai->probe(pdev, cpu_dai); <-------------------- execute the probe() function of cpu_dai.
if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev); <-------------------- execute the probe() function of codec_dai.
if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {
ret = platform->probe(pdev); <-------------------- execute the probe() function of platform.
if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work); <-------------------- DAPM(Dynamic Audio Power Manage) -related portions.
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
card->instantiated = 1;
return;
platform_err: <-------------------- the following is disposoal of error.
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);
}
BTW,
The registeration to cpu_dai, codec_dai, platform, or card always calls snd_soc_instantiate_cards() function presented above.
/**
* 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)
{
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_dai - Register a DAI with the ASoC core
*
* @dai: DAI to register
*/
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);
INIT_LIST_HEAD(&dai->list);
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;
}
/**
* snd_soc_register_platform - Register a platform with the ASoC core
*
* @platform: platform to register
*/
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);
snd_soc_instantiate_cards(); <----------------------------------------
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
return 0;
}
/**
* snd_soc_register_codec - Register a codec with the ASoC core
*
* @codec: codec to register
*/
int snd_soc_register_codec(struct snd_soc_codec *codec)
{
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);
mutex_lock(&client_mutex);
list_add(&codec->list, &codec_list);
snd_soc_instantiate_cards(); <----------------------------------------
mutex_unlock(&client_mutex);
pr_debug("Registered codec '%s'\n", codec->name);
return 0;
}