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

Signed-off-by: Davor Joja <davorj...@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.j...@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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to