设为首页 - 加入收藏 焦点技术网
热搜:java
当前位置:首页 >

Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

导读:Nouveau源码分析(三)向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照.对于匹配的设备,PCI模块就调用对应的probe函数,也就是nouveau_drm_probe. // /drivers/gpu/drm/nouveau/nouveau_drm.c281 static int nouveau_dr...。。。

Nouveau源码分析(三)

向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照.

对于匹配的设备,PCI模块就调用对应的probe函数,也就是nouveau_drm_probe.

// /drivers/gpu/drm/nouveau/nouveau_drm.c281 static int nouveau_drm_probe(struct pci_dev *pdev,282                              const struct pci_device_id *pent)283 {284         struct nouveau_device *device;285         struct apertures_struct *aper;286         bool boot = false;287         int ret;288 289         /* remove conflicting drivers (vesafb, efifb etc) */290         aper = alloc_apertures(3);291         if (!aper)292                 return -ENOMEM;293 294         aper->ranges[0].base = pci_resource_start(pdev, 1);295         aper->ranges[0].size = pci_resource_len(pdev, 1);296         aper->count = 1;297 298         if (pci_resource_len(pdev, 2)) {299                 aper->ranges[aper->count].base = pci_resource_start(pdev, 2);300                 aper->ranges[aper->count].size = pci_resource_len(pdev, 2);301                 aper->count++;302         }303 304         if (pci_resource_len(pdev, 3)) {305                 aper->ranges[aper->count].base = pci_resource_start(pdev, 3);306                 aper->ranges[aper->count].size = pci_resource_len(pdev, 3);307                 aper->count++;308         }309 310 #ifdef CONFIG_X86311         boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;312 #endif313         if (nouveau_modeset != 2)314                 remove_conflicting_framebuffers(aper, "nouveaufb", boot);315         kfree(aper);316 317         ret = nouveau_device_create(pdev, NOUVEAU_BUS_PCI,318                                     nouveau_pci_name(pdev), pci_name(pdev),319                                     nouveau_config, nouveau_debug, &device);320         if (ret)321                 return ret;322 323         pci_set_master(pdev);324 325         ret = drm_get_pci_dev(pdev, pent, &driver);326         if (ret) {327                 nouveau_object_ref(NULL, (struct nouveau_object **)&device);328                 return ret;329         }330 331         return 0;332 }

第290~315行,分配了一个aper,把资源位置写进去,调用了remove_conflicting_framebuffer,接着释放这个aper.

一行注释和函数名已经说的很明白,就是移除冲突的framebuffer.


第317行,创建一个NV设备的结构体,这个函数我们要仔细看

// /drivers/gpu/drm/nouveau/core/include/engine/device.h 13 #define nouveau_device_create(p,t,n,s,c,d,u)                                   \ 14         nouveau_device_create_((void *)(p), (t), (n), (s), (c), (d),           \ 15                                sizeof(**u), (void **)u) 16  17 int  nouveau_device_create_(void *, enum nv_bus_type type, u64 name, 18                             const char *sname, const char *cfg, const char *dbg, 19                             int, void **);
想起了什么? 对,就是上一节讲的内容,我们还是先来看结构体.

// /drivers/gpu/drm/nouveau/core/include/core/device.h 65 struct nouveau_device { 66         struct nouveau_engine base; 67         struct list_head head; 68  69         struct pci_dev *pdev; 70         struct platform_device *platformdev; 71         u64 handle; 72  73         struct nvkm_event event; 74  75         const char *cfgopt; 76         const char *dbgopt; 77         const char *name; 78         const char *cname; 79         u64 disable_mask; 80  81         enum { 82                 NV_04    = 0x04, 83                 NV_10    = 0x10, 84                 NV_11    = 0x11, 85                 NV_20    = 0x20, 86                 NV_30    = 0x30, 87                 NV_40    = 0x40, 88                 NV_50    = 0x50, 89                 NV_C0    = 0xc0, 90                 NV_E0    = 0xe0, 91                 GM100    = 0x110, 92         } card_type; 93         u32 chipset; 94         u32 crystal; 95  96         struct nouveau_oclass *oclass[NVDEV_SUBDEV_NR]; 97         struct nouveau_object *subdev[NVDEV_SUBDEV_NR]; 98  99         struct {100                 struct notifier_block nb;101         } acpi;102 };
第66行,可以看作是C++的基类,这个结构体等会再说吧.

