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

来源:互联网 发布:打口碟 淘宝 编辑:程序博客网 时间:2024/05/21 19:06

Nouveau源码分析(六)

上一篇中我们暂时忽略了两个函数,第一个是用于创建nvif_device对应的nouveau_object的ctor函数:
[cpp] view plaincopyprint?
  1. // /drivers/gpu/drm/nouveau/core/engine/device/base.c  
  2. 488 static struct nouveau_ofuncs  
  3. 489 nouveau_devobj_ofuncs = {  
  4. 490         .ctor = nouveau_devobj_ctor,  
  5. 491         .dtor = nouveau_devobj_dtor,  
  6. 492         .init = _nouveau_parent_init,  
  7. 493         .fini = _nouveau_parent_fini,  
  8. 494         .mthd = nouveau_devobj_mthd,  
  9. 495 };  
也就是对应的nouveau_devobj_ctor,这个函数的主要功能就是识别NVIDIA设备类型,然后初始化每一个subdev.
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/engine/device/base.c  
  2. 274 static int  
  3. 275 nouveau_devobj_ctor(struct nouveau_object *parent,  
  4. 276                     struct nouveau_object *engine,  
  5. 277                     struct nouveau_oclass *oclass, void *data, u32 size,  
  6. 278                     struct nouveau_object **pobject)  
  7. 279 {  
  8. 280         union {  
  9. 281                 struct nv_device_v0 v0;  
  10. 282         } *args = data;  
  11. 283         struct nouveau_client *client = nv_client(parent);  
  12. 284         struct nouveau_device *device;  
  13. 285         struct nouveau_devobj *devobj;  
  14. 286         u32 boot0, strap;  
  15. 287         u64 disable, mmio_base, mmio_size;  
  16. 288         void __iomem *map;  
  17. 289         int ret, i, c;  
  18. 290   
  19. 291         nv_ioctl(parent, "create device size %d\n", size);  
  20. 292         if (nvif_unpack(args->v0, 0, 0, false)) {  
  21. 293                 nv_ioctl(parent, "create device v%d device %016llx "  
  22. 294                                  "disable %016llx debug0 %016llx\n",  
  23. 295                          args->v0.version, args->v0.device,  
  24. 296                          args->v0.disable, args->v0.debug0);  
  25. 297         } else  
  26. 298                 return ret;  
  27. 299   
  28. 300         /* give priviledged clients register access */  
  29. 301         if (client->super)  
  30. 302                 oclass = &nouveau_devobj_oclass_super;  
  31. 303   
  32. 304         /* find the device subdev that matches what the client requested */  
  33. 305         device = nv_device(client->device);  
  34. 306         if (args->v0.device != ~0) {  
  35. 307                 device = nouveau_device_find(args->v0.device);  
  36. 308                 if (!device)  
  37. 309                         return -ENODEV;  
  38. 310         }  
  39. 311   
  40. 312         ret = nouveau_parent_create(parent, nv_object(device), oclass, 0,  
  41. 313                                     nouveau_control_oclass,  
  42. 314                                     (1ULL << NVDEV_ENGINE_DMAOBJ) |  
  43. 315                                     (1ULL << NVDEV_ENGINE_FIFO) |  
  44. 316                                     (1ULL << NVDEV_ENGINE_DISP) |  
  45. 317                                     (1ULL << NVDEV_ENGINE_PERFMON), &devobj);  
  46. 318         *pobject = nv_object(devobj);  
  47. 319         if (ret)  
  48. 320                 return ret;  
  49. 321   
  50. 322         mmio_base = nv_device_resource_start(device, 0);  
  51. 323         mmio_size = nv_device_resource_len(device, 0);  
  52. 324   
  53. 325         /* translate api disable mask into internal mapping */  
  54. 326         disable = args->v0.debug0;  
  55. 327         for (i = 0; i < NVDEV_SUBDEV_NR; i++) {  
  56. 328                 if (args->v0.disable & disable_map[i])  
  57. 329                         disable |= (1ULL << i);  
  58. 330         }  
  59. 331   
  60. 332         /* identify the chipset, and determine classes of subdev/engines */  
  61. 333         if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_IDENTIFY) &&  
  62. 334             !device->card_type) {  
  63. 335                 map = ioremap(mmio_base, 0x102000);  
  64. 336                 if (map == NULL)  
  65. 337                         return -ENOMEM;  
  66. 338   
  67. 339                 /* switch mmio to cpu's native endianness */  
  68. 340 #ifndef __BIG_ENDIAN  
  69. 341                 if (ioread32_native(map + 0x000004) != 0x00000000)  
  70. 342 #else  
  71. 343                 if (ioread32_native(map + 0x000004) == 0x00000000)  
  72. 344 #endif  
  73. 345                         iowrite32_native(0x01000001, map + 0x000004);  
  74. 346   
  75. 347                 /* read boot0 and strapping information */  
  76. 348                 boot0 = ioread32_native(map + 0x000000);  
  77. 349                 strap = ioread32_native(map + 0x101000);  
  78. 350                 iounmap(map);  
  79. 351   
  80. 352                 /* determine chipset and derive architecture from it */  
  81. 353                 if ((boot0 & 0x1f000000) > 0) {  
  82. 354                         device->chipset = (boot0 & 0x1ff00000) >> 20;  
  83. 355                         switch (device->chipset & 0x1f0) {  
  84. 356                         case 0x010: {  
  85. 357                                 if (0x461 & (1 << (device->chipset & 0xf)))  
  86. 358                                         device->card_type = NV_10;  
  87. 359                                 else  
  88. 360                                         device->card_type = NV_11;  
  89. 361                                 break;  
  90. 362                         }  
  91. 363                         case 0x020: device->card_type = NV_20; break;  
  92. 364                         case 0x030: device->card_type = NV_30; break;  
  93. 365                         case 0x040:  
  94. 366                         case 0x060: device->card_type = NV_40; break;  
  95. 367                         case 0x050:  
  96. 368                         case 0x080:  
  97. 369                         case 0x090:  
  98. 370                         case 0x0a0: device->card_type = NV_50; break;  
  99. 371                         case 0x0c0:  
  100. 372                         case 0x0d0: device->card_type = NV_C0; break;  
  101. 373                         case 0x0e0:  
  102. 374                         case 0x0f0:  
  103. 375                         case 0x100: device->card_type = NV_E0; break;  
  104. 376                         case 0x110: device->card_type = GM100; break;  
  105. 377                         default:  
  106. 378                                 break;  
  107. 379                         }  
  108. 380                 } else  
  109. 381                 if ((boot0 & 0xff00fff0) == 0x20004000) {  
  110. 382                         if (boot0 & 0x00f00000)  
  111. 383                                 device->chipset = 0x05;  
  112. 384                         else  
  113. 385                                 device->chipset = 0x04;  
  114. 386                         device->card_type = NV_04;  
  115. 387                 }  
  116. 388   
  117. 389                 switch (device->card_type) {  
  118. 390                 case NV_04: ret = nv04_identify(device); break;  
  119. 391                 case NV_10:  
  120. 392                 case NV_11: ret = nv10_identify(device); break;  
  121. 393                 case NV_20: ret = nv20_identify(device); break;  
  122. 394                 case NV_30: ret = nv30_identify(device); break;  
  123. 395                 case NV_40: ret = nv40_identify(device); break;  
  124. 396                 case NV_50: ret = nv50_identify(device); break;  
  125. 397                 case NV_C0: ret = nvc0_identify(device); break;  
  126. 398                 case NV_E0: ret = nve0_identify(device); break;  
  127. 399                 case GM100: ret = gm100_identify(device); break;  
  128. 400                 default:  
  129. 401                         ret = -EINVAL;  
  130. 402                         break;  
  131. 403                 }  
  132. 404   
  133. 405                 if (ret) {  
  134. 406                         nv_error(device, "unknown chipset, 0x%08x\n", boot0);  
  135. 407                         return ret;  
  136. 408                 }  
  137. 409   
  138. 410                 nv_info(device, "BOOT0  : 0x%08x\n", boot0);  
  139. 411                 nv_info(device, "Chipset: %s (NV%02X)\n",  
  140. 412                         device->cname, device->chipset);  
  141. 413                 nv_info(device, "Family : NV%02X\n", device->card_type);  
  142. 414   
  143. 415                 /* determine frequency of timing crystal */  
  144. 416                 if ( device->card_type <= NV_10 || device->chipset < 0x17 ||  
  145. 417                     (device->chipset >= 0x20 && device->chipset < 0x25))  
  146. 418                         strap &= 0x00000040;  
  147. 419                 else  
  148. 420                         strap &= 0x00400040;  
  149. 421   
  150. 422                 switch (strap) {  
  151. 423                 case 0x00000000: device->crystal = 13500; break;  
  152. 424                 case 0x00000040: device->crystal = 14318; break;  
  153. 425                 case 0x00400000: device->crystal = 27000; break;  
  154. 426                 case 0x00400040: device->crystal = 25000; break;  
  155. 427                 }  
  156. 428   
  157. 429                 nv_debug(device, "crystal freq: %dKHz\n", device->crystal);  
  158. 430         }  
  159. 431   
  160. 432         if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_MMIO) &&  
  161. 433             !nv_subdev(device)->mmio) {  
  162. 434                 nv_subdev(device)->mmio  = ioremap(mmio_base, mmio_size);  
  163. 435                 if (!nv_subdev(device)->mmio) {  
  164. 436                         nv_error(device, "unable to map device registers\n");  
  165. 437                         return -ENOMEM;  
  166. 438                 }  
  167. 439         }  
  168. 440   
  169. 441         /* ensure requested subsystems are available for use */  
  170. 442         for (i = 1, c = 1; i < NVDEV_SUBDEV_NR; i++) {  
  171. 443                 if (!(oclass = device->oclass[i]) || (disable & (1ULL << i)))  
  172. 444                         continue;  
  173. 445   
  174. 446                 if (device->subdev[i]) {  
  175. 447                         nouveau_object_ref(device->subdev[i],  
  176. 448                                           &devobj->subdev[i]);  
  177. 449                         continue;  
  178. 450                 }  
  179. 451   
  180. 452                 ret = nouveau_object_ctor(nv_object(device), NULL,  
  181. 453                                           oclass, NULL, i,  
  182. 454                                           &devobj->subdev[i]);  
  183. 455                 if (ret == -ENODEV)  
  184. 456                         continue;  
  185. 457                 if (ret)  
  186. 458                         return ret;  
  187. 459   
  188. 460                 device->subdev[i] = devobj->subdev[i];  
  189. 461   
  190. 462                 /* note: can't init *any* subdevs until devinit has been run 
  191. 463                  * due to not knowing exactly what the vbios init tables will 
  192. 464                  * mess with.  devinit also can't be run until all of its 
  193. 465                  * dependencies have been created. 
  194. 466                  * 
  195. 467                  * this code delays init of any subdev until all of devinit's 
  196. 468                  * dependencies have been created, and then initialises each 
  197. 469                  * subdev in turn as they're created. 
  198. 470                  */  
  199. 471                 while (i >= NVDEV_SUBDEV_DEVINIT_LAST && c <= i) {  
  200. 472                         struct nouveau_object *subdev = devobj->subdev[c++];  
  201. 473                         if (subdev && !nv_iclass(subdev, NV_ENGINE_CLASS)) {  
  202. 474                                 ret = nouveau_object_inc(subdev);  
  203. 475                                 if (ret)  
  204. 476                                         return ret;  
  205. 477                                 atomic_dec(&nv_object(device)->usecount);  
  206. 478                         } else  
  207. 479                         if (subdev) {  
  208. 480                                 nouveau_subdev_reset(subdev);  
  209. 481                         }  
  210. 482                 }  
  211. 483         }  
  212. 484   
  213. 485         return 0;  
  214. 486 }  
