Android AC97驱动杂记

AC97驱动从设备加载开始,对于AC97核心的加载暂时不说。
以WM9713+S3C6410为例说明一个设备的加载并且驱动,并且实现应用的整个过程。
对于驱动整个驱动而言以这四个文件为主
1.wm9713.c
2.s3c64xx_wm9713.c
3.s3c64xx_ac97.c
4.soc-core.c

在s3c64xx_wm9713.c中的smdk6400_init会注册一个platform设备,而设备的驱动在soc-core.c中,由于在platform驱动中以名称来匹配设备和驱动。
设备注册smdk6400_snd_ac97_device,同时将soc设备的数据存储到smdk6400_snd_ac97_device的私有数据中即private_data,这个在平时的驱动中很常见。
static struct platform_device *smdk6400_snd_ac97_device;
static int __init smdk6400_init(void)
{
 int ret;
//注册一个设备名为soc-audio的平台设备
 smdk6400_snd_ac97_device = platform_device_alloc("soc-audio", -1);
 if (!smdk6400_snd_ac97_device)
  return -ENOMEM;

 platform_set_drvdata(smdk6400_snd_ac97_device,&smdk6400_snd_ac97_devdata);
 smdk6400_snd_ac97_devdata.dev = &smdk6400_snd_ac97_device->dev; 把设备保存到soc-device中。在linux中利用采用交叉的办法互相保存信息。
 ret = platform_device_add(smdk6400_snd_ac97_device);

 if (ret)
  platform_device_put(smdk6400_snd_ac97_device);
 if(ret == 0)
  printk("smdk6400_init ok/n");

 return ret;
}
对于每一个soc设备,都会用如下的结构描述。
struct snd_soc_device {
 struct device *dev;
 struct snd_soc_card *card;  ---card
 struct snd_soc_codec *codec;  ---编解码器
 struct snd_soc_codec_device *codec_dev; --编解码器设备
 void *codec_data;    ---编解码器数据
};
如smdk6400的soc设备如下描述
static struct snd_soc_device smdk6400_snd_ac97_devdata = {
 .card = &smdk6400, 
 .codec_dev = &soc_codec_dev_wm9713,
};
对于每一个的soc的声卡都会有如下的结构去描述
/* SoC card */
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 are used to do any PM work before and
  * after the codec and DAI's do any PM work. */
 int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
 int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
 int (*resume_pre)(struct platform_device *pdev);
 int (*resume_post)(struct platform_device *pdev);

 /* callbacks */
 int (*set_bias_level)(struct snd_soc_card *,
         enum snd_soc_bias_level level);

 /* CPU <--> Codec DAI links  */
 struct snd_soc_dai_link *dai_link;  --dai(digital audio interface)类似有a-b之间的链路描述
 int num_links;

 struct snd_soc_device *socdev;   --用于保存其父的soc-device指针

 struct snd_soc_platform *platform; --保存平台
 struct delayed_work delayed_work;
 struct work_struct deferred_resume_work;
};
如smdk6400的card描述
static struct snd_soc_card smdk6400 = {
 .name = "SMDK6400",
 .platform = &s3c24xx_soc_platform,
 .dai_link = smdk6400_dai,
 .num_links = ARRAY_SIZE(smdk6400_dai),
};

/* SoC platform interface */
soc平台接口,主要是提供pcm的实现,其中主要是DMA的应用
struct snd_soc_platform {
 char *name;
 struct list_head list;

 int (*probe)(struct platform_device *pdev);
 int (*remove)(struct platform_device *pdev);
 int (*suspend)(struct snd_soc_dai *dai);
 int (*resume)(struct snd_soc_dai *dai);

 /* pcm creation and destruction */
 int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
  struct snd_pcm *);
 void (*pcm_free)(struct snd_pcm *);

 /* platform stream ops */
 struct snd_pcm_ops *pcm_ops;
};

struct snd_soc_platform s3c24xx_soc_platform = {
 .name  = "s3c24xx-audio",
 .pcm_ops  = &s3c24xx_pcm_ops,
 .pcm_new = s3c24xx_pcm_new,
 .pcm_free = s3c24xx_pcm_free_dma_buffers,
};

