new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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__ */
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