All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] drm/komeda: Add writeback support
@ 2019-05-23  9:36 ` james qian wang (Arm Technology China)
  0 siblings, 0 replies; 4+ messages in thread
From: james qian wang (Arm Technology China) @ 2019-05-23  9:36 UTC (permalink / raw)
  To: Liviu Dudau, airlied, Brian Starkey, maarten.lankhorst, sean
  Cc: Jonathan Chai (Arm Technology China),
	Julien Yin (Arm Technology China),
	thomas Sun (Arm Technology China),
	Lowry Li (Arm Technology China),
	Ayan Halder, Tiannan Zhu (Arm Technology China),
	Yiqi Kang (Arm Technology China),
	nd, linux-kernel, dri-devel, Ben Davis,
	Oscar Zhang (Arm Technology China),
	Channing Chen (Arm Technology China),
	james qian wang (Arm Technology China)

Komeda driver uses a individual component to describe the HW's writeback
caps, but drivers doesn't define a new structure and still uses the
existing "struct komeda_layer" to describe this new component.
The detailed changes as follow:

1. Initialize wb_layer according to HW and report it to CORE.
2. CORE exposes wb_layer as a resource to KMS by private_obj.
3. Report writeback supporting by add a wb_connector to KMS, and then
   wb_connector will take act as a component resources user,
   so the func komeda_wb_encoder_atomic_check claims komeda resources
   (scaler and wb_layer) accroding to its state configuration to the
   wb_connector. and the wb_state configuration will be validated on the
   specific component resources to see if the caps of component can
   meet the requirement of wb_connector. if not check failed.
4. Update irq_handler to notify the completion of writeback.

NOTE:
This change doesn't add scaling writeback support, that support will
be added in the future after the scaler support.

v2: Rebase
v3: Rebase and constify the d71_wb_layer_funcs
v4: Addressed Ayan's comments

Depends on:
- https://patchwork.freedesktop.org/series/59915/

Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
---
 drivers/gpu/drm/arm/display/komeda/Makefile   |   1 +
 .../arm/display/komeda/d71/d71_component.c    |  90 ++++++++-
 .../gpu/drm/arm/display/komeda/komeda_crtc.c  |  15 ++
 .../arm/display/komeda/komeda_framebuffer.c   |  19 ++
 .../gpu/drm/arm/display/komeda/komeda_kms.c   |   4 +
 .../gpu/drm/arm/display/komeda/komeda_kms.h   |  27 +++
 .../drm/arm/display/komeda/komeda_pipeline.h  |   7 +
 .../display/komeda/komeda_pipeline_state.c    |  51 ++++-
 .../arm/display/komeda/komeda_private_obj.c   |   6 +
 .../arm/display/komeda/komeda_wb_connector.c  | 181 ++++++++++++++++++
 10 files changed, 398 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c

diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
index 62bd1bff66a3..d7e29fc688c3 100644
--- a/drivers/gpu/drm/arm/display/komeda/Makefile
+++ b/drivers/gpu/drm/arm/display/komeda/Makefile
@@ -14,6 +14,7 @@ komeda-y := \
 	komeda_kms.o \
 	komeda_crtc.o \
 	komeda_plane.o \
+	komeda_wb_connector.o \
 	komeda_private_obj.o
 
 komeda-y += \
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 6bab816ed8e7..323e5994a55c 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -288,10 +288,98 @@ static int d71_layer_init(struct d71_dev *d71,
 	return 0;
 }
 
+static void d71_wb_layer_update(struct komeda_component *c,
+				struct komeda_component_state *state)
+{
+	struct komeda_layer_state *st = to_layer_st(state);
+	struct drm_connector_state *conn_st = state->wb_conn->state;
+	struct drm_framebuffer *fb = conn_st->writeback_job->fb;
+	struct komeda_fb *kfb = to_kfb(fb);
+	u32 __iomem *reg = c->reg;
+	u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN;
+	int i;
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_LOW,
+			       lower_32_bits(st->addr[i]));
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_HIGH,
+			       upper_32_bits(st->addr[i]));
+
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_STRIDE,
+			       fb->pitches[i] & 0xFFFF);
+	}
+
+	malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
+	malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
+	malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
+	malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
+}
+
+static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf)
+{
+	u32 v[12], i;
+
+	dump_block_header(sf, c->reg);
+
+	get_values_from_reg(c->reg, 0x80, 1, v);
+	seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+	get_values_from_reg(c->reg, 0xD0, 3, v);
+	seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]);
+	seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]);
+	seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]);
+
+	get_values_from_reg(c->reg, 0xE0, 1, v);
+	seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]);
+
+	for (i = 0; i < 2; i++) {
+		get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v);
+		seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]);
+		seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]);
+		seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]);
+	}
+
+	get_values_from_reg(c->reg, 0x130, 12, v);
+	for (i = 0; i < 12; i++)
+		seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
+}
+
+static void d71_wb_layer_disable(struct komeda_component *c)
+{
+	malidp_write32(c->reg, BLK_INPUT_ID0, 0);
+	malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0);
+}
+
+static const struct komeda_component_funcs d71_wb_layer_funcs = {
+	.update		= d71_wb_layer_update,
+	.disable	= d71_wb_layer_disable,
+	.dump_register	= d71_wb_layer_dump,
+};
+
 static int d71_wb_layer_init(struct d71_dev *d71,
 			     struct block_header *blk, u32 __iomem *reg)
 {
-	DRM_DEBUG("Detect D71_Wb_Layer.\n");
+	struct komeda_component *c;
+	struct komeda_layer *wb_layer;
+	u32 pipe_id, layer_id;
+
+	get_resources_id(blk->block_info, &pipe_id, &layer_id);
+
+	c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer),
+				 layer_id, BLOCK_INFO_INPUT_ID(blk->block_info),
+				 &d71_wb_layer_funcs,
+				 1, get_valid_inputs(blk), 0, reg,
+				 "LPU%d_LAYER_WR", pipe_id);
+	if (IS_ERR(c)) {
+		DRM_ERROR("Failed to add wb_layer component\n");
+		return PTR_ERR(c);
+	}
+
+	wb_layer = to_layer(c);
+	wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
+
+	set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
+	set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index 284ce079d8c4..6712603b8c7a 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -165,6 +165,15 @@ void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
 	if (events & KOMEDA_EVENT_VSYNC)
 		drm_crtc_handle_vblank(crtc);
 
+	if (events & KOMEDA_EVENT_EOW) {
+		struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
+
+		if (wb_conn)
+			drm_writeback_signal_completion(&wb_conn->base, 0);
+		else
+			DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
+				 drm_crtc_index(&kcrtc->base));
+	}
 	/* will handle it together with the write back support */
 	if (events & KOMEDA_EVENT_EOW)
 		DRM_DEBUG("EOW.\n");
@@ -201,6 +210,8 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
 	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
 	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
 	struct komeda_pipeline *master = kcrtc->master;
+	struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
+	struct drm_connector_state *conn_st;
 
 	DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
 			 drm_crtc_index(crtc),
@@ -210,6 +221,10 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
 	if (has_bit(master->id, kcrtc_st->affected_pipes))
 		komeda_pipeline_update(master, old->state);
 
+	conn_st = wb_conn ? wb_conn->base.base.state : NULL;
+	if (conn_st && conn_st->writeback_job)
+		drm_writeback_queue_job(&wb_conn->base, conn_st);
+
 	/* step 2: notify the HW to kickoff the update */
 	mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
 }
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
index 9cc9935024f7..4d8160cf09c3 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
@@ -165,3 +165,22 @@ komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
 
 	return obj->paddr + offset;
 }
+
+/* if the fb can be supported by a specific layer */
+bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type)
+{
+	struct drm_framebuffer *fb = &kfb->base;
+	struct komeda_dev *mdev = fb->dev->dev_private;
+	const struct komeda_format_caps *caps;
+	u32 fourcc = fb->format->format;
+	u64 modifier = fb->modifier;
+
+	caps = komeda_get_format_caps(&mdev->fmt_tbl, fourcc, modifier);
+	if (!caps)
+		return false;
+
+	if (!(caps->supported_layer_types & layer_type))
+		return false;
+
+	return true;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
index 86f6542afb40..3e58901fb776 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
@@ -188,6 +188,10 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
 	if (err)
 		goto cleanup_mode_config;
 
+	err = komeda_kms_add_wb_connectors(kms, mdev);
+	if (err)
+		goto cleanup_mode_config;
+
 	err = component_bind_all(mdev->dev, kms);
 	if (err)
 		goto cleanup_mode_config;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index ac3d9209b4d9..f16e9e577593 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -12,6 +12,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_writeback.h>
+#include <drm/drm_print.h>
 #include <video/videomode.h>
 #include <video/display_timing.h>
 
@@ -73,6 +74,9 @@ struct komeda_crtc {
 	 */
 	struct komeda_pipeline *slave;
 
+	/** @wb_conn: komeda write back connector */
+	struct komeda_wb_connector *wb_conn;
+
 	/** @disable_done: this flip_done is for tracing the disable */
 	struct completion *disable_done;
 };
@@ -116,6 +120,27 @@ struct komeda_kms_dev {
 #define to_kcrtc(p)	container_of(p, struct komeda_crtc, base)
 #define to_kcrtc_st(p)	container_of(p, struct komeda_crtc_state, base)
 #define to_kdev(p)	container_of(p, struct komeda_kms_dev, base)
+#define to_wb_conn(x)	container_of(x, struct drm_writeback_connector, base)
+
+static inline bool is_writeback_only(struct drm_crtc_state *st)
+{
+	struct komeda_wb_connector *wb_conn = to_kcrtc(st->crtc)->wb_conn;
+	struct drm_connector *conn = wb_conn ? &wb_conn->base.base : NULL;
+
+	return conn && (st->connector_mask == BIT(drm_connector_index(conn)));
+}
+
+static inline bool
+is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
+{
+	struct drm_crtc_state *old_st;
+	u32 changed_connectors;
+
+	old_st = drm_atomic_get_old_crtc_state(st->state, st->crtc);
+	changed_connectors = st->connector_mask ^ old_st->connector_mask;
+
+	return BIT(drm_connector_index(conn)) == changed_connectors;
+}
 
 int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 
@@ -123,6 +148,8 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
 				struct komeda_dev *mdev);
+int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
+				 struct komeda_dev *mdev);
 void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms);
 
 void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index bae8a32b81a6..1b7e933ea303 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -423,10 +423,17 @@ struct komeda_plane_state;
 struct komeda_crtc_state;
 struct komeda_crtc;
 
+void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
+			       u16 *hsize, u16 *vsize);
+
 int komeda_build_layer_data_flow(struct komeda_layer *layer,
 				 struct komeda_plane_state *kplane_st,
 				 struct komeda_crtc_state *kcrtc_st,
 				 struct komeda_data_flow_cfg *dflow);
+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);
 int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
 				   struct komeda_crtc_state *kcrtc_st);
 
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 36570d7dad61..9748c9438868 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -308,8 +308,41 @@ komeda_layer_validate(struct komeda_layer *layer,
 	return 0;
 }
 
-static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
-				      u16 *hsize, u16 *vsize)
+static int
+komeda_wb_layer_validate(struct komeda_layer *wb_layer,
+			 struct drm_connector_state *conn_st,
+			 struct komeda_data_flow_cfg *dflow)
+{
+	struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb);
+	struct komeda_component_state *c_st;
+	struct komeda_layer_state *st;
+	int i;
+
+	if (!komeda_fb_is_layer_supported(kfb, wb_layer->layer_type))
+		return -EINVAL;
+
+	c_st = komeda_component_get_state_and_set_user(&wb_layer->base,
+			conn_st->state, conn_st->connector, conn_st->crtc);
+	if (IS_ERR(c_st))
+		return PTR_ERR(c_st);
+
+	st = to_layer_st(c_st);
+
+	st->hsize = dflow->out_w;
+	st->vsize = dflow->out_h;
+
+	for (i = 0; i < kfb->base.format->num_planes; i++)
+		st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->out_x,
+						       dflow->out_y, i);
+
+	komeda_component_add_input(&st->base, &dflow->input, 0);
+	komeda_component_set_output(&dflow->input, &wb_layer->base, 0);
+
+	return 0;
+}
+
+void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
+			       u16 *hsize, u16 *vsize)
 {
 	struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode;
 
@@ -478,6 +511,20 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
 	return err;
 }
 
+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;
+	}
+
+	return komeda_wb_layer_validate(wb_layer, conn_st, dflow);
+}
+
 /* build display output data flow, the data path is:
  * compiz -> improc -> timing_ctrlr
  */
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 a54878cbd6e4..d53bd6c23c5d 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
@@ -252,6 +252,12 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
 				return err;
 		}
 
+		if (pipe->wb_layer) {
+			err = komeda_layer_obj_add(kms, pipe->wb_layer);
+			if (err)
+				return err;
+		}
+
 		err = komeda_compiz_obj_add(kms, pipe->compiz);
 		if (err)
 			return err;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
new file mode 100644
index 000000000000..0c1a4220c280
--- /dev/null
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+static int
+komeda_wb_init_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)
+{
+	struct drm_framebuffer *fb = conn_st->writeback_job ?
+				     conn_st->writeback_job->fb : NULL;
+
+	if (!fb)
+		return -EINVAL;
+
+	memset(dflow, 0, sizeof(*dflow));
+
+	dflow->out_w = fb->width;
+	dflow->out_h = fb->height;
+
+	/* the write back data comes from the compiz */
+	pipeline_composition_size(kcrtc_st, &dflow->in_w, &dflow->in_h);
+	dflow->input.component = &wb_layer->base.pipeline->compiz->base;
+	/* compiz doesn't output alpha */
+	dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
+	dflow->rot = DRM_MODE_ROTATE_0;
+
+	return 0;
+}
+
+static int
+komeda_wb_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_st,
+			       struct drm_connector_state *conn_st)
+{
+	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st);
+	struct komeda_layer *wb_layer;
+	struct komeda_data_flow_cfg dflow;
+	int err;
+
+	if (!crtc_st->active) {
+		DRM_DEBUG_ATOMIC("Cannot write the composition result out on a inactive CRTC.\n");
+		return -EINVAL;
+	}
+
+	wb_layer = to_kconn(to_wb_conn(conn_st->connector))->wb_layer;
+
+	/*
+	 * No need for a full modested when the only connector changed is the
+	 * writeback connector.
+	 */
+	if (crtc_st->connectors_changed &&
+	    is_only_changed_connector(crtc_st, conn_st->connector))
+		crtc_st->connectors_changed = false;
+
+	err = komeda_wb_init_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
+	if (err)
+		return err;
+
+	return komeda_build_wb_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
+}
+
+static const struct drm_encoder_helper_funcs komeda_wb_encoder_helper_funcs = {
+	.atomic_check = komeda_wb_encoder_atomic_check,
+};
+
+static int
+komeda_wb_connector_get_modes(struct drm_connector *connector)
+{
+	return 0;
+}
+
+static enum drm_mode_status
+komeda_wb_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	int w = mode->hdisplay, h = mode->vdisplay;
+
+	if ((w < mode_config->min_width) || (w > mode_config->max_width))
+		return MODE_BAD_HVALUE;
+
+	if ((h < mode_config->min_height) || (h > mode_config->max_height))
+		return MODE_BAD_VVALUE;
+
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs komeda_wb_conn_helper_funcs = {
+	.get_modes	= komeda_wb_connector_get_modes,
+	.mode_valid	= komeda_wb_connector_mode_valid,
+};
+
+static enum drm_connector_status
+komeda_wb_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static int
+komeda_wb_connector_fill_modes(struct drm_connector *connector,
+			       uint32_t maxX, uint32_t maxY)
+{
+	return 0;
+}
+
+static void komeda_wb_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+	kfree(to_kconn(to_wb_conn(connector)));
+}
+
+static const struct drm_connector_funcs komeda_wb_connector_funcs = {
+	.reset			= drm_atomic_helper_connector_reset,
+	.detect			= komeda_wb_connector_detect,
+	.fill_modes		= komeda_wb_connector_fill_modes,
+	.destroy		= komeda_wb_connector_destroy,
+	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
+};
+
+static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
+				   struct komeda_crtc *kcrtc)
+{
+	struct komeda_dev *mdev = kms->base.dev_private;
+	struct komeda_wb_connector *kwb_conn;
+	struct drm_writeback_connector *wb_conn;
+	u32 *formats, n_formats = 0;
+	int err;
+
+	if (!kcrtc->master->wb_layer)
+		return 0;
+
+	kwb_conn = kzalloc(sizeof(*wb_conn), GFP_KERNEL);
+	if (!kwb_conn)
+		return -ENOMEM;
+
+	kwb_conn->wb_layer = kcrtc->master->wb_layer;
+
+	wb_conn = &kwb_conn->base;
+	wb_conn->encoder.possible_crtcs = BIT(drm_crtc_index(&kcrtc->base));
+
+	formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
+					       kwb_conn->wb_layer->layer_type,
+					       &n_formats);
+
+	err = drm_writeback_connector_init(&kms->base, wb_conn,
+					   &komeda_wb_connector_funcs,
+					   &komeda_wb_encoder_helper_funcs,
+					   formats, n_formats);
+	komeda_put_fourcc_list(formats);
+	if (err)
+		return err;
+
+	drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
+
+	kcrtc->wb_conn = kwb_conn;
+
+	return 0;
+}
+
+int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
+				 struct komeda_dev *mdev)
+{
+	int i, err;
+
+	for (i = 0; i < kms->n_crtcs; i++) {
+		err = komeda_wb_connector_add(kms, &kms->crtcs[i]);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v4] drm/komeda: Add writeback support
@ 2019-05-23  9:36 ` james qian wang (Arm Technology China)
  0 siblings, 0 replies; 4+ messages in thread
