[PATCH v2] drm/exynos: ipp: fix image broken issue

Inki Dae inki.dae at samsung.com
Tue May 29 02:30:52 UTC 2018


This patch changes userspace parameter but Kernel driver shouldn't touch such parameter.
Please ignore this patch. Marek will post a generic solution soon, which fixes the image broken issue without touching userspace parameter.

Thanks,
Inki Dae


2018년 05월 24일 10:04에 Inki Dae 이(가) 쓴 글:
> Fixed image broken issue while video play back with 854x480.
> 
> GScaler device of Exynos5433 has IN_ID_MAX field in GSC_IN_CON
> register, which determines how much GScaler DMA has to drain
> data from system memory at once.
> 
> Therefore, image size should be fixed up before IPP driver verifies
> whether gem buffer is enough for it or not.
> 
> Changelog v2:
> - Fix align limit of image width size to 16bytes because with other values
>   , 4 and 8bytes, it doesn't work.
> - Change the function name from gst_get_align_size to gst_set_align_limit.
>   gst_set_align_limit function name is more reasonable.
> - Call fixup buffer function before calling size limit check.
> - Remove checking align of image offset, x and y. The offest values have
>   no any limit but only size.
> 
> Signed-off-by: Inki Dae <inki.dae at samsung.com>
> ---
>  drivers/gpu/drm/exynos/exynos_drm_gsc.c | 94 +++++++++++++++++++++++++++------
>  drivers/gpu/drm/exynos/exynos_drm_ipp.c | 18 +++++--
>  drivers/gpu/drm/exynos/exynos_drm_ipp.h | 12 +++++
>  3 files changed, 106 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
> index e99dd1e..8bc31b5 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
> @@ -86,6 +86,28 @@ struct gsc_scaler {
>  	unsigned long main_vratio;
>  };
>  
> +/**
> + * struct gsc_driverdata - per device type driver data for init time.
> + *
> + * @limits: picture size limits array
> + * @clk_names: names of clocks needed by this variant
> + * @num_clocks: the number of clocks needed by this variant
> + * @has_in_ic_max: indicate whether GSCALER_IN_CON register has
> + *			IN_IC_MAX field which means maxinum AXI
> + *			read capability.
> + * @in_ic_max_shift: indicate which position IN_IC_MAX field is located.
> + * @in_ic_max_mask: A mask value to IN_IC_MAX field.
> + */
> +struct gsc_driverdata {
> +	const struct drm_exynos_ipp_limit *limits;
> +	int		num_limits;
> +	const char	*clk_names[GSC_MAX_CLOCKS];
> +	int		num_clocks;
> +	unsigned int	has_in_ic_max:1;
> +	unsigned int	in_ic_max_shift;
> +	unsigned int	in_ic_max_mask;
> +};
> +
>  /*
>   * A structure of gsc context.
>   *
> @@ -96,6 +118,9 @@ struct gsc_scaler {
>   * @id: gsc id.
>   * @irq: irq number.
>   * @rotation: supports rotation of src.
> + * @align_size: A size that GSC_SRCIMG_WIDTH value of GSC_SRCIMG_SIZE
> + *		register should be aligned with(in byte).
> + * @driver_data: a pointer to gsc_driverdata.
>   */
>  struct gsc_context {
>  	struct exynos_drm_ipp ipp;
> @@ -114,20 +139,8 @@ struct gsc_context {
>  	int	id;
>  	int	irq;
>  	bool	rotation;
> -};
> -
> -/**
> - * struct gsc_driverdata - per device type driver data for init time.
> - *
> - * @limits: picture size limits array
> - * @clk_names: names of clocks needed by this variant
> - * @num_clocks: the number of clocks needed by this variant
> - */
> -struct gsc_driverdata {
> -	const struct drm_exynos_ipp_limit *limits;
> -	int		num_limits;
> -	const char	*clk_names[GSC_MAX_CLOCKS];
> -	int		num_clocks;
> +	unsigned int align_size;
> +	struct gsc_driverdata *driver_data;
>  };
>  
>  /* 8-tap Filter Coefficient */
> @@ -1095,6 +1108,15 @@ static void gsc_start(struct gsc_context *ctx)
>  	gsc_write(cfg, GSC_ENABLE);
>  }
>  
> +static void gsc_fixup(struct exynos_drm_ipp *ipp,
> +			  struct exynos_drm_ipp_task *task)
> +{
> +	struct gsc_context *ctx = container_of(ipp, struct gsc_context, ipp);
> +	struct exynos_drm_ipp_buffer *src = &task->src;
> +
> +	src->buf.width = ALIGN(src->buf.width, ctx->align_size);
> +}
> +
>  static int gsc_commit(struct exynos_drm_ipp *ipp,
>  			  struct exynos_drm_ipp_task *task)
>  {
> @@ -1124,6 +1146,41 @@ static int gsc_commit(struct exynos_drm_ipp *ipp,
>  	return 0;
>  }
>  
> +static void gsc_set_align_limit(struct gsc_context *ctx)
> +{
> +	const struct drm_exynos_ipp_limit *limits = ctx->driver_data->limits;
> +
> +	if (ctx->driver_data->has_in_ic_max) {
> +		u32 cfg, mask, shift;
> +
> +		mask = ctx->driver_data->in_ic_max_mask;
> +		shift = ctx->driver_data->in_ic_max_shift;
> +
> +		pm_runtime_get_sync(ctx->dev);
> +
> +		cfg = gsc_read(GSC_IN_CON);
> +
> +		/*
> +		 * fix align limit to 16bytes. With other limit values, 4
> +		 * and 8, it doesn't work.
> +		 */
> +		cfg = (cfg & ~(mask << shift));
> +		cfg |= 2 << shift;
> +
> +		gsc_write(cfg, GSC_IN_CON);
> +
> +		pm_runtime_mark_last_busy(ctx->dev);
> +		pm_runtime_put_autosuspend(ctx->dev);
> +
> +		ctx->align_size = 16;
> +	} else {
> +		/* No HW register to get the align limit so use fixed value. */
> +		ctx->align_size = limits->h.align;
> +	}
> +
> +	DRM_DEBUG_KMS("align_size = %d\n", ctx->align_size);
> +}
> +
>  static void gsc_abort(struct exynos_drm_ipp *ipp,
>  			  struct exynos_drm_ipp_task *task)
>  {
> @@ -1142,6 +1199,7 @@ static void gsc_abort(struct exynos_drm_ipp *ipp,
>  }
>  
>  static struct exynos_drm_ipp_funcs ipp_funcs = {
> +	.fixup = gsc_fixup,
>  	.commit = gsc_commit,
>  	.abort = gsc_abort,
>  };
> @@ -1155,6 +1213,8 @@ static int gsc_bind(struct device *dev, struct device *master, void *data)
>  	ctx->drm_dev = drm_dev;
>  	drm_iommu_attach_device(drm_dev, dev);
>  
> +	gsc_set_align_limit(ctx);
> +
>  	exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs,
>  			DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE |
>  			DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT,
> @@ -1221,6 +1281,7 @@ static int gsc_probe(struct platform_device *pdev)
>  	}
>  	ctx->formats = formats;
>  	ctx->num_formats = ARRAY_SIZE(gsc_formats);
> +	ctx->driver_data = driver_data;
>  
>  	/* clock control */
>  	for (i = 0; i < ctx->num_clocks; i++) {
> @@ -1340,7 +1401,7 @@ static int __maybe_unused gsc_runtime_resume(struct device *dev)
>  };
>  
>  static const struct drm_exynos_ipp_limit gsc_5433_limits[] = {
> -	{ IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8191, 2 }, .v = { 16, 8191, 2 }) },
> +	{ IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8191, 16 }, .v = { 16, 8191, 2 }) },
>  	{ IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 1 }, .v = { 8, 3344, 1 }) },
>  	{ IPP_SIZE_LIMIT(ROTATED, .h = { 32, 2047 }, .v = { 8, 8191 }) },
>  	{ IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 },
> @@ -1366,6 +1427,9 @@ static int __maybe_unused gsc_runtime_resume(struct device *dev)
>  	.num_clocks = 4,
>  	.limits = gsc_5433_limits,
>  	.num_limits = ARRAY_SIZE(gsc_5433_limits),
> +	.has_in_ic_max = 1,
> +	.in_ic_max_shift = 24,
> +	.in_ic_max_mask = 0x3,
>  };
>  
>  static const struct of_device_id exynos_drm_gsc_of_match[] = {
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
> index 26374e5..2319c12 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
> @@ -510,9 +510,7 @@ static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
>  	}
>  	__get_size_limit(limits, num_limits, id, &l);
>  	if (!__size_limit_check(buf->rect.w, lh) ||
> -	    !__align_check(buf->rect.x, lh->align) ||
> -	    !__size_limit_check(buf->rect.h, lv) ||
> -	    !__align_check(buf->rect.y, lv->align))
> +	    !__size_limit_check(buf->rect.h, lv))
>  		return -EINVAL;
>  
>  	return 0;
> @@ -560,6 +558,14 @@ static int exynos_drm_ipp_check_scale_limits(
>  	return 0;
>  }
>  
> +static void exynos_drm_ipp_fixup_buffer(struct exynos_drm_ipp_task *task)
> +{
> +	struct exynos_drm_ipp *ipp = task->ipp;
> +
> +	if (ipp->funcs->fixup)
> +		ipp->funcs->fixup(ipp, task);
> +}
> +
>  static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
>  {
>  	struct exynos_drm_ipp *ipp = task->ipp;
> @@ -607,6 +613,12 @@ static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
>  		return -EINVAL;
>  	}
>  
> +	/*
> +	 * image size should be fixed up before setup buffer call
> +	 * which verifies whether image size exceeds gem buffer size or not.
> +	 */
> +	exynos_drm_ipp_fixup_buffer(task);
> +
>  	src_fmt = __ipp_format_get(ipp, src->buf.fourcc, src->buf.modifier,
>  				   DRM_EXYNOS_IPP_FORMAT_SOURCE);
>  	if (!src_fmt) {
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
> index 0b27d4a..5c2059f 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
> @@ -20,6 +20,18 @@
>   */
>  struct exynos_drm_ipp_funcs {
>  	/**
> +	 * @fixup:
> +	 *
> +	 * Correct buffer size according to hardware limit of each DMA device.
> +	 * Some DMA device has maximum memory read capability through AXI bus,
> +	 * which reads data from memory as a given bytes.
> +	 * Therefore, IPP driver needs to check if the buffer size meets
> +	 * the HW limlit of each DMA device such as GScaler, Scaler,
> +	 * Rotator and FIMC.
> +	 */
> +	void (*fixup)(struct exynos_drm_ipp *ipp,
> +		     struct exynos_drm_ipp_task *task);
> +	/**
>  	 * @commit:
>  	 *
>  	 * This is the main entry point to start framebuffer processing
> 


More information about the dri-devel mailing list