首页 > 代码库 > Nouveau源码分析(四):NVIDIA设备初始化之nouveau_drm_load (1)
Nouveau源码分析(四):NVIDIA设备初始化之nouveau_drm_load (1)
Nouveau源码分析(四)
probe函数成功返回之后,DRM模块就会调用struct drm_driver的load函数,对应nouveau的nouveau_drm_load.
这个函数虽然看起来不是特别长,但每一个调用的函数展开后就会变得非常长了!
// /drivers/gpu/drm/nouveau/nouveau_drm.c 364 static int 365 nouveau_drm_load(struct drm_device *dev, unsigned long flags) 366 { 367 struct pci_dev *pdev = dev->pdev; 368 struct nouveau_drm *drm; 369 int ret; 370 371 ret = nouveau_cli_create(nouveau_name(dev), "DRM", sizeof(*drm), 372 (void **)&drm); 373 if (ret) 374 return ret; 375 376 dev->dev_private = drm; 377 drm->dev = dev; 378 nvkm_client(&drm->client.base)->debug = 379 nouveau_dbgopt(nouveau_debug, "DRM"); 380 381 INIT_LIST_HEAD(&drm->clients); 382 spin_lock_init(&drm->tile.lock); 383 384 nouveau_get_hdmi_dev(drm); 385 386 /* make sure AGP controller is in a consistent state before we 387 * (possibly) execute vbios init tables (see nouveau_agp.h) 388 */ 389 if (pdev && drm_pci_device_is_agp(dev) && dev->agp) { 390 const u64 enables = NV_DEVICE_V0_DISABLE_IDENTIFY | 391 NV_DEVICE_V0_DISABLE_MMIO; 392 /* dummy device object, doesn't init anything, but allows 393 * agp code access to registers 394 */ 395 ret = nvif_device_init(&drm->client.base.base, NULL, 396 NVDRM_DEVICE, NV_DEVICE, 397 &(struct nv_device_v0) { 398 .device = ~0, 399 .disable = ~enables, 400 .debug0 = ~0, 401 }, sizeof(struct nv_device_v0), 402 &drm->device); 403 if (ret) 404 goto fail_device; 405 406 nouveau_agp_reset(drm); 407 nvif_device_fini(&drm->device); 408 } 409 410 ret = nvif_device_init(&drm->client.base.base, NULL, NVDRM_DEVICE, 411 NV_DEVICE, 412 &(struct nv_device_v0) { 413 .device = ~0, 414 .disable = 0, 415 .debug0 = 0, 416 }, sizeof(struct nv_device_v0), 417 &drm->device); 418 if (ret) 419 goto fail_device; 420 421 dev->irq_enabled = true; 422 423 /* workaround an odd issue on nvc1 by disabling the device's 424 * nosnoop capability. hopefully won't cause issues until a 425 * better fix is found - assuming there is one... 426 */ 427 if (drm->device.info.chipset == 0xc1) 428 nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000); 429 430 nouveau_vga_init(drm); 431 nouveau_agp_init(drm); 432 433 if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { 434 ret = nouveau_vm_new(nvkm_device(&drm->device), 0, (1ULL << 40), 435 0x1000, &drm->client.vm); 436 if (ret) 437 goto fail_device; 438 439 nvkm_client(&drm->client.base)->vm = drm->client.vm; 440 } 441 442 ret = nouveau_ttm_init(drm); 443 if (ret) 444 goto fail_ttm; 445 446 ret = nouveau_bios_init(dev); 447 if (ret) 448 goto fail_bios; 449 450 ret = nouveau_display_create(dev); 451 if (ret) 452 goto fail_dispctor; 453 454 if (dev->mode_config.num_crtc) { 455 ret = nouveau_display_init(dev); 456 if (ret) 457 goto fail_dispinit; 458 } 459 460 nouveau_sysfs_init(dev); 461 nouveau_hwmon_init(dev); 462 nouveau_accel_init(drm); 463 nouveau_fbcon_init(dev); 464 465 if (nouveau_runtime_pm != 0) { 466 pm_runtime_use_autosuspend(dev->dev); 467 pm_runtime_set_autosuspend_delay(dev->dev, 5000); 468 pm_runtime_set_active(dev->dev); 469 pm_runtime_allow(dev->dev); 470 pm_runtime_mark_last_busy(dev->dev); 471 pm_runtime_put(dev->dev); 472 } 473 return 0; 474 475 fail_dispinit: 476 nouveau_display_destroy(dev); 477 fail_dispctor: 478 nouveau_bios_takedown(dev); 479 fail_bios: 480 nouveau_ttm_fini(drm); 481 fail_ttm: 482 nouveau_agp_fini(drm); 483 nouveau_vga_fini(drm); 484 fail_device: 485 nvif_device_fini(&drm->device); 486 nouveau_cli_destroy(&drm->client); 487 return ret; 488 }
第371行,创建一个nouveau_drm结构体. 这里,我们重新梳理一下nouveau中结构体的线索.
第一条线索 struct nouveau_xxx,这个我们在上一篇中已经见到了很多了,比如nouveau_device nouveau_client nouveau_object [以下简称nv结构体/nv对象]
另一条线索 struct nvif_xxx/struct nouveau_cli,这个我们从这一节会碰到,比如nouveau_cli nvif_client nvif_object nvif_device[以下简称nvif结构体/nvif对象]
每一个nvif对象都会有对应一个nv对象,可以通过ioctl来访问. 比如对一个nvif对象执行ioctl_rd32,就可以执行对应nv对象oclass里的rd32方法.
仔细阅读下面这个图片:
方块里的是类型名称,箭头上的是成员名称 [带*号的表示指针].
我们现在要创建的就是处于最顶层的nouveau_drm,可以看到如果要和上一节创建的nouveau_device对应起来,还有一段路要走.
因为nouveau_drm这个结构体成员包含的面很广,从物理显存、虚拟映射显存,到channel,dma,以及硬件加速移动显存,还有显示模式切换都会有所涉及,所以在此处先不分析这个结构体的每一个成员,先跟着代码走下去吧.
因为struct nouveau_drm第一个成员就是struct nouveau_cli client; 所以可以把nouveau_cli看作是它的base,因此,第371行创建一个struct nouveau_cli,大小是sizeof(struct nouveau_drm) .
第108行,因为nouveau_cli的base是nvif_client,所以这里调用nvif_client的init函数.
第112行,如果nvif_client初始化成功的话,就初始化一个mutex .
第113行,初始化usif. 这是个什么东西呢? 其实就是nvif对user空间的一个接口. 让运行在用户态下的程序能创建usif对象,然后进行读写什么的.
这个初始化usif的函数很简单,就是初始化两个链表.
再来看刚才的nvif_client_init函数:
仔细阅读下面这个图片:
方块里的是类型名称,箭头上的是成员名称 [带*号的表示指针].
我们现在要创建的就是处于最顶层的nouveau_drm,可以看到如果要和上一节创建的nouveau_device对应起来,还有一段路要走.
因为nouveau_drm这个结构体成员包含的面很广,从物理显存、虚拟映射显存,到channel,dma,以及硬件加速移动显存,还有显示模式切换都会有所涉及,所以在此处先不分析这个结构体的每一个成员,先跟着代码走下去吧.
因为struct nouveau_drm第一个成员就是struct nouveau_cli client; 所以可以把nouveau_cli看作是它的base,因此,第371行创建一个struct nouveau_cli,大小是sizeof(struct nouveau_drm) .
// /drivers/gpu/drm/nouveau/nouveau_drm.c 102 static int 103 nouveau_cli_create(u64 name, const char *sname, 104 int size, void **pcli) 105 { 106 struct nouveau_cli *cli = *pcli = kzalloc(size, GFP_KERNEL); 107 if (cli) { 108 int ret = nvif_client_init(NULL, NULL, sname, name, 109 nouveau_config, nouveau_debug, 110 &cli->base); 111 if (ret == 0) { 112 mutex_init(&cli->mutex); 113 usif_client_init(cli); 114 } 115 return ret; 116 } 117 return -ENOMEM; 118 }第106行,kzalloc分配内存.
第108行,因为nouveau_cli的base是nvif_client,所以这里调用nvif_client的init函数.
第112行,如果nvif_client初始化成功的话,就初始化一个mutex .
第113行,初始化usif. 这是个什么东西呢? 其实就是nvif对user空间的一个接口. 让运行在用户态下的程序能创建usif对象,然后进行读写什么的.
这个初始化usif的函数很简单,就是初始化两个链表.
// /drivers/gpu/drm/nouveau/nouveau_usif.c 379 void 380 usif_client_init(struct nouveau_cli *cli) 381 { 382 INIT_LIST_HEAD(&cli->objects); 383 INIT_LIST_HEAD(&cli->notifys); 384 }
再来看刚才的nvif_client_init函数:
// /drivers/gpu/drm/nouveau/nvif/client.c 69 int 70 nvif_client_init(void (*dtor)(struct nvif_client *), const char *driver, 71 const char *name, u64 device, const char *cfg, const char *dbg, 72 struct nvif_client *client) 73 { 74 int ret, i; 75 76 ret = nvif_object_init(NULL, (void*)dtor, 0, 0, NULL, 0, &client->base); 77 if (ret) 78 return ret; 79 80 client->base.parent = &client->base; 81 client->base.handle = ~0; 82 client->object = &client->base; 83 client->super = true; 84 85 for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) { 86 if (!driver || !strcmp(client->driver->name, driver)) { 87 ret = client->driver->init(name, device, cfg, dbg, 88 &client->base.priv); 89 if (!ret || driver) 90 break; 91 } 92 } 93 94 if (ret) 95 nvif_client_fini(client); 96 return ret; 97 }第76行,首先初始化nvif_object:
// /drivers/gpu/drm/nouveau/nvif/object.c 217 int 218 nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *), 219 u32 handle, u32 oclass, void *data, u32 size, 220 struct nvif_object *object) 221 { 222 struct ctor *ctor; 223 int ret = 0; 224 225 object->parent = NULL; 226 object->object = object; 227 nvif_object_ref(parent, &object->parent); 228 kref_init(&object->refcount); 229 object->handle = handle; 230 object->oclass = oclass; 231 object->data = http://www.mamicode.com/NULL;>首先各种填充字段:
这里的oclass是创建nouveau_object的时候用的.
这里的handle是nouveau_object和nvif_object一一对应用的.
然后dtor是析构函数指针,object是nvif_object()用的,其实就是nvif对象间的转换.// /drivers/gpu/drm/nouveau/nvif/object.h 37 #define nvif_object(a) (a)->object
前面说过,每一个nvif_object会对应一个nouveau_object,但对于这个例子来说,创建nouveau_object的代码实际上在第254行,由于object->parent为0,根本执行不到,所以并不会使用这个oclass和handle. [当然,这个nvif_object还是有对应的nouveau_object对象的,只不过不在这里创建了,等会儿会讲到.]
那个if语句的代码也暂时不展开,等到以后真正执行到再回来说. 于是这个函数就这么返回了.
先回到nvif_client_init,下面还有一个循环语句,遍历nvif_drivers:// /drivers/gpu/drm/nouveau/nvif/client.c 58 const struct nvif_driver * 59 nvif_drivers[] = { 60 #ifdef __KERNEL__ 61 &nvif_driver_nvkm, 62 #else 63 &nvif_driver_drm, 64 &nvif_driver_lib, 65 #endif 66 NULL 67 };很明显这是内核里的代码,因此只存在一个nvif_driver,那就是nvif_driver_nvkm.
再看看这个for循环,很明显我们将会调用nvif_driver_nvkm.init():// /drivers/gpu/drm/nouveau/nouveau_nvif.c 125 const struct nvif_driver 126 nvif_driver_nvkm = { 127 .name = "nvkm", 128 .init = nvkm_client_init, 129 .fini = nvkm_client_fini, 130 .suspend = nvkm_client_suspend, 131 .resume = nvkm_client_resume, 132 .ioctl = nvkm_client_ioctl, 133 .map = nvkm_client_map, 134 .unmap = nvkm_client_unmap, 135 .keep = false, 136 };// /drivers/gpu/drm/nouveau/nouveau_nvif.c 109 static int 110 nvkm_client_init(const char *name, u64 device, const char *cfg, 111 const char *dbg, void **ppriv) 112 { 113 struct nouveau_client *client; 114 int ret; 115 116 ret = nouveau_client_create(name, device, cfg, dbg, &client); 117 *ppriv = client; 118 if (ret) 119 return ret; 120 121 client->ntfy = nvkm_client_ntfy; 122 return 0; 123 }首先创建一个nouveau_client,这个其实就是一个nouveau_object,也就是说在这里创建了这个nvif_object对应的nouveau_object .
第121行,一个有关notify的函数. notify貌似是一个回调工具,创建一个里面包含函数指针,数据什么的;当发生某个条件后,可以触发这个notify,然后就会调用里面的函数.
再来看nouveau_client_create:// /drivers/gpu/drm/nouveau/core/include/core/client.h 39 #define nouveau_client_create(n,c,oc,od,d) 40 nouveau_client_create_((n), (c), (oc), (od), sizeof(**d), (void **)d) 41 42 int nouveau_client_create_(const char *name, u64 device, const char *cfg, 43 const char *dbg, int, void **);又一次见到了这个东西,直接去看nouveau_client_create_// /drivers/gpu/drm/nouveau/core/core/client.c 203 int 204 nouveau_client_create_(const char *name, u64 devname, const char *cfg, 205 const char *dbg, int length, void **pobject) 206 { 207 struct nouveau_object *device; 208 struct nouveau_client *client; 209 int ret; 210 211 device = (void *)nouveau_device_find(devname); 212 if (!device) 213 return -ENODEV; 214 215 ret = nouveau_namedb_create_(NULL, NULL, &nouveau_client_oclass, 216 NV_CLIENT_CLASS, NULL, 217 (1ULL << NVDEV_ENGINE_DEVICE), 218 length, pobject); 219 client = *pobject; 220 if (ret) 221 return ret; 222 223 ret = nouveau_handle_create(nv_object(client), ~0, ~0, 224 nv_object(client), &client->root); 225 if (ret) 226 return ret; 227 228 /* prevent init/fini being called, os in in charge of this */ 229 atomic_set(&nv_object(client)->usecount, 2); 230 231 nouveau_object_ref(device, &client->device); 232 snprintf(client->name, sizeof(client->name), "%s", name); 233 client->debug = nouveau_dbgopt(dbg, "CLIENT"); 234 return 0; 235 }第211行,首先寻找对应的nouveau_device.
第215行,创建namedb,通过看最开始的结构体,应该也能想象出是个什么东西:// /drivers/gpu/drm/nouveau/core/core/namedb.c 166 int 167 nouveau_namedb_create_(struct nouveau_object *parent, 168 struct nouveau_object *engine, 169 struct nouveau_oclass *oclass, u32 pclass, 170 struct nouveau_oclass *sclass, u64 engcls, 171 int length, void **pobject) 172 { 173 struct nouveau_namedb *namedb; 174 int ret; 175 176 ret = nouveau_parent_create_(parent, engine, oclass, pclass | 177 NV_NAMEDB_CLASS, sclass, engcls, 178 length, pobject); 179 namedb = *pobject; 180 if (ret) 181 return ret; 182 183 rwlock_init(&namedb->lock); 184 INIT_LIST_HEAD(&namedb->list); 185 return 0; 186 }先创建一个nouveau_parent,这个结构体和nouveau_engine都能把u32 oclass转换成对应的nouveau_oclass *oclass .
接下来初始化namedb的list链表,这个链表和nouveau_handle的node链表相连,表示储存在namedb里的nouveau_handle对象 .// /drivers/gpu/drm/nouveau/core/core/parent.c 110 int 111 nouveau_parent_create_(struct nouveau_object *parent, 112 struct nouveau_object *engine, 113 struct nouveau_oclass *oclass, u32 pclass, 114 struct nouveau_oclass *sclass, u64 engcls, 115 int size, void **pobject) 116 { 117 struct nouveau_parent *object; 118 struct nouveau_sclass *nclass; 119 int ret; 120 121 ret = nouveau_object_create_(parent, engine, oclass, pclass | 122 NV_PARENT_CLASS, size, pobject); 123 object = *pobject; 124 if (ret) 125 return ret; 126 127 while (sclass && sclass->ofuncs) { 128 nclass = kzalloc(sizeof(*nclass), GFP_KERNEL); 129 if (!nclass) 130 return -ENOMEM; 131 132 nclass->sclass = object->sclass; 133 object->sclass = nclass; 134 nclass->engine = engine ? nv_engine(engine) : NULL; 135 nclass->oclass = sclass; 136 sclass++; 137 } 138 139 object->engine = engcls; 140 return 0; 141 }首先创建一个nouveau_object对象.
然后下面这个while语句,把sclass连成了一个链表放在了object->sclass中.
然后engcls表示可以用来完成u32 oclass转换的engines.
如果要通过一个nouveau_parent对象来完成u32 oclass的转换,会先从object->sclass中查找.
如果没找到,那么就查看object->engine中指示可用的engine,再从这些engine中查找.
此处的sclass实际上是0,所以object->sclass为0. 而且因为这个结构体实际上是nouveau_client,转换u32 oclass的时候还有一个特别的处理,会直接使用client->device,也就是nouveau_device,到时候再具体说.
回到nouveau_client_create_,接下来创建一个nouveau_handle *root.// /drivers/gpu/drm/nouveau/core/core/handle.c 99 int 100 nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle, 101 struct nouveau_object *object, 102 struct nouveau_handle **phandle) 103 { 104 struct nouveau_object *namedb; 105 struct nouveau_handle *handle; 106 int ret; 107 108 namedb = parent; 109 while (!nv_iclass(namedb, NV_NAMEDB_CLASS)) 110 namedb = namedb->parent; 111 112 handle = kzalloc(sizeof(*handle), GFP_KERNEL); 113 if (!handle) 114 return -ENOMEM; 115 116 INIT_LIST_HEAD(&handle->head); 117 INIT_LIST_HEAD(&handle->tree); 118 handle->name = _handle; 119 handle->priv = ~0; 120 121 ret = nouveau_namedb_insert(nv_namedb(namedb), _handle, object, handle); 122 if (ret) { 123 kfree(handle); 124 return ret; 125 } 126 127 if (nv_parent(parent)->object_attach) { 128 ret = nv_parent(parent)->object_attach(parent, object, _handle); 129 if (ret < 0) { 130 nouveau_handle_destroy(handle); 131 return ret; 132 } 133 134 handle->priv = ret; 135 } 136 137 if (object != namedb) { 138 while (!nv_iclass(namedb, NV_CLIENT_CLASS)) 139 namedb = namedb->parent; 140 141 handle->parent = nouveau_namedb_get(nv_namedb(namedb), _parent); 142 if (handle->parent) { 143 list_add(&handle->head, &handle->parent->tree); 144 nouveau_namedb_put(handle->parent); 145 } 146 } 147 148 hprintk(handle, TRACE, "created\n"); 149 *phandle = handle; 150 return 0; 151 }首先把parent向上查找,直到找到一个nouveau_namedb,对于这个例子,parent本来就是一个nouveau_namedb,所以parent == namedb.
接着初始化handle的一些字段,紧接着使用nouveau_namedb_insert把这个插入到namedb中. handle的node链表和namedb的list链表相连.// /drivers/gpu/drm/nouveau/core/core/namedb.c 87 int 88 nouveau_namedb_insert(struct nouveau_namedb *namedb, u32 name, 89 struct nouveau_object *object, 90 struct nouveau_handle *handle) 91 { 92 int ret = -EEXIST; 93 write_lock_irq(&namedb->lock); 94 if (!nouveau_namedb_lookup(namedb, name)) { 95 nouveau_object_ref(object, &handle->object); 96 handle->namedb = namedb; 97 list_add(&handle->node, &namedb->list); 98 ret = 0; 99 } 100 write_unlock_irq(&namedb->lock); 101 return ret; 102 }这个insert函数首先查找是否有重复的,有就返回-EEXIST,然后用链表连起来,返回.
第127行,检查parent有没有一个关联handle和object的函数,对于这个例子,内存用kzalloc分配,之后也没有对这个进行初始化. 所以不会进入这个if语句.
第137行,如果object和namedb不一样,那么寻找_parent对应的handle,然后把当前handle的head链表加入到parent handle的tree链表中.
对于这个例子object和namedb相等,因此不会执行进去.
这个地方可以顺便看一下nouveau_namedb_get和nouveau_namedb_put:// /drivers/gpu/drm/nouveau/core/core/namedb.c 115 struct nouveau_handle * 116 nouveau_namedb_get(struct nouveau_namedb *namedb, u32 name) 117 { 118 struct nouveau_handle *handle; 119 read_lock(&namedb->lock); 120 handle = nouveau_namedb_lookup(namedb, name); 121 if (handle == NULL) 122 read_unlock(&namedb->lock); 123 return handle; 124 }// /drivers/gpu/drm/nouveau/core/core/namedb.c 159 void 160 nouveau_namedb_put(struct nouveau_handle *handle) 161 { 162 if (handle) 163 read_unlock(&handle->namedb->lock); 164 }// /drivers/gpu/drm/nouveau/core/core/namedb.c 30 static struct nouveau_handle * 31 nouveau_namedb_lookup(struct nouveau_namedb *namedb, u32 name) 32 { 33 struct nouveau_handle *handle; 34 35 list_for_each_entry(handle, &namedb->list, node) { 36 if (handle->name == name) 37 return handle; 38 } 39 40 return NULL; 41 }代码都很容易理解,自己尝试阅读一下,不多说了. 回到刚才那个函数:
第149行,把phandle赋值为handle,返回.
回到nouveau_client_create_,第229行,初始化几个字段,比如把device字段储存上对应的nouveau_device,这个也就是我们在上一篇中用nouveau_device_create创建的那个.
返回! 至此我们终于可以回到nouveau_drm_load了!
第376行,把新创建的nouveau_drm放进drm提供的结构体里.
第377行,把drm的结构体放进nouveau_drm里.
第378行,一个debug配置信息. 这里的nvkm_xxx就是获得nvif_object所对应的nouveau_object,并把它转换成nouveau_xxx .
第381行,初始化clients链表. 对nouveau设备文件进行open操作时就会创建一个nouveau_cli并加入这个链表. 里面包含的是每个文件特有的东西,比如前面提到的usif .
第384行,获取hdmi设备. 不知道hdmi是什么的可以去百度谷歌一下.// /drivers/gpu/drm/nouveau/nouveau_drm.c 336 static void 337 nouveau_get_hdmi_dev(struct nouveau_drm *drm) 338 { 339 struct pci_dev *pdev = drm->dev->pdev; 340 341 if (!pdev) { 342 DRM_INFO("not a PCI device; no HDMI\n"); 343 drm->hdmi_device = NULL; 344 return; 345 } 346 347 /* subfunction one is a hdmi audio device? */ 348 drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number, 349 PCI_DEVFN(PCI_SLOT(pdev->devfn), 1)); 350 351 if (!drm->hdmi_device) { 352 NV_DEBUG(drm, "hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1); 353 return; 354 } 355 356 if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) { 357 NV_DEBUG(drm, "possible hdmi device not audio %d\n", drm->hdmi_device->class); 358 pci_dev_put(drm->hdmi_device); 359 drm->hdmi_device = NULL; 360 return; 361 } 362 }第341行,检查是否是PCI设备.
第348行,使用PCI模块的函数获取HDMI对应的PCI设备.
第356行,检查获取的HDMI设备的class信息.
接下来的nvif_device_init又是一个大函数,暂且先到这. 下一篇再讨论nvif_device_init函数.
Nouveau源码分析(四):NVIDIA设备初始化之nouveau_drm_load (1)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。