From: james qian wang (Arm Technology China) @ 2019-05-23  9:36 UTC (permalink / raw)
  To: Liviu Dudau, airlied, Brian Starkey, maarten.lankhorst, sean
  Cc: Jonathan Chai (Arm Technology China),
	Julien Yin (Arm Technology China),
	thomas Sun (Arm Technology China),
	Lowry Li (Arm Technology China),
	Ayan Halder, Tiannan Zhu (Arm Technology China),
	Yiqi Kang (Arm Technology China),
	nd, linux-kernel, dri-devel, Ben Davis,
	Oscar Zhang (Arm Technology China),
	Channing Chen (Arm Technology China),
	james qian wang (Arm Technology China)

Komeda driver uses a individual component to describe the HW's writeback
caps, but drivers doesn't define a new structure and still uses the
existing "struct komeda_layer" to describe this new component.
The detailed changes as follow:

1. Initialize wb_layer according to HW and report it to CORE.
2. CORE exposes wb_layer as a resource to KMS by private_obj.
3. Report writeback supporting by add a wb_connector to KMS, and then
   wb_connector will take act as a component resources user,
   so the func komeda_wb_encoder_atomic_check claims komeda resources
   (scaler and wb_layer) accroding to its state configuration to the
   wb_connector. and the wb_state configuration will be validated on the
   specific component resources to see if the caps of component can
   meet the requirement of wb_connector. if not check failed.
