您的位置:首页 > 其它

Nouveau源码分析(四):NVIDIA设备初始化之nouveau_drm_load (1)

2015-09-01 16:38 501 查看


Nouveau源码分析(四)

probe函数成功返回之后,DRM模块就会调用struct drm_driver的load函数,对应nouveau的nouveau_drm_load.

这个函数虽然看起来不是特别长,但每一个调用的函数展开后就会变得非常长了!

[cpp] view
plaincopyprint?





// /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) .

[cpp] view
plaincopyprint?





// /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的函数很简单,就是初始化两个链表.

[cpp] view
plaincopyprint?





// /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函数:

[cpp] view
plaincopyprint?





// /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:

[cpp] view
plaincopyprint?





// /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 = NULL;

232 object->size = 0;

233 object->dtor = dtor;

234 object->map.ptr = NULL;

235 object->map.size = 0;

236

237 if (object->parent) {

238 if (!(ctor = kmalloc(sizeof(*ctor) + size, GFP_KERNEL))) {

239 nvif_object_fini(object);

240 return -ENOMEM;

241 }

242 object->data = ctor->new.data;

243 object->size = size;

244 memcpy(object->data, data, size);

245

246 ctor->ioctl.version = 0;

247 ctor->ioctl.type = NVIF_IOCTL_V0_NEW;

248 ctor->new.version = 0;

249 ctor->new.route = NVIF_IOCTL_V0_ROUTE_NVIF;

250 ctor->new.token = (unsigned long)(void *)object;

251 ctor->new.handle = handle;

252 ctor->new.oclass = oclass;

253

254 ret = nvif_object_ioctl(parent, ctor, sizeof(*ctor) +

255 object->size, &object->priv);

256 }

257

258 if (ret)

259 nvif_object_fini(object);

260 return ret;

261 }

首先各种填充字段:

这里的oclass是创建nouveau_object的时候用的.

这里的handle是nouveau_object和nvif_object一一对应用的.

然后dtor是析构函数指针,object是nvif_object()用的,其实就是nvif对象间的转换.

[cpp] view
plaincopyprint?





// /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:

[cpp] view
plaincopyprint?





// /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():

[cpp] view
plaincopyprint?





// /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 };

[cpp] view
plaincopyprint?





// /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:

[cpp] view
plaincopyprint?





// /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_

[cpp] view
plaincopyprint?





// /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,通过看最开始的结构体,应该也能想象出是个什么东西:

[cpp] view
plaincopyprint?





// /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对象 .

[cpp] view
plaincopyprint?





// /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.

[cpp] view
plaincopyprint?





// /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链表相连.

[cpp] view
plaincopyprint?





// /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:

[cpp] view
plaincopyprint?





// /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 }

[cpp] view
plaincopyprint?





// /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 }

[cpp] view
plaincopyprint?





// /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是什么的可以去百度谷歌一下.

[cpp] view
plaincopyprint?





// /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函数.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: