diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_video.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 121 |
1 files changed, 118 insertions, 3 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b903cc5471e0..15d08cb50bd1 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -205,6 +205,74 @@ static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) 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 */ @@ -280,22 +348,69 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, pipe->buffers_ready |= 1 << video->pipe_index; } +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; 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) { + if (entity->ops->configure) entity->ops->configure(entity, pipe, pipe->dl, VSP1_ENTITY_PARAMS_RUNTIME); - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_PARTITION); + } + + /* Run the first partition */ + pipe->current_partition = 0; + vsp1_video_pipeline_run_partition(pipe, 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; + + /* 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; |