4. Update irq_handler to notify the completion of writeback.

NOTE:
This change doesn't add scaling writeback support, that support will
be added in the future after the scaler support.

v2: Rebase
v3: Rebase and constify the d71_wb_layer_funcs
v4: Addressed Ayan's comments

Depends on:
- https://patchwork.freedesktop.org/series/59915/

Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
---
 drivers/gpu/drm/arm/display/komeda/Makefile   |   1 +
 .../arm/display/komeda/d71/d71_component.c    |  90 ++++++++-
 .../gpu/drm/arm/display/komeda/komeda_crtc.c  |  15 ++
 .../arm/display/komeda/komeda_framebuffer.c   |  19 ++
 .../gpu/drm/arm/display/komeda/komeda_kms.c   |   4 +
 .../gpu/drm/arm/display/komeda/komeda_kms.h   |  27 +++
 .../drm/arm/display/komeda/komeda_pipeline.h  |   7 +
 .../display/komeda/komeda_pipeline_state.c    |  51 ++++-
 .../arm/display/komeda/komeda_private_obj.c   |   6 +
 .../arm/display/komeda/komeda_wb_connector.c  | 181 ++++++++++++++++++
 10 files changed, 398 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c

diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
index 62bd1bff66a3..d7e29fc688c3 100644
--- a/drivers/gpu/drm/arm/display/komeda/Makefile
+++ b/drivers/gpu/drm/arm/display/komeda/Makefile
@@ -14,6 +14,7 @@ komeda-y := \
 	komeda_kms.o \
 	komeda_crtc.o \
 	komeda_plane.o \
+	komeda_wb_connector.o \
 	komeda_private_obj.o
 
 komeda-y += \
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 6bab816ed8e7..323e5994a55c 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -288,10 +288,98 @@ static int d71_layer_init(struct d71_dev *d71,
 	return 0;
 }
 
+static void d71_wb_layer_update(struct komeda_component *c,
+				struct komeda_component_state *state)
+{
+	struct komeda_layer_state *st = to_layer_st(state);
+	struct drm_connector_state *conn_st = state->wb_conn->state;
+	struct drm_framebuffer *fb = conn_st->writeback_job->fb;
+	struct komeda_fb *kfb = to_kfb(fb);
+	u32 __iomem *reg = c->reg;
+	u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN;
+	int i;
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_LOW,
+			       lower_32_bits(st->addr[i]));
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_HIGH,
+			       upper_32_bits(st->addr[i]));
+
+		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_STRIDE,
+			       fb->pitches[i] & 0xFFFF);
+	}
+
+	malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
+	malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
+	malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
+	malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
+}
+
+static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf)
+{
+	u32 v[12], i;
+
+	dump_block_header(sf, c->reg);
+
+	get_values_from_reg(c->reg, 0x80, 1, v);
+	seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+	get_values_from_reg(c->reg, 0xD0, 3, v);
+	seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]);
+	seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]);
+	seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]);
+
+	get_values_from_reg(c->reg, 0xE0, 1, v);
+	seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]);
+
+	for (i = 0; i < 2; i++) {
+		get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v);
+		seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]);
+		seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]);
+		seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]);
+	}
+
+	get_values_from_reg(c->reg, 0x130, 12, v);
+	for (i = 0; i < 12; i++)
+		seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
+}
+
+static void d71_wb_layer_disable(struct komeda_component *c)
+{
+	malidp_write32(c->reg, BLK_INPUT_ID0, 0);
+	malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0);
+}
+
+static const struct komeda_component_funcs d71_wb_layer_funcs = {
+	.update		= d71_wb_layer_update,
+	.disable	= d71_wb_layer_disable,
+	.dump_register	= d71_wb_layer_dump,
+};
+
 static int d71_wb_layer_init(struct d71_dev *d71,
 			     struct block_header *blk, u32 __iomem *reg)
 {
-	DRM_DEBUG("Detect D71_Wb_Layer.\n");
+	struct komeda_component *c;
+	struct komeda_layer *wb_layer;
+	u32 pipe_id, layer_id;
+
+	get_resources_id(blk->block_info, &pipe_id, &layer_id);
+
+	c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer),
+				 layer_id, BLOCK_INFO_INPUT_ID(blk->block_info),
+				 &d71_wb_layer_funcs,
+				 1, get_valid_inputs(blk), 0, reg,
+				 "LPU%d_LAYER_WR", pipe_id);
+	if (IS_ERR(c)) {
+		DRM_ERROR("Failed to add wb_layer component\n");
+		return PTR_ERR(c);
+	}
+
+	wb_layer = to_layer(c);
+	wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
+
+	set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
+	set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index 284ce079d8c4..6712603b8c7a 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -165,6 +165,15 @@ void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
 	if (events & KOMEDA_EVENT_VSYNC)
 		drm_crtc_handle_vblank(crtc);
 
+	if (events & KOMEDA_EVENT_EOW) {
+		struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
+
+		if (wb_conn)
+			drm_writeback_signal_completion(&wb_conn->base, 0);
+		else
+			DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
+				 drm_crtc_index(&kcrtc->base));
+	}
 	/* will handle it together with the write back support */
 	if (events & KOMEDA_EVENT_EOW)
 		DRM_DEBUG("EOW.\n");
@@ -201,6 +210,8 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
 	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
 	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
 	struct komeda_pipeline *master = kcrtc->master;
+	struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
+	struct drm_connector_state *conn_st;
 
 	DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
 			 drm_crtc_index(crtc),
@@ -210,6 +221,10 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
 	if (has_bit(master->id, kcrtc_st->affected_pipes))
 		komeda_pipeline_update(master, old->state);
 
+	conn_st = wb_conn ? wb_conn->base.base.state : NULL;
+	if (conn_st && conn_st->writeback_job)
+		drm_writeback_queue_job(&wb_conn->base, conn_st);
+
 	/* step 2: notify the HW to kickoff the update */
 	mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
 }
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
index 9cc9935024f7..4d8160cf09c3 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
@@ -165,3 +165,22 @@ komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
 
 	return obj->paddr + offset;
 }
+
+/* if the fb can be supported by a specific layer */
+bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type)
+{
+	struct drm_framebuffer *fb = &kfb->base;
+	struct komeda_dev *mdev = fb->dev->dev_private;
+	const struct komeda_format_caps *caps;
+	u32 fourcc = fb->format->format;
+	u64 modifier = fb->modifier;
+
+	caps = komeda_get_format_caps(&mdev->fmt_tbl, fourcc, modifier);
+	if (!caps)
+		return false;
+
+	if (!(caps->supported_layer_types & layer_type))
+		return false;
+
+	return true;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
index 86f6542afb40..3e58901fb776 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
@@ -188,6 +188,10 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
 	if (err)
 		goto cleanup_mode_config;
 
+	err = komeda_kms_add_wb_connectors(kms, mdev);
+	if (err)
+		goto cleanup_mode_config;
+
 	err = component_bind_all(mdev->dev, kms);
 	if (err)
 		goto cleanup_mode_config;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index ac3d9209b4d9..f16e9e577593 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -12,6 +12,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_writeback.h>
+#include <drm/drm_print.h>
 #include <video/videomode.h>
 #include <video/display_timing.h>
 
@@ -73,6 +74,9 @@ struct komeda_crtc {
 	 */
 	struct komeda_pipeline *slave;
 
+	/** @wb_conn: komeda write back connector */
+	struct komeda_wb_connector *wb_conn;
+
 	/** @disable_done: this flip_done is for tracing the disable */
 	struct completion *disable_done;
 };
@@ -116,6 +120,27 @@ struct komeda_kms_dev {
 #define to_kcrtc(p)	container_of(p, struct komeda_crtc, base)
 #define to_kcrtc_st(p)	container_of(p, struct komeda_crtc_state, base)
 #define to_kdev(p)	container_of(p, struct komeda_kms_dev, base)
+#define to_wb_conn(x)	container_of(x, struct drm_writeback_connector, base)
+
+static inline bool is_writeback_only(struct drm_crtc_state *st)
+{
+	struct komeda_wb_connector *wb_conn = to_kcrtc(st->crtc)->wb_conn;
+	struct drm_connector *conn = wb_conn ? &wb_conn->base.base : NULL;
+
+	return conn && (st->connector_mask == BIT(drm_connector_index(conn)));
+}
+
+static inline bool
+is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
+{
+	struct drm_crtc_state *old_st;
+	u32 changed_connectors;
+
+	old_st = drm_atomic_get_old_crtc_state(st->state, st->crtc);
+	changed_connectors = st->connector_mask ^ old_st->connector_mask;
+
+	return BIT(drm_connector_index(conn)) == changed_connectors;
+}
 
 int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 
@@ -123,6 +148,8 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
 				struct komeda_dev *mdev);
+int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
+				 struct komeda_dev *mdev);
 void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms);
 
 void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index bae8a32b81a6..1b7e933ea303 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -423,10 +423,17 @@ struct komeda_plane_state;
 struct komeda_crtc_state;
 struct komeda_crtc;
 
+void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
+			       u16 *hsize, u16 *vsize);
+
 int komeda_build_layer_data_flow(struct komeda_layer *layer,
 				 struct komeda_plane_state *kplane_st,
 				 struct komeda_crtc_state *kcrtc_st,
 				 struct komeda_data_flow_cfg *dflow);
