alsa:advanced Linux Sound architecture, which includes kernel-driven collections, API libraries, and tools. The user-layer program invokes the Libsound API library directly, without the need to open the device and so on, so programmers do not need to know the underlying details.
The core code of ALSA is not analyzed here, and how to program the sound card at the user layer is simply introduced to add a sound card driver on the ALSA architecture, that is, the sound Driver in the above figure. In fact, the document "Wirte an ALSA driver" very detailed description of how to write a ALSA driver, but that is a PCI sound card for example. In the embedded, the audio data transmission generally uses the I2S interface, controls the general use I2C or the SPI interface. The following is an example of an embedded sound card, whose driver code is typically placed below the SOUND/SOC.
The data structure is a clue, and the process is briefly analyzed. A similar label next to each important structure [XX],[XX] is [0], indicating that the structure is a large class containing a structure labeled [1] ... [XX] is [EXT], indicating that the structure is not used in this module.
CODEC
The driver code is located under Sound/soc/codec, such as UDA134X.C.
struct Snd_soc_dai [0]
View plain Copy to clipboard print? /* * Digital Audio Interface runtime data. * * holds runtime data for a dai. */ struct snd_soc_dai { /* DAI description */ char *name; unsigned int id; int ac97_control; struct device *dev; void *ac97_pdata; /* platform_data for the ac97 codec */ /* DAI callbacks */ int (*probe) (Struct platform_device *pdev,             &NBsp;struct snd_soc_dai *dai); void (*remove) (struct platform_device *pdev, struct snd_soc_dai *dai); int (* Suspend) (Struct snd_soc_dai *dai); int (*resume) (struct snd_soc_dai *dai); /* ops */ struct snd_soc_dai_ops *ops; /* dai capabilities */ struct snd_soc_pcm_stream capture; struct snd_soc_pcm_stream playback; unsigned int symmetric_rates:1; /* dai runtime info */ struct snd_pcm_runtime *runtime; struct snd_soc_codec *codec; unsigned int active; unsigned char pop_wait:1; void *dma_data; /* DAI private data */ void *private_data; /* parent platform */ struct snd_soc_platform *platform; struct list_head list; }; * * Digital Audio Interface data. * * Holds runtime data for a DAI. * * struct Snd_soc_dai {/* Dai description/char *name; unsigned int id; int ac97_control; struct device *dev; void *ac9 7_pdata; /* Platform_data for the AC97 codec///* DAI callbacks/Int (*probe) (struct platform_device, *pdev struct * DAI); void (*remove) (struct platform_device *pdev, struct snd_soc_dai); Int (*suspend) (struct Snd_soc_dai *dai); Int (*resume) (struct Snd_soc_dai *dai); /* OPS */struct snd_soc_dai_ops *ops; /* DAI capabilities * * struct snd_soc_pcm_stream capture; struct Snd_soc_pcm_stream playback; unsigned int symmetric_rates:1; * * DAI Runtime info/struct Snd_pcm_runtime *runtime; struct SND_SOC_CODEC *codec; unsigned int active; unsigned char pop_wait:1; void *dma_data; * DAI Private data */void *private_data; /* Parent Platform * * struct snd_soc_platform *platform; struct List_head list; };
In the module initialization function, you need to call Snd_soc_register_dai () to register the defined struct Snd_soc_dai into the ALSA. It is important to have several members of the struct Snd_soc_dai, such as name, capture, playback, and OPS.
Name specifies the module's sound card; capture is the recording parameter setting, playback is the playback parameter setting, all contains the channel number, pcm_rate and the pcm_fmtbit and so on information; OPS is the sound card action function collection pointer. This operation function set in the struct SND_SOC_DAI_OPS definition, the main implementation of the Hw_params (hardware parameters set), Digital_mute (Mute operation), SET_FMT (format configuration), and so on, the implementation of these functions are related to the hardware, According to the hardware data manual to achieve.
The following is an example of the definition of struct Snd_soc_dai:
View plain Copy to clipboard print? struct snd_soc_dai uda134x_dai = { .name = " uda134x ", /* playback capabilities */ .playback = {         .STREAM_ name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, } , /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, }; struct Snd_soc_dai uda134x_dai = {. Name = " Uda134x ",/* playback capabilities/* playback = {. Stream_name =" Playback ",. channels_min = 1,. Channels_max = 2,. Rate s = uda134x_rates,. formats = Uda134x_formats,},/* Capture capabilities/. Capture = {. Stream_name = "Capture",. Chann Els_min = 1,. Channels_max = 2,. Rates = uda134x_rates,. formats = Uda134x_formats,},/* PCM Operations */OPS = &uda134x_dai_ops,};
Summary: The above structure looks complex, the implementation of the basic structure is very simple and understandable. The work to be done is:
1/define Snd_soc_dai and Snd_soc_dai_ops These two constructs, the former set up the capture & playback parameters and the sound card function operation collection pointer, this pointer points to the SND_SOC_DAI_OPS structure body;
2/According to the hardware data manual to write related operational functions such as Hw_params, Set_fmt and Digital_mute;
3/writes the module initialization function Uda134x_init () and invokes the Snd_soc_dai defined before the Snd_soc_register_dai () registration.
Note: About 2 of the related operation function, before also mentioned control general with I2C or SPI interface. But in the operation function, we can use Codec->hw_write () to operate. Of course, in the probe function, the hw_write is initialized in probe, such as Codec->hw_write = (hw_write_t) i2c_master_send, which makes the control interface abstract.
struct Snd_soc_codec_device [EXT]
Next there is a structure snd_soc_codec_device to note, in general, the structure is defined under codec, but the registration operation is performed in a separate file, with 2410 uda134x as an example in sound/soc/s3c24xx/ S3c24xx_uda134x.c. These remain for later analysis, here just need to EXPORT this structure out on the line, such as: EXPORT_SYMBOL_GPL (soc_codec_dev_uda134x);
First look at the Snd_soc_codec_device structure definition:
View plain Copy to clipboard print? /* codec device */ struct snd_soc_codec_device { int (*probe) (Struct platform_device *pdev); int (*remove) (Struct platform_device *pdev); int (*suspend ) (struct platform_device *pdev, pm_message_t state); int (*resume) (Struct platform_device *pdev); }; /* codec device */struct SND_ soc_codec_device {int (*probe) (struct platform_device *pdev); int (*remove) (struct platform_device *pdev); Int (*suspend ) (struct Platform_device *pdev, pm_message_t State); Int (*resume) (struct platform_device *pdev); According to the structure definition, we can write the Uda134x_probe, Uda134x_remove, Uda134x_suspend, and Uda134x_resume functions step-by-Step, then export the structure to register in the later modules. Probe refers to the detection and initialization of sound card, remove refers to the uninstall of sound card, suspend refers to the sound card sleep, resume refers to the sound card from hibernation to recover. The probe function is described in detail.
First look at the probe code:
View plain Copy to clipboard print? Static int uda134x_soc_probe (Struct platform_device *pdev) { //obtains snd_soc_device structure struct snd_soc_device * Socdev = platform_get_drvdata (Pdev); struct snd_soc_codec *codec; ... //allocates memory for codec socdev->card->codec = kzalloc (sizeof (STRUCT SND_SOC_CODEC), Gfp_kernel); if (socdev->card->codec == null) return ret; codec = socdev->card->codec;   .... //Initialization codec codec->name = "uda134x"; codec->owner = THIS_MODULE; codec->dai = &uda134x_dai; // Point to Dai codec->num_dai = 1; , as defined above codec->read = uda134x_read_reg_cache; //Control Interface-Read codec->write = uda134x_write; //Control Interface-write ... mutex_init (& Codec->mutex); init_list_head (&codec->dapm_widgets); init_list_head (&codec->dapm_paths); ... /* register pcms */   ret = snd_soc_new_pcms (Socdev, sndrv_default_idx1, sndrv_default_ STR1); ... ret = snd_soc_add_controls (codec, uda134x_snd_controls, array_size (uda134x_snd_controls)); ... /* register card */ ret = snd_soc_init_card (Socdev); } static int uda134x_ Soc_probe (struct Platform_device *pdev) {//Get Snd_soc_device structure body struct Snd_soc_device *socdev = Platform_get_drvdata ( Pdev); struct SND_SOC_CODEC *codec; ...//Allocate memory for codec Socdev->card->codec = kzalloc (sizeof (struct snd_soc_cODEC), Gfp_kernel); if (Socdev->card->codec = NULL) return ret; codec = socdev->card->codec; ...//Initialize codec Codec->name = "uda134x"; Codec->owner = This_module; Codec->dai = &uda134x_dai; Point to the above defined Dai Codec->num_dai = 1; Codec->read = Uda134x_read_reg_cache; Control interface-Read codec->write = Uda134x_write; Control interface-Write ... mutex_init (&codec->mutex); Init_list_head (&codec->dapm_widgets); Init_list_head (&codec->dapm_paths); ... * * Register PCMS/ret = Snd_soc_new_pcms (Socdev, sndrv_default_idx1, SNDRV_DEFAULT_STR1); ... ret = Snd_soc_add_controls (codec, Uda134x_snd_controls, Array_size (Uda134x_snd_controls)); ... * * Register card/ret = Snd_soc_init_card (Socdev); }
Beginning to see Socdev = Platform_get_drvdata (Pdev) This is a little puzzled, in the end Pdev is where the initialization is good. The answer is in the sound/soc/<soc> directory of files, such as SOUND/SOC/S3C24XX/S3C24XX_UDA134X.C. In the sound card initialization process, in fact, the first is to invoke the sound/soc/<soc> under the relevant driven probe function, in probe has Platform_set_drvdata () operation, here is a conversion of the pointer type: (struct SND _soc_device *s) ==> (struct Platform_device *).
Snd_soc_add_controls () hangs the operation set up on the Card->control list, which implements the settings for each parameter during audio playback, mainly with. info,. Get and. Set. such as Playback volume CONTROL:SOC_DOUBLE_R_TLV ("Playback volume", Sndcard_reg_l_gain, Sndcard_reg_r_gain, 0, 0, digital _TLV), where Sndcard_reg_l_gain and Sndcard_reg_r_gain are the left and right channel volume gain register offsets respectively. The final call to the function is inside the soc-core.c, here only to provide some hardware-related parameters, greatly increased the code reusability.
This is described for function snd_soc_new_pcms (): Create a new sound card based upon the codec and interface PCMs. This function is very important to create a PCM instance to play the data stream. The important thing in the function is the following two sentences: View plain copy to clipboard print? ret = snd_card_create (idx, XID, Codec->owner, 0, &codec->card); ret = SOC_NEW_PCM (Socdev, &card->dai_link[i], i); ret = snd_card_create (idx, XID, Codec->owner, 0, &codec->card); ret = SOC_NEW_PCM (Socdev, &card->dai_link[i], i);
The former create and initialize a soundcard structure, and then this codec->card will be followed by Snd_soc_init_card () to initialize. The latter creates a stream of playback stream/recording streams and sets the soc_pcm_ops operation function of all playback/recording streams to the stream.
Summary: This part is a bit more complicated, to implement probe, remove, suspend, and resume, and a series of snd_kcontrol_new parameter set function arrays. There is another place to be clear: the initialization process is Soc_soc_init ()->platform_device_add ()->soc_codec_dev_uda134x.probe that is Uda134x_probe (). "For s3c24xx_uda134x is: S3c24xx_uda134x_probe ()->platform_device_add ()->soc_codec_dev_uda134x.probe that uda134x_ Soc_probe () "
SOC
The driver code is located under Sound/soc/<soc>, such as S3C24XX_UDA134X.C. This part of the probe is called before the probe in codec.
struct Snd_soc_device [0]
View plain Copy to clipboard print?/* soc device - the audio subsystem */ struct snd_soc_device { struct device *dev; struct snd_soc_card *card; struct snd_soc_codec_device *codec_dev; void *codec_data; }; /* SoC device-the Audio Subsystem * * struct Snd_soc_device {struct Device; *dev struct Soc_card *card; struct Snd_soc_codec_device *codec_dev; void *codec_data; This structure is used to register a device with the kernel. Initialization is generally as follows:
View plain Copy to clipboard print? static struct snd_soc_device soc_sndcard_snd_devdata = { .card = &snd_soc_s3c24xx_uda134x, .codec_dev = &soc_codec_dev_uda134x,//is the codec defined Snd_soc_codec_device structure .codec_data = &s3c24xx_uda134x, // Private data, generally stored Sndcard control interface information, such as I2C from the device address, etc. }; static struct Snd_soc_device Soc_sndcard_snd_devdata = { . Card = &snd_soc_s3c24xx_uda134x,. Codec_dev = &soc_codec_dev_uda134x,//is the snd_soc_codec_device structure defined by codec. Codec_data = &s3c24xx_uda134x,//private data, generally storing Sndcard control interface information, such as I2C from device address etc.};
For Module_init, in fact, with Platform_driver_register register a platform_driver structure, or directly to write a init or even, are not a problem. The former is more close to the Linux driver model. The general process of probe is as follows: View plain copy to clipboard print? Static int s3c24xx_uda134x_probe (Struct platform_device *pdev) { s3c24xx_ uda134x_snd_devdata.codec_dev = &soc_codec_dev_uda134x; S3c24xx_uda134x_snd_device = platform_device_alloc ("Soc-audio", -1); platform_set_drvdata (s3c24xx_uda134x_snd_device, & s3c24xx_uda134x_snd_devdata); s3c24xx_uda134x_snd_devdata.dev = & s3c24xx_uda134x_snd _device->dev; platform_device_add (S3C24XX_UDA134X_SND_device); } static int s3c24xx_uda134x_probe (struct platform_device *pdev) {s3c24xx_uda134x_ Snd_devdata.codec_dev = &soc_codec_dev_uda134x; S3c24xx_uda134x_snd_device = Platform_device_alloc ("Soc-audio",-1); Platform_set_drvdata (S3c24xx_uda134x_snd_device, & S3c24xx_uda134x_snd_devdata); S3c24xx_uda134x_snd_devdata.dev = & s3c24xx_uda134x_snd_device->dev; Platform_device_add (S3c24xx_uda134x_snd_device); }
You can see that the struct snd_soc_codec_device soc_codec_dev_uda134x the codec definition is platform_device_add here and then executes SOC_CODEC_DEV_ Uda134x.probe, the process can review the Uda134x_soc_probe () in codec.
The next is the origin of S3c24xx_uda134x_snd_devdata.card.
struct Snd_soc_card [1] view plain copy to clipboard print?/* soc card */ & nbsp struct snd_soc_card { char *name; struct device *dev; struct list_head list; int instantiated; int (*probe) (Struct platform_device *pdev); int (*remove) (Struct platform_device *pdev); /* the pre and post pm functions&n