.colplanes      = 1,
                .flags          = FMT_FLAGS_M2M,
        }, {
-               .name           = "XRGB-8-8-8-8, 32 bpp",
+               .name           = "ARGB8888, 32 bpp",
                .fourcc         = V4L2_PIX_FMT_RGB32,
                .depth          = { 32 },
                .color          = S5P_FIMC_RGB888,
                .memplanes      = 1,
                .colplanes      = 1,
-               .flags          = FMT_FLAGS_M2M,
+               .flags          = FMT_FLAGS_M2M | FMT_HAS_ALPHA,
+       }, {
+               .name           = "ARGB1555",
+               .fourcc         = V4L2_PIX_FMT_RGB555,
+               .depth          = { 16 },
+               .color          = S5P_FIMC_RGB555,
+               .memplanes      = 1,
+               .colplanes      = 1,
+               .flags          = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
+       }, {
+               .name           = "ARGB4444",
+               .fourcc         = V4L2_PIX_FMT_RGB444,
+               .depth          = { 16 },
+               .color          = S5P_FIMC_RGB444,
+               .memplanes      = 1,
+               .colplanes      = 1,
+               .flags          = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
        }, {
                .name           = "YUV 4:2:2 packed, YCbYCr",
                .fourcc         = V4L2_PIX_FMT_YUYV,
        },
 };
 
+static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
+{
+       if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               return FMT_FLAGS_M2M_IN;
+       else
+               return FMT_FLAGS_M2M_OUT;
+}
+
 int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
                            int dw, int dh, int rotation)
 {
        if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
                fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1);
 
-       if (ctx->state & FIMC_PARAMS)
+       if (ctx->state & FIMC_PARAMS) {
                fimc_hw_set_out_dma(ctx);
+               if (fimc->variant->has_alpha)
+                       fimc_hw_set_rgb_alpha(ctx);
+       }
 
        fimc_activate_capture(ctx);
 
 #define ctrl_to_ctx(__ctrl) \
        container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
 
-static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
+static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
 {
-       struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);
        struct fimc_dev *fimc = ctx->fimc_dev;
        struct samsung_fimc_variant *variant = fimc->variant;
-       unsigned long flags;
+       unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT;
        int ret = 0;
 
        if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
 
        switch (ctrl->id) {
        case V4L2_CID_HFLIP:
-               spin_lock_irqsave(&ctx->slock, flags);
                ctx->hflip = ctrl->val;
                break;
 
        case V4L2_CID_VFLIP:
-               spin_lock_irqsave(&ctx->slock, flags);
                ctx->vflip = ctrl->val;
                break;
 
        case V4L2_CID_ROTATE:
                if (fimc_capture_pending(fimc) ||
-                   fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+                   (ctx->state & flags) == flags) {
                        ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
                                        ctx->s_frame.height, ctx->d_frame.width,
                                        ctx->d_frame.height, ctrl->val);
-               }
-               if (ret) {
-                       v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
-                       return -EINVAL;
+                       if (ret)
+                               return -EINVAL;
                }
                if ((ctrl->val == 90 || ctrl->val == 270) &&
                    !variant->has_out_rot)
                        return -EINVAL;
-               spin_lock_irqsave(&ctx->slock, flags);
+
                ctx->rotation = ctrl->val;
                break;
 
-       default:
-               v4l2_err(fimc->v4l2_dev, "Invalid control: 0x%X\n", ctrl->id);
-               return -EINVAL;
+       case V4L2_CID_ALPHA_COMPONENT:
+               ctx->d_frame.alpha = ctrl->val;
+               break;
        }
        ctx->state |= FIMC_PARAMS;
        set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
-       spin_unlock_irqrestore(&ctx->slock, flags);
        return 0;
 }
 
