基于S3C2440的Linux-3.6.6移植——声卡驱动

Linux的ALSA声卡驱动较为复杂,它需要注册多个平台设备。在mach-zhaocj2440.c文件中的平台设备数组内一共有四个与ALSA相关的平台设备:

&s3c_device_iis,

&uda1340_codec,

&mini2440_audio,

&samsung_asoc_dma,

mini2440_audio和uda1340_codec的定义在该文件内给出:

/*本开发板所用到的是UDA1341芯片,而且S3C2440是用三个通用IO口来模拟L3总线*/

static struct s3c24xx_uda134x_platform_data mini2440_audio_pins = {

       .l3_clk= S3C2410_GPB(4),         //定义引脚

       .l3_mode= S3C2410_GPB(2),

       .l3_data= S3C2410_GPB(3),

       .model= UDA134X_UDA1341

};

 

static struct platform_devicemini2440_audio = {

       .name             = "s3c24xx_uda134x",

       .id          = 0,

       .dev        = {

              .platform_data       = &mini2440_audio_pins,

       },

};

 

static struct platform_device uda1340_codec= {

              .name= "uda134x-codec",

              .id= -1,

};

 

s3c_device_iis和samsung_asoc_dma是在arch/arm/plat-samsung/devs.c内定义的:

static struct resource s3c_iis_resource[] = {

       [0]= DEFINE_RES_MEM(S3C24XX_PA_IIS,S3C24XX_SZ_IIS),

};

 

struct platform_device s3c_device_iis = {

       .name             = "s3c24xx-iis",

       .id          = -1,

       .num_resources      = ARRAY_SIZE(s3c_iis_resource),

       .resource = s3c_iis_resource,

       .dev        = {

              .dma_mask            = &samsung_device_dma_mask,

              .coherent_dma_mask     = DMA_BIT_MASK(32),

       }

};

 

struct platform_device samsung_asoc_dma = {

       .name             = "samsung-audio",

       .id          = -1,

       .dev        = {

              .dma_mask            = &samsung_device_dma_mask,

              .coherent_dma_mask     = DMA_BIT_MASK(32),

       }

};

 

有了平台设备,就一定要有平台驱动才能工作,下面就逐一介绍与之匹配的平台驱动及工作流程。Linux3.6.6版本与之前的版本在ALSA声卡驱动上的结构有一些差异,所以还需要注意。

我们先来看名为“uda134x-codec”的设备驱动,它是在sound/soc/codecs/uda134x.c文件内给出的:

static struct platform_driveruda134x_codec_driver = {

       .driver= {

              .name= "uda134x-codec",

              .owner= THIS_MODULE,

       },

       .probe= uda134x_codec_probe,

       .remove= __devexit_p(uda134x_codec_remove),

};

设备和驱动的名字匹配后,就会调用.probe,这里是uda134x_codec_probe函数:

static int __devinituda134x_codec_probe(struct platform_device *pdev)

{

       returnsnd_soc_register_codec(&pdev->dev,

                     &soc_codec_dev_uda134x,&uda134x_dai, 1);

}

在这个函数中,主要是调用了snd_soc_register_codec函数,并用到了两个全局变量soc_codec_dev_uda134x和uda134x_dai,它们的定义为:

//CODEC的驱动结构

static struct snd_soc_codec_driversoc_codec_dev_uda134x = {

       .probe=        uda134x_soc_probe,

       .remove=       uda134x_soc_remove,

       .suspend=      uda134x_soc_suspend,

       .resume=       uda134x_soc_resume,

       .reg_cache_size= sizeof(uda134x_reg),

       .reg_word_size= sizeof(u8),

       .reg_cache_default= uda134x_reg,

       .reg_cache_step= 1,

       .read= uda134x_read_reg_cache,

       .write= uda134x_write,

       .set_bias_level= uda134x_set_bias_level,

};

//dai驱动结构

static struct snd_soc_dai_driveruda134x_dai = {

       .name= "uda134x-hifi",

       /*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,

};

 

再来看snd_soc_register_codec函数,它在sound/soc/soc-core.c被定义:

int snd_soc_register_codec(struct device*dev,

                        const struct snd_soc_codec_driver*codec_drv,

                        struct snd_soc_dai_driver *dai_drv,

                        int num_dai)

{

       size_treg_size;

       structsnd_soc_codec *codec;

       intret, i;

 

       dev_dbg(dev,"codec register %s\n", dev_name(dev));

       //开辟一段内存空间给snd_soc_codec结构

       codec= kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);

       if(codec == NULL)

              return-ENOMEM;

 

       /*create CODEC component name */

       //创建一个唯一的名字

       codec->name= fmt_single_name(dev, &codec->id);

       if(codec->name == NULL) {

              kfree(codec);

              return-ENOMEM;

       }

 

       //soc_codec_dev_uda134x没有定义compress_type,所以执行else语句

       if(codec_drv->compress_type)

              codec->compress_type= codec_drv->compress_type;

       else

              codec->compress_type= SND_SOC_FLAT_COMPRESSION;

 

       codec->write= codec_drv->write;        // uda134x_write

       codec->read= codec_drv->read;          // uda134x_read_reg_cache

       codec->volatile_register= codec_drv->volatile_register;

       codec->readable_register= codec_drv->readable_register;

       codec->writable_register= codec_drv->writable_register;

       codec->ignore_pmdown_time= codec_drv->ignore_pmdown_time;

