diff mbox

drm/exynos: ipp: fix image broken issue

Message ID 1526525047-16924-1-git-send-email-inki.dae@samsung.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Inki Dae May 17, 2018, 2:44 a.m. UTC
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.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_gsc.c | 87 +++++++++++++++++++++++++++------
 drivers/gpu/drm/exynos/exynos_drm_ipp.c | 15 ++++++
 drivers/gpu/drm/exynos/exynos_drm_ipp.h | 12 +++++
 3 files changed, 100 insertions(+), 14 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index e99dd1e..41cb82d 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,18 @@  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;
+
+	if (!ctx->driver_data->has_in_ic_max)
+		return;
+
+	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 +1149,32 @@  static int gsc_commit(struct exynos_drm_ipp *ipp,
 	return 0;
 }
 
+static void gsc_get_align_size(struct gsc_context *ctx)
+{
+	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);
+
+	cfg = (cfg & mask << shift) >> shift;
+
+	if (cfg == 0)
+		ctx->align_size = 4;
+	else if (cfg == 1)
+		ctx->align_size = 8;
+	else
+		ctx->align_size = 16;
+
+	DRM_DEBUG_KMS("align_size = %d\n", ctx->align_size);
+
+	pm_runtime_mark_last_busy(ctx->dev);
+	pm_runtime_put_autosuspend(ctx->dev);
+}
+
 static void gsc_abort(struct exynos_drm_ipp *ipp,
 			  struct exynos_drm_ipp_task *task)
 {
@@ -1142,6 +1193,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 +1207,9 @@  static int gsc_bind(struct device *dev, struct device *master, void *data)
 	ctx->drm_dev = drm_dev;
 	drm_iommu_attach_device(drm_dev, dev);
 
+	if (ctx->driver_data->has_in_ic_max)
+		gsc_get_align_size(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 +1276,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++) {
@@ -1366,6 +1422,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..3f90b05 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -560,6 +560,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;
@@ -618,6 +626,7 @@  static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
 					       rotate, false);
 	if (ret)
 		return ret;
+
 	ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
 						src_fmt->limits,
 						src_fmt->num_limits, swap);
@@ -654,6 +663,12 @@  static int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task,
 
 	DRM_DEBUG_DRIVER("Setting buffer for task %pK\n", task);
 
+	/*
+	 * 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);
+
 	ret = exynos_drm_ipp_task_setup_buffer(src, filp);
 	if (ret) {
 		DRM_DEBUG_DRIVER("Task %pK: src buffer setup failed\n", task);
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