From 77e196752bdd76a0c58ab082658d28c6a90fa40e Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 16 Dec 2008 11:54:34 +0800 Subject: [PATCH] [ARM] pxafb: allow video memory size to be configurable The amount of video memory size is decided according to the following order: 1. x x by default, which is the backward compatible way 2. size specified in platform data 3. size specified in module parameter 'options' string or specified in kernel boot command line (see updated Documentation/fb/pxafb.txt) And now since the memory is allocated from system memory, the pxafb_mmap can be removed and the default fb_mmap() should be working all right. Also, since we now have introduced the 'struct pxafb_dma_buff' for DMA descriptors and palettes, the allocation can be separated cleanly. NOTE: the LCD DMA actually supports chained transfer (i.e. page-based transfers), to simplify the logic and keep the performance (with less TLB misses when accessing from memory mapped user space), the memory is allocated by alloc_pages_*() to ensures it's physical contiguous. Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- Documentation/fb/pxafb.txt | 8 +- arch/arm/mach-pxa/include/mach/pxafb.h | 1 + drivers/video/pxafb.c | 131 ++++++++++--------------- drivers/video/pxafb.h | 17 +--- 4 files changed, 65 insertions(+), 92 deletions(-) diff --git a/Documentation/fb/pxafb.txt b/Documentation/fb/pxafb.txt index db9b8500b43b..ad94b5ca0095 100644 --- a/Documentation/fb/pxafb.txt +++ b/Documentation/fb/pxafb.txt @@ -5,9 +5,13 @@ The driver supports the following options, either via options= when modular or video=pxafb: when built in. For example: - modprobe pxafb options=mode:640x480-8,passive + modprobe pxafb options=vmem:2M,mode:640x480-8,passive or on the kernel command line - video=pxafb:mode:640x480-8,passive + video=pxafb:vmem:2M,mode:640x480-8,passive + +vmem: VIDEO_MEM_SIZE + Amount of video memory to allocate (can be suffixed with K or M + for kilobytes or megabytes) mode:XRESxYRES[-BPP] XRES == LCCR1_PPL + 1 diff --git a/arch/arm/mach-pxa/include/mach/pxafb.h b/arch/arm/mach-pxa/include/mach/pxafb.h index 4201a889ff4e..6932720ba04e 100644 --- a/arch/arm/mach-pxa/include/mach/pxafb.h +++ b/arch/arm/mach-pxa/include/mach/pxafb.h @@ -113,6 +113,7 @@ struct pxafb_mach_info { unsigned int num_modes; unsigned int lcd_conn; + unsigned long video_mem_size; u_int fixed_modes:1, cmap_inverse:1, diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index ab689597f259..25bf4b8b6b53 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -72,6 +72,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); +static unsigned long video_mem_size = 0; + static inline unsigned long lcd_readl(struct pxafb_info *fbi, unsigned int off) { @@ -498,20 +500,6 @@ static int pxafb_blank(int blank, struct fb_info *info) return 0; } -static int pxafb_mmap(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct pxafb_info *fbi = (struct pxafb_info *)info; - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; - - if (off < info->fix.smem_len) { - vma->vm_pgoff += fbi->video_offset / PAGE_SIZE; - return dma_mmap_writecombine(fbi->dev, vma, fbi->map_cpu, - fbi->map_dma, fbi->map_size); - } - return -EINVAL; -} - static struct fb_ops pxafb_ops = { .owner = THIS_MODULE, .fb_check_var = pxafb_check_var, @@ -521,7 +509,6 @@ static struct fb_ops pxafb_ops = { .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_blank = pxafb_blank, - .fb_mmap = pxafb_mmap, }; /* @@ -614,7 +601,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, dma_desc = &fbi->dma_buff->dma_desc[dma]; dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); - dma_desc->fsadr = fbi->screen_dma + offset; + dma_desc->fsadr = fbi->video_mem_phys + offset; dma_desc->fidr = 0; dma_desc->ldcmd = size; @@ -1267,69 +1254,30 @@ static int pxafb_resume(struct platform_device *dev) #define pxafb_resume NULL #endif -/* - * pxafb_map_video_memory(): - * Allocates the DRAM memory for the frame buffer. This buffer is - * remapped into a non-cached, non-buffered, memory region to - * allow palette and pixel writes to occur without flushing the - * cache. Once this area is remapped, all virtual memory - * access to the video memory should occur at the new region. - */ -static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi) +static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi) { - /* - * We reserve one page for the palette, plus the size - * of the framebuffer. - */ - fbi->video_offset = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); - fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + fbi->video_offset); - fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, - &fbi->map_dma, GFP_KERNEL); + int size = PAGE_ALIGN(fbi->video_mem_size); - if (fbi->map_cpu) { - /* prevent initial garbage on screen */ - memset(fbi->map_cpu, 0, fbi->map_size); - fbi->fb.screen_base = fbi->map_cpu + fbi->video_offset; - fbi->screen_dma = fbi->map_dma + fbi->video_offset; + fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); + if (fbi->video_mem == NULL) + return -ENOMEM; - /* - * FIXME: this is actually the wrong thing to place in - * smem_start. But fbdev suffers from the problem that - * it needs an API which doesn't exist (in this case, - * dma_writecombine_mmap) - */ - fbi->fb.fix.smem_start = fbi->screen_dma; - fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16; + fbi->video_mem_phys = virt_to_phys(fbi->video_mem); + fbi->video_mem_size = size; - fbi->dma_buff = (void *) fbi->map_cpu; - fbi->dma_buff_phys = fbi->map_dma; - fbi->palette_cpu = (u16 *) fbi->dma_buff->palette; + fbi->fb.fix.smem_start = fbi->video_mem_phys; + fbi->fb.fix.smem_len = fbi->video_mem_size; + fbi->fb.screen_base = fbi->video_mem; - pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16)); - } - - return fbi->map_cpu ? 0 : -ENOMEM; -} - -static void pxafb_decode_mode_info(struct pxafb_info *fbi, - struct pxafb_mode_info *modes, - unsigned int num_modes) -{ - unsigned int i, smemlen; - - pxafb_setmode(&fbi->fb.var, &modes[0]); - - for (i = 0; i < num_modes; i++) { - smemlen = modes[i].xres * modes[i].yres * modes[i].bpp / 8; - if (smemlen > fbi->fb.fix.smem_len) - fbi->fb.fix.smem_len = smemlen; - } + return fbi->video_mem ? 0 : -ENOMEM; } static void pxafb_decode_mach_info(struct pxafb_info *fbi, struct pxafb_mach_info *inf) { unsigned int lcd_conn = inf->lcd_conn; + struct pxafb_mode_info *m; + int i; fbi->cmap_inverse = inf->cmap_inverse; fbi->cmap_static = inf->cmap_static; @@ -1371,7 +1319,22 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi, fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; decode_mode: - pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes); + pxafb_setmode(&fbi->fb.var, &inf->modes[0]); + + /* decide video memory size as follows: + * 1. default to mode of maximum resolution + * 2. allow platform to override + * 3. allow module parameter to override + */ + for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++) + fbi->video_mem_size = max_t(size_t, fbi->video_mem_size, + m->xres * m->yres * m->bpp / 8); + + if (inf->video_mem_size > fbi->video_mem_size) + fbi->video_mem_size = inf->video_mem_size; + + if (video_mem_size > fbi->video_mem_size) + fbi->video_mem_size = video_mem_size; } static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) @@ -1499,7 +1462,9 @@ static int __devinit parse_opt(struct device *dev, char *this_opt) s[0] = '\0'; - if (!strncmp(this_opt, "mode:", 5)) { + if (!strncmp(this_opt, "vmem:", 5)) { + video_mem_size = memparse(this_opt + 5, NULL); + } else if (!strncmp(this_opt, "mode:", 5)) { return parse_opt_mode(dev, this_opt); } else if (!strncmp(this_opt, "pixclock:", 9)) { mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); @@ -1736,12 +1701,20 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed_free_res; } - /* Initialize video memory */ - ret = pxafb_map_video_memory(fbi); + fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); + fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size, + &fbi->dma_buff_phys, GFP_KERNEL); + if (fbi->dma_buff == NULL) { + dev_err(&dev->dev, "failed to allocate memory for DMA\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + ret = pxafb_init_video_memory(fbi); if (ret) { dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; - goto failed_free_io; + goto failed_free_dma; } irq = platform_get_irq(dev, 0); @@ -1811,8 +1784,10 @@ failed_free_cmap: failed_free_irq: free_irq(irq, fbi); failed_free_mem: - dma_free_writecombine(&dev->dev, fbi->map_size, - fbi->map_cpu, fbi->map_dma); + free_pages_exact(fbi->video_mem, fbi->video_mem_size); +failed_free_dma: + dma_free_coherent(&dev->dev, fbi->dma_buff_size, + fbi->dma_buff, fbi->dma_buff_phys); failed_free_io: iounmap(fbi->mmio_base); failed_free_res: @@ -1847,8 +1822,10 @@ static int __devexit pxafb_remove(struct platform_device *dev) irq = platform_get_irq(dev, 0); free_irq(irq, fbi); - dma_free_writecombine(&dev->dev, fbi->map_size, - fbi->map_cpu, fbi->map_dma); + free_pages_exact(fbi->video_mem, fbi->video_mem_size); + + dma_free_writecombine(&dev->dev, fbi->dma_buff_size, + fbi->dma_buff, fbi->dma_buff_phys); iounmap(fbi->mmio_base); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index d8eb93fa03a3..0981938682ef 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -69,24 +69,15 @@ struct pxafb_info { void __iomem *mmio_base; struct pxafb_dma_buff *dma_buff; + size_t dma_buff_size; dma_addr_t dma_buff_phys; dma_addr_t fdadr[DMA_MAX]; - /* - * These are the addresses we mapped - * the framebuffer memory region to. - */ - /* raw memory addresses */ - dma_addr_t map_dma; /* physical */ - u_char * map_cpu; /* virtual */ - u_int map_size; - - /* addresses of pieces placed in raw buffer */ - u_char * screen_cpu; /* virtual address of frame buffer */ - dma_addr_t screen_dma; /* physical address of frame buffer */ + void __iomem *video_mem; /* virtual address of frame buffer */ + unsigned long video_mem_phys; /* physical address of frame buffer */ + size_t video_mem_size; /* size of the frame buffer */ u16 * palette_cpu; /* virtual address of palette memory */ u_int palette_size; - ssize_t video_offset; u_int lccr0; u_int lccr3;