new file mode 100644
@@ -0,0 +1,676 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL functionality
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/xylonfb.h>
+
+#include "logicvc.h"
+#include "xylonfb_core.h"
+
+static int xylonfb_get_vblank(struct fb_vblank *vblank, struct fb_info *fbi)
+{
+ vblank->flags |= FB_VBLANK_HAVE_VSYNC;
+
+ return 0;
+}
+
+static void xylonfb_vsync_ctrl(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ u32 imr;
+
+ mutex_lock(&data->irq_mutex);
+
+ imr = data->reg_access.get_reg_val(data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+ if (enable) {
+ imr &= (~LOGICVC_INT_V_SYNC);
+ writel(LOGICVC_INT_V_SYNC,
+ data->dev_base + LOGICVC_INT_STAT_ROFF);
+ } else {
+ imr |= LOGICVC_INT_V_SYNC;
+ }
+
+ data->reg_access.set_reg_val(imr, data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+
+ mutex_unlock(&data->irq_mutex);
+}
+
+int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int ret, count;
+
+ mutex_lock(&data->irq_mutex);
+
+ count = data->vsync.count;
+
+ ret = wait_event_interruptible_timeout(data->vsync.wait,
+ (count != data->vsync.count),
+ HZ/10);
+
+ mutex_unlock(&data->irq_mutex);
+
+ if (ret < 0)
+ return ret;
+ else if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static unsigned int alpha_normalized(unsigned int alpha, unsigned int used_bits,
+ bool set)
+{
+ if (set)
+ return alpha / (255 / ((1 << used_bits) - 1));
+ else
+ return (((255 << 16) / ((1 << used_bits) - 1)) * alpha) >> 16;
+}
+
+static int xylonfb_layer_alpha(struct xylonfb_layer_data *ld, u8 *alpha,
+ bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ unsigned int used_bits;
+ u32 val;
+
+ if (fd->transparency != LOGICVC_ALPHA_LAYER)
+ return -EPERM;
+
+ switch (fd->type) {
+ case LOGICVC_LAYER_YUV:
+ used_bits = 8;
+ break;
+ case LOGICVC_LAYER_RGB:
+ switch (fd->bpp) {
+ case 8:
+ used_bits = 3;
+ break;
+ case 16:
+ used_bits = 6;
+ break;
+ case 32:
+ used_bits = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!set) {
+ val = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_ALPHA_ROFF,
+ ld);
+ *alpha = (u8)(val & (0xFF >> (8 - used_bits)));
+ }
+
+ /* get/set normalized alpha value */
+ *alpha = alpha_normalized(*alpha, used_bits, set);
+
+ if (set)
+ data->reg_access.set_reg_val(*alpha, ld->base,
+ LOGICVC_LAYER_ALPHA_ROFF,
+ ld);
+
+ return 0;
+}
+
+static int xylonfb_layer_buff(struct fb_info *fbi,
+ struct xylonfb_layer_buffer *layer_buff,
+ bool set)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ unsigned int layer_id = ld->fd->id;
+ u32 reg;
+
+ if (set) {
+ if (layer_buff->id >= LOGICVC_MAX_LAYER_BUFFERS)
+ return -EINVAL;
+
+ reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+ reg |= (1 << (10 + layer_id));
+ reg &= ~(0x03 << (layer_id << 1));
+ reg |= (layer_buff->id << (layer_id << 1));
+ writel(reg, ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+
+ xylonfb_vsync_wait(0, fbi);
+ } else {
+ reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+ reg >>= ((layer_id << 1));
+ layer_buff->id = reg & 0x03;
+ }
+
+ return 0;
+}
+
+static void xylonfb_rgb_yuv(u32 c1, u32 c2, u32 c3, u32 *pixel,
+ struct xylonfb_layer_data *ld, bool rgb2yuv)
+{
+ struct xylonfb_data *data = ld->data;
+ u32 r, g, b, y, u, v;
+
+ if (rgb2yuv) {
+ y = ((data->coeff.cyr * c1) + (data->coeff.cyg * c2) +
+ (data->coeff.cyb * c3) + data->coeff.cy) /
+ LOGICVC_YUV_NORM;
+ u = ((-data->coeff.cur * c1) - (data->coeff.cug * c2) +
+ (data->coeff.cub * c3) + LOGICVC_COEFF_U) /
+ LOGICVC_YUV_NORM;
+ v = ((data->coeff.cvr * c1) - (data->coeff.cvg * c2) -
+ (data->coeff.cvb * c3) + LOGICVC_COEFF_V) /
+ LOGICVC_YUV_NORM;
+
+ *pixel = (0xFF << 24) | (y << 16) | (u << 8) | v;
+ } else {
+ r = ((c1 * LOGICVC_RGB_NORM) + (LOGICVC_COEFF_R_U * c2) -
+ LOGICVC_COEFF_R) / LOGICVC_RGB_NORM;
+ g = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_G_U * c2) -
+ (LOGICVC_COEFF_G_V * c3) + LOGICVC_COEFF_G) /
+ LOGICVC_RGB_NORM;
+ b = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_B_V * c3) -
+ LOGICVC_COEFF_B) / LOGICVC_RGB_NORM;
+
+ *pixel = (0xFF << 24) | (r << 16) | (g << 8) | b;
+ }
+}
+
+static int xylonfb_layer_color_rgb(struct xylonfb_layer_data *ld,
+ struct xylonfb_layer_color *layer_color,
+ unsigned int reg_offset, bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ void __iomem *base;
+ u32 r = 0, g = 0, b = 0;
+ u32 raw_rgb, y, u, v;
+ int bpp, transparency;
+
+ if (reg_offset == LOGICVC_LAYER_TRANSP_COLOR_ROFF) {
+ base = ld->base;
+ bpp = fd->bpp;
+ transparency = fd->transparency;
+ } else /* if (reg_offset == LOGICVC_BACKGROUND_COLOR_ROFF) */ {
+ base = data->dev_base;
+ bpp = data->bg_layer_bpp;
+ transparency = -1;
+ }
+
+ if (set) {
+ if (layer_color->use_raw) {
+ raw_rgb = layer_color->raw_rgb;
+ } else if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+ r = layer_color->r;
+ g = layer_color->g;
+ b = layer_color->b;
+ xylonfb_rgb_yuv(r, g, b, &raw_rgb, ld, true);
+ } else {
+ r = layer_color->r;
+ g = layer_color->g;
+ b = layer_color->b;
+check_bpp_set:
+ switch (bpp) {
+ case 8:
+ switch (transparency) {
+ case LOGICVC_ALPHA_CLUT_16BPP:
+ bpp = 16;
+ goto check_bpp_set;
+ break;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_set;
+ break;
+ default:
+ raw_rgb = (r & 0xE0) |
+ ((g & 0xE0) >> 3) |
+ ((b & 0xC0) >> 6);
+ break;
+ }
+ break;
+ case 16:
+ raw_rgb = ((r & 0xF8) << 8) |
+ ((g & 0xFC) << 3) |
+ ((b & 0xF8) >> 3);
+ break;
+ case 32:
+ raw_rgb = (r << 16) | (g << 8) | b;
+ break;
+ default:
+ raw_rgb = 0;
+ }
+ }
+ data->reg_access.set_reg_val(raw_rgb, base, reg_offset, ld);
+ } else {
+ raw_rgb = data->reg_access.get_reg_val(base, reg_offset, ld);
+check_bpp_get:
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+ y = (raw_rgb >> 16) & 0xFF;
+ u = (raw_rgb >> 8) & 0xFF;
+ v = raw_rgb & 0xFF;
+ xylonfb_rgb_yuv(y, u, v, &raw_rgb, ld, false);
+ } else {
+ switch (bpp) {
+ case 8:
+ switch (transparency) {
+ case LOGICVC_ALPHA_CLUT_16BPP:
+ bpp = 16;
+ goto check_bpp_get;
+ break;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_get;
+ break;
+ default:
+ r = raw_rgb >> 5;
+ r = (((r << 3) | r) << 2) | (r >> 1);
+ g = (raw_rgb >> 2) & 0x07;
+ g = (((g << 3) | g) << 2) | (g >> 1);
+ b = raw_rgb & 0x03;
+ b = (b << 6) | (b << 4) | (b << 2) | b;
+ break;
+ }
+ break;
+ case 16:
+ r = raw_rgb >> 11;
+ r = (r << 3) | (r >> 2);
+ g = (raw_rgb >> 5) & 0x3F;
+ g = (g << 2) | (g >> 4);
+ b = raw_rgb & 0x1F;
+ b = (b << 3) | (b >> 2);
+ break;
+ case 32:
+ r = raw_rgb >> 16;
+ g = (raw_rgb >> 8) & 0xFF;
+ b = raw_rgb & 0xFF;
+ break;
+ default:
+ raw_rgb = r = g = b = 0;
+ }
+ }
+ layer_color->raw_rgb = raw_rgb;
+ layer_color->r = (u8)r;
+ layer_color->g = (u8)g;
+ layer_color->b = (u8)b;
+ }
+
+ return 0;
+}
+
+static int xylonfb_layer_geometry(struct fb_info *fbi,
+ struct xylonfb_layer_geometry *layer_geometry,
+ bool set)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 x, y, width, height, xoff, yoff, xres, yres;
+
+ xres = fbi->var.xres;
+ yres = fbi->var.yres;
+
+ if (set) {
+ x = layer_geometry->x;
+ y = layer_geometry->y;
+ width = layer_geometry->width;
+ height = layer_geometry->height;
+
+ if ((x > xres) || (y > yres))
+ return -EINVAL;
+
+ if ((width == 0) || (height == 0))
+ return -EINVAL;
+
+ if ((x + width) > xres) {
+ width = xres - x;
+ layer_geometry->width = width;
+ }
+ if ((y + height) > yres) {
+ height = yres - y;
+ layer_geometry->height = height;
+ }
+ /* YUV 4:2:2 layer type can only have even layer width */
+ if ((width > 2) && (fd->type == LOGICVC_LAYER_YUV) &&
+ (fd->bpp == 16))
+ width &= ~((unsigned long) + 1);
+
+ /*
+ * logiCVC 3.x registers write sequence:
+ * offset, size, position with implicit last write to
+ * LOGICVC_LAYER_VPOS_ROFF
+ * logiCVC 4.x registers write sequence:
+ * size, position with implicit last write to
+ * LOGICVC_LAYER_ADDR_ROFF
+ */
+ if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+ data->reg_access.set_reg_val(layer_geometry->x_offset,
+ ld->base,
+ LOGICVC_LAYER_HOFF_ROFF,
+ ld);
+ data->reg_access.set_reg_val(layer_geometry->y_offset,
+ ld->base,
+ LOGICVC_LAYER_VOFF_ROFF,
+ ld);
+ }
+ data->reg_access.set_reg_val((width - 1), ld->base,
+ LOGICVC_LAYER_HSIZE_ROFF,
+ ld);
+ data->reg_access.set_reg_val((height - 1), ld->base,
+ LOGICVC_LAYER_VSIZE_ROFF,
+ ld);
+ data->reg_access.set_reg_val((xres - (x + 1)), ld->base,
+ LOGICVC_LAYER_HPOS_ROFF,
+ ld);
+ data->reg_access.set_reg_val((yres - (y + 1)), ld->base,
+ LOGICVC_LAYER_VPOS_ROFF,
+ ld);
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+ xoff = layer_geometry->x_offset * (ld->fd->bpp / 8);
+ yoff = layer_geometry->y_offset * ld->fd->width *
+ (ld->fd->bpp / 8);
+
+ ld->fb_pbase_active = ld->fb_pbase + xoff + yoff;
+
+ data->reg_access.set_reg_val(ld->fb_pbase_active,
+ ld->base,
+ LOGICVC_LAYER_ADDR_ROFF,
+ ld);
+ }
+ } else {
+ x = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_HPOS_ROFF,
+ ld);
+ layer_geometry->x = xres - (x + 1);
+ y = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_VPOS_ROFF,
+ ld);
+ layer_geometry->y = yres - (y + 1);
+ layer_geometry->width =
+ data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_HSIZE_ROFF,
+ ld);
+ layer_geometry->width += 1;
+ layer_geometry->height =
+ data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_VSIZE_ROFF,
+ ld);
+ layer_geometry->height += 1;
+ }
+
+ return 0;
+}
+
+static int xylonfb_layer_reg_access(struct xylonfb_layer_data *ld,
+ struct xylonfb_hw_access *hw_access,
+ bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 offset;
+ u32 rel_offset;
+
+ if ((hw_access->offset < LOGICVC_LAYER_BASE_OFFSET) ||
+ (hw_access->offset > LOGICVC_LAYER_BASE_END))
+ return -EPERM;
+
+ if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+ offset = hw_access->offset;
+ if (set)
+ data->reg_access.set_reg_val(hw_access->value,
+ data->dev_base,
+ offset,
+ ld);
+ else
+ hw_access->value =
+ data->reg_access.get_reg_val(data->dev_base,
+ offset,
+ ld);
+ return 0;
+ }
+
+ rel_offset = hw_access->offset - (fd->id * 0x80) -
+ LOGICVC_LAYER_BASE_OFFSET;
+
+ if (rel_offset > LOGICVC_LAYER_BASE_END)
+ return -EINVAL;
+
+ if (set)
+ data->reg_access.set_reg_val(hw_access->value, ld->base,
+ rel_offset, ld);
+ else
+ hw_access->value = data->reg_access.get_reg_val(ld->base,
+ rel_offset, ld);
+
+ return 0;
+}
+
+int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ union {
+ struct fb_vblank vblank;
+ struct xylonfb_hw_access hw_access;
+ struct xylonfb_layer_buffer layer_buff;
+ struct xylonfb_layer_color layer_color;
+ struct xylonfb_layer_geometry layer_geometry;
+ struct xylonfb_layer_transparency layer_transp;
+ } ioctl;
+ void __user *argp = (void __user *)arg;
+ unsigned long val;
+ u32 var32;
+ int ret = 0;
+ bool flag;
+
+ switch (cmd) {
+ case FBIOGET_VBLANK:
+ if (copy_from_user(&ioctl.vblank, argp, sizeof(ioctl.vblank)))
+ return -EFAULT;
+
+ ret = xylonfb_get_vblank(&ioctl.vblank, fbi);
+ if (!ret &&
+ copy_to_user(argp, &ioctl.vblank, sizeof(ioctl.vblank)))
+ ret = -EFAULT;
+ break;
+
+ case FBIO_WAITFORVSYNC:
+ if (get_user(var32, (u32 __user *)arg))
+ return -EFAULT;
+
+ ret = xylonfb_vsync_wait(var32, fbi);
+ break;
+
+ case XYLONFB_VSYNC_CTRL:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ xylonfb_vsync_ctrl(fbi, flag);
+ break;
+
+ case XYLONFB_LAYER_IDX:
+ var32 = ld->fd->id;
+ put_user(var32, (u32 __user *)arg);
+ break;
+
+ case XYLONFB_LAYER_ALPHA:
+ if (copy_from_user(&ioctl.layer_transp, argp,
+ sizeof(ioctl.layer_transp)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_alpha(ld, &ioctl.layer_transp.alpha,
+ ioctl.layer_transp.set);
+ if (!ret && !ioctl.layer_transp.set)
+ if (copy_to_user(argp, &ioctl.layer_transp,
+ sizeof(ioctl.layer_transp)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_COLOR_TRANSP_CTRL:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ var32 = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ if (flag)
+ var32 |= LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+ else
+ var32 &= ~LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+ data->reg_access.set_reg_val(var32, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_COLOR_TRANSP:
+ if (copy_from_user(&ioctl.layer_color, argp,
+ sizeof(ioctl.layer_color)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+ LOGICVC_LAYER_TRANSP_COLOR_ROFF,
+ ioctl.layer_color.set);
+ if (!ret && !ioctl.layer_color.set)
+ if (copy_to_user(argp, &ioctl.layer_color,
+ sizeof(ioctl.layer_color)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_GEOMETRY:
+ if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+ return -EINVAL;
+
+ if (copy_from_user(&ioctl.layer_geometry, argp,
+ sizeof(ioctl.layer_geometry)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_geometry(fbi, &ioctl.layer_geometry,
+ ioctl.layer_geometry.set);
+ if (!ret && !ioctl.layer_geometry.set)
+ if (copy_to_user(argp, &ioctl.layer_geometry,
+ sizeof(ioctl.layer_geometry)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_BUFFER:
+ if (data->major >= 4)
+ return -EPERM;
+
+ if (copy_from_user(&ioctl.layer_buff, argp,
+ sizeof(ioctl.layer_buff)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_buff(fbi, &ioctl.layer_buff,
+ ioctl.layer_buff.set);
+ if (!ret && !ioctl.layer_buff.set)
+ if (copy_to_user(argp, &ioctl.layer_buff,
+ sizeof(ioctl.layer_buff)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_BUFFER_OFFSET:
+ if (data->major < 4) {
+ var32 = readl(ld->data->dev_base +
+ LOGICVC_VBUFF_SELECT_ROFF);
+ var32 >>= (ld->fd->id << 1);
+ var32 &= 0x03;
+ val = ld->fd->buffer_offset;
+ val *= var32;
+ } else {
+ val = ld->fd->buffer_offset;
+ }
+ put_user(val, (unsigned long __user *)arg);
+ break;
+
+ case XYLONFB_BACKGROUND_COLOR:
+ if (data->bg_layer_bpp == 0)
+ return -EPERM;
+
+ if (copy_from_user(&ioctl.layer_color, argp,
+ sizeof(ioctl.layer_color)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ioctl.layer_color.set);
+ if (!ret && !ioctl.layer_color.set)
+ if (copy_to_user(argp, &ioctl.layer_color,
+ sizeof(ioctl.layer_color)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_EXT_BUFF_SWITCH:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ var32 = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ if (flag)
+ var32 |= LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+ else
+ var32 &= ~LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+ data->reg_access.set_reg_val(var32, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_HW_ACCESS:
+ if (copy_from_user(&ioctl.hw_access, argp,
+ sizeof(ioctl.hw_access)))
+ return -EFAULT;
+
+ ret = xylonfb_layer_reg_access(ld, &ioctl.hw_access,
+ ioctl.hw_access.set);
+ if (!ret && !ioctl.hw_access.set)
+ if (copy_to_user(argp, &ioctl.hw_access,
+ sizeof(ioctl.hw_access)))
+ ret = -EFAULT;
+ break;
+
+ case XYLONFB_IP_CORE_VERSION:
+ var32 = (data->major << 16) | (data->minor << 8) | data->patch;
+ if (copy_to_user(argp, &var32, sizeof(u32)))
+ ret = -EFAULT;
+ break;
+
+ default:
+ dev_err(&data->pdev->dev, "unknown ioctl");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
Driver IOCTL providing advanced video controller functionality to be used by user space applications. Signed-off-by: Davor Joja <davorjoja@logicbricks.com> --- drivers/video/fbdev/xylon/xylonfb_ioctl.c | 676 ++++++++++++++++++++++++++++++ 1 file changed, 676 insertions(+) create mode 100644 drivers/video/fbdev/xylon/xylonfb_ioctl.c