第292行,首先检查参数的正确性,输出一些调试信息.
第302行,给有特权的client使用nouveau_devobj_oclass_super,这个与默认的nouveau_devobj_oclass的不同就是可以直接进行寄存器读写.
第305行,获取对应的device,如果特别指定了就从链表中查找指定的device.
第312行,创建这个devobj,其中几个参数简单说一下:
nouveau_control_oclass: 这个是作为sclass进去的,和sysfs有关. Nouveau初始化sysfs时候会创建一个nvif_object,这个时候会用到这个sclass.
(1ULL << NVDEV_ENGINE_DMAOBJ) | (1ULL << NVDEV_ENGINE_FIFO) | (1ULL << NVDEV_ENGINE_DISP) | (1ULL << NVDEV_ENGINE_PERFMON):
这个是作为engcls传送进去的,表示可以用来完成u32 oclass转换的subdev.
第223行,获取NVIDIA的MMIO地址和长度.
第327行,获取禁用的subdev.
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/engine/device/base.c  
  2. 206 static const u64 disable_map[] = {  
  3. 207         [NVDEV_SUBDEV_VBIOS]    = NV_DEVICE_V0_DISABLE_VBIOS,  
  4. 208         [NVDEV_SUBDEV_DEVINIT]  = NV_DEVICE_V0_DISABLE_CORE,  
  5. 209         [NVDEV_SUBDEV_GPIO]     = NV_DEVICE_V0_DISABLE_CORE,  
  6. 210         [NVDEV_SUBDEV_I2C]      = NV_DEVICE_V0_DISABLE_CORE,  
  7. 211         [NVDEV_SUBDEV_CLOCK]    = NV_DEVICE_V0_DISABLE_CORE,  
  8. 212         [NVDEV_SUBDEV_MXM]      = NV_DEVICE_V0_DISABLE_CORE,  
  9. 213         [NVDEV_SUBDEV_MC]       = NV_DEVICE_V0_DISABLE_CORE,  
  10. 214         [NVDEV_SUBDEV_BUS]      = NV_DEVICE_V0_DISABLE_CORE,  
  11. 215         [NVDEV_SUBDEV_TIMER]    = NV_DEVICE_V0_DISABLE_CORE,  
  12. 216         [NVDEV_SUBDEV_FB]       = NV_DEVICE_V0_DISABLE_CORE,  
  13. 217         [NVDEV_SUBDEV_LTC]      = NV_DEVICE_V0_DISABLE_CORE,  
  14. 218         [NVDEV_SUBDEV_IBUS]     = NV_DEVICE_V0_DISABLE_CORE,  
  15. 219         [NVDEV_SUBDEV_INSTMEM]  = NV_DEVICE_V0_DISABLE_CORE,  
  16. 220         [NVDEV_SUBDEV_VM]       = NV_DEVICE_V0_DISABLE_CORE,  
  17. 221         [NVDEV_SUBDEV_BAR]      = NV_DEVICE_V0_DISABLE_CORE,  
  18. 222         [NVDEV_SUBDEV_VOLT]     = NV_DEVICE_V0_DISABLE_CORE,  
  19. 223         [NVDEV_SUBDEV_THERM]    = NV_DEVICE_V0_DISABLE_CORE,  
  20. 224         [NVDEV_SUBDEV_PWR]      = NV_DEVICE_V0_DISABLE_CORE,  
  21. 225         [NVDEV_ENGINE_DMAOBJ]   = NV_DEVICE_V0_DISABLE_CORE,  
  22. 226         [NVDEV_ENGINE_PERFMON]  = NV_DEVICE_V0_DISABLE_CORE,  
  23. 227         [NVDEV_ENGINE_FIFO]     = NV_DEVICE_V0_DISABLE_FIFO,  
  24. 228         [NVDEV_ENGINE_SW]       = NV_DEVICE_V0_DISABLE_FIFO,  
  25. 229         [NVDEV_ENGINE_GR]       = NV_DEVICE_V0_DISABLE_GRAPH,  
  26. 230         [NVDEV_ENGINE_MPEG]     = NV_DEVICE_V0_DISABLE_MPEG,  
  27. 231         [NVDEV_ENGINE_ME]       = NV_DEVICE_V0_DISABLE_ME,  
  28. 232         [NVDEV_ENGINE_VP]       = NV_DEVICE_V0_DISABLE_VP,  
  29. 233         [NVDEV_ENGINE_CRYPT]    = NV_DEVICE_V0_DISABLE_CRYPT,  
  30. 234         [NVDEV_ENGINE_BSP]      = NV_DEVICE_V0_DISABLE_BSP,  
  31. 235         [NVDEV_ENGINE_PPP]      = NV_DEVICE_V0_DISABLE_PPP,  
  32. 236         [NVDEV_ENGINE_COPY0]    = NV_DEVICE_V0_DISABLE_COPY0,  
  33. 237         [NVDEV_ENGINE_COPY1]    = NV_DEVICE_V0_DISABLE_COPY1,  
  34. 238         [NVDEV_ENGINE_VIC]      = NV_DEVICE_V0_DISABLE_VIC,  
  35. 239         [NVDEV_ENGINE_VENC]     = NV_DEVICE_V0_DISABLE_VENC,  
  36. 240         [NVDEV_ENGINE_DISP]     = NV_DEVICE_V0_DISABLE_DISP,  
  37. 241         [NVDEV_SUBDEV_NR]       = 0,  
  38. 242 };  
也就是说把传进来的禁用列表转换成要禁用的具体subdev列表.
第333行,如果没有禁用识别NVIDIA设备类型,而且这个设备还没有被识别过设备类型,那么此处就要进行识别.
第335行,首先映射一下MMIO空间.
第345行,这里可以看注释. 切换MMIO空间的端模式到CPU的端模式. [大端/小端]
第348行和第349行,读取NVIDIA的寄存器. 要是NVIDIA能开放一点的话,或许我们现在可以把spec拿出来看看.
但我们通过读代码还是能知道这两个寄存器的格式以及储存的信息. boot0储存显卡的种类,strap储存晶振频率. [石英晶体谐振器]
第350行,把MMIO空间取消映射.
第353到387行,从boot0中获取显卡的Chipset和Family,没什么好说的. 
接下来,第389行到403行,调用对应显卡Family的identity函数,因为我的显卡是NVD9,对应的family是NVC0,所以以后就拿这个来分析具体与设备相关的东西了.
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/engine/device/nvc0.c  
  2.  56 int  
  3.  57 nvc0_identify(struct nouveau_device *device)  
  4.  58 {  
  5.  59         switch (device->chipset) {  
  6.         //此处省略一万字.......  
  7. 282         case 0xd9:  
  8. 283                 device->cname = "GF119";  
  9. 284                 device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;  
  10. 285                 device->oclass[NVDEV_SUBDEV_GPIO   ] =  nvd0_gpio_oclass;  
  11. 286                 device->oclass[NVDEV_SUBDEV_I2C    ] =  nvd0_i2c_oclass;  
  12. 287                 device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;  
  13. 288                 device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;  
  14. 289                 device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;  
  15. 290                 device->oclass[NVDEV_SUBDEV_DEVINIT] =  nvc0_devinit_oclass;  
  16. 291                 device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;  
  17. 292                 device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;  
  18. 293                 device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;  
  19. 294                 device->oclass[NVDEV_SUBDEV_FB     ] =  nvc0_fb_oclass;  
  20. 295                 device->oclass[NVDEV_SUBDEV_LTC    ] =  gf100_ltc_oclass;  
  21. 296                 device->oclass[NVDEV_SUBDEV_IBUS   ] = &nvc0_ibus_oclass;  
  22. 297                 device->oclass[NVDEV_SUBDEV_INSTMEM] =  nv50_instmem_oclass;  
  23. 298                 device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;  
  24. 299                 device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;  
  25. 300                 device->oclass[NVDEV_SUBDEV_PWR    ] =  nvd0_pwr_oclass;  
  26. 301                 device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;  
  27. 302                 device->oclass[NVDEV_ENGINE_DMAOBJ ] =  nvd0_dmaeng_oclass;  
  28. 303                 device->oclass[NVDEV_ENGINE_FIFO   ] =  nvc0_fifo_oclass;  
  29. 304                 device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;  
  30. 305                 device->oclass[NVDEV_ENGINE_GR     ] =  nvd9_graph_oclass;  
  31. 306                 device->oclass[NVDEV_ENGINE_VP     ] = &nvc0_vp_oclass;  
  32. 307                 device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;  
  33. 308                 device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;  
  34. 309                 device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;  
  35. 310                 device->oclass[NVDEV_ENGINE_DISP   ] =  nvd0_disp_oclass;  
  36. 311                 device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;  
  37. 312                 break;  
  38.         //此处省略一万字.......  
  39. 342         default:  
  40. 343                 nv_fatal(device, "unknown Fermi chipset\n");  
  41. 344                 return -EINVAL;  
  42. 345         }  
  43. 346   
  44. 347         return 0;  
  45. 348         }  
  46. 349   
对于每一个chipset都有这么一堆赋值函数,所以不再都列出来,只关心NVD9这个例子.
很明显这个函数的作用是初始化device->oclass,等会就会对每一个oclass使用nouveau_object_ctor创建一个object,并储存到devobj->subdev和device->subdev里.

回到nouveau_devobj_ctor,下面先检查ret,然后打印调试信息.
第416行,根据设备类型适当mask刚刚读取的strap.
第422行,根据strap的值,确定石英晶体谐振器的频率,并储存到device的一个字段里.
紧接着退出识别设备类型的if语句,下面第434行,初始化device的mmio空间.
然后第442行,初始化每一个subdev. 这个for循环本身并不大,但它会执行刚刚在identity函数里初始化的所有oclass的ctor和init函数,所以.......

第443行,首先检查这个subdev是否已经被禁用了,或者根本没有对应的oclass,如果这样的话直接进行下一次循环.
第446行,检查这个subdev是否已经被创建过,如果这样的话直接把它ref一遍就行了.
第452行,使用nouveau_object_ctor创建这个subdev,也就是调用这个oclass的ctor函数指针.
第460行,本来这个地方是要使用nouveau_object_inc来调用oclass里的init函数指针的,但是为什么要这样写呢?
首先,这里有一个名叫DEVINIT的subdev,这个subdev的init函数将会使用两个[在NVDEV_SUBDEV_DEVINIT和NVDEV_SUBDEV_DEVINIT_LAST之间的]必须被创建,但又不能被提前init的subdev.
如果此处直接执行这个subdev的init函数,那么就不可能出现一个已经被创建但没有被init的subdev了.
所以这个while循环,保证先创建但不init所有NVDEV_SUBDEV_DEVINIT_LAST之前的subdev,等到已经过了这个NVDEV_SUBDEV_DEVINIT_LAST后,再init所有之前创建过的subdev. 当然对于之后的subdev,则都是创建了立即init的.
[建议参考此处的注释,/drivers/gpu/drm/nouveau/core/include/core/device.h里的注释,以及具体代码来理解.]

