azx_probe、azx_probe_continue函数分析

azx_probe中的azx_create()函数分析


static int azx_create(struct snd_card *card, struct pci_dev *pci,
		      int dev, unsigned int driver_caps,
		      const struct hda_controller_ops *hda_ops,
		      struct azx **rchip)

在被azx_probe()调用的时候,传参如下:

	err = azx_create(card, pci, dev, pci_id->driver_data,
			 &pci_hda_ops, &chip);

1、hda = kzalloc(sizeof(*hda), GFP_KERNEL);
因为hda是由struct hda_intel定义

struct hda_intel {
	struct azx chip;
};

因为azx 的声卡芯片不止一个,由hda统一管理?所以先给hda分配一段内存。

2、 chip = &hda->chip;
这里的chip就是azx 结构体的内容

3、 spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
自旋锁,禁止内核抢占的机制
互斥锁,禁止共享资源访问

4、 chip->card = card;
chip->pci = pci;
chip->ops = hda_ops;
chip->irq = -1;
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
check_msi(chip);
chip->dev_index = dev;
chip->jackpoll_ms = jackpoll_ms;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->list);
init_vga_switcheroo(chip);
init_completion(&chip->probe_wait);
然后是一些初始化工作

5、 chip->position_fix[0] = chip->position_fix[1] =
check_position_fix(chip, position_fix[dev]);
/* combo mode uses LPIB for playback */
if (chip->position_fix[0] == POS_FIX_COMBO) {
chip->position_fix[0] = POS_FIX_LPIB;
chip->position_fix[1] = POS_FIX_AUTO;
}
1)首先在struct azx中是这样定义的,int position_fix[2];/*for both playback capture streams*/,所以是有 position_fix[0], position_fix[1]两个。应该0是对应playback, 1是对应capture。但是这个position_fix[dev]又是什么情况,这个position_fix是针对具体的dev还是针对播放/录音。
2)COMBO是混合模式,
3)position_fix 是一个标志位,在check_position_fix这个函数中,chip就是具体声卡控制器,position_fix[dev]就是要给该card设定的 position_fix的值,有LPIB,COMBO,VIACOMBO等,这个值是怎么来的呢, 在check_position_fix函数中有一个snd_pci_quirk_lookup函数,会返回position_fix的value,跟pci ssid相关,然后下面还有检查是否是VIAcontroller的函数,比较chip的driver_caps,选择返回POS_FIX_VIACOMBO还是POS_FIX_LPIB

6、 check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
chip->snoop = hda_snoop;
azx_check_snoop_available(chip);
snoop检测,兆芯已经默认

7、bdl_pos_adj

	if (bdl_pos_adj[dev] < 0) {
		switch (chip->driver_type) {
		case AZX_DRIVER_ICH:
		case AZX_DRIVER_PCH:
			bdl_pos_adj[dev] = 1;
			break;
		default:
			bdl_pos_adj[dev] = 32;
			break;
		}
	}
	chip->bdl_pos_adj = bdl_pos_adj;

这个bdl_pos_adj尚且不知道是干嘛的,在内核文档HD-Audio.txt中是这样解释的:In addition to that, every controller is known to be broken regarding the wake-up timing. It wakes up a few samples before actually processing the data on the buffer. This caused a lot of problems, for example, with ALSA dmix or JACK. Since 2.6.27 kernel, the driver puts an artificial delay to the wake up timing. This delay is controlled via bdl_pos_adj option.
When bdl_pos_adj is a negative value (as default), it’s assigned to an appropriate value depending on the controller chip. For Intel chips, it’d be 1 while it’d be 32 for others. Usually this works. Only in case it doesn’t work and you get warning messages, you should change this parameter to other values.

8、err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

9、INIT_WORK(&chip->probe_work, azx_probe_work);
这里会调用azx_probe_continue() 函数,因为azx_create()是在azx_probe()函数中被调用的

azx_probe_continue

static int azx_probe_continue(struct azx *chip)
{
	struct pci_dev *pci = chip->pci;
	int dev = chip->dev_index;

1、err = azx_first_init(chip);

因为这个函数里面多是关于PCI的一些设置,这里有关于PCI驱动使用较多的一些函数,https://blog.csdn.net/qiao_zz/article/details/53287749,大部分函数在这里面都能找到

	int dev = chip->dev_index;
	struct pci_dev *pci = chip->pci;
	struct snd_card *card = chip->card;
	int err;
	unsigned short gcap;//offset 00h,提供对controller和link的全局性的control

···1)、err = pci_request_regions(pci, “ICH HD audio”);
申请PCI io端口号和内存地址,参数表示是为一个 ICH HD Audio的pci设备申请资源。申请一块地址空间
···2)、chip->addr = pci_resource_start(pci, 0);
在《linux设备驱动程序》P314,这个函数返回pci设备的6个IO区域(pci有6个bar,基地址寄存器)之一的首地址(内存地址或IO端口号),这里取得就是bar0。获取基地址
···3)、chip->remap_addr = pci_ioremap_bar(pci, 0);
对于x86cpu来说,当cpu访问某个地址空间时,访问到的是主存还是pci设备,cpu是不知道的,这个也就是pci memory map的意义所在,map了之后,可以何普通内存一样访问。所以pci_resource_star取得的物理地址是pci设备上内存的地址,然后ioremap就可以转换这个地址,因为转换的过程是给这个物理地址建立一个页表,分配一个虚拟地址,设备驱动程序调用pci-remap-bar这个函数将写入bar的总线地址,映射到系统内存的虚拟地址,之后cpu就可以通过这个虚拟地址访问pci设备的存储空间)

···4) 、if (azx_acquire_irq(chip, 0) < 0)
这里通过下面方式调用函数azx_interrupt() 来设置interrupt handler

if (request_irq(chip->pci->irq, azx_interrupt,
		chip->msi ? 0 : IRQF_SHARED,
		KBUILD_MODNAME, chip)) 

下面链接具体分析azx_interrupt函数
https://mp.csdn.net/postedit/101534264

···5) 、gcap = azx_readw(chip, GCAP);
这个偏移量是声卡空间的首地址00h
···6) 、 chip->azx_dev = kcalloc(chip->num_streams,sizeof(chip->azx_dev),GFP_KERNEL);//给streams分配空间,因为支持15个codec
/ streams (x num_streams) */
struct azx_dev *azx_dev;
所以一个stream就是AZX的一个dev
···7) 、err = azx_alloc_stream_pages(chip);
分配了三个主要内存:BDL,DMA position buffer,CORB/CIRB
都是通过dma_alloc_pages() 的方式来分配buffer的
···8) 、azx_init_stream(chip);
这里涉及到每个stream的描述符
···9) 、azx_init_pci(chip);

···10) 、azx_init_chip(chip, (probe_only[dev] & 2) == 0);
初始化controller registers, 具体哪些寄存器,有CORB\RIRB、还有positon buffer等

2、 err = azx_codec_create
err = azx_codec_create(chip, model[dev],
azx_max_codecs[chip->driver_type],
power_save_addr);

codec相关函数见下面链接所示:
https://blog.csdn.net/zz2862625432/article/details/101348598

3、 err = snd_hda_build_pcms(chip->bus);
/* 用来create PCM streams */

4、 err = azx_mixer_create(chip);
/*用来 create mixer controls */

5、 err = snd_card_register(chip->card);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值