最近在调TFT分辨率,当使用1024x768 16bpp时正常,而当调整为1024x768 24bpp时报"Failed to allocate video memory"的错误.
出错的地方是在分配显示缓冲区.代码如下:
static __inline int allocate_video_memory_map(struct fb_info *fbinfo)
{
...
fbinfo->screen_base = dma_alloc_writecombine(fbi->dev, map_size, (dma_addr_t *)&fbinfo->fix.smem_start, GFP_KERNEL);
if (!fbinfo->screen_base) {
return -ENOMEM;
}
return 0;
}
...
{
ret = allocate_video_memory_map(fbinfo);
if (ret) {
dev_err(fbi->dev, "Failed to allocate video memory ma (%d)\n", ret);
return -ENOMEM;
}
}
dma_alloc_writecombine函数
/*
* Allocate DMA-coherent memory space and return both the kernel remapped
* virtual and bus address for that space.
*/
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
void *memory;
if (dma_alloc_from_coherent(dev, size, handle, &memory))
return memory;
if (arch_is_coherent()) {
void *virt;
virt = kmalloc(size, gfp);
if (!virt)
return NULL;
*handle = virt_to_dma(dev, virt);
return virt;
}
return __dma_alloc(dev, size, handle, gfp,
pgprot_noncached(pgprot_kernel));
}
EXPORT_SYMBOL(dma_alloc_coherent);
/*
* Allocate a writecombining region, in much the same way as
* dma_alloc_coherent above.
*/
void *
dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
return __dma_alloc(dev, size, handle, gfp,
pgprot_writecombine(pgprot_kernel));
}
EXPORT_SYMBOL(dma_alloc_writecombine);
由上方代码可以看出,两个函数都调用了__dma_alloc函数,差别只在于最后一个参数。
dma_alloc_coherent 在 arm 平台上会禁止页表项中的 C (Cacheable) 域以及 B (Bufferable)域。而 dma_alloc_writecombine 只禁止 C (Cacheable) 域.
C 代表是否应用高速缓冲存储器, 而 B 代表是否应用写缓冲区。
如许,dma_alloc_writecombine 分派出来的内存不应用缓存,然则会应用写缓冲区。而 dma_alloc_coherent 则二者都不应用。
C B 位的具体含义
0 0 无cache,无写缓冲;任何对memory的读写都反应到总线上。对 memory 的操纵过程中CPU须要守候。
0 1 无cache,有写缓冲;读操纵直接反应到总线上;写操纵,CPU将数据写入到写缓冲后持续运行,由写缓冲进行写回操纵。
1 0 有cache,写通模式;读操纵起首推敲cache hit;写操纵时直接将数据写入写缓冲,若是同时呈现cache hit,那么也更新cache。
1 1 有cache,写回模式;读操纵起首推敲cache hit;写操纵也起首推敲cache hit。
如许,两者的差别就很清楚了。
static void *
__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
pgprot_t prot)
{
struct page *page;
struct arm_vm_region *c;
unsigned long order;
u64 mask = ISA_DMA_THRESHOLD, limit;
if (!consistent_pte[0]) {
printk(KERN_ERR "%s: not initialised\n", __func__);
dump_stack();
return NULL;
}
if (dev) {
mask = dev->coherent_dma_mask;
/*
* Sanity check the DMA mask - it must be non-zero, and
* must be able to be satisfied by a DMA allocation.
*/
if (mask == 0) {
dev_warn(dev, "coherent DMA mask is unset\n");
goto no_page;
}
if ((~mask) & ISA_DMA_THRESHOLD) {
dev_warn(dev, "coherent DMA mask %#llx is smaller "
"than system GFP_DMA mask %#llx\n",
mask, (unsigned long long)ISA_DMA_THRESHOLD);
goto no_page;
}
}
/*
* Sanity check the allocation size.
*/
size = PAGE_ALIGN(size);
limit = (mask + 1) & ~mask;
if ((limit && size >= limit) ||
size >= (CONSISTENT_END - CONSISTENT_BASE)) {
printk(KERN_WARNING "coherent allocation too big "
"(requested %#x mask %#llx)\n", size, mask);
goto no_page;
}
order = get_order(size);
if (mask != 0xffffffff)
gfp |= GFP_DMA;
page = alloc_pages(gfp, order);
if (!page)
goto no_page;
/*
* Invalidate any data that might be lurking in the
* kernel direct-mapped region for device DMA.
*/
{
void *ptr = page_address(page);
memset(ptr, 0, size);
dmac_flush_range(ptr, ptr + size);
outer_flush_range(__pa(ptr), __pa(ptr) + size);
}
/*
* Allocate a virtual address in the consistent mapping region.
*/
c = arm_vm_region_alloc(&consistent_head, size,
gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
if (c) {
pte_t *pte;
struct page *end = page + (1 << order);
int idx = CONSISTENT_PTE_INDEX(c->vm_start);
u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
pte = consistent_pte[idx] + off;
c->vm_pages = page;
split_page(page, order);
/*
* Set the "dma handle"
*/
*handle = page_to_dma(dev, page);
do {
BUG_ON(!pte_none(*pte));
/*
* x86 does not mark the pages reserved...
*/
SetPageReserved(page);
set_pte_ext(pte, mk_pte(page, prot), 0);
page++;
pte++;
off++;
if (off >= PTRS_PER_PTE) {
off = 0;
pte = consistent_pte[++idx];
}
} while (size -= PAGE_SIZE);
/*
* Free the otherwise unused pages.
*/
while (page < end) {
__free_page(page);
page++;
}
return (void *)c->vm_start;
}
if (page)
__free_pages(page, order);
no_page:
*handle = ~0;
return NULL;
}
在文件arch/arm/include/asm/memory.h中找到有关CONSISTENT_DMA_SIZE的定义:
/*
* Size of DMA-consistent memory region. Must be multiple of 2M,
* between 2MB and 14MB inclusive.
*/
#ifndef CONSISTENT_DMA_SIZE
#define CONSISTENT_DMA_SIZE SZ_2M
#endif
而文件arch/arm/include/asm/memory.h定义的是Linux下整个ARM体系的公共参数,如果直接修改会引起别的平台的一些不可预料的结果.
幸运的是在memory.h文件开头有一个include文件
#include
找到
#define CONSISTENT_DMA_SIZE SZ_4M