linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Xylon framebuffer driver
@ 2014-10-17 13:06 Davor Joja
  2014-10-17 13:06 ` [PATCH 01/10] fbdev: " Davor Joja
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel

Hi,

The patches are based on:
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
at commit 0429fbc0bdc297d64188483ba029a23773ae07b0

The patches implement Xylon framebuffer driver for logiCVC IP core device.
logiCVC is Configurable Video Controller developed and optimized for Xilinx
FPGA Spartan 6 and Series 7 devices.
Driver compiles without errors or warnings on 3.17, andr has been tested
with Xilinx Linux ARM kernel 3.15 on Zynq SoC, with standard fb console on
embedded device LCDs.

Kind regards,
Davor


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 01/10] fbdev: Xylon framebuffer driver
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 02/10] fbdev: xylon: Framebuffer driver core Davor Joja
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver registration and OF binding.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/xylonfb_main.c | 526 +++++++++++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_main.c

diff --git a/drivers/video/fbdev/xylon/xylonfb_main.c b/drivers/video/fbdev/xylon/xylonfb_main.c
new file mode 100644
index 0000000..5777360
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_main.c
@@ -0,0 +1,526 @@
+/*
+ * Xylon logiCVC frame buffer Open Firmware driver
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+static void xylonfb_init_ctrl(struct device_node *dn, enum display_flags flags,
+			      u32 *ctrl)
+{
+	u32 ctrl_reg = (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+			LOGICVC_CTRL_DATA_ENABLE);
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (of_property_read_bool(dn, "hsync-active-low") ||
+	    (flags & DISPLAY_FLAGS_HSYNC_LOW))
+		ctrl_reg |= LOGICVC_CTRL_HSYNC_INVERT;
+	if (of_property_read_bool(dn, "vsync-active-low") ||
+	    (flags & DISPLAY_FLAGS_VSYNC_LOW))
+		ctrl_reg |= LOGICVC_CTRL_VSYNC_INVERT;
+	if (of_property_read_bool(dn, "data-enable-active-low") ||
+	    (flags & DISPLAY_FLAGS_DE_LOW))
+		ctrl_reg |= LOGICVC_CTRL_DATA_ENABLE_INVERT;
+	if (of_property_read_bool(dn, "pixel-data-invert"))
+		ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_INVERT;
+	if (of_property_read_bool(dn, "pixel-data-output-trigger-high") ||
+	    (flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+		ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT;
+
+	*ctrl = ctrl_reg;
+}
+
+static int xylonfb_layer_set_format(struct xylonfb_layer_fix_data *fd,
+				    struct device *dev)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	switch (fd->type) {
+	case LOGICVC_LAYER_ALPHA:
+		fd->format = XYLONFB_FORMAT_A8;
+		break;
+
+	case LOGICVC_LAYER_RGB:
+		switch (fd->bpp) {
+		case 8:
+			switch (fd->transparency) {
+			case LOGICVC_ALPHA_CLUT_16BPP:
+				fd->format = XYLONFB_FORMAT_C8;
+				fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB6565;
+				break;
+			case LOGICVC_ALPHA_CLUT_32BPP:
+				fd->format = XYLONFB_FORMAT_C8;
+				fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB8888;
+				break;
+			case LOGICVC_ALPHA_LAYER:
+				fd->format = XYLONFB_FORMAT_RGB332;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case 16:
+			if (fd->transparency != LOGICVC_ALPHA_LAYER)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_RGB565;
+			break;
+		case 32:
+			switch (fd->transparency) {
+			case LOGICVC_ALPHA_LAYER:
+				fd->format = XYLONFB_FORMAT_XRGB8888;
+				break;
+			case LOGICVC_ALPHA_PIXEL:
+				fd->format = XYLONFB_FORMAT_ARGB8888;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		}
+		break;
+
+	case LOGICVC_LAYER_YUV:
+		switch (fd->bpp) {
+		case 8:
+			if (fd->transparency != LOGICVC_ALPHA_CLUT_32BPP)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_C8;
+			fd->format_clut = XYLONFB_FORMAT_CLUT_AYUV8888;
+			break;
+		case 16:
+			if (fd->transparency != LOGICVC_ALPHA_LAYER)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_YUYV;
+			break;
+		case 32:
+			if (fd->transparency != LOGICVC_ALPHA_PIXEL)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_AYUV;
+			break;
+		}
+		break;
+
+	default:
+		dev_err(dev, "unsupported layer type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int xylonfb_parse_layer_info(struct device_node *parent_dn,
+				    struct xylonfb_data *data, int id)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn;
+	struct xylonfb_layer_fix_data *fd;
+	int ret;
+	char layer_name[10];
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	snprintf(layer_name, sizeof(layer_name), "layer_%d", id);
+	dn = of_get_child_by_name(parent_dn, layer_name);
+	if (!dn)
+		return 0;
+
+	data->layers++;
+
+	fd = devm_kzalloc(&data->pdev->dev,
+			  sizeof(struct xylonfb_layer_fix_data), GFP_KERNEL);
+	if (!fd) {
+		dev_err(dev, "failed allocate layer fix data (%d)\n", id);
+		return -ENOMEM;
+	}
+
+	data->fd[id] = fd;
+
+	fd->id = id;
+
+	ret = of_property_read_u32(dn, "address", &fd->address);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get address\n");
+		return ret;
+	}
+	ret = of_property_read_u32_index(dn, "address", 1, &fd->address_range);
+
+	ret = of_property_read_u32(dn, "buffer-offset", &fd->buffer_offset);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get buffer-offset\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "bits-per-pixel", &fd->bpp);
+	if (ret) {
+		dev_err(dev, "failed get bits-per-pixel\n");
+		return ret;
+	}
+	switch (fd->bpp) {
+	case 8:
+	case 16:
+	case 32:
+		break;
+	default:
+		dev_err(dev, "invalid bits-per-pixel value\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_string(dn, "type", &string);
+	if (ret) {
+		dev_err(dev, "failed get type\n");
+		return ret;
+	}
+	if (!strcmp(string, "alpha")) {
+		fd->type = LOGICVC_LAYER_ALPHA;
+	} else if (!strcmp(string, "rgb")) {
+		fd->type = LOGICVC_LAYER_RGB;
+	} else if (!strcmp(string, "yuv")) {
+		fd->type = LOGICVC_LAYER_YUV;
+	} else {
+		dev_err(dev, "unsupported layer type\n");
+		return -EINVAL;
+	}
+
+	if (fd->type != LOGICVC_LAYER_ALPHA) {
+		ret = of_property_read_string(dn, "transparency", &string);
+		if (ret) {
+			dev_err(dev, "failed get transparency\n");
+			return ret;
+		}
+		if (!strcmp(string, "clut16")) {
+			fd->transparency = LOGICVC_ALPHA_CLUT_16BPP;
+		} else if (!strcmp(string, "clut32")) {
+			fd->transparency = LOGICVC_ALPHA_CLUT_32BPP;
+		} else if (!strcmp(string, "layer")) {
+			fd->transparency = LOGICVC_ALPHA_LAYER;
+		} else if (!strcmp(string, "pixel")) {
+			fd->transparency = LOGICVC_ALPHA_PIXEL;
+		} else {
+			dev_err(dev, "unsupported layer transparency\n");
+			return -EINVAL;
+		}
+	}
+
+	if (of_property_read_bool(dn, "component-swap"))
+		fd->component_swap = true;
+
+	fd->width = data->pixel_stride;
+
+	ret = xylonfb_layer_set_format(fd, dev);
+	if (ret) {
+		dev_err(dev, "failed set layer format\n");
+		return ret;
+	}
+
+	of_node_put(dn);
+
+	return id + 1;
+}
+
+static int xylon_parse_hw_info(struct device_node *dn,
+			       struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	int ret;
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	ret = of_property_read_u32(dn, "background-layer-bits-per-pixel",
+				   &data->bg_layer_bpp);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get bg-layer-bits-per-pixel\n");
+		return ret;
+	} else if (ret == 0) {
+		data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER;
+
+		ret = of_property_read_string(dn, "background-layer-type",
+					      &string);
+		if (ret) {
+			dev_err(dev, "failed get bg-layer-type\n");
+			return ret;
+		}
+		if (!strcmp(string, "rgb")) {
+			data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_RGB;
+		} else if (!strcmp(string, "yuv")) {
+			data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_YUV;
+		} else {
+			dev_err(dev, "unsupported bg layer type\n");
+			return -EINVAL;
+		}
+	}
+
+	if (of_property_read_bool(dn, "display-interface-itu656"))
+		data->flags |= XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656;
+
+	if (of_property_read_bool(dn, "readable-regs"))
+		data->flags |= XYLONFB_FLAGS_READABLE_REGS;
+	else
+		dev_warn(dev, "logicvc registers not readable\n");
+
+	if (of_property_read_bool(dn, "size-position"))
+		data->flags |= XYLONFB_FLAGS_SIZE_POSITION;
+	else
+		dev_warn(dev, "logicvc size-position disabled\n");
+
+	ret = of_property_read_u32(dn, "pixel-stride", &data->pixel_stride);
+	if (ret) {
+		dev_err(dev, "failed get pixel-stride\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "power-delay", &data->pwr_delay);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get power-delay\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "signal-delay", &data->sig_delay);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get signal\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id logicvc_of_match[] = {
+	{ .compatible = "xylon,logicvc-3.02.b" },
+	{ .compatible = "xylon,logicvc-4.01.b" },
+	{/* end of table */}
+};
+
+static int xylonfb_get_logicvc_configuration(struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn = data->device;
+	const struct of_device_id *match;
+	struct videomode vm;
+	int i, ret;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	match = of_match_node(logicvc_of_match, dn);
+	if (!match) {
+		dev_err(dev, "failed match logicvc\n");
+		return -ENODEV;
+	}
+
+	ret = of_address_to_resource(dn, 0, &data->resource_mem);
+	if (ret) {
+		dev_err(dev, "failed get mem resource\n");
+		return ret;
+	}
+	data->irq = of_irq_to_resource(dn, 0, &data->resource_irq);
+	if (data->irq == 0) {
+		dev_err(dev, "failed get irq resource\n");
+		return ret;
+	}
+
+	ret = xylon_parse_hw_info(dn, data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < LOGICVC_MAX_LAYERS; i++) {
+		ret = xylonfb_parse_layer_info(dn, data, i);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			break;
+	}
+
+	if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER &&
+	    data->layers == LOGICVC_MAX_LAYERS) {
+		data->flags &= ~XYLONFB_FLAGS_BACKGROUND_LAYER;
+		data->layers--;
+		if (data->console_layer == data->layers)
+			data->console_layer--;
+
+		dev_warn(dev, "invalid last layer configuration\n");
+	}
+
+	if (data->vm.name[0] == 0) {
+		ret = of_get_videomode(dn, &vm, OF_USE_NATIVE_MODE);
+		if (!ret) {
+			fb_videomode_from_videomode(&vm, &data->vm.vmode);
+
+			sprintf(data->vm.name, "%dx%d",
+				data->vm.vmode.xres, data->vm.vmode.yres);
+
+			data->flags |= XYLONFB_FLAGS_VMODE_CUSTOM;
+		}
+	}
+
+	xylonfb_init_ctrl(dn, vm.flags, &data->vm.ctrl);
+
+	return 0;
+}
+
+static int xylonfb_get_driver_configuration(struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn = data->pdev->dev.of_node;
+	int ret;
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	data->device = of_parse_phandle(dn, "device", 0);
+	if (!data->device) {
+		dev_err(dev, "failed get device\n");
+		return -ENODEV;
+	}
+
+	data->pixel_clock = of_parse_phandle(dn, "clocks", 0);
+
+	ret = of_property_read_u32(dn, "console-layer", &data->console_layer);
+	if (ret && (ret != -EINVAL)) {
+			dev_err(dev, "failed get console-layer\n");
+			return ret;
+	} else {
+		data->flags |= XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+	}
+
+	if (of_property_read_bool(dn, "vsync-irq"))
+		data->flags |= XYLONFB_FLAGS_VSYNC_IRQ;
+
+	ret = of_property_read_string(dn, "video-mode", &string);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get video-mode\n");
+		return ret;
+	} else if (ret == 0) {
+		strcpy(data->vm.name, string);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int xylonfb_probe(struct platform_device *pdev)
+{
+	struct xylonfb_data *data;
+	int ret;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	data = devm_kzalloc(&pdev->dev, sizeof(struct xylonfb_data),
+			     GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "failed allocate init data\n");
+		return -ENOMEM;
+	}
+
+	data->pdev = pdev;
+
+	ret = xylonfb_get_driver_configuration(data);
+	if (ret)
+		goto xylonfb_probe_error;
+
+	ret = xylonfb_get_logicvc_configuration(data);
+	if (ret)
+		goto xylonfb_probe_error;
+
+	ret = xylonfb_init_core(data);
+
+xylonfb_probe_error:
+	return ret;
+}
+
+static int xylonfb_remove(struct platform_device *pdev)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	return xylonfb_deinit_core(pdev);
+}
+
+static const struct of_device_id xylonfb_of_match[] = {
+	{ .compatible = "xylon,fb-3.00.a" },
+	{/* end of table */},
+};
+MODULE_DEVICE_TABLE(of, xylonfb_of_match);
+
+static struct platform_driver xylonfb_driver = {
+	.probe = xylonfb_probe,
+	.remove = xylonfb_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = XYLONFB_DEVICE_NAME,
+		.of_match_table = xylonfb_of_match,
+	},
+};
+
+static int xylonfb_get_params(char *options)
+{
+	char *this_opt;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+		xylonfb_mode_option = this_opt;
+	}
+	return 0;
+}
+
+static int xylonfb_init(void)
+{
+	char *option = NULL;
+	/*
+	 *  Kernel boot options (in 'video=xxxfb:<options>' format)
+	 */
+	if (fb_get_options(XYLONFB_DRIVER_NAME, &option))
+		return -ENODEV;
+	/* Set internal module parameters */
+	xylonfb_get_params(option);
+
+	if (platform_driver_register(&xylonfb_driver)) {
+		pr_err("failed %s driver registration\n", XYLONFB_DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit xylonfb_exit(void)
+{
+	platform_driver_unregister(&xylonfb_driver);
+}
+
+module_init(xylonfb_init);
+module_exit(xylonfb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(XYLONFB_DRIVER_DESCRIPTION);
+MODULE_VERSION(XYLONFB_DRIVER_VERSION);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 02/10] fbdev: xylon: Framebuffer driver core
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
  2014-10-17 13:06 ` [PATCH 01/10] fbdev: " Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl Davor Joja
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver core functionality.

