diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2016-12-15 21:36:09 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2016-12-15 21:36:09 -0800 |
commit | ebfb0184ef560897fad35005989e82433419202c (patch) | |
tree | 226a8195fdae6c79d90d76baa1cbdaf80f794bb0 /drivers/media/platform/vsp1 | |
parent | f43d3ec3a889c7f6a196f3b6d6b13345ee46af8a (diff) | |
parent | 5191d88acc688743eef56f1c598a4e4cddf6c6cd (diff) | |
download | linux-ebfb0184ef560897fad35005989e82433419202c.tar.gz linux-ebfb0184ef560897fad35005989e82433419202c.tar.bz2 linux-ebfb0184ef560897fad35005989e82433419202c.zip |
Merge branch 'synaptics-rmi4' into next
Merge updated Synaptics RMI4 support, including support for SMBus
controllers and flashing firmware.
Diffstat (limited to 'drivers/media/platform/vsp1')
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; |