驱动开辟系列54 - Linux Graphics QXL显卡驱动代码分析(一)设备初始化 ...

打印 上一主题 下一主题

主题 1617|帖子 1617|积分 4851

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
一:概述

        QXL 是QEMU支持的一种假造显卡,用于假造化环境中的图形加速,旨在提高假造机的图形显示和长途桌面的用户体验;QEMU 也称 Quick Emulator,快速仿真器,是一个开源通用的仿真和假造化工具,可以模拟不同架构的盘算机以及运行假造机。本文介绍下QXL 假造显卡驱动,即Linux 内核代码中QXL显卡驱动部门。看看它是如何支持QEMU假造显卡的。 QXL代码分析基于v6.15-rc2内核版本。
二:模块初始化

        QXL 假造显卡是一个PCI设备,它必要QXL显卡驱动支持显示模式 (modeset) 设置能力,即支持在内核设置输出参数的能力(分辨率,帧率)。好比将显示器设置成1920x1080分辨率。同时QXL显卡驱动在Linux 内核代码中,它是一个独立的内核模块,代码位置是linux/drivers/gpu/drm/qxl。所以QXL内核模块的初始化方式是:
  1. drm_module_pci_driver_if_modeset(qxl_pci_driver, qxl_modeset);
复制代码
        这行代码的意思是:QXL 模块告诉内核,我是一个 PCI 设备驱动模块(基于 DRM 框架),如果你支持 modeset,那我会自动注册 qxl_pci_driver。
三:PCI设备初始化

        QXL显卡是PCI设备,所以前面模块初始化时注册了qxl_pci_driver,  下面看看 qxl_pci_driver的定义;
  1. static struct pci_driver qxl_pci_driver = {
  2.          .name = DRIVER_NAME,
  3.          .id_table = pciidlist,
  4.          .probe = qxl_pci_probe,
  5.          .remove = qxl_pci_remove,
  6.          .shutdown = qxl_pci_shutdown,
  7.          .driver.pm = &qxl_pm_ops,
  8. };
复制代码
       这段代码的意思是:我这个 QXL 驱动支持这些 PCI ID,如果你发现这样的设备,就用qxl_pci_probe()  来初始化,拔出时用 qxl_pci_remove() 清理,关机时调用 qxl_pci_shutdown,必要挂起/恢复就用 qxl_pm_ops 处理。 
        下面这个 pciidlist 是 QXL 驱动和 PCI 总线的匹配表:
  1. static const struct pci_device_id pciidlist[] = {
  2.     { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0 },
  3.     { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_OTHER << 8, 0xffff00, 0 },
  4.     { 0, 0, 0 },
  5. };
复制代码
      这内里每个结构是一个 struct pci_device_id,用于匹配设备的 Vendor ID、Device ID、Class 等信息:
  1. {
  2.     .vendor     = 0x1b36,     // 厂商 ID(Red Hat QEMU 的 PCI 厂商 ID)
  3.     .device     = 0x0100,     // 设备 ID(QXL 虚拟显卡的 ID)
  4.     .subvendor  = PCI_ANY_ID, // 子系统厂商 ID(任意)
  5.     .subdevice  = PCI_ANY_ID, // 子系统设备 ID(任意)
  6.     .class      = PCI_CLASS_DISPLAY_VGA << 8, // 设备类(VGA 显卡类)
  7.     .class_mask = 0xffff00,   // 匹配 class 的掩码
  8.     .driver_data = 0          // 可用于传递自定义参数,这里设为 0
  9. }