/* SoC machine DAI configuration, glues a codec and cpu DAI together */
soc的dai配置,将codec和cpu链接到一起
struct snd_soc_dai_link  {
 char *name;   /* Codec name */ codec名
 char *stream_name;  /* Stream name */ 流名

 /* DAI */
 struct snd_soc_dai *codec_dai; 
 struct snd_soc_dai *cpu_dai;

 /* machine stream operations */
 struct snd_soc_ops *ops; 

 /* codec/machine specific init - e.g. add machine controls */
 int (*init)(struct snd_soc_codec *codec);

 /* DAI pcm */
 struct snd_pcm *pcm;
};

static struct snd_soc_dai_link smdk6400_dai[] = {
{
 .name = "AC97",
 .stream_name = "AC97 HiFi",
 .cpu_dai = &s3c6400_ac97_dai[0],
 .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
},
};
下面是cpu和codec的dai。
struct snd_soc_dai s3c6400_ac97_dai[] = {
{
 .name = "s3c64xx-ac97",
 .id = 0,
 .ac97_control = 1,
// .type = SND_SOC_DAI_AC97,
 .probe = s3c6400_ac97_probe,
 .remove = s3c6400_ac97_remove,
 .playback = {
  .stream_name = "AC97 Playback",
  .channels_min = 2,
  .channels_max = 2,
  .rates = s3c6400_AC97_RATES,
  .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 .capture = {
  .stream_name = "AC97 Capture",
  .channels_min = 2,
  .channels_max = 2,
  .rates = s3c6400_AC97_RATES,
  .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 .ops = {
  .hw_params = s3c6400_ac97_hw_params,
  .prepare = s3c6400_ac97_hifi_prepare,
  .trigger = s3c6400_ac97_trigger},
},
};

struct snd_soc_dai wm9713_dai[] =
{
 {
 .name = "AC97 HiFi",
 .ac97_control = 1,
 .playback = {
  .stream_name = "HiFi Playback",
  .channels_min = 1,
  .channels_max = 2,
  .rates = WM9713_RATES,
  .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 .capture = {
  .stream_name = "HiFi Capture",
  .channels_min = 1,
  .channels_max = 2,
  .rates = WM9713_RATES,
  .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 .ops = {
  .prepare = ac97_hifi_prepare,
  .set_clkdiv = wm9713_set_dai_clkdiv,
  .set_pll = wm9713_set_dai_pll,},
 },
 {
 .name = "AC97 Aux",
 .playback = {
  .stream_name = "Aux Playback",
  .channels_min = 1,
  .channels_max = 1,
  .rates = WM9713_RATES,
  .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 .ops = {
  .prepare = ac97_aux_prepare,
  .set_clkdiv = wm9713_set_dai_clkdiv,
  .set_pll = wm9713_set_dai_pll,},
 },
 {
 .name = "WM9713 Voice",
 .playback = {
  .stream_name = "Voice Playback",
  .channels_min = 1,
  .channels_max = 1,
  .rates = WM9713_PCM_RATES,
  .formats = WM9713_PCM_FORMATS,},
 .capture = {
  .stream_name = "Voice Capture",
  .channels_min = 1,
  .channels_max = 2,
  .rates = WM9713_PCM_RATES,
  .formats = WM9713_PCM_FORMATS,},
 .ops = {
  .hw_params = wm9713_pcm_hw_params,
  .shutdown = wm9713_voiceshutdown,
  .set_clkdiv = wm9713_set_dai_clkdiv,
  .set_pll = wm9713_set_dai_pll,
  .set_fmt = wm9713_set_dai_fmt,
  .set_tristate = wm9713_set_dai_tristate,
 },
 },
};
以上是一些结构体说明。除了smdk6400_init外还有一些设备的init。
如:
static int __init s3c6400_ac97_init(void)
{
 return snd_soc_register_dais(s3c6400_ac97_dai,ARRAY_SIZE(s3c6400_ac97_dai));
}
static int __init wm9713_modinit(void)
{
 return snd_soc_register_dais(wm9713_dai,ARRAY_SIZE(wm9713_dai));
}
以上是设备以及dai等的注册
int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count)
{
 int i, ret;

 for (i = 0; i < count; i++) {
  ret = snd_soc_register_dai(&dai[i]);
  if (ret != 0)
   goto err;
 }

 return 0;

err:
 for (i--; i >= 0; i--)
  snd_soc_unregister_dai(&dai[i]);

 return ret;
}
/**
 * snd_soc_register_dai - Register a DAI with the ASoC core
 *
 * @dai: DAI to register
 */
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
 if (!dai->name)
  return -EINVAL;

 /* The device should become mandatory over time */
 if (!dai->dev)
  printk(KERN_WARNING "No device for DAI %s/n", dai->name);

 INIT_LIST_HEAD(&dai->list);

 mutex_lock(&client_mutex);
 list_add(&dai->list, &dai_list); --dai_list为一全局变量,所有的dai均会挂载该链表下。
 snd_soc_instantiate_cards();
 mutex_unlock(&client_mutex);

 pr_debug("Registered DAI '%s'/n", dai->name);

 return 0;
}
/*
 * Attempt to initialise any uninitalised cards.  Must be called with client_mutex.
 */
 尝试实例化所有的card,在上述情况下调用不会有任何用,因为所有的card_list为空,并没有card挂在其上。
static void snd_soc_instantiate_cards(void)
{
 struct snd_soc_card *card;
 list_for_each_entry(card, &card_list, list) --card_list为全局变量。list_for_each_entry查询每一个card的list成员
  snd_soc_instantiate_card(card);  --实例化card
}

asoc_platform注册
static int __init s3c24xx_soc_platform_init(void)
{
 return snd_soc_register_platform(&s3c24xx_soc_platform);
}

int snd_soc_register_platform(struct snd_soc_platform *platform)
{
 if (!platform->name)
  return -EINVAL;

 INIT_LIST_HEAD(&platform->list);

 mutex_lock(&client_mutex);
 list_add(&platform->list, &platform_list);
 snd_soc_instantiate_cards();
 mutex_unlock(&client_mutex);

 pr_debug("Registered platform '%s'/n", platform->name);

 return 0;
}
以上即为设备的注册。
以下是驱动的注册
设备驱动的注册,其对应着上面所说的smdk6400_snd_ac97_device平台设备。
static int __init snd_soc_init(void)
{
  return platform_driver_register(&soc_driver);
}

/* ASoC platform driver */
static struct platform_driver soc_driver = {
 .driver  = {
  .name  = "soc-audio",
  .owner  = THIS_MODULE,
 },
 .probe  = soc_probe,
 .remove  = soc_remove,
 .suspend = soc_suspend,
 .resume  = soc_resume,
};
在以上驱动注册好了之后,便是驱动和设备的探测probe,记住现在的platform_device为smdk6400_snd_ac97_device
static int soc_probe(struct platform_device *pdev)
{
 int ret = 0;
 struct snd_soc_device *socdev = platform_get_drvdata(pdev); -- smdk6400_snd_ac97_devdata
 struct snd_soc_card *card = socdev->card;  --smdk6400


 /* Bodge while we push things out of socdev */
 card->socdev = socdev;

 /* Bodge while we unpick instantiation */
 card->dev = &pdev->dev;
 ret = snd_soc_register_card(card); --注册card到ASoc核
 if (ret != 0) {
  dev_err(&pdev->dev, "Failed to register card/n");
  return ret;
 }

 return 0;
}

/**
 * snd_soc_register_card - Register a card with the ASoC core
 *
 * @card: Card to register
 *
 * Note that currently this is an internal only function: it will be
 * exposed to machine drivers after further backporting of ASoC v2
 * registration APIs.
 */
static int snd_soc_register_card(struct snd_soc_card *card)
{
 if (!card->name || !card->dev)
  return -EINVAL;

 INIT_LIST_HEAD(&card->list);
 card->instantiated = 0;

 mutex_lock(&client_mutex);
 list_add(&card->list, &card_list);
 snd_soc_instantiate_cards();  --这个在上面说过但是这个函数在这儿会被执行
 mutex_unlock(&client_mutex);

 dev_dbg(card->dev, "Registered card '%s'/n", card->name);

 return 0;
}

static void snd_soc_instantiate_cards(void)
{
 struct snd_soc_card *card;
 list_for_each_entry(card, &card_list, list) 现在card_list 上有一个card 就是smdk6400
  snd_soc_instantiate_card(card);
}

static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
 struct platform_device *pdev = container_of(card->dev, struct platform_device,dev); --从card->dev 倒推出platform_device,很常见
 struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev; --card->socdev->codec_dev为smdk6400_snd_ac97_devdata
 struct snd_soc_platform *platform;
 struct snd_soc_dai *dai;
 int i, found, ret, ac97;

