v4l: vsp1: Add BRU support
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Wed, 10 Jul 2013 21:03:46 +0000 (18:03 -0300)
committerSimon Horman <horms@verge.net.au>
Fri, 5 Dec 2014 00:22:35 +0000 (09:22 +0900)
The Blend ROP Unit performs blending and ROP operations for up to four
sources.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
(cherry picked from commit 629bb6d4b38fe62d36ab52ad22c3ab726f6ce6e8)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
13 files changed:
drivers/media/platform/vsp1/Makefile
drivers/media/platform/vsp1/vsp1.h
drivers/media/platform/vsp1/vsp1_bru.c [new file with mode: 0644]
drivers/media/platform/vsp1/vsp1_bru.h [new file with mode: 0644]
drivers/media/platform/vsp1/vsp1_drv.c
drivers/media/platform/vsp1/vsp1_entity.c
drivers/media/platform/vsp1/vsp1_entity.h
drivers/media/platform/vsp1/vsp1_regs.h
drivers/media/platform/vsp1/vsp1_rpf.c
drivers/media/platform/vsp1/vsp1_rwpf.h
drivers/media/platform/vsp1/vsp1_video.c
drivers/media/platform/vsp1/vsp1_video.h
drivers/media/platform/vsp1/vsp1_wpf.c

index 151cecd..6a93f92 100644 (file)
@@ -1,6 +1,6 @@
 vsp1-y                                 := vsp1_drv.o vsp1_entity.o vsp1_video.o
 vsp1-y                                 += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
 vsp1-y                                 += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
-vsp1-y                                 += vsp1_sru.o vsp1_uds.o
+vsp1-y                                 += vsp1_bru.o vsp1_sru.o vsp1_uds.o
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)       += vsp1.o
index 8626e9b..6ca2cf2 100644 (file)
@@ -28,6 +28,7 @@ struct clk;
 struct device;
 
 struct vsp1_platform_data;
+struct vsp1_bru;
 struct vsp1_hsit;
 struct vsp1_lif;
 struct vsp1_lut;
