OverviewThe architecture of the entire ASOC is described in the Asoc section, where machine is a key component of the ASOC architecture, with no machine components, and separate codec and platform are not working. So this section starts with the machine section, so how do we start? The answer, of course, is to start with the code, first into the ASOC in the kernel position: KERNEL/SOUND/SOC
root@test:~/test/kernel/sound/soc$ ls
adi au1x blackfin codecs dwc Generic jz4740 kirkwood mxs omap rockchip sh soc-cache.c soc-core.c soc-devres.c SOC-IO.C soc-pcm.c spear txx9
Atmel bcm Cirrus DaVinci FSL Intel Kconfig Makefile nuc900 pxa Samsung sirf soc-compress.c soc-dapm.c SOC-GENERIC-DMAENGINE-PCM.C soc-jack.c soc-utils.c Tegra ux500
This directory is the current platform to support the ASOC architecture, here for the Samsung architecture as a reference. Kernel Version: 3.18
Machine Code AnalysisThe machine code selection for the Samsung platform is: S3C24XX_UDA134X.C
This code registers platform-driven s3c24xx_uda134x_driver, and when the name of the platform driver and platform device (previously configured in DT) wants to match, it invokes the probe function s3c24xx_uda134x_probe in the platform driver.
S3c24xx_uda134x_snd_device = Platform_device_alloc ("Soc-audio",-1);
if (!s3c24xx_uda134x_snd_device) {
printk (kern_err "s3c24xx_uda134x SoC Audio:"
"unable to register\n");
Return-enomem;
}
Platform_set_drvdata (s3c24xx_uda134x_snd_device,&snd_soc_s3c24xx_uda134x);
This is a platform device that assigns the name "Soc-audio" and then sets the snd_soc_s3c24xx_uda134x to the dev->driver_data of the platform device. About the SND_SOC_S3C24XX_UDA134X structure is described later.
Since the "Soc-audio" device is registered here, there will be a driver with the name "Soc-audio", and the search for "Soc-audio" will be found in soc-core.c.
/* ASOC platform driver/
static struct Platform_driver soc_driver = {
. Driver = {
. Name = "Soc-au Dio ",
. Owner = this_module,
. PM = &snd_soc_pm_ops,
},
. Probe = Soc_probe,
. Remove = Soc_remove,
};
static int __init snd_soc_init (void)
{
snd_soc_util_init ();
Return Platform_driver_register (&soc_driver);
}
When Platform_device and Platform_driver are matched, the Soc_probe function is invoked.
static int soc_probe (struct platform_device *pdev) {struct Snd_soc_card *card
= Platform_get_drvdata (Pdev);
*
* No card, so machine driver should is registering card * We should not to be here and so
ret error
* /
if (!card)
return-einval;
Dev_warn (&pdev->dev,
"Asoc:machine%s should use Snd_soc_register_card () \ n",
card->name);
/* Bodge While we unpick instantiation * *
Card->dev = &pdev->dev;
return Snd_soc_register_card (card);
Snd_soc_register_card is called here and a card is registered in ASOC core. The card here is the SND_SOC_S3C24XX_UDA134X structure. Then talk about the role of this structure.
static struct Snd_soc_ops S3c24xx_uda134x_ops = {
. startup = S3c24xx_uda134x_startup,
. Shutdown = s3c24xx_ Uda134x_shutdown,
. hw_params = S3c24xx_uda134x_hw_params,
};
static struct Snd_soc_dai_link S3c24xx_uda134x_dai_link = {
. Name = "Uda134x",
. Stream_name = "uda134x",
. Codec_name = "Uda134x-codec",.
codec_dai_name = "Uda134x-hifi",
. Cpu_dai_name = "S3c24xx-iis",
. Ops = &s3c24xx_uda134x_ops,
. Platform_name = "S3c24xx-iis",
};
static struct Snd_soc_card snd_soc_s3c24xx_uda134x = {
. Name = "S3c24xx_uda134x",
. Owner = This_module,
. Dai_link = &s3c24xx_uda134x_dai_link,
. num_links = 1,
};
The Dai_link structure is used as a connection between platform and codec, indicating exactly the codec, the Platfrom. That is specified by what. If you are interested in a detailed look at Snd_soc_dai_link's comments, this comment is written very clearly.
struct Snd_soc_dai_link {/* Config-must is set by machine driver/const char *name; /* CODEC name */const char *stream_name; /* Stream Name *///////May specify the link ' s cpu-side device, either by device name, * or by dt/of node, but not Both. If This information is omitted, * The Cpu-side DAI is matched using. cpu_dai_name only, which hence * must to be globally Unique.
These fields are currently typically used-for-codec to codec links, or systems the using device tree.
* * const char *cpu_name;
struct Device_node *cpu_of_node; /* You may specify the Dai name of the CPU Dai. If This information are * omitted, the Cpu-side DAI is matched using. Cpu_name/.cpu_of_node * only, which only works we
ll when this device exposes a single DAI.
* * const char *cpu_dai_name;
* * You must specify the link's codec, either by device name, or by * dt/of node, but not both.
* * const char *codec_name;
struct Device_node *codec_of_node; /* You must specIfy the DAI name within the codec/const char *codec_dai_name;
struct Snd_soc_dai_link_component *codecs;
unsigned int num_codecs; /* You may specify the link's PLATFORM/PCM/DMA driver, either by * device name, or by Dt/of node, but not both.
Some Forms of link * do not need a platform.
* * const char *platform_name;
struct Device_node *platform_of_node; int be_id;
/* Optional ID for machine driver is identification/const struct Snd_soc_pcm_stream; unsigned int dai_fmt; /* format to set on init */enum Snd_soc_dpcm_trigger trigger[2];
/* Trigger type for DPCM * * * Keep DAI active over suspend * * unsigned int ignore_suspend:1;
/* Symmetry requirements * * unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* Do not create a PCM for this DAI link (backend link) * * unsigned int no_pcm:1; /* This DAI link can route to the other DAI links at runtime (frontend) */unsigned int dynamic: 1;
/* DPCM capture and Playback support */unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* Pmdown_time is ignored at stop */unsigned int ignore_pmdown_time:1;
/* Codec/machine specific init-e.g. Add machine controls/INT (*init) (struct snd_soc_pcm_runtime); /* Optional hw_params re-writing for IS and FE sync/int (*be_hw_params_fixup) (struct snd_soc_pcm_runtime *rtd, Stru
CT snd_pcm_hw_params *params);
/* Machine stream Operations/const struct SND_SOC_OPS *ops;
const struct SND_SOC_COMPR_OPS *compr_ops;
/* For unidirectional Dai links */bool playback_only;
BOOL Capture_only; };
. Cpu_dai_name:Used to specify the CPU side of the Dai name, which is known as the CPU side of the digital audio interface, is generally I2S interface. If omitted, the Cpu_name/cou_of_name is used.
. Codec_dai_name:Dai name for codec side, can not be omitted.
. Codec_name:Used to specify the codec chip. Can not be omitted.
. Platform_name:Used to specify CPU side platform drive, usually DMA driven, for transmission.
. Ops:A collection of related operations functions for audio.
Back again
Snd_soc_register_cardfunction, continue to analyze the role of machine.
1. According to the number of struct snd_soc_dai_link structure, here is a, detect the need to set the name has been set.
if (link->platform_name && link->platform_of_node) {
dev_err (Card->dev,
"Asoc:both Platform Name/of_node are set for%s\n ", link->name);
Return-einval;
}
2. Assign a struct snd_soc_pcm_runtime structure, and then according to Num_links, set up card, copy Dai_link and so on.
CARD->RTD = Devm_kzalloc (card->dev,sizeof (struct snd_soc_pcm_runtime) *
(card->num_links + card->num _aux_devs), gfp_kernel);
if (CARD->RTD = = NULL)
return-enomem;
CARD->NUM_RTD = 0;
Card->rtd_aux = &card->rtd[card->num_links];
for (i = 0; i < card->num_links; i++) {
card->rtd[i].card = card;
Card->rtd[i].dai_link = &card->dai_link[i];
Card->rtd[i].codec_dais = Devm_kzalloc (Card->dev,
sizeof (struct Snd_soc_dai *) *
(card->rtd[i). Dai_link->num_codecs),
gfp_kernel);
if (Card->rtd[i].codec_dais = = NULL)
return-enomem;
}
3. Then all the focus work is implemented in the Snd_soc_instantiate_card function.
Analysis
Snd_soc_instantiate_cardThe actual operation of the function: 1. According to the value of num_links, dais bind work. First step bind CPU side of Dai
Cpu_dai_component.name = dai_link->cpu_name;
Cpu_dai_component.of_node = dai_link->cpu_of_node;
Cpu_dai_component.dai_name = dai_link->cpu_dai_name;
Rtd->cpu_dai = Snd_soc_find_dai (&cpu_dai_component);
if (!rtd->cpu_dai) {
dev_err (Card->dev, "Asoc:cpu dai%s not registered\n",
dai_link->cpu_dai_name);
return-eprobe_defer;
}
Here Dai_link is registered in the machine struct snd_soc_dai_link structure, Cpu_dai_name is registered name, and finally through the Snd_soc_find_dai interface out to find.
static struct Snd_soc_dai *snd_soc_find_dai (
const struct snd_soc_dai_link_component *dlc)
{
struct snd_ Soc_component *component;
struct Snd_soc_dai *dai;
/* Find CPU DAI from registered dais*/
list_for_each_entry (component, &component_list, list) {
if (dlc-> Of_node && component->dev->of_node!= dlc->of_node)
continue;
if (Dlc->name && strcmp (Component->name, dlc->name))
continue;
List_for_each_entry (Dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp (dai-> Name, dlc->dai_name))
continue;
return dai;
}
}
return NULL;
}
This function finds the same name first in the Component_list list and then in Component->dai_list to find if there is an identical dai_name. The component_list are set up at the time of registering codec and platform. will be introduced in detail at the time of codec and platform. After you find the registered Cpu_dai, there is a cpu_dai in the snd_soc_pcm_runtime.
2. Then according to codec data, look for the codec side of Dai.
/* Find CODEC from registered CODECs * for
(i = 0; i < rtd->num_codecs; i++) {
Codec_dais[i] = Snd_soc_find _dai (&codecs[i]);
if (!codec_dais[i]) {
dev_err (Card->dev, "Asoc:codec DAI%s not registered\n",
codecs[i].dai_name);
Return-eprobe_defer
}
}
Then the codec side of the found Dai is also assigned to the Codec_dai in the Snd_soc_pcm_runtime.
3. Find Platfrom in the Platform_list list, according to the platform_name domain in Dai_link. If there is no platform_name, set to "Snd-soc-dummy"
/* If there ' s no platform we match on the empty platform * *
platform_name = dai_link->platform_name;
if (!platform_name &&!dai_link->platform_of_node)
platform_name = "Snd-soc-dummy";
/* Find one from the set of registered platforms * * *
list_for_each_entry (platform, &platform_list, list) {
if ( Dai_link->platform_of_node) {
if (platform->dev->of_node!=
dai_link->platform_of_node)
Continue;
} else {
if (strcmp (Platform->component.name, platform_name))
continue;
}
Rtd->platform = platform;
}
After this search is complete, the Snd_soc_pcm_runtime stores the codec, Dai, and platform found.
4. Then initialize the registered codec Cache,cache_init whether the representative has been initialized.
/* Initialize the register cache for each available codec * *
list_for_each_entry (codec, &codec_list, list) {
I F (codec->cache_init)
continue;
RET = Snd_soc_init_codec_cache (codec);
if (Ret < 0)
goto base_error;
}
5. Then call the function in Alsa to create the card: snd_card_new Create a card
* Card bind complete so register a sound card
/ret = snd_card_new (Card->dev, sndrv_default_idx1, sndrv_default_s TR1,
card->owner, 0, &card->snd_card);
if (Ret < 0) {
dev_err (Card->dev,
"Asoc:can ' t create sound card for card%s:%d\n",
Card->name, R ET);
Goto base_error;
}
6. Then call the probe function of each child part in turn
* Initialise the sound card only once/
if (card->probe) {
ret = card->probe (card);
if (Ret < 0)
goto card_probe_error;
}
/* Probe all components used by DAI links in this card * for
(order = Snd_soc_comp_order_first; Order <= Snd_soc_c Omp_order_last;
order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_link_components (card, I, order);
if (Ret < 0) {
dev_err (Card->dev,
"asoc:failed to instantiate card%d\n",
ret);
Goto probe_dai_err;
}
}
/* Probe all DAI links on this card *
/for (order = Snd_soc_comp_order_first, order <= Snd_soc_comp_order_last;
order++) {
for (i = 0; i < card->num_links; i++) {
ret = Soc_probe_link_dais (card, I, order);
if (Ret < 0) {
dev_err (Card->dev,
"asoc:failed to instantiate card%d\n",
ret);
Goto probe_dai_err;
}
}
7. In the Soc_probe_link_dais function, the probe function of the Codec_dai side is called in turn Cpu_dai
/* Probe the Cpu_dai
/if (!cpu_dai->probed &&
Cpu_dai->driver->probe_order = = order) {
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe (Cpu_dai);
if (Ret < 0) {
dev_err (Cpu_dai->dev,
"asoc:failed to probe CPU Dai%s:%d\n",
Cpu_dai->name, ret);
return ret;
}
}
cpu_dai->probed = 1;
}
/* Probe the CODEC DAI * *
for (i = 0; i < rtd->num_codecs; i++) {
ret = Soc_probe_codec_dai (Card, rtd-> Codec_dais[i], order);
if (ret) return
ret;
8. Final call to the SOC_NEW_PCM function to create a PCM device:
if (!dai_link->params) {/
* Create the PCM */
ret = SOC_NEW_PCM (RTD, num);
if (Ret < 0) {
dev_err (Card->dev, "Asoc:can ' t create PCM%s:%d\n",
Dai_link->stream_name, ret);
return ret;
}
Most of this function invokes the ALSA standard to create the interface of the PCM device: snd_pcm_new, and then sets the corresponding set of OPS operations for PCM. Then call the function to platform->driver->pcm_new. The function is not here.
9. Then the corresponding operation will be done in DAPM and Dai widgets, the control parameters will be set later, and the function Snd_card_register of the registration card of ALSA will eventually be invoked.
ret = Snd_card_register (card->snd_card);
if (Ret < 0) {
dev_err (Card->dev, "asoc:failed to register Soundcard%d\n",
ret);
Goto Probe_aux_dev_err;
}
Summary: After machine-driven registrations, machine will be based on a platform device registered with "Soc_audio", and then in the driver's probe function of the platform with the same name, depending on the name in the Snd_soc_dai_link structure, Make a match to find the corresponding codec, Codec_dai,platform, Cpu_dai. Once found, place all of these values in the corresponding position in the structure snd_soc_pcm_runtime, then register card, call codec in turn, initialize the corresponding probe function on the Platform,cpu_dai side, and then create the PCM device, Register the card into the system. In fact, ASOC is also in the ALSA on the basis of the encapsulation once again, so that write-driven more convenient, simple.
After this encapsulation, you can greatly simplify the drive writing, about the machine driver needs to do: 1. Register a platform device named "Soc-audio". 2. Assign a struct Snd_soc_card structure, and then set the Dai_link. Set the Dai_link again. 3. Put the struct snd_soc_card structure into the private data of the dev of the platform device. 4. Register the platform equipment.