+static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&ctx->slock, flags);
+       ret = __fimc_s_ctrl(ctx, ctrl);
+       spin_unlock_irqrestore(&ctx->slock, flags);
+
+       return ret;
+}
+
 static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
        .s_ctrl = fimc_s_ctrl,
 };
 
 int fimc_ctrls_create(struct fimc_ctx *ctx)
 {
+       struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+       unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt);
+
        if (ctx->ctrls_rdy)
                return 0;
-       v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+       v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
 
        ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
                                     V4L2_CID_HFLIP, 0, 1, 1, 0);
                                    V4L2_CID_VFLIP, 0, 1, 1, 0);
        ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
                                    V4L2_CID_ROTATE, 0, 270, 90, 0);
+       if (variant->has_alpha)
+               ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+                                   &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
+                                   0, max_alpha, 1, 0);
+       else
+               ctx->ctrl_alpha = NULL;
+
        ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
 
        return ctx->ctrl_handler.error;
        if (ctx->ctrls_rdy) {
                v4l2_ctrl_handler_free(&ctx->ctrl_handler);
                ctx->ctrls_rdy = false;
+               ctx->ctrl_alpha = NULL;
        }
 }
 
 void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)
 {
+       unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA;
+
        if (!ctx->ctrls_rdy)
                return;
 
        v4l2_ctrl_activate(ctx->ctrl_rotate, active);
        v4l2_ctrl_activate(ctx->ctrl_hflip, active);
        v4l2_ctrl_activate(ctx->ctrl_vflip, active);
+       if (ctx->ctrl_alpha)
+               v4l2_ctrl_activate(ctx->ctrl_alpha, active && has_alpha);
 
        if (active) {
                ctx->rotation = ctx->ctrl_rotate->val;
        mutex_unlock(&ctx->ctrl_handler.lock);
 }
 
+/* Update maximum value of the alpha color control */
+void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *fimc = ctx->fimc_dev;
+       struct v4l2_ctrl *ctrl = ctx->ctrl_alpha;
+
+       if (ctrl == NULL || !fimc->variant->has_alpha)
+               return;
+
+       v4l2_ctrl_lock(ctrl);
+       ctrl->maximum = fimc_get_alpha_mask(ctx->d_frame.fmt);
+
+       if (ctrl->cur.val > ctrl->maximum)
+               ctrl->cur.val = ctrl->maximum;
+
+       v4l2_ctrl_unlock(ctrl);
+}
+
 /*
  * V4L2 ioctl handlers
  */
 {
        struct fimc_fmt *fmt;
 
-       fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_M2M, f->index);
+       fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
+                              f->index);
        if (!fmt)
                return -EINVAL;
 
        pix->colorspace = V4L2_COLORSPACE_JPEG;
        pix->field = V4L2_FIELD_NONE;
        pix->num_planes = fmt->memplanes;
+       pix->pixelformat = fmt->fourcc;
        pix->height = height;
        pix->width = width;
 
 
        dbg("w: %d, h: %d", pix->width, pix->height);
 
-       fmt = fimc_find_format(&pix->pixelformat, NULL, FMT_FLAGS_M2M, 0);
+       fmt = fimc_find_format(&pix->pixelformat, NULL,
+                              get_m2m_fmt_flags(f->type), 0);
        if (WARN(fmt == NULL, "Pixel format lookup failed"))
                return -EINVAL;
 
 
        pix = &f->fmt.pix_mp;
        frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
-                                     FMT_FLAGS_M2M, 0);
+                                     get_m2m_fmt_flags(f->type), 0);
        if (!frame->fmt)
                return -EINVAL;
 