+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);
 int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
 				   struct komeda_crtc_state *kcrtc_st);
 
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 36570d7dad61..9748c9438868 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -308,8 +308,41 @@ komeda_layer_validate(struct komeda_layer *layer,
 	return 0;
 }
 
-static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
-				      u16 *hsize, u16 *vsize)
+static int
+komeda_wb_layer_validate(struct komeda_layer *wb_layer,
+			 struct drm_connector_state *conn_st,
+			 struct komeda_data_flow_cfg *dflow)
+{
+	struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb);
+	struct komeda_component_state *c_st;
+	struct komeda_layer_state *st;
+	int i;
+
+	if (!komeda_fb_is_layer_supported(kfb, wb_layer->layer_type))
+		return -EINVAL;
+
+	c_st = komeda_component_get_state_and_set_user(&wb_layer->base,
+			conn_st->state, conn_st->connector, conn_st->crtc);
+	if (IS_ERR(c_st))
+		return PTR_ERR(c_st);
+
+	st = to_layer_st(c_st);
+
+	st->hsize = dflow->out_w;
+	st->vsize = dflow->out_h;
+
+	for (i = 0; i < kfb->base.format->num_planes; i++)
+		st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->out_x,
+						       dflow->out_y, i);
+
+	komeda_component_add_input(&st->base, &dflow->input, 0);
+	komeda_component_set_output(&dflow->input, &wb_layer->base, 0);
+
+	return 0;
+}
+
+void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
+			       u16 *hsize, u16 *vsize)
 {
 	struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode;
 
@@ -478,6 +511,20 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
 	return err;
 }
 
+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;
+	}
+
+	return komeda_wb_layer_validate(wb_layer, conn_st, dflow);
+}
+
 /* build display output data flow, the data path is:
  * compiz -> improc -> timing_ctrlr
  */
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 a54878cbd6e4..d53bd6c23c5d 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
@@ -252,6 +252,12 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
 				return err;
 		}
 
+		if (pipe->wb_layer) {
+			err = komeda_layer_obj_add(kms, pipe->wb_layer);
+			if (err)
+				return err;
+		}
+
 		err = komeda_compiz_obj_add(kms, pipe->compiz);
 		if (err)
 			return err;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