@@ -49,6 +50,7 @@ struct vsp1_device {
        struct mutex lock;
        int ref_count;
 
+       struct vsp1_bru *bru;
        struct vsp1_hsit *hsi;
        struct vsp1_hsit *hst;
        struct vsp1_lif *lif;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
new file mode 100644 (file)
index 0000000..f806954
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * vsp1_bru.c  --  R-Car VSP1 Blend ROP Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+
+#define BRU_MIN_SIZE                           4U
+#define BRU_MAX_SIZE                           8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg)
+{
+       return vsp1_read(bru->entity.vsp1, reg);
+}
+
+static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
+{
+       vsp1_write(bru->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input)
+{
+       return media_entity_remote_pad(&bru->entity.pads[input]) != NULL;
+}
+
+static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+       struct vsp1_bru *bru = to_bru(subdev);
+       struct v4l2_mbus_framefmt *format;
+       unsigned int i;
+
+       if (!enable)
+               return 0;
+
+       format = &bru->entity.formats[BRU_PAD_SOURCE];
+
+       /* The hardware is extremely flexible but we have no userspace API to
+        * expose all the parameters, nor is it clear whether we would have use
+        * cases for all the supported modes. Let's just harcode the parameters
+        * to sane default values for now.
+        */
+
+       /* Disable both color data normalization and dithering. */
+       vsp1_bru_write(bru, VI6_BRU_INCTRL, 0);
+
+       /* Set the background position to cover the whole output image and
+        * set its color to opaque black.
+        */
+       vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
+                      (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
+                      (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
+       vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
+       vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL,
+                      0xff << VI6_BRU_VIRRPF_COL_A_SHIFT);
+
+       /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
+        * unit with a NOP operation to make BRU input 1 available as the
+        * Blend/ROP unit B SRC input.
+        */
+       vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
+                      VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
+                      VI6_BRU_ROP_AROP(VI6_ROP_NOP));
+
+       for (i = 0; i < 4; ++i) {
+               u32 ctrl = 0;
+
+               /* Configure all Blend/ROP units corresponding to an enabled BRU
+                * input for alpha blending. Blend/ROP units corresponding to
+                * disabled BRU inputs are used in ROP NOP mode to ignore the
+                * SRC input.
+                */
+               if (bru_is_input_enabled(bru, i))
+                       ctrl |= VI6_BRU_CTRL_RBC;
+               else
+                       ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
+                            |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
+
+               /* Select the virtual RPF as the Blend/ROP unit A DST input to
+                * serve as a background color.
+                */
+               if (i == 0)
+                       ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
+
+               /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to
+                * D in that order. The Blend/ROP unit B SRC is hardwired to the
+                * ROP unit output, the corresponding register bits must be set
+                * to 0.
+                */
+               if (i != 1)
+                       ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
+
+               vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl);
+
+               /* Harcode the blending formula to
+                *
+                *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
+                *      DSTa = DSTa * (1 - SRCa) + SRCa
+                */
+               vsp1_bru_write(bru, VI6_BRU_BLD(i),
+                              VI6_BRU_BLD_CCMDX_255_SRC_A |
+                              VI6_BRU_BLD_CCMDY_SRC_A |
+                              VI6_BRU_BLD_ACMDX_255_SRC_A |
+                              VI6_BRU_BLD_ACMDY_COEFY |
+                              (0xff << VI6_BRU_BLD_COEFY_SHIFT));
+       }
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+/*
+ * The BRU can't perform format conversion, all sink and source formats must be
+ * identical. We pick the format on the first sink pad (pad 0) and propagate it
+ * to all other pads.
+ */
+
+static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
+                             struct v4l2_subdev_fh *fh,
+                             struct v4l2_subdev_mbus_code_enum *code)
+{
+       static const unsigned int codes[] = {
+               V4L2_MBUS_FMT_ARGB8888_1X32,
+               V4L2_MBUS_FMT_AYUV8_1X32,
+       };
+       struct v4l2_mbus_framefmt *format;
+
+       if (code->pad == BRU_PAD_SINK(0)) {
+               if (code->index >= ARRAY_SIZE(codes))
+                       return -EINVAL;
+
+               code->code = codes[code->index];
+       } else {
+               if (code->index)
+                       return -EINVAL;
+
+               format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0));
+               code->code = format->code;
+       }
+
+       return 0;
+}
+
+static int bru_enum_frame_size(struct v4l2_subdev *subdev,
+                              struct v4l2_subdev_fh *fh,
+                              struct v4l2_subdev_frame_size_enum *fse)
+{
+       if (fse->index)
+               return -EINVAL;
+
+       if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+           fse->code != V4L2_MBUS_FMT_AYUV8_1X32)
+               return -EINVAL;
+
+       fse->min_width = BRU_MIN_SIZE;
+       fse->max_width = BRU_MAX_SIZE;
+       fse->min_height = BRU_MIN_SIZE;
+       fse->max_height = BRU_MAX_SIZE;
+
+       return 0;
+}
+
+static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
+                                        struct v4l2_subdev_fh *fh,
+                                        unsigned int pad, u32 which)
+{
+       switch (which) {
+       case V4L2_SUBDEV_FORMAT_TRY:
+               return v4l2_subdev_get_try_crop(fh, pad);
+       case V4L2_SUBDEV_FORMAT_ACTIVE:
+               return &bru->compose[pad];
+       default:
+               return NULL;
+       }
+}
+
+static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+                         struct v4l2_subdev_format *fmt)
+{
+       struct vsp1_bru *bru = to_bru(subdev);
+
+       fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+                                                 fmt->which);
+
+       return 0;
+}
+
+static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh,
+                          unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+                          enum v4l2_subdev_format_whence which)
+{
+       struct v4l2_mbus_framefmt *format;
+
+       switch (pad) {
+       case BRU_PAD_SINK(0):
+               /* Default to YUV if the requested format is not supported. */
+               if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+                   fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
+                       fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+               break;
+
+       default:
+               /* The BRU can't perform format conversion. */
+               format = vsp1_entity_get_pad_format(&bru->entity, fh,
+                                                   BRU_PAD_SINK(0), which);
+               fmt->code = format->code;
+               break;
+       }
+
+       fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
+       fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
+       fmt->field = V4L2_FIELD_NONE;
+       fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+                         struct v4l2_subdev_format *fmt)
+{
+       struct vsp1_bru *bru = to_bru(subdev);
+       struct v4l2_mbus_framefmt *format;
+
+       bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which);
+
+       format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+                                           fmt->which);
+       *format = fmt->format;
+
+       /* Reset the compose rectangle */
+       if (fmt->pad != BRU_PAD_SOURCE) {
+               struct v4l2_rect *compose;
+
+               compose = bru_get_compose(bru, fh, fmt->pad, fmt->which);
+               compose->left = 0;
+               compose->top = 0;
+               compose->width = format->width;
+               compose->height = format->height;
+       }
+
+       /* Propagate the format code to all pads */
+       if (fmt->pad == BRU_PAD_SINK(0)) {
+               unsigned int i;
+
+               for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
+                       format = vsp1_entity_get_pad_format(&bru->entity, fh,
+                                                           i, fmt->which);
+                       format->code = fmt->format.code;
+               }
+       }
+
+       return 0;
+}
+
+static int bru_get_selection(struct v4l2_subdev *subdev,
+                            struct v4l2_subdev_fh *fh,
+                            struct v4l2_subdev_selection *sel)
+{
+       struct vsp1_bru *bru = to_bru(subdev);
+
+       if (sel->pad == BRU_PAD_SOURCE)
+               return -EINVAL;
+
+       switch (sel->target) {
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+               sel->r.left = 0;
+               sel->r.top = 0;
+               sel->r.width = BRU_MAX_SIZE;
+               sel->r.height = BRU_MAX_SIZE;
+               return 0;
+
+       case V4L2_SEL_TGT_COMPOSE:
+               sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which);
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int bru_set_selection(struct v4l2_subdev *subdev,
+                            struct v4l2_subdev_fh *fh,
+                            struct v4l2_subdev_selection *sel)
+{
+       struct vsp1_bru *bru = to_bru(subdev);
+       struct v4l2_mbus_framefmt *format;
+       struct v4l2_rect *compose;
+
+       if (sel->pad == BRU_PAD_SOURCE)
+               return -EINVAL;
+
+       if (sel->target != V4L2_SEL_TGT_COMPOSE)
+               return -EINVAL;
+
+       /* The compose rectangle top left corner must be inside the output
+        * frame.
+        */
+       format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE,
+                                           sel->which);
+       sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
+       sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
+
+       /* Scaling isn't supported, the compose rectangle size must be identical
+        * to the sink format size.
+        */
+       format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad,
+                                           sel->which);
+       sel->r.width = format->width;
+       sel->r.height = format->height;
+
+       compose = bru_get_compose(bru, fh, sel->pad, sel->which);
+       *compose = sel->r;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops bru_video_ops = {
+       .s_stream = bru_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops bru_pad_ops = {
+       .enum_mbus_code = bru_enum_mbus_code,
+       .enum_frame_size = bru_enum_frame_size,
+       .get_fmt = bru_get_format,
+       .set_fmt = bru_set_format,
+       .get_selection = bru_get_selection,
+       .set_selection = bru_set_selection,
+};
+
+static struct v4l2_subdev_ops bru_ops = {
+       .video  = &bru_video_ops,
+       .pad    = &bru_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
+{
+       struct v4l2_subdev *subdev;
+       struct vsp1_bru *bru;
+       int ret;
+
+       bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
+       if (bru == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       bru->entity.type = VSP1_ENTITY_BRU;
+
+       ret = vsp1_entity_init(vsp1, &bru->entity, 5);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       /* Initialize the V4L2 subdev. */
+       subdev = &bru->entity.subdev;
+       v4l2_subdev_init(subdev, &bru_ops);
+
+       subdev->entity.ops = &vsp1_media_ops;
+       subdev->internal_ops = &vsp1_subdev_internal_ops;
+       snprintf(subdev->name, sizeof(subdev->name), "%s bru",
+                dev_name(vsp1->dev));
+       v4l2_set_subdevdata(subdev, bru);
+       subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+       vsp1_entity_init_formats(subdev, NULL);
+
+       return bru;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
new file mode 100644 (file)
index 0000000..3706270
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * vsp1_bru.h  --  R-Car VSP1 Blend ROP Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_BRU_H__
+#define __VSP1_BRU_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define BRU_PAD_SINK(n)                                (n)
+#define BRU_PAD_SOURCE                         4
+
+struct vsp1_bru {
+       struct vsp1_entity entity;
+
+       struct v4l2_rect compose[4];
+};
+
+static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
+{
+       return container_of(subdev, struct vsp1_bru, entity.subdev);
+}
+
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_BRU_H__ */
index 3cd2df5..28e1de3 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/videodev2.h>
 
 #include "vsp1.h"
+#include "vsp1_bru.h"
 #include "vsp1_hsit.h"
 #include "vsp1_lif.h"
 #include "vsp1_lut.h"
@@ -155,6 +156,14 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
        }
 
        /* Instantiate all the entities. */
+       vsp1->bru = vsp1_bru_create(vsp1);
+       if (IS_ERR(vsp1->bru)) {
+               ret = PTR_ERR(vsp1->bru);
+               goto done;
+       }
+
+       list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
+
        vsp1->hsi = vsp1_hsit_create(vsp1, true);
        if (IS_ERR(vsp1->hsi)) {
                ret = PTR_ERR(vsp1->hsi);
index a9022f8..4416783 100644 (file)
@@ -119,6 +119,9 @@ const struct media_entity_operations vsp1_media_ops = {
  */
 
 static const struct vsp1_route vsp1_routes[] = {
+       { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
+         { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
+           VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } },
        { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
        { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
        { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
index 3c6a5c8..7afbd8a 100644 (file)
@@ -20,6 +20,7 @@
 struct vsp1_device;
 
 enum vsp1_entity_type {
+       VSP1_ENTITY_BRU,
        VSP1_ENTITY_HSI,
        VSP1_ENTITY_HST,
        VSP1_ENTITY_LIF,
index 2865080..3e74b44 100644 (file)
  * BRU Control Registers
  */
 
+#define VI6_ROP_NOP                    0
+#define VI6_ROP_AND                    1
+#define VI6_ROP_AND_REV                        2
+#define VI6_ROP_COPY                   3
+#define VI6_ROP_AND_INV                        4
+#define VI6_ROP_CLEAR                  5
+#define VI6_ROP_XOR                    6
+#define VI6_ROP_OR                     7
+#define VI6_ROP_NOR                    8
+#define VI6_ROP_EQUIV                  9
+#define VI6_ROP_INVERT                 10
+#define VI6_ROP_OR_REV                 11
+#define VI6_ROP_COPY_INV               12
+#define VI6_ROP_OR_INV                 13
+#define VI6_ROP_NAND                   14
+#define VI6_ROP_SET                    15
+
 #define VI6_BRU_INCTRL                 0x2c00
+#define VI6_BRU_INCTRL_NRM             (1 << 28)
+#define VI6_BRU_INCTRL_DnON            (1 << (16 + (n)))
+#define VI6_BRU_INCTRL_DITHn_OFF       (0 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_18BPP     (1 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_16BPP     (2 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_15BPP     (3 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_12BPP     (4 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_8BPP      (5 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_MASK      (7 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_SHIFT     ((n) * 4)
+
 #define VI6_BRU_VIRRPF_SIZE            0x2c04
+#define VI6_BRU_VIRRPF_SIZE_HSIZE_MASK (0x1fff << 16)
+#define VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT        16
+#define VI6_BRU_VIRRPF_SIZE_VSIZE_MASK (0x1fff << 0)
+#define VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT        0
+
 #define VI6_BRU_VIRRPF_LOC             0x2c08
+#define VI6_BRU_VIRRPF_LOC_HCOORD_MASK (0x1fff << 16)
+#define VI6_BRU_VIRRPF_LOC_HCOORD_SHIFT        16
+#define VI6_BRU_VIRRPF_LOC_VCOORD_MASK (0x1fff << 0)
+#define VI6_BRU_VIRRPF_LOC_VCOORD_SHIFT        0
+
 #define VI6_BRU_VIRRPF_COL             0x2c0c
+#define VI6_BRU_VIRRPF_COL_A_MASK      (0xff << 24)
+#define VI6_BRU_VIRRPF_COL_A_SHIFT     24
+#define VI6_BRU_VIRRPF_COL_RCR_MASK    (0xff << 16)
+#define VI6_BRU_VIRRPF_COL_RCR_SHIFT   16
+#define VI6_BRU_VIRRPF_COL_GY_MASK     (0xff << 8)
+#define VI6_BRU_VIRRPF_COL_GY_SHIFT    8
+#define VI6_BRU_VIRRPF_COL_BCB_MASK    (0xff << 0)
+#define VI6_BRU_VIRRPF_COL_BCB_SHIFT   0
+
 #define VI6_BRU_CTRL(n)                        (0x2c10 + (n) * 8)
+#define VI6_BRU_CTRL_RBC               (1 << 31)
+#define VI6_BRU_CTRL_DSTSEL_BRUIN(n)   ((n) << 20)
+#define VI6_BRU_CTRL_DSTSEL_VRPF       (4 << 20)
+#define VI6_BRU_CTRL_DSTSEL_MASK       (7 << 20)
+#define VI6_BRU_CTRL_SRCSEL_BRUIN(n)   ((n) << 16)
+#define VI6_BRU_CTRL_SRCSEL_VRPF       (4 << 16)
+#define VI6_BRU_CTRL_SRCSEL_MASK       (7 << 16)
+#define VI6_BRU_CTRL_CROP(rop)         ((rop) << 4)
+#define VI6_BRU_CTRL_CROP_MASK         (0xf << 4)
+#define VI6_BRU_CTRL_AROP(rop)         ((rop) << 0)
+#define VI6_BRU_CTRL_AROP_MASK         (0xf << 0)
+
 #define VI6_BRU_BLD(n)                 (0x2c14 + (n) * 8)
+#define VI6_BRU_BLD_CBES               (1 << 31)
+#define VI6_BRU_BLD_CCMDX_DST_A                (0 << 28)
+#define VI6_BRU_BLD_CCMDX_255_DST_A    (1 << 28)
+#define VI6_BRU_BLD_CCMDX_SRC_A                (2 << 28)
+#define VI6_BRU_BLD_CCMDX_255_SRC_A    (3 << 28)
+#define VI6_BRU_BLD_CCMDX_COEFX                (4 << 28)
+#define VI6_BRU_BLD_CCMDX_MASK         (7 << 28)
+#define VI6_BRU_BLD_CCMDY_DST_A                (0 << 24)
+#define VI6_BRU_BLD_CCMDY_255_DST_A    (1 << 24)
+#define VI6_BRU_BLD_CCMDY_SRC_A                (2 << 24)
+#define VI6_BRU_BLD_CCMDY_255_SRC_A    (3 << 24)
+#define VI6_BRU_BLD_CCMDY_COEFY                (4 << 24)
+#define VI6_BRU_BLD_CCMDY_MASK         (7 << 24)
+#define VI6_BRU_BLD_CCMDY_SHIFT                24
+#define VI6_BRU_BLD_ABES               (1 << 23)
+#define VI6_BRU_BLD_ACMDX_DST_A                (0 << 20)
+#define VI6_BRU_BLD_ACMDX_255_DST_A    (1 << 20)
+#define VI6_BRU_BLD_ACMDX_SRC_A                (2 << 20)
+#define VI6_BRU_BLD_ACMDX_255_SRC_A    (3 << 20)
+#define VI6_BRU_BLD_ACMDX_COEFX                (4 << 20)
+#define VI6_BRU_BLD_ACMDX_MASK         (7 << 20)
+#define VI6_BRU_BLD_ACMDY_DST_A                (0 << 16)
+#define VI6_BRU_BLD_ACMDY_255_DST_A    (1 << 16)
+#define VI6_BRU_BLD_ACMDY_SRC_A                (2 << 16)
+#define VI6_BRU_BLD_ACMDY_255_SRC_A    (3 << 16)
+#define VI6_BRU_BLD_ACMDY_COEFY                (4 << 16)
+#define VI6_BRU_BLD_ACMDY_MASK         (7 << 16)
+#define VI6_BRU_BLD_COEFX_MASK         (0xff << 8)
+#define VI6_BRU_BLD_COEFX_SHIFT                8
+#define VI6_BRU_BLD_COEFY_MASK         (0xff << 0)
+#define VI6_BRU_BLD_COEFY_SHIFT                0
+
 #define VI6_BRU_ROP                    0x2c30
+#define VI6_BRU_ROP_DSTSEL_BRUIN(n)    ((n) << 20)
+#define VI6_BRU_ROP_DSTSEL_VRPF                (4 << 20)
+#define VI6_BRU_ROP_DSTSEL_MASK                (7 << 20)
+#define VI6_BRU_ROP_CROP(rop)          ((rop) << 4)
+#define VI6_BRU_ROP_CROP_MASK          (0xf << 4)
+#define VI6_BRU_ROP_AROP(rop)          ((rop) << 0)
+#define VI6_BRU_ROP_AROP_MASK          (0xf << 0)
 
 /* -----------------------------------------------------------------------------
  * HGO Control Registers
index 4244456..c3d9864 100644 (file)
@@ -96,8 +96,10 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
        vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
        vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
 
-       /* Output location. Composing isn't supported yet. */
-       vsp1_rpf_write(rpf, VI6_RPF_LOC, 0);
+       /* Output location */
+       vsp1_rpf_write(rpf, VI6_RPF_LOC,
+                      (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
+                      (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
 
        /* Disable alpha, mask and color key. Set the alpha channel to a fixed
         * value of 255.
index 5c5ee81..b4fb65e 100644 (file)
@@ -30,6 +30,10 @@ struct vsp1_rwpf {
        unsigned int max_width;
        unsigned int max_height;
 
+       struct {
+               unsigned int left;
+               unsigned int top;
+       } location;
        struct v4l2_rect crop;
 
        unsigned int offsets[2];
index 3926458..41a5015 100644 (file)
@@ -28,6 +28,7 @@
 #include <media/videobuf2-dma-contig.h>
 
 #include "vsp1.h"
+#include "vsp1_bru.h"
 #include "vsp1_entity.h"
 #include "vsp1_rwpf.h"
 #include "vsp1_video.h"
@@ -280,6 +281,9 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
        struct media_pad *pad;
        bool uds_found = false;
 
+       input->location.left = 0;
+       input->location.top = 0;
+
        pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
 
        while (1) {
@@ -292,6 +296,17 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
 
                entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
 
+               /* A BRU is present in the pipeline, store the compose rectangle
+                * location in the input RPF for use when configuring the RPF.
+                */
+               if (entity->type == VSP1_ENTITY_BRU) {
+                       struct vsp1_bru *bru = to_bru(&entity->subdev);
+                       struct v4l2_rect *rect = &bru->compose[pad->index];
+
+                       input->location.left = rect->left;
+                       input->location.top = rect->top;
+               }
+
                /* We've reached the WPF, we're done. */
                if (entity->type == VSP1_ENTITY_WPF)
                        break;
@@ -363,6 +378,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
                        rwpf->video.pipe_index = 0;
                } else if (e->type == VSP1_ENTITY_LIF) {
                        pipe->lif = e;
+               } else if (e->type == VSP1_ENTITY_BRU) {
+                       pipe->bru = e;
                }
        }
 
@@ -392,6 +409,7 @@ error:
        pipe->num_video = 0;
        pipe->num_inputs = 0;
        pipe->output = NULL;
+       pipe->bru = NULL;
        pipe->lif = NULL;
        return ret;
 }
@@ -430,6 +448,7 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
                pipe->num_video = 0;
                pipe->num_inputs = 0;
                pipe->output = NULL;
+               pipe->bru = NULL;
                pipe->lif = NULL;
        }
 
index 6063a36..7284320 100644 (file)
@@ -75,6 +75,7 @@ struct vsp1_pipeline {
        unsigned int num_inputs;
        struct vsp1_rwpf *inputs[VPS1_MAX_RPF];
        struct vsp1_rwpf *output;
+       struct vsp1_entity *bru;
        struct vsp1_entity *lif;
 
        struct list_head entities;
index ef9f88e..1294340 100644 (file)
@@ -58,13 +58,21 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
                return 0;
        }
 
-       /* Sources */
+       /* Sources. If the pipeline has a single input configure it as the
+        * master layer. Otherwise configure all inputs as sub-layers and
+        * select the virtual RPF as the master layer.
+        */
        for (i = 0; i < pipe->num_inputs; ++i) {
                struct vsp1_rwpf *input = pipe->inputs[i];
 
-               srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index);
+               srcrpf |= pipe->num_inputs == 1
+                       ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
+                       : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
        }
 
+       if (pipe->num_inputs > 1)
+               srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
+
        vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf);
 
        /* Destination stride. */