diff mbox

[03/10] fbdev: xylon: Framebuffer driver ioctl

Message ID 76e287ed-2887-487e-ad44-57badea28824@mail.xylon.local (mailing list archive)
State New, archived
Headers show

Commit Message

Davor Joja March 11, 2015, 3:27 p.m. UTC
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 mbox

Patch

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;
+}