new file mode 100644
index 000000000000..0c1a4220c280
--- /dev/null
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+static int
+komeda_wb_init_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)
+{
+	struct drm_framebuffer *fb = conn_st->writeback_job ?
+				     conn_st->writeback_job->fb : NULL;
+
+	if (!fb)
+		return -EINVAL;
+
+	memset(dflow, 0, sizeof(*dflow));
+
+	dflow->out_w = fb->width;
+	dflow->out_h = fb->height;
+
+	/* the write back data comes from the compiz */
+	pipeline_composition_size(kcrtc_st, &dflow->in_w, &dflow->in_h);
+	dflow->input.component = &wb_layer->base.pipeline->compiz->base;
+	/* compiz doesn't output alpha */
+	dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
+	dflow->rot = DRM_MODE_ROTATE_0;
+
+	return 0;
+}
+
+static int
+komeda_wb_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_st,
+			       struct drm_connector_state *conn_st)
+{
+	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st);
+	struct komeda_layer *wb_layer;
+	struct komeda_data_flow_cfg dflow;
+	int err;
+
+	if (!crtc_st->active) {
+		DRM_DEBUG_ATOMIC("Cannot write the composition result out on a inactive CRTC.\n");
+		return -EINVAL;
+	}
+
+	wb_layer = to_kconn(to_wb_conn(conn_st->connector))->wb_layer;
+
+	/*
+	 * No need for a full modested when the only connector changed is the
+	 * writeback connector.
+	 */
+	if (crtc_st->connectors_changed &&
+	    is_only_changed_connector(crtc_st, conn_st->connector))
+		crtc_st->connectors_changed = false;
+
+	err = komeda_wb_init_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
+	if (err)
+		return err;
+
+	return komeda_build_wb_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
+}
+
+static const struct drm_encoder_helper_funcs komeda_wb_encoder_helper_funcs = {
+	.atomic_check = komeda_wb_encoder_atomic_check,
+};
+
+static int
+komeda_wb_connector_get_modes(struct drm_connector *connector)
+{
+	return 0;
+}
+
+static enum drm_mode_status
+komeda_wb_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	int w = mode->hdisplay, h = mode->vdisplay;
+
+	if ((w < mode_config->min_width) || (w > mode_config->max_width))
+		return MODE_BAD_HVALUE;
+
+	if ((h < mode_config->min_height) || (h > mode_config->max_height))
+		return MODE_BAD_VVALUE;
+
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs komeda_wb_conn_helper_funcs = {
+	.get_modes	= komeda_wb_connector_get_modes,
+	.mode_valid	= komeda_wb_connector_mode_valid,
+};
+
+static enum drm_connector_status
+komeda_wb_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static int
+komeda_wb_connector_fill_modes(struct drm_connector *connector,
+			       uint32_t maxX, uint32_t maxY)
+{
+	return 0;
+}
+
+static void komeda_wb_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+	kfree(to_kconn(to_wb_conn(connector)));
+}
+
+static const struct drm_connector_funcs komeda_wb_connector_funcs = {
+	.reset			= drm_atomic_helper_connector_reset,
+	.detect			= komeda_wb_connector_detect,
+	.fill_modes		= komeda_wb_connector_fill_modes,
+	.destroy		= komeda_wb_connector_destroy,
+	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
+};
+
+static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
+				   struct komeda_crtc *kcrtc)
+{
+	struct komeda_dev *mdev = kms->base.dev_private;
+	struct komeda_wb_connector *kwb_conn;
+	struct drm_writeback_connector *wb_conn;
+	u32 *formats, n_formats = 0;
+	int err;
+
+	if (!kcrtc->master->wb_layer)
+		return 0;
+
+	kwb_conn = kzalloc(sizeof(*wb_conn), GFP_KERNEL);
+	if (!kwb_conn)
+		return -ENOMEM;
+
+	kwb_conn->wb_layer = kcrtc->master->wb_layer;
+
+	wb_conn = &kwb_conn->base;
+	wb_conn->encoder.possible_crtcs = BIT(drm_crtc_index(&kcrtc->base));
+
+	formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
+					       kwb_conn->wb_layer->layer_type,
+					       &n_formats);
+
+	err = drm_writeback_connector_init(&kms->base, wb_conn,
+					   &komeda_wb_connector_funcs,
+					   &komeda_wb_encoder_helper_funcs,
+					   formats, n_formats);
+	komeda_put_fourcc_list(formats);
+	if (err)
+		return err;
+
+	drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
+
+	kcrtc->wb_conn = kwb_conn;
+
+	return 0;
+}
+
+int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
+				 struct komeda_dev *mdev)
+{
+	int i, err;
+
+	for (i = 0; i < kms->n_crtcs; i++) {
+		err = komeda_wb_connector_add(kms, &kms->crtcs[i]);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v4] drm/komeda: Add writeback support
  2019-05-23  9:36 ` james qian wang (Arm Technology China)
@ 2019-05-24 10:25   ` Ayan Halder
  -1 siblings, 0 replies; 4+ messages in thread
From: Ayan Halder @ 2019-05-24 10:25 UTC (permalink / raw)
  To: james qian wang (Arm Technology China)
  Cc: Liviu Dudau, airlied, Brian Starkey, maarten.lankhorst, sean,
	Jonathan Chai (Arm Technology China),
	Julien Yin (Arm Technology China),
	thomas Sun (Arm Technology China),
	Lowry Li (Arm Technology China),
	Tiannan Zhu (Arm Technology China),
	Yiqi Kang (Arm Technology China),
	nd, linux-kernel, dri-devel, Ben Davis,
	Oscar Zhang (Arm Technology China),
	Channing Chen (Arm Technology China)

On Thu, May 23, 2019 at 10:36:38AM +0100, james qian wang (Arm Technology China) wrote:
> Komeda driver uses a individual component to describe the HW's writeback
> caps, but drivers doesn't define a new structure and still uses the
> existing "struct komeda_layer" to describe this new component.
> The detailed changes as follow:
> 
> 1. Initialize wb_layer according to HW and report it to CORE.
> 2. CORE exposes wb_layer as a resource to KMS by private_obj.
> 3. Report writeback supporting by add a wb_connector to KMS, and then
>    wb_connector will take act as a component resources user,
>    so the func komeda_wb_encoder_atomic_check claims komeda resources
>    (scaler and wb_layer) accroding to its state configuration to the
>    wb_connector. and the wb_state configuration will be validated on the
>    specific component resources to see if the caps of component can
>    meet the requirement of wb_connector. if not check failed.
> 4. Update irq_handler to notify the completion of writeback.
> 
> NOTE:
> This change doesn't add scaling writeback support, that support will
> be added in the future after the scaler support.
> 
> v2: Rebase
> v3: Rebase and constify the d71_wb_layer_funcs
> v4: Addressed Ayan's comments
> 
> Depends on:
> - https://patchwork.freedesktop.org/series/59915/
> 
> Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
> ---
>  drivers/gpu/drm/arm/display/komeda/Makefile   |   1 +
>  .../arm/display/komeda/d71/d71_component.c    |  90 ++++++++-
>  .../gpu/drm/arm/display/komeda/komeda_crtc.c  |  15 ++
>  .../arm/display/komeda/komeda_framebuffer.c   |  19 ++
>  .../gpu/drm/arm/display/komeda/komeda_kms.c   |   4 +
>  .../gpu/drm/arm/display/komeda/komeda_kms.h   |  27 +++
>  .../drm/arm/display/komeda/komeda_pipeline.h  |   7 +
>  .../display/komeda/komeda_pipeline_state.c    |  51 ++++-
>  .../arm/display/komeda/komeda_private_obj.c   |   6 +
>  .../arm/display/komeda/komeda_wb_connector.c  | 181 ++++++++++++++++++
>  10 files changed, 398 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> 
> diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
> index 62bd1bff66a3..d7e29fc688c3 100644
> --- a/drivers/gpu/drm/arm/display/komeda/Makefile
> +++ b/drivers/gpu/drm/arm/display/komeda/Makefile
> @@ -14,6 +14,7 @@ komeda-y := \
>  	komeda_kms.o \
>  	komeda_crtc.o \
>  	komeda_plane.o \
> +	komeda_wb_connector.o \
>  	komeda_private_obj.o
>  
>  komeda-y += \
> 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 6bab816ed8e7..323e5994a55c 100644
> --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
> +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
> @@ -288,10 +288,98 @@ static int d71_layer_init(struct d71_dev *d71,
>  	return 0;
>  }
>  
> +static void d71_wb_layer_update(struct komeda_component *c,
> +				struct komeda_component_state *state)
> +{
> +	struct komeda_layer_state *st = to_layer_st(state);
> +	struct drm_connector_state *conn_st = state->wb_conn->state;
> +	struct drm_framebuffer *fb = conn_st->writeback_job->fb;
> +	struct komeda_fb *kfb = to_kfb(fb);
> +	u32 __iomem *reg = c->reg;
> +	u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN;
> +	int i;
> +
> +	for (i = 0; i < fb->format->num_planes; i++) {
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_LOW,
> +			       lower_32_bits(st->addr[i]));
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_HIGH,
> +			       upper_32_bits(st->addr[i]));
> +
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_STRIDE,
> +			       fb->pitches[i] & 0xFFFF);
> +	}
> +
> +	malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
> +	malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
> +	malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
> +	malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
> +}
> +
> +static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf)
> +{
> +	u32 v[12], i;
> +
> +	dump_block_header(sf, c->reg);
> +
> +	get_values_from_reg(c->reg, 0x80, 1, v);
> +	seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]);
> +
> +	get_values_from_reg(c->reg, 0xD0, 3, v);
> +	seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]);
> +	seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]);
> +	seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]);
> +
> +	get_values_from_reg(c->reg, 0xE0, 1, v);
> +	seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]);
> +
> +	for (i = 0; i < 2; i++) {
> +		get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v);
> +		seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]);
> +		seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]);
> +		seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]);
> +	}
> +
> +	get_values_from_reg(c->reg, 0x130, 12, v);
> +	for (i = 0; i < 12; i++)
> +		seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
> +}
> +
> +static void d71_wb_layer_disable(struct komeda_component *c)
> +{
> +	malidp_write32(c->reg, BLK_INPUT_ID0, 0);
> +	malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0);
> +}
> +
> +static const struct komeda_component_funcs d71_wb_layer_funcs = {
> +	.update		= d71_wb_layer_update,
> +	.disable	= d71_wb_layer_disable,
> +	.dump_register	= d71_wb_layer_dump,
> +};
> +
>  static int d71_wb_layer_init(struct d71_dev *d71,
>  			     struct block_header *blk, u32 __iomem *reg)
>  {
> -	DRM_DEBUG("Detect D71_Wb_Layer.\n");
> +	struct komeda_component *c;
> +	struct komeda_layer *wb_layer;
> +	u32 pipe_id, layer_id;
> +
> +	get_resources_id(blk->block_info, &pipe_id, &layer_id);
> +
> +	c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer),
> +				 layer_id, BLOCK_INFO_INPUT_ID(blk->block_info),
> +				 &d71_wb_layer_funcs,
> +				 1, get_valid_inputs(blk), 0, reg,
> +				 "LPU%d_LAYER_WR", pipe_id);
> +	if (IS_ERR(c)) {
> +		DRM_ERROR("Failed to add wb_layer component\n");
> +		return PTR_ERR(c);
> +	}
> +
> +	wb_layer = to_layer(c);
> +	wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
> +
> +	set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
> +	set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> index 284ce079d8c4..6712603b8c7a 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> @@ -165,6 +165,15 @@ void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
>  	if (events & KOMEDA_EVENT_VSYNC)
>  		drm_crtc_handle_vblank(crtc);
>  
> +	if (events & KOMEDA_EVENT_EOW) {
> +		struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
> +
> +		if (wb_conn)
> +			drm_writeback_signal_completion(&wb_conn->base, 0);
> +		else
> +			DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
> +				 drm_crtc_index(&kcrtc->base));
> +	}
>  	/* will handle it together with the write back support */
>  	if (events & KOMEDA_EVENT_EOW)
>  		DRM_DEBUG("EOW.\n");
> @@ -201,6 +210,8 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
>  	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
>  	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
>  	struct komeda_pipeline *master = kcrtc->master;
> +	struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
> +	struct drm_connector_state *conn_st;
>  
>  	DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
>  			 drm_crtc_index(crtc),
> @@ -210,6 +221,10 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
>  	if (has_bit(master->id, kcrtc_st->affected_pipes))
>  		komeda_pipeline_update(master, old->state);
>  
> +	conn_st = wb_conn ? wb_conn->base.base.state : NULL;
> +	if (conn_st && conn_st->writeback_job)
> +		drm_writeback_queue_job(&wb_conn->base, conn_st);
> +
>  	/* step 2: notify the HW to kickoff the update */
>  	mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
>  }
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> index 9cc9935024f7..4d8160cf09c3 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> @@ -165,3 +165,22 @@ komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
>  
>  	return obj->paddr + offset;
>  }
> +
> +/* if the fb can be supported by a specific layer */
> +bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type)
> +{
> +	struct drm_framebuffer *fb = &kfb->base;
> +	struct komeda_dev *mdev = fb->dev->dev_private;
> +	const struct komeda_format_caps *caps;
> +	u32 fourcc = fb->format->format;
> +	u64 modifier = fb->modifier;
> +
> +	caps = komeda_get_format_caps(&mdev->fmt_tbl, fourcc, modifier);
> +	if (!caps)
> +		return false;
> +
> +	if (!(caps->supported_layer_types & layer_type))
> +		return false;
> +
> +	return true;
> +}
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> index 86f6542afb40..3e58901fb776 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> @@ -188,6 +188,10 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
>  	if (err)
>  		goto cleanup_mode_config;
>  
> +	err = komeda_kms_add_wb_connectors(kms, mdev);
> +	if (err)
> +		goto cleanup_mode_config;
> +
>  	err = component_bind_all(mdev->dev, kms);
>  	if (err)
>  		goto cleanup_mode_config;
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> index ac3d9209b4d9..f16e9e577593 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> @@ -12,6 +12,7 @@
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_device.h>
>  #include <drm/drm_writeback.h>
> +#include <drm/drm_print.h>
>  #include <video/videomode.h>
>  #include <video/display_timing.h>
>  
> @@ -73,6 +74,9 @@ struct komeda_crtc {
>  	 */
>  	struct komeda_pipeline *slave;
>  
> +	/** @wb_conn: komeda write back connector */
> +	struct komeda_wb_connector *wb_conn;
> +
>  	/** @disable_done: this flip_done is for tracing the disable */
>  	struct completion *disable_done;
>  };
> @@ -116,6 +120,27 @@ struct komeda_kms_dev {
>  #define to_kcrtc(p)	container_of(p, struct komeda_crtc, base)
>  #define to_kcrtc_st(p)	container_of(p, struct komeda_crtc_state, base)
>  #define to_kdev(p)	container_of(p, struct komeda_kms_dev, base)
> +#define to_wb_conn(x)	container_of(x, struct drm_writeback_connector, base)
> +
> +static inline bool is_writeback_only(struct drm_crtc_state *st)
> +{
> +	struct komeda_wb_connector *wb_conn = to_kcrtc(st->crtc)->wb_conn;
> +	struct drm_connector *conn = wb_conn ? &wb_conn->base.base : NULL;
> +
> +	return conn && (st->connector_mask == BIT(drm_connector_index(conn)));
> +}
> +
> +static inline bool
> +is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
> +{
> +	struct drm_crtc_state *old_st;
> +	u32 changed_connectors;
> +
> +	old_st = drm_atomic_get_old_crtc_state(st->state, st->crtc);
> +	changed_connectors = st->connector_mask ^ old_st->connector_mask;
> +
> +	return BIT(drm_connector_index(conn)) == changed_connectors;
> +}
>  
>  int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  
> @@ -123,6 +148,8 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
>  				struct komeda_dev *mdev);
> +int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
> +				 struct komeda_dev *mdev);
>  void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms);
>  
>  void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> index bae8a32b81a6..1b7e933ea303 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> @@ -423,10 +423,17 @@ struct komeda_plane_state;
>  struct komeda_crtc_state;
>  struct komeda_crtc;
>  
> +void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> +			       u16 *hsize, u16 *vsize);
> +
>  int komeda_build_layer_data_flow(struct komeda_layer *layer,
>  				 struct komeda_plane_state *kplane_st,
>  				 struct komeda_crtc_state *kcrtc_st,
>  				 struct komeda_data_flow_cfg *dflow);
> +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);
>  int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
>  				   struct komeda_crtc_state *kcrtc_st);
>  
> 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 36570d7dad61..9748c9438868 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
> @@ -308,8 +308,41 @@ komeda_layer_validate(struct komeda_layer *layer,
>  	return 0;
>  }
>  
> -static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> -				      u16 *hsize, u16 *vsize)
> +static int
> +komeda_wb_layer_validate(struct komeda_layer *wb_layer,
> +			 struct drm_connector_state *conn_st,
> +			 struct komeda_data_flow_cfg *dflow)
> +{
> +	struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb);
> +	struct komeda_component_state *c_st;
> +	struct komeda_layer_state *st;
> +	int i;
> +
> +	if (!komeda_fb_is_layer_supported(kfb, wb_layer->layer_type))
> +		return -EINVAL;
> +
> +	c_st = komeda_component_get_state_and_set_user(&wb_layer->base,
> +			conn_st->state, conn_st->connector, conn_st->crtc);
> +	if (IS_ERR(c_st))
> +		return PTR_ERR(c_st);
> +
> +	st = to_layer_st(c_st);
> +
> +	st->hsize = dflow->out_w;
> +	st->vsize = dflow->out_h;
> +
> +	for (i = 0; i < kfb->base.format->num_planes; i++)
> +		st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->out_x,
> +						       dflow->out_y, i);
> +
> +	komeda_component_add_input(&st->base, &dflow->input, 0);
> +	komeda_component_set_output(&dflow->input, &wb_layer->base, 0);
> +
> +	return 0;
> +}
> +
> +void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> +			       u16 *hsize, u16 *vsize)
>  {
>  	struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode;
>  
> @@ -478,6 +511,20 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
>  	return err;
>  }
>  
> +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;
> +	}
> +
> +	return komeda_wb_layer_validate(wb_layer, conn_st, dflow);
> +}
> +
>  /* build display output data flow, the data path is:
>   * compiz -> improc -> timing_ctrlr
>   */
> 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 a54878cbd6e4..d53bd6c23c5d 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
> @@ -252,6 +252,12 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
>  				return err;
>  		}
>  
> +		if (pipe->wb_layer) {
> +			err = komeda_layer_obj_add(kms, pipe->wb_layer);
> +			if (err)
> +				return err;
> +		}
> +
>  		err = komeda_compiz_obj_add(kms, pipe->compiz);
>  		if (err)
>  			return err;
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> new file mode 100644
> index 000000000000..0c1a4220c280
> --- /dev/null
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
> + * Author: James.Qian.Wang <james.qian.wang@arm.com>
> + *
> + */
> +#include "komeda_dev.h"
> +#include "komeda_kms.h"
> +
> +static int
> +komeda_wb_init_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)
> +{
> +	struct drm_framebuffer *fb = conn_st->writeback_job ?
> +				     conn_st->writeback_job->fb : NULL;
> +
> +	if (!fb)
> +		return -EINVAL;
> +
> +	memset(dflow, 0, sizeof(*dflow));
> +
> +	dflow->out_w = fb->width;
> +	dflow->out_h = fb->height;
> +
> +	/* the write back data comes from the compiz */
> +	pipeline_composition_size(kcrtc_st, &dflow->in_w, &dflow->in_h);
> +	dflow->input.component = &wb_layer->base.pipeline->compiz->base;
> +	/* compiz doesn't output alpha */
> +	dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
> +	dflow->rot = DRM_MODE_ROTATE_0;
> +
> +	return 0;
> +}
> +
> +static int
> +komeda_wb_encoder_atomic_check(struct drm_encoder *encoder,
> +			       struct drm_crtc_state *crtc_st,
> +			       struct drm_connector_state *conn_st)
> +{
> +	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st);
> +	struct komeda_layer *wb_layer;
> +	struct komeda_data_flow_cfg dflow;
> +	int err;
> +
> +	if (!crtc_st->active) {
> +		DRM_DEBUG_ATOMIC("Cannot write the composition result out on a inactive CRTC.\n");
> +		return -EINVAL;
> +	}
> +
> +	wb_layer = to_kconn(to_wb_conn(conn_st->connector))->wb_layer;
> +
> +	/*
> +	 * No need for a full modested when the only connector changed is the
> +	 * writeback connector.
> +	 */
> +	if (crtc_st->connectors_changed &&
> +	    is_only_changed_connector(crtc_st, conn_st->connector))
> +		crtc_st->connectors_changed = false;
> +
> +	err = komeda_wb_init_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
> +	if (err)
> +		return err;
> +
> +	return komeda_build_wb_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
> +}
> +
> +static const struct drm_encoder_helper_funcs komeda_wb_encoder_helper_funcs = {
> +	.atomic_check = komeda_wb_encoder_atomic_check,
> +};
> +
> +static int
> +komeda_wb_connector_get_modes(struct drm_connector *connector)
> +{
> +	return 0;
> +}
> +
> +static enum drm_mode_status
> +komeda_wb_connector_mode_valid(struct drm_connector *connector,
> +			       struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct drm_mode_config *mode_config = &dev->mode_config;
> +	int w = mode->hdisplay, h = mode->vdisplay;
> +
> +	if ((w < mode_config->min_width) || (w > mode_config->max_width))
> +		return MODE_BAD_HVALUE;
> +
> +	if ((h < mode_config->min_height) || (h > mode_config->max_height))
> +		return MODE_BAD_VVALUE;
> +
> +	return MODE_OK;
> +}
> +
> +static const struct drm_connector_helper_funcs komeda_wb_conn_helper_funcs = {
> +	.get_modes	= komeda_wb_connector_get_modes,
> +	.mode_valid	= komeda_wb_connector_mode_valid,
> +};
> +
> +static enum drm_connector_status
> +komeda_wb_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	return connector_status_connected;
> +}
> +
> +static int
> +komeda_wb_connector_fill_modes(struct drm_connector *connector,
> +			       uint32_t maxX, uint32_t maxY)
> +{
> +	return 0;
> +}
> +
> +static void komeda_wb_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_cleanup(connector);
> +	kfree(to_kconn(to_wb_conn(connector)));
> +}
> +
> +static const struct drm_connector_funcs komeda_wb_connector_funcs = {
> +	.reset			= drm_atomic_helper_connector_reset,
> +	.detect			= komeda_wb_connector_detect,
> +	.fill_modes		= komeda_wb_connector_fill_modes,
> +	.destroy		= komeda_wb_connector_destroy,
> +	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
> +				   struct komeda_crtc *kcrtc)
> +{
> +	struct komeda_dev *mdev = kms->base.dev_private;
> +	struct komeda_wb_connector *kwb_conn;
> +	struct drm_writeback_connector *wb_conn;
> +	u32 *formats, n_formats = 0;
> +	int err;
> +
> +	if (!kcrtc->master->wb_layer)
> +		return 0;
> +
> +	kwb_conn = kzalloc(sizeof(*wb_conn), GFP_KERNEL);
> +	if (!kwb_conn)
> +		return -ENOMEM;
> +
> +	kwb_conn->wb_layer = kcrtc->master->wb_layer;
> +
> +	wb_conn = &kwb_conn->base;
> +	wb_conn->encoder.possible_crtcs = BIT(drm_crtc_index(&kcrtc->base));
> +
> +	formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
> +					       kwb_conn->wb_layer->layer_type,
> +					       &n_formats);
> +
> +	err = drm_writeback_connector_init(&kms->base, wb_conn,
> +					   &komeda_wb_connector_funcs,
> +					   &komeda_wb_encoder_helper_funcs,
> +					   formats, n_formats);
> +	komeda_put_fourcc_list(formats);
> +	if (err)
> +		return err;
> +
> +	drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
> +
> +	kcrtc->wb_conn = kwb_conn;
> +
> +	return 0;
> +}
> +
> +int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
> +				 struct komeda_dev *mdev)
> +{
> +	int i, err;
> +
> +	for (i = 0; i < kms->n_crtcs; i++) {
> +		err = komeda_wb_connector_add(kms, &kms->crtcs[i]);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> -- 
lgtm, Acked-by: Ayan Kumar Halder <ayan.halder@arm.com>
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v4] drm/komeda: Add writeback support
@ 2019-05-24 10:25   ` Ayan Halder
  0 siblings, 0 replies; 4+ messages in thread
From: Ayan Halder @ 2019-05-24 10:25 UTC (permalink / raw)
  To: james qian wang (Arm Technology China)
  Cc: nd, Oscar Zhang (Arm Technology China),
	Tiannan Zhu (Arm Technology China),
	airlied, Liviu Dudau, Jonathan Chai (Arm Technology China),
	linux-kernel, dri-devel, Julien Yin (Arm Technology China),
	Channing Chen (Arm Technology China),
	Yiqi Kang (Arm Technology China),
	thomas Sun (Arm Technology China),
	Lowry Li (Arm Technology China),
	sean, Ben Davis

On Thu, May 23, 2019 at 10:36:38AM +0100, james qian wang (Arm Technology China) wrote:
> Komeda driver uses a individual component to describe the HW's writeback
> caps, but drivers doesn't define a new structure and still uses the
> existing "struct komeda_layer" to describe this new component.
> The detailed changes as follow:
> 
> 1. Initialize wb_layer according to HW and report it to CORE.
> 2. CORE exposes wb_layer as a resource to KMS by private_obj.
> 3. Report writeback supporting by add a wb_connector to KMS, and then
>    wb_connector will take act as a component resources user,
>    so the func komeda_wb_encoder_atomic_check claims komeda resources
>    (scaler and wb_layer) accroding to its state configuration to the
>    wb_connector. and the wb_state configuration will be validated on the
>    specific component resources to see if the caps of component can
>    meet the requirement of wb_connector. if not check failed.
> 4. Update irq_handler to notify the completion of writeback.
> 
> NOTE:
> This change doesn't add scaling writeback support, that support will
> be added in the future after the scaler support.
> 
> v2: Rebase
> v3: Rebase and constify the d71_wb_layer_funcs
> v4: Addressed Ayan's comments
> 
> Depends on:
> - https://patchwork.freedesktop.org/series/59915/
> 
> Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
> ---
>  drivers/gpu/drm/arm/display/komeda/Makefile   |   1 +
>  .../arm/display/komeda/d71/d71_component.c    |  90 ++++++++-
>  .../gpu/drm/arm/display/komeda/komeda_crtc.c  |  15 ++
>  .../arm/display/komeda/komeda_framebuffer.c   |  19 ++
>  .../gpu/drm/arm/display/komeda/komeda_kms.c   |   4 +
>  .../gpu/drm/arm/display/komeda/komeda_kms.h   |  27 +++
>  .../drm/arm/display/komeda/komeda_pipeline.h  |   7 +
>  .../display/komeda/komeda_pipeline_state.c    |  51 ++++-
>  .../arm/display/komeda/komeda_private_obj.c   |   6 +
>  .../arm/display/komeda/komeda_wb_connector.c  | 181 ++++++++++++++++++
>  10 files changed, 398 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> 
> diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
> index 62bd1bff66a3..d7e29fc688c3 100644
> --- a/drivers/gpu/drm/arm/display/komeda/Makefile
> +++ b/drivers/gpu/drm/arm/display/komeda/Makefile
> @@ -14,6 +14,7 @@ komeda-y := \
>  	komeda_kms.o \
>  	komeda_crtc.o \
>  	komeda_plane.o \
> +	komeda_wb_connector.o \
>  	komeda_private_obj.o
>  
>  komeda-y += \
> 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 6bab816ed8e7..323e5994a55c 100644
> --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
> +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
> @@ -288,10 +288,98 @@ static int d71_layer_init(struct d71_dev *d71,
>  	return 0;
>  }
>  
> +static void d71_wb_layer_update(struct komeda_component *c,
> +				struct komeda_component_state *state)
> +{
> +	struct komeda_layer_state *st = to_layer_st(state);
> +	struct drm_connector_state *conn_st = state->wb_conn->state;
> +	struct drm_framebuffer *fb = conn_st->writeback_job->fb;
> +	struct komeda_fb *kfb = to_kfb(fb);
> +	u32 __iomem *reg = c->reg;
> +	u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN;
> +	int i;
> +
> +	for (i = 0; i < fb->format->num_planes; i++) {
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_LOW,
> +			       lower_32_bits(st->addr[i]));
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_PTR_HIGH,
> +			       upper_32_bits(st->addr[i]));
> +
> +		malidp_write32(reg + i * LAYER_PER_PLANE_REGS, BLK_P0_STRIDE,
> +			       fb->pitches[i] & 0xFFFF);
> +	}
> +
> +	malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
> +	malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
> +	malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
> +	malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
> +}
> +
> +static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf)
> +{
> +	u32 v[12], i;
> +
> +	dump_block_header(sf, c->reg);
> +
> +	get_values_from_reg(c->reg, 0x80, 1, v);
> +	seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]);
> +
> +	get_values_from_reg(c->reg, 0xD0, 3, v);
> +	seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]);
> +	seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]);
> +	seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]);
> +
> +	get_values_from_reg(c->reg, 0xE0, 1, v);
> +	seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]);
> +
> +	for (i = 0; i < 2; i++) {
> +		get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v);
> +		seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]);
> +		seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]);
> +		seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]);
> +	}
> +
> +	get_values_from_reg(c->reg, 0x130, 12, v);
> +	for (i = 0; i < 12; i++)
> +		seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
> +}
> +
> +static void d71_wb_layer_disable(struct komeda_component *c)
> +{
> +	malidp_write32(c->reg, BLK_INPUT_ID0, 0);
> +	malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0);
> +}
> +
> +static const struct komeda_component_funcs d71_wb_layer_funcs = {
> +	.update		= d71_wb_layer_update,
> +	.disable	= d71_wb_layer_disable,
> +	.dump_register	= d71_wb_layer_dump,
> +};
> +
>  static int d71_wb_layer_init(struct d71_dev *d71,
>  			     struct block_header *blk, u32 __iomem *reg)
>  {
> -	DRM_DEBUG("Detect D71_Wb_Layer.\n");
> +	struct komeda_component *c;
> +	struct komeda_layer *wb_layer;
> +	u32 pipe_id, layer_id;
> +
> +	get_resources_id(blk->block_info, &pipe_id, &layer_id);
> +
> +	c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer),
> +				 layer_id, BLOCK_INFO_INPUT_ID(blk->block_info),
> +				 &d71_wb_layer_funcs,
> +				 1, get_valid_inputs(blk), 0, reg,
> +				 "LPU%d_LAYER_WR", pipe_id);
> +	if (IS_ERR(c)) {
> +		DRM_ERROR("Failed to add wb_layer component\n");
> +		return PTR_ERR(c);
> +	}
> +
> +	wb_layer = to_layer(c);
> +	wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
> +
> +	set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
> +	set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> index 284ce079d8c4..6712603b8c7a 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
> @@ -165,6 +165,15 @@ void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
>  	if (events & KOMEDA_EVENT_VSYNC)
>  		drm_crtc_handle_vblank(crtc);
>  
> +	if (events & KOMEDA_EVENT_EOW) {
> +		struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
> +
> +		if (wb_conn)
> +			drm_writeback_signal_completion(&wb_conn->base, 0);
> +		else
> +			DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
> +				 drm_crtc_index(&kcrtc->base));
> +	}
>  	/* will handle it together with the write back support */
>  	if (events & KOMEDA_EVENT_EOW)
>  		DRM_DEBUG("EOW.\n");
> @@ -201,6 +210,8 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
>  	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
>  	struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
>  	struct komeda_pipeline *master = kcrtc->master;
> +	struct komeda_wb_connector *wb_conn = kcrtc->wb_conn;
> +	struct drm_connector_state *conn_st;
>  
>  	DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
>  			 drm_crtc_index(crtc),
> @@ -210,6 +221,10 @@ komeda_crtc_do_flush(struct drm_crtc *crtc,
>  	if (has_bit(master->id, kcrtc_st->affected_pipes))
>  		komeda_pipeline_update(master, old->state);
>  
> +	conn_st = wb_conn ? wb_conn->base.base.state : NULL;
> +	if (conn_st && conn_st->writeback_job)
> +		drm_writeback_queue_job(&wb_conn->base, conn_st);
> +
>  	/* step 2: notify the HW to kickoff the update */
>  	mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
>  }
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> index 9cc9935024f7..4d8160cf09c3 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
> @@ -165,3 +165,22 @@ komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
>  
>  	return obj->paddr + offset;
>  }
> +
> +/* if the fb can be supported by a specific layer */
> +bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type)
> +{
> +	struct drm_framebuffer *fb = &kfb->base;
> +	struct komeda_dev *mdev = fb->dev->dev_private;
> +	const struct komeda_format_caps *caps;
> +	u32 fourcc = fb->format->format;
> +	u64 modifier = fb->modifier;
> +
> +	caps = komeda_get_format_caps(&mdev->fmt_tbl, fourcc, modifier);
> +	if (!caps)
> +		return false;
> +
> +	if (!(caps->supported_layer_types & layer_type))
> +		return false;
> +
> +	return true;
> +}
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> index 86f6542afb40..3e58901fb776 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
> @@ -188,6 +188,10 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
>  	if (err)
>  		goto cleanup_mode_config;
>  
> +	err = komeda_kms_add_wb_connectors(kms, mdev);
> +	if (err)
> +		goto cleanup_mode_config;
> +
>  	err = component_bind_all(mdev->dev, kms);
>  	if (err)
>  		goto cleanup_mode_config;
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> index ac3d9209b4d9..f16e9e577593 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
> @@ -12,6 +12,7 @@
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_device.h>
>  #include <drm/drm_writeback.h>
> +#include <drm/drm_print.h>
>  #include <video/videomode.h>
>  #include <video/display_timing.h>
>  
> @@ -73,6 +74,9 @@ struct komeda_crtc {
>  	 */
>  	struct komeda_pipeline *slave;
>  
> +	/** @wb_conn: komeda write back connector */
> +	struct komeda_wb_connector *wb_conn;
> +
>  	/** @disable_done: this flip_done is for tracing the disable */
>  	struct completion *disable_done;
>  };
> @@ -116,6 +120,27 @@ struct komeda_kms_dev {
>  #define to_kcrtc(p)	container_of(p, struct komeda_crtc, base)
>  #define to_kcrtc_st(p)	container_of(p, struct komeda_crtc_state, base)
>  #define to_kdev(p)	container_of(p, struct komeda_kms_dev, base)
> +#define to_wb_conn(x)	container_of(x, struct drm_writeback_connector, base)
> +
> +static inline bool is_writeback_only(struct drm_crtc_state *st)
> +{
> +	struct komeda_wb_connector *wb_conn = to_kcrtc(st->crtc)->wb_conn;
> +	struct drm_connector *conn = wb_conn ? &wb_conn->base.base : NULL;
> +
> +	return conn && (st->connector_mask == BIT(drm_connector_index(conn)));
> +}
> +
> +static inline bool
> +is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
> +{
> +	struct drm_crtc_state *old_st;
> +	u32 changed_connectors;
> +
> +	old_st = drm_atomic_get_old_crtc_state(st->state, st->crtc);
> +	changed_connectors = st->connector_mask ^ old_st->connector_mask;
> +
> +	return BIT(drm_connector_index(conn)) == changed_connectors;
> +}
>  
>  int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  
> @@ -123,6 +148,8 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
>  int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
>  				struct komeda_dev *mdev);
> +int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
> +				 struct komeda_dev *mdev);
>  void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms);
>  
>  void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> index bae8a32b81a6..1b7e933ea303 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
> @@ -423,10 +423,17 @@ struct komeda_plane_state;
>  struct komeda_crtc_state;
>  struct komeda_crtc;
>  
> +void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> +			       u16 *hsize, u16 *vsize);
> +
>  int komeda_build_layer_data_flow(struct komeda_layer *layer,
>  				 struct komeda_plane_state *kplane_st,
>  				 struct komeda_crtc_state *kcrtc_st,
>  				 struct komeda_data_flow_cfg *dflow);
> +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);
>  int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
>  				   struct komeda_crtc_state *kcrtc_st);
>  
> 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 36570d7dad61..9748c9438868 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
> @@ -308,8 +308,41 @@ komeda_layer_validate(struct komeda_layer *layer,
>  	return 0;
>  }
>  
> -static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> -				      u16 *hsize, u16 *vsize)
> +static int
> +komeda_wb_layer_validate(struct komeda_layer *wb_layer,
> +			 struct drm_connector_state *conn_st,
> +			 struct komeda_data_flow_cfg *dflow)
> +{
> +	struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb);
> +	struct komeda_component_state *c_st;
> +	struct komeda_layer_state *st;
> +	int i;
> +
> +	if (!komeda_fb_is_layer_supported(kfb, wb_layer->layer_type))
> +		return -EINVAL;
> +
> +	c_st = komeda_component_get_state_and_set_user(&wb_layer->base,
> +			conn_st->state, conn_st->connector, conn_st->crtc);
> +	if (IS_ERR(c_st))
> +		return PTR_ERR(c_st);
> +
> +	st = to_layer_st(c_st);
> +
> +	st->hsize = dflow->out_w;
> +	st->vsize = dflow->out_h;
> +
> +	for (i = 0; i < kfb->base.format->num_planes; i++)
> +		st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->out_x,
> +						       dflow->out_y, i);
> +
> +	komeda_component_add_input(&st->base, &dflow->input, 0);
> +	komeda_component_set_output(&dflow->input, &wb_layer->base, 0);
> +
> +	return 0;
> +}
> +
> +void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
> +			       u16 *hsize, u16 *vsize)
>  {
>  	struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode;
>  
> @@ -478,6 +511,20 @@ int komeda_build_layer_data_flow(struct komeda_layer *layer,
>  	return err;
>  }
>  
> +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;
> +	}
> +
> +	return komeda_wb_layer_validate(wb_layer, conn_st, dflow);
> +}
> +
>  /* build display output data flow, the data path is:
>   * compiz -> improc -> timing_ctrlr
>   */
> 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 a54878cbd6e4..d53bd6c23c5d 100644
> --- a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
> @@ -252,6 +252,12 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
>  				return err;
>  		}
>  
> +		if (pipe->wb_layer) {
> +			err = komeda_layer_obj_add(kms, pipe->wb_layer);
> +			if (err)
> +				return err;
> +		}
> +
>  		err = komeda_compiz_obj_add(kms, pipe->compiz);
>  		if (err)
>  			return err;
> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> new file mode 100644
> index 000000000000..0c1a4220c280
> --- /dev/null
> +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
> + * Author: James.Qian.Wang <james.qian.wang@arm.com>
> + *
> + */
> +#include "komeda_dev.h"
> +#include "komeda_kms.h"
> +
> +static int
> +komeda_wb_init_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)
> +{
> +	struct drm_framebuffer *fb = conn_st->writeback_job ?
> +				     conn_st->writeback_job->fb : NULL;
> +
> +	if (!fb)
> +		return -EINVAL;
> +
> +	memset(dflow, 0, sizeof(*dflow));
> +
> +	dflow->out_w = fb->width;
> +	dflow->out_h = fb->height;
> +
> +	/* the write back data comes from the compiz */
> +	pipeline_composition_size(kcrtc_st, &dflow->in_w, &dflow->in_h);
> +	dflow->input.component = &wb_layer->base.pipeline->compiz->base;
> +	/* compiz doesn't output alpha */
> +	dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
> +	dflow->rot = DRM_MODE_ROTATE_0;
> +
> +	return 0;
> +}
> +
> +static int
> +komeda_wb_encoder_atomic_check(struct drm_encoder *encoder,
> +			       struct drm_crtc_state *crtc_st,
> +			       struct drm_connector_state *conn_st)
> +{
> +	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st);
> +	struct komeda_layer *wb_layer;
> +	struct komeda_data_flow_cfg dflow;
> +	int err;
> +
> +	if (!crtc_st->active) {
> +		DRM_DEBUG_ATOMIC("Cannot write the composition result out on a inactive CRTC.\n");
> +		return -EINVAL;
> +	}
> +
> +	wb_layer = to_kconn(to_wb_conn(conn_st->connector))->wb_layer;
> +
> +	/*
> +	 * No need for a full modested when the only connector changed is the
> +	 * writeback connector.
> +	 */
> +	if (crtc_st->connectors_changed &&
> +	    is_only_changed_connector(crtc_st, conn_st->connector))
> +		crtc_st->connectors_changed = false;
> +
> +	err = komeda_wb_init_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
> +	if (err)
> +		return err;
> +
> +	return komeda_build_wb_data_flow(wb_layer, conn_st, kcrtc_st, &dflow);
> +}
> +
> +static const struct drm_encoder_helper_funcs komeda_wb_encoder_helper_funcs = {
> +	.atomic_check = komeda_wb_encoder_atomic_check,
> +};
> +
> +static int
> +komeda_wb_connector_get_modes(struct drm_connector *connector)
> +{
> +	return 0;
> +}
> +
> +static enum drm_mode_status
> +komeda_wb_connector_mode_valid(struct drm_connector *connector,
> +			       struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct drm_mode_config *mode_config = &dev->mode_config;
> +	int w = mode->hdisplay, h = mode->vdisplay;
> +
> +	if ((w < mode_config->min_width) || (w > mode_config->max_width))
> +		return MODE_BAD_HVALUE;
> +
> +	if ((h < mode_config->min_height) || (h > mode_config->max_height))
> +		return MODE_BAD_VVALUE;
> +
> +	return MODE_OK;
> +}
> +
> +static const struct drm_connector_helper_funcs komeda_wb_conn_helper_funcs = {
> +	.get_modes	= komeda_wb_connector_get_modes,
> +	.mode_valid	= komeda_wb_connector_mode_valid,
> +};
> +
> +static enum drm_connector_status
> +komeda_wb_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	return connector_status_connected;
> +}
> +
> +static int
> +komeda_wb_connector_fill_modes(struct drm_connector *connector,
> +			       uint32_t maxX, uint32_t maxY)
> +{
> +	return 0;
> +}
> +
> +static void komeda_wb_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_cleanup(connector);
> +	kfree(to_kconn(to_wb_conn(connector)));
> +}
> +
> +static const struct drm_connector_funcs komeda_wb_connector_funcs = {
> +	.reset			= drm_atomic_helper_connector_reset,
> +	.detect			= komeda_wb_connector_detect,
> +	.fill_modes		= komeda_wb_connector_fill_modes,
> +	.destroy		= komeda_wb_connector_destroy,
> +	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
> +				   struct komeda_crtc *kcrtc)
> +{
> +	struct komeda_dev *mdev = kms->base.dev_private;
> +	struct komeda_wb_connector *kwb_conn;
> +	struct drm_writeback_connector *wb_conn;
> +	u32 *formats, n_formats = 0;
> +	int err;
> +
> +	if (!kcrtc->master->wb_layer)
> +		return 0;
> +
> +	kwb_conn = kzalloc(sizeof(*wb_conn), GFP_KERNEL);
> +	if (!kwb_conn)
> +		return -ENOMEM;
> +
> +	kwb_conn->wb_layer = kcrtc->master->wb_layer;
> +
> +	wb_conn = &kwb_conn->base;
> +	wb_conn->encoder.possible_crtcs = BIT(drm_crtc_index(&kcrtc->base));
> +
> +	formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
> +					       kwb_conn->wb_layer->layer_type,
> +					       &n_formats);
> +
> +	err = drm_writeback_connector_init(&kms->base, wb_conn,
> +					   &komeda_wb_connector_funcs,
> +					   &komeda_wb_encoder_helper_funcs,
> +					   formats, n_formats);
> +	komeda_put_fourcc_list(formats);
> +	if (err)
> +		return err;
> +
> +	drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
> +
> +	kcrtc->wb_conn = kwb_conn;
> +
> +	return 0;
> +}
> +
> +int komeda_kms_add_wb_connectors(struct komeda_kms_dev *kms,
> +				 struct komeda_dev *mdev)
> +{
> +	int i, err;
> +
> +	for (i = 0; i < kms->n_crtcs; i++) {
> +		err = komeda_wb_connector_add(kms, &kms->crtcs[i]);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> -- 
lgtm, Acked-by: Ayan Kumar Halder <ayan.halder@arm.com>
> 2.17.1
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-05-24 10:25 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-23  9:36 [PATCH v4] drm/komeda: Add writeback support james qian wang (Arm Technology China)
2019-05-23  9:36 ` james qian wang (Arm Technology China)
2019-05-24 10:25 ` Ayan Halder
2019-05-24 10:25   ` Ayan Halder

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.