* [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
2015-03-11 15:27 ` [PATCH 02/10] fbdev: xylon: Framebuffer driver core Davor Joja
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ 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] 11+ messages in thread
* [PATCH 02/10] fbdev: xylon: Framebuffer driver core
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
2015-03-11 15:27 ` [PATCH 01/10] fbdev: Xylon " Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl Davor Joja
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ 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 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..9970215
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.c
@@ -0,0 +1,1691 @@
+/*
+ * Xylon logiCVC frame buffer driver core functions
+ *
+ * Copyright (C) 2015 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@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] + (((a & 0xFF) << to) |
+ ((r[id] & 0xFF) << ro) |
+ ((g[id] & 0xFF) << go) |
+ ((b[id] & 0xFF) << bo));
+ len--;
+ id++;
+ }
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int xylonfb_set_color(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *fbi)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ return xylonfb_set_color_hw((u16 *)&transp,
+ (u16 *)&red, (u16 *)&green, (u16 *)&blue,
+ 1, regno, fbi);
+}
+
+static int xylonfb_set_cmap(struct fb_cmap *cmap, struct fb_info *fbi)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ return xylonfb_set_color_hw(cmap->transp,
+ cmap->red, cmap->green, cmap->blue,
+ cmap->len, cmap->start, fbi);
+}
+
+static void xylonfb_set_pixels(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld,
+ int bpp, unsigned int pix)
+{
+ u32 *vmem;
+ u8 *vmem8;
+ u16 *vmem16;
+ u32 *vmem32;
+ int x, y, pixoffset;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ vmem = ld->fb_base + (fbi->var.xoffset * (fbi->var.bits_per_pixel/4)) +
+ (fbi->var.yoffset * fbi->var.xres_virtual *
+ (fbi->var.bits_per_pixel/4));
+
+ switch (bpp) {
+ case 8:
+ vmem8 = (u8 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem8[pixoffset + x] = pix;
+ }
+ break;
+ case 16:
+ vmem16 = (u16 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem16[pixoffset + x] = pix;
+ }
+ break;
+ case 32:
+ vmem32 = (u32 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem32[pixoffset + x] = pix;
+ }
+ break;
+ }
+}
+
+static int xylonfb_blank(int blank_mode, struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ void __iomem *dev_base = data->dev_base;
+ u32 ctrl = data->reg_access.get_reg_val(dev_base,
+ LOGICVC_CTRL_ROFF,
+ ld);
+ u32 power = readl(dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ XYLONFB_DBG(INFO, "FB_BLANK_UNBLANK");
+ ctrl |= (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+ LOGICVC_CTRL_DATA_ENABLE);
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ power |= LOGICVC_V_EN_MSK;
+ writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ mdelay(50);
+ break;
+
+ case FB_BLANK_NORMAL:
+ XYLONFB_DBG(INFO, "FB_BLANK_NORMAL");
+ switch (fd->format) {
+ case XYLONFB_FORMAT_C8:
+ xylonfb_set_color(0, 0, 0, 0, 0xFF, fbi);
+ xylonfb_set_pixels(fbi, ld, 8, 0);
+ break;
+ case XYLONFB_FORMAT_RGB332:
+ xylonfb_set_pixels(fbi, ld, 8, 0x00);
+ break;
+ case XYLONFB_FORMAT_RGB565:
+ xylonfb_set_pixels(fbi, ld, 16, 0x0000);
+ break;
+ case XYLONFB_FORMAT_XRGB8888:
+ case XYLONFB_FORMAT_ARGB8888:
+ xylonfb_set_pixels(fbi, ld, 32, 0xFF000000);
+ break;
+ }
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ XYLONFB_DBG(INFO, "FB_BLANK_POWERDOWN");
+ ctrl &= ~(LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+ LOGICVC_CTRL_DATA_ENABLE);
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ power &= ~LOGICVC_V_EN_MSK;
+ writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ mdelay(50);
+ break;
+
+ case FB_BLANK_VSYNC_SUSPEND:
+ XYLONFB_DBG(INFO, "FB_BLANK_VSYNC_SUSPEND");
+ ctrl &= ~LOGICVC_CTRL_VSYNC;
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+ break;
+
+ case FB_BLANK_HSYNC_SUSPEND:
+ XYLONFB_DBG(INFO, "FB_BLANK_HSYNC_SUSPEND");
+ ctrl &= ~LOGICVC_CTRL_HSYNC;
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+ break;
+ }
+
+ return 0;
+}
+
+static int xylonfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+ return -EINVAL;
+
+ if ((fbi->var.xoffset = var->xoffset) &&
+ (fbi->var.yoffset = var->yoffset))
+ return 0;
+
+ if (fbi->var.vmode & FB_VMODE_YWRAP)
+ return -EINVAL;
+
+ if (((var->xoffset + fbi->var.xres) > fbi->var.xres_virtual) ||
+ ((var->yoffset + fbi->var.yres) > fbi->var.yres_virtual))
+ return -EINVAL;
+
+ if (fd->format = XYLONFB_FORMAT_YUYV)
+ var->xoffset &= ~((unsigned long) + 1);
+
+ fbi->var.xoffset = var->xoffset;
+ fbi->var.yoffset = var->yoffset;
+
+ if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+ data->reg_access.set_reg_val(var->xoffset, ld->base,
+ LOGICVC_LAYER_HOFF_ROFF, ld);
+ data->reg_access.set_reg_val(var->yoffset, ld->base,
+ LOGICVC_LAYER_VOFF_ROFF, ld);
+ }
+ data->reg_access.set_reg_val((fbi->var.xres - 1), ld->base,
+ LOGICVC_LAYER_HPOS_ROFF, ld);
+ data->reg_access.set_reg_val((fbi->var.yres - 1), ld->base,
+ LOGICVC_LAYER_VPOS_ROFF, ld);
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+ ld->fb_pbase_active = ld->fb_pbase +
+ ((var->xoffset * (fd->bpp / 8)) +
+ (var->yoffset * fd->width *
+ (fd->bpp / 8)));
+ data->reg_access.set_reg_val(ld->fb_pbase_active, ld->base,
+ LOGICVC_LAYER_ADDR_ROFF, ld);
+ }
+
+ return 0;
+}
+
+static struct fb_ops xylonfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = xylonfb_open,
+ .fb_release = xylonfb_release,
+ .fb_check_var = xylonfb_check_var,
+ .fb_set_par = xylonfb_set_par,
+ .fb_setcolreg = xylonfb_set_color,
+ .fb_setcmap = xylonfb_set_cmap,
+ .fb_blank = xylonfb_blank,
+ .fb_pan_display = xylonfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_ioctl = xylonfb_ioctl,
+};
+
+static int xylonfb_find_next_layer(struct xylonfb_data *data, int layers,
+ int id)
+{
+ dma_addr_t address = data->fd[id]->address;
+ dma_addr_t temp_address = ((unsigned long) - 1);
+ dma_addr_t loop_address;
+ int next = -1;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ for (i = 0; i < layers; i++) {
+ loop_address = data->fd[i]->address;
+ if ((address < loop_address) && (loop_address < temp_address)) {
+ next = i;
+ temp_address = loop_address;
+ }
+ }
+
+ return next;
+}
+
+static void xylonfb_get_vmem_height(struct xylonfb_data *data, int layers,
+ int id)
+{
+ struct xylonfb_layer_fix_data *fd = data->fd[id];
+ dma_addr_t vmem_start = fd->address;
+ dma_addr_t vmem_end;
+ int next;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (fd->address_range && (id < (layers - 1))) {
+ fd->height = fd->address_range / (fd->width * (fd->bpp / 8));
+ return;
+ }
+
+ vmem_start = fd->address;
+
+ next = xylonfb_find_next_layer(data, layers, id);
+ if (next = -1) {
+ if (fd->address_range) {
+ fd->height = fd->address_range /
+ (fd->width * (fd->bpp / 8));
+ } else {
+ if (fd->buffer_offset)
+ fd->height = fd->buffer_offset *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ else
+ fd->height = XYLONFB_VRES_DEFAULT;
+ }
+ } else {
+ vmem_end = data->fd[next]->address;
+ fd->height = (vmem_end - vmem_start) /
+ (fd->width * (fd->bpp / 8));
+ }
+
+ if (fd->height > (LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS))
+ fd->height = LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS;
+}
+
+static void xylonfb_set_fbi_var_screeninfo(struct fb_var_screeninfo *var,
+ struct xylonfb_data *data)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ var->xres = data->vm_active.vmode.xres;
+ var->yres = data->vm_active.vmode.yres;
+ var->pixclock = data->vm_active.vmode.pixclock;
+ var->left_margin = data->vm_active.vmode.left_margin;
+ var->right_margin = data->vm_active.vmode.right_margin;
+ var->upper_margin = data->vm_active.vmode.upper_margin;
+ var->lower_margin = data->vm_active.vmode.lower_margin;
+ var->hsync_len = data->vm_active.vmode.hsync_len;
+ var->vsync_len = data->vm_active.vmode.vsync_len;
+ var->sync = data->vm_active.vmode.sync;
+ var->vmode = data->vm_active.vmode.vmode;
+}
+
+static void xylonfb_fbi_update(struct fb_info *fbi)
+{
+ struct fb_info **afbi = dev_get_drvdata(fbi->device);
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i, layers, id;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (!afbi)
+ return;
+
+ layers = data->layers;
+ id = ld->fd->id;
+
+ for (i = 0; i < layers; i++) {
+ if (i = id)
+ continue;
+
+ xylonfb_set_fbi_var_screeninfo(&afbi[i]->var, data);
+ afbi[i]->monspecs = afbi[id]->monspecs;
+ }
+}
+
+static void xylonfb_set_hw_specifics(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld,
+ struct xylonfb_layer_fix_data *fd)
+{
+ struct xylonfb_data *data = ld->data;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ fbi->fix.smem_start = ld->fb_pbase;
+ fbi->fix.smem_len = ld->fb_size;
+ if (fd->type = LOGICVC_LAYER_RGB) {
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ } else if (fd->type = LOGICVC_LAYER_YUV) {
+ if (fd->format = XYLONFB_FORMAT_C8)
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ else
+ fbi->fix.type = FB_TYPE_FOURCC;
+ }
+ if ((fd->type = LOGICVC_LAYER_YUV) ||
+ (fd->type = LOGICVC_LAYER_ALPHA)) {
+ if (fd->format = XYLONFB_FORMAT_C8)
+ fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ fbi->fix.visual = FB_VISUAL_FOURCC;
+ } else if (fd->format = XYLONFB_FORMAT_C8) {
+ fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ } else {
+ /*
+ * Other logiCVC layer pixel formats:
+ * - 8 bpp: LAYER or PIXEL alpha
+ * It is not true color, RGB triplet is stored in 8 bits.
+ * - 16 bpp:
+ * LAYER alpha: RGB triplet is stored in 16 bits
+ * - 32 bpp: LAYER or PIXEL alpha
+ * True color, RGB triplet or ARGB quadriplet
+ * is stored in 32 bits.
+ */
+ fbi->fix.visual = FB_VISUAL_TRUECOLOR;
+ }
+
+ fbi->fix.xpanstep = 1;
+ fbi->fix.ypanstep = 1;
+ fbi->fix.ywrapstep = 0;
+ fbi->fix.line_length = fd->width * (fd->bpp / 8);
+ fbi->fix.mmio_start = ld->pbase;
+ fbi->fix.mmio_len = LOGICVC_LAYER_REGISTERS_RANGE;
+ fbi->fix.accel = FB_ACCEL_NONE;
+
+ fbi->var.xres_virtual = fd->width;
+ fbi->var.yres_virtual = fd->height;
+
+ fbi->var.bits_per_pixel = fd->bpp;
+
+ switch (fd->type) {
+ case LOGICVC_LAYER_ALPHA:
+ fbi->var.grayscale = LOGICVC_PIX_FMT_ALPHA;
+ break;
+ case LOGICVC_LAYER_RGB:
+ fbi->var.grayscale = 0;
+ break;
+ case LOGICVC_LAYER_YUV:
+ if (fd->format = XYLONFB_FORMAT_C8) {
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+ } else if (fd->format = XYLONFB_FORMAT_YUYV) {
+ if (fd->component_swap)
+ fbi->var.grayscale = V4L2_PIX_FMT_VYUY;
+ else
+ fbi->var.grayscale = V4L2_PIX_FMT_YVYU;
+ } else if (fd->format = XYLONFB_FORMAT_AYUV) {
+ if (fd->component_swap)
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AVUY;
+ else
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+ }
+ break;
+ }
+
+ /*
+ * Set values according to logiCVC layer data width configuration:
+ * layer data width can be 1, 2, 4 bytes
+ */
+ if (fd->transparency = LOGICVC_ALPHA_LAYER) {
+ fbi->var.transp.offset = 0;
+ fbi->var.transp.length = 0;
+ }
+
+ switch (fd->format) {
+ case XYLONFB_FORMAT_A8:
+ fbi->var.transp.offset = 0;
+ fbi->var.transp.length = 8;
+ break;
+ case XYLONFB_FORMAT_C8:
+ switch (fd->format_clut) {
+ case XYLONFB_FORMAT_CLUT_ARGB6565:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 6;
+ fbi->var.red.offset = 19;
+ fbi->var.red.length = 5;
+ fbi->var.green.offset = 10;
+ fbi->var.green.length = 6;
+ fbi->var.blue.offset = 3;
+ fbi->var.blue.length = 5;
+ break;
+ case XYLONFB_FORMAT_CLUT_ARGB8888:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ case XYLONFB_FORMAT_CLUT_AYUV8888:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ }
+ break;
+ case XYLONFB_FORMAT_RGB332:
+ fbi->var.red.offset = 5;
+ fbi->var.red.length = 3;
+ fbi->var.green.offset = 2;
+ fbi->var.green.length = 3;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 2;
+ break;
+ case XYLONFB_FORMAT_RGB565:
+ fbi->var.red.offset = 11;
+ fbi->var.red.length = 5;
+ fbi->var.green.offset = 5;
+ fbi->var.green.length = 6;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 5;
+ break;
+ case XYLONFB_FORMAT_ARGB8888:
+ case XYLONFB_FORMAT_AYUV:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ case XYLONFB_FORMAT_XRGB8888:
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ }
+ fbi->var.transp.msb_right = 0;
+ fbi->var.red.msb_right = 0;
+ fbi->var.green.msb_right = 0;
+ fbi->var.blue.msb_right = 0;
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ fbi->var.height = 0;
+ fbi->var.width = 0;
+ fbi->var.sync = 0;
+ if (!(data->vm_active.ctrl & LOGICVC_CTRL_HSYNC_INVERT))
+ fbi->var.sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (!(data->vm_active.ctrl & LOGICVC_CTRL_VSYNC_INVERT))
+ fbi->var.sync |= FB_SYNC_VERT_HIGH_ACT;
+ fbi->var.rotate = 0;
+}
+
+static int xylonfb_set_timings(struct fb_info *fbi, int bpp)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct fb_var_screeninfo fb_var;
+ struct fb_videomode *vm;
+ int rc;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if ((data->flags & XYLONFB_FLAGS_VMODE_INIT) &&
+ (data->flags & XYLONFB_FLAGS_VMODE_CUSTOM) &&
+ memchr(data->vm.name, 'x', 10)) {
+ data->vm_active = data->vm;
+ vm = &data->vm.vmode;
+ data->vm_active.vmode.refresh + DIV_ROUND_CLOSEST((PICOS2KHZ(vm->pixclock) * 1000),
+ ((vm->xres + vm->left_margin +
+ vm->right_margin + vm->hsync_len) *
+ (vm->yres + vm->upper_margin +
+ vm->lower_margin + vm->vsync_len)));
+ return 0;
+ }
+
+ rc = fb_find_mode(&fb_var, fbi, xylonfb_mode_option, NULL, 0,
+ &xylonfb_vm.vmode, bpp);
+
+ switch (rc) {
+ case 0:
+ dev_err(fbi->dev, "failed find video mode\n"
+ "using driver default mode %dx%dM-%d@%d\n",
+ xylonfb_vm.vmode.xres,
+ xylonfb_vm.vmode.yres,
+ bpp,
+ xylonfb_vm.vmode.refresh);
+ break;
+ case 1:
+ dev_dbg(fbi->dev, "video mode %s", xylonfb_mode_option);
+ break;
+ case 2:
+ dev_warn(fbi->dev, "video mode %s with ignored refresh rate\n",
+ xylonfb_mode_option);
+ break;
+ case 3:
+ dev_warn(fbi->dev, "default video mode %dx%dM-%d@%d\n",
+ xylonfb_vm.vmode.xres,
+ xylonfb_vm.vmode.yres,
+ bpp,
+ xylonfb_vm.vmode.refresh);
+ break;
+ case 4:
+ dev_warn(fbi->dev, "video mode fallback\n");
+ break;
+ default:
+ break;
+ }
+
+ data->vm_active.ctrl = data->vm.ctrl;
+ data->vm_active.vmode.xres = fb_var.xres;
+ data->vm_active.vmode.yres = fb_var.yres;
+ data->vm_active.vmode.pixclock = fb_var.pixclock;
+ data->vm_active.vmode.left_margin = fb_var.left_margin;
+ data->vm_active.vmode.right_margin = fb_var.right_margin;
+ data->vm_active.vmode.upper_margin = fb_var.upper_margin;
+ data->vm_active.vmode.lower_margin = fb_var.lower_margin;
+ data->vm_active.vmode.hsync_len = fb_var.hsync_len;
+ data->vm_active.vmode.vsync_len = fb_var.vsync_len;
+ data->vm_active.vmode.sync = fb_var.sync;
+ data->vm_active.vmode.vmode = fb_var.vmode;
+ data->vm_active.vmode.refresh + DIV_ROUND_CLOSEST((PICOS2KHZ(fb_var.pixclock) * 1000),
+ ((fb_var.xres + fb_var.left_margin +
+ fb_var.right_margin + fb_var.hsync_len) *
+ (fb_var.yres + fb_var.upper_margin +
+ fb_var.lower_margin + fb_var.vsync_len)));
+ strcpy(data->vm_active.opts_cvt, data->vm.opts_cvt);
+ strcpy(data->vm_active.opts_ext, data->vm.opts_ext);
+ sprintf(data->vm_active.name, "%dx%d%s-%d@%d%s",
+ fb_var.xres, fb_var.yres,
+ data->vm_active.opts_cvt,
+ fb_var.bits_per_pixel,
+ data->vm_active.vmode.refresh,
+ data->vm_active.opts_ext);
+
+ if (!memchr(data->vm.name, 'x', 10))
+ data->vm = data->vm_active;
+
+ return 0;
+}
+
+static int xylonfb_register_fb(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld, int id,
+ int *regfb)
+{
+ struct device *dev = fbi->dev;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ int transp;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ fbi->flags = FBINFO_DEFAULT;
+ fbi->screen_base = (char __iomem *)ld->fb_base;
+ fbi->screen_size = ld->fb_size;
+ fbi->pseudo_palette = kzalloc(sizeof(u32) * XYLONFB_PSEUDO_PALETTE_SIZE,
+ GFP_KERNEL);
+ fbi->fbops = &xylonfb_ops;
+
+ sprintf(fbi->fix.id, "Xylon FB%d", id);
+ xylonfb_set_hw_specifics(fbi, ld, fd);
+
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_DEFAULT)) {
+ if (!xylonfb_set_timings(fbi, fbi->var.bits_per_pixel))
+ data->flags |= XYLONFB_FLAGS_VMODE_DEFAULT;
+ else
+ dev_err(dev, "videomode not set\n");
+ }
+ xylonfb_set_fbi_var_screeninfo(&fbi->var, data);
+ fbi->mode = &data->vm_active.vmode;
+ fbi->mode->name = data->vm_active.name;
+
+ if (fd->transparency = LOGICVC_ALPHA_LAYER)
+ transp = 0;
+ else
+ transp = 1;
+ if (fb_alloc_cmap(&fbi->cmap, XYLONFB_PSEUDO_PALETTE_SIZE, transp))
+ return -ENOMEM;
+
+ /*
+ * After fb driver registration, values in struct fb_info
+ * must not be changed anywhere else in driver except in
+ * xylonfb_set_par() function
+ */
+ *regfb = register_framebuffer(fbi);
+ if (*regfb) {
+ dev_err(dev, "failed register fb %d\n", id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void xylonfb_layer_initialize(struct xylonfb_layer_data *ld)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 reg = ld->data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ reg |= LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_DISABLE;
+ if (fd->component_swap)
+ reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_ABGR;
+ ld->data->reg_access.set_reg_val(reg, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)
+ data->reg_access.set_reg_val(ld->fb_pbase, ld->base,
+ LOGICVC_LAYER_ADDR_ROFF,
+ ld);
+}
+
+static int xylonfb_vmem_init(struct xylonfb_layer_data *ld, int id, bool *mmap)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ struct device *dev = &data->pdev->dev;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (fd->address) {
+ ld->fb_pbase = fd->address;
+
+ xylonfb_get_vmem_height(data, data->layers, id);
+ ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+ if (*mmap) {
+ ld->fb_base = (__force void *)ioremap_wc(ld->fb_pbase,
+ ld->fb_size);
+ if (!ld->fb_base) {
+ dev_err(dev, "failed map video memory\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ if (fd->buffer_offset)
+ fd->height = fd->buffer_offset *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ else
+ fd->height = XYLONFB_VRES_DEFAULT *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+ ld->fb_base = dma_alloc_coherent(&data->pdev->dev,
+ PAGE_ALIGN(ld->fb_size),
+ &ld->fb_pbase, GFP_KERNEL);
+ if (!ld->fb_base) {
+ dev_err(dev, "failed allocate video buffer ID%d\n", id);
+ return -ENOMEM;
+ }
+
+ data->flags |= XYLONFB_FLAGS_DMA_BUFFER;
+ }
+
+ ld->fb_pbase_active = ld->fb_pbase;
+
+ *mmap = false;
+
+ return 0;
+}
+
+static void xylonfb_logicvc_disp_ctrl(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ void __iomem *dev_base = data->dev_base;
+ u32 val;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (enable) {
+ val = LOGICVC_EN_VDD_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ mdelay(data->pwr_delay);
+ val |= LOGICVC_V_EN_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ mdelay(data->sig_delay);
+ val |= LOGICVC_EN_BLIGHT_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ } else {
+ writel(0, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ }
+}
+
+static void xylonfb_logicvc_layer_enable(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ u32 reg;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ reg = ld->data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+
+ if (enable) {
+ reg |= LOGICVC_LAYER_CTRL_ENABLE;
+ ld->flags |= XYLONFB_FLAGS_LAYER_ENABLED;
+ } else {
+ reg &= ~LOGICVC_LAYER_CTRL_ENABLE;
+ ld->flags &= ~XYLONFB_FLAGS_LAYER_ENABLED;
+ }
+
+ ld->data->reg_access.set_reg_val(reg, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+}
+
+static void xylonfb_enable_logicvc_output(struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ void __iomem *dev_base = data->dev_base;
+ struct fb_videomode *vm = &data->vm_active.vmode;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ writel(vm->right_margin - 1, dev_base + LOGICVC_HSYNC_FRONT_PORCH_ROFF);
+ writel(vm->hsync_len - 1, dev_base + LOGICVC_HSYNC_ROFF);
+ writel(vm->left_margin - 1, dev_base + LOGICVC_HSYNC_BACK_PORCH_ROFF);
+ writel(vm->xres - 1, dev_base + LOGICVC_HRES_ROFF);
+ writel(vm->lower_margin - 1, dev_base + LOGICVC_VSYNC_FRONT_PORCH_ROFF);
+ writel(vm->vsync_len - 1, dev_base + LOGICVC_VSYNC_ROFF);
+ writel(vm->upper_margin - 1, dev_base + LOGICVC_VSYNC_BACK_PORCH_ROFF);
+ writel(vm->yres - 1, dev_base + LOGICVC_VRES_ROFF);
+ data->reg_access.set_reg_val(data->vm_active.ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV)
+ data->reg_access.set_reg_val(LOGICVC_COLOR_YUV888_BLACK,
+ dev_base,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ld);
+ else
+ data->reg_access.set_reg_val(LOGICVC_COLOR_RGB_BLACK,
+ dev_base,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ld);
+
+ writel(LOGICVC_DTYPE_REG_INIT, dev_base + LOGICVC_DTYPE_ROFF);
+
+ XYLONFB_DBG(INFO, "logiCVC HW parameters:\n" \
+ " Horizontal Front Porch: %d pixclks\n" \
+ " Horizontal Sync: %d pixclks\n" \
+ " Horizontal Back Porch: %d pixclks\n" \
+ " Vertical Front Porch: %d pixclks\n" \
+ " Vertical Sync: %d pixclks\n" \
+ " Vertical Back Porch: %d pixclks\n" \
+ " Pixel Clock: %d ps\n" \
+ " Horizontal Resolution: %d pixels\n" \
+ " Vertical Resolution: %d lines\n", \
+ vm->right_margin, vm->hsync_len, vm->left_margin,
+ vm->lower_margin, vm->vsync_len, vm->upper_margin,
+ vm->pixclock, vm->xres, vm->yres);
+}
+
+static void xylonfb_disable_logicvc_output(struct fb_info *fbi)
+{
+ struct fb_info **afbi = dev_get_drvdata(fbi->device);
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (afbi)
+ for (i = 0; i < data->layers; i++)
+ xylonfb_logicvc_layer_enable(afbi[i], false);
+}
+
+static void xylonfb_start(struct fb_info **afbi, int layers)
+{
+ struct fb_info *fbi = afbi[0];
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ for (i = 0; i < layers; i++) {
+ ld = afbi[i]->par;
+ if (ld->flags & XYLONFB_FLAGS_LAYER_ENABLED)
+ continue;
+
+ xylonfb_logicvc_layer_enable(afbi[i], false);
+ }
+
+ if (data->flags & XYLONFB_FLAGS_VSYNC_IRQ) {
+ writel(LOGICVC_INT_V_SYNC,
+ data->dev_base + LOGICVC_INT_STAT_ROFF);
+ data->reg_access.set_reg_val(~LOGICVC_INT_V_SYNC,
+ data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+ }
+
+ for (i = 0; i < layers; i++) {
+ ld = afbi[i]->par;
+ XYLONFB_DBG(INFO, "logiCVC layer %d\n" \
+ " Registers Base Address: 0x%lX\n" \
+ " Layer Video Memory Address: 0x%lX\n" \
+ " X resolution: %d\n" \
+ " Y resolution: %d\n" \
+ " X resolution (virtual): %d\n" \
+ " Y resolution (virtual): %d\n" \
+ " Line length (bytes): %d\n" \
+ " Bits per Pixel: %d\n" \
+ "\n", \
+ i,
+ (unsigned long)ld->pbase,
+ (unsigned long)ld->fb_pbase,
+ afbi[i]->var.xres,
+ afbi[i]->var.yres,
+ afbi[i]->var.xres_virtual,
+ afbi[i]->var.yres_virtual,
+ afbi[i]->fix.line_length,
+ afbi[i]->var.bits_per_pixel);
+ }
+}
+
+static void xylonfb_get_vmode_opts(struct xylonfb_data *data)
+{
+ char *s, *opt, *ext, *c;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ s = data->vm.name;
+ opt = data->vm.opts_cvt;
+ ext = data->vm.opts_ext;
+
+ data->vm.vmode.vmode = 0;
+
+ c = strchr(s, 'M');
+ if (c)
+ *opt++ = *c;
+ c = strchr(s, 'R');
+ if (c)
+ *opt = *c;
+ c = strchr(s, 'i');
+ if (c) {
+ *ext++ = *c;
+ data->vm.vmode.vmode |= FB_VMODE_INTERLACED;
+ }
+ c = strchr(s, 'm');
+ if (c)
+ *ext = *c;
+}
+
+static bool xylonfb_allow_console(struct xylonfb_layer_fix_data *fd)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ switch (fd->format) {
+ case XYLONFB_FORMAT_C8:
+ case XYLONFB_FORMAT_RGB332:
+ case XYLONFB_FORMAT_RGB565:
+ case XYLONFB_FORMAT_XRGB8888:
+ case XYLONFB_FORMAT_ARGB8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int xylonfb_init_core(struct xylonfb_data *data)
+{
+ struct device *dev = &data->pdev->dev;
+ struct fb_info **afbi, *fbi;
+ struct xylonfb_layer_data *ld;
+ void __iomem *dev_base;
+ u32 ip_ver;
+ int i, ret, layers, console_layer;
+ int regfb[LOGICVC_MAX_LAYERS];
+ size_t size;
+ unsigned short layer_base_off[] = {
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_0_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_1_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_2_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_3_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_4_OFFSET)
+ };
+ unsigned short clut_base_off[] = {
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L0_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L1_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L2_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L3_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L4_CLUT_0_OFFSET),
+ };
+ bool memmap;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ dev_base = devm_ioremap_resource(dev, &data->resource_mem);
+ if (IS_ERR(dev_base)) {
+ dev_err(dev, "failed ioremap mem resource\n");
+ return PTR_ERR(dev_base);
+ }
+ data->dev_base = dev_base;
+
+ data->irq = data->resource_irq.start;
+ ret = devm_request_irq(dev, data->irq, xylonfb_isr, IRQF_TRIGGER_HIGH,
+ XYLONFB_DEVICE_NAME, dev);
+ if (ret)
+ return ret;
+
+ ip_ver = readl(dev_base + LOGICVC_IP_VERSION_ROFF);
+ data->major = (ip_ver >> LOGICVC_MAJOR_REVISION_SHIFT) &
+ LOGICVC_MAJOR_REVISION_MASK;
+ data->minor = (ip_ver >> LOGICVC_MINOR_REVISION_SHIFT) &
+ LOGICVC_MINOR_REVISION_MASK;
+ data->patch = ip_ver & LOGICVC_PATCH_LEVEL_MASK;
+ dev_info(dev, "logiCVC IP core %d.%02d.%c\n",
+ data->major, data->minor, ('a' + data->patch));
+
+ if (data->major >= 4)
+ data->flags |= XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS;
+
+ layers = data->layers;
+ if (layers = 0) {
+ dev_err(dev, "no available layers\n");
+ return -ENODEV;
+ }
+ console_layer = data->console_layer;
+ if (console_layer >= layers) {
+ dev_err(dev, "invalid console layer ID\n");
+ console_layer = 0;
+ }
+
+ if (data->flags & XYLONFB_FLAGS_CHECK_CONSOLE_LAYER) {
+ if (!xylonfb_allow_console(data->fd[console_layer])) {
+ dev_err(dev, "invalid console layer format\n");
+ return -EINVAL;
+ }
+ data->flags &= ~XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+ }
+
+ size = sizeof(struct fb_info *);
+ afbi = devm_kzalloc(dev, (size * layers), GFP_KERNEL);
+ if (!afbi) {
+ dev_err(dev, "failed allocate internal data\n");
+ return -ENOMEM;
+ }
+
+ if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+ data->reg_access.get_reg_val = xylonfb_get_reg;
+ data->reg_access.set_reg_val = xylonfb_set_reg;
+ } else {
+ size = sizeof(struct xylonfb_registers);
+ data->reg_access.get_reg_val = xylonfb_get_reg_mem;
+ data->reg_access.set_reg_val = xylonfb_set_reg_mem;
+ }
+
+ data->coeff.cyr = LOGICVC_COEFF_Y_R;
+ data->coeff.cyg = LOGICVC_COEFF_Y_G;
+ data->coeff.cyb = LOGICVC_COEFF_Y_B;
+ if (data->flags & XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656) {
+ data->coeff.cy = LOGICVC_COEFF_ITU656_Y;
+ data->coeff.cur = LOGICVC_COEFF_ITU656_U_R;
+ data->coeff.cug = LOGICVC_COEFF_ITU656_U_G;
+ data->coeff.cub = LOGICVC_COEFF_ITU656_U_B;
+ data->coeff.cvr = LOGICVC_COEFF_ITU656_V_R;
+ data->coeff.cvg = LOGICVC_COEFF_ITU656_V_G;
+ data->coeff.cvb = LOGICVC_COEFF_ITU656_V_B;
+ } else {
+ data->coeff.cy = LOGICVC_COEFF_Y;
+ data->coeff.cur = LOGICVC_COEFF_U_R;
+ data->coeff.cug = LOGICVC_COEFF_U_G;
+ data->coeff.cub = LOGICVC_COEFF_U_B;
+ data->coeff.cvr = LOGICVC_COEFF_V_R;
+ data->coeff.cvg = LOGICVC_COEFF_V_G;
+ data->coeff.cvb = LOGICVC_COEFF_V_B;
+ }
+
+ atomic_set(&data->refcount, 0);
+
+ data->flags |= XYLONFB_FLAGS_VMODE_INIT;
+
+ sprintf(data->vm.name, "%s-%d@%d",
+ data->vm.name, data->fd[console_layer]->bpp,
+ data->vm.vmode.refresh);
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_CUSTOM))
+ xylonfb_mode_option = data->vm.name;
+ xylonfb_get_vmode_opts(data);
+
+ if (data->pixel_clock) {
+ if (xylonfb_hw_pixclk_supported(dev, data->pixel_clock)) {
+ data->flags |= XYLONFB_FLAGS_PIXCLK_VALID;
+ } else {
+ dev_warn(dev, "pixel clock not supported\n");
+ ret = -EPROBE_DEFER;
+ goto err_probe;
+ }
+ } else {
+ dev_info(dev, "external pixel clock\n");
+ }
+
+ ld = NULL;
+
+ for (i = 0; i < layers; i++)
+ regfb[i] = -1;
+ memmap = true;
+
+ /*
+ * /dev/fb0 will be default console layer,
+ * no matter how logiCVC layers are sorted in memory
+ */
+ for (i = console_layer; i < layers; i++) {
+ if (regfb[i] != -1)
+ continue;
+
+ size = sizeof(struct xylonfb_layer_data);
+ fbi = framebuffer_alloc(size, dev);
+ if (!fbi) {
+ dev_err(dev, "failed allocate fb info\n");
+ ret = -ENOMEM;
+ goto err_probe;
+ }
+ fbi->dev = dev;
+ afbi[i] = fbi;
+
+ ld = fbi->par;
+ ld->data = data;
+ ld->fd = data->fd[i];
+
+ atomic_set(&ld->refcount, 0);
+
+ ld->pbase = data->resource_mem.start + layer_base_off[i];
+ ld->base = dev_base + layer_base_off[i];
+ ld->clut_base = dev_base + clut_base_off[i];
+
+ ret = xylonfb_vmem_init(ld, i, &memmap);
+ if (ret)
+ goto err_probe;
+
+ xylonfb_layer_initialize(ld);
+
+ ret = xylonfb_register_fb(fbi, ld, i, ®fb[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) {
+ if (ld->fb_base)
+ dma_free_coherent(dev,
+ PAGE_ALIGN(ld->fb_size),
+ ld->fb_base, ld->fb_pbase);
+ } else {
+ if (ld->fb_base)
+ iounmap((void __iomem *)ld->fb_base);
+ }
+ kfree(fbi->pseudo_palette);
+ framebuffer_release(fbi);
+ }
+ }
+
+ return ret;
+}
+
+int xylonfb_deinit_core(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fb_info **afbi = dev_get_drvdata(dev);
+ struct fb_info *fbi = afbi[0];
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (atomic_read(&data->refcount) != 0) {
+ dev_err(dev, "driver in use\n");
+ return -EINVAL;
+ }
+
+ xylonfb_disable_logicvc_output(fbi);
+
+ xylonfb_hw_pixclk_unload(data->pixel_clock);
+
+ for (i = data->layers - 1; i >= 0; i--) {
+ fbi = afbi[i];
+ ld = fbi->par;
+
+ unregister_framebuffer(fbi);
+ fb_dealloc_cmap(&fbi->cmap);
+ if (data->flags & XYLONFB_FLAGS_DMA_BUFFER) {
+ dma_free_coherent(dev,
+ PAGE_ALIGN(ld->fb_size),
+ ld->fb_base, ld->fb_pbase);
+ }
+ kfree(fbi->pseudo_palette);
+ framebuffer_release(fbi);
+ }
+
+ return 0;
+}
diff --git a/drivers/video/fbdev/xylon/xylonfb_core.h b/drivers/video/fbdev/xylon/xylonfb_core.h
new file mode 100644
index 0000000..1a3dee8
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.h
@@ -0,0 +1,252 @@
+/*
+ * Xylon logiCVC frame buffer driver internal data structures
+ *
+ * Copyright (C) 2015 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@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 INFO 1
+#define CORE 2
+#define DEBUG_LEVEL CORE
+
+#ifdef DEBUG
+#define XYLONFB_DBG(level, format, ...) \
+ do { \
+ if (level >= DEBUG_LEVEL) \
+ pr_info(format "\n", ## __VA_ARGS__); \
+ } while (0)
+#else
+#define XYLONFB_DBG(format, ...) do { } while (0)
+#endif
+
+#define LOGICVC_MAX_LAYERS 5
+
+/* Xylon FB driver flags */
+#define XYLONFB_FLAGS_READABLE_REGS (1 << 0)
+#define XYLONFB_FLAGS_SIZE_POSITION (1 << 1)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER (1 << 2)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_RGB (1 << 3)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_YUV (1 << 4)
+#define XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656 (1 << 5)
+#define XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS (1 << 6)
+#define XYLONFB_FLAGS_CHECK_CONSOLE_LAYER (1 << 7)
+#define XYLONFB_FLAGS_PIXCLK_VALID (1 << 8)
+#define XYLONFB_FLAGS_DMA_BUFFER (1 << 9)
+#define XYLONFB_FLAGS_VSYNC_IRQ (1 << 10)
+#define XYLONFB_FLAGS_VMODE_CUSTOM (1 << 11)
+#define XYLONFB_FLAGS_VMODE_INIT (1 << 12)
+#define XYLONFB_FLAGS_VMODE_DEFAULT (1 << 13)
+#define XYLONFB_FLAGS_VMODE_SET (1 << 14)
+#define XYLONFB_FLAGS_LAYER_ENABLED (1 << 15)
+#define XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN (1 << 16)
+
+/* Xylon FB driver color formats */
+enum xylonfb_color_format {
+ XYLONFB_FORMAT_A8,
+ XYLONFB_FORMAT_C8,
+ XYLONFB_FORMAT_RGB332,
+ XYLONFB_FORMAT_RGB565,
+ XYLONFB_FORMAT_XRGB8888,
+ XYLONFB_FORMAT_ARGB8888,
+ XYLONFB_FORMAT_YUYV,
+ XYLONFB_FORMAT_AYUV,
+ XYLONFB_FORMAT_CLUT_ARGB6565,
+ XYLONFB_FORMAT_CLUT_ARGB8888,
+ XYLONFB_FORMAT_CLUT_AYUV8888
+};
+
+struct xylonfb_layer_data;
+struct xylonfb_data;
+
+#define VMODE_NAME_SIZE 21
+#define VMODE_OPTS_SIZE 3
+
+struct xylonfb_vmode {
+ u32 ctrl;
+ struct fb_videomode vmode;
+ char name[VMODE_NAME_SIZE];
+ char opts_cvt[VMODE_OPTS_SIZE];
+ char opts_ext[VMODE_OPTS_SIZE];
+};
+
+struct xylonfb_registers {
+ u32 ctrl;
+ u32 dtype;
+ u32 bg;
+ u32 unused[3];
+ u32 int_mask;
+};
+
+union xylonfb_layer_reg_0 {
+ u32 addr;
+ u32 hoff;
+};
+
+union xylonfb_layer_reg_1 {
+ u32 unused;
+ u32 voff;
+};
+
+struct xylonfb_layer_registers {
+ union xylonfb_layer_reg_0 reg_0;
+ union xylonfb_layer_reg_1 reg_1;
+ u32 hpos;
+ u32 vpos;
+ u32 hsize;
+ u32 vsize;
+ u32 alpha;
+ u32 ctrl;
+ u32 transp;
+};
+
+struct xylonfb_register_access {
+ u32 (*get_reg_val)(void __iomem *dev_base, unsigned int offset,
+ struct xylonfb_layer_data *layer_data);
+ void (*set_reg_val)(u32 value, void __iomem *dev_base,
+ unsigned int offset,
+ struct xylonfb_layer_data *layer_data);
+};
+
+struct xylonfb_layer_fix_data {
+ unsigned int id;
+ u32 address;
+ u32 address_range;
+ u32 buffer_offset;
+ u32 bpp;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 format_clut;
+ u32 transparency;
+ u32 type;
+ bool component_swap;
+};
+
+struct xylonfb_layer_data {
+ struct mutex mutex;
+
+ atomic_t refcount;
+
+ struct xylonfb_data *data;
+ struct xylonfb_layer_fix_data *fd;
+ struct xylonfb_layer_registers regs;
+
+ dma_addr_t pbase;
+ void __iomem *base;
+ void __iomem *clut_base;
+
+ dma_addr_t fb_pbase;
+ void *fb_base;
+ u32 fb_size;
+
+ dma_addr_t fb_pbase_active;
+
+ u32 flags;
+};
+
+struct xylonfb_rgb2yuv_coeff {
+ s32 cy;
+ s32 cyr;
+ s32 cyg;
+ s32 cyb;
+ s32 cur;
+ s32 cug;
+ s32 cub;
+ s32 cvr;
+ s32 cvg;
+ s32 cvb;
+};
+
+struct xylonfb_sync {
+ wait_queue_head_t wait;
+ unsigned int count;
+};
+
+struct xylonfb_data {
+ struct platform_device *pdev;
+
+ struct device_node *device;
+ struct device_node *pixel_clock;
+
+ struct resource resource_mem;
+ struct resource resource_irq;
+
+ void __iomem *dev_base;
+
+ struct mutex irq_mutex;
+
+ struct xylonfb_register_access reg_access;
+ struct xylonfb_sync vsync;
+ struct xylonfb_vmode vm;
+ struct xylonfb_vmode vm_active;
+ struct xylonfb_rgb2yuv_coeff coeff;
+
+ struct xylonfb_layer_fix_data *fd[LOGICVC_MAX_LAYERS];
+ struct xylonfb_registers regs;
+
+ u32 bg_layer_bpp;
+ u32 console_layer;
+ u32 pixel_stride;
+
+ atomic_t refcount;
+
+ u32 flags;
+ int irq;
+ u8 layers;
+ /*
+ * Delay after applying display power and
+ * before applying display signals
+ */
+ u32 pwr_delay;
+ /*
+ * Delay after applying display signal and
+ * before applying display backlight power supply
+ */
+ u32 sig_delay;
+ /* IP version */
+ u8 major;
+ u8 minor;
+ u8 patch;
+};
+
+/* Xylon FB video mode options */
+extern char *xylonfb_mode_option;
+
+/* Xylon FB core pixel clock interface functions */
+extern bool xylonfb_hw_pixclk_supported(struct device *dev,
+ struct device_node *dn);
+extern void xylonfb_hw_pixclk_unload(struct device_node *dn);
+extern int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+ unsigned long pixclk_khz);
+
+/* Xylon FB core V sync wait function */
+extern int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi);
+
+/* Xylon FB core interface functions */
+extern int xylonfb_init_core(struct xylonfb_data *data);
+extern int xylonfb_deinit_core(struct platform_device *pdev);
+extern int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd,
+ unsigned long arg);
+
+#endif /* __XYLONFB_CORE_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
2015-03-11 15:27 ` [PATCH 01/10] fbdev: Xylon " Davor Joja
2015-03-11 15:27 ` [PATCH 02/10] fbdev: xylon: Framebuffer driver core Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock Davor Joja
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ 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 IOCTL providing advanced video controller functionality used by user
space applications.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_ioctl.c | 672 ++++++++++++++++++++++++++++++
1 file changed, 672 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..131bdcf
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_ioctl.c
@@ -0,0 +1,672 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL functionality
+ *
+ * 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/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;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_set;
+ 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;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_get;
+ 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_DISABLE;
+ else
+ var32 &= ~LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_DISABLE;
+ 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] 11+ messages in thread
* [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (2 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 03/10] fbdev: xylon: Framebuffer driver ioctl Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions Davor Joja
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ 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 pixel clock control functionality supporting optional Xylon logiCLK IP
core clock generator.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_pixclk.c | 119 +++++++++++++++++++++++++++++
1 file changed, 119 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..d63dbeb
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_pixclk.c
@@ -0,0 +1,119 @@
+/*
+ * Xylon logiCVC frame buffer driver pixel clock control
+ *
+ * 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/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)
+{
+#if defined(CONFIG_FB_XYLON_PIXCLK)
+ 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;
+ }
+#else
+ return true;
+#endif
+}
+
+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] 11+ messages in thread
* [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (3 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 04/10] fbdev: xylon: Framebuffer driver pixel clock Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration Davor Joja
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ 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 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..7c1266e
--- /dev/null
+++ b/drivers/video/fbdev/xylon/logicvc.h
@@ -0,0 +1,192 @@
+/*
+ * Xylon logiCVC IP core parameters
+ *
+ * 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.
+ */
+
+#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_DISABLE (1 << 1)
+#define LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH (1 << 2)
+#define LOGICVC_LAYER_CTRL_INTERLACE (1 << 3)
+#define LOGICVC_LAYER_CTRL_PIXEL_FORMAT_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] 11+ messages in thread
* [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (4 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 05/10] fbdev: xylon: logiCVC IP core definitions Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file Davor Joja
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ 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 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] 11+ messages in thread
* [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (5 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 06/10] fbdev: xylon: Framebuffer driver kernel and make configuration Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile Davor Joja
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ 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 header file containing structures and IOCTLs for user space
applications to be able to use advanced 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..662c02f
--- /dev/null
+++ b/include/uapi/linux/xylonfb.h
@@ -0,0 +1,85 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL parameters
+ *
+ * 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.
+ */
+
+#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] 11+ messages in thread
* [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (6 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 07/10] uapi: linux: Xylon framebuffer driver header file Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 09/10] bindings: video: Xylon logiCVC IP core device binding Davor Joja
2015-03-11 15:27 ` [PATCH 10/10] bindings: video: Xylon framebuffer driver binding Davor Joja
9 siblings, 0 replies; 11+ 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
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 b3dd417..3bd0693 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2454,6 +2454,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..57cb259 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-$(CONFIG_FB_XYLON) += xylon/
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
--
1.9.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 09/10] bindings: video: Xylon logiCVC IP core device binding
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (7 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 08/10] fbdev: Xylon framebuffer driver to fbdev Kconfig and Makefile Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
2015-03-11 15:27 ` [PATCH 10/10] bindings: video: Xylon framebuffer driver binding Davor Joja
9 siblings, 0 replies; 11+ 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
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..d49c574
--- /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-3.00.a", "xylon,logicvc-4.00.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] 11+ messages in thread
* [PATCH 10/10] bindings: video: Xylon framebuffer driver binding
2015-03-11 15:27 Add Xylon logiCVC framebuffer driver Davor Joja
` (8 preceding siblings ...)
2015-03-11 15:27 ` [PATCH 09/10] bindings: video: Xylon logiCVC IP core device binding Davor Joja
@ 2015-03-11 15:27 ` Davor Joja
9 siblings, 0 replies; 11+ 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
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] 11+ messages in thread