diff options
Diffstat (limited to 'drivers/media')
21 files changed, 221 insertions, 92 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_brx.c b/drivers/media/platform/vsp1/vsp1_brx.c index 58ad248cd7f7..2d86c718a5cf 100644 --- a/drivers/media/platform/vsp1/vsp1_brx.c +++ b/drivers/media/platform/vsp1/vsp1_brx.c @@ -283,6 +283,7 @@ static const struct v4l2_subdev_ops brx_ops = { static void brx_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_brx *brx = to_brx(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index 942fc14c19d1..a47b23bf5abf 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -171,6 +171,7 @@ static const struct v4l2_subdev_ops clu_ops = { static void clu_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_clu *clu = to_clu(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 26289adaf658..104b6f514536 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -178,7 +178,7 @@ struct vsp1_dl_cmd_pool { * @post_cmd: post command to be issued through extended dl header * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain - * @internal: whether the display list is used for internal purpose + * @flags: display list flags, a combination of VSP1_DL_FRAME_END_* */ struct vsp1_dl_list { struct list_head list; @@ -197,7 +197,7 @@ struct vsp1_dl_list { bool has_chain; struct list_head chain; - bool internal; + unsigned int flags; }; /** @@ -699,8 +699,8 @@ struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) * which bodies are added. * * Adding a body to a display list passes ownership of the body to the list. The - * caller retains its reference to the fragment when adding it to the display - * list, but is not allowed to add new entries to the body. + * caller retains its reference to the body when adding it to the display list, + * but is not allowed to add new entries to the body. * * The reference must be explicitly released by a call to vsp1_dl_body_put() * when the body isn't needed anymore. @@ -770,17 +770,35 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) } dl->header->num_lists = num_lists; + dl->header->flags = 0; - if (!list_empty(&dl->chain) && !is_last) { + /* + * Enable the interrupt for the end of each frame. In continuous mode + * chained lists are used with one list per frame, so enable the + * interrupt for each list. In singleshot mode chained lists are used + * to partition a single frame, so enable the interrupt for the last + * list only. + */ + if (!dlm->singleshot || is_last) + dl->header->flags |= VSP1_DLH_INT_ENABLE; + + /* + * In continuous mode enable auto-start for all lists, as the VSP must + * loop on the same list until a new one is queued. In singleshot mode + * enable auto-start for all lists but the last to chain processing of + * partitions without software intervention. + */ + if (!dlm->singleshot || !is_last) + dl->header->flags |= VSP1_DLH_AUTO_START; + + if (!is_last) { /* - * If this display list's chain is not empty, we are on a list, - * and the next item is the display list that we must queue for - * automatic processing by the hardware. + * If this is not the last display list in the chain, queue the + * next item for automatic processing by the hardware. */ struct vsp1_dl_list *next = list_next_entry(dl, chain); dl->header->next_header = next->dma; - dl->header->flags = VSP1_DLH_AUTO_START; } else if (!dlm->singleshot) { /* * if the display list manager works in continuous mode, the VSP @@ -788,13 +806,6 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) * instructed to do otherwise. */ dl->header->next_header = dl->dma; - dl->header->flags = VSP1_DLH_INT_ENABLE | VSP1_DLH_AUTO_START; - } else { - /* - * Otherwise, in mem-to-mem mode, we work in single-shot mode - * and the next display list must not be started automatically. - */ - dl->header->flags = VSP1_DLH_INT_ENABLE; } if (!dl->extension) @@ -861,13 +872,15 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) * * If a display list is already pending we simply drop it as the new * display list is assumed to contain a more recent configuration. It is - * an error if the already pending list has the internal flag set, as - * there is then a process waiting for that list to complete. This - * shouldn't happen as the waiting process should perform proper - * locking, but warn just in case. + * an error if the already pending list has the + * VSP1_DL_FRAME_END_INTERNAL flag set, as there is then a process + * waiting for that list to complete. This shouldn't happen as the + * waiting process should perform proper locking, but warn just in + * case. */ if (vsp1_dl_list_hw_update_pending(dlm)) { - WARN_ON(dlm->pending && dlm->pending->internal); + WARN_ON(dlm->pending && + (dlm->pending->flags & VSP1_DL_FRAME_END_INTERNAL)); __vsp1_dl_list_put(dlm->pending); dlm->pending = dl; return; @@ -897,7 +910,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) dlm->active = dl; } -void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_next; @@ -912,7 +925,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) vsp1_dl_list_fill_header(dl_next, last); } - dl->internal = internal; + dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; spin_lock_irqsave(&dlm->lock, flags); @@ -941,9 +954,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) * set in single-shot mode as display list processing is then not continuous and * races never occur. * - * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list - * has completed and had been queued with the internal notification flag. - * Internal notification is only supported for continuous mode. + * The following flags are only supported for continuous mode. + * + * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just + * became active had been queued with the internal notification flag. + * + * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active + * display list had been queued with the writeback flag. */ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { @@ -982,13 +999,24 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) goto done; /* + * If the active display list has the writeback flag set, the frame + * completion marks the end of the writeback capture. Return the + * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's + * writeback flag. + */ + if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) { + flags |= VSP1_DL_FRAME_END_WRITEBACK; + dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK; + } + + /* * The device starts processing the queued display list right after the * frame end interrupt. The display list thus becomes active. */ if (dlm->queued) { - if (dlm->queued->internal) + if (dlm->queued->flags & VSP1_DL_FRAME_END_INTERNAL) flags |= VSP1_DL_FRAME_END_INTERNAL; - dlm->queued->internal = false; + dlm->queued->flags &= ~VSP1_DL_FRAME_END_INTERNAL; __vsp1_dl_list_put(dlm->active); dlm->active = dlm->queued; diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 125750dc8b5c..bebe16483ca5 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -17,8 +17,10 @@ struct vsp1_dl_body_pool; struct vsp1_dl_list; struct vsp1_dl_manager; +/* Keep these flags in sync with VSP1_DU_STATUS_* in include/media/vsp1.h. */ #define VSP1_DL_FRAME_END_COMPLETED BIT(0) -#define VSP1_DL_FRAME_END_INTERNAL BIT(1) +#define VSP1_DL_FRAME_END_WRITEBACK BIT(1) +#define VSP1_DL_FRAME_END_INTERNAL BIT(2) /** * struct vsp1_dl_ext_cmd - Extended Display command @@ -61,7 +63,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); void vsp1_dl_list_put(struct vsp1_dl_list *dl); struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl); struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl); -void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal); +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags); struct vsp1_dl_body_pool * vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 84895385d2e5..a4a45d68a6ef 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -34,14 +34,16 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, unsigned int completion) { struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); - bool complete = completion == VSP1_DL_FRAME_END_COMPLETED; if (drm_pipe->du_complete) { struct vsp1_entity *uif = drm_pipe->uif; + unsigned int status = completion + & (VSP1_DU_STATUS_COMPLETE | + VSP1_DU_STATUS_WRITEBACK); u32 crc; crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0; - drm_pipe->du_complete(drm_pipe->du_private, complete, crc); + drm_pipe->du_complete(drm_pipe->du_private, status, crc); } if (completion & VSP1_DL_FRAME_END_INTERNAL) { @@ -537,6 +539,12 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_entity *next; struct vsp1_dl_list *dl; struct vsp1_dl_body *dlb; + unsigned int dl_flags = 0; + + if (drm_pipe->force_brx_release) + dl_flags |= VSP1_DL_FRAME_END_INTERNAL; + if (pipe->output->writeback) + dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; dl = vsp1_dl_list_get(pipe->output->dlm); dlb = vsp1_dl_list_get_body0(dl); @@ -554,12 +562,42 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, pipe, dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); vsp1_entity_configure_partition(entity, pipe, dl, dlb); } - vsp1_dl_list_commit(dl, drm_pipe->force_brx_release); + vsp1_dl_list_commit(dl, dl_flags); +} + +static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, + struct vsp1_rwpf *rwpf, + u32 pixelformat, unsigned int pitch) +{ + const struct vsp1_format_info *fmtinfo; + unsigned int chroma_hsub; + + fmtinfo = vsp1_get_format_info(vsp1, pixelformat); + if (!fmtinfo) { + dev_dbg(vsp1->dev, "Unsupported pixel format %08x\n", + pixelformat); + return -EINVAL; + } + + /* + * Only formats with three planes can affect the chroma planes pitch. + * All formats with two planes have a horizontal subsampling value of 2, + * but combine U and V in a single chroma plane, which thus results in + * the luma plane and chroma plane having the same pitch. + */ + chroma_hsub = (fmtinfo->planes == 3) ? fmtinfo->hsub : 1; + + rwpf->fmtinfo = fmtinfo; + rwpf->format.num_planes = fmtinfo->planes; + rwpf->format.plane_fmt[0].bytesperline = pitch; + rwpf->format.plane_fmt[1].bytesperline = pitch / chroma_hsub; + + return 0; } /* ----------------------------------------------------------------------------- @@ -700,8 +738,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe->du_private = cfg->callback_data; /* Disable the display interrupts. */ - vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); - vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0); + vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); + vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); /* Configure all entities in the pipeline. */ vsp1_du_pipeline_configure(pipe); @@ -769,9 +807,8 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; - const struct vsp1_format_info *fmtinfo; - unsigned int chroma_hsub; struct vsp1_rwpf *rpf; + int ret; if (rpf_index >= vsp1->info->rpf_count) return -EINVAL; @@ -804,25 +841,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, * Store the format, stride, memory buffer address, crop and compose * rectangles and Z-order position and for the input. */ - fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat); - if (!fmtinfo) { - dev_dbg(vsp1->dev, "Unsupported pixel format %08x for RPF\n", - cfg->pixelformat); - return -EINVAL; - } - - /* - * Only formats with three planes can affect the chroma planes pitch. - * All formats with two planes have a horizontal subsampling value of 2, - * but combine U and V in a single chroma plane, which thus results in - * the luma plane and chroma plane having the same pitch. - */ - chroma_hsub = (fmtinfo->planes == 3) ? fmtinfo->hsub : 1; + ret = vsp1_du_pipeline_set_rwpf_format(vsp1, rpf, cfg->pixelformat, + cfg->pitch); + if (ret < 0) + return ret; - rpf->fmtinfo = fmtinfo; - rpf->format.num_planes = fmtinfo->planes; - rpf->format.plane_fmt[0].bytesperline = cfg->pitch; - rpf->format.plane_fmt[1].bytesperline = cfg->pitch / chroma_hsub; rpf->alpha = cfg->alpha; rpf->mem.addr[0] = cfg->mem[0]; @@ -851,12 +874,31 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; + int ret; drm_pipe->crc = cfg->crc; mutex_lock(&vsp1->drm->lock); + + if (cfg->writeback.pixelformat) { + const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback; + + ret = vsp1_du_pipeline_set_rwpf_format(vsp1, pipe->output, + wb_cfg->pixelformat, + wb_cfg->pitch); + if (WARN_ON(ret < 0)) + goto done; + + pipe->output->mem.addr[0] = wb_cfg->mem[0]; + pipe->output->mem.addr[1] = wb_cfg->mem[1]; + pipe->output->mem.addr[2] = wb_cfg->mem[2]; + pipe->output->writeback = true; + } + vsp1_du_pipeline_setup_inputs(vsp1, pipe); vsp1_du_pipeline_configure(pipe); + +done: mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index 8dfd274a59e2..e85ad4366fbb 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -42,7 +42,7 @@ struct vsp1_drm_pipeline { struct vsp1_du_crc_config crc; /* Frame synchronisation */ - void (*du_complete)(void *data, bool completed, u32 crc); + void (*du_complete)(void *data, unsigned int status, u32 crc); void *du_private; }; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index a54ab528b060..aa9d2286056e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -71,10 +71,11 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, void vsp1_entity_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dlb); + entity->ops->configure_stream(entity, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 97acb7795cf1..a1ceb37bb837 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -67,7 +67,9 @@ struct vsp1_route { * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. * @configure_stream: Setup the hardware parameters for the stream which do - * not vary between frames (pipeline, formats). + * not vary between frames (pipeline, formats). Note that + * the vsp1_dl_list argument is only valid for display + * pipeline and will be NULL for mem-to-mem pipelines. * @configure_frame: Configure the runtime parameters for each frame. * @configure_partition: Configure partition specific parameters. * @max_width: Return the max supported width of data that the entity can @@ -78,7 +80,7 @@ struct vsp1_route { struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_body *); + struct vsp1_dl_list *, struct vsp1_dl_body *); void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, struct vsp1_dl_list *, struct vsp1_dl_body *); void (*configure_partition)(struct vsp1_entity *, @@ -155,6 +157,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, void vsp1_entity_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); void vsp1_entity_configure_frame(struct vsp1_entity *entity, diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/vsp1/vsp1_hgo.c index 827373c25351..bf3f981f93a1 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/vsp1/vsp1_hgo.c @@ -131,6 +131,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { static void hgo_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_hgo *hgo = to_hgo(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/vsp1/vsp1_hgt.c index bb6ce6fdd5f4..aa1c718e0453 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/vsp1/vsp1_hgt.c @@ -127,6 +127,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { static void hgt_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_hgt *hgt = to_hgt(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 39ab2e0c7c18..d5ebd9d08c8a 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -129,6 +129,7 @@ static const struct v4l2_subdev_ops hsit_ops = { static void hsit_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 8b0a26335d70..14ed5d7bd061 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -84,6 +84,7 @@ static const struct v4l2_subdev_ops lif_ops = { static void lif_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { const struct v4l2_mbus_framefmt *format; diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 64c48d9459b0..9f88842d7048 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -147,6 +147,7 @@ static const struct v4l2_subdev_ops lut_ops = { static void lut_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_lut *lut = to_lut(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index f6e4157095cc..1bb1d39c60d9 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -39,12 +39,12 @@ #define VI6_WFP_IRQ_STA_DFE (1 << 1) #define VI6_WFP_IRQ_STA_FRE (1 << 0) -#define VI6_DISP_IRQ_ENB 0x0078 +#define VI6_DISP_IRQ_ENB(n) (0x0078 + (n) * 60) #define VI6_DISP_IRQ_ENB_DSTE (1 << 8) #define VI6_DISP_IRQ_ENB_MAEE (1 << 5) #define VI6_DISP_IRQ_ENB_LNEE(n) (1 << (n)) -#define VI6_DISP_IRQ_STA 0x007c +#define VI6_DISP_IRQ_STA(n) (0x007c + (n) * 60) #define VI6_DISP_IRQ_STA_DST (1 << 8) #define VI6_DISP_IRQ_STA_MAE (1 << 5) #define VI6_DISP_IRQ_STA_LNE(n) (1 << (n)) @@ -307,7 +307,7 @@ #define VI6_WPF_DSTM_ADDR_C0 0x1028 #define VI6_WPF_DSTM_ADDR_C1 0x102c -#define VI6_WPF_WRBCK_CTRL 0x1034 +#define VI6_WPF_WRBCK_CTRL(n) (0x1034 + (n) * 0x100) #define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0) /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 616afa7e165f..85587c1b6a37 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -57,6 +57,7 @@ static const struct v4l2_subdev_ops rpf_ops = { static void rpf_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 70742ecf766f..2f3582590618 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -61,6 +61,7 @@ struct vsp1_rwpf { } flip; struct vsp1_rwpf_memory mem; + bool writeback; struct vsp1_dl_manager *dlm; }; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index b1617cb1f2b9..2b65457ee12f 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -269,6 +269,7 @@ static const struct v4l2_subdev_ops sru_ops = { static void sru_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { const struct vsp1_sru_param *param; diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 27012af973b2..5fc04c082d1a 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -257,6 +257,7 @@ static const struct v4l2_subdev_ops uds_ops = { static void uds_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c index 4b58d51df231..467d1072577b 100644 --- a/drivers/media/platform/vsp1/vsp1_uif.c +++ b/drivers/media/platform/vsp1/vsp1_uif.c @@ -192,6 +192,7 @@ static const struct v4l2_subdev_ops uif_ops = { static void uif_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uif *uif = to_uif(&entity->subdev); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 7ceaf3222145..fd98e483b2f4 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -307,11 +307,6 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) * This function completes the current buffer by filling its sequence number, * time stamp and payload size, and hands it back to the videobuf core. * - * When operating in DU output mode (deep pipeline to the DU through the LIF), - * the VSP1 needs to constantly supply frames to the display. In that case, if - * no other buffer is queued, reuse the one that has just been processed instead - * of handing it back to the videobuf core. - * * Return the next queued buffer or NULL if the queue is empty. */ static struct vsp1_vb2_buffer * @@ -333,12 +328,6 @@ vsp1_video_complete_buffer(struct vsp1_video *video) done = list_first_entry(&video->irqqueue, struct vsp1_vb2_buffer, queue); - /* In DU output mode reuse the buffer if the list is singular. */ - if (pipe->lif && list_is_singular(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return done; - } - list_del(&done->queue); if (!list_empty(&video->irqqueue)) @@ -432,7 +421,7 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) } /* Complete, and commit the head display list. */ - vsp1_dl_list_commit(dl, false); + vsp1_dl_list_commit(dl, 0); pipe->configured = true; vsp1_pipeline_run(pipe); @@ -836,7 +825,8 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, pipe->stream_config); + vsp1_entity_configure_stream(entity, pipe, NULL, + pipe->stream_config); } return 0; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 32bb207b2007..208498fa6ed7 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -232,17 +232,41 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } +static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, + struct vsp1_dl_list *dl) +{ + unsigned int index = wpf->entity.index; + struct vsp1_dl_list *dl_next; + struct vsp1_dl_body *dlb; + + dl_next = vsp1_dl_list_get(wpf->dlm); + if (!dl_next) { + dev_err(wpf->entity.vsp1->dev, + "Failed to obtain a dl list, disabling writeback\n"); + return -ENOMEM; + } + + dlb = vsp1_dl_list_get_body0(dl_next); + vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0); + vsp1_dl_list_add_chain(dl, dl_next); + + return 0; +} + static void wpf_configure_stream(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; + unsigned int index = wpf->entity.index; unsigned int i; u32 outfmt = 0; u32 srcrpf = 0; + int ret; sink_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, @@ -250,8 +274,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity, source_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SOURCE); + /* Format */ - if (!pipe->lif) { + if (!pipe->lif || wpf->writeback) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -276,8 +301,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); - if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && - wpf->entity.index == 0) + if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0) vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, VI6_WPF_ROT_CTRL_LN16 | (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); @@ -288,11 +312,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity, wpf->outfmt = outfmt; - vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index), VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0); - /* * Sources. If the pipeline has a single input and BRx is not used, * configure it as the master layer. Otherwise configure all @@ -318,9 +340,26 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf); /* Enable interrupts. */ - vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0); - vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0); + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index), VI6_WFP_IRQ_ENB_DFEE); + + /* + * Configure writeback for display pipelines (the wpf writeback flag is + * never set for memory-to-memory pipelines). Start by adding a chained + * display list to disable writeback after a single frame, and process + * to enable writeback. If the display list allocation fails don't + * enable writeback as we wouldn't be able to safely disable it, + * resulting in possible memory corruption. + */ + if (wpf->writeback) { + ret = wpf_configure_writeback_chain(wpf, dl); + if (ret < 0) + wpf->writeback = false; + } + + vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), + wpf->writeback ? VI6_WPF_WRBCK_CTRL_WBMD : 0); } static void wpf_configure_frame(struct vsp1_entity *entity, @@ -362,6 +401,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; unsigned int height; + unsigned int left; unsigned int offset; unsigned int flip; unsigned int i; @@ -371,13 +411,16 @@ static void wpf_configure_partition(struct vsp1_entity *entity, RWPF_PAD_SINK); width = sink_format->width; height = sink_format->height; + left = 0; /* * Cropping. The partition algorithm can split the image into * multiple slices. */ - if (pipe->partitions > 1) + if (pipe->partitions > 1) { width = pipe->partition->wpf.width; + left = pipe->partition->wpf.left; + } vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | @@ -386,7 +429,11 @@ static void wpf_configure_partition(struct vsp1_entity *entity, (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - if (pipe->lif) + /* + * For display pipelines without writeback enabled there's no memory + * address to configure, return now. + */ + if (pipe->lif && !wpf->writeback) return; /* @@ -408,13 +455,11 @@ static void wpf_configure_partition(struct vsp1_entity *entity, flip = wpf->flip.active; if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) - offset = format->width - pipe->partition->wpf.left - - pipe->partition->wpf.width; + offset = format->width - left - width; else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) - offset = format->height - pipe->partition->wpf.left - - pipe->partition->wpf.width; + offset = format->height - left - width; else - offset = pipe->partition->wpf.left; + offset = left; for (i = 0; i < format->num_planes; ++i) { unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; @@ -436,7 +481,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, * image height. */ if (wpf->flip.rotate) - height = pipe->partition->wpf.width; + height = width; else height = format->height; @@ -477,6 +522,12 @@ static void wpf_configure_partition(struct vsp1_entity *entity, vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); + + /* + * Writeback operates in single-shot mode and lasts for a single frame, + * reset the writeback flag to false for the next frame. + */ + wpf->writeback = false; } static unsigned int wpf_max_width(struct vsp1_entity *entity, |