2019-04-15 07:09:58

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 0/6] Added scaler support for komeda

This patch series added scaling and image enhancement support for komeda
driver.
Enabled two different scaling usage:
- layer scaling: scaling a input image before composite it with others
- write-back scaling: scaling the composition result and write it to
memory.

This patchset depends on:
- https://patchwork.freedesktop.org/series/54449/
- https://patchwork.freedesktop.org/series/54450/
- https://patchwork.freedesktop.org/series/58976/
- https://patchwork.freedesktop.org/series/59000/

James Qian Wang (Arm Technology China) (6):
drm/komeda: Attach scaler to drm as private object
drm/komeda: Add the initial scaler support for CORE
drm/komeda: Implement D71 scaler support
drm/komeda: Add writeback scaling support
drm/komeda: Add engine clock requirement check for the downscaling
drm/komeda: Add image enhancement support

.../arm/display/komeda/d71/d71_component.c | 177 +++++++++++++++++-
.../gpu/drm/arm/display/komeda/d71/d71_dev.c | 2 +-
.../gpu/drm/arm/display/komeda/d71/d71_dev.h | 2 +
.../gpu/drm/arm/display/komeda/komeda_crtc.c | 67 ++++++-
.../gpu/drm/arm/display/komeda/komeda_kms.h | 14 +-
.../drm/arm/display/komeda/komeda_pipeline.c | 14 ++
.../drm/arm/display/komeda/komeda_pipeline.h | 33 +++-
.../display/komeda/komeda_pipeline_state.c | 164 +++++++++++++++-
.../gpu/drm/arm/display/komeda/komeda_plane.c | 77 +++++++-
.../arm/display/komeda/komeda_private_obj.c | 49 +++++
.../arm/display/komeda/komeda_wb_connector.c | 2 +
11 files changed, 585 insertions(+), 16 deletions(-)

--
2.17.1


2019-04-15 07:10:04

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 1/6] drm/komeda: Attach scaler to drm as private object

According to the komeda pipeline configuration, attach scaler to drm as
private object.

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../arm/display/komeda/komeda_private_obj.c | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)

diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
index d53bd6c23c5d..bac90ab8fdc9 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
@@ -60,6 +60,49 @@ static int komeda_layer_obj_add(struct komeda_kms_dev *kms,
return 0;
}

+static struct drm_private_state *
+komeda_scaler_atomic_duplicate_state(struct drm_private_obj *obj)
+{
+ struct komeda_scaler_state *st;
+
+ st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return NULL;
+
+ komeda_component_state_reset(&st->base);
+ __drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj);
+
+ return &st->base.obj;
+}
+
+static void
+komeda_scaler_atomic_destroy_state(struct drm_private_obj *obj,
+ struct drm_private_state *state)
+{
+ kfree(to_scaler_st(priv_to_comp_st(state)));
+}
+
+static const struct drm_private_state_funcs komeda_scaler_obj_funcs = {
+ .atomic_duplicate_state = komeda_scaler_atomic_duplicate_state,
+ .atomic_destroy_state = komeda_scaler_atomic_destroy_state,
+};
+
+static int komeda_scaler_obj_add(struct komeda_kms_dev *kms,
+ struct komeda_scaler *scaler)
+{
+ struct komeda_scaler_state *st;
+
+ st = kzalloc(sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->base.component = &scaler->base;
+ drm_atomic_private_obj_init(&kms->base,
+ &scaler->base.obj, &st->base.obj,
+ &komeda_scaler_obj_funcs);
+ return 0;
+}
+
static struct drm_private_state *
komeda_compiz_atomic_duplicate_state(struct drm_private_obj *obj)
{
@@ -258,6 +301,12 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
return err;
}

+ for (j = 0; j < pipe->n_scalers; j++) {
+ err = komeda_scaler_obj_add(kms, pipe->scalers[j]);
+ if (err)
+ return err;
+ }
+
err = komeda_compiz_obj_add(kms, pipe->compiz);
if (err)
return err;
--
2.17.1

2019-04-15 07:10:14

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 2/6] drm/komeda: Add the initial scaler support for CORE

This patch add the initial and necessary logic for CORE to support scaler:
- Complete the struct komeda_scaler and komeda_scaler_state for adding
the scaler specific features and capablities.
- Implement komeda_scaler_validate to check the scaler with the data flow
configurations.
- Enable scaling support for plane input path (layer input data flow).

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../drm/arm/display/komeda/komeda_pipeline.c | 14 +++
.../drm/arm/display/komeda/komeda_pipeline.h | 20 ++-
.../display/komeda/komeda_pipeline_state.c | 116 ++++++++++++++++++
.../gpu/drm/arm/display/komeda/komeda_plane.c | 8 ++
4 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
index 07398efc40f5..d0c27a12ddbe 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
@@ -126,6 +126,20 @@ komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id)
return c;
}