第67行,链接所有NV设备的链表.

第69行,对应的PCI设备.

第70行,对应的platform设备 (两者选一,要么是PCI设备,要么是platform设备,主要讨论前者).

第71行,算是一个标识符,在创建这个结构体的时候就比较它,有相同的就认为已经被创建,返回-EEXIST.

第73行,一个事件,是和电源有关的,由AC Adapter在ACPI中发出,CLOCK中接受.

第75行,config设置.

第76行,debug设置.

第77行,PCI名称.

第78行,NV名称,比如GK110,GK20A.

第79行,表示禁用的subdev.

第92行,设备类别 [Family].

第93行,更精确的设备类别 [Chipset].

第94行,晶振频率.

第96行,每一个subdev的oclass,oclass的含义参考上一节.

第97行,subdev列表.

第101行,是传给acpi用于触发上面那个event的一个东西.


然后来看nouveau_engine

// /drivers/gpu/drm/nouveau/core/include/core/engine.h 10 struct nouveau_engine { 11         struct nouveau_subdev base; 12         struct nouveau_oclass *cclass; 13         struct nouveau_oclass *sclass; 14  15         struct list_head contexts; 16         spinlock_t lock; 17  18         void (*tile_prog)(struct nouveau_engine *, int region); 19         int  (*tlb_flush)(struct nouveau_engine *); 20 };
第11行,又是base结构体,等会再说.

第12行,貌似是context oclass,构造context object的时候用的.

第13行,通过u32 oclass得到nouveau_oclass *oclass的一个东西.

第15行,context object链表.

第16行,自旋锁.

第18~19行,瓦片? 意义不明,只在NV01~NV40 有用. [我准备讨论NVC0,因为我的显卡family就是NVC0,方便实验]


// /drivers/gpu/drm/nouveau/core/include/core/subdev.h  9 struct nouveau_subdev { 10         struct nouveau_object base; 11         struct mutex mutex; 12         const char *name; 13         void __iomem *mmio; 14         u32 debug; 15         u32 unit; 16  17         void (*intr)(struct nouveau_subdev *); 18 };
第10行,还是base结构体.

第11行,锁.

第12行,名称,主要输出调试信息的时候用.

第13行,MMIO地址.

第14行,调试级别,用于判断是否输出调试信息.

第15行,subdev析构的使用用的,猜测是禁用这个subdev.

第17行,中断处理函数指针.


终于到了object结构体------nouveau_object了.

// /drivers/gpu/drm/nouveau/core/include/core/object.h 17 struct nouveau_object { 18         struct nouveau_oclass *oclass; 19         struct nouveau_object *parent; 20         struct nouveau_object *engine; 21         atomic_t refcount; 22         atomic_t usecount; 23 #if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA 24 #define NOUVEAU_OBJECT_MAGIC 0x75ef0bad 25         struct list_head list; 26         u32 _magic; 27 #endif 28 };
第18行,oclass,作用和上一篇一样,里面包括read,write寄存器,init,fini构造析构等函数的指针.

第19行,parent,就是父结构体.

第20行,对应的engine.

第21行和第22行,两个计数器.

第23到第27行,调试用的魔数.


然后到了xxx_create_函数:

