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申请和注册。