       codec->dapm.bias_level= SND_SOC_BIAS_OFF;

       codec->dapm.dev= dev;

       codec->dapm.codec= codec;

       codec->dapm.seq_notifier= codec_drv->seq_notifier;

       codec->dapm.stream_event= codec_drv->stream_event;

       codec->dev= dev;         // uda1340_codec平台设备

       codec->driver= codec_drv;          // soc_codec_dev_uda134x

       codec->num_dai= num_dai;        //1

       mutex_init(&codec->mutex);

 

       /*allocate CODEC register cache */

       //soc_codec_dev_uda134x定义了reg_cache_size和reg_word_size,所以执行if内容

       if(codec_drv->reg_cache_size && codec_drv->reg_word_size) {

              reg_size= codec_drv->reg_cache_size * codec_drv->reg_word_size;

              codec->reg_size= reg_size;

              /*it is necessary to make a copy of the default register cache

               * because in the case of using a compressiontype that requires

               * the default register cache to be marked as__devinitconst the

               * kernel might have freed the array by thetime we initialize

               * the cache.

               */

              //soc_codec_dev_uda134x定义了reg_cache_default,所以执行if内容

              if(codec_drv->reg_cache_default) {

                     codec->reg_def_copy= kmemdup(codec_drv->reg_cache_default,

                                                reg_size, GFP_KERNEL);

                     if(!codec->reg_def_copy) {

                            ret= -ENOMEM;

                            gotofail;

                     }

              }

       }

       //soc_codec_dev_uda134x没有定义了reg_access_size,所以不执行if内容

       if(codec_drv->reg_access_size && codec_drv->reg_access_default) {

              if(!codec->volatile_register)

                     codec->volatile_register= snd_soc_default_volatile_register;

              if(!codec->readable_register)

                     codec->readable_register= snd_soc_default_readable_register;

              if(!codec->writable_register)

                     codec->writable_register= snd_soc_default_writable_register;

       }

 

       for(i = 0; i < num_dai; i++) {

              //修正DAI格式的字节顺序

              fixup_codec_formats(&dai_drv[i].playback);

              fixup_codec_formats(&dai_drv[i].capture);

       }

 

       mutex_lock(&client_mutex);

       //把该函数定义的CODEC添加到CODEC列表中

       list_add(&codec->list,&codec_list);

       mutex_unlock(&client_mutex);

 

       /*register any DAIs */

       if(num_dai) {

              //注册DAI

              ret= snd_soc_register_dais(dev, dai_drv, num_dai);

              if(ret < 0)

                     dev_err(codec->dev,"Failed to regster DAIs: %d\n",

                            ret);

       }

 

       pr_debug("Registeredcodec '%s'\n", codec->name);

       return0;

 

fail:

       kfree(codec->reg_def_copy);

       codec->reg_def_copy= NULL;

       kfree(codec->name);

       kfree(codec);

       returnret;

}

 

下面我们看注册DAIs函数:

int snd_soc_register_dais(struct device*dev,

              structsnd_soc_dai_driver *dai_drv, size_t count)

{

       structsnd_soc_codec *codec;

       structsnd_soc_dai *dai;

       inti, ret = 0;

 

       dev_dbg(dev,"dai register %s #%Zu\n", dev_name(dev), count);

 

       for(i = 0; i < count; i++) {

              //为DAI开辟内存空间

              dai= kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);

              if(dai == NULL) {

                     ret= -ENOMEM;

                     gotoerr;

              }

 

              /*create DAI component name */

              //创建一个唯一的名字

              dai->name= fmt_multiple_name(dev, &dai_drv[i]);

              if(dai->name == NULL) {

                     kfree(dai);

                     ret= -EINVAL;

                     gotoerr;

              }

 

              dai->dev= dev;            // uda1340_codec平台设备

              dai->driver= &dai_drv[i];           // uda134x_dai

              //uda134x_dai没有定义id,所以执行else语句

              if(dai->driver->id)

                     dai->id= dai->driver->id;

              else

                     dai->id= i;

              dai->dapm.dev= dev;

              //uda134x_dai定义了ops,为uda134x_dai_ops,所以不执行if内容

              if(!dai->driver->ops)

                     dai->driver->ops= &null_dai_ops;

 

              mutex_lock(&client_mutex);

              /*遍历CODEC列表中的所有CODEC,在snd_soc_register_codec函数内,已经添加了一个CODEC,在这里,就把这个CODEC提取出来*/

              list_for_each_entry(codec,&codec_list, list) {

                     if(codec->dev == dev) {      //相同,都是uda1340_codec平台设备

                            dev_dbg(dev,"Mapped DAI %s to CODEC %s\n",

                                   dai->name,codec->name);

                            dai->codec= codec;              //赋值

                            break;

                     }

              }

              //把该函数定义的DAI添加到DAI列表中

              list_add(&dai->list,&dai_list);

 

              mutex_unlock(&client_mutex);

 

              pr_debug("RegisteredDAI '%s'\n", dai->name);

       }

 

       return0;

 

err:

       for(i--; i >= 0; i--)

              snd_soc_unregister_dai(dev);

 

       returnret;

}

 

通过上面两个函数,我们在CODEC和DAI列表中分别有了一项内容。

下面我们再来看名为“s3c24xx-iis”的平台驱动,它是在sound/soc/samsung/s3c24xx-i2s.c文件内被定义的:

static struct platform_driver s3c24xx_iis_driver = {

