summaryrefslogtreecommitdiffstats
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/vsp1.h14
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.c85
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.h9
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c22
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c42
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h10
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c72
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c107
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.h1
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c63
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c219
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h11
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c72
16 files changed, 558 insertions, 179 deletions
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 6ca2cf20d545..12467191dff4 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -36,9 +36,9 @@ struct vsp1_rwpf;
struct vsp1_sru;
struct vsp1_uds;
-#define VPS1_MAX_RPF 5
-#define VPS1_MAX_UDS 3
-#define VPS1_MAX_WPF 4
+#define VSP1_MAX_RPF 5
+#define VSP1_MAX_UDS 3
+#define VSP1_MAX_WPF 4
struct vsp1_device {
struct device *dev;
@@ -55,10 +55,10 @@ struct vsp1_device {
struct vsp1_hsit *hst;
struct vsp1_lif *lif;
struct vsp1_lut *lut;
- struct vsp1_rwpf *rpf[VPS1_MAX_RPF];
+ struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
struct vsp1_sru *sru;
- struct vsp1_uds *uds[VPS1_MAX_UDS];
- struct vsp1_rwpf *wpf[VPS1_MAX_WPF];
+ struct vsp1_uds *uds[VSP1_MAX_UDS];
+ struct vsp1_rwpf *wpf[VSP1_MAX_WPF];
struct list_head entities;
@@ -66,7 +66,7 @@ struct vsp1_device {
struct media_device media_dev;
};
-struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1);
+int vsp1_device_get(struct vsp1_device *vsp1);
void vsp1_device_put(struct vsp1_device *vsp1);
static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index f80695480060..a0c1984c733e 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -18,6 +18,7 @@
#include "vsp1.h"
#include "vsp1_bru.h"
+#include "vsp1_rwpf.h"
#define BRU_MIN_SIZE 4U
#define BRU_MAX_SIZE 8190U
@@ -37,19 +38,47 @@ static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
}
/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Core Operations
+ * Controls
*/
-static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input)
+static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
{
- return media_entity_remote_pad(&bru->entity.pads[input]) != NULL;
+ struct vsp1_bru *bru =
+ container_of(ctrl->handler, struct vsp1_bru, ctrls);
+
+ if (!vsp1_entity_is_streaming(&bru->entity))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BG_COLOR:
+ vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val |
+ (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
+ break;
+ }
+
+ return 0;
}
+static const struct v4l2_ctrl_ops bru_ctrl_ops = {
+ .s_ctrl = bru_s_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
{
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
+ unsigned int flags;
unsigned int i;
+ int ret;
+
+ ret = vsp1_entity_set_streaming(&bru->entity, enable);
+ if (ret < 0)
+ return ret;
if (!enable)
return 0;
@@ -62,18 +91,19 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
* to sane default values for now.
*/
- /* Disable both color data normalization and dithering. */
- vsp1_bru_write(bru, VI6_BRU_INCTRL, 0);
-
- /* Set the background position to cover the whole output image and
- * set its color to opaque black.
+ /* Disable dithering and enable color data normalization unless the
+ * format at the pipeline output is premultiplied.
*/
+ flags = pipe->output ? pipe->output->video.format.flags : 0;
+ vsp1_bru_write(bru, VI6_BRU_INCTRL,
+ flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
+ 0 : VI6_BRU_INCTRL_NRM);
+
+ /* Set the background position to cover the whole output image. */
vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
(format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
(format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
- vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL,
- 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT);
/* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
* unit with a NOP operation to make BRU input 1 available as the
@@ -84,6 +114,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
for (i = 0; i < 4; ++i) {
+ bool premultiplied = false;
u32 ctrl = 0;
/* Configure all Blend/ROP units corresponding to an enabled BRU
@@ -91,11 +122,15 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
* disabled BRU inputs are used in ROP NOP mode to ignore the
* SRC input.
*/
- if (bru_is_input_enabled(bru, i))
+ if (bru->inputs[i].rpf) {
ctrl |= VI6_BRU_CTRL_RBC;
- else
+
+ premultiplied = bru->inputs[i].rpf->video.format.flags
+ & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
+ } else {
ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
| VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
+ }
/* Select the virtual RPF as the Blend/ROP unit A DST input to
* serve as a background color.
@@ -117,10 +152,18 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
*
* DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
* DSTa = DSTa * (1 - SRCa) + SRCa
+ *
+ * when the SRC input isn't premultiplied, and to
+ *
+ * DSTc = DSTc * (1 - SRCa) + SRCc
+ * DSTa = DSTa * (1 - SRCa) + SRCa
+ *
+ * otherwise.
*/
vsp1_bru_write(bru, VI6_BRU_BLD(i),
VI6_BRU_BLD_CCMDX_255_SRC_A |
- VI6_BRU_BLD_CCMDY_SRC_A |
+ (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
+ VI6_BRU_BLD_CCMDY_SRC_A) |
VI6_BRU_BLD_ACMDX_255_SRC_A |
VI6_BRU_BLD_ACMDY_COEFY |
(0xff << VI6_BRU_BLD_COEFY_SHIFT));
@@ -192,7 +235,7 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(fh, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &bru->compose[pad];
+ return &bru->inputs[pad].compose;
default:
return NULL;
}
@@ -391,5 +434,19 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
vsp1_entity_init_formats(subdev, NULL);
+ /* Initialize the control handler. */
+ v4l2_ctrl_handler_init(&bru->ctrls, 1);
+ v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
+ 0, 0xffffff, 1, 0);
+
+ bru->entity.subdev.ctrl_handler = &bru->ctrls;
+
+ if (bru->ctrls.error) {
+ dev_err(vsp1->dev, "bru: failed to initialize controls\n");
+ ret = bru->ctrls.error;
+ vsp1_entity_destroy(&bru->entity);
+ return ERR_PTR(ret);
+ }
+
return bru;
}
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
index 37062704dbf6..16b1c6554911 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.h
+++ b/drivers/media/platform/vsp1/vsp1_bru.h
@@ -14,11 +14,13 @@
#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)
#define BRU_PAD_SOURCE 4
@@ -26,7 +28,12 @@ struct vsp1_device;
struct vsp1_bru {
struct vsp1_entity entity;
- struct v4l2_rect compose[4];
+ struct v4l2_ctrl_handler ctrls;
+
+ struct {
+ struct vsp1_rwpf *rpf;
+ struct v4l2_rect compose;
+ } inputs[4];
};
static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index c69ee0657f75..3e6601b5b4de 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -345,36 +345,32 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
* Increment the VSP1 reference count and initialize the device if the first
* reference is taken.
*
- * Return a pointer to the VSP1 device or NULL if an error occurred.
+ * Return 0 on success or a negative error code otherwise.
*/
-struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)
+int vsp1_device_get(struct vsp1_device *vsp1)
{
- struct vsp1_device *__vsp1 = vsp1;
- int ret;
+ int ret = 0;
mutex_lock(&vsp1->lock);
if (vsp1->ref_count > 0)
goto done;
ret = clk_prepare_enable(vsp1->clock);
- if (ret < 0) {
- __vsp1 = NULL;
+ if (ret < 0)
goto done;
- }
ret = vsp1_device_init(vsp1);
if (ret < 0) {
clk_disable_unprepare(vsp1->clock);
- __vsp1 = NULL;
goto done;
}
done:
- if (__vsp1)
+ if (!ret)
vsp1->ref_count++;
mutex_unlock(&vsp1->lock);
- return __vsp1;
+ return ret;
}
/*
@@ -440,19 +436,19 @@ static int vsp1_validate_platform_data(struct platform_device *pdev,
return -EINVAL;
}
- if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) {
+ if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) {
dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
pdata->rpf_count);
return -EINVAL;
}
- if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) {
+ if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) {
dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
pdata->uds_count);
return -EINVAL;
}
- if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) {
+ if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) {
dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
pdata->wpf_count);
return -EINVAL;
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 44167834285d..79af71d5e270 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -20,6 +20,42 @@
#include "vsp1.h"
#include "vsp1_entity.h"
+#include "vsp1_video.h"
+
+bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
+{
+ bool streaming;
+
+ mutex_lock(&entity->lock);
+ streaming = entity->streaming;
+ mutex_unlock(&entity->lock);
+
+ return streaming;
+}
+
+int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
+{
+ int ret;
+
+ mutex_lock(&entity->lock);
+ entity->streaming = streaming;
+ mutex_unlock(&entity->lock);
+
+ if (!streaming)
+ return 0;
+
+ if (!entity->subdev.ctrl_handler)
+ return 0;
+
+ ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
+ if (ret < 0) {
+ mutex_lock(&entity->lock);
+ entity->streaming = false;
+ mutex_unlock(&entity->lock);
+ }
+
+ return ret;
+}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
@@ -157,6 +193,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
if (i == ARRAY_SIZE(vsp1_routes))
return -EINVAL;
+ mutex_init(&entity->lock);
+
entity->vsp1 = vsp1;
entity->source_pad = num_pads - 1;
@@ -185,7 +223,11 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
void vsp1_entity_destroy(struct vsp1_entity *entity)
{
+ if (entity->video)
+ vsp1_video_cleanup(entity->video);
if (entity->subdev.ctrl_handler)
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
media_entity_cleanup(&entity->subdev.entity);
+
+ mutex_destroy(&entity->lock);
}
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index 7afbd8a7ba66..aa20aaa58208 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -14,10 +14,12 @@
#define __VSP1_ENTITY_H__
#include <linux/list.h>
+#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
struct vsp1_device;
+struct vsp1_video;
enum vsp1_entity_type {
VSP1_ENTITY_BRU,
@@ -68,6 +70,11 @@ struct vsp1_entity {
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt *formats;
+
+ struct vsp1_video *video;
+
+ struct mutex lock; /* Protects the streaming field */
+ bool streaming;
};
static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
@@ -89,4 +96,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity,
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh);
+bool vsp1_entity_is_streaming(struct vsp1_entity *entity);
+int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming);
+
#endif /* __VSP1_ENTITY_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 3e74b44286f6..55f163d32d15 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -336,7 +336,9 @@
*/
#define VI6_SRU_CTRL0 0x2200
+#define VI6_SRU_CTRL0_PARAM0_MASK (0x1ff << 16)
#define VI6_SRU_CTRL0_PARAM0_SHIFT 16
+#define VI6_SRU_CTRL0_PARAM1_MASK (0x1f << 8)
#define VI6_SRU_CTRL0_PARAM1_SHIFT 8
#define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4)
#define VI6_SRU_CTRL0_PARAM2 (1 << 3)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index c3d98642a4aa..d14d26b718ef 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -39,6 +39,36 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
}
/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static int rpf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vsp1_rwpf *rpf =
+ container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
+ struct vsp1_pipeline *pipe;
+
+ if (!vsp1_entity_is_streaming(&rpf->entity))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ALPHA_COMPONENT:
+ vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
+ ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+
+ pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity);
+ vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops rpf_ctrl_ops = {
+ .s_ctrl = rpf_s_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
@@ -50,6 +80,11 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
const struct v4l2_rect *crop = &rpf->crop;
u32 pstride;
u32 infmt;
+ int ret;
+
+ ret = vsp1_entity_set_streaming(&rpf->entity, enable);
+ if (ret < 0)
+ return ret;
if (!enable)
return 0;
@@ -101,12 +136,13 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
(rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
(rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
- /* Disable alpha, mask and color key. Set the alpha channel to a fixed
- * value of 255.
+ /* Use the alpha channel (extended to 8 bits) when available or an
+ * alpha value set through the V4L2_CID_ALPHA_COMPONENT control
+ * otherwise. Disable color keying.
*/
- vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
- vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
- 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
+ (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
+ : VI6_RPF_ALPH_SEL_ASEL_FIXED));
vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
@@ -196,6 +232,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
vsp1_entity_init_formats(subdev, NULL);
+ /* Initialize the control handler. */
+ v4l2_ctrl_handler_init(&rpf->ctrls, 1);
+ v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
+ 0, 255, 1, 255);
+
+ rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
+
+ if (rpf->ctrls.error) {
+ dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
+ index);
+ ret = rpf->ctrls.error;
+ goto error;
+ }
+
/* Initialize the video device. */
video = &rpf->video;
@@ -205,7 +255,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
ret = vsp1_video_init(video, &rpf->entity);
if (ret < 0)
- goto error_video;
+ goto error;
+
+ rpf->entity.video = video;
/* Connect the video device to the RPF. */
ret = media_entity_create_link(&rpf->video.video.entity, 0,
@@ -214,13 +266,11 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (ret < 0)
- goto error_link;
+ goto error;
return rpf;
-error_link:
- vsp1_video_cleanup(video);
-error_video:
- media_entity_cleanup(&rpf->entity.subdev.entity);
+error:
+ vsp1_entity_destroy(&rpf->entity);
return ERR_PTR(ret);
}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index b4fb65e58770..28dd9e7b3838 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -14,6 +14,7 @@
#define __VSP1_RWPF_H__
#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
@@ -26,6 +27,7 @@
struct vsp1_rwpf {
struct vsp1_entity entity;
struct vsp1_video video;
+ struct v4l2_ctrl_handler ctrls;
unsigned int max_width;
unsigned int max_height;
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index aa0e04c56f3f..b7d3c8b9f189 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -42,38 +42,6 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data)
#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1)
-static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vsp1_sru *sru =
- container_of(ctrl->handler, struct vsp1_sru, ctrls);
-
- switch (ctrl->id) {
- case V4L2_CID_VSP1_SRU_INTENSITY:
- sru->intensity = ctrl->val;
- break;
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops sru_ctrl_ops = {
- .s_ctrl = sru_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config sru_intensity_control = {
- .ops = &sru_ctrl_ops,
- .id = V4L2_CID_VSP1_SRU_INTENSITY,
- .name = "Intensity",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = 1,
- .max = 6,
- .step = 1,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 Subdevice Core Operations
- */
-
struct vsp1_sru_param {
u32 ctrl0;
u32 ctrl2;
@@ -110,22 +78,66 @@ static const struct vsp1_sru_param vsp1_sru_params[] = {
},
};
+static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vsp1_sru *sru =
+ container_of(ctrl->handler, struct vsp1_sru, ctrls);
+ const struct vsp1_sru_param *param;
+ u32 value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VSP1_SRU_INTENSITY:
+ param = &vsp1_sru_params[ctrl->val - 1];
+
+ value = vsp1_sru_read(sru, VI6_SRU_CTRL0);
+ value &= ~(VI6_SRU_CTRL0_PARAM0_MASK |
+ VI6_SRU_CTRL0_PARAM1_MASK);
+ value |= param->ctrl0;
+ vsp1_sru_write(sru, VI6_SRU_CTRL0, value);
+
+ vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops sru_ctrl_ops = {
+ .s_ctrl = sru_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config sru_intensity_control = {
+ .ops = &sru_ctrl_ops,
+ .id = V4L2_CID_VSP1_SRU_INTENSITY,
+ .name = "Intensity",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = 6,
+ .def = 1,
+ .step = 1,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_sru *sru = to_sru(subdev);
- const struct vsp1_sru_param *param;
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
- bool upscale;
u32 ctrl0;
+ int ret;
+
+ ret = vsp1_entity_set_streaming(&sru->entity, enable);
+ if (ret < 0)
+ return ret;
if (!enable)
return 0;
input = &sru->entity.formats[SRU_PAD_SINK];
output = &sru->entity.formats[SRU_PAD_SOURCE];
- upscale = input->width != output->width;
- param = &vsp1_sru_params[sru->intensity];
if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
@@ -133,10 +145,18 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
else
ctrl0 = VI6_SRU_CTRL0_PARAM3;
- vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 |
- (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0));
+ if (input->width != output->width)
+ ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
+
+ /* Take the control handler lock to ensure that the CTRL0 value won't be
+ * changed behind our back by a set control operation.
+ */
+ mutex_lock(sru->ctrls.lock);
+ ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
+ & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
+ mutex_unlock(sru->ctrls.lock);
+
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
- vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
return 0;
}
@@ -348,8 +368,15 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&sru->ctrls, 1);
v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
- v4l2_ctrl_handler_setup(&sru->ctrls);
+
sru->entity.subdev.ctrl_handler = &sru->ctrls;
+ if (sru->ctrls.error) {
+ dev_err(vsp1->dev, "sru: failed to initialize controls\n");
+ ret = sru->ctrls.error;
+ vsp1_entity_destroy(&sru->entity);
+ return ERR_PTR(ret);
+ }
+
return sru;
}
diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h
index 381870b74780..b6768bf3dc47 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.h
+++ b/drivers/media/platform/vsp1/vsp1_sru.h
@@ -28,7 +28,6 @@ struct vsp1_sru {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
- unsigned int intensity;
};
static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index 0293bdbb4401..de92ef4944b3 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -45,6 +45,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
* Scaling Computation
*/
+void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha)
+{
+ vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
+}
+
/*
* uds_output_size - Return the output size for an input size and scaling ratio
* @input: input size in pixels
@@ -105,49 +110,56 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
return (input - 1) * 4096 / (output - 1);
}
-static void uds_compute_ratios(struct vsp1_uds *uds)
-{
- struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK];
- struct v4l2_mbus_framefmt *output =
- &uds->entity.formats[UDS_PAD_SOURCE];
-
- uds->hscale = uds_compute_ratio(input->width, output->width);
- uds->vscale = uds_compute_ratio(input->height, output->height);
-
- dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n",
- uds->hscale, uds->vscale);
-}
-
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
{
- const struct v4l2_mbus_framefmt *format;
struct vsp1_uds *uds = to_uds(subdev);
+ const struct v4l2_mbus_framefmt *output;
+ const struct v4l2_mbus_framefmt *input;
+ unsigned int hscale;
+ unsigned int vscale;
+ bool multitap;
if (!enable)
return 0;
- /* Enable multi-tap scaling. */
- vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC);
+ input = &uds->entity.formats[UDS_PAD_SINK];
+ output = &uds->entity.formats[UDS_PAD_SOURCE];
+
+ hscale = uds_compute_ratio(input->width, output->width);
+ vscale = uds_compute_ratio(input->height, output->height);
+
+ dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale);
+
+ /* Multi-tap scaling can't be enabled along with alpha scaling when
+ * scaling down with a factor lower than or equal to 1/2 in either
+ * direction.
+ */
+ if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192))
+ multitap = false;
+ else
+ multitap = true;
+
+ vsp1_uds_write(uds, VI6_UDS_CTRL,
+ (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
+ (multitap ? VI6_UDS_CTRL_BC : 0));
vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
- (uds_passband_width(uds->hscale)
+ (uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
- (uds_passband_width(uds->vscale)
+ (uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios and the output size. */
- format = &uds->entity.formats[UDS_PAD_SOURCE];
-
vsp1_uds_write(uds, VI6_UDS_SCALE,
- (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
- (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
+ (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
+ (vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
- (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
- (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+ (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
return 0;
}
@@ -280,9 +292,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
}
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- uds_compute_ratios(uds);
-
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
index 479d12df1180..031ac0da1b66 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.h
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -25,9 +25,7 @@ struct vsp1_device;
struct vsp1_uds {
struct vsp1_entity entity;
-
- unsigned int hscale;
- unsigned int vscale;
+ bool scale_alpha;
};
static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
@@ -37,4 +35,6 @@ 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_uds *uds, unsigned int alpha);
+
#endif /* __VSP1_UDS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 8a1253e51f04..915a20eb003e 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -31,6 +31,7 @@
#include "vsp1_bru.h"
#include "vsp1_entity.h"
#include "vsp1_rwpf.h"
+#include "vsp1_uds.h"
#include "vsp1_video.h"
#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV
@@ -50,70 +51,85 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
{ V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 8, 0, 0 }, false, false, 1, 1 },
- { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ 1, { 8, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1 },
- { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1 },
+ 1, { 16, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
- 1, { 16, 0, 0 }, false, false, 1, 1 },
+ 1, { 16, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1 },
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1 },
- { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
- 1, { 32, 0, 0 }, false, false, 1, 1 },
- { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, true },
+ { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 32, 0, 0 }, false, false, 1, 1 },
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, false, 2, 1 },
+ 1, { 16, 0, 0 }, false, false, 2, 1, false },
{ V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, true, 2, 1 },
+ 1, { 16, 0, 0 }, false, true, 2, 1, false },
{ V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, true, false, 2, 1 },
+ 1, { 16, 0, 0 }, true, false, 2, 1, false },
{ V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, true, true, 2, 1 },
+ 1, { 16, 0, 0 }, true, true, 2, 1, false },
{ V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, false, 2, 2 },
+ 2, { 8, 16, 0 }, false, false, 2, 2, false },
{ V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, true, 2, 2 },
+ 2, { 8, 16, 0 }, false, true, 2, 2, false },
{ V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, false, 2, 1 },
+ 2, { 8, 16, 0 }, false, false, 2, 1, false },
{ V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 2, { 8, 16, 0 }, false, true, 2, 1 },
+ 2, { 8, 16, 0 }, false, true, 2, 1, false },
{ V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 3, { 8, 8, 8 }, false, false, 2, 2 },
+ 3, { 8, 8, 8 }, false, false, 2, 2, false },
};
/*
@@ -181,11 +197,29 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
struct v4l2_pix_format_mplane *pix,
const struct vsp1_format_info **fmtinfo)
{
+ static const u32 xrgb_formats[][2] = {
+ { V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 },
+ { V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 },
+ { V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 },
+ { V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 },
+ };
+
const struct vsp1_format_info *info;
unsigned int width = pix->width;
unsigned int height = pix->height;
unsigned int i;
+ /* Backward compatibility: replace deprecated RGB formats by their XRGB
+ * equivalent. This selects the format older userspace applications want
+ * while still exposing the new format.
+ */
+ for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) {
+ if (xrgb_formats[i][0] == pix->pixelformat) {
+ pix->pixelformat = xrgb_formats[i][1];
+ break;
+ }
+ }
+
/* Retrieve format information and select the default format if the
* requested format isn't supported.
*/
@@ -273,13 +307,14 @@ vsp1_video_format_adjust(struct vsp1_video *video,
* Pipeline Management
*/
-static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
+static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
+ struct vsp1_rwpf *input,
struct vsp1_rwpf *output)
{
struct vsp1_entity *entity;
unsigned int entities = 0;
struct media_pad *pad;
- bool uds_found = false;
+ bool bru_found = false;
input->location.left = 0;
input->location.top = 0;
@@ -301,10 +336,15 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
*/
if (entity->type == VSP1_ENTITY_BRU) {
struct vsp1_bru *bru = to_bru(&entity->subdev);
- struct v4l2_rect *rect = &bru->compose[pad->index];
+ struct v4l2_rect *rect =
+ &bru->inputs[pad->index].compose;
+
+ bru->inputs[pad->index].rpf = input;
input->location.left = rect->left;
input->location.top = rect->top;
+
+ bru_found = true;
}
/* We've reached the WPF, we're done. */
@@ -319,9 +359,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
/* UDS can't be chained. */
if (entity->type == VSP1_ENTITY_UDS) {
- if (uds_found)
+ if (pipe->uds)
return -EPIPE;
- uds_found = true;
+
+ pipe->uds = entity;
+ pipe->uds_input = bru_found ? pipe->bru
+ : &input->entity;
}
/* Follow the source link. The link setup operations ensure
@@ -340,6 +383,27 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
return 0;
}
+static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
+{
+ if (pipe->bru) {
+ struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
+ bru->inputs[i].rpf = NULL;
+ }
+
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ pipe->buffers_ready = 0;
+ pipe->num_video = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->bru = NULL;
+ pipe->lif = NULL;
+ pipe->uds = NULL;
+}
+
static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
struct vsp1_video *video)
{
@@ -395,7 +459,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
* contains no loop and that all branches end at the output WPF.
*/
for (i = 0; i < pipe->num_inputs; ++i) {
- ret = vsp1_pipeline_validate_branch(pipe->inputs[i],
+ ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i],
pipe->output);
if (ret < 0)
goto error;
@@ -404,13 +468,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
return 0;
error:
- INIT_LIST_HEAD(&pipe->entities);
- pipe->buffers_ready = 0;
- pipe->num_video = 0;
- pipe->num_inputs = 0;
- pipe->output = NULL;
- pipe->bru = NULL;
- pipe->lif = NULL;
+ __vsp1_pipeline_cleanup(pipe);
return ret;
}
@@ -441,16 +499,8 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
mutex_lock(&pipe->lock);
/* If we're the last user clean up the pipeline. */
- if (--pipe->use_count == 0) {
- INIT_LIST_HEAD(&pipe->entities);
- pipe->state = VSP1_PIPELINE_STOPPED;
- pipe->buffers_ready = 0;
- pipe->num_video = 0;
- pipe->num_inputs = 0;
- pipe->output = NULL;
- pipe->bru = NULL;
- pipe->lif = NULL;
- }
+ if (--pipe->use_count == 0)
+ __vsp1_pipeline_cleanup(pipe);
mutex_unlock(&pipe->lock);
}
@@ -471,7 +521,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
int ret;
spin_lock_irqsave(&pipe->irqlock, flags);
- pipe->state = VSP1_PIPELINE_STOPPING;
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
spin_unlock_irqrestore(&pipe->irqlock, flags);
ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED,
@@ -479,7 +530,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
ret = ret == 0 ? -ETIMEDOUT : 0;
list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->route)
+ if (entity->route && entity->route->reg)
vsp1_write(entity->vsp1, entity->route->reg,
VI6_DPR_NODE_UNUSED);
@@ -576,6 +627,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
+ enum vsp1_pipeline_state state;
unsigned long flags;
unsigned int i;
@@ -591,11 +643,13 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
spin_lock_irqsave(&pipe->irqlock, flags);
+ state = pipe->state;
+ pipe->state = VSP1_PIPELINE_STOPPED;
+
/* If a stop has been requested, mark the pipeline as stopped and
* return.
*/
- if (pipe->state == VSP1_PIPELINE_STOPPING) {
- pipe->state = VSP1_PIPELINE_STOPPED;
+ if (state == VSP1_PIPELINE_STOPPING) {
wake_up(&pipe->wq);
goto done;
}
@@ -608,6 +662,47 @@ done:
spin_unlock_irqrestore(&pipe->irqlock, flags);
}
+/*
+ * Propagate the alpha value through the pipeline.
+ *
+ * As the UDS has restricted scaling capabilities when the alpha component needs
+ * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
+ * value. The UDS then outputs a fixed alpha value which needs to be programmed
+ * from the input RPF alpha.
+ */
+void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
+ struct vsp1_entity *input,
+ unsigned int alpha)
+{
+ struct vsp1_entity *entity;
+ struct media_pad *pad;
+
+ pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
+
+ while (pad) {
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+
+ /* The BRU background color has a fixed alpha value set to 255,
+ * the output alpha value is thus always equal to 255.
+ */
+ if (entity->type == VSP1_ENTITY_BRU)
+ alpha = 255;
+
+ if (entity->type == VSP1_ENTITY_UDS) {
+ struct vsp1_uds *uds = to_uds(&entity->subdev);
+
+ vsp1_uds_set_alpha(uds, alpha);
+ break;
+ }
+
+ pad = &entity->pads[entity->source_pad];
+ pad = media_entity_remote_pad(pad);
+ }
+}
+
/* -----------------------------------------------------------------------------
* videobuf2 Queue Operations
*/
@@ -654,8 +749,6 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb)
if (vb->num_planes < format->num_planes)
return -EINVAL;
- buf->video = video;
-
for (i = 0; i < vb->num_planes; ++i) {
buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
buf->length[i] = vb2_plane_size(vb, i);
@@ -717,6 +810,25 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
mutex_lock(&pipe->lock);
if (pipe->stream_count == pipe->num_video - 1) {
+ if (pipe->uds) {
+ struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
+
+ /* If a BRU is present in the pipeline before the UDS,
+ * the alpha component doesn't need to be scaled as the
+ * BRU output alpha value is fixed to 255. Otherwise we
+ * need to scale the alpha component only when available
+ * at the input RPF.
+ */
+ if (pipe->uds_input->type == VSP1_ENTITY_BRU) {
+ uds->scale_alpha = false;
+ } else {
+ struct vsp1_rwpf *rpf =
+ to_rwpf(&pipe->uds_input->subdev);
+
+ uds->scale_alpha = rpf->video.fmtinfo->alpha;
+ }
+ }
+
list_for_each_entry(entity, &pipe->entities, list_pipe) {
vsp1_entity_route_setup(entity);
@@ -744,6 +856,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_video_buffer *buffer;
unsigned long flags;
int ret;
@@ -761,6 +874,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
/* Remove all buffers from the IRQ queue. */
spin_lock_irqsave(&video->irqlock, flags);
+ list_for_each_entry(buffer, &video->irqqueue, queue)
+ vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&video->irqqueue);
spin_unlock_irqrestore(&video->irqlock, flags);
}
@@ -950,8 +1065,8 @@ static int vsp1_video_open(struct file *file)
file->private_data = vfh;
- if (!vsp1_device_get(video->vsp1)) {
- ret = -EBUSY;
+ ret = vsp1_device_get(video->vsp1);
+ if (ret < 0) {
v4l2_fh_del(vfh);
kfree(vfh);
}
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index c04d48fa2999..fd2851a82e00 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -33,6 +33,7 @@ struct vsp1_video;
* @swap_uv: the U and V components are swapped (V comes before U)
* @hsub: horizontal subsampling factor
* @vsub: vertical subsampling factor
+ * @alpha: has an alpha channel
*/
struct vsp1_format_info {
u32 fourcc;
@@ -45,6 +46,7 @@ struct vsp1_format_info {
bool swap_uv;
unsigned int hsub;
unsigned int vsub;
+ bool alpha;
};
enum vsp1_pipeline_state {
@@ -73,10 +75,12 @@ struct vsp1_pipeline {
unsigned int num_video;
unsigned int num_inputs;
- struct vsp1_rwpf *inputs[VPS1_MAX_RPF];
+ struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
struct vsp1_rwpf *output;
struct vsp1_entity *bru;
struct vsp1_entity *lif;
+ struct vsp1_entity *uds;
+ struct vsp1_entity *uds_input;
struct list_head entities;
};
@@ -90,7 +94,6 @@ static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
}
struct vsp1_video_buffer {
- struct vsp1_video *video;
struct vb2_buffer buf;
struct list_head queue;
@@ -142,4 +145,8 @@ void vsp1_video_cleanup(struct vsp1_video *video);
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
+void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
+ struct vsp1_entity *input,
+ unsigned int alpha);
+
#endif /* __VSP1_VIDEO_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 1294340dcb36..6e057762c933 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -39,22 +39,56 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data)
}
/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static int wpf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vsp1_rwpf *wpf =
+ container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
+ u32 value;
+
+ if (!vsp1_entity_is_streaming(&wpf->entity))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ALPHA_COMPONENT:
+ value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT);
+ value &= ~VI6_WPF_OUTFMT_PDV_MASK;
+ value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT;
+ vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops wpf_ctrl_ops = {
+ .s_ctrl = wpf_s_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
{
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_rwpf *wpf = to_rwpf(subdev);
- struct vsp1_pipeline *pipe =
- to_vsp1_pipeline(&wpf->entity.subdev.entity);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
const struct v4l2_rect *crop = &wpf->crop;
unsigned int i;
u32 srcrpf = 0;
u32 outfmt = 0;
+ int ret;
+
+ ret = vsp1_entity_set_streaming(&wpf->entity, enable);
+ if (ret < 0)
+ return ret;
if (!enable) {
vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
+ vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0);
return 0;
}
@@ -99,6 +133,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
+ if (fmtinfo->alpha)
+ outfmt |= VI6_WPF_OUTFMT_PXA;
if (fmtinfo->swap_yc)
outfmt |= VI6_WPF_OUTFMT_SPYCS;
if (fmtinfo->swap_uv)
@@ -111,7 +147,13 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
wpf->entity.formats[RWPF_PAD_SOURCE].code)
outfmt |= VI6_WPF_OUTFMT_CSC;
+ /* Take the control handler lock to ensure that the PDV value won't be
+ * changed behind our back by a set control operation.
+ */
+ mutex_lock(wpf->ctrls.lock);
+ outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK;
vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt);
+ mutex_unlock(wpf->ctrls.lock);
vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
@@ -207,6 +249,20 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
vsp1_entity_init_formats(subdev, NULL);
+ /* Initialize the control handler. */
+ v4l2_ctrl_handler_init(&wpf->ctrls, 1);
+ v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
+ 0, 255, 1, 255);
+
+ wpf->entity.subdev.ctrl_handler = &wpf->ctrls;
+
+ if (wpf->ctrls.error) {
+ dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
+ index);
+ ret = wpf->ctrls.error;
+ goto error;
+ }
+
/* Initialize the video device. */
video = &wpf->video;
@@ -216,7 +272,9 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
ret = vsp1_video_init(video, &wpf->entity);
if (ret < 0)
- goto error_video;
+ goto error;
+
+ wpf->entity.video = video;
/* Connect the video device to the WPF. All connections are immutable
* except for the WPF0 source link if a LIF is present.
@@ -229,15 +287,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
RWPF_PAD_SOURCE,
&wpf->video.video.entity, 0, flags);
if (ret < 0)
- goto error_link;
+ goto error;
wpf->entity.sink = &wpf->video.video.entity;
return wpf;
-error_link:
- vsp1_video_cleanup(video);
-error_video:
- media_entity_cleanup(&wpf->entity.subdev.entity);
+error:
+ vsp1_entity_destroy(&wpf->entity);
return ERR_PTR(ret);
}