Linux ALSA Audio Driver Six: Machine__linux in ASOC architecture

Source: Internet
Author: User
Tags ack data structures goto


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.
1. Registered Platform Device

ASOC the sound card registration as platform DevicE, we have a WM8994 to assemble a Samsung's development Board SMDK for example, WM8994 is a Wolfson production of multi-functional codec chip.

The code is located at:/sound/soc/samsung/smdk_wm8994.c, we focus on the initialization function of the module:[CPP]View plain copy static int __initSmdk_audio_init(void)//module_init function;
{int ret; Smdk_snd_device = PLatform_device_alloc("Soc-audio",-1);//create a platform device
if (!smdk_snd_device) Return-enomem;Platform_set_drvdata(Smdk_snd_device, &SMDK); Result Platform_device->dev->p->driver_data =Data; a ret =Platform_device_add(Smdk_snd_device);//platform_device_register is calling this function, registering the Platform_device device; if (ret) platform_device_put (smdk_snd_device); //error execution here;
return ret; }


Thus, when the module is initialized, a platform device named Soc-audio is registered , and the smdk is set to the Platform_device structure Dev->p->driver_data field, this leads to the first instance of the data structure Snd_soc_card SMDK, and his definition is as follows:


static struct snd_soc_card smdk = {. Name = "Smdk-i2s",. dai_link = Smdk_dai,. num_links = A Rray_size (Smdk_dai),};

The Snd_soc_dai_link structure is called here;


[CPP]View plain copy static StrucTSnd_soc_dai_linK smdk_dai[] = {/* Primary dai i/f/. Name = "WM8994 AIF1",. Stream_name = "Pri_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",. C           Pu_dai_name = "Samsung-i2s.4",. Codec_dai_name = "Wm8994-aif1",. Platform_name = "Samsung-audio", . Codec_name = "Wm8994-codec",. ops = &sMdk_ops,       },   };

Through the Snd_soc_card structure, it also leads to two other data structures machine driven: Snd_soc_dai_link (instance: 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 already registered in the system. Dai, these registered parts are defined in the corresponding platform drive and codec-driven code files, so the machine-driven device initialization code is simply choosing the right platform and codec and Dai, Use them to populate these data structures, and then register platform devices. 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.

Or start at the entrance of the module: [CPP] view plain copy static int __init snd_soc_init (void) //module_nint {...   Return platform_driver_registeR (&soc_driver); }

The

Soc_driver is defined as follows: [CPP] view plain copy/* asoc platform driver */    static struct platform_driver soc_driver = {        .driver     = {            .name       =  "Soc-audio",            .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 the name in Platform_device, according to the Linux device model, The platform bus matches the two device and driver with the same name and 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 traverse each pair of dai_link, for codec, platform, Dai binding work , The following is just a section of the Code section, the detailed code please refer to the complete code tree directly. [CPP] view plain copy/* bind DAIs */for (i = 0; i < card->num_links; i++) soc_bind_dai_link (card, i);

The

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, according to the names in card->dai_link[], and assigns the corresponding Codec,dai and platform instances to the card->rtd[ ] in (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: [CPP] view plain copy/* 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:[CPP]View Plain Copy/* Initialise the sound card only once/if (card->probe) {ret = card->probe (card);   if (Ret < 0) goto Card_probe_error;           }/* Early DAI link probe * * (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:
[CPP]View plain copy static intSoc_probe_dai_link(struct snd_soc_card *card, int num, int order)    {            ......       /* set default  power off timeout */       rtd->pmdown_time =  pmdown_time;          /* probe the cpu_dai */        if  (!cpu_dai->probed &&                cpu_dai->driver->probe_order == order)  {               if  (cpu_dai->driver->probe)  {   &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;RET&NBSP;=&NBSP;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_probe_codec (CARD,&NBSP;CODEC);        }          /* probe the platform */    &NBSP;&NBSP;&NBSP; 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;          &nbsp .....        /* create the pcm */       ret =  SOC_NEW_PCM (rtd, num);           ........       return 0;  }  

In addition to calling the Codec,dai and platform-driven probe functions, the function finally called the SOC_NEW_PCM () function to create a standard ALSA-driven PCM logic device. Now post some of the code for the function as well:


[CPP] View Plain copy/* create a new pcm */   INT&NBSP;SOC_NEW_PCM (struct snd_ Soc_pcm_runtime *rtd, int num)    {       ......        struct snd_pcm_ops *soc_pcm_ops = &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;   &NBSP;&NBSP;&NBSP;&NBSP;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, &NBSP;CAPTURE,&NBSP;&AMP;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;   &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;SOC_ pcm_ops->ack = platform->driver->ops->ack;            soc_pcm_ops->page = platform->driver->ops->page;        }      &n

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.