Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77e19675 authored by Eric Miao's avatar Eric Miao
Browse files

[ARM] pxafb: allow video memory size to be configurable



The amount of video memory size is decided according to the following
order:

1. <xres> x <yres> x <bits_per_pixel> 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: default avatarEric Miao <eric.miao@marvell.com>
Signed-off-by: default avatarEric Miao <ycmiao@ycmiao-hp520.(none)>
parent 5bfb4093
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -5,9 +5,13 @@ The driver supports the following options, either via
options=<OPTIONS> when modular or video=pxafb:<OPTIONS> 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
+1 −0
Original line number Diff line number Diff line
@@ -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,
+54 −77
Original line number Diff line number Diff line
@@ -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);

	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;
	int size = PAGE_ALIGN(fbi->video_mem_size);

		/*
		 * 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 = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
	if (fbi->video_mem == NULL)
		return -ENOMEM;

		fbi->dma_buff = (void *) fbi->map_cpu;
		fbi->dma_buff_phys = fbi->map_dma;
		fbi->palette_cpu = (u16 *) fbi->dma_buff->palette;
	fbi->video_mem_phys = virt_to_phys(fbi->video_mem);
	fbi->video_mem_size = size;

	        pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16));
	}
	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;

	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 @@ static int __devinit pxafb_probe(struct platform_device *dev)
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);

+4 −13
Original line number Diff line number Diff line
@@ -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;