 if (card->instantiated)
  return;

 found = 0;
 list_for_each_entry(platform, &platform_list, list) --现在的platform_list上有一个platform s3c24xx_soc_platform
  if (card->platform == platform) { 判断card->platform是否注册
   found = 1;
   break;
  }
 if (!found) {
  dev_dbg(card->dev, "Platform %s not registered/n",
   card->platform->name);
  return;
 }

 ac97 = 0;
 for (i = 0; i < card->num_links; i++) {   num_links = ARRAY_SIZE(smdk6400_dai)  = 1
  found = 0;
  list_for_each_entry(dai, &dai_list, list) 现在的dai_list上有多个dais,ARRAY_SIZE(s3c6400_ac97_dai)+ARRAY_SIZE(wm9713_dai)
   if (card->dai_link[i].cpu_dai == dai) { 判断card->dai_link[i].cpu_dai是否注册
    found = 1;
    break;
   }
  if (!found) {
   dev_dbg(card->dev, "DAI %s not registered/n",
    card->dai_link[i].cpu_dai->name);
   return;
  }

  if (card->dai_link[i].cpu_dai->ac97_control) 在cpu_dai中的ac97_control=1
   ac97 = 1;
 }

 /* If we have AC97 in the system then don't wait for the
  * codec.  This will need revisiting if we have to handle
  * systems with mixed AC97 and non-AC97 parts.  Only check for
  * DAIs currently; we can't do this per link since some AC97
  * codecs have non-AC97 DAIs.
  */
 if (!ac97)
  for (i = 0; i < card->num_links; i++) {
   found = 0;
   list_for_each_entry(dai, &dai_list, list)
    if (card->dai_link[i].codec_dai == dai) {
     found = 1;
     break;
    }
   if (!found) {
    dev_dbg(card->dev, "DAI %s not registered/n",
     card->dai_link[i].codec_dai->name);
    return;
   }
  }