Signed-off-by: Davor Joja <davorjoja@logicbricks.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..b20cedd
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.c
@@ -0,0 +1,1691 @@
+/*
+ * Xylon logiCVC frame buffer driver core functions
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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@%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@%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] =
+					(((t[id] & 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;
+	} else {
+		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@%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;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (fd->component_swap) {
+		reg = ld->data->reg_access.get_reg_val(ld->base,
+						       LOGICVC_LAYER_CTRL_ROFF,
+						       ld);
+		reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_BIT_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@%dbpp",
+			 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) {
+				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..e41ed0c
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.h
@@ -0,0 +1,252 @@
+/*
+ * Xylon logiCVC frame buffer driver internal data structures
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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 DEBUG_LEVEL	1
+#define CORE		1
+#define INFO		2
+
+#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


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
  2014-10-17 13:06 ` [PATCH 01/10] fbdev: " Davor Joja
  2014-10-17 13:06 ` [PATCH 02/10] fbdev: xylon: Framebuffer driver core Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock Davor Joja
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver IOCTL providing advanced video controller functionality to be
used by user space applications.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/xylonfb_ioctl.c | 676 ++++++++++++++++++++++++++++++
 1 file changed, 676 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_ioctl.c

diff --git a/drivers/video/fbdev/xylon/xylonfb_ioctl.c b/drivers/video/fbdev/xylon/xylonfb_ioctl.c
new file mode 100644
index 0000000..296aef80
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_ioctl.c
@@ -0,0 +1,676 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL functionality
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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/platform_device.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/xylonfb.h>
+
+#include "logicvc.h"
+#include "xylonfb_core.h"
+
+static int xylonfb_get_vblank(struct fb_vblank *vblank, struct fb_info *fbi)
+{
+	vblank->flags |= FB_VBLANK_HAVE_VSYNC;
+
+	return 0;
+}
+
+static void xylonfb_vsync_ctrl(struct fb_info *fbi, bool enable)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	u32 imr;
+
+	mutex_lock(&data->irq_mutex);
+
+	imr = data->reg_access.get_reg_val(data->dev_base,
+					   LOGICVC_INT_MASK_ROFF, ld);
+	if (enable) {
+		imr &= (~LOGICVC_INT_V_SYNC);
+		writel(LOGICVC_INT_V_SYNC,
+		       data->dev_base + LOGICVC_INT_STAT_ROFF);
+	} else {
+		imr |= LOGICVC_INT_V_SYNC;
+	}
+
+	data->reg_access.set_reg_val(imr, data->dev_base,
+				     LOGICVC_INT_MASK_ROFF, ld);
+
+	mutex_unlock(&data->irq_mutex);
+}
+
+int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	int ret, count;
+
+	mutex_lock(&data->irq_mutex);
+
+	count = data->vsync.count;
+
+	ret = wait_event_interruptible_timeout(data->vsync.wait,
+					       (count != data->vsync.count),
+					       HZ/10);
+
+	mutex_unlock(&data->irq_mutex);
+
+	if (ret < 0)
+		return ret;
+	else if (ret == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static unsigned int alpha_normalized(unsigned int alpha, unsigned int used_bits,
+				     bool set)
+{
+	if (set)
+		return alpha / (255 / ((1 << used_bits) - 1));
+	else
+		return (((255 << 16) / ((1 << used_bits) - 1)) * alpha) >> 16;
+}
+
+static int xylonfb_layer_alpha(struct xylonfb_layer_data *ld, u8 *alpha,
+			       bool set)
+{
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	unsigned int used_bits;
+	u32 val;
+
+	if (fd->transparency != LOGICVC_ALPHA_LAYER)
+		return -EPERM;
+
+	switch (fd->type) {
+	case LOGICVC_LAYER_YUV:
+		used_bits = 8;
+		break;
+	case LOGICVC_LAYER_RGB:
+		switch (fd->bpp) {
+		case 8:
+			used_bits = 3;
+			break;
+		case 16:
+			used_bits = 6;
+			break;
+		case 32:
+			used_bits = 8;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!set) {
+		val = data->reg_access.get_reg_val(ld->base,
+						   LOGICVC_LAYER_ALPHA_ROFF,
+						   ld);
+		*alpha = (u8)(val & (0xFF >> (8 - used_bits)));
+	}
+
+	/* get/set normalized alpha value */
+	*alpha = alpha_normalized(*alpha, used_bits, set);
+
+	if (set)
+		data->reg_access.set_reg_val(*alpha, ld->base,
+					     LOGICVC_LAYER_ALPHA_ROFF,
+					     ld);
+
+	return 0;
+}
+
+static int xylonfb_layer_buff(struct fb_info *fbi,
+			      struct xylonfb_layer_buffer *layer_buff,
+			      bool set)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	unsigned int layer_id = ld->fd->id;
+	u32 reg;
+
+	if (set) {
+		if (layer_buff->id >= LOGICVC_MAX_LAYER_BUFFERS)
+			return -EINVAL;
+
+		reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+		reg |= (1 << (10 + layer_id));
+		reg &= ~(0x03 << (layer_id << 1));
+		reg |= (layer_buff->id << (layer_id << 1));
+		writel(reg, ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+
+		xylonfb_vsync_wait(0, fbi);
+	} else {
+		reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+		reg >>= ((layer_id << 1));
+		layer_buff->id = reg & 0x03;
+	}
+
+	return 0;
+}
+
+static void xylonfb_rgb_yuv(u32 c1, u32 c2, u32 c3, u32 *pixel,
+			    struct xylonfb_layer_data *ld, bool rgb2yuv)
+{
+	struct xylonfb_data *data = ld->data;
+	u32 r, g, b, y, u, v;
+
+	if (rgb2yuv) {
+		y = ((data->coeff.cyr * c1) + (data->coeff.cyg * c2) +
+		     (data->coeff.cyb * c3) + data->coeff.cy) /
+		     LOGICVC_YUV_NORM;
+		u = ((-data->coeff.cur * c1) - (data->coeff.cug * c2) +
+		     (data->coeff.cub * c3) + LOGICVC_COEFF_U) /
+		     LOGICVC_YUV_NORM;
+		v = ((data->coeff.cvr * c1) - (data->coeff.cvg * c2) -
+		     (data->coeff.cvb * c3) + LOGICVC_COEFF_V) /
+		     LOGICVC_YUV_NORM;
+
+		*pixel = (0xFF << 24) | (y << 16) | (u << 8) | v;
+	} else {
+		r = ((c1 * LOGICVC_RGB_NORM) + (LOGICVC_COEFF_R_U * c2) -
+		     LOGICVC_COEFF_R) / LOGICVC_RGB_NORM;
+		g = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_G_U * c2) -
+		     (LOGICVC_COEFF_G_V * c3) + LOGICVC_COEFF_G) /
+		     LOGICVC_RGB_NORM;
+		b = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_B_V * c3) -
+		     LOGICVC_COEFF_B) / LOGICVC_RGB_NORM;
+
+		*pixel = (0xFF << 24) | (r << 16) | (g << 8) | b;
+	}
+}
+
+static int xylonfb_layer_color_rgb(struct xylonfb_layer_data *ld,
+				   struct xylonfb_layer_color *layer_color,
+				   unsigned int reg_offset, bool set)
+{
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	void __iomem *base;
+	u32 r = 0, g = 0, b = 0;
+	u32 raw_rgb, y, u, v;
+	int bpp, transparency;
+
+	if (reg_offset == LOGICVC_LAYER_TRANSP_COLOR_ROFF) {
+		base = ld->base;
+		bpp = fd->bpp;
+		transparency = fd->transparency;
+	} else /* if (reg_offset == LOGICVC_BACKGROUND_COLOR_ROFF) */ {
+		base = data->dev_base;
+		bpp = data->bg_layer_bpp;
+		transparency = -1;
+	}
+
+	if (set) {
+		if (layer_color->use_raw) {
+			raw_rgb = layer_color->raw_rgb;
+		} else if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+			r = layer_color->r;
+			g = layer_color->g;
+			b = layer_color->b;
+			xylonfb_rgb_yuv(r, g, b, &raw_rgb, ld, true);
+		} else {
+			r = layer_color->r;
+			g = layer_color->g;
+			b = layer_color->b;
+check_bpp_set:
+			switch (bpp) {
+			case 8:
+				switch (transparency) {
+				case LOGICVC_ALPHA_CLUT_16BPP:
+					bpp = 16;
+					goto check_bpp_set;
+					break;
+				case LOGICVC_ALPHA_CLUT_32BPP:
+					bpp = 32;
+					goto check_bpp_set;
+					break;
+				default:
+					raw_rgb = (r & 0xE0) |
+						   ((g & 0xE0) >> 3) |
+						   ((b & 0xC0) >> 6);
+					break;
+				}
+				break;
+			case 16:
+				raw_rgb = ((r & 0xF8) << 8) |
+					   ((g & 0xFC) << 3) |
+					   ((b & 0xF8) >> 3);
+				break;
+			case 32:
+				raw_rgb = (r << 16) | (g << 8) | b;
+				break;
+			default:
+				raw_rgb = 0;
+			}
+		}
+		data->reg_access.set_reg_val(raw_rgb, base, reg_offset, ld);
+	} else {
+		raw_rgb = data->reg_access.get_reg_val(base, reg_offset, ld);
+check_bpp_get:
+		if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+			y = (raw_rgb >> 16) & 0xFF;
+			u = (raw_rgb >> 8) & 0xFF;
+			v = raw_rgb & 0xFF;
+			xylonfb_rgb_yuv(y, u, v, &raw_rgb, ld, false);
+		} else {
+			switch (bpp) {
+			case 8:
+				switch (transparency) {
+				case LOGICVC_ALPHA_CLUT_16BPP:
+					bpp = 16;
+					goto check_bpp_get;
+					break;
+				case LOGICVC_ALPHA_CLUT_32BPP:
+					bpp = 32;
+					goto check_bpp_get;
+					break;
+				default:
+					r = raw_rgb >> 5;
+					r = (((r << 3) | r) << 2) | (r >> 1);
+					g = (raw_rgb >> 2) & 0x07;
+					g = (((g << 3) | g) << 2) | (g >> 1);
+					b = raw_rgb & 0x03;
+					b = (b << 6) | (b << 4) | (b << 2) | b;
+					break;
+				}
+				break;
+			case 16:
+				r = raw_rgb >> 11;
+				r = (r << 3) | (r >> 2);
+				g = (raw_rgb >> 5) & 0x3F;
+				g = (g << 2) | (g >> 4);
+				b = raw_rgb & 0x1F;
+				b = (b << 3) | (b >> 2);
+				break;
+			case 32:
+				r = raw_rgb >> 16;
+				g = (raw_rgb >> 8) & 0xFF;
+				b = raw_rgb & 0xFF;
+				break;
+			default:
+				raw_rgb = r = g = b = 0;
+			}
+		}
+		layer_color->raw_rgb = raw_rgb;
+		layer_color->r = (u8)r;
+		layer_color->g = (u8)g;
+		layer_color->b = (u8)b;
+	}
+
+	return 0;
+}
+
+static int xylonfb_layer_geometry(struct fb_info *fbi,
+				  struct xylonfb_layer_geometry *layer_geometry,
+				  bool set)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	u32 x, y, width, height, xoff, yoff, xres, yres;
+
+	xres = fbi->var.xres;
+	yres = fbi->var.yres;
+
+	if (set) {
+		x = layer_geometry->x;
+		y = layer_geometry->y;
+		width = layer_geometry->width;
+		height = layer_geometry->height;
+
+		if ((x > xres) || (y > yres))
+			return -EINVAL;
+
+		if ((width == 0) || (height == 0))
+			return -EINVAL;
+
+		if ((x + width) > xres) {
+			width = xres - x;
+			layer_geometry->width = width;
+		}
+		if ((y + height) > yres) {
+			height = yres - y;
+			layer_geometry->height = height;
+		}
+		/* YUV 4:2:2 layer type can only have even layer width */
+		if ((width > 2) && (fd->type == LOGICVC_LAYER_YUV) &&
+		    (fd->bpp == 16))
+			width &= ~((unsigned long) + 1);
+
+		/*
+		 * logiCVC 3.x registers write sequence:
+		 * offset, size, position with implicit last write to
+		 * LOGICVC_LAYER_VPOS_ROFF
+		 * logiCVC 4.x registers write sequence:
+		 * size, position with implicit last write to
+		 * LOGICVC_LAYER_ADDR_ROFF
+		 */
+		if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+			data->reg_access.set_reg_val(layer_geometry->x_offset,
+						     ld->base,
+						     LOGICVC_LAYER_HOFF_ROFF,
+						     ld);
+			data->reg_access.set_reg_val(layer_geometry->y_offset,
+						     ld->base,
+						     LOGICVC_LAYER_VOFF_ROFF,
+						     ld);
+		}
+		data->reg_access.set_reg_val((width - 1), ld->base,
+					     LOGICVC_LAYER_HSIZE_ROFF,
+					     ld);
+		data->reg_access.set_reg_val((height - 1), ld->base,
+					     LOGICVC_LAYER_VSIZE_ROFF,
+					     ld);
+		data->reg_access.set_reg_val((xres - (x + 1)), ld->base,
+					     LOGICVC_LAYER_HPOS_ROFF,
+					     ld);
+		data->reg_access.set_reg_val((yres - (y + 1)), ld->base,
+					     LOGICVC_LAYER_VPOS_ROFF,
+					     ld);
+		if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+			xoff = layer_geometry->x_offset * (ld->fd->bpp / 8);
+			yoff = layer_geometry->y_offset * ld->fd->width *
+			       (ld->fd->bpp / 8);
+
+			ld->fb_pbase_active = ld->fb_pbase + xoff + yoff;
+
+			data->reg_access.set_reg_val(ld->fb_pbase_active,
+						     ld->base,
+						     LOGICVC_LAYER_ADDR_ROFF,
+						     ld);
+		}
+	} else {
+		x = data->reg_access.get_reg_val(ld->base,
+						 LOGICVC_LAYER_HPOS_ROFF,
+						 ld);
+		layer_geometry->x = xres - (x + 1);
+		y = data->reg_access.get_reg_val(ld->base,
+						 LOGICVC_LAYER_VPOS_ROFF,
+						 ld);
+		layer_geometry->y = yres - (y + 1);
+		layer_geometry->width =
+			data->reg_access.get_reg_val(ld->base,
+						     LOGICVC_LAYER_HSIZE_ROFF,
+						     ld);
+		layer_geometry->width += 1;
+		layer_geometry->height =
+			data->reg_access.get_reg_val(ld->base,
+						     LOGICVC_LAYER_VSIZE_ROFF,
+						     ld);
+		layer_geometry->height += 1;
+	}
+
+	return 0;
+}
+
+static int xylonfb_layer_reg_access(struct xylonfb_layer_data *ld,
+				    struct xylonfb_hw_access *hw_access,
+				    bool set)
+{
+	struct xylonfb_data *data = ld->data;
+	struct xylonfb_layer_fix_data *fd = ld->fd;
+	u32 offset;
+	u32 rel_offset;
+
+	if ((hw_access->offset < LOGICVC_LAYER_BASE_OFFSET) ||
+	    (hw_access->offset > LOGICVC_LAYER_BASE_END))
+		return -EPERM;
+
+	if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+		offset = hw_access->offset;
+		if (set)
+			data->reg_access.set_reg_val(hw_access->value,
+						     data->dev_base,
+						     offset,
+						     ld);
+		else
+			hw_access->value =
+				data->reg_access.get_reg_val(data->dev_base,
+							     offset,
+							     ld);
+		return 0;
+	}
+
+	rel_offset = hw_access->offset - (fd->id * 0x80) -
+		     LOGICVC_LAYER_BASE_OFFSET;
+
+	if (rel_offset > LOGICVC_LAYER_BASE_END)
+		return -EINVAL;
+
+	if (set)
+		data->reg_access.set_reg_val(hw_access->value, ld->base,
+					     rel_offset, ld);
+	else
+		hw_access->value = data->reg_access.get_reg_val(ld->base,
+								rel_offset, ld);
+
+	return 0;
+}
+
+int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+	struct xylonfb_layer_data *ld = fbi->par;
+	struct xylonfb_data *data = ld->data;
+	union {
+		struct fb_vblank vblank;
+		struct xylonfb_hw_access hw_access;
+		struct xylonfb_layer_buffer layer_buff;
+		struct xylonfb_layer_color layer_color;
+		struct xylonfb_layer_geometry layer_geometry;
+		struct xylonfb_layer_transparency layer_transp;
+	} ioctl;
+	void __user *argp = (void __user *)arg;
+	unsigned long val;
+	u32 var32;
+	int ret = 0;
+	bool flag;
+
+	switch (cmd) {
+	case FBIOGET_VBLANK:
+		if (copy_from_user(&ioctl.vblank, argp, sizeof(ioctl.vblank)))
+			return -EFAULT;
+
+		ret = xylonfb_get_vblank(&ioctl.vblank, fbi);
+		if (!ret &&
+		    copy_to_user(argp, &ioctl.vblank, sizeof(ioctl.vblank)))
+			ret = -EFAULT;
+		break;
+
+	case FBIO_WAITFORVSYNC:
+		if (get_user(var32, (u32 __user *)arg))
+			return -EFAULT;
+
+		ret = xylonfb_vsync_wait(var32, fbi);
+		break;
+
+	case XYLONFB_VSYNC_CTRL:
+		if (get_user(flag, (u8 __user *)arg))
+			return -EFAULT;
+
+		xylonfb_vsync_ctrl(fbi, flag);
+		break;
+
+	case XYLONFB_LAYER_IDX:
+		var32 = ld->fd->id;
+		put_user(var32, (u32 __user *)arg);
+		break;
+
+	case XYLONFB_LAYER_ALPHA:
+		if (copy_from_user(&ioctl.layer_transp, argp,
+				   sizeof(ioctl.layer_transp)))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		ret = xylonfb_layer_alpha(ld, &ioctl.layer_transp.alpha,
+					  ioctl.layer_transp.set);
+		if (!ret && !ioctl.layer_transp.set)
+			if (copy_to_user(argp, &ioctl.layer_transp,
+					 sizeof(ioctl.layer_transp)))
+				ret = -EFAULT;
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_COLOR_TRANSP_CTRL:
+		if (get_user(flag, (u8 __user *)arg))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		var32 = data->reg_access.get_reg_val(ld->base,
+						     LOGICVC_LAYER_CTRL_ROFF,
+						     ld);
+		if (flag)
+			var32 |= LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+		else
+			var32 &= ~LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+		data->reg_access.set_reg_val(var32, ld->base,
+					     LOGICVC_LAYER_CTRL_ROFF,
+					     ld);
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_COLOR_TRANSP:
+		if (copy_from_user(&ioctl.layer_color, argp,
+				   sizeof(ioctl.layer_color)))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+					      LOGICVC_LAYER_TRANSP_COLOR_ROFF,
+					      ioctl.layer_color.set);
+		if (!ret && !ioctl.layer_color.set)
+			if (copy_to_user(argp, &ioctl.layer_color,
+					 sizeof(ioctl.layer_color)))
+				ret = -EFAULT;
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_GEOMETRY:
+		if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+			return -EINVAL;
+
+		if (copy_from_user(&ioctl.layer_geometry, argp,
+				   sizeof(ioctl.layer_geometry)))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		ret = xylonfb_layer_geometry(fbi, &ioctl.layer_geometry,
+					     ioctl.layer_geometry.set);
+		if (!ret && !ioctl.layer_geometry.set)
+			if (copy_to_user(argp, &ioctl.layer_geometry,
+					 sizeof(ioctl.layer_geometry)))
+				ret = -EFAULT;
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_BUFFER:
+		if (data->major >= 4)
+			return -EPERM;
+
+		if (copy_from_user(&ioctl.layer_buff, argp,
+				   sizeof(ioctl.layer_buff)))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		ret = xylonfb_layer_buff(fbi, &ioctl.layer_buff,
+					 ioctl.layer_buff.set);
+		if (!ret && !ioctl.layer_buff.set)
+			if (copy_to_user(argp, &ioctl.layer_buff,
+					 sizeof(ioctl.layer_buff)))
+				ret = -EFAULT;
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_BUFFER_OFFSET:
+		if (data->major < 4) {
+			var32 = readl(ld->data->dev_base +
+				      LOGICVC_VBUFF_SELECT_ROFF);
+			var32 >>= (ld->fd->id << 1);
+			var32 &= 0x03;
+			val = ld->fd->buffer_offset;
+			val *= var32;
+		} else {
+			val = ld->fd->buffer_offset;
+		}
+		put_user(val, (unsigned long __user *)arg);
+		break;
+
+	case XYLONFB_BACKGROUND_COLOR:
+		if (data->bg_layer_bpp == 0)
+			return -EPERM;
+
+		if (copy_from_user(&ioctl.layer_color, argp,
+				   sizeof(ioctl.layer_color)))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+					      LOGICVC_BACKGROUND_COLOR_ROFF,
+					      ioctl.layer_color.set);
+		if (!ret && !ioctl.layer_color.set)
+			if (copy_to_user(argp, &ioctl.layer_color,
+					 sizeof(ioctl.layer_color)))
+				ret = -EFAULT;
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_LAYER_EXT_BUFF_SWITCH:
+		if (get_user(flag, (u8 __user *)arg))
+			return -EFAULT;
+
+		mutex_lock(&ld->mutex);
+		var32 = data->reg_access.get_reg_val(ld->base,
+						     LOGICVC_LAYER_CTRL_ROFF,
+						     ld);
+		if (flag)
+			var32 |= LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+		else
+			var32 &= ~LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+		data->reg_access.set_reg_val(var32, ld->base,
+					     LOGICVC_LAYER_CTRL_ROFF,
+					     ld);
+		mutex_unlock(&ld->mutex);
+		break;
+
+	case XYLONFB_HW_ACCESS:
+		if (copy_from_user(&ioctl.hw_access, argp,
+				   sizeof(ioctl.hw_access)))
+			return -EFAULT;
+
+		ret = xylonfb_layer_reg_access(ld, &ioctl.hw_access,
+					       ioctl.hw_access.set);
+		if (!ret && !ioctl.hw_access.set)
+			if (copy_to_user(argp, &ioctl.hw_access,
+					 sizeof(ioctl.hw_access)))
+				ret = -EFAULT;
+		break;
+
+	case XYLONFB_IP_CORE_VERSION:
+		var32 = (data->major << 16) | (data->minor << 8) | data->patch;
+		if (copy_to_user(argp, &var32, sizeof(u32)))
+			ret = -EFAULT;
+		break;
+
+	default:
+		dev_err(&data->pdev->dev, "unknown ioctl");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (2 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions Davor Joja
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver pixel clock control functionality supporting Xylon logiCLK IP
core clock generator.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/xylonfb_pixclk.c | 115 +++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_pixclk.c

diff --git a/drivers/video/fbdev/xylon/xylonfb_pixclk.c b/drivers/video/fbdev/xylon/xylonfb_pixclk.c
new file mode 100644
index 0000000..5f82b50
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_pixclk.c
@@ -0,0 +1,115 @@
+/*
+ * Xylon logiCVC frame buffer driver pixel clock control
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+struct xylonfb_pixclk {
+	struct device *dev;
+	struct device_node *dn;
+	struct clk *clk;
+};
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+static const struct of_device_id logiclk_of_match[] = {
+	{ .compatible = "xylon,logiclk-1.02.b" },
+	{/* end of table */}
+};
+static struct xylonfb_pixclk logiclk;
+#endif
+
+bool xylonfb_hw_pixclk_supported(struct device *dev, struct device_node *dn);
+void xylonfb_hw_pixclk_unload(struct device_node *dn);
+int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+			  unsigned long pixclk_khz);
+
+#if defined(CONFIG_FB_XYLON_PIXCLK)
+static int xylonfb_hw_pixclk_set_freq(struct device *dev,
+				      struct device_node *dn,
+				      unsigned long freq_khz)
+{
+	struct clk *clk = NULL;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+	if (dn == logiclk.dn)
+		clk = logiclk.clk;
+#endif
+
+	if (clk && clk_set_rate(clk, (freq_khz * 1000))) {
+		dev_err(dev, "failed set pixel clock frequency\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif
+
+bool xylonfb_hw_pixclk_supported(struct device *dev, struct device_node *dn)
+{
+	struct clk **clk;
+	bool clk_dev = false;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+	if (of_match_node(logiclk_of_match, of_get_parent(dn))) {
+		clk = &logiclk.clk;
+		logiclk.dev = dev;
+		logiclk.dn = dn;
+		clk_dev = true;
+	}
+#endif
+
+	if (clk_dev) {
+		*clk = devm_clk_get(dev, NULL);
+		if (IS_ERR(*clk)) {
+			dev_err(dev, "failed get pixel clock\n");
+			return false;
+		}
+		if (clk_prepare_enable(*clk)) {
+			dev_err(dev,
+				"failed prepare/enable pixel clock\n");
+			return false;
+		}
+
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void xylonfb_hw_pixclk_unload(struct device_node *dn)
+{
+	struct clk *clk = NULL;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+	clk = logiclk.clk;
+#endif
+
+	if (clk)
+		clk_disable_unprepare(clk);
+}
+
+int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+			  unsigned long pixclk_khz)
+{
+#if defined(CONFIG_FB_XYLON_PIXCLK)
+	return xylonfb_hw_pixclk_set_freq(dev, dn, pixclk_khz);
+#else
+	dev_warn(dev, "pixel clock control not supported\n");
+	return -ENODEV;
+#endif
+}
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (3 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration Davor Joja
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver hw parameters and definitions for logiCVC IP core.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/logicvc.h | 192 ++++++++++++++++++++++++++++++++++++
 1 file changed, 192 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/logicvc.h

diff --git a/drivers/video/fbdev/xylon/logicvc.h b/drivers/video/fbdev/xylon/logicvc.h
new file mode 100644
index 0000000..07b28b5
--- /dev/null
+++ b/drivers/video/fbdev/xylon/logicvc.h
@@ -0,0 +1,192 @@
+/*
+ * Xylon logiCVC IP core parameters
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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 __LOGICVC_H__
+#define __LOGICVC_H__
+
+/*
+ * All logiCVC registers are only 32-bit accessible.
+ * All logiCVC registers are aligned to 8 byte boundary.
+ */
+#define LOGICVC_REG_STRIDE		8
+#define LOGICVC_HSYNC_FRONT_PORCH_ROFF	(0  * LOGICVC_REG_STRIDE)
+#define LOGICVC_HSYNC_ROFF		(1  * LOGICVC_REG_STRIDE)
+#define LOGICVC_HSYNC_BACK_PORCH_ROFF	(2  * LOGICVC_REG_STRIDE)
+#define LOGICVC_HRES_ROFF		(3  * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_FRONT_PORCH_ROFF	(4  * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_ROFF		(5  * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_BACK_PORCH_ROFF	(6  * LOGICVC_REG_STRIDE)
+#define LOGICVC_VRES_ROFF		(7  * LOGICVC_REG_STRIDE)
+#define LOGICVC_CTRL_ROFF		(8  * LOGICVC_REG_STRIDE)
+#define LOGICVC_DTYPE_ROFF		(9  * LOGICVC_REG_STRIDE)
+#define LOGICVC_BACKGROUND_COLOR_ROFF	(10 * LOGICVC_REG_STRIDE)
+#define LOGICVC_CLUT_SELECT_ROFF	(12 * LOGICVC_REG_STRIDE)
+#define LOGICVC_INT_STAT_ROFF		(13 * LOGICVC_REG_STRIDE)
+#define LOGICVC_INT_MASK_ROFF		(14 * LOGICVC_REG_STRIDE)
+#define LOGICVC_POWER_CTRL_ROFF		(15 * LOGICVC_REG_STRIDE)
+#define LOGICVC_IP_VERSION_ROFF		(31 * LOGICVC_REG_STRIDE)
+/* Version 3.x */
+#define LOGICVC_VBUFF_SELECT_ROFF	(11 * LOGICVC_REG_STRIDE)
+
+/*
+ * logiCVC layer registers offsets (common for each layer)
+ * Last possible logiCVC layer (No.4) implements only "Layer memory address"
+ * and "Layer control" registers.
+ */
+/* Version 3.x */
+#define LOGICVC_LAYER_HOFF_ROFF		(0 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VOFF_ROFF		(1 * LOGICVC_REG_STRIDE)
+/* Version 4.x */
+#define LOGICVC_LAYER_ADDR_ROFF		(0 * LOGICVC_REG_STRIDE)
+/* Common */
+#define LOGICVC_LAYER_HPOS_ROFF		(2 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VPOS_ROFF		(3 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_HSIZE_ROFF	(4 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VSIZE_ROFF	(5 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_ALPHA_ROFF	(6 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_CTRL_ROFF		(7 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_TRANSP_COLOR_ROFF	(8 * LOGICVC_REG_STRIDE)
+
+/* logiCVC interrupt bits */
+#define LOGICVC_INT_L0_UPDATED		(1 << 0)
+#define LOGICVC_INT_L1_UPDATED		(1 << 1)
+#define LOGICVC_INT_L2_UPDATED		(1 << 2)
+#define LOGICVC_INT_L3_UPDATED		(1 << 3)
+#define LOGICVC_INT_L4_UPDATED		(1 << 4)
+#define LOGICVC_INT_V_SYNC		(1 << 5)
+#define LOGICVC_INT_E_VIDEO_VALID	(1 << 6)
+#define LOGICVC_INT_FIFO_UNDERRUN	(1 << 7)
+#define LOGICVC_INT_L0_CLUT_SW		(1 << 8)
+#define LOGICVC_INT_L1_CLUT_SW		(1 << 9)
+#define LOGICVC_INT_L2_CLUT_SW		(1 << 10)
+#define LOGICVC_INT_L3_CLUT_SW		(1 << 11)
+#define LOGICVC_INT_L4_CLUT_SW		(1 << 12)
+
+/* logiCVC layer base offsets */
+#define LOGICVC_LAYER_OFFSET		0x80
+#define LOGICVC_LAYER_BASE_OFFSET	0x100
+#define LOGICVC_LAYER_BASE_END		0x338
+#define LOGICVC_LAYER_0_OFFSET		(0 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_1_OFFSET		(1 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_2_OFFSET		(2 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_3_OFFSET		(3 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_4_OFFSET		(4 * LOGICVC_LAYER_OFFSET)
+
+/* logiCVC layer CLUT base offsets */
+#define LOGICVC_CLUT_OFFSET		0x800
+#define LOGICVC_CLUT_BASE_OFFSET	0x1000
+#define LOGICVC_CLUT_L0_CLUT_0_OFFSET	(0 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L0_CLUT_1_OFFSET	(1 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L1_CLUT_0_OFFSET	(2 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L1_CLUT_1_OFFSET	(3 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L2_CLUT_0_OFFSET	(4 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L2_CLUT_1_OFFSET	(5 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L3_CLUT_0_OFFSET	(6 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L3_CLUT_1_OFFSET	(7 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L4_CLUT_0_OFFSET	(8 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L4_CLUT_1_OFFSET	(9 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_REGISTER_SIZE	8
+#define LOGICVC_CLUT_0_INDEX_OFFSET	2
+#define LOGICVC_CLUT_1_INDEX_OFFSET	1
+
+/* logiCVC registers range */
+#define LOGICVC_REGISTERS_RANGE		0x6000
+#define LOGICVC_LAYER_REGISTERS_RANGE	0x80
+
+/* logiCVC control register bits */
+#define LOGICVC_CTRL_HSYNC			(1 << 0)
+#define LOGICVC_CTRL_HSYNC_INVERT		(1 << 1)
+#define LOGICVC_CTRL_VSYNC			(1 << 2)
+#define LOGICVC_CTRL_VSYNC_INVERT		(1 << 3)
+#define LOGICVC_CTRL_DATA_ENABLE		(1 << 4)
+#define LOGICVC_CTRL_DATA_ENABLE_INVERT		(1 << 5)
+#define LOGICVC_CTRL_PIXEL_DATA_INVERT		(1 << 7)
+#define LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT	(1 << 8)
+#define LOGICVC_CTRL_DISABLE_LAYER_UPDATE	(1 << 9)
+
+/* logiCVC layer control register bits */
+#define LOGICVC_LAYER_CTRL_ENABLE			(1 << 0)
+#define LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT	(1 << 1)
+#define LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH	(1 << 2)
+#define LOGICVC_LAYER_CTRL_INTERLACE_BIT		(1 << 3)
+#define LOGICVC_LAYER_CTRL_PIXEL_FORMAT_BIT_ABGR	(1 << 4)
+
+/* logiCVC register initial values */
+#define LOGICVC_DTYPE_REG_INIT	0
+
+/* logiCVC display power signals */
+#define LOGICVC_EN_BLIGHT_MSK	(1 << 0)
+#define LOGICVC_EN_VDD_MSK	(1 << 1)
+#define LOGICVC_EN_VEE_MSK	(1 << 2)
+#define LOGICVC_V_EN_MSK	(1 << 3)
+
+/* logiCVC various definitions */
+#define LOGICVC_MAJOR_REVISION_SHIFT	11
+#define LOGICVC_MAJOR_REVISION_MASK	0x3F
+#define LOGICVC_MINOR_REVISION_SHIFT	5
+#define LOGICVC_MINOR_REVISION_MASK	0x3F
+#define LOGICVC_PATCH_LEVEL_MASK	0x1F
+
+#define LOGICVC_MAX_LAYER_BUFFERS	3
+#define LOGICVC_MIN_XRES		64
+#define LOGICVC_MIN_VRES		1
+#define LOGICVC_MAX_XRES		2048
+#define LOGICVC_MAX_VRES		2048
+#define LOGICVC_CLUT_SIZE		256
+
+#define LOGICVC_COEFF_Y_R		29900
+#define LOGICVC_COEFF_Y_G		58700
+#define LOGICVC_COEFF_Y_B		11400
+#define LOGICVC_COEFF_Y			0
+#define LOGICVC_COEFF_U			12800000
+#define LOGICVC_COEFF_V			12800000
+#define LOGICVC_COEFF_U_R		16868
+#define LOGICVC_COEFF_U_G		33107
+#define LOGICVC_COEFF_U_B		49970
+#define LOGICVC_COEFF_V_R		49980
+#define LOGICVC_COEFF_V_G		41850
+#define LOGICVC_COEFF_V_B		8128
+#define LOGICVC_COEFF_ITU656_Y		1600000
+#define LOGICVC_COEFF_ITU656_U_R	17258
+#define LOGICVC_COEFF_ITU656_U_G	33881
+#define LOGICVC_COEFF_ITU656_U_B	51140
+#define LOGICVC_COEFF_ITU656_V_R	51138
+#define LOGICVC_COEFF_ITU656_V_G	42820
+#define LOGICVC_COEFF_ITU656_V_B	8316
+#define LOGICVC_YUV_NORM		100000
+#define LOGICVC_COEFF_R_U		1402524
+#define LOGICVC_COEFF_R			179000000
+#define LOGICVC_COEFF_G_U		714403
+#define LOGICVC_COEFF_G_V		344340
+#define LOGICVC_COEFF_G			135000000
+#define LOGICVC_COEFF_B_V		1773049
+#define LOGICVC_COEFF_B			226000000
+#define LOGICVC_RGB_NORM		1000000
+
+enum xylonfb_layer_type {
+	LOGICVC_LAYER_RGB,
+	LOGICVC_LAYER_YUV,
+	LOGICVC_LAYER_ALPHA
+};
+
+enum xylonfb_alpha_type {
+	LOGICVC_ALPHA_LAYER,
+	LOGICVC_ALPHA_PIXEL,
+	LOGICVC_ALPHA_CLUT_16BPP,
+	LOGICVC_ALPHA_CLUT_32BPP
+};
+
+#endif /* __LOGICVC_H__ */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (4 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file Davor Joja
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver kernel and build configuration.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/Kconfig  | 31 +++++++++++++++++++++++++++++++
 drivers/video/fbdev/xylon/Makefile |  3 +++
 2 files changed, 34 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/Kconfig
 create mode 100644 drivers/video/fbdev/xylon/Makefile

diff --git a/drivers/video/fbdev/xylon/Kconfig b/drivers/video/fbdev/xylon/Kconfig
new file mode 100644
index 0000000..74278ad
--- /dev/null
+++ b/drivers/video/fbdev/xylon/Kconfig
@@ -0,0 +1,31 @@
+menuconfig FB_XYLON
+	tristate "Xylon logiCVC frame buffer support"
+	depends on FB
+	default n
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select FB_MODE_HELPERS
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you want to use the Xylon logiCVC as frame
+	  buffer device. Without the support of PCI & AGP.
+
+config FB_XYLON_PIXCLK
+	bool "Xylon logiCVC pixel clock"
+	depends on FB_XYLON
+	default n
+	help
+	  logiCVC pixel clock generated from:
+	  - External generator not controllable by Xylon framebuffer driver
+	    This is default selection.
+	  - Generators controllable by Xylon framebuffer driver
+
+config FB_XYLON_PIXCLK_LOGICLK
+	bool "Xylon logiCLK pixel clock generator"
+	depends on FB_XYLON && FB_XYLON_PIXCLK
+	default n
+	select COMMON_CLK_LOGICLK
+	help
+	  Support for controlling pixel clock generation from
+	  Xylon logiCLK FGPA IP core.
diff --git a/drivers/video/fbdev/xylon/Makefile b/drivers/video/fbdev/xylon/Makefile
new file mode 100644
index 0000000..3baa96a
--- /dev/null
+++ b/drivers/video/fbdev/xylon/Makefile
@@ -0,0 +1,3 @@
+xylonfb-y := xylonfb_main.o xylonfb_core.o xylonfb_ioctl.o xylonfb_pixclk.o
+
+obj-$(CONFIG_FB_XYLON) += xylonfb.o
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (5 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:06 ` [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile Davor Joja
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Driver header file containing structures and IOCTLs for user space
apps to be able to use complete logiCVC hw functionality.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 include/uapi/linux/xylonfb.h | 85 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 include/uapi/linux/xylonfb.h

diff --git a/include/uapi/linux/xylonfb.h b/include/uapi/linux/xylonfb.h
new file mode 100644
index 0000000..5db8344
--- /dev/null
+++ b/include/uapi/linux/xylonfb.h
@@ -0,0 +1,85 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL parameters
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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_H__
+#define __XYLONFB_H__
+
+#include <linux/types.h>
+
+struct xylonfb_hw_access {
+	__u32 offset;
+	__u32 value;
+	bool set;
+};
+
+/* Used only with logiCVC 3.x */
+struct xylonfb_layer_buffer {
+	__u8 id;
+	bool set;
+};
+
+struct xylonfb_layer_color {
+	__u32 raw_rgb;
+	__u8 use_raw;
+	__u8 r;
+	__u8 g;
+	__u8 b;
+	bool set;
+};
+
+struct xylonfb_layer_geometry {
+	__u16 x;
+	__u16 y;
+	__u16 width;
+	__u16 height;
+	__u16 x_offset;
+	__u16 y_offset;
+	bool set;
+};
+
+struct xylonfb_layer_transparency {
+	__u8 alpha;
+	bool set;
+};
+
+/* Xylon FB IOCTL's */
+#define XYLONFB_IOW(num, dtype)		_IOW('x', num, dtype)
+#define XYLONFB_IOR(num, dtype)		_IOR('x', num, dtype)
+#define XYLONFB_IOWR(num, dtype)	_IOWR('x', num, dtype)
+#define XYLONFB_IO(num)			_IO('x', num)
+
+#define XYLONFB_VSYNC_CTRL		XYLONFB_IOR(30, bool)
+#define XYLONFB_LAYER_IDX		XYLONFB_IOR(31, unsigned int)
+#define XYLONFB_LAYER_ALPHA		\
+	XYLONFB_IOR(32, struct xylonfb_layer_transparency)
+#define XYLONFB_LAYER_COLOR_TRANSP_CTRL	XYLONFB_IOW(33, bool)
+#define XYLONFB_LAYER_COLOR_TRANSP \
+	XYLONFB_IOR(34, struct xylonfb_layer_color)
+#define XYLONFB_LAYER_GEOMETRY \
+	XYLONFB_IOR(35, struct xylonfb_layer_geometry)
+#define XYLONFB_LAYER_BUFFER \
+	XYLONFB_IOR(36, struct xylonfb_layer_buffer)
+#define XYLONFB_LAYER_BUFFER_OFFSET	XYLONFB_IOR(37, unsigned int)
+#define XYLONFB_BACKGROUND_COLOR \
+	XYLONFB_IOR(38, struct xylonfb_layer_color)
+#define XYLONFB_LAYER_EXT_BUFF_SWITCH	XYLONFB_IOW(39, bool)
+#define XYLONFB_HW_ACCESS \
+	XYLONFB_IOR(40, struct xylonfb_hw_access)
+#define XYLONFB_IP_CORE_VERSION		XYLONFB_IOR(41, __u32)
+#define XYLONFB_WAIT_EDID		XYLONFB_IOW(42, unsigned int)
+#define XYLONFB_GET_EDID		XYLONFB_IOR(43, char)
+
+#endif /* __XYLONFB_H__ */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (6 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file Davor Joja
@ 2014-10-17 13:06 ` Davor Joja
  2014-10-17 13:07 ` [PATCH 09/10] bindings: video: Xylon framebuffer driver binding Davor Joja
  2014-10-17 13:07 ` [PATCH 10/10] bindings: video: Xylon logiCVC IP core device binding Davor Joja
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Adding Xylon framebuffer driver to fbdev Kconfig and Makefile.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/Kconfig  | 1 +
 drivers/video/fbdev/Makefile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index ccbe2ae..9fb8dcc 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2473,6 +2473,7 @@ source "drivers/video/fbdev/omap/Kconfig"
 source "drivers/video/fbdev/omap2/Kconfig"
 source "drivers/video/fbdev/exynos/Kconfig"
 source "drivers/video/fbdev/mmp/Kconfig"
+source "drivers/video/fbdev/xylon/Kconfig"
 
 config FB_SH_MOBILE_MERAM
 	tristate "SuperH Mobile MERAM read ahead support"
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 1979aff..537fcb2 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_FB_JZ4740)		  += jz4740_fb.o
 obj-$(CONFIG_FB_PUV3_UNIGFX)      += fb-puv3.o
 obj-$(CONFIG_FB_HYPERV)		  += hyperv_fb.o
 obj-$(CONFIG_FB_OPENCORES)	  += ocfb.o
+obj-y                             += xylon/
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 09/10] bindings: video: Xylon framebuffer driver binding
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (7 preceding siblings ...)
  2014-10-17 13:06 ` [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile Davor Joja
@ 2014-10-17 13:07 ` Davor Joja
  2014-10-17 13:07 ` [PATCH 10/10] bindings: video: Xylon logiCVC IP core device binding Davor Joja
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:07 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Xylon framebuffer driver binding description.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 .../devicetree/bindings/video/xylon-fb.txt         | 52 ++++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/xylon-fb.txt

diff --git a/Documentation/devicetree/bindings/video/xylon-fb.txt b/Documentation/devicetree/bindings/video/xylon-fb.txt
new file mode 100644
index 0000000..665166f
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/xylon-fb.txt
@@ -0,0 +1,52 @@
+Binding for Xylon FB driver
+
+Xylon FB driver supports Xylon configurable video controller
+logiCVC FPGA IP core device.
+
+Required properties:
+ - compatible: value must be "xylon,fb-3.00.a"
+ - device: the phandle for logiCVC video controller device
+
+Optional properties:
+ - clocks: the phandle for the pixel clock generator
+ - console-layer: layer ID for FB console (0 - 4)
+      If omitted, FB console started on default layer ID 0.
+      Note: FB console can be started only on layer configured with one
+      of parameters combination below:
+      ======================================
+      bits-per-pixel    type    transparency
+      ======================================
+             8           yuv       clut32
+             8           rgb       clut16
+             8           rgb       clut32
+             8           rgb       layer
+            16           rgb       layer
+            32           rgb       layer
+            32           rgb       pixel
+      ======================================
+
+ - edid-video-mode: video mode set to preferred EDID resolution
+      If omitted, configured according to "video-mode" property.
+ - edid-print: prints EDID description in system log
+      Must be used only with "edid-video-mode".
+      If omitted, functionality is not available.
+ - vsync-irq: generate interrupt on vertical synchronization pulse
+ - video-mode: preferred video mode resolution
+      If omitted, configures logiCVC to default video resolution "1024x768".
+   - M: Coordinated Video Timings (CVT)
+   - R: reduced video timings for digital displays
+   - i: calculate for an interlaced mode
+   - m: add margins to calculation calculation
+        (1.8% of xres rounded down to 8 pixels and 1.8% of yres).
+Example:
+
+	xylon_fb {
+		compatible = "xylon,fb-3.00.a";
+		clocks = <&clkout_0>;
+		device = <&logicvc_0>;
+		console-layer = <0>;
+		edid-video-mode;
+		edid-print;
+		vsync-irq;
+		video-mode = "1920x1080MR";
+	};
\ No newline at end of file
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 10/10] bindings: video: Xylon logiCVC IP core device binding
  2014-10-17 13:06 Xylon framebuffer driver Davor Joja
                   ` (8 preceding siblings ...)
  2014-10-17 13:07 ` [PATCH 09/10] bindings: video: Xylon framebuffer driver binding Davor Joja
@ 2014-10-17 13:07 ` Davor Joja
  9 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2014-10-17 13:07 UTC (permalink / raw)
  To: linux-kernel; +Cc: Davor Joja

Xylon logiCVC IP core binding description.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 .../devicetree/bindings/video/xylon-logicvc.txt    | 207 +++++++++++++++++++++
 1 file changed, 207 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/xylon-logicvc.txt

diff --git a/Documentation/devicetree/bindings/video/xylon-logicvc.txt b/Documentation/devicetree/bindings/video/xylon-logicvc.txt
new file mode 100644
index 0000000..12f24c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/xylon-logicvc.txt
@@ -0,0 +1,207 @@
+Binding for Xylon logiCVC configurable video controller IP core
+
+Required properties:
+ - compatible: "xylon,logicvc-4.00.a", "xylon,logicvc-4.01.a"
+ - reg: MMIO base address and size of the logiCVC IP core address space
+ - interrupts-parent: the phandle for interrupt controller
+ - interrupts: the interrupt number
+ - pixel-stride: layer width in pixels
+      Common for all available logiCVC standard layers.
+
+ - layer_N: layer has its own configuration described with below properties,
+      where N is layer ID in range 0 - 4
+   - bits-per-pixel: layer bits per pixel configuration (8, 16, 32)
+         Layer is configured to be used with specified pixel width in bits.
+         Pixels written to layer video memory must match in size to configured
+         "bits-per-pixel" value.
+         Note: Allowed "transparency" for values of "bits-per-pixel":
+
+         ===============================================
+             bits-per-pixel             transparency
+         ===============================================
+                   8                       clut16
+                   8                       clut32
+                   8                       layer
+                   16                      layer
+                   32                      layer, pixel
+         ===============================================
+
+   - type: layer type (rgb, yuv, alpha)
+         Layer is configured to be used with specified color space.
+         Pixels written to layer video memory must match specified color format.
+         Type is set to "alpha" if layer contains only pixel alpha values
+         used by layer above. Permitted layer IDs for configuring layer alpha
+         are 1 and 3.
+   - transparency: layer transparency (clut16, clut32, layer, pixel)
+         logiCVC layer can be configured to have transparency control on
+         CLUT, layer or pixel level.
+         "CLUT" mode enables controlling of layer transparency by changing
+         alpha value in logiCVC CLUT registers.
+         "Layer" mode enables controlling of layer transparency by changing
+         alpha value in single logiCVC register.
+         "Pixel" mode enables controlling of pixel transparency by changing
+         dedicated alpha bits of specific pixel in video memory.
+         Note: "transparency" property is not used if "type" property is set
+         to "alpha".
+
+Optional properties:
+ - background-layer-bits-per-pixel: background layer bits per pixel (8, 16, 32)
+      If omitted, last available layer is logiCVC standard layer, which has its
+      own video memory of specific size, color format and specified
+      bits per pixel.
+      If 8, 16 or 32, last available layer is logiCVC background layer,
+      with only specified bits per pixel value.
+ - background-layer-type: background layer type (rgb, yuv)
+      Must be used only when "background-layer-bits-per-pixel" exist.
+      If set to "rgb", in case of 32 bits per pixel, background color register
+      must be written with XRGB8888 value. In case of 16 bits per pixel,
+      background color register must be written with RGB565 value.
+      If set to "yuv", background color register must be written with XYUV8888
+      value.
+ - hsync-active-low: horizontal synchronization pulse is active low "L"
+      If omitted, generated horizontal synchronization pulse polarity is logic
+      high "H".
+ - vsync-active-low: vertical synchronization pulse is active low "L"
+      If omitted, generated vertical synchronization pulse polarity is logic
+      high "H".
+ - pixel-data-invert: output pixel data polarity is inverted
+      If omitted, logiCVC outputs pixel data at default interface polarity.
+ - pixel-data-output-trigger-high: output pixel data triggers on rising edge
+      If omitted, logiCVC outputs pixel data on falling edge of pixel clock.
+ - readable-regs: all logiCVC registers are available for reading
+      If omitted, only Interrupt Status, Power Control and IP Version registers
+      are available for reading.
+ - size-position: logiCVC functionality for controlling on screen layer size
+      and position available
+      If omitted, functionality not available.
+ - component-swap: swap the order of colour components inside the pixel
+ - data-enable-active-low: data enable signal is active low "L"
+      If omitted, generated data enable polarity is logic high "H".
+ - display-interface-itu656: modifies RGB to YUV conversion coefficients
+ - address: layer video memory address for layer_N where
+      N is layer ID in range 0 - 4.
+      logiCVC can be configured to have layer video memory address hardcoded
+      as layer register reset value. This video memory is not part of the system
+      memory. Still it is accessible by CPU and HW devices.
+      If omitted, layer video memory address is set dynamically by
+      device driver.Layer video memory address can be changed dynamically
+      despite the hardcoded address.
+      Note: Additionally, address range parameter can be set. It defines range
+      of video memory in bytes starting from provided address. It is mandatory
+      for physically last layer in memory. If range parameter is omitted for
+      physically last layer, range will be by default set to 2048 lines.
+      If omitted for any other layer then physically last layer,
+      range will be calculated according to address of next physical layer.
+ - buffer-offset: buffer address offset represented in number of lines
+      Used only for HW buffer switching.
+      If omitted, buffer offset variable is by default set to "0".
+ - power-delay: delay in ms after enabling display power supply
+      If omitted, delay is by default set to "0".
+ - signal-delay: delay in ms after enabling display control and data signals
+      in parallel and LVDS interface
+      If omitted, delay is by default set to "0".
+ - display-timings: custom display video mode timing parameters
+      If omitted, driver will use its default video mode timings.
+      native-mode optional parameter determines which video mode timings from
+      the list are used. If native-mode parameter is omitted, first available
+      video mode timings are used.
+
+Example:
+
+	logicvc_0: logicvc@40000000 {
+		compatible = "xylon,logicvc-4.00.a";
+		reg = <0x40030000 0x6000>;
+		interrupt-parent = <&ps7_scugic_0>;
+		interrupts = <0 59 4>;
+		background-layer-bits-per-pixel = <32>;
+		background-layer-type = "rgb";
+		display-interface-itu656;
+		hsync-active-low;
+		vsync-active-low;
+		data-enable-active-low;
+		pixel-data-invert;
+		pixel-data-output-trigger-high;
+		readable-regs;
+		size-position;
+		pixel-stride = <2048>;
+		power-delay = <100>;
+		signal-delay = <50>;
+		layer_0 {
+			address = <0x30000000>
+			buffer-offset = <512>;
+			bits-per-pixel = <16>;
+			type = "yuv";
+			transparency = "layer";
+			component-swap;
+		};
+		layer_1 {
+			address = <0x30600000>;
+			bits-per-pixel = <32>;
+			type = "rgb";
+			transparency = "pixel";
+		};
+		layer_2 {
+			address = <0x31F50000>;
+			bits-per-pixel = <8>;
+			type = "rgb";
+			transparency = "clut32";
+		};
+		layer_3 {
+			address = <0x3216C000 0xCA8000>;
+			bits-per-pixel = <16>;
+			type = "rgb";
+			transparency = "layer";
+		};
+
+		display-timings {
+			native-mode = <&wsxga>;
+			hd720p: 1280x720 {
+				clock-frequency = <74250000>;
+				hactive = <1280>;
+				vactive = <720>;
+				hfront-porch = <110>;
+				hback-porch = <220>;
+				hsync-len = <40>;
+				vfront-porch = <5>;
+				vback-porch = <20>;
+				vsync-len = <5>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <1>;
+				pixelclk-active = <1>;
+			};
+			wsxga: 1680x1050 {
+				clock-frequency = <119000000>;
+				hactive = <1680>;
+				vactive = <1050>;
+				hfront-porch = <48>;
+				hback-porch = <80>;
+				hsync-len = <32>;
+				vfront-porch = <3>;
+				vback-porch = <21>;
+				vsync-len = <6>;
+			};
+			hd1080p: 1920x1080 {
+				clock-frequency = <148500000>;
+				hactive = <1920>;
+				vactive = <1080>;
+				hfront-porch = <88>;
+				hback-porch = <148>;
+				hsync-len = <44>;
+				vfront-porch = <4>;
+				vback-porch = <36>;
+				vsync-len = <5>;
+			};
+			TM050RBH01: 800x480 {
+				clock-frequency = <30000000>;
+				hactive = <800>;
+				vactive = <480>;
+				hfront-porch = <40>;
+				hback-porch = <40>;
+				hsync-len = <48>;
+				vfront-porch = <13>;
+				vback-porch = <29>;
+				vsync-len = <3>;
+			};
+		};
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 01/10] fbdev: Xylon framebuffer driver
  2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
  0 siblings, 0 replies; 12+ messages in thread
From: Davor Joja @ 2015-03-11 15:27 UTC (permalink / raw)
  To: linux-kernel, linux-fbdev, tomi.valkeinen, geert; +Cc: Davor Joja

Driver registration and OF binding.

Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
 drivers/video/fbdev/xylon/xylonfb_main.c | 528 +++++++++++++++++++++++++++++++
 1 file changed, 528 insertions(+)
 create mode 100644 drivers/video/fbdev/xylon/xylonfb_main.c

diff --git a/drivers/video/fbdev/xylon/xylonfb_main.c b/drivers/video/fbdev/xylon/xylonfb_main.c
new file mode 100644
index 0000000..3313da5
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_main.c
@@ -0,0 +1,528 @@
+/*
+ * Xylon logiCVC frame buffer Open Firmware driver
+ *
+ * Copyright (C) 2015 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.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/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+static void xylonfb_init_ctrl(struct device_node *dn, enum display_flags flags,
+			      u32 *ctrl)
+{
+	u32 ctrl_reg = (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+			LOGICVC_CTRL_DATA_ENABLE);
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (of_property_read_bool(dn, "hsync-active-low") ||
+	    (flags & DISPLAY_FLAGS_HSYNC_LOW))
+		ctrl_reg |= LOGICVC_CTRL_HSYNC_INVERT;
+	if (of_property_read_bool(dn, "vsync-active-low") ||
+	    (flags & DISPLAY_FLAGS_VSYNC_LOW))
+		ctrl_reg |= LOGICVC_CTRL_VSYNC_INVERT;
+	if (of_property_read_bool(dn, "data-enable-active-low") ||
+	    (flags & DISPLAY_FLAGS_DE_LOW))
+		ctrl_reg |= LOGICVC_CTRL_DATA_ENABLE_INVERT;
+	if (of_property_read_bool(dn, "pixel-data-invert"))
+		ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_INVERT;
+	if (of_property_read_bool(dn, "pixel-data-output-trigger-high") ||
+	    (flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+		ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT;
+
+	*ctrl = ctrl_reg;
+}
+
+static int xylonfb_layer_set_format(struct xylonfb_layer_fix_data *fd,
+				    struct device *dev)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	switch (fd->type) {
+	case LOGICVC_LAYER_ALPHA:
+		fd->format = XYLONFB_FORMAT_A8;
+		break;
+
+	case LOGICVC_LAYER_RGB:
+		switch (fd->bpp) {
+		case 8:
+			switch (fd->transparency) {
+			case LOGICVC_ALPHA_CLUT_16BPP:
+				fd->format = XYLONFB_FORMAT_C8;
+				fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB6565;
+				break;
+			case LOGICVC_ALPHA_CLUT_32BPP:
+				fd->format = XYLONFB_FORMAT_C8;
+				fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB8888;
+				break;
+			case LOGICVC_ALPHA_LAYER:
+				fd->format = XYLONFB_FORMAT_RGB332;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case 16:
+			if (fd->transparency != LOGICVC_ALPHA_LAYER)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_RGB565;
+			break;
+		case 32:
+			switch (fd->transparency) {
+			case LOGICVC_ALPHA_LAYER:
+				fd->format = XYLONFB_FORMAT_XRGB8888;
+				break;
+			case LOGICVC_ALPHA_PIXEL:
+				fd->format = XYLONFB_FORMAT_ARGB8888;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		}
+		break;
+
+	case LOGICVC_LAYER_YUV:
+		switch (fd->bpp) {
+		case 8:
+			if (fd->transparency != LOGICVC_ALPHA_CLUT_32BPP)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_C8;
+			fd->format_clut = XYLONFB_FORMAT_CLUT_AYUV8888;
+			break;
+		case 16:
+			if (fd->transparency != LOGICVC_ALPHA_LAYER)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_YUYV;
+			break;
+		case 32:
+			if (fd->transparency != LOGICVC_ALPHA_PIXEL)
+				return -EINVAL;
+
+			fd->format = XYLONFB_FORMAT_AYUV;
+			break;
+		}
+		break;
+
+	default:
+		dev_err(dev, "unsupported layer type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int xylonfb_parse_layer_info(struct device_node *parent_dn,
+				    struct xylonfb_data *data, int id)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn;
+	struct xylonfb_layer_fix_data *fd;
+	int ret;
+	char layer_name[10];
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	snprintf(layer_name, sizeof(layer_name), "layer_%d", id);
+	dn = of_get_child_by_name(parent_dn, layer_name);
+	if (!dn)
+		return 0;
+
+	data->layers++;
+
+	fd = devm_kzalloc(&data->pdev->dev,
+			  sizeof(struct xylonfb_layer_fix_data), GFP_KERNEL);
+	if (!fd) {
+		dev_err(dev, "failed allocate layer fix data (%d)\n", id);
+		return -ENOMEM;
+	}
+
+	data->fd[id] = fd;
+
+	fd->id = id;
+
+	ret = of_property_read_u32(dn, "address", &fd->address);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get address\n");
+		return ret;
+	}
+	ret = of_property_read_u32_index(dn, "address", 1, &fd->address_range);
+
+	ret = of_property_read_u32(dn, "buffer-offset", &fd->buffer_offset);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get buffer-offset\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "bits-per-pixel", &fd->bpp);
+	if (ret) {
+		dev_err(dev, "failed get bits-per-pixel\n");
+		return ret;
+	}
+	switch (fd->bpp) {
+	case 8:
+	case 16:
+	case 32:
+		break;
+	default:
+		dev_err(dev, "invalid bits-per-pixel value\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_string(dn, "type", &string);
+	if (ret) {
+		dev_err(dev, "failed get type\n");
+		return ret;
+	}
+	if (!strcmp(string, "alpha")) {
+		fd->type = LOGICVC_LAYER_ALPHA;
+	} else if (!strcmp(string, "rgb")) {
+		fd->type = LOGICVC_LAYER_RGB;
+	} else if (!strcmp(string, "yuv")) {
+		fd->type = LOGICVC_LAYER_YUV;
+	} else {
+		dev_err(dev, "unsupported layer type\n");
+		return -EINVAL;
+	}
+
+	if (fd->type != LOGICVC_LAYER_ALPHA) {
+		ret = of_property_read_string(dn, "transparency", &string);
+		if (ret) {
+			dev_err(dev, "failed get transparency\n");
+			return ret;
+		}
+		if (!strcmp(string, "clut16")) {
+			fd->transparency = LOGICVC_ALPHA_CLUT_16BPP;
+		} else if (!strcmp(string, "clut32")) {
+			fd->transparency = LOGICVC_ALPHA_CLUT_32BPP;
+		} else if (!strcmp(string, "layer")) {
+			fd->transparency = LOGICVC_ALPHA_LAYER;
+		} else if (!strcmp(string, "pixel")) {
+			fd->transparency = LOGICVC_ALPHA_PIXEL;
+		} else {
+			dev_err(dev, "unsupported layer transparency\n");
+			return -EINVAL;
+		}
+	}
+
+	if (of_property_read_bool(dn, "component-swap"))
+		fd->component_swap = true;
+
+	fd->width = data->pixel_stride;
+
+	ret = xylonfb_layer_set_format(fd, dev);
+	if (ret) {
+		dev_err(dev, "failed set layer format\n");
+		return ret;
+	}
+
+	of_node_put(dn);
+
+	return id + 1;
+}
+
+static int xylon_parse_hw_info(struct device_node *dn,
+			       struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	int ret;
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	ret = of_property_read_u32(dn, "background-layer-bits-per-pixel",
+				   &data->bg_layer_bpp);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get bg-layer-bits-per-pixel\n");
+		return ret;
+	} else if (ret == 0) {
+		data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER;
+
+		ret = of_property_read_string(dn, "background-layer-type",
+					      &string);
+		if (ret) {
+			dev_err(dev, "failed get bg-layer-type\n");
+			return ret;
+		}
+		if (!strcmp(string, "rgb")) {
+			data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_RGB;
+		} else if (!strcmp(string, "yuv")) {
+			data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_YUV;
+		} else {
+			dev_err(dev, "unsupported bg layer type\n");
+			return -EINVAL;
+		}
+	}
+
+	if (of_property_read_bool(dn, "display-interface-itu656"))
+		data->flags |= XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656;
+
+	if (of_property_read_bool(dn, "readable-regs"))
+		data->flags |= XYLONFB_FLAGS_READABLE_REGS;
+	else
+		dev_warn(dev, "logicvc registers not readable\n");
+
+	if (of_property_read_bool(dn, "size-position"))
+		data->flags |= XYLONFB_FLAGS_SIZE_POSITION;
+	else
+		dev_warn(dev, "logicvc size-position disabled\n");
+
+	ret = of_property_read_u32(dn, "pixel-stride", &data->pixel_stride);
+	if (ret) {
+		dev_err(dev, "failed get pixel-stride\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "power-delay", &data->pwr_delay);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get power-delay\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dn, "signal-delay", &data->sig_delay);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get signal\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id logicvc_of_match[] = {
+	{ .compatible = "xylon,logicvc-3.00.a" },
+	{ .compatible = "xylon,logicvc-4.00.a" },
+	{/* end of table */}
+};
+
+static int xylonfb_get_logicvc_configuration(struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn = data->device;
+	const struct of_device_id *match;
+	struct videomode vm;
+	int i, ret;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	match = of_match_node(logicvc_of_match, dn);
+	if (!match) {
+		dev_err(dev, "failed match logicvc\n");
+		return -ENODEV;
+	}
+
+	ret = of_address_to_resource(dn, 0, &data->resource_mem);
+	if (ret) {
+		dev_err(dev, "failed get mem resource\n");
+		return ret;
+	}
+	data->irq = of_irq_to_resource(dn, 0, &data->resource_irq);
+	if (data->irq == 0) {
+		dev_err(dev, "failed get irq resource\n");
+		return ret;
+	}
+
+	ret = xylon_parse_hw_info(dn, data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < LOGICVC_MAX_LAYERS; i++) {
+		ret = xylonfb_parse_layer_info(dn, data, i);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			break;
+	}
+
+	if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER &&
+	    data->layers == LOGICVC_MAX_LAYERS) {
+		data->flags &= ~XYLONFB_FLAGS_BACKGROUND_LAYER;
+		data->layers--;
+		if (data->console_layer == data->layers)
+			data->console_layer--;
+
+		dev_warn(dev, "invalid last layer configuration\n");
+	}
+
+	memset(&vm, 0, sizeof(vm));
+
+	if (data->vm.name[0] == 0) {
+		ret = of_get_videomode(dn, &vm, OF_USE_NATIVE_MODE);
+		if (!ret) {
+			fb_videomode_from_videomode(&vm, &data->vm.vmode);
+
+			sprintf(data->vm.name, "%dx%d",
+				data->vm.vmode.xres, data->vm.vmode.yres);
+
+			data->flags |= XYLONFB_FLAGS_VMODE_CUSTOM;
+		}
+	}
+
+	xylonfb_init_ctrl(dn, vm.flags, &data->vm.ctrl);
+
+	return 0;
+}
+
+static int xylonfb_get_driver_configuration(struct xylonfb_data *data)
+{
+	struct device *dev = &data->pdev->dev;
+	struct device_node *dn = data->pdev->dev.of_node;
+	int ret;
+	const char *string;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	data->device = of_parse_phandle(dn, "device", 0);
+	if (!data->device) {
+		dev_err(dev, "failed get device\n");
+		return -ENODEV;
+	}
+
+	data->pixel_clock = of_parse_phandle(dn, "clocks", 0);
+
+	ret = of_property_read_u32(dn, "console-layer", &data->console_layer);
+	if (ret && (ret != -EINVAL)) {
+			dev_err(dev, "failed get console-layer\n");
+			return ret;
+	} else {
+		data->flags |= XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+	}
+
+	if (of_property_read_bool(dn, "vsync-irq"))
+		data->flags |= XYLONFB_FLAGS_VSYNC_IRQ;
+
+	ret = of_property_read_string(dn, "video-mode", &string);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "failed get video-mode\n");
+		return ret;
+	} else if (ret == 0) {
+		strcpy(data->vm.name, string);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int xylonfb_probe(struct platform_device *pdev)
+{
+	struct xylonfb_data *data;
+	int ret;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	data = devm_kzalloc(&pdev->dev, sizeof(struct xylonfb_data),
+			     GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "failed allocate init data\n");
+		return -ENOMEM;
+	}
+
+	data->pdev = pdev;
+
+	ret = xylonfb_get_driver_configuration(data);
+	if (ret)
+		goto xylonfb_probe_error;
+
+	ret = xylonfb_get_logicvc_configuration(data);
+	if (ret)
+		goto xylonfb_probe_error;
+
+	ret = xylonfb_init_core(data);
+
+xylonfb_probe_error:
+	return ret;
+}
+
+static int xylonfb_remove(struct platform_device *pdev)
+{
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	return xylonfb_deinit_core(pdev);
+}
+
+static const struct of_device_id xylonfb_of_match[] = {
+	{ .compatible = "xylon,fb-3.00.a" },
+	{/* end of table */},
+};
+MODULE_DEVICE_TABLE(of, xylonfb_of_match);
+
+static struct platform_driver xylonfb_driver = {
+	.probe = xylonfb_probe,
+	.remove = xylonfb_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = XYLONFB_DEVICE_NAME,
+		.of_match_table = xylonfb_of_match,
+	},
+};
+
+static int xylonfb_get_params(char *options)
+{
+	char *this_opt;
+
+	XYLONFB_DBG(INFO, "%s", __func__);
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+		xylonfb_mode_option = this_opt;
+	}
+	return 0;
+}
+
+static int xylonfb_init(void)
+{
+	char *option = NULL;
+	/*
+	 *  Kernel boot options (in 'video=xxxfb:<options>' format)
+	 */
+	if (fb_get_options(XYLONFB_DRIVER_NAME, &option))
+		return -ENODEV;
+	/* Set internal module parameters */
+	xylonfb_get_params(option);
+
+	if (platform_driver_register(&xylonfb_driver)) {
+		pr_err("failed %s driver registration\n", XYLONFB_DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit xylonfb_exit(void)
+{
+	platform_driver_unregister(&xylonfb_driver);
+}
+
+module_init(xylonfb_init);
+module_exit(xylonfb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(XYLONFB_DRIVER_DESCRIPTION);
+MODULE_VERSION(XYLONFB_DRIVER_VERSION);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-03-11 15:33 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-17 13:06 Xylon framebuffer driver Davor Joja
2014-10-17 13:06 ` [PATCH 01/10] fbdev: " Davor Joja
2014-10-17 13:06 ` [PATCH 02/10] fbdev: xylon: Framebuffer driver core Davor Joja
2014-10-17 13:06 ` [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl Davor Joja
2014-10-17 13:06 ` [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock Davor Joja
2014-10-17 13:06 ` [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions Davor Joja
2014-10-17 13:06 ` [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration Davor Joja
2014-10-17 13:06 ` [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file Davor Joja
2014-10-17 13:06 ` [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile Davor Joja
2014-10-17 13:07 ` [PATCH 09/10] bindings: video: Xylon framebuffer driver binding Davor Joja
2014-10-17 13:07 ` [PATCH 10/10] bindings: video: Xylon logiCVC IP core device binding Davor Joja
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
2015-03-11 15:27 ` [PATCH 01/10] fbdev: Xylon " Davor Joja

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).