由于这些ctor/init函数加起来实在太长,可能要连续说几篇才能说完,所以暂且先来看上一篇中我们忽略的另一个函数吧. 调用的位置:
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/nvif/device.c  
  2.  33 int  
  3.  34 nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *),  
  4.  35                  u32 handle, u32 oclass, void *data, u32 size,  
  5.  36                  struct nvif_device *device)  
  6.  37 {  
  7.  38         int ret = nvif_object_init(parent, (void *)dtor, handle, oclass,  
  8.  39                                    data, size, &device->base);  
  9.  40         if (ret == 0) {  
  10.  41                 device->object = &device->base;  
  11.  42                 device->info.version = 0;  
  12.  43                 ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO,  
  13.  44                                        &device->info, sizeof(device->info));  
  14.  45         }  
  15.  46         return ret;  
  16.  47 }  
第43行,使用mthd获取设备的基本信息,并储存到device->info里.
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/nvif/object.c  
  2. 115 int  
  3. 116 nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)  
  4. 117 {  
  5. 118         struct {  
  6. 119                 struct nvif_ioctl_v0 ioctl;  
  7. 120                 struct nvif_ioctl_mthd_v0 mthd;  
  8. 121         } *args;  
  9. 122         u8 stack[128];  
  10. 123         int ret;  
  11. 124   
  12. 125         if (sizeof(*args) + size > sizeof(stack)) {  
  13. 126                 if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))  
  14. 127                         return -ENOMEM;  
  15. 128         } else {  
  16. 129                 args = (void *)stack;  
  17. 130         }  
  18. 131         args->ioctl.version = 0;  
  19. 132         args->ioctl.type = NVIF_IOCTL_V0_MTHD;  
  20. 133         args->mthd.version = 0;  
  21. 134         args->mthd.method = mthd;  
  22. 135   
  23. 136         memcpy(args->mthd.data, data, size);  
  24. 137         ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);  
  25. 138         memcpy(data, args->mthd.data, size);  
  26. 139         if (args != (void *)stack)  
  27. 140                 kfree(args);  
  28. 141         return ret;  
  29. 142 }  
