• 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,比这次这个函数不知道长多少倍.....

  • 相关阅读:
    WebService通过SOAP头进行身份验证
    【笔记】NIOS II spi详解
    【笔记】关于NIOS II PIO的几个问题
    【笔记】NIOS II Interval Timer Core详解
    【原创】基于NIOS II SPI的ads8321驱动
    【笔记】MATLAB一些常用函数
    【原创】解决on_chip_memory过小的问题,解决Unable to reach errno ...的问题
    【原创】基于FPGA的等精度测频方法(学习实验)
    【原创】等效采样状态机控制工程(测试通过,待完善说明书)
    【原创】基于NIOS II的ADS1256 SPI驱动
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6789744.html
Copyright © 2020-2023  润新知