ルギア君の戯言

雑多な記事。

Matrox カードでデュアルスクリーン環境を構築する

以下のようなパッチを xorg-x11-drv-mga に当てた。

diff -ur xf86-video-mga-1.4.13.orig/src/mga.h xf86-video-mga-1.4.13/src/mga.h
--- xf86-video-mga-1.4.13.orig/src/mga.h        2012-02-08 02:36:22.062533733 -0500
+++ xf86-video-mga-1.4.13/src/mga.h     2012-02-07 21:50:14.572730442 -0500
@@ -337,6 +337,13 @@
     int                        mastervideoRam;
     int                        slavevideoRam;
     Bool               directRenderingEnabled;
+
+    void  *            mappedIOBase;
+    int                        mappedIOUsage;
+
+    void  *            mappedILOADBase;
+    int                        mappedILOADUsage;
+
     ScrnInfoPtr        pScrn_1;
     ScrnInfoPtr        pScrn_2;
 } MGAEntRec, *MGAEntPtr;
diff -ur xf86-video-mga-1.4.13.orig/src/mga_driver.c xf86-video-mga-1.4.13/src/mga_driver.c
--- xf86-video-mga-1.4.13.orig/src/mga_driver.c 2012-02-08 02:36:22.091533731 -0500
+++ xf86-video-mga-1.4.13/src/mga_driver.c      2012-02-07 21:51:58.448335431 -0500
@@ -1374,6 +1374,9 @@
 
     xf86SetDDCproperties(pScrn, MonInfo);
 
+ /* MGARestore(pScrn);  */
+ /* MGAUnmapMem(pScrn); */
+
     return MonInfo;
 }
 
@@ -2674,30 +2677,51 @@
 #ifdef XSERVER_LIBPCIACCESS
     struct pci_device *const dev = pMga->PciInfo;
     struct pci_mem_region *region;
