All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
To: linux-fbdev-devel@lists.sourceforge.net
Cc: linux-omap@vger.kernel.org
Subject: [PATCH 06/12] DSS: OMAPFB: fb driver for new display subsystem
Date: Mon, 12 Jan 2009 13:47:58 +0200	[thread overview]
Message-ID: <20090112114758.1003.54003.stgit@tubuntu> (raw)
In-Reply-To: <20090112114718.1003.23643.stgit@tubuntu>

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
---

 arch/arm/plat-omap/Makefile              |    2 
 arch/arm/plat-omap/fb-vram.c             |  646 +++++++++++++
 arch/arm/plat-omap/fb.c                  |   22 
 arch/arm/plat-omap/include/mach/omapfb.h |   14 
 drivers/video/Kconfig                    |    1 
 drivers/video/Makefile                   |    1 
 drivers/video/omap/Kconfig               |    5 
 drivers/video/omap2/Kconfig              |   42 +
 drivers/video/omap2/Makefile             |    2 
 drivers/video/omap2/omapfb-ioctl.c       |  460 ++++++++++
 drivers/video/omap2/omapfb-main.c        | 1442 ++++++++++++++++++++++++++++++
 drivers/video/omap2/omapfb-sysfs.c       |  901 +++++++++++++++++++
 drivers/video/omap2/omapfb.h             |  115 ++
 13 files changed, 3650 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/plat-omap/fb-vram.c
 create mode 100644 drivers/video/omap2/Kconfig
 create mode 100644 drivers/video/omap2/Makefile
 create mode 100644 drivers/video/omap2/omapfb-ioctl.c
 create mode 100644 drivers/video/omap2/omapfb-main.c
 create mode 100644 drivers/video/omap2/omapfb-sysfs.c
 create mode 100644 drivers/video/omap2/omapfb.h

diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index 2740497..7d602a6 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -4,7 +4,7 @@
 
 # Common support
 obj-y := common.o sram.o clock.o devices.o dma.o mux.o gpio.o \
-	 usb.o fb.o io.o
+	 usb.o fb.o fb-vram.o io.o
 obj-m :=
 obj-n :=
 obj-  :=
diff --git a/arch/arm/plat-omap/fb-vram.c b/arch/arm/plat-omap/fb-vram.c
new file mode 100644
index 0000000..2994f8f
--- /dev/null
+++ b/arch/arm/plat-omap/fb-vram.c
@@ -0,0 +1,646 @@
+/*
+ * linux/arch/arm/plat-omap/fb-vram.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#define DEBUG
+
+#include <linux/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/bootmem.h>
+
+#include <asm/setup.h>
+
+#include <mach/sram.h>
+#include <mach/omapfb.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) printk(KERN_DEBUG "VRAM: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define OMAP2_SRAM_START		0x40200000
+/* Maximum size, in reality this is smaller if SRAM is partially locked. */
+#define OMAP2_SRAM_SIZE			0xa0000		/* 640k */
+
+#define REG_MAP_SIZE(_page_cnt)						\
+	((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8)
+#define REG_MAP_PTR(_rg, _page_nr)					\
+	(((_rg)->map) + (_page_nr) / (sizeof(unsigned long) * 8))
+#define REG_MAP_MASK(_page_nr)						\
+	(1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1)))
+
+#if defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
+
+/* postponed regions are used to temporarily store region information at boot
+ * time when we cannot yet allocate the region list */
+#define MAX_POSTPONED_REGIONS 10
+
+static int postponed_cnt __initdata;
+static struct {
+	unsigned long paddr;
+	size_t size;
+} postponed_regions[MAX_POSTPONED_REGIONS] __initdata;
+
+struct vram_alloc {
+	struct list_head list;
+	unsigned long paddr;
+	unsigned pages;
+};
+
+struct vram_region {
+	struct list_head list;
+	struct list_head alloc_list;
+	unsigned long	paddr;
+	void		*vaddr;
+	unsigned	pages;
+	unsigned	dma_alloced:1;
+};
+
+static DEFINE_MUTEX(region_mutex);
+static LIST_HEAD(region_list);
+
+static inline int region_mem_type(unsigned long paddr)
+{
+	if (paddr >= OMAP2_SRAM_START &&
+	    paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
+		return OMAPFB_MEMTYPE_SRAM;
+	else
+		return OMAPFB_MEMTYPE_SDRAM;
+}
+
+static struct vram_region *omap_vram_create_region(unsigned long paddr,
+		void *vaddr, unsigned pages)
+{
+	struct vram_region *rm;
+
+	rm = kzalloc(sizeof(*rm), GFP_KERNEL);
+
+	if (rm) {
+		INIT_LIST_HEAD(&rm->alloc_list);
+		rm->paddr = paddr;
+		rm->vaddr = vaddr;
+		rm->pages = pages;
+	}
+
+	return rm;
+}
+
+static void omap_vram_free_region(struct vram_region *vr)
+{
+	list_del(&vr->list);
+	kfree(vr);
+}
+
+static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr,
+		unsigned long paddr, unsigned pages)
+{
+	struct vram_alloc *va;
+	struct vram_alloc *new;
+
+	new = kzalloc(sizeof(*va), GFP_KERNEL);
+
+	if (!new)
+		return NULL;
+
+	new->paddr = paddr;
+	new->pages = pages;
+
+	list_for_each_entry(va, &vr->alloc_list, list) {
+		if (va->paddr > new->paddr)
+			break;
+	}
+
+	list_add_tail(&new->list, &va->list);
+
+	return new;
+}
+
+static void omap_vram_free_allocation(struct vram_alloc *va)
+{
+	list_del(&va->list);
+	kfree(va);
+}
+
+static __init int omap_vram_add_region_postponed(unsigned long paddr, size_t size)
+{
+	if (postponed_cnt == MAX_POSTPONED_REGIONS)
+		return -ENOMEM;
+
+	postponed_regions[postponed_cnt].paddr = paddr;
+	postponed_regions[postponed_cnt].size = size;
+
+	++postponed_cnt;
+
+	return 0;
+}
+
+/* add/remove_region can be exported if there's need to add/remove regions
+ * runtime */
+static int omap_vram_add_region(unsigned long paddr, size_t size)
+{
+	struct vram_region *rm;
+	void *vaddr;
+	unsigned pages;
+
+	DBG("adding region paddr %08lx size %d\n",
+			paddr, size);
+
+	size &= PAGE_MASK;
+	pages = size >> PAGE_SHIFT;
+
+	vaddr = ioremap_wc(paddr, size);
+	if (vaddr == NULL)
+		return -ENOMEM;
+
+	rm = omap_vram_create_region(paddr, vaddr, pages);
+	if (rm == NULL) {
+		iounmap(vaddr);
+		return -ENOMEM;
+	}
+
+	list_add(&rm->list, &region_list);
+
+	return 0;
+}
+
+#if 0
+int omap_vram_remove_region(unsigned long paddr)
+{
+	struct region *rm;
+	unsigned i;
+
+	DBG("remove region paddr %08lx\n", paddr);
+	list_for_each_entry(rm, &region_list, list)
+		if (rm->paddr != paddr)
+			continue;
+
+	if (rm->paddr != paddr)
+		return -EINVAL;
+
+	for (i = 0; i < rm->page_cnt; i++)
+		if (region_page_reserved(rm, i))
+			return -EBUSY;
+
+	iounmap(rm->vaddr);
+
+	list_del(&rm->list);
+
+	kfree(rm);
+
+	return 0;
+}
+#endif
+
+int omap_vram_free(unsigned long paddr, void *vaddr, size_t size)
+{
+	struct vram_region *rm;
+	struct vram_alloc *alloc;
+	unsigned start, end;
+
+	DBG("free mem paddr %08lx vaddr %p size %d\n",
+			paddr, vaddr, size);
+
+	size = PAGE_ALIGN(size);
+
+	mutex_lock(&region_mutex);
+
+	list_for_each_entry(rm, &region_list, list) {
+		list_for_each_entry(alloc, &rm->alloc_list, list) {
+			start = alloc->paddr;
+			end = alloc->paddr + (alloc->pages >> PAGE_SHIFT);
+
+			if (start >= paddr && end < paddr + size)
+				goto found;
+		}
+	}
+
+	mutex_unlock(&region_mutex);
+	return -EINVAL;
+
+found:
+	if (rm->dma_alloced) {
+		DBG("freeing dma-alloced\n");
+		dma_free_writecombine(NULL, size, vaddr, paddr);
+		omap_vram_free_allocation(alloc);
+		omap_vram_free_region(rm);
+	} else {
+		omap_vram_free_allocation(alloc);
+	}
+
+	mutex_unlock(&region_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(omap_vram_free);
+
+#if 0
+void *omap_vram_reserve(unsigned long paddr, size_t size)
+{
+
+	struct region *rm;
+	unsigned start_page;
+	unsigned end_page;
+	unsigned i;
+	void *vaddr;
+
+	size = PAGE_ALIGN(size);
+
+	rm = region_find_region(paddr, size);
+
+	DBG("reserve mem paddr %08lx size %d\n",
+			paddr, size);
+
+	BUG_ON(rm == NULL);
+
+	start_page = (paddr - rm->paddr) >> PAGE_SHIFT;
+	end_page = start_page + (size >> PAGE_SHIFT);
+	for (i = start_page; i < end_page; i++)
+		region_reserve_page(rm, i);
+
+	vaddr = rm->vaddr + (start_page << PAGE_SHIFT);
+
+	return vaddr;
+}
+EXPORT_SYMBOL(omap_vram_reserve);
+#endif
+static void *_omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr)
+{
+	struct vram_region *rm;
+	struct vram_alloc *alloc;
+	void *vaddr;
+
+	list_for_each_entry(rm, &region_list, list) {
+		unsigned long start, end;
+
+		DBG("checking region %lx %d\n", rm->paddr, rm->pages);
+
+		if (region_mem_type(rm->paddr) != mtype)
+			continue;
+
+		start = rm->paddr;
+
+		list_for_each_entry(alloc, &rm->alloc_list, list) {
+			end = alloc->paddr;
+
+			if (end - start >= pages << PAGE_SHIFT)
+				goto found;
+
+			start = alloc->paddr + (alloc->pages << PAGE_SHIFT);
+		}
+
+		end = rm->paddr + (rm->pages << PAGE_SHIFT);
+found:
+		if (end - start < pages << PAGE_SHIFT)
+			continue;
+
+		DBG("FOUND %lx, end %lx\n", start, end);
+
+		if (omap_vram_create_allocation(rm, start, pages) == NULL)
+			return NULL;
+
+		*paddr = start;
+		vaddr = rm->vaddr + (start - rm->paddr);
+
+		return vaddr;
+	}
+
+	return NULL;
+}
+
+static void *_omap_vram_alloc_dma(unsigned pages, unsigned long *paddr)
+{
+	struct vram_region *rm;
+	void *vaddr;
+
+	vaddr = dma_alloc_writecombine(NULL, pages << PAGE_SHIFT,
+			(dma_addr_t *)paddr, GFP_KERNEL);
+
+	if (vaddr == NULL)
+		return NULL;
+
+	rm = omap_vram_create_region(*paddr, vaddr, pages);
+	if (rm == NULL) {
+		dma_free_writecombine(NULL, pages << PAGE_SHIFT, vaddr,
+				(dma_addr_t)*paddr);
+		return NULL;
+	}
+
+	rm->dma_alloced = 1;
+
+	if (omap_vram_create_allocation(rm, *paddr, pages) == NULL) {
+		dma_free_writecombine(NULL, pages << PAGE_SHIFT, vaddr,
+				(dma_addr_t)*paddr);
+		kfree(rm);
+		return NULL;
+	}
+
+	list_add(&rm->list, &region_list);
+
+	return vaddr;
+}
+
+void *omap_vram_alloc(int mtype, size_t size, unsigned long *paddr)
+{
+	void *vaddr;
+	unsigned pages;
+
+	BUG_ON(mtype > OMAPFB_MEMTYPE_MAX || !size);
+
+	DBG("alloc mem type %d size %d\n", mtype, size);
+
+	size = PAGE_ALIGN(size);
+	pages = size >> PAGE_SHIFT;
+
+	mutex_lock(&region_mutex);
+
+	vaddr = _omap_vram_alloc(mtype, pages, paddr);
+
+	if (vaddr == NULL && mtype == OMAPFB_MEMTYPE_SDRAM) {
+		DBG("fallback to dma_alloc\n");
+
+		vaddr = _omap_vram_alloc_dma(pages, paddr);
+	}
+
+	mutex_unlock(&region_mutex);
+
+	return vaddr;
+}
+EXPORT_SYMBOL(omap_vram_alloc);
+
+#ifdef CONFIG_PROC_FS
+static void *r_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct list_head *l = v;
+
+	(*pos)++;
+
+	if (list_is_last(l, &region_list))
+		return 0;
+
+	return l->next;
+}
+
+static void *r_start(struct seq_file *m, loff_t *pos)
+{
+	loff_t p = *pos;
+	struct list_head *l = &region_list;
+
+	mutex_lock(&region_mutex);
+
+	do {
+		l = l->next;
+		if (l == &region_list)
+			return NULL;
+	} while (p--);
+
+	return l;
+}
+
+static void r_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&region_mutex);
+}
+
+static int r_show(struct seq_file *m, void *v)
+{
+	struct vram_region *vr;
+	struct vram_alloc *va;
+	unsigned size;
+
+	vr = list_entry(v, struct vram_region, list);
+
+	size = vr->pages << PAGE_SHIFT;
+	seq_printf(m, "%08lx-%08lx v:%p-%p (%d bytes) %s\n",
+			vr->paddr, vr->paddr + size,
+			vr->vaddr, vr->vaddr + size,
+			size,
+			vr->dma_alloced ? "dma_alloc" : "");
+
+	list_for_each_entry(va, &vr->alloc_list, list) {
+		size = va->pages << PAGE_SHIFT;
+		seq_printf(m, "    %08lx-%08lx (%d bytes)\n",
+				va->paddr, va->paddr + size,
+				size);
+	}
+
+
+
+	return 0;
+}
+
+static const struct seq_operations resource_op = {
+	.start	= r_start,
+	.next	= r_next,
+	.stop	= r_stop,
+	.show	= r_show,
+};
+
+static int vram_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &resource_op);
+}
+
+static const struct file_operations proc_vram_operations = {
+	.open		= vram_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int __init omap_vram_create_proc(void)
+{
+	proc_create("omap-vram", 0, NULL, &proc_vram_operations);
+
+	return 0;
+}
+#endif
+
+static __init int omap_vram_init(void)
+{
+	int i, r;
+
+	for (i = 0; i < postponed_cnt; i++)
+		omap_vram_add_region(postponed_regions[i].paddr,
+				postponed_regions[i].size);
+
+#ifdef CONFIG_PROC_FS
+	r = omap_vram_create_proc();
+	if (r)
+		return -ENOMEM;
+#endif
+
+	return 0;
+}
+
+arch_initcall(omap_vram_init);
+
+/* boottime vram alloc stuff */
+static u32 omapfb_sram_vram_start __initdata;
+static u32 omapfb_sram_vram_size __initdata;
+
+static u32 omapfb_sdram_vram_start __initdata;
+static u32 omapfb_sdram_vram_size __initdata;
+
+static u32 omapfb_def_sdram_vram_size __initdata;
+
+static void __init omapfb_early_vram(char **p)
+{
+	unsigned long size;
+	size  = memparse(*p, p);
+	omapfb_def_sdram_vram_size = size;
+}
+__early_param("vram=", omapfb_early_vram);
+
+/*
+ * Called from map_io. We need to call to this early enough so that we
+ * can reserve the fixed SDRAM regions before VM could get hold of them.
+ */
+void __init omapfb_reserve_sdram(void)
+{
+	struct bootmem_data	*bdata;
+	unsigned long		sdram_start, sdram_size;
+	unsigned long		reserved;
+	u32 paddr;
+	u32 size;
+
+	bdata = NODE_DATA(0)->bdata;
+	sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
+	sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
+	reserved = 0;
+
+	/* cmdline arg overrides the board file definition */
+	if (omapfb_def_sdram_vram_size) {
+		size = omapfb_def_sdram_vram_size;
+		paddr = 0;
+	} else {
+		size = omapfb_sdram_vram_size;
+		paddr = omapfb_sdram_vram_start;
+	}
+
+	if (size) {
+		if (paddr) {
+			if (paddr < sdram_start ||
+					paddr + size > sdram_start + sdram_size) {
+				printk(KERN_ERR "Illegal SDRAM region for VRAM\n");
+				return;
+			}
+
+			reserve_bootmem(paddr, size, BOOTMEM_DEFAULT);
+		} else {
+			if (size > sdram_size) {
+				printk(KERN_ERR "Illegal SDRAM size for VRAM\n");
+				return;
+			}
+
+			paddr = virt_to_phys(alloc_bootmem(size));
+		}
+
+		reserved += size;
+		omap_vram_add_region_postponed(paddr, size);
+	}
+
+	if (reserved)
+		pr_info("Reserving %lu bytes SDRAM for VRAM\n", reserved);
+}
+
+/*
+ * Called at sram init time, before anything is pushed to the SRAM stack.
+ * Because of the stack scheme, we will allocate everything from the
+ * start of the lowest address region to the end of SRAM. This will also
+ * include padding for page alignment and possible holes between regions.
+ *
+ * As opposed to the SDRAM case, we'll also do any dynamic allocations at
+ * this point, since the driver built as a module would have problem with
+ * freeing / reallocating the regions.
+ */
+unsigned long __init omapfb_reserve_sram(unsigned long sram_pstart,
+				  unsigned long sram_vstart,
+				  unsigned long sram_size,
+				  unsigned long pstart_avail,
+				  unsigned long size_avail)
+{
+	unsigned long			pend_avail;
+	unsigned long			reserved;
+	u32 paddr;
+	u32 size;
+
+	paddr = omapfb_sram_vram_start;
+	size = omapfb_sram_vram_size;
+
+	reserved = 0;
+	pend_avail = pstart_avail + size_avail;
+
+
+	if (!paddr) {
+		/* Dynamic allocation */
+		if ((size_avail & PAGE_MASK) < size) {
+			printk(KERN_ERR "Not enough SRAM for VRAM\n");
+			return 0;
+		}
+		size_avail = (size_avail - size) & PAGE_MASK;
+		paddr = pstart_avail + size_avail;
+	}
+
+	if (paddr < sram_pstart ||
+			paddr + size > sram_pstart + sram_size) {
+		printk(KERN_ERR "Illegal SRAM region for VRAM\n");
+		return 0;
+	}
+
+	/* Reserve everything above the start of the region. */
+	if (pend_avail - paddr > reserved)
+		reserved = pend_avail - paddr;
+	size_avail = pend_avail - reserved - pstart_avail;
+
+	/*
+	 * We have a kernel mapping for this already, so the
+	 * driver won't have to make one.
+	 */
+	/* XXX do we need the vaddr? */
+	/* rg.vaddr = (void *)(sram_vstart + paddr - sram_pstart); */
+
+	omap_vram_add_region_postponed(paddr, size);
+
+	if (reserved)
+		pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved);
+
+	return reserved;
+}
+
+void __init omap2_set_sdram_vram(u32 size, u32 start)
+{
+	omapfb_sdram_vram_start = start;
+	omapfb_sdram_vram_size = size;
+}
+
+void __init omap2_set_sram_vram(u32 size, u32 start)
+{
+	omapfb_sram_vram_start = start;
+	omapfb_sram_vram_size = size;
+}
+
+#endif
+
diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c
index 3746222..ee2cc6f 100644
--- a/arch/arm/plat-omap/fb.c
+++ b/arch/arm/plat-omap/fb.c
@@ -327,6 +327,28 @@ static inline int omap_init_fb(void)
 
 arch_initcall(omap_init_fb);
 