       .probe  = s3c24xx_iis_dev_probe,

       .remove= __devexit_p(s3c24xx_iis_dev_remove),

       .driver= {

              .name= "s3c24xx-iis",

              .owner= THIS_MODULE,

       },

};

再来看.probe函数:

static __devinit int s3c24xx_iis_dev_probe(struct platform_device *pdev)

{

       returnsnd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);

}

 

在这个函数中,主要是调用了snd_soc_register_dai函数,并用到了一个全局变量s3c24xx_i2s_dai,它的定义为:

static struct snd_soc_dai_driver s3c24xx_i2s_dai = {

       .probe= s3c24xx_i2s_probe,

       .suspend= s3c24xx_i2s_suspend,

       .resume= s3c24xx_i2s_resume,

       .playback= {

              .channels_min= 2,

              .channels_max= 2,

              .rates= S3C24XX_I2S_RATES,

              .formats= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},

       .capture= {

              .channels_min= 2,

              .channels_max= 2,

              .rates= S3C24XX_I2S_RATES,

              .formats= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},

       .ops= &s3c24xx_i2s_dai_ops,

};

 

需要注意的是s3c24xx_iis_dev_probe函数调用的是snd_soc_register_dai函数,而上面介绍的函数是snd_soc_register_dais,之间差了一个“s”,从函数名就可以猜出一个是注册一个DAI,而另一个是注册一群DAIs。snd_soc_register_dai函数也是在sound/soc/soc-core.c被定义:

int snd_soc_register_dai(struct device*dev,

              structsnd_soc_dai_driver *dai_drv)

{

       structsnd_soc_codec *codec;

       structsnd_soc_dai *dai;

 

       dev_dbg(dev,"dai register %s\n", dev_name(dev));

       //开辟内存空间

       dai= kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);

       if(dai == NULL)

              return-ENOMEM;

 

       /*create DAI component name */

       //创建一个唯一的名字

       dai->name= fmt_single_name(dev, &dai->id);

       if(dai->name == NULL) {

              kfree(dai);

              return-ENOMEM;

       }

       //这里的dev是s3c_device_iis结构中的dev

       dai->dev= dev;

       dai->driver= dai_drv;          // s3c24xx_i2s_dai

       dai->dapm.dev= dev;

       //s3c24xx_i2s_dai定义了ops,为s3c24xx_i2s_dai_ops,所以不执行if内容

       if(!dai->driver->ops)

              dai->driver->ops= &null_dai_ops;

 

       mutex_lock(&client_mutex);

       //遍历CODEC列表

       list_for_each_entry(codec,&codec_list, list) {

              //在这里,两者不相等,所以不能执行if内容

              if(codec->dev == dev) {

                     dev_dbg(dev,"Mapped DAI %s to CODEC %s\n",

                            dai->name,codec->name);

                     dai->codec= codec;

                     break;

              }

       }

       //把该函数内定义的DAI添加进DAI列表中

       list_add(&dai->list,&dai_list);

 

       mutex_unlock(&client_mutex);

 

       pr_debug("RegisteredDAI '%s'\n", dai->name);

 

       return0;

}

运行完上面的函数后,DAI列表中一共有了两项内容。

下面给出名为“samsung-audio”的平台驱动,它在sound/soc/samsung/dma.c文件内被定义:

static struct platform_driverasoc_dma_driver = {

       .driver= {

              .name= "samsung-audio",

              .owner= THIS_MODULE,

       },

 

       .probe= samsung_asoc_platform_probe,

       .remove= __devexit_p(samsung_asoc_platform_remove),

};

再给出它的.probe函数:

static int __devinitsamsung_asoc_platform_probe(struct platform_device *pdev)

{

       returnsnd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);

}

在这个函数中,主要是调用了snd_soc_register_platform函数,并用到了一个全局变量samsung_asoc_platform,它的定义为:

static struct snd_soc_platform_driversamsung_asoc_platform = {

       .ops        = &dma_ops,

       .pcm_new       = dma_new,

       .pcm_free       = dma_free_dma_buffers,

};

snd_soc_register_platform函数在sound/soc/soc-core.c被定义,为:

int snd_soc_register_platform(struct device*dev,

              structsnd_soc_platform_driver *platform_drv)

{

       structsnd_soc_platform *platform;

 

       dev_dbg(dev,"platform register %s\n", dev_name(dev));

       //开辟内存空间

       platform= kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);

       if(platform == NULL)

              return-ENOMEM;

 

       /*create platform component name */

       //创建一个唯一的名字

       platform->name= fmt_single_name(dev, &platform->id);

       if(platform->name == NULL) {

              kfree(platform);

              return-ENOMEM;

       }

 

       platform->dev= dev;

       platform->driver= platform_drv;        // 为samsung_asoc_platform

       platform->dapm.dev= dev;

       platform->dapm.platform= platform;

       platform->dapm.stream_event= platform_drv->stream_event;

       mutex_init(&platform->mutex);

 

       mutex_lock(&client_mutex);

       //把该函数定义的platform添加进列表中

       list_add(&platform->list,&platform_list);

       mutex_unlock(&client_mutex);

 

       pr_debug("Registeredplatform '%s'\n", platform->name);

 

       return0;

}

这时,在platform列表内也有了一项内容。

我们最后来看名为“s3c24xx_uda134x”的设备驱动,它是在sound/soc/samsung/s3c24xx_uda134x.c文件内给出的。

static struct platform_driver s3c24xx_uda134x_driver = {

       .probe  = s3c24xx_uda134x_probe,

       .remove= s3c24xx_uda134x_remove,

       .driver= {

              .name= "s3c24xx_uda134x",

              .owner= THIS_MODULE,

       },

};

设备和驱动的名字匹配后,就会调用.probe,这里是s3c24xx_uda134x_probe:

static int s3c24xx_uda134x_probe(struct platform_device *pdev)

{

       intret;

 

       printk(KERN_INFO"S3C24XX_UDA134X SoC Audiodriver\n");

 

       //提取出定义好的模拟L3总线的2440通用IO口,s3c24xx_uda134x_l3_pins为全局变量

       s3c24xx_uda134x_l3_pins =pdev->dev.platform_data;

       //如果没有定义,则退出

       if(s3c24xx_uda134x_l3_pins ==NULL) {

              printk(KERN_ERR"S3C24XX_UDA134X SoC Audio:"

                     "unable to find platformdata\n");

              return-ENODEV;

       }

       //为s3c24xx_uda134x赋值,s3c24xx_uda134x也是全局变量

       s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;

       s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;

 

       //为L3_DATA、L3_CLK和L3_MODE配置IO

       if(s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,

                                  "data") < 0)

              return-EBUSY;

       if(s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,

                                  "clk") < 0) {

              gpio_free(s3c24xx_uda134x_l3_pins->l3_data);

              return-EBUSY;

       }

       if(s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,

                                  "mode") < 0) {

              gpio_free(s3c24xx_uda134x_l3_pins->l3_data);

              gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);

              return-EBUSY;

       }

 

       //申请一个名为“soc-audio”的平台设备,该设备就是声卡

       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;

       }

       //为声卡赋值,内容为全局变量snd_soc_s3c24xx_uda134x

       platform_set_drvdata(s3c24xx_uda134x_snd_device,

                          &snd_soc_s3c24xx_uda134x);

       //为声卡添加数据s3c24xx_uda134x

       platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x));

       //把声卡添加进系统内

       ret= platform_device_add(s3c24xx_uda134x_snd_device);

       if(ret) {

              printk(KERN_ERR"S3C24XX_UDA134X SoC Audio:Unable to add\n");

              platform_device_put(s3c24xx_uda134x_snd_device);

       }

 

       returnret;

}

 

在该函数内用到了一个很重要的全局变量——snd_soc_s3c24xx_uda134x:

static struct snd_soc_card snd_soc_s3c24xx_uda134x = {

       .name= "S3C24XX_UDA134X",

       .owner= THIS_MODULE,

       .dai_link= &s3c24xx_uda134x_dai_link,

       .num_links= 1,

};

s3c24xx_uda134x_dai_link为DAI接口连接结构:

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      = "samsung-audio",

};

 

再回顾一下该函数,它定义了一个名为“soc-audio”声卡平台设备,要想使该声卡真正起作用,需要声卡平台驱动。声卡平台驱动在sound/soc/soc-core.c文件内给出:

static struct platform_driver soc_driver ={

       .driver            = {

              .name             = "soc-audio",

              .owner           = THIS_MODULE,

              .pm         = &snd_soc_pm_ops,

       },

       .probe            = soc_probe,

       .remove          = soc_remove,

};

该平台驱动已经在系统启动时被注册。这样系统就会调用soc_probe函数:

static int soc_probe(struct platform_device*pdev)

{

       structsnd_soc_card *card = platform_get_drvdata(pdev);          //得到声卡设备

       intret = 0;

 

       /*

        * no card, so machine driver should beregistering card

        * we should not be here in that case so reterror

        */

       if(!card)

              return-EINVAL;

 

       dev_warn(&pdev->dev,

               "ASoC machine %s should usesnd_soc_register_card()\n",

               card->name);

 

       /*Bodge while we unpick instantiation */

       card->dev= &pdev->dev;

 

       //注册声卡

       ret= snd_soc_register_card(card);

       if(ret != 0) {

              dev_err(&pdev->dev,"Failed to register card\n");

              returnret;

       }

 

       return0;

}

 

重要的snd_soc_register_card函数:

int snd_soc_register_card(structsnd_soc_card *card)