首先检查需要的空间,如果在一定范围之内就使用栈里的,不然kmalloc分配内存.
第131行到第134行,初始化几个字段,主要描述了请求的版本信息和请求的类型种类.
第136行和第138行是对称的,前者把data复制进args->mthd.data,后者则把args->mthd.data复制到data里去.
中间夹着的137行就是核心代码了,这个nvif_object_ioctl我们以前分析过,这里直接看:
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/core/ioctl.c  
  2. 439 static struct {  
  3. 440         int version;  
  4. 441         int (*func)(struct nouveau_handle *, void *, u32);  
  5. 442 }  
  6. 443 nvkm_ioctl_v0[] = {  
  7. 444         { 0x00, nvkm_ioctl_nop },  
  8. 445         { 0x00, nvkm_ioctl_sclass },  
  9. 446         { 0x00, nvkm_ioctl_new },  
  10. 447         { 0x00, nvkm_ioctl_del },  
  11. 448         { 0x00, nvkm_ioctl_mthd },  
  12. 449         { 0x00, nvkm_ioctl_rd },  
  13. 450         { 0x00, nvkm_ioctl_wr },  
  14. 451         { 0x00, nvkm_ioctl_map },  
  15. 452         { 0x00, nvkm_ioctl_unmap },  
  16. 453         { 0x00, nvkm_ioctl_ntfy_new },  
  17. 454         { 0x00, nvkm_ioctl_ntfy_del },  
  18. 455         { 0x00, nvkm_ioctl_ntfy_get },  
  19. 456         { 0x00, nvkm_ioctl_ntfy_put },  
  20. 457 };  
