* @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;
unsigned int div_size;
unsigned int partitions;
+ struct v4l2_rect partition;
+ unsigned int current_partition;
};
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
}
if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- const struct v4l2_rect *crop;
unsigned int offsets[2];
+ struct v4l2_rect crop;
/* Source size and crop offsets.
*
* offsets are needed, as planes 2 and 3 always have identical
* strides.
*/
- crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+ 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);
+
+ 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));
+ (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 << 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;
+ 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
+ offsets[1] = crop.top * format->plane_fmt[1].bytesperline
+ + crop.left / fmtinfo->hsub
* fmtinfo->bpp[1] / 8;
else
offsets[1] = 0;
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_pipe.h"
#include "vsp1_uds.h"
#define UDS_MIN_SIZE 4U
unsigned int vscale;
bool multitap;
+ 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;
(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,
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
*/
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;
/* 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));
/* 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.
+ * 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;