diff mbox series

[v3,6/8] media: hantro: enumerate scaled output formats

Message ID 20210618131526.566762-7-benjamin.gaignard@collabora.com (mailing list archive)
State New
Headers show
Series Additional features for Hantro HEVC | expand

Commit Message

Benjamin Gaignard June 18, 2021, 1:15 p.m. UTC
When enumerating the output formats take care of the hardware scaling
capabilities.
For a given input size G2 hardware block is capable of down scale the
output by 2, 4 or 8 factor. When decoding 4K streams that to be could
helpful to save memory bandwidth.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
 drivers/staging/media/hantro/hantro.h         |  4 ++
 .../staging/media/hantro/hantro_g2_hevc_dec.c | 46 ++++++++++++++++++-
 drivers/staging/media/hantro/hantro_g2_regs.h |  6 +++
 drivers/staging/media/hantro/hantro_hw.h      |  1 +
 drivers/staging/media/hantro/hantro_v4l2.c    | 10 ++--
 drivers/staging/media/hantro/imx8m_vpu_hw.c   |  1 +
 6 files changed, 63 insertions(+), 5 deletions(-)

Comments

Ezequiel Garcia June 18, 2021, 7:24 p.m. UTC | #1
Hi Benjamin,

Thanks for working on this.

On Fri, 2021-06-18 at 15:15 +0200, Benjamin Gaignard wrote:
> When enumerating the output formats take care of the hardware scaling
> capabilities.
> For a given input size G2 hardware block is capable of down scale the
> output by 2, 4 or 8 factor. When decoding 4K streams that to be could
> helpful to save memory bandwidth.
> 

Looking at https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-stateless-decoder.html
I see that this case should be covered by the spec.

If I understand correctly, it would be:

1. VIDIOC_S_FMT(OUTPUT)
2. VIDIOC_ENUM_FMT(CAPTURE) / VIDIOC_ENUM_FRAMESIZES(CAPTURE)
3. VIDIOC_S_FMT(CAPTURE)
4. VIDIOC_G_FMT(CAPTURE) again to get buffer information.

Does v4l2codecs support this case as-is, if changes are needed,
I'd like to have the MR ready and reviewed by Nicolas.

I know it's a staging driver, but I believe it's important
to have users for new cases/feature to avoid bitrotting.

> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
>  drivers/staging/media/hantro/hantro.h         |  4 ++
>  .../staging/media/hantro/hantro_g2_hevc_dec.c | 46 ++++++++++++++++++-
>  drivers/staging/media/hantro/hantro_g2_regs.h |  6 +++
>  drivers/staging/media/hantro/hantro_hw.h      |  1 +
>  drivers/staging/media/hantro/hantro_v4l2.c    | 10 ++--
>  drivers/staging/media/hantro/imx8m_vpu_hw.c   |  1 +
>  6 files changed, 63 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h
> index 6a21d1e95b34..ca9038b0384a 100644
> --- a/drivers/staging/media/hantro/hantro.h
> +++ b/drivers/staging/media/hantro/hantro.h
> @@ -71,6 +71,9 @@ struct hantro_irq {
>   * @reg_names:                 array of register range names
>   * @num_regs:                  number of register range names in the array
>   * @postproc_regs:             &struct hantro_postproc_regs pointer
> + * @scaling:                   Set possible scaled output formats.
> + *                             Returns zero if OK, a negative value in error cases.
> + *                             Optional.
>   */
>  struct hantro_variant {
>         unsigned int enc_offset;
> @@ -92,6 +95,7 @@ struct hantro_variant {
>         const char * const *reg_names;
>         int num_regs;
>         const struct hantro_postproc_regs *postproc_regs;
> +       int (*scaling)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize);

Please add some .ops field, so we can put this
and move init and runtime_resume as well.
 
>  };
>  
>  /**
> diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> index 41dc89ec926c..3a8aa2ff109c 100644
> --- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> +++ b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> @@ -396,6 +396,17 @@ static void set_ref_pic_list(struct hantro_ctx *ctx)
>         }
>  }
>  
> +static int down_scale_factor(struct hantro_ctx *ctx)
> +{
> +       const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
> +       const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
> +
> +       if (sps->pic_width_in_luma_samples == ctx->dst_fmt.width)
> +               return 0;
> +
> +       return DIV_ROUND_CLOSEST(sps->pic_width_in_luma_samples, ctx->dst_fmt.width);
> +}
> +
>  static int set_ref(struct hantro_ctx *ctx)
>  {
>         const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
> @@ -409,6 +420,7 @@ static int set_ref(struct hantro_ctx *ctx)
>         size_t mv_offset = hantro_hevc_motion_vectors_offset(sps);
>         size_t compress_luma_offset = hantro_hevc_luma_compress_offset(sps);
>         size_t compress_chroma_offset = hantro_hevc_chroma_compress_offset(sps);
> +       int down_scale = down_scale_factor(ctx);
>         u32 max_ref_frames;
>         u16 dpb_longterm_e;
>         static const struct hantro_reg cur_poc[] = {
> @@ -521,8 +533,18 @@ static int set_ref(struct hantro_ctx *ctx)
>         hantro_write_addr(vpu, G2_REG_CHR_REF(i), chroma_addr);
>         hantro_write_addr(vpu, G2_REG_DMV_REF(i++), mv_addr);
>  
> -       hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
> -       hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
> +       if (down_scale) {
> +               chroma_addr = luma_addr + (cr_offset >> down_scale);
> +               hantro_reg_write(vpu, &g2_down_scale_e, 1);
> +               hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2);
> +               hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2);
> +               hantro_write_addr(vpu, G2_DS_DST, luma_addr);
> +               hantro_write_addr(vpu, G2_DS_DST_CHR, chroma_addr);
> +       } else {
> +               hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
> +               hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
> +       }
> +
>         hantro_write_addr(vpu, G2_ADDR_DST_MV, mv_addr);
>         hantro_write_addr(vpu, G2_COMP_ADDR_DST, compress_luma_addr);
>         hantro_write_addr(vpu, G2_COMP_CHR, compress_chroma_addr);
> @@ -603,6 +625,26 @@ static void hantro_g2_check_idle(struct hantro_dev *vpu)
>         }
>  }
>  
> +int hantro_g2_hevc_dec_scaling(struct hantro_ctx *ctx,
> +                              struct v4l2_frmsizeenum *fsize)

Maybe

s/hantro_g2_hevc_dec_scaling/hantro_g2_hevc_enum_framesizes

would be clear?

Is this restricted to HEVC or is it something that will
work on VP9 as well?

> +{
> +       /**
> +        * G2 scaler can scale down by 0, 2, 4 or 8
> +        * use fsize->index has power of 2 diviser
> +        **/

Please use

/*
 *
 */

style.

> +       if (fsize->index > 3)
> +               return -EINVAL;
> +
> +       if (!ctx->src_fmt.width || !ctx->src_fmt.height)
> +               return -EINVAL;
> +
> +       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +       fsize->discrete.width = ctx->src_fmt.width >> fsize->index;
> +       fsize->discrete.height = ctx->src_fmt.height >> fsize->index;
> +
> +       return 0;
> +}
> +
[..]
> -       /* This only makes sense for coded formats */
> -       if (fmt->codec_mode == HANTRO_MODE_NONE)
> +       /* For non-coded formats check if scaling is possible */
> +       if (fmt->codec_mode == HANTRO_MODE_NONE) {
> +               if (ctx->dev->variant->scaling)
> +                       return ctx->dev->variant->scaling(ctx, fsize);
> +
>                 return -EINVAL;

I wonder why we are returning EINVAL here. Can we support
.vidioc_enum_framesizes for coded and non-coded?

Thanks,
Ezequiel
Nicolas Dufresne June 22, 2021, 9:45 p.m. UTC | #2
Le vendredi 18 juin 2021 à 15:15 +0200, Benjamin Gaignard a écrit :
> When enumerating the output formats take care of the hardware scaling
> capabilities.
> For a given input size G2 hardware block is capable of down scale the
> output by 2, 4 or 8 factor. When decoding 4K streams that to be could
> helpful to save memory bandwidth.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
>  drivers/staging/media/hantro/hantro.h         |  4 ++
>  .../staging/media/hantro/hantro_g2_hevc_dec.c | 46 ++++++++++++++++++-
>  drivers/staging/media/hantro/hantro_g2_regs.h |  6 +++
>  drivers/staging/media/hantro/hantro_hw.h      |  1 +
>  drivers/staging/media/hantro/hantro_v4l2.c    | 10 ++--
>  drivers/staging/media/hantro/imx8m_vpu_hw.c   |  1 +
>  6 files changed, 63 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h
> index 6a21d1e95b34..ca9038b0384a 100644
> --- a/drivers/staging/media/hantro/hantro.h
> +++ b/drivers/staging/media/hantro/hantro.h
> @@ -71,6 +71,9 @@ struct hantro_irq {
>   * @reg_names:			array of register range names
>   * @num_regs:			number of register range names in the array
>   * @postproc_regs:		&struct hantro_postproc_regs pointer
> + * @scaling:			Set possible scaled output formats.
> + *				Returns zero if OK, a negative value in error cases.
> + *				Optional.
>   */
>  struct hantro_variant {
>  	unsigned int enc_offset;
> @@ -92,6 +95,7 @@ struct hantro_variant {
>  	const char * const *reg_names;
>  	int num_regs;
>  	const struct hantro_postproc_regs *postproc_regs;
> +	int (*scaling)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize);
>  };
>  
>  /**
> diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> index 41dc89ec926c..3a8aa2ff109c 100644
> --- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> +++ b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
> @@ -396,6 +396,17 @@ static void set_ref_pic_list(struct hantro_ctx *ctx)
>  	}
>  }
>  
> +static int down_scale_factor(struct hantro_ctx *ctx)
> +{
> +	const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
> +	const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
> +
> +	if (sps->pic_width_in_luma_samples == ctx->dst_fmt.width)
> +		return 0;
> +
> +	return DIV_ROUND_CLOSEST(sps->pic_width_in_luma_samples, ctx->dst_fmt.width);
> +}
> +
>  static int set_ref(struct hantro_ctx *ctx)
>  {
>  	const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
> @@ -409,6 +420,7 @@ static int set_ref(struct hantro_ctx *ctx)
>  	size_t mv_offset = hantro_hevc_motion_vectors_offset(sps);
>  	size_t compress_luma_offset = hantro_hevc_luma_compress_offset(sps);
>  	size_t compress_chroma_offset = hantro_hevc_chroma_compress_offset(sps);
> +	int down_scale = down_scale_factor(ctx);
>  	u32 max_ref_frames;
>  	u16 dpb_longterm_e;
>  	static const struct hantro_reg cur_poc[] = {
> @@ -521,8 +533,18 @@ static int set_ref(struct hantro_ctx *ctx)
>  	hantro_write_addr(vpu, G2_REG_CHR_REF(i), chroma_addr);
>  	hantro_write_addr(vpu, G2_REG_DMV_REF(i++), mv_addr);
>  
> -	hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
> -	hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
> +	if (down_scale) {
> +		chroma_addr = luma_addr + (cr_offset >> down_scale);
> +		hantro_reg_write(vpu, &g2_down_scale_e, 1);
> +		hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2);
> +		hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2);
> +		hantro_write_addr(vpu, G2_DS_DST, luma_addr);
> +		hantro_write_addr(vpu, G2_DS_DST_CHR, chroma_addr);
> +	} else {
> +		hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
> +		hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
> +	}
> +
>  	hantro_write_addr(vpu, G2_ADDR_DST_MV, mv_addr);
>  	hantro_write_addr(vpu, G2_COMP_ADDR_DST, compress_luma_addr);
>  	hantro_write_addr(vpu, G2_COMP_CHR, compress_chroma_addr);
> @@ -603,6 +625,26 @@ static void hantro_g2_check_idle(struct hantro_dev *vpu)
>  	}
>  }
>  
> +int hantro_g2_hevc_dec_scaling(struct hantro_ctx *ctx,
> +			       struct v4l2_frmsizeenum *fsize)
> +{
> +	/**
> +	 * G2 scaler can scale down by 0, 2, 4 or 8
> +	 * use fsize->index has power of 2 diviser
> +	 **/
> +	if (fsize->index > 3)
> +		return -EINVAL;
> +
> +	if (!ctx->src_fmt.width || !ctx->src_fmt.height)
> +		return -EINVAL;
> +
> +	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +	fsize->discrete.width = ctx->src_fmt.width >> fsize->index;
> +	fsize->discrete.height = ctx->src_fmt.height >> fsize->index;

Have you given some thought around coded vs display size ? In this context, we
return an enumerated of coded scaled size. The VIDIOC_ENUM_FRAMESIZES
documentation [0] does not say anything useful about these two. So this
ambiguity also exist for existing M2M scaler mainline.

Shall we enumerate the frame sizes in term of coded or display size ? Of course
the scaling  factor remains the same, but the application will likely need to
convert. Which leads me to think this patch should come with additional
documentation.

[0] https://www.kernel.org/doc/html/v4.11/media/uapi/v4l/vidioc-enum-framesizes.html

p.s. my feeling is that enumerating coded size is what makes most sense, but the
PP could also have some cropping capability, in which case it may want to lock
the src_fmt width/height to the display size instead of padding it. Not knowing
what the driver will do seems problematic for applications.

> +
> +	return 0;
> +}
> +
>  int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx)
>  {
>  	struct hantro_dev *vpu = ctx->dev;
> diff --git a/drivers/staging/media/hantro/hantro_g2_regs.h b/drivers/staging/media/hantro/hantro_g2_regs.h
> index 941e5482d27b..54f3d78ce46e 100644
> --- a/drivers/staging/media/hantro/hantro_g2_regs.h
> +++ b/drivers/staging/media/hantro/hantro_g2_regs.h
> @@ -174,6 +174,10 @@
>  #define g2_buswidth		G2_DEC_REG(58, 8,  0x7)
>  #define g2_max_burst		G2_DEC_REG(58, 0,  0xff)
>  
> +#define g2_down_scale_e	G2_DEC_REG(184, 7, 0x1)
> +#define g2_down_scale_y	G2_DEC_REG(184, 2, 0x3)
> +#define g2_down_scale_x	G2_DEC_REG(184, 0, 0x3)
> +
>  #define G2_REG_CONFIG				G2_SWREG(58)
>  #define G2_REG_CONFIG_DEC_CLK_GATE_E		BIT(16)
>  #define G2_REG_CONFIG_DEC_CLK_GATE_IDLE_E	BIT(17)
> @@ -193,6 +197,8 @@
>  #define G2_TILE_FILTER		(G2_SWREG(179))
>  #define G2_TILE_SAO		(G2_SWREG(181))
>  #define G2_TILE_BSD		(G2_SWREG(183))
> +#define G2_DS_DST		(G2_SWREG(186))
> +#define G2_DS_DST_CHR		(G2_SWREG(188))
>  #define G2_COMP_ADDR_DST	(G2_SWREG(190))
>  #define G2_COMP_ADDR_REF(i)	(G2_SWREG(192) + ((i) * 0x8))
>  #define G2_COMP_CHR		(G2_SWREG(224))
> diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h
> index c5374cd74d66..2edb890f10af 100644
> --- a/drivers/staging/media/hantro/hantro_hw.h
> +++ b/drivers/staging/media/hantro/hantro_hw.h
> @@ -241,6 +241,7 @@ int hantro_hevc_dec_init(struct hantro_ctx *ctx);
>  void hantro_hevc_dec_exit(struct hantro_ctx *ctx);
>  int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx);
>  void hantro_g2_hevc_dec_done(struct hantro_ctx *ctx);
> +int hantro_g2_hevc_dec_scaling(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize);
>  int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx);
>  dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, int poc);
>  void hantro_hevc_ref_remove_unused(struct hantro_ctx *ctx);
> diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c
> index bcb0bdff4a9a..566b645f661d 100644
> --- a/drivers/staging/media/hantro/hantro_v4l2.c
> +++ b/drivers/staging/media/hantro/hantro_v4l2.c
> @@ -118,7 +118,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
>  	struct hantro_ctx *ctx = fh_to_ctx(priv);
>  	const struct hantro_fmt *fmt;
>  
> -	if (fsize->index != 0) {
> +	if (fsize->index != 0 && !ctx->dev->variant->scaling) {
>  		vpu_debug(0, "invalid frame size index (expected 0, got %d)\n",
>  			  fsize->index);
>  		return -EINVAL;
> @@ -131,9 +131,13 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
>  		return -EINVAL;
>  	}
>  
> -	/* This only makes sense for coded formats */
> -	if (fmt->codec_mode == HANTRO_MODE_NONE)
> +	/* For non-coded formats check if scaling is possible */
> +	if (fmt->codec_mode == HANTRO_MODE_NONE) {
> +		if (ctx->dev->variant->scaling)
> +			return ctx->dev->variant->scaling(ctx, fsize);
> +
>  		return -EINVAL;
> +	}
>  
>  	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
>  	fsize->stepwise = fmt->frmsize;
> diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c
> index 65bcf46740d7..d3b760569b52 100644
> --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c
> +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c
> @@ -304,4 +304,5 @@ const struct hantro_variant imx8mq_vpu_g2_variant = {
>  	.num_irqs = ARRAY_SIZE(imx8mq_g2_irqs),
>  	.clk_names = imx8mq_clk_names,
>  	.num_clocks = ARRAY_SIZE(imx8mq_clk_names),
> +	.scaling = hantro_g2_hevc_dec_scaling,
>  };
diff mbox series

Patch

diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h
index 6a21d1e95b34..ca9038b0384a 100644
--- a/drivers/staging/media/hantro/hantro.h
+++ b/drivers/staging/media/hantro/hantro.h
@@ -71,6 +71,9 @@  struct hantro_irq {
  * @reg_names:			array of register range names
  * @num_regs:			number of register range names in the array
  * @postproc_regs:		&struct hantro_postproc_regs pointer
+ * @scaling:			Set possible scaled output formats.
+ *				Returns zero if OK, a negative value in error cases.
+ *				Optional.
  */
 struct hantro_variant {
 	unsigned int enc_offset;
@@ -92,6 +95,7 @@  struct hantro_variant {
 	const char * const *reg_names;
 	int num_regs;
 	const struct hantro_postproc_regs *postproc_regs;
+	int (*scaling)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize);
 };
 
 /**
diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
index 41dc89ec926c..3a8aa2ff109c 100644
--- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
+++ b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c
@@ -396,6 +396,17 @@  static void set_ref_pic_list(struct hantro_ctx *ctx)
 	}
 }
 
+static int down_scale_factor(struct hantro_ctx *ctx)
+{
+	const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
+	const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
+
+	if (sps->pic_width_in_luma_samples == ctx->dst_fmt.width)
+		return 0;
+
+	return DIV_ROUND_CLOSEST(sps->pic_width_in_luma_samples, ctx->dst_fmt.width);
+}
+
 static int set_ref(struct hantro_ctx *ctx)
 {
 	const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
@@ -409,6 +420,7 @@  static int set_ref(struct hantro_ctx *ctx)
 	size_t mv_offset = hantro_hevc_motion_vectors_offset(sps);
 	size_t compress_luma_offset = hantro_hevc_luma_compress_offset(sps);
 	size_t compress_chroma_offset = hantro_hevc_chroma_compress_offset(sps);
+	int down_scale = down_scale_factor(ctx);
 	u32 max_ref_frames;
 	u16 dpb_longterm_e;
 	static const struct hantro_reg cur_poc[] = {
@@ -521,8 +533,18 @@  static int set_ref(struct hantro_ctx *ctx)
 	hantro_write_addr(vpu, G2_REG_CHR_REF(i), chroma_addr);
 	hantro_write_addr(vpu, G2_REG_DMV_REF(i++), mv_addr);
 
-	hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
-	hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
+	if (down_scale) {
+		chroma_addr = luma_addr + (cr_offset >> down_scale);
+		hantro_reg_write(vpu, &g2_down_scale_e, 1);
+		hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2);
+		hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2);
+		hantro_write_addr(vpu, G2_DS_DST, luma_addr);
+		hantro_write_addr(vpu, G2_DS_DST_CHR, chroma_addr);
+	} else {
+		hantro_write_addr(vpu, G2_ADDR_DST, luma_addr);
+		hantro_write_addr(vpu, G2_ADDR_DST_CHR, chroma_addr);
+	}
+
 	hantro_write_addr(vpu, G2_ADDR_DST_MV, mv_addr);
 	hantro_write_addr(vpu, G2_COMP_ADDR_DST, compress_luma_addr);
 	hantro_write_addr(vpu, G2_COMP_CHR, compress_chroma_addr);
@@ -603,6 +625,26 @@  static void hantro_g2_check_idle(struct hantro_dev *vpu)
 	}
 }
 
+int hantro_g2_hevc_dec_scaling(struct hantro_ctx *ctx,
+			       struct v4l2_frmsizeenum *fsize)
+{
+	/**
+	 * G2 scaler can scale down by 0, 2, 4 or 8
+	 * use fsize->index has power of 2 diviser
+	 **/
+	if (fsize->index > 3)
+		return -EINVAL;
+
+	if (!ctx->src_fmt.width || !ctx->src_fmt.height)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fsize->discrete.width = ctx->src_fmt.width >> fsize->index;
+	fsize->discrete.height = ctx->src_fmt.height >> fsize->index;
+
+	return 0;
+}
+
 int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx)
 {
 	struct hantro_dev *vpu = ctx->dev;
diff --git a/drivers/staging/media/hantro/hantro_g2_regs.h b/drivers/staging/media/hantro/hantro_g2_regs.h
index 941e5482d27b..54f3d78ce46e 100644
--- a/drivers/staging/media/hantro/hantro_g2_regs.h
+++ b/drivers/staging/media/hantro/hantro_g2_regs.h
@@ -174,6 +174,10 @@ 
 #define g2_buswidth		G2_DEC_REG(58, 8,  0x7)
 #define g2_max_burst		G2_DEC_REG(58, 0,  0xff)
 
+#define g2_down_scale_e	G2_DEC_REG(184, 7, 0x1)
+#define g2_down_scale_y	G2_DEC_REG(184, 2, 0x3)
+#define g2_down_scale_x	G2_DEC_REG(184, 0, 0x3)
+
 #define G2_REG_CONFIG				G2_SWREG(58)
 #define G2_REG_CONFIG_DEC_CLK_GATE_E		BIT(16)
 #define G2_REG_CONFIG_DEC_CLK_GATE_IDLE_E	BIT(17)
@@ -193,6 +197,8 @@ 
 #define G2_TILE_FILTER		(G2_SWREG(179))
 #define G2_TILE_SAO		(G2_SWREG(181))
 #define G2_TILE_BSD		(G2_SWREG(183))
+#define G2_DS_DST		(G2_SWREG(186))
+#define G2_DS_DST_CHR		(G2_SWREG(188))
 #define G2_COMP_ADDR_DST	(G2_SWREG(190))
 #define G2_COMP_ADDR_REF(i)	(G2_SWREG(192) + ((i) * 0x8))
 #define G2_COMP_CHR		(G2_SWREG(224))
diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h
index c5374cd74d66..2edb890f10af 100644
--- a/drivers/staging/media/hantro/hantro_hw.h
+++ b/drivers/staging/media/hantro/hantro_hw.h
@@ -241,6 +241,7 @@  int hantro_hevc_dec_init(struct hantro_ctx *ctx);
 void hantro_hevc_dec_exit(struct hantro_ctx *ctx);
 int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx);
 void hantro_g2_hevc_dec_done(struct hantro_ctx *ctx);
