diff mbox

[v2,07/14] v4l: vsp1: Add support for the BRS entity

Message ID 20170626181226.29575-8-laurent.pinchart+renesas@ideasonboard.com (mailing list archive)
State New, archived
Headers show

Commit Message

Laurent Pinchart June 26, 2017, 6:12 p.m. UTC
The Blend/ROP Sub Unit (BRS) is a stripped-down version of the BRU found
in several VSP2 instances. Compared to a regular BRU, it supports two
inputs only, and thus has no ROP unit.

Add support for the BRS by modeling it as a new entity type, but reuse
the vsp1_bru object underneath. Chaining the BRU and BRS entities seems
to be supported by the hardware but isn't implemented yet as it isn't
the primary use case for the BRS.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/media/platform/vsp1/vsp1.h        |  2 +
 drivers/media/platform/vsp1/vsp1_bru.c    | 45 ++++++++++++++--------
 drivers/media/platform/vsp1/vsp1_bru.h    |  4 +-
 drivers/media/platform/vsp1/vsp1_drv.c    | 19 +++++++++-
 drivers/media/platform/vsp1/vsp1_entity.c | 13 ++++++-
 drivers/media/platform/vsp1/vsp1_entity.h |  1 +
 drivers/media/platform/vsp1/vsp1_pipe.c   |  7 ++--
 drivers/media/platform/vsp1/vsp1_regs.h   | 26 +++++++++----
 drivers/media/platform/vsp1/vsp1_video.c  | 63 ++++++++++++++++++++-----------
 drivers/media/platform/vsp1/vsp1_wpf.c    |  4 +-
 10 files changed, 130 insertions(+), 54 deletions(-)

Comments

Kieran Bingham July 13, 2017, 1:38 p.m. UTC | #1
On 26/06/17 19:12, Laurent Pinchart wrote:
> The Blend/ROP Sub Unit (BRS) is a stripped-down version of the BRU found
> in several VSP2 instances. Compared to a regular BRU, it supports two
> inputs only, and thus has no ROP unit.
> 
> Add support for the BRS by modeling it as a new entity type, but reuse

s/modeling/modelling/


> the vsp1_bru object underneath. Chaining the BRU and BRS entities seems
> to be supported by the hardware but isn't implemented yet as it isn't
> the primary use case for the BRS.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