 /* Note that we do not current check for codec components */

 dev_dbg(card->dev, "All components present, instantiating/n");

 /* Found everything, bring it up */
 以下是一个一个看probe是否存在,存在就调用
 if (card->probe) {      card->probe=NULL
  ret = card->probe(pdev);
  if (ret < 0)
   return;
 }

 for (i = 0; i < card->num_links; i++) {   探测链路,因为s3c6400_ac97_dai存在probe,会调用s3c6400_ac97_probe
  struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
  if (cpu_dai->probe) {
   ret = cpu_dai->probe(pdev, cpu_dai);
   if (ret < 0)
    goto cpu_dai_err;
  }
 }

 if (codec_dev->probe) {    codec_dev->probe 存在为wm9713_soc_probe,会在这儿调用
  ret = codec_dev->probe(pdev);
  if (ret < 0)
   goto cpu_dai_err;
 }

 if (platform->probe) {    这个为空。
  ret = platform->probe(pdev);
  if (ret < 0)
   goto platform_err;
 }
   忽略动态电源管理
 /* DAPM stream work */
 INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
 /* deferred resume work */
 INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif

 card->instantiated = 1;

 return;

platform_err:
 if (codec_dev->remove)
  codec_dev->remove(pdev);

cpu_dai_err:
 for (i--; i >= 0; i--) {
  struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
  if (cpu_dai->remove)
   cpu_dai->remove(pdev, cpu_dai);
 }

