zoukankan      html  css  js  c++  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.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
    ");
     52                                 return -ENODEV;
     53                         }
     54 
     55                         nv_warn(engine, "ignoring hw/fw engine disable
    ");
     56                 }
     57 
     58                 if (!nouveau_boolopt(device->cfgopt, iname, enable)) {
     59                         if (!enable)
     60                                 nv_warn(engine, "disabled, %s=1 to enable
    ", 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,比这次这个函数不知道长多少倍.....

  • 相关阅读:
    Ext JS学习第三天 我们所熟悉的javascript(二)
    Ext JS学习第二天 我们所熟悉的javascript(一)
    Ext JS学习第十七天 事件机制event(二)
    Ext JS学习第十六天 事件机制event(一)
    Ext JS学习第十五天 Ext基础之 Ext.DomQuery
    Ext JS学习第十四天 Ext基础之 Ext.DomHelper
    Ext JS学习第十三天 Ext基础之 Ext.Element
    Ext JS学习第十天 Ext基础之 扩展原生的javascript对象(二)
    针对错误 “服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF” 的原因分析
    C# 使用HttpWebRequest通过PHP接口 上传文件
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6789744.html
Copyright © 2011-2022 走看看