aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/vsp1
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/vsp1')
-rw-r--r--drivers/media/platform/vsp1/Makefile4
-rw-r--r--drivers/media/platform/vsp1/vsp1.h16
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.h48
-rw-r--r--drivers/media/platform/vsp1/vsp1_brx.c (renamed from drivers/media/platform/vsp1/vsp1_bru.c)218
-rw-r--r--drivers/media/platform/vsp1/vsp1_brx.h44
-rw-r--r--drivers/media/platform/vsp1/vsp1_clu.c184
-rw-r--r--drivers/media/platform/vsp1/vsp1_clu.h7
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c441
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.h36
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c945
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.h31
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c38
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c140
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h60
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgo.c32
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgo.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgt.c34
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgt.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_histo.c67
-rw-r--r--drivers/media/platform/vsp1/vsp1_histo.h9
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.c26
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c96
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.c151
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.h7
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.c127
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.h24
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h46
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c207
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c6
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h10
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c30
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c79
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h8
-rw-r--r--drivers/media/platform/vsp1/vsp1_uif.c264
-rw-r--r--drivers/media/platform/vsp1/vsp1_uif.h32
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c220
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h9
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c340
41 files changed, 2345 insertions, 1721 deletions
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile
index f5cd6f0491cb..4bb4dcbef7b5 100644
--- a/drivers/media/platform/vsp1/Makefile
+++ b/drivers/media/platform/vsp1/Makefile
@@ -3,8 +3,8 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o
vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o
vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
-vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
+vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
-vsp1-y += vsp1_lif.o
+vsp1-y += vsp1_lif.o vsp1_uif.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 78ef838416b3..33f632331474 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1.h -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2014 Renesas Electronics 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_H__
#define __VSP1_H__
@@ -30,7 +26,7 @@ struct rcar_fcp_device;
struct vsp1_drm;
struct vsp1_entity;
struct vsp1_platform_data;
-struct vsp1_bru;
+struct vsp1_brx;
struct vsp1_clu;
struct vsp1_hgo;
struct vsp1_hgt;
@@ -40,10 +36,12 @@ struct vsp1_lut;
struct vsp1_rwpf;
struct vsp1_sru;
struct vsp1_uds;
+struct vsp1_uif;
#define VSP1_MAX_LIF 2
#define VSP1_MAX_RPF 5
#define VSP1_MAX_UDS 3
+#define VSP1_MAX_UIF 2
#define VSP1_MAX_WPF 4
#define VSP1_HAS_LUT (1 << 1)
@@ -64,6 +62,7 @@ struct vsp1_device_info {
unsigned int lif_count;
unsigned int rpf_count;
unsigned int uds_count;
+ unsigned int uif_count;
unsigned int wpf_count;
unsigned int num_bru_inputs;
bool uapi;
@@ -78,8 +77,8 @@ struct vsp1_device {
struct rcar_fcp_device *fcp;
struct device *bus_master;
- struct vsp1_bru *brs;
- struct vsp1_bru *bru;
+ struct vsp1_brx *brs;
+ struct vsp1_brx *bru;
struct vsp1_clu *clu;
struct vsp1_hgo *hgo;
struct vsp1_hgt *hgt;
@@ -90,6 +89,7 @@ struct vsp1_device {
struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
struct vsp1_sru *sru;
struct vsp1_uds *uds[VSP1_MAX_UDS];
+ struct vsp1_uif *uif[VSP1_MAX_UIF];
struct vsp1_rwpf *wpf[VSP1_MAX_WPF];
struct list_head entities;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
deleted file mode 100644
index c98ed96d8de6..000000000000
--- a/drivers/media/platform/vsp1/vsp1_bru.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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-ctrls.h>
-#include <media/v4l2-subdev.h>
-
-#include "vsp1_entity.h"
-
-struct vsp1_device;
-struct vsp1_rwpf;
-
-#define BRU_PAD_SINK(n) (n)
-
-struct vsp1_bru {
- struct vsp1_entity entity;
- unsigned int base;
-
- struct v4l2_ctrl_handler ctrls;
-
- struct {
- struct vsp1_rwpf *rpf;
- } inputs[VSP1_MAX_RPF];
-
- u32 bgcolor;
-};
-
-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,
- enum vsp1_entity_type type);
-
-#endif /* __VSP1_BRU_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_brx.c
index e8fd2ae3b3eb..359917b5d842 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_brx.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
+ * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS)
*
* 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>
@@ -17,45 +13,45 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
-#define BRU_MIN_SIZE 1U
-#define BRU_MAX_SIZE 8190U
+#define BRX_MIN_SIZE 1U
+#define BRX_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_brx_write(struct vsp1_brx *brx,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, bru->base + reg, data);
+ vsp1_dl_body_write(dlb, brx->base + reg, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
-static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
+static int brx_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct vsp1_bru *bru =
- container_of(ctrl->handler, struct vsp1_bru, ctrls);
+ struct vsp1_brx *brx =
+ container_of(ctrl->handler, struct vsp1_brx, ctrls);
switch (ctrl->id) {
case V4L2_CID_BG_COLOR:
- bru->bgcolor = ctrl->val;
+ brx->bgcolor = ctrl->val;
break;
}
return 0;
}
-static const struct v4l2_ctrl_ops bru_ctrl_ops = {
- .s_ctrl = bru_s_ctrl,
+static const struct v4l2_ctrl_ops brx_ctrl_ops = {
+ .s_ctrl = brx_s_ctrl,
};
/* -----------------------------------------------------------------------------
@@ -63,12 +59,12 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = {
*/
/*
- * The BRU can't perform format conversion, all sink and source formats must be
+ * The BRx 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,
+static int brx_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
@@ -81,7 +77,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
ARRAY_SIZE(codes));
}
-static int bru_enum_frame_size(struct v4l2_subdev *subdev,
+static int brx_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
@@ -92,29 +88,29 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev,
fse->code != MEDIA_BUS_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;
+ fse->min_width = BRX_MIN_SIZE;
+ fse->max_width = BRX_MAX_SIZE;
+ fse->min_height = BRX_MIN_SIZE;
+ fse->max_height = BRX_MAX_SIZE;
return 0;
}
-static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
+static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
{
- return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad);
+ return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad);
}
-static void bru_try_format(struct vsp1_bru *bru,
+static void brx_try_format(struct vsp1_brx *brx,
struct v4l2_subdev_pad_config *config,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
{
struct v4l2_mbus_framefmt *format;
switch (pad) {
- case BRU_PAD_SINK(0):
+ case BRX_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
@@ -122,46 +118,46 @@ static void bru_try_format(struct vsp1_bru *bru,
break;
default:
- /* The BRU can't perform format conversion. */
- format = vsp1_entity_get_pad_format(&bru->entity, config,
- BRU_PAD_SINK(0));
+ /* The BRx can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&brx->entity, config,
+ BRX_PAD_SINK(0));
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->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);
+ fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
-static int bru_set_format(struct v4l2_subdev *subdev,
+static int brx_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
int ret = 0;
- mutex_lock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
- config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which);
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which);
if (!config) {
ret = -EINVAL;
goto done;
}
- bru_try_format(bru, config, fmt->pad, &fmt->format);
+ brx_try_format(brx, config, fmt->pad, &fmt->format);
- format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad);
*format = fmt->format;
/* Reset the compose rectangle */
- if (fmt->pad != bru->entity.source_pad) {
+ if (fmt->pad != brx->entity.source_pad) {
struct v4l2_rect *compose;
- compose = bru_get_compose(bru, config, fmt->pad);
+ compose = brx_get_compose(brx, config, fmt->pad);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
@@ -169,48 +165,48 @@ static int bru_set_format(struct v4l2_subdev *subdev,
}
/* Propagate the format code to all pads */
- if (fmt->pad == BRU_PAD_SINK(0)) {
+ if (fmt->pad == BRX_PAD_SINK(0)) {
unsigned int i;
- for (i = 0; i <= bru->entity.source_pad; ++i) {
- format = vsp1_entity_get_pad_format(&bru->entity,
+ for (i = 0; i <= brx->entity.source_pad; ++i) {
+ format = vsp1_entity_get_pad_format(&brx->entity,
config, i);
format->code = fmt->format.code;
}
}
done:
- mutex_unlock(&bru->entity.lock);
+ mutex_unlock(&brx->entity.lock);
return ret;
}
-static int bru_get_selection(struct v4l2_subdev *subdev,
+static int brx_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
- if (sel->pad == bru->entity.source_pad)
+ if (sel->pad == brx->entity.source_pad)
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;
+ sel->r.width = BRX_MAX_SIZE;
+ sel->r.height = BRX_MAX_SIZE;
return 0;
case V4L2_SEL_TGT_COMPOSE:
- config = vsp1_entity_get_pad_config(&bru->entity, cfg,
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg,
sel->which);
if (!config)
return -EINVAL;
- mutex_lock(&bru->entity.lock);
- sel->r = *bru_get_compose(bru, config, sel->pad);
- mutex_unlock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
+ sel->r = *brx_get_compose(brx, config, sel->pad);
+ mutex_unlock(&brx->entity.lock);
return 0;
default:
@@ -218,25 +214,25 @@ static int bru_get_selection(struct v4l2_subdev *subdev,
}
}
-static int bru_set_selection(struct v4l2_subdev *subdev,
+static int brx_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
int ret = 0;
- if (sel->pad == bru->entity.source_pad)
+ if (sel->pad == brx->entity.source_pad)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
- mutex_lock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
- config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which);
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which);
if (!config) {
ret = -EINVAL;
goto done;
@@ -246,8 +242,8 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
* The compose rectangle top left corner must be inside the output
* frame.
*/
- format = vsp1_entity_get_pad_format(&bru->entity, config,
- bru->entity.source_pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config,
+ brx->entity.source_pad);
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);
@@ -255,51 +251,47 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
- format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad);
sel->r.width = format->width;
sel->r.height = format->height;
- compose = bru_get_compose(bru, config, sel->pad);
+ compose = brx_get_compose(brx, config, sel->pad);
*compose = sel->r;
done:
- mutex_unlock(&bru->entity.lock);
+ mutex_unlock(&brx->entity.lock);
return ret;
}
-static const struct v4l2_subdev_pad_ops bru_pad_ops = {
+static const struct v4l2_subdev_pad_ops brx_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
- .enum_mbus_code = bru_enum_mbus_code,
- .enum_frame_size = bru_enum_frame_size,
+ .enum_mbus_code = brx_enum_mbus_code,
+ .enum_frame_size = brx_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = bru_set_format,
- .get_selection = bru_get_selection,
- .set_selection = bru_set_selection,
+ .set_fmt = brx_set_format,
+ .get_selection = brx_get_selection,
+ .set_selection = brx_set_selection,
};
-static const struct v4l2_subdev_ops bru_ops = {
- .pad = &bru_pad_ops,
+static const struct v4l2_subdev_ops brx_ops = {
+ .pad = &brx_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
-static void bru_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void brx_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
- struct vsp1_bru *bru = to_bru(&entity->subdev);
+ struct vsp1_brx *brx = to_brx(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
- format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
- bru->entity.source_pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
+ brx->entity.source_pad);
/*
* The hardware is extremely flexible but we have no userspace API to
@@ -313,7 +305,7 @@ static void bru_configure(struct vsp1_entity *entity,
* format at the pipeline output is premultiplied.
*/
flags = pipe->output ? pipe->output->format.flags : 0;
- vsp1_bru_write(bru, dl, VI6_BRU_INCTRL,
+ vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
0 : VI6_BRU_INCTRL_NRM);
@@ -321,12 +313,12 @@ static void bru_configure(struct vsp1_entity *entity,
* Set the background position to cover the whole output image and
* configure its color.
*/
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
(format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
(format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor |
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
(0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
/*
@@ -336,25 +328,25 @@ static void bru_configure(struct vsp1_entity *entity,
* unit.
*/
if (entity->type == VSP1_ENTITY_BRU)
- vsp1_bru_write(bru, dl, VI6_BRU_ROP,
+ vsp1_brx_write(brx, dlb, 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) {
+ for (i = 0; i < brx->entity.source_pad; ++i) {
bool premultiplied = false;
u32 ctrl = 0;
/*
- * Configure all Blend/ROP units corresponding to an enabled BRU
+ * Configure all Blend/ROP units corresponding to an enabled BRx
* input for alpha blending. Blend/ROP units corresponding to
- * disabled BRU inputs are used in ROP NOP mode to ignore the
+ * disabled BRx inputs are used in ROP NOP mode to ignore the
* SRC input.
*/
- if (bru->inputs[i].rpf) {
+ if (brx->inputs[i].rpf) {
ctrl |= VI6_BRU_CTRL_RBC;
- premultiplied = bru->inputs[i].rpf->format.flags
+ premultiplied = brx->inputs[i].rpf->format.flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
} else {
ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
@@ -378,7 +370,7 @@ static void bru_configure(struct vsp1_entity *entity,
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);
+ vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
/*
* Harcode the blending formula to
@@ -393,7 +385,7 @@ static void bru_configure(struct vsp1_entity *entity,
*
* otherwise.
*/
- vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
+ vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
VI6_BRU_BLD_CCMDX_255_SRC_A |
(premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
VI6_BRU_BLD_CCMDY_SRC_A) |
@@ -403,29 +395,29 @@ static void bru_configure(struct vsp1_entity *entity,
}
}
-static const struct vsp1_entity_operations bru_entity_ops = {
- .configure = bru_configure,
+static const struct vsp1_entity_operations brx_entity_ops = {
+ .configure_stream = brx_configure_stream,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
-struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
+struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
enum vsp1_entity_type type)
{
- struct vsp1_bru *bru;
+ struct vsp1_brx *brx;
unsigned int num_pads;
const char *name;
int ret;
- bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
- if (bru == NULL)
+ brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
+ if (brx == 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 = type;
+ brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
+ brx->entity.ops = &brx_entity_ops;
+ brx->entity.type = type;
if (type == VSP1_ENTITY_BRU) {
num_pads = vsp1->info->num_bru_inputs + 1;
@@ -435,26 +427,26 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
name = "brs";
}
- ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops,
+ ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the control handler. */
- v4l2_ctrl_handler_init(&bru->ctrls, 1);
- v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
+ v4l2_ctrl_handler_init(&brx->ctrls, 1);
+ v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
0, 0xffffff, 1, 0);
- bru->bgcolor = 0;
+ brx->bgcolor = 0;
- bru->entity.subdev.ctrl_handler = &bru->ctrls;
+ brx->entity.subdev.ctrl_handler = &brx->ctrls;
- if (bru->ctrls.error) {
+ if (brx->ctrls.error) {
dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
- ret = bru->ctrls.error;
- vsp1_entity_destroy(&bru->entity);
+ ret = brx->ctrls.error;
+ vsp1_entity_destroy(&brx->entity);
return ERR_PTR(ret);
}
- return bru;
+ return brx;
}
diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/vsp1/vsp1_brx.h
new file mode 100644
index 000000000000..6abbb8c3343c
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_brx.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_brx.h -- R-Car VSP1 Blend ROP Unit (BRU and BRS)
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+#ifndef __VSP1_BRX_H__
+#define __VSP1_BRX_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+struct vsp1_rwpf;
+
+#define BRX_PAD_SINK(n) (n)
+
+struct vsp1_brx {
+ struct vsp1_entity entity;
+ unsigned int base;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ struct {
+ struct vsp1_rwpf *rpf;
+ } inputs[VSP1_MAX_RPF];
+
+ u32 bgcolor;
+};
+
+static inline struct vsp1_brx *to_brx(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_brx, entity.subdev);
+}
+
+struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
+ enum vsp1_entity_type type);
+
+#endif /* __VSP1_BRX_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c
index f2fb26e5ab4e..942fc14c19d1 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/vsp1/vsp1_clu.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table
*
* Copyright (C) 2015-2016 Renesas Electronics 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>
@@ -23,14 +19,16 @@
#define CLU_MIN_SIZE 4U
#define CLU_MAX_SIZE 8190U
+#define CLU_SIZE (17 * 17 * 17)
+
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_clu_write(struct vsp1_clu *clu,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -47,19 +45,19 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
struct vsp1_dl_body *dlb;
unsigned int i;
- dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17);
+ dlb = vsp1_dl_body_get(clu->pool);
if (!dlb)
return -ENOMEM;
- vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0);
- for (i = 0; i < 17 * 17 * 17; ++i)
- vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
+ vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0);
+ for (i = 0; i < CLU_SIZE; ++i)
+ vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
spin_lock_irq(&clu->lock);
swap(clu->clu, dlb);
spin_unlock_irq(&clu->lock);
- vsp1_dl_fragment_free(dlb);
+ vsp1_dl_body_put(dlb);
return 0;
}
@@ -118,18 +116,18 @@ static const struct v4l2_ctrl_config clu_mode_control = {
* V4L2 Subdevice Pad Operations
*/
+static const unsigned int clu_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
static int clu_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AHSV8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
- ARRAY_SIZE(codes));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, clu_codes,
+ ARRAY_SIZE(clu_codes));
}
static int clu_enum_frame_size(struct v4l2_subdev *subdev,
@@ -145,51 +143,10 @@ static int clu_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_clu *clu = to_clu(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
-
- mutex_lock(&clu->entity.lock);
-
- config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
- fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
- fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
-
- format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad);
-
- if (fmt->pad == CLU_PAD_SOURCE) {
- /* The CLU output format can't be modified. */
- fmt->format = *format;
- goto done;
- }
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- CLU_MIN_SIZE, CLU_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- CLU_MIN_SIZE, CLU_MAX_SIZE);
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt->format = *format;
-
- /* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&clu->entity, config,
- CLU_PAD_SOURCE);
- *format = fmt->format;
-
-done:
- mutex_unlock(&clu->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, clu_codes,
+ ARRAY_SIZE(clu_codes),
+ CLU_MIN_SIZE, CLU_MIN_SIZE,
+ CLU_MAX_SIZE, CLU_MAX_SIZE);
}
/* -----------------------------------------------------------------------------
@@ -212,57 +169,65 @@ static const struct v4l2_subdev_ops clu_ops = {
* VSP1 Entity Operations
*/
-static void clu_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void clu_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_clu *clu = to_clu(&entity->subdev);
- struct vsp1_dl_body *dlb;
+ struct v4l2_mbus_framefmt *format;
+
+ /*
+ * The yuv_mode can't be changed during streaming. Cache it internally
+ * for future runtime configuration calls.
+ */
+ format = vsp1_entity_get_pad_format(&clu->entity,
+ clu->entity.config,
+ CLU_PAD_SINK);
+ clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
+}
+
+static void clu_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_clu *clu = to_clu(&entity->subdev);
+ struct vsp1_dl_body *clu_dlb;
unsigned long flags;
u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
- switch (params) {
- case VSP1_ENTITY_PARAMS_INIT: {
- /*
- * The format can't be changed during streaming, only verify it
- * at setup time and store the information internally for future
- * runtime configuration calls.
- */
- struct v4l2_mbus_framefmt *format;
-
- format = vsp1_entity_get_pad_format(&clu->entity,
- clu->entity.config,
- CLU_PAD_SINK);
- clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
- break;
- }
-
- case VSP1_ENTITY_PARAMS_PARTITION:
- break;
+ /* 2D mode can only be used with the YCbCr pixel encoding. */
+ if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode)
+ ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D
+ | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D
+ | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D;
- case VSP1_ENTITY_PARAMS_RUNTIME:
- /* 2D mode can only be used with the YCbCr pixel encoding. */
- if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode)
- ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D
- | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D
- | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D;
+ vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl);
- vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl);
+ spin_lock_irqsave(&clu->lock, flags);
+ clu_dlb = clu->clu;
+ clu->clu = NULL;
+ spin_unlock_irqrestore(&clu->lock, flags);
- spin_lock_irqsave(&clu->lock, flags);
- dlb = clu->clu;
- clu->clu = NULL;
- spin_unlock_irqrestore(&clu->lock, flags);
+ if (clu_dlb) {
+ vsp1_dl_list_add_body(dl, clu_dlb);
- if (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
- break;
+ /* Release our local reference. */
+ vsp1_dl_body_put(clu_dlb);
}
}
+static void clu_destroy(struct vsp1_entity *entity)
+{
+ struct vsp1_clu *clu = to_clu(&entity->subdev);
+
+ vsp1_dl_body_pool_destroy(clu->pool);
+}
+
static const struct vsp1_entity_operations clu_entity_ops = {
- .configure = clu_configure,
+ .configure_stream = clu_configure_stream,
+ .configure_frame = clu_configure_frame,
+ .destroy = clu_destroy,
};
/* -----------------------------------------------------------------------------
@@ -288,6 +253,17 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
if (ret < 0)
return ERR_PTR(ret);
+ /*
+ * Pre-allocate a body pool, with 3 bodies allowing a userspace update
+ * before the hardware has committed a previous set of tables, handling
+ * both the queued and pending dl entries. One extra entry is added to
+ * the CLU_SIZE to allow for the VI6_CLU_ADDR header.
+ */
+ clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1,
+ 0);
+ if (!clu->pool)
+ return ERR_PTR(-ENOMEM);
+
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&clu->ctrls, 2);
v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL);
diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h
index 036e0a2f1a42..cef2f44481ba 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.h
+++ b/drivers/media/platform/vsp1/vsp1_clu.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table
*
* Copyright (C) 2015 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_CLU_H__
#define __VSP1_CLU_H__
@@ -36,6 +32,7 @@ struct vsp1_clu {
spinlock_t lock;
unsigned int mode;
struct vsp1_dl_body *clu;
+ struct vsp1_dl_body_pool *pool;
};
static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 0b86ed01e85d..d9b9cdd8fbe2 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -1,19 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_dl.h -- R-Car VSP1 Display List
+ * vsp1_dl.c -- R-Car VSP1 Display List
*
* Copyright (C) 2015 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/dma-mapping.h>
#include <linux/gfp.h>
+#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -45,14 +42,22 @@ struct vsp1_dl_entry {
/**
* struct vsp1_dl_body - Display list body
* @list: entry in the display list list of bodies
+ * @free: entry in the pool free body list
+ * @pool: pool to which this body belongs
* @vsp1: the VSP1 device
* @entries: array of entries
* @dma: DMA address of the entries
* @size: size of the DMA memory in bytes
* @num_entries: number of stored entries
+ * @max_entries: number of entries available
*/
struct vsp1_dl_body {
struct list_head list;
+ struct list_head free;
+
+ refcount_t refcnt;
+
+ struct vsp1_dl_body_pool *pool;
struct vsp1_device *vsp1;
struct vsp1_dl_entry *entries;
@@ -60,6 +65,31 @@ struct vsp1_dl_body {
size_t size;
unsigned int num_entries;
+ unsigned int max_entries;
+};
+
+/**
+ * struct vsp1_dl_body_pool - display list body pool
+ * @dma: DMA address of the entries
+ * @size: size of the full DMA memory pool in bytes
+ * @mem: CPU memory pointer for the pool
+ * @bodies: Array of DLB structures for the pool
+ * @free: List of free DLB entries
+ * @lock: Protects the free list
+ * @vsp1: the VSP1 device
+ */
+struct vsp1_dl_body_pool {
+ /* DMA allocation */
+ dma_addr_t dma;
+ size_t size;
+ void *mem;
+
+ /* Body management */
+ struct vsp1_dl_body *bodies;
+ struct list_head free;
+ spinlock_t lock;
+
+ struct vsp1_device *vsp1;
};
/**
@@ -69,9 +99,10 @@ struct vsp1_dl_body {
* @header: display list header, NULL for headerless lists
* @dma: DMA address for the header
* @body0: first display list body
- * @fragments: list of extra display list bodies
+ * @bodies: list of extra display list bodies
* @has_chain: if true, indicates that there's a partition chain
* @chain: entry in the display list partition chain
+ * @internal: whether the display list is used for internal purpose
*/
struct vsp1_dl_list {
struct list_head list;
@@ -80,11 +111,13 @@ struct vsp1_dl_list {
struct vsp1_dl_header *header;
dma_addr_t dma;
- struct vsp1_dl_body body0;
- struct list_head fragments;
+ struct vsp1_dl_body *body0;
+ struct list_head bodies;
bool has_chain;
struct list_head chain;
+
+ bool internal;
};
enum vsp1_dl_mode {
@@ -98,13 +131,12 @@ enum vsp1_dl_mode {
* @mode: display list operation mode (header or headerless)
* @singleshot: execute the display list in single-shot mode
* @vsp1: the VSP1 device
- * @lock: protects the free, active, queued, pending and gc_fragments lists
+ * @lock: protects the free, active, queued, and pending lists
* @free: array of all free display lists
* @active: list currently being processed (loaded) by hardware
* @queued: list queued to the hardware (written to the DL registers)
* @pending: list waiting to be queued to the hardware
- * @gc_work: fragments garbage collector work struct
- * @gc_fragments: array of display list fragments waiting to be freed
+ * @pool: body pool for the display list bodies
*/
struct vsp1_dl_manager {
unsigned int index;
@@ -118,109 +150,164 @@ struct vsp1_dl_manager {
struct vsp1_dl_list *queued;
struct vsp1_dl_list *pending;
- struct work_struct gc_work;
- struct list_head gc_fragments;
+ struct vsp1_dl_body_pool *pool;
};
/* -----------------------------------------------------------------------------
* Display List Body Management
*/
-/*
- * Initialize a display list body object and allocate DMA memory for the body
- * data. The display list body object is expected to have been initialized to
- * 0 when allocated.
+/**
+ * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation
+ * @vsp1: The VSP1 device
+ * @num_bodies: The number of bodies to allocate
+ * @num_entries: The maximum number of entries that a body can contain
+ * @extra_size: Extra allocation provided for the bodies
+ *
+ * Allocate a pool of display list bodies each with enough memory to contain the
+ * requested number of entries plus the @extra_size.
+ *
+ * Return a pointer to a pool on success or NULL if memory can't be allocated.
*/
-static int vsp1_dl_body_init(struct vsp1_device *vsp1,
- struct vsp1_dl_body *dlb, unsigned int num_entries,
- size_t extra_size)
+struct vsp1_dl_body_pool *
+vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
+ unsigned int num_entries, size_t extra_size)
{
- size_t size = num_entries * sizeof(*dlb->entries) + extra_size;
+ struct vsp1_dl_body_pool *pool;
+ size_t dlb_size;
+ unsigned int i;
- dlb->vsp1 = vsp1;
- dlb->size = size;
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return NULL;
- dlb->entries = dma_alloc_wc(vsp1->bus_master, dlb->size, &dlb->dma,
- GFP_KERNEL);
- if (!dlb->entries)
- return -ENOMEM;
+ pool->vsp1 = vsp1;
- return 0;
+ /*
+ * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate
+ * extra memory for the display list header. We need only one header per
+ * display list, not per display list body, thus this allocation is
+ * extraneous and should be reworked in the future.
+ */
+ dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size;
+ pool->size = dlb_size * num_bodies;
+
+ pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL);
+ if (!pool->bodies) {
+ kfree(pool);
+ return NULL;
+ }
+
+ pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma,
+ GFP_KERNEL);
+ if (!pool->mem) {
+ kfree(pool->bodies);
+ kfree(pool);
+ return NULL;
+ }
+
+ spin_lock_init(&pool->lock);
+ INIT_LIST_HEAD(&pool->free);
+
+ for (i = 0; i < num_bodies; ++i) {
+ struct vsp1_dl_body *dlb = &pool->bodies[i];
+
+ dlb->pool = pool;
+ dlb->max_entries = num_entries;
+
+ dlb->dma = pool->dma + i * dlb_size;
+ dlb->entries = pool->mem + i * dlb_size;
+
+ list_add_tail(&dlb->free, &pool->free);
+ }
+
+ return pool;
}
-/*
- * Cleanup a display list body and free allocated DMA memory allocated.
+/**
+ * vsp1_dl_body_pool_destroy - Release a body pool
+ * @pool: The body pool
+ *
+ * Release all components of a pool allocation.
*/
-static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
+void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool)
{
- dma_free_wc(dlb->vsp1->bus_master, dlb->size, dlb->entries, dlb->dma);
+ if (!pool)
+ return;
+
+ if (pool->mem)
+ dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem,
+ pool->dma);
+
+ kfree(pool->bodies);
+ kfree(pool);
}
/**
- * vsp1_dl_fragment_alloc - Allocate a display list fragment
- * @vsp1: The VSP1 device
- * @num_entries: The maximum number of entries that the fragment can contain
+ * vsp1_dl_body_get - Obtain a body from a pool
+ * @pool: The body pool
*
- * Allocate a display list fragment with enough memory to contain the requested
- * number of entries.
+ * Obtain a body from the pool without blocking.
*
- * Return a pointer to a fragment on success or NULL if memory can't be
- * allocated.
+ * Returns a display list body or NULL if there are none available.
*/
-struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
- unsigned int num_entries)
+struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)
{
- struct vsp1_dl_body *dlb;
- int ret;
+ struct vsp1_dl_body *dlb = NULL;
+ unsigned long flags;
- dlb = kzalloc(sizeof(*dlb), GFP_KERNEL);
- if (!dlb)
- return NULL;
+ spin_lock_irqsave(&pool->lock, flags);
- ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0);
- if (ret < 0) {
- kfree(dlb);
- return NULL;
+ if (!list_empty(&pool->free)) {
+ dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free);
+ list_del(&dlb->free);
+ refcount_set(&dlb->refcnt, 1);
}
+ spin_unlock_irqrestore(&pool->lock, flags);
+
return dlb;
}
/**
- * vsp1_dl_fragment_free - Free a display list fragment
- * @dlb: The fragment
- *
- * Free the given display list fragment and the associated DMA memory.
- *
- * Fragments must only be freed explicitly if they are not added to a display
- * list, as the display list will take ownership of them and free them
- * otherwise. Manual free typically happens at cleanup time for fragments that
- * have been allocated but not used.
+ * vsp1_dl_body_put - Return a body back to its pool
+ * @dlb: The display list body
*
- * Passing a NULL pointer to this function is safe, in that case no operation
- * will be performed.
+ * Return a body back to the pool, and reset the num_entries to clear the list.
*/
-void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb)
+void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
{
+ unsigned long flags;
+
if (!dlb)
return;
- vsp1_dl_body_cleanup(dlb);
- kfree(dlb);
+ if (!refcount_dec_and_test(&dlb->refcnt))
+ return;
+
+ dlb->num_entries = 0;
+
+ spin_lock_irqsave(&dlb->pool->lock, flags);
+ list_add_tail(&dlb->free, &dlb->pool->free);
+ spin_unlock_irqrestore(&dlb->pool->lock, flags);
}
/**
- * vsp1_dl_fragment_write - Write a register to a display list fragment
- * @dlb: The fragment
+ * vsp1_dl_body_write - Write a register to a display list body
+ * @dlb: The body
* @reg: The register address
* @data: The register value
*
- * Write the given register and value to the display list fragment. The maximum
- * number of entries that can be written in a fragment is specified when the
- * fragment is allocated by vsp1_dl_fragment_alloc().
+ * Write the given register and value to the display list body. The maximum
+ * number of entries that can be written in a body is specified when the body is
+ * allocated by vsp1_dl_body_alloc().
*/
-void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
+void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
+ if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
+ "DLB size exceeded (max %u)", dlb->max_entries))
+ return;
+
dlb->entries[dlb->num_entries].addr = reg;
dlb->entries[dlb->num_entries].data = data;
dlb->num_entries++;
@@ -233,51 +320,47 @@ void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl_list *dl;
- size_t header_size;
- int ret;
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
if (!dl)
return NULL;
- INIT_LIST_HEAD(&dl->fragments);
+ INIT_LIST_HEAD(&dl->bodies);
dl->dlm = dlm;
- /*
- * Initialize the display list body and allocate DMA memory for the body
- * and the optional header. Both are allocated together to avoid memory
- * fragmentation, with the header located right after the body in
- * memory.
- */
- header_size = dlm->mode == VSP1_DL_MODE_HEADER
- ? ALIGN(sizeof(struct vsp1_dl_header), 8)
- : 0;
-
- ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES,
- header_size);
- if (ret < 0) {
- kfree(dl);
+ /* Get a default body for our list. */
+ dl->body0 = vsp1_dl_body_get(dlm->pool);
+ if (!dl->body0)
return NULL;
- }
-
if (dlm->mode == VSP1_DL_MODE_HEADER) {
- size_t header_offset = VSP1_DL_NUM_ENTRIES
- * sizeof(*dl->body0.entries);
+ size_t header_offset = dl->body0->max_entries
+ * sizeof(*dl->body0->entries);
- dl->header = ((void *)dl->body0.entries) + header_offset;
- dl->dma = dl->body0.dma + header_offset;
+ dl->header = ((void *)dl->body0->entries) + header_offset;
+ dl->dma = dl->body0->dma + header_offset;
memset(dl->header, 0, sizeof(*dl->header));
- dl->header->lists[0].addr = dl->body0.dma;
+ dl->header->lists[0].addr = dl->body0->dma;
}
return dl;
}
+static void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl)
+{
+ struct vsp1_dl_body *dlb, *tmp;
+
+ list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) {
+ list_del(&dlb->list);
+ vsp1_dl_body_put(dlb);
+ }
+}
+
static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
{
- vsp1_dl_body_cleanup(&dl->body0);
- list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
+ vsp1_dl_body_put(dl->body0);
+ vsp1_dl_list_bodies_put(dl);
+
kfree(dl);
}
@@ -331,18 +414,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
dl->has_chain = false;
+ vsp1_dl_list_bodies_put(dl);
+
/*
- * We can't free fragments here as DMA memory can only be freed in
- * interruptible context. Move all fragments to the display list
- * manager's list of fragments to be freed, they will be
- * garbage-collected by the work queue.
+ * body0 is reused as as an optimisation as presently every display list
+ * has at least one body, thus we reinitialise the entries list.
*/
- if (!list_empty(&dl->fragments)) {
- list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
- schedule_work(&dl->dlm->gc_work);
- }
-
- dl->body0.num_entries = 0;
+ dl->body0->num_entries = 0;
list_add_tail(&dl->list, &dl->dlm->free);
}
@@ -369,43 +447,46 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl)
}
/**
- * vsp1_dl_list_write - Write a register to the display list
+ * vsp1_dl_list_get_body0 - Obtain the default body for the display list
* @dl: The display list
- * @reg: The register address
- * @data: The register value
*
- * Write the given register and value to the display list. Up to 256 registers
- * can be written per display list.
+ * Obtain a pointer to the internal display list body allowing this to be passed
+ * directly to configure operations.
*/
-void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data)
+struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl)
{
- vsp1_dl_fragment_write(&dl->body0, reg, data);
+ return dl->body0;
}
/**
- * vsp1_dl_list_add_fragment - Add a fragment to the display list
+ * vsp1_dl_list_add_body - Add a body to the display list
* @dl: The display list
- * @dlb: The fragment
+ * @dlb: The body
+ *
+ * Add a display list body to a display list. Registers contained in bodies are
+ * processed after registers contained in the main display list, in the order in
+ * which bodies are added.
*
- * Add a display list body as a fragment to a display list. Registers contained
- * in fragments are processed after registers contained in the main display
- * list, in the order in which fragments are added.
+ * Adding a body to a display list passes ownership of the body to the list. The
+ * caller retains its reference to the fragment when adding it to the display
+ * list, but is not allowed to add new entries to the body.
*
- * Adding a fragment to a display list passes ownership of the fragment to the
- * list. The caller must not touch the fragment after this call, and must not
- * free it explicitly with vsp1_dl_fragment_free().
+ * The reference must be explicitly released by a call to vsp1_dl_body_put()
+ * when the body isn't needed anymore.
*
- * Fragments are only usable for display lists in header mode. Attempt to
- * add a fragment to a header-less display list will return an error.
+ * Additional bodies are only usable for display lists in header mode.
+ * Attempting to add a body to a header-less display list will return an error.
*/
-int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
- struct vsp1_dl_body *dlb)
+int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb)
{
/* Multi-body lists are only available in header mode. */
if (dl->dlm->mode != VSP1_DL_MODE_HEADER)
return -EINVAL;
- list_add_tail(&dlb->list, &dl->fragments);
+ refcount_inc(&dlb->refcnt);
+
+ list_add_tail(&dlb->list, &dl->bodies);
+
return 0;
}
@@ -451,10 +532,10 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
* list was allocated.
*/
- hdr->num_bytes = dl->body0.num_entries
+ hdr->num_bytes = dl->body0->num_entries
* sizeof(*dl->header->lists);
- list_for_each_entry(dlb, &dl->fragments, list) {
+ list_for_each_entry(dlb, &dl->bodies, list) {
num_lists++;
hdr++;
@@ -525,9 +606,9 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl)
* bit will be cleared by the hardware when the display list
* processing starts.
*/
- vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma);
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma);
vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
- (dl->body0.num_entries * sizeof(*dl->header->lists)));
+ (dl->body0->num_entries * sizeof(*dl->header->lists)));
} else {
/*
* In header mode, program the display list header address. If
@@ -550,8 +631,16 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl)
* case we can't replace the queued list by the new one, as we could
* race with the hardware. We thus mark the update as pending, it will
* be queued up to the hardware by the frame end interrupt handler.
+ *
+ * If a display list is already pending we simply drop it as the new
+ * display list is assumed to contain a more recent configuration. It is
+ * an error if the already pending list has the internal flag set, as
+ * there is then a process waiting for that list to complete. This
+ * shouldn't happen as the waiting process should perform proper
+ * locking, but warn just in case.
*/
if (vsp1_dl_list_hw_update_pending(dlm)) {
+ WARN_ON(dlm->pending && dlm->pending->internal);
__vsp1_dl_list_put(dlm->pending);
dlm->pending = dl;
return;
@@ -581,7 +670,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl)
dlm->active = dl;
}
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_dl_list *dl_child;
@@ -598,6 +687,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
}
}
+ dl->internal = internal;
+
spin_lock_irqsave(&dlm->lock, flags);
if (dlm->singleshot)
@@ -616,14 +707,22 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
* vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
* @dlm: the display list manager
*
- * Return true if the previous display list has completed at frame end, or false
- * if it has been delayed by one frame because the display list commit raced
- * with the frame end interrupt. The function always returns true in header mode
- * as display list processing is then not continuous and races never occur.
+ * Return a set of flags that indicates display list completion status.
+ *
+ * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list
+ * has completed at frame end. If the flag is not returned display list
+ * completion has been delayed by one frame because the display list commit
+ * raced with the frame end interrupt. The function always returns with the flag
+ * set in header mode as display list processing is then not continuous and
+ * races never occur.
+ *
+ * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list
+ * has completed and had been queued with the internal notification flag.
+ * Internal notification is only supported for continuous mode.
*/
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
+unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
{
- bool completed = false;
+ unsigned int flags = 0;
spin_lock(&dlm->lock);
@@ -634,7 +733,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
if (dlm->singleshot) {
__vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
- completed = true;
+ flags |= VSP1_DL_FRAME_END_COMPLETED;
goto done;
}
@@ -652,10 +751,14 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
* frame end interrupt. The display list thus becomes active.
*/
if (dlm->queued) {
+ if (dlm->queued->internal)
+ flags |= VSP1_DL_FRAME_END_INTERNAL;
+ dlm->queued->internal = false;
+
__vsp1_dl_list_put(dlm->active);
dlm->active = dlm->queued;
dlm->queued = NULL;
- completed = true;
+ flags |= VSP1_DL_FRAME_END_COMPLETED;
}
/*
@@ -672,7 +775,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
done:
spin_unlock(&dlm->lock);
- return completed;
+ return flags;
}
/* Hardware Setup */
@@ -710,38 +813,9 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
dlm->pending = NULL;
}
-/*
- * Free all fragments awaiting to be garbage-collected.
- *
- * This function must be called without the display list manager lock held.
- */
-static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm)
+struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dlm->lock, flags);
-
- while (!list_empty(&dlm->gc_fragments)) {
- struct vsp1_dl_body *dlb;
-
- dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body,
- list);
- list_del(&dlb->list);
-
- spin_unlock_irqrestore(&dlm->lock, flags);
- vsp1_dl_fragment_free(dlb);
- spin_lock_irqsave(&dlm->lock, flags);
- }
-
- spin_unlock_irqrestore(&dlm->lock, flags);
-}
-
-static void vsp1_dlm_garbage_collect(struct work_struct *work)
-{
- struct vsp1_dl_manager *dlm =
- container_of(work, struct vsp1_dl_manager, gc_work);
-
- vsp1_dlm_fragments_free(dlm);
+ return vsp1_dl_body_get(dlm->pool);
}
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
@@ -749,6 +823,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int prealloc)
{
struct vsp1_dl_manager *dlm;
+ size_t header_size;
unsigned int i;
dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
@@ -763,8 +838,22 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
spin_lock_init(&dlm->lock);
INIT_LIST_HEAD(&dlm->free);
- INIT_LIST_HEAD(&dlm->gc_fragments);
- INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect);
+
+ /*
+ * Initialize the display list body and allocate DMA memory for the body
+ * and the optional header. Both are allocated together to avoid memory
+ * fragmentation, with the header located right after the body in
+ * memory. An extra body is allocated on top of the prealloc to account
+ * for the cached body used by the vsp1_pipeline object.
+ */
+ header_size = dlm->mode == VSP1_DL_MODE_HEADER
+ ? ALIGN(sizeof(struct vsp1_dl_header), 8)
+ : 0;
+
+ dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1,
+ VSP1_DL_NUM_ENTRIES, header_size);
+ if (!dlm->pool)
+ return NULL;
for (i = 0; i < prealloc; ++i) {
struct vsp1_dl_list *dl;
@@ -786,12 +875,10 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)
if (!dlm)
return;
- cancel_work_sync(&dlm->gc_work);
-
list_for_each_entry_safe(dl, next, &dlm->free, list) {
list_del(&dl->list);
vsp1_dl_list_free(dl);
}
- vsp1_dlm_fragments_free(dlm);
+ vsp1_dl_body_pool_destroy(dlm->pool);
}
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index ee3508172f0a..7dba0469c92e 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_dl.h -- R-Car VSP1 Display List
*
* Copyright (C) 2015 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_DL_H__
#define __VSP1_DL_H__
@@ -16,10 +12,14 @@
#include <linux/types.h>
struct vsp1_device;
-struct vsp1_dl_fragment;
+struct vsp1_dl_body;
+struct vsp1_dl_body_pool;
struct vsp1_dl_list;
struct vsp1_dl_manager;
+#define VSP1_DL_FRAME_END_COMPLETED BIT(0)
+#define VSP1_DL_FRAME_END_INTERNAL BIT(1)
+
void vsp1_dlm_setup(struct vsp1_device *vsp1);
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
@@ -27,19 +27,23 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int prealloc);
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
-void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data);
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
-
-struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
- unsigned int num_entries);
-void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb);
-void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
-int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
- struct vsp1_dl_body *dlb);
+struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl);
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal);
+
+struct vsp1_dl_body_pool *
+vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
+ unsigned int num_entries, size_t extra_size);
+void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool);
+struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
+void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
+
+void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
+int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
#endif /* __VSP1_DL_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index b8fee1834253..edb35a5c57ea 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_drm.c -- R-Car VSP1 DRM API
+ * vsp1_drm.c -- R-Car VSP1 DRM/KMS Interface
*
* Copyright (C) 2015 Renesas Electronics 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>
@@ -20,26 +16,550 @@
#include <media/vsp1.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_drm.h"
#include "vsp1_lif.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
+#include "vsp1_uif.h"
-#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
+#define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
- bool completed)
+ unsigned int completion)
{
struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ bool complete = completion == VSP1_DL_FRAME_END_COMPLETED;
+
+ if (drm_pipe->du_complete) {
+ struct vsp1_entity *uif = drm_pipe->uif;
+ u32 crc;
- if (drm_pipe->du_complete)
- drm_pipe->du_complete(drm_pipe->du_private, completed);
+ crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0;
+ drm_pipe->du_complete(drm_pipe->du_private, complete, crc);
+ }
+
+ if (completion & VSP1_DL_FRAME_END_INTERNAL) {
+ drm_pipe->force_brx_release = false;
+ wake_up(&drm_pipe->wait_queue);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Configuration
+ */
+
+/*
+ * Insert the UIF in the pipeline between the prev and next entities. If no UIF
+ * is available connect the two entities directly.
+ */
+static int vsp1_du_insert_uif(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_entity *uif,
+ struct vsp1_entity *prev, unsigned int prev_pad,
+ struct vsp1_entity *next, unsigned int next_pad)
+{
+ struct v4l2_subdev_format format;
+ int ret;
+
+ if (!uif) {
+ /*
+ * If there's no UIF to be inserted, connect the previous and
+ * next entities directly.
+ */
+ prev->sink = next;
+ prev->sink_pad = next_pad;
+ return 0;
+ }
+
+ prev->sink = uif;
+ prev->sink_pad = UIF_PAD_SINK;
+
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = prev_pad;
+
+ ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ format.pad = UIF_PAD_SINK;
+
+ ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on UIF sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code);
+
+ /*
+ * The UIF doesn't mangle the format between its sink and source pads,
+ * so there is no need to retrieve the format on its source pad.
+ */
+
+ uif->sink = next;
+ uif->sink_pad = next_pad;
+
+ return 0;
+}
+
+/* Setup one RPF and the connected BRx sink pad. */
+static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_rwpf *rpf,
+ struct vsp1_entity *uif,
+ unsigned int brx_input)
+{
+ struct v4l2_subdev_selection sel;
+ struct v4l2_subdev_format format;
+ const struct v4l2_rect *crop;
+ int ret;
+
+ /*
+ * Configure the format on the RPF sink pad and propagate it up to the
+ * BRx sink pad.
+ */
+ crop = &vsp1->drm->inputs[rpf->entity.index].crop;
+
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = crop->width + crop->left;
+ format.format.height = crop->height + crop->top;
+ format.format.code = rpf->fmtinfo->mbus;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set format %ux%u (%x) on RPF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sel.pad = RWPF_PAD_SINK;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r = *crop;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
+ &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ rpf->entity.index);
+
+ /*
+ * RPF source, hardcode the format to ARGB8888 to turn on format
+ * conversion if needed.
+ */
+ format.pad = RWPF_PAD_SOURCE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: got format %ux%u (%x) on RPF%u source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ /* Insert and configure the UIF if available. */
+ ret = vsp1_du_insert_uif(vsp1, pipe, uif, &rpf->entity, RWPF_PAD_SOURCE,
+ pipe->brx, brx_input);
+ if (ret < 0)
+ return ret;
+
+ /* BRx sink, propagate the format from the RPF source. */
+ format.pad = brx_input;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, BRX_NAME(pipe->brx), format.pad);
+
+ sel.pad = brx_input;
+ sel.target = V4L2_SEL_TGT_COMPOSE;
+ sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_selection, NULL,
+ &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ BRX_NAME(pipe->brx), sel.pad);
+
+ return 0;
+}
+
+/* Setup the BRx source pad. */
+static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe);
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe);
+
+static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct vsp1_entity *brx;
+ int ret;
+
+ /*
+ * Pick a BRx:
+ * - If we need more than two inputs, use the BRU.
+ * - Otherwise, if we are not forced to release our BRx, keep it.
+ * - Else, use any free BRx (randomly starting with the BRU).
+ */
+ if (pipe->num_inputs > 2)
+ brx = &vsp1->bru->entity;
+ else if (pipe->brx && !drm_pipe->force_brx_release)
+ brx = pipe->brx;
+ else if (!vsp1->bru->entity.pipe)
+ brx = &vsp1->bru->entity;
+ else
+ brx = &vsp1->brs->entity;
+
+ /* Switch BRx if needed. */
+ if (brx != pipe->brx) {
+ struct vsp1_entity *released_brx = NULL;
+
+ /* Release our BRx if we have one. */
+ if (pipe->brx) {
+ dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n",
+ __func__, pipe->lif->index,
+ BRX_NAME(pipe->brx));
+
+ /*
+ * The BRx might be acquired by the other pipeline in
+ * the next step. We must thus remove it from the list
+ * of entities for this pipeline. The other pipeline's
+ * hardware configuration will reconfigure the BRx
+ * routing.
+ *
+ * However, if the other pipeline doesn't acquire our
+ * BRx, we need to keep it in the list, otherwise the
+ * hardware configuration step won't disconnect it from
+ * the pipeline. To solve this, store the released BRx
+ * pointer to add it back to the list of entities later
+ * if it isn't acquired by the other pipeline.
+ */
+ released_brx = pipe->brx;
+
+ list_del(&pipe->brx->list_pipe);
+ pipe->brx->sink = NULL;
+ pipe->brx->pipe = NULL;
+ pipe->brx = NULL;
+ }
+
+ /*
+ * If the BRx we need is in use, force the owner pipeline to
+ * switch to the other BRx and wait until the switch completes.
+ */
+ if (brx->pipe) {
+ struct vsp1_drm_pipeline *owner_pipe;
+
+ dev_dbg(vsp1->dev, "%s: pipe %u: waiting for %s\n",
+ __func__, pipe->lif->index, BRX_NAME(brx));
+
+ owner_pipe = to_vsp1_drm_pipeline(brx->pipe);
+ owner_pipe->force_brx_release = true;
+
+ vsp1_du_pipeline_setup_inputs(vsp1, &owner_pipe->pipe);
+ vsp1_du_pipeline_configure(&owner_pipe->pipe);
+
+ ret = wait_event_timeout(owner_pipe->wait_queue,
+ !owner_pipe->force_brx_release,
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev,
+ "DRM pipeline %u reconfiguration timeout\n",
+ owner_pipe->pipe.lif->index);
+ }
+
+ /*
+ * If the BRx we have released previously hasn't been acquired
+ * by the other pipeline, add it back to the entities list (with
+ * the pipe pointer NULL) to let vsp1_du_pipeline_configure()
+ * disconnect it from the hardware pipeline.
+ */
+ if (released_brx && !released_brx->pipe)
+ list_add_tail(&released_brx->list_pipe,
+ &pipe->entities);
+
+ /* Add the BRx to the pipeline. */
+ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n",
+ __func__, pipe->lif->index, BRX_NAME(brx));
+
+ pipe->brx = brx;
+ pipe->brx->pipe = pipe;
+ pipe->brx->sink = &pipe->output->entity;
+ pipe->brx->sink_pad = 0;
+
+ list_add_tail(&pipe->brx->list_pipe, &pipe->entities);
+ }
+
+ /*
+ * Configure the format on the BRx source and verify that it matches the
+ * requested format. We don't set the media bus code as it is configured
+ * on the BRx sink pad 0 and propagated inside the entity, not on the
+ * source pad.
+ */
+ format.pad = pipe->brx->source_pad;
+ format.format.width = drm_pipe->width;
+ format.format.height = drm_pipe->height;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad);
+
+ if (format.format.width != drm_pipe->width ||
+ format.format.height != drm_pipe->height) {
+ dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
+{
+ return vsp1->drm->inputs[rpf->entity.index].zpos;
+}
+
+/* Setup the input side of the pipeline (RPFs and BRx). */
+static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
+ struct vsp1_entity *uif;
+ bool use_uif = false;
+ struct vsp1_brx *brx;
+ unsigned int i;
+ int ret;
+
+ /* Count the number of enabled inputs and sort them by Z-order. */
+ pipe->num_inputs = 0;
+
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf = vsp1->rpf[i];
+ unsigned int j;
+
+ if (!pipe->inputs[i])
+ continue;
+
+ /* Insert the RPF in the sorted RPFs array. */
+ for (j = pipe->num_inputs++; j > 0; --j) {
+ if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
+ break;
+ inputs[j] = inputs[j-1];
+ }
+
+ inputs[j] = rpf;
+ }
+
+ /*
+ * Setup the BRx. This must be done before setting up the RPF input
+ * pipelines as the BRx sink compose rectangles depend on the BRx source
+ * format.
+ */
+ ret = vsp1_du_pipeline_setup_brx(vsp1, pipe);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "%s: failed to setup %s source\n", __func__,
+ BRX_NAME(pipe->brx));
+ return ret;
+ }
+
+ brx = to_brx(&pipe->brx->subdev);
+
+ /* Setup the RPF input pipeline for every enabled input. */
+ for (i = 0; i < pipe->brx->source_pad; ++i) {
+ struct vsp1_rwpf *rpf = inputs[i];
+
+ if (!rpf) {
+ brx->inputs[i].rpf = NULL;
+ continue;
+ }
+
+ if (!rpf->entity.pipe) {
+ rpf->entity.pipe = pipe;
+ list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
+ }
+
+ brx->inputs[i].rpf = rpf;
+ rpf->brx_input = i;
+ rpf->entity.sink = pipe->brx;
+ rpf->entity.sink_pad = i;
+
+ dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
+ __func__, rpf->entity.index, BRX_NAME(pipe->brx), i);
+
+ uif = drm_pipe->crc.source == VSP1_DU_CRC_PLANE &&
+ drm_pipe->crc.index == i ? drm_pipe->uif : NULL;
+ if (uif)
+ use_uif = true;
+ ret = vsp1_du_pipeline_setup_rpf(vsp1, pipe, rpf, uif, i);
+ if (ret < 0) {
+ dev_err(vsp1->dev,
+ "%s: failed to setup RPF.%u\n",
+ __func__, rpf->entity.index);
+ return ret;
+ }
+ }
+
+ /* Insert and configure the UIF at the BRx output if available. */
+ uif = drm_pipe->crc.source == VSP1_DU_CRC_OUTPUT ? drm_pipe->uif : NULL;
+ if (uif)
+ use_uif = true;
+ ret = vsp1_du_insert_uif(vsp1, pipe, uif,
+ pipe->brx, pipe->brx->source_pad,
+ &pipe->output->entity, 0);
+ if (ret < 0)
+ dev_err(vsp1->dev, "%s: failed to setup UIF after %s\n",
+ __func__, BRX_NAME(pipe->brx));
+
+ /*
+ * If the UIF is not in use schedule it for removal by setting its pipe
+ * pointer to NULL, vsp1_du_pipeline_configure() will remove it from the
+ * hardware pipeline and from the pipeline's list of entities. Otherwise
+ * make sure it is present in the pipeline's list of entities if it
+ * wasn't already.
+ */
+ if (!use_uif) {
+ drm_pipe->uif->pipe = NULL;
+ } else if (!drm_pipe->uif->pipe) {
+ drm_pipe->uif->pipe = pipe;
+ list_add_tail(&drm_pipe->uif->list_pipe, &pipe->entities);
+ }
+
+ return 0;
+}
+
+/* Setup the output side of the pipeline (WPF and LIF). */
+static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct v4l2_subdev_format format = { 0, };
+ int ret;
+
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = drm_pipe->width;
+ format.format.height = drm_pipe->height;
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->output->entity.index);
+
+ format.pad = RWPF_PAD_SOURCE;
+ ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->output->entity.index);
+
+ format.pad = LIF_PAD_SINK;
+ ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->lif->index);
+
+ /*
+ * Verify that the format at the output of the pipeline matches the
+ * requested frame size and media bus code.
+ */
+ if (format.format.width != drm_pipe->width ||
+ format.format.height != drm_pipe->height ||
+ format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
+ dev_dbg(vsp1->dev, "%s: format mismatch on LIF%u\n", __func__,
+ pipe->lif->index);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+/* Configure all entities in the pipeline. */
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct vsp1_entity *entity;
+ struct vsp1_entity *next;
+ struct vsp1_dl_list *dl;
+ struct vsp1_dl_body *dlb;
+
+ dl = vsp1_dl_list_get(pipe->output->dlm);
+ dlb = vsp1_dl_list_get_body0(dl);
+
+ list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
+ /* Disconnect unused entities from the pipeline. */
+ if (!entity->pipe) {
+ vsp1_dl_body_write(dlb, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+
+ entity->sink = NULL;
+ list_del(&entity->list_pipe);
+
+ continue;
+ }
+
+ vsp1_entity_route_setup(entity, pipe, dlb);
+ vsp1_entity_configure_stream(entity, pipe, dlb);
+ vsp1_entity_configure_frame(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe, dl, dlb);
+ }
+
+ vsp1_dl_list_commit(dl, drm_pipe->force_brx_release);
}
/* -----------------------------------------------------------------------------
@@ -64,8 +584,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_init);
* @cfg: the LIF configuration
*
* Configure the output part of VSP DRM pipeline for the given frame @cfg.width
- * and @cfg.height. This sets up formats on the blend unit (BRU or BRS) source
- * pad, the WPF sink and source pads, and the LIF sink pad.
+ * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and
+ * source pads, and the LIF sink pad.
*
* The @pipe_index argument selects which DRM pipeline to setup. The number of
* available pipelines depend on the VSP instance.
@@ -84,11 +604,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe;
struct vsp1_pipeline *pipe;
- struct vsp1_bru *bru;
- struct vsp1_entity *entity;
- struct vsp1_entity *next;
- struct vsp1_dl_list *dl;
- struct v4l2_subdev_format format;
unsigned long flags;
unsigned int i;
int ret;
@@ -98,9 +613,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe = &vsp1->drm->pipe[pipe_index];
pipe = &drm_pipe->pipe;
- bru = to_bru(&pipe->bru->subdev);
if (!cfg) {
+ struct vsp1_brx *brx;
+
+ mutex_lock(&vsp1->drm->lock);
+
+ brx = to_brx(&pipe->brx->subdev);
+
/*
* NULL configuration means the CRTC is being disabled, stop
* the pipeline and turn the light off.
@@ -109,8 +629,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
if (ret == -ETIMEDOUT)
dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
- media_pipeline_stop(&pipe->output->entity.subdev.entity);
-
for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
struct vsp1_rwpf *rpf = pipe->inputs[i];
@@ -118,19 +636,30 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
continue;
/*
- * Remove the RPF from the pipe and the list of BRU
+ * Remove the RPF from the pipe and the list of BRx
* inputs.
*/
- WARN_ON(list_empty(&rpf->entity.list_pipe));
- list_del_init(&rpf->entity.list_pipe);
+ WARN_ON(!rpf->entity.pipe);
+ rpf->entity.pipe = NULL;
+ list_del(&rpf->entity.list_pipe);
pipe->inputs[i] = NULL;
- bru->inputs[rpf->bru_input].rpf = NULL;
+ brx->inputs[rpf->brx_input].rpf = NULL;
}
drm_pipe->du_complete = NULL;
pipe->num_inputs = 0;
+ dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n",
+ __func__, pipe->lif->index,
+ BRX_NAME(pipe->brx));
+
+ list_del(&pipe->brx->list_pipe);
+ pipe->brx->pipe = NULL;
+ pipe->brx = NULL;
+
+ mutex_unlock(&vsp1->drm->lock);
+
vsp1_dlm_reset(pipe->output->dlm);
vsp1_device_put(vsp1);
@@ -139,100 +668,27 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
return 0;
}
+ drm_pipe->width = cfg->width;
+ drm_pipe->height = cfg->height;
+
dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n",
__func__, pipe_index, cfg->width, cfg->height);
- /*
- * Configure the format at the BRU sinks and propagate it through the
- * pipeline.
- */
- memset(&format, 0, sizeof(format));
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
-
- for (i = 0; i < pipe->bru->source_pad; ++i) {
- format.pad = i;
-
- format.format.width = cfg->width;
- format.format.height = cfg->height;
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad,
- set_fmt, NULL, &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), i);
- }
-
- format.pad = pipe->bru->source_pad;
- format.format.width = cfg->width;
- format.format.height = cfg->height;
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), i);
-
- format.pad = RWPF_PAD_SINK;
- ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe->output->entity.index);
+ mutex_lock(&vsp1->drm->lock);
- format.pad = RWPF_PAD_SOURCE;
- ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL,
- &format);
+ /* Setup formats through the pipeline. */
+ ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe);
if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe->output->entity.index);
+ goto unlock;
- format.pad = LIF_PAD_SINK;
- ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL,
- &format);
+ ret = vsp1_du_pipeline_setup_output(vsp1, pipe);
if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe_index);
+ goto unlock;
- /*
- * Verify that the format at the output of the pipeline matches the
- * requested frame size and media bus code.
- */
- if (format.format.width != cfg->width ||
- format.format.height != cfg->height ||
- format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
- dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
- return -EPIPE;
- }
-
- /*
- * Mark the pipeline as streaming and enable the VSP1. This will store
- * the pipeline pointer in all entities, which the s_stream handlers
- * will need. We don't start the entities themselves right at this point
- * as there's no plane configured yet, so we can't start processing
- * buffers.
- */
+ /* Enable the VSP1. */
ret = vsp1_device_get(vsp1);
if (ret < 0)
- return ret;
+ goto unlock;
/*
* Register a callback to allow us to notify the DRM driver of frame
@@ -241,35 +697,18 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe->du_complete = cfg->callback;
drm_pipe->du_private = cfg->callback_data;
- ret = media_pipeline_start(&pipe->output->entity.subdev.entity,
- &pipe->pipe);
- if (ret < 0) {
- dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__);
- vsp1_device_put(vsp1);
- return ret;
- }
-
/* Disable the display interrupts. */
vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
/* Configure all entities in the pipeline. */
- dl = vsp1_dl_list_get(pipe->output->dlm);
+ vsp1_du_pipeline_configure(pipe);
- list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
- vsp1_entity_route_setup(entity, pipe, dl);
-
- if (entity->ops->configure) {
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_INIT);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
- }
+unlock:
+ mutex_unlock(&vsp1->drm->lock);
- vsp1_dl_list_commit(dl);
+ if (ret < 0)
+ return ret;
/* Start the pipeline. */
spin_lock_irqsave(&pipe->irqlock, flags);
@@ -290,9 +729,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
- drm_pipe->enabled = drm_pipe->pipe.num_inputs != 0;
+ mutex_lock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
@@ -345,10 +783,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
rpf_index);
/*
- * Remove the RPF from the pipe's inputs. The atomic flush
- * handler will disable the input and remove the entity from the
- * pipe's entities list.
+ * Remove the RPF from the pipeline's inputs. Keep it in the
+ * pipeline's entity list to let vsp1_du_pipeline_configure()
+ * remove it from the hardware pipeline.
*/
+ rpf->entity.pipe = NULL;
drm_pipe->pipe.inputs[rpf_index] = NULL;
return 0;
}
@@ -392,214 +831,24 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
-static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
- struct vsp1_pipeline *pipe,
- struct vsp1_rwpf *rpf, unsigned int bru_input)
-{
- struct v4l2_subdev_selection sel;
- struct v4l2_subdev_format format;
- const struct v4l2_rect *crop;
- int ret;
-
- /*
- * Configure the format on the RPF sink pad and propagate it up to the
- * BRU sink pad.
- */
- crop = &vsp1->drm->inputs[rpf->entity.index].crop;
-
- memset(&format, 0, sizeof(format));
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- format.pad = RWPF_PAD_SINK;
- format.format.width = crop->width + crop->left;
- format.format.height = crop->height + crop->top;
- format.format.code = rpf->fmtinfo->mbus;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: set format %ux%u (%x) on RPF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, rpf->entity.index);
-
- memset(&sel, 0, sizeof(sel));
- sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sel.pad = RWPF_PAD_SINK;
- sel.target = V4L2_SEL_TGT_CROP;
- sel.r = *crop;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
- &sel);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
- __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
- rpf->entity.index);
-
- /*
- * RPF source, hardcode the format to ARGB8888 to turn on format
- * conversion if needed.
- */
- format.pad = RWPF_PAD_SOURCE;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: got format %ux%u (%x) on RPF%u source\n",
- __func__, format.format.width, format.format.height,
- format.format.code, rpf->entity.index);
-
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- /* BRU sink, propagate the format from the RPF source. */
- format.pad = bru_input;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), format.pad);
-
- sel.pad = bru_input;
- sel.target = V4L2_SEL_TGT_COMPOSE;
- sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_selection, NULL,
- &sel);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
- __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
- BRU_NAME(pipe->bru), sel.pad);
-
- return 0;
-}
-
-static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
-{
- return vsp1->drm->inputs[rpf->entity.index].zpos;
-}
-
/**
* vsp1_du_atomic_flush - Commit an atomic update
* @dev: the VSP device
* @pipe_index: the DRM pipeline index
+ * @cfg: atomic pipe configuration
*/
-void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
+void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
+ const struct vsp1_du_atomic_pipe_config *cfg)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
- struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
- struct vsp1_entity *entity;
- struct vsp1_entity *next;
- struct vsp1_dl_list *dl;
- unsigned int i;
- int ret;
-
- /* Prepare the display list. */
- dl = vsp1_dl_list_get(pipe->output->dlm);
-
- /* Count the number of enabled inputs and sort them by Z-order. */
- pipe->num_inputs = 0;
-
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
- struct vsp1_rwpf *rpf = vsp1->rpf[i];
- unsigned int j;
- /*
- * Make sure we don't accept more inputs than the hardware can
- * handle. This is a temporary fix to avoid display stall, we
- * need to instead allocate the BRU or BRS to display pipelines
- * dynamically based on the number of planes they each use.
- */
- if (pipe->num_inputs >= pipe->bru->source_pad)
- pipe->inputs[i] = NULL;
+ drm_pipe->crc = cfg->crc;
- if (!pipe->inputs[i])
- continue;
-
- /* Insert the RPF in the sorted RPFs array. */
- for (j = pipe->num_inputs++; j > 0; --j) {
- if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
- break;
- inputs[j] = inputs[j-1];
- }
-
- inputs[j] = rpf;
- }
-
- /* Setup the RPF input pipeline for every enabled input. */
- for (i = 0; i < pipe->bru->source_pad; ++i) {
- struct vsp1_rwpf *rpf = inputs[i];
-
- if (!rpf) {
- bru->inputs[i].rpf = NULL;
- continue;
- }
-
- if (list_empty(&rpf->entity.list_pipe))
- list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
-
- bru->inputs[i].rpf = rpf;
- rpf->bru_input = i;
- rpf->entity.sink = pipe->bru;
- rpf->entity.sink_pad = i;
-
- dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
- __func__, rpf->entity.index, BRU_NAME(pipe->bru), i);
-
- ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i);
- if (ret < 0)
- dev_err(vsp1->dev,
- "%s: failed to setup RPF.%u\n",
- __func__, rpf->entity.index);
- }
-
- /* Configure all entities in the pipeline. */
- list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
- /* Disconnect unused RPFs from the pipeline. */
- if (entity->type == VSP1_ENTITY_RPF &&
- !pipe->inputs[entity->index]) {
- vsp1_dl_list_write(dl, entity->route->reg,
- VI6_DPR_NODE_UNUSED);
-
- list_del_init(&entity->list_pipe);
-
- continue;
- }
-
- vsp1_entity_route_setup(entity, pipe, dl);
-
- if (entity->ops->configure) {
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_INIT);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
- }
-
- vsp1_dl_list_commit(dl);
+ vsp1_du_pipeline_setup_inputs(vsp1, pipe);
+ vsp1_du_pipeline_configure(pipe);
+ mutex_unlock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
@@ -638,31 +887,40 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
if (!vsp1->drm)
return -ENOMEM;
+ mutex_init(&vsp1->drm->lock);
+
/* Create one DRM pipeline per LIF. */
for (i = 0; i < vsp1->info->lif_count; ++i) {
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
+ init_waitqueue_head(&drm_pipe->wait_queue);
+
vsp1_pipeline_init(pipe);
+ pipe->frame_end = vsp1_du_pipeline_frame_end;
+
/*
- * The DRM pipeline is static, add entities manually. The first
- * pipeline uses the BRU and the second pipeline the BRS.
+ * The output side of the DRM pipeline is static, add the
+ * corresponding entities manually.
*/
- pipe->bru = i == 0 ? &vsp1->bru->entity : &vsp1->brs->entity;
- pipe->lif = &vsp1->lif[i]->entity;
pipe->output = vsp1->wpf[i];
- pipe->output->pipe = pipe;
- pipe->frame_end = vsp1_du_pipeline_frame_end;
+ pipe->lif = &vsp1->lif[i]->entity;
- pipe->bru->sink = &pipe->output->entity;
- pipe->bru->sink_pad = 0;
+ pipe->output->entity.pipe = pipe;
pipe->output->entity.sink = pipe->lif;
pipe->output->entity.sink_pad = 0;
+ list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
- list_add_tail(&pipe->bru->list_pipe, &pipe->entities);
+ pipe->lif->pipe = pipe;
list_add_tail(&pipe->lif->list_pipe, &pipe->entities);
- list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
+
+ /*
+ * CRC computation is initially disabled, don't add the UIF to
+ * the pipeline.
+ */
+ if (i < vsp1->info->uif_count)
+ drm_pipe->uif = &vsp1->uif[i]->entity;
}
/* Disable all RPFs initially. */
@@ -677,4 +935,5 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
void vsp1_drm_cleanup(struct vsp1_device *vsp1)
{
+ mutex_destroy(&vsp1->drm->lock);
}
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index 1cd9db785bf7..8dfd274a59e2 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -1,46 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface
*
* Copyright (C) 2015 Renesas Electronics 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_DRM_H__
#define __VSP1_DRM_H__
+#include <linux/mutex.h>
#include <linux/videodev2.h>
+#include <linux/wait.h>
+
+#include <media/vsp1.h>
#include "vsp1_pipe.h"
/**
* vsp1_drm_pipeline - State for the API exposed to the DRM driver
* @pipe: the VSP1 pipeline used for display
- * @enabled: pipeline state at the beginning of an update
+ * @width: output display width
+ * @height: output display height
+ * @force_brx_release: when set, release the BRx during the next reconfiguration
+ * @wait_queue: wait queue to wait for BRx release completion
+ * @uif: UIF entity if available for the pipeline
+ * @crc: CRC computation configuration
* @du_complete: frame completion callback for the DU driver (optional)
* @du_private: data to be passed to the du_complete callback
*/
struct vsp1_drm_pipeline {
struct vsp1_pipeline pipe;
- bool enabled;
+
+ unsigned int width;
+ unsigned int height;
+
+ bool force_brx_release;
+ wait_queue_head_t wait_queue;
+
+ struct vsp1_entity *uif;
+ struct vsp1_du_crc_config crc;
/* Frame synchronisation */
- void (*du_complete)(void *, bool);
+ void (*du_complete)(void *data, bool completed, u32 crc);
void *du_private;
};
/**
* vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
+ * @lock: protects the BRU and BRS allocation
* @inputs: source crop rectangle, destination compose rectangle and z-order
* position for every input (indexed by RPF index)
*/
struct vsp1_drm {
struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF];
+ struct mutex lock;
struct {
struct v4l2_rect crop;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index eed9516e25e1..5d82f6ee56ea 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2015 Renesas Electronics 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/clk.h>
@@ -26,7 +22,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_clu.h"
#include "vsp1_dl.h"
#include "vsp1_drm.h"
@@ -39,6 +35,7 @@
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
+#include "vsp1_uif.h"
#include "vsp1_video.h"
/* -----------------------------------------------------------------------------
@@ -63,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
if (status & VI6_WFP_IRQ_STA_DFE) {
- vsp1_pipeline_frame_end(wpf->pipe);
+ vsp1_pipeline_frame_end(wpf->entity.pipe);
ret = IRQ_HANDLED;
}
}
@@ -269,7 +266,7 @@ 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);
+ vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS);
if (IS_ERR(vsp1->brs)) {
ret = PTR_ERR(vsp1->brs);
goto done;
@@ -279,7 +276,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
}
if (vsp1->info->features & VSP1_HAS_BRU) {
- vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU);
+ vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU);
if (IS_ERR(vsp1->bru)) {
ret = PTR_ERR(vsp1->bru);
goto done;
@@ -413,6 +410,19 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
+ for (i = 0; i < vsp1->info->uif_count; ++i) {
+ struct vsp1_uif *uif;
+
+ uif = vsp1_uif_create(vsp1, i);
+ if (IS_ERR(uif)) {
+ ret = PTR_ERR(uif);
+ goto done;
+ }
+
+ vsp1->uif[i] = uif;
+ list_add_tail(&uif->entity.list_dev, &vsp1->entities);
+ }
+
for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
@@ -517,6 +527,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
for (i = 0; i < vsp1->info->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
+ for (i = 0; i < vsp1->info->uif_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
@@ -576,7 +589,7 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev)
* restarted explicitly by the DU.
*/
if (!vsp1->drm)
- vsp1_pipelines_suspend(vsp1);
+ vsp1_video_suspend(vsp1);
pm_runtime_force_suspend(vsp1->dev);
@@ -594,7 +607,7 @@ static int __maybe_unused vsp1_pm_resume(struct device *dev)
* restarted explicitly by the DU.
*/
if (!vsp1->drm)
- vsp1_pipelines_resume(vsp1);
+ vsp1_video_resume(vsp1);
return 0;
}
@@ -744,6 +757,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
.lif_count = 1,
.rpf_count = 5,
+ .uif_count = 1,
.wpf_count = 2,
.num_bru_inputs = 5,
}, {
@@ -753,6 +767,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRS | VSP1_HAS_BRU,
.lif_count = 1,
.rpf_count = 5,
+ .uif_count = 1,
.wpf_count = 1,
.num_bru_inputs = 5,
}, {
@@ -762,6 +777,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRS | VSP1_HAS_BRU,
.lif_count = 2,
.rpf_count = 5,
+ .uif_count = 2,
.wpf_count = 2,
.num_bru_inputs = 5,
},
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 54de15095709..36a29e13109e 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_entity.c -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 Renesas Electronics 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>
@@ -26,7 +22,7 @@
void vsp1_entity_route_setup(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl)
+ struct vsp1_dl_body *dlb)
{
struct vsp1_entity *source;
u32 route;
@@ -42,7 +38,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT)
| (source->route->output << VI6_DPR_SMPPT_PT_SHIFT);
- vsp1_dl_list_write(dl, VI6_DPR_HGO_SMPPT, smppt);
+ vsp1_dl_body_write(dlb, VI6_DPR_HGO_SMPPT, smppt);
return;
} else if (entity->type == VSP1_ENTITY_HGT) {
u32 smppt;
@@ -55,7 +51,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT)
| (source->route->output << VI6_DPR_SMPPT_PT_SHIFT);
- vsp1_dl_list_write(dl, VI6_DPR_HGT_SMPPT, smppt);
+ vsp1_dl_body_write(dlb, VI6_DPR_HGT_SMPPT, smppt);
return;
}
@@ -70,7 +66,33 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
*/
if (source->type == VSP1_ENTITY_BRS)
route |= VI6_DPR_ROUTE_BRSSEL;
- vsp1_dl_list_write(dl, source->route->reg, route);
+ vsp1_dl_body_write(dlb, source->route->reg, route);
+}
+
+void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_stream)
+ entity->ops->configure_stream(entity, pipe, dlb);
+}
+
+void vsp1_entity_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_frame)
+ entity->ops->configure_frame(entity, pipe, dl, dlb);
+}
+
+void vsp1_entity_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_partition)
+ entity->ops->configure_partition(entity, pipe, dl, dlb);
}
/* -----------------------------------------------------------------------------
@@ -311,6 +333,97 @@ done:
return ret;
}
+/*
+ * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler
+ * @subdev: V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: V4L2 subdev format
+ * @codes: Array of supported media bus codes
+ * @ncodes: Number of supported media bus codes
+ * @min_width: Minimum image width
+ * @min_height: Minimum image height
+ * @max_width: Maximum image width
+ * @max_height: Maximum image height
+ *
+ * This function implements the subdev set_fmt pad operation for entities that
+ * do not support scaling or cropping. It defaults to the first supplied media
+ * bus code if the requested code isn't supported, clamps the size to the
+ * supplied minimum and maximum, and propagates the sink pad format to the
+ * source pad.
+ */
+int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt,
+ const unsigned int *codes, unsigned int ncodes,
+ unsigned int min_width, unsigned int min_height,
+ unsigned int max_width, unsigned int max_height)
+{
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *selection;
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&entity->lock);
+
+ config = vsp1_entity_get_pad_config(entity, cfg, fmt->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ format = vsp1_entity_get_pad_format(entity, config, fmt->pad);
+
+ if (fmt->pad == entity->source_pad) {
+ /* The output format can't be modified. */
+ fmt->format = *format;
+ goto done;
+ }
+
+ /*
+ * Default to the first media bus code if the requested format is not
+ * supported.
+ */
+ for (i = 0; i < ncodes; ++i) {
+ if (fmt->format.code == codes[i])
+ break;
+ }
+
+ format->code = i < ncodes ? codes[i] : codes[0];
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ min_width, max_width);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ min_height, max_height);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(entity, config, entity->source_pad);
+ *format = fmt->format;
+
+ /* Reset the crop and compose rectangles */
+ selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad,
+ V4L2_SEL_TGT_CROP);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+
+ selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad,
+ V4L2_SEL_TGT_COMPOSE);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+
+done:
+ mutex_unlock(&entity->lock);
+ return ret;
+}
+
/* -----------------------------------------------------------------------------
* Media Operations
*/
@@ -452,6 +565,10 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
{ VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \
{ VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) }
+#define VSP1_ENTITY_ROUTE_UIF(idx) \
+ { VSP1_ENTITY_UIF, idx, VI6_DPR_UIF_ROUTE(idx), \
+ { VI6_DPR_NODE_UIF(idx) }, VI6_DPR_NODE_UIF(idx) }
+
#define VSP1_ENTITY_ROUTE_WPF(idx) \
{ VSP1_ENTITY_WPF, idx, 0, \
{ VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
@@ -480,6 +597,8 @@ static const struct vsp1_route vsp1_routes[] = {
VSP1_ENTITY_ROUTE_UDS(0),
VSP1_ENTITY_ROUTE_UDS(1),
VSP1_ENTITY_ROUTE_UDS(2),
+ VSP1_ENTITY_ROUTE_UIF(0), /* Named UIF4 in the documentation */
+ VSP1_ENTITY_ROUTE_UIF(1), /* Named UIF5 in the documentation */
VSP1_ENTITY_ROUTE_WPF(0),
VSP1_ENTITY_ROUTE_WPF(1),
VSP1_ENTITY_ROUTE_WPF(2),
@@ -511,7 +630,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
entity->source_pad = num_pads - 1;
/* Allocate and initialize pads. */
- entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
+ entity->pads = devm_kcalloc(vsp1->dev,
+ num_pads, sizeof(*entity->pads),
GFP_KERNEL);
if (entity->pads == NULL)
return -ENOMEM;
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index 408602ebeb97..97acb7795cf1 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_entity.h -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 Renesas Electronics 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_ENTITY_H__
#define __VSP1_ENTITY_H__
@@ -19,6 +15,7 @@
#include <media/v4l2-subdev.h>
struct vsp1_device;
+struct vsp1_dl_body;
struct vsp1_dl_list;
struct vsp1_pipeline;
struct vsp1_partition;
@@ -37,21 +34,10 @@ enum vsp1_entity_type {
VSP1_ENTITY_RPF,
VSP1_ENTITY_SRU,
VSP1_ENTITY_UDS,
+ VSP1_ENTITY_UIF,
VSP1_ENTITY_WPF,
};
-/**
- * enum vsp1_entity_params - Entity configuration parameters class
- * @VSP1_ENTITY_PARAMS_INIT - Initial parameters
- * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters
- * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters
- */
-enum vsp1_entity_params {
- VSP1_ENTITY_PARAMS_INIT,
- VSP1_ENTITY_PARAMS_PARTITION,
- VSP1_ENTITY_PARAMS_RUNTIME,
-};
-
#define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */
/*
@@ -80,8 +66,10 @@ struct vsp1_route {
/**
* struct vsp1_entity_operations - Entity operations
* @destroy: Destroy the entity.
- * @configure: Setup the hardware based on the entity state (pipeline, formats,
- * selection rectangles, ...)
+ * @configure_stream: Setup the hardware parameters for the stream which do
+ * not vary between frames (pipeline, formats).
+ * @configure_frame: Configure the runtime parameters for each frame.
+ * @configure_partition: Configure partition specific parameters.
* @max_width: Return the max supported width of data that the entity can
* process in a single operation.
* @partition: Process the partition construction based on this entity's
@@ -89,8 +77,14 @@ struct vsp1_route {
*/
struct vsp1_entity_operations {
void (*destroy)(struct vsp1_entity *);
- void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_dl_list *, enum vsp1_entity_params);
+ void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *,
+ struct vsp1_dl_body *);
+ void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *,
+ struct vsp1_dl_list *, struct vsp1_dl_body *);
+ void (*configure_partition)(struct vsp1_entity *,
+ struct vsp1_pipeline *,
+ struct vsp1_dl_list *,
+ struct vsp1_dl_body *);
unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *,
struct vsp1_partition *, unsigned int,
@@ -106,6 +100,8 @@ struct vsp1_entity {
unsigned int index;
const struct vsp1_route *route;
+ struct vsp1_pipeline *pipe;
+
struct list_head list_dev;
struct list_head list_pipe;
@@ -155,13 +151,33 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
void vsp1_entity_route_setup(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl);
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad);
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt);
+int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt,
+ const unsigned int *codes, unsigned int ncodes,
+ unsigned int min_width, unsigned int min_height,
+ unsigned int max_width, unsigned int max_height);
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code,
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/vsp1/vsp1_hgo.c
index 50309c053b78..827373c25351 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/vsp1/vsp1_hgo.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hgo.c -- R-Car VSP1 Histogram Generator 1D
*
* Copyright (C) 2016 Renesas Electronics 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>
@@ -32,10 +28,10 @@ static inline u32 vsp1_hgo_read(struct vsp1_hgo *hgo, u32 reg)
return vsp1_read(hgo->histo.entity.vsp1, reg);
}
-static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_hgo_write(struct vsp1_hgo *hgo,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -133,10 +129,9 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = {
* VSP1 Entity Operations
*/
-static void hgo_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hgo_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hgo *hgo = to_hgo(&entity->subdev);
struct v4l2_rect *compose;
@@ -144,21 +139,18 @@ static void hgo_configure(struct vsp1_entity *entity,
unsigned int hratio;
unsigned int vratio;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
crop = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
compose = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK,
V4L2_SEL_TGT_COMPOSE);
- vsp1_hgo_write(hgo, dl, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
- vsp1_hgo_write(hgo, dl, VI6_HGO_OFFSET,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_OFFSET,
(crop->left << VI6_HGO_OFFSET_HOFFSET_SHIFT) |
(crop->top << VI6_HGO_OFFSET_VOFFSET_SHIFT));
- vsp1_hgo_write(hgo, dl, VI6_HGO_SIZE,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_SIZE,
(crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGO_SIZE_VSIZE_SHIFT));
@@ -170,7 +162,7 @@ static void hgo_configure(struct vsp1_entity *entity,
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
- vsp1_hgo_write(hgo, dl, VI6_HGO_MODE,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_MODE,
(hgo->num_bins == 256 ? VI6_HGO_MODE_STEP : 0) |
(hgo->max_rgb ? VI6_HGO_MODE_MAXRGB : 0) |
(hratio << VI6_HGO_MODE_HRATIO_SHIFT) |
@@ -178,7 +170,7 @@ static void hgo_configure(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations hgo_entity_ops = {
- .configure = hgo_configure,
+ .configure_stream = hgo_configure_stream,
.destroy = vsp1_histogram_destroy,
};
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/vsp1/vsp1_hgo.h
index c6c0b7a80e0c..6b0c8580e1bf 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.h
+++ b/drivers/media/platform/vsp1/vsp1_hgo.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hgo.h -- R-Car VSP1 Histogram Generator 1D
*
* Copyright (C) 2016 Renesas Electronics 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_HGO_H__
#define __VSP1_HGO_H__
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/vsp1/vsp1_hgt.c
index b5ce305e3e6f..bb6ce6fdd5f4 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/vsp1/vsp1_hgt.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hgt.c -- R-Car VSP1 Histogram Generator 2D
*
* Copyright (C) 2016 Renesas Electronics Corporation
*
* Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se)
- *
- * 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>
@@ -32,10 +28,10 @@ static inline u32 vsp1_hgt_read(struct vsp1_hgt *hgt, u32 reg)
return vsp1_read(hgt->histo.entity.vsp1, reg);
}
-static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_hgt_write(struct vsp1_hgt *hgt,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -129,10 +125,9 @@ static const struct v4l2_ctrl_config hgt_hue_areas = {
* VSP1 Entity Operations
*/
-static void hgt_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hgt_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hgt *hgt = to_hgt(&entity->subdev);
struct v4l2_rect *compose;
@@ -143,21 +138,18 @@ static void hgt_configure(struct vsp1_entity *entity,
u8 upper;
unsigned int i;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
crop = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
compose = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK,
V4L2_SEL_TGT_COMPOSE);
- vsp1_hgt_write(hgt, dl, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
- vsp1_hgt_write(hgt, dl, VI6_HGT_OFFSET,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_OFFSET,
(crop->left << VI6_HGT_OFFSET_HOFFSET_SHIFT) |
(crop->top << VI6_HGT_OFFSET_VOFFSET_SHIFT));
- vsp1_hgt_write(hgt, dl, VI6_HGT_SIZE,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_SIZE,
(crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGT_SIZE_VSIZE_SHIFT));
@@ -165,7 +157,7 @@ static void hgt_configure(struct vsp1_entity *entity,
for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
lower = hgt->hue_areas[i*2 + 0];
upper = hgt->hue_areas[i*2 + 1];
- vsp1_hgt_write(hgt, dl, VI6_HGT_HUE_AREA(i),
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i),
(lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) |
(upper << VI6_HGT_HUE_AREA_UPPER_SHIFT));
}
@@ -173,13 +165,13 @@ static void hgt_configure(struct vsp1_entity *entity,
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
- vsp1_hgt_write(hgt, dl, VI6_HGT_MODE,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_MODE,
(hratio << VI6_HGT_MODE_HRATIO_SHIFT) |
(vratio << VI6_HGT_MODE_VRATIO_SHIFT));
}
static const struct vsp1_entity_operations hgt_entity_ops = {
- .configure = hgt_configure,
+ .configure_stream = hgt_configure_stream,
.destroy = vsp1_histogram_destroy,
};
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/vsp1/vsp1_hgt.h
index 83f2e130942a..38ec237bdd2d 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.h
+++ b/drivers/media/platform/vsp1/vsp1_hgt.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hgt.h -- R-Car VSP1 Histogram Generator 2D
*
* Copyright (C) 2016 Renesas Electronics Corporation
*
* Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se)
- *
- * 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_HGT_H__
#define __VSP1_HGT_H__
diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c
index afab77cf4fa5..5e15c8ff88d9 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/vsp1/vsp1_histo.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_histo.c -- R-Car VSP1 Histogram API
*
@@ -5,11 +6,6 @@
* Copyright (C) 2016 Laurent Pinchart
*
* 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>
@@ -61,7 +57,7 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
struct vsp1_histogram_buffer *buf,
size_t size)
{
- struct vsp1_pipeline *pipe = histo->pipe;
+ struct vsp1_pipeline *pipe = histo->entity.pipe;
unsigned long flags;
/*
@@ -393,65 +389,14 @@ static int histo_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_format *fmt)
{
struct vsp1_histogram *histo = subdev_to_histo(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *selection;
- unsigned int i;
- int ret = 0;
if (fmt->pad != HISTO_PAD_SINK)
return histo_get_format(subdev, cfg, fmt);
- mutex_lock(&histo->entity.lock);
-
- config = vsp1_entity_get_pad_config(&histo->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /*
- * Default to the first format if the requested format is not
- * supported.
- */
- for (i = 0; i < histo->num_formats; ++i) {
- if (fmt->format.code == histo->formats[i])
- break;
- }
- if (i == histo->num_formats)
- fmt->format.code = histo->formats[0];
-
- format = vsp1_entity_get_pad_format(&histo->entity, config, fmt->pad);
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- HISTO_MIN_SIZE, HISTO_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- HISTO_MIN_SIZE, HISTO_MAX_SIZE);
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt->format = *format;
-
- /* Reset the crop and compose rectangles */
- selection = vsp1_entity_get_pad_selection(&histo->entity, config,
- fmt->pad, V4L2_SEL_TGT_CROP);
- selection->left = 0;
- selection->top = 0;
- selection->width = format->width;
- selection->height = format->height;
-
- selection = vsp1_entity_get_pad_selection(&histo->entity, config,
- fmt->pad,
- V4L2_SEL_TGT_COMPOSE);
- selection->left = 0;
- selection->top = 0;
- selection->width = format->width;
- selection->height = format->height;
-
-done:
- mutex_unlock(&histo->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt,
+ histo->formats, histo->num_formats,
+ HISTO_MIN_SIZE, HISTO_MIN_SIZE,
+ HISTO_MAX_SIZE, HISTO_MAX_SIZE);
}
static const struct v4l2_subdev_pad_ops histo_pad_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/vsp1/vsp1_histo.h
index af2874f6031d..06f029846244 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.h
+++ b/drivers/media/platform/vsp1/vsp1_histo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_histo.h -- R-Car VSP1 Histogram API
*
@@ -5,11 +6,6 @@
* Copyright (C) 2016 Laurent Pinchart
*
* 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_HISTO_H__
#define __VSP1_HISTO_H__
@@ -25,7 +21,6 @@
#include "vsp1_entity.h"
struct vsp1_device;
-struct vsp1_pipeline;
#define HISTO_PAD_SINK 0
#define HISTO_PAD_SOURCE 1
@@ -37,8 +32,6 @@ struct vsp1_histogram_buffer {
};
struct vsp1_histogram {
- struct vsp1_pipeline *pipe;
-
struct vsp1_entity entity;
struct video_device video;
struct media_pad pad;
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
index 764d405345ee..39ab2e0c7c18 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* 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>
@@ -28,9 +24,9 @@
*/
static inline void vsp1_hsit_write(struct vsp1_hsit *hsit,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -131,24 +127,20 @@ static const struct v4l2_subdev_ops hsit_ops = {
* VSP1 Entity Operations
*/
-static void hsit_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hsit_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
if (hsit->inverse)
- vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
+ vsp1_hsit_write(hsit, dlb, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
- vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN);
+ vsp1_hsit_write(hsit, dlb, VI6_HST_CTRL, VI6_HST_CTRL_EN);
}
static const struct vsp1_entity_operations hsit_entity_ops = {
- .configure = hsit_configure,
+ .configure_stream = hsit_configure_stream,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h
index 82f1c8426900..a658b1aa49e7 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.h
+++ b/drivers/media/platform/vsp1/vsp1_hsit.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* 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_HSIT_H__
#define __VSP1_HSIT_H__
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index 704920753998..0cb63244b21a 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 Renesas Electronics 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>
@@ -27,27 +23,28 @@
* Device Access
*/
-static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_lif_write(struct vsp1_lif *lif,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + lif->entity.index * VI6_LIF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + lif->entity.index * VI6_LIF_OFFSET,
+ data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
+static const unsigned int lif_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
- ARRAY_SIZE(codes));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lif_codes,
+ ARRAY_SIZE(lif_codes));
}
static int lif_enum_frame_size(struct v4l2_subdev *subdev,
@@ -63,53 +60,10 @@ static int lif_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_lif *lif = to_lif(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
-
- mutex_lock(&lif->entity.lock);
-
- config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
- fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
-
- format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad);
-
- if (fmt->pad == LIF_PAD_SOURCE) {
- /*
- * The LIF source format is always identical to its sink
- * format.
- */
- fmt->format = *format;
- goto done;
- }
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- LIF_MIN_SIZE, LIF_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- LIF_MIN_SIZE, LIF_MAX_SIZE);
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt->format = *format;
-
- /* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&lif->entity, config,
- LIF_PAD_SOURCE);
- *format = fmt->format;
-
-done:
- mutex_unlock(&lif->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lif_codes,
+ ARRAY_SIZE(lif_codes),
+ LIF_MIN_SIZE, LIF_MIN_SIZE,
+ LIF_MAX_SIZE, LIF_MAX_SIZE);
}
static const struct v4l2_subdev_pad_ops lif_pad_ops = {
@@ -128,10 +82,9 @@ static const struct v4l2_subdev_ops lif_ops = {
* VSP1 Entity Operations
*/
-static void lif_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void lif_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(&entity->subdev);
@@ -139,19 +92,16 @@ static void lif_configure(struct vsp1_entity *entity,
unsigned int obth = 400;
unsigned int lbth = 200;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
LIF_PAD_SOURCE);
obth = min(obth, (format->width + 1) / 2 * format->height - 4);
- vsp1_lif_write(lif, dl, VI6_LIF_CSBTH,
+ vsp1_lif_write(lif, dlb, VI6_LIF_CSBTH,
(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
- vsp1_lif_write(lif, dl, VI6_LIF_CTRL,
+ vsp1_lif_write(lif, dlb, VI6_LIF_CTRL,
(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
@@ -164,13 +114,13 @@ static void lif_configure(struct vsp1_entity *entity,
*/
if ((entity->vsp1->version & VI6_IP_VERSION_MASK) ==
(VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M))
- vsp1_lif_write(lif, dl, VI6_LIF_LBA,
+ vsp1_lif_write(lif, dlb, VI6_LIF_LBA,
VI6_LIF_LBA_LBA0 |
(1536 << VI6_LIF_LBA_LBA1_SHIFT));
}
static const struct vsp1_entity_operations lif_entity_ops = {
- .configure = lif_configure,
+ .configure_stream = lif_configure_stream,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
index 3417339379b1..71a4eda9c2b2 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.h
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 Renesas Electronics 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_LIF_H__
#define __VSP1_LIF_H__
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
index c67cc60db0db..64c48d9459b0 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_lut.c -- R-Car VSP1 Look-Up Table
*
* 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>
@@ -23,14 +19,16 @@
#define LUT_MIN_SIZE 4U
#define LUT_MAX_SIZE 8190U
+#define LUT_SIZE 256
+
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_lut_write(struct vsp1_lut *lut,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -44,19 +42,19 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
struct vsp1_dl_body *dlb;
unsigned int i;
- dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256);
+ dlb = vsp1_dl_body_get(lut->pool);
if (!dlb)
return -ENOMEM;
- for (i = 0; i < 256; ++i)
- vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
+ for (i = 0; i < LUT_SIZE; ++i)
+ vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i,
ctrl->p_new.p_u32[i]);
spin_lock_irq(&lut->lock);
swap(lut->lut, dlb);
spin_unlock_irq(&lut->lock);
- vsp1_dl_fragment_free(dlb);
+ vsp1_dl_body_put(dlb);
return 0;
}
@@ -87,25 +85,25 @@ static const struct v4l2_ctrl_config lut_table_control = {
.max = 0x00ffffff,
.step = 1,
.def = 0,
- .dims = { 256},
+ .dims = { LUT_SIZE },
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
+static const unsigned int lut_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
- static const unsigned int codes[] = {
- MEDIA_BUS_FMT_ARGB8888_1X32,
- MEDIA_BUS_FMT_AHSV8888_1X32,
- MEDIA_BUS_FMT_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
- ARRAY_SIZE(codes));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lut_codes,
+ ARRAY_SIZE(lut_codes));
}
static int lut_enum_frame_size(struct v4l2_subdev *subdev,
@@ -121,51 +119,10 @@ static int lut_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_lut *lut = to_lut(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
-
- mutex_lock(&lut->entity.lock);
-
- config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
- fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
- fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
-
- format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad);
-
- if (fmt->pad == LUT_PAD_SOURCE) {
- /* The LUT output format can't be modified. */
- fmt->format = *format;
- goto done;
- }
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- LUT_MIN_SIZE, LUT_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- LUT_MIN_SIZE, LUT_MAX_SIZE);
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt->format = *format;
-
- /* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&lut->entity, config,
- LUT_PAD_SOURCE);
- *format = fmt->format;
-
-done:
- mutex_unlock(&lut->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes,
+ ARRAY_SIZE(lut_codes),
+ LUT_MIN_SIZE, LUT_MIN_SIZE,
+ LUT_MAX_SIZE, LUT_MAX_SIZE);
}
/* -----------------------------------------------------------------------------
@@ -188,37 +145,48 @@ static const struct v4l2_subdev_ops lut_ops = {
* VSP1 Entity Operations
*/
-static void lut_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void lut_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
- struct vsp1_dl_body *dlb;
- unsigned long flags;
- switch (params) {
- case VSP1_ENTITY_PARAMS_INIT:
- vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
- break;
+ vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
+}
- case VSP1_ENTITY_PARAMS_PARTITION:
- break;
+static void lut_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_lut *lut = to_lut(&entity->subdev);
+ struct vsp1_dl_body *lut_dlb;
+ unsigned long flags;
- case VSP1_ENTITY_PARAMS_RUNTIME:
- spin_lock_irqsave(&lut->lock, flags);
- dlb = lut->lut;
- lut->lut = NULL;
- spin_unlock_irqrestore(&lut->lock, flags);
+ spin_lock_irqsave(&lut->lock, flags);
+ lut_dlb = lut->lut;
+ lut->lut = NULL;
+ spin_unlock_irqrestore(&lut->lock, flags);
- if (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
- break;
+ if (lut_dlb) {
+ vsp1_dl_list_add_body(dl, lut_dlb);
+
+ /* Release our local reference. */
+ vsp1_dl_body_put(lut_dlb);
}
}
+static void lut_destroy(struct vsp1_entity *entity)
+{
+ struct vsp1_lut *lut = to_lut(&entity->subdev);
+
+ vsp1_dl_body_pool_destroy(lut->pool);
+}
+
static const struct vsp1_entity_operations lut_entity_ops = {
- .configure = lut_configure,
+ .configure_stream = lut_configure_stream,
+ .configure_frame = lut_configure_frame,
+ .destroy = lut_destroy,
};
/* -----------------------------------------------------------------------------
@@ -244,6 +212,15 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
if (ret < 0)
return ERR_PTR(ret);
+ /*
+ * Pre-allocate a body pool, with 3 bodies allowing a userspace update
+ * before the hardware has committed a previous set of tables, handling
+ * both the queued and pending dl entries.
+ */
+ lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0);
+ if (!lut->pool)
+ return ERR_PTR(-ENOMEM);
+
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&lut->ctrls, 1);
v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL);
diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h
index f8c4e8f0a79d..8cb0df1b7e27 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.h
+++ b/drivers/media/platform/vsp1/vsp1_lut.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_lut.h -- R-Car VSP1 Look-Up Table
*
* 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_LUT_H__
#define __VSP1_LUT_H__
@@ -33,6 +29,7 @@ struct vsp1_lut {
spinlock_t lock;
struct vsp1_dl_body *lut;
+ struct vsp1_dl_body_pool *pool;
};
static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index 44944ac86d9b..54ff539ffea0 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_pipe.c -- R-Car VSP1 Pipeline
*
* Copyright (C) 2013-2015 Renesas Electronics 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/delay.h>
@@ -20,7 +16,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_entity.h"
#include "vsp1_hgo.h"
@@ -185,44 +181,29 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
{
+ struct vsp1_entity *entity;
unsigned int i;
- if (pipe->bru) {
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
-
- for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
- bru->inputs[i].rpf = NULL;
- }
-
- for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
- if (pipe->inputs[i]) {
- pipe->inputs[i]->pipe = NULL;
- pipe->inputs[i] = NULL;
- }
- }
+ if (pipe->brx) {
+ struct vsp1_brx *brx = to_brx(&pipe->brx->subdev);
- if (pipe->output) {
- pipe->output->pipe = NULL;
- pipe->output = NULL;
+ for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i)
+ brx->inputs[i].rpf = NULL;
}
- if (pipe->hgo) {
- struct vsp1_hgo *hgo = to_hgo(&pipe->hgo->subdev);
-
- hgo->histo.pipe = NULL;
- }
+ for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
+ pipe->inputs[i] = NULL;
- if (pipe->hgt) {
- struct vsp1_hgt *hgt = to_hgt(&pipe->hgt->subdev);
+ pipe->output = NULL;
- hgt->histo.pipe = NULL;
- }
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ entity->pipe = NULL;
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
pipe->buffers_ready = 0;
pipe->num_inputs = 0;
- pipe->bru = NULL;
+ pipe->brx = NULL;
pipe->hgo = NULL;
pipe->hgt = NULL;
pipe->lif = NULL;
@@ -330,17 +311,17 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
- bool completed;
+ unsigned int flags;
if (pipe == NULL)
return;
/*
* If the DL commit raced with the frame end interrupt, the commit ends
- * up being postponed by one frame. @completed represents whether the
+ * up being postponed by one frame. The returned flags tell whether the
* active frame was finished or postponed.
*/
- completed = vsp1_dlm_irq_frame_end(pipe->output->dlm);
+ flags = vsp1_dlm_irq_frame_end(pipe->output->dlm);
if (pipe->hgo)
vsp1_hgo_frame_end(pipe->hgo);
@@ -353,7 +334,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
* frame_end to account for vblank events.
*/
if (pipe->frame_end)
- pipe->frame_end(pipe, completed);
+ pipe->frame_end(pipe, flags);
pipe->sequence++;
}
@@ -367,7 +348,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
* from the input RPF alpha.
*/
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, unsigned int alpha)
+ struct vsp1_dl_body *dlb, unsigned int alpha)
{
if (!pipe->uds)
return;
@@ -380,7 +361,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
pipe->uds_input->type == VSP1_ENTITY_BRS)
alpha = 255;
- vsp1_uds_set_alpha(pipe->uds, dl, alpha);
+ vsp1_uds_set_alpha(pipe->uds, dlb, alpha);
}
/*
@@ -405,73 +386,3 @@ void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
}
}
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
-{
- unsigned long flags;
- unsigned int i;
- int ret;
-
- /*
- * To avoid increasing the system suspend time needlessly, loop over the
- * pipelines twice, first to set them all to the stopping state, and
- * then to wait for the stop to complete.
- */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
- }
-
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
- msecs_to_jiffies(500));
- if (ret == 0)
- dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
- wpf->entity.index);
- }
-}
-
-void vsp1_pipelines_resume(struct vsp1_device *vsp1)
-{
- unsigned long flags;
- unsigned int i;
-
- /* Resume all running pipelines. */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (vsp1_pipeline_ready(pipe))
- vsp1_pipeline_run(pipe);
- spin_unlock_irqrestore(&pipe->irqlock, flags);
- }
-}
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
index dfff9b5685fe..743d8f0db45c 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_pipe.h -- R-Car VSP1 Pipeline
*
* Copyright (C) 2013-2015 Renesas Electronics 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_PIPE_H__
#define __VSP1_PIPE_H__
@@ -99,14 +95,15 @@ struct vsp1_partition {
* @num_inputs: number of RPFs
* @inputs: array of RPFs in the pipeline (indexed by RPF index)
* @output: WPF at the output of the pipeline
- * @bru: BRU entity, if present
+ * @brx: BRx entity, if present
* @hgo: HGO entity, if present
* @hgt: HGT entity, if present
* @lif: LIF entity, if present
* @uds: UDS entity, if present
* @uds_input: entity at the input of the UDS, if the UDS is present
* @entities: list of entities in the pipeline
- * @dl: display list associated with the pipeline
+ * @stream_config: cached stream configuration for video pipelines
+ * @configured: when false the @stream_config shall be written to the hardware
* @partitions: The number of partitions used to process one frame
* @partition: The current partition for configuration to process
* @part_table: The pre-calculated partitions used by the pipeline
@@ -118,7 +115,7 @@ struct vsp1_pipeline {
enum vsp1_pipeline_state state;
wait_queue_head_t wq;
- void (*frame_end)(struct vsp1_pipeline *pipe, bool completed);
+ void (*frame_end)(struct vsp1_pipeline *pipe, unsigned int completion);
struct mutex lock;
struct kref kref;
@@ -129,7 +126,7 @@ struct vsp1_pipeline {
unsigned int num_inputs;
struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
struct vsp1_rwpf *output;
- struct vsp1_entity *bru;
+ struct vsp1_entity *brx;
struct vsp1_entity *hgo;
struct vsp1_entity *hgt;
struct vsp1_entity *lif;
@@ -143,7 +140,8 @@ struct vsp1_pipeline {
*/
struct list_head entities;
- struct vsp1_dl_list *dl;
+ struct vsp1_dl_body *stream_config;
+ bool configured;
unsigned int partitions;
struct vsp1_partition *partition;
@@ -161,16 +159,14 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, unsigned int alpha);
+ struct vsp1_dl_body *dlb,
+ unsigned int alpha);
void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int index,
struct vsp1_partition_window *window);
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
-void vsp1_pipelines_resume(struct vsp1_device *vsp1);
-
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
u32 fourcc);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index dae0c1901297..0d249ff9f564 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
* Copyright (C) 2013 Renesas Electronics 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 version 2
- * as published by the Free Software Foundation.
*/
#ifndef __VSP1_REGS_H__
@@ -311,6 +308,44 @@
#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
/* -----------------------------------------------------------------------------
+ * UIF Control Registers
+ */
+
+#define VI6_UIF_OFFSET 0x100
+
+#define VI6_UIF_DISCOM_DOCMCR 0x1c00
+#define VI6_UIF_DISCOM_DOCMCR_CMPRU (1 << 16)
+#define VI6_UIF_DISCOM_DOCMCR_CMPR (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMSTR 0x1c04
+#define VI6_UIF_DISCOM_DOCMSTR_CMPPRE (1 << 1)
+#define VI6_UIF_DISCOM_DOCMSTR_CMPST (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMCLSTR 0x1c08
+#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLPRE (1 << 1)
+#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLST (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMIENR 0x1c0c
+#define VI6_UIF_DISCOM_DOCMIENR_CMPPREIEN (1 << 1)
+#define VI6_UIF_DISCOM_DOCMIENR_CMPIEN (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMMDR 0x1c10
+#define VI6_UIF_DISCOM_DOCMMDR_INTHRH(n) ((n) << 16)
+
+#define VI6_UIF_DISCOM_DOCMPMR 0x1c14
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDFF(n) ((n) << 17)
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDFA(n) ((n) << 8)
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDAUF (1 << 7)
+#define VI6_UIF_DISCOM_DOCMPMR_SEL(n) ((n) << 0)
+
+#define VI6_UIF_DISCOM_DOCMECRCR 0x1c18
+#define VI6_UIF_DISCOM_DOCMCCRCR 0x1c1c
+#define VI6_UIF_DISCOM_DOCMSPXR 0x1c20
+#define VI6_UIF_DISCOM_DOCMSPYR 0x1c24
+#define VI6_UIF_DISCOM_DOCMSZXR 0x1c28
+#define VI6_UIF_DISCOM_DOCMSZYR 0x1c2c
+
+/* -----------------------------------------------------------------------------
* DPR Control Registers
*/
@@ -342,7 +377,10 @@
#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
#define VI6_DPR_SMPPT_PT_SHIFT 0
+#define VI6_DPR_UIF_ROUTE(n) (0x2074 + (n) * 4)
+
#define VI6_DPR_NODE_RPF(n) (n)
+#define VI6_DPR_NODE_UIF(n) (12 + (n))
#define VI6_DPR_NODE_SRU 16
#define VI6_DPR_NODE_UDS(n) (17 + (n))
#define VI6_DPR_NODE_LUT 22
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index fe0633da5a5f..69e5fe6e6b50 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
*
* Copyright (C) 2013-2014 Renesas Electronics 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>
@@ -29,9 +25,10 @@
*/
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET,
+ data);
}
/* -----------------------------------------------------------------------------
@@ -46,10 +43,9 @@ static const struct v4l2_subdev_ops rpf_ops = {
* VSP1 Entity Operations
*/
-static void rpf_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void rpf_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
@@ -61,80 +57,6 @@ static void rpf_configure(struct vsp1_entity *entity,
u32 pstride;
u32 infmt;
- if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
- vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
- rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
- vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
- (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
-
- vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha);
- return;
- }
-
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- struct vsp1_device *vsp1 = rpf->entity.vsp1;
- struct vsp1_rwpf_memory mem = rpf->mem;
- struct v4l2_rect crop;
-
- /*
- * Source size and crop offsets.
- *
- * The crop offsets correspond to the location of the crop
- * rectangle top left corner in the plane buffer. Only two
- * offsets are needed, as planes 2 and 3 always have identical
- * strides.
- */
- crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config);
-
- /*
- * Partition Algorithm Control
- *
- * The partition algorithm can split this frame into multiple
- * slices. We must scale our partition window based on the pipe
- * configuration to match the destination partition window.
- * To achieve this, we adjust our crop to provide a 'sub-crop'
- * matching the expected partition window. Only 'left' and
- * 'width' need to be adjusted.
- */
- if (pipe->partitions > 1) {
- crop.width = pipe->partition->rpf.width;
- crop.left += pipe->partition->rpf.left;
- }
-
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
- (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
- (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
-
- mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
- + crop.left * fmtinfo->bpp[0] / 8;
-
- if (format->num_planes > 1) {
- unsigned int offset;
-
- offset = crop.top * format->plane_fmt[1].bytesperline
- + crop.left / fmtinfo->hsub
- * fmtinfo->bpp[1] / 8;
- mem.addr[1] += offset;
- mem.addr[2] += offset;
- }
-
- /*
- * On Gen3 hardware the SPUVS bit has no effect on 3-planar
- * formats. Swap the U and V planes manually in that case.
- */
- if (vsp1->info->gen == 3 && format->num_planes == 3 &&
- fmtinfo->swap_uv)
- swap(mem.addr[1], mem.addr[2]);
-
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
- return;
- }
-
/* Stride */
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
@@ -142,7 +64,7 @@ static void rpf_configure(struct vsp1_entity *entity,
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
@@ -163,22 +85,22 @@ static void rpf_configure(struct vsp1_entity *entity,
if (sink_format->code != source_format->code)
infmt |= VI6_RPF_INFMT_CSC;
- vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt);
- vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);
/* Output location */
- if (pipe->bru) {
+ if (pipe->brx) {
const struct v4l2_rect *compose;
- compose = vsp1_entity_get_pad_selection(pipe->bru,
- pipe->bru->config,
- rpf->bru_input,
+ compose = vsp1_entity_get_pad_selection(pipe->brx,
+ pipe->brx->config,
+ rpf->brx_input,
V4L2_SEL_TGT_COMPOSE);
left = compose->left;
top = compose->top;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
(left << VI6_RPF_LOC_HCOORD_SHIFT) |
(top << VI6_RPF_LOC_VCOORD_SHIFT));
@@ -191,10 +113,10 @@ static void rpf_configure(struct vsp1_entity *entity,
* alpha channel by a fixed global alpha value, and multiply the pixel
* components to convert the input to premultiplied alpha.
*
- * As alpha premultiplication is available in the BRU for both Gen2 and
+ * As alpha premultiplication is available in the BRx for both Gen2 and
* Gen3 we handle it there and use the Gen3 alpha multiplier for global
* alpha multiplication only. This however prevents conversion to
- * premultiplied alpha if no BRU is present in the pipeline. If that use
+ * premultiplied alpha if no BRx is present in the pipeline. If that use
* case turns out to be useful we will revisit the implementation (for
* Gen3 only).
*
@@ -205,7 +127,7 @@ static void rpf_configure(struct vsp1_entity *entity,
*
* In all cases, disable color keying.
*/
- vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
@@ -242,9 +164,94 @@ static void rpf_configure(struct vsp1_entity *entity,
rpf->mult_alpha = mult;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0);
- vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0);
+
+}
+
+static void rpf_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET,
+ rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
+ (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
+
+ vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha);
+}
+
+static void rpf_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+ struct vsp1_rwpf_memory mem = rpf->mem;
+ struct vsp1_device *vsp1 = rpf->entity.vsp1;
+ const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+ const struct v4l2_pix_format_mplane *format = &rpf->format;
+ struct v4l2_rect crop;
+
+ /*
+ * Source size and crop offsets.
+ *
+ * The crop offsets correspond to the location of the crop
+ * rectangle top left corner in the plane buffer. Only two
+ * offsets are needed, as planes 2 and 3 always have identical
+ * strides.
+ */
+ crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+
+ /*
+ * Partition Algorithm Control
+ *
+ * The partition algorithm can split this frame into multiple
+ * slices. We must scale our partition window based on the pipe
+ * configuration to match the destination partition window.
+ * To achieve this, we adjust our crop to provide a 'sub-crop'
+ * matching the expected partition window. Only 'left' and
+ * 'width' need to be adjusted.
+ */
+ if (pipe->partitions > 1) {
+ crop.width = pipe->partition->rpf.width;
+ crop.left += pipe->partition->rpf.left;
+ }
+
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,
+ (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,
+ (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
+ + crop.left * fmtinfo->bpp[0] / 8;
+
+ if (format->num_planes > 1) {
+ unsigned int offset;
+
+ offset = crop.top * format->plane_fmt[1].bytesperline
+ + crop.left / fmtinfo->hsub
+ * fmtinfo->bpp[1] / 8;
+ mem.addr[1] += offset;
+ mem.addr[2] += offset;
+ }
+
+ /*
+ * On Gen3 hardware the SPUVS bit has no effect on 3-planar
+ * formats. Swap the U and V planes manually in that case.
+ */
+ if (vsp1->info->gen == 3 && format->num_planes == 3 &&
+ fmtinfo->swap_uv)
+ swap(mem.addr[1], mem.addr[2]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
}
static void rpf_partition(struct vsp1_entity *entity,
@@ -257,7 +264,9 @@ static void rpf_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations rpf_entity_ops = {
- .configure = rpf_configure,
+ .configure_stream = rpf_configure_stream,
+ .configure_frame = rpf_configure_frame,
+ .configure_partition = rpf_configure_partition,
.partition = rpf_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index cfd8f1904fa6..049bdd958e56 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 Renesas Electronics 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 <media/v4l2-subdev.h>
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 58215a7ab631..70742ecf766f 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 Renesas Electronics 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_RWPF_H__
#define __VSP1_RWPF_H__
@@ -27,7 +23,6 @@
struct v4l2_ctrl;
struct vsp1_dl_manager;
-struct vsp1_pipeline;
struct vsp1_rwpf;
struct vsp1_video;
@@ -39,7 +34,6 @@ struct vsp1_rwpf {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
- struct vsp1_pipeline *pipe;
struct vsp1_video *video;
unsigned int max_width;
@@ -47,7 +41,7 @@ struct vsp1_rwpf {
struct v4l2_pix_format_mplane format;
const struct vsp1_format_info *fmtinfo;
- unsigned int bru_input;
+ unsigned int brx_input;
unsigned int alpha;
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index 51e5691187c3..04e4e05af6ae 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_sru.c -- R-Car VSP1 Super Resolution 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>
@@ -28,10 +24,10 @@
* Device Access
*/
-static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_sru_write(struct vsp1_sru *sru,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -271,10 +267,9 @@ static const struct v4l2_subdev_ops sru_ops = {
* VSP1 Entity Operations
*/
-static void sru_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void sru_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
const struct vsp1_sru_param *param;
struct vsp1_sru *sru = to_sru(&entity->subdev);
@@ -282,9 +277,6 @@ static void sru_configure(struct vsp1_entity *entity,
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
SRU_PAD_SINK);
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
@@ -303,9 +295,9 @@ static void sru_configure(struct vsp1_entity *entity,
ctrl0 |= param->ctrl0;
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0);
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
}
static unsigned int sru_max_width(struct vsp1_entity *entity,
@@ -351,7 +343,7 @@ static void sru_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations sru_entity_ops = {
- .configure = sru_configure,
+ .configure_stream = sru_configure_stream,
.max_width = sru_max_width,
.partition = sru_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h
index 85e241457af2..ddb00eadd1ea 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.h
+++ b/drivers/media/platform/vsp1/vsp1_sru.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_sru.h -- R-Car VSP1 Super Resolution 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_SRU_H__
#define __VSP1_SRU_H__
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index 72f72a9d2152..c20c84b54936 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 Renesas Electronics 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>
@@ -31,22 +27,22 @@
* Device Access
*/
-static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_uds_write(struct vsp1_uds *uds,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + uds->entity.index * VI6_UDS_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Scaling Computation
*/
-void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_body *dlb,
unsigned int alpha)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
- vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL,
+ vsp1_uds_write(uds, dlb, VI6_UDS_ALPVAL,
alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
}
@@ -259,10 +255,9 @@ static const struct v4l2_subdev_ops uds_ops = {
* VSP1 Entity Operations
*/
-static void uds_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void uds_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
@@ -276,27 +271,6 @@ static void uds_configure(struct vsp1_entity *entity,
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
UDS_PAD_SOURCE);
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- struct vsp1_partition *partition = pipe->partition;
-
- /* Input size clipping */
- vsp1_uds_write(uds, dl, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
- (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) |
- (partition->uds_sink.width
- << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT));
-
- /* Output size clipping */
- vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE,
- (partition->uds_source.width
- << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
- (output->height
- << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
- return;
- }
-
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
@@ -312,22 +286,48 @@ static void uds_configure(struct vsp1_entity *entity,
else
multitap = true;
- vsp1_uds_write(uds, dl, VI6_UDS_CTRL,
+ vsp1_uds_write(uds, dlb, VI6_UDS_CTRL,
(uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
(multitap ? VI6_UDS_CTRL_BC : 0));
- vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH,
+ vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios. */
- vsp1_uds_write(uds, dl, VI6_UDS_SCALE,
+ vsp1_uds_write(uds, dlb, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
}
+static void uds_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_uds *uds = to_uds(&entity->subdev);
+ struct vsp1_partition *partition = pipe->partition;
+ const struct v4l2_mbus_framefmt *output;
+
+ output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
+ UDS_PAD_SOURCE);
+
+ /* Input size clipping */
+ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
+ (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) |
+ (partition->uds_sink.width
+ << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT));
+
+ /* Output size clipping */
+ vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE,
+ (partition->uds_source.width
+ << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (output->height
+ << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+}
+
static unsigned int uds_max_width(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe)
{
@@ -384,7 +384,8 @@ static void uds_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations uds_entity_ops = {
- .configure = uds_configure,
+ .configure_stream = uds_configure_stream,
+ .configure_partition = uds_configure_partition,
.max_width = uds_max_width,
.partition = uds_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
index 7bf3cdcffc65..c34f95a666d2 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.h
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 Renesas Electronics 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_UDS_H__
#define __VSP1_UDS_H__
@@ -35,7 +31,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
-void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_body *dlb,
unsigned int alpha);
#endif /* __VSP1_UDS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c
new file mode 100644
index 000000000000..4b58d51df231
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uif.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_uif.c -- R-Car VSP1 User Logic Interface
+ *
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/sys_soc.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_dl.h"
+#include "vsp1_entity.h"
+#include "vsp1_uif.h"
+
+#define UIF_MIN_SIZE 4U
+#define UIF_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_uif_read(struct vsp1_uif *uif, u32 reg)
+{
+ return vsp1_read(uif->entity.vsp1,
+ uif->entity.index * VI6_UIF_OFFSET + reg);
+}
+
+static inline void vsp1_uif_write(struct vsp1_uif *uif,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
+{
+ vsp1_dl_body_write(dlb, reg + uif->entity.index * VI6_UIF_OFFSET, data);
+}
+
+u32 vsp1_uif_get_crc(struct vsp1_uif *uif)
+{
+ return vsp1_uif_read(uif, VI6_UIF_DISCOM_DOCMCCRCR);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static const unsigned int uif_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
+static int uif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, uif_codes,
+ ARRAY_SIZE(uif_codes));
+}
+
+static int uif_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return vsp1_subdev_enum_frame_size(subdev, cfg, fse, UIF_MIN_SIZE,
+ UIF_MIN_SIZE, UIF_MAX_SIZE,
+ UIF_MAX_SIZE);
+}
+
+static int uif_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, uif_codes,
+ ARRAY_SIZE(uif_codes),
+ UIF_MIN_SIZE, UIF_MIN_SIZE,
+ UIF_MAX_SIZE, UIF_MAX_SIZE);
+}
+
+static int uif_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_uif *uif = to_uif(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ if (sel->pad != UIF_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&uif->entity.lock);
+
+ config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ format = vsp1_entity_get_pad_format(&uif->entity, config,
+ UIF_PAD_SINK);
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = format->width;
+ sel->r.height = format->height;
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config,
+ sel->pad, sel->target);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+done:
+ mutex_unlock(&uif->entity.lock);
+ return ret;
+}
+
+static int uif_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_uif *uif = to_uif(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *selection;
+ int ret = 0;
+
+ if (sel->pad != UIF_PAD_SINK ||
+ sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ mutex_lock(&uif->entity.lock);
+
+ config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* The crop rectangle must be inside the input frame. */
+ format = vsp1_entity_get_pad_format(&uif->entity, config, UIF_PAD_SINK);
+
+ 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);
+ sel->r.width = clamp_t(unsigned int, sel->r.width, UIF_MIN_SIZE,
+ format->width - sel->r.left);
+ sel->r.height = clamp_t(unsigned int, sel->r.height, UIF_MIN_SIZE,
+ format->height - sel->r.top);
+
+ /* Store the crop rectangle. */
+ selection = vsp1_entity_get_pad_selection(&uif->entity, config,
+ sel->pad, V4L2_SEL_TGT_CROP);
+ *selection = sel->r;
+
+done:
+ mutex_unlock(&uif->entity.lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static const struct v4l2_subdev_pad_ops uif_pad_ops = {
+ .init_cfg = vsp1_entity_init_cfg,
+ .enum_mbus_code = uif_enum_mbus_code,
+ .enum_frame_size = uif_enum_frame_size,
+ .get_fmt = vsp1_subdev_get_pad_format,
+ .set_fmt = uif_set_format,
+ .get_selection = uif_get_selection,
+ .set_selection = uif_set_selection,
+};
+
+static const struct v4l2_subdev_ops uif_ops = {
+ .pad = &uif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void uif_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_uif *uif = to_uif(&entity->subdev);
+ const struct v4l2_rect *crop;
+ unsigned int left;
+ unsigned int width;
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR,
+ VI6_UIF_DISCOM_DOCMPMR_SEL(9));
+
+ crop = vsp1_entity_get_pad_selection(entity, entity->config,
+ UIF_PAD_SINK, V4L2_SEL_TGT_CROP);
+
+ left = crop->left;
+ width = crop->width;
+
+ /* On M3-W the horizontal coordinates are twice the register value. */
+ if (uif->m3w_quirk) {
+ left /= 2;
+ width /= 2;
+ }
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPXR, left);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPYR, crop->top);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZXR, width);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZYR, crop->height);
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMCR,
+ VI6_UIF_DISCOM_DOCMCR_CMPR);
+}
+
+static const struct vsp1_entity_operations uif_entity_ops = {
+ .configure_stream = uif_configure_stream,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+static const struct soc_device_attribute vsp1_r8a7796[] = {
+ { .soc_id = "r8a7796" },
+ { /* sentinel */ }
+};
+
+struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct vsp1_uif *uif;
+ char name[6];
+ int ret;
+
+ uif = devm_kzalloc(vsp1->dev, sizeof(*uif), GFP_KERNEL);
+ if (!uif)
+ return ERR_PTR(-ENOMEM);
+
+ if (soc_device_match(vsp1_r8a7796))
+ uif->m3w_quirk = true;
+
+ uif->entity.ops = &uif_entity_ops;
+ uif->entity.type = VSP1_ENTITY_UIF;
+ uif->entity.index = index;
+
+ /* The datasheet names the two UIF instances UIF4 and UIF5. */
+ sprintf(name, "uif.%u", index + 4);
+ ret = vsp1_entity_init(vsp1, &uif->entity, name, 2, &uif_ops,
+ MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return uif;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/vsp1/vsp1_uif.h
new file mode 100644
index 000000000000..c71ab5f6a6f8
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uif.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_uif.h -- R-Car VSP1 User Logic Interface
+ *
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+#ifndef __VSP1_UIF_H__
+#define __VSP1_UIF_H__
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define UIF_PAD_SINK 0
+#define UIF_PAD_SOURCE 1
+
+struct vsp1_uif {
+ struct vsp1_entity entity;
+ bool m3w_quirk;
+};
+
+static inline struct vsp1_uif *to_uif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_uif, entity.subdev);
+}
+
+struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index);
+u32 vsp1_uif_get_crc(struct vsp1_uif *uif);
+
+#endif /* __VSP1_UIF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index c2d3b8f0f487..81d47a09d7bc 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_video.c -- R-Car VSP1 Video Node
*
* Copyright (C) 2013-2015 Renesas Electronics 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/list.h>
@@ -28,7 +24,7 @@
#include <media/videobuf2-dma-contig.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_entity.h"
#include "vsp1_hgo.h"
@@ -324,7 +320,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
static struct vsp1_vb2_buffer *
vsp1_video_complete_buffer(struct vsp1_video *video)
{
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *next = NULL;
struct vsp1_vb2_buffer *done;
unsigned long flags;
@@ -382,69 +378,71 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
unsigned int partition)
{
+ struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
struct vsp1_entity *entity;
pipe->partition = &pipe->part_table[partition];
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ vsp1_entity_configure_partition(entity, pipe, dl, dlb);
}
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
struct vsp1_entity *entity;
+ struct vsp1_dl_body *dlb;
+ struct vsp1_dl_list *dl;
unsigned int partition;
- if (!pipe->dl)
- pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
+ dl = vsp1_dl_list_get(pipe->output->dlm);
/*
- * Start with the runtime parameters as the configure operation can
- * compute/cache information needed when configuring partitions. This
- * is the case with flipping in the WPF.
+ * If the VSP hardware isn't configured yet (which occurs either when
+ * processing the first frame or after a system suspend/resume), add the
+ * cached stream configuration to the display list to perform a full
+ * initialisation.
*/
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- }
+ if (!pipe->configured)
+ vsp1_dl_list_add_body(dl, pipe->stream_config);
+
+ dlb = vsp1_dl_list_get_body0(dl);
- /* Run the first partition */
- vsp1_video_pipeline_run_partition(pipe, pipe->dl, 0);
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ vsp1_entity_configure_frame(entity, pipe, dl, dlb);
- /* Process consecutive partitions as necessary */
+ /* Run the first partition. */
+ vsp1_video_pipeline_run_partition(pipe, dl, 0);
+
+ /* Process consecutive partitions as necessary. */
for (partition = 1; partition < pipe->partitions; ++partition) {
- struct vsp1_dl_list *dl;
+ struct vsp1_dl_list *dl_next;
- dl = vsp1_dl_list_get(pipe->output->dlm);
+ dl_next = vsp1_dl_list_get(pipe->output->dlm);
/*
* An incomplete chain will still function, but output only
* the partitions that had a dl available. The frame end
* interrupt will be marked on the last dl in the chain.
*/
- if (!dl) {
+ if (!dl_next) {
dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n");
break;
}
- vsp1_video_pipeline_run_partition(pipe, dl, partition);
- vsp1_dl_list_add_chain(pipe->dl, dl);
+ vsp1_video_pipeline_run_partition(pipe, dl_next, partition);
+ vsp1_dl_list_add_chain(dl, dl_next);
}
/* Complete, and commit the head display list. */
- vsp1_dl_list_commit(pipe->dl);
- pipe->dl = NULL;
+ vsp1_dl_list_commit(dl, false);
+ pipe->configured = true;
vsp1_pipeline_run(pipe);
}
static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
- bool completed)
+ unsigned int completion)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
enum vsp1_pipeline_state state;
@@ -452,7 +450,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
unsigned int i;
/* M2M Pipelines should never call here with an incomplete frame. */
- WARN_ON_ONCE(!completed);
+ WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED));
spin_lock_irqsave(&pipe->irqlock, flags);
@@ -488,7 +486,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;
- struct vsp1_bru *bru = NULL;
+ struct vsp1_brx *brx = NULL;
int ret;
ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev);
@@ -524,14 +522,14 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
if (entity->type == VSP1_ENTITY_BRU ||
entity->type == VSP1_ENTITY_BRS) {
/* BRU and BRS can't be chained. */
- if (bru) {
+ if (brx) {
ret = -EPIPE;
goto out;
}
- bru = to_bru(&entity->subdev);
- bru->inputs[pad->index].rpf = input;
- input->bru_input = pad->index;
+ brx = to_brx(&entity->subdev);
+ brx->inputs[pad->index].rpf = input;
+ input->brx_input = pad->index;
}
/* We've reached the WPF, we're done. */
@@ -553,7 +551,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
}
pipe->uds = entity;
- pipe->uds_input = bru ? &bru->entity : &input->entity;
+ pipe->uds_input = brx ? &brx->entity : &input->entity;
}
/* Follow the source link, ignoring any HGO or HGT. */
@@ -598,20 +596,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
subdev = media_entity_to_v4l2_subdev(entity);
e = to_vsp1_entity(subdev);
list_add_tail(&e->list_pipe, &pipe->entities);
+ e->pipe = pipe;
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;
break;
case VSP1_ENTITY_WPF:
rwpf = to_rwpf(subdev);
pipe->output = rwpf;
rwpf->video->pipe_index = 0;
- rwpf->pipe = pipe;
break;
case VSP1_ENTITY_LIF:
@@ -620,17 +617,15 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
case VSP1_ENTITY_BRU:
case VSP1_ENTITY_BRS:
- pipe->bru = e;
+ pipe->brx = e;
break;
case VSP1_ENTITY_HGO:
pipe->hgo = e;
- to_hgo(subdev)->histo.pipe = pipe;
break;
case VSP1_ENTITY_HGT:
pipe->hgt = e;
- to_hgt(subdev)->histo.pipe = pipe;
break;
default:
@@ -682,7 +677,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
* Otherwise allocate a new pipeline and initialize it, it will be freed
* when the last reference is released.
*/
- if (!video->rwpf->pipe) {
+ if (!video->rwpf->entity.pipe) {
pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
if (!pipe)
return ERR_PTR(-ENOMEM);
@@ -694,7 +689,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
return ERR_PTR(ret);
}
} else {
- pipe = video->rwpf->pipe;
+ pipe = video->rwpf->entity.pipe;
kref_get(&pipe->kref);
}
@@ -777,7 +772,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
unsigned long flags;
bool empty;
@@ -812,11 +807,6 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
if (ret < 0)
return ret;
- /* Prepare the display list. */
- pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
- if (!pipe->dl)
- return -ENOMEM;
-
if (pipe->uds) {
struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
@@ -838,20 +828,25 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
}
}
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- vsp1_entity_route_setup(entity, pipe, pipe->dl);
+ /*
+ * Compute and cache the stream configuration into a body. The cached
+ * body will be added to the display list by vsp1_video_pipeline_run()
+ * whenever the pipeline needs to be fully reconfigured.
+ */
+ pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm);
+ if (!pipe->stream_config)
+ return -ENOMEM;
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl,
- VSP1_ENTITY_PARAMS_INIT);
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
+ vsp1_entity_configure_stream(entity, pipe, pipe->stream_config);
}
return 0;
}
-static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
+static void vsp1_video_release_buffers(struct vsp1_video *video)
{
- struct vsp1_video *video = pipe->output->video;
struct vsp1_vb2_buffer *buffer;
unsigned long flags;
@@ -861,18 +856,26 @@ static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&video->irqqueue);
spin_unlock_irqrestore(&video->irqlock, flags);
+}
+
+static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
+{
+ lockdep_assert_held(&pipe->lock);
+
+ /* Release any cached configuration from our output video. */
+ vsp1_dl_body_put(pipe->stream_config);
+ pipe->stream_config = NULL;
+ pipe->configured = false;
/* Release our partition table allocation */
- mutex_lock(&pipe->lock);
kfree(pipe->part_table);
pipe->part_table = NULL;
- mutex_unlock(&pipe->lock);
}
static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
bool start_pipeline = false;
unsigned long flags;
int ret;
@@ -881,8 +884,9 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
if (pipe->stream_count == pipe->num_inputs) {
ret = vsp1_video_setup_pipeline(pipe);
if (ret < 0) {
- mutex_unlock(&pipe->lock);
+ vsp1_video_release_buffers(video);
vsp1_video_cleanup_pipeline(pipe);
+ mutex_unlock(&pipe->lock);
return ret;
}
@@ -913,7 +917,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
static void vsp1_video_stop_streaming(struct vb2_queue *vq)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
unsigned long flags;
int ret;
@@ -932,13 +936,12 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
if (ret == -ETIMEDOUT)
dev_err(video->vsp1->dev, "pipeline stop timeout\n");
- vsp1_dl_list_put(pipe->dl);
- pipe->dl = NULL;
+ vsp1_video_cleanup_pipeline(pipe);
}
mutex_unlock(&pipe->lock);
media_pipeline_stop(&video->video.entity);
- vsp1_video_cleanup_pipeline(pipe);
+ vsp1_video_release_buffers(video);
vsp1_video_pipeline_put(pipe);
}
@@ -1173,6 +1176,87 @@ static const struct v4l2_file_operations vsp1_video_fops = {
};
/* -----------------------------------------------------------------------------
+ * Suspend and Resume
+ */
+
+void vsp1_video_suspend(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ /*
+ * To avoid increasing the system suspend time needlessly, loop over the
+ * pipelines twice, first to set them all to the stopping state, and
+ * then to wait for the stop to complete.
+ */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
+ wpf->entity.index);
+ }
+}
+
+void vsp1_video_resume(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ /* Resume all running pipelines. */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ /*
+ * The hardware may have been reset during a suspend and will
+ * need a full reconfiguration.
+ */
+ pipe->configured = false;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_video_pipeline_run(pipe);
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+}
+
+/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index 50ea7f02205f..f3cf5e2fdf5a 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_video.h -- R-Car VSP1 Video Node
*
* Copyright (C) 2013-2015 Renesas Electronics 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_VIDEO_H__
#define __VSP1_VIDEO_H__
@@ -55,6 +51,9 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
return container_of(vdev, struct vsp1_video, video);
}
+void vsp1_video_suspend(struct vsp1_device *vsp1);
+void vsp1_video_resume(struct vsp1_device *vsp1);
+
struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
struct vsp1_rwpf *rwpf);
void vsp1_video_cleanup(struct vsp1_video *video);
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 8bd6b2f1af15..23c8f706b3f2 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
*
* Copyright (C) 2013-2014 Renesas Electronics 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>
@@ -31,9 +27,9 @@
*/
static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
@@ -236,10 +232,9 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)
vsp1_dlm_destroy(wpf->dlm);
}
-static void wpf_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void wpf_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
@@ -249,149 +244,12 @@ static void wpf_configure(struct vsp1_entity *entity,
u32 outfmt = 0;
u32 srcrpf = 0;
- if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
- const unsigned int mask = BIT(WPF_CTRL_VFLIP)
- | BIT(WPF_CTRL_HFLIP);
- unsigned long flags;
-
- spin_lock_irqsave(&wpf->flip.lock, flags);
- wpf->flip.active = (wpf->flip.active & ~mask)
- | (wpf->flip.pending & mask);
- spin_unlock_irqrestore(&wpf->flip.lock, flags);
-
- outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
-
- if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
- outfmt |= VI6_WPF_OUTFMT_FLP;
- if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
- outfmt |= VI6_WPF_OUTFMT_HFLP;
-
- vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt);
- return;
- }
-
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
wpf->entity.config,
RWPF_PAD_SINK);
source_format = vsp1_entity_get_pad_format(&wpf->entity,
wpf->entity.config,
RWPF_PAD_SOURCE);
-
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- const struct v4l2_pix_format_mplane *format = &wpf->format;
- const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
- struct vsp1_rwpf_memory mem = wpf->mem;
- unsigned int flip = wpf->flip.active;
- unsigned int width = sink_format->width;
- unsigned int height = sink_format->height;
- unsigned int offset;
-
- /*
- * Cropping. The partition algorithm can split the image into
- * multiple slices.
- */
- if (pipe->partitions > 1)
- width = pipe->partition->wpf.width;
-
- vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
- (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
- vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
- (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
-
- if (pipe->lif)
- return;
-
- /*
- * Update the memory offsets based on flipping configuration.
- * The destination addresses point to the locations where the
- * VSP starts writing to memory, which can be any corner of the
- * image depending on the combination of flipping and rotation.
- */
-
- /*
- * First take the partition left coordinate into account.
- * Compute the offset to order the partitions correctly on the
- * output based on whether flipping is enabled. Consider
- * horizontal flipping when rotation is disabled but vertical
- * flipping when rotation is enabled, as rotating the image
- * switches the horizontal and vertical directions. The offset
- * is applied horizontally or vertically accordingly.
- */
- if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
- offset = format->width - pipe->partition->wpf.left
- - pipe->partition->wpf.width;
- else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
- offset = format->height - pipe->partition->wpf.left
- - pipe->partition->wpf.width;
- else
- offset = pipe->partition->wpf.left;
-
- for (i = 0; i < format->num_planes; ++i) {
- unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
- unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
-
- if (wpf->flip.rotate)
- mem.addr[i] += offset / vsub
- * format->plane_fmt[i].bytesperline;
- else
- mem.addr[i] += offset / hsub
- * fmtinfo->bpp[i] / 8;
- }
-
- if (flip & BIT(WPF_CTRL_VFLIP)) {
- /*
- * When rotating the output (after rotation) image
- * height is equal to the partition width (before
- * rotation). Otherwise it is equal to the output
- * image height.
- */
- if (wpf->flip.rotate)
- height = pipe->partition->wpf.width;
- else
- height = format->height;
-
- mem.addr[0] += (height - 1)
- * format->plane_fmt[0].bytesperline;
-
- if (format->num_planes > 1) {
- offset = (height / fmtinfo->vsub - 1)
- * format->plane_fmt[1].bytesperline;
- mem.addr[1] += offset;
- mem.addr[2] += offset;
- }
- }
-
- if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
- unsigned int hoffset = max(0, (int)format->width - 16);
-
- /*
- * Compute the output coordinate. The partition
- * horizontal (left) offset becomes a vertical offset.
- */
- for (i = 0; i < format->num_planes; ++i) {
- unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
-
- mem.addr[i] += hoffset / hsub
- * fmtinfo->bpp[i] / 8;
- }
- }
-
- /*
- * On Gen3 hardware the SPUVS bit has no effect on 3-planar
- * formats. Swap the U and V planes manually in that case.
- */
- if (vsp1->info->gen == 3 && format->num_planes == 3 &&
- fmtinfo->swap_uv)
- swap(mem.addr[1], mem.addr[2]);
-
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
- return;
- }
-
/* Format */
if (!pipe->lif) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
@@ -410,17 +268,17 @@ static void wpf_configure(struct vsp1_entity *entity,
outfmt |= VI6_WPF_OUTFMT_SPUVS;
/* Destination stride and byte swapping. */
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y,
format->plane_fmt[0].bytesperline);
if (format->num_planes > 1)
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C,
format->plane_fmt[1].bytesperline);
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap);
if (vsp1->info->features & VSP1_HAS_WPF_HFLIP &&
wpf->entity.index == 0)
- vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
VI6_WPF_ROT_CTRL_LN16 |
(256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
}
@@ -430,13 +288,13 @@ static void wpf_configure(struct vsp1_entity *entity,
wpf->outfmt = outfmt;
- vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
- vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
+ vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0);
/*
- * Sources. If the pipeline has a single input and BRU is not used,
+ * Sources. If the pipeline has a single input and BRx is not used,
* configure it as the master layer. Otherwise configure all
* inputs as sub-layers and select the virtual RPF as the master
* layer.
@@ -447,24 +305,180 @@ static void wpf_configure(struct vsp1_entity *entity,
if (!input)
continue;
- srcrpf |= (!pipe->bru && pipe->num_inputs == 1)
+ srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
- if (pipe->bru)
- srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU
+ if (pipe->brx)
+ srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU
? VI6_WPF_SRCRPF_VIRACT_MST
: VI6_WPF_SRCRPF_VIRACT2_MST;
- vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
/* Enable interrupts */
- vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
- vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index),
+ vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
+ vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index),
VI6_WFP_IRQ_ENB_DFEE);
}
+static void wpf_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ const unsigned int mask = BIT(WPF_CTRL_VFLIP)
+ | BIT(WPF_CTRL_HFLIP);
+ struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
+ unsigned long flags;
+ u32 outfmt;
+
+ spin_lock_irqsave(&wpf->flip.lock, flags);
+ wpf->flip.active = (wpf->flip.active & ~mask)
+ | (wpf->flip.pending & mask);
+ spin_unlock_irqrestore(&wpf->flip.lock, flags);
+
+ outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
+
+ if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
+ outfmt |= VI6_WPF_OUTFMT_FLP;
+ if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
+ outfmt |= VI6_WPF_OUTFMT_HFLP;
+
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt);
+}
+
+static void wpf_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
+ struct vsp1_device *vsp1 = wpf->entity.vsp1;
+ struct vsp1_rwpf_memory mem = wpf->mem;
+ const struct v4l2_mbus_framefmt *sink_format;
+ const struct v4l2_pix_format_mplane *format = &wpf->format;
+ const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
+ unsigned int width;
+ unsigned int height;
+ unsigned int offset;
+ unsigned int flip;
+ unsigned int i;
+
+ sink_format = vsp1_entity_get_pad_format(&wpf->entity,
+ wpf->entity.config,
+ RWPF_PAD_SINK);
+ width = sink_format->width;
+ height = sink_format->height;
+
+ /*
+ * Cropping. The partition algorithm can split the image into
+ * multiple slices.
+ */
+ if (pipe->partitions > 1)
+ width = pipe->partition->wpf.width;
+
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
+ (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
+ (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
+
+ if (pipe->lif)
+ return;
+
+ /*
+ * Update the memory offsets based on flipping configuration.
+ * The destination addresses point to the locations where the
+ * VSP starts writing to memory, which can be any corner of the
+ * image depending on the combination of flipping and rotation.
+ */
+
+ /*
+ * First take the partition left coordinate into account.
+ * Compute the offset to order the partitions correctly on the
+ * output based on whether flipping is enabled. Consider
+ * horizontal flipping when rotation is disabled but vertical
+ * flipping when rotation is enabled, as rotating the image
+ * switches the horizontal and vertical directions. The offset
+ * is applied horizontally or vertically accordingly.
+ */
+ flip = wpf->flip.active;
+
+ if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
+ offset = format->width - pipe->partition->wpf.left
+ - pipe->partition->wpf.width;
+ else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
+ offset = format->height - pipe->partition->wpf.left
+ - pipe->partition->wpf.width;
+ else
+ offset = pipe->partition->wpf.left;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
+ unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
+
+ if (wpf->flip.rotate)
+ mem.addr[i] += offset / vsub
+ * format->plane_fmt[i].bytesperline;
+ else
+ mem.addr[i] += offset / hsub
+ * fmtinfo->bpp[i] / 8;
+ }
+
+ if (flip & BIT(WPF_CTRL_VFLIP)) {
+ /*
+ * When rotating the output (after rotation) image
+ * height is equal to the partition width (before
+ * rotation). Otherwise it is equal to the output
+ * image height.
+ */
+ if (wpf->flip.rotate)
+ height = pipe->partition->wpf.width;
+ else
+ height = format->height;
+
+ mem.addr[0] += (height - 1)
+ * format->plane_fmt[0].bytesperline;
+
+ if (format->num_planes > 1) {
+ offset = (height / fmtinfo->vsub - 1)
+ * format->plane_fmt[1].bytesperline;
+ mem.addr[1] += offset;
+ mem.addr[2] += offset;
+ }
+ }
+
+ if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
+ unsigned int hoffset = max(0, (int)format->width - 16);
+
+ /*
+ * Compute the output coordinate. The partition
+ * horizontal (left) offset becomes a vertical offset.
+ */
+ for (i = 0; i < format->num_planes; ++i) {
+ unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
+
+ mem.addr[i] += hoffset / hsub
+ * fmtinfo->bpp[i] / 8;
+ }
+ }
+
+ /*
+ * On Gen3 hardware the SPUVS bit has no effect on 3-planar
+ * formats. Swap the U and V planes manually in that case.
+ */
+ if (vsp1->info->gen == 3 && format->num_planes == 3 &&
+ fmtinfo->swap_uv)
+ swap(mem.addr[1], mem.addr[2]);
+
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
+}
+
static unsigned int wpf_max_width(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe)
{
@@ -484,7 +498,9 @@ static void wpf_partition(struct vsp1_entity *entity,
static const struct vsp1_entity_operations wpf_entity_ops = {
.destroy = vsp1_wpf_destroy,
- .configure = wpf_configure,
+ .configure_stream = wpf_configure_stream,
+ .configure_frame = wpf_configure_frame,
+ .configure_partition = wpf_configure_partition,
.max_width = wpf_max_width,
.partition = wpf_partition,
};