 if (card->remove)
  card->remove(pdev);
}

硬件资源申请并初始化
static int s3c6400_ac97_probe(struct platform_device *pdev)
{
 int ret;

 s3cdbg("Entered %s/n", __FUNCTION__);

 s3c24xx_ac97.regs = ioremap(S3C6400_PA_AC97, 0x100);
 if (s3c24xx_ac97.regs == NULL)
  return -ENXIO;

 s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
 if (s3c24xx_ac97.ac97_clk == NULL) {
  printk(KERN_ERR "s3c6400-ac97 failed to get ac97_clock/n");
  iounmap(s3c24xx_ac97.regs);
  return -ENODEV;
 }
 clk_enable(s3c24xx_ac97.ac97_clk);
 
        s3c_gpio_cfgpin(S3C64XX_GPD(0),S3C64XX_GPD0_AC97_BITCLK);
        s3c_gpio_cfgpin(S3C64XX_GPD(1),S3C64XX_GPD1_AC97_nRESET);
        s3c_gpio_cfgpin(S3C64XX_GPD(2),S3C64XX_GPD2_AC97_SYNC);
        s3c_gpio_cfgpin(S3C64XX_GPD(3),S3C64XX_GPD3_AC97_SDI);
        s3c_gpio_cfgpin(S3C64XX_GPD(4),S3C64XX_GPD4_AC97_SDO);

        s3c_gpio_setpull(S3C64XX_GPD(0),S3C_GPIO_PULL_NONE);
        s3c_gpio_setpull(S3C64XX_GPD(1),S3C_GPIO_PULL_NONE);
        s3c_gpio_setpull(S3C64XX_GPD(2),S3C_GPIO_PULL_NONE);
        s3c_gpio_setpull(S3C64XX_GPD(3),S3C_GPIO_PULL_NONE);
        s3c_gpio_setpull(S3C64XX_GPD(4),S3C_GPIO_PULL_NONE);

 ret = request_irq(IRQ_AC97, s3c6400_ac97_irq,
  IRQF_DISABLED, "AC97", NULL);
 if (ret < 0) {
  printk(KERN_ERR "s3c24xx-ac97: interrupt request failed./n");
  clk_disable(s3c24xx_ac97.ac97_clk);
  clk_put(s3c24xx_ac97.ac97_clk);
  iounmap(s3c24xx_ac97.regs);
 }

 return ret;
}

wm9713的初始化以及管理。
static int wm9713_soc_probe(struct platform_device *pdev)
{
 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 struct snd_soc_codec *codec;
 int ret = 0, reg;

 printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s/n", WM9713_VERSION);

 socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 为编解码器结构开辟空间
 if (socdev->codec == NULL)
  return -ENOMEM;
 codec = socdev->codec;
 mutex_init(&codec->mutex);

 codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL);
 if (codec->reg_cache == NULL) {
  ret = -ENOMEM;
  goto cache_err;
 }
 codec->reg_cache_size = sizeof(wm9713_reg);
 codec->reg_cache_step = 2;

 codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
 if (codec->private_data == NULL) {
  ret = -ENOMEM;
  goto priv_err;
 }

 codec->name = "WM9713";
 codec->owner = THIS_MODULE;
 codec->dai = wm9713_dai;
 codec->num_dai = ARRAY_SIZE(wm9713_dai);
 codec->write = ac97_write;
 codec->read = ac97_read;
 codec->set_bias_level = wm9713_set_bias_level;
 INIT_LIST_HEAD(&codec->dapm_widgets);
 INIT_LIST_HEAD(&codec->dapm_paths);
//初始化一个AC97接口的编解码器在soc_ac97_ops中实现soc的ac97的冷复位,软复位,读写,实际是为编解码器的提供一个基于AC97总线的操作
 ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
 if (ret < 0)
  goto codec_err;

 /* register pcms */
 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 if (ret < 0)
  goto pcm_err;

 /* do a cold reset for the controller and then try
  * a warm reset followed by an optional cold reset for codec */
 wm9713_reset(codec, 0);
 ret = wm9713_reset(codec, 1);
 if (ret < 0) {
  printk(KERN_ERR "Failed to reset WM9713: AC97 link error/n");
  goto reset_err;
 }

 wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