{

       inti, ret;

 

       if(!card->name || !card->dev)

              return-EINVAL;

 

       //遍历系统中的所有声卡设备,在这里只有一个声卡

       for(i = 0; i < card->num_links; i++) {

              //提取出DAI接口连接,即s3c24xx_uda134x_dai_link

              structsnd_soc_dai_link *link = &card->dai_link[i];

 

              /*

               * Codec must be specified by 1 of name or OFnode,

               * not both or neither.

               */

              //这里定义了codec_name,为uda134x-codec,所以不执行if的内容

              if(!!link->codec_name == !!link->codec_of_node) {

                     dev_err(card->dev,

                            "Neither/bothcodec name/of_node are set for %s\n",

                            link->name);

                     return-EINVAL;

              }

              /*Codec DAI name must be specified */

              //这里定义了codec_dai_name,为uda134x-hifi,所以不执行if的内容

              if(!link->codec_dai_name) {

                     dev_err(card->dev,"codec_dai_name not set for %s\n",

                            link->name);

                     return-EINVAL;

              }

 

              /*

               * Platform may be specified by either name orOF node, but

               * can be left unspecified, and a dummyplatform will be used.

               */

              //这里定义了platform_name,为samsung-audio,所以不执行if的内容

              if(link->platform_name && link->platform_of_node) {

                     dev_err(card->dev,

                            "Bothplatform name/of_node are set for %s\n", link->name);

                     return-EINVAL;

              }

 

              /*

               * CPU device may be specified by either nameor OF node, but

               * can be left unspecified, and will be matchedbased on DAI

               * name alone..

               */

              //这里没有定义了cpu_name和cpu_of_node,所以不执行if的内容

              if(link->cpu_name && link->cpu_of_node) {

                     dev_err(card->dev,

                            "Neither/bothcpu name/of_node are set for %s\n",

                            link->name);

                     return-EINVAL;

              }

              /*

               * At least one of CPU DAI name or CPU devicename/node must be

               * specified

               */

              //这里定义了cpu_dai_name,为s3c24xx-iis,所以不执行if的内容

              if(!link->cpu_dai_name &&

                  !(link->cpu_name ||link->cpu_of_node)) {

                     dev_err(card->dev,

                            "Neithercpu_dai_name nor cpu_name/of_node are set for %s\n",

                            link->name);

                     return-EINVAL;

              }

       }

       //设置声卡设备驱动信息

       dev_set_drvdata(card->dev,card);

       //初始化声卡设备列表

       snd_soc_initialize_card_lists(card);

 

       soc_init_card_debugfs(card);

       //为声卡中的snd_soc_pcm_runtime数据结构分配内存空间

       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].dai_link= &card->dai_link[i];

 

       INIT_LIST_HEAD(&card->list);

       INIT_LIST_HEAD(&card->dapm_dirty);

       card->instantiated= 0;          //表明声卡还没有被初始化

       mutex_init(&card->mutex);

       mutex_init(&card->dapm_mutex);

       //初始化声卡

       ret= snd_soc_instantiate_card(card);

       if(ret != 0)

              soc_cleanup_card_debugfs(card);

 

       returnret;

}

 

初始化声卡函数:

static int snd_soc_instantiate_card(structsnd_soc_card *card)

{

       structsnd_soc_codec *codec;

       structsnd_soc_codec_conf *codec_conf;

       enumsnd_soc_compress_type compress_type;

       structsnd_soc_dai_link *dai_link;

       intret, i, order, dai_fmt;

 

       mutex_lock_nested(&card->mutex,SND_SOC_CARD_CLASS_INIT);

 

       /*bind DAIs */

       for(i = 0; i < card->num_links; i++) {

              //逐一绑定声卡的各类DAI链接,下面会详细介绍该函数

              ret= soc_bind_dai_link(card, i);

              if(ret != 0)

                     gotobase_error;

       }

 

       /*check aux_devs too */

       //snd_soc_s3c24xx_uda134x没有定义num_aux_devs,所以不执行for内容

       for(i = 0; i < card->num_aux_devs; i++) {

              ret= soc_check_aux_dev(card, i);

              if(ret != 0)

                     gotobase_error;

       }

 

       /*initialize the register cache for each available codec */

       //遍历CODEC列表中的所有CODEC

       list_for_each_entry(codec,&codec_list, list) {

              //CODEC缓存是否已初始化

              if(codec->cache_init)

                     continue;

              /*by default we don't override the compress_type */

              //设置压缩类型

              compress_type= 0;

              /*check to see if we need to override the compress_type */

              for(i = 0; i < card->num_configs; ++i) {

                     codec_conf= &card->codec_conf[i];

                     if(!strcmp(codec->name, codec_conf->dev_name)) {

                            compress_type= codec_conf->compress_type;

                            if(compress_type && compress_type

                                != codec->compress_type)

                                   break;

                     }

              }

              /*初始化CODEC缓存,该函数最终调用sound/soc/soc-cache.c文件内的snd_soc_flat_cache_init函数,为缓存(reg_cache)开辟内存空间,并把codec->cache_init置为1*/

              ret= snd_soc_init_codec_cache(codec, compress_type);

              if(ret < 0)

                     gotobase_error;

       }

 

       /*card bind complete so register a sound card */

       //创建声卡

       ret= snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,

                     card->owner,0, &card->snd_card);

       if(ret < 0) {

              pr_err("asoc:can't create sound card for card %s: %d\n",

                     card->name,ret);

              gotobase_error;

       }

       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);

 

#ifdef CONFIG_DEBUG_FS

       snd_soc_dapm_debugfs_init(&card->dapm,card->debugfs_card_root);

#endif

 

#ifdef CONFIG_PM_SLEEP

       /*deferred resume work */

       INIT_WORK(&card->deferred_resume_work,soc_resume_deferred);