+       /* Update RGB Alpha control state and value range */
+       fimc_alpha_ctrl_update(ctx);
+
        for (i = 0; i < frame->fmt->colplanes; i++) {
                frame->payload[i] =
                        (pix->width * pix->height * frame->fmt->depth[i]) / 8;
        if (!ctx)
                return -ENOMEM;
        v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
+       ctx->fimc_dev = fimc;
+
+       /* Default color format */
+       ctx->s_frame.fmt = &fimc_formats[0];
+       ctx->d_frame.fmt = &fimc_formats[0];
+
        ret = fimc_ctrls_create(ctx);
        if (ret)
                goto error_fh;
        file->private_data = &ctx->fh;
        v4l2_fh_add(&ctx->fh);
 
-       ctx->fimc_dev = fimc;
-       /* Default color format */
-       ctx->s_frame.fmt = &fimc_formats[0];
-       ctx->d_frame.fmt = &fimc_formats[0];
        /* Setup the device context for memory-to-memory mode */
        ctx->state = FIMC_CTX_M2M;
        ctx->flags = 0;
        .has_cam_if      = 1,
        .has_cistatus2   = 1,
        .has_mainscaler_ext = 1,
+       .has_alpha       = 1,
        .min_inp_pixsize = 16,
        .min_out_pixsize = 16,
        .hor_offs_align  = 2,
        .has_cam_if      = 1,
        .has_cistatus2   = 1,
        .has_mainscaler_ext = 1,
+       .has_alpha       = 1,
        .min_inp_pixsize = 16,
        .min_out_pixsize = 16,
        .hor_offs_align  = 2,
 
 };
 
 enum fimc_color_fmt {
-       S5P_FIMC_RGB565 = 0x10,
+       S5P_FIMC_RGB444 = 0x10,
+       S5P_FIMC_RGB555,
+       S5P_FIMC_RGB565,
        S5P_FIMC_RGB666,
        S5P_FIMC_RGB888,
        S5P_FIMC_RGB30_LOCAL,
        u16     colplanes;
        u8      depth[VIDEO_MAX_PLANES];
        u16     flags;
-#define FMT_FLAGS_CAM  (1 << 0)
-#define FMT_FLAGS_M2M  (1 << 1)
+#define FMT_FLAGS_CAM          (1 << 0)
+#define FMT_FLAGS_M2M_IN       (1 << 1)
+#define FMT_FLAGS_M2M_OUT      (1 << 2)
+#define FMT_FLAGS_M2M          (1 << 1 | 1 << 2)
+#define FMT_HAS_ALPHA          (1 << 3)
 };
 
 /**
        struct fimc_addr        paddr;
        struct fimc_dma_offset  dma_offset;
        struct fimc_fmt         *fmt;
+       u8                      alpha;
 };
 
 /**
        unsigned int    has_cistatus2:1;
        unsigned int    has_mainscaler_ext:1;
        unsigned int    has_cam_if:1;
+       unsigned int    has_alpha:1;
        struct fimc_pix_limit *pix_limit;
        u16             min_inp_pixsize;
        u16             min_out_pixsize;
  * @ctrl_handler:      v4l2 controls handler
  * @ctrl_rotate                image rotation control
  * @ctrl_hflip         horizontal flip control
- * @ctrl_vflip         vartical flip control
+ * @ctrl_vflip         vertical flip control
+ * @ctrl_alpha         RGB alpha control
  * @ctrls_rdy:         true if the control handler is initialized
  */
 struct fimc_ctx {
        struct v4l2_ctrl        *ctrl_rotate;
        struct v4l2_ctrl        *ctrl_hflip;
        struct v4l2_ctrl        *ctrl_vflip;
+       struct v4l2_ctrl        *ctrl_alpha;
        bool                    ctrls_rdy;
 };
 
        return fmt->fourcc == V4L2_PIX_FMT_NV12MT;
 }
 
+/* Return the alpha component bit mask */
+static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt)
+{
+       switch (fmt->color) {
+       case S5P_FIMC_RGB444:   return 0x0f;
+       case S5P_FIMC_RGB555:   return 0x01;
+       case S5P_FIMC_RGB888:   return 0xff;
+       default:                return 0;
+       };
+}
+
 static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
 {
        u32 cfg = readl(dev->regs + S5P_CIGCTRL);
 void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
 void fimc_hw_en_capture(struct fimc_ctx *ctx);
 void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
+void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx);
 void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
 void fimc_hw_set_input_path(struct fimc_ctx *ctx);
 void fimc_hw_set_output_path(struct fimc_ctx *ctx);
 int fimc_ctrls_create(struct fimc_ctx *ctx);
 void fimc_ctrls_delete(struct fimc_ctx *ctx);
 void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
