How to mmap multiple pages?

Oliver Zander <[email protected]> Mon, 08 Oct 2007 16:00:04 +0200
Newsgroups gmane.linux.fbdev.user
Organization COMO - Computer & Motion GmbH
Message-ID <1191852004.5793.29.camel@oliver-linux>
Hi all,
I wrote a framebuffer driver for an Atmel AVR32 microcontroller with a
simple monochrome LCD wired to GPIO. The fb uses system RAM.
My problem is that I need 8 PAGES of mem (256*64*16 bpp) and cannot find
code which is able to mmap more than 1 PAGE.

Here's my code:
-------------------------------------------------------------------------------
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

#include <asm/uaccess.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/types.h>

#define MEM_SIZE    (256*64*2)
#define SCREEN_SIZE (256*64*2)
#define LINE_LEN    (256*2)

u8 *lm6800_buffer;
EXPORT_SYMBOL_GPL(lm6800_buffer);

static struct fb_var_screeninfo meinfb_var __initdata = {
	.xres =		256,
	.yres =		64,
	.xres_virtual =	256,
	.yres_virtual =	64,
	.bits_per_pixel = 16,
	.red =		{ 11,5, 0 },
      	.green =	{ 5, 6, 0 },
      	.blue =		{ 0, 5, 0 },
      	.left_margin =	0,
      	.right_margin =	0,
      	.upper_margin =	0,
      	.lower_margin =	0,
      	.vmode =	FB_VMODE_NONINTERLACED,
};

static struct fb_fix_screeninfo meinfb_fix __initdata = {
	.id =		"LM6800 COLOR FB",
	.type =		FB_TYPE_PACKED_PIXELS,
	.smem_len =     MEM_SIZE,
	.visual =	FB_VISUAL_TRUECOLOR,
	.xpanstep =	0,
	.ypanstep =	0,
	.ywrapstep =	0,
	.line_length =  LINE_LEN,
	.accel =	FB_ACCEL_NONE,
};

//do I need this at all?
static int meinfb_setcolreg(unsigned regno, unsigned red, unsigned
green,
			   unsigned blue, unsigned transp,
			   struct fb_info *info)
{
	info->cmap = *fb_default_cmap(16);
	return 0;
}

static int meinfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
	return vm_insert_page(vma, vma->vm_start,
		virt_to_page(lm6800_buffer));

}

static struct fb_ops meinfb_ops = {
	.owner          = THIS_MODULE,
	.fb_read        = fb_sys_read,
	.fb_write       = fb_sys_write,
	.fb_setcolreg   = meinfb_setcolreg,
	.fb_fillrect	= sys_fillrect,
	.fb_copyarea	= sys_copyarea,
	.fb_imageblit	= sys_imageblit,
	.fb_mmap        = meinfb_mmap,
};

static int __init meinfb_probe(struct platform_device *dev)
{
	struct fb_info *info;
	int retval = -ENOMEM;

	// allocate in lcd driver?
	// PAGE_SIZE is 4096 so I need 8 pages (256*64*2 / 4096 = 8)
	if (!(lm6800_buffer = (u8 *) __get_free_pages(GFP_KERNEL, 3)))
		return retval;
	memset(lm6800_buffer, 0, MEM_SIZE);

	info = framebuffer_alloc(0, &dev->dev);
	if (!info)
		goto err;

	info->screen_base = (char __iomem *)lm6800_buffer;
	info->screen_size = SCREEN_SIZE;
	info->fbops = &meinfb_ops;
	info->var = meinfb_var;
	info->fix = meinfb_fix;
	info->pseudo_palette = NULL;
	info->par = NULL;
	info->flags = FBINFO_FLAG_DEFAULT;

	fb_alloc_cmap(&info->cmap, 16, 0);
	info->cmap = *fb_default_cmap(16);

	retval = register_framebuffer(info);
	if (retval < 0)
		goto err1;
	platform_set_drvdata(dev, info);

	return 0;

err1:
	framebuffer_release(info);
err:
	free_pages((unsigned long) lm6800_buffer, 3);
	return retval;
}

static int meinfb_remove(struct platform_device *dev)
{
	struct fb_info *info = platform_get_drvdata(dev);

	if (info) {
		unregister_framebuffer(info);
		free_pages((unsigned long) lm6800_buffer, 3);
		framebuffer_release(info);
	}
	return 0;
}

static struct platform_driver meinfb_driver = {
	.probe	= meinfb_probe,
	.remove = meinfb_remove,
	.driver = {
		.name	= "meinfb",
	},
};

static struct platform_device *meinfb_device;

static int __init meinfb_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&meinfb_driver);

	if (!ret) {
		meinfb_device = platform_device_alloc("meinfb", 0);

		if (meinfb_device)
			ret = platform_device_add(meinfb_device);
		else
			ret = -ENOMEM;

		if (ret) {
			platform_device_put(meinfb_device);
			platform_driver_unregister(&meinfb_driver);
		}
	}

	return ret;
}

static void __exit meinfb_exit(void)
{
	platform_device_unregister(meinfb_device);
	platform_driver_unregister(&meinfb_driver);
}

module_init(meinfb_init);
module_exit(meinfb_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Zander");
MODULE_DESCRIPTION("LM6800 framebuffer driver with color support");
-------------------------------------------------------------------------------

I'm very new to Linux and driver development so please be forgiving :)
TIA
O.Zander


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/