On 01/03/2019 17:08, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham+rene...@ideasonboard.com>
> 
> To improve image quality when scaling using the UDS we need to correctly
> determine the start phase value for each partition window, and apply a
> margin to overlap discontinous pixels.
> 
> Provide helper functions for calculating the phase parameters, and source
> locations for a given output position and use these values to calculate our
> parition window parameters.
> 
> Extend the partition algorithm to sweep first backwards, then forwards
> through the entity list. Each entity is given the opportunity to expand it's 
> window on the reverse sweep, and clip


An oddly long line there - I'll rewrap on next iteration.

> or increase the offset on the forwards sweep.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham+rene...@ideasonboard.com>
> 
> ---
> v2:
>  - Configure HSTP and HEDP in uds_configure_partition for single partitions
>  - refactored to use individual functions for various phase and position 
> calculations
>  - squashed forwards and backwards propagation work to a single patch
>  - Fixed a few 'off-by-ones'
>  - considerable other changes :)
> ---
>  drivers/media/platform/vsp1/vsp1_entity.h |   2 +-
>  drivers/media/platform/vsp1/vsp1_pipe.c   |  40 +++++-
>  drivers/media/platform/vsp1/vsp1_pipe.h   |   6 +
>  drivers/media/platform/vsp1/vsp1_rpf.c    |   8 +-
>  drivers/media/platform/vsp1/vsp1_sru.c    |  37 +++++-
>  drivers/media/platform/vsp1/vsp1_uds.c    | 151 +++++++++++++++++++++-
>  drivers/media/platform/vsp1/vsp1_video.c  |   1 +
>  drivers/media/platform/vsp1/vsp1_wpf.c    |  16 ++-
>  8 files changed, 242 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/platform/vsp1/vsp1_entity.h 
> b/drivers/media/platform/vsp1/vsp1_entity.h
> index 97acb7795cf1..772492877764 100644
> --- a/drivers/media/platform/vsp1/vsp1_entity.h
> +++ b/drivers/media/platform/vsp1/vsp1_entity.h
> @@ -88,7 +88,7 @@ struct vsp1_entity_operations {
>       unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
>       void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *,
>                         struct vsp1_partition *, unsigned int,
> -                       struct vsp1_partition_window *);
> +                       struct vsp1_partition_window *, bool);
>  };
>  
>  struct vsp1_entity {
> diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c 
> b/drivers/media/platform/vsp1/vsp1_pipe.c
> index f1bd21a01bcd..137ebe0ecad2 100644
> --- a/drivers/media/platform/vsp1/vsp1_pipe.c
> +++ b/drivers/media/platform/vsp1/vsp1_pipe.c
> @@ -375,10 +375,32 @@ bool vsp1_pipeline_partitioned(struct vsp1_pipeline 
> *pipe)
>  /*
>   * Propagate the partition calculations through the pipeline
>   *
> - * Work backwards through the pipe, allowing each entity to update the 
> partition
> - * parameters based on its configuration, and the entity connected to its
> - * source. Each entity must produce the partition required for the previous
> - * entity in the pipeline.
> + * Work backwards through the pipe, allowing each entity to update the
> + * partition parameters based on its configuration. Each entity must produce
> + * the partition window required for the previous entity in the pipeline
> + * to generate. This window can be passed through if no changes are 
> necessary.
> + *
> + * Entities are processed in reverse order:
> + *   DDDD = Destination pixels
> + *   SSSS = Source pixels
> + *   ==== = Intermediate pixels
> + *   ____ = Disposable pixels
> + *
> + * WPF                           |DDDD|      WPF determines it's required 
> partition
> + * SRU                           |====|      Interconnected entities pass 
> through
> + * UDS(source)                  |<====>|     UDS Source requests overlap
> + * UDS(sink)         |<-|======|->|  UDS Sink calculates input size
> + * RPF                       |__SSSSSSSS__|  RPF provides extra pixels
> + *
> + * Then work forwards through the pipe allowing entities to communicate any
> + * clipping required based on any overlap and expansions they may have
> + * generated.
> + *
> + * RPF                       |__SSSSSSSS__|  Partition window is propagated 
> forwards
> + * UDS(sink)         |============|
> + * UDS(source)                  |<====>|     UDS Source reports overlap
> + * SRU                          |======|     Interconnected entities are 
> updated
> + * WPF                          |_DDDD_|     WPF handles clipping
>   */
>  void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
>                                      struct vsp1_partition *partition,
> @@ -387,10 +409,18 @@ void vsp1_pipeline_propagate_partition(struct 
> vsp1_pipeline *pipe,
>  {
>       struct vsp1_entity *entity;
>  
> +     /* Move backwards through the pipeline to propagate any expansion. */
>       list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) {
>               if (entity->ops->partition)
>                       entity->ops->partition(entity, pipe, partition, index,
> -                                            window);
> +                                            window, false);
> +     }
> +
> +     /* Move forwards through the pipeline and propagate any updates. */
> +     list_for_each_entry(entity, &pipe->entities, list_pipe) {
> +             if (entity->ops->partition)
> +                     entity->ops->partition(entity, pipe, partition, index,
> +                                            window, true);
>       }
>  }
>  
> diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h 
> b/drivers/media/platform/vsp1/vsp1_pipe.h
> index dd8b2cdc6452..3e263a60f79b 100644
> --- a/drivers/media/platform/vsp1/vsp1_pipe.h
> +++ b/drivers/media/platform/vsp1/vsp1_pipe.h
> @@ -58,10 +58,12 @@ enum vsp1_pipeline_state {
>   * @left: horizontal coordinate of the partition start in pixels relative to 
> the
>   *     left edge of the image
>   * @width: partition width in pixels
> + * @offset: The number of pixels from the left edge of the window to clip
>   */
>  struct vsp1_partition_window {
>       unsigned int left;
>       unsigned int width;
> +     unsigned int offset;
>  };
>  
>  /*
> @@ -71,6 +73,8 @@ struct vsp1_partition_window {
>   * @uds_source: The UDS output partition window configuration
>   * @sru: The SRU partition window configuration
>   * @wpf: The WPF partition window configuration
> + * @start_phase: The UDS start phase for this partition
> + * @end_phase: The UDS end phase for this partition
>   */
>  struct vsp1_partition {
>       struct vsp1_partition_window rpf;
> @@ -78,6 +82,8 @@ struct vsp1_partition {
>       struct vsp1_partition_window uds_source;
>       struct vsp1_partition_window sru;
>       struct vsp1_partition_window wpf;
> +     unsigned int start_phase;
> +     unsigned int end_phase;
>  };
>  
>  /*
> diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c 
> b/drivers/media/platform/vsp1/vsp1_rpf.c
> index ef9bf5dd55a0..46d270644fe2 100644
> --- a/drivers/media/platform/vsp1/vsp1_rpf.c
> +++ b/drivers/media/platform/vsp1/vsp1_rpf.c
> @@ -324,9 +324,13 @@ static void rpf_partition(struct vsp1_entity *entity,
>                         struct vsp1_pipeline *pipe,
>                         struct vsp1_partition *partition,
>                         unsigned int partition_idx,
> -                       struct vsp1_partition_window *window)
> +                       struct vsp1_partition_window *window,
> +                       bool forwards)
>  {
> -     partition->rpf = *window;
> +     if (forwards)
> +             *window = partition->rpf;
> +     else
> +             partition->rpf = *window;
>  }
>  
>  static const struct vsp1_entity_operations rpf_entity_ops = {
> diff --git a/drivers/media/platform/vsp1/vsp1_sru.c 
> b/drivers/media/platform/vsp1/vsp1_sru.c
> index b1617cb1f2b9..39f6e80a02a9 100644
> --- a/drivers/media/platform/vsp1/vsp1_sru.c
> +++ b/drivers/media/platform/vsp1/vsp1_sru.c
> @@ -327,24 +327,57 @@ static void sru_partition(struct vsp1_entity *entity,
>                         struct vsp1_pipeline *pipe,
>                         struct vsp1_partition *partition,
>                         unsigned int partition_idx,
> -                       struct vsp1_partition_window *window)
> +                       struct vsp1_partition_window *window,
> +                       bool forwards)
>  {
>       struct vsp1_sru *sru = to_sru(&entity->subdev);
>       struct v4l2_mbus_framefmt *input;
>       struct v4l2_mbus_framefmt *output;
> +     int scale_up;
> +
> +     /* The partition->sru represents the SRU sink pad configuration. */

In actual fact, only the 'offset' needs to be stored for the SRU. It's
more of a passive entity, and no partition parameters get programmed at
runtime.

For consistency, I think it's still worth keeping partition->sru the
same type as the other entities.

I'll remove this line and update the comment where the structure is
stored...

>  
>       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);
>  
> +     scale_up = (input->width != output->width);
> +
> +     if (forwards) {
> +             /* Propagate the clipping offsets forwards. */
> +             window->offset += partition->sru.offset;
> +
> +             if (scale_up)
> +                     window->offset *= 2;
> +
> +             return;
> +     }
> +
>       /* Adapt if SRUx2 is enabled. */
> -     if (input->width != output->width) {
> +     if (scale_up) {
> +             /* Clipping offsets are not back-propagated. */
>               window->width /= 2;
>               window->left /= 2;
> +
> +             /* SRUx2 requires an extra pixel at the right edge. */
> +             window->width++;
>       }
>  
> +     /* Store our adapted sink window. */


/*
 * The partition->sru represents the SRU sink pad configuration.
 * However, the SRU is a passive component in the partition algorithm.
 */

>       partition->sru = *window;> +
> +     /* Expand to the left edge. */
> +     if (window->left != 0) {
> +             window->left--;
> +             window->width++;
> +             partition->sru.offset = 1;
> +     } else {
> +             partition->sru.offset = 0;
> +     }
> +
> +     /* Expand to the right edge. */
> +     window->width++;
>  }
>  
>  static const struct vsp1_entity_operations sru_entity_ops = {
> diff --git a/drivers/media/platform/vsp1/vsp1_uds.c 
> b/drivers/media/platform/vsp1/vsp1_uds.c
> index c71c24363d54..e2bd44740ad6 100644
> --- a/drivers/media/platform/vsp1/vsp1_uds.c
> +++ b/drivers/media/platform/vsp1/vsp1_uds.c
> @@ -58,6 +58,85 @@ static unsigned int uds_multiplier(int ratio)
>       return mp < 4 ? 1 : (mp < 8 ? 2 : 4);
>  }
>  
> +/*
> + *  These functions all assume a starting phase of 0.
> + *   i.e. the left edge of the image.
> + */
> +
> +/*
> + * uds_residual - Return the residual phase cycle at the given position
> + * @pos: source destination position

Source destination position is horrible here.

I think I'll reword this (and the others) as

@pos: source output position.

Even that might need some more thought...


> + * @ratio: scaling ratio in U4.12 fixed-point format
> + */
> +static unsigned int uds_residual(unsigned int pos, unsigned int ratio)
> +{
> +     unsigned int mp = uds_multiplier(ratio);
> +     unsigned int residual = (pos * ratio) % (mp * 4096);
> +
> +     return residual;
> +}
> +
> +/*
> + * uds_left_src_pixel - Return the sink pixel location for the given source
> + * position
> + *
> + * @pos: source destination position
> + * @ratio: scaling ratio in U4.12 fixed-point format
> + */
> +static unsigned int uds_left_src_pixel(unsigned int pos, unsigned int ratio)


This function needs to be renamed to uds_left_sink_pixel

s/src/sink/...

> +{
> +     unsigned int mp = uds_multiplier(ratio);
> +     unsigned int prefilter_out = (pos * ratio) / (mp * 4096);
> +     unsigned int residual = (pos * ratio) % (mp * 4096);
> +
> +     /* Todo: Section 32.3.7.5 : Procedure 3
> +      *
> +      * A restriction is described where the destination position must
> +      * satisfy the following conditions:
> +      *
> +      *  (pos * ratio) must be a multiple of mp
> +      *
> +      * This is not yet guaranteed and thus this check is in place
> +      * until the pull-back is correctly calculated for all ratio
> +      * and position values.
> +      */
> +     WARN_ONCE((mp == 2 && (residual & 0x01)) ||
> +               (mp == 4 && (residual & 0x03)),
> +                    "uds_left_pixel restrictions failed");
> +
> +     return mp * (prefilter_out + (residual ? 1 : 0));
> +}
> +
> +/*
> + * uds_right_src_pixel - Return the sink pixel location for the given source
> + * position
> + *
> + * @pos: source destination position
> + * @ratio: scaling ratio in U4.12 fixed-point format
> + */
> +static unsigned int uds_right_src_pixel(unsigned int pos, unsigned int ratio)

And the same here, it should be uds_right_sink_pixel.

It's the 'source' of the UDS - but in our system the input to the UDS is
the 'sink'


> +{
> +     unsigned int mp = uds_multiplier(ratio);
> +     unsigned int prefilter_out = (pos * ratio) / (mp * 4096);
> +
> +     return mp * (prefilter_out + 2) + (mp / 2);
> +}
> +
> +/*
> + * uds_start_phase - Return the sink pixel location for the given source
> + * position
> + *
> + * @pos: source destination position
> + * @ratio: scaling ratio in U4.12 fixed-point format
> + */
> +static unsigned int uds_start_phase(unsigned int pos, unsigned int ratio)
> +{
> +     unsigned int mp = uds_multiplier(ratio);
> +     unsigned int residual = (pos * ratio) % (mp * 4096);
> +
> +     return residual ? (4096 - residual / mp) : 0;
> +}
> +
>  /*
>   * uds_output_size - Return the output size for an input size and scaling 
> ratio
>   * @input: input size in pixels
> @@ -270,6 +349,7 @@ static void uds_configure_stream(struct vsp1_entity 
> *entity,
>       const struct v4l2_mbus_framefmt *input;
>       unsigned int hscale;
>       unsigned int vscale;
> +     bool manual_phase = vsp1_pipeline_partitioned(pipe);
>       bool multitap;
>  
>       input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
> @@ -294,7 +374,8 @@ static void uds_configure_stream(struct vsp1_entity 
> *entity,
>  
>       vsp1_uds_write(uds, dlb, VI6_UDS_CTRL,
>                      (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
> -                    (multitap ? VI6_UDS_CTRL_BC : 0));
> +                    (multitap ? VI6_UDS_CTRL_BC : 0) |
> +                    (manual_phase ? VI6_UDS_CTRL_AMDSLH : 0));
>  
>       vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH,
>                      (uds_passband_width(hscale)
> @@ -332,6 +413,12 @@ static void uds_configure_partition(struct vsp1_entity 
> *entity,
>                               << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
>                      (output->height
>                               << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
> +
> +     vsp1_uds_write(uds, dlb, VI6_UDS_HPHASE,
> +                    (partition->start_phase
> +                             << VI6_UDS_HPHASE_HSTP_SHIFT) |
> +                    (partition->end_phase
> +                             << VI6_UDS_HPHASE_HEDP_SHIFT));
>  }
>  
>  static unsigned int uds_max_width(struct vsp1_entity *entity,
> @@ -374,11 +461,23 @@ static void uds_partition(struct vsp1_entity *entity,
>                         struct vsp1_pipeline *pipe,
>                         struct vsp1_partition *partition,
>                         unsigned int partition_idx,
> -                       struct vsp1_partition_window *window)
> +                       struct vsp1_partition_window *window,
> +                       bool forwards)
>  {
>       struct vsp1_uds *uds = to_uds(&entity->subdev);
>       const struct v4l2_mbus_framefmt *output;
>       const struct v4l2_mbus_framefmt *input;
> +     unsigned int hscale;
> +     unsigned int right_sink;
> +     unsigned int margin;
> +     unsigned int left;
> +     unsigned int right;
> +
> +     /* For forwards propagation - simply pass on our output. */
> +     if (forwards) {
> +             *window = partition->uds_source;
> +             return;
> +     }
>  
>       /* Initialise the partition state. */
>       partition->uds_sink = *window;
> @@ -389,11 +488,51 @@ static void uds_partition(struct vsp1_entity *entity,
>       output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
>                                           UDS_PAD_SOURCE);
>  
> -     partition->uds_sink.width = window->width * input->width
> -                               / output->width;
> -     partition->uds_sink.left = window->left * input->width
> -                              / output->width;
> +     hscale = uds_compute_ratio(input->width, output->width);
> +
> +     /*
> +      * Quantify the margin required for discontinuous overlap, and expand
> +      * the window no further than the limits of the image.
> +      */
> +     margin = hscale < 0x200 ? 32 : /* 8 <  scale */
> +              hscale < 0x400 ? 16 : /* 4 <  scale <= 8 */
> +              hscale < 0x800 ?  8 : /* 2 <  scale <= 4 */
> +                                4;  /*      scale <= 2 */
> +
> +     left = max_t(int, 0, window->left - margin);
> +     right = min_t(int, output->width - 1,
> +                        window->left + window->width - 1 + margin);
> +
> +     /*
> +      * Handle our output partition configuration.
> +      * We can clip the pixels from the right edge, thus the
> +      * uds_source.width does not include the right margin.
> +      */
> +     partition->uds_source.left = left;
> +     partition->uds_source.width = window->left - left + window->width;
> +
> +     /*
> +      * The UDS can not clip the left pixels so this value will be
> +      * propagated forwards until it reaches the WPF.
> +      */
> +     partition->uds_source.offset = window->left - left;
> +
> +     /* Identify the input positions from the expanded partition. */
> +     partition->uds_sink.left = uds_left_src_pixel(left, hscale);
> +
> +     right_sink = uds_right_src_pixel(right, hscale);
> +     partition->uds_sink.width = right_sink - partition->uds_sink.left;
> +
> +     /*
> +      * We do not currently use VI6_UDS_CTRL_AMD from VI6_UDS_CTRL.
> +      * In the event that we enable VI6_UDS_CTRL_AMD, we must set the end
> +      * phase for the final partition to the phase_edge.
> +      */
> +     partition->end_phase = 0;
> +     partition->start_phase = uds_start_phase(partition->uds_source.left,
> +                                              hscale);
>  
> +     /* Pass a copy of our sink down to the previous entity. */
>       *window = partition->uds_sink;
>  }
>  
> diff --git a/drivers/media/platform/vsp1/vsp1_video.c 
> b/drivers/media/platform/vsp1/vsp1_video.c
> index d1ecc3d91290..3638a4e9bb19 100644
> --- a/drivers/media/platform/vsp1/vsp1_video.c
> +++ b/drivers/media/platform/vsp1/vsp1_video.c
> @@ -221,6 +221,7 @@ static void vsp1_video_calculate_partition(struct 
> vsp1_pipeline *pipe,
>       /* Initialise the partition with sane starting conditions. */
>       window.left = index * div_size;
>       window.width = div_size;
> +     window.offset = 0;
>  
>       modulus = format->width % div_size;
>  
> diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c 
> b/drivers/media/platform/vsp1/vsp1_wpf.c
> index 9e8dbf99878b..2e8cc4195c31 100644
> --- a/drivers/media/platform/vsp1/vsp1_wpf.c
> +++ b/drivers/media/platform/vsp1/vsp1_wpf.c
> @@ -371,16 +371,19 @@ static void wpf_configure_partition(struct vsp1_entity 
> *entity,
>                                                RWPF_PAD_SINK);
>       width = sink_format->width;
>       height = sink_format->height;
> +     offset = 0;
>  
>       /*
>        * Cropping. The partition algorithm can split the image into
>        * multiple slices.
>        */
> -     if (vsp1_pipeline_partitioned(pipe))
> +     if (vsp1_pipeline_partitioned(pipe)) {
>               width = pipe->partition->wpf.width;
> +             offset = pipe->partition->wpf.offset;
> +     }
>  
>       vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
> -                    (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
> +                    (offset << VI6_WPF_SZCLIP_OFST_SHIFT) |
>                      (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
>       vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
>                      (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
> @@ -491,8 +494,15 @@ static void wpf_partition(struct vsp1_entity *entity,
>                         struct vsp1_pipeline *pipe,
>                         struct vsp1_partition *partition,
>                         unsigned int partition_idx,
> -                       struct vsp1_partition_window *window)
> +                       struct vsp1_partition_window *window,
> +                       bool forwards)
>  {
> +     if (forwards) {
> +             /* Only handle incoming cropping requirements. */
> +             partition->wpf.offset = window->offset;
> +             return;
> +     }
> +
>       partition->wpf = *window;
>  }
>  
> 

Reply via email to