 /* unmute the adc - move to kcontrol */
 reg = ac97_read(codec, AC97_CD) & 0x7fff;
 ac97_write(codec, AC97_CD, reg);
    /****************************************/
 printk("[WM9713]Open speaker volume./n");
 ac97_write(codec, AC97_MASTER, 0x8080);
    /****************************************/
 wm9713_add_controls(codec);
 wm9713_add_widgets(codec);
 ret = snd_soc_init_card(socdev);
 int i=0;
 for(i=0;i<100000;i++);
 if (ret < 0)
  goto reset_err;

 return 0;

reset_err:
 snd_soc_free_pcms(socdev);

pcm_err:
 snd_soc_free_ac97_codec(codec);

codec_err:
 kfree(codec->private_data);

priv_err:
 kfree(codec->reg_cache);

cache_err:
 kfree(socdev->codec);
 socdev->codec = NULL;
 return ret;
}
 struct snd_ac97_bus_ops {
 void (*reset) (struct snd_ac97 *ac97);
 void (*warm_reset)(struct snd_ac97 *ac97);
 void (*write) (struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
 unsigned short (*read) (struct snd_ac97 *ac97, unsigned short reg);
 void (*wait) (struct snd_ac97 *ac97);
 void (*init) (struct snd_ac97 *ac97);
};

struct snd_ac97_bus_ops soc_ac97_ops = {
 .read = s3c6400_ac97_read,
 .write = s3c6400_ac97_write,
 .warm_reset = s3c6400_ac97_warm_reset,
 .reset = s3c6400_ac97_cold_reset,
};
/**
 * snd_soc_new_ac97_codec - initailise AC97 device
 * @codec: audio codec
 * @ops: AC97 bus operations
 * @num: AC97 codec number
 *
 * Initialises AC97 codec resources for use by ad-hoc devices only.
 */
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,struct snd_ac97_bus_ops *ops, int num)
{
 mutex_lock(&codec->mutex);

 codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
 if (codec->ac97 == NULL) {
  mutex_unlock(&codec->mutex);
  return -ENOMEM;
 }

 codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
 if (codec->ac97->bus == NULL) {
  kfree(codec->ac97);
  codec->ac97 = NULL;
  mutex_unlock(&codec->mutex);
  return -ENOMEM;
 }

 codec->ac97->bus->ops = ops;
 codec->ac97->num = num;
 mutex_unlock(&codec->mutex);
 return 0;
}

int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
{
 struct snd_soc_codec *codec = socdev->codec;  --记住现在codec为在wm9713_probe中初始化的codec,name为"WM9713"
 struct snd_soc_card *card = socdev->card;    --记住现在card依然为smdk6400
 int ret = 0, i;

 mutex_lock(&codec->mutex);

 /* register a sound card */
 codec->card = snd_card_new(idx, xid, codec->owner, 0); --申请和注册一个声卡结构
 if (!codec->card) {
  printk(KERN_ERR "asoc: can't create sound card for codec %s/n",
   codec->name);
  mutex_unlock(&codec->mutex);
  return -ENODEV;
 }

 codec->card->dev = socdev->dev;
 codec->card->private_data = codec;
 strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));

 /* create the pcms */
 for (i = 0; i < card->num_links; i++) {   card 为smdk6400
  ret = soc_new_pcm(socdev, &card->dai_link[i], i);
  if (ret < 0) {
   printk(KERN_ERR "asoc: can't create pcm %s/n",
    card->dai_link[i].stream_name);
   mutex_unlock(&codec->mutex);
   return ret;
  }
 }

 mutex_unlock(&codec->mutex);
 return ret;
}

/* runtime channel data */
struct snd_soc_pcm_runtime {
 struct snd_soc_dai_link *dai;
 struct snd_soc_device *socdev;
};