#endif

 

       if(card->dapm_widgets)

              snd_soc_dapm_new_controls(&card->dapm,card->dapm_widgets,

                                     card->num_dapm_widgets);

 

       /*initialise the sound card only once */

       //这里没有定义probe的回调函数,所以不执行if内容

       if(card->probe) {

              ret= card->probe(card);

              if(ret < 0)

                     gotocard_probe_error;

       }

 

       /*probe all components used by 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++) {

                     //probe所有声卡上被用来DAI链接的成员,下面会详细介绍

                     ret= soc_probe_link_components(card, i, order);

                     if(ret < 0) {

                            pr_err("asoc:failed to instantiate card %s: %d\n",

                                   card->name, ret);

                            gotoprobe_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++) {

                     /*probe所有声卡上的DAI链接,主要执行两个任务:一是调用了s3c24xx_i2s_probe函数,完成i2s控制器时钟启动和s3c24xx的i2s对应的io口配置初始化;二是执行了soc_new_pcm函数来创建PCM*/

                     ret= soc_probe_link_dais(card, i, order);

                     if(ret < 0) {

                            pr_err("asoc:failed to instantiate card %s: %d\n",

                                   card->name, ret);

                            gotoprobe_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);

                     gotoprobe_aux_dev_err;

              }

       }

 

       snd_soc_dapm_link_dai_widgets(card);

 

       if(card->controls)

              snd_soc_add_card_controls(card,card->controls, card->num_controls);

 

       if(card->dapm_routes)

              snd_soc_dapm_add_routes(&card->dapm,card->dapm_routes,

                                   card->num_dapm_routes);

 

       snd_soc_dapm_new_widgets(&card->dapm);

 

       for(i = 0; i < card->num_links; i++) {

              dai_link= &card->dai_link[i];

              dai_fmt= dai_link->dai_fmt;

 

              if(dai_fmt) {

                     //配置DAI硬件音频格式

                     ret= snd_soc_dai_set_fmt(card->rtd[i].codec_dai,

                                            dai_fmt);

                     if(ret != 0 && ret != -ENOTSUPP)

                            dev_warn(card->rtd[i].codec_dai->dev,

                                    "Failed to set DAI format: %d\n",

                                    ret);

              }

 

              /*If this is a regular CPU link there will be a platform */

              if(dai_fmt &&

                  (dai_link->platform_name ||dai_link->platform_of_node)) {

                     ret= snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,

                                            dai_fmt);

                     if(ret != 0 && ret != -ENOTSUPP)

                            dev_warn(card->rtd[i].cpu_dai->dev,

                                    "Failed to set DAI format: %d\n",

                                    ret);

              }else if (dai_fmt) {

                     /*Flip the polarity for the "CPU" end */

                     dai_fmt&= ~SND_SOC_DAIFMT_MASTER_MASK;

                     switch(dai_link->dai_fmt &

                            SND_SOC_DAIFMT_MASTER_MASK){

                     caseSND_SOC_DAIFMT_CBM_CFM:

                            dai_fmt|= SND_SOC_DAIFMT_CBS_CFS;

                            break;

                     caseSND_SOC_DAIFMT_CBM_CFS:

                            dai_fmt|= SND_SOC_DAIFMT_CBS_CFM;

                            break;

                     caseSND_SOC_DAIFMT_CBS_CFM:

                            dai_fmt|= SND_SOC_DAIFMT_CBM_CFS;

                            break;

                     caseSND_SOC_DAIFMT_CBS_CFS:

                            dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;

                            break;

                     }

 

                     ret= snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,

                                            dai_fmt);

                     if(ret != 0 && ret != -ENOTSUPP)

                            dev_warn(card->rtd[i].cpu_dai->dev,

                                    "Failed to set DAI format: %d\n",

                                    ret);

              }

       }

 

       snprintf(card->snd_card->shortname,sizeof(card->snd_card->shortname),

               "%s", card->name);

       snprintf(card->snd_card->longname,sizeof(card->snd_card->longname),

               "%s", card->long_name ?card->long_name : card->name);

       snprintf(card->snd_card->driver,sizeof(card->snd_card->driver),

               "%s", card->driver_name ?card->driver_name : card->name);

       for(i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {

              switch(card->snd_card->driver[i]) {

              case'_':

              case'-':

              case'\0':

                     break;

              default:

                     if(!isalnum(card->snd_card->driver[i]))

                            card->snd_card->driver[i]= '_';

                     break;

              }

       }

 

       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);

                     gotoprobe_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) {

              pr_err("asoc:failed to register soundcard for %s: %d\n",

                                                 card->name,ret);

              gotoprobe_aux_dev_err;

       }

 

#ifdef CONFIG_SND_SOC_AC97_BUS

       /*register any AC97 codecs */

       for(i = 0; i < card->num_rtd; i++) {

              ret= soc_register_ac97_dai_link(&card->rtd[i]);

              if(ret < 0) {

                     pr_err("asoc:failed to register AC97 %s: %d\n",

                                                 card->name,ret);

                     while(--i >= 0)

                            soc_unregister_ac97_dai_link(card->rtd[i].codec);

                     gotoprobe_aux_dev_err;

              }

       }

#endif

 

       card->instantiated= 1;

       snd_soc_dapm_sync(&card->dapm);

       mutex_unlock(&card->mutex);

 

       return0;

 

probe_aux_dev_err:

       for(i = 0; i < card->num_aux_devs; i++)

              soc_remove_aux_dev(card,i);

 

probe_dai_err:

       soc_remove_dai_links(card);

 

card_probe_error:

       if(card->remove)

              card->remove(card);

 

       snd_card_free(card->snd_card);

 

base_error:

       mutex_unlock(&card->mutex);

 

       returnret;

}

 

soc_bind_dai_link函数为:

static int soc_bind_dai_link(structsnd_soc_card *card, int num)