> ---
>  drivers/media/platform/vsp1/vsp1.h        |  2 +
>  drivers/media/platform/vsp1/vsp1_bru.c    | 45 ++++++++++++++--------
>  drivers/media/platform/vsp1/vsp1_bru.h    |  4 +-
>  drivers/media/platform/vsp1/vsp1_drv.c    | 19 +++++++++-
>  drivers/media/platform/vsp1/vsp1_entity.c | 13 ++++++-
>  drivers/media/platform/vsp1/vsp1_entity.h |  1 +
>  drivers/media/platform/vsp1/vsp1_pipe.c   |  7 ++--
>  drivers/media/platform/vsp1/vsp1_regs.h   | 26 +++++++++----
>  drivers/media/platform/vsp1/vsp1_video.c  | 63 ++++++++++++++++++++-----------
>  drivers/media/platform/vsp1/vsp1_wpf.c    |  4 +-
>  10 files changed, 130 insertions(+), 54 deletions(-)
> 
> diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
> index 847963b6e9eb..73858a0ed35c 100644
> --- a/drivers/media/platform/vsp1/vsp1.h
> +++ b/drivers/media/platform/vsp1/vsp1.h
> @@ -54,6 +54,7 @@ struct vsp1_uds;
>  #define VSP1_HAS_WPF_HFLIP	(1 << 6)
>  #define VSP1_HAS_HGO		(1 << 7)
>  #define VSP1_HAS_HGT		(1 << 8)
> +#define VSP1_HAS_BRS		(1 << 9)
>  
>  struct vsp1_device_info {
>  	u32 version;
> @@ -76,6 +77,7 @@ struct vsp1_device {
>  	struct rcar_fcp_device *fcp;
>  	struct device *bus_master;
>  
> +	struct vsp1_bru *brs;
>  	struct vsp1_bru *bru;
>  	struct vsp1_clu *clu;
>  	struct vsp1_hgo *hgo;
> diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
> index 85362c5ef57a..e8fd2ae3b3eb 100644
> --- a/drivers/media/platform/vsp1/vsp1_bru.c
> +++ b/drivers/media/platform/vsp1/vsp1_bru.c
> @@ -33,7 +33,7 @@
>  static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
>  				  u32 reg, u32 data)
>  {
> -	vsp1_dl_list_write(dl, reg, data);
> +	vsp1_dl_list_write(dl, bru->base + reg, data);
>  }
>  
>  /* -----------------------------------------------------------------------------
> @@ -332,11 +332,14 @@ static void bru_configure(struct vsp1_entity *entity,
>  	/*
>  	 * 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.
> +	 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
> +	 * unit.
>  	 */
> -	vsp1_bru_write(bru, dl, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
> -		       VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
> -		       VI6_BRU_ROP_AROP(VI6_ROP_NOP));
> +	if (entity->type == VSP1_ENTITY_BRU)
> +		vsp1_bru_write(bru, dl, 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 < bru->entity.source_pad; ++i) {
>  		bool premultiplied = false;
> @@ -366,12 +369,13 @@ static void bru_configure(struct vsp1_entity *entity,
>  			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.
> +		 * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
> +		 * in that order. In the BRU the Blend/ROP unit B SRC is
> +		 * hardwired to the ROP unit output, the corresponding register
> +		 * bits must be set to 0. The BRS has no ROP unit and doesn't
> +		 * need any special processing.
>  		 */
> -		if (i != 1)
> +		if (!(entity->type == VSP1_ENTITY_BRU && i == 1))

If we're using this module for both BRU and BRS, would an is_bru(entity) and
is_brs(entity) be cleaner here ?

Not required - just thinking outloud...

Actaully - it's only this line that would be affected so not really needed.  I
thought there would be more uses/differences.

>  			ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
>  
>  		vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl);
> @@ -407,20 +411,31 @@ static const struct vsp1_entity_operations bru_entity_ops = {
>   * Initialization and Cleanup
>   */
>  
> -struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
> +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
> +				 enum vsp1_entity_type type)
>  {
>  	struct vsp1_bru *bru;
> +	unsigned int num_pads;
> +	const char *name;
>  	int ret;
>  
>  	bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
>  	if (bru == NULL)
>  		return ERR_PTR(-ENOMEM);
>  
> +	bru->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
>  	bru->entity.ops = &bru_entity_ops;
> -	bru->entity.type = VSP1_ENTITY_BRU;
> +	bru->entity.type = type;
> +
> +	if (type == VSP1_ENTITY_BRU) {
> +		num_pads = vsp1->info->num_bru_inputs + 1;
> +		name = "bru";
> +	} else {
> +		num_pads = 3;
> +		name = "brs";
> +	}
>  
> -	ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
> -			       vsp1->info->num_bru_inputs + 1, &bru_ops,
> +	ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops,
>  			       MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
>  	if (ret < 0)
>  		return ERR_PTR(ret);
> @@ -435,7 +450,7 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
>  	bru->entity.subdev.ctrl_handler = &bru->ctrls;
>  
>  	if (bru->ctrls.error) {
> -		dev_err(vsp1->dev, "bru: failed to initialize controls\n");
> +		dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
>  		ret = bru->ctrls.error;
>  		vsp1_entity_destroy(&bru->entity);
>  		return ERR_PTR(ret);
> diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
> index 828a3fcadea8..c98ed96d8de6 100644
> --- a/drivers/media/platform/vsp1/vsp1_bru.h
> +++ b/drivers/media/platform/vsp1/vsp1_bru.h
> @@ -26,6 +26,7 @@ struct vsp1_rwpf;
>  
>  struct vsp1_bru {
>  	struct vsp1_entity entity;
> +	unsigned int base;
>  
>  	struct v4l2_ctrl_handler ctrls;
>  
> @@ -41,6 +42,7 @@ 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);
> +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
> +				 enum vsp1_entity_type type);
>  
>  #endif /* __VSP1_BRU_H__ */
> diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
> index 5a467b118a1c..6a9aeb71aedf 100644
> --- a/drivers/media/platform/vsp1/vsp1_drv.c
> +++ b/drivers/media/platform/vsp1/vsp1_drv.c
> @@ -84,6 +84,10 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
>   *
>   * - from a UDS to a UDS (UDS entities can't be chained)
>   * - from an entity to itself (no loops are allowed)
> + *
> + * Furthermore, the BRS can't be connected to histogram generators, but no
> + * special check is currently needed as all VSP instances that include a BRS
> + * have no histogram generator.
>   */
>  static int vsp1_create_sink_links(struct vsp1_device *vsp1,
>  				  struct vsp1_entity *sink)
> @@ -261,8 +265,18 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
>  	}
>  
>  	/* Instantiate all the entities. */
> +	if (vsp1->info->features & VSP1_HAS_BRS) {
> +		vsp1->brs = vsp1_bru_create(vsp1, VSP1_ENTITY_BRS);
> +		if (IS_ERR(vsp1->brs)) {
> +			ret = PTR_ERR(vsp1->brs);
> +			goto done;
> +		}
> +
> +		list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities);
> +	}
> +
>  	if (vsp1->info->features & VSP1_HAS_BRU) {
> -		vsp1->bru = vsp1_bru_create(vsp1);
> +		vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU);
>  		if (IS_ERR(vsp1->bru)) {
>  			ret = PTR_ERR(vsp1->bru);
>  			goto done;
> @@ -502,6 +516,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
>  	vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
>  	vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
>  
> +	if (vsp1->info->features & VSP1_HAS_BRS)
> +		vsp1_write(vsp1, VI6_DPR_ILV_BRS_ROUTE, VI6_DPR_NODE_UNUSED);
> +
>  	vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
>  		   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
>  	vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
> diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
> index 71dd903263ad..c06f7db093db 100644
> --- a/drivers/media/platform/vsp1/vsp1_entity.c
> +++ b/drivers/media/platform/vsp1/vsp1_entity.c
> @@ -29,6 +29,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
>  			     struct vsp1_dl_list *dl)
>  {
>  	struct vsp1_entity *source;
> +	u32 route;
>  
>  	if (entity->type == VSP1_ENTITY_HGO) {
>  		u32 smppt;
> @@ -62,8 +63,14 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
>  	if (source->route->reg == 0)
>  		return;
>  
> -	vsp1_dl_list_write(dl, source->route->reg,
> -			   source->sink->route->inputs[source->sink_pad]);
> +	route = source->sink->route->inputs[source->sink_pad];
> +	/*
> +	 * The ILV and BRS share the same data path route. The extra BRSSEL bit
> +	 * selects between the ILV and BRS.
> +	 */
> +	if (source->type == VSP1_ENTITY_BRS)
> +		route |= VI6_DPR_ROUTE_BRSSEL;
> +	vsp1_dl_list_write(dl, source->route->reg, route);
>  }
>  
>  /* -----------------------------------------------------------------------------
> @@ -450,6 +457,8 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
>  	  { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
>  
>  static const struct vsp1_route vsp1_routes[] = {
> +	{ VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE,
> +	  { VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 },
>  	{ 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),
> diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
> index 4362cd4e90ba..11f8363fa6b0 100644
> --- a/drivers/media/platform/vsp1/vsp1_entity.h
> +++ b/drivers/media/platform/vsp1/vsp1_entity.h
> @@ -23,6 +23,7 @@ struct vsp1_dl_list;
>  struct vsp1_pipeline;
>  
>  enum vsp1_entity_type {
> +	VSP1_ENTITY_BRS,
>  	VSP1_ENTITY_BRU,
>  	VSP1_ENTITY_CLU,
>  	VSP1_ENTITY_HGO,
> diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
> index e817623b84e0..9bb961298af2 100644
> --- a/drivers/media/platform/vsp1/vsp1_pipe.c
> +++ b/drivers/media/platform/vsp1/vsp1_pipe.c
> @@ -373,10 +373,11 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
>  		return;
>  
>  	/*
> -	 * The BRU background color has a fixed alpha value set to 255, the
> -	 * output alpha value is thus always equal to 255.
> +	 * The BRU and BRS background color has a fixed alpha value set to 255,
> +	 * the output alpha value is thus always equal to 255.
>  	 */
> -	if (pipe->uds_input->type == VSP1_ENTITY_BRU)
> +	if (pipe->uds_input->type == VSP1_ENTITY_BRU ||
> +	    pipe->uds_input->type == VSP1_ENTITY_BRS)
>  		alpha = 255;
>  
>  	vsp1_uds_set_alpha(pipe->uds, dl, alpha);
> diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
> index cd3e32af6e3b..744217e020b9 100644
> --- a/drivers/media/platform/vsp1/vsp1_regs.h
> +++ b/drivers/media/platform/vsp1/vsp1_regs.h
> @@ -238,6 +238,10 @@
>  #define VI6_WPF_SRCRPF_VIRACT_SUB	(1 << 28)
>  #define VI6_WPF_SRCRPF_VIRACT_MST	(2 << 28)
>  #define VI6_WPF_SRCRPF_VIRACT_MASK	(3 << 28)
> +#define VI6_WPF_SRCRPF_VIRACT2_DIS	(0 << 24)
> +#define VI6_WPF_SRCRPF_VIRACT2_SUB	(1 << 24)
> +#define VI6_WPF_SRCRPF_VIRACT2_MST	(2 << 24)
> +#define VI6_WPF_SRCRPF_VIRACT2_MASK	(3 << 24)
>  #define VI6_WPF_SRCRPF_RPF_ACT_DIS(n)	(0 << ((n) * 2))
>  #define VI6_WPF_SRCRPF_RPF_ACT_SUB(n)	(1 << ((n) * 2))
>  #define VI6_WPF_SRCRPF_RPF_ACT_MST(n)	(2 << ((n) * 2))
> @@ -321,6 +325,8 @@
>  #define VI6_DPR_HST_ROUTE		0x2044
>  #define VI6_DPR_HSI_ROUTE		0x2048
>  #define VI6_DPR_BRU_ROUTE		0x204c
> +#define VI6_DPR_ILV_BRS_ROUTE		0x2050
> +#define VI6_DPR_ROUTE_BRSSEL		(1 << 28)
>  #define VI6_DPR_ROUTE_FXA_MASK		(0xff << 16)
>  #define VI6_DPR_ROUTE_FXA_SHIFT		16
>  #define VI6_DPR_ROUTE_FP_MASK		(0x3f << 8)
> @@ -344,6 +350,7 @@
>  #define VI6_DPR_NODE_CLU		29
>  #define VI6_DPR_NODE_HST		30
>  #define VI6_DPR_NODE_HSI		31
> +#define VI6_DPR_NODE_BRS_IN(n)		(38 + (n))
>  #define VI6_DPR_NODE_LIF		55
>  #define VI6_DPR_NODE_WPF(n)		(56 + (n))
>  #define VI6_DPR_NODE_UNUSED		63
> @@ -476,7 +483,7 @@
>  #define VI6_HSI_CTRL_EN			(1 << 0)
>  
>  /* -----------------------------------------------------------------------------
> - * BRU Control Registers
> + * BRS and BRU Control Registers
>   */
>  
>  #define VI6_ROP_NOP			0
> @@ -496,7 +503,10 @@
>  #define VI6_ROP_NAND			14
>  #define VI6_ROP_SET			15
>  
> -#define VI6_BRU_INCTRL			0x2c00
> +#define VI6_BRU_BASE			0x2c00
> +#define VI6_BRS_BASE			0x3900
> +
> +#define VI6_BRU_INCTRL			0x0000
>  #define VI6_BRU_INCTRL_NRM		(1 << 28)
>  #define VI6_BRU_INCTRL_DnON		(1 << (16 + (n)))
>  #define VI6_BRU_INCTRL_DITHn_OFF	(0 << ((n) * 4))
> @@ -508,19 +518,19 @@
>  #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		0x0004
>  #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		0x0008
>  #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		0x000c
>  #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)
> @@ -530,7 +540,7 @@
>  #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 + ((n) <= 3 ? 0 : 4))
> +#define VI6_BRU_CTRL(n)			(0x0010 + (n) * 8 + ((n) <= 3 ? 0 : 4))
>  #define VI6_BRU_CTRL_RBC		(1 << 31)
>  #define VI6_BRU_CTRL_DSTSEL_BRUIN(n)	(((n) <= 3 ? (n) : (n)+1) << 20)
>  #define VI6_BRU_CTRL_DSTSEL_VRPF	(4 << 20)
> @@ -543,7 +553,7 @@
>  #define VI6_BRU_CTRL_AROP(rop)		((rop) << 0)
>  #define VI6_BRU_CTRL_AROP_MASK		(0xf << 0)
>  
> -#define VI6_BRU_BLD(n)			(0x2c14 + (n) * 8 + ((n) <= 3 ? 0 : 4))
> +#define VI6_BRU_BLD(n)			(0x0014 + (n) * 8 + ((n) <= 3 ? 0 : 4))
>  #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)
> @@ -576,7 +586,7 @@
>  #define VI6_BRU_BLD_COEFY_MASK		(0xff << 0)
>  #define VI6_BRU_BLD_COEFY_SHIFT		0
>  
> -#define VI6_BRU_ROP			0x2c30
> +#define VI6_BRU_ROP			0x0030	/* Only available on BRU */
>  #define VI6_BRU_ROP_DSTSEL_BRUIN(n)	(((n) <= 3 ? (n) : (n)+1) << 20)
>  #define VI6_BRU_ROP_DSTSEL_VRPF		(4 << 20)
>  #define VI6_BRU_ROP_DSTSEL_MASK		(7 << 20)
> diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
> index 5af3486afe07..84139affb871 100644
> --- a/drivers/media/platform/vsp1/vsp1_video.c
> +++ b/drivers/media/platform/vsp1/vsp1_video.c
> @@ -481,7 +481,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
>  	struct media_entity_enum ent_enum;
>  	struct vsp1_entity *entity;
>  	struct media_pad *pad;
> -	bool bru_found = false;
> +	struct vsp1_bru *bru = NULL;
>  	int ret;
>  
>  	ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev);
> @@ -511,16 +511,20 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
>  			media_entity_to_v4l2_subdev(pad->entity));
>  
>  		/*
> -		 * A BRU is present in the pipeline, store the BRU input pad
> +		 * A BRU or BRS is present in the pipeline, store its input pad
>  		 * number in the input RPF for use when configuring the RPF.
>  		 */
> -		if (entity->type == VSP1_ENTITY_BRU) {
> -			struct vsp1_bru *bru = to_bru(&entity->subdev);
> +		if (entity->type == VSP1_ENTITY_BRU ||
> +		    entity->type == VSP1_ENTITY_BRS) {
> +			/* BRU and BRS can't be chained. */
> +			if (bru) {
> +				ret = -EPIPE;
> +				goto out;
> +			}
>  
> +			bru = to_bru(&entity->subdev);
>  			bru->inputs[pad->index].rpf = input;
>  			input->bru_input = pad->index;
> -
> -			bru_found = true;
>  		}
>  
>  		/* We've reached the WPF, we're done. */
> @@ -542,8 +546,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
>  			}
>  
>  			pipe->uds = entity;
> -			pipe->uds_input = bru_found ? pipe->bru
> -					: &input->entity;
> +			pipe->uds_input = bru ? &bru->entity : &input->entity;
>  		}
>  
>  		/* Follow the source link, ignoring any HGO or HGT. */
> @@ -589,30 +592,42 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
>  		e = to_vsp1_entity(subdev);
>  		list_add_tail(&e->list_pipe, &pipe->entities);
>  
> -		if (e->type == VSP1_ENTITY_RPF) {
> +		switch (e->type) {
> +		case VSP1_ENTITY_RPF:
>  			rwpf = to_rwpf(subdev);
>  			pipe->inputs[rwpf->entity.index] = rwpf;
>  			rwpf->video->pipe_index = ++pipe->num_inputs;
>  			rwpf->pipe = pipe;
> -		} else if (e->type == VSP1_ENTITY_WPF) {
> +			break;
> +
> +		case VSP1_ENTITY_WPF:
>  			rwpf = to_rwpf(subdev);
>  			pipe->output = rwpf;
>  			rwpf->video->pipe_index = 0;
>  			rwpf->pipe = pipe;
> -		} else if (e->type == VSP1_ENTITY_LIF) {
> +			break;
> +
> +		case VSP1_ENTITY_LIF:
>  			pipe->lif = e;
> -		} else if (e->type == VSP1_ENTITY_BRU) {
> +			break;
> +
> +		case VSP1_ENTITY_BRU:
> +		case VSP1_ENTITY_BRS:
>  			pipe->bru = e;
> -		} else if (e->type == VSP1_ENTITY_HGO) {
> -			struct vsp1_hgo *hgo = to_hgo(subdev);
> +			break;
>  
> +		case VSP1_ENTITY_HGO:
>  			pipe->hgo = e;
> -			hgo->histo.pipe = pipe;
> -		} else if (e->type == VSP1_ENTITY_HGT) {
> -			struct vsp1_hgt *hgt = to_hgt(subdev);
> +			to_hgo(subdev)->histo.pipe = pipe;
> +			break;
>  
> +		case VSP1_ENTITY_HGT:
>  			pipe->hgt = e;
> -			hgt->histo.pipe = pipe;
> +			to_hgt(subdev)->histo.pipe = pipe;
> +			break;
> +
> +		default:
> +			break;
>  		}
>  	}
>  
> @@ -796,12 +811,14 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
>  		struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
>  
>  		/*
> -		 * If a BRU is present in the pipeline before the UDS, the alpha
> -		 * component doesn't need to be scaled as the BRU output alpha
> -		 * value is fixed to 255. Otherwise we need to scale the alpha
> -		 * component only when available at the input RPF.
> +		 * If a BRU or BRS is present in the pipeline before the UDS,
> +		 * the alpha component doesn't need to be scaled as the BRU and
> +		 * BRS output alpha value is fixed to 255. Otherwise we need to
> +		 * scale the alpha component only when available at the input
> +		 * RPF.
>  		 */
> -		if (pipe->uds_input->type == VSP1_ENTITY_BRU) {
> +		if (pipe->uds_input->type == VSP1_ENTITY_BRU ||
> +		    pipe->uds_input->type == VSP1_ENTITY_BRS) {
>  			uds->scale_alpha = false;
>  		} else {
>  			struct vsp1_rwpf *rpf =
> diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
> index 32df109b119f..b6c902be225b 100644
> --- a/drivers/media/platform/vsp1/vsp1_wpf.c
> +++ b/drivers/media/platform/vsp1/vsp1_wpf.c
> @@ -453,7 +453,9 @@ static void wpf_configure(struct vsp1_entity *entity,
>  	}
>  
>  	if (pipe->bru || pipe->num_inputs > 1)
> -		srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
> +		srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU
> +			? VI6_WPF_SRCRPF_VIRACT_MST
> +			: VI6_WPF_SRCRPF_VIRACT2_MST;
>  
>  	vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf);
>  
>
Mauro Carvalho Chehab July 20, 2017, 1:58 p.m. UTC | #2
Em Thu, 13 Jul 2017 14:38:40 +0100
Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> escreveu:

> On 26/06/17 19:12, Laurent Pinchart wrote:
> > The Blend/ROP Sub Unit (BRS) is a stripped-down version of the BRU found
> > in several VSP2 instances. Compared to a regular BRU, it supports two
> > inputs only, and thus has no ROP unit.
> > 
> > Add support for the BRS by modeling it as a new entity type, but reuse  
> 
> s/modeling/modelling/
> 
> 
> > the vsp1_bru object underneath. Chaining the BRU and BRS entities seems
> > to be supported by the hardware but isn't implemented yet as it isn't
> > the primary use case for the BRS.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>  
> 
> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

Acked-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>

Thanks,
Mauro
diff mbox

Patch

diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 847963b6e9eb..73858a0ed35c 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -54,6 +54,7 @@  struct vsp1_uds;
 #define VSP1_HAS_WPF_HFLIP	(1 << 6)
 #define VSP1_HAS_HGO		(1 << 7)
 #define VSP1_HAS_HGT		(1 << 8)
+#define VSP1_HAS_BRS		(1 << 9)
 
 struct vsp1_device_info {
 	u32 version;
@@ -76,6 +77,7 @@  struct vsp1_device {
 	struct rcar_fcp_device *fcp;
 	struct device *bus_master;
 
+	struct vsp1_bru *brs;
 	struct vsp1_bru *bru;
 	struct vsp1_clu *clu;
 	struct vsp1_hgo *hgo;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index 85362c5ef57a..e8fd2ae3b3eb 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -33,7 +33,7 @@ 
 static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
 				  u32 reg, u32 data)
 {
-	vsp1_dl_list_write(dl, reg, data);
+	vsp1_dl_list_write(dl, bru->base + reg, data);
 }
 
 /* -----------------------------------------------------------------------------
@@ -332,11 +332,14 @@  static void bru_configure(struct vsp1_entity *entity,
 	/*
 	 * 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.
+	 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
+	 * unit.
 	 */
-	vsp1_bru_write(bru, dl, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
-		       VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
-		       VI6_BRU_ROP_AROP(VI6_ROP_NOP));
+	if (entity->type == VSP1_ENTITY_BRU)
+		vsp1_bru_write(bru, dl, 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 < bru->entity.source_pad; ++i) {
 		bool premultiplied = false;
@@ -366,12 +369,13 @@  static void bru_configure(struct vsp1_entity *entity,
 			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.
+		 * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
+		 * in that order. In the BRU the Blend/ROP unit B SRC is
+		 * hardwired to the ROP unit output, the corresponding register
+		 * bits must be set to 0. The BRS has no ROP unit and doesn't
+		 * need any special processing.
 		 */
-		if (i != 1)
+		if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
 			ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
 
 		vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl);
@@ -407,20 +411,31 @@  static const struct vsp1_entity_operations bru_entity_ops = {
  * Initialization and Cleanup
  */
 
-struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
+				 enum vsp1_entity_type type)
 {
 	struct vsp1_bru *bru;
+	unsigned int num_pads;
+	const char *name;
 	int ret;
 
 	bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
 	if (bru == NULL)
 		return ERR_PTR(-ENOMEM);
 
+	bru->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
 	bru->entity.ops = &bru_entity_ops;
-	bru->entity.type = VSP1_ENTITY_BRU;
+	bru->entity.type = type;
+
+	if (type == VSP1_ENTITY_BRU) {
+		num_pads = vsp1->info->num_bru_inputs + 1;
+		name = "bru";
+	} else {
+		num_pads = 3;
+		name = "brs";
+	}
 
-	ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
-			       vsp1->info->num_bru_inputs + 1, &bru_ops,
+	ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops,
 			       MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
 	if (ret < 0)
 		return ERR_PTR(ret);
@@ -435,7 +450,7 @@  struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
 	bru->entity.subdev.ctrl_handler = &bru->ctrls;
 
 	if (bru->ctrls.error) {
-		dev_err(vsp1->dev, "bru: failed to initialize controls\n");
+		dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
 		ret = bru->ctrls.error;
 		vsp1_entity_destroy(&bru->entity);
 		return ERR_PTR(ret);
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
index 828a3fcadea8..c98ed96d8de6 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.h
+++ b/drivers/media/platform/vsp1/vsp1_bru.h
@@ -26,6 +26,7 @@  struct vsp1_rwpf;
 
 struct vsp1_bru {
 	struct vsp1_entity entity;
+	unsigned int base;
 
 	struct v4l2_ctrl_handler ctrls;
 
@@ -41,6 +42,7 @@  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);
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
+				 enum vsp1_entity_type type);
 
 #endif /* __VSP1_BRU_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 5a467b118a1c..6a9aeb71aedf 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -84,6 +84,10 @@  static irqreturn_t vsp1_irq_handler(int irq, void *data)
  *
  * - from a UDS to a UDS (UDS entities can't be chained)
  * - from an entity to itself (no loops are allowed)
+ *
+ * Furthermore, the BRS can't be connected to histogram generators, but no
+ * special check is currently needed as all VSP instances that include a BRS
+ * have no histogram generator.
  */
 static int vsp1_create_sink_links(struct vsp1_device *vsp1,
 				  struct vsp1_entity *sink)
@@ -261,8 +265,18 @@  static int vsp1_create_entities(struct vsp1_device *vsp1)
 	}
 
 	/* Instantiate all the entities. */
+	if (vsp1->info->features & VSP1_HAS_BRS) {
+		vsp1->brs = vsp1_bru_create(vsp1, VSP1_ENTITY_BRS);
+		if (IS_ERR(vsp1->brs)) {
+			ret = PTR_ERR(vsp1->brs);
+			goto done;
+		}
+
+		list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities);
+	}
+
 	if (vsp1->info->features & VSP1_HAS_BRU) {
-		vsp1->bru = vsp1_bru_create(vsp1);
+		vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU);
 		if (IS_ERR(vsp1->bru)) {
 			ret = PTR_ERR(vsp1->bru);
 			goto done;
@@ -502,6 +516,9 @@  static int vsp1_device_init(struct vsp1_device *vsp1)
 	vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
 	vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
 
+	if (vsp1->info->features & VSP1_HAS_BRS)
+		vsp1_write(vsp1, VI6_DPR_ILV_BRS_ROUTE, VI6_DPR_NODE_UNUSED);
+
 	vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
 		   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
 	vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 71dd903263ad..c06f7db093db 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -29,6 +29,7 @@  void vsp1_entity_route_setup(struct vsp1_entity *entity,
 			     struct vsp1_dl_list *dl)
 {
 	struct vsp1_entity *source;
+	u32 route;
 
 	if (entity->type == VSP1_ENTITY_HGO) {
 		u32 smppt;
@@ -62,8 +63,14 @@  void vsp1_entity_route_setup(struct vsp1_entity *entity,
 	if (source->route->reg == 0)
 		return;
 
-	vsp1_dl_list_write(dl, source->route->reg,
-			   source->sink->route->inputs[source->sink_pad]);
+	route = source->sink->route->inputs[source->sink_pad];
+	/*
+	 * The ILV and BRS share the same data path route. The extra BRSSEL bit
+	 * selects between the ILV and BRS.
+	 */
+	if (source->type == VSP1_ENTITY_BRS)
+		route |= VI6_DPR_ROUTE_BRSSEL;
+	vsp1_dl_list_write(dl, source->route->reg, route);
 }
 
 /* -----------------------------------------------------------------------------
@@ -450,6 +457,8 @@  struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
 	  { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
 
 static const struct vsp1_route vsp1_routes[] = {
+	{ VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE,
+	  { VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 },
 	{ 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),
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index 4362cd4e90ba..11f8363fa6b0 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -23,6 +23,7 @@  struct vsp1_dl_list;
 struct vsp1_pipeline;
 
 enum vsp1_entity_type {
+	VSP1_ENTITY_BRS,
 	VSP1_ENTITY_BRU,
 	VSP1_ENTITY_CLU,
 	VSP1_ENTITY_HGO,
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index e817623b84e0..9bb961298af2 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -373,10 +373,11 @@  void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
 		return;
 
 	/*
-	 * The BRU background color has a fixed alpha value set to 255, the
-	 * output alpha value is thus always equal to 255.
+	 * The BRU and BRS background color has a fixed alpha value set to 255,
+	 * the output alpha value is thus always equal to 255.
 	 */
-	if (pipe->uds_input->type == VSP1_ENTITY_BRU)
+	if (pipe->uds_input->type == VSP1_ENTITY_BRU ||
+	    pipe->uds_input->type == VSP1_ENTITY_BRS)
 		alpha = 255;
 
 	vsp1_uds_set_alpha(pipe->uds, dl, alpha);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index cd3e32af6e3b..744217e020b9 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -238,6 +238,10 @@ 
 #define VI6_WPF_SRCRPF_VIRACT_SUB	(1 << 28)
 #define VI6_WPF_SRCRPF_VIRACT_MST	(2 << 28)
 #define VI6_WPF_SRCRPF_VIRACT_MASK	(3 << 28)
+#define VI6_WPF_SRCRPF_VIRACT2_DIS	(0 << 24)
+#define VI6_WPF_SRCRPF_VIRACT2_SUB	(1 << 24)
+#define VI6_WPF_SRCRPF_VIRACT2_MST	(2 << 24)
+#define VI6_WPF_SRCRPF_VIRACT2_MASK	(3 << 24)
 #define VI6_WPF_SRCRPF_RPF_ACT_DIS(n)	(0 << ((n) * 2))
 #define VI6_WPF_SRCRPF_RPF_ACT_SUB(n)	(1 << ((n) * 2))
 #define VI6_WPF_SRCRPF_RPF_ACT_MST(n)	(2 << ((n) * 2))
@@ -321,6 +325,8 @@ 
 #define VI6_DPR_HST_ROUTE		0x2044
 #define VI6_DPR_HSI_ROUTE		0x2048
 #define VI6_DPR_BRU_ROUTE		0x204c
+#define VI6_DPR_ILV_BRS_ROUTE		0x2050
+#define VI6_DPR_ROUTE_BRSSEL		(1 << 28)
 #define VI6_DPR_ROUTE_FXA_MASK		(0xff << 16)
 #define VI6_DPR_ROUTE_FXA_SHIFT		16
 #define VI6_DPR_ROUTE_FP_MASK		(0x3f << 8)
@@ -344,6 +350,7 @@ 
 #define VI6_DPR_NODE_CLU		29
 #define VI6_DPR_NODE_HST		30
 #define VI6_DPR_NODE_HSI		31
+#define VI6_DPR_NODE_BRS_IN(n)		(38 + (n))
 #define VI6_DPR_NODE_LIF		55
 #define VI6_DPR_NODE_WPF(n)		(56 + (n))
 #define VI6_DPR_NODE_UNUSED		63
@@ -476,7 +483,7 @@ 
 #define VI6_HSI_CTRL_EN			(1 << 0)
 
 /* -----------------------------------------------------------------------------
- * BRU Control Registers
+ * BRS and BRU Control Registers
  */
 
 #define VI6_ROP_NOP			0
@@ -496,7 +503,10 @@ 
 #define VI6_ROP_NAND			14
 #define VI6_ROP_SET			15
 
-#define VI6_BRU_INCTRL			0x2c00
+#define VI6_BRU_BASE			0x2c00
+#define VI6_BRS_BASE			0x3900
+
+#define VI6_BRU_INCTRL			0x0000
 #define VI6_BRU_INCTRL_NRM		(1 << 28)
 #define VI6_BRU_INCTRL_DnON		(1 << (16 + (n)))
 #define VI6_BRU_INCTRL_DITHn_OFF	(0 << ((n) * 4))
@@ -508,19 +518,19 @@ 
 #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		0x0004
 #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		0x0008
 #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		0x000c
 #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)
@@ -530,7 +540,7 @@ 
 #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 + ((n) <= 3 ? 0 : 4))
+#define VI6_BRU_CTRL(n)			(0x0010 + (n) * 8 + ((n) <= 3 ? 0 : 4))
 #define VI6_BRU_CTRL_RBC		(1 << 31)
 #define VI6_BRU_CTRL_DSTSEL_BRUIN(n)	(((n) <= 3 ? (n) : (n)+1) << 20)
 #define VI6_BRU_CTRL_DSTSEL_VRPF	(4 << 20)
@@ -543,7 +553,7 @@ 
 #define VI6_BRU_CTRL_AROP(rop)		((rop) << 0)
 #define VI6_BRU_CTRL_AROP_MASK		(0xf << 0)
 
-#define VI6_BRU_BLD(n)			(0x2c14 + (n) * 8 + ((n) <= 3 ? 0 : 4))
+#define VI6_BRU_BLD(n)			(0x0014 + (n) * 8 + ((n) <= 3 ? 0 : 4))
 #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)
@@ -576,7 +586,7 @@ 
 #define VI6_BRU_BLD_COEFY_MASK		(0xff << 0)
 #define VI6_BRU_BLD_COEFY_SHIFT		0
 
-#define VI6_BRU_ROP			0x2c30
+#define VI6_BRU_ROP			0x0030	/* Only available on BRU */
 #define VI6_BRU_ROP_DSTSEL_BRUIN(n)	(((n) <= 3 ? (n) : (n)+1) << 20)
 #define VI6_BRU_ROP_DSTSEL_VRPF		(4 << 20)
 #define VI6_BRU_ROP_DSTSEL_MASK		(7 << 20)
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 5af3486afe07..84139affb871 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -481,7 +481,7 @@  static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
 	struct media_entity_enum ent_enum;
 	struct vsp1_entity *entity;
 	struct media_pad *pad;
-	bool bru_found = false;
+	struct vsp1_bru *bru = NULL;
 	int ret;
 
 	ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev);
@@ -511,16 +511,20 @@  static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
 			media_entity_to_v4l2_subdev(pad->entity));
 
 		/*
-		 * A BRU is present in the pipeline, store the BRU input pad
+		 * A BRU or BRS is present in the pipeline, store its input pad
 		 * number in the input RPF for use when configuring the RPF.
 		 */
-		if (entity->type == VSP1_ENTITY_BRU) {
-			struct vsp1_bru *bru = to_bru(&entity->subdev);
+		if (entity->type == VSP1_ENTITY_BRU ||
+		    entity->type == VSP1_ENTITY_BRS) {
+			/* BRU and BRS can't be chained. */
+			if (bru) {
+				ret = -EPIPE;
+				goto out;
+			}
 
+			bru = to_bru(&entity->subdev);
 			bru->inputs[pad->index].rpf = input;
 			input->bru_input = pad->index;
-
-			bru_found = true;
 		}
 
 		/* We've reached the WPF, we're done. */
@@ -542,8 +546,7 @@  static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
 			}
 
 			pipe->uds = entity;
-			pipe->uds_input = bru_found ? pipe->bru
-					: &input->entity;
+			pipe->uds_input = bru ? &bru->entity : &input->entity;
 		}
 
 		/* Follow the source link, ignoring any HGO or HGT. */
@@ -589,30 +592,42 @@  static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
 		e = to_vsp1_entity(subdev);
 		list_add_tail(&e->list_pipe, &pipe->entities);
 
-		if (e->type == VSP1_ENTITY_RPF) {
+		switch (e->type) {
+		case VSP1_ENTITY_RPF:
 			rwpf = to_rwpf(subdev);
 			pipe->inputs[rwpf->entity.index] = rwpf;
 			rwpf->video->pipe_index = ++pipe->num_inputs;
 			rwpf->pipe = pipe;
-		} else if (e->type == VSP1_ENTITY_WPF) {
+			break;
+
+		case VSP1_ENTITY_WPF:
 			rwpf = to_rwpf(subdev);
 			pipe->output = rwpf;
 			rwpf->video->pipe_index = 0;
 			rwpf->pipe = pipe;
-		} else if (e->type == VSP1_ENTITY_LIF) {
+			break;
+
+		case VSP1_ENTITY_LIF:
 			pipe->lif = e;
-		} else if (e->type == VSP1_ENTITY_BRU) {
+			break;
+
+		case VSP1_ENTITY_BRU:
+		case VSP1_ENTITY_BRS:
 			pipe->bru = e;
-		} else if (e->type == VSP1_ENTITY_HGO) {
-			struct vsp1_hgo *hgo = to_hgo(subdev);
+			break;
 
+		case VSP1_ENTITY_HGO:
 			pipe->hgo = e;
-			hgo->histo.pipe = pipe;
-		} else if (e->type == VSP1_ENTITY_HGT) {
-			struct vsp1_hgt *hgt = to_hgt(subdev);
+			to_hgo(subdev)->histo.pipe = pipe;
+			break;
 
+		case VSP1_ENTITY_HGT:
 			pipe->hgt = e;
-			hgt->histo.pipe = pipe;
+			to_hgt(subdev)->histo.pipe = pipe;
+			break;
+
+		default:
+			break;
 		}
 	}
 
@@ -796,12 +811,14 @@  static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
 		struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
 
 		/*
-		 * If a BRU is present in the pipeline before the UDS, the alpha
-		 * component doesn't need to be scaled as the BRU output alpha
-		 * value is fixed to 255. Otherwise we need to scale the alpha
-		 * component only when available at the input RPF.
+		 * If a BRU or BRS is present in the pipeline before the UDS,
+		 * the alpha component doesn't need to be scaled as the BRU and
+		 * BRS output alpha value is fixed to 255. Otherwise we need to
+		 * scale the alpha component only when available at the input
+		 * RPF.
 		 */
-		if (pipe->uds_input->type == VSP1_ENTITY_BRU) {
+		if (pipe->uds_input->type == VSP1_ENTITY_BRU ||
+		    pipe->uds_input->type == VSP1_ENTITY_BRS) {
 			uds->scale_alpha = false;
 		} else {
 			struct vsp1_rwpf *rpf =
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 32df109b119f..b6c902be225b 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -453,7 +453,9 @@  static void wpf_configure(struct vsp1_entity *entity,
 	}
 
 	if (pipe->bru || pipe->num_inputs > 1)
-		srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
+		srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU
+			? VI6_WPF_SRCRPF_VIRACT_MST
+			: VI6_WPF_SRCRPF_VIRACT2_MST;
 
 	vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf);