日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android AC97驱动杂记

發(fā)布時間:2024/3/24 Android 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android AC97驱动杂记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

AC97驅(qū)動從設(shè)備加載開始,對于AC97核心的加載暫時不說。
以WM9713+S3C6410為例說明一個設(shè)備的加載并且驅(qū)動,并且實(shí)現(xiàn)應(yīng)用的整個過程。
對于驅(qū)動整個驅(qū)動而言以這四個文件為主
1.wm9713.c
2.s3c64xx_wm9713.c
3.s3c64xx_ac97.c
4.soc-core.c

在s3c64xx_wm9713.c中的smdk6400_init會注冊一個platform設(shè)備,而設(shè)備的驅(qū)動在soc-core.c中,由于在platform驅(qū)動中以名稱來匹配設(shè)備和驅(qū)動。
設(shè)備注冊smdk6400_snd_ac97_device,同時將soc設(shè)備的數(shù)據(jù)存儲到smdk6400_snd_ac97_device的私有數(shù)據(jù)中即private_data,這個在平時的驅(qū)動中很常見。
static struct platform_device *smdk6400_snd_ac97_device;
static int __init smdk6400_init(void)
{
?int ret;
//注冊一個設(shè)備名為soc-audio的平臺設(shè)備
?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; 把設(shè)備保存到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設(shè)備,都會用如下的結(jié)構(gòu)描述。
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; --編解碼器設(shè)備
?void *codec_data;??? ---編解碼器數(shù)據(jù)
};
如smdk6400的soc設(shè)備如下描述
static struct snd_soc_device smdk6400_snd_ac97_devdata = {
?.card = &smdk6400,?
?.codec_dev = &soc_codec_dev_wm9713,
};
對于每一個的soc的聲卡都會有如下的結(jié)構(gòu)去描述
/* SoC card */
struct snd_soc_card {
?char *name;?? --聲卡名
?struct device *dev;??

?struct list_head list; --聲卡鏈表,可將該聲卡掛到總聲卡鏈表

?int instantiated;? --是否實(shí)例化

?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的實(shí)現(xiàn),其中主要是DMA的應(yīng)用
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,
?},
?},
};
以上是一些結(jié)構(gòu)體說明。除了smdk6400_init外還有一些設(shè)備的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));
}
以上是設(shè)備以及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.
?*/
?嘗試實(shí)例化所有的card,在上述情況下調(diào)用不會有任何用,因?yàn)樗械腸ard_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);? --實(shí)例化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;
}
以上即為設(shè)備的注冊。
以下是驅(qū)動的注冊
設(shè)備驅(qū)動的注冊,其對應(yīng)著上面所說的smdk6400_snd_ac97_device平臺設(shè)備。
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,
};
在以上驅(qū)動注冊好了之后,便是驅(qū)動和設(shè)備的探測probe,記住現(xiàn)在的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();? --這個在上面說過但是這個函數(shù)在這兒會被執(zhí)行
?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) 現(xiàn)在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) --現(xiàn)在的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) 現(xiàn)在的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是否存在,存在就調(diào)用
?if (card->probe) {????? card->probe=NULL
??ret = card->probe(pdev);
??if (ret < 0)
???return;
?}

?for (i = 0; i < card->num_links; i++) {?? 探測鏈路,因?yàn)閟3c6400_ac97_dai存在probe,會調(diào)用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,會在這兒調(diào)用
??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;
?}
?? 忽略動態(tài)電源管理
?/* 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); 為編解碼器結(jié)構(gòu)開辟空間
?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中實(shí)現(xiàn)soc的ac97的冷復(fù)位,軟復(fù)位,讀寫,實(shí)際是為編解碼器的提供一個基于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;? --記住現(xiàn)在codec為在wm9713_probe中初始化的codec,name為"WM9713"
?struct snd_soc_card *card = socdev->card;??? --記住現(xiàn)在card依然為smdk6400
?int ret = 0, i;

?mutex_lock(&codec->mutex);

?/* register a sound card */
?codec->card = snd_card_new(idx, xid, codec->owner, 0); --申請和注冊一個聲卡結(jié)構(gòu)
?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;
}

以上即是從設(shè)備和驅(qū)動注冊到探測,在這一過程中完成card,codec以及pcm,stream申請和注冊。

總結(jié)

以上是生活随笔為你收集整理的Android AC97驱动杂记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。