{

       //这里的dai_link为s3c24xx_uda134x_dai_link

       structsnd_soc_dai_link *dai_link = &card->dai_link[num];             

       structsnd_soc_pcm_runtime *rtd = &card->rtd[num];

       structsnd_soc_codec *codec;

       structsnd_soc_platform *platform;

       structsnd_soc_dai *codec_dai, *cpu_dai;

       constchar *platform_name;

 

       dev_dbg(card->dev,"binding %s at idx %d\n", dai_link->name, num);

 

       /*Find CPU DAI from registered DAIs*/

       /*在前面介绍过的snd_soc_register_dais函数和snd_soc_register_dai函数内,为DAI列表添加了两项内容,这两项的driver分别为uda134x_dai和s3c24xx_i2s_dai,现在遍历该列表*/

       list_for_each_entry(cpu_dai,&dai_list, list) {

              //s3c24xx_uda134x_dai_link没有定义cpu_of_node,所以不执行if内容

              if(dai_link->cpu_of_node &&

                  (cpu_dai->dev->of_node !=dai_link->cpu_of_node))

                     continue;

              //s3c24xx_uda134x_dai_link没有定义cpu_name,所以不执行if内容

              if(dai_link->cpu_name &&

                  strcmp(dev_name(cpu_dai->dev),dai_link->cpu_name))

                     continue;

              /*s3c24xx_uda134x_dai_link定义的cpu_dai_name为s3c24xx-iis。DAI列表中两项内容name分别为uda134x-hifi和s3c24xx-iis。所以当DAI列表中的内容name为uda134x-hifi时,执行if语句;为s3c24xx-iis时,不执行if语句

              if(dai_link->cpu_dai_name &&

                  strcmp(cpu_dai->name,dai_link->cpu_dai_name))

                     continue;

              //由上一条if语句分析得到rtd->cpu_dai为设备名为s3c24xx-iis的DAI

              rtd->cpu_dai= cpu_dai;

       }

 

       if(!rtd->cpu_dai) {

              dev_err(card->dev,"CPU DAI %s not registered\n",

                     dai_link->cpu_dai_name);

              return-EPROBE_DEFER;

       }

 

       /*Find CODEC from registered CODECs */

       /*在前面介绍过的snd_soc_register_codec函数内,为CODEC列表添加了一项内容,它的dev为uda1340_codec,codec_drv为soc_codec_dev_uda134x,现在遍历该列表*/

       list_for_each_entry(codec,&codec_list, list) {

              //s3c24xx_uda134x_dai_link没有定义codec_of_node,所以执行else内容

              if(dai_link->codec_of_node) {

                     if(codec->dev->of_node != dai_link->codec_of_node)

                            continue;

              }else {

                     //name都是uda134x-codec,所以不执行if语句

                     if(strcmp(codec->name, dai_link->codec_name))

                            continue;

              }

              //把CODEC列表中唯一的一项赋值给rtd->codec

              rtd->codec= codec;

 

              /*

               * CODEC found, so find CODEC DAI fromregistered DAIs from

               * this CODEC

               */

              //再一次遍历DAI列表

              list_for_each_entry(codec_dai,&dai_list, list) {

                     /*dev都是uda1340_codec平台设备,而且name都是uda134x-hifi,所以执行if内容*/

                     if(codec->dev == codec_dai->dev &&

                            !strcmp(codec_dai->name,

                                   dai_link->codec_dai_name)){

                            //把uda134x_dai赋值给rtd->codec_dai

                            rtd->codec_dai= codec_dai;

                     }

              }

 

              if(!rtd->codec_dai) {

                     dev_err(card->dev,"CODEC DAI %s not registered\n",

                            dai_link->codec_dai_name);

                     return-EPROBE_DEFER;

              }

       }

 

       if(!rtd->codec) {

              dev_err(card->dev,"CODEC %s not registered\n",

                     dai_link->codec_name);

              return-EPROBE_DEFER;

       }

 

       /*if there's no platform we match on the empty platform */

       platform_name= dai_link->platform_name;              //为samsung-audio

       if(!platform_name && !dai_link->platform_of_node)

              platform_name= "snd-soc-dummy";

 

       /*find one from the set of registered platforms */

       /*在前面介绍过的snd_soc_register_platform函数内,为platform列表添加了一项内容,它的dev为samsung_asoc_dma,现在遍历该列表*/

       list_for_each_entry(platform,&platform_list, list) {

              //s3c24xx_uda134x_dai_link没有定义platform_of_node,所以执行else内容

              if(dai_link->platform_of_node) {

                     if(platform->dev->of_node !=

                         dai_link->platform_of_node)

                            continue;

              }else {

                     //名字都是samsung-audio,所以不执行if内容

                     if(strcmp(platform->name, platform_name))

                            continue;

              }

              //把dev为samsung_asoc_dma的内容赋值给rtd->platform

              rtd->platform= platform;

       }

       if(!rtd->platform) {

              dev_err(card->dev,"platform %s not registered\n",

                     dai_link->platform_name);

              return-EPROBE_DEFER;

       }

 

       card->num_rtd++;

 

       return0;

}

 

soc_probe_link_components函数为:

static int soc_probe_link_components(structsnd_soc_card *card, int num,

                                 int order)

{

       structsnd_soc_pcm_runtime *rtd = &card->rtd[num];

       structsnd_soc_dai *cpu_dai = rtd->cpu_dai;

       structsnd_soc_dai *codec_dai = rtd->codec_dai;

       structsnd_soc_platform *platform = rtd->platform;

       intret;

 

       /*probe the CPU-side component, if it is a CODEC */

       /*由soc_bind_dai_link函数可知,cup_dai的平台设备是s3c24xx-iis,它没有codec回调函数,所以不执行if内容*/

       if(cpu_dai->codec &&

           !cpu_dai->codec->probed &&

          cpu_dai->codec->driver->probe_order == order) {

              ret= soc_probe_codec(card, cpu_dai->codec);

              if(ret < 0)

                     returnret;

       }

 

       /*probe the CODEC-side component */

       //codec_dai指的是uda134x_dai,它的codec是uda1340_codec

       if(!codec_dai->codec->probed &&

          codec_dai->codec->driver->probe_order == order) {

              //主要是调用uda134x_soc_probe,并为CODEC的控制数组——uda1340_snd_controls

              ret= soc_probe_codec(card, codec_dai->codec);

              if(ret < 0)

                     returnret;

       }

 

       /*probe the platform */

       //没有为samsung_asoc_platform定义probe回调函数

       if(!platform->probed &&

           platform->driver->probe_order ==order) {

              ret= soc_probe_platform(card, platform);

              if(ret < 0)

                     returnret;

       }

 

       return0;

}

 

在soc_new_pcm函数内,通过调用snd_pcm_new函数又调用了_snd_pcm_new函数。_snd_pcm_new函数使用snd_device_new函数创建ALSA声卡,并把它添加进声卡设备列表中,其中该设备的ops为:

static struct snd_device_ops ops = {

              .dev_free= snd_pcm_dev_free,

              .dev_register=       snd_pcm_dev_register,

              .dev_disconnect= snd_pcm_dev_disconnect,

       };

另外_snd_pcm_new函数还使用snd_pcm_new_stream函数创建了PCM的放音和录音两个流。

在soc_new_pcm函数内,还为PCM的操作符(ops)赋值,并且应用snd_pcm_set_ops函数把该ops赋值给了snd_pcm_substream的ops。

在snd_soc_instantiate_card函数内用到了snd_card_create函数和snd_card_register函数创建和注册声卡。在snd_card_create函数内,主要是使用snd_ctl_create函数,然后再次调用snd_device_new函数把声卡设备添加进列表中。其中设备的ops为:

static struct snd_device_ops ops = {

              .dev_free= snd_ctl_dev_free,

              .dev_register=       snd_ctl_dev_register,

              .dev_disconnect= snd_ctl_dev_disconnect,

       };

 

在snd_card_register函数内,它首先使用device_create函数创建声卡设备节点,然后又使用snd_device_register_all函数来真正注册声卡设备:

int snd_device_register_all(struct snd_card*card)

{

       structsnd_device *dev;

       interr;

      

       if(snd_BUG_ON(!card))

              return-ENXIO;

       //遍历声卡设备列表,在前面提到的snd_device_new函数已经添加了两个card->devices,现在就把它提取出来

       list_for_each_entry(dev,&card->devices, list) {

              if(dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {

                     //dev->ops->dev_register(dev)实际上回调了前面介绍过的两个ops的dev_register,分别为snd_ctl_dev_register函数和snd_pcm_dev_register函数

                     if((err = dev->ops->dev_register(dev)) < 0)

                            returnerr;

                     dev->state= SNDRV_DEV_REGISTERED;

              }

       }

       return0;

}

snd_ctl_dev_register函数和snd_pcm_dev_register函数主要的作用都是调用snd_register_device_for_dev函数注册,并把相关信息保存到snd_minors数组内,其中用snd_ctl_dev_register函数保存的Control文件操作为:

static const struct file_operationssnd_ctl_f_ops =

{

       .owner= THIS_MODULE,

       .read=           snd_ctl_read,

       .open=          snd_ctl_open,

       .release= snd_ctl_release,

       .llseek=  no_llseek,

       .poll=            snd_ctl_poll,

       .unlocked_ioctl=   snd_ctl_ioctl,

       .compat_ioctl=      snd_ctl_ioctl_compat,

       .fasync= snd_ctl_fasync,

};

用snd_pcm_dev_register函数保存的PCM文件操作为:

const struct file_operationssnd_pcm_f_ops[2] = {

       {

              .owner=        THIS_MODULE,

              .write=          snd_pcm_write,

              .aio_write=           snd_pcm_aio_write,

              .open=                 snd_pcm_playback_open,

              .release=        snd_pcm_release,

              .llseek=         no_llseek,

              .poll=                   snd_pcm_playback_poll,

              .unlocked_ioctl=   snd_pcm_playback_ioctl,

              .compat_ioctl=     snd_pcm_ioctl_compat,

              .mmap=               snd_pcm_mmap,

              .fasync=        snd_pcm_fasync,

              .get_unmapped_area=   snd_pcm_get_unmapped_area,

       },

       {

              .owner=        THIS_MODULE,

              .read=                  snd_pcm_read,

              .aio_read=            snd_pcm_aio_read,

              .open=                 snd_pcm_capture_open,

              .release=        snd_pcm_release,

              .llseek=         no_llseek,

              .poll=                   snd_pcm_capture_poll,

              .unlocked_ioctl=   snd_pcm_capture_ioctl,

              .compat_ioctl=     snd_pcm_ioctl_compat,

              .mmap=               snd_pcm_mmap,

              .fasync=        snd_pcm_fasync,

              .get_unmapped_area=   snd_pcm_get_unmapped_area,

       }

};

 

至此,创建声卡的整个过程就完成了。

你可能感兴趣的:(基于S3C2440的Linux-3.6.6移植——声卡驱动)