@@ -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[] = {
@@ -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);
@@ -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
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(-)