您的位置:首页 > 其它

使用dma_alloc_writecombine申请内存空间大小的限制

2012-12-06 10:36 501 查看
最近在调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 <mach/memory.h>

找到<mach/memory.h>文件arch/arm/mach-xxx/include/mach/memory.h,在其中添加CONSISTENT_DMA_SIZE的定义即可,如:

#define CONSISTENT_DMA_SIZE SZ_4M
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: