foxsen 于 2007-2-2 02:28
初步搞定3D驱动
glxgears fps with dri: 297 FPS
without: 66 FPS
好像效率还不高,也许是驱动里边太保守的缘故。具体过程明天再来写了
福珑3D攻略
很多朋友希望看到福珑上的3D,因此我化了一些时间去做这个事情,现在基本完成,在此把过程大概总结一下,以供参考。
我以前也没有玩过3D相关的东西,所以第一个碰到的问题就是了解概念。dri.freedesktop.org是一个不错的起点,看了看
之后大概了解了整体架构:
* 内核提供drm驱动,为应用程序提供访问显卡3D硬件资源的API
* xorg中包含2D驱动,服务器端的GLX和能装载3D驱动的GL库
* 3D驱动在mesa的源码包中
由于我们使用debian,源代码包就不用从cvs下了,直接用debian打包好的,具体涉及的代码包括:
xorg-server-1.1.1
xserver-xorg-video-ati-6.6.3
libdrm-2.0.2
mesa-6.5.1
kernel source 2.6.18
除了最后一个都是apt-get source取来的,因此编译方便得很,只要用apt-get build-dep packagename就可以装上依赖的东西,
不用重复编译很多次。
其实这些东西的二进制都是现成的,因此我一上来就想看一下是不是可以白捡:
把/etc/X11/xorg.conf中的dri/glx模块装载上
Section "Module"
Load "i2c"
Load "bitmap"
Load "ddc"
Load "dri" //这个
Load "extmod"
Load "freetype"
Load "glx" //这个
Load "int10"
Load "vbe"
EndSection
对应的是/usr/lib/xorg/modules/extensions/下的libdri.so和libglx.so,源代码则在xorg-xserver-1.1.1里边
把内核的radeon drm模块选上,安装(drivers/char/drm/{drm.ko,radeon.ko})
(具体过程略,没时间了;主要碰到的问题包括drmopen失败(page size问题),writeback test failed(内存访问cache一致性问题),死锁(drm lock原语不支持mips))
xorg-server-1.1.1:
1. hw/xfree86/dri/sarea.h
由于龙芯采用16k page size,因此SAREA_MAX必须定义为0x4000
/* SAREA area needs to be at least a page */
#if defined(__alpha__)
#define SAREA_MAX 0x2000
+#elif defined(__mips__)
+#define SAREA_MAX 0x4000
#elif defined(__ia64__)
#define SAREA_MAX 0x10000 /* 64kB */
#else
/* Intel 830M driver needs at least 8k SAREA */
#define SAREA_MAX 0x2000
#endif
系统头文件和meda/libdrm以及内核等也有类似文件,都需要同样修改,以免应用程序出问题:
/usr/include/xorg/sarea.h
/usr/include/drm/drm_sarea.h
<kernel>/drivers/char/drm/drm_sarea.h
<mesa>/include/GL/internal/sarea.h
2. hw/xfree86/os-support/
增加mips架构的锁操作
--- x.h 2009-01-30 13:07:12.000000000 +0800
+++ xf86drm.h 2007-01-30 02:04:04.000000000 +0800
@@ -327,6 +327,30 @@
"r" (new)); \
} while(0)
+#elif defined(__mips__)
+
+#define DRM_CAS(lock, old, new, ret) \
+ do { \
+ __asm__ __volatile__( \
+ " .set mips3;.set noreorder;\n" \
+ " sync; \n" \
+ " ll %1, %0;\n" \
+ " bne %1, %2, 1f;\n" \
+ " li %1, 1; \n" \
+ " move %1, %3; \n" \
+ " sc %1, %0;\n" \
+ " xori %1, %1, 1\n" \
+ "1: \n" \
+ " .set mips0; .set reorder;\n" \
+ : "=m" (__drm_dummy_lock(lock)),\
+ "=&r" (ret) \
+ : "r" (old), \
+ "r" (new) \
+ :"memory","$8" \
+ ); \
+ } while(0)
+
+
#elif defined(__sparc__)
#define DRM_CAS(lock,old,new,__ret) \
@@ -408,7 +432,7 @@
#define DRM_CAS(lock,old,new,ret) do { ret=1; } while (0) /* FAST LOCK FAILS */
#endif
-#if defined(__alpha__) || defined(__powerpc__)
+#if defined(__alpha__) || defined(__powerpc__) || defined(__mips__)
#define DRM_CAS_RESULT(_result) int _result
#else
#define DRM_CAS_RESULT(_result) char _result
同样,系统头文件/usr/include/xf86drm.h也应该同样修改,这样mesa等编译的时候才会正确
mesa-6.5.1:
相应头文件改过后重新编译即可。
kernel: (见相关注释)
Index: drm_bufs.c
===================================================================
--- drm_bufs.c (revision 15)
+++ drm_bufs.c (working copy)
@@ -194,7 +194,8 @@
break;
case _DRM_SHM: /* 用uncache 方式访问以避免cache一致性问题,不确定对SHM是否必须 */
- map->handle = vmalloc_32(map->size);
+ //map->handle = vmalloc_32(map->size);
+ map->handle = __vmalloc(map->size, GFP_KERNEL | __GFP_HIGHMEM , pgprot_noncached(PAGE_KERNEL));
DRM_DEBUG("%lu %d %p\n",
map->size, drm_order(map->size), map->handle);
if (!map->handle) {
Index: drm_scatter.c
===================================================================
--- drm_scatter.c (revision 15)
+++ drm_scatter.c (working copy)
@@ -113,7 +113,8 @@
}
memset((void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr));
/* 用uncache 方式访问以避免cache一致性问题, sg内存是可能用来DMA的*/
- entry->virtual = vmalloc_32(pages << PAGE_SHIFT);
+ //entry->virtual = vmalloc_32(pages << PAGE_SHIFT);
+ entry->virtual = __vmalloc(pages << PAGE_SHIFT, GFP_KERNEL | __GFP_HIGHMEM , pgprot_noncached(PAGE_KERNEL));
if (!entry->virtual) {
drm_free(entry->busaddr,
entry->pages * sizeof(*entry->busaddr), DRM_MEM_PAGES);
@@ -162,6 +163,7 @@
unsigned long *tmp;
tmp = page_address(entry->pagelist);
/* 用uncached地址读写,才能通过writeback test*/
+ tmp = (unsigned long*)UNCAC_ADDR((unsigned long)tmp);
for (j = 0;
j < PAGE_SIZE / sizeof(unsigned long);
j++, tmp++) {
@@ -180,6 +182,7 @@
}
}
tmp = page_address(entry->pagelist);
+ tmp = (unsigned long*)UNCAC_ADDR((unsigned long)tmp);
for (j = 0;
j < PAGE_SIZE / sizeof(unsigned long);
j++, tmp++) {
@@ -191,6 +194,7 @@
}
#endif
+
return 0;
failed:
Index: radeon_cp.c
===================================================================
--- radeon_cp.c (revision 15)
+++ radeon_cp.c (working copy)
@@ -877,7 +877,6 @@
}
#if RADEON_FIFO_DEBUG
- DRM_ERROR("failed!\n");
radeon_status(dev_priv);
#endif
return DRM_ERR(EBUSY);
@@ -898,7 +897,6 @@
}
#if RADEON_FIFO_DEBUG
- DRM_ERROR("failed!\n");
radeon_status(dev_priv);
#endif
return DRM_ERR(EBUSY);
@@ -924,7 +922,6 @@
}
#if RADEON_FIFO_DEBUG
- DRM_ERROR("failed!\n");
radeon_status(dev_priv);
#endif
return DRM_ERR(EBUSY);
@@ -2216,9 +2213,15 @@
if (ret != 0)
return ret;
/* first open 时增加这个map,但是根据pci bar的长度和物理显存大小不一致,会导致
xorg match map list时失败
*/
+#if 0
ret = drm_addmap(dev, drm_get_resource_start(dev, 0),
drm_get_resource_len(dev, 0), _DRM_FRAME_BUFFER,
_DRM_WRITE_COMBINING, &map);
+#else
+ ret = drm_addmap(dev, drm_get_resource_start(dev, 0),
+ 0x1000000, _DRM_FRAME_BUFFER,
+ _DRM_WRITE_COMBINING, &map);
+#endif
if (ret != 0)
return ret;
Index: drm_sarea.h
===================================================================
--- drm_sarea.h (revision 15)
+++ drm_sarea.h (working copy)
/* 使用16k page,SAREA_MAX至少为1页 */
@@ -37,6 +37,8 @@
/* SAREA area needs to be at least a page */
#if defined(__alpha__)
#define SAREA_MAX 0x2000
+#elif defined(__mips__)
+#define SAREA_MAX 0x4000
#elif defined(__ia64__)
#define SAREA_MAX 0x10000 /* 64kB */
#else
Index: drm_pci.c
===================================================================
--- drm_pci.c (revision 15)
+++ drm_pci.c (working copy)
/* virt_to_page 只能对付cached地址 */
@@ -113,7 +113,8 @@
/* Reserve */
for (addr = (unsigned long)dmah->vaddr, sz = size;
sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- SetPageReserved(virt_to_page(addr));
+// SetPageReserved(virt_to_page(addr));
+ SetPageReserved(virt_to_page(CAC_ADDR(addr)));
}
return dmah;
@@ -147,7 +148,8 @@
/* Unreserve */
for (addr = (unsigned long)dmah->vaddr, sz = dmah->size;
sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- ClearPageReserved(virt_to_page(addr));
+ // ClearPageReserved(virt_to_page(addr));
+ ClearPageReserved(virt_to_page(CAC_ADDR(addr)));
}
dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr,
dmah->busaddr);
--- drm_vm.c (revision 15) /* 2007.02.02更新 */
+++ drm_vm.c (working copy)
@@ -155,7 +155,7 @@
offset = address - vma->vm_start;
i = (unsigned long)map->handle + offset;
page = (map->type == _DRM_CONSISTENT) ?
- virt_to_page((void *)i) : vmalloc_to_page((void *)i);
+ virt_to_page((void *)CAC_ADDR(i)) : vmalloc_to_page((void *)i);
if (!page)
return NOPAGE_OOM;
get_page(page);
@@ -279,7 +279,7 @@
offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
page_nr = offset >> PAGE_SHIFT;
- page = virt_to_page((dma->pagelist[page_nr] + (offset & (~PAGE_MASK))));
+ page = virt_to_page(CAC_ADDR(dma->pagelist[page_nr] + (offset & (~PAGE_MASK))));
get_page(page);
@@ -479,6 +479,9 @@
vma->vm_ops = &drm_vm_dma_ops;
vma->vm_flags |= VM_RESERVED; /* Don't swap */
+#ifdef __mips__
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
vma->vm_file = filp; /* Needed for drm_vm_open() */
drm_vm_open(vma);
@@ -503,6 +506,17 @@
EXPORT_SYMBOL(drm_core_get_reg_ofs);
+#ifdef __mips__
+static inline pgprot_t pgprot_noncached_accelerated(pgprot_t _prot)
+{
+ unsigned long prot = pgprot_val(_prot);
+
+ prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED_ACCELERATED;
+
+ return __pgprot(prot);
+}
+#endif
+
/**
* mmap DMA memory.
*
@@ -625,6 +639,13 @@
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
#else
+#ifdef __mips__
+ if (map->type == _DRM_FRAME_BUFFER)
+ vma->vm_page_prot = pgprot_noncached_accelerated(vma->vm_page_prot);
+ //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
if (io_remap_pfn_range(vma, vma->vm_start,
(map->offset + offset) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
@@ -646,11 +667,17 @@
/* Don't let this area swap. Change when
DRM_KERNEL advisory is supported. */
vma->vm_flags |= VM_RESERVED;
+#ifdef __mips__
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
break;
case _DRM_SCATTER_GATHER:
vma->vm_ops = &drm_vm_sg_ops;
vma->vm_private_data = (void *)map;
vma->vm_flags |= VM_RESERVED;
+#ifdef __mips__
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
break;
default:
return -EINVAL; /* This should never happen. */
Index: ati_pcigart.c
===================================================================
--- ati_pcigart.c (revision 15)
+++ ati_pcigart.c (working copy)
@@ -175,7 +175,8 @@
bus_address, (unsigned long)address);
}
/* 用uncache地址确保写入*/
- pci_gart = (u32 *) address;
+// pci_gart = (u32 *) address;
+ pci_gart = (u32 *) UNCAC_ADDR(address);
pages = (entry->pages <= ATI_MAX_PCIGART_PAGES)
? entry->pages : ATI_MAX_PCIGART_PAGES;
大致如此,有事离开,其中碰到的几个关键现象和解决思路回头有空慢慢补上。
附件 | 大小 |
---|---|
debian_mesa_sarea 补丁 | 524 字节 |
debian_libdrm_lock 补丁 | 1.63 千字节 |
debian_xserver-xore-core_sarea 补丁 | 518 字节 |
96_lnx_video_mmap.diff | 1.03 千字节 |