// /drivers/gpu/drm/nouveau/core/engine/device/base.c662 int663 nouveau_device_create_(void *dev, enum nv_bus_type type, u64 name,664                        const char *sname, const char *cfg, const char *dbg,665                        int length, void **pobject)666 {667         struct nouveau_device *device;668         int ret = -EEXIST;669 670         mutex_lock(&nv_devices_mutex);671         list_for_each_entry(device, &nv_devices, head) {672                 if (device->handle == name)673                         goto done;674         }675 676         ret = nouveau_engine_create_(NULL, NULL, &nouveau_device_oclass, true,677                                      "DEVICE", "device", length, pobject);678         device = *pobject;679         if (ret)680                 goto done;681 682         switch (type) {683         case NOUVEAU_BUS_PCI:684                 device->pdev = dev;685                 break;686         case NOUVEAU_BUS_PLATFORM:687                 device->platformdev = dev;688                 break;689         }690         device->handle = name;691         device->cfgopt = cfg;692         device->dbgopt = dbg;693         device->name = sname;694 695         nv_subdev(device)->debug = nouveau_dbgopt(device->dbgopt, "DEVICE");696         nv_engine(device)->sclass = nouveau_device_sclass;697         list_add(&device->head, &nv_devices);698 699         ret = nvkm_event_init(&nouveau_device_event_func, 1, 1,700                               &device->event);701 done:702         mutex_unlock(&nv_devices_mutex);703         return ret;704 }

首先获取锁,然后遍历nv_devices链表,如果handle一样,那么说明这个设备已经被创建了,返回.

676行,初始化base结构体nouveau_engine. [照例等会再看.]

但这边有一个oclass,这个必须要看:

// /drivers/gpu/drm/nouveau/core/engine/device/base.c652 static struct nouveau_oclass653 nouveau_device_oclass = {654         .handle = NV_ENGINE(DEVICE, 0x00),655         .ofuncs = &(struct nouveau_ofuncs) {656                 .dtor = nouveau_device_dtor,657                 .init = nouveau_device_init,658                 .fini = nouveau_device_fini,659         },660 };
由出现了一个handle,这个要注意区分:

1. nouveau_device的handle是用于标识设备,防止一个设备被注册多次

2. nouveau_oclass的handle,这个比较复杂,最低两位可以表示subdev的type,然后还能表示class的type [就是engine,subdev,object之类的]

3. 还有一个没接触到的nouveau_handle的handle,这个用于nouveau_namedb中搜索特定handle.

剩下的三个函数指针,执行到的时候再说吧.


然后初始化device的pdev,handle,cfgopt,dbgopt,name,这些字段上面都介绍过了,不再多说.

nv_subdev,nv_engine是什么呢? 其实就是把指针强制转换为nouveau_subdev *,nouveau_engine *,当某个控制调试程度的宏大于某个值时,会增加检查语句.

因为各种base全都是结构体的第一个字段,所以可以强制转换而不出问题.

第695行,初始化(nouveau_subdev *)device的debug字段.

第696行,这个注意一下. nouveau_engine的sclass字段前面介绍过,就是控制u32 oclass到nouveau_oclass *oclass的转换,所以我们来看看:

// /drivers/gpu/drm/nouveau/core/engine/device/base.c501 static struct nouveau_oclass502 nouveau_device_sclass[] = {503         { 0x0080, &nouveau_devobj_ofuncs },504         {}505 };
记住这个数据,0x0080,以后会用到的.

第699行,初始化device->event,然后解锁,返回.


好了,我们来看nouveau_engine_create_.

// /drivers/gpu/drm/nouveau/core/core/engine.c 29 int 30 nouveau_engine_create_(struct nouveau_object *parent, 31                        struct nouveau_object *engobj, 32                        struct nouveau_oclass *oclass, bool enable, 33                        const char *iname, const char *fname, 34                        int length, void **pobject) 35 { 36         struct nouveau_engine *engine; 37         int ret; 38  39         ret = nouveau_subdev_create_(parent, engobj, oclass, NV_ENGINE_CLASS, 40                                      iname, fname, length, pobject); 41         engine = *pobject; 42         if (ret) 43                 return ret; 44  45         if (parent) { 46                 struct nouveau_device *device = nv_device(parent); 47                 int engidx = nv_engidx(nv_object(engine)); 48  49                 if (device->disable_mask & (1ULL << engidx)) { 50                         if (!nouveau_boolopt(device->cfgopt, iname, false)) { 51                                 nv_debug(engine, "engine disabled by hw/fw\n"); 52                                 return -ENODEV; 53                         } 54  55                         nv_warn(engine, "ignoring hw/fw engine disable\n"); 56                 } 57  58                 if (!nouveau_boolopt(device->cfgopt, iname, enable)) { 59                         if (!enable) 60                                 nv_warn(engine, "disabled, %s=1 to enable\n", iname); 61                         return -ENODEV; 62                 } 63         } 64  65         INIT_LIST_HEAD(&engine->contexts); 66         spin_lock_init(&engine->lock); 67         return 0; 68 }
第39行,首先创建subdev.

