intel声卡驱动probe分析--hda_intel.c alsa

1. 关键代码及注释:

1. intel声卡初始化流程:
/sound/pci/hda/hda_intel.c
	azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) {
    
    
		snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); {
    
    //创建snd_card的一个实例
		    card->dev = parent;  
		    card->number = idx;  
		    card->module = module;    
		    INIT_LIST_HEAD(&card->devices);                  //初始化声卡设备列表
																#define INIT_LIST_HEAD(ptr) do { \
																	  (ptr)->next = (ptr); (ptr)->prev = (ptr); \
																} while (0)
		    init_rwsem(&card->controls_rwsem);              //初始化读写锁
		    rwlock_init(&card->ctl_files_rwlock);			//初始化读写锁
		    mutex_init(&card->user_ctl_lock);				//初始化锁
		    INIT_LIST_HEAD(&card->controls);				//初始化controls列表
		    INIT_LIST_HEAD(&card->ctl_files);				//初始化ctl_files列表
		    spin_lock_init(&card->files_lock);				//初始化自旋锁
		    INIT_LIST_HEAD(&card->files_list);				//初始化files_list列表
			device_initialize(&card->card_dev);				//初始化声卡设备
			err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);//the control interface cannot be accessed from the user space until snd_cards_bitmask and snd_cards are set with snd_card_register
			err = snd_ctl_create(card);{
    
    						
				snd_device_initialize(&card->ctl_dev, card); //初始化control设备                                                                                                                                   
        		dev_set_name(&card->ctl_dev, "controlC%d", card->number);
				err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //create an ALSA device component

			} 
			err = snd_info_card_create(card);{
    
    
				sprintf(str, "card%i", card->number);
				entry = create_subdir(card->module, str); {
    
    
					entry = snd_info_create_module_entry(mod, name, NULL); {
    
    //Creates a new info entry and assigns it to the given module.
						struct snd_info_entry *entry = snd_info_create_entry(name, parent);
					}
					snd_info_register(entry){
    
    
						if (S_ISDIR(entry->mode)) {
    
            //判断是否是目录
								p = proc_mkdir_mode(entry->name, entry->mode, root);
								if (!p) {
    
    
									    mutex_unlock(&info_mutex);
									    return -ENOMEM;
								}
						} else {
    
    
								const struct file_operations *ops;
								if (entry->content == SNDRV_INFO_CONTENT_DATA) //判断entry内容是SNDRV_INFO_CONTENT_DATA 1还是SNDRV_INFO_CONTENT_TEXT		0
									    ops = &snd_info_entry_operations;
								else
									    ops = &snd_info_text_entry_ops;
								p = proc_create_data(entry->name, entry->mode, root,
									                 ops, entry);
								if (!p) {
    
    
									    mutex_unlock(&info_mutex);
									    return -ENOMEM;
								}
								proc_set_size(p, entry->size);
						}
					}

				}
			}
		}		
		azx_create(card, pci, dev, pci_id->driver_data, &chip){
    
    
			INIT_LIST_HEAD(&chip->pcm_list);
			INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
			INIT_LIST_HEAD(&hda->list);
			init_vga_switcheroo(chip);
			init_completion(&hda->probe_wait);
			azx_bus_init(chip, model[dev]);{
    
                                 	//HD-audio bus initialization
				snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops, io_ops); {
    
    //初始化chip->bus结构体
					bus->io_ops = io_ops;
					INIT_LIST_HEAD(&bus->stream_list);   //初始化stream_list
					INIT_LIST_HEAD(&bus->codec_list);    //初始化codec_list
					INIT_WORK(&bus->unsol_work, process_unsol_events);
					spin_lock_init(&bus->reg_lock);
					mutex_init(&bus->cmd_mutex);
				}
			}
			snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);{
    
    			//create an ALSA device component,把芯片的专有数据注册为声卡的一个低阶设备:
				 struct snd_device *pdev = list_entry(p, struct snd_device, list);//insert the entry in an incrementally sorted list 
			}
			INIT_WORK(&hda->probe_work, azx_probe_work){
    
    
				azx_probe_continue(&hda->chip){
    
    
					azx_first_init(chip){
    
    
						err = pci_request_regions(chip->pci, "LS HD audio"); //
					    bus->addr = pci_resource_start(chip->pci, 0);
					    bus->remap_addr = pci_ioremap_bar(chip->pci, 0);
						azx_acquire_irq(chip, 0) {
    
    //请求中断
							request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, KBUILD_MODNAME, chip)
						}
						gcap = azx_readw(chip, GCAP);                             
						dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
						/* allow 64bit DMA address if supported by H/W */
						if ((gcap & AZX_GCAP_64OK) && !dma_set_mask(chip->card->dev, DMA_BIT_MASK(64)))
								dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(64));
						else {
    
            
								dma_set_mask(chip->card->dev, DMA_BIT_MASK(32));
								dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(32));
						} 
						/* read number of streams from GCAP register instead of using
						 * hardcoded value
						 */
						chip->capture_streams = (gcap >> 8) & 0x0f;
						chip->playback_streams = (gcap >> 12) & 0x0f;
						if (!chip->playback_streams && !chip->capture_streams) {
    
    
								/* gcap didn't give any info, switching to old method */
								chip->playback_streams = ICH6_NUM_PLAYBACK;
								chip->capture_streams = ICH6_NUM_CAPTURE;
						} 
						/* initialize streams */
						err = azx_init_streams(chip);{
    
                          //initialize each stream (aka device)  assign the starting bdl address to each stream (device) and initialize
							dir = stream_direction(chip, i);
							snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), i, dir, tag);{
    
    
								azx_dev->bus = bus;
								/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
								azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
								/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
								azx_dev->sd_int_sta_mask = 1 << idx;
								azx_dev->index = idx;
								azx_dev->direction = direction;
								azx_dev->stream_tag = tag;
								snd_hdac_dsp_lock_init(azx_dev); 
   								list_add_tail(&azx_dev->list, &bus->stream_list);
							}						
						}
						azx_alloc_stream_pages(chip);{
    
    
							snd_hdac_bus_alloc_stream_pages(azx_bus(chip)){
    
     //snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
								err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, BDL_SIZE, &s->bdl); //allocate memory for the BDL for each stream 
								err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, num_streams * 8, &bus->posbuf); //allocate memory for the position buffer
								list_for_each_entry(s, &bus->stream_list, list)
									s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
								err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, PAGE_SIZE, &bus->rb); //single page (at least 4096 bytes) must suffice for both ringbuffes 
							}				 
						}
						azx_init_chip(chip, (probe_only[dev] & 2) == 0);  {
    
                                   //初始化pci设备,reset and start the controller registers
							snd_hdac_bus_init_chip(azx_bus(chip), full_reset){
    
     //snd_hdac_bus_init_chip - reset and start the controller registers
								if (bus->chip_init)
										return false;
						 
								/* reset controller */
								azx_reset(bus, full_reset);
						 
								/* initialize interrupts */
								azx_int_clear(bus);
								azx_int_enable(bus);
							
								/* initialize the codec command I/O */
								snd_hdac_bus_init_cmd_io(bus);{
    
    
									spin_lock_irq(&bus->reg_lock);
									/* disable ringbuffer DMAs */
									snd_hdac_chip_writeb(bus, RIRBCTL, 0);
									snd_hdac_chip_writeb(bus, CORBCTL, 0);
									hdac_wait_for_cmd_dmas(bus);
									/* disable unsolicited responses */         
									snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
									spin_unlock_irq(&bus->reg_lock);

								}
																	 
								/* program the position buffer */
								if (bus->use_posbuf && bus->posbuf.addr) {
    
    
										snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
										snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
								}
						 
								bus->chip_init = true;
							}
							azx_writew(chip, RINTCNT, 0xc0);
						}
						snd_hdac_i915_set_bclk(bus);
						strcpy(card->driver, "HDA-Intel");                  //设置Driver的ID和名字
						strlcpy(card->shortname, driver_short_names[chip->driver_type], sizeof(card->shortname));
						snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, bus->addr, bus->irq);
					}
					/* create codec instances */
					if (bus->codec_mask) {
    
    
						err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);{
    
    
							robe_codec(chip, c){
    
    
								snd_hdac_bus_send_cmd(bus, cmd); 
								snd_hdac_bus_get_response(bus, addr, &res);
								snd_hdac_ext_bus_device_init(ebus, addr); {
    
    //initialize the HDA extended codec base device
									snd_hdac_device_init(hdev, bus, name, addr); {
    
    
										snd_hdac_bus_add_device(bus, codec);{
    
    		//Add a codec to bus 
											list_add_tail(&codec->list, &bus->codec_list); 
										}
										codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
										codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
										err = snd_hdac_refresh_widgets(codec);
										codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
										err = get_codec_vendor_name(codec);
									}
									ret = snd_hdac_device_register(hdev); {
    
    
										err = device_add(&card->card_dev);
										err = snd_device_register_all(card)) 
										snd_info_card_register(card); {
    
    /* register pending info  register the card proc file*/
											proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
										}
										init_info_for_card(card);{
    
    
											entry = snd_info_create_card_entry(card, "id", card->proc_root);
										}
									}
								}
							}
							azx_stop_chip(chip);
							azx_init_chip(chip, true);
						}
						if (err < 0)
							goto out_free;
					}
					err = snd_card_register(chip->card);                   //注册声卡
					azx_add_card_list(chip);
				}
			}
		}
		schedule_work(&hda->probe_work);
	}