只看函数名就能得出结论,我们最终会调用nvkm_ioctl_mthd:
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/core/ioctl.c  
  2. 203 static int  
  3. 204 nvkm_ioctl_mthd(struct nouveau_handle *handle, void *data, u32 size)  
  4. 205 {  
  5. 206         struct nouveau_object *object = handle->object;  
  6. 207         struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;  
  7. 208         union {  
  8. 209                 struct nvif_ioctl_mthd_v0 v0;  
  9. 210         } *args = data;  
  10. 211         int ret;  
  11. 212   
  12. 213         nv_ioctl(object, "mthd size %d\n", size);  
  13. 214         if (nvif_unpack(args->v0, 0, 0, true)) {  
  14. 215                 nv_ioctl(object, "mthd vers %d mthd %02x\n",  
  15. 216                          args->v0.version, args->v0.method);  
  16. 217                 if (ret = -ENODEV, ofuncs->mthd)  
  17. 218                         ret = ofuncs->mthd(object, args->v0.method, data, size);  
  18. 219         }  
  19. 220   
  20. 221         return ret;  
  21. 222 }  
第213行是打印调试信息的,无视之.
第214行,检查参数的正确性.
第218行,调用nvif_object对应的nouveau_object的ofuncs里的mthd.
这里毫无疑问,nvif_device对应的就是刚才我们看的devobj,直接去看它的mthd函数:
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/engine/device/base.c  
  2. 148 static int  
  3. 149 nouveau_devobj_mthd(struct nouveau_object *object, u32 mthd,  
  4. 150                     void *data, u32 size)  
  5. 151 {  
  6. 152         switch (mthd) {  
  7. 153         case NV_DEVICE_V0_INFO:  
  8. 154                 return nouveau_devobj_info(object, data, size);  
  9. 155         default:  
  10. 156                 break;  
  11. 157         }  
  12. 158         return -EINVAL;  
  13. 159 }  
一个switch语句判断类型,任务交给nouveau_devobj_info来完成.
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // /drivers/gpu/drm/nouveau/core/engine/device/base.c  
  2.  79 static int  
  3.  80 nouveau_devobj_info(struct nouveau_object *object, void *data, u32 size)  
  4.  81 {  
  5.  82         struct nouveau_device *device = nv_device(object);  
  6.  83         struct nouveau_fb *pfb = nouveau_fb(device);  
  7.  84         struct nouveau_instmem *imem = nouveau_instmem(device);  
  8.  85         union {  
  9.  86                 struct nv_device_info_v0 v0;  
  10.  87         } *args = data;  
  11.  88         int ret;  
  12.  89   
  13.  90         nv_ioctl(object, "device info size %d\n", size);  
  14.  91         if (nvif_unpack(args->v0, 0, 0, false)) {  
  15.  92                 nv_ioctl(object, "device info vers %d\n", args->v0.version);  
  16.  93         } else  
  17.  94                 return ret;  
  18.  95   
  19.  96         switch (device->chipset) {  
  20.  97         case 0x01a:  
  21.  98         case 0x01f:  
  22.  99         case 0x04c:  
  23. 100         case 0x04e:  
  24. 101         case 0x063:  
  25. 102         case 0x067:  
  26. 103         case 0x068:  
  27. 104         case 0x0aa:  
  28. 105         case 0x0ac:  
  29. 106         case 0x0af:  
  30. 107                 args->v0.platform = NV_DEVICE_INFO_V0_IGP;  
  31. 108                 break;  
  32. 109         default:  
  33. 110                 if (device->pdev) {  
  34. 111                         if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP))  
  35. 112                                 args->v0.platform = NV_DEVICE_INFO_V0_AGP;  
  36. 113                         else  
  37. 114                         if (pci_is_pcie(device->pdev))  
  38. 115                                 args->v0.platform = NV_DEVICE_INFO_V0_PCIE;  
  39. 116                         else  
  40. 117                                 args->v0.platform = NV_DEVICE_INFO_V0_PCI;  
  41. 118                 } else {  
  42. 119                         args->v0.platform = NV_DEVICE_INFO_V0_SOC;  
  43. 120                 }  
  44. 121                 break;  
  45. 122         }  
  46. 123   
  47. 124         switch (device->card_type) {  
  48. 125         case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break;  
  49. 126         case NV_10:  
  50. 127         case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break;  
  51. 128         case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break;  
  52. 129         case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break;  
  53. 130         case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break;  
  54. 131         case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break;  
  55. 132         case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break;  
  56. 133         case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break;  
  57. 134         case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break;  
  58. 135         default:  
  59. 136                 args->v0.family = 0;  
  60. 137                 break;  
  61. 138         }  
  62. 139   
  63. 140         args->v0.chipset  = device->chipset;  
  64. 141         args->v0.revision = device->chipset >= 0x10 ? nv_rd32(device, 0) : 0x00;  
  65. 142         if (pfb)  args->v0.ram_size = args->v0.ram_user = pfb->ram->size;  
  66. 143         else      args->v0.ram_size = args->v0.ram_user = 0;  
  67. 144         if (imem) args->v0.ram_user = args->v0.ram_user - imem->reserved;  
  68. 145         return 0;  
  69. 146 }  