/* create a new pcm */
static struct snd_pcm_ops s3c24xx_pcm_ops = {
 .open  = s3c24xx_pcm_open,
 .close  = s3c24xx_pcm_close,
 .ioctl  = snd_pcm_lib_ioctl,
 .hw_params = s3c24xx_pcm_hw_params,
 .hw_free = s3c24xx_pcm_hw_free,
 .prepare = s3c24xx_pcm_prepare,
 .trigger = s3c24xx_pcm_trigger,
 .pointer = s3c24xx_pcm_pointer,
 .mmap  = s3c24xx_pcm_mmap,
};
static int soc_new_pcm(struct snd_soc_device *socdev,
 struct snd_soc_dai_link *dai_link, int num)
{
 struct snd_soc_codec *codec = socdev->codec;
 struct snd_soc_card *card = socdev->card;
 struct snd_soc_platform *platform = card->platform;
 struct snd_soc_dai *codec_dai = dai_link->codec_dai;  --wm9713_dai
 struct snd_soc_dai *cpu_dai = dai_link->cpu_dai; --s3c6400_ac97_dai
 struct snd_soc_pcm_runtime *rtd;
 struct snd_pcm *pcm;
 char new_name[64];
 int ret = 0, playback = 0, capture = 0;

 rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
 if (rtd == NULL)
  return -ENOMEM;

 rtd->dai = dai_link;      -- smdk6400_dai
 rtd->socdev = socdev;     -- smdk6400_snd_ac97_devdata
 codec_dai->codec = socdev->codec;

 /* check client and interface hw capabilities */
 sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
  num);

 if (codec_dai->playback.channels_min)
  playback = 1;
 if (codec_dai->capture.channels_min)
  capture = 1;

 ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
  capture, &pcm);
 if (ret < 0) {
  printk(KERN_ERR "asoc: can't create pcm for codec %s/n",
   codec->name);
  kfree(rtd);
  return ret;
 }

 dai_link->pcm = pcm;
 pcm->private_data = rtd;
 soc_pcm_ops.mmap = platform->pcm_ops->mmap;   --s3c24xx_pcm_mmap
 soc_pcm_ops.pointer = platform->pcm_ops->pointer;   --s3c24xx_pcm_pointer
 soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
 soc_pcm_ops.copy = platform->pcm_ops->copy;
 soc_pcm_ops.silence = platform->pcm_ops->silence;
 soc_pcm_ops.ack = platform->pcm_ops->ack;
 soc_pcm_ops.page = platform->pcm_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);
  
 ret = platform->pcm_new(codec->card, codec_dai, pcm);  --s3c24xx_pcm_new
 if (ret < 0) {
  printk(KERN_ERR "asoc: platform pcm constructor failed/n");
  kfree(rtd);
  return ret;
 }

 pcm->private_free = platform->pcm_free;
 printk("asoc: %s <-> %s mapping ok/n", codec_dai->name,
  cpu_dai->name);
 return ret;
}


/**
 * snd_pcm_new - create a new PCM instance
 * @card: the card instance
 * @id: the id string
 * @device: the device index (zero based)
 * @playback_count: the number of substreams for playback
 * @capture_count: the number of substreams for capture
 * @rpcm: the pointer to store the new pcm instance
 *
 * Creates a new PCM instance.
 *
 * The pcm operators have to be set afterwards to the new instance
 * via snd_pcm_set_ops().
 *
 * Returns zero if successful, or a negative error code on failure.
 */
int snd_pcm_new(struct snd_card *card, char *id, int device,
  int playback_count, int capture_count,
         struct snd_pcm ** rpcm)
{
 struct snd_pcm *pcm;
 int err;
 static struct snd_device_ops ops = {
  .dev_free = snd_pcm_dev_free,
  .dev_register = snd_pcm_dev_register,
  .dev_disconnect = snd_pcm_dev_disconnect,
 };

 if (snd_BUG_ON(!card))
  return -ENXIO;
 if (rpcm)
  *rpcm = NULL;
 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 if (pcm == NULL) {
  snd_printk(KERN_ERR "Cannot allocate PCM/n");
  return -ENOMEM;
 }
 pcm->card = card;
 pcm->device = device;
 if (id)
  strlcpy(pcm->id, id, sizeof(pcm->id));
 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
  snd_pcm_free(pcm);
  printk("ERR to snd_pcm_new_stream PLAYBACK/n");
  return err;
 }
 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
  snd_pcm_free(pcm);
  printk("ERR to snd_pcm_new_stream CAPTURE/n");
  return err;
 }
 mutex_init(&pcm->open_mutex);
 init_waitqueue_head(&pcm->open_wait);
 if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
  snd_pcm_free(pcm);
  return err;
 }
 if (rpcm)
  *rpcm = pcm;
 return 0;
}