+void fimc_alpha_ctrl_update(struct fimc_ctx *ctx);
 int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
 void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
                               struct v4l2_pix_format_mplane *pix);
 
                  S5P_CITRGFMT_VSIZE_MASK);
 
        switch (frame->fmt->color) {
-       case S5P_FIMC_RGB565...S5P_FIMC_RGB888:
+       case S5P_FIMC_RGB444...S5P_FIMC_RGB888:
                cfg |= S5P_CITRGFMT_RGB;
                break;
        case S5P_FIMC_YCBCR420:
        struct fimc_dev *dev = ctx->fimc_dev;
        struct fimc_frame *frame = &ctx->d_frame;
        struct fimc_dma_offset *offset = &frame->dma_offset;
+       struct fimc_fmt *fmt = frame->fmt;
 
        /* Set the input dma offsets. */
        cfg = 0;
        cfg = readl(dev->regs + S5P_CIOCTRL);
 
        cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
-                S5P_CIOCTRL_YCBCR_PLANE_MASK);
+                S5P_CIOCTRL_YCBCR_PLANE_MASK | S5P_CIOCTRL_RGB16FMT_MASK);
 
-       if (frame->fmt->colplanes == 1)
+       if (fmt->colplanes == 1)
                cfg |= ctx->out_order_1p;
-       else if (frame->fmt->colplanes == 2)
+       else if (fmt->colplanes == 2)
                cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
-       else if (frame->fmt->colplanes == 3)
+       else if (fmt->colplanes == 3)
                cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
 
+       if (fmt->color == S5P_FIMC_RGB565)
+               cfg |= S5P_CIOCTRL_RGB565;
+       else if (fmt->color == S5P_FIMC_RGB555)
+               cfg |= S5P_CIOCTRL_ARGB1555;
+       else if (fmt->color == S5P_FIMC_RGB444)
+               cfg |= S5P_CIOCTRL_ARGB4444;
+
        writel(cfg, dev->regs + S5P_CIOCTRL);
 }
 
        if (sc->copy_mode)
                cfg |= S5P_CISCCTRL_ONE2ONE;
 
-
        if (ctx->in_path == FIMC_DMA) {
-               if (src_frame->fmt->color == S5P_FIMC_RGB565)
+               switch (src_frame->fmt->color) {
+               case S5P_FIMC_RGB565:
                        cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
-               else if (src_frame->fmt->color == S5P_FIMC_RGB666)
+                       break;
+               case S5P_FIMC_RGB666:
                        cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
-               else if (src_frame->fmt->color == S5P_FIMC_RGB888)
+                       break;
+               case S5P_FIMC_RGB888:
                        cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+                       break;
+               }
        }
 
        if (ctx->out_path == FIMC_DMA) {
-               if (dst_frame->fmt->color == S5P_FIMC_RGB565)
+               u32 color = dst_frame->fmt->color;
+
+               if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565)
                        cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
-               else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
+               else if (color == S5P_FIMC_RGB666)
                        cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
-               else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
+               else if (color == S5P_FIMC_RGB888)
                        cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
        } else {
                cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
        writel(cfg, dev->regs + S5P_CIIMGEFF);
 }
 
+void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->d_frame;
+       u32 cfg;
+
+       if (!(frame->fmt->flags & FMT_HAS_ALPHA))
+               return;
+
+       cfg = readl(dev->regs + S5P_CIOCTRL);
+       cfg &= ~S5P_CIOCTRL_ALPHA_OUT_MASK;
+       cfg |= (frame->alpha << 4);
+       writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
 static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
 {
        struct fimc_dev *dev = ctx->fimc_dev;