具体注册过程:

module_init(alsa_card_azx_init)初始化声卡模块,会调用alsa_card_azx_init函数,在该函数中去调用platform_driver_register去注册azx_driver,而azx_driver是一个结构体,
里面存储有.driver.name = "ls-audio",
发现一个变量:cards_limit=1, 感觉像是声卡数量限制1,在alsa_sound_init时会调用到.

几个细节:
platform_driver_register(&azx_pci_driver){
    
    
	 __platform_driver_register(drv, THIS_MODULE){
    
    
		drv->driver.owner = owner;
		drv->driver.bus = &platform_bus_type;
		drv->driver.probe = platform_drv_probe;
		drv->driver.remove = platform_drv_remove;
		drv->driver.shutdown = platform_drv_shutdown;
		driver_register(&drv->driver){
    
    
			other = driver_find(drv->name, drv->bus);    //查找驱动是否已经在总线上注册,如已注册,则直接返回
			ret = bus_add_driver(drv);{
    
    					//Add a driver to the bus. 将驱动添加到虚拟总线上。
				bus = bus_get(drv->bus);
				klist_init(&priv->klist_devices, NULL, NULL);
				error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
				klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
				error = driver_attach(drv);
				module_add_driver(drv->owner, drv);
				error = driver_create_file(drv, &driver_attr_uevent);
				error = driver_add_groups(drv, bus->drv_groups);
				error = add_bind_files(drv);
			}
			ret = driver_add_groups(drv, drv->groups);
			kobject_uevent(&drv->p->kobj, KOBJ_ADD);
		}
	}
}