+struct komeda_component *
+komeda_pipeline_get_first_component(struct komeda_pipeline *pipe,
+ u32 comp_mask)
+{
+ struct komeda_component *c = NULL;
+ int id;
+
+ id = find_first_bit((unsigned long *)&comp_mask, 32);
+ if (id < 32)
+ c = komeda_pipeline_get_component(pipe, id);
+
+ return c;
+}
+
/** komeda_component_add - Add a component to &komeda_pipeline */
struct komeda_component *
komeda_component_add(struct komeda_pipeline *pipe,
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index ba5bc0810c81..85d7ec341acc 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -244,11 +244,17 @@ struct komeda_layer_state {

struct komeda_scaler {
struct komeda_component base;
- /* scaler features and caps */
+ struct malidp_range hsize, vsize;
+ u32 max_upscaling;
+ u32 max_downscaling;
};

struct komeda_scaler_state {
struct komeda_component_state base;
+ u16 hsize_in, vsize_in;
+ u16 hsize_out, vsize_out;
+ u8 en_scaling : 1,
+ en_alpha : 1; /* enable alpha processing */
};

struct komeda_compiz {
@@ -307,6 +313,7 @@ struct komeda_data_flow_cfg {
u32 rot;
int blending_zorder;
u8 pixel_blend_mode, layer_alpha;
+ u8 needs_scaling : 1;
};

/** struct komeda_pipeline_funcs */
@@ -407,6 +414,9 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev,
int komeda_assemble_pipelines(struct komeda_dev *mdev);
struct komeda_component *
komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id);
+struct komeda_component *
+komeda_pipeline_get_first_component(struct komeda_pipeline *pipe,
+ u32 comp_mask);

void komeda_pipeline_dump_register(struct komeda_pipeline *pipe,
struct seq_file *sf);
@@ -423,6 +433,14 @@ komeda_component_add(struct komeda_pipeline *pipe,
void komeda_component_destroy(struct komeda_dev *mdev,
struct komeda_component *c);

+static inline struct komeda_component *
+komeda_component_pickup_output(struct komeda_component *c, u32 avail_comps)
+{
+ u32 avail_inputs = c->supported_outputs & (avail_comps);
+
+ return komeda_pipeline_get_first_component(c->pipeline, avail_inputs);
+}
+
struct komeda_plane_state;
struct komeda_crtc_state;
struct komeda_crtc;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
index 9b29e9a9f49c..1dec321c727b 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -247,6 +247,26 @@ komeda_component_validate_private(struct komeda_component *c,
return err;
}

+/* Get current available scaler from the component->supported_outputs */
+static struct komeda_scaler *
+komeda_component_get_avail_scaler(struct komeda_component *c,
+ struct drm_atomic_state *state)
+{
+ struct komeda_pipeline_state *pipe_st;
+ u32 avail_scalers;
+
+ pipe_st = komeda_pipeline_get_state(c->pipeline, state);
+ if (!pipe_st)
+ return NULL;
+
+ avail_scalers = (pipe_st->active_comps & KOMEDA_PIPELINE_SCALERS) ^
+ KOMEDA_PIPELINE_SCALERS;
+
+ c = komeda_component_pickup_output(c, avail_scalers);
+
+ return to_scaler(c);
+}
+
static int
komeda_layer_check_cfg(struct komeda_layer *layer,
struct komeda_plane_state *kplane_st,
@@ -353,6 +373,98 @@ komeda_wb_layer_validate(struct komeda_layer *wb_layer,
return 0;
}

+static bool scaling_ratio_valid(u32 size_in, u32 size_out,
+ u32 max_upscaling, u32 max_downscaling)
+{
+ if (size_out > size_in * max_upscaling)
+ return false;
+ else if (size_in > size_out * max_downscaling)
+ return false;
+ return true;
+}
+
+static int
+komeda_scaler_check_cfg(struct komeda_scaler *scaler,
+ struct komeda_data_flow_cfg *dflow)
+{
+ u32 hsize_in, vsize_in, hsize_out, vsize_out;
+
+ hsize_in = dflow->in_w;
+ vsize_in = dflow->in_h;
+ hsize_out = dflow->out_w;
+ vsize_out = dflow->out_h;
+
+ if (!in_range(&scaler->hsize, hsize_in) ||
+ !in_range(&scaler->hsize, hsize_out)) {
+ DRM_DEBUG_ATOMIC("Invalid horizontal sizes");
+ return -EINVAL;
+ }
+
+ if (!in_range(&scaler->vsize, vsize_in) ||
+ !in_range(&scaler->vsize, vsize_out)) {
+ DRM_DEBUG_ATOMIC("Invalid vertical sizes");
+ return -EINVAL;
+ }
+
+ if (!scaling_ratio_valid(hsize_in, hsize_out, scaler->max_upscaling,
+ scaler->max_downscaling)) {
+ DRM_DEBUG_ATOMIC("Invalid horizontal scaling ratio");
+ return -EINVAL;
+ }
+
+ if (!scaling_ratio_valid(vsize_in, vsize_out, scaler->max_upscaling,
+ scaler->max_downscaling)) {
+ DRM_DEBUG_ATOMIC("Invalid vertical scaling ratio");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
+komeda_scaler_validate(void *user,
+ struct komeda_crtc_state *kcrtc_st,
+ struct komeda_data_flow_cfg *dflow)
+{
+ struct drm_atomic_state *drm_st = kcrtc_st->base.state;
+ struct komeda_component_state *c_st;
+ struct komeda_scaler_state *st;
+ struct komeda_scaler *scaler;
+ int err = 0;
+
+ if (!dflow->needs_scaling)
+ return 0;
+
+ scaler = komeda_component_get_avail_scaler(dflow->input.component,
+ drm_st);
+ if (!scaler) {
+ DRM_DEBUG_ATOMIC("No scaler available");
+ return -EINVAL;
+ }
+
+ err = komeda_scaler_check_cfg(scaler, dflow);
+ if (err)
+ return err;
+
+ c_st = komeda_component_get_state_and_set_user(&scaler->base,
+ drm_st, user, kcrtc_st->base.crtc);
+ if (IS_ERR(c_st))
+ return PTR_ERR(c_st);
+
+ st = to_scaler_st(c_st);
+
+ st->hsize_in = dflow->in_w;
+ st->vsize_in = dflow->in_h;
+ st->hsize_out = dflow->out_w;
+ st->vsize_out = dflow->out_w;
+ st->en_scaling = dflow->needs_scaling;
+ /* Enable alpha processing if the next stage needs the pixel alpha */
+ st->en_alpha = dflow->pixel_blend_mode != DRM_MODE_BLEND_PIXEL_NONE;
+
+ komeda_component_add_input(&st->base, &dflow->input, 0);
+ komeda_component_set_output(&dflow->input, &scaler->base, 0);
+ return err;
+}
+
void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
u16 *hsize, u16 *vsize)
{
@@ -518,6 +630,10 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
if (err)
return err;

+ err = komeda_scaler_validate(plane, kcrtc_st, dflow);
+ if (err)
+ return err;
+
err = komeda_compiz_set_input(pipe->compiz, kcrtc_st, dflow);

return err;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
index 14d68612052f..38c94e8f0ce2 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
@@ -15,6 +15,7 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,
struct komeda_data_flow_cfg *dflow)
{
struct drm_framebuffer *fb = st->fb;
+ u32 w, h;

memset(dflow, 0, sizeof(*dflow));

@@ -35,6 +36,13 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,
dflow->in_w = st->src_w >> 16;
dflow->in_h = st->src_h >> 16;

+ w = dflow->in_w;
+ h = dflow->in_h;
+ if (drm_rotation_90_or_270(dflow->rot))
+ swap(w, h);
+
+ dflow->needs_scaling = (w != dflow->out_w) || (h != dflow->out_h);
+
return 0;
}

--
2.17.1

2019-04-15 07:10:25

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 3/6] drm/komeda: Implement D71 scaler support

1. Add scaler component and initialize it according to D71 HW.
2. Implement d71_scaler_update/disable/dump

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../arm/display/komeda/d71/d71_component.c | 131 +++++++++++++++++-
1 file changed, 130 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
index 33ca1718b5cd..5a4c8fd122da 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -551,6 +551,132 @@ static int d71_compiz_init(struct d71_dev *d71,
return 0;
}

+static void d71_scaler_update_filter_lut(u32 __iomem *reg, u32 hsize_in,
+ u32 vsize_in, u32 hsize_out,
+ u32 vsize_out)
+{
+ u32 val = 0;
+
+ if (hsize_in <= hsize_out)
+ val |= 0x62;
+ else if (hsize_in <= (hsize_out + hsize_out / 2))
+ val |= 0x63;
+ else if (hsize_in <= hsize_out * 2)
+ val |= 0x64;
+ else if (hsize_in <= hsize_out * 2 + (hsize_out * 3) / 4)
+ val |= 0x65;
+ else
+ val |= 0x66;
+
+ if (vsize_in <= vsize_out)
+ val |= SC_VTSEL(0x6A);
+ else if (vsize_in <= (vsize_out + vsize_out / 2))
+ val |= SC_VTSEL(0x6B);
+ else if (vsize_in <= vsize_out * 2)
+ val |= SC_VTSEL(0x6C);
+ else if (vsize_in <= vsize_out * 2 + vsize_out * 3 / 4)
+ val |= SC_VTSEL(0x6D);
+ else
+ val |= SC_VTSEL(0x6E);
+
+ malidp_write32(reg, SC_COEFFTAB, val);
+}
+
+static void d71_scaler_update(struct komeda_component *c,
+ struct komeda_component_state *state)
+{
+ struct komeda_scaler_state *st = to_scaler_st(state);
+ u32 __iomem *reg = c->reg;
+ u32 init_ph, delta_ph, ctrl;
+
+ d71_scaler_update_filter_lut(reg, st->hsize_in, st->vsize_in,
+ st->hsize_out, st->vsize_out);
+
+ malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize_in, st->vsize_in));
+ malidp_write32(reg, SC_OUT_SIZE, HV_SIZE(st->hsize_out, st->vsize_out));
+
+ init_ph = (st->hsize_in << 15) / st->hsize_out;
+ malidp_write32(reg, SC_H_INIT_PH, init_ph);
+
+ delta_ph = (st->hsize_in << 16) / st->hsize_out;
+ malidp_write32(reg, SC_H_DELTA_PH, delta_ph);
+
+ init_ph = (st->vsize_in << 15) / st->vsize_out;
+ malidp_write32(reg, SC_V_INIT_PH, init_ph);
+
+ delta_ph = (st->vsize_in << 16) / st->vsize_out;
+ malidp_write32(reg, SC_V_DELTA_PH, delta_ph);
+
+ ctrl = 0;
+ ctrl |= st->en_scaling ? SC_CTRL_SCL : 0;
+ ctrl |= st->en_alpha ? SC_CTRL_AP : 0;
+
+ malidp_write32(reg, BLK_CONTROL, ctrl);
+ malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
+}
+
+static void d71_scaler_dump(struct komeda_component *c, struct seq_file *sf)
+{
+ u32 v[9];
+
+ dump_block_header(sf, c->reg);
+
+ get_values_from_reg(c->reg, 0x80, 1, v);
+ seq_printf(sf, "SC_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, 0xD0, 1, v);
+ seq_printf(sf, "SC_CONTROL:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, 0xDC, 9, v);
+ seq_printf(sf, "SC_COEFFTAB:\t\t0x%X\n", v[0]);
+ seq_printf(sf, "SC_IN_SIZE:\t\t0x%X\n", v[1]);
+ seq_printf(sf, "SC_OUT_SIZE:\t\t0x%X\n", v[2]);
+ seq_printf(sf, "SC_H_CROP:\t\t0x%X\n", v[3]);
+ seq_printf(sf, "SC_V_CROP:\t\t0x%X\n", v[4]);
+ seq_printf(sf, "SC_H_INIT_PH:\t\t0x%X\n", v[5]);
+ seq_printf(sf, "SC_H_DELTA_PH:\t\t0x%X\n", v[6]);
+ seq_printf(sf, "SC_V_INIT_PH:\t\t0x%X\n", v[7]);
+ seq_printf(sf, "SC_V_DELTA_PH:\t\t0x%X\n", v[8]);
+}
+
+static struct komeda_component_funcs d71_scaler_funcs = {
+ .update = d71_scaler_update,
+ .disable = d71_component_disable,
+ .dump_register = d71_scaler_dump,
+};
+
+static int d71_scaler_init(struct d71_dev *d71,
+ struct block_header *blk, u32 __iomem *reg)
+{
+ struct komeda_component *c;
+ struct komeda_scaler *scaler;
+ u32 pipe_id, comp_id;
+
+ get_resources_id(blk->block_info, &pipe_id, &comp_id);
+
+ c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*scaler),
+ comp_id, BLOCK_INFO_INPUT_ID(blk->block_info),
+ &d71_scaler_funcs,
+ 1, get_valid_inputs(blk), 1, reg,
+ "CU%d_SCALER%d",
+ pipe_id, BLOCK_INFO_BLK_ID(blk->block_info));
+
+ if (!c) {
+ DRM_ERROR("Failed to initialize scaler");
+ return -1;
+ }
+
+ scaler = to_scaler(c);
+ set_range(&scaler->hsize, 4, d71->max_line_size);
+ set_range(&scaler->vsize, 4, 4096);
+ scaler->max_downscaling = 6;
+ scaler->max_upscaling = 64;
+
+ malidp_write32(c->reg, BLK_CONTROL, 0);
+
+ return 0;
+}
+
static void d71_improc_update(struct komeda_component *c,
struct komeda_component_state *state)
{
@@ -770,8 +896,11 @@ int d71_probe_block(struct d71_dev *d71,
err = d71_compiz_init(d71, blk, reg);
break;

- case D71_BLK_TYPE_CU_SPLITTER:
case D71_BLK_TYPE_CU_SCALER:
+ err = d71_scaler_init(d71, blk, reg);
+ break;
+
+ case D71_BLK_TYPE_CU_SPLITTER:
case D71_BLK_TYPE_CU_MERGER:
break;

--
2.17.1

2019-04-15 07:11:12

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 4/6] drm/komeda: Add writeback scaling support

1. Add scaler to writeback pipeline to enable the writeback scaling support
2. Display HW can not do upscaling for writeback, check it when validate.

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../drm/arm/display/komeda/komeda_pipeline.h | 2 ++
.../display/komeda/komeda_pipeline_state.c | 36 +++++++++++++++----
.../gpu/drm/arm/display/komeda/komeda_plane.c | 8 +----
.../arm/display/komeda/komeda_wb_connector.c | 2 ++
4 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index 85d7ec341acc..bfad7d03f801 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -470,4 +470,6 @@ void komeda_pipeline_disable(struct komeda_pipeline *pipe,
void komeda_pipeline_update(struct komeda_pipeline *pipe,
struct drm_atomic_state *old_state);

+void komeda_complete_data_flow_cfg(struct komeda_data_flow_cfg *dflow);
+
#endif /* _KOMEDA_PIPELINE_H_*/
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
index 1dec321c727b..c6f19969fc10 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -388,6 +388,7 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler,
struct komeda_data_flow_cfg *dflow)
{
u32 hsize_in, vsize_in, hsize_out, vsize_out;
+ u32 max_upscaling;

hsize_in = dflow->in_w;
vsize_in = dflow->in_h;
@@ -406,13 +407,21 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler,
return -EINVAL;
}

- if (!scaling_ratio_valid(hsize_in, hsize_out, scaler->max_upscaling,
+ /* If input comes from compiz that means the scaling is for writeback
+ * and scaler can not do upscaling for writeback
+ */
+ if (has_bit(dflow->input.component->id, KOMEDA_PIPELINE_COMPIZS))
+ max_upscaling = 1;
+ else
+ max_upscaling = scaler->max_upscaling;
+
+ if (!scaling_ratio_valid(hsize_in, hsize_out, max_upscaling,
scaler->max_downscaling)) {
DRM_DEBUG_ATOMIC("Invalid horizontal scaling ratio");
return -EINVAL;
}

- if (!scaling_ratio_valid(vsize_in, vsize_out, scaler->max_upscaling,
+ if (!scaling_ratio_valid(vsize_in, vsize_out, max_upscaling,
scaler->max_downscaling)) {
DRM_DEBUG_ATOMIC("Invalid vertical scaling ratio");
return -EINVAL;
@@ -612,6 +621,17 @@ komeda_timing_ctrlr_validate(struct komeda_timing_ctrlr *ctrlr,
return 0;
}

+void komeda_complete_data_flow_cfg(struct komeda_data_flow_cfg *dflow)
+{
+ u32 w = dflow->in_w;
+ u32 h = dflow->in_h;
+
+ if (drm_rotation_90_or_270(dflow->rot))
+ swap(w, h);
+
+ dflow->needs_scaling = (w != dflow->out_w) || (h != dflow->out_h);
+}
+
int komeda_build_layer_data_flow(struct komeda_layer *layer,
struct komeda_plane_state *kplane_st,
struct komeda_crtc_state *kcrtc_st,
@@ -639,16 +659,18 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
return err;
}

+/* writeback data path: compiz -> scaler -> wb_layer -> memory */
int komeda_build_wb_data_flow(struct komeda_layer *wb_layer,
struct drm_connector_state *conn_st,
struct komeda_crtc_state *kcrtc_st,
struct komeda_data_flow_cfg *dflow)
{
- if ((dflow->in_w != dflow->out_w) ||
- (dflow->in_h != dflow->out_h)) {
- DRM_DEBUG_ATOMIC("current do not support scaling writeback.\n");
- return -EINVAL;
- }
+ struct drm_connector *conn = conn_st->connector;
+ int err;
+
+ err = komeda_scaler_validate(conn, kcrtc_st, dflow);
+ if (err)
+ return err;

return komeda_wb_layer_validate(wb_layer, conn_st, dflow);
}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
index 38c94e8f0ce2..5de462172151 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
@@ -15,7 +15,6 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,
struct komeda_data_flow_cfg *dflow)
{
struct drm_framebuffer *fb = st->fb;
- u32 w, h;

memset(dflow, 0, sizeof(*dflow));

@@ -36,12 +35,7 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,
dflow->in_w = st->src_w >> 16;
dflow->in_h = st->src_h >> 16;

- w = dflow->in_w;
- h = dflow->in_h;
- if (drm_rotation_90_or_270(dflow->rot))
- swap(w, h);
-
- dflow->needs_scaling = (w != dflow->out_w) || (h != dflow->out_h);
+ komeda_complete_data_flow_cfg(dflow);

return 0;
}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
index 0c1a4220c280..eed521218ef3 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -31,6 +31,8 @@ komeda_wb_init_data_flow(struct komeda_layer *wb_layer,
dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
dflow->rot = DRM_MODE_ROTATE_0;

+ komeda_complete_data_flow_cfg(dflow);
+
return 0;
}

--
2.17.1

2019-04-15 07:11:17

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 6/6] drm/komeda: Add image enhancement support

Besides scaling, Arm display scaler also can support image enhancement.
For support it, Add a new property "img_enhancement" to plane, then user
can turn on/off it by this property, and kernel follow user's requirement
to maitain the state and enable/disable the real HW image enhancement.

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../arm/display/komeda/d71/d71_component.c | 1 +
.../gpu/drm/arm/display/komeda/komeda_kms.h | 6 +-
.../drm/arm/display/komeda/komeda_pipeline.h | 6 +-
.../display/komeda/komeda_pipeline_state.c | 3 +-
.../gpu/drm/arm/display/komeda/komeda_plane.c | 75 ++++++++++++++++++-
5 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
index bf214dc9e372..21d2b3a5c630 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -610,6 +610,7 @@ static void d71_scaler_update(struct komeda_component *c,
ctrl = 0;
ctrl |= st->en_scaling ? SC_CTRL_SCL : 0;
ctrl |= st->en_alpha ? SC_CTRL_AP : 0;
+ ctrl |= st->en_img_enhancement ? SC_CTRL_IENH : 0;

malidp_write32(reg, BLK_CONTROL, ctrl);
malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index db59a9042beb..e6e059f2af52 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -32,6 +32,9 @@ struct komeda_plane {
* Layers with same capabilities.
*/
struct komeda_layer *layer;
+
+ /** @prop_img_enhancement: for on/off image enhancement */
+ struct drm_property *prop_img_enhancement;
};

/**
@@ -44,7 +47,8 @@ struct komeda_plane_state {
/** @base: &drm_plane_state */
struct drm_plane_state base;

- /* private properties */
+ /* @img_enhancement: on/off image enhancement */
+ u8 img_enhancement : 1;
};

/**
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index b26c8552a2d1..a32a8d697f34 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -254,7 +254,8 @@ struct komeda_scaler_state {
u16 hsize_in, vsize_in;
u16 hsize_out, vsize_out;
u8 en_scaling : 1,
- en_alpha : 1; /* enable alpha processing */
+ en_alpha : 1, /* enable alpha processing */
+ en_img_enhancement : 1;
};

struct komeda_compiz {
@@ -313,7 +314,8 @@ struct komeda_data_flow_cfg {
u32 rot;
int blending_zorder;
u8 pixel_blend_mode, layer_alpha;
- u8 needs_scaling : 1;
+ u8 needs_scaling : 1,
+ needs_img_enhancement : 1;
};

/* struct komeda_pipeline_funcs */
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
index 53eb40941281..2430388a78c3 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -455,7 +455,7 @@ komeda_scaler_validate(void *user,
struct komeda_scaler *scaler;
int err = 0;

- if (!dflow->needs_scaling)
+ if (!(dflow->needs_scaling || dflow->needs_img_enhancement))
return 0;

scaler = komeda_component_get_avail_scaler(dflow->input.component,
@@ -483,6 +483,7 @@ komeda_scaler_validate(void *user,
st->en_scaling = dflow->needs_scaling;
/* Enable alpha processing if the next stage needs the pixel alpha */
st->en_alpha = dflow->pixel_blend_mode != DRM_MODE_BLEND_PIXEL_NONE;
+ st->en_img_enhancement = dflow->needs_img_enhancement;

komeda_component_add_input(&st->base, &dflow->input, 0);
komeda_component_set_output(&dflow->input, &scaler->base, 0);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
index 5de462172151..fa455665aa77 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
@@ -14,6 +14,7 @@ static int
komeda_plane_init_data_flow(struct drm_plane_state *st,
struct komeda_data_flow_cfg *dflow)
{
+ struct komeda_plane_state *kplane_st = to_kplane_st(st);
struct drm_framebuffer *fb = st->fb;

memset(dflow, 0, sizeof(*dflow));
@@ -22,7 +23,7 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,

/* if format doesn't have alpha, fix blend mode to PIXEL_NONE */
dflow->pixel_blend_mode = fb->format->has_alpha ?
- st->pixel_blend_mode : DRM_MODE_BLEND_PIXEL_NONE;
+ st->pixel_blend_mode : DRM_MODE_BLEND_PIXEL_NONE;
dflow->layer_alpha = st->alpha >> 8;

dflow->out_x = st->crtc_x;
@@ -35,6 +36,8 @@ komeda_plane_init_data_flow(struct drm_plane_state *st,
dflow->in_w = st->src_w >> 16;
dflow->in_h = st->src_h >> 16;

+ dflow->needs_img_enhancement = kplane_st->img_enhancement;
+
komeda_complete_data_flow_cfg(dflow);

return 0;
@@ -130,7 +133,7 @@ static void komeda_plane_reset(struct drm_plane *plane)
static struct drm_plane_state *
komeda_plane_atomic_duplicate_state(struct drm_plane *plane)
{
- struct komeda_plane_state *new;
+ struct komeda_plane_state *new, *old;

if (WARN_ON(!plane->state))
return NULL;
@@ -141,6 +144,10 @@ komeda_plane_atomic_duplicate_state(struct drm_plane *plane)

__drm_atomic_helper_plane_duplicate_state(plane, &new->base);

+ old = to_kplane_st(plane->state);
+
+ new->img_enhancement = old->img_enhancement;
+
return &new->base;
}

@@ -152,6 +159,40 @@ komeda_plane_atomic_destroy_state(struct drm_plane *plane,
kfree(to_kplane_st(state));
}

+static int
+komeda_plane_atomic_get_property(struct drm_plane *plane,
+ const struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct komeda_plane *kplane = to_kplane(plane);
+ struct komeda_plane_state *st = to_kplane_st(state);
+
+ if (property == kplane->prop_img_enhancement)
+ *val = st->img_enhancement;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+komeda_plane_atomic_set_property(struct drm_plane *plane,
+ struct drm_plane_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct komeda_plane *kplane = to_kplane(plane);
+ struct komeda_plane_state *st = to_kplane_st(state);
+
+ if (property == kplane->prop_img_enhancement)
+ st->img_enhancement = !!val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static bool
komeda_plane_format_mod_supported(struct drm_plane *plane,
u32 format, u64 modifier)
@@ -171,9 +212,33 @@ static const struct drm_plane_funcs komeda_plane_funcs = {
.reset = komeda_plane_reset,
.atomic_duplicate_state = komeda_plane_atomic_duplicate_state,
.atomic_destroy_state = komeda_plane_atomic_destroy_state,
+ .atomic_get_property = komeda_plane_atomic_get_property,
+ .atomic_set_property = komeda_plane_atomic_set_property,
.format_mod_supported = komeda_plane_format_mod_supported,
};

+static int
+komeda_plane_create_layer_properties(struct komeda_plane *kplane,
+ struct komeda_layer *layer)
+{
+ struct drm_device *drm = kplane->base.dev;
+ struct drm_plane *plane = &kplane->base;
+ struct drm_property *prop = NULL;
+
+ /* property: layer image_enhancement */
+ if (layer->base.supported_outputs & KOMEDA_PIPELINE_SCALERS) {
+ prop = drm_property_create_bool(drm, DRM_MODE_PROP_ATOMIC,
+ "img_enhancement");
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&plane->base, prop, 0);
+ kplane->prop_img_enhancement = prop;
+ }
+
+ return 0;
+}
+
/* for komeda, which is pipeline can be share between crtcs */
static u32 get_possible_crtcs(struct komeda_kms_dev *kms,
struct komeda_pipeline *pipe)
@@ -241,11 +306,15 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,

err = drm_plane_create_blend_mode_property(plane,
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
- BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE));
if (err)
goto cleanup;

+ err = komeda_plane_create_layer_properties(kplane, layer);
+ if (err)
+ goto cleanup;
+
return 0;
cleanup:
komeda_plane_destroy(plane);
--
2.17.1

2019-04-15 07:12:29

by James Qian Wang

[permalink] [raw]
Subject: [PATCH v1 5/6] drm/komeda: Add engine clock requirement check for the downscaling

For downscaling there is a restriction, the downscaling needed engine
clock can not acceed the real engine clock, and the clock requirement
mostly depend on the specific HW, to solve this problem:
1. Add a pipeline func - downscaling_clk_check for CORE to query the real
HW if downscaling can be supported.
2. Add new property clock ratio which is the ratio of:
(mclk << 32) / pxlclk
then User driver can use this ratio to do the clock check to avoid post
an invalid downscaling to kernel.

Signed-off-by: James Qian Wang (Arm Technology China) <[email protected]>
---
.../arm/display/komeda/d71/d71_component.c | 45 +++++++++++++
.../gpu/drm/arm/display/komeda/d71/d71_dev.c | 2 +-
.../gpu/drm/arm/display/komeda/d71/d71_dev.h | 2 +
.../gpu/drm/arm/display/komeda/komeda_crtc.c | 67 ++++++++++++++++++-
.../gpu/drm/arm/display/komeda/komeda_kms.h | 8 +++
.../drm/arm/display/komeda/komeda_pipeline.h | 9 ++-
.../display/komeda/komeda_pipeline_state.c | 17 ++++-
7 files changed, 144 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
index 5a4c8fd122da..bf214dc9e372 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -677,6 +677,47 @@ static int d71_scaler_init(struct d71_dev *d71,
return 0;
}

+static int d71_downscaling_clk_check(struct komeda_pipeline *pipe,
+ struct drm_display_mode *mode,
+ unsigned long mclk_rate,
+ struct komeda_data_flow_cfg *dflow)
+{
+ u32 h_in = dflow->in_w;
+ u32 v_in = dflow->in_h;
+ u32 v_out = dflow->out_h;
+ u64 fraction, denominator;
+
+ /* D71 downscaling must satisfy the following equation
+ *
+ * MCLK h_in * v_in
+ * ------- >= ---------------------------------------------
+ * PXLCLK (h_total - (1 + 2 * v_in / v_out)) * v_out
+ *
+ * In only horizontal downscaling situation, the right side should be
+ * multiplied by (h_total - 3) / (h_active - 3), then equation becomes
+ *
+ * MCLK h_in
+ * ------- >= ----------------
+ * PXLCLK (h_active - 3)
+ *
+ * To avoid precision lost the equation 1 will be convert to:
+ *
+ * MCLK h_in * v_in
+ * ------- >= -----------------------------------
+ * PXLCLK (h_total -1 ) * v_out - 2 * v_in
+ */
+ if (v_in == v_out) {
+ fraction = h_in;
+ denominator = mode->hdisplay - 3;
+ } else {
+ fraction = h_in * v_in;
+ denominator = (mode->htotal - 1) * v_out - 2 * v_in;
+ }
+
+ return mclk_rate * denominator >= mode->clock * 1000 * fraction ?
+ 0 : -EINVAL;
+}
+
static void d71_improc_update(struct komeda_component *c,
struct komeda_component_state *state)
{
@@ -938,3 +979,7 @@ int d71_probe_block(struct d71_dev *d71,

return err;
}
+
+struct komeda_pipeline_funcs d71_pipeline_funcs = {
+ .downscaling_clk_check = d71_downscaling_clk_check,
+};
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
index 34506ef7ad40..dd99e06180fd 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
@@ -390,7 +390,7 @@ static int d71_enum_resources(struct komeda_dev *mdev)

for (i = 0; i < d71->num_pipelines; i++) {
pipe = komeda_pipeline_add(mdev, sizeof(struct d71_pipeline),
- NULL);
+ &d71_pipeline_funcs);
if (IS_ERR(pipe)) {
err = PTR_ERR(pipe);
goto err_cleanup;
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
index 7465c57d9774..40f87955ee16 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
@@ -43,6 +43,8 @@ struct d71_dev {

#define to_d71_pipeline(x) container_of(x, struct d71_pipeline, base)

+extern struct komeda_pipeline_funcs d71_pipeline_funcs;
+
int d71_probe_block(struct d71_dev *d71,
struct block_header *blk, u32 __iomem *reg);
void d71_read_block_header(u32 __iomem *reg, struct block_header *blk);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index c0e98605def8..817ae892f7b4 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -18,6 +18,22 @@
#include "komeda_dev.h"
#include "komeda_kms.h"

+static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st)
+{
+ u64 pxlclk, mclk;
+
+ if (!kcrtc_st->base.active) {
+ kcrtc_st->clock_ratio = 0;
+ return;
+ }
+
+ pxlclk = kcrtc_st->base.adjusted_mode.clock * 1000;
+ mclk = komeda_calc_mclk(kcrtc_st) << 32;
+
+ do_div(mclk, pxlclk);
+ kcrtc_st->clock_ratio = mclk;
+}
+
/**
* komeda_crtc_atomic_check - build display output data flow
* @crtc: DRM crtc
@@ -38,6 +54,9 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
int err;

+ if (drm_atomic_crtc_needs_modeset(state))
+ komeda_crtc_update_clock_ratio(kcrtc_st);
+
if (state->active) {
err = komeda_build_display_data_flow(kcrtc, kcrtc_st);
if (err)
@@ -52,11 +71,12 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}

-u32 komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
+unsigned long komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
{
- unsigned long mclk = kcrtc_st->base.adjusted_mode.clock * 1000;
+ struct komeda_dev *mdev = kcrtc_st->base.crtc->dev->dev_private;
+ unsigned long pxlclk = kcrtc_st->base.adjusted_mode.clock;

- return mclk;
+ return clk_round_rate(mdev->mclk, pxlclk * 1000);
}

/* For active a crtc, mainly need two parts of preparation
@@ -404,6 +424,7 @@ komeda_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);

new->affected_pipes = old->active_pipes;
+ new->clock_ratio = old->clock_ratio;

return &new->base;
}
@@ -432,6 +453,25 @@ static void komeda_crtc_vblank_disable(struct drm_crtc *crtc)
mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, false);
}

+static int
+komeda_crtc_atomic_get_property(struct drm_crtc *crtc,
+ const struct drm_crtc_state *state,
+ struct drm_property *property, uint64_t *val)
+{
+ struct komeda_crtc *kcrtc = to_kcrtc(crtc);
+ struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
+
+ if (property == kcrtc->clock_ratio_property) {
+ *val = kcrtc_st->clock_ratio;
+ pr_info("Get property: %llu.\n", kcrtc_st->clock_ratio);
+ } else {
+ DRM_DEBUG_DRIVER("Unknown property %s\n", property->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct drm_crtc_funcs komeda_crtc_funcs = {
.gamma_set = drm_atomic_helper_legacy_gamma_set,
.destroy = drm_crtc_cleanup,
@@ -442,6 +482,7 @@ static const struct drm_crtc_funcs komeda_crtc_funcs = {
.atomic_destroy_state = komeda_crtc_atomic_destroy_state,
.enable_vblank = komeda_crtc_vblank_enable,
.disable_vblank = komeda_crtc_vblank_disable,
+ .atomic_get_property = komeda_crtc_atomic_get_property,
};

int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
@@ -477,6 +518,22 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
return 0;
}

+static int komeda_crtc_create_clock_ratio_property(struct komeda_crtc *kcrtc)
+{
+ struct drm_crtc *crtc = &kcrtc->base;
+ struct drm_property *prop;
+
+ prop = drm_property_create_range(crtc->dev, DRM_MODE_PROP_ATOMIC,
+ "CLOCK_RATIO", 0, U64_MAX);
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&crtc->base, prop, 0);
+ kcrtc->clock_ratio_property = prop;
+
+ return 0;
+}
+
static struct drm_plane *
get_crtc_primary(struct komeda_kms_dev *kms, struct komeda_crtc *crtc)
{
@@ -513,6 +570,10 @@ static int komeda_crtc_add(struct komeda_kms_dev *kms,

crtc->port = kcrtc->master->of_output_port;

+ err = komeda_crtc_create_clock_ratio_property(kcrtc);
+ if (err)
+ return err;
+
return 0;
}

diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index f16e9e577593..db59a9042beb 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -79,6 +79,9 @@ struct komeda_crtc {

/** @disable_done: this flip_done is for tracing the disable */
struct completion *disable_done;
+
+ /** @clock_ratio_property: property for ratio of (mclk << 32)/pxlclk */
+ struct drm_property *clock_ratio_property;
};

/**
@@ -101,6 +104,9 @@ struct komeda_crtc_state {
* the active pipelines in once display instance
*/
u32 active_pipes;
+
+ /** @clock_ratio: ratio of (mclk << 32)/pxlclk */
+ u64 clock_ratio;
};

/** struct komeda_kms_dev - for gather KMS related things */
@@ -142,6 +148,8 @@ is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
return BIT(drm_connector_index(conn)) == changed_connectors;
}

+unsigned long komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st);
+
int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);

int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index bfad7d03f801..b26c8552a2d1 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -316,8 +316,15 @@ struct komeda_data_flow_cfg {
u8 needs_scaling : 1;
};

-/** struct komeda_pipeline_funcs */
+/* struct komeda_pipeline_funcs */
struct komeda_pipeline_funcs {
+ /* check if the mclk (main engine clock) can satisfy the clock
+ * requirements of the downscaling that specified by dflow
+ */
+ int (*downscaling_clk_check)(struct komeda_pipeline *pipe,
+ struct drm_display_mode *mode,
+ unsigned long mclk_rate,
+ struct komeda_data_flow_cfg *dflow);
/* dump_register: Optional, dump registers to seq_file */
void (*dump_register)(struct komeda_pipeline *pipe,
struct seq_file *sf);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
index c6f19969fc10..53eb40941281 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -385,6 +385,7 @@ static bool scaling_ratio_valid(u32 size_in, u32 size_out,

static int
komeda_scaler_check_cfg(struct komeda_scaler *scaler,
+ struct komeda_crtc_state *kcrtc_st,
struct komeda_data_flow_cfg *dflow)
{
u32 hsize_in, vsize_in, hsize_out, vsize_out;
@@ -426,6 +427,20 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler,
DRM_DEBUG_ATOMIC("Invalid vertical scaling ratio");
return -EINVAL;
}
+
+ if (hsize_in > hsize_out || vsize_in > vsize_out) {
+ struct komeda_pipeline *pipe = scaler->base.pipeline;
+ int err;
+
+ err = pipe->funcs->downscaling_clk_check(pipe,
+ &kcrtc_st->base.adjusted_mode,
+ komeda_calc_mclk(kcrtc_st), dflow);
+ if (err) {
+ DRM_DEBUG_ATOMIC("mclk can't satisfy the clock requirement of the downscaling");
+ return err;
+ }
+ }
+
return 0;
}

@@ -450,7 +465,7 @@ komeda_scaler_validate(void *user,
return -EINVAL;
}

- err = komeda_scaler_check_cfg(scaler, dflow);
+ err = komeda_scaler_check_cfg(scaler, kcrtc_st, dflow);
if (err)
return err;

--
2.17.1