summaryrefslogtreecommitdiff
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.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.c36
-rw-r--r--drivers/media/platform/vsp1/vsp1_clu.c62
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c126
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.h1
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c26
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c45
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c22
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h25
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.c20
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c20
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.c42
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.c13
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.h11
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c109
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c83
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h13
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c50
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c71
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c192
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c132
22 files changed, 825 insertions, 278 deletions
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 06a2ec7e5ad4..b23fa879a9aa 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -53,6 +53,7 @@ struct vsp1_uds;
struct vsp1_device_info {
u32 version;
+ const char *model;
unsigned int gen;
unsigned int features;
unsigned int rpf_count;
@@ -65,6 +66,7 @@ struct vsp1_device_info {
struct vsp1_device {
struct device *dev;
const struct vsp1_device_info *info;
+ u32 version;
void __iomem *mmio;
struct rcar_fcp_device *fcp;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index 8268b87727a7..ee8355c28f94 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -142,10 +142,15 @@ static int bru_set_format(struct v4l2_subdev *subdev,
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ mutex_lock(&bru->entity.lock);
config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
bru_try_format(bru, config, fmt->pad, &fmt->format);
@@ -174,7 +179,9 @@ static int bru_set_format(struct v4l2_subdev *subdev,
}
}
- return 0;
+done:
+ mutex_unlock(&bru->entity.lock);
+ return ret;
}
static int bru_get_selection(struct v4l2_subdev *subdev,
@@ -201,7 +208,9 @@ static int bru_get_selection(struct v4l2_subdev *subdev,
if (!config)
return -EINVAL;
+ mutex_lock(&bru->entity.lock);
sel->r = *bru_get_compose(bru, config, sel->pad);
+ mutex_unlock(&bru->entity.lock);
return 0;
default:
@@ -217,6 +226,7 @@ static int bru_set_selection(struct v4l2_subdev *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)
return -EINVAL;
@@ -224,11 +234,16 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
+ mutex_lock(&bru->entity.lock);
+
config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
- /* The compose rectangle top left corner must be inside the output
+ /*
+ * The compose rectangle top left corner must be inside the output
* frame.
*/
format = vsp1_entity_get_pad_format(&bru->entity, config,
@@ -246,7 +261,9 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
compose = bru_get_compose(bru, config, sel->pad);
*compose = sel->r;
- return 0;
+done:
+ mutex_unlock(&bru->entity.lock);
+ return ret;
}
static const struct v4l2_subdev_pad_ops bru_pad_ops = {
@@ -269,14 +286,15 @@ static const struct v4l2_subdev_ops bru_ops = {
static void bru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_bru *bru = to_bru(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
- if (!full)
+ if (params != VSP1_ENTITY_PARAMS_INIT)
return;
format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c
index b63d2dbe5ea3..f2fb26e5ab4e 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/vsp1/vsp1_clu.c
@@ -148,10 +148,15 @@ static int clu_set_format(struct v4l2_subdev *subdev,
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)
- return -EINVAL;
+ 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 &&
@@ -164,7 +169,7 @@ static int clu_set_format(struct v4l2_subdev *subdev,
if (fmt->pad == CLU_PAD_SOURCE) {
/* The CLU output format can't be modified. */
fmt->format = *format;
- return 0;
+ goto done;
}
format->code = fmt->format.code;
@@ -182,7 +187,9 @@ static int clu_set_format(struct v4l2_subdev *subdev,
CLU_PAD_SOURCE);
*format = fmt->format;
- return 0;
+done:
+ mutex_unlock(&clu->entity.lock);
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -207,42 +214,51 @@ static const struct v4l2_subdev_ops clu_ops = {
static void clu_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_clu *clu = to_clu(&entity->subdev);
struct vsp1_dl_body *dlb;
unsigned long flags;
u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
- /* The format can't be changed during streaming, only verify it at
- * stream start and store the information internally for future partial
- * reconfiguration calls.
- */
- if (full) {
+ 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;
- return;
+ 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_PARTITION:
+ break;
+
+ 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, dl, VI6_CLU_CTRL, ctrl);
+ vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl);
- spin_lock_irqsave(&clu->lock, flags);
- 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 (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
+ if (dlb)
+ vsp1_dl_list_add_fragment(dl, dlb);
+ break;
+ }
}
static const struct vsp1_entity_operations clu_entity_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 37c3518aa2a8..ad545aff4e35 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -21,7 +21,6 @@
#include "vsp1_dl.h"
#define VSP1_DL_NUM_ENTRIES 256
-#define VSP1_DL_NUM_LISTS 3
#define VSP1_DLH_INT_ENABLE (1 << 1)
#define VSP1_DLH_AUTO_START (1 << 0)
@@ -71,6 +70,7 @@ struct vsp1_dl_body {
* @dma: DMA address for the header
* @body0: first display list body
* @fragments: list of extra display list bodies
+ * @chain: entry in the display list partition chain
*/
struct vsp1_dl_list {
struct list_head list;
@@ -81,6 +81,9 @@ struct vsp1_dl_list {
struct vsp1_dl_body body0;
struct list_head fragments;
+
+ bool has_chain;
+ struct list_head chain;
};
enum vsp1_dl_mode {
@@ -262,7 +265,6 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
memset(dl->header, 0, sizeof(*dl->header));
dl->header->lists[0].addr = dl->body0.dma;
- dl->header->flags = VSP1_DLH_INT_ENABLE;
}
return dl;
@@ -293,6 +295,12 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
if (!list_empty(&dlm->free)) {
dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list);
list_del(&dl->list);
+
+ /*
+ * The display list chain must be initialised to ensure every
+ * display list can assert list_empty() if it is not in a chain.
+ */
+ INIT_LIST_HEAD(&dl->chain);
}
spin_unlock_irqrestore(&dlm->lock, flags);
@@ -303,10 +311,24 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
/* This function must be called with the display list manager lock held.*/
static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
{
+ struct vsp1_dl_list *dl_child;
+
if (!dl)
return;
- /* We can't free fragments here as DMA memory can only be freed in
+ /*
+ * Release any linked display-lists which were chained for a single
+ * hardware operation.
+ */
+ if (dl->has_chain) {
+ list_for_each_entry(dl_child, &dl->chain, chain)
+ __vsp1_dl_list_put(dl_child);
+ }
+
+ dl->has_chain = false;
+
+ /*
+ * 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.
@@ -383,6 +405,76 @@ int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
return 0;
}
+/**
+ * vsp1_dl_list_add_chain - Add a display list to a chain
+ * @head: The head display list
+ * @dl: The new display list
+ *
+ * Add a display list to an existing display list chain. The chained lists
+ * will be automatically processed by the hardware without intervention from
+ * the CPU. A display list end interrupt will only complete after the last
+ * display list in the chain has completed processing.
+ *
+ * Adding a display list to a chain passes ownership of the display list to
+ * the head display list item. The chain is released when the head dl item is
+ * put back with __vsp1_dl_list_put().
+ *
+ * Chained display lists are only usable in header mode. Attempts to add a
+ * display list to a chain in header-less mode will return an error.
+ */
+int vsp1_dl_list_add_chain(struct vsp1_dl_list *head,
+ struct vsp1_dl_list *dl)
+{
+ /* Chained lists are only available in header mode. */
+ if (head->dlm->mode != VSP1_DL_MODE_HEADER)
+ return -EINVAL;
+
+ head->has_chain = true;
+ list_add_tail(&dl->chain, &head->chain);
+ return 0;
+}
+
+static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
+{
+ struct vsp1_dl_header_list *hdr = dl->header->lists;
+ struct vsp1_dl_body *dlb;
+ unsigned int num_lists = 0;
+
+ /*
+ * Fill the header with the display list bodies addresses and sizes. The
+ * address of the first body has already been filled when the display
+ * list was allocated.
+ */
+
+ hdr->num_bytes = dl->body0.num_entries
+ * sizeof(*dl->header->lists);
+
+ list_for_each_entry(dlb, &dl->fragments, list) {
+ num_lists++;
+ hdr++;
+
+ hdr->addr = dlb->dma;
+ hdr->num_bytes = dlb->num_entries
+ * sizeof(*dl->header->lists);
+ }
+
+ dl->header->num_lists = num_lists;
+
+ /*
+ * If this display list's chain is not empty, we are on a list, where
+ * the next item in the list is the display list entity which should be
+ * automatically queued by the hardware.
+ */
+ if (!list_empty(&dl->chain) && !is_last) {
+ struct vsp1_dl_list *next = list_next_entry(dl, chain);
+
+ dl->header->next_header = next->dma;
+ dl->header->flags = VSP1_DLH_AUTO_START;
+ } else {
+ dl->header->flags = VSP1_DLH_INT_ENABLE;
+ }
+}
+
void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
{
struct vsp1_dl_manager *dlm = dl->dlm;
@@ -393,30 +485,26 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
spin_lock_irqsave(&dlm->lock, flags);
if (dl->dlm->mode == VSP1_DL_MODE_HEADER) {
- struct vsp1_dl_header_list *hdr = dl->header->lists;
- struct vsp1_dl_body *dlb;
- unsigned int num_lists = 0;
+ struct vsp1_dl_list *dl_child;
- /* Fill the header with the display list bodies addresses and
- * sizes. The address of the first body has already been filled
- * when the display list was allocated.
- *
+ /*
* In header mode the caller guarantees that the hardware is
* idle at this point.
*/
- hdr->num_bytes = dl->body0.num_entries
- * sizeof(*dl->header->lists);
- list_for_each_entry(dlb, &dl->fragments, list) {
- num_lists++;
- hdr++;
+ /* Fill the header for the head and chained display lists. */
+ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain));
- hdr->addr = dlb->dma;
- hdr->num_bytes = dlb->num_entries
- * sizeof(*dl->header->lists);
+ list_for_each_entry(dl_child, &dl->chain, chain) {
+ bool last = list_is_last(&dl_child->chain, &dl->chain);
+
+ vsp1_dl_list_fill_header(dl_child, last);
}
- dl->header->num_lists = num_lists;
+ /*
+ * Commit the head display list to hardware. Chained headers
+ * will auto-start.
+ */
vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);
dlm->active = dl;
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index de387cd4d745..7131aa3c5978 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -41,5 +41,6 @@ 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);
+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 fe9665e57b3b..cd209dccff1b 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -276,17 +276,18 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
}
dev_dbg(vsp1->dev,
- "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n",
+ "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
__func__, rpf_index,
cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
- cfg->zpos);
+ &cfg->mem[2], cfg->zpos);
- /* Store the format, stride, memory buffer address, crop and compose
+ /*
+ * Store the format, stride, memory buffer address, crop and compose
* rectangles and Z-order position and for the input.
*/
- fmtinfo = vsp1_get_format_info(cfg->pixelformat);
+ fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat);
if (!fmtinfo) {
dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
cfg->pixelformat);
@@ -301,7 +302,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
rpf->mem.addr[0] = cfg->mem[0];
rpf->mem.addr[1] = cfg->mem[1];
- rpf->mem.addr[2] = 0;
+ rpf->mem.addr[2] = cfg->mem[2];
vsp1->drm->inputs[rpf_index].crop = cfg->src;
vsp1->drm->inputs[rpf_index].compose = cfg->dst;
@@ -492,16 +493,13 @@ void vsp1_du_atomic_flush(struct device *dev)
vsp1_entity_route_setup(entity, pipe->dl);
if (entity->ops->configure) {
- entity->ops->configure(entity, pipe, pipe->dl, true);
- entity->ops->configure(entity, pipe, pipe->dl, false);
+ entity->ops->configure(entity, pipe, pipe->dl,
+ VSP1_ENTITY_PARAMS_INIT);
+ entity->ops->configure(entity, pipe, pipe->dl,
+ VSP1_ENTITY_PARAMS_RUNTIME);
+ entity->ops->configure(entity, pipe, pipe->dl,
+ VSP1_ENTITY_PARAMS_PARTITION);
}
-
- /* The memory buffer address must be applied after configuring
- * the RPF to make sure the crop offset are computed.
- */
- if (entity->type == VSP1_ENTITY_RPF)
- vsp1_rwpf_set_memory(to_rwpf(&entity->subdev),
- pipe->dl);
}
vsp1_dl_list_commit(pipe->dl);
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index cc316d281687..57c713a4e1df 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -60,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
- if (status & VI6_WFP_IRQ_STA_FRE) {
+ if (status & VI6_WFP_IRQ_STA_DFE) {
vsp1_pipeline_frame_end(wpf->pipe);
ret = IRQ_HANDLED;
}
@@ -220,7 +220,8 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
int ret;
mdev->dev = vsp1->dev;
- strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
+ mdev->hw_revision = vsp1->version;
+ strlcpy(mdev->model, vsp1->info->model, sizeof(mdev->model));
snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
dev_name(mdev->dev));
media_device_init(mdev);
@@ -559,6 +560,7 @@ static const struct dev_pm_ops vsp1_pm_ops = {
static const struct vsp1_device_info vsp1_device_infos[] = {
{
.version = VI6_IP_VERSION_MODEL_VSPS_H2,
+ .model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
@@ -569,6 +571,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
+ .model = "VSP1-R",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
@@ -578,6 +581,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
+ .model = "VSP1-D",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
.rpf_count = 4,
@@ -587,6 +591,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPS_M2,
+ .model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
@@ -596,7 +601,30 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.num_bru_inputs = 4,
.uapi = true,
}, {
+ .version = VI6_IP_VERSION_MODEL_VSPS_V2H,
+ .model = "VSP1V-S",
+ .gen = 2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
+ .rpf_count = 4,
+ .uds_count = 1,
+ .wpf_count = 4,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPD_V2H,
+ .model = "VSP1V-D",
+ .gen = 2,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
+ | VSP1_HAS_LIF,
+ .rpf_count = 4,
+ .uds_count = 1,
+ .wpf_count = 1,
+ .num_bru_inputs = 4,
+ .uapi = true,
+ }, {
.version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
+ .model = "VSP2-I",
.gen = 3,
.features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU
| VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
@@ -606,6 +634,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
+ .model = "VSP2-BD",
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
@@ -614,6 +643,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
+ .model = "VSP2-BC",
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_WPF_VFLIP,
@@ -623,6 +653,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
+ .model = "VSP2-D",
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
@@ -638,7 +669,6 @@ static int vsp1_probe(struct platform_device *pdev)
struct resource *irq;
struct resource *io;
unsigned int i;
- u32 version;
int ret;
vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
@@ -689,11 +719,11 @@ static int vsp1_probe(struct platform_device *pdev)
if (ret < 0)
goto done;
- version = vsp1_read(vsp1, VI6_IP_VERSION);
+ vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION);
pm_runtime_put_sync(&pdev->dev);
for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
- if ((version & VI6_IP_VERSION_MODEL_MASK) ==
+ if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
vsp1_device_infos[i].version) {
vsp1->info = &vsp1_device_infos[i];
break;
@@ -701,12 +731,13 @@ static int vsp1_probe(struct platform_device *pdev)
}
if (!vsp1->info) {
- dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
+ dev_err(&pdev->dev, "unsupported IP version 0x%08x\n",
+ vsp1->version);
ret = -ENXIO;
goto done;
}
- dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
+ dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version);
/* Instanciate entities */
ret = vsp1_create_entities(vsp1);
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 4cf6cc719c00..da673495c222 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -51,6 +51,9 @@ void vsp1_entity_route_setup(struct vsp1_entity *source,
* @cfg: the TRY pad configuration
* @which: configuration selector (ACTIVE or TRY)
*
+ * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold
+ * the entity lock to access the returned configuration.
+ *
* Return the pad configuration requested by the which argument. The TRY
* configuration is passed explicitly to the function through the cfg argument
* and simply returned when requested. The ACTIVE configuration comes from the
@@ -160,7 +163,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
if (!config)
return -EINVAL;
+ mutex_lock(&entity->lock);
fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad);
+ mutex_unlock(&entity->lock);
return 0;
}
@@ -204,8 +209,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
if (!config)
return -EINVAL;
+ mutex_lock(&entity->lock);
format = vsp1_entity_get_pad_format(entity, config, 0);
code->code = format->code;
+ mutex_unlock(&entity->lock);
}
return 0;
@@ -235,6 +242,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
struct vsp1_entity *entity = to_vsp1_entity(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
config = vsp1_entity_get_pad_config(entity, cfg, fse->which);
if (!config)
@@ -242,8 +250,12 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
format = vsp1_entity_get_pad_format(entity, config, fse->pad);
- if (fse->index || fse->code != format->code)
- return -EINVAL;
+ mutex_lock(&entity->lock);
+
+ if (fse->index || fse->code != format->code) {
+ ret = -EINVAL;
+ goto done;
+ }
if (fse->pad == 0) {
fse->min_width = min_width;
@@ -260,7 +272,9 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
fse->max_height = format->height;
}
- return 0;
+done:
+ mutex_unlock(&entity->lock);
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -358,6 +372,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;
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index b43457fd2c43..901146f807b9 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -14,7 +14,7 @@
#define __VSP1_ENTITY_H__
#include <linux/list.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
@@ -35,6 +35,18 @@ enum vsp1_entity_type {
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 */
/*
@@ -63,17 +75,16 @@ struct vsp1_route {
/**
* struct vsp1_entity_operations - Entity operations
* @destroy: Destroy the entity.
- * @set_memory: Setup memory buffer access. This operation applies the settings
- * stored in the rwpf mem field to the display list. Valid for RPF
- * and WPF only.
* @configure: Setup the hardware based on the entity state (pipeline, formats,
* selection rectangles, ...)
+ * @max_width: Return the max supported width of data that the entity can
+ * process in a single operation.
*/
struct vsp1_entity_operations {
void (*destroy)(struct vsp1_entity *);
- void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl);
void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_dl_list *, bool);
+ struct vsp1_dl_list *, enum vsp1_entity_params);
+ unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
};
struct vsp1_entity {
@@ -96,6 +107,8 @@ struct vsp1_entity {
struct v4l2_subdev subdev;
struct v4l2_subdev_pad_config *config;
+
+ struct mutex lock; /* Protects the pad config */
};
static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
index 6e5077beb38c..94316afc54ff 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -71,10 +71,15 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
struct vsp1_hsit *hsit = to_hsit(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ mutex_lock(&hsit->entity.lock);
config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad);
@@ -83,7 +88,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
* modified.
*/
fmt->format = *format;
- return 0;
+ goto done;
}
format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32
@@ -104,7 +109,9 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
: MEDIA_BUS_FMT_AHSV8888_1X32;
- return 0;
+done:
+ mutex_unlock(&hsit->entity.lock);
+ return ret;
}
static const struct v4l2_subdev_pad_ops hsit_pad_ops = {
@@ -125,11 +132,12 @@ static const struct v4l2_subdev_ops hsit_ops = {
static void hsit_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
- if (!full)
+ if (params != VSP1_ENTITY_PARAMS_INIT)
return;
if (hsit->inverse)
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index a720063f38c5..e32acae1fc6e 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -66,10 +66,15 @@ static int lif_set_format(struct v4l2_subdev *subdev,
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)
- return -EINVAL;
+ 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 &&
@@ -83,7 +88,7 @@ static int lif_set_format(struct v4l2_subdev *subdev,
* format.
*/
fmt->format = *format;
- return 0;
+ goto done;
}
format->code = fmt->format.code;
@@ -101,7 +106,9 @@ static int lif_set_format(struct v4l2_subdev *subdev,
LIF_PAD_SOURCE);
*format = fmt->format;
- return 0;
+done:
+ mutex_unlock(&lif->entity.lock);
+ return ret;
}
static const struct v4l2_subdev_pad_ops lif_pad_ops = {
@@ -122,7 +129,8 @@ static const struct v4l2_subdev_ops lif_ops = {
static void lif_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(&entity->subdev);
@@ -130,7 +138,7 @@ static void lif_configure(struct vsp1_entity *entity,
unsigned int obth = 400;
unsigned int lbth = 200;
- if (!full)
+ if (params != VSP1_ENTITY_PARAMS_INIT)
return;
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
index dc31de9602ba..c67cc60db0db 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -124,10 +124,15 @@ static int lut_set_format(struct v4l2_subdev *subdev,
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)
- return -EINVAL;
+ 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 &&
@@ -140,7 +145,7 @@ static int lut_set_format(struct v4l2_subdev *subdev,
if (fmt->pad == LUT_PAD_SOURCE) {
/* The LUT output format can't be modified. */
fmt->format = *format;
- return 0;
+ goto done;
}
format->code = fmt->format.code;
@@ -158,7 +163,9 @@ static int lut_set_format(struct v4l2_subdev *subdev,
LUT_PAD_SOURCE);
*format = fmt->format;
- return 0;
+done:
+ mutex_unlock(&lut->entity.lock);
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -183,24 +190,31 @@ static const struct v4l2_subdev_ops lut_ops = {
static void lut_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
struct vsp1_dl_body *dlb;
unsigned long flags;
- if (full) {
+ switch (params) {
+ case VSP1_ENTITY_PARAMS_INIT:
vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
- return;
- }
+ break;
- spin_lock_irqsave(&lut->lock, flags);
- dlb = lut->lut;
- lut->lut = NULL;
- spin_unlock_irqrestore(&lut->lock, flags);
+ case VSP1_ENTITY_PARAMS_PARTITION:
+ break;
+
+ case VSP1_ENTITY_PARAMS_RUNTIME:
+ spin_lock_irqsave(&lut->lock, flags);
+ dlb = lut->lut;
+ lut->lut = NULL;
+ spin_unlock_irqrestore(&lut->lock, flags);
- if (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
+ if (dlb)
+ vsp1_dl_list_add_fragment(dl, dlb);
+ break;
+ }
}
static const struct vsp1_entity_operations lut_entity_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index 3e75fb3fcace..756ca4ea7668 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -136,17 +136,23 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
3, { 8, 8, 8 }, false, true, 1, 1, false },
};
-/*
+/**
* vsp1_get_format_info - Retrieve format information for a 4CC
+ * @vsp1: the VSP1 device
* @fourcc: the format 4CC
*
* Return a pointer to the format information structure corresponding to the
* given V4L2 format 4CC, or NULL if no corresponding format can be found.
*/
-const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc)
+const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
+ u32 fourcc)
{
unsigned int i;
+ /* Special case, the VYUY format is supported on Gen2 only. */
+ if (vsp1->info->gen != 2 && fourcc == V4L2_PIX_FMT_VYUY)
+ return NULL;
+
for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
const struct vsp1_format_info *info = &vsp1_video_formats[i];
@@ -365,6 +371,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
void vsp1_pipelines_resume(struct vsp1_device *vsp1)
{
+ unsigned long flags;
unsigned int i;
/* Resume all running pipelines. */
@@ -379,7 +386,9 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1)
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 d20d997b1fda..ac4ad2655551 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -77,6 +77,9 @@ enum vsp1_pipeline_state {
* @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
+ * @div_size: The maximum allowed partition size for the pipeline
+ * @partitions: The number of partitions used to process one frame
+ * @current_partition: The partition number currently being configured
*/
struct vsp1_pipeline {
struct media_pipeline pipe;
@@ -104,6 +107,11 @@ struct vsp1_pipeline {
struct list_head entities;
struct vsp1_dl_list *dl;
+
+ unsigned int div_size;
+ unsigned int partitions;
+ struct v4l2_rect partition;
+ unsigned int current_partition;
};
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
@@ -122,6 +130,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
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(u32 fourcc);
+const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
+ u32 fourcc);
#endif /* __VSP1_PIPE_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 3b03007ba625..47b1dee044fb 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -660,6 +660,8 @@
#define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8)
#define VI6_IP_VERSION_MODEL_VSPD_GEN2 (0x0b << 8)
#define VI6_IP_VERSION_MODEL_VSPS_M2 (0x0c << 8)
+#define VI6_IP_VERSION_MODEL_VSPS_V2H (0x12 << 8)
+#define VI6_IP_VERSION_MODEL_VSPD_V2H (0x13 << 8)
#define VI6_IP_VERSION_MODEL_VSPI_GEN3 (0x14 << 8)
#define VI6_IP_VERSION_MODEL_VSPBD_GEN3 (0x15 << 8)
#define VI6_IP_VERSION_MODEL_VSPBC_GEN3 (0x16 << 8)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index 388838913205..b2e34a800ffa 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -46,34 +46,22 @@ static const struct v4l2_subdev_ops rpf_ops = {
* VSP1 Entity Operations
*/
-static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
-{
- struct vsp1_rwpf *rpf = entity_to_rwpf(entity);
-
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
- rpf->mem.addr[0] + rpf->offsets[0]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
- rpf->mem.addr[1] + rpf->offsets[1]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
- rpf->mem.addr[2] + rpf->offsets[1]);
-}
-
static void rpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->format;
const struct v4l2_mbus_framefmt *source_format;
const struct v4l2_mbus_framefmt *sink_format;
- const struct v4l2_rect *crop;
unsigned int left = 0;
unsigned int top = 0;
u32 pstride;
u32 infmt;
- if (!full) {
+ 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 |
@@ -83,34 +71,80 @@ static void rpf_configure(struct vsp1_entity *entity,
return;
}
- /* Source size, stride 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);
+ if (params == VSP1_ENTITY_PARAMS_PARTITION) {
+ unsigned int offsets[2];
+ 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) {
+ const struct v4l2_mbus_framefmt *output;
+ struct vsp1_entity *wpf = &pipe->output->entity;
+ unsigned int input_width = crop.width;
+
+ /*
+ * Scale the partition window based on the configuration
+ * of the pipeline.
+ */
+ output = vsp1_entity_get_pad_format(wpf, wpf->config,
+ RWPF_PAD_SOURCE);
- 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));
+ crop.width = pipe->partition.width * input_width
+ / output->width;
+ crop.left += pipe->partition.left * input_width
+ / output->width;
+ }
+
+ 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));
+
+ offsets[0] = crop.top * format->plane_fmt[0].bytesperline
+ + crop.left * fmtinfo->bpp[0] / 8;
+
+ if (format->num_planes > 1)
+ offsets[1] = crop.top * format->plane_fmt[1].bytesperline
+ + crop.left / fmtinfo->hsub
+ * fmtinfo->bpp[1] / 8;
+ else
+ offsets[1] = 0;
+
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
+ rpf->mem.addr[0] + offsets[0]);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
+ rpf->mem.addr[1] + offsets[1]);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
+ rpf->mem.addr[2] + offsets[1]);
+ return;
+ }
- rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
- + crop->left * fmtinfo->bpp[0] / 8;
+ /* Stride */
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
-
- if (format->num_planes > 1) {
- rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
- + crop->left * fmtinfo->bpp[1] / 8;
+ if (format->num_planes > 1)
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
- } else {
- rpf->offsets[1] = 0;
- }
vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
@@ -215,7 +249,6 @@ static void rpf_configure(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations rpf_entity_ops = {
- .set_memory = rpf_set_memory,
.configure = rpf_configure,
};
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index 8d461b375e91..66e4d7ea31d6 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -66,11 +66,15 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
+ int ret = 0;
+
+ mutex_lock(&rwpf->entity.lock);
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which);
- if (!config)
- return -EINVAL;
+ 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 &&
@@ -85,7 +89,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
*/
format->code = fmt->format.code;
fmt->format = *format;
- return 0;
+ goto done;
}
format->code = fmt->format.code;
@@ -98,19 +102,25 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
fmt->format = *format;
- /* Update the sink crop rectangle. */
- crop = vsp1_rwpf_get_crop(rwpf, config);
- crop->left = 0;
- crop->top = 0;
- crop->width = fmt->format.width;
- crop->height = fmt->format.height;
+ if (rwpf->entity.type == VSP1_ENTITY_RPF) {
+ struct v4l2_rect *crop;
+
+ /* Update the sink crop rectangle. */
+ crop = vsp1_rwpf_get_crop(rwpf, config);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+ }
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SOURCE);
*format = fmt->format;
- return 0;
+done:
+ mutex_unlock(&rwpf->entity.lock);
+ return ret;
}
static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
@@ -120,14 +130,22 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
- /* Cropping is implemented on the sink pad. */
- if (sel->pad != RWPF_PAD_SINK)
+ /*
+ * Cropping is only supported on the RPF and is implemented on the sink
+ * pad.
+ */
+ if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
return -EINVAL;
+ mutex_lock(&rwpf->entity.lock);
+
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
@@ -144,10 +162,13 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
- return 0;
+done:
+ mutex_unlock(&rwpf->entity.lock);
+ return ret;
}
static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
@@ -158,21 +179,27 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
+ int ret = 0;
- /* Cropping is implemented on the sink pad. */
- if (sel->pad != RWPF_PAD_SINK)
+ /*
+ * Cropping is only supported on the RPF and is implemented on the sink
+ * pad.
+ */
+ if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
+ mutex_lock(&rwpf->entity.lock);
+
config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
- /* Make sure the crop rectangle is entirely contained in the image. The
- * WPF top and left offsets are limited to 255.
- */
+ /* Make sure the crop rectangle is entirely contained in the image. */
format = vsp1_entity_get_pad_format(&rwpf->entity, config,
RWPF_PAD_SINK);
@@ -188,10 +215,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
- if (rwpf->entity.type == VSP1_ENTITY_WPF) {
- sel->r.left = min_t(unsigned int, sel->r.left, 255);
- sel->r.top = min_t(unsigned int, sel->r.top, 255);
- }
sel->r.width = min_t(unsigned int, sel->r.width,
format->width - sel->r.left);
sel->r.height = min_t(unsigned int, sel->r.height,
@@ -206,7 +229,9 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
format->width = crop->width;
format->height = crop->height;
- return 0;
+done:
+ mutex_unlock(&rwpf->entity.lock);
+ return ret;
}
const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index cb20484e80da..1c98aff3da5d 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -61,7 +61,6 @@ struct vsp1_rwpf {
unsigned int active;
} flip;
- unsigned int offsets[2];
struct vsp1_rwpf_memory mem;
struct vsp1_dl_manager *dlm;
@@ -86,17 +85,5 @@ extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops;
struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
struct v4l2_subdev_pad_config *config);
-/**
- * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF
- * @rwpf: the [RW]PF instance
- * @dl: the display list
- *
- * This function applies the cached memory buffer address to the display list.
- */
-static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf,
- struct vsp1_dl_list *dl)
-{
- rwpf->entity.ops->set_memory(&rwpf->entity, dl);
-}
#endif /* __VSP1_RWPF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index 47f5e0cea2ce..b4e568a3b4ed 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -128,6 +128,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which);
if (!config)
@@ -135,8 +136,12 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK);
- if (fse->index || fse->code != format->code)
- return -EINVAL;
+ mutex_lock(&sru->entity.lock);
+
+ if (fse->index || fse->code != format->code) {
+ ret = -EINVAL;
+ goto done;
+ }
if (fse->pad == SRU_PAD_SINK) {
fse->min_width = SRU_MIN_SIZE;
@@ -156,7 +161,9 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
}
}
- return 0;
+done:
+ mutex_unlock(&sru->entity.lock);
+ return ret;
}
static void sru_try_format(struct vsp1_sru *sru,
@@ -217,10 +224,15 @@ static int sru_set_format(struct v4l2_subdev *subdev,
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ mutex_lock(&sru->entity.lock);
config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
sru_try_format(sru, config, fmt->pad, &fmt->format);
@@ -236,7 +248,9 @@ static int sru_set_format(struct v4l2_subdev *subdev,
sru_try_format(sru, config, SRU_PAD_SOURCE, format);
}
- return 0;
+done:
+ mutex_unlock(&sru->entity.lock);
+ return ret;
}
static const struct v4l2_subdev_pad_ops sru_pad_ops = {
@@ -257,7 +271,8 @@ static const struct v4l2_subdev_ops sru_ops = {
static void sru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
const struct vsp1_sru_param *param;
struct vsp1_sru *sru = to_sru(&entity->subdev);
@@ -265,7 +280,7 @@ static void sru_configure(struct vsp1_entity *entity,
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
- if (!full)
+ if (params != VSP1_ENTITY_PARAMS_INIT)
return;
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
@@ -291,8 +306,27 @@ static void sru_configure(struct vsp1_entity *entity,
vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2);
}
+static unsigned int sru_max_width(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_sru *sru = to_sru(&entity->subdev);
+ struct v4l2_mbus_framefmt *input;
+ struct v4l2_mbus_framefmt *output;
+
+ 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,
+ SRU_PAD_SOURCE);
+
+ if (input->width != output->width)
+ return 512;
+ else
+ return 256;
+}
+
static const struct vsp1_entity_operations sru_entity_ops = {
.configure = sru_configure,
+ .max_width = sru_max_width,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index 652dcd895022..da8f89a31ea4 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -18,6 +18,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_pipe.h"
#include "vsp1_uds.h"
#define UDS_MIN_SIZE 4U
@@ -133,6 +134,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which);
if (!config)
@@ -141,8 +143,12 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
format = vsp1_entity_get_pad_format(&uds->entity, config,
UDS_PAD_SINK);
- if (fse->index || fse->code != format->code)
- return -EINVAL;
+ mutex_lock(&uds->entity.lock);
+
+ if (fse->index || fse->code != format->code) {
+ ret = -EINVAL;
+ goto done;
+ }
if (fse->pad == UDS_PAD_SINK) {
fse->min_width = UDS_MIN_SIZE;
@@ -156,7 +162,9 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
&fse->max_height);
}
- return 0;
+done:
+ mutex_unlock(&uds->entity.lock);
+ return ret;
}
static void uds_try_format(struct vsp1_uds *uds,
@@ -202,10 +210,15 @@ static int uds_set_format(struct v4l2_subdev *subdev,
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ mutex_lock(&uds->entity.lock);
config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which);
- if (!config)
- return -EINVAL;
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
uds_try_format(uds, config, fmt->pad, &fmt->format);
@@ -221,7 +234,9 @@ static int uds_set_format(struct v4l2_subdev *subdev,
uds_try_format(uds, config, UDS_PAD_SOURCE, format);
}
- return 0;
+done:
+ mutex_unlock(&uds->entity.lock);
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -246,7 +261,8 @@ static const struct v4l2_subdev_ops uds_ops = {
static void uds_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
@@ -255,7 +271,16 @@ static void uds_configure(struct vsp1_entity *entity,
unsigned int vscale;
bool multitap;
- if (!full)
+ if (params == VSP1_ENTITY_PARAMS_PARTITION) {
+ const struct v4l2_rect *clip = &pipe->partition;
+
+ vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE,
+ (clip->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (clip->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+ return;
+ }
+
+ if (params != VSP1_ENTITY_PARAMS_INIT)
return;
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
@@ -287,17 +312,39 @@ static void uds_configure(struct vsp1_entity *entity,
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
- /* Set the scaling ratios and the output size. */
+ /* Set the scaling ratios. */
vsp1_uds_write(uds, dl, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
- vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE,
- (output->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)
+{
+ struct vsp1_uds *uds = to_uds(&entity->subdev);
+ const struct v4l2_mbus_framefmt *output;
+ const struct v4l2_mbus_framefmt *input;
+ unsigned int hscale;
+
+ input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
+ UDS_PAD_SINK);
+ output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
+ UDS_PAD_SOURCE);
+ hscale = output->width / input->width;
+
+ if (hscale <= 2)
+ return 256;
+ else if (hscale <= 4)
+ return 512;
+ else if (hscale <= 8)
+ return 1024;
+ else
+ return 2048;
}
static const struct vsp1_entity_operations uds_entity_ops = {
.configure = uds_configure,
+ .max_width = uds_max_width,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 9fb4fc26a359..d351b9c768d2 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -117,9 +117,9 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
/* Retrieve format information and select the default format if the
* requested format isn't supported.
*/
- info = vsp1_get_format_info(pix->pixelformat);
+ info = vsp1_get_format_info(video->vsp1, pix->pixelformat);
if (info == NULL)
- info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+ info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
pix->pixelformat = info->fourcc;
pix->colorspace = V4L2_COLORSPACE_SRGB;
@@ -169,6 +169,113 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
}
/* -----------------------------------------------------------------------------
+ * VSP1 Partition Algorithm support
+ */
+
+static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_entity *entity;
+ unsigned int div_size;
+
+ format = vsp1_entity_get_pad_format(&pipe->output->entity,
+ pipe->output->entity.config,
+ RWPF_PAD_SOURCE);
+ div_size = format->width;
+
+ /* Gen2 hardware doesn't require image partitioning. */
+ if (vsp1->info->gen == 2) {
+ pipe->div_size = div_size;
+ pipe->partitions = 1;
+ return;
+ }
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ unsigned int entity_max = VSP1_VIDEO_MAX_WIDTH;
+
+ if (entity->ops->max_width) {
+ entity_max = entity->ops->max_width(entity, pipe);
+ if (entity_max)
+ div_size = min(div_size, entity_max);
+ }
+ }
+
+ pipe->div_size = div_size;
+ pipe->partitions = DIV_ROUND_UP(format->width, div_size);
+}
+
+/**
+ * vsp1_video_partition - Calculate the active partition output window
+ *
+ * @div_size: pre-determined maximum partition division size
+ * @index: partition index
+ *
+ * Returns a v4l2_rect describing the partition window.
+ */
+static struct v4l2_rect vsp1_video_partition(struct vsp1_pipeline *pipe,
+ unsigned int div_size,
+ unsigned int index)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect partition;
+ unsigned int modulus;
+
+ format = vsp1_entity_get_pad_format(&pipe->output->entity,
+ pipe->output->entity.config,
+ RWPF_PAD_SOURCE);
+
+ /* A single partition simply processes the output size in full. */
+ if (pipe->partitions <= 1) {
+ partition.left = 0;
+ partition.top = 0;
+ partition.width = format->width;
+ partition.height = format->height;
+ return partition;
+ }
+
+ /* Initialise the partition with sane starting conditions. */
+ partition.left = index * div_size;
+ partition.top = 0;
+ partition.width = div_size;
+ partition.height = format->height;
+
+ modulus = format->width % div_size;
+
+ /*
+ * We need to prevent the last partition from being smaller than the
+ * *minimum* width of the hardware capabilities.
+ *
+ * If the modulus is less than half of the partition size,
+ * the penultimate partition is reduced to half, which is added
+ * to the final partition: |1234|1234|1234|12|341|
+ * to prevents this: |1234|1234|1234|1234|1|.
+ */
+ if (modulus) {
+ /*
+ * pipe->partitions is 1 based, whilst index is a 0 based index.
+ * Normalise this locally.
+ */
+ unsigned int partitions = pipe->partitions - 1;
+
+ if (modulus < div_size / 2) {
+ if (index == partitions - 1) {
+ /* Halve the penultimate partition. */
+ partition.width = div_size / 2;
+ } else if (index == partitions) {
+ /* Increase the final partition. */
+ partition.width = (div_size / 2) + modulus;
+ partition.left -= div_size / 2;
+ }
+ } else if (index == partitions) {
+ partition.width = modulus;
+ }
+ }
+
+ return partition;
+}
+
+/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -234,44 +341,81 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
{
struct vsp1_video *video = rwpf->video;
struct vsp1_vb2_buffer *buf;
- unsigned long flags;
buf = vsp1_video_complete_buffer(video);
if (buf == NULL)
return;
- spin_lock_irqsave(&pipe->irqlock, flags);
-
video->rwpf->mem = buf->mem;
pipe->buffers_ready |= 1 << video->pipe_index;
+}
- spin_unlock_irqrestore(&pipe->irqlock, flags);
+static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl)
+{
+ struct vsp1_entity *entity;
+
+ pipe->partition = vsp1_video_partition(pipe, pipe->div_size,
+ pipe->current_partition);
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ if (entity->ops->configure)
+ entity->ops->configure(entity, pipe, dl,
+ VSP1_ENTITY_PARAMS_PARTITION);
+ }
}
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
struct vsp1_entity *entity;
- unsigned int i;
if (!pipe->dl)
pipe->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.
+ */
list_for_each_entry(entity, &pipe->entities, list_pipe) {
if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl, false);
+ entity->ops->configure(entity, pipe, pipe->dl,
+ VSP1_ENTITY_PARAMS_RUNTIME);
}
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
- struct vsp1_rwpf *rwpf = pipe->inputs[i];
+ /* Run the first partition */
+ pipe->current_partition = 0;
+ vsp1_video_pipeline_run_partition(pipe, pipe->dl);
- if (rwpf)
- vsp1_rwpf_set_memory(rwpf, pipe->dl);
- }
+ /* Process consecutive partitions as necessary */
+ for (pipe->current_partition = 1;
+ pipe->current_partition < pipe->partitions;
+ pipe->current_partition++) {
+ struct vsp1_dl_list *dl;
- if (!pipe->lif)
- vsp1_rwpf_set_memory(pipe->output, pipe->dl);
+ /*
+ * Partition configuration operations will utilise
+ * the pipe->current_partition variable to determine
+ * the work they should complete.
+ */
+ dl = 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) {
+ dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n");
+ break;
+ }
+
+ vsp1_video_pipeline_run_partition(pipe, dl);
+ vsp1_dl_list_add_chain(pipe->dl, dl);
+ }
+ /* Complete, and commit the head display list. */
vsp1_dl_list_commit(pipe->dl);
pipe->dl = NULL;
@@ -285,6 +429,8 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe)
unsigned long flags;
unsigned int i;
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
/* Complete buffers on all video nodes. */
for (i = 0; i < vsp1->info->rpf_count; ++i) {
if (!pipe->inputs[i])
@@ -295,8 +441,6 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe)
vsp1_video_frame_end(pipe, pipe->output);
- spin_lock_irqsave(&pipe->irqlock, flags);
-
state = pipe->state;
pipe->state = VSP1_PIPELINE_STOPPED;
@@ -607,6 +751,9 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
{
struct vsp1_entity *entity;
+ /* Determine this pipelines sizes for image partitioning support. */
+ vsp1_video_pipeline_setup_partitions(pipe);
+
/* Prepare the display list. */
pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
if (!pipe->dl)
@@ -634,7 +781,8 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
vsp1_entity_route_setup(entity, pipe->dl);
if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl, true);
+ entity->ops->configure(entity, pipe, pipe->dl,
+ VSP1_ENTITY_PARAMS_INIT);
}
return 0;
@@ -675,6 +823,14 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
unsigned long flags;
int ret;
+ /*
+ * Clear the buffers ready flag to make sure the device won't be started
+ * by a QBUF on the video node on the other side of the pipeline.
+ */
+ spin_lock_irqsave(&video->irqlock, flags);
+ pipe->buffers_ready &= ~(1 << video->pipe_index);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
mutex_lock(&pipe->lock);
if (--pipe->stream_count == pipe->num_inputs) {
/* Stop the pipeline. */
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 31983169c24a..7c48f81cd5c1 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -173,58 +173,28 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)
vsp1_dlm_destroy(wpf->dlm);
}
-static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
-{
- struct vsp1_rwpf *wpf = entity_to_rwpf(entity);
- const struct v4l2_pix_format_mplane *format = &wpf->format;
- struct vsp1_rwpf_memory mem = wpf->mem;
- unsigned int flip = wpf->flip.active;
- unsigned int offset;
-
- /* 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 different corners of the image
- * depending on vertical flipping. Horizontal flipping is handled
- * through a line buffer and doesn't modify the start address.
- */
- if (flip & BIT(WPF_CTRL_VFLIP)) {
- mem.addr[0] += (format->height - 1)
- * format->plane_fmt[0].bytesperline;
-
- if (format->num_planes > 1) {
- offset = (format->height / wpf->fmtinfo->vsub - 1)
- * format->plane_fmt[1].bytesperline;
- mem.addr[1] += offset;
- mem.addr[2] += offset;
- }
- }
-
- 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]);
-}
-
static void wpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, bool full)
+ struct vsp1_dl_list *dl,
+ enum vsp1_entity_params params)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
const struct v4l2_mbus_framefmt *source_format;
const struct v4l2_mbus_framefmt *sink_format;
- const struct v4l2_rect *crop;
unsigned int i;
u32 outfmt = 0;
u32 srcrpf = 0;
- if (!full) {
+ if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
const unsigned int mask = BIT(WPF_CTRL_VFLIP)
| BIT(WPF_CTRL_HFLIP);
+ unsigned long flags;
- spin_lock(&wpf->flip.lock);
+ spin_lock_irqsave(&wpf->flip.lock, flags);
wpf->flip.active = (wpf->flip.active & ~mask)
| (wpf->flip.pending & mask);
- spin_unlock(&wpf->flip.lock);
+ spin_unlock_irqrestore(&wpf->flip.lock, flags);
outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
@@ -237,17 +207,6 @@ static void wpf_configure(struct vsp1_entity *entity,
return;
}
- /* Cropping */
- crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config);
-
- vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
- (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT));
- vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
- (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT));
-
- /* Format */
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
wpf->entity.config,
RWPF_PAD_SINK);
@@ -255,6 +214,80 @@ static void wpf_configure(struct vsp1_entity *entity,
wpf->entity.config,
RWPF_PAD_SOURCE);
+ if (params == VSP1_ENTITY_PARAMS_PARTITION) {
+ const struct v4l2_pix_format_mplane *format = &wpf->format;
+ struct vsp1_rwpf_memory mem = wpf->mem;
+ unsigned int flip = wpf->flip.active;
+ unsigned int width = source_format->width;
+ unsigned int height = source_format->height;
+ unsigned int offset;
+
+ /*
+ * Cropping. The partition algorithm can split the image into
+ * multiple slices.
+ */
+ if (pipe->partitions > 1)
+ width = pipe->partition.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 different corners
+ * of the image depending on vertical flipping.
+ */
+ if (pipe->partitions > 1) {
+ const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
+
+ /*
+ * Horizontal flipping is handled through a line buffer
+ * and doesn't modify the start address, but still needs
+ * to be handled when image partitioning is in effect to
+ * order the partitions correctly.
+ */
+ if (flip & BIT(WPF_CTRL_HFLIP))
+ offset = format->width - pipe->partition.left
+ - pipe->partition.width;
+ else
+ offset = pipe->partition.left;
+
+ mem.addr[0] += offset * fmtinfo->bpp[0] / 8;
+ if (format->num_planes > 1) {
+ mem.addr[1] += offset / fmtinfo->hsub
+ * fmtinfo->bpp[1] / 8;
+ mem.addr[2] += offset / fmtinfo->hsub
+ * fmtinfo->bpp[2] / 8;
+ }
+ }
+
+ if (flip & BIT(WPF_CTRL_VFLIP)) {
+ mem.addr[0] += (format->height - 1)
+ * format->plane_fmt[0].bytesperline;
+
+ if (format->num_planes > 1) {
+ offset = (format->height / wpf->fmtinfo->vsub - 1)
+ * format->plane_fmt[1].bytesperline;
+ mem.addr[1] += offset;
+ mem.addr[2] += offset;
+ }
+ }
+
+ 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;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
@@ -318,12 +351,11 @@ static void wpf_configure(struct vsp1_entity *entity,
/* 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),
- VI6_WFP_IRQ_ENB_FREE);
+ VI6_WFP_IRQ_ENB_DFEE);
}
static const struct vsp1_entity_operations wpf_entity_ops = {
.destroy = vsp1_wpf_destroy,
- .set_memory = wpf_set_memory,
.configure = wpf_configure,
};
@@ -360,7 +392,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
return ERR_PTR(ret);
/* Initialize the display list manager. */
- wpf->dlm = vsp1_dlm_create(vsp1, index, 4);
+ wpf->dlm = vsp1_dlm_create(vsp1, index, 64);
if (!wpf->dlm) {
ret = -ENOMEM;
goto error;