-    void **memory[2];
-    int i, err;
+    int err;
 #endif
 
 
     if (!pMga->FBDev) {
 #ifdef XSERVER_LIBPCIACCESS
-        memory[pMga->io_bar] = &pMga->IOBase;
-        memory[pMga->framebuffer_bar] = &pMga->FbBase;
 
-        for (i = 0; i < 2; i++) {
-            region = &dev->regions[i];
-            err = pci_device_map_range(dev,
-                                       region->base_addr, region->size,
-                                       PCI_DEV_MAP_FLAG_WRITABLE,
-                                       memory[i]);
+           pciaddr_t fbaddr = pMga->FbAddress;
+           pciaddr_t fbsize = pMga->FbMapSize;
+       err = pci_device_map_range(dev,
+                                  fbaddr, fbsize,
+                                  PCI_DEV_MAP_FLAG_WRITABLE,
+                                  (void **)&pMga->FbBase);
 
-            if (err) {
+       if (err) {
                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-                           "Unable to map BAR %i.  %s (%d)\n",
-                           i, strerror(err), err);
+                           "Unable to map Framebuffer %08llX %llx.  %s (%d)\n", (long long)fbaddr, (long long)fbsize,
+                           strerror(err), err);
                 return FALSE;
-            }
-        }
+       }
+       else
+                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+                           "MAPPED Framebuffer %08llX %llx to %p.\n", (long long)fbaddr, (long long)fbsize, pMga->FbBase);
+
+
+
+       if(pMga->entityPrivate->mappedIOUsage == 0)
+       {
+               region = &dev->regions[pMga->io_bar];
+               err = pci_device_map_range(dev,
+                                          region->base_addr, region->size,
+                                          PCI_DEV_MAP_FLAG_WRITABLE,
+                                          &pMga->entityPrivate->mappedIOBase);
+
+               if (err) {
+                       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                                  "Unable to map IO Region %i.  %s (%d)\n",
+                                  pMga->io_bar, strerror(err), err);
+                       return FALSE;
+               }
+       }
+       
+       pMga->entityPrivate->mappedIOUsage ++;
+       pMga->IOBase = pMga->entityPrivate->mappedIOBase;
+
 #else
        /*
         * For Alpha, we need to map SPARSE memory, since we need
@@ -2748,16 +2772,23 @@
     if (pMga->iload_bar != -1) {
 #ifdef XSERVER_LIBPCIACCESS
         region = &dev->regions[pMga->iload_bar];
-        err = pci_device_map_range(dev,
+
+       if(pMga->entityPrivate->mappedILOADUsage == 0)
+       {
+               err = pci_device_map_range(dev,
                                    region->base_addr, region->size,
                                    PCI_DEV_MAP_FLAG_WRITABLE,
-                                   (void *) &pMga->ILOADBase);
-       if (err) {
-           xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-                      "Unable to map BAR 2 (ILOAD region).  %s (%d)\n",
-                      strerror(err), err);
-           return FALSE;
+                                   (void *) &pMga->entityPrivate->mappedILOADBase);
+               if (err) {
+                       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                                  "Unable to map BAR 2 (ILOAD region).  %s (%d)\n",
+                                  strerror(err), err);
+                       return FALSE;
+               }
        }
+
+       pMga->ILOADBase = pMga->entityPrivate->mappedILOADBase;
+       pMga->entityPrivate->mappedILOADUsage ++;
 #else
        pMga->ILOADBase = xf86MapPciMem(pScrn->scrnIndex,
                                        VIDMEM_MMIO | VIDMEM_MMIO_32BIT |
@@ -2787,10 +2818,17 @@
     
     if (!pMga->FBDev) {
 #ifdef XSERVER_LIBPCIACCESS
-        pci_device_unmap_range(dev, pMga->IOBase, 
-                              dev->regions[pMga->io_bar].size);
+           pMga->entityPrivate->mappedIOUsage --;
+           if(pMga->entityPrivate->mappedIOUsage == 0)
+           {
+                   pci_device_unmap_range(dev, pMga->IOBase, 
+                                          dev->regions[pMga->io_bar].size);
+                   pMga->entityPrivate->mappedIOBase = NULL;
+           }
+
+           xf86DrvMsg(pScrn->scrnIndex, X_INFO, "UNMAPPING framebuffer 0x%08llX, %p.\n", (long long)pMga->FbBase, pMga->FbMapSize);
         pci_device_unmap_range(dev, pMga->FbBase, 
-                              dev->regions[pMga->framebuffer_bar].size);
+                              pMga->FbMapSize);
 #else
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pMga->IOBase, 0x4000);
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pMga->FbBase, pMga->FbMapSize);
@@ -2803,8 +2841,14 @@
 
     if ((pMga->iload_bar != -1) && (pMga->ILOADBase != NULL)) {
 #ifdef XSERVER_LIBPCIACCESS
-        pci_device_unmap_range(dev, pMga->ILOADBase,
+           pMga->entityPrivate->mappedILOADUsage --;
+           if(pMga->entityPrivate->mappedILOADUsage == 0)
+           {
+                   pci_device_unmap_range(dev, pMga->ILOADBase,
                               dev->regions[pMga->iload_bar].size);
+                   pMga->entityPrivate->mappedILOADBase = NULL;
+           }
+
 #else
        pMga->ILOADBase = xf86MapPciMem(pScrn->scrnIndex,
                                        VIDMEM_MMIO | VIDMEM_MMIO_32BIT |
@@ -2787,10 +2818,17 @@
     
     if (!pMga->FBDev) {
 #ifdef XSERVER_LIBPCIACCESS
-        pci_device_unmap_range(dev, pMga->IOBase, 
-                              dev->regions[pMga->io_bar].size);
+           pMga->entityPrivate->mappedIOUsage --;
+           if(pMga->entityPrivate->mappedIOUsage == 0)
+           {
+                   pci_device_unmap_range(dev, pMga->IOBase, 
+                                          dev->regions[pMga->io_bar].size);
+                   pMga->entityPrivate->mappedIOBase = NULL;
+           }
+
+           xf86DrvMsg(pScrn->scrnIndex, X_INFO, "UNMAPPING framebuffer 0x%08llX, %p.\n", (long long)pMga->FbBase, pMga->FbMapSize);
         pci_device_unmap_range(dev, pMga->FbBase, 
-                              dev->regions[pMga->framebuffer_bar].size);
+                              pMga->FbMapSize);
 #else
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pMga->IOBase, 0x4000);
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pMga->FbBase, pMga->FbMapSize);
@@ -2803,8 +2841,14 @@
 
     if ((pMga->iload_bar != -1) && (pMga->ILOADBase != NULL)) {
 #ifdef XSERVER_LIBPCIACCESS
-        pci_device_unmap_range(dev, pMga->ILOADBase,
+           pMga->entityPrivate->mappedILOADUsage --;
+           if(pMga->entityPrivate->mappedILOADUsage == 0)
+           {
+                   pci_device_unmap_range(dev, pMga->ILOADBase,
                               dev->regions[pMga->iload_bar].size);
+                   pMga->entityPrivate->mappedILOADBase = NULL;
+           }
+
 #else
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pMga->ILOADBase, 0x800000);
 #endif

ちなみにタイムゾーンが -0500 (EST) になっていることに対するツッコミはなしで。

/etc/X11/xorg.conf
Section "Monitor"
 Identifier  "Monitor0"
 VendorName  "CPQ"
 ModelName   "P110"
 Option      "DPMS" "On"
 HorizSync   30-107
 VertRefresh 60,75,85
EndSection

Section "Monitor"
 Identifier  "Monitor1"
 VendorName  "SAMSUNG"
 Option      "DPMS" "On"
 ModelName   "SyncMaster 740BX"
 VertRefresh 60
EndSection

Section "Screen"
 Identifier   "Screen0"
 Device       "Videocard0"
 Monitor      "Monitor0"
 DefaultDepth 24
 SubSection "Display"
  Viewport  0 0
  Depth     24
  Modes     "1600x1200" "1280x1024" "1024x768" "800x600" "640x480"
 EndSubSection
EndSection

Section "Screen"
 Identifier   "Screen1"
 Device       "Videocard1"
 Monitor      "Monitor1"
 DefaultDepth 24
 SubSection  "Display"
  Viewport   0 0
  Depth      24
  Modes      "1280x1024" "1024x768" "800x600" "640x480"
 EndSubSection
EndSection

Section "Device"
 Identifier "Videocard0"
 Driver     "mga"
 Screen     0
 BusID      "PCI:1:0:0"
EndSection

Section "Device"
 Identifier "Videocard1"
 Driver     "mga"
 Screen     1
 BusID      "PCI:1:0:0"
EndSection

Section "ServerLayout"
 Option     "Xinerama" "on"
 Screen 0   "Screen0" 0 0
 Screen 1   "Screen1" Rightof "Screen0"
 Identifier "DualHead"
EndSection

これで動いている (正確に言えば P110 は 1280x1024 モードになっている *1 ) けど、このパッチは

  • Launchpad の記事で対象になっている Ubuntu でも Merge されていない (少なくとも Natty (11.04) では変更されている様子はなかった)
  • 一応 Bugfix であるが
    • このパッチだけではデュアルディスプレイにできない (RandR に対応していない)
    • よって xorg.conf を書かない限り認識しない
    • 高解像度デュアルディスプレイは不可 (前述)
    • RandR を無効にするので一部のソフトが動かなくなる (わりと深刻)
    • ほとんどのアプリ (gtk?) を起動する際に「Xサーバーの RandR が無効です!」と警告がでる。

ので、Momonga への Merge も保留にしておく。

*1:ドライバの制限でデュアルディスプレイ時は (?) 高解像度が利用できないと書かれている記事を見つけたけど見失った