相关知识点:

char *kstrdup(const char *s, gfp_t gfp) //用于通过kmalloc() 申请一段内存将形参s的内容copy到这段新申请的内存中.这个函数如果返回null只有两种情况,一种是新参s为null,一种是没有申请到内存为null
snd_hdac_chip_writel
/* set the corb size to 256 entries (ULI requires explicitly) */
snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
/* set the corb write pointer to 0 */
snd_hdac_chip_writew(bus, CORBWP, 0);
/* reset the corb hw read pointer */
snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);

/* reset the corb hw read pointer */
snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);

proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
创建目录函数proc_mkdir()struct proc_dir_entry *proc_mkdir( const char *name, struct proc_dir_entry *parent)
该函数将创建一个目录,父目录为parent。
创建符号链接函数proc_symlink()struct proc_dir_entry *proc_symlink( const char *name, struct proc_dir_entry *parent, char *dest)
该函数在parent目录下创建一个名字为name的符号链接文件,链接的目标是dest。
创建设备文件函数proc_mknod()struct proc_dir_entry *proc_mknod( const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t *rdev)
该函数在parent目录下创建一个名字为name的设备文件,文件类型和权限为mode,设备号为rdev。
snd_info_create_card_entry(card, "id", card->proc_root); //create an info entry for the given card 


猜你喜欢

转载自blog.csdn.net/qq_38350702/article/details/111995812
今日推荐