+int hantro_g2_hevc_dec_scaling(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize);
 int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx);
 dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, int poc);
 void hantro_hevc_ref_remove_unused(struct hantro_ctx *ctx);
diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c
index bcb0bdff4a9a..566b645f661d 100644
--- a/drivers/staging/media/hantro/hantro_v4l2.c
+++ b/drivers/staging/media/hantro/hantro_v4l2.c
@@ -118,7 +118,7 @@  static int vidioc_enum_framesizes(struct file *file, void *priv,
 	struct hantro_ctx *ctx = fh_to_ctx(priv);
 	const struct hantro_fmt *fmt;
 
-	if (fsize->index != 0) {
+	if (fsize->index != 0 && !ctx->dev->variant->scaling) {
 		vpu_debug(0, "invalid frame size index (expected 0, got %d)\n",
 			  fsize->index);
 		return -EINVAL;
@@ -131,9 +131,13 @@  static int vidioc_enum_framesizes(struct file *file, void *priv,
 		return -EINVAL;
 	}
 
-	/* This only makes sense for coded formats */
-	if (fmt->codec_mode == HANTRO_MODE_NONE)
+	/* For non-coded formats check if scaling is possible */
+	if (fmt->codec_mode == HANTRO_MODE_NONE) {
+		if (ctx->dev->variant->scaling)
+			return ctx->dev->variant->scaling(ctx, fsize);
+
 		return -EINVAL;
+	}
 
 	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
 	fsize->stepwise = fmt->frmsize;
diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c
index 65bcf46740d7..d3b760569b52 100644
--- a/drivers/staging/media/hantro/imx8m_vpu_hw.c
+++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c
@@ -304,4 +304,5 @@  const struct hantro_variant imx8mq_vpu_g2_variant = {
 	.num_irqs = ARRAY_SIZE(imx8mq_g2_irqs),
 	.clk_names = imx8mq_clk_names,
 	.num_clocks = ARRAY_SIZE(imx8mq_clk_names),
+	.scaling = hantro_g2_hevc_dec_scaling,
 };