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

时间:2022-12-31 17:24:03

Nouveau源代码分析(三)

向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比.

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

// /drivers/gpu/drm/nouveau/nouveau_drm.c
281 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_X86
311 boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
312 #endif
313 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.c
662 int
663 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.c
652 static struct nouveau_oclass
653 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.c
501 static struct nouveau_oclass
502 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.h
106 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_PARANOIA
118 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 #endif
124
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标识符. [这个数究竟是多少能够向上翻,意义也非常easy理解.]

初始化refcount和usecount.

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

然后58行,返回!

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

第323行,启用Bus-Mastering.

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

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

至于cfgopt,dbgopt,到头来各自是nouveau_config和nouveau_debug,是Nouveau的模块參数,有兴趣能够自己实验.

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

Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe的更多相关文章

  1. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  2. Volley简单学习使用五—— 源代码分析三

    一.Volley工作流程图: 二.Network     在NetworkDispatcher中须要处理的网络请求.由以下进行处理: NetworkResponse networkResponse = ...

  3. 【原创】Kakfa utils源代码分析&lpar;三&rpar;

    Kafka utils包最后一篇~~~ 十五.ShutdownableThread.scala 可关闭的线程抽象类! 继承自Thread同时还接收一个boolean变量isInterruptible表 ...

  4. Red5源代码分析 - 关键类及其初始化过程

    原文地址:http://semi-sleep.javaeye.com/blog/348768 Red5如何响应rmpt的请求,中间涉及哪些关键类? 响应请求的流程如下: 1.Red5在启动时会调用RT ...

  5. &lbrack;Android&rsqb;Fragment源代码分析&lpar;三&rpar; 事务

    Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩.我们先引入一个简单经常使用的Fragment事务管理代码片段: FragmentTransaction ft = thi ...

  6. hostapd源代码分析(三):管理帧的收发和处理

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  7. SDL2源代码分析1:初始化(SDL&lowbar;Init&lpar;&rpar;)

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  8. Zepto源代码分析之二~三个API

    因为时间关系:本次仅仅对这三个API($.camelCase.$.contains.$.each)方法进行分析 第一个方法变量转驼峰:$.camelCase('hello-world-welcome' ...

  9. Openck&lowbar;Swift源代码分析——添加、删除设备时算法详细的实现过程

    1 初始加入设备后.上传Object的详细流程  前几篇博客中,我们讲到环的基本原理即详细的实现过程,加入我们在初始创建Ring是执行例如以下几条命令: •swift-ring-builder obj ...

随机推荐

  1. No&period;25

    每天三件事必做: 1.背单词: 2.跑步: 3.读书.

  2. tableview最后一行显示不全

    最后一行显示不全是因为表格的高度大于了控制view的高度,减小表格的高度小于的等于控制的view的高度即可

  3. Gym 100818G (模拟退火)

    题目大意 给一张n个点的无向图,要求给每个点染色0或1,使得每个点的相邻相同颜色点的数量小于等于其度数的一半. 解题分析 没想到什么好的算法,就随机乱搞了. 若某个状态时,一个点的度数为cnt,相邻相 ...

  4. Centos6&period;4安装Mono和MonoDevelop

    Mono官方网站:http://www.mono-project.com MonoDevelop官方网站:http://monodevelop.com/ 注:整个安装过程最好在同一个终端下完成!   ...

  5. make makefile

    Gcc的编译流程预处理阶段: gcc –E hello.c –o hello.i编译阶段: gcc –S hello.i –o hello.s汇编阶段:gcc –c hello.s –o hello. ...

  6. 原生javascript学习

    首先在这里要非常感谢无私分享作品的网友们,这些代码片段主要由网友们平时分享的作品代码里面和经常去逛网站然后查看源文件收集到的.把平时网站上常用的一些实用功能代码片段通通收集起来,方便网友们学习使用,利 ...

  7. STM32 对内部FLASH读写接口函数(转)

    源:STM32 对内部FLASH读写接口函数 因为要用内部FLASH代替外部EEPROM,把参数放在STM32的0x08000000+320K处,其中20K是bootloader,300K是应用程序. ...

  8. 深入探究Lua的GC算法(下)-《Lua设计与实现》

    紧接着上一篇文章zblade:深入探究Lua的GC算法(上)-<Lua设计与实现> 这篇文章让我们收尾GC的具体后续操作.转载请标明出处:http://www.cnblogs.com/zb ...

  9. ajax对象方法的使用

    change.js文件的内容对象函数关键字:fnjQuery.fn.change = function () { this.css({"background": "red ...

  10. 一个改写MBR的例子

    前言 想要对MBR类的病毒进行一下研究与学习,在此期间,看了很多资料,其中帮助最大的就是金龟子学姐和willj学长发表的文章.一个从源码与实现角度来讲了一下,另外一个从反病毒角度来分析.   功能描述 ...