+#elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
+
+static u64 omap_fb_dma_mask = ~(u32)0;
+
+static struct platform_device omap_fb_device = {
+	.name		= "omapfb",
+	.id		= -1,
+	.dev = {
+		.dma_mask		= &omap_fb_dma_mask,
+		.coherent_dma_mask	= ~(u32)0,
+		.platform_data		= NULL,
+	},
+	.num_resources = 0,
+};
+
+static inline int omap_init_fb(void)
+{
+	return platform_device_register(&omap_fb_device);
+}
+
+arch_initcall(omap_init_fb);
+
 #else
 
 void omapfb_reserve_sdram(void) {}
diff --git a/arch/arm/plat-omap/include/mach/omapfb.h b/arch/arm/plat-omap/include/mach/omapfb.h
index b226bdf..0800f92 100644
--- a/arch/arm/plat-omap/include/mach/omapfb.h
+++ b/arch/arm/plat-omap/include/mach/omapfb.h
@@ -90,6 +90,13 @@ enum omapfb_color_format {
 	OMAPFB_COLOR_CLUT_1BPP,
 	OMAPFB_COLOR_RGB444,
 	OMAPFB_COLOR_YUY422,
+
+	OMAPFB_COLOR_ARGB16,
+	OMAPFB_COLOR_RGB24U,	/* RGB24, 32-bit container */
+	OMAPFB_COLOR_RGB24P,	/* RGB24, 24-bit container */
+	OMAPFB_COLOR_ARGB32,
+	OMAPFB_COLOR_RGBA32,
+	OMAPFB_COLOR_RGBX32,
 };
 
 struct omapfb_update_window {
@@ -393,6 +400,13 @@ extern int  omapfb_update_window_async(struct fb_info *fbi,
 /* in arch/arm/plat-omap/fb.c */
 extern void omapfb_set_ctrl_platform_data(void *pdata);
 
+/* in arch/arm/plat-omap/fb-vram */
+int omap_vram_free(unsigned long paddr, void *vaddr, size_t size);
+void *omap_vram_reserve(unsigned long paddr, size_t size);
+void *omap_vram_alloc(int mtype, size_t size, unsigned long *paddr);
+extern void omap2_set_sdram_vram(u32 size, u32 start);
+extern void omap2_set_sram_vram(u32 size, u32 start);
+
 #endif /* __KERNEL__ */
 
 #endif /* __OMAPFB_H */
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3f3ce13..689a3b1 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2116,6 +2116,7 @@ config FB_PRE_INIT_FB
 	  the bootloader.
 
 source "drivers/video/omap/Kconfig"
+source "drivers/video/omap2/Kconfig"
 
 source "drivers/video/backlight/Kconfig"
 source "drivers/video/display/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e39e33e..3d9d50e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FB_SM501)            += sm501fb.o
 obj-$(CONFIG_FB_XILINX)           += xilinxfb.o
 obj-$(CONFIG_FB_SH_MOBILE_LCDC)	  += sh_mobile_lcdcfb.o
 obj-$(CONFIG_FB_OMAP)             += omap/
+obj-$(CONFIG_OMAP2_DSS)           += omap2/
 obj-$(CONFIG_XEN_FBDEV_FRONTEND)  += xen-fbfront.o
 obj-$(CONFIG_FB_CARMINE)          += carminefb.o
 obj-$(CONFIG_FB_MB862XX)	  += mb862xx/
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig
index c355b59..541fab3 100644
--- a/drivers/video/omap/Kconfig
+++ b/drivers/video/omap/Kconfig
@@ -1,6 +1,7 @@
 config FB_OMAP
 	tristate "OMAP frame buffer support (EXPERIMENTAL)"
-	depends on FB && ARCH_OMAP
+	depends on FB && ARCH_OMAP && (OMAP2_DSS = "n")
+
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
@@ -80,7 +81,7 @@ config FB_OMAP_BOOTLOADER_INIT
 
 config FB_OMAP_CONSISTENT_DMA_SIZE
 	int "Consistent DMA memory size (MB)"
-	depends on FB_OMAP
+	depends on FB && ARCH_OMAP
 	range 1 14
 	default 2
 	help
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
new file mode 100644
index 0000000..8be51a3
--- /dev/null
+++ b/drivers/video/omap2/Kconfig
@@ -0,0 +1,42 @@
+config FB_OMAP2
+        tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)"
+        depends on FB && OMAP2_DSS
+
+        select FB_CFB_FILLRECT
+        select FB_CFB_COPYAREA
+        select FB_CFB_IMAGEBLIT
+        help
+          Frame buffer driver for OMAP2/3 based boards.
+
+config FB_OMAP2_DEBUG
+	bool "Debug support for OMAP2/3 FB"
+	default y
+	depends on FB_OMAP2
+	help
+	  Support for debug output. You have to enable the actual printing
+	  with debug module parameter.
+
+config FB_OMAP2_FORCE_AUTO_UPDATE
+	bool "Force main display to automatic update mode"
+	depends on FB_OMAP2
+	help
+	  Forces main display to automatic update mode (if possible),
+	  and also enables tearsync (if possible). By default
+	  displays that support manual update are started in manual
+	  update mode.
+
+config FB_OMAP2_NUM_FBS
+	int "Number of framebuffers"
+	range 1 10
+	default 3
+	depends on FB_OMAP2
+	help
+	  Select the number of framebuffers created. OMAP2/3 has 3 overlays
+	  so normally this would be 3.
+
+menu "OMAP2/3 Display Device Drivers"
+        depends on OMAP2_DSS
+
+
+endmenu
+
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
new file mode 100644
index 0000000..51c2e00
--- /dev/null
+++ b/drivers/video/omap2/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FB_OMAP2) += omapfb.o
+omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o
diff --git a/drivers/video/omap2/omapfb-ioctl.c b/drivers/video/omap2/omapfb-ioctl.c
new file mode 100644
index 0000000..0cb0370
--- /dev/null
+++ b/drivers/video/omap2/omapfb-ioctl.c
@@ -0,0 +1,460 @@
+/*
+ * linux/drivers/video/omap2/omapfb-ioctl.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#include "omapfb.h"
+
+static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+	struct omap_overlay *ovl;
+	int r = 0;
+
+	DBG("omapfb_setup_plane\n");
+
+	omapfb_lock(fbdev);
+
+	if (ofbi->num_overlays != 1) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	/* XXX uses only the first overlay */
+	ovl = ofbi->overlays[0];
+
+	if (pi->enabled && !ofbi->region.size) {
+		/*
+		 * This plane's memory was freed, can't enable it
+		 * until it's reallocated.
+		 */
+		r = -EINVAL;
+		goto out;
+	}
+
+	if (pi->enabled) {
+		r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
+				pi->out_width, pi->out_height);
+		if (r)
+			goto out;
+	}
+
+	ovl->enable(ovl, pi->enabled);
+
+	if (ovl->manager)
+		ovl->manager->apply(ovl->manager);
+
+	if (display) {
+		if (display->sync)
+			display->sync(display);
+
+		if (display->update)
+			display->update(display, 0, 0,
+					display->panel->timings.x_res,
+					display->panel->timings.y_res);
+	}
+
+out:
+	omapfb_unlock(fbdev);
+	if (r)
+		dev_err(fbdev->dev, "setup_plane failed\n");
+	return r;
+}
+
+static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+
+	omapfb_lock(fbdev);
+
+	if (ofbi->num_overlays != 1) {
+		memset(pi, 0, sizeof(*pi));
+	} else {
+		struct omap_overlay_info *ovli;
+		struct omap_overlay *ovl;
+
+		ovl = ofbi->overlays[0];
+		ovli = &ovl->info;
+
+		pi->pos_x = ovli->pos_x;
+		pi->pos_y = ovli->pos_y;
+		pi->enabled = ovli->enabled;
+		pi->channel_out = 0; /* xxx */
+		pi->mirror = 0;
+		pi->out_width = ovli->out_width;
+		pi->out_height = ovli->out_height;
+	}
+
+	omapfb_unlock(fbdev);
+
+	return 0;
+}
+
+static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omapfb_mem_region *rg;
+	struct omap_display *display = fb2display(fbi);
+	int r, i;
+	size_t size;
+
+	if (mi->type > OMAPFB_MEMTYPE_MAX)
+		return -EINVAL;
+
+	size = PAGE_ALIGN(mi->size);
+
+	rg = &ofbi->region;
+
+	omapfb_lock(fbdev);
+
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		if (ofbi->overlays[i]->info.enabled) {
+			r = -EBUSY;
+			goto out;
+		}
+	}
+
+	if (rg->size != size || rg->type != mi->type) {
+		struct fb_var_screeninfo new_var;
+		unsigned long old_size = rg->size;
+
+		if (display->sync)
+			display->sync(display);
+
+		r = omapfb_realloc_fbmem(fbdev, ofbi->id, size);
+		if (r)
+			goto out;
+
+		if (old_size != size) {
+			if (size) {
+				memcpy(&new_var, &fbi->var, sizeof(new_var));
+				r = check_fb_var(fbi, &new_var);
+				if (r < 0)
+					goto out;
+				memcpy(&fbi->var, &new_var, sizeof(fbi->var));
+				set_fb_fix(fbi);
+			} else {
+				/*
+				 * Set these explicitly to indicate that the
+				 * plane memory is dealloce'd, the other
+				 * screen parameters in var / fix are invalid.
+				 */
+				fbi->fix.smem_start = 0;
+				fbi->fix.smem_len = 0;
+			}
+		}
+	}
+
+	r = 0;
+out:
+	omapfb_unlock(fbdev);
+
+	return r;
+}
+
+static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omapfb_mem_region *rg;
+
+	rg = &ofbi->region;
+	memset(mi, 0, sizeof(*mi));
+
+	omapfb_lock(fbdev);
+	mi->size = rg->size;
+	mi->type = rg->type;
+	omapfb_unlock(fbdev);
+
+	return 0;
+}
+
+static int omapfb_update_window(struct fb_info *fbi,
+		u32 x, u32 y, u32 w, u32 h)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+
+	if (!display)
+		return 0;
+
+	if (w == 0 || h == 0)
+		return 0;
+
+	if (x + w > display->panel->timings.x_res ||
+			y + h > display->panel->timings.y_res)
+		return -EINVAL;
+
+	omapfb_lock(fbdev);
+	display->update(display, x, y, w, h);
+	omapfb_unlock(fbdev);
+
+	return 0;
+}
+
+static int omapfb_set_update_mode(struct fb_info *fbi,
+				   enum omapfb_update_mode mode)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+	enum omap_dss_update_mode um;
+	int r;
+
+	if (!display || !display->set_update_mode)
+		return -EINVAL;
+
+	switch (mode) {
+	case OMAPFB_UPDATE_DISABLED:
+		um = OMAP_DSS_UPDATE_DISABLED;
+		break;
+
+	case OMAPFB_AUTO_UPDATE:
+		um = OMAP_DSS_UPDATE_AUTO;
+		break;
+
+	case OMAPFB_MANUAL_UPDATE:
+		um = OMAP_DSS_UPDATE_MANUAL;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	omapfb_lock(fbdev);
+	r = display->set_update_mode(display, um);
+	omapfb_unlock(fbdev);
+
+	return r;
+}
+
+static int omapfb_get_update_mode(struct fb_info *fbi,
+		enum omapfb_update_mode *mode)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+	enum omap_dss_update_mode m;
+
+	if (!display || !display->get_update_mode)
+		return -EINVAL;
+
+	omapfb_lock(fbdev);
+	m = display->get_update_mode(display);
+	omapfb_unlock(fbdev);
+
+	switch (m) {
+	case OMAP_DSS_UPDATE_DISABLED:
+		*mode = OMAPFB_UPDATE_DISABLED;
+		break;
+	case OMAP_DSS_UPDATE_AUTO:
+		*mode = OMAPFB_AUTO_UPDATE;
+		break;
+	case OMAP_DSS_UPDATE_MANUAL:
+		*mode = OMAPFB_MANUAL_UPDATE;
+		break;
+	default:
+		BUG();
+	}
+
+	return 0;
+}
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+
+	union {
+		struct omapfb_update_window_old	uwnd_o;
+		struct omapfb_update_window	uwnd;
+		struct omapfb_plane_info	plane_info;
+		struct omapfb_caps		caps;
+		struct omapfb_mem_info          mem_info;
+		enum omapfb_update_mode		update_mode;
+		int test_num;
+	} p;
+
+	int r = 0;
+
+	DBG("ioctl %x (%d)\n", cmd, cmd & 0xff);
+
+	switch (cmd) {
+	case OMAPFB_SYNC_GFX:
+		if (!display || !display->sync) {
+			/* DSS1 never returns an error here, so we neither */
+			/*r = -EINVAL;*/
+			break;
+		}
+
+		omapfb_lock(fbdev);
+		r = display->sync(display);
+		omapfb_unlock(fbdev);
+		break;
+
+	case OMAPFB_UPDATE_WINDOW_OLD:
+		if (!display || !display->update) {
+			r = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(&p.uwnd_o,
+					(void __user *)arg,
+					sizeof(p.uwnd_o))) {
+			r = -EFAULT;
+			break;
+		}
+
+		r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y,
+				p.uwnd_o.width, p.uwnd_o.height);
+		break;
+
+	case OMAPFB_UPDATE_WINDOW:
+		if (!display || !display->update) {
+			r = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(&p.uwnd, (void __user *)arg,
+					sizeof(p.uwnd))) {
+			r = -EFAULT;
+			break;
+		}
+
+		r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y,
+				p.uwnd.width, p.uwnd.height);
+		break;
+
+	case OMAPFB_SETUP_PLANE:
+		if (copy_from_user(&p.plane_info, (void __user *)arg,
+					sizeof(p.plane_info)))
+			r = -EFAULT;
+		else
+			r = omapfb_setup_plane(fbi, &p.plane_info);
+		break;
+
+	case OMAPFB_QUERY_PLANE:
+		r = omapfb_query_plane(fbi, &p.plane_info);
+		if (r < 0)
+			break;
+		if (copy_to_user((void __user *)arg, &p.plane_info,
+					sizeof(p.plane_info)))
+			r = -EFAULT;
+		break;
+
+	case OMAPFB_SETUP_MEM:
+		if (copy_from_user(&p.mem_info, (void __user *)arg,
+					sizeof(p.mem_info)))
+			r = -EFAULT;
+		else
+			r = omapfb_setup_mem(fbi, &p.mem_info);
+		break;
+
+	case OMAPFB_QUERY_MEM:
+		r = omapfb_query_mem(fbi, &p.mem_info);
+		if (r < 0)
+			break;
+		if (copy_to_user((void __user *)arg, &p.mem_info,
+					sizeof(p.mem_info)))
+			r = -EFAULT;
+		break;
+
+	case OMAPFB_GET_CAPS:
+		if (!display) {
+			r = -EINVAL;
+			break;
+		}
+
+		p.caps.ctrl = display->caps;
+
+		if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
+			r = -EFAULT;
+		break;
+
+	case OMAPFB_SET_UPDATE_MODE:
+		if (get_user(p.update_mode, (int __user *)arg))
+			r = -EFAULT;
+		else
+			r = omapfb_set_update_mode(fbi, p.update_mode);
+		break;
+
+	case OMAPFB_GET_UPDATE_MODE:
+		r = omapfb_get_update_mode(fbi, &p.update_mode);
+		if (r)
+			break;
+		if (put_user(p.update_mode,
+					(enum omapfb_update_mode __user *)arg))
+			r = -EFAULT;
+		break;
+
+	/* LCD and CTRL tests do the same thing for backward
+	 * compatibility */
+	case OMAPFB_LCD_TEST:
+		if (get_user(p.test_num, (int __user *)arg)) {
+			r = -EFAULT;
+			break;
+		}
+		if (!display || !display->run_test) {
+			r = -EINVAL;
+			break;
+		}
+
+		r = display->run_test(display, p.test_num);
+
+		break;
+
+	case OMAPFB_CTRL_TEST:
+		if (get_user(p.test_num, (int __user *)arg)) {
+			r = -EFAULT;
+			break;
+		}
+		if (!display || !display->run_test) {
+			r = -EINVAL;
+			break;
+		}
+
+		r = display->run_test(display, p.test_num);
+
+		break;
+
+	default:
+		DBG("ioctl unhandled\n");
+		r = -EINVAL;
+	}
+
+	return r;
+}
+
+
diff --git a/drivers/video/omap2/omapfb-main.c b/drivers/video/omap2/omapfb-main.c
new file mode 100644
index 0000000..d043c43
--- /dev/null
+++ b/drivers/video/omap2/omapfb-main.c
@@ -0,0 +1,1442 @@
+/*
+ * linux/drivers/video/omap2/omapfb-main.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#include "omapfb.h"
+
+#define MODULE_NAME     "omapfb"
+
+static char *def_mode;
+static char *def_vram;
+
+#ifdef DEBUG
+unsigned int omapfb_debug;
+module_param_named(debug, omapfb_debug, bool, 0644);
+#endif
+
+#ifdef DEBUG
+static void fill_fb(void *addr, struct fb_info *fbi)
+{
+	struct fb_var_screeninfo *var = &fbi->var;
+
+	const short w = var->xres_virtual;
+	const short h = var->yres_virtual;
+
+	int y, x;
+	u8 *p = addr;
+
+	for (y = 0; y < h; y++) {
+		for (x = 0; x < w; x++) {
+			if (var->bits_per_pixel == 16) {
+				u16 *pw = (u16 *)p;
+
+				if (x < 20 && y < 20)
+					*pw = 0xffff;
+				else if (x == 20 || x == w - 20 ||
+						y == 20 || y == h - 20)
+					*pw = 0xffff;
+				else if (x == y || w - x == h - y)
+					*pw = ((1<<5)-1)<<11;
+				else if (w - x == y || x == h - y)
+					*pw = ((1<<6)-1)<<5;
+				else {
+					int t = x / (w/3);
+					if (t == 0)
+						*pw = y % 32;
+					else if (t == 1)
+						*pw = (y % 64) << 5;
+					else if (t == 2)
+						*pw = (y % 32) << 11;
+				}
+			} else if (var->bits_per_pixel == 24) {
+				u8 *pb = (u8 *)p;
+
+				int r = 0, g = 0, b = 0;
+
+				if (x < 20 && y < 20)
+					r = g = b = 0xff;
+				else if (x == 20 || x == w - 20 ||
+						y == 20 || y == h - 20)
+					r = g = b = 0xff;
+				else if (x == y || w - x == h - y)
+					r = 0xff;
+				else if (w - x == y || x == h - y)
+					g = 0xff;
+				else {
+					int q = x / (w / 3);
+					u8 base = 255 - (y % 256);
+					if (q == 0)
+						r = base;
+					else if (q == 1)
+						g = base;
+					else if (q == 2)
+						b = base;
+				}
+
+				pb[0] = b;
+				pb[1] = g;
+				pb[2] = r;
+
+			} else if (var->bits_per_pixel == 32) {
+				u32 *pd = (u32 *)p;
+
+				if (x < 20 && y < 20)
+					*pd = 0xffffff;
+				else if (x == 20 || x == w - 20 ||
+						y == 20 || y == h - 20)
+					*pd = 0xffffff;
+				else if (x == y || w - x == h - y)
+					*pd = 0xff0000;
+				else if (w - x == y || x == h - y)
+					*pd = 0x00ff00;
+				else {
+					u8 base = 255 - (y % 256);
+					*pd = base << ((x / (w/3)) << 3);
+				}
+			}
+
+			p += var->bits_per_pixel >> 3;
+		}
+	}
+}
+#endif
+
+static enum omap_color_mode fb_mode_to_dss_mode(struct fb_var_screeninfo *var)
+{
+	switch (var->nonstd) {
+	case 0:
+		break;
+	case OMAPFB_COLOR_YUV422:
+		return OMAP_DSS_COLOR_UYVY;
+
+	case OMAPFB_COLOR_YUY422:
+		return OMAP_DSS_COLOR_YUV2;
+
+	case OMAPFB_COLOR_ARGB16:
+		return OMAP_DSS_COLOR_ARGB16;
+
+	case OMAPFB_COLOR_ARGB32:
+		return OMAP_DSS_COLOR_ARGB32;
+
+	case OMAPFB_COLOR_RGBA32:
+		return OMAP_DSS_COLOR_RGBA32;
+
+	case OMAPFB_COLOR_RGBX32:
+		return OMAP_DSS_COLOR_RGBX32;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (var->bits_per_pixel) {
+	case 1:
+		return OMAP_DSS_COLOR_CLUT1;
+	case 2:
+		return OMAP_DSS_COLOR_CLUT2;
+	case 4:
+		return OMAP_DSS_COLOR_CLUT4;
+	case 8:
+		return OMAP_DSS_COLOR_CLUT8;
+	case 12:
+		return OMAP_DSS_COLOR_RGB12U;
+	case 16:
+		return OMAP_DSS_COLOR_RGB16;
+	case 24:
+		return OMAP_DSS_COLOR_RGB24P;
+	case 32:
+		return OMAP_DSS_COLOR_RGB24U;
+	default:
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+void set_fb_fix(struct fb_info *fbi)
+{
+	struct fb_fix_screeninfo *fix = &fbi->fix;
+	struct fb_var_screeninfo *var = &fbi->var;
+	struct omapfb_mem_region *rg = &FB2OFB(fbi)->region;
+
+	DBG("set_fb_fix\n");
+
+	/* used by open/write in fbmem.c */
+	fbi->screen_base        = (char __iomem *)rg->vaddr;
+
+	/* used by mmap in fbmem.c */
+	fix->smem_start         = rg->paddr;
+	fix->smem_len           = rg->size;
+
+	fix->type = FB_TYPE_PACKED_PIXELS;
+
+	if (var->nonstd)
+		fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	else {
+		switch (var->bits_per_pixel) {
+		case 32:
+		case 24:
+		case 16:
+		case 12:
+			fix->visual = FB_VISUAL_TRUECOLOR;
+			/* 12bpp is stored in 16 bits */
+			break;
+		case 1:
+		case 2:
+		case 4:
+		case 8:
+			fix->visual = FB_VISUAL_PSEUDOCOLOR;
+			break;
+		}
+	}
+
+	fix->accel = FB_ACCEL_NONE;
+	fix->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
+
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+}
+
+/* check new var and possibly modify it to be ok */
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omap_display *display = fb2display(fbi);
+	unsigned long max_frame_size;
+	unsigned long line_size;
+	int xres_min, xres_max;
+	int yres_min, yres_max;
+	enum omap_color_mode mode = 0;
+	struct omap_overlay *ovl;
+
+	DBG("check_fb_var %d\n", ofbi->id);
+
+	if (ofbi->region.size == 0) {
+		memset(var, 0, sizeof(*var));
+		return 0;
+	}
+
+	if (ofbi->num_overlays == 0) {
+		dev_err(ofbi->fbdev->dev, "no overlays, aborting\n");
+		return -EINVAL;
+	}
+
+	/* XXX: uses the first overlay */
+	ovl = ofbi->overlays[0];
+
+	/* if we are using non standard mode, fix the bpp first */
+	switch (var->nonstd) {
+	case 0:
+		break;
+	case OMAPFB_COLOR_YUV422:
+	case OMAPFB_COLOR_YUY422:
+	case OMAPFB_COLOR_ARGB16:
+		var->bits_per_pixel = 16;
+		break;
+	case OMAPFB_COLOR_ARGB32:
+	case OMAPFB_COLOR_RGBA32:
+	case OMAPFB_COLOR_RGBX32:
+		var->bits_per_pixel = 32;
+		break;
+	default:
+		DBG("invalid nonstd mode\n");
+		return -EINVAL;
+	}
+
+	mode = fb_mode_to_dss_mode(var);
+	if (mode < 0) {
+		DBG("cannot convert var to omap dss mode\n");
+		return -EINVAL;
+	}
+
+	if ((ovl->supported_modes & mode) == 0) {
+		DBG("invalid mode\n");
+		return -EINVAL;
+	}
+
+	xres_min = OMAPFB_PLANE_XRES_MIN;
+	xres_max = (display ? display->panel->timings.x_res : 2048) - ovl->info.pos_x;
+	yres_min = OMAPFB_PLANE_YRES_MIN;
+	yres_max = (display ? display->panel->timings.y_res : 2048) - ovl->info.pos_y;
+
+	if (var->xres < xres_min)
+		var->xres = xres_min;
+	if (var->yres < yres_min)
+		var->yres = yres_min;
+	if (var->xres_virtual < var->xres)
+		var->xres_virtual = var->xres;
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+	max_frame_size = ofbi->region.size;
+	line_size = (var->xres_virtual * var->bits_per_pixel) >> 3;
+
+	if (line_size * var->yres_virtual > max_frame_size) {
+		/* Try to keep yres_virtual first */
+		line_size = max_frame_size / var->yres_virtual;
+		var->xres_virtual = line_size * 8 / var->bits_per_pixel;
+		if (var->xres_virtual < var->xres) {
+			/* Still doesn't fit. Shrink yres_virtual too */
+			var->xres_virtual = var->xres;
+			line_size = var->xres * var->bits_per_pixel / 8;
+			var->yres_virtual = max_frame_size / line_size;
+		}
+		/* Recheck this, as the virtual size changed. */
+		if (var->xres_virtual < var->xres)
+			var->xres = var->xres_virtual;
+		if (var->yres_virtual < var->yres)
+			var->yres = var->yres_virtual;
+		if (var->xres < xres_min || var->yres < yres_min) {
+			DBG("Cannot fit FB to memory\n");
+			return -EINVAL;
+		}
+	}
+	if (var->xres + var->xoffset > var->xres_virtual)
+		var->xoffset = var->xres_virtual - var->xres;
+	if (var->yres + var->yoffset > var->yres_virtual)
+		var->yoffset = var->yres_virtual - var->yres;
+
+	if (var->bits_per_pixel == 16) {
+		var->red.offset  = 11; var->red.length   = 5;
+		var->red.msb_right   = 0;
+		var->green.offset = 5;  var->green.length = 6;
+		var->green.msb_right = 0;
+		var->blue.offset = 0;  var->blue.length  = 5;
+		var->blue.msb_right  = 0;
+	} else if (var->bits_per_pixel == 24) {
+		var->red.offset  = 16; var->red.length   = 8;
+		var->red.msb_right   = 0;
+		var->green.offset = 8;  var->green.length = 8;
+		var->green.msb_right = 0;
+		var->blue.offset = 0;  var->blue.length  = 8;
+		var->blue.msb_right  = 0;
+		var->transp.offset = 0; var->transp.length = 0;
+	} else if (var->bits_per_pixel == 32) {
+		var->red.offset  = 16; var->red.length   = 8;
+		var->red.msb_right   = 0;
+		var->green.offset = 8;  var->green.length = 8;
+		var->green.msb_right = 0;
+		var->blue.offset = 0;  var->blue.length  = 8;
+		var->blue.msb_right  = 0;
+		var->transp.offset = 0; var->transp.length = 0;
+	} else {
+		DBG("failed to setup fb color mask\n");
+		return -EINVAL;
+	}
+
+	DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n",
+			var->xres, var->yres,
+			var->xres_virtual, var->yres_virtual);
+
+	var->height             = -1;
+	var->width              = -1;
+	var->grayscale          = 0;
+
+	if (display && display->get_timings) {
+		struct omap_video_timings timings;
+		display->get_timings(display, &timings);
+
+		/* pixclock in ps, the rest in pixclock */
+		var->pixclock = timings.pixel_clock != 0 ?
+			KHZ2PICOS(timings.pixel_clock) :
+			0;
+		var->left_margin = timings.hfp;
+		var->right_margin = timings.hbp;
+		var->upper_margin = timings.vfp;
+		var->lower_margin = timings.vbp;
+		var->hsync_len = timings.hsw;
+		var->vsync_len = timings.vsw;
+	} else {
+		var->pixclock = 0;
+		var->left_margin = 0;
+		var->right_margin = 0;
+		var->upper_margin = 0;
+		var->lower_margin = 0;
+		var->hsync_len = 0;
+		var->vsync_len = 0;
+	}
+
+	/* TODO: get these from panel->config */
+	var->vmode              = FB_VMODE_NONINTERLACED;
+	var->sync               = 0;
+
+	return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * fbdev framework callbacks
+ * ---------------------------------------------------------------------------
+ */
+static int omapfb_open(struct fb_info *fbi, int user)
+{
+	return 0;
+}
+
+static int omapfb_release(struct fb_info *fbi, int user)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+
+	DBG("Closing fb with plane index %d\n", ofbi->id);
+
+	omapfb_lock(fbdev);
+#if 1
+	if (display) {
+		/* XXX Is this really needed ? */
+		if (display->sync)
+			display->sync(display);
+
+		if (display->update)
+			display->update(display,
+					0, 0,
+					display->panel->timings.x_res,
+					display->panel->timings.y_res);
+	}
+#endif
+
+	if (display && display->sync)
+		display->sync(display);
+
+	omapfb_unlock(fbdev);
+
+	return 0;
+}
+
+/* setup overlay according to the fb */
+int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+		int posx, int posy, int outw, int outh)
+{
+	int r = 0;
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct fb_var_screeninfo *var = &fbi->var;
+	enum omap_color_mode mode = 0;
+	int offset;
+	u32 data_start_p;
+	void *data_start_v;
+
+	DBG("setup_overlay %d\n", ofbi->id);
+
+	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
+			(outw != var->xres || outh != var->yres)) {
+		r = -EINVAL;
+		goto err;
+	}
+
+	offset = ((var->yoffset * var->xres_virtual +
+				var->xoffset) * var->bits_per_pixel) >> 3;
+
+	data_start_p = ofbi->region.paddr + offset;
+	data_start_v = ofbi->region.vaddr + offset;
+
+	mode = fb_mode_to_dss_mode(var);
+
+	if (mode == -EINVAL) {
+		r = -EINVAL;
+		goto err;
+	}
+
+	r = ovl->setup_input(ovl,
+			data_start_p, data_start_v,
+			var->xres_virtual,
+			var->xres, var->yres,
+			mode);
+
+	if (r)
+		goto err;
+
+	r = ovl->setup_output(ovl,
+			posx, posy,
+			outw, outh);
+
+	if (r)
+		goto err;
+
+	return 0;
+
+err:
+	DBG("setup_overlay failed\n");
+	return r;
+}
+
+/* apply var to the overlay */
+int omapfb_apply_changes(struct fb_info *fbi, int init)
+{
+	int r = 0;
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct fb_var_screeninfo *var = &fbi->var;
+	struct omap_overlay *ovl;
+	int posx, posy;
+	int outw, outh;
+	int i;
+
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		ovl = ofbi->overlays[i];
+
+		DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);
+
+		if (ofbi->region.size == 0) {
+			/* the fb is not available. disable the overlay */
+			ovl->enable(ovl, 0);
+			if (!init && ovl->manager)
+				ovl->manager->apply(ovl->manager);
+			continue;
+		}
+
+		if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+			outw = var->xres;
+			outh = var->yres;
+		} else {
+			outw = ovl->info.out_width;
+			outh = ovl->info.out_height;
+		}
+
+		if (init) {
+			posx = 0;
+			posy = 0;
+		} else {
+			posx = ovl->info.pos_x;
+			posy = ovl->info.pos_y;
+		}
+
+		r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh);
+		if (r)
+			goto err;
+
+		if (!init && ovl->manager)
+			ovl->manager->apply(ovl->manager);
+	}
+	return 0;
+err:
+	DBG("apply_changes failed\n");
+	return r;
+}
+
+/* checks var and eventually tweaks it to something supported,
+ * DO NOT MODIFY PAR */
+static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+	int r;
+
+	DBG("check_var(%d)\n", FB2OFB(fbi)->id);
+
+	r = check_fb_var(fbi, var);
+
+	return r;
+}
+
+/* set the video mode according to info->var */
+static int omapfb_set_par(struct fb_info *fbi)
+{
+	int r;
+
+	DBG("set_par(%d)\n", FB2OFB(fbi)->id);
+
+	set_fb_fix(fbi);
+	r = omapfb_apply_changes(fbi, 0);
+
+	return r;
+}
+
+static void omapfb_rotate(struct fb_info *fbi, int rotate)
+{
+	DBG("rotate(%d)\n", FB2OFB(fbi)->id);
+	return;
+}
+
+static int omapfb_pan_display(struct fb_var_screeninfo *var,
+		struct fb_info *fbi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	int r = 0;
+
+	DBG("pan_display(%d)\n", ofbi->id);
+
+	omapfb_lock(fbdev);
+
+	if (var->xoffset != fbi->var.xoffset ||
+	    var->yoffset != fbi->var.yoffset) {
+		struct fb_var_screeninfo new_var;
+
+		new_var = fbi->var;
+		new_var.xoffset = var->xoffset;
+		new_var.yoffset = var->yoffset;
+
+		r = check_fb_var(fbi, &new_var);
+
+		if (r == 0) {
+			fbi->var = new_var;
+			set_fb_fix(fbi);
+			r = omapfb_apply_changes(fbi, 0);
+		}
+	}
+
+	omapfb_unlock(fbdev);
+
+	return r;
+}
+
+static void mmap_user_open(struct vm_area_struct *vma)
+{
+	struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+	atomic_inc(&ofbi->map_count);
+}
+
+static void mmap_user_close(struct vm_area_struct *vma)
+{
+	struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+	atomic_dec(&ofbi->map_count);
+}
+
+static struct vm_operations_struct mmap_user_ops = {
+	.open = mmap_user_open,
+	.close = mmap_user_close,
+};
+
+static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb_mem_region *rg = &ofbi->region;
+	unsigned long off;
+	unsigned long start;
+	u32 len;
+
+	if (vma->vm_end - vma->vm_start == 0)
+		return 0;
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+	off = vma->vm_pgoff << PAGE_SHIFT;
+
+	start = rg->paddr;
+	len = rg->size;
+	if (off >= len)
+		return -EINVAL;
+	if ((vma->vm_end - vma->vm_start + off) > len)
+		return -EINVAL;
+	off += start;
+	vma->vm_pgoff = off >> PAGE_SHIFT;
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+	vma->vm_ops = &mmap_user_ops;
+	vma->vm_private_data = ofbi;
+	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -EAGAIN;
+	/* vm_ops.open won't be called for mmap itself. */
+	atomic_inc(&ofbi->map_count);
+	return 0;
+}
+
+/* Store a single color palette entry into a pseudo palette or the hardware
+ * palette if one is available. For now we support only 16bpp and thus store
+ * the entry only to the pseudo palette.
+ */
+static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
+		u_int blue, u_int transp, int update_hw_pal)
+{
+	/*struct omapfb_info *ofbi = FB2OFB(fbi);*/
+	/*struct omapfb2_device *fbdev = ofbi->fbdev;*/
+	struct fb_var_screeninfo *var = &fbi->var;
+	int r = 0;
+
+	enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */
+
+	/*switch (plane->color_mode) {*/
+	switch (mode) {
+	case OMAPFB_COLOR_YUV422:
+	case OMAPFB_COLOR_YUV420:
+	case OMAPFB_COLOR_YUY422:
+		r = -EINVAL;
+		break;
+	case OMAPFB_COLOR_CLUT_8BPP:
+	case OMAPFB_COLOR_CLUT_4BPP:
+	case OMAPFB_COLOR_CLUT_2BPP:
+	case OMAPFB_COLOR_CLUT_1BPP:
+		/*
+		   if (fbdev->ctrl->setcolreg)
+		   r = fbdev->ctrl->setcolreg(regno, red, green, blue,
+		   transp, update_hw_pal);
+		   */
+		/* Fallthrough */
+		r = -EINVAL;
+		break;
+	case OMAPFB_COLOR_RGB565:
+	case OMAPFB_COLOR_RGB444:
+	case OMAPFB_COLOR_RGB24P:
+	case OMAPFB_COLOR_RGB24U:
+		if (r != 0)
+			break;
+
+		if (regno < 0) {
+			r = -EINVAL;
+			break;
+		}
+
+		if (regno < 16) {
+			u16 pal;
+			pal = ((red >> (16 - var->red.length)) <<
+					var->red.offset) |
+				((green >> (16 - var->green.length)) <<
+				 var->green.offset) |
+				(blue >> (16 - var->blue.length));
+			((u32 *)(fbi->pseudo_palette))[regno] = pal;
+		}
+		break;
+	default:
+		BUG();
+	}
+	return r;
+}
+
+static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+		u_int transp, struct fb_info *info)
+{
+	DBG("setcolreg\n");
+
+	return _setcolreg(info, regno, red, green, blue, transp, 1);
+}
+
+static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+	int count, index, r;
+	u16 *red, *green, *blue, *transp;
+	u16 trans = 0xffff;
+
+	DBG("setcmap\n");
+
+	red     = cmap->red;
+	green   = cmap->green;
+	blue    = cmap->blue;
+	transp  = cmap->transp;
+	index   = cmap->start;
+
+	for (count = 0; count < cmap->len; count++) {
+		if (transp)
+			trans = *transp++;
+		r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
+				count == cmap->len - 1);
+		if (r != 0)
+			return r;
+	}
+
+	return 0;
+}
+
+static int omapfb_blank(int blank, struct fb_info *fbi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	struct omap_display *display = fb2display(fbi);
+	int do_update = 0;
+	int r = 0;
+
+	omapfb_lock(fbdev);
+
+	switch (blank) {
+	case FB_BLANK_UNBLANK:
+		if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+			r = -EINVAL;
+			goto exit;
+		}
+
+		if (display->resume)
+			r = display->resume(display);
+
+		if (r == 0 && display->get_update_mode &&
+				display->get_update_mode(display) ==
+				OMAP_DSS_UPDATE_MANUAL)
+			do_update = 1;
+
+		break;
+
+	case FB_BLANK_POWERDOWN:
+		if (display->state != OMAP_DSS_DISPLAY_ACTIVE) {
+			r = -EINVAL;
+			goto exit;
+		}
+
+		if (display->suspend)
+			r = display->suspend(display);
+
+		break;
+
+	default:
+		r = -EINVAL;
+	}
+
+exit:
+	omapfb_unlock(fbdev);
+
+	if (r == 0 && do_update && display->update)
+		r = display->update(display,
+				0, 0,
+				display->panel->timings.x_res,
+				display->panel->timings.y_res);
+
+	return r;
+}
+
+static struct fb_ops omapfb_ops = {
+	.owner          = THIS_MODULE,
+	.fb_open        = omapfb_open,
+	.fb_release     = omapfb_release,
+	.fb_fillrect    = cfb_fillrect,
+	.fb_copyarea    = cfb_copyarea,
+	.fb_imageblit   = cfb_imageblit,
+	.fb_blank       = omapfb_blank,
+	.fb_ioctl       = omapfb_ioctl,
+	.fb_check_var   = omapfb_check_var,
+	.fb_set_par     = omapfb_set_par,
+	.fb_rotate      = omapfb_rotate,
+	.fb_pan_display = omapfb_pan_display,
+	.fb_mmap	= omapfb_mmap,
+	.fb_setcolreg	= omapfb_setcolreg,
+	.fb_setcmap	= omapfb_setcmap,
+};
+
+static void omapfb_free_fbmem(struct omapfb2_device *fbdev, int fbnum)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[fbnum]);
+	struct omapfb_mem_region *rg;
+
+	rg = &ofbi->region;
+
+	if (rg->paddr)
+		if (omap_vram_free(rg->paddr, rg->vaddr, rg->size))
+			dev_err(fbdev->dev, "VRAM FREE failed\n");
+
+	rg->vaddr = NULL;
+	rg->paddr = 0;
+	rg->alloc = 0;
+	rg->size = 0;
+}
+
+static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev)
+{
+	int i;
+
+	DBG("free all fbmem\n");
+
+	for (i = 0; i < fbdev->num_fbs; i++)
+		omapfb_free_fbmem(fbdev, i);
+
+	return 0;
+}
+
+static int omapfb_alloc_fbmem(struct omapfb2_device *fbdev, int fbnum,
+		unsigned long size)
+{
+	struct omapfb_info *ofbi;
+	struct omapfb_mem_region *rg;
+	unsigned long paddr;
+	void *vaddr;
+
+	size = PAGE_ALIGN(size);
+
+	ofbi = FB2OFB(fbdev->fbs[fbnum]);
+	rg = &ofbi->region;
+	memset(rg, 0, sizeof(*rg));
+
+	DBG("allocating %lu bytes for fb %d\n",
+			size, ofbi->id);
+
+	vaddr = omap_vram_alloc(OMAPFB_MEMTYPE_SDRAM, size, &paddr);
+	DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr);
+
+	if (vaddr == NULL) {
+		dev_err(fbdev->dev,
+				"failed to allocate framebuffer\n");
+		return -ENOMEM;
+	}
+
+	rg->paddr = paddr;
+	rg->vaddr = vaddr;
+	rg->size = size;
+	rg->alloc = 1;
+
+	return 0;
+}
+
+int omapfb_realloc_fbmem(struct omapfb2_device *fbdev, int fbnum,
+		unsigned long size)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[fbnum]);
+	struct omapfb_mem_region *rg = &ofbi->region;
+	unsigned old_size = rg->size;
+	int r;
+
+	size = PAGE_ALIGN(size);
+
+	omapfb_free_fbmem(fbdev, fbnum);
+
+	if (size == 0)
+		return 0;
+
+	r = omapfb_alloc_fbmem(fbdev, fbnum, size);
+
+	if (r)
+		omapfb_alloc_fbmem(fbdev, fbnum, old_size);
+
+	return r;
+}
+
+/* allocate fbmem using display resolution as reference */
+static int omapfb_alloc_fbmem_display(struct omapfb2_device *fbdev, int fbnum,
+		unsigned long def_vram)
+{
+	struct omapfb_info *ofbi;
+	struct omap_display *display;
+	int bytespp;
+	unsigned long size;
+
+	ofbi = FB2OFB(fbdev->fbs[fbnum]);
+	display =  fb2display(fbdev->fbs[fbnum]);
+
+	if (!display)
+		return 0;
+
+	switch (display->panel->bpp) {
+	case 16:
+		bytespp = 2;
+		break;
+	case 24:
+	case 32:
+		bytespp = 4;
+		break;
+	default:
+		bytespp = 4;
+		break;
+	}
+
+	if (def_vram)
+		size = def_vram;
+	else
+		size = display->panel->timings.x_res *
+			display->panel->timings.y_res *
+			bytespp;
+
+	return omapfb_alloc_fbmem(fbdev, fbnum, size);
+}
+
+static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
+{
+	int i, r;
+	unsigned long vrams[10];
+
+	memset(vrams, 0, sizeof(vrams));
+
+	if (def_vram) {
+		char str[64];
+		char *tok, *s;
+
+		if (strlen(def_vram) > sizeof(str) - 1) {
+			dev_err(fbdev->dev, "Illegal vram parameters\n");
+			return -EINVAL;
+		}
+
+		strcpy(str, def_vram);
+
+		s = str;
+		i = 0;
+
+		while ((tok = strsep(&s, ","))) {
+			unsigned long size;
+
+			size = memparse(tok, NULL);
+
+			if (size == 0) {
+				dev_err(fbdev->dev, "illegal vram size\n");
+				break;
+			}
+
+			vrams[i++] = size;
+		}
+	}
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		/* allocate memory automatically only for fb0, or if
+		 * excplicitly defined with vram option */
+		if (i == 0 || vrams[i] != 0) {
+			r = omapfb_alloc_fbmem_display(fbdev, i, vrams[i]);
+
+			if (r)
+				return r;
+		}
+	}
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+		struct omapfb_mem_region *rg;
+		rg = &ofbi->region;
+
+		DBG("region%d phys %08x virt %p size=%lu\n",
+				i,
+				rg->paddr,
+				rg->vaddr,
+				rg->size);
+	}
+
+	return 0;
+}
+
+/* initialize fb_info, var, fix to something sane based on the display */
+static int fbinfo_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+	struct fb_var_screeninfo *var = &fbi->var;
+	struct fb_fix_screeninfo *fix = &fbi->fix;
+	struct omap_display *display = fb2display(fbi);
+	int r = 0;
+
+	fbi->fbops = &omapfb_ops;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->pseudo_palette = fbdev->pseudo_palette;
+
+	strncpy(fix->id, MODULE_NAME, sizeof(fix->id));
+
+	var->nonstd = 0;
+
+	if (display) {
+		var->xres = display->panel->timings.x_res;
+		var->yres = display->panel->timings.y_res;
+		var->xres_virtual = var->xres;
+		var->yres_virtual = var->yres;
+		/*        var->rotate       = def_rotate; */
+
+		switch (display->panel->bpp) {
+		case 16:
+			var->bits_per_pixel = 16;
+			break;
+		case 18:
+			var->bits_per_pixel = 16;
+			break;
+		case 24:
+			var->bits_per_pixel = 32;
+			break;
+		default:
+			dev_err(fbdev->dev, "illegal display bpp\n");
+			return -EINVAL;
+		}
+	}
+
+	r = check_fb_var(fbi, var);
+	if (r)
+		goto err;
+
+	set_fb_fix(fbi);
+
+#ifdef DEBUG
+	if (omapfb_debug)
+		fill_fb(FB2OFB(fbi)->region.vaddr, fbi);
+#endif
+err:
+	return r;
+}
+
+static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+	fb_dealloc_cmap(&fbi->cmap);
+}
+
+
+static void omapfb_free_resources(struct omapfb2_device *fbdev)
+{
+	int i;
+
+	DBG("free_resources\n");
+
+	if (fbdev == NULL)
+		return;
+
+	for (i = 0; i < fbdev->num_fbs; i++)
+		unregister_framebuffer(fbdev->fbs[i]);
+
+	/* free the reserved fbmem */
+	omapfb_free_all_fbmem(fbdev);
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		fbinfo_cleanup(fbdev, fbdev->fbs[i]);
+		framebuffer_release(fbdev->fbs[i]);
+	}
+
+	for (i = 0; i < fbdev->num_displays; i++) {
+		if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED)
+			fbdev->displays[i]->disable(fbdev->displays[i]);
+
+		omap_dss_put_display(fbdev->displays[i]);
+	}
+
+	dev_set_drvdata(fbdev->dev, NULL);
+	kfree(fbdev);
+}
+
+static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
+{
+	int r, i;
+
+	fbdev->num_fbs = 0;
+
+	DBG("create %d framebuffers\n",	CONFIG_FB_OMAP2_NUM_FBS);
+
+	/* allocate fb_infos */
+	for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) {
+		struct fb_info *fbi;
+		struct omapfb_info *ofbi;
+
+		fbi = framebuffer_alloc(sizeof(struct omapfb_info),
+				fbdev->dev);
+
+		if (fbi == NULL) {
+			dev_err(fbdev->dev,
+				"unable to allocate memory for plane info\n");
+			return -ENOMEM;
+		}
+
+		fbdev->fbs[i] = fbi;
+
+		ofbi = FB2OFB(fbi);
+		ofbi->fbdev = fbdev;
+		ofbi->id = i;
+		fbdev->num_fbs++;
+	}
+
+	DBG("fb_infos allocated\n");
+
+	/* assign overlays for the fbs */
+	for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+		ofbi->overlays[0] = fbdev->overlays[i];
+		ofbi->num_overlays = 1;
+	}
+
+	/* allocate fb memories */
+	r = omapfb_allocate_all_fbs(fbdev);
+	if (r) {
+		dev_err(fbdev->dev, "failed to allocate fbmem\n");
+		return r;
+	}
+
+	DBG("fbmems allocated\n");
+
+	/* setup fb_infos */
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		r = fbinfo_init(fbdev, fbdev->fbs[i]);
+		if (r) {
+			dev_err(fbdev->dev, "failed to setup fb_info\n");
+			return r;
+		}
+	}
+
+	DBG("fb_infos initialized\n");
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		r = register_framebuffer(fbdev->fbs[i]);
+		if (r != 0) {
+			dev_err(fbdev->dev,
+				"registering framebuffer %d failed\n", i);
+			return r;
+		}
+	}
+
+	DBG("framebuffers registered\n");
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		r = omapfb_apply_changes(fbdev->fbs[i], 1);
+		if (r)
+			dev_err(fbdev->dev, "failed to change mode\n");
+	}
+
+	/* Enable fb0 */
+	if (fbdev->num_fbs > 0) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);
+
+		if (ofbi->num_overlays > 0 ) {
+			struct omap_overlay *ovl = ofbi->overlays[0];
+
+			ovl->enable(ovl, 1);
+		}
+	}
+
+	DBG("create_framebuffers done\n");
+
+	return 0;
+}
+
+int omapfb_mode_to_timings(const char *mode_str,
+		struct omap_video_timings *timings, unsigned *bpp)
+{
+	struct fb_info fbi;
+	struct fb_var_screeninfo var;
+	struct fb_ops fbops;
+	int r;
+
+	/* this is quite a hack, but I wanted to use the modedb and for
+	 * that we need fb_info and var, so we create dummy ones */
+
+	memset(&fbi, 0, sizeof(fbi));
+	memset(&var, 0, sizeof(var));
+	memset(&fbops, 0, sizeof(fbops));
+	fbi.fbops = &fbops;
+
+	r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24);
+
+	if (r != 0) {
+		timings->pixel_clock = PICOS2KHZ(var.pixclock);
+		timings->hfp = var.left_margin;
+		timings->hbp = var.right_margin;
+		timings->vfp = var.upper_margin;
+		timings->vbp = var.lower_margin;
+		timings->hsw = var.hsync_len;
+		timings->vsw = var.vsync_len;
+		timings->x_res = var.xres;
+		timings->y_res = var.yres;
+
+		switch (var.bits_per_pixel) {
+		case 16:
+			*bpp = 16;
+			break;
+		case 24:
+		case 32:
+		default:
+			*bpp = 24;
+			break;
+		}
+
+		return 0;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static int omapfb_probe(struct platform_device *pdev)
+{
+	struct omapfb2_device *fbdev = NULL;
+	int r = 0;
+	int i, t;
+	struct omap_overlay *ovl;
+	struct omap_display *def_display;
+
+	DBG("omapfb_probe\n");
+
+	if (pdev->num_resources != 0) {
+		dev_err(&pdev->dev, "probed for an unknown device\n");
+		r = -ENODEV;
+		goto err0;
+	}
+
+	fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL);
+	if (fbdev == NULL) {
+		r = -ENOMEM;
+		goto err0;
+	}
+
+	mutex_init(&fbdev->mtx);
+
+	fbdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, fbdev);
+
+	fbdev->num_displays = 0;
+	t = omap_dss_get_num_displays();
+	for (i = 0; i < t; i++) {
+		struct omap_display *display;
+		display = omap_dss_get_display(i);
+		if (!display) {
+			dev_err(&pdev->dev, "can't get display %d\n", i);
+			r = -EINVAL;
+			goto cleanup;
+		}
+
+		fbdev->displays[fbdev->num_displays++] = display;
+	}
+
+	if (fbdev->num_displays == 0) {
+		dev_err(&pdev->dev, "no displays\n");
+		r = -EINVAL;
+		goto cleanup;
+	}
+
+	fbdev->num_overlays = omap_dss_get_num_overlays();
+	for (i = 0; i < fbdev->num_overlays; i++)
+		fbdev->overlays[i] = omap_dss_get_overlay(i);
+
+	fbdev->num_managers = omap_dss_get_num_overlay_managers();
+	for (i = 0; i < fbdev->num_managers; i++)
+		fbdev->managers[i] = omap_dss_get_overlay_manager(i);
+
+
+	/* gfx overlay should be the default one. find a display
+	 * connected to that, and use it as default display */
+	ovl = omap_dss_get_overlay(0);
+	if (ovl->manager && ovl->manager->display) {
+		def_display = ovl->manager->display;
+	} else {
+		dev_err(&pdev->dev, "cannot find default display\n");
+		r = -EINVAL;
+		goto cleanup;
+	}
+
+	if (def_mode && strlen(def_mode) > 0)
+	{
+		struct omap_video_timings timings;
+		unsigned bpp;
+
+		if (omapfb_mode_to_timings(def_mode, &timings, &bpp) == 0) {
+			if (def_display->set_timings)
+				def_display->set_timings(def_display, &timings);
+
+			def_display->panel->bpp = bpp;
+		}
+	}
+
+	r = omapfb_create_framebuffers(fbdev);
+	if (r)
+		goto cleanup;
+
+	for (i = 0; i < fbdev->num_managers; i++) {
+		struct omap_overlay_manager *mgr;
+		mgr = fbdev->managers[i];
+		r = mgr->apply(mgr);
+		if (r) {
+			dev_err(fbdev->dev, "failed to apply dispc config\n");
+			goto cleanup;
+		}
+	}
+
+	DBG("mgr->apply'ed\n");
+
+	r = def_display->enable(def_display);
+	if (r) {
+		dev_err(fbdev->dev, "Failed to enable display '%s'\n",
+				def_display->name);
+		goto cleanup;
+	}
+
+	/* set the update mode */
+	if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
+		if (def_display->set_update_mode)
+			def_display->set_update_mode(def_display,
+					OMAP_DSS_UPDATE_AUTO);
+		if (def_display->enable_te)
+			def_display->enable_te(def_display, 1);
+#else
+		if (def_display->set_update_mode)
+			def_display->set_update_mode(def_display,
+					OMAP_DSS_UPDATE_MANUAL);
+		if (def_display->enable_te)
+			def_display->enable_te(def_display, 0);
+#endif
+	} else {
+		if (def_display->set_update_mode)
+			def_display->set_update_mode(def_display,
+					OMAP_DSS_UPDATE_AUTO);
+	}
+
+	for (i = 0; i < fbdev->num_displays; i++) {
+		struct omap_display *display = fbdev->displays[i];
+
+		if (display->update)
+			display->update(display,
+					0, 0,
+					display->panel->timings.x_res,
+					display->panel->timings.y_res);
+	}
+
+	DBG("display->updated\n");
+
+	omapfb_create_sysfs(fbdev);
+	DBG("sysfs created\n");
+
+	return 0;
+
+cleanup:
+	omapfb_free_resources(fbdev);
+err0:
+	dev_err(&pdev->dev, "failed to setup omapfb\n");
+	return r;
+}
+
+static int omapfb_remove(struct platform_device *pdev)
+{
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+
+	/* FIXME: wait till completion of pending events */
+
+	omapfb_remove_sysfs(fbdev);
+
+	omapfb_free_resources(fbdev);
+
+	return 0;
+}
+
+static struct platform_driver omapfb_driver = {
+	.probe          = omapfb_probe,
+	.remove         = omapfb_remove,
+	.driver         = {
+		.name   = "omapfb",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init omapfb_init(void)
+{
+	DBG("omapfb_init\n");
+
+	if (platform_driver_register(&omapfb_driver)) {
+		printk(KERN_ERR "failed to register omapfb driver\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit omapfb_exit(void)
+{
+	DBG("omapfb_exit\n");
+	platform_driver_unregister(&omapfb_driver);
+}
+
+module_param_named(video_mode, def_mode, charp, 0);
+module_param_named(vram, def_vram, charp, 0);
+
+/* late_initcall to let panel/ctrl drivers loaded first.
+ * I guess better option would be a more dynamic approach,
+ * so that omapfb reacts to new panels when they are loaded */
+late_initcall(omapfb_init);
+/*module_init(omapfb_init);*/
+module_exit(omapfb_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("OMAP2/3 Framebuffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/omap2/omapfb-sysfs.c b/drivers/video/omap2/omapfb-sysfs.c
new file mode 100644
index 0000000..0e153b9
--- /dev/null
+++ b/drivers/video/omap2/omapfb-sysfs.c
@@ -0,0 +1,901 @@
+/*
+ * linux/drivers/video/omap2/omapfb-sysfs.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+
+#include <mach/display.h>
+#include <mach/omapfb.h>
+
+#include "omapfb.h"
+
+static int omapfb_attach_framebuffer(struct fb_info *fbi,
+		struct omap_overlay *ovl)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+	int i, t;
+	int r;
+
+	if (ofbi->num_overlays >= OMAPFB_MAX_OVL_PER_FB) {
+		dev_err(fbdev->dev, "fb has max number of overlays already\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		if (ofbi->overlays[i] == ovl) {
+			dev_err(fbdev->dev, "fb already attached to overlay\n");
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
+		for (t = 0; t < ofbi2->num_overlays; t++) {
+			if (ofbi2->overlays[t] == ovl) {
+				dev_err(fbdev->dev, "overlay already in use\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	ofbi->overlays[ofbi->num_overlays++] = ovl;
+
+/*
+	if (ovl->manager && ovl->manager->display)
+		omapfb_adjust_fb(fbi, ovl, 0, 0);
+*/
+	r = omapfb_apply_changes(fbi, 1);
+	if (r)
+		return r;
+
+	if (ovl->manager)
+		ovl->manager->apply(ovl->manager);
+
+	return 0;
+}
+
+static int omapfb_detach_framebuffer(struct fb_info *fbi,
+		struct omap_overlay *ovl)
+{
+	int i;
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	struct omapfb2_device *fbdev = ofbi->fbdev;
+
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		if (ofbi->overlays[i] == ovl)
+			break;
+	}
+
+	if (i == ofbi->num_overlays) {
+		dev_err(fbdev->dev, "cannot detach fb, overlay not attached\n");
+		return -EINVAL;
+	}
+
+	ovl->enable(ovl, 0);
+
+	if (ovl->manager)
+		ovl->manager->apply(ovl->manager);
+
+	for (i = i + 1; i < ofbi->num_overlays; i++)
+		ofbi->overlays[i-1] = ofbi->overlays[i];
+
+	ofbi->num_overlays--;
+
+	return 0;
+}
+
+
+static ssize_t show_framebuffers(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	ssize_t l = 0, size = PAGE_SIZE;
+	int i, t;
+
+	omapfb_lock(fbdev);
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+		struct omapfb_mem_region *rg;
+
+		rg = &ofbi->region;
+
+		l += snprintf(buf + l, size - l, "%d p:%08x v:%p size:%lu t:",
+				ofbi->id,
+				rg->paddr, rg->vaddr, rg->size);
+
+		if (ofbi->num_overlays == 0)
+			l += snprintf(buf + l, size - l, "none");
+
+		for (t = 0; t < ofbi->num_overlays; t++) {
+			struct omap_overlay *ovl;
+			ovl = ofbi->overlays[t];
+
+			l += snprintf(buf + l, size - l, "%s%s",
+					t == 0 ? "" : ",",
+					ovl->name);
+		}
+
+		l += snprintf(buf + l, size - l, "\n");
+	}
+
+	omapfb_unlock(fbdev);
+
+	return l;
+}
+
+static struct omap_overlay *find_overlay_by_name(struct omapfb2_device *fbdev,
+		char *name)
+{
+	int i;
+
+	for (i = 0; i < fbdev->num_overlays; i++)
+		if (strcmp(name, fbdev->overlays[i]->name) == 0)
+			return fbdev->overlays[i];
+
+	return NULL;
+}
+
+static struct omap_display *find_display_by_name(struct omapfb2_device *fbdev,
+		char *name)
+{
+	int i;
+
+	for (i = 0; i < fbdev->num_displays; i++)
+		if (strcmp(name, fbdev->displays[i]->name) == 0)
+			return fbdev->displays[i];
+
+	return NULL;
+}
+
+static struct omap_overlay_manager *find_manager_by_name(
+		struct omapfb2_device *fbdev,
+		char *name)
+{
+	int i;
+
+	for (i = 0; i < fbdev->num_managers; i++)
+		if (strcmp(name, fbdev->managers[i]->name) == 0)
+			return fbdev->managers[i];
+
+	return NULL;
+}
+
+static int parse_overlays(struct omapfb2_device *fbdev, char *str,
+		struct omap_overlay *ovls[])
+{
+	int num_ovls = 0;
+	int s, e = 0;
+	char ovlname[10];
+
+	while (1) {
+		struct omap_overlay *ovl;
+
+		s = e;
+
+		while (e < strlen(str) && str[e] != ',')
+			e++;
+
+		strncpy(ovlname, str + s, e - s);
+		ovlname[e-s] = 0;
+
+		DBG("searching for '%s'\n", ovlname);
+		ovl = find_overlay_by_name(fbdev, ovlname);
+
+		if (ovl) {
+			DBG("found an overlay\n");
+			ovls[num_ovls] = ovl;
+			num_ovls++;
+		} else {
+			DBG("unknown overlay %s\n", str);
+			return 0;
+		}
+
+		if (e == strlen(str))
+			break;
+
+		e++;
+	}
+
+	return num_ovls;
+}
+
+static ssize_t store_framebuffers(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	int idx;
+	char fbname[3];
+	unsigned long fbnum;
+	char ovlnames[40];
+	int num_ovls = 0;
+	struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB];
+	struct fb_info *fbi;
+	struct omapfb_info *ofbi;
+	int r, i;
+
+	idx = 0;
+	while (idx < count && buf[idx] != ' ')
+		++idx;
+
+	if (idx == count)
+		return -EINVAL;
+
+	if (idx >= sizeof(fbname))
+		return -EINVAL;
+
+	strncpy(fbname, buf, idx);
+	fbname[idx] = 0;
+	idx++;
+
+	if (strict_strtoul(fbname, 10, &fbnum))
+		return -EINVAL;
+
+	r = sscanf(buf + idx, "t:%39s", ovlnames);
+
+	if (r != 1) {
+		r = -EINVAL;
+		goto err;
+	}
+
+	omapfb_lock(fbdev);
+
+	if (fbnum >= fbdev->num_fbs) {
+		dev_err(dev, "fb not found\n");
+		r = -EINVAL;
+		goto err;
+	}
+
+	fbi = fbdev->fbs[fbnum];
+	ofbi = FB2OFB(fbi);
+
+	if (strcmp(ovlnames, "none") == 0) {
+		num_ovls = 0;
+	} else {
+		num_ovls = parse_overlays(fbdev, ovlnames, ovls);
+
+		if (num_ovls == 0) {
+			dev_err(dev, "overlays not found\n");
+			r = -EINVAL;
+			goto err;
+		}
+	}
+
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		r = omapfb_detach_framebuffer(fbi, ofbi->overlays[i]);
+		if (r) {
+			dev_err(dev, "detach failed\n");
+			goto err;
+		}
+	}
+
+	if (num_ovls > 0) {
+		for (i = 0; i < num_ovls; i++) {
+			r = omapfb_attach_framebuffer(fbi, ovls[i]);
+			if (r) {
+				dev_err(dev, "attach failed\n");
+				goto err;
+			}
+		}
+	}
+
+	omapfb_unlock(fbdev);
+	return count;
+
+err:
+	omapfb_unlock(fbdev);
+	return r;
+}
+
+static ssize_t show_overlays(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	ssize_t l = 0, size = PAGE_SIZE;
+	int i, mgr_num;
+
+	omapfb_lock(fbdev);
+
+	for (i = 0; i < fbdev->num_overlays; i++) {
+		struct omap_overlay *ovl;
+		struct omap_overlay_manager *mgr;
+
+		ovl = fbdev->overlays[i];
+		mgr = ovl->manager;
+
+		for (mgr_num = 0; mgr_num < fbdev->num_managers; mgr_num++)
+			if (fbdev->managers[mgr_num] == mgr)
+				break;
+
+		l += snprintf(buf + l, size - l,
+			"%s t:%s x:%d y:%d iw:%d ih:%d w:%d h:%d e:%d\n",
+			ovl->name,
+			mgr ? mgr->name : "none",
+			ovl->info.pos_x,
+			ovl->info.pos_y,
+			ovl->info.width,
+			ovl->info.height,
+			ovl->info.out_width,
+			ovl->info.out_height,
+			ovl->info.enabled);
+	}
+
+	omapfb_unlock(fbdev);
+
+	return l;
+}
+
+static ssize_t store_overlays(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	int idx;
+	struct omap_overlay *ovl = NULL;
+	struct omap_overlay_manager *mgr;
+	int r;
+	char ovlname[10];
+	int posx, posy, outw, outh;
+	int enabled;
+
+	idx = 0;
+	while (idx < count && buf[idx] != ' ')
+		++idx;
+
+	if (idx == count)
+		return -EINVAL;
+
+	if (idx >= sizeof(ovlname))
+		return -EINVAL;
+
+	strncpy(ovlname, buf, idx);
+	ovlname[idx] = 0;
+	idx++;
+
+	omapfb_lock(fbdev);
+
+	ovl = find_overlay_by_name(fbdev, ovlname);
+
+	if (!ovl) {
+		dev_err(dev, "ovl not found\n");
+		r = -EINVAL;
+		goto err;
+	}
+
+	DBG("ovl %s found\n", ovl->name);
+
+	mgr = ovl->manager;
+
+	posx = ovl->info.pos_x;
+	posy = ovl->info.pos_y;
+	outw = ovl->info.out_width;
+	outh = ovl->info.out_height;
+	enabled = ovl->info.enabled;
+
+	while (idx < count) {
+		char c;
+		int val;
+		int len;
+		char sval[10];
+
+		r = sscanf(buf + idx, "%c:%d%n", &c, &val, &len);
+
+		if (r != 2) {
+			val = 0;
+
+			r = sscanf(buf + idx, "%c:%9s%n", &c, sval, &len);
+
+			if (r != 2) {
+				dev_err(dev, "sscanf failed, aborting\n");
+				r = -EINVAL;
+				goto err;
+			}
+		} else {
+			sval[0] = 0;
+		}
+
+		switch (c) {
+		case 't':
+			if (strcmp(sval, "none") == 0) {
+				mgr = NULL;
+			} else {
+				mgr = find_manager_by_name(fbdev, sval);
+
+				if (mgr == NULL) {
+					dev_err(dev, "no such manager\n");
+					r = -EINVAL;
+					goto err;
+				}
+
+				DBG("manager %s found\n", mgr->name);
+			}
+
+			break;
+
+		case 'x':
+			posx = val;
+			break;
+
+		case 'y':
+			posy = val;
+			break;
+
+		case 'w':
+			if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE)
+				outw = val;
+			break;
+
+		case 'h':
+			if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE)
+				outh = val;
+			break;
+
+		case 'e':
+			enabled = val;
+			break;
+
+		default:
+			dev_err(dev, "unknown option %c\n", c);
+			r = -EINVAL;
+			goto err;
+		}
+
+		idx += len + 1;
+	}
+
+	r = ovl->setup_output(ovl, posx, posy, outw, outh);
+
+	if (r) {
+		dev_err(dev, "setup overlay failed\n");
+		goto err;
+	}
+
+	if (mgr != ovl->manager) {
+		/* detach old manager */
+		if (ovl->manager) {
+			r = ovl->unset_manager(ovl);
+			if (r) {
+				dev_err(dev, "detach failed\n");
+				goto err;
+			}
+		}
+
+		if (mgr) {
+			r = ovl->set_manager(ovl, mgr);
+			if (r) {
+				dev_err(dev, "Failed to attach overlay\n");
+				goto err;
+			}
+		}
+	}
+
+	r = ovl->enable(ovl, enabled);
+
+	if (r) {
+		dev_err(dev, "enable overlay failed\n");
+		goto err;
+	}
+
+	if (mgr) {
+		r = mgr->apply(mgr);
+		if (r) {
+			dev_err(dev, "failed to apply dispc config\n");
+			goto err;
+		}
+	} else {
+		ovl->enable(ovl, 0);
+	}
+
+	if (mgr && mgr->display && mgr->display->update)
+		mgr->display->update(mgr->display,
+				0, 0,
+				mgr->display->panel->timings.x_res,
+				mgr->display->panel->timings.y_res);
+
+	omapfb_unlock(fbdev);
+	return count;
+
+err:
+	omapfb_unlock(fbdev);
+	return r;
+}
+
+static ssize_t show_managers(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	ssize_t l = 0, size = PAGE_SIZE;
+	int i;
+
+	omapfb_lock(fbdev);
+
+	for (i = 0; i < fbdev->num_managers; i++) {
+		struct omap_display *display;
+		struct omap_overlay_manager *mgr;
+
+		mgr = fbdev->managers[i];
+		display = mgr->display;
+
+		l += snprintf(buf + l, size - l, "%s t:%s\n",
+				mgr->name, display ? display->name : "none");
+	}
+
+	omapfb_unlock(fbdev);
+
+	return l;
+}
+
+static ssize_t store_managers(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	int idx;
+	struct omap_overlay_manager *mgr;
+	struct omap_display *display;
+	char mgrname[10];
+	char displayname[10];
+	int r;
+
+	idx = 0;
+	while (idx < count && buf[idx] != ' ')
+		++idx;
+
+	if (idx == count)
+		return -EINVAL;
+
+	if (idx >= sizeof(mgrname))
+		return -EINVAL;
+
+	strncpy(mgrname, buf, idx);
+	mgrname[idx] = 0;
+	idx++;
+
+	omapfb_lock(fbdev);
+
+	mgr = find_manager_by_name(fbdev, mgrname);
+
+	if (!mgr) {
+		dev_err(dev, "manager not found\n");
+		r = -EINVAL;
+		goto err;
+	}
+
+	r = sscanf(buf + idx, "t:%9s", displayname);
+
+	if (r != 1) {
+		r = -EINVAL;
+		goto err;
+	}
+
+	if (strcmp(displayname, "none") == 0) {
+		display = NULL;
+	} else {
+		display = find_display_by_name(fbdev, displayname);
+
+		if (!display) {
+			dev_err(dev, "display not found\n");
+			r = -EINVAL;
+			goto err;
+		}
+	}
+
+	if (mgr->display) {
+		r = mgr->unset_display(mgr);
+		if (r) {
+			dev_err(dev, "failed to unset display\n");
+			goto err;
+		}
+	}
+
+	if (display) {
+		r = mgr->set_display(mgr, display);
+		if (r) {
+			dev_err(dev, "failed to set manager\n");
+			goto err;
+		}
+
+		r = mgr->apply(mgr);
+		if (r) {
+			dev_err(dev, "failed to apply dispc config\n");
+			goto err;
+		}
+	}
+
+	omapfb_unlock(fbdev);
+	return count;
+
+err:
+	omapfb_unlock(fbdev);
+	return r;
+}
+
+static ssize_t show_displays(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	ssize_t l = 0, size = PAGE_SIZE;
+	int i;
+	struct omap_video_timings timings;
+
+	omapfb_lock(fbdev);
+
+	for (i = 0; i < fbdev->num_displays; i++) {
+		struct omap_display *display;
+		enum omap_dss_update_mode mode = -1;
+		int te = 0;
+
+		display = fbdev->displays[i];
+
+		if (display->get_update_mode)
+			mode = display->get_update_mode(display);
+
+		if (display->get_te)
+			te = display->get_te(display);
+
+		if (display->get_timings)
+			display->get_timings(display, &timings);
+		else
+			memset(&timings, 0, sizeof(timings));
+
+		l += snprintf(buf + l, size - l,
+				"%s e:%d u:%d t:%d h:%u/%u/%u/%u "
+				"v:%u/%u/%u/%u p:%u\n",
+				display->name,
+				display->state != OMAP_DSS_DISPLAY_DISABLED,
+				mode, te,
+				timings.x_res,
+				timings.hfp, timings.hbp, timings.hsw,
+				timings.y_res,
+				timings.vfp, timings.vbp, timings.vsw,
+				timings.pixel_clock);
+	}
+
+	omapfb_unlock(fbdev);
+
+	return l;
+}
+
+static ssize_t store_displays(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+	int enable;
+	struct omap_video_timings old_timings;
+	struct omap_video_timings new_timings;
+	enum omap_dss_update_mode mode;
+	struct omap_display *display = NULL;
+	int r;
+	int te;
+	char str[128];
+	char *s, *tok;
+
+	if (strlen(buf) > sizeof(str) - 1)
+		return -EINVAL;
+
+	strcpy(str, buf);
+
+	/* remove trailing linefeeds */
+	s = str + strlen(str) - 1;
+	while (s >= str	&& *s == '\n') {
+		*s = 0;
+		s--;
+	}
+
+	s = str;
+
+	if ((tok = strsep(&s, " ")) == 0)
+		return -EINVAL;
+
+	omapfb_lock(fbdev);
+
+	display = find_display_by_name(fbdev, tok);
+
+	if (!display) {
+		dev_err(dev, "display not found\n");
+		r = -EINVAL;
+		goto err;
+	}
+
+	enable = display->state != OMAP_DSS_DISPLAY_DISABLED;
+	if (display->get_update_mode)
+		mode = display->get_update_mode(display);
+	else
+		mode = 0;
+
+	if (display->get_te)
+		te = display->get_te(display);
+	else
+		te = 0;
+
+	if (display->get_timings)
+		display->get_timings(display, &old_timings);
+	else
+		memset(&old_timings, 0, sizeof(old_timings));
+
+	memcpy(&new_timings, &old_timings, sizeof(new_timings));
+
+	while ((tok = strsep(&s, " "))) {
+		char c, *o;
+
+		if (strlen(tok) < 3 || tok[1] != ':') {
+			dev_err(dev, "illegal option\n");
+			r = -EINVAL;
+			goto err;
+		}
+
+		c = tok[0];
+		o = tok + 2;
+
+		switch (c) {
+		case 'e':
+			enable = simple_strtoul(o, NULL, 0);
+			break;
+
+		case 'u':
+			mode = simple_strtoul(o, NULL, 0);
+			break;
+
+		case 't':
+			te = simple_strtoul(o, NULL, 0);
+			break;
+
+		case 'm': {
+			unsigned bpp;
+			if (omapfb_mode_to_timings(o, &new_timings, &bpp) != 0)
+				memset(&new_timings, 0, sizeof(new_timings));
+
+			break;
+		}
+
+		case 'h': {
+			unsigned xres, hfp, hbp, hsw;
+
+			if (sscanf(o, "%u/%u/%u/%u",
+						&xres, &hfp, &hbp, &hsw) != 4) {
+				dev_err(dev, "illegal horizontal timings\n");
+				r = -EINVAL;
+				goto err;
+			}
+
+			new_timings.x_res = xres;
+			new_timings.hfp = hfp;
+			new_timings.hbp = hbp;
+			new_timings.hsw = hsw;
+			break;
+		}
+
+		case 'v': {
+			unsigned yres, vfp, vbp, vsw;
+
+			if (sscanf(o, "%u/%u/%u/%u",
+						&yres, &vfp, &vbp, &vsw) != 4) {
+				dev_err(dev, "illegal vertical timings\n");
+				r = -EINVAL;
+				goto err;
+			}
+
+			new_timings.y_res = yres;
+			new_timings.vfp = vfp;
+			new_timings.vbp = vbp;
+			new_timings.vsw = vsw;
+			break;
+		}
+
+		case 'p':
+			new_timings.pixel_clock = simple_strtoul(o, NULL, 0);
+			break;
+
+		default:
+			dev_err(dev, "unknown option %c\n", c);
+			r = -EINVAL;
+			goto err;
+		}
+	}
+
+	if (memcmp(&new_timings, &old_timings, sizeof(new_timings)) != 0) {
+		if (display->set_timings)
+			display->set_timings(display, &new_timings);
+
+		/* sigh, bpp is not a setting of the display, but
+		 * the overlay. */
+		//def_display->panel->bpp = bpp;
+	}
+
+	if (enable != (display->state != OMAP_DSS_DISPLAY_DISABLED)) {
+		if (enable) {
+			r = display->enable(display);
+			if (r)
+				dev_err(dev, "failed to enable display\n");
+		} else {
+			display->disable(display);
+		}
+	}
+
+	if (display->set_update_mode && display->get_update_mode) {
+		if (mode != display->get_update_mode(display))
+			display->set_update_mode(display, mode);
+	}
+
+	if (display->enable_te && display->get_te) {
+		if (te != display->get_te(display))
+			display->enable_te(display, te);
+	}
+
+	r = count;
+err:
+	omapfb_unlock(fbdev);
+	return r;
+}
+
+
+static DEVICE_ATTR(framebuffers, S_IRUGO | S_IWUSR,
+		show_framebuffers, store_framebuffers);
+static DEVICE_ATTR(overlays, S_IRUGO | S_IWUSR,
+		show_overlays, store_overlays);
+static DEVICE_ATTR(managers, S_IRUGO | S_IWUSR,
+		show_managers, store_managers);
+static DEVICE_ATTR(displays, S_IRUGO | S_IWUSR,
+		show_displays, store_displays);
+
+static struct attribute *omapfb_attrs[] = {
+	&dev_attr_framebuffers.attr,
+	&dev_attr_overlays.attr,
+	&dev_attr_managers.attr,
+	&dev_attr_displays.attr,
+	NULL,
+};
+
+static struct attribute_group omapfb_attr_group = {
+	.attrs = omapfb_attrs,
+};
+
+void omapfb_create_sysfs(struct omapfb2_device *fbdev)
+{
+	int r;
+
+	r = sysfs_create_group(&fbdev->dev->kobj, &omapfb_attr_group);
+	if (r)
+		dev_err(fbdev->dev, "failed to create sysfs clk file\n");
+}
+
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev)
+{
+	sysfs_remove_group(&fbdev->dev->kobj, &omapfb_attr_group);
+}
+
diff --git a/drivers/video/omap2/omapfb.h b/drivers/video/omap2/omapfb.h
new file mode 100644
index 0000000..9ba4f1b
--- /dev/null
+++ b/drivers/video/omap2/omapfb.h
@@ -0,0 +1,115 @@
+/*
+ * linux/drivers/video/omap2/omapfb.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+
+#ifdef CONFIG_FB_OMAP2_DEBUG
+#define DEBUG
+#endif
+
+#ifdef DEBUG
+extern unsigned int omapfb_debug;
+#define DBG(format, ...) \
+	if (omapfb_debug) \
+		printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par))
+
+/* max number of overlays to which a framebuffer data can be direct */
+#define OMAPFB_MAX_OVL_PER_FB 3
+
+/* appended to fb_info */
+struct omapfb_info {
+	int id;
+	struct omapfb_mem_region region;
+	atomic_t map_count;
+	int num_overlays;
+	struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
+	struct omapfb2_device *fbdev;
+};
+
+struct omapfb2_device {
+	struct device *dev;
+	struct mutex  mtx;
+
+	u32 pseudo_palette[17];
+
+	int state;
+
+	int num_fbs;
+	struct fb_info *fbs[10];
+
+	int num_displays;
+	struct omap_display *displays[10];
+	int num_overlays;
+	struct omap_overlay *overlays[10];
+	int num_managers;
+	struct omap_overlay_manager *managers[10];
+};
+
+void set_fb_fix(struct fb_info *fbi);
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var);
+int omapfb_realloc_fbmem(struct omapfb2_device *fbdev, int fbnum,
+		unsigned long size);
+int omapfb_apply_changes(struct fb_info *fbi, int init);
+int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+		int posx, int posy, int outw, int outh);
+
+void omapfb_create_sysfs(struct omapfb2_device *fbdev);
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev);
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg);
+
+int omapfb_mode_to_timings(const char *mode_str,
+		struct omap_video_timings *timings, unsigned *bpp);
+
+/* find the display connected to this fb, if any */
+static inline struct omap_display *fb2display(struct fb_info *fbi)
+{
+	struct omapfb_info *ofbi = FB2OFB(fbi);
+	int i;
+
+	/* XXX: returns the display connected to first attached overlay */
+	for (i = 0; i < ofbi->num_overlays; i++) {
+		if (ofbi->overlays[i]->manager)
+			return ofbi->overlays[i]->manager->display;
+	}
+
+	return NULL;
+}
+
+static inline void omapfb_lock(struct omapfb2_device *fbdev)
+{
+	mutex_lock(&fbdev->mtx);
+}
+
+static inline void omapfb_unlock(struct omapfb2_device *fbdev)
+{
+	mutex_unlock(&fbdev->mtx);
+}
+
+
+#endif


  parent reply	other threads:[~2009-01-12 11:47 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-01-12 11:47 [PATCH 00/12] DSS: Series description Tomi Valkeinen
2009-01-12 11:47 ` [PATCH 01/12] DSS: Documentation for DSS2 Tomi Valkeinen
2009-01-12 11:47 ` [PATCH 02/12] DSS: Display subsystem for OMAP2/3 Tomi Valkeinen
2009-01-12 13:02   ` [Linux-fbdev-devel] " Tony Lindgren
2009-01-12 13:06     ` Shah, Hardik
2009-01-12 13:17       ` Tony Lindgren
2009-01-12 11:47 ` [PATCH 03/12] DSS: RFBI support Tomi Valkeinen
2009-01-12 13:03   ` Tony Lindgren
2009-01-12 11:47 ` [PATCH 04/12] DSS: VENC support Tomi Valkeinen
2009-01-12 11:47 ` [PATCH 05/12] DSS: DSI support Tomi Valkeinen
2009-01-12 11:47 ` Tomi Valkeinen [this message]
2009-01-12 11:48 ` [PATCH 07/12] DSS: Add generic DVI panel Tomi Valkeinen
2009-01-12 11:48 ` [PATCH 08/12] DSS: support for Beagle Board Tomi Valkeinen
2009-01-13 11:14   ` David Brownell
2009-01-13 11:37     ` Tomi Valkeinen
2009-01-12 11:48 ` [PATCH 09/12] DSS: Sharp LS037V7DW01 LCD Panel driver Tomi Valkeinen
2009-01-12 11:48 ` [PATCH 10/12] DSS: Support for OMAP3 SDP board Tomi Valkeinen
2009-01-13 11:15   ` David Brownell
2009-01-12 11:48 ` [PATCH 11/12] DSS: Support for OMAP3 EVM board Tomi Valkeinen
2009-01-12 11:48 ` [PATCH 12/12] DSS: Hacked N810 support Tomi Valkeinen
2009-01-12 13:44 ` [PATCH 00/12] DSS: Series description Koen Kooi
2009-01-12 14:52   ` Hiremath, Vaibhav
2009-01-12 15:39     ` Koen Kooi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090112114758.1003.54003.stgit@tubuntu \
    --to=tomi.valkeinen@nokia.com \
    --cc=linux-fbdev-devel@lists.sourceforge.net \
    --cc=linux-omap@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.