复制代码
        当内核检测到有匹配的PCI设备插入时,即检测到QXL显卡设备时,就调用设备初始化函数qxl_pci_probe,下面我们看看它做了什么工作:
  1. static int
  2. qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  3. {
  4.         struct qxl_device *qdev;
  5.         int ret;
  6.         if (pdev->revision < 4) {
  7.                 DRM_ERROR("qxl too old, doesn't support client_monitors_config,"
  8.                           " use xf86-video-qxl in user mode");
  9.                 return -EINVAL; /* TODO: ENODEV ? */
  10.         }
  11.         qdev = devm_drm_dev_alloc(&pdev->dev, &qxl_driver,
  12.                                   struct qxl_device, ddev);
  13.         if (IS_ERR(qdev)) {
  14.                 pr_err("Unable to init drm dev");
  15.                 return -ENOMEM;
  16.         }
  17.         ret = pci_enable_device(pdev);
  18.         if (ret)
  19.                 return ret;
  20.         ret = aperture_remove_conflicting_pci_devices(pdev, qxl_driver.name);
  21.         if (ret)
  22.                 goto disable_pci;
  23.         if (pci_is_vga(pdev) && pdev->revision < 5) {
  24.                 ret = vga_get_interruptible(pdev, VGA_RSRC_LEGACY_IO);
  25.                 if (ret) {
  26.                         DRM_ERROR("can't get legacy vga ioports\n");
  27.                         goto disable_pci;
  28.                 }
  29.         }
  30.         ret = qxl_device_init(qdev, pdev);
  31.         if (ret)
  32.                 goto put_vga;
  33.         ret = qxl_modeset_init(qdev);
  34.         if (ret)
  35.                 goto unload;
  36.         drm_kms_helper_poll_init(&qdev->ddev);
  37.         /* Complete initialization. */
  38.         ret = drm_dev_register(&qdev->ddev, ent->driver_data);
  39.         if (ret)
  40.                 goto modeset_cleanup;
  41.         drm_client_setup(&qdev->ddev, NULL);
  42.         return 0;
  43. modeset_cleanup:
  44.         qxl_modeset_fini(qdev);
  45. unload:
  46.         qxl_device_fini(qdev);
  47. put_vga:
  48.         if (pci_is_vga(pdev) && pdev->revision < 5)
  49.                 vga_put(pdev, VGA_RSRC_LEGACY_IO);
  50. disable_pci:
  51.         pci_disable_device(pdev);
  52.         return ret;
  53. }
复制代码
        这个代码做的第一件事就是分配一个qxl_device设备;并注册了一个 qxl_driver 驱动,与qxl_device建立绑定关系;关于qxl_device, qxl_driver 背面再说;这里可以理解为他们是QXL设备,和对应的QXL设备驱动。  
        这个代码做的第二件事就是打开PCI BAR和使能PCI制止访问。也就是说PCI BAR 是PCI设备袒露的“资源入口”,通过它内核可以访问显卡的内存,I/O端口和使能制止,实现真正驱动硬件的能力;PCI设备可以产生制止,好比GPU渲染完成时通知CPU。
        这个代码做的第三件事就是初始化QXL设备的显存,寄存器等,这个背面再具体睁开说。
        这个代码做的第四件事就是配置QXL设备的显示输出(KMS),好比CRTC,Encoder,Connector,这个背面再具体睁开说。
        这个代码做的第五件事就是向内核注册QXL设备,这样用户空间才能通过 /dev/dri/cardX 访问到该设备。 
四:QXL设备初始化

        前面在 qxl_pci_probe 分配和初始化了 qxl_device,并将其和qxl_drvier建立了绑定关系。下面看看qxl_driver 和 qxl_device 的定义。
  1. static struct drm_driver qxl_driver = {
  2.         .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_CURSOR_HOTSPOT,
  3.         .dumb_create = qxl_mode_dumb_create,
  4.         .dumb_map_offset = drm_gem_ttm_dumb_map_offset,
  5. #if defined(CONFIG_DEBUG_FS)
  6.         .debugfs_init = qxl_debugfs_init,
  7. #endif
  8.         .gem_prime_import_sg_table = qxl_gem_prime_import_sg_table,
  9.         DRM_FBDEV_TTM_DRIVER_OPS,
  10.         .fops = &qxl_fops,
  11.         .ioctls = qxl_ioctls,
  12.         .num_ioctls = ARRAY_SIZE(qxl_ioctls),
  13.         .name = DRIVER_NAME,
  14.         .desc = DRIVER_DESC,
  15.         .major = 0,
  16.         .minor = 1,
  17.         .patchlevel = 0,
  18.         .release = qxl_drm_release,
  19. };
复制代码
        这个代码定义了一个 drm_driver 结构体,从这里也可以看出,QXL驱动确实是基于DRM框架的(DRM介绍详见本专栏的相关文章)。 drm_device 是QXL显卡在Linux DRM 框架中的核心驱动结构,这个结构注册后会与DRM核心框架配合,为显卡提供设备初始化,缓冲区管理,图形输出等功能。 
  1. struct qxl_device {
  2.         struct drm_device ddev;
  3.         resource_size_t vram_base, vram_size;
  4.         resource_size_t surfaceram_base, surfaceram_size;
  5.         resource_size_t rom_base, rom_size;
  6.         struct qxl_rom *rom;
  7.         struct qxl_mode *modes;
  8.         struct qxl_bo *monitors_config_bo;
  9.         struct qxl_monitors_config *monitors_config;
  10.         /* last received client_monitors_config */
  11.         struct qxl_monitors_config *client_monitors_config;
  12.         int io_base;
  13.         void *ram;
  14.         struct qxl_mman                mman;
  15.         struct qxl_gem                gem;
  16.         void *ram_physical;
  17.         struct qxl_ring *release_ring;
  18.         struct qxl_ring *command_ring;
  19.         struct qxl_ring *cursor_ring;
  20.         struct qxl_ram_header *ram_header;
  21.         struct qxl_bo *primary_bo;
  22.         struct qxl_bo *dumb_shadow_bo;
  23.         struct qxl_head *dumb_heads;
  24.         struct qxl_memslot main_slot;
  25.         struct qxl_memslot surfaces_slot;
  26.         spinlock_t        release_lock;
  27.         struct idr        release_idr;
  28.         uint32_t        release_seqno;
  29.         atomic_t        release_count;
  30.         wait_queue_head_t release_event;
  31.         spinlock_t release_idr_lock;
  32.         struct mutex        async_io_mutex;
  33.         unsigned int last_sent_io_cmd;
  34.         /* interrupt handling */
  35.         atomic_t irq_received;
  36.         atomic_t irq_received_display;
  37.         atomic_t irq_received_cursor;
  38.         atomic_t irq_received_io_cmd;
  39.         unsigned int irq_received_error;
  40.         wait_queue_head_t display_event;
  41.         wait_queue_head_t cursor_event;
  42.         wait_queue_head_t io_cmd_event;
  43.         struct work_struct client_monitors_config_work;
  44.         /* debugfs */
  45.         struct qxl_debugfs        debugfs[QXL_DEBUGFS_MAX_COMPONENTS];
  46.         unsigned int debugfs_count;
  47.         struct mutex                update_area_mutex;
  48.         struct idr        surf_id_idr;
  49.         spinlock_t surf_id_idr_lock;
  50.         int last_alloced_surf_id;
  51.         struct mutex surf_evict_mutex;
  52.         struct io_mapping *vram_mapping;
  53.         struct io_mapping *surface_mapping;
  54.         /* */
  55.         struct mutex release_mutex;
  56.         struct qxl_bo *current_release_bo[3];
  57.         int current_release_bo_offset[3];
  58.         struct work_struct gc_work;
  59.         struct drm_property *hotplug_mode_update_property;
  60.         int monitors_config_width;
  61.         int monitors_config_height;
  62. };
复制代码
        qxl_device 是QXL DRM驱动中定义的“设备结构体”,代表一个QXL显卡实例,在驱动运行时用于管理显卡的状态、资源、内存和与内核/用户空间交互。它是QXL驱动的核心数据结构,每个被探测到的QXL设备(好比前面说的通过PCI探测)都会分配这样一个结构并初始化,贯穿设备的整个生命周期。 
        下面看看QXL设备驱动是如何操作QXL设备的。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

八卦阵

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表