第45行,判断parent,紧接着把他转换成nouveau_device,注意这个不是直接的强制转换.

// /drivers/gpu/drm/nouveau/core/include/core/device.h106 static inline struct nouveau_device *107 nv_device(void *obj)108 {109         struct nouveau_object *object = nv_object(obj);110         struct nouveau_object *device = object;111 112         if (device->engine)113                 device = device->engine;114         if (device->parent)115                 device = device->parent;116 117 #if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA118         if (unlikely(!nv_iclass(device, NV_SUBDEV_CLASS) ||119                      (nv_hclass(device) & 0xff) != NVDEV_ENGINE_DEVICE)) {120                 nv_assert("BAD CAST -> NvDevice, 0x%08x 0x%08x",121                           nv_hclass(object), nv_hclass(device));122         }123 #endif124 125         return (void *)device;126 }
第113行,先把device赋值为device->engine,然后第115行再赋值为device->parent并返回.

当然对于这个例子,device->engine和device->parent都为0,所以直接返回device.


回到刚才那个函数,第47行获取engidx,其实就是subidx,再展开就是oclass::handle的最低两位.可以参考下面这个enum:

// /drivers/gpu/drm/nouveau/core/include/core/device.h  8 enum nv_subdev_type {  9         NVDEV_ENGINE_DEVICE, 10         NVDEV_SUBDEV_VBIOS, 11  12         /* All subdevs from DEVINIT to DEVINIT_LAST will be created before 13          * *any* of them are initialised.  This subdev category is used 14          * for any subdevs that the VBIOS init table parsing may call out 15          * to during POST. 16          */ 17         NVDEV_SUBDEV_DEVINIT, 18         NVDEV_SUBDEV_GPIO, 19         NVDEV_SUBDEV_I2C, 20         NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_I2C, 21  22         /* This grouping of subdevs are initialised right after they've 23          * been created, and are allowed to assume any subdevs in the 24          * list above them exist and have been initialised. 25          */ 26         NVDEV_SUBDEV_MXM, 27         NVDEV_SUBDEV_MC, 28         NVDEV_SUBDEV_BUS, 29         NVDEV_SUBDEV_TIMER, 30         NVDEV_SUBDEV_FB, 31         NVDEV_SUBDEV_LTCG, 32         NVDEV_SUBDEV_IBUS, 33         NVDEV_SUBDEV_INSTMEM, 34         NVDEV_SUBDEV_VM, 35         NVDEV_SUBDEV_BAR, 36         NVDEV_SUBDEV_PWR, 37         NVDEV_SUBDEV_VOLT, 38         NVDEV_SUBDEV_THERM, 39         NVDEV_SUBDEV_CLOCK, 40  41         NVDEV_ENGINE_FIRST, 42         NVDEV_ENGINE_DMAOBJ = NVDEV_ENGINE_FIRST, 43         NVDEV_ENGINE_FIFO, 44         NVDEV_ENGINE_SW, 45         NVDEV_ENGINE_GR, 46         NVDEV_ENGINE_MPEG, 47         NVDEV_ENGINE_ME, 48         NVDEV_ENGINE_VP, 49         NVDEV_ENGINE_CRYPT, 50         NVDEV_ENGINE_BSP, 51         NVDEV_ENGINE_PPP, 52         NVDEV_ENGINE_COPY0, 53         NVDEV_ENGINE_COPY1, 54         NVDEV_ENGINE_COPY2, 55         NVDEV_ENGINE_VIC, 56         NVDEV_ENGINE_VENC, 57         NVDEV_ENGINE_DISP, 58         NVDEV_ENGINE_PERFMON, 59  60         NVDEV_SUBDEV_NR, 61 };
对于nouveau_device,就是NVDEV_ENGINE_DEVICE.

