lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <eec94ef6-9ab6-4676-b835-aed7193dd826@mail.xylon.local>
Date:	Wed, 11 Mar 2015 16:27:03 +0100
From:	Davor Joja <davorjoja@...icbricks.com>
To:	<linux-kernel@...r.kernel.org>, <linux-fbdev@...r.kernel.org>,
	<tomi.valkeinen@...com>, <geert@...ux-m68k.org>
CC:	Davor Joja <davorjoja@...icbricks.com>
Subject: [PATCH 02/10] fbdev: xylon: Framebuffer driver core

Driver core functionality.

Signed-off-by: Davor Joja <davorjoja@...icbricks.com>
---
 drivers/video/fbdev/xylon/xylonfb_core.c | 1691 ++++++++++++++++++++++++++++++
 drivers/video/fbdev/xylon/xylonfb_core.h |  252 +++++
 2 files changed, 1943 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_core.c
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_core.h

diff --git a/drivers/video/fbdev/xylon/xylonfb_core.c b/drivers/video/fbdev/xylon/xylonfb_core.c
new file mode 100644
index 0000000..9970215
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.c
@@ -0,0 +1,1691 @@
+/*
+ * Xylon logiCVC frame buffer driver core functions
+ *
+ * Copyright (C) 2015 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@...icbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+#define LOGICVC_PIX_FMT_AYUV	v4l2_fourcc('A', 'Y', 'U', 'V')
+#define LOGICVC_PIX_FMT_AVUY	v4l2_fourcc('A', 'V', 'U', 'Y')
+#define LOGICVC_PIX_FMT_ALPHA	v4l2_fourcc('A', '8', ' ', ' ')
+
+#define XYLONFB_PSEUDO_PALETTE_SIZE	256
+#define XYLONFB_VRES_DEFAULT		1080
+
+#define LOGICVC_COLOR_RGB_BLACK		0
+#define LOGICVC_COLOR_RGB_WHITE		0xFFFFFF
+#define LOGICVC_COLOR_YUV888_BLACK	0x8080
+#define LOGICVC_COLOR_YUV888_WHITE	0xFF8080
+
+char *xylonfb_mode_option;
+
+static const struct xylonfb_vmode xylonfb_vm = {
+	.vmode = {
+		.refresh = 60,
+		.xres = 1024,
+		.yres = 768,
+		.pixclock = KHZ2PICOS(65000),
+		.left_margin = 160,
+		.right_margin = 24,
+		.upper_margin = 29,
+		.lower_margin = 3,
+		.hsync_len = 136,
+		.vsync_len = 6,
+		.vmode = FB_VMODE_NONINTERLACED
+	},
+	.name = "1024x768"
+};
+
+static int xylonfb_set_timings(struct fb_info *fbi, int bpp);
+static void xylonfb_logicvc_disp_ctrl(struct fb_info *fbi, bool enable);
+static void xylonfb_enable_logicvc_output(struct fb_info *fbi);
+static void xylonfb_disable_logicvc_output(struct fb_info *fbi);
+static void xylonfb_logicvc_layer_enable(struct fb_info *fbi, bool enable);
+static void xylonfb_fbi_update(struct fb_info *fbi);
+
+static u32 xylonfb_get_reg(void __iomem *base, unsigned int offset,
+			   struct xylonfb_layer_data *ld)
+{
+	return readl(base + offset);
+}
+
+static void xylonfb_set_reg(u32 value, void __iomem *base, unsigned int offset,
+			    struct xylonfb_layer_data *ld)
+{
+	writel(value, (base + offset));
+}
+
+static unsigned long xylonfb_get_reg_mem_addr(void __iomem *base,
+					      unsigned int offset,
+					      struct xylonfb_layer_data *ld)
+{
+	unsigned int ordinal = offset / LOGICVC_REG_STRIDE;
+
+	if ((unsigned long)base - (unsigned long)ld->data->dev_base) {
+		return (unsigned long)(&ld->regs) + (ordinal * sizeof(u32));
+	} else {
+		ordinal -= (LOGICVC_CTRL_ROFF / LOGICVC_REG_STRIDE);
+		return (unsigned long)(&ld->data->regs) +
+				       (ordinal * sizeof(u32));
+	}
+}
+
+static u32 xylonfb_get_reg_mem(void __iomem *base, unsigned int offset,
+			       struct xylonfb_layer_data *ld)
+{
+	return *((unsigned long *)xylonfb_get_reg_mem_addr(base, offset, ld));
+}
+
+static void xylonfb_set_reg_mem(u32 value, void __iomem *base,
+				unsigned int offset,
+				struct xylonfb_layer_data *ld)
+{
+	unsigned long *reg_mem_addr =
+		(unsigned long *)xylonfb_get_reg_mem_addr(base, offset, ld);
+	*reg_mem_addr = value;
+	writel((*reg_mem_addr), (base + offset));
+}
+
+static irqreturn_t xylonfb_isr(int irq, void *dev_id)
+{
+	struct fb_info **afbi = dev_get_drvdata(dev_id);
+	struct fb_info *fbi = afbi[0];
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	void __iomem *dev_base = data->dev_base;
+	u32 isr;
+
+	XYLONFB_DBG(CORE, "%s", __func__);
+
+	isr = readl(dev_base + LOGICVC_INT_STAT_ROFF);
+	if (isr & LOGICVC_INT_V_SYNC) {
+		writel(LOGICVC_INT_V_SYNC, dev_base + LOGICVC_INT_STAT_ROFF);
+
+		data->vsync.count++;
+
+		if (waitqueue_active(&data->vsync.wait))
+			wake_up_interruptible(&data->vsync.wait);
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int xylonfb_open(struct fb_info *fbi, int user)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int ret;
+	bool enable = false;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (atomic_read(&ld->refcount) == 0) {
+		if (ld->flags & XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN) {
+			ld->flags &= ~XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN;
+			enable = true;
+		} else {
+			if (fbi->var.activate == FB_ACTIVATE_NOW) {
+				enable = true;
+			} else if (fbi->var.activate == FB_ACTIVATE_NXTOPEN) {
+				ld->flags |= XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN;
+				return 0;
+			} else if (fbi->var.activate == FB_ACTIVATE_VBL) {
+				ret = xylonfb_vsync_wait(0, fbi);
+				if (ret > 0)
+					enable = true;
+				else
+					return ret;
+			}
+		}
+
+		if (enable) {
+			xylonfb_logicvc_layer_enable(fbi, true);
+			atomic_inc(&data->refcount);
+		}
+	}
+
+	atomic_inc(&ld->refcount);
+
+	return 0;
+}
+
+static int xylonfb_release(struct fb_info *fbi, int user)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (atomic_read(&ld->refcount) > 0) {
+		atomic_dec(&ld->refcount);
+
+		if (atomic_read(&ld->refcount) == 0) {
+			xylonfb_logicvc_layer_enable(fbi, false);
+			atomic_dec(&data->refcount);
+		}
+	}
+
+	return 0;
+}
+
+static int xylonfb_check_var(struct fb_var_screeninfo *var,
+			     struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (var->xres < LOGICVC_MIN_XRES)
+		var->xres = LOGICVC_MIN_XRES;
+	if (var->xres > LOGICVC_MAX_XRES)
+		var->xres = LOGICVC_MAX_XRES;
+	if (var->yres < LOGICVC_MIN_VRES)
+		var->yres = LOGICVC_MIN_VRES;
+	if (var->yres > LOGICVC_MAX_VRES)
+		var->yres = LOGICVC_MAX_VRES;
+
+	if (var->xres_virtual < var->xres)
+		var->xres_virtual = var->xres;
+	if (var->xres_virtual > fd->width)
+		var->xres_virtual = fd->width;
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+	if (var->yres_virtual > fd->height)
+		var->yres_virtual = fd->height;
+
+	/* YUV 4:2:2 layer type can only have even layer xoffset */
+	if (fd->format == XYLONFB_FORMAT_YUYV)
+		var->xoffset &= ~((unsigned long) + 1);
+
+	if ((var->xoffset + var->xres) >= var->xres_virtual)
+		var->xoffset = var->xres_virtual - var->xres - 1;
+	if ((var->yoffset + var->yres) >= var->yres_virtual)
+		var->yoffset = var->yres_virtual - var->yres - 1;
+
+	if (var->bits_per_pixel != fbi->var.bits_per_pixel) {
+		if (var->bits_per_pixel == 24)
+			var->bits_per_pixel = 32;
+		else
+			var->bits_per_pixel = fbi->var.bits_per_pixel;
+	}
+
+	var->grayscale = fbi->var.grayscale;
+
+	var->transp.offset = fbi->var.transp.offset;
+	var->transp.length = fbi->var.transp.length;
+	var->transp.msb_right = fbi->var.transp.msb_right;
+	var->red.offset = fbi->var.red.offset;
+	var->red.length = fbi->var.red.length;
+	var->red.msb_right = fbi->var.red.msb_right;
+	var->green.offset = fbi->var.green.offset;
+	var->green.length = fbi->var.green.length;
+	var->green.msb_right = fbi->var.green.msb_right;
+	var->blue.offset = fbi->var.blue.offset;
+	var->blue.length = fbi->var.blue.length;
+	var->blue.msb_right = fbi->var.blue.msb_right;
+	var->height = fbi->var.height;
+	var->width = fbi->var.width;
+	var->sync = fbi->var.sync;
+	var->rotate = fbi->var.rotate;
+
+	return 0;
+}
+
+static int xylonfb_set_par(struct fb_info *fbi)
+{
+	struct device *dev = fbi->dev;
+	struct fb_info **afbi = NULL;
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	unsigned long f;
+	int i, bpp;
+	int ret = 0;
+	char vmode_opt[VMODE_NAME_SIZE];
+	bool resolution_change, layer_on[LOGICVC_MAX_LAYERS];
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (data->flags & XYLONFB_FLAGS_VMODE_SET)
+		return 0;
+
+	if ((fbi->var.xres == data->vm_active.vmode.xres) ||
+	    (fbi->var.yres == data->vm_active.vmode.yres))
+		resolution_change = false;
+	else
+		resolution_change = true;
+
+	if (resolution_change || (data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+		if (!(data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+			struct xylonfb_layer_data *ld;
+
+			afbi = dev_get_drvdata(fbi->device);
+			for (i = 0; i < data->layers; i++) {
+				ld = afbi[i]->par;
+				if (ld->flags & XYLONFB_FLAGS_LAYER_ENABLED)
+					layer_on[i] = true;
+				else
+					layer_on[i] = false;
+			}
+		}
+
+		xylonfb_disable_logicvc_output(fbi);
+		xylonfb_logicvc_disp_ctrl(fbi, false);
+
+		if (!(data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+			data->vm_active.vmode.refresh = 60;
+			sprintf(vmode_opt, "%dx%d%s-%d@...s",
+				fbi->var.xres, fbi->var.yres,
+				data->vm_active.opts_cvt,
+				fbi->var.bits_per_pixel,
+				data->vm_active.vmode.refresh,
+				data->vm_active.opts_ext);
+			if (!strcmp(data->vm.name, vmode_opt)) {
+				data->vm_active = data->vm;
+			} else {
+				bpp = fbi->var.bits_per_pixel;
+				xylonfb_mode_option = vmode_opt;
+				ret = xylonfb_set_timings(fbi, bpp);
+				xylonfb_mode_option = NULL;
+			}
+		}
+		if (!ret) {
+			f = PICOS2KHZ(data->vm_active.vmode.pixclock);
+			if (data->flags & XYLONFB_FLAGS_PIXCLK_VALID)
+				if (xylonfb_hw_pixclk_set(&data->pdev->dev,
+							  data->pixel_clock, f))
+					dev_err(dev,
+						"failed set pixel clock\n");
+
+			xylonfb_fbi_update(fbi);
+			XYLONFB_DBG(INFO, "video mode: %dx%d%s-%d@...s\n",
+				    fbi->var.xres, fbi->var.yres,
+				    data->vm_active.opts_cvt,
+				    fbi->var.bits_per_pixel,
+				    data->vm_active.vmode.refresh,
+				    data->vm_active.opts_ext);
+		}
+
+		xylonfb_enable_logicvc_output(fbi);
+		xylonfb_logicvc_disp_ctrl(fbi, true);
+
+		if (data->flags & XYLONFB_FLAGS_VMODE_INIT)
+			data->flags |= XYLONFB_FLAGS_VMODE_SET;
+
+		if (!(data->flags & XYLONFB_FLAGS_VMODE_SET)) {
+			if (!afbi) {
+				xylonfb_logicvc_layer_enable(fbi, true);
+				return ret;
+			}
+
+			for (i = 0; i < data->layers; i++) {
+				if (layer_on[i])
+					xylonfb_logicvc_layer_enable(afbi[i],
+								     true);
+			}
+		}
+	}
+
+	return ret;
+}
+
+static void xylonfb_set_color_hw_rgb2yuv(u16 t, u16 r, u16 g, u16 b, u32 *yuv,
+					 struct xylonfb_layer_data *ld)
+{
+	struct xylonfb_data *data = ld->data;
+	u32 y, u, v;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	y = ((data->coeff.cyr * (r & 0xFF)) + (data->coeff.cyg * (g & 0xFF)) +
+	     (data->coeff.cyb * (b & 0xFF)) + data->coeff.cy) /
+	     LOGICVC_YUV_NORM;
+	u = ((-data->coeff.cur * (r & 0xFF)) - (data->coeff.cug * (g & 0xFF)) +
+	     (data->coeff.cub * (b & 0xFF)) + LOGICVC_COEFF_U) /
+	     LOGICVC_YUV_NORM;
+	v = ((data->coeff.cvr * (r & 0xFF)) - (data->coeff.cvg * (g & 0xFF)) -
+	     (data->coeff.cvb * (b & 0xFF)) + LOGICVC_COEFF_V) /
+	     LOGICVC_YUV_NORM;
+
+	*yuv = ((t & 0xFF) << 24) | (y << 16) | (u << 8) | v;
+}
+
+static int xylonfb_set_color_hw(u16 *t, u16 *r, u16 *g, u16 *b,
+				int len, int id, struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	u32 pixel, pixel_clut;
+	u16 a = 0xFF;
+	int bpp, to, ro, go, bo;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	bpp = fd->bpp;
+
+	to = fbi->var.transp.offset;
+	ro = fbi->var.red.offset;
+	go = fbi->var.green.offset;
+	bo = fbi->var.blue.offset;
+
+	switch (fbi->fix.visual) {
+	case FB_VISUAL_PSEUDOCOLOR:
+		if ((id > (LOGICVC_CLUT_SIZE - 1)) || (len > LOGICVC_CLUT_SIZE))
+			return -EINVAL;
+
+		switch (fd->format_clut) {
+		case XYLONFB_FORMAT_CLUT_ARGB6565:
+			while (len > 0) {
+				if (t)
+					a = t[id];
+				pixel_clut = ((((a & 0xFC) >> 2) << to) |
+					      (((r[id] & 0xF8) >> 3) << ro) |
+					      (((g[id] & 0xFC) >> 2) << go) |
+					      (((b[id] & 0xF8) >> 3) << bo));
+				writel(pixel_clut, ld->clut_base +
+				       (id*LOGICVC_CLUT_REGISTER_SIZE));
+				len--;
+				id++;
+			}
+			break;
+		case XYLONFB_FORMAT_CLUT_ARGB8888:
+			while (len > 0) {
+				if (t)
+					a = t[id];
+				pixel_clut = (((a & 0xFF) << to) |
+					      ((r[id] & 0xFF) << ro) |
+					      ((g[id] & 0xFF) << go) |
+					      ((b[id] & 0xFF) << bo));
+				writel(pixel_clut, ld->clut_base +
+				       (id*LOGICVC_CLUT_REGISTER_SIZE));
+				len--;
+				id++;
+			}
+			break;
+		case XYLONFB_FORMAT_CLUT_AYUV8888:
+			while (len > 0) {
+				if (t)
+					a = t[id];
+				xylonfb_set_color_hw_rgb2yuv(a, r[id],
+							     g[id], b[id],
+					 		     &pixel_clut,
+					 		     ld);
+				writel(pixel_clut, ld->clut_base +
+				       (id*LOGICVC_CLUT_REGISTER_SIZE));
+				len--;
+				id++;
+			}
+			break;
+		}
+		break;
+	case FB_VISUAL_TRUECOLOR:
+		switch (fd->format) {
+		case XYLONFB_FORMAT_RGB332:
+			while (len > 0) {
+				pixel = ((((r[id] & 0xE0) >> 5) << ro) |
+					(((g[id] & 0xE0) >> 5) << go) |
+					(((b[id] & 0xC0) >> 6) << bo));
+				((u32 *)(fbi->pseudo_palette))[id] =
+					(pixel << 24) | (pixel << 16) |
+					(pixel << 8) | pixel;
+				len--;
+				id++;
+			}
+			break;
+		case XYLONFB_FORMAT_RGB565:
+			while (len > 0) {
+				pixel = ((((r[id] & 0xF8) >> 3) << ro) |
+					(((g[id] & 0xFC) >> 2) << go) |
+					(((b[id] & 0xF8) >> 3) << bo));
+				((u32 *)(fbi->pseudo_palette))[id] =
+					(pixel << 16) | pixel;
+				len--;
+				id++;
+			}
+			break;
+		case XYLONFB_FORMAT_XRGB8888:
+			while (len > 0) {
+				((u32 *)(fbi->pseudo_palette))[id] =
+					(((r[id] & 0xFF) << ro) |
+					((g[id] & 0xFF) << go) |
+					((b[id] & 0xFF) << bo));
+				len--;
+				id++;
+			}
+			break;
+		case XYLONFB_FORMAT_ARGB8888:
+			while (len > 0) {
+				if (t)
+					a = t[id];
+				((u32 *)(fbi->pseudo_palette))[id] =
+					(((a & 0xFF) << to) |
+					((r[id] & 0xFF) << ro) |
+					((g[id] & 0xFF) << go) |
+					((b[id] & 0xFF) << bo));
+				len--;
+				id++;
+			}
+			break;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int xylonfb_set_color(unsigned regno,
+			     unsigned red, unsigned green, unsigned blue,
+			     unsigned transp, struct fb_info *fbi)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	return xylonfb_set_color_hw((u16 *)&transp,
+				    (u16 *)&red, (u16 *)&green, (u16 *)&blue,
+				    1, regno, fbi);
+}
+
+static int xylonfb_set_cmap(struct fb_cmap *cmap, struct fb_info *fbi)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	return xylonfb_set_color_hw(cmap->transp,
+				    cmap->red, cmap->green, cmap->blue,
+				    cmap->len, cmap->start, fbi);
+}
+
+static void xylonfb_set_pixels(struct fb_info *fbi,
+			       struct xylonfb_layer_data *ld,
+			       int bpp, unsigned int pix)
+{
+	u32 *vmem;
+	u8 *vmem8;
+	u16 *vmem16;
+	u32 *vmem32;
+	int x, y, pixoffset;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	vmem = ld->fb_base + (fbi->var.xoffset * (fbi->var.bits_per_pixel/4)) +
+	       (fbi->var.yoffset * fbi->var.xres_virtual *
+	       (fbi->var.bits_per_pixel/4));
+
+	switch (bpp) {
+	case 8:
+		vmem8 = (u8 *)vmem;
+		for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+			pixoffset = (y * fbi->var.xres_virtual);
+			for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+				vmem8[pixoffset + x] = pix;
+		}
+		break;
+	case 16:
+		vmem16 = (u16 *)vmem;
+		for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+			pixoffset = (y * fbi->var.xres_virtual);
+			for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+				vmem16[pixoffset + x] = pix;
+		}
+		break;
+	case 32:
+		vmem32 = (u32 *)vmem;
+		for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+			pixoffset = (y * fbi->var.xres_virtual);
+			for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+				vmem32[pixoffset + x] = pix;
+		}
+		break;
+	}
+}
+
+static int xylonfb_blank(int blank_mode, struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	void __iomem *dev_base = data->dev_base;
+	u32 ctrl = data->reg_access.get_reg_val(dev_base,
+						LOGICVC_CTRL_ROFF,
+						ld);
+	u32 power = readl(dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	switch (blank_mode) {
+	case FB_BLANK_UNBLANK:
+		XYLONFB_DBG(INFO, "FB_BLANK_UNBLANK");
+		ctrl |= (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+			LOGICVC_CTRL_DATA_ENABLE);
+		data->reg_access.set_reg_val(ctrl, dev_base,
+					     LOGICVC_CTRL_ROFF, ld);
+
+		power |= LOGICVC_V_EN_MSK;
+		writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+		mdelay(50);
+		break;
+
+	case FB_BLANK_NORMAL:
+		XYLONFB_DBG(INFO, "FB_BLANK_NORMAL");
+		switch (fd->format) {
+		case XYLONFB_FORMAT_C8:
+			xylonfb_set_color(0, 0, 0, 0, 0xFF, fbi);
+			xylonfb_set_pixels(fbi, ld, 8, 0);
+			break;
+		case XYLONFB_FORMAT_RGB332:
+			xylonfb_set_pixels(fbi, ld, 8, 0x00);
+			break;
+		case XYLONFB_FORMAT_RGB565:
+			xylonfb_set_pixels(fbi, ld, 16, 0x0000);
+			break;
+		case XYLONFB_FORMAT_XRGB8888:
+		case XYLONFB_FORMAT_ARGB8888:
+			xylonfb_set_pixels(fbi, ld, 32, 0xFF000000);
+			break;
+		}
+		break;
+
+	case FB_BLANK_POWERDOWN:
+		XYLONFB_DBG(INFO, "FB_BLANK_POWERDOWN");
+		ctrl &= ~(LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+			LOGICVC_CTRL_DATA_ENABLE);
+		data->reg_access.set_reg_val(ctrl, dev_base,
+					     LOGICVC_CTRL_ROFF, ld);
+
+		power &= ~LOGICVC_V_EN_MSK;
+		writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+		mdelay(50);
+		break;
+
+	case FB_BLANK_VSYNC_SUSPEND:
+		XYLONFB_DBG(INFO, "FB_BLANK_VSYNC_SUSPEND");
+		ctrl &= ~LOGICVC_CTRL_VSYNC;
+		data->reg_access.set_reg_val(ctrl, dev_base,
+					     LOGICVC_CTRL_ROFF, ld);
+		break;
+
+	case FB_BLANK_HSYNC_SUSPEND:
+		XYLONFB_DBG(INFO, "FB_BLANK_HSYNC_SUSPEND");
+		ctrl &= ~LOGICVC_CTRL_HSYNC;
+		data->reg_access.set_reg_val(ctrl, dev_base,
+					     LOGICVC_CTRL_ROFF, ld);
+		break;
+	}
+
+	return 0;
+}
+
+static int xylonfb_pan_display(struct fb_var_screeninfo *var,
+			       struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+		return -EINVAL;
+
+	if ((fbi->var.xoffset == var->xoffset) &&
+	    (fbi->var.yoffset == var->yoffset))
+		return 0;
+
+	if (fbi->var.vmode & FB_VMODE_YWRAP)
+		return -EINVAL;
+
+	if (((var->xoffset + fbi->var.xres) > fbi->var.xres_virtual) ||
+	    ((var->yoffset + fbi->var.yres) > fbi->var.yres_virtual))
+		return -EINVAL;
+
+	if (fd->format == XYLONFB_FORMAT_YUYV)
+		var->xoffset &= ~((unsigned long) + 1);
+
+	fbi->var.xoffset = var->xoffset;
+	fbi->var.yoffset = var->yoffset;
+
+	if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+		data->reg_access.set_reg_val(var->xoffset, ld->base,
+					     LOGICVC_LAYER_HOFF_ROFF, ld);
+		data->reg_access.set_reg_val(var->yoffset, ld->base,
+					     LOGICVC_LAYER_VOFF_ROFF, ld);
+	}
+	data->reg_access.set_reg_val((fbi->var.xres - 1), ld->base,
+				     LOGICVC_LAYER_HPOS_ROFF, ld);
+	data->reg_access.set_reg_val((fbi->var.yres - 1), ld->base,
+				     LOGICVC_LAYER_VPOS_ROFF, ld);
+	if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+		ld->fb_pbase_active = ld->fb_pbase +
+				      ((var->xoffset * (fd->bpp / 8)) +
+				      (var->yoffset * fd->width *
+				      (fd->bpp / 8)));
+		data->reg_access.set_reg_val(ld->fb_pbase_active, ld->base,
+					     LOGICVC_LAYER_ADDR_ROFF, ld);
+	}
+
+	return 0;
+}
+
+static struct fb_ops xylonfb_ops = {
+	.owner = THIS_MODULE,
+	.fb_open = xylonfb_open,
+	.fb_release = xylonfb_release,
+	.fb_check_var = xylonfb_check_var,
+	.fb_set_par = xylonfb_set_par,
+	.fb_setcolreg = xylonfb_set_color,
+	.fb_setcmap = xylonfb_set_cmap,
+	.fb_blank = xylonfb_blank,
+	.fb_pan_display = xylonfb_pan_display,
+	.fb_fillrect = cfb_fillrect,
+	.fb_copyarea = cfb_copyarea,
+	.fb_imageblit = cfb_imageblit,
+	.fb_ioctl = xylonfb_ioctl,
+};
+
+static int xylonfb_find_next_layer(struct xylonfb_data *data, int layers,
+				   int id)
+{
+	dma_addr_t address = data->fd[id]->address;
+	dma_addr_t temp_address = ((unsigned long) - 1);
+	dma_addr_t loop_address;
+	int next = -1;
+	int i;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	for (i = 0; i < layers; i++) {
+		loop_address = data->fd[i]->address;
+		if ((address < loop_address) && (loop_address < temp_address)) {
+			next = i;
+			temp_address = loop_address;
+		}
+	}
+
+	return next;
+}
+
+static void xylonfb_get_vmem_height(struct xylonfb_data *data, int layers,
+				    int id)
+{
+	struct xylonfb_layer_fix_data *fd = data->fd[id];
+	dma_addr_t vmem_start = fd->address;
+	dma_addr_t vmem_end;
+	int next;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (fd->address_range && (id < (layers - 1))) {
+		fd->height = fd->address_range / (fd->width * (fd->bpp / 8));
+		return;
+	}
+
+	vmem_start = fd->address;
+
+	next = xylonfb_find_next_layer(data, layers, id);
+	if (next == -1) {
+		if (fd->address_range) {
+			fd->height = fd->address_range /
+				     (fd->width * (fd->bpp / 8));
+		} else {
+			if (fd->buffer_offset)
+				fd->height = fd->buffer_offset *
+					     LOGICVC_MAX_LAYER_BUFFERS;
+			else
+				fd->height = XYLONFB_VRES_DEFAULT;
+		}
+	} else {
+		vmem_end = data->fd[next]->address;
+		fd->height = (vmem_end - vmem_start) /
+			     (fd->width * (fd->bpp / 8));
+	}
+
+	if (fd->height > (LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS))
+		fd->height = LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS;
+}
+
+static void xylonfb_set_fbi_var_screeninfo(struct fb_var_screeninfo *var,
+					   struct xylonfb_data *data)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	var->xres = data->vm_active.vmode.xres;
+	var->yres = data->vm_active.vmode.yres;
+	var->pixclock = data->vm_active.vmode.pixclock;
+	var->left_margin = data->vm_active.vmode.left_margin;
+	var->right_margin = data->vm_active.vmode.right_margin;
+	var->upper_margin = data->vm_active.vmode.upper_margin;
+	var->lower_margin = data->vm_active.vmode.lower_margin;
+	var->hsync_len = data->vm_active.vmode.hsync_len;
+	var->vsync_len = data->vm_active.vmode.vsync_len;
+	var->sync = data->vm_active.vmode.sync;
+	var->vmode = data->vm_active.vmode.vmode;
+}
+
+static void xylonfb_fbi_update(struct fb_info *fbi)
+{
+	struct fb_info **afbi = dev_get_drvdata(fbi->device);
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int i, layers, id;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (!afbi)
+		return;
+
+	layers = data->layers;
+	id = ld->fd->id;
+
+	for (i = 0; i < layers; i++) {
+		if (i == id)
+			continue;
+
+		xylonfb_set_fbi_var_screeninfo(&afbi[i]->var, data);
+		afbi[i]->monspecs = afbi[id]->monspecs;
+	}
+}
+
+static void xylonfb_set_hw_specifics(struct fb_info *fbi,
+				     struct xylonfb_layer_data *ld,
+				     struct xylonfb_layer_fix_data *fd)
+{
+	struct xylonfb_data *data = ld->data;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	fbi->fix.smem_start = ld->fb_pbase;
+	fbi->fix.smem_len = ld->fb_size;
+	if (fd->type == LOGICVC_LAYER_RGB) {
+		fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+	} else if (fd->type == LOGICVC_LAYER_YUV) {
+		if (fd->format == XYLONFB_FORMAT_C8)
+			fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+		else
+			fbi->fix.type = FB_TYPE_FOURCC;
+	}
+	if ((fd->type == LOGICVC_LAYER_YUV) ||
+	    (fd->type == LOGICVC_LAYER_ALPHA)) {
+		if (fd->format == XYLONFB_FORMAT_C8)
+			fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		else
+			fbi->fix.visual = FB_VISUAL_FOURCC;
+	} else if (fd->format == XYLONFB_FORMAT_C8) {
+		fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	} else {
+		/*
+		 * Other logiCVC layer pixel formats:
+		 * - 8 bpp: LAYER or PIXEL alpha
+		 *   It is not true color, RGB triplet is stored in 8 bits.
+		 * - 16 bpp:
+		 *   LAYER alpha: RGB triplet is stored in 16 bits
+		 * - 32 bpp: LAYER or PIXEL alpha
+		 *   True color, RGB triplet or ARGB quadriplet
+		 *   is stored in 32 bits.
+		 */
+		fbi->fix.visual = FB_VISUAL_TRUECOLOR;
+	}
+
+	fbi->fix.xpanstep = 1;
+	fbi->fix.ypanstep = 1;
+	fbi->fix.ywrapstep = 0;
+	fbi->fix.line_length = fd->width * (fd->bpp / 8);
+	fbi->fix.mmio_start = ld->pbase;
+	fbi->fix.mmio_len = LOGICVC_LAYER_REGISTERS_RANGE;
+	fbi->fix.accel = FB_ACCEL_NONE;
+
+	fbi->var.xres_virtual = fd->width;
+	fbi->var.yres_virtual = fd->height;
+
+	fbi->var.bits_per_pixel = fd->bpp;
+
+	switch (fd->type) {
+	case LOGICVC_LAYER_ALPHA:
+		fbi->var.grayscale = LOGICVC_PIX_FMT_ALPHA;
+		break;
+	case LOGICVC_LAYER_RGB:
+		fbi->var.grayscale = 0;
+		break;
+	case LOGICVC_LAYER_YUV:
+		if (fd->format == XYLONFB_FORMAT_C8) {
+			fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+		} else if (fd->format == XYLONFB_FORMAT_YUYV) {
+			if (fd->component_swap)
+				fbi->var.grayscale = V4L2_PIX_FMT_VYUY;
+			else
+				fbi->var.grayscale = V4L2_PIX_FMT_YVYU;
+		} else if (fd->format == XYLONFB_FORMAT_AYUV) {
+			if (fd->component_swap)
+				fbi->var.grayscale = LOGICVC_PIX_FMT_AVUY;
+			else
+				fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+		}
+		break;
+	}
+
+	/*
+	 * Set values according to logiCVC layer data width configuration:
+	 * layer data width can be 1, 2, 4 bytes
+	 */
+	if (fd->transparency == LOGICVC_ALPHA_LAYER) {
+		fbi->var.transp.offset = 0;
+		fbi->var.transp.length = 0;
+	}
+
+	switch (fd->format) {
+	case XYLONFB_FORMAT_A8:
+		fbi->var.transp.offset = 0;
+		fbi->var.transp.length = 8;
+		break;
+	case XYLONFB_FORMAT_C8:
+		switch (fd->format_clut) {
+		case XYLONFB_FORMAT_CLUT_ARGB6565:
+			fbi->var.transp.offset = 24;
+			fbi->var.transp.length = 6;
+			fbi->var.red.offset = 19;
+			fbi->var.red.length = 5;
+			fbi->var.green.offset = 10;
+			fbi->var.green.length = 6;
+			fbi->var.blue.offset = 3;
+			fbi->var.blue.length = 5;
+			break;
+		case XYLONFB_FORMAT_CLUT_ARGB8888:
+			fbi->var.transp.offset = 24;
+			fbi->var.transp.length = 8;
+			fbi->var.red.offset = 16;
+			fbi->var.red.length = 8;
+			fbi->var.green.offset = 8;
+			fbi->var.green.length = 8;
+			fbi->var.blue.offset = 0;
+			fbi->var.blue.length = 8;
+			break;
+		case XYLONFB_FORMAT_CLUT_AYUV8888:
+			fbi->var.transp.offset = 24;
+			fbi->var.transp.length = 8;
+			fbi->var.red.offset = 16;
+			fbi->var.red.length = 8;
+			fbi->var.green.offset = 8;
+			fbi->var.green.length = 8;
+			fbi->var.blue.offset = 0;
+			fbi->var.blue.length = 8;
+			break;
+		}
+		break;
+	case XYLONFB_FORMAT_RGB332:
+		fbi->var.red.offset = 5;
+		fbi->var.red.length = 3;
+		fbi->var.green.offset = 2;
+		fbi->var.green.length = 3;
+		fbi->var.blue.offset = 0;
+		fbi->var.blue.length = 2;
+		break;
+	case XYLONFB_FORMAT_RGB565:
+		fbi->var.red.offset = 11;
+		fbi->var.red.length = 5;
+		fbi->var.green.offset = 5;
+		fbi->var.green.length = 6;
+		fbi->var.blue.offset = 0;
+		fbi->var.blue.length = 5;
+		break;
+	case XYLONFB_FORMAT_ARGB8888:
+	case XYLONFB_FORMAT_AYUV:
+		fbi->var.transp.offset = 24;
+		fbi->var.transp.length = 8;
+	case XYLONFB_FORMAT_XRGB8888:
+		fbi->var.red.offset = 16;
+		fbi->var.red.length = 8;
+		fbi->var.green.offset = 8;
+		fbi->var.green.length = 8;
+		fbi->var.blue.offset = 0;
+		fbi->var.blue.length = 8;
+		break;
+	}
+	fbi->var.transp.msb_right = 0;
+	fbi->var.red.msb_right = 0;
+	fbi->var.green.msb_right = 0;
+	fbi->var.blue.msb_right = 0;
+	fbi->var.activate = FB_ACTIVATE_NOW;
+	fbi->var.height = 0;
+	fbi->var.width = 0;
+	fbi->var.sync = 0;
+	if (!(data->vm_active.ctrl & LOGICVC_CTRL_HSYNC_INVERT))
+		fbi->var.sync |= FB_SYNC_HOR_HIGH_ACT;
+	if (!(data->vm_active.ctrl & LOGICVC_CTRL_VSYNC_INVERT))
+		fbi->var.sync |= FB_SYNC_VERT_HIGH_ACT;
+	fbi->var.rotate = 0;
+}
+
+static int xylonfb_set_timings(struct fb_info *fbi, int bpp)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	struct fb_var_screeninfo fb_var;
+	struct fb_videomode *vm;
+	int rc;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if ((data->flags & XYLONFB_FLAGS_VMODE_INIT) &&
+	    (data->flags & XYLONFB_FLAGS_VMODE_CUSTOM) &&
+	    memchr(data->vm.name, 'x', 10)) {
+		data->vm_active = data->vm;
+		vm = &data->vm.vmode;
+		data->vm_active.vmode.refresh =
+			DIV_ROUND_CLOSEST((PICOS2KHZ(vm->pixclock) * 1000),
+					  ((vm->xres + vm->left_margin +
+					  vm->right_margin + vm->hsync_len) *
+					  (vm->yres + vm->upper_margin +
+					  vm->lower_margin + vm->vsync_len)));
+		return 0;
+	}
+
+	rc = fb_find_mode(&fb_var, fbi, xylonfb_mode_option, NULL, 0,
+			  &xylonfb_vm.vmode, bpp);
+
+	switch (rc) {
+	case 0:
+		dev_err(fbi->dev, "failed find video mode\n"
+			"using driver default mode %dx%dM-%d@%d\n",
+			xylonfb_vm.vmode.xres,
+			xylonfb_vm.vmode.yres,
+			bpp,
+			xylonfb_vm.vmode.refresh);
+		break;
+	case 1:
+		dev_dbg(fbi->dev, "video mode %s", xylonfb_mode_option);
+		break;
+	case 2:
+		dev_warn(fbi->dev, "video mode %s with ignored refresh rate\n",
+			 xylonfb_mode_option);
+		break;
+	case 3:
+		dev_warn(fbi->dev, "default video mode %dx%dM-%d@%d\n",
+			 xylonfb_vm.vmode.xres,
+			 xylonfb_vm.vmode.yres,
+			 bpp,
+			 xylonfb_vm.vmode.refresh);
+		break;
+	case 4:
+		dev_warn(fbi->dev, "video mode fallback\n");
+		break;
+	default:
+		break;
+	}
+
+	data->vm_active.ctrl = data->vm.ctrl;
+	data->vm_active.vmode.xres = fb_var.xres;
+	data->vm_active.vmode.yres = fb_var.yres;
+	data->vm_active.vmode.pixclock = fb_var.pixclock;
+	data->vm_active.vmode.left_margin = fb_var.left_margin;
+	data->vm_active.vmode.right_margin = fb_var.right_margin;
+	data->vm_active.vmode.upper_margin = fb_var.upper_margin;
+	data->vm_active.vmode.lower_margin = fb_var.lower_margin;
+	data->vm_active.vmode.hsync_len = fb_var.hsync_len;
+	data->vm_active.vmode.vsync_len = fb_var.vsync_len;
+	data->vm_active.vmode.sync = fb_var.sync;
+	data->vm_active.vmode.vmode = fb_var.vmode;
+	data->vm_active.vmode.refresh =
+		DIV_ROUND_CLOSEST((PICOS2KHZ(fb_var.pixclock) * 1000),
+				  ((fb_var.xres + fb_var.left_margin +
+				  fb_var.right_margin + fb_var.hsync_len) *
+				  (fb_var.yres + fb_var.upper_margin +
+				  fb_var.lower_margin + fb_var.vsync_len)));
+	strcpy(data->vm_active.opts_cvt, data->vm.opts_cvt);
+	strcpy(data->vm_active.opts_ext, data->vm.opts_ext);
+	sprintf(data->vm_active.name, "%dx%d%s-%d@...s",
+		fb_var.xres, fb_var.yres,
+		data->vm_active.opts_cvt,
+		fb_var.bits_per_pixel,
+		data->vm_active.vmode.refresh,
+		data->vm_active.opts_ext);
+
+	if (!memchr(data->vm.name, 'x', 10))
+		data->vm = data->vm_active;
+
+	return 0;
+}
+
+static int xylonfb_register_fb(struct fb_info *fbi,
+			       struct xylonfb_layer_data *ld, int id,
+			       int *regfb)
+{
+	struct device *dev = fbi->dev;
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	int transp;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	fbi->flags = FBINFO_DEFAULT;
+	fbi->screen_base = (char __iomem *)ld->fb_base;
+	fbi->screen_size = ld->fb_size;
+	fbi->pseudo_palette = kzalloc(sizeof(u32) * XYLONFB_PSEUDO_PALETTE_SIZE,
+				      GFP_KERNEL);
+	fbi->fbops = &xylonfb_ops;
+
+	sprintf(fbi->fix.id, "Xylon FB%d", id);
+	xylonfb_set_hw_specifics(fbi, ld, fd);
+
+	if (!(data->flags & XYLONFB_FLAGS_VMODE_DEFAULT)) {
+		if (!xylonfb_set_timings(fbi, fbi->var.bits_per_pixel))
+			data->flags |= XYLONFB_FLAGS_VMODE_DEFAULT;
+		else
+			dev_err(dev, "videomode not set\n");
+	}
+	xylonfb_set_fbi_var_screeninfo(&fbi->var, data);
+	fbi->mode = &data->vm_active.vmode;
+	fbi->mode->name = data->vm_active.name;
+
+	if (fd->transparency == LOGICVC_ALPHA_LAYER)
+		transp = 0;
+	else
+		transp = 1;
+	if (fb_alloc_cmap(&fbi->cmap, XYLONFB_PSEUDO_PALETTE_SIZE, transp))
+		return -ENOMEM;
+
+	/*
+	 * After fb driver registration, values in struct fb_info
+	 * must not be changed anywhere else in driver except in
+	 * xylonfb_set_par() function
+	 */
+	*regfb = register_framebuffer(fbi);
+	if (*regfb) {
+		dev_err(dev, "failed register fb %d\n", id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void xylonfb_layer_initialize(struct xylonfb_layer_data *ld)
+{
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	u32 reg = ld->data->reg_access.get_reg_val(ld->base,
+						   LOGICVC_LAYER_CTRL_ROFF,
+						   ld);
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	reg |= LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_DISABLE;
+	if (fd->component_swap)
+		reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_ABGR;
+	ld->data->reg_access.set_reg_val(reg, ld->base,
+					 LOGICVC_LAYER_CTRL_ROFF,
+					 ld);
+
+	if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)
+		data->reg_access.set_reg_val(ld->fb_pbase, ld->base,
+					     LOGICVC_LAYER_ADDR_ROFF,
+					     ld);
+}
+
+static int xylonfb_vmem_init(struct xylonfb_layer_data *ld, int id, bool *mmap)
+{
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	struct device *dev = &data->pdev->dev;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (fd->address) {
+		ld->fb_pbase = fd->address;
+
+		xylonfb_get_vmem_height(data, data->layers, id);
+		ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+		if (*mmap) {
+			ld->fb_base = (__force void *)ioremap_wc(ld->fb_pbase,
+								 ld->fb_size);
+			if (!ld->fb_base) {
+				dev_err(dev, "failed map video memory\n");
+				return -EINVAL;
+			}
+		}
+	} else {
+		if (fd->buffer_offset)
+			fd->height = fd->buffer_offset *
+				     LOGICVC_MAX_LAYER_BUFFERS;
+		else
+			fd->height = XYLONFB_VRES_DEFAULT *
+				     LOGICVC_MAX_LAYER_BUFFERS;
+		ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+		ld->fb_base = dma_alloc_coherent(&data->pdev->dev,
+						 PAGE_ALIGN(ld->fb_size),
+						 &ld->fb_pbase, GFP_KERNEL);
+		if (!ld->fb_base) {
+			dev_err(dev, "failed allocate video buffer ID%d\n", id);
+			return -ENOMEM;
+		}
+
+		data->flags |= XYLONFB_FLAGS_DMA_BUFFER;
+	}
+
+	ld->fb_pbase_active = ld->fb_pbase;
+
+	*mmap = false;
+
+	return 0;
+}
+
+static void xylonfb_logicvc_disp_ctrl(struct fb_info *fbi, bool enable)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	void __iomem *dev_base = data->dev_base;
+	u32 val;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (enable) {
+		val = LOGICVC_EN_VDD_MSK;
+		writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+		mdelay(data->pwr_delay);
+		val |= LOGICVC_V_EN_MSK;
+		writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+		mdelay(data->sig_delay);
+		val |= LOGICVC_EN_BLIGHT_MSK;
+		writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+	} else {
+		writel(0, dev_base + LOGICVC_POWER_CTRL_ROFF);
+	}
+}
+
+static void xylonfb_logicvc_layer_enable(struct fb_info *fbi, bool enable)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	u32 reg;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	reg = ld->data->reg_access.get_reg_val(ld->base,
+					       LOGICVC_LAYER_CTRL_ROFF,
+					       ld);
+
+	if (enable) {
+		reg |= LOGICVC_LAYER_CTRL_ENABLE;
+		ld->flags |= XYLONFB_FLAGS_LAYER_ENABLED;
+	} else {
+		reg &= ~LOGICVC_LAYER_CTRL_ENABLE;
+		ld->flags &= ~XYLONFB_FLAGS_LAYER_ENABLED;
+	}
+
+	ld->data->reg_access.set_reg_val(reg, ld->base,
+					 LOGICVC_LAYER_CTRL_ROFF,
+					 ld);
+}
+
+static void xylonfb_enable_logicvc_output(struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	void __iomem *dev_base = data->dev_base;
+	struct fb_videomode *vm = &data->vm_active.vmode;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	writel(vm->right_margin - 1, dev_base + LOGICVC_HSYNC_FRONT_PORCH_ROFF);
+	writel(vm->hsync_len - 1, dev_base + LOGICVC_HSYNC_ROFF);
+	writel(vm->left_margin - 1, dev_base + LOGICVC_HSYNC_BACK_PORCH_ROFF);
+	writel(vm->xres - 1, dev_base + LOGICVC_HRES_ROFF);
+	writel(vm->lower_margin - 1, dev_base + LOGICVC_VSYNC_FRONT_PORCH_ROFF);
+	writel(vm->vsync_len - 1, dev_base + LOGICVC_VSYNC_ROFF);
+	writel(vm->upper_margin - 1, dev_base + LOGICVC_VSYNC_BACK_PORCH_ROFF);
+	writel(vm->yres - 1, dev_base + LOGICVC_VRES_ROFF);
+	data->reg_access.set_reg_val(data->vm_active.ctrl, dev_base,
+				     LOGICVC_CTRL_ROFF, ld);
+
+	if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV)
+		data->reg_access.set_reg_val(LOGICVC_COLOR_YUV888_BLACK,
+					     dev_base,
+					     LOGICVC_BACKGROUND_COLOR_ROFF,
+					     ld);
+	else
+		data->reg_access.set_reg_val(LOGICVC_COLOR_RGB_BLACK,
+					     dev_base,
+					     LOGICVC_BACKGROUND_COLOR_ROFF,
+					     ld);
+
+	writel(LOGICVC_DTYPE_REG_INIT, dev_base + LOGICVC_DTYPE_ROFF);
+
+	XYLONFB_DBG(INFO, "logiCVC HW parameters:\n" \
+		"    Horizontal Front Porch: %d pixclks\n" \
+		"    Horizontal Sync:        %d pixclks\n" \
+		"    Horizontal Back Porch:  %d pixclks\n" \
+		"    Vertical Front Porch:   %d pixclks\n" \
+		"    Vertical Sync:          %d pixclks\n" \
+		"    Vertical Back Porch:    %d pixclks\n" \
+		"    Pixel Clock:            %d ps\n" \
+		"    Horizontal Resolution:  %d pixels\n" \
+		"    Vertical Resolution:    %d lines\n", \
+		vm->right_margin, vm->hsync_len, vm->left_margin,
+		vm->lower_margin, vm->vsync_len, vm->upper_margin,
+		vm->pixclock, vm->xres, vm->yres);
+}
+
+static void xylonfb_disable_logicvc_output(struct fb_info *fbi)
+{
+	struct fb_info **afbi = dev_get_drvdata(fbi->device);
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int i;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (afbi)
+		for (i = 0; i < data->layers; i++)
+			xylonfb_logicvc_layer_enable(afbi[i], false);
+}
+
+static void xylonfb_start(struct fb_info **afbi, int layers)
+{
+	struct fb_info *fbi = afbi[0];
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int i;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	for (i = 0; i < layers; i++) {
+		ld = afbi[i]->par;
+		if (ld->flags & XYLONFB_FLAGS_LAYER_ENABLED)
+			continue;
+
+		xylonfb_logicvc_layer_enable(afbi[i], false);
+	}
+
+	if (data->flags & XYLONFB_FLAGS_VSYNC_IRQ) {
+		writel(LOGICVC_INT_V_SYNC,
+		       data->dev_base + LOGICVC_INT_STAT_ROFF);
+		data->reg_access.set_reg_val(~LOGICVC_INT_V_SYNC,
+					     data->dev_base,
+					     LOGICVC_INT_MASK_ROFF, ld);
+	}
+
+	for (i = 0; i < layers; i++) {
+		ld = afbi[i]->par;
+		XYLONFB_DBG(INFO, "logiCVC layer %d\n" \
+			"    Registers Base Address:     0x%lX\n" \
+			"    Layer Video Memory Address: 0x%lX\n" \
+			"    X resolution:               %d\n" \
+			"    Y resolution:               %d\n" \
+			"    X resolution (virtual):     %d\n" \
+			"    Y resolution (virtual):     %d\n" \
+			"    Line length (bytes):        %d\n" \
+			"    Bits per Pixel:             %d\n" \
+			"\n", \
+			i,
+			(unsigned long)ld->pbase,
+			(unsigned long)ld->fb_pbase,
+			afbi[i]->var.xres,
+			afbi[i]->var.yres,
+			afbi[i]->var.xres_virtual,
+			afbi[i]->var.yres_virtual,
+			afbi[i]->fix.line_length,
+			afbi[i]->var.bits_per_pixel);
+	}
+}
+
+static void xylonfb_get_vmode_opts(struct xylonfb_data *data)
+{
+	char *s, *opt, *ext, *c;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	s = data->vm.name;
+	opt = data->vm.opts_cvt;
+	ext = data->vm.opts_ext;
+
+	data->vm.vmode.vmode = 0;
+
+	c = strchr(s, 'M');
+	if (c)
+		*opt++ = *c;
+	c = strchr(s, 'R');
+	if (c)
+		*opt = *c;
+	c = strchr(s, 'i');
+	if (c) {
+		*ext++ = *c;
+		data->vm.vmode.vmode |= FB_VMODE_INTERLACED;
+	}
+	c = strchr(s, 'm');
+	if (c)
+		*ext = *c;
+}
+
+static bool xylonfb_allow_console(struct xylonfb_layer_fix_data *fd)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	switch (fd->format) {
+	case XYLONFB_FORMAT_C8:
+	case XYLONFB_FORMAT_RGB332:
+	case XYLONFB_FORMAT_RGB565:
+	case XYLONFB_FORMAT_XRGB8888:
+	case XYLONFB_FORMAT_ARGB8888:
+		return true;
+	default:
+		return false;
+	}
+}
+
+int xylonfb_init_core(struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	struct fb_info **afbi, *fbi;
+	struct xylonfb_layer_data *ld;
+	void __iomem *dev_base;
+	u32 ip_ver;
+	int i, ret, layers, console_layer;
+	int regfb[LOGICVC_MAX_LAYERS];
+	size_t size;
+	unsigned short layer_base_off[] = {
+		(LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_0_OFFSET),
+		(LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_1_OFFSET),
+		(LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_2_OFFSET),
+		(LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_3_OFFSET),
+		(LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_4_OFFSET)
+	};
+	unsigned short clut_base_off[] = {
+		(LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L0_CLUT_0_OFFSET),
+		(LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L1_CLUT_0_OFFSET),
+		(LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L2_CLUT_0_OFFSET),
+		(LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L3_CLUT_0_OFFSET),
+		(LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L4_CLUT_0_OFFSET),
+	};
+	bool memmap;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	dev_base = devm_ioremap_resource(dev, &data->resource_mem);
+	if (IS_ERR(dev_base)) {
+		dev_err(dev, "failed ioremap mem resource\n");
+		return PTR_ERR(dev_base);
+	}
+	data->dev_base = dev_base;
+
+	data->irq = data->resource_irq.start;
+	ret = devm_request_irq(dev, data->irq, xylonfb_isr, IRQF_TRIGGER_HIGH,
+			       XYLONFB_DEVICE_NAME, dev);
+	if (ret)
+		return ret;
+
+	ip_ver = readl(dev_base + LOGICVC_IP_VERSION_ROFF);
+	data->major = (ip_ver >> LOGICVC_MAJOR_REVISION_SHIFT) &
+		      LOGICVC_MAJOR_REVISION_MASK;
+	data->minor = (ip_ver >> LOGICVC_MINOR_REVISION_SHIFT) &
+		      LOGICVC_MINOR_REVISION_MASK;
+	data->patch = ip_ver & LOGICVC_PATCH_LEVEL_MASK;
+	dev_info(dev, "logiCVC IP core %d.%02d.%c\n",
+		 data->major, data->minor, ('a' + data->patch));
+
+	if (data->major >= 4)
+		data->flags |= XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS;
+
+	layers = data->layers;
+	if (layers == 0) {
+		dev_err(dev, "no available layers\n");
+		return -ENODEV;
+	}
+	console_layer = data->console_layer;
+	if (console_layer >= layers) {
+		dev_err(dev, "invalid console layer ID\n");
+		console_layer = 0;
+	}
+
+	if (data->flags & XYLONFB_FLAGS_CHECK_CONSOLE_LAYER) {
+		if (!xylonfb_allow_console(data->fd[console_layer])) {
+			dev_err(dev, "invalid console layer format\n");
+			return -EINVAL;
+		}
+		data->flags &= ~XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+	}
+
+	size = sizeof(struct fb_info *);
+	afbi = devm_kzalloc(dev, (size * layers), GFP_KERNEL);
+	if (!afbi) {
+		dev_err(dev, "failed allocate internal data\n");
+		return -ENOMEM;
+	}
+
+	if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+		data->reg_access.get_reg_val = xylonfb_get_reg;
+		data->reg_access.set_reg_val = xylonfb_set_reg;
+	} else {
+		size = sizeof(struct xylonfb_registers);
+		data->reg_access.get_reg_val = xylonfb_get_reg_mem;
+		data->reg_access.set_reg_val = xylonfb_set_reg_mem;
+	}
+
+	data->coeff.cyr = LOGICVC_COEFF_Y_R;
+	data->coeff.cyg = LOGICVC_COEFF_Y_G;
+	data->coeff.cyb = LOGICVC_COEFF_Y_B;
+	if (data->flags & XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656) {
+		data->coeff.cy = LOGICVC_COEFF_ITU656_Y;
+		data->coeff.cur = LOGICVC_COEFF_ITU656_U_R;
+		data->coeff.cug = LOGICVC_COEFF_ITU656_U_G;
+		data->coeff.cub = LOGICVC_COEFF_ITU656_U_B;
+		data->coeff.cvr = LOGICVC_COEFF_ITU656_V_R;
+		data->coeff.cvg = LOGICVC_COEFF_ITU656_V_G;
+		data->coeff.cvb = LOGICVC_COEFF_ITU656_V_B;
+	} else {
+		data->coeff.cy = LOGICVC_COEFF_Y;
+		data->coeff.cur = LOGICVC_COEFF_U_R;
+		data->coeff.cug = LOGICVC_COEFF_U_G;
+		data->coeff.cub = LOGICVC_COEFF_U_B;
+		data->coeff.cvr = LOGICVC_COEFF_V_R;
+		data->coeff.cvg = LOGICVC_COEFF_V_G;
+		data->coeff.cvb = LOGICVC_COEFF_V_B;
+	}
+
+	atomic_set(&data->refcount, 0);
+
+	data->flags |= XYLONFB_FLAGS_VMODE_INIT;
+
+	sprintf(data->vm.name, "%s-%d@%d",
+		data->vm.name, data->fd[console_layer]->bpp,
+		data->vm.vmode.refresh);
+	if (!(data->flags & XYLONFB_FLAGS_VMODE_CUSTOM))
+		xylonfb_mode_option = data->vm.name;
+	xylonfb_get_vmode_opts(data);
+
+	if (data->pixel_clock) {
+		if (xylonfb_hw_pixclk_supported(dev, data->pixel_clock)) {
+			data->flags |= XYLONFB_FLAGS_PIXCLK_VALID;
+		} else {
+			dev_warn(dev, "pixel clock not supported\n");
+			ret = -EPROBE_DEFER;
+			goto err_probe;
+		}
+	} else {
+		dev_info(dev, "external pixel clock\n");
+	}
+
+	ld = NULL;
+
+	for (i = 0; i < layers; i++)
+		regfb[i] = -1;
+	memmap = true;
+
+	/*
+	 * /dev/fb0 will be default console layer,
+	 * no matter how logiCVC layers are sorted in memory
+	 */
+	for (i = console_layer; i < layers; i++) {
+		if (regfb[i] != -1)
+			continue;
+
+		size = sizeof(struct xylonfb_layer_data);
+		fbi = framebuffer_alloc(size, dev);
+		if (!fbi) {
+			dev_err(dev, "failed allocate fb info\n");
+			ret = -ENOMEM;
+			goto err_probe;
+		}
+		fbi->dev = dev;
+		afbi[i] = fbi;
+
+		ld = fbi->par;
+		ld->data = data;
+		ld->fd = data->fd[i];
+
+		atomic_set(&ld->refcount, 0);
+
+		ld->pbase = data->resource_mem.start + layer_base_off[i];
+		ld->base = dev_base + layer_base_off[i];
+		ld->clut_base = dev_base + clut_base_off[i];
+
+		ret = xylonfb_vmem_init(ld, i, &memmap);
+		if (ret)
+			goto err_probe;
+
+		xylonfb_layer_initialize(ld);
+
+		ret = xylonfb_register_fb(fbi, ld, i, &regfb[i]);
+		if (ret)
+			goto err_probe;
+
+		if (console_layer >= 0)
+			fbi->monspecs = afbi[console_layer]->monspecs;
+
+		mutex_init(&ld->mutex);
+
+		XYLONFB_DBG(INFO, "Layer parameters\n" \
+			    "    ID %d\n" \
+			    "    Width %d pixels\n" \
+			    "    Height %d lines\n" \
+			    "    Bits per pixel %d\n" \
+			    "    Buffer size %d bytes\n", \
+			    ld->fd->id,
+			    ld->fd->width,
+			    ld->fd->height,
+			    ld->fd->bpp,
+			    ld->fb_size);
+
+		if (console_layer > 0) {
+			i = -1;
+			console_layer = -1;
+		}
+	}
+
+	if (ld) {
+		if (!(data->flags & XYLONFB_FLAGS_READABLE_REGS))
+			data->reg_access.set_reg_val(0xFFFF, dev_base,
+						     LOGICVC_INT_MASK_ROFF,
+						     ld);
+	} else {
+		dev_warn(dev, "initialization not completed\n");
+	}
+
+	if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER)
+		dev_info(dev, "BG layer: %s@...pp",
+			 data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_RGB ? \
+			 "RGB" : "YUV", data->bg_layer_bpp);
+
+	mutex_init(&data->irq_mutex);
+	init_waitqueue_head(&data->vsync.wait);
+	atomic_set(&data->refcount, 0);
+
+	dev_set_drvdata(dev, (void *)afbi);
+
+	data->flags &= ~(XYLONFB_FLAGS_VMODE_INIT |
+			 XYLONFB_FLAGS_VMODE_DEFAULT | XYLONFB_FLAGS_VMODE_SET);
+	xylonfb_mode_option = NULL;
+
+	xylonfb_start(afbi, layers);
+
+	return 0;
+
+err_probe:
+	for (i = layers - 1; i >= 0; i--) {
+		fbi = afbi[i];
+		if (!fbi)
+			continue;
+		ld = fbi->par;
+		if (regfb[i] == 0)
+			unregister_framebuffer(fbi);
+		else
+			regfb[i] = 0;
+		if (fbi->cmap.red)
+			fb_dealloc_cmap(&fbi->cmap);
+		if (ld) {
+			if (data->flags & XYLONFB_FLAGS_DMA_BUFFER) {
+				if (ld->fb_base)
+					dma_free_coherent(dev,
+						PAGE_ALIGN(ld->fb_size),
+						ld->fb_base, ld->fb_pbase);
+			} else {
+				if (ld->fb_base)
+					iounmap((void __iomem *)ld->fb_base);
+			}
+			kfree(fbi->pseudo_palette);
+			framebuffer_release(fbi);
+		}
+	}
+
+	return ret;
+}
+
+int xylonfb_deinit_core(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fb_info **afbi = dev_get_drvdata(dev);
+	struct fb_info *fbi = afbi[0];
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int i;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (atomic_read(&data->refcount) != 0) {
+		dev_err(dev, "driver in use\n");
+		return -EINVAL;
+	}
+
+	xylonfb_disable_logicvc_output(fbi);
+
+	xylonfb_hw_pixclk_unload(data->pixel_clock);
+
+	for (i = data->layers - 1; i >= 0; i--) {
+		fbi = afbi[i];
+		ld = fbi->par;
+
+		unregister_framebuffer(fbi);
+		fb_dealloc_cmap(&fbi->cmap);
+		if (data->flags & XYLONFB_FLAGS_DMA_BUFFER) {
+			dma_free_coherent(dev,
+					  PAGE_ALIGN(ld->fb_size),
+					  ld->fb_base, ld->fb_pbase);
+		}
+		kfree(fbi->pseudo_palette);
+		framebuffer_release(fbi);
+	}
+
+	return 0;
+}
diff --git a/drivers/video/fbdev/xylon/xylonfb_core.h b/drivers/video/fbdev/xylon/xylonfb_core.h
new file mode 100644
index 0000000..1a3dee8
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.h
@@ -0,0 +1,252 @@
+/*
+ * Xylon logiCVC frame buffer driver internal data structures
+ *
+ * Copyright (C) 2015 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@...icbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __XYLONFB_CORE_H__
+#define __XYLONFB_CORE_H__
+
+#include <linux/fb.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+#define XYLONFB_DRIVER_NAME "xylonfb"
+#define XYLONFB_DEVICE_NAME "logicvc"
+#define XYLONFB_DRIVER_DESCRIPTION "Xylon logiCVC frame buffer driver"
+#define XYLONFB_DRIVER_VERSION "3.0"
+
+#define INFO		1
+#define CORE		2
+#define DEBUG_LEVEL	CORE
+
+#ifdef DEBUG
+#define XYLONFB_DBG(level, format, ...)				\
+	do {							\
+		if (level >= DEBUG_LEVEL)			\
+			pr_info(format "\n", ## __VA_ARGS__);	\
+	} while (0)
+#else
+#define XYLONFB_DBG(format, ...) do { } while (0)
+#endif
+
+#define LOGICVC_MAX_LAYERS	5
+
+/* Xylon FB driver flags */
+#define XYLONFB_FLAGS_READABLE_REGS		(1 << 0)
+#define XYLONFB_FLAGS_SIZE_POSITION		(1 << 1)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER		(1 << 2)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_RGB	(1 << 3)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_YUV	(1 << 4)
+#define XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656	(1 << 5)
+#define XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS	(1 << 6)
+#define XYLONFB_FLAGS_CHECK_CONSOLE_LAYER	(1 << 7)
+#define XYLONFB_FLAGS_PIXCLK_VALID		(1 << 8)
+#define XYLONFB_FLAGS_DMA_BUFFER		(1 << 9)
+#define XYLONFB_FLAGS_VSYNC_IRQ			(1 << 10)
+#define XYLONFB_FLAGS_VMODE_CUSTOM		(1 << 11)
+#define XYLONFB_FLAGS_VMODE_INIT		(1 << 12)
+#define XYLONFB_FLAGS_VMODE_DEFAULT		(1 << 13)
+#define XYLONFB_FLAGS_VMODE_SET			(1 << 14)
+#define XYLONFB_FLAGS_LAYER_ENABLED		(1 << 15)
+#define XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN	(1 << 16)
+
+/* Xylon FB driver color formats */
+enum xylonfb_color_format {
+	XYLONFB_FORMAT_A8,
+	XYLONFB_FORMAT_C8,
+	XYLONFB_FORMAT_RGB332,
+	XYLONFB_FORMAT_RGB565,
+	XYLONFB_FORMAT_XRGB8888,
+	XYLONFB_FORMAT_ARGB8888,
+	XYLONFB_FORMAT_YUYV,
+	XYLONFB_FORMAT_AYUV,
+	XYLONFB_FORMAT_CLUT_ARGB6565,
+	XYLONFB_FORMAT_CLUT_ARGB8888,
+	XYLONFB_FORMAT_CLUT_AYUV8888
+};
+
+struct xylonfb_layer_data;
+struct xylonfb_data;
+
+#define VMODE_NAME_SIZE	21
+#define VMODE_OPTS_SIZE	3
+
+struct xylonfb_vmode {
+	u32 ctrl;
+	struct fb_videomode vmode;
+	char name[VMODE_NAME_SIZE];
+	char opts_cvt[VMODE_OPTS_SIZE];
+	char opts_ext[VMODE_OPTS_SIZE];
+};
+
+struct xylonfb_registers {
+	u32 ctrl;
+	u32 dtype;
+	u32 bg;
+	u32 unused[3];
+	u32 int_mask;
+};
+
+union xylonfb_layer_reg_0 {
+	u32 addr;
+	u32 hoff;
+};
+
+union xylonfb_layer_reg_1 {
+	u32 unused;
+	u32 voff;
+};
+
+struct xylonfb_layer_registers {
+	union xylonfb_layer_reg_0 reg_0;
+	union xylonfb_layer_reg_1 reg_1;
+	u32 hpos;
+	u32 vpos;
+	u32 hsize;
+	u32 vsize;
+	u32 alpha;
+	u32 ctrl;
+	u32 transp;
+};
+
+struct xylonfb_register_access {
+	u32 (*get_reg_val)(void __iomem *dev_base, unsigned int offset,
+			   struct xylonfb_layer_data *layer_data);
+	void (*set_reg_val)(u32 value, void __iomem *dev_base,
+			    unsigned int offset,
+			    struct xylonfb_layer_data *layer_data);
+};
+
+struct xylonfb_layer_fix_data {
+	unsigned int id;
+	u32 address;
+	u32 address_range;
+	u32 buffer_offset;
+	u32 bpp;
+	u32 width;
+	u32 height;
+	u32 format;
+	u32 format_clut;
+	u32 transparency;
+	u32 type;
+	bool component_swap;
+};
+
+struct xylonfb_layer_data {
+	struct mutex mutex;
+
+	atomic_t refcount;
+
+	struct xylonfb_data *data;
+	struct xylonfb_layer_fix_data *fd;
+	struct xylonfb_layer_registers regs;
+
+	dma_addr_t pbase;
+	void __iomem *base;
+	void __iomem *clut_base;
+
+	dma_addr_t fb_pbase;
+	void *fb_base;
+	u32 fb_size;
+
+	dma_addr_t fb_pbase_active;
+
+	u32 flags;
+};
+
+struct xylonfb_rgb2yuv_coeff {
+	s32 cy;
+	s32 cyr;
+	s32 cyg;
+	s32 cyb;
+	s32 cur;
+	s32 cug;
+	s32 cub;
+	s32 cvr;
+	s32 cvg;
+	s32 cvb;
+};
+
+struct xylonfb_sync {
+	wait_queue_head_t wait;
+	unsigned int count;
+};
+
+struct xylonfb_data {
+	struct platform_device *pdev;
+
+	struct device_node *device;
+	struct device_node *pixel_clock;
+
+	struct resource resource_mem;
+	struct resource resource_irq;
+
+	void __iomem *dev_base;
+
+	struct mutex irq_mutex;
+
+	struct xylonfb_register_access reg_access;
+	struct xylonfb_sync vsync;
+	struct xylonfb_vmode vm;
+	struct xylonfb_vmode vm_active;
+	struct xylonfb_rgb2yuv_coeff coeff;
+
+	struct xylonfb_layer_fix_data *fd[LOGICVC_MAX_LAYERS];
+	struct xylonfb_registers regs;
+
+	u32 bg_layer_bpp;
+	u32 console_layer;
+	u32 pixel_stride;
+
+	atomic_t refcount;
+
+	u32 flags;
+	int irq;
+	u8 layers;
+	/*
+	 * Delay after applying display power and
+	 * before applying display signals
+	 */
+	u32 pwr_delay;
+	/*
+	 * Delay after applying display signal and
+	 * before applying display backlight power supply
+	 */
+	u32 sig_delay;
+	/* IP version */
+	u8 major;
+	u8 minor;
+	u8 patch;
+};
+
+/* Xylon FB video mode options */
+extern char *xylonfb_mode_option;
+
+/* Xylon FB core pixel clock interface functions */
+extern bool xylonfb_hw_pixclk_supported(struct device *dev,
+					struct device_node *dn);
+extern void xylonfb_hw_pixclk_unload(struct device_node *dn);
+extern int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+				 unsigned long pixclk_khz);
+
+/* Xylon FB core V sync wait function */
+extern int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi);
+
+/* Xylon FB core interface functions */
+extern int xylonfb_init_core(struct xylonfb_data *data);
+extern int xylonfb_deinit_core(struct platform_device *pdev);
+extern int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd,
+			 unsigned long arg);
+
+#endif /* __XYLONFB_CORE_H__ */
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