As we mentioned in the previous section, ASOC is divided into machine, platform and codec, where the machine drive is responsible for coupling between platform and codec as well as parts and equipment or board-specific code, Again, refer to the previous section: The machine driver is responsible for handling some of the machine-specific controls and audio events (for example, when audio is played, an amplifier needs to be opened first); The individual platform and codec drivers are not working, It must be combined by the machine driver to complete the audio processing of the entire device.
Asoc everything from the machine driver, including the registration of sound card, binding platform and codec driver, and so on, let's start from the machine driver discussion.
/********************************************************************************************/
Statement: This Bo content by Http://blog.csdn.net/droidphone Original, reproduced please indicate the source, thank you.
/********************************************************************************************/
1. Registered Platform Device
ASOC the sound card registration as Platform Device, we have a WM8994 to assemble a Samsung's development Board SMDK as an example, WM8994 is a Wolfson production of multi-functional codec chip.
Where the code is located:/sound/soc/samsung/smdk_wm8994.c, we focus on the initialization function of the module:
static int __init smdk_audio_init (void)
{
int ret;
Smdk_snd_device = Platform_device_alloc ("Soc-audio",-1);
if (!smdk_snd_device)
Return-enomem;
Platform_set_drvdata (Smdk_snd_device, &SMDK);
ret = Platform_device_add (smdk_snd_device);
if (ret)
platform_device_put (smdk_snd_device);
return ret;
}
This shows that the module initialization, registered a platform device named Soc-audio, while the SMDK set to the platform_device structure of the Dev.drvdata field, which leads to the first data structure SND_SOC_ Card instance SMDK, his definition is as follows:
static struct Snd_soc_dai_link smdk_dai[] = {
* * Primary dai i/f/
. Name = "WM8994 AIF1",
. Stream_name = "P Ri_dai ",
. Cpu_dai_name =" samsung-i2s.0 ",
. Codec_dai_name =" Wm8994-aif1 ",
. Platform_name =" Samsung-audio ",
. Codec_name =" Wm8994-codec ",
. init = SMDK_WM8994_INIT_PAIFTX,
. Ops = &smdk_ops,
{/* Sec_fifo Playback i/f/*
. Name = "Sec_fifo TX",
. Stream_name = "Sec_dai",
. Cpu_dai_name = "Sam Sung-i2s.4 ",
. Codec_dai_name =" Wm8994-aif1 ",
. Platform_name =" Samsung-audio ",
. Codec_name =" Wm8994-codec ",
. Ops = &smdk_ops,
},
};
static struct Snd_soc_card smdk = {
. Name = "Smdk-i2s",
. Owner = This_module,
. Dai_link = Smdk_dai,
. Num_links = Array_size (Smdk_dai),
};
Through the Snd_soc_card structure, but also leads to the machine drive two other data structures: Snd_soc_dai_link (example: smdk_dai[]) Snd_soc_ops (example: smdk_ops)
In Snd_soc_dai_link, the names of platform, Codec, Codec_dai, and Cpu_dai are specified, and later machine drivers will use these names to match the Platform,codec,dai already registered in the system. These registered parts are defined in the corresponding platform drive and codec-driven code files, so that the machine-driven device initialization code is simply choosing the right platform and codec and Dai, filling them with several data structures, Then register the platform device. Of course also to implement the connection platform and codec dai_link corresponding OPS implementation, this example is smdk_ops, it only implements the Hw_params function: Smdk_hw_params. 2. Registered Platform Driver
According to the model of Linux equipment, there are platform_device, there must be platform_driver. ASOC's platform_driver is defined in the following file: Sound/soc/soc-core.c.
First look at the entrance to the module:
static int __init snd_soc_init (void)
{
...
Return Platform_driver_register (&soc_driver);
}
The definition of Soc_driver is as follows:
/* ASOC platform driver/
static struct Platform_driver soc_driver = {
. Driver = {
. Name = "Soc-aud Io ",
. Owner = this_module,
. PM = &soc_pm_ops,
},
. Probe = Soc_probe,
. remove = Soc_remove,
};
We see Platform_driver's Name field Soc-audio, exactly the same as in Platform_device, according to the Linux device model, the platform bus matches the two names of device and driver, It also triggers the soc_probe call, which is the portal of the entire ASOC-driven initialization. 3. Initialization of the entrance soc_probe ()
The Soc_probe function itself is simple, it takes the Snd_soc_card out of the Platform_device parameter, and then calls Snd_soc_register_card, through Snd_soc_register_card, for Snd_ The Soc_pcm_runtime array applies memory, each dai_link corresponds to a unit of the Snd_soc_pcm_runtime array, and then copies the Dai_link configuration in Snd_soc_card to the corresponding snd_soc_pcm_ runtime, and finally, most of the work is done in Snd_soc_instantiate_card, and here's a look at what Snd_soc_instantiate_card did:
The function first uses card->instantiated to determine whether the card has been instantiated, if it has been instantiated to return directly, otherwise traversing each pair of Dai_link, codec, platform, Dai binding work, the next is only part of the Code selection section, For detailed code, refer directly to the complete code tree.
/* Bind DAIs *
/for (i = 0; i < card->num_links; i++)
soc_bind_dai_link (card, i);
ASOC defines three global chain header variables: codec_list, Dai_list, platform_list, all codec, Dai, and platform in the system are connected to these three global lists when registering. The Soc_bind_dai_link function scans each of these three lists, matching them according to the names in card->dai_link[], and assigns the corresponding Codec,dai and platform instances to card->rtd[after the match (Snd_ Soc_pcm_runtime). After this process, Snd_soc_pcm_runtime: (CARD->RTD) saves the Codec,dai and platform-driven information used in this machine.
Snd_soc_instantiate_card then initializes the codec register cache and then invokes the standard ALSA function to create an instance of the sound card:
* Card bind complete so register a sound card
/ret = Snd_card_create (sndrv_default_idx1, SNDRV_DEFAULT_STR1,
CA Rd->owner, 0, &card->snd_card);
Card->snd_card->dev = card->dev;
Card->dapm.bias_level = Snd_soc_bias_off;
Card->dapm.dev = card->dev;
Card->dapm.card = Card;
List_add (&card->dapm.list, &card->dapm_list);
Then, call the probe function of each child structure in turn:
* Initialise the sound card only once/
if (card->probe) {
ret = card->probe (card);
if (Ret < 0)
goto card_probe_error;
}
/* Early DAI link probe * * 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_dai_link (card, I, order);
if (Ret < 0) {
pr_err ("asoc:failed to instantiate card%s:%d\n",
Card->name, ret);
Goto probe_dai_err;
}
}
for (i = 0; i < card->num_aux_devs; i++) {
ret = Soc_probe_aux_dev (card, i);
if (Ret < 0) {
pr_err ("asoc:failed to add auxiliary devices%s:%d\n",
Card->name, ret);
Goto probe_aux_dev_err;
}
}
Do a lot of things in the Soc_probe_dai_link () function above, and get him started on the discussion:
static int Soc_probe_dai_link (struct snd_soc_card *card, int num, int order) {.../* Set default power off Ti
Meout * * Rtd->pmdown_time = pmdown_time; /* Probe the Cpu_dai */if (!cpu_dai->probed && Cpu_dai->driver->probe_order = = order) {if (cpu_d
Ai->driver->probe) {ret = Cpu_dai->driver->probe (Cpu_dai);
} cpu_dai->probed = 1;
/* Mark Cpu_dai as probed and add to card Dai list */List_add (&cpu_dai->card_list, &card->dai_dev_list); }/* Probe the CODEC */if (!codec->probed && Codec->driver->probe_order = = order) {ret = Soc_p
Robe_codec (card, codec); }/* Probe the platform */if (!platform->probed && Platform->driver->probe_order = = order) {RET
= Soc_probe_platform (card, platform); }/* Probe the CODEC DAI */if (!codec_dai->probed && Codec_dai->driver->probe_order = = order) {if (Codec_dai->driver->probe) {ret = codeC_dai->driver->probe (Codec_dai);
}/* Mark Codec_dai as probed and add to card Dai list */codec_dai->probed = 1;
List_add (&codec_dai->card_list, &card->dai_dev_list);
}/* Complete DAI probe during last probe/if (order!= Snd_soc_comp_order_last) return 0;
ret = Soc_post_component_init (card, codec, num, 0);
if (ret) return ret;
... * * Create the PCM */ret = SOC_NEW_PCM (RTD, num);
... return 0; }
The function, which calls the Codec,dai and platform-driven probe functions, also calls the SOC_NEW_PCM () function to create a standard ALSA-driven PCM logic device at the end. Now post some of the code for the function as well:
/* Create a new PCM */int soc_new_pcm (struct snd_soc_pcm_runtime *rtd, int num) {... struct snd_pcm_ops *soc_pcm_op
s = &rtd->ops;
Soc_pcm_ops->open = Soc_pcm_open;
Soc_pcm_ops->close = Soc_pcm_close;
Soc_pcm_ops->hw_params = Soc_pcm_hw_params;
Soc_pcm_ops->hw_free = Soc_pcm_hw_free;
Soc_pcm_ops->prepare = Soc_pcm_prepare;
Soc_pcm_ops->trigger = Soc_pcm_trigger;
Soc_pcm_ops->pointer = Soc_pcm_pointer;
ret = Snd_pcm_new (Rtd->card->snd_card, new_name, num, playback, capture, &PCM);
* DAPM Dai Link Stream Work * * * init_delayed_work (&rtd->delayed_work, close_delayed_work);
RTD->PCM = PCM;
Pcm->private_data = RTD;
if (platform->driver->ops) {soc_pcm_ops->mmap = platform->driver->ops->mmap;
Soc_pcm_ops->pointer = platform->driver->ops->pointer;
Soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
Soc_pcm_ops->copy = platform->driver->ops->copy; Soc_pcm_oPs->silence = platform->driver->ops->silence;
Soc_pcm_ops->ack = platform->driver->ops->ack;
Soc_pcm_ops->page = platform->driver->ops->page;
} if (playback) snd_pcm_set_ops (PCM, Sndrv_pcm_stream_playback, Soc_pcm_ops);
if (capture) Snd_pcm_set_ops (PCM, Sndrv_pcm_stream_capture, Soc_pcm_ops);
if (platform->driver->pcm_new) {ret = Platform->driver->pcm_new (rtd);
if (Ret < 0) {Pr_err ("Asoc:platform PCM constructor failed\n");
return ret;
}} pcm->private_free = platform->driver->pcm_free;
return ret;
}
The function first initializes the Snd_pcm_ops field in the Snd_soc_runtime, that is, some members of the rtd->ops, such as Open,close,hw_params, and then invokes the function that creates PCM in the standard ALSA drive SND _pcm_new () Creates a PCM instance of the sound card, the PCM private_data field is set to the runtime variable RTD, and then replaces the Snd_pcm_ops field in part PCM with the Snd_pcm_ops in the platform driver, and finally, Invokes the platform-driven Pcm_new callback, which implements work related to DMA memory requests and DMA initialization under the platform. Here, the sound card and his PCM instance are created to complete.
After returning to the Snd_soc_instantiate_card function, completing the creation of Snd_card and SND_PCM, and then making some initial combination setup work on DAPM and Dai supported formats, the Card->late_probe (card) was invoked. Make some final initial compounding work, and finally, call the standard ALSA-driven sound card registration function to register the sound card:
if (card->late_probe) {
ret = card->late_probe (card);
if (Ret < 0) {
dev_err (Card->dev, "%s Late_probe () failed:%d\n",
Card->name, ret);
Goto probe_aux_dev_err;
}
}
Snd_soc_dapm_new_widgets (&CARD->DAPM);
if (card->fully_routed)
list_for_each_entry (codec, &card->codec_dev_list, card_list)
snd_soc_ Dapm_auto_nc_codec_pins (codec);
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_aux_dev_err;
}
At this point, the entire machine driver initialization has been completed, through the probe of the various substructure calls, in fact, also completed a part of the Platfrom drive and codec-driven initialization work, the whole process can be used in sequence diagram to represent:
Figure 3.1 based on the 3.0 kernel soc_probe sequence diagram
The following sequence diagram is the first version of this article, based on kernel 2.6.35, you can also refer to the difference of two versions:
Figure 3.2 based on 2.6.35 soc_probe sequence diagram