第91行,照例校验参数,打印调试信息.
第96行的一个大switch语句,其实就是获得设备的platform.
第124行,把设备的family转化为更加文字化的构架名.
第140行,貌似是顺便把设备的chipset和boot0寄存器的值放到info里?
之后的那三行是获取显卡内存大小的,因为那些subdev/engine的初始化还没展开,所以可能不理解. 以后会说到的.

下面先来简单介绍一下各个subdev/engine的作用功能把[按初始化的顺序来]:
NV_ENGINE_DEVICE: 这个早就创建和初始化了....

NV_SUBDEV_VBIOS: 识别并分析NVIDIA的BIOS,这里面储存这很多东西,比如NVIDA设备初始化表格,显示器的链接状态,HDMI信息等.
NV_SUBDEV_DEVINIT: 执行BIOS中的NVIDIA设备初始化表格,当然有时候BIOS已经执行好了,那么啥事没干.
NV_SUBDEV_GPIO: 一种总线,比较底层的一个SUBDEV,其他SUBDEV/ENGINE依赖.
NV_SUBDEV_I2C: 另一种总线,作用同上.
NV_SUBDEV_MXM: 根据MXMS信息来修正VBIOS中的数据,MXMS信息是从I2C中得到的.
NV_SUBDEV_MC: 管理NVIDIA设备中断分发给各个SUBDEV.
NV_SUBDEV_BUS: 接受处理BUS的中断,还包括hwsq接口,其他SUBDEV/ENGINE会用到.
NV_SUBDEV_TIMER: 定时器,到时间会发出中断.
NV_SUBDEV_FB: 这可不是FrameBuffer的缩写,只是一个得到显卡内存信息、管理显卡内存的东西.
NV_SUBDEV_LTC: 有一个zbc color的功能,还没仔细看,意义不明;另一个是压缩显卡内存的功能. [不是增大容量,是节省带宽.]
NV_SUBDEV_IBUS: 只有一个中断处理函数.
NV_SUBDEV_INSTMEN: 也是有关显卡物理内存管理的东西. 比NV_SUBDEV_PFB高一层,有了访问的接口.
NV_SUBDEV_VM: 显卡虚拟内存管理. 虚拟内存有三块,两块是PCI里的两个BAR,还有一块是nouveau_channel,但最终都是在这里管理的.
NV_SUBDEV_BAR: 如上所述,PCI里的两个BAR的虚拟内存映射.
NV_SUBDEV_PWR: 应该是一个处理器.
NV_SUBDEV_VOLT: 电压读取和设置.
NV_SUBDEV_THERM: 读取显卡温度,读取和设置显卡风扇转速.
NV_SUBDEV_CLOCK: 显卡时钟频率的读取、设置.

NV_ENGINE_DMAOBJ:显然是和DMA有关的,比如系统内存中的数据交给显卡处理.
NV_ENGINE_IFB: 没有具体的oclass,只是在中断中充当了一个case语句.
NV_ENGINE_FIFO: 这个是nouveau_channl用的.
NV_ENGINE_SW: 其中一个功能是当更换framebuffer完成时,硬件通知软件.
NV_ENGINE_GR: 图形处理相关.
NV_ENGINE_MPEG: NV50之前有效,不分析.
NV_ENGINE_ME: 意义不明,貌似和NV_ENGINE_IFB差不多.
NV_ENGINE_VP: 应该也是一个处理器.
NV_ENGINE_CRYPT: 只在NV50有效.
NV_ENGINE_BSP: 貌似还是一个处理器.
NV_ENGINE_PPP: video post-processing engine,意义不明.
NV_ENGINE_COPY0/COPY1/COPY2: 大概还是处理器把.
NV_ENGINE_VIC: 应该和NV_ENGINE_IFB差不多,意义不明.
NV_ENGINE_VENC: 同上.
NV_ENGINE_DISP: 有关显示模式设定,以及切换framebuffer,vblank之类的东西.
NV_ENGINE_PERFMON: 对于NVC0,是一个目前还没有做好的东西. 里面几个空数组.

等具体说的时候争取尽量附上envytools的链接,里面有一些对ENGINE/SUBDEV简介很不错.
这么多东西又不知道得写到猴年马月了...... 
0 0
原创粉丝点击