| From e71ae3c9c4d461976d62989144f495cffca5daeb Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Date: Wed, 10 Jul 2013 18:03:46 -0300 |
| Subject: [media] v4l: vsp1: Add BRU support |
| |
| 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> |
| --- |
| drivers/media/platform/vsp1/Makefile | 2 +- |
| drivers/media/platform/vsp1/vsp1.h | 2 + |
| drivers/media/platform/vsp1/vsp1_bru.c | 395 ++++++++++++++++++++++++++++++ |
| drivers/media/platform/vsp1/vsp1_bru.h | 39 +++ |
| drivers/media/platform/vsp1/vsp1_drv.c | 9 + |
| drivers/media/platform/vsp1/vsp1_entity.c | 3 + |
| drivers/media/platform/vsp1/vsp1_entity.h | 1 + |
| drivers/media/platform/vsp1/vsp1_regs.h | 98 ++++++++ |
| drivers/media/platform/vsp1/vsp1_rpf.c | 6 +- |
| drivers/media/platform/vsp1/vsp1_rwpf.h | 4 + |
| drivers/media/platform/vsp1/vsp1_video.c | 19 ++ |
| drivers/media/platform/vsp1/vsp1_video.h | 1 + |
| drivers/media/platform/vsp1/vsp1_wpf.c | 12 +- |
| 13 files changed, 586 insertions(+), 5 deletions(-) |
| create mode 100644 drivers/media/platform/vsp1/vsp1_bru.c |
| create mode 100644 drivers/media/platform/vsp1/vsp1_bru.h |
| |
| diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile |
| index 151cecd0ea25..6a93f928dfde 100644 |
| --- a/drivers/media/platform/vsp1/Makefile |
| +++ b/drivers/media/platform/vsp1/Makefile |
| @@ -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 |
| diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h |
| index 8626e9b956c2..6ca2cf20d545 100644 |
| --- a/drivers/media/platform/vsp1/vsp1.h |
| +++ b/drivers/media/platform/vsp1/vsp1.h |
| @@ -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 |
| index 000000000000..f80695480060 |
| --- /dev/null |
| +++ b/drivers/media/platform/vsp1/vsp1_bru.c |
| @@ -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 |
| index 000000000000..37062704dbf6 |
| --- /dev/null |
| +++ b/drivers/media/platform/vsp1/vsp1_bru.h |
| @@ -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__ */ |
| diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c |
| index 3cd2df5af90e..28e1de3270e0 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_drv.c |
| +++ b/drivers/media/platform/vsp1/vsp1_drv.c |
| @@ -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); |
| diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c |
| index a9022f858aa5..44167834285d 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_entity.c |
| +++ b/drivers/media/platform/vsp1/vsp1_entity.c |
| @@ -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, } }, |
| diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h |
| index 3c6a5c831bcf..7afbd8a7ba66 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_entity.h |
| +++ b/drivers/media/platform/vsp1/vsp1_entity.h |
| @@ -20,6 +20,7 @@ |
| struct vsp1_device; |
| |
| enum vsp1_entity_type { |
| + VSP1_ENTITY_BRU, |
| VSP1_ENTITY_HSI, |
| VSP1_ENTITY_HST, |
| VSP1_ENTITY_LIF, |
| diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h |
| index 28650806c20f..3e74b44286f6 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_regs.h |
| +++ b/drivers/media/platform/vsp1/vsp1_regs.h |
| @@ -451,13 +451,111 @@ |
| * 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 |
| diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c |
| index 42444568b9fd..c3d98642a4aa 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_rpf.c |
| +++ b/drivers/media/platform/vsp1/vsp1_rpf.c |
| @@ -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. |
| diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h |
| index 5c5ee81bbeae..b4fb65e58770 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_rwpf.h |
| +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h |
| @@ -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]; |
| diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c |
| index f0406ccc329b..2fc67b6161f1 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_video.c |
| +++ b/drivers/media/platform/vsp1/vsp1_video.c |
| @@ -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; |
| } |
| |
| diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h |
| index 53e4b3745940..c04d48fa2999 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_video.h |
| +++ b/drivers/media/platform/vsp1/vsp1_video.h |
| @@ -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; |
| diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c |
| index ef9f88ead319..1294340dcb36 100644 |
| --- a/drivers/media/platform/vsp1/vsp1_wpf.c |
| +++ b/drivers/media/platform/vsp1/vsp1_wpf.c |
| @@ -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. */ |
| -- |
| 2.1.2 |
| |