/**
 * snd_soc_init_card - register sound card
 * @socdev: the SoC audio device
 *
 * Register a SoC sound card. Also registers an AC97 device if the
 * codec is AC97 for ad hoc devices.
 *
 * Returns 0 for success, else error.
 */
int snd_soc_init_card(struct snd_soc_device *socdev)
{
 struct snd_soc_codec *codec = socdev->codec;
 struct snd_soc_card *card = socdev->card;
 int ret = 0, i, ac97 = 0, err = 0;

 for (i = 0; i < card->num_links; i++) {
  if (card->dai_link[i].init) {
   err = card->dai_link[i].init(codec);
   if (err < 0) {
    printk(KERN_ERR "asoc: failed to init %s/n",
     card->dai_link[i].stream_name);
    continue;
   }
  }
  if (card->dai_link[i].codec_dai->ac97_control)
   ac97 = 1;
 }
 snprintf(codec->card->shortname, sizeof(codec->card->shortname),
   "%s",  card->name);
 snprintf(codec->card->longname, sizeof(codec->card->longname),
   "%s (%s)", card->name, codec->name);

 ret = snd_card_register(codec->card);
 if (ret < 0) {
  printk(KERN_ERR "asoc: failed to register soundcard for %s/n",
    codec->name);
  goto out;
 }

 mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
 /* Only instantiate AC97 if not already done by the adaptor
  * for the generic AC97 subsystem.
  */
 if (ac97 && strcmp(codec->name, "AC97") != 0) {
  ret = soc_ac97_dev_register(codec);
  if (ret < 0) {
   printk(KERN_ERR "asoc: AC97 device register failed/n");
   snd_card_free(codec->card);
   mutex_unlock(&codec->mutex);
   goto out;
  }
 }
#endif

 err = snd_soc_dapm_sys_add(socdev->dev);
 if (err < 0)
  printk(KERN_WARNING "asoc: failed to add dapm sysfs entries/n");

 err = device_create_file(socdev->dev, &dev_attr_codec_reg);
 if (err < 0)
  printk(KERN_WARNING "asoc: failed to add codec sysfs files/n");

 soc_init_codec_debugfs(socdev->codec);
 mutex_unlock(&codec->mutex);

out:
 return ret;
}

/**
 *  snd_card_register - register the soundcard
 *  @card: soundcard structure
 *
 *  This function registers all the devices assigned to the soundcard.
 *  Until calling this, the ALSA control interface is blocked from the
 *  external accesses.  Thus, you should call this function at the end
 *  of the initialization of the card.
 *
 *  Returns zero otherwise a negative error code if the registrain failed.
 */
int snd_card_register(struct snd_card *card)
{
 int err;

 if (snd_BUG_ON(!card))
  return -EINVAL;
#ifndef CONFIG_SYSFS_DEPRECATED
 if (!card->card_dev) {
  card->card_dev = device_create(sound_class, card->dev,
            MKDEV(0, 0), card,
            "card%i", card->number);
  if (IS_ERR(card->card_dev))
   card->card_dev = NULL;
 }
#endif
 if ((err = snd_device_register_all(card)) < 0)
  return err;
 mutex_lock(&snd_card_mutex);
 if (snd_cards[card->number]) {
  /* already registered */
  mutex_unlock(&snd_card_mutex);
  return 0;
 }
 if (card->id[0] == '/0')
  choose_default_id(card);
 snd_cards[card->number] = card;
 mutex_unlock(&snd_card_mutex);
 init_info_for_card(card);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
 if (snd_mixer_oss_notify_callback)
  snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
#ifndef CONFIG_SYSFS_DEPRECATED
 if (card->card_dev) {
  err = device_create_file(card->card_dev, &card_id_attrs);
  if (err < 0)
   return err;
  err = device_create_file(card->card_dev, &card_number_attrs);
  if (err < 0)
   return err;
 }
#endif
 return 0;
}

以上即是从设备和驱动注册到探测,在这一过程中完成card,codec以及pcm,stream申请和注册。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值