接着第49行检查这个engine有没有被禁用,再根据cfgopt的值决定做法.

第58行,检查cfgopt和enable是否对应,出错就返回.

第65,66行,初始化链表和自旋锁,返回.


紧接着,来看nouveau_subdev_create_:

// /drivers/gpu/drm/nouveau/core/core/subdev.c 86 int 87 nouveau_subdev_create_(struct nouveau_object *parent, 88                        struct nouveau_object *engine, 89                        struct nouveau_oclass *oclass, u32 pclass, 90                        const char *subname, const char *sysname, 91                        int size, void **pobject) 92 { 93         struct nouveau_subdev *subdev; 94         int ret; 95  96         ret = nouveau_object_create_(parent, engine, oclass, pclass | 97                                      NV_SUBDEV_CLASS, size, pobject); 98         subdev = *pobject; 99         if (ret)100                 return ret;101 102         __mutex_init(&subdev->mutex, subname, &oclass->lock_class_key);103         subdev->name = subname;104 105         if (parent) {106                 struct nouveau_device *device = nv_device(parent);107                 subdev->debug = nouveau_dbgopt(device->dbgopt, subname);108                 subdev->mmio  = nv_subdev(device)->mmio;109         }110 111         return 0;112 }
先创建nouveau_object (PS:终于快到头了!) .

接着初始化mutex,name字段

如果parent不为0,那么就把subdev的debug和mmio字段初始化为对应的这两个字段.

就这个例子来说,parent就是0,所以不会执行进去的.


最后nouveau_object_create_ :

// /drivers/gpu/drm/nouveau/core/core/object.c 33 int 34 nouveau_object_create_(struct nouveau_object *parent, 35                        struct nouveau_object *engine, 36                        struct nouveau_oclass *oclass, u32 pclass, 37                        int size, void **pobject) 38 { 39         struct nouveau_object *object; 40  41         object = *pobject = kzalloc(size, GFP_KERNEL); 42         if (!object) 43                 return -ENOMEM; 44  45         nouveau_object_ref(parent, &object->parent); 46         nouveau_object_ref(engine, &object->engine); 47         object->oclass = oclass; 48         object->oclass->handle |= pclass; 49         atomic_set(&object->refcount, 1); 50         atomic_set(&object->usecount, 0); 51  52 #ifdef NOUVEAU_OBJECT_MAGIC 53         object->_magic = NOUVEAU_OBJECT_MAGIC; 54         spin_lock(&_objlist_lock); 55         list_add(&object->list, &_objlist); 56         spin_unlock(&_objlist_lock); 57 #endif 58         return 0; 59 }
用kzmalloc分配一个大小为size (这个数是一路传下来的,大小就是sizeof(nouveau_device)) 且已经清零的内存,

因为parent,engine,object->parent,object->engine都为0,所以第45.46行代码其实啥也没做.

初始化oclass字段,然后把oclass->handle或上plass标识符. [这个数到底是多少可以向上翻,意义也很容易理解.]

初始化refcount和usecount.

接下来的魔数忽略掉,调试查错用的.

然后58行,返回!


于是我们就这么回到了nouveau_drm_probe.

第323行,启用Bus-Mastering.

第325行,向DRM注册PCI设备.

然后就是错误处理,如果失败还要把device处理一下,比如从链表中删除,释放空间等.


至于cfgopt,dbgopt,到头来分别是nouveau_config和nouveau_debug,是Nouveau的模块参数,有兴趣可以自己实验.

初始化远远没有结束,还有nouveau_drm_load,比这次这个函数不知道长多少倍.....

(编辑: GoodQt)

网友评论
相关文章