All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/17] Add writeback block support for DPU
@ 2022-04-20  1:45 Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 01/17] drm: allow passing possible_crtcs to drm_writeback_connector_init() Abhinav Kumar
                   ` (16 more replies)
  0 siblings, 17 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

This series adds support for writeback block on DPU. Writeback
block is extremely useful to validate boards having no physical displays
in addition to many other use-cases where we want to get the output
of the display pipeline to examine whether issue is with the display
pipeline or with the panel.

These changes have been validated on SM8250 RB5 boards with IGT KMS
writeback test-suite thereby further increasing the IGT test coverage
for DPU. I am sharing the test results below.

root@linaro-developer:~/igt_repo/igt-gpu-tools/build/tests# ./kms_writeback
[   35.066157] Console: switching to colour dummy device 80x25
[   35.071964] [IGT] kms_writeback: executing
IGT-Version: 1.26-gae2eb9e1 (aarch64) (Linux: 5.16.0-rc2-62171-g132577e2697b aarch64)
[   35.611418] [IGT] kms_writeback: starting subtest writeback-pixel-formats
Starting subtest: writeback-pixel-formats
[   35.618528] [IGT] kms_writeback: starting subtest writeback-invalid-parameters
Subtest writeback-pixel-formats: SUCCESS (0.000s)
Starting subtest: writeback-invalid-parameters
Subtest writeback-invalid-parameters: SUCCESS (0.028s)   35.657437] [IGT] kms_writeback: starting subtest writeback-fb-id
Starting subtest: writeback-fb-id
Subtest writeback-fb-id: SUCCESS (0.030s)
[   35.698957] [IGT] kms_writeback: starting subtest writeback-check-output
Starting subtest: writeback-check-output
[   35.852834] [IGT] kms_writeback: exiting, ret=0
Subtest writeback-check-output: SUCCESS (0.142s)
[   35.861291] Console: switching to colour frame buffer device 240x67
root@linaro-developer:~/igt_repo/igt-gpu-tools/build/tests# 

The changes can easily be extended to support any other chipset using
the DPU driver by adding the support in the catalog.

Writeback block supports various formats and features. The support
for all of them can be incrementally added on top of this framework when
validation is improved and the test frameworks are extended to validate
them.

changes in v2:
	- rebase on tip of msm-next and address related dependencies
	- fix review comments from Dmitry
	- absorb the DRM writeback core changes which have been acked in
	  this series so that it can be landed together
	
Abhinav Kumar (17):
  drm: allow passing possible_crtcs to drm_writeback_connector_init()
  drm: introduce drm_writeback_connector_init_with_encoder() API
  drm: allow real encoder to be passed for drm_writeback_connector
  drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
  drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl
  drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  drm/msm/dpu: add writeback blocks to DPU RM
  drm/msm/dpu: add changes to support writeback in hw_ctl
  drm/msm/dpu: add an API to reset the encoder related hw blocks
  drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  drm/msm/dpu: add encoder operations to prepare/cleanup wb job
  drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file
  drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  drm/msm/dpu: add the writeback connector layer
  drm/msm/dpu: initialize dpu encoder and connector for writeback
  drm/msm/dpu: gracefully handle null fb commits for writeback
  drm/msm/dpu: add writeback blocks to the display snapshot

 .../drm/arm/display/komeda/komeda_wb_connector.c   |   4 +-
 drivers/gpu/drm/arm/malidp_mw.c                    |   4 +-
 drivers/gpu/drm/drm_writeback.c                    |  79 ++-
 drivers/gpu/drm/msm/Makefile                       |   3 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c           |   9 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 217 +++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |  22 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  46 ++
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 763 +++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c     |  74 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h     |  66 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c         |  73 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h         |  23 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c        |  25 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h        |   4 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c          | 273 ++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h          | 131 ++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |  63 ++
 drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c          |  27 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c             |  22 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h             |  12 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c      |  68 ++
 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h      |  25 +
 drivers/gpu/drm/rcar-du/rcar_du_writeback.c        |   4 +-
 drivers/gpu/drm/vc4/vc4_txp.c                      |   7 +-
 drivers/gpu/drm/vkms/vkms_writeback.c              |   4 +-
 include/drm/drm_writeback.h                        |  31 +-
 27 files changed, 2003 insertions(+), 76 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h

-- 
2.7.4


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

* [PATCH v2 01/17] drm: allow passing possible_crtcs to drm_writeback_connector_init()
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 02/17] drm: introduce drm_writeback_connector_init_with_encoder() API Abhinav Kumar
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Clients of drm_writeback_connector_init() initialize the
possible_crtcs and then invoke the call to this API.

To simplify things, allow passing possible_crtcs as a parameter
to drm_writeback_connector_init() and make changes to the
other drm drivers to make them compatible with this change.

changes in v2:
	- split the changes according to their functionality

changes in v3:
	- allow passing possible_crtcs for existing users of
	  drm_writeback_connector_init()
	- squash the vendor changes into the same commit so
	  that each patch in the series can compile individually

changes in v4:
    - keep only changes related to possible_crtcs
	- add line breaks after ARRAY_SIZE
	- stop using temporary variables for possible_crtcs

changes in v5:
    - None

changes in v6:
    - None

changes in v7:
	- wrap long lines to match the coding style of existing drivers
	- Fix indentation and remove parenthesis where not needed
	- use u32 instead of uint32_t for possible_crtcs

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Acked-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c | 4 ++--
 drivers/gpu/drm/arm/malidp_mw.c                          | 4 ++--
 drivers/gpu/drm/drm_writeback.c                          | 7 ++++++-
 drivers/gpu/drm/rcar-du/rcar_du_writeback.c              | 4 ++--
 drivers/gpu/drm/vc4/vc4_txp.c                            | 3 ++-
 drivers/gpu/drm/vkms/vkms_writeback.c                    | 4 ++--
 include/drm/drm_writeback.h                              | 3 ++-
 7 files changed, 18 insertions(+), 11 deletions(-)

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 e465cc4..ce4b760 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -155,7 +155,6 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
 	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,
@@ -164,7 +163,8 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
 	err = drm_writeback_connector_init(&kms->base, wb_conn,
 					   &komeda_wb_connector_funcs,
 					   &komeda_wb_encoder_helper_funcs,
-					   formats, n_formats);
+					   formats, n_formats,
+					   BIT(drm_crtc_index(&kcrtc->base)));
 	komeda_put_fourcc_list(formats);
 	if (err) {
 		kfree(kwb_conn);
diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
index f5847a7..204c869 100644
--- a/drivers/gpu/drm/arm/malidp_mw.c
+++ b/drivers/gpu/drm/arm/malidp_mw.c
@@ -212,7 +212,6 @@ int malidp_mw_connector_init(struct drm_device *drm)
 	if (!malidp->dev->hw->enable_memwrite)
 		return 0;
 
-	malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
 	drm_connector_helper_add(&malidp->mw_connector.base,
 				 &malidp_mw_connector_helper_funcs);
 
@@ -223,7 +222,8 @@ int malidp_mw_connector_init(struct drm_device *drm)
 	ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
 					   &malidp_mw_connector_funcs,
 					   &malidp_mw_encoder_helper_funcs,
-					   formats, n_formats);
+					   formats, n_formats,
+					   1 << drm_crtc_index(&malidp->crtc));
 	kfree(formats);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index dccf4504..9e0b845 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -157,6 +157,7 @@ static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
  * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
  * @formats: Array of supported pixel formats for the writeback engine
  * @n_formats: Length of the formats array
+ * @possible_crtcs: possible crtcs for the internal writeback encoder
  *
  * This function creates the writeback-connector-specific properties if they
  * have not been already created, initializes the connector as
@@ -174,7 +175,8 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 struct drm_writeback_connector *wb_connector,
 				 const struct drm_connector_funcs *con_funcs,
 				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
-				 const u32 *formats, int n_formats)
+				 const u32 *formats, int n_formats,
+				 u32 possible_crtcs)
 {
 	struct drm_property_blob *blob;
 	struct drm_connector *connector = &wb_connector->base;
@@ -190,6 +192,9 @@ int drm_writeback_connector_init(struct drm_device *dev,
 		return PTR_ERR(blob);
 
 	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
+
+	wb_connector->encoder.possible_crtcs = possible_crtcs;
+
 	ret = drm_encoder_init(dev, &wb_connector->encoder,
 			       &drm_writeback_encoder_funcs,
 			       DRM_MODE_ENCODER_VIRTUAL, NULL);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
index c79d125..505a905 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
@@ -200,7 +200,6 @@ int rcar_du_writeback_init(struct rcar_du_device *rcdu,
 {
 	struct drm_writeback_connector *wb_conn = &rcrtc->writeback;
 
-	wb_conn->encoder.possible_crtcs = 1 << drm_crtc_index(&rcrtc->crtc);
 	drm_connector_helper_add(&wb_conn->base,
 				 &rcar_du_wb_conn_helper_funcs);
 
@@ -208,7 +207,8 @@ int rcar_du_writeback_init(struct rcar_du_device *rcdu,
 					    &rcar_du_wb_conn_funcs,
 					    &rcar_du_wb_enc_helper_funcs,
 					    writeback_formats,
-					    ARRAY_SIZE(writeback_formats));
+					    ARRAY_SIZE(writeback_formats),
+					    1 << drm_crtc_index(&rcrtc->crtc));
 }
 
 void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 9809ca3..3447eb6 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -497,7 +497,8 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 	ret = drm_writeback_connector_init(drm, &txp->connector,
 					   &vc4_txp_connector_funcs,
 					   &vc4_txp_encoder_helper_funcs,
-					   drm_fmts, ARRAY_SIZE(drm_fmts));
+					   drm_fmts, ARRAY_SIZE(drm_fmts),
+					   0);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
index af1604d..0a31522 100644
--- a/drivers/gpu/drm/vkms/vkms_writeback.c
+++ b/drivers/gpu/drm/vkms/vkms_writeback.c
@@ -140,12 +140,12 @@ int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
 {
 	struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;
 
-	vkmsdev->output.wb_connector.encoder.possible_crtcs = 1;
 	drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
 
 	return drm_writeback_connector_init(&vkmsdev->drm, wb,
 					    &vkms_wb_connector_funcs,
 					    &vkms_wb_encoder_helper_funcs,
 					    vkms_wb_formats,
-					    ARRAY_SIZE(vkms_wb_formats));
+					    ARRAY_SIZE(vkms_wb_formats),
+					    1);
 }
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index 9697d27..5d9263f 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -150,7 +150,8 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 struct drm_writeback_connector *wb_connector,
 				 const struct drm_connector_funcs *con_funcs,
 				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
-				 const u32 *formats, int n_formats);
+				 const u32 *formats, int n_formats,
+				 u32 possible_crtcs);
 
 int drm_writeback_set_fb(struct drm_connector_state *conn_state,
 			 struct drm_framebuffer *fb);
-- 
2.7.4


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

* [PATCH v2 02/17] drm: introduce drm_writeback_connector_init_with_encoder() API
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 01/17] drm: allow passing possible_crtcs to drm_writeback_connector_init() Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 03/17] drm: allow real encoder to be passed for drm_writeback_connector Abhinav Kumar
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

For vendors drivers which pass an already allocated and
initialized encoder especially for cases where the encoder
hardware is shared OR the writeback encoder shares the resources
with the rest of the display pipeline introduce a new API,
drm_writeback_connector_init_with_encoder() which expects
an initialized encoder as a parameter and only sets up the
writeback connector.

changes in v4:
	- removed the possible_crtcs part

changes in v5:
	- reorder this change to come before in the series
	  to avoid incorrect functionality in subsequent changes
	- continue using struct drm_encoder instead of
	  struct drm_encoder * and switch it in next change

changes in v6:
	- remove drm_writeback_connector_setup() and instead
	  directly call drm_writeback_connector_init_with_encoder()
	- fix a drm_writeback_connector typo and function doc which
	  incorrectly shows that the function accepts enc_helper_funcs
	- pass encoder as a parameter explicitly to the new API
	  for better readability

changes in v7:
	- fix the function doc slightly as suggested by Liviu

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/drm_writeback.c | 72 +++++++++++++++++++++++++++++++++--------
 include/drm/drm_writeback.h     |  6 ++++
 2 files changed, 64 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index 9e0b845..92658ad 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -178,6 +178,62 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 const u32 *formats, int n_formats,
 				 u32 possible_crtcs)
 {
+	int ret = 0;
+
+	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
+
+	wb_connector->encoder.possible_crtcs = possible_crtcs;
+
+	ret = drm_encoder_init(dev, &wb_connector->encoder,
+			       &drm_writeback_encoder_funcs,
+			       DRM_MODE_ENCODER_VIRTUAL, NULL);
+	if (ret)
+		return ret;
+
+	ret = drm_writeback_connector_init_with_encoder(dev, wb_connector, &wb_connector->encoder,
+			con_funcs, formats, n_formats);
+
+	if (ret)
+		drm_encoder_cleanup(&wb_connector->encoder);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_writeback_connector_init);
+
+/**
+ * drm_writeback_connector_init_with_encoder - Initialize a writeback connector and its properties
+ * using the encoder which already assigned and initialized
+ *
+ * @dev: DRM device
+ * @wb_connector: Writeback connector to initialize
+ * @enc: handle to the already initialized drm encoder
+ * @con_funcs: Connector funcs vtable
+ * @formats: Array of supported pixel formats for the writeback engine
+ * @n_formats: Length of the formats array
+ *
+ * This function creates the writeback-connector-specific properties if they
+ * have not been already created, initializes the connector as
+ * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
+ * values.
+ *
+ * This function assumes that the drm_writeback_connector's encoder has already been
+ * created and initialized before invoking this function.
+ *
+ * In addition, this function also assumes that callers of this API will manage
+ * assigning the encoder helper functions, possible_crtcs and any other encoder
+ * specific operation.
+ *
+ * Drivers should always use this function instead of drm_connector_init() to
+ * set up writeback connectors if they want to manage themselves the lifetime of the
+ * associated encoder.
+ *
+ * Returns: 0 on success, or a negative error code
+ */
+int drm_writeback_connector_init_with_encoder(struct drm_device *dev,
+		struct drm_writeback_connector *wb_connector, struct drm_encoder *enc,
+		const struct drm_connector_funcs *con_funcs, const u32 *formats,
+		int n_formats)
+{
 	struct drm_property_blob *blob;
 	struct drm_connector *connector = &wb_connector->base;
 	struct drm_mode_config *config = &dev->mode_config;
@@ -191,15 +247,6 @@ int drm_writeback_connector_init(struct drm_device *dev,
 	if (IS_ERR(blob))
 		return PTR_ERR(blob);
 
-	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
-
-	wb_connector->encoder.possible_crtcs = possible_crtcs;
-
-	ret = drm_encoder_init(dev, &wb_connector->encoder,
-			       &drm_writeback_encoder_funcs,
-			       DRM_MODE_ENCODER_VIRTUAL, NULL);
-	if (ret)
-		goto fail;
 
 	connector->interlace_allowed = 0;
 
@@ -208,8 +255,7 @@ int drm_writeback_connector_init(struct drm_device *dev,
 	if (ret)
 		goto connector_fail;
 
-	ret = drm_connector_attach_encoder(connector,
-						&wb_connector->encoder);
+	ret = drm_connector_attach_encoder(connector, enc);
 	if (ret)
 		goto attach_fail;
 
@@ -238,12 +284,10 @@ int drm_writeback_connector_init(struct drm_device *dev,
 attach_fail:
 	drm_connector_cleanup(connector);
 connector_fail:
-	drm_encoder_cleanup(&wb_connector->encoder);
-fail:
 	drm_property_blob_put(blob);
 	return ret;
 }
-EXPORT_SYMBOL(drm_writeback_connector_init);
+EXPORT_SYMBOL(drm_writeback_connector_init_with_encoder);
 
 int drm_writeback_set_fb(struct drm_connector_state *conn_state,
 			 struct drm_framebuffer *fb)
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index 5d9263f..bb306fa 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -153,6 +153,12 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 const u32 *formats, int n_formats,
 				 u32 possible_crtcs);
 
+int drm_writeback_connector_init_with_encoder(struct drm_device *dev,
+				struct drm_writeback_connector *wb_connector,
+				struct drm_encoder *enc,
+				const struct drm_connector_funcs *con_funcs, const u32 *formats,
+				int n_formats);
+
 int drm_writeback_set_fb(struct drm_connector_state *conn_state,
 			 struct drm_framebuffer *fb);
 
-- 
2.7.4


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

* [PATCH v2 03/17] drm: allow real encoder to be passed for drm_writeback_connector
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 01/17] drm: allow passing possible_crtcs to drm_writeback_connector_init() Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 02/17] drm: introduce drm_writeback_connector_init_with_encoder() API Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  1:45 ` [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog Abhinav Kumar
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

For some vendor driver implementations, display hardware can
be shared between the encoder used for writeback and the physical
display.

In addition resources such as clocks and interrupts can
also be shared between writeback and the real encoder.

To accommodate such vendor drivers and hardware, allow
real encoder to be passed for drm_writeback_connector.

For existing clients, drm_writeback_connector_init() will use
an internal_encoder under the hood and hence no changes will
be needed.

changes in v7:
	- move this change before the vc4 change in the series
	  to minimize the changes to vendor drivers in drm core
	  changes

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/drm_writeback.c | 18 ++++++++++++------
 drivers/gpu/drm/vc4/vc4_txp.c   |  4 ++--
 include/drm/drm_writeback.h     | 22 ++++++++++++++++++++--
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index 92658ad..0538674 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -180,21 +180,21 @@ int drm_writeback_connector_init(struct drm_device *dev,
 {
 	int ret = 0;
 
-	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
+	drm_encoder_helper_add(&wb_connector->internal_encoder, enc_helper_funcs);
 
-	wb_connector->encoder.possible_crtcs = possible_crtcs;
+	wb_connector->internal_encoder.possible_crtcs = possible_crtcs;
 
-	ret = drm_encoder_init(dev, &wb_connector->encoder,
+	ret = drm_encoder_init(dev, &wb_connector->internal_encoder,
 			       &drm_writeback_encoder_funcs,
 			       DRM_MODE_ENCODER_VIRTUAL, NULL);
 	if (ret)
 		return ret;
 
-	ret = drm_writeback_connector_init_with_encoder(dev, wb_connector, &wb_connector->encoder,
-			con_funcs, formats, n_formats);
+	ret = drm_writeback_connector_init_with_encoder(dev, wb_connector,
+			&wb_connector->internal_encoder, con_funcs, formats, n_formats);
 
 	if (ret)
-		drm_encoder_cleanup(&wb_connector->encoder);
+		drm_encoder_cleanup(&wb_connector->internal_encoder);
 
 	return ret;
 }
@@ -239,6 +239,12 @@ int drm_writeback_connector_init_with_encoder(struct drm_device *dev,
 	struct drm_mode_config *config = &dev->mode_config;
 	int ret = create_writeback_properties(dev);
 
+	/*
+	 * Assign the encoder passed to this API to the wb_connector's encoder.
+	 * For drm_writeback_connector_init(), this shall be the internal_encoder
+	 */
+	wb_connector->encoder = enc;
+
 	if (ret != 0)
 		return ret;
 
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 3447eb6..7e063a9 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -159,7 +159,7 @@ struct vc4_txp {
 
 static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
 {
-	return container_of(encoder, struct vc4_txp, connector.encoder);
+	return container_of(encoder, struct vc4_txp, connector.internal_encoder);
 }
 
 static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn)
@@ -507,7 +507,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	encoder = &txp->connector.encoder;
+	encoder = txp->connector.encoder;
 	encoder->possible_crtcs = drm_crtc_mask(crtc);
 
 	ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index bb306fa..3fbae9d 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -25,13 +25,31 @@ struct drm_writeback_connector {
 	struct drm_connector base;
 
 	/**
-	 * @encoder: Internal encoder used by the connector to fulfill
+	 * @encoder: handle to drm_encoder used by the connector to fulfill
 	 * the DRM framework requirements. The users of the
 	 * @drm_writeback_connector control the behaviour of the @encoder
 	 * by passing the @enc_funcs parameter to drm_writeback_connector_init()
 	 * function.
+	 *
+	 * For some vendor drivers, the hardware resources are shared between
+	 * writeback encoder and rest of the display pipeline.
+	 * To accommodate such cases, encoder is a handle to the real encoder
+	 * hardware.
+	 *
+	 * For current existing writeback users, this shall continue to be the
+	 * embedded encoder for the writeback connector.
+	 */
+	struct drm_encoder *encoder;
+
+	/**
+	 * @internal_encoder: internal encoder used by writeback when
+	 * drm_writeback_connector_init() is used.
+	 * @encoder will be assigned to this for those cases
+	 *
+	 * This will be unused when drm_writeback_connector_init_with_encoder()
+	 * is used.
 	 */
-	struct drm_encoder encoder;
+	struct drm_encoder internal_encoder;
 
 	/**
 	 * @pixel_formats_blob_ptr:
-- 
2.7.4


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

* [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (2 preceding siblings ...)
  2022-04-20  1:45 ` [PATCH v2 03/17] drm: allow real encoder to be passed for drm_writeback_connector Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-21 12:16   ` Liviu Dudau
  2022-04-20  1:45 ` [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl Abhinav Kumar
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add writeback blocks to the sm8250 DPU hardware catalog. Other
chipsets support writeback too but add it to sm8250 to prototype
the feature so that it can be easily extended to other chipsets.

changes in v2:
	- none

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 74 +++++++++++++++++++++++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 ++++++++++++++++++++++-
 2 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
index b0a0ef7..bcb5273 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
@@ -120,6 +121,16 @@
 			  BIT(MDP_AD4_0_INTR) | \
 			  BIT(MDP_AD4_1_INTR))
 
+#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \
+			 BIT(DPU_WB_UBWC) | \
+			 BIT(DPU_WB_YUV_CONFIG) | \
+			 BIT(DPU_WB_PIPE_ALPHA) | \
+			 BIT(DPU_WB_XY_ROI_OFFSET) | \
+			 BIT(DPU_WB_QOS) | \
+			 BIT(DPU_WB_QOS_8LVL) | \
+			 BIT(DPU_WB_CDP) | \
+			 BIT(DPU_WB_INPUT_CTRL))
+
 #define DEFAULT_PIXEL_RAM_SIZE		(50 * 1024)
 #define DEFAULT_DPU_LINE_WIDTH		2048
 #define DEFAULT_DPU_OUTPUT_LINE_WIDTH	2560
@@ -211,6 +222,40 @@ static const u32 rotation_v2_formats[] = {
 	/* TODO add formats after validation */
 };
 
+static const uint32_t wb2_formats[] = {
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_RGBX5551,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_RGBX4444,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_BGRX5551,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_BGRX4444,
+	DRM_FORMAT_XBGR4444,
+};
+
 /*************************************************************
  * DPU sub blocks config
  *************************************************************/
@@ -448,6 +493,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = {
 			.reg_off = 0x2C4, .bit_off = 8},
 	.clk_ctrls[DPU_CLK_CTRL_REG_DMA] = {
 			.reg_off = 0x2BC, .bit_off = 20},
+	.clk_ctrls[DPU_CLK_CTRL_WB2] = {
+			.reg_off = 0x3B8, .bit_off = 24},
 	},
 };
 
@@ -1235,6 +1282,29 @@ static const struct dpu_intf_cfg qcm2290_intf[] = {
 };
 
 /*************************************************************
+ * Writeback blocks config
+ *************************************************************/
+#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \
+		__xin_id, vbif_id, _reg, _wb_done_bit) \
+	{ \
+	.name = _name, .id = _id, \
+	.base = _base, .len = 0x2c8, \
+	.features = _features, \
+	.format_list = wb2_formats, \
+	.num_formats = ARRAY_SIZE(wb2_formats), \
+	.clk_ctrl = _clk_ctrl, \
+	.xin_id = __xin_id, \
+	.vbif_idx = vbif_id, \
+	.maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \
+	.intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \
+	}
+
+static const struct dpu_wb_cfg sm8250_wb[] = {
+	WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6,
+			VBIF_RT, MDP_SSPP_TOP0_INTR, 4),
+};
+
+/*************************************************************
  * VBIF sub blocks config
  *************************************************************/
 /* VBIF QOS remap */
@@ -1832,6 +1902,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg)
 		.intf = sm8150_intf,
 		.vbif_count = ARRAY_SIZE(sdm845_vbif),
 		.vbif = sdm845_vbif,
+		.wb_count = ARRAY_SIZE(sm8250_wb),
+		.wb = sm8250_wb,
 		.reg_dma_count = 1,
 		.dma_cfg = sm8250_regdma,
 		.perf = sm8250_perf_data,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
index 866fd7a..8cb6d1f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DPU_HW_CATALOG_H
@@ -214,6 +216,42 @@ enum {
 };
 
 /**
+  * WB sub-blocks and features
+  * @DPU_WB_LINE_MODE        Writeback module supports line/linear mode
+  * @DPU_WB_BLOCK_MODE       Writeback module supports block mode read
+  * @DPU_WB_CHROMA_DOWN,     Writeback chroma down block,
+  * @DPU_WB_DOWNSCALE,       Writeback integer downscaler,
+  * @DPU_WB_DITHER,          Dither block
+  * @DPU_WB_TRAFFIC_SHAPER,  Writeback traffic shaper bloc
+  * @DPU_WB_UBWC,            Writeback Universal bandwidth compression
+  * @DPU_WB_YUV_CONFIG       Writeback supports output of YUV colorspace
+  * @DPU_WB_PIPE_ALPHA       Writeback supports pipe alpha
+  * @DPU_WB_XY_ROI_OFFSET    Writeback supports x/y-offset of out ROI in
+  *                          the destination image
+  * @DPU_WB_QOS,             Writeback supports QoS control, danger/safe/creq
+  * @DPU_WB_QOS_8LVL,        Writeback supports 8-level QoS control
+  * @DPU_WB_CDP              Writeback supports client driven prefetch
+  * @DPU_WB_INPUT_CTRL       Writeback supports from which pp block input pixel
+  *                          data arrives.
+  * @DPU_WB_CROP             CWB supports cropping
+  * @DPU_WB_MAX              maximum value
+  */
+enum {
+	DPU_WB_LINE_MODE = 0x1,
+	DPU_WB_BLOCK_MODE,
+	DPU_WB_UBWC,
+	DPU_WB_YUV_CONFIG,
+	DPU_WB_PIPE_ALPHA,
+	DPU_WB_XY_ROI_OFFSET,
+	DPU_WB_QOS,
+	DPU_WB_QOS_8LVL,
+	DPU_WB_CDP,
+	DPU_WB_INPUT_CTRL,
+	DPU_WB_CROP,
+	DPU_WB_MAX
+};
+
+/**
  * VBIF sub-blocks and features
  * @DPU_VBIF_QOS_OTLIM        VBIF supports OT Limit
  * @DPU_VBIF_QOS_REMAP        VBIF supports QoS priority remap
@@ -460,6 +498,7 @@ enum dpu_clk_ctrl_type {
 	DPU_CLK_CTRL_CURSOR1,
 	DPU_CLK_CTRL_INLINE_ROT0_SSPP,
 	DPU_CLK_CTRL_REG_DMA,
+	DPU_CLK_CTRL_WB2,
 	DPU_CLK_CTRL_MAX,
 };
 
@@ -608,6 +647,28 @@ struct dpu_intf_cfg  {
 };
 
 /**
+ * struct dpu_wb_cfg - information of writeback blocks
+ * @DPU_HW_BLK_INFO:    refer to the description above for DPU_HW_BLK_INFO
+ * @vbif_idx:           vbif client index
+ * @maxlinewidth:       max line width supported by writeback block
+ * @xin_id:             bus client identifier
+ * @intr_wb_done:       interrupt index for WB_DONE
+ * @format_list:	    list of formats supported by this writeback block
+ * @num_formats:	    number of formats supported by this writeback block
+ * @clk_ctrl:	        clock control identifier
+ */
+struct dpu_wb_cfg {
+	DPU_HW_BLK_INFO;
+	u8 vbif_idx;
+	u32 maxlinewidth;
+	u32 xin_id;
+	s32 intr_wb_done;
+	const u32 *format_list;
+	u32 num_formats;
+	enum dpu_clk_ctrl_type clk_ctrl;
+};
+
+/**
  * struct dpu_vbif_dynamic_ot_cfg - dynamic OT setting
  * @pps                pixel per seconds
  * @ot_limit           OT limit to use up to specified pixel per second
@@ -792,6 +853,9 @@ struct dpu_mdss_cfg {
 	u32 vbif_count;
 	const struct dpu_vbif_cfg *vbif;
 
+	u32 wb_count;
+	const struct dpu_wb_cfg *wb;
+
 	u32 reg_dma_count;
 	struct dpu_reg_dma_cfg dma_cfg;
 
-- 
2.7.4


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

* [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (3 preceding siblings ...)
  2022-04-20  1:45 ` [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  7:02   ` Dmitry Baryshkov
  2022-04-20  1:45 ` [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks Abhinav Kumar
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add a reset_intf_cfg operation for dpu_hw_ctl to reset the
entire CTL path by disabling each component namely layer mixer,
3d-merge and interface blocks.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 32 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h |  8 ++++++++
 2 files changed, 40 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
index dc27579..524f024 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
@@ -563,6 +563,37 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
 	DPU_REG_WRITE(c, CTL_TOP, intf_cfg);
 }
 
+static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
+		struct dpu_hw_intf_cfg *cfg)
+{
+	struct dpu_hw_blk_reg_map *c = &ctx->hw;
+	u32 intf_active = 0;
+	u32 merge3d_active = 0;
+
+	/*
+	 * This API resets each portion of the CTL path namely,
+	 * clearing the sspps staged on the lm, merge_3d block,
+	 * interfaces etc to ensure clean teardown of the pipeline.
+	 * This will be used for writeback to begin with to have a
+	 * proper teardown of the writeback session but upon further
+	 * validation, this can be extended to all interfaces.
+	 */
+	if (cfg->merge_3d) {
+		merge3d_active = DPU_REG_READ(c, CTL_MERGE_3D_ACTIVE);
+		merge3d_active &= ~BIT(cfg->merge_3d - MERGE_3D_0);
+		DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
+				merge3d_active);
+	}
+
+	dpu_hw_ctl_clear_all_blendstages(ctx);
+
+	if (cfg->intf) {
+		intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
+		intf_active &= ~BIT(cfg->intf - INTF_0);
+		DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
+	}
+}
+
 static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
 	unsigned long *fetch_active)
 {
@@ -586,6 +617,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
 	if (cap & BIT(DPU_CTL_ACTIVE_CFG)) {
 		ops->trigger_flush = dpu_hw_ctl_trigger_flush_v1;
 		ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg_v1;
+		ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
 		ops->update_pending_flush_intf =
 			dpu_hw_ctl_update_pending_flush_intf_v1;
 		ops->update_pending_flush_merge_3d =
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
index 97f326d..c61a8fd 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
@@ -140,6 +140,14 @@ struct dpu_hw_ctl_ops {
 	void (*setup_intf_cfg)(struct dpu_hw_ctl *ctx,
 		struct dpu_hw_intf_cfg *cfg);
 
+	/**
+	 * reset ctl_path interface config
+	 * @ctx    : ctl path ctx pointer
+	 * @cfg    : interface config structure pointer
+	 */
+	void (*reset_intf_cfg)(struct dpu_hw_ctl *ctx,
+			struct dpu_hw_intf_cfg *cfg);
+
 	int (*reset)(struct dpu_hw_ctl *c);
 
 	/*
-- 
2.7.4


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

* [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (4 preceding siblings ...)
  2022-04-20  1:45 ` [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  7:20   ` Dmitry Baryshkov
  2022-04-20  1:45 ` [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM Abhinav Kumar
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add the dpu_hw_wb abstraction to program registers related to the
writeback block. These will be invoked once all the configuration
is set and ready to be programmed to the registers.

changes in v2:
	- remove multiple empty lines at the end of the file
	- change dpu_hw_wb_bind_pingpong_blk to preserve upper bits

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/msm/Makefile              |   1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
 3 files changed, 405 insertions(+)
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index d5ca2e6..ca779c1 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
 	disp/dpu1/dpu_hw_top.o \
 	disp/dpu1/dpu_hw_util.o \
 	disp/dpu1/dpu_hw_vbif.o \
+	disp/dpu1/dpu_hw_wb.o \
 	disp/dpu1/dpu_kms.o \
 	disp/dpu1/dpu_plane.o \
 	disp/dpu1/dpu_rm.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
new file mode 100644
index 0000000..afa8aab
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
+  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
+  */
+
+#include "dpu_hw_mdss.h"
+#include "dpu_hwio.h"
+#include "dpu_hw_catalog.h"
+#include "dpu_hw_wb.h"
+#include "dpu_formats.h"
+#include "dpu_kms.h"
+
+#define WB_DST_FORMAT                         0x000
+#define WB_DST_OP_MODE                        0x004
+#define WB_DST_PACK_PATTERN                   0x008
+#define WB_DST0_ADDR                          0x00C
+#define WB_DST1_ADDR                          0x010
+#define WB_DST2_ADDR                          0x014
+#define WB_DST3_ADDR                          0x018
+#define WB_DST_YSTRIDE0                       0x01C
+#define WB_DST_YSTRIDE1                       0x020
+#define WB_DST_YSTRIDE1                       0x020
+#define WB_DST_DITHER_BITDEPTH                0x024
+#define WB_DST_MATRIX_ROW0                    0x030
+#define WB_DST_MATRIX_ROW1                    0x034
+#define WB_DST_MATRIX_ROW2                    0x038
+#define WB_DST_MATRIX_ROW3                    0x03C
+#define WB_DST_WRITE_CONFIG                   0x048
+#define WB_ROTATION_DNSCALER                  0x050
+#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
+#define WB_N16_INIT_PHASE_X_C03               0x060
+#define WB_N16_INIT_PHASE_X_C12               0x064
+#define WB_N16_INIT_PHASE_Y_C03               0x068
+#define WB_N16_INIT_PHASE_Y_C12               0x06C
+#define WB_OUT_SIZE                           0x074
+#define WB_ALPHA_X_VALUE                      0x078
+#define WB_DANGER_LUT                         0x084
+#define WB_SAFE_LUT                           0x088
+#define WB_QOS_CTRL                           0x090
+#define WB_CREQ_LUT_0                         0x098
+#define WB_CREQ_LUT_1                         0x09C
+#define WB_UBWC_STATIC_CTRL                   0x144
+#define WB_MUX                                0x150
+#define WB_CROP_CTRL                          0x154
+#define WB_CROP_OFFSET                        0x158
+#define WB_CSC_BASE                           0x260
+#define WB_DST_ADDR_SW_STATUS                 0x2B0
+#define WB_CDP_CNTL                           0x2B4
+#define WB_OUT_IMAGE_SIZE                     0x2C0
+#define WB_OUT_XY                             0x2C4
+
+/* WB_QOS_CTRL */
+#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
+
+static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
+		const struct dpu_mdss_cfg *m, void __iomem *addr,
+		struct dpu_hw_blk_reg_map *b)
+{
+	int i;
+
+	for (i = 0; i < m->wb_count; i++) {
+		if (wb == m->wb[i].id) {
+			b->base_off = addr;
+			b->blk_off = m->wb[i].base;
+			b->length = m->wb[i].len;
+			b->hwversion = m->hwversion;
+			return &m->wb[i];
+		}
+	}
+	return ERR_PTR(-EINVAL);
+}
+
+static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
+		struct dpu_hw_wb_cfg *data)
+{
+	struct dpu_hw_blk_reg_map *c = &ctx->hw;
+
+	DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
+	DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
+	DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
+	DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
+}
+
+static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
+		struct dpu_hw_wb_cfg *data)
+{
+	struct dpu_hw_blk_reg_map *c = &ctx->hw;
+	const struct dpu_format *fmt = data->dest.format;
+	u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
+	u32 write_config = 0;
+	u32 opmode = 0;
+	u32 dst_addr_sw = 0;
+
+	chroma_samp = fmt->chroma_sample;
+
+	dst_format = (chroma_samp << 23) |
+		(fmt->fetch_planes << 19) |
+		(fmt->bits[C3_ALPHA] << 6) |
+		(fmt->bits[C2_R_Cr] << 4) |
+		(fmt->bits[C1_B_Cb] << 2) |
+		(fmt->bits[C0_G_Y] << 0);
+
+	if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
+		dst_format |= BIT(8); /* DSTC3_EN */
+		if (!fmt->alpha_enable ||
+			!(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
+			dst_format |= BIT(14); /* DST_ALPHA_X */
+	}
+
+	pattern = (fmt->element[3] << 24) |
+		(fmt->element[2] << 16) |
+		(fmt->element[1] << 8)  |
+		(fmt->element[0] << 0);
+
+	dst_format |= (fmt->unpack_align_msb << 18) |
+		(fmt->unpack_tight << 17) |
+		((fmt->unpack_count - 1) << 12) |
+		((fmt->bpp - 1) << 9);
+
+	ystride0 = data->dest.plane_pitch[0] |
+		(data->dest.plane_pitch[1] << 16);
+	ystride1 = data->dest.plane_pitch[2] |
+	(data->dest.plane_pitch[3] << 16);
+
+	if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
+		outsize = (drm_rect_height(&data->roi) << 16) | drm_rect_width(&data->roi);
+	else
+		outsize = (data->dest.height << 16) | data->dest.width;
+
+	DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
+	DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
+	DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
+	DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
+	DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
+	DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
+	DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
+	DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
+	DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
+}
+
+static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg *wb)
+{
+	struct dpu_hw_blk_reg_map *c = &ctx->hw;
+	u32 image_size, out_size, out_xy;
+
+	image_size = (wb->dest.height << 16) | wb->dest.width;
+	out_xy = 0;
+	out_size = (drm_rect_height(&wb->roi) << 16) | drm_rect_width(&wb->roi);
+
+	DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
+	DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
+	DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
+}
+
+static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
+		struct dpu_hw_wb_qos_cfg *cfg)
+{
+	struct dpu_hw_blk_reg_map *c = &ctx->hw;
+	u32 qos_ctrl = 0;
+
+	if (!ctx || !cfg)
+		return;
+
+	DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
+	DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
+
+	if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
+		DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
+		DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
+	}
+
+	if (cfg->danger_safe_en)
+		qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
+
+	DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
+}
+
+static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
+		struct dpu_hw_wb_cdp_cfg *cfg)
+{
+	struct dpu_hw_blk_reg_map *c;
+	u32 cdp_cntl = 0;
+
+	if (!ctx || !cfg)
+		return;
+
+	c = &ctx->hw;
+
+	if (cfg->enable)
+		cdp_cntl |= BIT(0);
+	if (cfg->ubwc_meta_enable)
+		cdp_cntl |= BIT(1);
+	if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
+		cdp_cntl |= BIT(3);
+
+	DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
+}
+
+static void dpu_hw_wb_bind_pingpong_blk(
+		struct dpu_hw_wb *ctx,
+		bool enable, const enum dpu_pingpong pp)
+{
+	struct dpu_hw_blk_reg_map *c;
+	int mux_cfg;
+
+	if (!ctx)
+		return;
+
+	c = &ctx->hw;
+
+	mux_cfg = DPU_REG_READ(c, WB_MUX);
+	mux_cfg &= ~0xf;
+
+	if (enable)
+		mux_cfg |= (pp - PINGPONG_0) & 0x7;
+	else
+		mux_cfg |= 0xf;
+
+	DPU_REG_WRITE(c, WB_MUX, mux_cfg);
+}
+
+static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
+		unsigned long features)
+{
+	ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
+	ops->setup_outformat = dpu_hw_wb_setup_format;
+
+	if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
+		ops->setup_roi = dpu_hw_wb_roi;
+
+	if (test_bit(DPU_WB_QOS, &features))
+		ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
+
+	if (test_bit(DPU_WB_CDP, &features))
+		ops->setup_cdp = dpu_hw_wb_setup_cdp;
+
+	if (test_bit(DPU_WB_INPUT_CTRL, &features))
+		ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
+}
+
+struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
+		void __iomem *addr, const struct dpu_mdss_cfg *m)
+{
+	struct dpu_hw_wb *c;
+	const struct dpu_wb_cfg *cfg;
+
+	if (!addr || !m)
+		return ERR_PTR(-EINVAL);
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return ERR_PTR(-ENOMEM);
+
+	cfg = _wb_offset(idx, m, addr, &c->hw);
+	if (IS_ERR(cfg)) {
+		WARN(1, "Unable to find wb idx=%d\n", idx);
+		kfree(c);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Assign ops */
+	c->mdp = &m->mdp[0];
+	c->idx = idx;
+	c->caps = cfg;
+	_setup_wb_ops(&c->ops, c->caps->features);
+
+	return c;
+}
+
+void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
+{
+	kfree(hw_wb);
+}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
new file mode 100644
index 0000000..80def96
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
+ */
+
+#ifndef _DPU_HW_WB_H
+#define _DPU_HW_WB_H
+
+#include "dpu_hw_catalog.h"
+#include "dpu_hw_mdss.h"
+#include "dpu_hw_top.h"
+#include "dpu_hw_util.h"
+#include "dpu_hw_pingpong.h"
+
+struct dpu_hw_wb;
+
+struct dpu_hw_wb_cfg {
+	struct dpu_hw_fmt_layout dest;
+	enum dpu_intf_mode intf_mode;
+	struct drm_rect roi;
+	struct drm_rect crop;
+};
+
+/**
+ * enum CDP preload ahead address size
+ */
+enum {
+	DPU_WB_CDP_PRELOAD_AHEAD_32,
+	DPU_WB_CDP_PRELOAD_AHEAD_64
+};
+
+/**
+ * struct dpu_hw_wb_cdp_cfg : CDP configuration
+ * @enable: true to enable CDP
+ * @ubwc_meta_enable: true to enable ubwc metadata preload
+ * @tile_amortize_enable: true to enable amortization control for tile format
+ * @preload_ahead: number of request to preload ahead
+ * SDE_WB_CDP_PRELOAD_AHEAD_32,
+ * SDE_WB_CDP_PRELOAD_AHEAD_64
+ */
+struct dpu_hw_wb_cdp_cfg {
+	bool enable;
+	bool ubwc_meta_enable;
+	bool tile_amortize_enable;
+	u32 preload_ahead;
+};
+
+/**
+ * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
+ * @danger_lut: LUT for generate danger level based on fill level
+ * @safe_lut: LUT for generate safe level based on fill level
+ * @creq_lut: LUT for generate creq level based on fill level
+ * @danger_safe_en: enable danger safe generation
+ */
+struct dpu_hw_wb_qos_cfg {
+	u32 danger_lut;
+	u32 safe_lut;
+	u64 creq_lut;
+	bool danger_safe_en;
+};
+
+/**
+ *
+ * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
+ *  Assumption is these functions will be called after clocks are enabled
+ *  @setup_outaddress: setup output address from the writeback job
+ *  @setup_outformat: setup output format of writeback block from writeback job
+ *  @setup_qos_lut:   setup qos LUT for writeback block based on input
+ *  @setup_cdp:       setup chroma down prefetch block for writeback block
+ *  @bind_pingpong_blk: enable/disable the connection with ping-pong block
+ */
+struct dpu_hw_wb_ops {
+	void (*setup_outaddress)(struct dpu_hw_wb *ctx,
+			struct dpu_hw_wb_cfg *wb);
+
+	void (*setup_outformat)(struct dpu_hw_wb *ctx,
+			struct dpu_hw_wb_cfg *wb);
+
+	void (*setup_roi)(struct dpu_hw_wb *ctx,
+			struct dpu_hw_wb_cfg *wb);
+
+	void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
+			struct dpu_hw_wb_qos_cfg *cfg);
+
+	void (*setup_cdp)(struct dpu_hw_wb *ctx,
+			struct dpu_hw_wb_cdp_cfg *cfg);
+
+	void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
+			bool enable, const enum dpu_pingpong pp);
+};
+
+/**
+ * struct dpu_hw_wb : WB driver object
+ * @hw: block hardware details
+ * @mdp: pointer to associated mdp portion of the catalog
+ * @idx: hardware index number within type
+ * @wb_hw_caps: hardware capabilities
+ * @ops: function pointers
+ * @hw_mdp: MDP top level hardware block
+ */
+struct dpu_hw_wb {
+	struct dpu_hw_blk_reg_map hw;
+	const struct dpu_mdp_cfg *mdp;
+
+	/* wb path */
+	int idx;
+	const struct dpu_wb_cfg *caps;
+
+	/* ops */
+	struct dpu_hw_wb_ops ops;
+
+	struct dpu_hw_mdp *hw_mdp;
+};
+
+/**
+ * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
+ * @idx:  wb_path index for which driver object is required
+ * @addr: mapped register io address of MDP
+ * @m :   pointer to mdss catalog data
+ */
+struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
+		void __iomem *addr,
+		const struct dpu_mdss_cfg *m);
+
+/**
+ * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
+ * @hw_wb:  Pointer to writeback hw driver object
+ */
+void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
+
+#endif /*_DPU_HW_WB_H */
-- 
2.7.4


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

* [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (5 preceding siblings ...)
  2022-04-20  1:45 ` [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks Abhinav Kumar
@ 2022-04-20  1:45 ` Abhinav Kumar
  2022-04-20  6:47   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl Abhinav Kumar
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:45 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add writeback blocks to DPU resource manager so that
the encoders can directly request them through RM.

changes in v2:
	- stop global tracking of WB blocks similar to INTF
	- align usage of hw_wb to be similar to that of hw_intf

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 22 ++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 12 ++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
index 0e6634b..bb01d31 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
@@ -9,6 +9,7 @@
 #include "dpu_hw_ctl.h"
 #include "dpu_hw_pingpong.h"
 #include "dpu_hw_intf.h"
+#include "dpu_hw_wb.h"
 #include "dpu_hw_dspp.h"
 #include "dpu_hw_merge3d.h"
 #include "dpu_hw_dsc.h"
@@ -87,6 +88,9 @@ int dpu_rm_destroy(struct dpu_rm *rm)
 		}
 	}
 
+	for (i = 0; i < ARRAY_SIZE(rm->hw_wb); i++)
+		dpu_hw_wb_destroy(rm->hw_wb[i]);
+
 	return 0;
 }
 
@@ -186,6 +190,24 @@ int dpu_rm_init(struct dpu_rm *rm,
 		rm->hw_intf[intf->id - INTF_0] = hw;
 	}
 
+	for (i = 0; i < cat->wb_count; i++) {
+		struct dpu_hw_wb *hw;
+		const struct dpu_wb_cfg *wb = &cat->wb[i];
+
+		if (wb->id < WB_0 || wb->id >= WB_MAX) {
+			DPU_ERROR("skip intf %d with invalid id\n", wb->id);
+			continue;
+		}
+
+		hw = dpu_hw_wb_init(wb->id, mmio, cat);
+		if (IS_ERR_OR_NULL(hw)) {
+			rc = PTR_ERR(hw);
+			DPU_ERROR("failed wb object creation: err %d\n", rc);
+			goto fail;
+		}
+		rm->hw_wb[wb->id - WB_0] = hw;
+	}
+
 	for (i = 0; i < cat->ctl_count; i++) {
 		struct dpu_hw_ctl *hw;
 		const struct dpu_ctl_cfg *ctl = &cat->ctl[i];
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
index 32e0d8a..ba82e54 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
@@ -19,6 +19,7 @@ struct dpu_global_state;
  * @mixer_blks: array of layer mixer hardware resources
  * @ctl_blks: array of ctl hardware resources
  * @hw_intf: array of intf hardware resources
+ * @hw_wb: array of wb hardware resources
  * @dspp_blks: array of dspp hardware resources
  */
 struct dpu_rm {
@@ -26,6 +27,7 @@ struct dpu_rm {
 	struct dpu_hw_blk *mixer_blks[LM_MAX - LM_0];
 	struct dpu_hw_blk *ctl_blks[CTL_MAX - CTL_0];
 	struct dpu_hw_intf *hw_intf[INTF_MAX - INTF_0];
+	struct dpu_hw_wb *hw_wb[WB_MAX - WB_0];
 	struct dpu_hw_blk *dspp_blks[DSPP_MAX - DSPP_0];
 	struct dpu_hw_blk *merge_3d_blks[MERGE_3D_MAX - MERGE_3D_0];
 	struct dpu_hw_blk *dsc_blks[DSC_MAX - DSC_0];
@@ -96,5 +98,15 @@ static inline struct dpu_hw_intf *dpu_rm_get_intf(struct dpu_rm *rm, enum dpu_in
 	return rm->hw_intf[intf_idx - INTF_0];
 }
 
+/**
+ * dpu_rm_get_wb - Return a struct dpu_hw_wb instance given it's index.
+ * @rm: DPU Resource Manager handle
+ * @wb_idx: WB index
+ */
+static inline struct dpu_hw_wb *dpu_rm_get_wb(struct dpu_rm *rm, enum dpu_intf wb_idx)
+{
+	return rm->hw_wb[wb_idx - WB_0];
+}
+
 #endif /* __DPU_RM_H__ */
 
-- 
2.7.4


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

* [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (6 preceding siblings ...)
  2022-04-20  1:45 ` [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  6:59   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks Abhinav Kumar
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add changes to support writeback module in the dpu_hw_ctl
interface.

changes in v2:
	- keep only the wb specific changes to reset_intf_cfg
	- use cfg->intf / cfg->wb to identify intf or wb
	- use bit-wise OR for the wb bits while programming

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +++++++++++++++++++++++++++---
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 15 ++++++++++-
 2 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
index 524f024..495a9cd 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -23,10 +24,12 @@
 #define   CTL_SW_RESET                  0x030
 #define   CTL_LAYER_EXTN_OFFSET         0x40
 #define   CTL_MERGE_3D_ACTIVE           0x0E4
+#define   CTL_WB_ACTIVE                 0x0EC
 #define   CTL_INTF_ACTIVE               0x0F4
 #define   CTL_MERGE_3D_FLUSH            0x100
 #define   CTL_DSC_ACTIVE                0x0E8
 #define   CTL_DSC_FLUSH                0x104
+#define   CTL_WB_FLUSH                  0x108
 #define   CTL_INTF_FLUSH                0x110
 #define   CTL_INTF_MASTER               0x134
 #define   CTL_FETCH_PIPE_ACTIVE         0x0FC
@@ -38,6 +41,7 @@
 #define  MERGE_3D_IDX   23
 #define  DSC_IDX        22
 #define  INTF_IDX       31
+#define WB_IDX          16
 #define CTL_INVALID_BIT                 0xffff
 #define CTL_DEFAULT_GROUP_ID		0xf
 
@@ -135,6 +139,9 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
 	if (ctx->pending_flush_mask & BIT(INTF_IDX))
 		DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH,
 				ctx->pending_intf_flush_mask);
+	if (ctx->pending_flush_mask & BIT(WB_IDX))
+		DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
+				ctx->pending_wb_flush_mask);
 
 	DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
 }
@@ -255,6 +262,13 @@ static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx,
 	}
 }
 
+static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx,
+		enum dpu_wb wb)
+{
+	ctx->pending_wb_flush_mask |= BIT(wb - WB_0);
+	ctx->pending_flush_mask |= BIT(WB_IDX);
+}
+
 static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx,
 		enum dpu_intf intf)
 {
@@ -504,6 +518,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
 {
 	struct dpu_hw_blk_reg_map *c = &ctx->hw;
 	u32 intf_active = 0;
+	u32 wb_active = 0;
 	u32 mode_sel = 0;
 
 	/* CTL_TOP[31:28] carries group_id to collate CTL paths
@@ -519,11 +534,20 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
 	if (cfg->intf_mode_sel == DPU_CTL_MODE_SEL_CMD)
 		mode_sel |= BIT(17);
 
-	intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
-	intf_active |= BIT(cfg->intf - INTF_0);
+	if (cfg->intf) {
+		intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
+		intf_active |= BIT(cfg->intf - INTF_0);
+	}
+
+	if (cfg->wb) {
+		wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
+		wb_active |= BIT(cfg->wb - WB_0);
+	}
 
 	DPU_REG_WRITE(c, CTL_TOP, mode_sel);
 	DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
+	DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
+
 	if (cfg->merge_3d)
 		DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
 			      BIT(cfg->merge_3d - MERGE_3D_0));
@@ -546,6 +570,9 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
 		intf_cfg |= (cfg->mode_3d - 0x1) << 20;
 	}
 
+	if (cfg->wb)
+		intf_cfg |= (cfg->wb & 0x3) + 2;
+
 	switch (cfg->intf_mode_sel) {
 	case DPU_CTL_MODE_SEL_VID:
 		intf_cfg &= ~BIT(17);
@@ -568,12 +595,13 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
 {
 	struct dpu_hw_blk_reg_map *c = &ctx->hw;
 	u32 intf_active = 0;
+	u32 wb_active = 0;
 	u32 merge3d_active = 0;
 
 	/*
 	 * This API resets each portion of the CTL path namely,
 	 * clearing the sspps staged on the lm, merge_3d block,
-	 * interfaces etc to ensure clean teardown of the pipeline.
+	 * interfaces , writeback etc to ensure clean teardown of the pipeline.
 	 * This will be used for writeback to begin with to have a
 	 * proper teardown of the writeback session but upon further
 	 * validation, this can be extended to all interfaces.
@@ -592,6 +620,12 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
 		intf_active &= ~BIT(cfg->intf - INTF_0);
 		DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
 	}
+
+	if (cfg->wb) {
+		wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
+		wb_active &= ~BIT(cfg->wb - WB_0);
+		DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
+	}
 }
 
 static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
@@ -622,6 +656,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
 			dpu_hw_ctl_update_pending_flush_intf_v1;
 		ops->update_pending_flush_merge_3d =
 			dpu_hw_ctl_update_pending_flush_merge_3d_v1;
+		ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1;
 	} else {
 		ops->trigger_flush = dpu_hw_ctl_trigger_flush;
 		ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
index c61a8fd..df8f8e9 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DPU_HW_CTL_H
@@ -44,6 +45,7 @@ struct dpu_hw_stage_cfg {
  */
 struct dpu_hw_intf_cfg {
 	enum dpu_intf intf;
+	enum dpu_wb wb;
 	enum dpu_3d_blend_mode mode_3d;
 	enum dpu_merge_3d merge_3d;
 	enum dpu_ctl_mode_sel intf_mode_sel;
@@ -102,6 +104,15 @@ struct dpu_hw_ctl_ops {
 		u32 flushbits);
 
 	/**
+	 * OR in the given flushbits to the cached pending_(wb_)flush_mask
+	 * No effect on hardware
+	 * @ctx       : ctl path ctx pointer
+	 * @blk       : writeback block index
+	 */
+	void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx,
+		enum dpu_wb blk);
+
+	/**
 	 * OR in the given flushbits to the cached pending_(intf_)flush_mask
 	 * No effect on hardware
 	 * @ctx       : ctl path ctx pointer
@@ -199,6 +210,7 @@ struct dpu_hw_ctl_ops {
  * @mixer_hw_caps: mixer hardware capabilities
  * @pending_flush_mask: storage for pending ctl_flush managed via ops
  * @pending_intf_flush_mask: pending INTF flush
+ * @pending_wb_flush_mask: pending WB flush
  * @ops: operation list
  */
 struct dpu_hw_ctl {
@@ -212,6 +224,7 @@ struct dpu_hw_ctl {
 	const struct dpu_lm_cfg *mixer_hw_caps;
 	u32 pending_flush_mask;
 	u32 pending_intf_flush_mask;
+	u32 pending_wb_flush_mask;
 	u32 pending_merge_3d_flush_mask;
 
 	/* ops */
-- 
2.7.4


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

* [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (7 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:23   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder Abhinav Kumar
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add an API to reset the encoder related hw blocks to ensure
a proper teardown of the pipeline. At the moment this is being
used only for the writeback encoder but eventually we can start
using this for all interfaces.

changes in v2:
	- split the writeback part to another commit

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 82 ++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  7 ++
 2 files changed, 89 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 4523693..0e31ad3 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
@@ -22,6 +23,7 @@
 #include "dpu_hw_ctl.h"
 #include "dpu_hw_dspp.h"
 #include "dpu_hw_dsc.h"
+#include "dpu_hw_merge3d.h"
 #include "dpu_formats.h"
 #include "dpu_encoder_phys.h"
 #include "dpu_crtc.h"
@@ -1838,6 +1840,86 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
 	DPU_ATRACE_END("encoder_kickoff");
 }
 
+static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_mixer_cfg mixer;
+	int i, num_lm;
+	u32 flush_mask = 0;
+	struct dpu_global_state *global_state;
+	struct dpu_hw_blk *hw_lm[2];
+	struct dpu_hw_mixer *hw_mixer[2];
+	struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
+
+	memset(&mixer, 0, sizeof(mixer));
+
+	/* reset all mixers for this encoder */
+	if (phys_enc->hw_ctl->ops.clear_all_blendstages)
+		phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
+
+	global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
+
+	num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state,
+		phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm));
+
+	for (i = 0; i < num_lm; i++) {
+		hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
+		flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, hw_mixer[i]->idx);
+		if (phys_enc->hw_ctl->ops.update_pending_flush)
+			phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask);
+
+		/* clear all blendstages */
+		if (phys_enc->hw_ctl->ops.setup_blendstage)
+			phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL);
+	}
+}
+
+void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
+	struct dpu_hw_intf_cfg intf_cfg = { 0 };
+	int i;
+	struct dpu_encoder_virt *dpu_enc;
+
+	dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
+
+	phys_enc->hw_ctl->ops.reset(ctl);
+
+	dpu_encoder_helper_reset_mixers(phys_enc);
+
+	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+		if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
+			phys_enc->hw_intf->ops.bind_pingpong_blk(
+					dpu_enc->phys_encs[i]->hw_intf, false,
+					dpu_enc->phys_encs[i]->hw_pp->idx);
+
+		/* mark INTF flush as pending */
+		if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
+			phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
+					dpu_enc->phys_encs[i]->hw_intf->idx);
+	}
+
+	/* reset the merge 3D HW block */
+	if (phys_enc->hw_pp->merge_3d) {
+		phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
+				BLEND_3D_NONE);
+		if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d)
+			phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl,
+					phys_enc->hw_pp->merge_3d->idx);
+	}
+
+	intf_cfg.stream_sel = 0; /* Don't care value for video mode */
+	intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
+	if (phys_enc->hw_pp->merge_3d)
+		intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
+
+	if (ctl->ops.reset_intf_cfg)
+		ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
+
+	ctl->ops.trigger_flush(ctl);
+	ctl->ops.trigger_start(ctl);
+	ctl->ops.clear_pending_flush(ctl);
+}
+
 void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc)
 {
 	struct dpu_encoder_virt *dpu_enc;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 706b566..544a9a4 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
  */
 
@@ -350,4 +351,10 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
 		void (*func)(void *arg, int irq_idx),
 		struct dpu_encoder_wait_info *wait_info);
 
+/**
+ * dpu_encoder_helper_phys_cleanup - helper to cleanup dpu pipeline
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc);
+
 #endif /* __dpu_encoder_phys_H__ */
-- 
2.7.4


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

* [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (8 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:44   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 11/17] drm/msm/dpu: add encoder operations to prepare/cleanup wb job Abhinav Kumar
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Make changes to dpu_encoder to support virtual encoder needed
to support writeback for dpu.

changes in v2:
	- add the writeback parts to dpu_encoder_helper_phys_cleanup
	- rebase on tip of msm-next and fix related dependencies
	- get the writeback blocks directly from RM

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71 +++++++++++++++++-------
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
 2 files changed, 54 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 0e31ad3..06b8631 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -928,6 +928,7 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 	struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
 	int num_lm, num_ctl, num_pp, num_dsc;
 	unsigned int dsc_mask = 0;
+	enum dpu_hw_blk_type blk_type;
 	int i;
 
 	if (!drm_enc) {
@@ -1009,12 +1010,21 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 		phys->hw_pp = dpu_enc->hw_pp[i];
 		phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
 
-		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
-			phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
+		if (dpu_encoder_get_intf_mode(&dpu_enc->base) == INTF_MODE_WB_LINE)
+			blk_type = DPU_HW_BLK_WB;
+		else
+			blk_type = DPU_HW_BLK_INTF;
+
+		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
+			if (blk_type == DPU_HW_BLK_INTF)
+				phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
+			else if (blk_type == DPU_HW_BLK_WB)
+				phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm, phys->intf_idx);
+		}
 
-		if (!phys->hw_intf) {
+		if (!phys->hw_intf && !phys->hw_wb) {
 			DPU_ERROR_ENC(dpu_enc,
-				      "no intf block assigned at idx: %d\n", i);
+				      "no intf ow wb block assigned at idx: %d\n", i);
 			return;
 		}
 
@@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
-static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
+static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg *catalog,
 		enum dpu_intf_type type, u32 controller_id)
 {
 	int i = 0;
 
-	for (i = 0; i < catalog->intf_count; i++) {
-		if (catalog->intf[i].type == type
-		    && catalog->intf[i].controller_id == controller_id) {
-			return catalog->intf[i].id;
+	if (type != INTF_WB) {
+		for (i = 0; i < catalog->intf_count; i++) {
+			if (catalog->intf[i].type == type
+				&& catalog->intf[i].controller_id == controller_id) {
+				return catalog->intf[i].id;
+			}
+		}
+	} else {
+		for (i = 0; i < catalog->wb_count; i++) {
+			if (catalog->wb[i].id == controller_id)
+				return catalog->wb[i].id;
 		}
 	}
 
@@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
 
 	dpu_encoder_helper_reset_mixers(phys_enc);
 
-	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
-		if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
-			phys_enc->hw_intf->ops.bind_pingpong_blk(
-					dpu_enc->phys_encs[i]->hw_intf, false,
-					dpu_enc->phys_encs[i]->hw_pp->idx);
+	if (phys_enc->hw_wb) {
+		/* disable the PP block */
+		if (phys_enc->hw_wb->ops.bind_pingpong_blk)
+			phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, false,
+					phys_enc->hw_pp->idx);
 
-		/* mark INTF flush as pending */
-		if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
-			phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
-					dpu_enc->phys_encs[i]->hw_intf->idx);
+		/* mark WB flush as pending */
+		if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
+			phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx);
+	} else {
+		for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+			if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
+				phys_enc->hw_intf->ops.bind_pingpong_blk(
+						dpu_enc->phys_encs[i]->hw_intf, false,
+						dpu_enc->phys_encs[i]->hw_pp->idx);
+
+			/* mark INTF flush as pending */
+			if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
+				phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
+						dpu_enc->phys_encs[i]->hw_intf->idx);
+		}
 	}
 
 	/* reset the merge 3D HW block */
@@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 	case DRM_MODE_ENCODER_TMDS:
 		intf_type = INTF_DP;
 		break;
+	case DRM_MODE_ENCODER_VIRTUAL:
+		intf_type = INTF_WB;
+		break;
 	}
 
 	WARN_ON(disp_info->num_of_h_tiles < 1);
@@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 		DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
 				i, controller_id, phys_params.split_role);
 
-		phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
+		phys_params.intf_idx = dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
 													intf_type,
 													controller_id);
 		if (phys_params.intf_idx == INTF_MAX) {
-			DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id %d\n",
+			DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d, id %d\n",
 						  intf_type, controller_id);
 			ret = -EINVAL;
 		}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 544a9a4..0b80af4 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -11,6 +11,7 @@
 
 #include "dpu_kms.h"
 #include "dpu_hw_intf.h"
+#include "dpu_hw_wb.h"
 #include "dpu_hw_pingpong.h"
 #include "dpu_hw_ctl.h"
 #include "dpu_hw_top.h"
@@ -165,6 +166,7 @@ enum dpu_intr_idx {
  * @hw_ctl:		Hardware interface to the ctl registers
  * @hw_pp:		Hardware interface to the ping pong registers
  * @hw_intf:		Hardware interface to the intf registers
+ * @hw_wb:		Hardware interface to the wb registers
  * @dpu_kms:		Pointer to the dpu_kms top level
  * @cached_mode:	DRM mode cached at mode_set time, acted on in enable
  * @enabled:		Whether the encoder has enabled and running a mode
@@ -193,6 +195,7 @@ struct dpu_encoder_phys {
 	struct dpu_hw_ctl *hw_ctl;
 	struct dpu_hw_pingpong *hw_pp;
 	struct dpu_hw_intf *hw_intf;
+	struct dpu_hw_wb *hw_wb;
 	struct dpu_kms *dpu_kms;
 	struct drm_display_mode cached_mode;
 	enum dpu_enc_split_role split_role;
-- 
2.7.4


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

* [PATCH v2 11/17] drm/msm/dpu: add encoder operations to prepare/cleanup wb job
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (9 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  1:46 ` [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file Abhinav Kumar
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

add dpu encoder APIs to prepare and cleanup writeback job
for the writeback encoder. These shall be invoked from the
prepare_wb_job/cleanup_wb_job hooks of the drm_writeback
framework.

changes in v2:
	- rebased on tip of msm-next

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 34 ++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h      | 16 +++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  5 ++++
 3 files changed, 55 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 06b8631..b117cad 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -912,6 +912,40 @@ static int dpu_encoder_resource_control(struct drm_encoder *drm_enc,
 	return 0;
 }
 
+void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
+		struct drm_writeback_job *job)
+{
+	struct dpu_encoder_virt *dpu_enc;
+	int i;
+
+	dpu_enc = to_dpu_encoder_virt(drm_enc);
+
+	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+		struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
+
+		if (phys->ops.prepare_wb_job)
+			phys->ops.prepare_wb_job(phys, job);
+
+	}
+}
+
+void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
+		struct drm_writeback_job *job)
+{
+	struct dpu_encoder_virt *dpu_enc;
+	int i;
+
+	dpu_enc = to_dpu_encoder_virt(drm_enc);
+
+	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+		struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
+
+		if (phys->ops.cleanup_wb_job)
+			phys->ops.cleanup_wb_job(phys, job);
+
+	}
+}
+
 static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 					     struct drm_crtc_state *crtc_state,
 					     struct drm_connector_state *conn_state)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index 2903e65..6ceec1d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -180,4 +180,20 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc);
  */
 bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc);
 
+/**
+ * dpu_encoder_prepare_wb_job - prepare writeback job for the encoder.
+ * @drm_enc:    Pointer to previously created drm encoder structure
+ * @job:        Pointer to the current drm writeback job
+ */
+void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
+		struct drm_writeback_job *job);
+
+/**
+ * dpu_encoder_cleanup_wb_job - cleanup writeback job for the encoder.
+ * @drm_enc:    Pointer to previously created drm encoder structure
+ * @job:        Pointer to the current drm writeback job
+ */
+void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
+		struct drm_writeback_job *job);
+
 #endif /* __DPU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 0b80af4..00951f3 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -7,6 +7,7 @@
 #ifndef __DPU_ENCODER_PHYS_H__
 #define __DPU_ENCODER_PHYS_H__
 
+#include <drm/drm_writeback.h>
 #include <linux/jiffies.h>
 
 #include "dpu_kms.h"
@@ -137,6 +138,10 @@ struct dpu_encoder_phys_ops {
 	void (*restore)(struct dpu_encoder_phys *phys);
 	int (*get_line_count)(struct dpu_encoder_phys *phys);
 	int (*get_frame_count)(struct dpu_encoder_phys *phys);
+	void (*prepare_wb_job)(struct dpu_encoder_phys *phys_enc,
+			struct drm_writeback_job *job);
+	void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc,
+			struct drm_writeback_job *job);
 };
 
 /**
-- 
2.7.4


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

* [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (10 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 11/17] drm/msm/dpu: add encoder operations to prepare/cleanup wb job Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:26   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback Abhinav Kumar
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

_dpu_plane_get_qos_lut() is not specific to just dpu_plane.
It can take any fill level and return the LUT matching it.
This can be used even for other modules like dpu_writeback.

Move _dpu_plane_get_qos_lut() to the common dpu_hw_util file
and rename it to _dpu_hw_get_qos_lut().

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c | 25 +++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h |  4 ++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c   | 27 +--------------------------
 3 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
index aad8511..512316f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
@@ -422,3 +422,28 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map *c,
 	DPU_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]);
 	DPU_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]);
 }
+
+/**
+ * _dpu_hw_get_qos_lut - get LUT mapping based on fill level
+ * @tbl:		Pointer to LUT table
+ * @total_fl:		fill level
+ * Return: LUT setting corresponding to the fill level
+ */
+u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
+		u32 total_fl)
+{
+	int i;
+
+	if (!tbl || !tbl->nentry || !tbl->entries)
+		return 0;
+
+	for (i = 0; i < tbl->nentry; i++)
+		if (total_fl <= tbl->entries[i].fl)
+			return tbl->entries[i].lut;
+
+	/* if last fl is zero, use as default */
+	if (!tbl->entries[i-1].fl)
+		return tbl->entries[i-1].lut;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
index 3913475..529a6e0 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
@@ -9,6 +9,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include "dpu_hw_mdss.h"
+#include "dpu_hw_catalog.h"
 
 #define REG_MASK(n)                     ((BIT(n)) - 1)
 
@@ -324,4 +325,7 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map  *c,
 		u32 csc_reg_off,
 		const struct dpu_csc_cfg *data, bool csc10);
 
+u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
+		u32 total_fl);
+
 #endif /* _DPU_HW_UTIL_H */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
index c77c3d9d..730f0a3 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -280,31 +280,6 @@ static int _dpu_plane_calc_fill_level(struct drm_plane *plane,
 }
 
 /**
- * _dpu_plane_get_qos_lut - get LUT mapping based on fill level
- * @tbl:		Pointer to LUT table
- * @total_fl:		fill level
- * Return: LUT setting corresponding to the fill level
- */
-static u64 _dpu_plane_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
-		u32 total_fl)
-{
-	int i;
-
-	if (!tbl || !tbl->nentry || !tbl->entries)
-		return 0;
-
-	for (i = 0; i < tbl->nentry; i++)
-		if (total_fl <= tbl->entries[i].fl)
-			return tbl->entries[i].lut;
-
-	/* if last fl is zero, use as default */
-	if (!tbl->entries[i-1].fl)
-		return tbl->entries[i-1].lut;
-
-	return 0;
-}
-
-/**
  * _dpu_plane_set_qos_lut - set QoS LUT of the given plane
  * @plane:		Pointer to drm plane
  * @fb:			Pointer to framebuffer associated with the given plane
@@ -333,7 +308,7 @@ static void _dpu_plane_set_qos_lut(struct drm_plane *plane,
 			lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;
 	}
 
-	qos_lut = _dpu_plane_get_qos_lut(
+	qos_lut = _dpu_hw_get_qos_lut(
 			&pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl);
 
 	trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0,
-- 
2.7.4


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

* [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (11 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:49   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer Abhinav Kumar
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Introduce the dpu_encoder_phys_* for the writeback interface
to handle writeback specific hardware programming.

changes in v2:
	- rebase on msm-next and fix related dependencies namely
	  the irq cleanup
	- move cdp_cfg, aspace out of dpu_encoder_phys_wb
	- leave a comment about wb master
	- start using _dpu_hw_get_qos_lut from dpu_hw_util
	- replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/Makefile                       |   1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751 +++++++++++++++++++++
 3 files changed, 782 insertions(+)
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index ca779c1..0387f22 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
 	disp/dpu1/dpu_encoder.o \
 	disp/dpu1/dpu_encoder_phys_cmd.o \
 	disp/dpu1/dpu_encoder_phys_vid.o \
+	disp/dpu1/dpu_encoder_phys_wb.o \
 	disp/dpu1/dpu_formats.o \
 	disp/dpu1/dpu_hw_catalog.o \
 	disp/dpu1/dpu_hw_ctl.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 00951f3..5452f98 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
  * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
  * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
  * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
+ * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
  */
 enum dpu_intr_idx {
 	INTR_IDX_VSYNC,
@@ -157,6 +158,7 @@ enum dpu_intr_idx {
 	INTR_IDX_UNDERRUN,
 	INTR_IDX_CTL_START,
 	INTR_IDX_RDPTR,
+	INTR_IDX_WB_DONE,
 	INTR_IDX_MAX,
 };
 
@@ -224,6 +226,27 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
 }
 
 /**
+ * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to handle command
+ *	mode specific operations
+ * @base:	Baseclass physical encoder structure
+ * @wbirq_refcount:     Reference count of writeback interrupt
+ * @wb_done_timeout_cnt: number of wb done irq timeout errors
+ * @wb_cfg:  writeback block config to store fb related details
+ * @wb_conn: backpointer to writeback connector
+ * @wb_job: backpointer to current writeback job
+ * @dest:   dpu buffer layout for current writeback output buffer
+ */
+struct dpu_encoder_phys_wb {
+	struct dpu_encoder_phys base;
+	atomic_t wbirq_refcount;
+	int wb_done_timeout_cnt;
+	struct dpu_hw_wb_cfg wb_cfg;
+	struct drm_writeback_connector *wb_conn;
+	struct drm_writeback_job *wb_job;
+	struct dpu_hw_fmt_layout dest;
+};
+
+/**
  * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command
  *	mode specific operations
  * @base:	Baseclass physical encoder structure
@@ -291,6 +314,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
 		struct dpu_enc_phys_init_params *p);
 
 /**
+ * dpu_encoder_phys_wb_init - initialize writeback encoder
+ * @init:	Pointer to init info structure with initialization params
+ */
+struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
+		struct dpu_enc_phys_init_params *p);
+
+/**
  * dpu_encoder_helper_trigger_start - control start helper function
  *	This helper function may be optionally specified by physical
  *	encoders if they require ctl_start triggering.
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
new file mode 100644
index 0000000..128317fe
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
@@ -0,0 +1,751 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <linux/debugfs.h>
+
+#include "dpu_encoder_phys.h"
+#include "dpu_formats.h"
+#include "dpu_hw_top.h"
+#include "dpu_hw_wb.h"
+#include "dpu_hw_lm.h"
+#include "dpu_hw_blk.h"
+#include "dpu_hw_merge3d.h"
+#include "dpu_hw_interrupts.h"
+#include "dpu_core_irq.h"
+#include "dpu_vbif.h"
+#include "dpu_crtc.h"
+#include "disp/msm_disp_snapshot.h"
+
+#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
+
+#define to_dpu_encoder_phys_wb(x) \
+	container_of(x, struct dpu_encoder_phys_wb, base)
+
+/**
+ * dpu_encoder_phys_wb_is_master - report wb always as master encoder
+ */
+static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys *phys_enc)
+{
+	/* there is only one physical enc for dpu_writeback */
+	return true;
+}
+
+/**
+ * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback interface
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_set_ot_limit(
+		struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
+	struct dpu_vbif_set_ot_params ot_params;
+
+	memset(&ot_params, 0, sizeof(ot_params));
+	ot_params.xin_id = hw_wb->caps->xin_id;
+	ot_params.num = hw_wb->idx - WB_0;
+	ot_params.width = phys_enc->cached_mode.hdisplay;
+	ot_params.height = phys_enc->cached_mode.vdisplay;
+	ot_params.is_wfd = true;
+	ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
+	ot_params.vbif_idx = hw_wb->caps->vbif_idx;
+	ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
+	ot_params.rd = false;
+
+	dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
+}
+
+/**
+ * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_set_qos_remap(
+		struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb;
+	struct dpu_vbif_set_qos_params qos_params;
+
+	if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
+		DPU_ERROR("invalid arguments\n");
+		return;
+	}
+
+	if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
+		DPU_ERROR("invalid writeback hardware\n");
+		return;
+	}
+
+	hw_wb = phys_enc->hw_wb;
+
+	memset(&qos_params, 0, sizeof(qos_params));
+	qos_params.vbif_idx = hw_wb->caps->vbif_idx;
+	qos_params.xin_id = hw_wb->caps->xin_id;
+	qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
+	qos_params.num = hw_wb->idx - WB_0;
+	qos_params.is_rt = false;
+
+	DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
+			qos_params.num,
+			qos_params.vbif_idx,
+			qos_params.xin_id, qos_params.is_rt);
+
+	dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
+}
+
+/**
+ * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb;
+	struct dpu_hw_wb_qos_cfg qos_cfg;
+	struct dpu_mdss_cfg *catalog;
+	struct dpu_qos_lut_tbl *qos_lut_tb;
+
+	if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
+		DPU_ERROR("invalid parameter(s)\n");
+		return;
+	}
+
+	catalog = phys_enc->dpu_kms->catalog;
+
+	hw_wb = phys_enc->hw_wb;
+
+	memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
+	qos_cfg.danger_safe_en = true;
+	qos_cfg.danger_lut =
+		catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
+
+	qos_cfg.safe_lut = catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
+
+	qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
+	qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
+
+	if (hw_wb->ops.setup_qos_lut)
+		hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
+}
+
+/**
+ * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
+ * @phys_enc:	Pointer to physical encoder
+ * @fb:		Pointer to output framebuffer
+ * @wb_roi:	Pointer to output region of interest
+ */
+static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
+		struct drm_framebuffer *fb)
+{
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+	struct dpu_hw_wb *hw_wb;
+	struct dpu_hw_wb_cfg *wb_cfg;
+	struct dpu_hw_wb_cdp_cfg cdp_cfg;
+
+	if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
+		DPU_ERROR("invalid encoder\n");
+		return;
+	}
+
+	hw_wb = phys_enc->hw_wb;
+	wb_cfg = &wb_enc->wb_cfg;
+
+	wb_cfg->intf_mode = phys_enc->intf_mode;
+	wb_cfg->roi.x1 = 0;
+	wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
+	wb_cfg->roi.y1 = 0;
+	wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
+
+	if (hw_wb->ops.setup_roi)
+		hw_wb->ops.setup_roi(hw_wb, wb_cfg);
+
+	if (hw_wb->ops.setup_outformat)
+		hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
+
+	if (hw_wb->ops.setup_cdp) {
+		memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
+
+		cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
+				[DPU_PERF_CDP_USAGE_NRT].wr_enable;
+		cdp_cfg.ubwc_meta_enable =
+				DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
+		cdp_cfg.tile_amortize_enable =
+				DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
+				DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
+		cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
+
+		hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
+	}
+
+	if (hw_wb->ops.setup_outaddress)
+		hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
+}
+
+/**
+ * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
+ * @phys_enc:Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb;
+	struct dpu_hw_ctl *ctl;
+
+	if (!phys_enc) {
+		DPU_ERROR("invalid encoder\n");
+		return;
+	}
+
+	hw_wb = phys_enc->hw_wb;
+	ctl = phys_enc->hw_ctl;
+
+	if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
+		(phys_enc->hw_ctl &&
+		 phys_enc->hw_ctl->ops.setup_intf_cfg)) {
+		struct dpu_hw_intf_cfg intf_cfg = {0};
+		struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
+		enum dpu_3d_blend_mode mode_3d;
+
+		mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
+
+		intf_cfg.intf = DPU_NONE;
+		intf_cfg.wb = hw_wb->idx;
+
+		if (mode_3d && hw_pp && hw_pp->merge_3d)
+			intf_cfg.merge_3d = hw_pp->merge_3d->idx;
+
+		if (phys_enc->hw_pp->merge_3d && phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
+			phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
+					mode_3d);
+
+		/* setup which pp blk will connect to this wb */
+		if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
+			phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, true,
+					phys_enc->hw_pp->idx);
+
+		phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
+	} else if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg) {
+		struct dpu_hw_intf_cfg intf_cfg = {0};
+
+		intf_cfg.intf = DPU_NONE;
+		intf_cfg.wb = hw_wb->idx;
+		intf_cfg.mode_3d =
+			dpu_encoder_helper_get_3d_blend_mode(phys_enc);
+		phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
+	}
+}
+
+/**
+ * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic states
+ * @phys_enc:	Pointer to physical encoder
+ * @crtc_state:	Pointer to CRTC atomic state
+ * @conn_state:	Pointer to connector atomic state
+ */
+static int dpu_encoder_phys_wb_atomic_check(
+		struct dpu_encoder_phys *phys_enc,
+		struct drm_crtc_state *crtc_state,
+		struct drm_connector_state *conn_state)
+{
+	struct drm_framebuffer *fb;
+	const struct drm_display_mode *mode;
+
+	DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
+			phys_enc->intf_idx, mode->name, mode->hdisplay, mode->vdisplay);
+
+	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
+		return 0;
+
+	fb = conn_state->writeback_job->fb;
+	mode = &crtc_state->mode;
+
+	if (!conn_state || !conn_state->connector) {
+		DPU_ERROR("invalid connector state\n");
+		return -EINVAL;
+	} else if (conn_state->connector->status !=
+			connector_status_connected) {
+		DPU_ERROR("connector not connected %d\n",
+				conn_state->connector->status);
+		return -EINVAL;
+	}
+
+	DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
+			fb->width, fb->height);
+
+	if (fb->width != mode->hdisplay) {
+		DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
+				mode->hdisplay);
+		return -EINVAL;
+	} else if (fb->height != mode->vdisplay) {
+		DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
+				  mode->vdisplay);
+		return -EINVAL;
+	} else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
+		DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
+				  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/**
+ * _dpu_encoder_phys_wb_update_flush - flush hardware update
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void _dpu_encoder_phys_wb_update_flush(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb;
+	struct dpu_hw_ctl *hw_ctl;
+	struct dpu_hw_pingpong *hw_pp;
+	u32 pending_flush = 0;
+
+	if (!phys_enc)
+		return;
+
+	hw_wb = phys_enc->hw_wb;
+	hw_pp = phys_enc->hw_pp;
+	hw_ctl = phys_enc->hw_ctl;
+
+	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
+
+	if (!hw_ctl) {
+		DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
+		return;
+	}
+
+	if (hw_ctl->ops.update_pending_flush_wb)
+		hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
+
+	if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && hw_pp->merge_3d)
+		hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
+				hw_pp->merge_3d->idx);
+
+	if (hw_ctl->ops.get_pending_flush)
+		pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
+
+	DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
+			hw_ctl->idx - CTL_0, pending_flush,
+			hw_wb->idx - WB_0);
+}
+
+/**
+ * dpu_encoder_phys_wb_setup - setup writeback encoder
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_setup(
+		struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
+	struct drm_display_mode mode = phys_enc->cached_mode;
+	struct drm_framebuffer *fb = NULL;
+
+	DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
+			hw_wb->idx - WB_0, mode.name,
+			mode.hdisplay, mode.vdisplay);
+
+	dpu_encoder_phys_wb_set_ot_limit(phys_enc);
+
+	dpu_encoder_phys_wb_set_qos_remap(phys_enc);
+
+	dpu_encoder_phys_wb_set_qos(phys_enc);
+
+	dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
+
+	dpu_encoder_phys_wb_setup_cdp(phys_enc);
+
+}
+
+static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
+{
+	struct dpu_encoder_phys *phys_enc = arg;
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+
+	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
+	unsigned long lock_flags;
+	u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
+
+	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
+
+	if (phys_enc->parent_ops->handle_frame_done)
+		phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
+				phys_enc, event);
+
+	if (phys_enc->parent_ops->handle_vblank_virt)
+		phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
+				phys_enc);
+
+	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
+	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+
+	if (wb_enc->wb_conn)
+		drm_writeback_signal_completion(wb_enc->wb_conn, 0);
+
+	/* Signal any waiting atomic commit thread */
+	wake_up_all(&phys_enc->pending_kickoff_wq);
+}
+
+/**
+ * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
+ * @arg:	Pointer to writeback encoder
+ * @irq_idx:	interrupt index
+ */
+static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
+{
+	_dpu_encoder_phys_wb_frame_done_helper(arg);
+}
+
+/**
+ * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
+ * @phys:	Pointer to physical encoder
+ * @enable:	indicates enable or disable interrupts
+ */
+static void dpu_encoder_phys_wb_irq_ctrl(
+		struct dpu_encoder_phys *phys, bool enable)
+{
+
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
+	int ret = 0;
+	int refcount;
+
+	refcount = atomic_read(&wb_enc->wbirq_refcount);
+
+	if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
+		dpu_core_irq_register_callback(phys->dpu_kms,
+				phys->irq[INTR_IDX_WB_DONE], dpu_encoder_phys_wb_done_irq, phys);
+		if (ret)
+			atomic_dec_return(&wb_enc->wbirq_refcount);
+	} else if (!enable &&
+			atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
+		dpu_core_irq_unregister_callback(phys->dpu_kms, phys->irq[INTR_IDX_WB_DONE]);
+		if (ret)
+			atomic_inc_return(&wb_enc->wbirq_refcount);
+	}
+}
+
+static void dpu_encoder_phys_wb_atomic_mode_set(
+		struct dpu_encoder_phys *phys_enc,
+		struct drm_crtc_state *crtc_state,
+		struct drm_connector_state *conn_state)
+{
+
+	phys_enc->irq[INTR_IDX_WB_DONE] = phys_enc->hw_wb->caps->intr_wb_done;
+}
+
+static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
+		struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+	u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
+
+	wb_enc->wb_done_timeout_cnt++;
+
+	if (wb_enc->wb_done_timeout_cnt == 1)
+		msm_disp_snapshot_state(phys_enc->parent->dev);
+
+	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+
+	/* request a ctl reset before the next kickoff */
+	phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
+
+	if (wb_enc->wb_conn)
+		drm_writeback_signal_completion(wb_enc->wb_conn, 0);
+
+	if (phys_enc->parent_ops->handle_frame_done)
+		phys_enc->parent_ops->handle_frame_done(
+				phys_enc->parent, phys_enc, frame_event);
+}
+
+/**
+ * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is committed
+ * @phys_enc:	Pointer to physical encoder
+ */
+static int dpu_encoder_phys_wb_wait_for_commit_done(
+		struct dpu_encoder_phys *phys_enc)
+{
+	unsigned long ret;
+	struct dpu_encoder_wait_info wait_info;
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+
+	wait_info.wq = &phys_enc->pending_kickoff_wq;
+	wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
+	wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
+
+	ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
+			dpu_encoder_phys_wb_done_irq, &wait_info);
+	if (ret == -ETIMEDOUT)
+		_dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
+	else if (!ret)
+		wb_enc->wb_done_timeout_cnt = 0;
+
+	return ret;
+}
+
+/**
+ * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
+ * @phys_enc:	Pointer to physical encoder
+ * Returns:	Zero on success
+ */
+static void dpu_encoder_phys_wb_prepare_for_kickoff(
+		struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+	struct drm_connector *drm_conn;
+	struct drm_connector_state *state;
+
+	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
+
+	if (!wb_enc->wb_conn || !wb_enc->wb_job) {
+		DPU_ERROR("invalid wb_conn or wb_job\n");
+		return;
+	}
+
+	drm_conn = &wb_enc->wb_conn->base;
+	state = drm_conn->state;
+
+	if (wb_enc->wb_conn && wb_enc->wb_job)
+		drm_writeback_queue_job(wb_enc->wb_conn, state);
+
+	dpu_encoder_phys_wb_setup(phys_enc);
+
+	_dpu_encoder_phys_wb_update_flush(phys_enc);
+}
+
+/**
+ * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
+ * @phys_enc:	Pointer to physical encoder
+ */
+static bool dpu_encoder_phys_wb_needs_single_flush(struct dpu_encoder_phys *phys_enc)
+{
+	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
+	return false;
+}
+
+/**
+ * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_handle_post_kickoff(
+		struct dpu_encoder_phys *phys_enc)
+{
+	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
+
+}
+
+/**
+ * dpu_encoder_phys_wb_enable - enable writeback encoder
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys *phys_enc)
+{
+	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
+	phys_enc->enable_state = DPU_ENC_ENABLED;
+}
+/**
+ * dpu_encoder_phys_wb_disable - disable writeback encoder
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
+	struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
+
+	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
+
+	if (phys_enc->enable_state == DPU_ENC_DISABLED) {
+		DPU_ERROR("encoder is already disabled\n");
+		return;
+	}
+
+	/* reset h/w before final flush */
+	if (phys_enc->hw_ctl->ops.clear_pending_flush)
+		phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
+
+	/*
+	 * New CTL reset sequence from 5.0 MDP onwards.
+	 * If has_3d_merge_reset is not set, legacy reset
+	 * sequence is executed.
+	 *
+	 * Legacy reset sequence has not been implemented yet.
+	 * Any target earlier than SM8150 will need it and when
+	 * WB support is added to those targets will need to add
+	 * the legacy teardown sequence as well.
+	 */
+	if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
+		dpu_encoder_helper_phys_cleanup(phys_enc);
+
+	phys_enc->enable_state = DPU_ENC_DISABLED;
+}
+
+/**
+ * dpu_encoder_phys_wb_destroy - destroy writeback encoder
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys *phys_enc)
+{
+	DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
+
+	if (!phys_enc)
+		return;
+
+	kfree(phys_enc);
+}
+
+static void dpu_encoder_phys_wb_prepare_wb_job(struct dpu_encoder_phys *phys_enc,
+		struct drm_writeback_job *job)
+{
+	const struct msm_format *format;
+	struct msm_gem_address_space *aspace;
+	struct dpu_hw_wb_cfg *wb_cfg;
+	int ret;
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+
+	if (!job->fb)
+		return;
+
+	wb_enc->wb_job = job;
+	wb_enc->wb_conn = job->connector;
+	aspace = phys_enc->dpu_kms->base.aspace;
+
+	wb_cfg = &wb_enc->wb_cfg;
+
+	memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
+
+	ret = msm_framebuffer_prepare(job->fb, aspace, false);
+	if (ret) {
+		DPU_ERROR("prep fb failed, %d\n", ret);
+		return;
+	}
+
+	format = msm_framebuffer_format(job->fb);
+
+	wb_cfg->dest.format = dpu_get_dpu_format_ext(
+			format->pixel_format, job->fb->modifier);
+	if (!wb_cfg->dest.format) {
+		/* this error should be detected during atomic_check */
+		DPU_ERROR("failed to get format %x\n", format->pixel_format);
+		return;
+	}
+
+	ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
+	if (ret) {
+		DPU_DEBUG("failed to populate layout %d\n", ret);
+		return;
+	}
+
+	wb_cfg->dest.width = job->fb->width;
+	wb_cfg->dest.height = job->fb->height;
+	wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
+
+	if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
+			(wb_cfg->dest.format->element[0] == C1_B_Cb))
+		swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
+
+	DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
+			wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
+			wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
+
+	DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
+			wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
+			wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
+}
+
+static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc,
+		struct drm_writeback_job *job)
+{
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+	struct msm_gem_address_space *aspace;
+
+	if (!job->fb)
+		return;
+
+	aspace = phys_enc->dpu_kms->base.aspace;
+
+	msm_framebuffer_cleanup(job->fb, aspace, false);
+	wb_enc->wb_job = NULL;
+	wb_enc->wb_conn = NULL;
+}
+
+/**
+ * dpu_encoder_phys_wb_init_ops - initialize writeback operations
+ * @ops:	Pointer to encoder operation table
+ */
+static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
+{
+	ops->is_master = dpu_encoder_phys_wb_is_master;
+	ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
+	ops->enable = dpu_encoder_phys_wb_enable;
+	ops->disable = dpu_encoder_phys_wb_disable;
+	ops->destroy = dpu_encoder_phys_wb_destroy;
+	ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
+	ops->wait_for_commit_done = dpu_encoder_phys_wb_wait_for_commit_done;
+	ops->prepare_for_kickoff = dpu_encoder_phys_wb_prepare_for_kickoff;
+	ops->handle_post_kickoff = dpu_encoder_phys_wb_handle_post_kickoff;
+	ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
+	ops->trigger_start = dpu_encoder_helper_trigger_start;
+	ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
+	ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
+	ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
+}
+
+/**
+ * dpu_encoder_phys_wb_init - initialize writeback encoder
+ * @init:	Pointer to init info structure with initialization params
+ */
+struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
+		struct dpu_enc_phys_init_params *p)
+{
+	struct dpu_encoder_phys *phys_enc = NULL;
+	struct dpu_encoder_phys_wb *wb_enc = NULL;
+	int ret = 0;
+	int i;
+
+	DPU_DEBUG("\n");
+
+	if (!p || !p->parent) {
+		DPU_ERROR("invalid params\n");
+		ret = -EINVAL;
+		goto fail_alloc;
+	}
+
+	wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
+	if (!wb_enc) {
+		DPU_ERROR("failed to allocate wb phys_enc enc\n");
+		ret = -ENOMEM;
+		goto fail_alloc;
+	}
+
+	phys_enc = &wb_enc->base;
+	phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
+	phys_enc->intf_idx = p->intf_idx;
+
+	dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
+	phys_enc->parent = p->parent;
+	phys_enc->parent_ops = p->parent_ops;
+	phys_enc->dpu_kms = p->dpu_kms;
+	phys_enc->split_role = p->split_role;
+	phys_enc->intf_mode = INTF_MODE_WB_LINE;
+	phys_enc->intf_idx = p->intf_idx;
+	phys_enc->enc_spinlock = p->enc_spinlock;
+
+	atomic_set(&wb_enc->wbirq_refcount, 0);
+
+	for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
+		phys_enc->irq[i] = -EINVAL;
+
+	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
+	atomic_set(&phys_enc->vblank_refcount, 0);
+	wb_enc->wb_done_timeout_cnt = 0;
+
+	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
+	phys_enc->enable_state = DPU_ENC_DISABLED;
+
+	DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
+			phys_enc->intf_idx);
+
+	return phys_enc;
+
+fail_alloc:
+	return ERR_PTR(ret);
+}
-- 
2.7.4


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

* [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (12 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:52   ` Dmitry Baryshkov
  2022-04-20 19:26   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback Abhinav Kumar
                   ` (2 subsequent siblings)
  16 siblings, 2 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Introduce the dpu_writeback module which serves as the
interface between dpu operations and the drm_writeback.

This module manages the connector related operations for
dpu writeback.

changes in v2:
	- start using drm_writeback_connector_init_with_encoder()
	- drop unnecessary arguments from dpu_writeback_init()
	- rebase on msm-next tip and remove usage of priv->connectors

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/Makefile                  |  1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 68 +++++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 25 ++++++++++
 3 files changed, 94 insertions(+)
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
 create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 0387f22..66395ee 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -80,6 +80,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
 	disp/dpu1/dpu_plane.o \
 	disp/dpu1/dpu_rm.o \
 	disp/dpu1/dpu_vbif.o \
+	disp/dpu1/dpu_writeback.o
 
 msm-$(CONFIG_DRM_MSM_MDSS) += \
 	msm_mdss.o \
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
new file mode 100644
index 0000000..526d884
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "dpu_writeback.h"
+
+static int dpu_wb_conn_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+
+	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+			dev->mode_config.max_height);
+}
+
+static const struct drm_connector_funcs dpu_wb_conn_funcs = {
+	.reset = drm_atomic_helper_connector_reset,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector,
+		struct drm_writeback_job *job)
+{
+	if (!job->fb)
+		return 0;
+
+	dpu_encoder_prepare_wb_job(connector->encoder, job);
+
+	return 0;
+}
+
+static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector,
+		struct drm_writeback_job *job)
+{
+	if (!job->fb)
+		return;
+
+	dpu_encoder_cleanup_wb_job(connector->encoder, job);
+}
+
+static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = {
+	.get_modes = dpu_wb_conn_get_modes,
+	.prepare_writeback_job = dpu_wb_conn_prepare_job,
+	.cleanup_writeback_job = dpu_wb_conn_cleanup_job,
+};
+
+int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
+		const u32 *format_list, u32 num_formats)
+{
+	struct dpu_wb_connector *dpu_wb_conn;
+	int rc = 0;
+
+	dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL);
+
+	drm_connector_helper_add(&dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs);
+
+	/* DPU initializes the encoder and sets it up completely for writeback
+	 * cases and hence should use the new API drm_writeback_connector_init_with_encoder
+	 * to initialize the writeback connector
+	 */
+	rc = drm_writeback_connector_init_with_encoder(dev, &dpu_wb_conn->base, enc,
+			&dpu_wb_conn_funcs, format_list, num_formats);
+
+	return rc;
+}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
new file mode 100644
index 0000000..05aff05
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _DPU_WRITEBACK_H
+#define _DPU_WRITEBACK_H
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_file.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_writeback.h>
+
+#include "msm_drv.h"
+#include "dpu_kms.h"
+#include "dpu_encoder_phys.h"
+
+struct dpu_wb_connector {
+	struct drm_writeback_connector base;
+};
+
+int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
+		const u32 *format_list, u32 num_formats);
+
+#endif /*_DPU_WRITEBACK_H */
-- 
2.7.4


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

* [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (13 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:54   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits " Abhinav Kumar
  2022-04-20  1:46 ` [PATCH v2 17/17] drm/msm/dpu: add writeback blocks to the display snapshot Abhinav Kumar
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Initialize dpu encoder and connector for writeback if the
target supports it in the catalog.

changes in v2:
	- start initialing the encoder for writeback since we
	have migrated to using drm_writeback_connector_init_with_encoder()
	- instead of checking for WB_2 inside _dpu_kms_initialize_writeback
	call it only when its WB_2
	- rebase on tip of msm-next and remove usage of priv->encoders

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 27 ++++++++++----
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     | 58 +++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index b117cad..b1475dd 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -2085,7 +2085,7 @@ static void dpu_encoder_early_unregister(struct drm_encoder *encoder)
 }
 
 static int dpu_encoder_virt_add_phys_encs(
-		u32 display_caps,
+		struct msm_display_info *disp_info,
 		struct dpu_encoder_virt *dpu_enc,
 		struct dpu_enc_phys_init_params *params)
 {
@@ -2104,7 +2104,7 @@ static int dpu_encoder_virt_add_phys_encs(
 		return -EINVAL;
 	}
 
-	if (display_caps & MSM_DISPLAY_CAP_VID_MODE) {
+	if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) {
 		enc = dpu_encoder_phys_vid_init(params);
 
 		if (IS_ERR_OR_NULL(enc)) {
@@ -2117,7 +2117,7 @@ static int dpu_encoder_virt_add_phys_encs(
 		++dpu_enc->num_phys_encs;
 	}
 
-	if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) {
+	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
 		enc = dpu_encoder_phys_cmd_init(params);
 
 		if (IS_ERR_OR_NULL(enc)) {
@@ -2130,6 +2130,19 @@ static int dpu_encoder_virt_add_phys_encs(
 		++dpu_enc->num_phys_encs;
 	}
 
+	if (disp_info->intf_type == DRM_MODE_ENCODER_VIRTUAL) {
+		enc = dpu_encoder_phys_wb_init(params);
+
+		if (IS_ERR_OR_NULL(enc)) {
+			DPU_ERROR_ENC(dpu_enc, "failed to init wb enc: %ld\n",
+					PTR_ERR(enc));
+			return enc == NULL ? -EINVAL : PTR_ERR(enc);
+		}
+
+		dpu_enc->phys_encs[dpu_enc->num_phys_encs] = enc;
+		++dpu_enc->num_phys_encs;
+	}
+
 	if (params->split_role == ENC_ROLE_SLAVE)
 		dpu_enc->cur_slave = enc;
 	else
@@ -2220,9 +2233,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 		}
 
 		if (!ret) {
-			ret = dpu_encoder_virt_add_phys_encs(disp_info->capabilities,
-												 dpu_enc,
-												 &phys_params);
+			ret = dpu_encoder_virt_add_phys_encs(disp_info,
+					dpu_enc, &phys_params);
 			if (ret)
 				DPU_ERROR_ENC(dpu_enc, "failed to add phys encs\n");
 		}
@@ -2339,8 +2351,9 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev,
 	if (!dpu_enc)
 		return ERR_PTR(-ENOMEM);
 
+
 	rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs,
-			drm_enc_mode, NULL);
+							  drm_enc_mode, NULL);
 	if (rc) {
 		devm_kfree(dev->dev, dpu_enc);
 		return ERR_PTR(rc);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index c683cab..0a50509 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
@@ -15,6 +16,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_file.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_writeback.h>
 
 #include "msm_drv.h"
 #include "msm_mmu.h"
@@ -29,6 +31,7 @@
 #include "dpu_kms.h"
 #include "dpu_plane.h"
 #include "dpu_vbif.h"
+#include "dpu_writeback.h"
 
 #define CREATE_TRACE_POINTS
 #include "dpu_trace.h"
@@ -648,6 +651,45 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
 	return 0;
 }
 
+static int _dpu_kms_initialize_writeback(struct drm_device *dev,
+		struct msm_drm_private *priv, struct dpu_kms *dpu_kms,
+		const u32 *wb_formats, int n_formats)
+{
+	struct drm_encoder *encoder = NULL;
+	struct msm_display_info info;
+	int rc;
+
+	encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_VIRTUAL);
+	if (IS_ERR(encoder)) {
+		DPU_ERROR("encoder init failed for dsi display\n");
+		return PTR_ERR(encoder);
+	}
+
+	memset(&info, 0, sizeof(info));
+
+	rc = dpu_writeback_init(dev, encoder, wb_formats,
+			n_formats);
+	if (rc) {
+		DPU_ERROR("dpu_writeback_init, rc = %d\n", rc);
+		drm_encoder_cleanup(encoder);
+		return rc;
+	}
+
+	info.num_of_h_tiles = 1;
+	/* use only WB idx 2 instance for DPU */
+	info.h_tile_instance[0] = WB_2;
+	info.intf_type = encoder->encoder_type;
+
+	rc = dpu_encoder_setup(dev, encoder, &info);
+	if (rc) {
+		DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
+				  encoder->base.id, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
 /**
  * _dpu_kms_setup_displays - create encoders, bridges and connectors
  *                           for underlying displays
@@ -661,6 +703,7 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
 				    struct dpu_kms *dpu_kms)
 {
 	int rc = 0;
+	int i;
 
 	rc = _dpu_kms_initialize_dsi(dev, priv, dpu_kms);
 	if (rc) {
@@ -674,6 +717,21 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
 		return rc;
 	}
 
+	/* Since WB isn't a driver check the catalog before initializing */
+	if (dpu_kms->catalog->wb_count) {
+		for (i = 0; i < dpu_kms->catalog->wb_count; i++) {
+			if (dpu_kms->catalog->wb[i].id == WB_2) {
+				rc = _dpu_kms_initialize_writeback(dev, priv, dpu_kms,
+						dpu_kms->catalog->wb[i].format_list,
+						dpu_kms->catalog->wb[i].num_formats);
+				if (rc) {
+					DPU_ERROR("initialize_WB failed, rc = %d\n", rc);
+					return rc;
+				}
+			}
+		}
+	}
+
 	return rc;
 }
 
-- 
2.7.4


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

* [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits for writeback
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (14 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  2022-04-20  7:55   ` Dmitry Baryshkov
  2022-04-20  1:46 ` [PATCH v2 17/17] drm/msm/dpu: add writeback blocks to the display snapshot Abhinav Kumar
  16 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

kms_writeback test cases also verify with a null fb for the
writeback connector job. In addition there are also other
commit paths which can result in kickoffs without a valid
framebuffer like while closing the fb which results in the
callback to drm_atomic_helper_dirtyfb() which internally
triggers a commit.

Add protection in the dpu driver to ensure that commits for
writeback encoders without a valid fb are gracefully skipped.

changes in v2:
	- rename dpu_encoder_has_valid_fb to dpu_encoder_is_valid_for_commit

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c            |  9 +++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c         | 21 +++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h         |  6 ++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h    |  1 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 12 ++++++++++++
 5 files changed, 49 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index 7763558..d65e124 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -869,6 +869,13 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
 
 	DPU_ATRACE_BEGIN("crtc_commit");
 
+	drm_for_each_encoder_mask(encoder, crtc->dev,
+			crtc->state->encoder_mask) {
+		if (!dpu_encoder_is_valid_for_commit(encoder)) {
+			DRM_DEBUG_ATOMIC("invalid FB not kicking off crtc\n");
+			goto end;
+		}
+	}
 	/*
 	 * Encoder will flush/start now, unless it has a tx pending. If so, it
 	 * may delay and flush at an irq event (e.g. ppdone)
@@ -891,6 +898,8 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
 		dpu_encoder_kickoff(encoder);
 
 	reinit_completion(&dpu_crtc->frame_done_comp);
+
+end:
 	DPU_ATRACE_END("crtc_commit");
 }
 
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index b1475dd..d07e3ee 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1850,6 +1850,27 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
 		dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
 }
 
+bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
+{
+	struct dpu_encoder_virt *dpu_enc;
+	unsigned int i;
+	struct dpu_encoder_phys *phys;
+
+	dpu_enc = to_dpu_encoder_virt(drm_enc);
+
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
+		for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+			phys = dpu_enc->phys_encs[i];
+			if (phys->ops.is_valid_for_commit && !phys->ops.is_valid_for_commit(phys)) {
+				DPU_DEBUG("invalid FB not kicking off\n");
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
 void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
 {
 	struct dpu_encoder_virt *dpu_enc;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index 6ceec1d..781d41c 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -196,4 +196,10 @@ void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
 void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
 		struct drm_writeback_job *job);
 
+/**
+ * dpu_encoder_is_valid_for_commit - check if encode has valid parameters for commit.
+ * @drm_enc:    Pointer to drm encoder structure
+ */
+bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc);
+
 #endif /* __DPU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 5452f98..04d037e 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -142,6 +142,7 @@ struct dpu_encoder_phys_ops {
 			struct drm_writeback_job *job);
 	void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc,
 			struct drm_writeback_job *job);
+	bool (*is_valid_for_commit)(struct dpu_encoder_phys *phys_enc);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
index 128317fe..9acbce0 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
@@ -667,6 +667,16 @@ static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc
 	wb_enc->wb_conn = NULL;
 }
 
+static bool dpu_encoder_phys_wb_is_valid_for_commit(struct dpu_encoder_phys *phys_enc)
+{
+	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
+
+	if (wb_enc->wb_job)
+		return true;
+	else
+		return false;
+}
+
 /**
  * dpu_encoder_phys_wb_init_ops - initialize writeback operations
  * @ops:	Pointer to encoder operation table
@@ -687,6 +697,8 @@ static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
 	ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
 	ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
 	ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
+	ops->is_valid_for_commit = dpu_encoder_phys_wb_is_valid_for_commit;
+
 }
 
 /**
-- 
2.7.4


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

* [PATCH v2 17/17] drm/msm/dpu: add writeback blocks to the display snapshot
  2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
                   ` (15 preceding siblings ...)
  2022-04-20  1:46 ` [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits " Abhinav Kumar
@ 2022-04-20  1:46 ` Abhinav Kumar
  16 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20  1:46 UTC (permalink / raw)
  To: freedreno
  Cc: markyacoub, liviu.dudau, Abhinav Kumar, dri-devel, swboyd,
	seanpaul, laurent.pinchart, dmitry.baryshkov, quic_jesszhan,
	quic_aravindh

Add writeback block information while capturing the display
snapshot.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 0a50509..86c98db 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -944,6 +944,11 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
 		msm_disp_snapshot_add_block(disp_state, cat->mixer[i].len,
 				dpu_kms->mmio + cat->mixer[i].base, "lm_%d", i);
 
+	/* dump WB sub-blocks HW regs info */
+	for (i = 0; i < cat->wb_count; i++)
+		msm_disp_snapshot_add_block(disp_state, cat->wb[i].len,
+				dpu_kms->mmio + cat->wb[i].base, "wb_%d", i);
+
 	msm_disp_snapshot_add_block(disp_state, top->hw.length,
 			dpu_kms->mmio + top->hw.blk_off, "top");
 
-- 
2.7.4


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

* Re: [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM
  2022-04-20  1:45 ` [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM Abhinav Kumar
@ 2022-04-20  6:47   ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  6:47 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:45, Abhinav Kumar wrote:
> Add writeback blocks to DPU resource manager so that
> the encoders can directly request them through RM.
> 
> changes in v2:
> 	- stop global tracking of WB blocks similar to INTF
> 	- align usage of hw_wb to be similar to that of hw_intf
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 22 ++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 12 ++++++++++++
>   2 files changed, 34 insertions(+)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> index 0e6634b..bb01d31 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> @@ -9,6 +9,7 @@
>   #include "dpu_hw_ctl.h"
>   #include "dpu_hw_pingpong.h"
>   #include "dpu_hw_intf.h"
> +#include "dpu_hw_wb.h"
>   #include "dpu_hw_dspp.h"
>   #include "dpu_hw_merge3d.h"
>   #include "dpu_hw_dsc.h"
> @@ -87,6 +88,9 @@ int dpu_rm_destroy(struct dpu_rm *rm)
>   		}
>   	}
>   
> +	for (i = 0; i < ARRAY_SIZE(rm->hw_wb); i++)
> +		dpu_hw_wb_destroy(rm->hw_wb[i]);
> +
>   	return 0;
>   }
>   
> @@ -186,6 +190,24 @@ int dpu_rm_init(struct dpu_rm *rm,
>   		rm->hw_intf[intf->id - INTF_0] = hw;
>   	}
>   
> +	for (i = 0; i < cat->wb_count; i++) {
> +		struct dpu_hw_wb *hw;
> +		const struct dpu_wb_cfg *wb = &cat->wb[i];
> +
> +		if (wb->id < WB_0 || wb->id >= WB_MAX) {
> +			DPU_ERROR("skip intf %d with invalid id\n", wb->id);
> +			continue;
> +		}
> +
> +		hw = dpu_hw_wb_init(wb->id, mmio, cat);
> +		if (IS_ERR_OR_NULL(hw)) {

Just IS_ERR() please.

> +			rc = PTR_ERR(hw);
> +			DPU_ERROR("failed wb object creation: err %d\n", rc);
> +			goto fail;
> +		}
> +		rm->hw_wb[wb->id - WB_0] = hw;
> +	}
> +
>   	for (i = 0; i < cat->ctl_count; i++) {
>   		struct dpu_hw_ctl *hw;
>   		const struct dpu_ctl_cfg *ctl = &cat->ctl[i];
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
> index 32e0d8a..ba82e54 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h
> @@ -19,6 +19,7 @@ struct dpu_global_state;
>    * @mixer_blks: array of layer mixer hardware resources
>    * @ctl_blks: array of ctl hardware resources
>    * @hw_intf: array of intf hardware resources
> + * @hw_wb: array of wb hardware resources
>    * @dspp_blks: array of dspp hardware resources
>    */
>   struct dpu_rm {
> @@ -26,6 +27,7 @@ struct dpu_rm {
>   	struct dpu_hw_blk *mixer_blks[LM_MAX - LM_0];
>   	struct dpu_hw_blk *ctl_blks[CTL_MAX - CTL_0];
>   	struct dpu_hw_intf *hw_intf[INTF_MAX - INTF_0];
> +	struct dpu_hw_wb *hw_wb[WB_MAX - WB_0];
>   	struct dpu_hw_blk *dspp_blks[DSPP_MAX - DSPP_0];
>   	struct dpu_hw_blk *merge_3d_blks[MERGE_3D_MAX - MERGE_3D_0];
>   	struct dpu_hw_blk *dsc_blks[DSC_MAX - DSC_0];
> @@ -96,5 +98,15 @@ static inline struct dpu_hw_intf *dpu_rm_get_intf(struct dpu_rm *rm, enum dpu_in
>   	return rm->hw_intf[intf_idx - INTF_0];
>   }
>   
> +/**
> + * dpu_rm_get_wb - Return a struct dpu_hw_wb instance given it's index.
> + * @rm: DPU Resource Manager handle
> + * @wb_idx: WB index
> + */
> +static inline struct dpu_hw_wb *dpu_rm_get_wb(struct dpu_rm *rm, enum dpu_intf wb_idx)
> +{
> +	return rm->hw_wb[wb_idx - WB_0];
> +}
> +
>   #endif /* __DPU_RM_H__ */
>   


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl
  2022-04-20  1:46 ` [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl Abhinav Kumar
@ 2022-04-20  6:59   ` Dmitry Baryshkov
  2022-04-20 17:16     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  6:59 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Add changes to support writeback module in the dpu_hw_ctl
> interface.
> 
> changes in v2:
> 	- keep only the wb specific changes to reset_intf_cfg
> 	- use cfg->intf / cfg->wb to identify intf or wb
> 	- use bit-wise OR for the wb bits while programming
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 +++++++++++++++++++++++++++---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 15 ++++++++++-
>   2 files changed, 53 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> index 524f024..495a9cd 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> @@ -1,5 +1,6 @@
>   // SPDX-License-Identifier: GPL-2.0-only
> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>    */
>   
>   #include <linux/delay.h>
> @@ -23,10 +24,12 @@
>   #define   CTL_SW_RESET                  0x030
>   #define   CTL_LAYER_EXTN_OFFSET         0x40
>   #define   CTL_MERGE_3D_ACTIVE           0x0E4
> +#define   CTL_WB_ACTIVE                 0x0EC
>   #define   CTL_INTF_ACTIVE               0x0F4
>   #define   CTL_MERGE_3D_FLUSH            0x100
>   #define   CTL_DSC_ACTIVE                0x0E8
>   #define   CTL_DSC_FLUSH                0x104
> +#define   CTL_WB_FLUSH                  0x108
>   #define   CTL_INTF_FLUSH                0x110
>   #define   CTL_INTF_MASTER               0x134
>   #define   CTL_FETCH_PIPE_ACTIVE         0x0FC
> @@ -38,6 +41,7 @@
>   #define  MERGE_3D_IDX   23
>   #define  DSC_IDX        22
>   #define  INTF_IDX       31
> +#define WB_IDX          16
>   #define CTL_INVALID_BIT                 0xffff
>   #define CTL_DEFAULT_GROUP_ID		0xf
>   
> @@ -135,6 +139,9 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
>   	if (ctx->pending_flush_mask & BIT(INTF_IDX))
>   		DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH,
>   				ctx->pending_intf_flush_mask);
> +	if (ctx->pending_flush_mask & BIT(WB_IDX))
> +		DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
> +				ctx->pending_wb_flush_mask);
>   
>   	DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
>   }
> @@ -255,6 +262,13 @@ static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx,
>   	}
>   }
>   
> +static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx,
> +		enum dpu_wb wb)
> +{
> +	ctx->pending_wb_flush_mask |= BIT(wb - WB_0);
> +	ctx->pending_flush_mask |= BIT(WB_IDX);
> +}
> +
>   static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx,
>   		enum dpu_intf intf)
>   {
> @@ -504,6 +518,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
>   {
>   	struct dpu_hw_blk_reg_map *c = &ctx->hw;
>   	u32 intf_active = 0;
> +	u32 wb_active = 0;
>   	u32 mode_sel = 0;
>   
>   	/* CTL_TOP[31:28] carries group_id to collate CTL paths
> @@ -519,11 +534,20 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx,
>   	if (cfg->intf_mode_sel == DPU_CTL_MODE_SEL_CMD)
>   		mode_sel |= BIT(17);
>   
> -	intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
> -	intf_active |= BIT(cfg->intf - INTF_0);
> +	if (cfg->intf) {
> +		intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
> +		intf_active |= BIT(cfg->intf - INTF_0);
> +	}
> +
> +	if (cfg->wb) {
> +		wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
> +		wb_active |= BIT(cfg->wb - WB_0);
> +	}
>   
>   	DPU_REG_WRITE(c, CTL_TOP, mode_sel);
>   	DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
> +	DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);

This will not work as expected. If cfg->intf is not set, CTL_INTF_ACTIVE 
will be reset to 0 (while it should have been retained). Please change 
this to always read CTL_INTF_ACTIVE/CTL_WB_ACTIVE.

> +
>   	if (cfg->merge_3d)
>   		DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
>   			      BIT(cfg->merge_3d - MERGE_3D_0));
> @@ -546,6 +570,9 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
>   		intf_cfg |= (cfg->mode_3d - 0x1) << 20;
>   	}
>   
> +	if (cfg->wb)
> +		intf_cfg |= (cfg->wb & 0x3) + 2;
> +

Ugh. I see that we have the same code in downstream driver. And that we 
do not support WB_0 at all. But maybe we should be more explicit here.

>   	switch (cfg->intf_mode_sel) {
>   	case DPU_CTL_MODE_SEL_VID:
>   		intf_cfg &= ~BIT(17);
> @@ -568,12 +595,13 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
>   {
>   	struct dpu_hw_blk_reg_map *c = &ctx->hw;
>   	u32 intf_active = 0;
> +	u32 wb_active = 0;
>   	u32 merge3d_active = 0;
>   
>   	/*
>   	 * This API resets each portion of the CTL path namely,
>   	 * clearing the sspps staged on the lm, merge_3d block,
> -	 * interfaces etc to ensure clean teardown of the pipeline.
> +	 * interfaces , writeback etc to ensure clean teardown of the pipeline.
>   	 * This will be used for writeback to begin with to have a
>   	 * proper teardown of the writeback session but upon further
>   	 * validation, this can be extended to all interfaces.
> @@ -592,6 +620,12 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
>   		intf_active &= ~BIT(cfg->intf - INTF_0);
>   		DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
>   	}
> +
> +	if (cfg->wb) {
> +		wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
> +		wb_active &= ~BIT(cfg->wb - WB_0);
> +		DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
> +	}
>   }
>   
>   static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
> @@ -622,6 +656,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
>   			dpu_hw_ctl_update_pending_flush_intf_v1;
>   		ops->update_pending_flush_merge_3d =
>   			dpu_hw_ctl_update_pending_flush_merge_3d_v1;
> +		ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1;

Do we also need the update_pending_flush_wb for non-ACTIVE_CTL case? I 
think we do.

>   	} else {
>   		ops->trigger_flush = dpu_hw_ctl_trigger_flush;
>   		ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> index c61a8fd..df8f8e9 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> @@ -1,5 +1,6 @@
>   /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>    */
>   
>   #ifndef _DPU_HW_CTL_H
> @@ -44,6 +45,7 @@ struct dpu_hw_stage_cfg {
>    */
>   struct dpu_hw_intf_cfg {
>   	enum dpu_intf intf;
> +	enum dpu_wb wb;
>   	enum dpu_3d_blend_mode mode_3d;
>   	enum dpu_merge_3d merge_3d;
>   	enum dpu_ctl_mode_sel intf_mode_sel;
> @@ -102,6 +104,15 @@ struct dpu_hw_ctl_ops {
>   		u32 flushbits);
>   
>   	/**
> +	 * OR in the given flushbits to the cached pending_(wb_)flush_mask
> +	 * No effect on hardware
> +	 * @ctx       : ctl path ctx pointer
> +	 * @blk       : writeback block index
> +	 */
> +	void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx,
> +		enum dpu_wb blk);
> +
> +	/**
>   	 * OR in the given flushbits to the cached pending_(intf_)flush_mask
>   	 * No effect on hardware
>   	 * @ctx       : ctl path ctx pointer
> @@ -199,6 +210,7 @@ struct dpu_hw_ctl_ops {
>    * @mixer_hw_caps: mixer hardware capabilities
>    * @pending_flush_mask: storage for pending ctl_flush managed via ops
>    * @pending_intf_flush_mask: pending INTF flush
> + * @pending_wb_flush_mask: pending WB flush
>    * @ops: operation list
>    */
>   struct dpu_hw_ctl {
> @@ -212,6 +224,7 @@ struct dpu_hw_ctl {
>   	const struct dpu_lm_cfg *mixer_hw_caps;
>   	u32 pending_flush_mask;
>   	u32 pending_intf_flush_mask;
> +	u32 pending_wb_flush_mask;
>   	u32 pending_merge_3d_flush_mask;
>   
>   	/* ops */


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl
  2022-04-20  1:45 ` [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl Abhinav Kumar
@ 2022-04-20  7:02   ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:02 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:45, Abhinav Kumar wrote:
> Add a reset_intf_cfg operation for dpu_hw_ctl to reset the
> entire CTL path by disabling each component namely layer mixer,
> 3d-merge and interface blocks.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 32 ++++++++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h |  8 ++++++++
>   2 files changed, 40 insertions(+)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> index dc27579..524f024 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> @@ -563,6 +563,37 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl *ctx,
>   	DPU_REG_WRITE(c, CTL_TOP, intf_cfg);
>   }
>   
> +static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx,
> +		struct dpu_hw_intf_cfg *cfg)
> +{
> +	struct dpu_hw_blk_reg_map *c = &ctx->hw;
> +	u32 intf_active = 0;
> +	u32 merge3d_active = 0;
> +
> +	/*
> +	 * This API resets each portion of the CTL path namely,
> +	 * clearing the sspps staged on the lm, merge_3d block,
> +	 * interfaces etc to ensure clean teardown of the pipeline.
> +	 * This will be used for writeback to begin with to have a
> +	 * proper teardown of the writeback session but upon further
> +	 * validation, this can be extended to all interfaces.
> +	 */
> +	if (cfg->merge_3d) {
> +		merge3d_active = DPU_REG_READ(c, CTL_MERGE_3D_ACTIVE);
> +		merge3d_active &= ~BIT(cfg->merge_3d - MERGE_3D_0);
> +		DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
> +				merge3d_active);
> +	}
> +
> +	dpu_hw_ctl_clear_all_blendstages(ctx);
> +
> +	if (cfg->intf) {
> +		intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
> +		intf_active &= ~BIT(cfg->intf - INTF_0);
> +		DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
> +	}
> +}
> +
>   static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
>   	unsigned long *fetch_active)
>   {
> @@ -586,6 +617,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops,
>   	if (cap & BIT(DPU_CTL_ACTIVE_CFG)) {
>   		ops->trigger_flush = dpu_hw_ctl_trigger_flush_v1;
>   		ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg_v1;
> +		ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
>   		ops->update_pending_flush_intf =
>   			dpu_hw_ctl_update_pending_flush_intf_v1;
>   		ops->update_pending_flush_merge_3d =
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> index 97f326d..c61a8fd 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> @@ -140,6 +140,14 @@ struct dpu_hw_ctl_ops {
>   	void (*setup_intf_cfg)(struct dpu_hw_ctl *ctx,
>   		struct dpu_hw_intf_cfg *cfg);
>   
> +	/**
> +	 * reset ctl_path interface config
> +	 * @ctx    : ctl path ctx pointer
> +	 * @cfg    : interface config structure pointer
> +	 */
> +	void (*reset_intf_cfg)(struct dpu_hw_ctl *ctx,
> +			struct dpu_hw_intf_cfg *cfg);
> +
>   	int (*reset)(struct dpu_hw_ctl *c);
>   
>   	/*


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20  1:45 ` [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks Abhinav Kumar
@ 2022-04-20  7:20   ` Dmitry Baryshkov
  2022-04-20 17:01     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:20 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:45, Abhinav Kumar wrote:
> Add the dpu_hw_wb abstraction to program registers related to the
> writeback block. These will be invoked once all the configuration
> is set and ready to be programmed to the registers.
> 
> changes in v2:
> 	- remove multiple empty lines at the end of the file
> 	- change dpu_hw_wb_bind_pingpong_blk to preserve upper bits
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

It's still Reviewed-by, few nits below.

> ---
>   drivers/gpu/drm/msm/Makefile              |   1 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273 ++++++++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
>   3 files changed, 405 insertions(+)
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index d5ca2e6..ca779c1 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>   	disp/dpu1/dpu_hw_top.o \
>   	disp/dpu1/dpu_hw_util.o \
>   	disp/dpu1/dpu_hw_vbif.o \
> +	disp/dpu1/dpu_hw_wb.o \
>   	disp/dpu1/dpu_kms.o \
>   	disp/dpu1/dpu_plane.o \
>   	disp/dpu1/dpu_rm.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> new file mode 100644
> index 0000000..afa8aab
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> @@ -0,0 +1,273 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> + /*
> +  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
> +  */
> +
> +#include "dpu_hw_mdss.h"
> +#include "dpu_hwio.h"
> +#include "dpu_hw_catalog.h"
> +#include "dpu_hw_wb.h"
> +#include "dpu_formats.h"
> +#include "dpu_kms.h"
> +
> +#define WB_DST_FORMAT                         0x000
> +#define WB_DST_OP_MODE                        0x004
> +#define WB_DST_PACK_PATTERN                   0x008
> +#define WB_DST0_ADDR                          0x00C
> +#define WB_DST1_ADDR                          0x010
> +#define WB_DST2_ADDR                          0x014
> +#define WB_DST3_ADDR                          0x018
> +#define WB_DST_YSTRIDE0                       0x01C
> +#define WB_DST_YSTRIDE1                       0x020
> +#define WB_DST_YSTRIDE1                       0x020
> +#define WB_DST_DITHER_BITDEPTH                0x024
> +#define WB_DST_MATRIX_ROW0                    0x030
> +#define WB_DST_MATRIX_ROW1                    0x034
> +#define WB_DST_MATRIX_ROW2                    0x038
> +#define WB_DST_MATRIX_ROW3                    0x03C
> +#define WB_DST_WRITE_CONFIG                   0x048
> +#define WB_ROTATION_DNSCALER                  0x050
> +#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
> +#define WB_N16_INIT_PHASE_X_C03               0x060
> +#define WB_N16_INIT_PHASE_X_C12               0x064
> +#define WB_N16_INIT_PHASE_Y_C03               0x068
> +#define WB_N16_INIT_PHASE_Y_C12               0x06C
> +#define WB_OUT_SIZE                           0x074
> +#define WB_ALPHA_X_VALUE                      0x078
> +#define WB_DANGER_LUT                         0x084
> +#define WB_SAFE_LUT                           0x088
> +#define WB_QOS_CTRL                           0x090
> +#define WB_CREQ_LUT_0                         0x098
> +#define WB_CREQ_LUT_1                         0x09C
> +#define WB_UBWC_STATIC_CTRL                   0x144
> +#define WB_MUX                                0x150
> +#define WB_CROP_CTRL                          0x154
> +#define WB_CROP_OFFSET                        0x158
> +#define WB_CSC_BASE                           0x260
> +#define WB_DST_ADDR_SW_STATUS                 0x2B0
> +#define WB_CDP_CNTL                           0x2B4
> +#define WB_OUT_IMAGE_SIZE                     0x2C0
> +#define WB_OUT_XY                             0x2C4
> +
> +/* WB_QOS_CTRL */
> +#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
> +
> +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
> +		const struct dpu_mdss_cfg *m, void __iomem *addr,
> +		struct dpu_hw_blk_reg_map *b)
> +{
> +	int i;
> +
> +	for (i = 0; i < m->wb_count; i++) {
> +		if (wb == m->wb[i].id) {
> +			b->base_off = addr;
> +			b->blk_off = m->wb[i].base;
> +			b->length = m->wb[i].len;
> +			b->hwversion = m->hwversion;
> +			return &m->wb[i];
> +		}
> +	}
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
> +		struct dpu_hw_wb_cfg *data)
> +{
> +	struct dpu_hw_blk_reg_map *c = &ctx->hw;
> +
> +	DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
> +	DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
> +	DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
> +	DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
> +}
> +
> +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
> +		struct dpu_hw_wb_cfg *data)
> +{

This function shares significant logic with dpu_hw_sspp_setup_format().

We should consider splitting the common code to the helper at some point 
(later).

> +	struct dpu_hw_blk_reg_map *c = &ctx->hw;
> +	const struct dpu_format *fmt = data->dest.format;
> +	u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
> +	u32 write_config = 0;
> +	u32 opmode = 0;
> +	u32 dst_addr_sw = 0;
> +
> +	chroma_samp = fmt->chroma_sample;
> +
> +	dst_format = (chroma_samp << 23) |
> +		(fmt->fetch_planes << 19) |
> +		(fmt->bits[C3_ALPHA] << 6) |
> +		(fmt->bits[C2_R_Cr] << 4) |
> +		(fmt->bits[C1_B_Cb] << 2) |
> +		(fmt->bits[C0_G_Y] << 0);
> +
> +	if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
> +		dst_format |= BIT(8); /* DSTC3_EN */
> +		if (!fmt->alpha_enable ||
> +			!(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
> +			dst_format |= BIT(14); /* DST_ALPHA_X */
> +	}
> +
> +	pattern = (fmt->element[3] << 24) |
> +		(fmt->element[2] << 16) |
> +		(fmt->element[1] << 8)  |
> +		(fmt->element[0] << 0);
> +
> +	dst_format |= (fmt->unpack_align_msb << 18) |
> +		(fmt->unpack_tight << 17) |
> +		((fmt->unpack_count - 1) << 12) |
> +		((fmt->bpp - 1) << 9);
> +
> +	ystride0 = data->dest.plane_pitch[0] |
> +		(data->dest.plane_pitch[1] << 16);
> +	ystride1 = data->dest.plane_pitch[2] |
> +	(data->dest.plane_pitch[3] << 16);
> +
> +	if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
> +		outsize = (drm_rect_height(&data->roi) << 16) | drm_rect_width(&data->roi);
> +	else
> +		outsize = (data->dest.height << 16) | data->dest.width;
> +
> +	DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
> +	DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
> +	DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
> +	DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
> +	DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
> +	DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
> +	DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
> +	DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
> +	DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
> +}
> +
> +static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg *wb)
> +{
> +	struct dpu_hw_blk_reg_map *c = &ctx->hw;
> +	u32 image_size, out_size, out_xy;
> +
> +	image_size = (wb->dest.height << 16) | wb->dest.width;
> +	out_xy = 0;
> +	out_size = (drm_rect_height(&wb->roi) << 16) | drm_rect_width(&wb->roi);
> +
> +	DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
> +	DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
> +	DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
> +}
> +
> +static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
> +		struct dpu_hw_wb_qos_cfg *cfg)
I like the single call approach. Maybe we should adopt it for the SSPP 
QoS LUT too.

> +{
> +	struct dpu_hw_blk_reg_map *c = &ctx->hw;
> +	u32 qos_ctrl = 0;
> +
> +	if (!ctx || !cfg)
> +		return;
> +
> +	DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
> +	DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
> +
> +	if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
> +		DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
> +		DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
> +	}

Is there a plain WB_CREQ_LUT for the non-8LVL case?

> +
> +	if (cfg->danger_safe_en)
> +		qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
> +
> +	DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
> +}
> +
> +static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
> +		struct dpu_hw_wb_cdp_cfg *cfg)

Can we use dpu_hw_pipe_cdp_cfg here? Maybe after renaming it to more 
generic dpu_hw_cdp_cfg.

> +{
> +	struct dpu_hw_blk_reg_map *c;
> +	u32 cdp_cntl = 0;
> +
> +	if (!ctx || !cfg)
> +		return;
> +
> +	c = &ctx->hw;
> +
> +	if (cfg->enable)
> +		cdp_cntl |= BIT(0);
> +	if (cfg->ubwc_meta_enable)
> +		cdp_cntl |= BIT(1);
> +	if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
> +		cdp_cntl |= BIT(3);
> +
> +	DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
> +}
> +
> +static void dpu_hw_wb_bind_pingpong_blk(
> +		struct dpu_hw_wb *ctx,
> +		bool enable, const enum dpu_pingpong pp)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +	int mux_cfg;
> +
> +	if (!ctx)
> +		return;
> +
> +	c = &ctx->hw;
> +
> +	mux_cfg = DPU_REG_READ(c, WB_MUX);
> +	mux_cfg &= ~0xf;
> +
> +	if (enable)
> +		mux_cfg |= (pp - PINGPONG_0) & 0x7;
> +	else
> +		mux_cfg |= 0xf;
> +
> +	DPU_REG_WRITE(c, WB_MUX, mux_cfg);
> +}
> +
> +static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
> +		unsigned long features)
> +{
> +	ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
> +	ops->setup_outformat = dpu_hw_wb_setup_format;
> +
> +	if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
> +		ops->setup_roi = dpu_hw_wb_roi;
> +
> +	if (test_bit(DPU_WB_QOS, &features))
> +		ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
> +
> +	if (test_bit(DPU_WB_CDP, &features))
> +		ops->setup_cdp = dpu_hw_wb_setup_cdp;
> +
> +	if (test_bit(DPU_WB_INPUT_CTRL, &features))
> +		ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
> +}
> +
> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> +		void __iomem *addr, const struct dpu_mdss_cfg *m)
> +{
> +	struct dpu_hw_wb *c;
> +	const struct dpu_wb_cfg *cfg;
> +
> +	if (!addr || !m)
> +		return ERR_PTR(-EINVAL);
> +
> +	c = kzalloc(sizeof(*c), GFP_KERNEL);
> +	if (!c)
> +		return ERR_PTR(-ENOMEM);
> +
> +	cfg = _wb_offset(idx, m, addr, &c->hw);
> +	if (IS_ERR(cfg)) {
> +		WARN(1, "Unable to find wb idx=%d\n", idx);
> +		kfree(c);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	/* Assign ops */
> +	c->mdp = &m->mdp[0];
> +	c->idx = idx;
> +	c->caps = cfg;
> +	_setup_wb_ops(&c->ops, c->caps->features);
> +
> +	return c;
> +}
> +
> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
> +{
> +	kfree(hw_wb);
> +}
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> new file mode 100644
> index 0000000..80def96
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
> + */
> +
> +#ifndef _DPU_HW_WB_H
> +#define _DPU_HW_WB_H
> +
> +#include "dpu_hw_catalog.h"
> +#include "dpu_hw_mdss.h"
> +#include "dpu_hw_top.h"
> +#include "dpu_hw_util.h"
> +#include "dpu_hw_pingpong.h"
> +
> +struct dpu_hw_wb;
> +
> +struct dpu_hw_wb_cfg {
> +	struct dpu_hw_fmt_layout dest;
> +	enum dpu_intf_mode intf_mode;
> +	struct drm_rect roi;
> +	struct drm_rect crop;
> +};
> +
> +/**
> + * enum CDP preload ahead address size
> + */
> +enum {
> +	DPU_WB_CDP_PRELOAD_AHEAD_32,
> +	DPU_WB_CDP_PRELOAD_AHEAD_64
> +};
> +
> +/**
> + * struct dpu_hw_wb_cdp_cfg : CDP configuration
> + * @enable: true to enable CDP
> + * @ubwc_meta_enable: true to enable ubwc metadata preload
> + * @tile_amortize_enable: true to enable amortization control for tile format
> + * @preload_ahead: number of request to preload ahead
> + * SDE_WB_CDP_PRELOAD_AHEAD_32,
> + * SDE_WB_CDP_PRELOAD_AHEAD_64
> + */
> +struct dpu_hw_wb_cdp_cfg {
> +	bool enable;
> +	bool ubwc_meta_enable;
> +	bool tile_amortize_enable;
> +	u32 preload_ahead;
> +};
> +
> +/**
> + * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
> + * @danger_lut: LUT for generate danger level based on fill level
> + * @safe_lut: LUT for generate safe level based on fill level
> + * @creq_lut: LUT for generate creq level based on fill level
> + * @danger_safe_en: enable danger safe generation
> + */
> +struct dpu_hw_wb_qos_cfg {
> +	u32 danger_lut;
> +	u32 safe_lut;
> +	u64 creq_lut;
> +	bool danger_safe_en;
> +};
> +
> +/**
> + *
> + * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
> + *  Assumption is these functions will be called after clocks are enabled
> + *  @setup_outaddress: setup output address from the writeback job
> + *  @setup_outformat: setup output format of writeback block from writeback job
> + *  @setup_qos_lut:   setup qos LUT for writeback block based on input
> + *  @setup_cdp:       setup chroma down prefetch block for writeback block
> + *  @bind_pingpong_blk: enable/disable the connection with ping-pong block
> + */
> +struct dpu_hw_wb_ops {
> +	void (*setup_outaddress)(struct dpu_hw_wb *ctx,
> +			struct dpu_hw_wb_cfg *wb);
> +
> +	void (*setup_outformat)(struct dpu_hw_wb *ctx,
> +			struct dpu_hw_wb_cfg *wb);
> +
> +	void (*setup_roi)(struct dpu_hw_wb *ctx,
> +			struct dpu_hw_wb_cfg *wb);
> +
> +	void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
> +			struct dpu_hw_wb_qos_cfg *cfg);
> +
> +	void (*setup_cdp)(struct dpu_hw_wb *ctx,
> +			struct dpu_hw_wb_cdp_cfg *cfg);
> +
> +	void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
> +			bool enable, const enum dpu_pingpong pp);
> +};
> +
> +/**
> + * struct dpu_hw_wb : WB driver object
> + * @hw: block hardware details
> + * @mdp: pointer to associated mdp portion of the catalog
> + * @idx: hardware index number within type
> + * @wb_hw_caps: hardware capabilities
> + * @ops: function pointers
> + * @hw_mdp: MDP top level hardware block
> + */
> +struct dpu_hw_wb {
> +	struct dpu_hw_blk_reg_map hw;
> +	const struct dpu_mdp_cfg *mdp;
> +
> +	/* wb path */
> +	int idx;
> +	const struct dpu_wb_cfg *caps;
> +
> +	/* ops */
> +	struct dpu_hw_wb_ops ops;
> +
> +	struct dpu_hw_mdp *hw_mdp;
> +};
> +
> +/**
> + * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
> + * @idx:  wb_path index for which driver object is required
> + * @addr: mapped register io address of MDP
> + * @m :   pointer to mdss catalog data
> + */
> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> +		void __iomem *addr,
> +		const struct dpu_mdss_cfg *m);
> +
> +/**
> + * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
> + * @hw_wb:  Pointer to writeback hw driver object
> + */
> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
> +
> +#endif /*_DPU_HW_WB_H */


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks
  2022-04-20  1:46 ` [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks Abhinav Kumar
@ 2022-04-20  7:23   ` Dmitry Baryshkov
  2022-04-20 21:28     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:23 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Add an API to reset the encoder related hw blocks to ensure
> a proper teardown of the pipeline. At the moment this is being
> used only for the writeback encoder but eventually we can start
> using this for all interfaces.
> 
> changes in v2:
> 	- split the writeback part to another commit
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

A minor question: do we need to also reset the DSPP alongside resetting 
the LM?

> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 82 ++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  7 ++
>   2 files changed, 89 insertions(+)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 4523693..0e31ad3 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1,5 +1,6 @@
>   // SPDX-License-Identifier: GPL-2.0-only
>   /*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
>    * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved.
>    * Copyright (C) 2013 Red Hat
>    * Author: Rob Clark <robdclark@gmail.com>
> @@ -22,6 +23,7 @@
>   #include "dpu_hw_ctl.h"
>   #include "dpu_hw_dspp.h"
>   #include "dpu_hw_dsc.h"
> +#include "dpu_hw_merge3d.h"
>   #include "dpu_formats.h"
>   #include "dpu_encoder_phys.h"
>   #include "dpu_crtc.h"
> @@ -1838,6 +1840,86 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
>   	DPU_ATRACE_END("encoder_kickoff");
>   }
>   
> +static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_mixer_cfg mixer;
> +	int i, num_lm;
> +	u32 flush_mask = 0;
> +	struct dpu_global_state *global_state;
> +	struct dpu_hw_blk *hw_lm[2];
> +	struct dpu_hw_mixer *hw_mixer[2];
> +	struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
> +
> +	memset(&mixer, 0, sizeof(mixer));
> +
> +	/* reset all mixers for this encoder */
> +	if (phys_enc->hw_ctl->ops.clear_all_blendstages)
> +		phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
> +
> +	global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
> +
> +	num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state,
> +		phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm));
> +
> +	for (i = 0; i < num_lm; i++) {
> +		hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
> +		flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, hw_mixer[i]->idx);
> +		if (phys_enc->hw_ctl->ops.update_pending_flush)
> +			phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask);
> +
> +		/* clear all blendstages */
> +		if (phys_enc->hw_ctl->ops.setup_blendstage)
> +			phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL);
> +	}
> +}
> +
> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
> +	struct dpu_hw_intf_cfg intf_cfg = { 0 };
> +	int i;
> +	struct dpu_encoder_virt *dpu_enc;
> +
> +	dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
> +
> +	phys_enc->hw_ctl->ops.reset(ctl);
> +
> +	dpu_encoder_helper_reset_mixers(phys_enc);
> +
> +	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> +		if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
> +			phys_enc->hw_intf->ops.bind_pingpong_blk(
> +					dpu_enc->phys_encs[i]->hw_intf, false,
> +					dpu_enc->phys_encs[i]->hw_pp->idx);
> +
> +		/* mark INTF flush as pending */
> +		if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
> +			phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
> +					dpu_enc->phys_encs[i]->hw_intf->idx);
> +	}
> +
> +	/* reset the merge 3D HW block */
> +	if (phys_enc->hw_pp->merge_3d) {
> +		phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
> +				BLEND_3D_NONE);
> +		if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d)
> +			phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl,
> +					phys_enc->hw_pp->merge_3d->idx);
> +	}
> +
> +	intf_cfg.stream_sel = 0; /* Don't care value for video mode */
> +	intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
> +	if (phys_enc->hw_pp->merge_3d)
> +		intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
> +
> +	if (ctl->ops.reset_intf_cfg)
> +		ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
> +
> +	ctl->ops.trigger_flush(ctl);
> +	ctl->ops.trigger_start(ctl);
> +	ctl->ops.clear_pending_flush(ctl);
> +}
> +
>   void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc)
>   {
>   	struct dpu_encoder_virt *dpu_enc;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 706b566..544a9a4 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -1,5 +1,6 @@
>   /* SPDX-License-Identifier: GPL-2.0-only */
>   /*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
>    * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
>    */
>   
> @@ -350,4 +351,10 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc,
>   		void (*func)(void *arg, int irq_idx),
>   		struct dpu_encoder_wait_info *wait_info);
>   
> +/**
> + * dpu_encoder_helper_phys_cleanup - helper to cleanup dpu pipeline
> + * @phys_enc: Pointer to physical encoder structure
> + */
> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc);
> +
>   #endif /* __dpu_encoder_phys_H__ */


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file
  2022-04-20  1:46 ` [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file Abhinav Kumar
@ 2022-04-20  7:26   ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:26 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> _dpu_plane_get_qos_lut() is not specific to just dpu_plane.
> It can take any fill level and return the LUT matching it.
> This can be used even for other modules like dpu_writeback.
> 
> Move _dpu_plane_get_qos_lut() to the common dpu_hw_util file
> and rename it to _dpu_hw_get_qos_lut().
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c | 25 +++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h |  4 ++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c   | 27 +--------------------------
>   3 files changed, 30 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
> index aad8511..512316f 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c
> @@ -422,3 +422,28 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map *c,
>   	DPU_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]);
>   	DPU_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]);
>   }
> +
> +/**
> + * _dpu_hw_get_qos_lut - get LUT mapping based on fill level
> + * @tbl:		Pointer to LUT table
> + * @total_fl:		fill level
> + * Return: LUT setting corresponding to the fill level
> + */
> +u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
> +		u32 total_fl)
> +{
> +	int i;
> +
> +	if (!tbl || !tbl->nentry || !tbl->entries)
> +		return 0;
> +
> +	for (i = 0; i < tbl->nentry; i++)
> +		if (total_fl <= tbl->entries[i].fl)
> +			return tbl->entries[i].lut;
> +
> +	/* if last fl is zero, use as default */
> +	if (!tbl->entries[i-1].fl)
> +		return tbl->entries[i-1].lut;
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
> index 3913475..529a6e0 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h
> @@ -9,6 +9,7 @@
>   #include <linux/io.h>
>   #include <linux/slab.h>
>   #include "dpu_hw_mdss.h"
> +#include "dpu_hw_catalog.h"
>   
>   #define REG_MASK(n)                     ((BIT(n)) - 1)
>   
> @@ -324,4 +325,7 @@ void dpu_hw_csc_setup(struct dpu_hw_blk_reg_map  *c,
>   		u32 csc_reg_off,
>   		const struct dpu_csc_cfg *data, bool csc10);
>   
> +u64 _dpu_hw_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
> +		u32 total_fl);
> +
>   #endif /* _DPU_HW_UTIL_H */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> index c77c3d9d..730f0a3 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> @@ -280,31 +280,6 @@ static int _dpu_plane_calc_fill_level(struct drm_plane *plane,
>   }
>   
>   /**
> - * _dpu_plane_get_qos_lut - get LUT mapping based on fill level
> - * @tbl:		Pointer to LUT table
> - * @total_fl:		fill level
> - * Return: LUT setting corresponding to the fill level
> - */
> -static u64 _dpu_plane_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
> -		u32 total_fl)
> -{
> -	int i;
> -
> -	if (!tbl || !tbl->nentry || !tbl->entries)
> -		return 0;
> -
> -	for (i = 0; i < tbl->nentry; i++)
> -		if (total_fl <= tbl->entries[i].fl)
> -			return tbl->entries[i].lut;
> -
> -	/* if last fl is zero, use as default */
> -	if (!tbl->entries[i-1].fl)
> -		return tbl->entries[i-1].lut;
> -
> -	return 0;
> -}
> -
> -/**
>    * _dpu_plane_set_qos_lut - set QoS LUT of the given plane
>    * @plane:		Pointer to drm plane
>    * @fb:			Pointer to framebuffer associated with the given plane
> @@ -333,7 +308,7 @@ static void _dpu_plane_set_qos_lut(struct drm_plane *plane,
>   			lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;
>   	}
>   
> -	qos_lut = _dpu_plane_get_qos_lut(
> +	qos_lut = _dpu_hw_get_qos_lut(
>   			&pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl);
>   
>   	trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0,


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20  1:46 ` [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder Abhinav Kumar
@ 2022-04-20  7:44   ` Dmitry Baryshkov
  2022-04-20 17:41     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:44 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Make changes to dpu_encoder to support virtual encoder needed
> to support writeback for dpu.
> 
> changes in v2:
> 	- add the writeback parts to dpu_encoder_helper_phys_cleanup
> 	- rebase on tip of msm-next and fix related dependencies
> 	- get the writeback blocks directly from RM
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71 +++++++++++++++++-------
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
>   2 files changed, 54 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 0e31ad3..06b8631 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -928,6 +928,7 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>   	struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
>   	int num_lm, num_ctl, num_pp, num_dsc;
>   	unsigned int dsc_mask = 0;
> +	enum dpu_hw_blk_type blk_type;
>   	int i;
>   
>   	if (!drm_enc) {
> @@ -1009,12 +1010,21 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>   		phys->hw_pp = dpu_enc->hw_pp[i];
>   		phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>   
> -		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
> -			phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
> +		if (dpu_encoder_get_intf_mode(&dpu_enc->base) == INTF_MODE_WB_LINE)
> +			blk_type = DPU_HW_BLK_WB;
> +		else
> +			blk_type = DPU_HW_BLK_INTF;
> +
> +		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
> +			if (blk_type == DPU_HW_BLK_INTF)
> +				phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
> +			else if (blk_type == DPU_HW_BLK_WB)
> +				phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm, phys->intf_idx);
> +		}
>   
> -		if (!phys->hw_intf) {
> +		if (!phys->hw_intf && !phys->hw_wb) {
>   			DPU_ERROR_ENC(dpu_enc,
> -				      "no intf block assigned at idx: %d\n", i);
> +				      "no intf ow wb block assigned at idx: %d\n", i);

or wb

>   			return;
>   		}
>   
> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg *catalog,
>   		enum dpu_intf_type type, u32 controller_id)
>   {
>   	int i = 0;
>   
> -	for (i = 0; i < catalog->intf_count; i++) {
> -		if (catalog->intf[i].type == type
> -		    && catalog->intf[i].controller_id == controller_id) {
> -			return catalog->intf[i].id;
> +	if (type != INTF_WB) {
> +		for (i = 0; i < catalog->intf_count; i++) {
> +			if (catalog->intf[i].type == type
> +				&& catalog->intf[i].controller_id == controller_id) {
> +				return catalog->intf[i].id;
> +			}
> +		}
> +	} else {
> +		for (i = 0; i < catalog->wb_count; i++) {
> +			if (catalog->wb[i].id == controller_id)
> +				return catalog->wb[i].id;
>   		}
>   	}
>   
> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
>   
>   	dpu_encoder_helper_reset_mixers(phys_enc);
>   
> -	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> -		if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
> -			phys_enc->hw_intf->ops.bind_pingpong_blk(
> -					dpu_enc->phys_encs[i]->hw_intf, false,
> -					dpu_enc->phys_encs[i]->hw_pp->idx);
> +	if (phys_enc->hw_wb) {

I think this adds a hidden knowledge here. That there is always just a 
single phys_enc for the WB encoder. I'd still do this cleanup in a loop 
together with the INTF cleanup.

> +		/* disable the PP block */
> +		if (phys_enc->hw_wb->ops.bind_pingpong_blk)
> +			phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, false,
> +					phys_enc->hw_pp->idx);
>   
> -		/* mark INTF flush as pending */
> -		if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
> -			phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
> -					dpu_enc->phys_encs[i]->hw_intf->idx);
> +		/* mark WB flush as pending */
> +		if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
> +			phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx);
> +	} else {
> +		for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> +			if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk)
> +				phys_enc->hw_intf->ops.bind_pingpong_blk(
> +						dpu_enc->phys_encs[i]->hw_intf, false,
> +						dpu_enc->phys_encs[i]->hw_pp->idx);
> +
> +			/* mark INTF flush as pending */
> +			if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
> +				phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
> +						dpu_enc->phys_encs[i]->hw_intf->idx);
> +		}
>   	}
>   
>   	/* reset the merge 3D HW block */
> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>   	case DRM_MODE_ENCODER_TMDS:
>   		intf_type = INTF_DP;
>   		break;
> +	case DRM_MODE_ENCODER_VIRTUAL:
> +		intf_type = INTF_WB;
> +		break;
>   	}
>   
>   	WARN_ON(disp_info->num_of_h_tiles < 1);
> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>   		DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>   				i, controller_id, phys_params.split_role);
>   
> -		phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
> +		phys_params.intf_idx = dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
>   													intf_type,
>   													controller_id);

I'd prefer to be more explicit here. intf_idx is of type enum dpu_intf, 
while WBs are enumerated with enum dpu_wb.

I's suggest adding a separate phys_params.wb_idx and a 
dpu_encoder_get_wb() and calling one here depending on intf_type.

Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check 
intf_mode, but get both intf and wb and report an error if none was 
provided.

>   		if (phys_params.intf_idx == INTF_MAX) {
> -			DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id %d\n",
> +			DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d, id %d\n",
>   						  intf_type, controller_id);
>   			ret = -EINVAL;
>   		}
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 544a9a4..0b80af4 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -11,6 +11,7 @@
>   
>   #include "dpu_kms.h"
>   #include "dpu_hw_intf.h"
> +#include "dpu_hw_wb.h"
>   #include "dpu_hw_pingpong.h"
>   #include "dpu_hw_ctl.h"
>   #include "dpu_hw_top.h"
> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
>    * @hw_ctl:		Hardware interface to the ctl registers
>    * @hw_pp:		Hardware interface to the ping pong registers
>    * @hw_intf:		Hardware interface to the intf registers
> + * @hw_wb:		Hardware interface to the wb registers
>    * @dpu_kms:		Pointer to the dpu_kms top level
>    * @cached_mode:	DRM mode cached at mode_set time, acted on in enable
>    * @enabled:		Whether the encoder has enabled and running a mode
> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
>   	struct dpu_hw_ctl *hw_ctl;
>   	struct dpu_hw_pingpong *hw_pp;
>   	struct dpu_hw_intf *hw_intf;
> +	struct dpu_hw_wb *hw_wb;
>   	struct dpu_kms *dpu_kms;
>   	struct drm_display_mode cached_mode;
>   	enum dpu_enc_split_role split_role;


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20  1:46 ` [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback Abhinav Kumar
@ 2022-04-20  7:49   ` Dmitry Baryshkov
  2022-04-20 18:17     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:49 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Introduce the dpu_encoder_phys_* for the writeback interface
> to handle writeback specific hardware programming.
> 
> changes in v2:
> 	- rebase on msm-next and fix related dependencies namely
> 	  the irq cleanup
> 	- move cdp_cfg, aspace out of dpu_encoder_phys_wb
> 	- leave a comment about wb master
> 	- start using _dpu_hw_get_qos_lut from dpu_hw_util
> 	- replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>   drivers/gpu/drm/msm/Makefile                       |   1 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751 +++++++++++++++++++++
>   3 files changed, 782 insertions(+)
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index ca779c1..0387f22 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>   	disp/dpu1/dpu_encoder.o \
>   	disp/dpu1/dpu_encoder_phys_cmd.o \
>   	disp/dpu1/dpu_encoder_phys_vid.o \
> +	disp/dpu1/dpu_encoder_phys_wb.o \
>   	disp/dpu1/dpu_formats.o \
>   	disp/dpu1/dpu_hw_catalog.o \
>   	disp/dpu1/dpu_hw_ctl.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 00951f3..5452f98 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
>    * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
>    * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
>    * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
> + * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
>    */
>   enum dpu_intr_idx {
>   	INTR_IDX_VSYNC,
> @@ -157,6 +158,7 @@ enum dpu_intr_idx {
>   	INTR_IDX_UNDERRUN,
>   	INTR_IDX_CTL_START,
>   	INTR_IDX_RDPTR,
> +	INTR_IDX_WB_DONE,
>   	INTR_IDX_MAX,
>   };
>   
> @@ -224,6 +226,27 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
>   }
>   
>   /**
> + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to handle command
> + *	mode specific operations
> + * @base:	Baseclass physical encoder structure
> + * @wbirq_refcount:     Reference count of writeback interrupt
> + * @wb_done_timeout_cnt: number of wb done irq timeout errors
> + * @wb_cfg:  writeback block config to store fb related details
> + * @wb_conn: backpointer to writeback connector
> + * @wb_job: backpointer to current writeback job
> + * @dest:   dpu buffer layout for current writeback output buffer
> + */
> +struct dpu_encoder_phys_wb {
> +	struct dpu_encoder_phys base;
> +	atomic_t wbirq_refcount;
> +	int wb_done_timeout_cnt;
> +	struct dpu_hw_wb_cfg wb_cfg;
> +	struct drm_writeback_connector *wb_conn;
> +	struct drm_writeback_job *wb_job;
> +	struct dpu_hw_fmt_layout dest;
> +};
> +
> +/**
>    * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command
>    *	mode specific operations
>    * @base:	Baseclass physical encoder structure
> @@ -291,6 +314,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
>   		struct dpu_enc_phys_init_params *p);
>   
>   /**
> + * dpu_encoder_phys_wb_init - initialize writeback encoder
> + * @init:	Pointer to init info structure with initialization params
> + */
> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
> +		struct dpu_enc_phys_init_params *p);
> +
> +/**
>    * dpu_encoder_helper_trigger_start - control start helper function
>    *	This helper function may be optionally specified by physical
>    *	encoders if they require ctl_start triggering.
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> new file mode 100644
> index 0000000..128317fe
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> @@ -0,0 +1,751 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
> +
> +#include <linux/debugfs.h>
> +
> +#include "dpu_encoder_phys.h"
> +#include "dpu_formats.h"
> +#include "dpu_hw_top.h"
> +#include "dpu_hw_wb.h"
> +#include "dpu_hw_lm.h"
> +#include "dpu_hw_blk.h"
> +#include "dpu_hw_merge3d.h"
> +#include "dpu_hw_interrupts.h"
> +#include "dpu_core_irq.h"
> +#include "dpu_vbif.h"
> +#include "dpu_crtc.h"
> +#include "disp/msm_disp_snapshot.h"
> +
> +#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
> +
> +#define to_dpu_encoder_phys_wb(x) \
> +	container_of(x, struct dpu_encoder_phys_wb, base)
> +
> +/**
> + * dpu_encoder_phys_wb_is_master - report wb always as master encoder
> + */
> +static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys *phys_enc)
> +{
> +	/* there is only one physical enc for dpu_writeback */
> +	return true;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback interface
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_set_ot_limit(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> +	struct dpu_vbif_set_ot_params ot_params;
> +
> +	memset(&ot_params, 0, sizeof(ot_params));
> +	ot_params.xin_id = hw_wb->caps->xin_id;
> +	ot_params.num = hw_wb->idx - WB_0;
> +	ot_params.width = phys_enc->cached_mode.hdisplay;
> +	ot_params.height = phys_enc->cached_mode.vdisplay;
> +	ot_params.is_wfd = true;
> +	ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
> +	ot_params.vbif_idx = hw_wb->caps->vbif_idx;
> +	ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
> +	ot_params.rd = false;
> +
> +	dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_set_qos_remap(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb;
> +	struct dpu_vbif_set_qos_params qos_params;
> +
> +	if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
> +		DPU_ERROR("invalid arguments\n");
> +		return;
> +	}
> +
> +	if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
> +		DPU_ERROR("invalid writeback hardware\n");
> +		return;
> +	}
> +
> +	hw_wb = phys_enc->hw_wb;
> +
> +	memset(&qos_params, 0, sizeof(qos_params));
> +	qos_params.vbif_idx = hw_wb->caps->vbif_idx;
> +	qos_params.xin_id = hw_wb->caps->xin_id;
> +	qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
> +	qos_params.num = hw_wb->idx - WB_0;
> +	qos_params.is_rt = false;
> +
> +	DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
> +			qos_params.num,
> +			qos_params.vbif_idx,
> +			qos_params.xin_id, qos_params.is_rt);
> +
> +	dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb;
> +	struct dpu_hw_wb_qos_cfg qos_cfg;
> +	struct dpu_mdss_cfg *catalog;
> +	struct dpu_qos_lut_tbl *qos_lut_tb;
> +
> +	if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
> +		DPU_ERROR("invalid parameter(s)\n");
> +		return;
> +	}
> +
> +	catalog = phys_enc->dpu_kms->catalog;
> +
> +	hw_wb = phys_enc->hw_wb;
> +
> +	memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
> +	qos_cfg.danger_safe_en = true;
> +	qos_cfg.danger_lut =
> +		catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> +
> +	qos_cfg.safe_lut = catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> +
> +	qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> +	qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
> +
> +	if (hw_wb->ops.setup_qos_lut)
> +		hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
> + * @phys_enc:	Pointer to physical encoder
> + * @fb:		Pointer to output framebuffer
> + * @wb_roi:	Pointer to output region of interest
> + */
> +static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
> +		struct drm_framebuffer *fb)
> +{
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +	struct dpu_hw_wb *hw_wb;
> +	struct dpu_hw_wb_cfg *wb_cfg;
> +	struct dpu_hw_wb_cdp_cfg cdp_cfg;
> +
> +	if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) {
> +		DPU_ERROR("invalid encoder\n");
> +		return;
> +	}
> +
> +	hw_wb = phys_enc->hw_wb;
> +	wb_cfg = &wb_enc->wb_cfg;
> +
> +	wb_cfg->intf_mode = phys_enc->intf_mode;
> +	wb_cfg->roi.x1 = 0;
> +	wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
> +	wb_cfg->roi.y1 = 0;
> +	wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
> +
> +	if (hw_wb->ops.setup_roi)
> +		hw_wb->ops.setup_roi(hw_wb, wb_cfg);
> +
> +	if (hw_wb->ops.setup_outformat)
> +		hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
> +
> +	if (hw_wb->ops.setup_cdp) {
> +		memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
> +
> +		cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
> +				[DPU_PERF_CDP_USAGE_NRT].wr_enable;
> +		cdp_cfg.ubwc_meta_enable =
> +				DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
> +		cdp_cfg.tile_amortize_enable =
> +				DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
> +				DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
> +		cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
> +
> +		hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
> +	}
> +
> +	if (hw_wb->ops.setup_outaddress)
> +		hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
> + * @phys_enc:Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb;
> +	struct dpu_hw_ctl *ctl;
> +
> +	if (!phys_enc) {
> +		DPU_ERROR("invalid encoder\n");
> +		return;
> +	}
> +
> +	hw_wb = phys_enc->hw_wb;
> +	ctl = phys_enc->hw_ctl;
> +
> +	if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
> +		(phys_enc->hw_ctl &&
> +		 phys_enc->hw_ctl->ops.setup_intf_cfg)) {
> +		struct dpu_hw_intf_cfg intf_cfg = {0};
> +		struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
> +		enum dpu_3d_blend_mode mode_3d;
> +
> +		mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
> +
> +		intf_cfg.intf = DPU_NONE;
> +		intf_cfg.wb = hw_wb->idx;
> +
> +		if (mode_3d && hw_pp && hw_pp->merge_3d)
> +			intf_cfg.merge_3d = hw_pp->merge_3d->idx;
> +
> +		if (phys_enc->hw_pp->merge_3d && phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
> +			phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
> +					mode_3d);
> +
> +		/* setup which pp blk will connect to this wb */
> +		if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
> +			phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, true,
> +					phys_enc->hw_pp->idx);
> +
> +		phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
> +	} else if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg) {
> +		struct dpu_hw_intf_cfg intf_cfg = {0};
> +
> +		intf_cfg.intf = DPU_NONE;
> +		intf_cfg.wb = hw_wb->idx;
> +		intf_cfg.mode_3d =
> +			dpu_encoder_helper_get_3d_blend_mode(phys_enc);
> +		phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
> +	}
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic states
> + * @phys_enc:	Pointer to physical encoder
> + * @crtc_state:	Pointer to CRTC atomic state
> + * @conn_state:	Pointer to connector atomic state
> + */
> +static int dpu_encoder_phys_wb_atomic_check(
> +		struct dpu_encoder_phys *phys_enc,
> +		struct drm_crtc_state *crtc_state,
> +		struct drm_connector_state *conn_state)
> +{
> +	struct drm_framebuffer *fb;
> +	const struct drm_display_mode *mode;
> +
> +	DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
> +			phys_enc->intf_idx, mode->name, mode->hdisplay, mode->vdisplay);
> +
> +	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
> +		return 0;
> +
> +	fb = conn_state->writeback_job->fb;
> +	mode = &crtc_state->mode;
> +
> +	if (!conn_state || !conn_state->connector) {
> +		DPU_ERROR("invalid connector state\n");
> +		return -EINVAL;
> +	} else if (conn_state->connector->status !=
> +			connector_status_connected) {
> +		DPU_ERROR("connector not connected %d\n",
> +				conn_state->connector->status);
> +		return -EINVAL;
> +	}
> +
> +	DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
> +			fb->width, fb->height);
> +
> +	if (fb->width != mode->hdisplay) {
> +		DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
> +				mode->hdisplay);
> +		return -EINVAL;
> +	} else if (fb->height != mode->vdisplay) {
> +		DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
> +				  mode->vdisplay);
> +		return -EINVAL;
> +	} else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
> +		DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
> +				  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
> +		return -EINVAL;
> +	}

Being not an expert in the DRM WB API, I have a question: are those 
hardware limitations or the DRM requirement that we can not write to the 
region inside the attached FB?

> +
> +	return 0;
> +}
> +
> +
> +/**
> + * _dpu_encoder_phys_wb_update_flush - flush hardware update
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void _dpu_encoder_phys_wb_update_flush(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb;
> +	struct dpu_hw_ctl *hw_ctl;
> +	struct dpu_hw_pingpong *hw_pp;
> +	u32 pending_flush = 0;
> +
> +	if (!phys_enc)
> +		return;
> +
> +	hw_wb = phys_enc->hw_wb;
> +	hw_pp = phys_enc->hw_pp;
> +	hw_ctl = phys_enc->hw_ctl;
> +
> +	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> +
> +	if (!hw_ctl) {
> +		DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
> +		return;
> +	}
> +
> +	if (hw_ctl->ops.update_pending_flush_wb)
> +		hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
> +
> +	if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && hw_pp->merge_3d)
> +		hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
> +				hw_pp->merge_3d->idx);
> +
> +	if (hw_ctl->ops.get_pending_flush)
> +		pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
> +
> +	DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
> +			hw_ctl->idx - CTL_0, pending_flush,
> +			hw_wb->idx - WB_0);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_setup - setup writeback encoder
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_setup(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> +	struct drm_display_mode mode = phys_enc->cached_mode;
> +	struct drm_framebuffer *fb = NULL;
> +
> +	DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
> +			hw_wb->idx - WB_0, mode.name,
> +			mode.hdisplay, mode.vdisplay);
> +
> +	dpu_encoder_phys_wb_set_ot_limit(phys_enc);
> +
> +	dpu_encoder_phys_wb_set_qos_remap(phys_enc);
> +
> +	dpu_encoder_phys_wb_set_qos(phys_enc);
> +
> +	dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
> +
> +	dpu_encoder_phys_wb_setup_cdp(phys_enc);
> +
> +}
> +
> +static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
> +{
> +	struct dpu_encoder_phys *phys_enc = arg;
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +
> +	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> +	unsigned long lock_flags;
> +	u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
> +
> +	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> +
> +	if (phys_enc->parent_ops->handle_frame_done)
> +		phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
> +				phys_enc, event);
> +
> +	if (phys_enc->parent_ops->handle_vblank_virt)
> +		phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
> +				phys_enc);
> +
> +	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
> +	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
> +	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
> +
> +	if (wb_enc->wb_conn)
> +		drm_writeback_signal_completion(wb_enc->wb_conn, 0);
> +
> +	/* Signal any waiting atomic commit thread */
> +	wake_up_all(&phys_enc->pending_kickoff_wq);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
> + * @arg:	Pointer to writeback encoder
> + * @irq_idx:	interrupt index
> + */
> +static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
> +{
> +	_dpu_encoder_phys_wb_frame_done_helper(arg);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
> + * @phys:	Pointer to physical encoder
> + * @enable:	indicates enable or disable interrupts
> + */
> +static void dpu_encoder_phys_wb_irq_ctrl(
> +		struct dpu_encoder_phys *phys, bool enable)
> +{
> +
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
> +	int ret = 0;
> +	int refcount;
> +
> +	refcount = atomic_read(&wb_enc->wbirq_refcount);
> +
> +	if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
> +		dpu_core_irq_register_callback(phys->dpu_kms,
> +				phys->irq[INTR_IDX_WB_DONE], dpu_encoder_phys_wb_done_irq, phys);
> +		if (ret)
> +			atomic_dec_return(&wb_enc->wbirq_refcount);
> +	} else if (!enable &&
> +			atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
> +		dpu_core_irq_unregister_callback(phys->dpu_kms, phys->irq[INTR_IDX_WB_DONE]);
> +		if (ret)
> +			atomic_inc_return(&wb_enc->wbirq_refcount);
> +	}
> +}
> +
> +static void dpu_encoder_phys_wb_atomic_mode_set(
> +		struct dpu_encoder_phys *phys_enc,
> +		struct drm_crtc_state *crtc_state,
> +		struct drm_connector_state *conn_state)
> +{
> +
> +	phys_enc->irq[INTR_IDX_WB_DONE] = phys_enc->hw_wb->caps->intr_wb_done;
> +}
> +
> +static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +	u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
> +
> +	wb_enc->wb_done_timeout_cnt++;
> +
> +	if (wb_enc->wb_done_timeout_cnt == 1)
> +		msm_disp_snapshot_state(phys_enc->parent->dev);
> +
> +	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
> +
> +	/* request a ctl reset before the next kickoff */
> +	phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
> +
> +	if (wb_enc->wb_conn)
> +		drm_writeback_signal_completion(wb_enc->wb_conn, 0);
> +
> +	if (phys_enc->parent_ops->handle_frame_done)
> +		phys_enc->parent_ops->handle_frame_done(
> +				phys_enc->parent, phys_enc, frame_event);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is committed
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static int dpu_encoder_phys_wb_wait_for_commit_done(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	unsigned long ret;
> +	struct dpu_encoder_wait_info wait_info;
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +
> +	wait_info.wq = &phys_enc->pending_kickoff_wq;
> +	wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
> +	wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
> +
> +	ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
> +			dpu_encoder_phys_wb_done_irq, &wait_info);
> +	if (ret == -ETIMEDOUT)
> +		_dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
> +	else if (!ret)
> +		wb_enc->wb_done_timeout_cnt = 0;
> +
> +	return ret;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
> + * @phys_enc:	Pointer to physical encoder
> + * Returns:	Zero on success
> + */
> +static void dpu_encoder_phys_wb_prepare_for_kickoff(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +	struct drm_connector *drm_conn;
> +	struct drm_connector_state *state;
> +
> +	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> +
> +	if (!wb_enc->wb_conn || !wb_enc->wb_job) {
> +		DPU_ERROR("invalid wb_conn or wb_job\n");
> +		return;
> +	}
> +
> +	drm_conn = &wb_enc->wb_conn->base;
> +	state = drm_conn->state;
> +
> +	if (wb_enc->wb_conn && wb_enc->wb_job)
> +		drm_writeback_queue_job(wb_enc->wb_conn, state);
> +
> +	dpu_encoder_phys_wb_setup(phys_enc);
> +
> +	_dpu_encoder_phys_wb_update_flush(phys_enc);
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static bool dpu_encoder_phys_wb_needs_single_flush(struct dpu_encoder_phys *phys_enc)
> +{
> +	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> +	return false;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_handle_post_kickoff(
> +		struct dpu_encoder_phys *phys_enc)
> +{
> +	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> +
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_enable - enable writeback encoder
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys *phys_enc)
> +{
> +	DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> +	phys_enc->enable_state = DPU_ENC_ENABLED;
> +}
> +/**
> + * dpu_encoder_phys_wb_disable - disable writeback encoder
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> +	struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
> +
> +	DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> +
> +	if (phys_enc->enable_state == DPU_ENC_DISABLED) {
> +		DPU_ERROR("encoder is already disabled\n");
> +		return;
> +	}
> +
> +	/* reset h/w before final flush */
> +	if (phys_enc->hw_ctl->ops.clear_pending_flush)
> +		phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
> +
> +	/*
> +	 * New CTL reset sequence from 5.0 MDP onwards.
> +	 * If has_3d_merge_reset is not set, legacy reset
> +	 * sequence is executed.
> +	 *
> +	 * Legacy reset sequence has not been implemented yet.
> +	 * Any target earlier than SM8150 will need it and when
> +	 * WB support is added to those targets will need to add
> +	 * the legacy teardown sequence as well.
> +	 */
> +	if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
> +		dpu_encoder_helper_phys_cleanup(phys_enc);
> +
> +	phys_enc->enable_state = DPU_ENC_DISABLED;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_destroy - destroy writeback encoder
> + * @phys_enc:	Pointer to physical encoder
> + */
> +static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys *phys_enc)
> +{
> +	DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
> +
> +	if (!phys_enc)
> +		return;
> +
> +	kfree(phys_enc);
> +}
> +
> +static void dpu_encoder_phys_wb_prepare_wb_job(struct dpu_encoder_phys *phys_enc,
> +		struct drm_writeback_job *job)
> +{
> +	const struct msm_format *format;
> +	struct msm_gem_address_space *aspace;
> +	struct dpu_hw_wb_cfg *wb_cfg;
> +	int ret;
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +
> +	if (!job->fb)
> +		return;
> +
> +	wb_enc->wb_job = job;
> +	wb_enc->wb_conn = job->connector;
> +	aspace = phys_enc->dpu_kms->base.aspace;
> +
> +	wb_cfg = &wb_enc->wb_cfg;
> +
> +	memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
> +
> +	ret = msm_framebuffer_prepare(job->fb, aspace, false);
> +	if (ret) {
> +		DPU_ERROR("prep fb failed, %d\n", ret);
> +		return;
> +	}
> +
> +	format = msm_framebuffer_format(job->fb);
> +
> +	wb_cfg->dest.format = dpu_get_dpu_format_ext(
> +			format->pixel_format, job->fb->modifier);
> +	if (!wb_cfg->dest.format) {
> +		/* this error should be detected during atomic_check */
> +		DPU_ERROR("failed to get format %x\n", format->pixel_format);
> +		return;
> +	}
> +
> +	ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
> +	if (ret) {
> +		DPU_DEBUG("failed to populate layout %d\n", ret);
> +		return;
> +	}
> +
> +	wb_cfg->dest.width = job->fb->width;
> +	wb_cfg->dest.height = job->fb->height;
> +	wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
> +
> +	if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
> +			(wb_cfg->dest.format->element[0] == C1_B_Cb))
> +		swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
> +
> +	DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
> +			wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
> +			wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
> +
> +	DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
> +			wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
> +			wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
> +}
> +
> +static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc,
> +		struct drm_writeback_job *job)
> +{
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +	struct msm_gem_address_space *aspace;
> +
> +	if (!job->fb)
> +		return;
> +
> +	aspace = phys_enc->dpu_kms->base.aspace;
> +
> +	msm_framebuffer_cleanup(job->fb, aspace, false);
> +	wb_enc->wb_job = NULL;
> +	wb_enc->wb_conn = NULL;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_init_ops - initialize writeback operations
> + * @ops:	Pointer to encoder operation table
> + */
> +static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
> +{
> +	ops->is_master = dpu_encoder_phys_wb_is_master;
> +	ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
> +	ops->enable = dpu_encoder_phys_wb_enable;
> +	ops->disable = dpu_encoder_phys_wb_disable;
> +	ops->destroy = dpu_encoder_phys_wb_destroy;
> +	ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
> +	ops->wait_for_commit_done = dpu_encoder_phys_wb_wait_for_commit_done;
> +	ops->prepare_for_kickoff = dpu_encoder_phys_wb_prepare_for_kickoff;
> +	ops->handle_post_kickoff = dpu_encoder_phys_wb_handle_post_kickoff;
> +	ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
> +	ops->trigger_start = dpu_encoder_helper_trigger_start;
> +	ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
> +	ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
> +	ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
> +}
> +
> +/**
> + * dpu_encoder_phys_wb_init - initialize writeback encoder
> + * @init:	Pointer to init info structure with initialization params
> + */
> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
> +		struct dpu_enc_phys_init_params *p)
> +{
> +	struct dpu_encoder_phys *phys_enc = NULL;
> +	struct dpu_encoder_phys_wb *wb_enc = NULL;
> +	int ret = 0;
> +	int i;
> +
> +	DPU_DEBUG("\n");
> +
> +	if (!p || !p->parent) {
> +		DPU_ERROR("invalid params\n");
> +		ret = -EINVAL;
> +		goto fail_alloc;
> +	}
> +
> +	wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
> +	if (!wb_enc) {
> +		DPU_ERROR("failed to allocate wb phys_enc enc\n");
> +		ret = -ENOMEM;
> +		goto fail_alloc;
> +	}
> +
> +	phys_enc = &wb_enc->base;
> +	phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
> +	phys_enc->intf_idx = p->intf_idx;
> +
> +	dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
> +	phys_enc->parent = p->parent;
> +	phys_enc->parent_ops = p->parent_ops;
> +	phys_enc->dpu_kms = p->dpu_kms;
> +	phys_enc->split_role = p->split_role;
> +	phys_enc->intf_mode = INTF_MODE_WB_LINE;
> +	phys_enc->intf_idx = p->intf_idx;
> +	phys_enc->enc_spinlock = p->enc_spinlock;
> +
> +	atomic_set(&wb_enc->wbirq_refcount, 0);
> +
> +	for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
> +		phys_enc->irq[i] = -EINVAL;
> +
> +	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
> +	atomic_set(&phys_enc->vblank_refcount, 0);
> +	wb_enc->wb_done_timeout_cnt = 0;
> +
> +	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
> +	phys_enc->enable_state = DPU_ENC_DISABLED;
> +
> +	DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
> +			phys_enc->intf_idx);
> +
> +	return phys_enc;
> +
> +fail_alloc:
> +	return ERR_PTR(ret);
> +}


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer
  2022-04-20  1:46 ` [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer Abhinav Kumar
@ 2022-04-20  7:52   ` Dmitry Baryshkov
  2022-04-20 19:10     ` Abhinav Kumar
  2022-04-20 19:26   ` Dmitry Baryshkov
  1 sibling, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:52 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Introduce the dpu_writeback module which serves as the
> interface between dpu operations and the drm_writeback.
> 
> This module manages the connector related operations for
> dpu writeback.
> 
> changes in v2:
> 	- start using drm_writeback_connector_init_with_encoder()
> 	- drop unnecessary arguments from dpu_writeback_init()
> 	- rebase on msm-next tip and remove usage of priv->connectors
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>   drivers/gpu/drm/msm/Makefile                  |  1 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 68 +++++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 25 ++++++++++
>   3 files changed, 94 insertions(+)
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 0387f22..66395ee 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -80,6 +80,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>   	disp/dpu1/dpu_plane.o \
>   	disp/dpu1/dpu_rm.o \
>   	disp/dpu1/dpu_vbif.o \
> +	disp/dpu1/dpu_writeback.o
>   
>   msm-$(CONFIG_DRM_MSM_MDSS) += \
>   	msm_mdss.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
> new file mode 100644
> index 0000000..526d884
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include "dpu_writeback.h"
> +
> +static int dpu_wb_conn_get_modes(struct drm_connector *connector)
> +{
> +	struct drm_device *dev = connector->dev;
> +
> +	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
> +			dev->mode_config.max_height);
> +}
> +
> +static const struct drm_connector_funcs dpu_wb_conn_funcs = {
> +	.reset = drm_atomic_helper_connector_reset,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = drm_connector_cleanup,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector,
> +		struct drm_writeback_job *job)
> +{
> +	if (!job->fb)
> +		return 0;
> +
> +	dpu_encoder_prepare_wb_job(connector->encoder, job);
> +
> +	return 0;
> +}
> +
> +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector,
> +		struct drm_writeback_job *job)
> +{
> +	if (!job->fb)
> +		return;
> +
> +	dpu_encoder_cleanup_wb_job(connector->encoder, job);
> +}
> +
> +static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = {
> +	.get_modes = dpu_wb_conn_get_modes,
> +	.prepare_writeback_job = dpu_wb_conn_prepare_job,
> +	.cleanup_writeback_job = dpu_wb_conn_cleanup_job,
> +};
> +
> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
> +		const u32 *format_list, u32 num_formats)
> +{
> +	struct dpu_wb_connector *dpu_wb_conn;
> +	int rc = 0;
> +
> +	dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL);
> +
> +	drm_connector_helper_add(&dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs);
> +
> +	/* DPU initializes the encoder and sets it up completely for writeback
> +	 * cases and hence should use the new API drm_writeback_connector_init_with_encoder
> +	 * to initialize the writeback connector
> +	 */
> +	rc = drm_writeback_connector_init_with_encoder(dev, &dpu_wb_conn->base, enc,
> +			&dpu_wb_conn_funcs, format_list, num_formats);
> +
> +	return rc;
> +}
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> new file mode 100644
> index 0000000..05aff05
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#ifndef _DPU_WRITEBACK_H
> +#define _DPU_WRITEBACK_H
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_writeback.h>
> +
> +#include "msm_drv.h"
> +#include "dpu_kms.h"
> +#include "dpu_encoder_phys.h"
> +
> +struct dpu_wb_connector {
> +	struct drm_writeback_connector base;
> +};

Do you plan to add more fields to this struct? If not, we can probably 
drop it and use struct drm_writeback_connector directly.

> +
> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
> +		const u32 *format_list, u32 num_formats);
> +
> +#endif /*_DPU_WRITEBACK_H */


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback
  2022-04-20  1:46 ` [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback Abhinav Kumar
@ 2022-04-20  7:54   ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:54 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Initialize dpu encoder and connector for writeback if the
> target supports it in the catalog.
> 
> changes in v2:
> 	- start initialing the encoder for writeback since we
> 	have migrated to using drm_writeback_connector_init_with_encoder()
> 	- instead of checking for WB_2 inside _dpu_kms_initialize_writeback
> 	call it only when its WB_2
> 	- rebase on tip of msm-next and remove usage of priv->encoders
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>


Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 27 ++++++++++----
>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     | 58 +++++++++++++++++++++++++++++
>   2 files changed, 78 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index b117cad..b1475dd 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -2085,7 +2085,7 @@ static void dpu_encoder_early_unregister(struct drm_encoder *encoder)
>   }
>   
>   static int dpu_encoder_virt_add_phys_encs(
> -		u32 display_caps,
> +		struct msm_display_info *disp_info,
>   		struct dpu_encoder_virt *dpu_enc,
>   		struct dpu_enc_phys_init_params *params)
>   {
> @@ -2104,7 +2104,7 @@ static int dpu_encoder_virt_add_phys_encs(
>   		return -EINVAL;
>   	}
>   
> -	if (display_caps & MSM_DISPLAY_CAP_VID_MODE) {
> +	if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) {
>   		enc = dpu_encoder_phys_vid_init(params);
>   
>   		if (IS_ERR_OR_NULL(enc)) {
> @@ -2117,7 +2117,7 @@ static int dpu_encoder_virt_add_phys_encs(
>   		++dpu_enc->num_phys_encs;
>   	}
>   
> -	if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) {
> +	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
>   		enc = dpu_encoder_phys_cmd_init(params);
>   
>   		if (IS_ERR_OR_NULL(enc)) {
> @@ -2130,6 +2130,19 @@ static int dpu_encoder_virt_add_phys_encs(
>   		++dpu_enc->num_phys_encs;
>   	}
>   
> +	if (disp_info->intf_type == DRM_MODE_ENCODER_VIRTUAL) {
> +		enc = dpu_encoder_phys_wb_init(params);
> +
> +		if (IS_ERR_OR_NULL(enc)) {
> +			DPU_ERROR_ENC(dpu_enc, "failed to init wb enc: %ld\n",
> +					PTR_ERR(enc));
> +			return enc == NULL ? -EINVAL : PTR_ERR(enc);
> +		}
> +
> +		dpu_enc->phys_encs[dpu_enc->num_phys_encs] = enc;
> +		++dpu_enc->num_phys_encs;
> +	}
> +
>   	if (params->split_role == ENC_ROLE_SLAVE)
>   		dpu_enc->cur_slave = enc;
>   	else
> @@ -2220,9 +2233,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>   		}
>   
>   		if (!ret) {
> -			ret = dpu_encoder_virt_add_phys_encs(disp_info->capabilities,
> -												 dpu_enc,
> -												 &phys_params);
> +			ret = dpu_encoder_virt_add_phys_encs(disp_info,
> +					dpu_enc, &phys_params);
>   			if (ret)
>   				DPU_ERROR_ENC(dpu_enc, "failed to add phys encs\n");
>   		}
> @@ -2339,8 +2351,9 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev,
>   	if (!dpu_enc)
>   		return ERR_PTR(-ENOMEM);
>   
> +
>   	rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs,
> -			drm_enc_mode, NULL);
> +							  drm_enc_mode, NULL);
>   	if (rc) {
>   		devm_kfree(dev->dev, dpu_enc);
>   		return ERR_PTR(rc);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index c683cab..0a50509 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -1,5 +1,6 @@
>   // SPDX-License-Identifier: GPL-2.0-only
>   /*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
>    * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
>    * Copyright (C) 2013 Red Hat
>    * Author: Rob Clark <robdclark@gmail.com>
> @@ -15,6 +16,7 @@
>   #include <drm/drm_crtc.h>
>   #include <drm/drm_file.h>
>   #include <drm/drm_vblank.h>
> +#include <drm/drm_writeback.h>
>   
>   #include "msm_drv.h"
>   #include "msm_mmu.h"
> @@ -29,6 +31,7 @@
>   #include "dpu_kms.h"
>   #include "dpu_plane.h"
>   #include "dpu_vbif.h"
> +#include "dpu_writeback.h"
>   
>   #define CREATE_TRACE_POINTS
>   #include "dpu_trace.h"
> @@ -648,6 +651,45 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
>   	return 0;
>   }
>   
> +static int _dpu_kms_initialize_writeback(struct drm_device *dev,
> +		struct msm_drm_private *priv, struct dpu_kms *dpu_kms,
> +		const u32 *wb_formats, int n_formats)
> +{
> +	struct drm_encoder *encoder = NULL;
> +	struct msm_display_info info;
> +	int rc;
> +
> +	encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_VIRTUAL);
> +	if (IS_ERR(encoder)) {
> +		DPU_ERROR("encoder init failed for dsi display\n");
> +		return PTR_ERR(encoder);
> +	}
> +
> +	memset(&info, 0, sizeof(info));
> +
> +	rc = dpu_writeback_init(dev, encoder, wb_formats,
> +			n_formats);
> +	if (rc) {
> +		DPU_ERROR("dpu_writeback_init, rc = %d\n", rc);
> +		drm_encoder_cleanup(encoder);
> +		return rc;
> +	}
> +
> +	info.num_of_h_tiles = 1;
> +	/* use only WB idx 2 instance for DPU */
> +	info.h_tile_instance[0] = WB_2;
> +	info.intf_type = encoder->encoder_type;
> +
> +	rc = dpu_encoder_setup(dev, encoder, &info);
> +	if (rc) {
> +		DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
> +				  encoder->base.id, rc);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
>   /**
>    * _dpu_kms_setup_displays - create encoders, bridges and connectors
>    *                           for underlying displays
> @@ -661,6 +703,7 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
>   				    struct dpu_kms *dpu_kms)
>   {
>   	int rc = 0;
> +	int i;
>   
>   	rc = _dpu_kms_initialize_dsi(dev, priv, dpu_kms);
>   	if (rc) {
> @@ -674,6 +717,21 @@ static int _dpu_kms_setup_displays(struct drm_device *dev,
>   		return rc;
>   	}
>   
> +	/* Since WB isn't a driver check the catalog before initializing */
> +	if (dpu_kms->catalog->wb_count) {
> +		for (i = 0; i < dpu_kms->catalog->wb_count; i++) {
> +			if (dpu_kms->catalog->wb[i].id == WB_2) {
> +				rc = _dpu_kms_initialize_writeback(dev, priv, dpu_kms,
> +						dpu_kms->catalog->wb[i].format_list,
> +						dpu_kms->catalog->wb[i].num_formats);
> +				if (rc) {
> +					DPU_ERROR("initialize_WB failed, rc = %d\n", rc);
> +					return rc;
> +				}
> +			}
> +		}
> +	}
> +
>   	return rc;
>   }
>   


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits for writeback
  2022-04-20  1:46 ` [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits " Abhinav Kumar
@ 2022-04-20  7:55   ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20  7:55 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> kms_writeback test cases also verify with a null fb for the
> writeback connector job. In addition there are also other
> commit paths which can result in kickoffs without a valid
> framebuffer like while closing the fb which results in the
> callback to drm_atomic_helper_dirtyfb() which internally
> triggers a commit.
> 
> Add protection in the dpu driver to ensure that commits for
> writeback encoders without a valid fb are gracefully skipped.
> 
> changes in v2:
> 	- rename dpu_encoder_has_valid_fb to dpu_encoder_is_valid_for_commit
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c            |  9 +++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c         | 21 +++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h         |  6 ++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h    |  1 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 12 ++++++++++++
>   5 files changed, 49 insertions(+)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index 7763558..d65e124 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -869,6 +869,13 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
>   
>   	DPU_ATRACE_BEGIN("crtc_commit");
>   
> +	drm_for_each_encoder_mask(encoder, crtc->dev,
> +			crtc->state->encoder_mask) {
> +		if (!dpu_encoder_is_valid_for_commit(encoder)) {
> +			DRM_DEBUG_ATOMIC("invalid FB not kicking off crtc\n");
> +			goto end;
> +		}
> +	}
>   	/*
>   	 * Encoder will flush/start now, unless it has a tx pending. If so, it
>   	 * may delay and flush at an irq event (e.g. ppdone)
> @@ -891,6 +898,8 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
>   		dpu_encoder_kickoff(encoder);
>   
>   	reinit_completion(&dpu_crtc->frame_done_comp);
> +
> +end:
>   	DPU_ATRACE_END("crtc_commit");
>   }
>   
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index b1475dd..d07e3ee 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1850,6 +1850,27 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
>   		dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
>   }
>   
> +bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
> +{
> +	struct dpu_encoder_virt *dpu_enc;
> +	unsigned int i;
> +	struct dpu_encoder_phys *phys;
> +
> +	dpu_enc = to_dpu_encoder_virt(drm_enc);
> +
> +	if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
> +		for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> +			phys = dpu_enc->phys_encs[i];
> +			if (phys->ops.is_valid_for_commit && !phys->ops.is_valid_for_commit(phys)) {
> +				DPU_DEBUG("invalid FB not kicking off\n");
> +				return false;
> +			}
> +		}
> +	}
> +
> +	return true;
> +}
> +
>   void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
>   {
>   	struct dpu_encoder_virt *dpu_enc;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> index 6ceec1d..781d41c 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> @@ -196,4 +196,10 @@ void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc,
>   void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
>   		struct drm_writeback_job *job);
>   
> +/**
> + * dpu_encoder_is_valid_for_commit - check if encode has valid parameters for commit.
> + * @drm_enc:    Pointer to drm encoder structure
> + */
> +bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc);
> +
>   #endif /* __DPU_ENCODER_H__ */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 5452f98..04d037e 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -142,6 +142,7 @@ struct dpu_encoder_phys_ops {
>   			struct drm_writeback_job *job);
>   	void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc,
>   			struct drm_writeback_job *job);
> +	bool (*is_valid_for_commit)(struct dpu_encoder_phys *phys_enc);
>   };
>   
>   /**
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> index 128317fe..9acbce0 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> @@ -667,6 +667,16 @@ static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc
>   	wb_enc->wb_conn = NULL;
>   }
>   
> +static bool dpu_encoder_phys_wb_is_valid_for_commit(struct dpu_encoder_phys *phys_enc)
> +{
> +	struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
> +
> +	if (wb_enc->wb_job)
> +		return true;
> +	else
> +		return false;
> +}
> +
>   /**
>    * dpu_encoder_phys_wb_init_ops - initialize writeback operations
>    * @ops:	Pointer to encoder operation table
> @@ -687,6 +697,8 @@ static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
>   	ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
>   	ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
>   	ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
> +	ops->is_valid_for_commit = dpu_encoder_phys_wb_is_valid_for_commit;
> +
>   }
>   
>   /**


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20  7:20   ` Dmitry Baryshkov
@ 2022-04-20 17:01     ` Abhinav Kumar
  2022-04-20 17:49       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 17:01 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:20 AM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:45, Abhinav Kumar wrote:
>> Add the dpu_hw_wb abstraction to program registers related to the
>> writeback block. These will be invoked once all the configuration
>> is set and ready to be programmed to the registers.
>>
>> changes in v2:
>>     - remove multiple empty lines at the end of the file
>>     - change dpu_hw_wb_bind_pingpong_blk to preserve upper bits
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> 
> It's still Reviewed-by, few nits below.
> 
>> ---
>>   drivers/gpu/drm/msm/Makefile              |   1 +
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273 
>> ++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
>>   3 files changed, 405 insertions(+)
>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index d5ca2e6..ca779c1 100644
>> --- a/drivers/gpu/drm/msm/Makefile
>> +++ b/drivers/gpu/drm/msm/Makefile
>> @@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>       disp/dpu1/dpu_hw_top.o \
>>       disp/dpu1/dpu_hw_util.o \
>>       disp/dpu1/dpu_hw_vbif.o \
>> +    disp/dpu1/dpu_hw_wb.o \
>>       disp/dpu1/dpu_kms.o \
>>       disp/dpu1/dpu_plane.o \
>>       disp/dpu1/dpu_rm.o \
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>> new file mode 100644
>> index 0000000..afa8aab
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>> @@ -0,0 +1,273 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> + /*
>> +  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved
>> +  */
>> +
>> +#include "dpu_hw_mdss.h"
>> +#include "dpu_hwio.h"
>> +#include "dpu_hw_catalog.h"
>> +#include "dpu_hw_wb.h"
>> +#include "dpu_formats.h"
>> +#include "dpu_kms.h"
>> +
>> +#define WB_DST_FORMAT                         0x000
>> +#define WB_DST_OP_MODE                        0x004
>> +#define WB_DST_PACK_PATTERN                   0x008
>> +#define WB_DST0_ADDR                          0x00C
>> +#define WB_DST1_ADDR                          0x010
>> +#define WB_DST2_ADDR                          0x014
>> +#define WB_DST3_ADDR                          0x018
>> +#define WB_DST_YSTRIDE0                       0x01C
>> +#define WB_DST_YSTRIDE1                       0x020
>> +#define WB_DST_YSTRIDE1                       0x020
>> +#define WB_DST_DITHER_BITDEPTH                0x024
>> +#define WB_DST_MATRIX_ROW0                    0x030
>> +#define WB_DST_MATRIX_ROW1                    0x034
>> +#define WB_DST_MATRIX_ROW2                    0x038
>> +#define WB_DST_MATRIX_ROW3                    0x03C
>> +#define WB_DST_WRITE_CONFIG                   0x048
>> +#define WB_ROTATION_DNSCALER                  0x050
>> +#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
>> +#define WB_N16_INIT_PHASE_X_C03               0x060
>> +#define WB_N16_INIT_PHASE_X_C12               0x064
>> +#define WB_N16_INIT_PHASE_Y_C03               0x068
>> +#define WB_N16_INIT_PHASE_Y_C12               0x06C
>> +#define WB_OUT_SIZE                           0x074
>> +#define WB_ALPHA_X_VALUE                      0x078
>> +#define WB_DANGER_LUT                         0x084
>> +#define WB_SAFE_LUT                           0x088
>> +#define WB_QOS_CTRL                           0x090
>> +#define WB_CREQ_LUT_0                         0x098
>> +#define WB_CREQ_LUT_1                         0x09C
>> +#define WB_UBWC_STATIC_CTRL                   0x144
>> +#define WB_MUX                                0x150
>> +#define WB_CROP_CTRL                          0x154
>> +#define WB_CROP_OFFSET                        0x158
>> +#define WB_CSC_BASE                           0x260
>> +#define WB_DST_ADDR_SW_STATUS                 0x2B0
>> +#define WB_CDP_CNTL                           0x2B4
>> +#define WB_OUT_IMAGE_SIZE                     0x2C0
>> +#define WB_OUT_XY                             0x2C4
>> +
>> +/* WB_QOS_CTRL */
>> +#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
>> +
>> +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
>> +        const struct dpu_mdss_cfg *m, void __iomem *addr,
>> +        struct dpu_hw_blk_reg_map *b)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < m->wb_count; i++) {
>> +        if (wb == m->wb[i].id) {
>> +            b->base_off = addr;
>> +            b->blk_off = m->wb[i].base;
>> +            b->length = m->wb[i].len;
>> +            b->hwversion = m->hwversion;
>> +            return &m->wb[i];
>> +        }
>> +    }
>> +    return ERR_PTR(-EINVAL);
>> +}
>> +
>> +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
>> +        struct dpu_hw_wb_cfg *data)
>> +{
>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>> +
>> +    DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
>> +    DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
>> +    DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
>> +    DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
>> +}
>> +
>> +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
>> +        struct dpu_hw_wb_cfg *data)
>> +{
> 
> This function shares significant logic with dpu_hw_sspp_setup_format().
> 
> We should consider splitting the common code to the helper at some point 
> (later).

Agreed, I do see some similarities. Will take this up in another change.

> 
>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>> +    const struct dpu_format *fmt = data->dest.format;
>> +    u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
>> +    u32 write_config = 0;
>> +    u32 opmode = 0;
>> +    u32 dst_addr_sw = 0;
>> +
>> +    chroma_samp = fmt->chroma_sample;
>> +
>> +    dst_format = (chroma_samp << 23) |
>> +        (fmt->fetch_planes << 19) |
>> +        (fmt->bits[C3_ALPHA] << 6) |
>> +        (fmt->bits[C2_R_Cr] << 4) |
>> +        (fmt->bits[C1_B_Cb] << 2) |
>> +        (fmt->bits[C0_G_Y] << 0);
>> +
>> +    if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
>> +        dst_format |= BIT(8); /* DSTC3_EN */
>> +        if (!fmt->alpha_enable ||
>> +            !(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
>> +            dst_format |= BIT(14); /* DST_ALPHA_X */
>> +    }
>> +
>> +    pattern = (fmt->element[3] << 24) |
>> +        (fmt->element[2] << 16) |
>> +        (fmt->element[1] << 8)  |
>> +        (fmt->element[0] << 0);
>> +
>> +    dst_format |= (fmt->unpack_align_msb << 18) |
>> +        (fmt->unpack_tight << 17) |
>> +        ((fmt->unpack_count - 1) << 12) |
>> +        ((fmt->bpp - 1) << 9);
>> +
>> +    ystride0 = data->dest.plane_pitch[0] |
>> +        (data->dest.plane_pitch[1] << 16);
>> +    ystride1 = data->dest.plane_pitch[2] |
>> +    (data->dest.plane_pitch[3] << 16);
>> +
>> +    if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
>> +        outsize = (drm_rect_height(&data->roi) << 16) | 
>> drm_rect_width(&data->roi);
>> +    else
>> +        outsize = (data->dest.height << 16) | data->dest.width;
>> +
>> +    DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
>> +    DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
>> +    DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
>> +    DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
>> +    DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
>> +    DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
>> +}
>> +
>> +static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg 
>> *wb)
>> +{
>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>> +    u32 image_size, out_size, out_xy;
>> +
>> +    image_size = (wb->dest.height << 16) | wb->dest.width;
>> +    out_xy = 0;
>> +    out_size = (drm_rect_height(&wb->roi) << 16) | 
>> drm_rect_width(&wb->roi);
>> +
>> +    DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
>> +    DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
>> +}
>> +
>> +static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
>> +        struct dpu_hw_wb_qos_cfg *cfg)
> I like the single call approach. Maybe we should adopt it for the SSPP 
> QoS LUT too.

Perhaps yes.

I had an overall question on this. all the dpu_hw_*** APIs accept their 
own unique ctx (which is the *** hardware they are programming). What is 
the approach you are suggesting to unify these?

For helper calls I understood and already have addressed it in this series.

But looking ahead for dpu_hw_*** calls, I am still unclear on the 
unification plan.

Again, this is for a follow-up change but I am just trying to understand it.

> 
>> +{
>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>> +    u32 qos_ctrl = 0;
>> +
>> +    if (!ctx || !cfg)
>> +        return;
>> +
>> +    DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
>> +    DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
>> +
>> +    if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
>> +    }
> 
> Is there a plain WB_CREQ_LUT for the non-8LVL case?
> 
>> +
>> +    if (cfg->danger_safe_en)
>> +        qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
>> +
>> +    DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
>> +}
>> +
>> +static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
>> +        struct dpu_hw_wb_cdp_cfg *cfg)
> 
> Can we use dpu_hw_pipe_cdp_cfg here? Maybe after renaming it to more 
> generic dpu_hw_cdp_cfg.


ack, I can absorb this in this series itself and re-spin this.
It will save us one more new structure.

> 
>> +{
>> +    struct dpu_hw_blk_reg_map *c;
>> +    u32 cdp_cntl = 0;
>> +
>> +    if (!ctx || !cfg)
>> +        return;
>> +
>> +    c = &ctx->hw;
>> +
>> +    if (cfg->enable)
>> +        cdp_cntl |= BIT(0);
>> +    if (cfg->ubwc_meta_enable)
>> +        cdp_cntl |= BIT(1);
>> +    if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
>> +        cdp_cntl |= BIT(3);
>> +
>> +    DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
>> +}
>> +
>> +static void dpu_hw_wb_bind_pingpong_blk(
>> +        struct dpu_hw_wb *ctx,
>> +        bool enable, const enum dpu_pingpong pp)
>> +{
>> +    struct dpu_hw_blk_reg_map *c;
>> +    int mux_cfg;
>> +
>> +    if (!ctx)
>> +        return;
>> +
>> +    c = &ctx->hw;
>> +
>> +    mux_cfg = DPU_REG_READ(c, WB_MUX);
>> +    mux_cfg &= ~0xf;
>> +
>> +    if (enable)
>> +        mux_cfg |= (pp - PINGPONG_0) & 0x7;
>> +    else
>> +        mux_cfg |= 0xf;
>> +
>> +    DPU_REG_WRITE(c, WB_MUX, mux_cfg);
>> +}
>> +
>> +static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
>> +        unsigned long features)
>> +{
>> +    ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
>> +    ops->setup_outformat = dpu_hw_wb_setup_format;
>> +
>> +    if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
>> +        ops->setup_roi = dpu_hw_wb_roi;
>> +
>> +    if (test_bit(DPU_WB_QOS, &features))
>> +        ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
>> +
>> +    if (test_bit(DPU_WB_CDP, &features))
>> +        ops->setup_cdp = dpu_hw_wb_setup_cdp;
>> +
>> +    if (test_bit(DPU_WB_INPUT_CTRL, &features))
>> +        ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
>> +}
>> +
>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
>> +        void __iomem *addr, const struct dpu_mdss_cfg *m)
>> +{
>> +    struct dpu_hw_wb *c;
>> +    const struct dpu_wb_cfg *cfg;
>> +
>> +    if (!addr || !m)
>> +        return ERR_PTR(-EINVAL);
>> +
>> +    c = kzalloc(sizeof(*c), GFP_KERNEL);
>> +    if (!c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    cfg = _wb_offset(idx, m, addr, &c->hw);
>> +    if (IS_ERR(cfg)) {
>> +        WARN(1, "Unable to find wb idx=%d\n", idx);
>> +        kfree(c);
>> +        return ERR_PTR(-EINVAL);
>> +    }
>> +
>> +    /* Assign ops */
>> +    c->mdp = &m->mdp[0];
>> +    c->idx = idx;
>> +    c->caps = cfg;
>> +    _setup_wb_ops(&c->ops, c->caps->features);
>> +
>> +    return c;
>> +}
>> +
>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
>> +{
>> +    kfree(hw_wb);
>> +}
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>> new file mode 100644
>> index 0000000..80def96
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>> @@ -0,0 +1,131 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved
>> + */
>> +
>> +#ifndef _DPU_HW_WB_H
>> +#define _DPU_HW_WB_H
>> +
>> +#include "dpu_hw_catalog.h"
>> +#include "dpu_hw_mdss.h"
>> +#include "dpu_hw_top.h"
>> +#include "dpu_hw_util.h"
>> +#include "dpu_hw_pingpong.h"
>> +
>> +struct dpu_hw_wb;
>> +
>> +struct dpu_hw_wb_cfg {
>> +    struct dpu_hw_fmt_layout dest;
>> +    enum dpu_intf_mode intf_mode;
>> +    struct drm_rect roi;
>> +    struct drm_rect crop;
>> +};
>> +
>> +/**
>> + * enum CDP preload ahead address size
>> + */
>> +enum {
>> +    DPU_WB_CDP_PRELOAD_AHEAD_32,
>> +    DPU_WB_CDP_PRELOAD_AHEAD_64
>> +};
>> +
>> +/**
>> + * struct dpu_hw_wb_cdp_cfg : CDP configuration
>> + * @enable: true to enable CDP
>> + * @ubwc_meta_enable: true to enable ubwc metadata preload
>> + * @tile_amortize_enable: true to enable amortization control for 
>> tile format
>> + * @preload_ahead: number of request to preload ahead
>> + * SDE_WB_CDP_PRELOAD_AHEAD_32,
>> + * SDE_WB_CDP_PRELOAD_AHEAD_64
>> + */
>> +struct dpu_hw_wb_cdp_cfg {
>> +    bool enable;
>> +    bool ubwc_meta_enable;
>> +    bool tile_amortize_enable;
>> +    u32 preload_ahead;
>> +};
>> +
>> +/**
>> + * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
>> + * @danger_lut: LUT for generate danger level based on fill level
>> + * @safe_lut: LUT for generate safe level based on fill level
>> + * @creq_lut: LUT for generate creq level based on fill level
>> + * @danger_safe_en: enable danger safe generation
>> + */
>> +struct dpu_hw_wb_qos_cfg {
>> +    u32 danger_lut;
>> +    u32 safe_lut;
>> +    u64 creq_lut;
>> +    bool danger_safe_en;
>> +};
>> +
>> +/**
>> + *
>> + * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
>> + *  Assumption is these functions will be called after clocks are 
>> enabled
>> + *  @setup_outaddress: setup output address from the writeback job
>> + *  @setup_outformat: setup output format of writeback block from 
>> writeback job
>> + *  @setup_qos_lut:   setup qos LUT for writeback block based on input
>> + *  @setup_cdp:       setup chroma down prefetch block for writeback 
>> block
>> + *  @bind_pingpong_blk: enable/disable the connection with ping-pong 
>> block
>> + */
>> +struct dpu_hw_wb_ops {
>> +    void (*setup_outaddress)(struct dpu_hw_wb *ctx,
>> +            struct dpu_hw_wb_cfg *wb);
>> +
>> +    void (*setup_outformat)(struct dpu_hw_wb *ctx,
>> +            struct dpu_hw_wb_cfg *wb);
>> +
>> +    void (*setup_roi)(struct dpu_hw_wb *ctx,
>> +            struct dpu_hw_wb_cfg *wb);
>> +
>> +    void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
>> +            struct dpu_hw_wb_qos_cfg *cfg);
>> +
>> +    void (*setup_cdp)(struct dpu_hw_wb *ctx,
>> +            struct dpu_hw_wb_cdp_cfg *cfg);
>> +
>> +    void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
>> +            bool enable, const enum dpu_pingpong pp);
>> +};
>> +
>> +/**
>> + * struct dpu_hw_wb : WB driver object
>> + * @hw: block hardware details
>> + * @mdp: pointer to associated mdp portion of the catalog
>> + * @idx: hardware index number within type
>> + * @wb_hw_caps: hardware capabilities
>> + * @ops: function pointers
>> + * @hw_mdp: MDP top level hardware block
>> + */
>> +struct dpu_hw_wb {
>> +    struct dpu_hw_blk_reg_map hw;
>> +    const struct dpu_mdp_cfg *mdp;
>> +
>> +    /* wb path */
>> +    int idx;
>> +    const struct dpu_wb_cfg *caps;
>> +
>> +    /* ops */
>> +    struct dpu_hw_wb_ops ops;
>> +
>> +    struct dpu_hw_mdp *hw_mdp;
>> +};
>> +
>> +/**
>> + * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
>> + * @idx:  wb_path index for which driver object is required
>> + * @addr: mapped register io address of MDP
>> + * @m :   pointer to mdss catalog data
>> + */
>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
>> +        void __iomem *addr,
>> +        const struct dpu_mdss_cfg *m);
>> +
>> +/**
>> + * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
>> + * @hw_wb:  Pointer to writeback hw driver object
>> + */
>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
>> +
>> +#endif /*_DPU_HW_WB_H */
> 
> 

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

* Re: [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl
  2022-04-20  6:59   ` Dmitry Baryshkov
@ 2022-04-20 17:16     ` Abhinav Kumar
  2022-04-20 18:48       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 17:16 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/19/2022 11:59 PM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:46, Abhinav Kumar wrote:
>> Add changes to support writeback module in the dpu_hw_ctl
>> interface.
>>
>> changes in v2:
>>     - keep only the wb specific changes to reset_intf_cfg
>>     - use cfg->intf / cfg->wb to identify intf or wb
>>     - use bit-wise OR for the wb bits while programming
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43 
>> +++++++++++++++++++++++++++---
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 15 ++++++++++-
>>   2 files changed, 53 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
>> index 524f024..495a9cd 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
>> @@ -1,5 +1,6 @@
>>   // SPDX-License-Identifier: GPL-2.0-only
>> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>>    */
>>   #include <linux/delay.h>
>> @@ -23,10 +24,12 @@
>>   #define   CTL_SW_RESET                  0x030
>>   #define   CTL_LAYER_EXTN_OFFSET         0x40
>>   #define   CTL_MERGE_3D_ACTIVE           0x0E4
>> +#define   CTL_WB_ACTIVE                 0x0EC
>>   #define   CTL_INTF_ACTIVE               0x0F4
>>   #define   CTL_MERGE_3D_FLUSH            0x100
>>   #define   CTL_DSC_ACTIVE                0x0E8
>>   #define   CTL_DSC_FLUSH                0x104
>> +#define   CTL_WB_FLUSH                  0x108
>>   #define   CTL_INTF_FLUSH                0x110
>>   #define   CTL_INTF_MASTER               0x134
>>   #define   CTL_FETCH_PIPE_ACTIVE         0x0FC
>> @@ -38,6 +41,7 @@
>>   #define  MERGE_3D_IDX   23
>>   #define  DSC_IDX        22
>>   #define  INTF_IDX       31
>> +#define WB_IDX          16
>>   #define CTL_INVALID_BIT                 0xffff
>>   #define CTL_DEFAULT_GROUP_ID        0xf
>> @@ -135,6 +139,9 @@ static inline void 
>> dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
>>       if (ctx->pending_flush_mask & BIT(INTF_IDX))
>>           DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH,
>>                   ctx->pending_intf_flush_mask);
>> +    if (ctx->pending_flush_mask & BIT(WB_IDX))
>> +        DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
>> +                ctx->pending_wb_flush_mask);
>>       DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
>>   }
>> @@ -255,6 +262,13 @@ static void 
>> dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx,
>>       }
>>   }
>> +static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl 
>> *ctx,
>> +        enum dpu_wb wb)
>> +{
>> +    ctx->pending_wb_flush_mask |= BIT(wb - WB_0);
>> +    ctx->pending_flush_mask |= BIT(WB_IDX);
>> +}
>> +
>>   static void dpu_hw_ctl_update_pending_flush_intf_v1(struct 
>> dpu_hw_ctl *ctx,
>>           enum dpu_intf intf)
>>   {
>> @@ -504,6 +518,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct 
>> dpu_hw_ctl *ctx,
>>   {
>>       struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>       u32 intf_active = 0;
>> +    u32 wb_active = 0;
>>       u32 mode_sel = 0;
>>       /* CTL_TOP[31:28] carries group_id to collate CTL paths
>> @@ -519,11 +534,20 @@ static void dpu_hw_ctl_intf_cfg_v1(struct 
>> dpu_hw_ctl *ctx,
>>       if (cfg->intf_mode_sel == DPU_CTL_MODE_SEL_CMD)
>>           mode_sel |= BIT(17);
>> -    intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
>> -    intf_active |= BIT(cfg->intf - INTF_0);
>> +    if (cfg->intf) {
>> +        intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
>> +        intf_active |= BIT(cfg->intf - INTF_0);
>> +    }
>> +
>> +    if (cfg->wb) {
>> +        wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
>> +        wb_active |= BIT(cfg->wb - WB_0);
>> +    }
>>       DPU_REG_WRITE(c, CTL_TOP, mode_sel);
>>       DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
>> +    DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
> 
> This will not work as expected. If cfg->intf is not set, CTL_INTF_ACTIVE 
> will be reset to 0 (while it should have been retained). Please change 
> this to always read CTL_INTF_ACTIVE/CTL_WB_ACTIVE.

ack, and thanks for catching this.
Yes, i need to add the always read part back.

> 
>> +
>>       if (cfg->merge_3d)
>>           DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
>>                     BIT(cfg->merge_3d - MERGE_3D_0));
>> @@ -546,6 +570,9 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl 
>> *ctx,
>>           intf_cfg |= (cfg->mode_3d - 0x1) << 20;
>>       }
>> +    if (cfg->wb)
>> +        intf_cfg |= (cfg->wb & 0x3) + 2;
>> +
> 
> Ugh. I see that we have the same code in downstream driver. And that we 
> do not support WB_0 at all. But maybe we should be more explicit here.

Sorry, I didnt follow this comment. Why is this related to WB_0?

All this code is doing is that its programming the lower bits of CTL_TOP 
register to be used for WB mode.

The correct value of this register for linear wb mode which we use is 0x5.

Which will still be correct now because cfg->wb will be 0x3.

Coming to other non-WB_2 values, this code is still correct.

Lets say cfg->wb was 0x1 ( for WB_0), then the register will be 
programmed to 0x3 which is the correct value to use because then we will 
be using rotation and not linear writeback.

Perhaps, you need a comment here to explain this?

> 
>>       switch (cfg->intf_mode_sel) {
>>       case DPU_CTL_MODE_SEL_VID:
>>           intf_cfg &= ~BIT(17);
>> @@ -568,12 +595,13 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct 
>> dpu_hw_ctl *ctx,
>>   {
>>       struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>       u32 intf_active = 0;
>> +    u32 wb_active = 0;
>>       u32 merge3d_active = 0;
>>       /*
>>        * This API resets each portion of the CTL path namely,
>>        * clearing the sspps staged on the lm, merge_3d block,
>> -     * interfaces etc to ensure clean teardown of the pipeline.
>> +     * interfaces , writeback etc to ensure clean teardown of the 
>> pipeline.
>>        * This will be used for writeback to begin with to have a
>>        * proper teardown of the writeback session but upon further
>>        * validation, this can be extended to all interfaces.
>> @@ -592,6 +620,12 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct 
>> dpu_hw_ctl *ctx,
>>           intf_active &= ~BIT(cfg->intf - INTF_0);
>>           DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
>>       }
>> +
>> +    if (cfg->wb) {
>> +        wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
>> +        wb_active &= ~BIT(cfg->wb - WB_0);
>> +        DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
>> +    }
>>   }
>>   static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
>> @@ -622,6 +656,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops 
>> *ops,
>>               dpu_hw_ctl_update_pending_flush_intf_v1;
>>           ops->update_pending_flush_merge_3d =
>>               dpu_hw_ctl_update_pending_flush_merge_3d_v1;
>> +        ops->update_pending_flush_wb = 
>> dpu_hw_ctl_update_pending_flush_wb_v1;
> 
> Do we also need the update_pending_flush_wb for non-ACTIVE_CTL case? I 
> think we do.

Yes, but the bits will be different. I can update it.

> 
>>       } else {
>>           ops->trigger_flush = dpu_hw_ctl_trigger_flush;
>>           ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
>> index c61a8fd..df8f8e9 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
>> @@ -1,5 +1,6 @@
>>   /* SPDX-License-Identifier: GPL-2.0-only */
>> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>>    */
>>   #ifndef _DPU_HW_CTL_H
>> @@ -44,6 +45,7 @@ struct dpu_hw_stage_cfg {
>>    */
>>   struct dpu_hw_intf_cfg {
>>       enum dpu_intf intf;
>> +    enum dpu_wb wb;
>>       enum dpu_3d_blend_mode mode_3d;
>>       enum dpu_merge_3d merge_3d;
>>       enum dpu_ctl_mode_sel intf_mode_sel;
>> @@ -102,6 +104,15 @@ struct dpu_hw_ctl_ops {
>>           u32 flushbits);
>>       /**
>> +     * OR in the given flushbits to the cached pending_(wb_)flush_mask
>> +     * No effect on hardware
>> +     * @ctx       : ctl path ctx pointer
>> +     * @blk       : writeback block index
>> +     */
>> +    void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx,
>> +        enum dpu_wb blk);
>> +
>> +    /**
>>        * OR in the given flushbits to the cached 
>> pending_(intf_)flush_mask
>>        * No effect on hardware
>>        * @ctx       : ctl path ctx pointer
>> @@ -199,6 +210,7 @@ struct dpu_hw_ctl_ops {
>>    * @mixer_hw_caps: mixer hardware capabilities
>>    * @pending_flush_mask: storage for pending ctl_flush managed via ops
>>    * @pending_intf_flush_mask: pending INTF flush
>> + * @pending_wb_flush_mask: pending WB flush
>>    * @ops: operation list
>>    */
>>   struct dpu_hw_ctl {
>> @@ -212,6 +224,7 @@ struct dpu_hw_ctl {
>>       const struct dpu_lm_cfg *mixer_hw_caps;
>>       u32 pending_flush_mask;
>>       u32 pending_intf_flush_mask;
>> +    u32 pending_wb_flush_mask;
>>       u32 pending_merge_3d_flush_mask;
>>       /* ops */
> 
> 

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

* Re: [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20  7:44   ` Dmitry Baryshkov
@ 2022-04-20 17:41     ` Abhinav Kumar
  2022-04-20 18:37       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 17:41 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:44 AM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:46, Abhinav Kumar wrote:
>> Make changes to dpu_encoder to support virtual encoder needed
>> to support writeback for dpu.
>>
>> changes in v2:
>>     - add the writeback parts to dpu_encoder_helper_phys_cleanup
>>     - rebase on tip of msm-next and fix related dependencies
>>     - get the writeback blocks directly from RM
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71 
>> +++++++++++++++++-------
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
>>   2 files changed, 54 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> index 0e31ad3..06b8631 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> @@ -928,6 +928,7 @@ static void 
>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>       struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
>>       int num_lm, num_ctl, num_pp, num_dsc;
>>       unsigned int dsc_mask = 0;
>> +    enum dpu_hw_blk_type blk_type;
>>       int i;
>>       if (!drm_enc) {
>> @@ -1009,12 +1010,21 @@ static void 
>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>           phys->hw_pp = dpu_enc->hw_pp[i];
>>           phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>> -        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
>> -            phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, 
>> phys->intf_idx);
>> +        if (dpu_encoder_get_intf_mode(&dpu_enc->base) == 
>> INTF_MODE_WB_LINE)
>> +            blk_type = DPU_HW_BLK_WB;
>> +        else
>> +            blk_type = DPU_HW_BLK_INTF;
>> +
>> +        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
>> +            if (blk_type == DPU_HW_BLK_INTF)
>> +                phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, 
>> phys->intf_idx);
>> +            else if (blk_type == DPU_HW_BLK_WB)
>> +                phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm, 
>> phys->intf_idx);
>> +        }
>> -        if (!phys->hw_intf) {
>> +        if (!phys->hw_intf && !phys->hw_wb) {
>>               DPU_ERROR_ENC(dpu_enc,
>> -                      "no intf block assigned at idx: %d\n", i);
>> +                      "no intf ow wb block assigned at idx: %d\n", i);
> 
> or wb
ack
> 
>>               return;
>>           }
>> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct 
>> drm_encoder *drm_enc)
>>       mutex_unlock(&dpu_enc->enc_lock);
>>   }
>> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
>> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg 
>> *catalog,
>>           enum dpu_intf_type type, u32 controller_id)
>>   {
>>       int i = 0;
>> -    for (i = 0; i < catalog->intf_count; i++) {
>> -        if (catalog->intf[i].type == type
>> -            && catalog->intf[i].controller_id == controller_id) {
>> -            return catalog->intf[i].id;
>> +    if (type != INTF_WB) {
>> +        for (i = 0; i < catalog->intf_count; i++) {
>> +            if (catalog->intf[i].type == type
>> +                && catalog->intf[i].controller_id == controller_id) {
>> +                return catalog->intf[i].id;
>> +            }
>> +        }
>> +    } else {
>> +        for (i = 0; i < catalog->wb_count; i++) {
>> +            if (catalog->wb[i].id == controller_id)
>> +                return catalog->wb[i].id;
>>           }
>>       }
>> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct 
>> dpu_encoder_phys *phys_enc)
>>       dpu_encoder_helper_reset_mixers(phys_enc);
>> -    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>> -        if (dpu_enc->phys_encs[i] && 
>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>> -            phys_enc->hw_intf->ops.bind_pingpong_blk(
>> -                    dpu_enc->phys_encs[i]->hw_intf, false,
>> -                    dpu_enc->phys_encs[i]->hw_pp->idx);
>> +    if (phys_enc->hw_wb) {
> 
> I think this adds a hidden knowledge here. That there is always just a 
> single phys_enc for the WB encoder. I'd still do this cleanup in a loop 
> together with the INTF cleanup.
alright, I can make this change.
> 
>> +        /* disable the PP block */
>> +        if (phys_enc->hw_wb->ops.bind_pingpong_blk)
>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, 
>> false,
>> +                    phys_enc->hw_pp->idx);
>> -        /* mark INTF flush as pending */
>> -        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>> -            
>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>> -                    dpu_enc->phys_encs[i]->hw_intf->idx);
>> +        /* mark WB flush as pending */
>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
>> +            phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, 
>> phys_enc->hw_wb->idx);
>> +    } else {
>> +        for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>> +            if (dpu_enc->phys_encs[i] && 
>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>> +                phys_enc->hw_intf->ops.bind_pingpong_blk(
>> +                        dpu_enc->phys_encs[i]->hw_intf, false,
>> +                        dpu_enc->phys_encs[i]->hw_pp->idx);
>> +
>> +            /* mark INTF flush as pending */
>> +            if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>> +                
>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>> +                        dpu_enc->phys_encs[i]->hw_intf->idx);
>> +        }
>>       }
>>       /* reset the merge 3D HW block */
>> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct 
>> dpu_encoder_virt *dpu_enc,
>>       case DRM_MODE_ENCODER_TMDS:
>>           intf_type = INTF_DP;
>>           break;
>> +    case DRM_MODE_ENCODER_VIRTUAL:
>> +        intf_type = INTF_WB;
>> +        break;
>>       }
>>       WARN_ON(disp_info->num_of_h_tiles < 1);
>> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct 
>> dpu_encoder_virt *dpu_enc,
>>           DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>>                   i, controller_id, phys_params.split_role);
>> -        phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
>> +        phys_params.intf_idx = 
>> dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
>>                                                       intf_type,
>>                                                       controller_id);
> 
> I'd prefer to be more explicit here. intf_idx is of type enum dpu_intf, 
> while WBs are enumerated with enum dpu_wb.
> 
> I's suggest adding a separate phys_params.wb_idx and a 
> dpu_encoder_get_wb() and calling one here depending on intf_type.
> 
> Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check 
> intf_mode, but get both intf and wb and report an error if none was 
> provided.

Your suggestion is valid and I also thought about it.

Let me explain what prevented me from making the change here and put it 
in a to-do bucket.

1) This needs a slighly bigger cleanup including the traces, debug 
prints and some helpers as none of them are aware of the wb_idx

2) Some of the checks need to be adjusted like this one

if (phys_params.intf_idx == INTF_MAX) {
             DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d, 
id %d\n",
                           intf_type, controller_id);
             ret = -EINVAL;
         }

So, when we have a separate intf_idx and a wb_idx, having either one of 
them is enough . Consider a case where a board has no physical display.
That would have no intf_idx but wb_idx can be valid.

So i think this needs a little careful cleanup.

Considering that I need to test that out more, I decided that for a 
short-term we can live with the fact that wb_idx is of type enum 
dpu_intf because dpu_encoder layer understands only that.

Let me know if you agree on this separation to go as a follow-up.

> 
>>           if (phys_params.intf_idx == INTF_MAX) {
>> -            DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id 
>> %d\n",
>> +            DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type 
>> %d, id %d\n",
>>                             intf_type, controller_id);
>>               ret = -EINVAL;
>>           }
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> index 544a9a4..0b80af4 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> @@ -11,6 +11,7 @@
>>   #include "dpu_kms.h"
>>   #include "dpu_hw_intf.h"
>> +#include "dpu_hw_wb.h"
>>   #include "dpu_hw_pingpong.h"
>>   #include "dpu_hw_ctl.h"
>>   #include "dpu_hw_top.h"
>> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
>>    * @hw_ctl:        Hardware interface to the ctl registers
>>    * @hw_pp:        Hardware interface to the ping pong registers
>>    * @hw_intf:        Hardware interface to the intf registers
>> + * @hw_wb:        Hardware interface to the wb registers
>>    * @dpu_kms:        Pointer to the dpu_kms top level
>>    * @cached_mode:    DRM mode cached at mode_set time, acted on in 
>> enable
>>    * @enabled:        Whether the encoder has enabled and running a mode
>> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
>>       struct dpu_hw_ctl *hw_ctl;
>>       struct dpu_hw_pingpong *hw_pp;
>>       struct dpu_hw_intf *hw_intf;
>> +    struct dpu_hw_wb *hw_wb;
>>       struct dpu_kms *dpu_kms;
>>       struct drm_display_mode cached_mode;
>>       enum dpu_enc_split_role split_role;
> 
> 

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

* Re: [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20 17:01     ` Abhinav Kumar
@ 2022-04-20 17:49       ` Dmitry Baryshkov
  2022-04-20 18:11         ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 17:49 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On Wed, 20 Apr 2022 at 20:01, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>
>
>
> On 4/20/2022 12:20 AM, Dmitry Baryshkov wrote:
> > On 20/04/2022 04:45, Abhinav Kumar wrote:
> >> Add the dpu_hw_wb abstraction to program registers related to the
> >> writeback block. These will be invoked once all the configuration
> >> is set and ready to be programmed to the registers.
> >>
> >> changes in v2:
> >>     - remove multiple empty lines at the end of the file
> >>     - change dpu_hw_wb_bind_pingpong_blk to preserve upper bits
> >>
> >> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> >
> > It's still Reviewed-by, few nits below.
> >
> >> ---
> >>   drivers/gpu/drm/msm/Makefile              |   1 +
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273
> >> ++++++++++++++++++++++++++++++
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
> >>   3 files changed, 405 insertions(+)
> >>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >>
> >> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> >> index d5ca2e6..ca779c1 100644
> >> --- a/drivers/gpu/drm/msm/Makefile
> >> +++ b/drivers/gpu/drm/msm/Makefile
> >> @@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> >>       disp/dpu1/dpu_hw_top.o \
> >>       disp/dpu1/dpu_hw_util.o \
> >>       disp/dpu1/dpu_hw_vbif.o \
> >> +    disp/dpu1/dpu_hw_wb.o \
> >>       disp/dpu1/dpu_kms.o \
> >>       disp/dpu1/dpu_plane.o \
> >>       disp/dpu1/dpu_rm.o \
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >> new file mode 100644
> >> index 0000000..afa8aab
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >> @@ -0,0 +1,273 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> + /*
> >> +  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >> reserved
> >> +  */
> >> +
> >> +#include "dpu_hw_mdss.h"
> >> +#include "dpu_hwio.h"
> >> +#include "dpu_hw_catalog.h"
> >> +#include "dpu_hw_wb.h"
> >> +#include "dpu_formats.h"
> >> +#include "dpu_kms.h"
> >> +
> >> +#define WB_DST_FORMAT                         0x000
> >> +#define WB_DST_OP_MODE                        0x004
> >> +#define WB_DST_PACK_PATTERN                   0x008
> >> +#define WB_DST0_ADDR                          0x00C
> >> +#define WB_DST1_ADDR                          0x010
> >> +#define WB_DST2_ADDR                          0x014
> >> +#define WB_DST3_ADDR                          0x018
> >> +#define WB_DST_YSTRIDE0                       0x01C
> >> +#define WB_DST_YSTRIDE1                       0x020
> >> +#define WB_DST_YSTRIDE1                       0x020
> >> +#define WB_DST_DITHER_BITDEPTH                0x024
> >> +#define WB_DST_MATRIX_ROW0                    0x030
> >> +#define WB_DST_MATRIX_ROW1                    0x034
> >> +#define WB_DST_MATRIX_ROW2                    0x038
> >> +#define WB_DST_MATRIX_ROW3                    0x03C
> >> +#define WB_DST_WRITE_CONFIG                   0x048
> >> +#define WB_ROTATION_DNSCALER                  0x050
> >> +#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
> >> +#define WB_N16_INIT_PHASE_X_C03               0x060
> >> +#define WB_N16_INIT_PHASE_X_C12               0x064
> >> +#define WB_N16_INIT_PHASE_Y_C03               0x068
> >> +#define WB_N16_INIT_PHASE_Y_C12               0x06C
> >> +#define WB_OUT_SIZE                           0x074
> >> +#define WB_ALPHA_X_VALUE                      0x078
> >> +#define WB_DANGER_LUT                         0x084
> >> +#define WB_SAFE_LUT                           0x088
> >> +#define WB_QOS_CTRL                           0x090
> >> +#define WB_CREQ_LUT_0                         0x098
> >> +#define WB_CREQ_LUT_1                         0x09C
> >> +#define WB_UBWC_STATIC_CTRL                   0x144
> >> +#define WB_MUX                                0x150
> >> +#define WB_CROP_CTRL                          0x154
> >> +#define WB_CROP_OFFSET                        0x158
> >> +#define WB_CSC_BASE                           0x260
> >> +#define WB_DST_ADDR_SW_STATUS                 0x2B0
> >> +#define WB_CDP_CNTL                           0x2B4
> >> +#define WB_OUT_IMAGE_SIZE                     0x2C0
> >> +#define WB_OUT_XY                             0x2C4
> >> +
> >> +/* WB_QOS_CTRL */
> >> +#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
> >> +
> >> +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
> >> +        const struct dpu_mdss_cfg *m, void __iomem *addr,
> >> +        struct dpu_hw_blk_reg_map *b)
> >> +{
> >> +    int i;
> >> +
> >> +    for (i = 0; i < m->wb_count; i++) {
> >> +        if (wb == m->wb[i].id) {
> >> +            b->base_off = addr;
> >> +            b->blk_off = m->wb[i].base;
> >> +            b->length = m->wb[i].len;
> >> +            b->hwversion = m->hwversion;
> >> +            return &m->wb[i];
> >> +        }
> >> +    }
> >> +    return ERR_PTR(-EINVAL);
> >> +}
> >> +
> >> +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
> >> +        struct dpu_hw_wb_cfg *data)
> >> +{
> >> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >> +
> >> +    DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
> >> +    DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
> >> +    DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
> >> +    DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
> >> +}
> >> +
> >> +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
> >> +        struct dpu_hw_wb_cfg *data)
> >> +{
> >
> > This function shares significant logic with dpu_hw_sspp_setup_format().
> >
> > We should consider splitting the common code to the helper at some point
> > (later).
>
> Agreed, I do see some similarities. Will take this up in another change.

As I wrote, this can be a separate update.

>
> >
> >> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >> +    const struct dpu_format *fmt = data->dest.format;
> >> +    u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
> >> +    u32 write_config = 0;
> >> +    u32 opmode = 0;
> >> +    u32 dst_addr_sw = 0;
> >> +
> >> +    chroma_samp = fmt->chroma_sample;
> >> +
> >> +    dst_format = (chroma_samp << 23) |
> >> +        (fmt->fetch_planes << 19) |
> >> +        (fmt->bits[C3_ALPHA] << 6) |
> >> +        (fmt->bits[C2_R_Cr] << 4) |
> >> +        (fmt->bits[C1_B_Cb] << 2) |
> >> +        (fmt->bits[C0_G_Y] << 0);
> >> +
> >> +    if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
> >> +        dst_format |= BIT(8); /* DSTC3_EN */
> >> +        if (!fmt->alpha_enable ||
> >> +            !(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
> >> +            dst_format |= BIT(14); /* DST_ALPHA_X */
> >> +    }
> >> +
> >> +    pattern = (fmt->element[3] << 24) |
> >> +        (fmt->element[2] << 16) |
> >> +        (fmt->element[1] << 8)  |
> >> +        (fmt->element[0] << 0);
> >> +
> >> +    dst_format |= (fmt->unpack_align_msb << 18) |
> >> +        (fmt->unpack_tight << 17) |
> >> +        ((fmt->unpack_count - 1) << 12) |
> >> +        ((fmt->bpp - 1) << 9);
> >> +
> >> +    ystride0 = data->dest.plane_pitch[0] |
> >> +        (data->dest.plane_pitch[1] << 16);
> >> +    ystride1 = data->dest.plane_pitch[2] |
> >> +    (data->dest.plane_pitch[3] << 16);
> >> +
> >> +    if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
> >> +        outsize = (drm_rect_height(&data->roi) << 16) |
> >> drm_rect_width(&data->roi);
> >> +    else
> >> +        outsize = (data->dest.height << 16) | data->dest.width;
> >> +
> >> +    DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
> >> +    DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
> >> +    DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
> >> +    DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
> >> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
> >> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
> >> +    DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
> >> +    DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
> >> +    DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
> >> +}
> >> +
> >> +static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg
> >> *wb)
> >> +{
> >> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >> +    u32 image_size, out_size, out_xy;
> >> +
> >> +    image_size = (wb->dest.height << 16) | wb->dest.width;
> >> +    out_xy = 0;
> >> +    out_size = (drm_rect_height(&wb->roi) << 16) |
> >> drm_rect_width(&wb->roi);
> >> +
> >> +    DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
> >> +    DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
> >> +    DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
> >> +}
> >> +
> >> +static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
> >> +        struct dpu_hw_wb_qos_cfg *cfg)
> > I like the single call approach. Maybe we should adopt it for the SSPP
> > QoS LUT too.
>
> Perhaps yes.
>
> I had an overall question on this. all the dpu_hw_*** APIs accept their
> own unique ctx (which is the *** hardware they are programming). What is
> the approach you are suggesting to unify these?
>
> For helper calls I understood and already have addressed it in this series.
>
> But looking ahead for dpu_hw_*** calls, I am still unclear on the
> unification plan.
>
> Again, this is for a follow-up change but I am just trying to understand it.

I liked that this function programs all QoS LUT params. (compared to
dpu_hw_sspp_setup_qos_ctrl() + dpu_hw_sspp_setup_creq_lut()

>
> >
> >> +{
> >> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >> +    u32 qos_ctrl = 0;
> >> +
> >> +    if (!ctx || !cfg)
> >> +        return;
> >> +
> >> +    DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
> >> +    DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
> >> +
> >> +    if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
> >> +        DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
> >> +        DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
> >> +    }
> >
> > Is there a plain WB_CREQ_LUT for the non-8LVL case?

I was comparing the SSPP with WB programming. So if possible could you
please check?

> >> +
> >> +    if (cfg->danger_safe_en)
> >> +        qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
> >> +
> >> +    DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
> >> +}
> >> +
> >> +static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
> >> +        struct dpu_hw_wb_cdp_cfg *cfg)
> >
> > Can we use dpu_hw_pipe_cdp_cfg here? Maybe after renaming it to more
> > generic dpu_hw_cdp_cfg.
>
>
> ack, I can absorb this in this series itself and re-spin this.
> It will save us one more new structure.

Good!

> >> +{
> >> +    struct dpu_hw_blk_reg_map *c;
> >> +    u32 cdp_cntl = 0;
> >> +
> >> +    if (!ctx || !cfg)
> >> +        return;
> >> +
> >> +    c = &ctx->hw;
> >> +
> >> +    if (cfg->enable)
> >> +        cdp_cntl |= BIT(0);
> >> +    if (cfg->ubwc_meta_enable)
> >> +        cdp_cntl |= BIT(1);
> >> +    if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
> >> +        cdp_cntl |= BIT(3);
> >> +
> >> +    DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
> >> +}
> >> +
> >> +static void dpu_hw_wb_bind_pingpong_blk(
> >> +        struct dpu_hw_wb *ctx,
> >> +        bool enable, const enum dpu_pingpong pp)
> >> +{
> >> +    struct dpu_hw_blk_reg_map *c;
> >> +    int mux_cfg;
> >> +
> >> +    if (!ctx)
> >> +        return;
> >> +
> >> +    c = &ctx->hw;
> >> +
> >> +    mux_cfg = DPU_REG_READ(c, WB_MUX);
> >> +    mux_cfg &= ~0xf;
> >> +
> >> +    if (enable)
> >> +        mux_cfg |= (pp - PINGPONG_0) & 0x7;
> >> +    else
> >> +        mux_cfg |= 0xf;
> >> +
> >> +    DPU_REG_WRITE(c, WB_MUX, mux_cfg);
> >> +}
> >> +
> >> +static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
> >> +        unsigned long features)
> >> +{
> >> +    ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
> >> +    ops->setup_outformat = dpu_hw_wb_setup_format;
> >> +
> >> +    if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
> >> +        ops->setup_roi = dpu_hw_wb_roi;
> >> +
> >> +    if (test_bit(DPU_WB_QOS, &features))
> >> +        ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
> >> +
> >> +    if (test_bit(DPU_WB_CDP, &features))
> >> +        ops->setup_cdp = dpu_hw_wb_setup_cdp;
> >> +
> >> +    if (test_bit(DPU_WB_INPUT_CTRL, &features))
> >> +        ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
> >> +}
> >> +
> >> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> >> +        void __iomem *addr, const struct dpu_mdss_cfg *m)
> >> +{
> >> +    struct dpu_hw_wb *c;
> >> +    const struct dpu_wb_cfg *cfg;
> >> +
> >> +    if (!addr || !m)
> >> +        return ERR_PTR(-EINVAL);
> >> +
> >> +    c = kzalloc(sizeof(*c), GFP_KERNEL);
> >> +    if (!c)
> >> +        return ERR_PTR(-ENOMEM);
> >> +
> >> +    cfg = _wb_offset(idx, m, addr, &c->hw);
> >> +    if (IS_ERR(cfg)) {
> >> +        WARN(1, "Unable to find wb idx=%d\n", idx);
> >> +        kfree(c);
> >> +        return ERR_PTR(-EINVAL);
> >> +    }
> >> +
> >> +    /* Assign ops */
> >> +    c->mdp = &m->mdp[0];
> >> +    c->idx = idx;
> >> +    c->caps = cfg;
> >> +    _setup_wb_ops(&c->ops, c->caps->features);
> >> +
> >> +    return c;
> >> +}
> >> +
> >> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
> >> +{
> >> +    kfree(hw_wb);
> >> +}
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >> new file mode 100644
> >> index 0000000..80def96
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >> @@ -0,0 +1,131 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +/*
> >> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >> reserved
> >> + */
> >> +
> >> +#ifndef _DPU_HW_WB_H
> >> +#define _DPU_HW_WB_H
> >> +
> >> +#include "dpu_hw_catalog.h"
> >> +#include "dpu_hw_mdss.h"
> >> +#include "dpu_hw_top.h"
> >> +#include "dpu_hw_util.h"
> >> +#include "dpu_hw_pingpong.h"
> >> +
> >> +struct dpu_hw_wb;
> >> +
> >> +struct dpu_hw_wb_cfg {
> >> +    struct dpu_hw_fmt_layout dest;
> >> +    enum dpu_intf_mode intf_mode;
> >> +    struct drm_rect roi;
> >> +    struct drm_rect crop;
> >> +};
> >> +
> >> +/**
> >> + * enum CDP preload ahead address size
> >> + */
> >> +enum {
> >> +    DPU_WB_CDP_PRELOAD_AHEAD_32,
> >> +    DPU_WB_CDP_PRELOAD_AHEAD_64
> >> +};
> >> +
> >> +/**
> >> + * struct dpu_hw_wb_cdp_cfg : CDP configuration
> >> + * @enable: true to enable CDP
> >> + * @ubwc_meta_enable: true to enable ubwc metadata preload
> >> + * @tile_amortize_enable: true to enable amortization control for
> >> tile format
> >> + * @preload_ahead: number of request to preload ahead
> >> + * SDE_WB_CDP_PRELOAD_AHEAD_32,
> >> + * SDE_WB_CDP_PRELOAD_AHEAD_64
> >> + */
> >> +struct dpu_hw_wb_cdp_cfg {
> >> +    bool enable;
> >> +    bool ubwc_meta_enable;
> >> +    bool tile_amortize_enable;
> >> +    u32 preload_ahead;
> >> +};
> >> +
> >> +/**
> >> + * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
> >> + * @danger_lut: LUT for generate danger level based on fill level
> >> + * @safe_lut: LUT for generate safe level based on fill level
> >> + * @creq_lut: LUT for generate creq level based on fill level
> >> + * @danger_safe_en: enable danger safe generation
> >> + */
> >> +struct dpu_hw_wb_qos_cfg {
> >> +    u32 danger_lut;
> >> +    u32 safe_lut;
> >> +    u64 creq_lut;
> >> +    bool danger_safe_en;
> >> +};
> >> +
> >> +/**
> >> + *
> >> + * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
> >> + *  Assumption is these functions will be called after clocks are
> >> enabled
> >> + *  @setup_outaddress: setup output address from the writeback job
> >> + *  @setup_outformat: setup output format of writeback block from
> >> writeback job
> >> + *  @setup_qos_lut:   setup qos LUT for writeback block based on input
> >> + *  @setup_cdp:       setup chroma down prefetch block for writeback
> >> block
> >> + *  @bind_pingpong_blk: enable/disable the connection with ping-pong
> >> block
> >> + */
> >> +struct dpu_hw_wb_ops {
> >> +    void (*setup_outaddress)(struct dpu_hw_wb *ctx,
> >> +            struct dpu_hw_wb_cfg *wb);
> >> +
> >> +    void (*setup_outformat)(struct dpu_hw_wb *ctx,
> >> +            struct dpu_hw_wb_cfg *wb);
> >> +
> >> +    void (*setup_roi)(struct dpu_hw_wb *ctx,
> >> +            struct dpu_hw_wb_cfg *wb);
> >> +
> >> +    void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
> >> +            struct dpu_hw_wb_qos_cfg *cfg);
> >> +
> >> +    void (*setup_cdp)(struct dpu_hw_wb *ctx,
> >> +            struct dpu_hw_wb_cdp_cfg *cfg);
> >> +
> >> +    void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
> >> +            bool enable, const enum dpu_pingpong pp);
> >> +};
> >> +
> >> +/**
> >> + * struct dpu_hw_wb : WB driver object
> >> + * @hw: block hardware details
> >> + * @mdp: pointer to associated mdp portion of the catalog
> >> + * @idx: hardware index number within type
> >> + * @wb_hw_caps: hardware capabilities
> >> + * @ops: function pointers
> >> + * @hw_mdp: MDP top level hardware block
> >> + */
> >> +struct dpu_hw_wb {
> >> +    struct dpu_hw_blk_reg_map hw;
> >> +    const struct dpu_mdp_cfg *mdp;
> >> +
> >> +    /* wb path */
> >> +    int idx;
> >> +    const struct dpu_wb_cfg *caps;
> >> +
> >> +    /* ops */
> >> +    struct dpu_hw_wb_ops ops;
> >> +
> >> +    struct dpu_hw_mdp *hw_mdp;
> >> +};
> >> +
> >> +/**
> >> + * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
> >> + * @idx:  wb_path index for which driver object is required
> >> + * @addr: mapped register io address of MDP
> >> + * @m :   pointer to mdss catalog data
> >> + */
> >> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> >> +        void __iomem *addr,
> >> +        const struct dpu_mdss_cfg *m);
> >> +
> >> +/**
> >> + * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
> >> + * @hw_wb:  Pointer to writeback hw driver object
> >> + */
> >> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
> >> +
> >> +#endif /*_DPU_HW_WB_H */
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20 17:49       ` Dmitry Baryshkov
@ 2022-04-20 18:11         ` Abhinav Kumar
  2022-04-20 18:49           ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 18:11 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

Hi Dmitry

Sorry, I missed answering one question.

On 4/20/2022 10:49 AM, Dmitry Baryshkov wrote:
> On Wed, 20 Apr 2022 at 20:01, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>>
>>
>>
>> On 4/20/2022 12:20 AM, Dmitry Baryshkov wrote:
>>> On 20/04/2022 04:45, Abhinav Kumar wrote:
>>>> Add the dpu_hw_wb abstraction to program registers related to the
>>>> writeback block. These will be invoked once all the configuration
>>>> is set and ready to be programmed to the registers.
>>>>
>>>> changes in v2:
>>>>      - remove multiple empty lines at the end of the file
>>>>      - change dpu_hw_wb_bind_pingpong_blk to preserve upper bits
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>>>
>>> It's still Reviewed-by, few nits below.
>>>
>>>> ---
>>>>    drivers/gpu/drm/msm/Makefile              |   1 +
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273
>>>> ++++++++++++++++++++++++++++++
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
>>>>    3 files changed, 405 insertions(+)
>>>>    create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>>>>    create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>>>> index d5ca2e6..ca779c1 100644
>>>> --- a/drivers/gpu/drm/msm/Makefile
>>>> +++ b/drivers/gpu/drm/msm/Makefile
>>>> @@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>>>        disp/dpu1/dpu_hw_top.o \
>>>>        disp/dpu1/dpu_hw_util.o \
>>>>        disp/dpu1/dpu_hw_vbif.o \
>>>> +    disp/dpu1/dpu_hw_wb.o \
>>>>        disp/dpu1/dpu_kms.o \
>>>>        disp/dpu1/dpu_plane.o \
>>>>        disp/dpu1/dpu_rm.o \
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>>>> new file mode 100644
>>>> index 0000000..afa8aab
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
>>>> @@ -0,0 +1,273 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> + /*
>>>> +  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
>>>> reserved
>>>> +  */
>>>> +
>>>> +#include "dpu_hw_mdss.h"
>>>> +#include "dpu_hwio.h"
>>>> +#include "dpu_hw_catalog.h"
>>>> +#include "dpu_hw_wb.h"
>>>> +#include "dpu_formats.h"
>>>> +#include "dpu_kms.h"
>>>> +
>>>> +#define WB_DST_FORMAT                         0x000
>>>> +#define WB_DST_OP_MODE                        0x004
>>>> +#define WB_DST_PACK_PATTERN                   0x008
>>>> +#define WB_DST0_ADDR                          0x00C
>>>> +#define WB_DST1_ADDR                          0x010
>>>> +#define WB_DST2_ADDR                          0x014
>>>> +#define WB_DST3_ADDR                          0x018
>>>> +#define WB_DST_YSTRIDE0                       0x01C
>>>> +#define WB_DST_YSTRIDE1                       0x020
>>>> +#define WB_DST_YSTRIDE1                       0x020
>>>> +#define WB_DST_DITHER_BITDEPTH                0x024
>>>> +#define WB_DST_MATRIX_ROW0                    0x030
>>>> +#define WB_DST_MATRIX_ROW1                    0x034
>>>> +#define WB_DST_MATRIX_ROW2                    0x038
>>>> +#define WB_DST_MATRIX_ROW3                    0x03C
>>>> +#define WB_DST_WRITE_CONFIG                   0x048
>>>> +#define WB_ROTATION_DNSCALER                  0x050
>>>> +#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
>>>> +#define WB_N16_INIT_PHASE_X_C03               0x060
>>>> +#define WB_N16_INIT_PHASE_X_C12               0x064
>>>> +#define WB_N16_INIT_PHASE_Y_C03               0x068
>>>> +#define WB_N16_INIT_PHASE_Y_C12               0x06C
>>>> +#define WB_OUT_SIZE                           0x074
>>>> +#define WB_ALPHA_X_VALUE                      0x078
>>>> +#define WB_DANGER_LUT                         0x084
>>>> +#define WB_SAFE_LUT                           0x088
>>>> +#define WB_QOS_CTRL                           0x090
>>>> +#define WB_CREQ_LUT_0                         0x098
>>>> +#define WB_CREQ_LUT_1                         0x09C
>>>> +#define WB_UBWC_STATIC_CTRL                   0x144
>>>> +#define WB_MUX                                0x150
>>>> +#define WB_CROP_CTRL                          0x154
>>>> +#define WB_CROP_OFFSET                        0x158
>>>> +#define WB_CSC_BASE                           0x260
>>>> +#define WB_DST_ADDR_SW_STATUS                 0x2B0
>>>> +#define WB_CDP_CNTL                           0x2B4
>>>> +#define WB_OUT_IMAGE_SIZE                     0x2C0
>>>> +#define WB_OUT_XY                             0x2C4
>>>> +
>>>> +/* WB_QOS_CTRL */
>>>> +#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
>>>> +
>>>> +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
>>>> +        const struct dpu_mdss_cfg *m, void __iomem *addr,
>>>> +        struct dpu_hw_blk_reg_map *b)
>>>> +{
>>>> +    int i;
>>>> +
>>>> +    for (i = 0; i < m->wb_count; i++) {
>>>> +        if (wb == m->wb[i].id) {
>>>> +            b->base_off = addr;
>>>> +            b->blk_off = m->wb[i].base;
>>>> +            b->length = m->wb[i].len;
>>>> +            b->hwversion = m->hwversion;
>>>> +            return &m->wb[i];
>>>> +        }
>>>> +    }
>>>> +    return ERR_PTR(-EINVAL);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
>>>> +        struct dpu_hw_wb_cfg *data)
>>>> +{
>>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
>>>> +    DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
>>>> +    DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
>>>> +    DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
>>>> +        struct dpu_hw_wb_cfg *data)
>>>> +{
>>>
>>> This function shares significant logic with dpu_hw_sspp_setup_format().
>>>
>>> We should consider splitting the common code to the helper at some point
>>> (later).
>>
>> Agreed, I do see some similarities. Will take this up in another change.
> 
> As I wrote, this can be a separate update.
> 
>>
>>>
>>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>>> +    const struct dpu_format *fmt = data->dest.format;
>>>> +    u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
>>>> +    u32 write_config = 0;
>>>> +    u32 opmode = 0;
>>>> +    u32 dst_addr_sw = 0;
>>>> +
>>>> +    chroma_samp = fmt->chroma_sample;
>>>> +
>>>> +    dst_format = (chroma_samp << 23) |
>>>> +        (fmt->fetch_planes << 19) |
>>>> +        (fmt->bits[C3_ALPHA] << 6) |
>>>> +        (fmt->bits[C2_R_Cr] << 4) |
>>>> +        (fmt->bits[C1_B_Cb] << 2) |
>>>> +        (fmt->bits[C0_G_Y] << 0);
>>>> +
>>>> +    if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
>>>> +        dst_format |= BIT(8); /* DSTC3_EN */
>>>> +        if (!fmt->alpha_enable ||
>>>> +            !(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
>>>> +            dst_format |= BIT(14); /* DST_ALPHA_X */
>>>> +    }
>>>> +
>>>> +    pattern = (fmt->element[3] << 24) |
>>>> +        (fmt->element[2] << 16) |
>>>> +        (fmt->element[1] << 8)  |
>>>> +        (fmt->element[0] << 0);
>>>> +
>>>> +    dst_format |= (fmt->unpack_align_msb << 18) |
>>>> +        (fmt->unpack_tight << 17) |
>>>> +        ((fmt->unpack_count - 1) << 12) |
>>>> +        ((fmt->bpp - 1) << 9);
>>>> +
>>>> +    ystride0 = data->dest.plane_pitch[0] |
>>>> +        (data->dest.plane_pitch[1] << 16);
>>>> +    ystride1 = data->dest.plane_pitch[2] |
>>>> +    (data->dest.plane_pitch[3] << 16);
>>>> +
>>>> +    if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
>>>> +        outsize = (drm_rect_height(&data->roi) << 16) |
>>>> drm_rect_width(&data->roi);
>>>> +    else
>>>> +        outsize = (data->dest.height << 16) | data->dest.width;
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
>>>> +    DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
>>>> +    DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
>>>> +    DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
>>>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
>>>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
>>>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
>>>> +    DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
>>>> +    DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg
>>>> *wb)
>>>> +{
>>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>>> +    u32 image_size, out_size, out_xy;
>>>> +
>>>> +    image_size = (wb->dest.height << 16) | wb->dest.width;
>>>> +    out_xy = 0;
>>>> +    out_size = (drm_rect_height(&wb->roi) << 16) |
>>>> drm_rect_width(&wb->roi);
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
>>>> +    DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
>>>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
>>>> +        struct dpu_hw_wb_qos_cfg *cfg)
>>> I like the single call approach. Maybe we should adopt it for the SSPP
>>> QoS LUT too.
>>
>> Perhaps yes.
>>
>> I had an overall question on this. all the dpu_hw_*** APIs accept their
>> own unique ctx (which is the *** hardware they are programming). What is
>> the approach you are suggesting to unify these?
>>
>> For helper calls I understood and already have addressed it in this series.
>>
>> But looking ahead for dpu_hw_*** calls, I am still unclear on the
>> unification plan.
>>
>> Again, this is for a follow-up change but I am just trying to understand it.
> 
> I liked that this function programs all QoS LUT params. (compared to
> dpu_hw_sspp_setup_qos_ctrl() + dpu_hw_sspp_setup_creq_lut()
> 
>>
>>>
>>>> +{
>>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
>>>> +    u32 qos_ctrl = 0;
>>>> +
>>>> +    if (!ctx || !cfg)
>>>> +        return;
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
>>>> +    DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
>>>> +
>>>> +    if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
>>>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
>>>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
>>>> +    }
>>>
>>> Is there a plain WB_CREQ_LUT for the non-8LVL case?
> 
> I was comparing the SSPP with WB programming. So if possible could you
> please check?

No, there is no plain WB_CREQ_LUT. I checked the register set.

So this programming is correct. I confirmed it.


> 
>>>> +
>>>> +    if (cfg->danger_safe_en)
>>>> +        qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
>>>> +        struct dpu_hw_wb_cdp_cfg *cfg)
>>>
>>> Can we use dpu_hw_pipe_cdp_cfg here? Maybe after renaming it to more
>>> generic dpu_hw_cdp_cfg.
>>
>>
>> ack, I can absorb this in this series itself and re-spin this.
>> It will save us one more new structure.
> 
> Good!
> 
>>>> +{
>>>> +    struct dpu_hw_blk_reg_map *c;
>>>> +    u32 cdp_cntl = 0;
>>>> +
>>>> +    if (!ctx || !cfg)
>>>> +        return;
>>>> +
>>>> +    c = &ctx->hw;
>>>> +
>>>> +    if (cfg->enable)
>>>> +        cdp_cntl |= BIT(0);
>>>> +    if (cfg->ubwc_meta_enable)
>>>> +        cdp_cntl |= BIT(1);
>>>> +    if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
>>>> +        cdp_cntl |= BIT(3);
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
>>>> +}
>>>> +
>>>> +static void dpu_hw_wb_bind_pingpong_blk(
>>>> +        struct dpu_hw_wb *ctx,
>>>> +        bool enable, const enum dpu_pingpong pp)
>>>> +{
>>>> +    struct dpu_hw_blk_reg_map *c;
>>>> +    int mux_cfg;
>>>> +
>>>> +    if (!ctx)
>>>> +        return;
>>>> +
>>>> +    c = &ctx->hw;
>>>> +
>>>> +    mux_cfg = DPU_REG_READ(c, WB_MUX);
>>>> +    mux_cfg &= ~0xf;
>>>> +
>>>> +    if (enable)
>>>> +        mux_cfg |= (pp - PINGPONG_0) & 0x7;
>>>> +    else
>>>> +        mux_cfg |= 0xf;
>>>> +
>>>> +    DPU_REG_WRITE(c, WB_MUX, mux_cfg);
>>>> +}
>>>> +
>>>> +static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
>>>> +        unsigned long features)
>>>> +{
>>>> +    ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
>>>> +    ops->setup_outformat = dpu_hw_wb_setup_format;
>>>> +
>>>> +    if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
>>>> +        ops->setup_roi = dpu_hw_wb_roi;
>>>> +
>>>> +    if (test_bit(DPU_WB_QOS, &features))
>>>> +        ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
>>>> +
>>>> +    if (test_bit(DPU_WB_CDP, &features))
>>>> +        ops->setup_cdp = dpu_hw_wb_setup_cdp;
>>>> +
>>>> +    if (test_bit(DPU_WB_INPUT_CTRL, &features))
>>>> +        ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
>>>> +}
>>>> +
>>>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
>>>> +        void __iomem *addr, const struct dpu_mdss_cfg *m)
>>>> +{
>>>> +    struct dpu_hw_wb *c;
>>>> +    const struct dpu_wb_cfg *cfg;
>>>> +
>>>> +    if (!addr || !m)
>>>> +        return ERR_PTR(-EINVAL);
>>>> +
>>>> +    c = kzalloc(sizeof(*c), GFP_KERNEL);
>>>> +    if (!c)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    cfg = _wb_offset(idx, m, addr, &c->hw);
>>>> +    if (IS_ERR(cfg)) {
>>>> +        WARN(1, "Unable to find wb idx=%d\n", idx);
>>>> +        kfree(c);
>>>> +        return ERR_PTR(-EINVAL);
>>>> +    }
>>>> +
>>>> +    /* Assign ops */
>>>> +    c->mdp = &m->mdp[0];
>>>> +    c->idx = idx;
>>>> +    c->caps = cfg;
>>>> +    _setup_wb_ops(&c->ops, c->caps->features);
>>>> +
>>>> +    return c;
>>>> +}
>>>> +
>>>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
>>>> +{
>>>> +    kfree(hw_wb);
>>>> +}
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>>>> new file mode 100644
>>>> index 0000000..80def96
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
>>>> @@ -0,0 +1,131 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>>> +/*
>>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
>>>> reserved
>>>> + */
>>>> +
>>>> +#ifndef _DPU_HW_WB_H
>>>> +#define _DPU_HW_WB_H
>>>> +
>>>> +#include "dpu_hw_catalog.h"
>>>> +#include "dpu_hw_mdss.h"
>>>> +#include "dpu_hw_top.h"
>>>> +#include "dpu_hw_util.h"
>>>> +#include "dpu_hw_pingpong.h"
>>>> +
>>>> +struct dpu_hw_wb;
>>>> +
>>>> +struct dpu_hw_wb_cfg {
>>>> +    struct dpu_hw_fmt_layout dest;
>>>> +    enum dpu_intf_mode intf_mode;
>>>> +    struct drm_rect roi;
>>>> +    struct drm_rect crop;
>>>> +};
>>>> +
>>>> +/**
>>>> + * enum CDP preload ahead address size
>>>> + */
>>>> +enum {
>>>> +    DPU_WB_CDP_PRELOAD_AHEAD_32,
>>>> +    DPU_WB_CDP_PRELOAD_AHEAD_64
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct dpu_hw_wb_cdp_cfg : CDP configuration
>>>> + * @enable: true to enable CDP
>>>> + * @ubwc_meta_enable: true to enable ubwc metadata preload
>>>> + * @tile_amortize_enable: true to enable amortization control for
>>>> tile format
>>>> + * @preload_ahead: number of request to preload ahead
>>>> + * SDE_WB_CDP_PRELOAD_AHEAD_32,
>>>> + * SDE_WB_CDP_PRELOAD_AHEAD_64
>>>> + */
>>>> +struct dpu_hw_wb_cdp_cfg {
>>>> +    bool enable;
>>>> +    bool ubwc_meta_enable;
>>>> +    bool tile_amortize_enable;
>>>> +    u32 preload_ahead;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
>>>> + * @danger_lut: LUT for generate danger level based on fill level
>>>> + * @safe_lut: LUT for generate safe level based on fill level
>>>> + * @creq_lut: LUT for generate creq level based on fill level
>>>> + * @danger_safe_en: enable danger safe generation
>>>> + */
>>>> +struct dpu_hw_wb_qos_cfg {
>>>> +    u32 danger_lut;
>>>> +    u32 safe_lut;
>>>> +    u64 creq_lut;
>>>> +    bool danger_safe_en;
>>>> +};
>>>> +
>>>> +/**
>>>> + *
>>>> + * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
>>>> + *  Assumption is these functions will be called after clocks are
>>>> enabled
>>>> + *  @setup_outaddress: setup output address from the writeback job
>>>> + *  @setup_outformat: setup output format of writeback block from
>>>> writeback job
>>>> + *  @setup_qos_lut:   setup qos LUT for writeback block based on input
>>>> + *  @setup_cdp:       setup chroma down prefetch block for writeback
>>>> block
>>>> + *  @bind_pingpong_blk: enable/disable the connection with ping-pong
>>>> block
>>>> + */
>>>> +struct dpu_hw_wb_ops {
>>>> +    void (*setup_outaddress)(struct dpu_hw_wb *ctx,
>>>> +            struct dpu_hw_wb_cfg *wb);
>>>> +
>>>> +    void (*setup_outformat)(struct dpu_hw_wb *ctx,
>>>> +            struct dpu_hw_wb_cfg *wb);
>>>> +
>>>> +    void (*setup_roi)(struct dpu_hw_wb *ctx,
>>>> +            struct dpu_hw_wb_cfg *wb);
>>>> +
>>>> +    void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
>>>> +            struct dpu_hw_wb_qos_cfg *cfg);
>>>> +
>>>> +    void (*setup_cdp)(struct dpu_hw_wb *ctx,
>>>> +            struct dpu_hw_wb_cdp_cfg *cfg);
>>>> +
>>>> +    void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
>>>> +            bool enable, const enum dpu_pingpong pp);
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct dpu_hw_wb : WB driver object
>>>> + * @hw: block hardware details
>>>> + * @mdp: pointer to associated mdp portion of the catalog
>>>> + * @idx: hardware index number within type
>>>> + * @wb_hw_caps: hardware capabilities
>>>> + * @ops: function pointers
>>>> + * @hw_mdp: MDP top level hardware block
>>>> + */
>>>> +struct dpu_hw_wb {
>>>> +    struct dpu_hw_blk_reg_map hw;
>>>> +    const struct dpu_mdp_cfg *mdp;
>>>> +
>>>> +    /* wb path */
>>>> +    int idx;
>>>> +    const struct dpu_wb_cfg *caps;
>>>> +
>>>> +    /* ops */
>>>> +    struct dpu_hw_wb_ops ops;
>>>> +
>>>> +    struct dpu_hw_mdp *hw_mdp;
>>>> +};
>>>> +
>>>> +/**
>>>> + * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
>>>> + * @idx:  wb_path index for which driver object is required
>>>> + * @addr: mapped register io address of MDP
>>>> + * @m :   pointer to mdss catalog data
>>>> + */
>>>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
>>>> +        void __iomem *addr,
>>>> +        const struct dpu_mdss_cfg *m);
>>>> +
>>>> +/**
>>>> + * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
>>>> + * @hw_wb:  Pointer to writeback hw driver object
>>>> + */
>>>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
>>>> +
>>>> +#endif /*_DPU_HW_WB_H */
>>>
>>>
> 
> 
> 

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

* Re: [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20  7:49   ` Dmitry Baryshkov
@ 2022-04-20 18:17     ` Abhinav Kumar
  2022-04-20 19:26       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 18:17 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:49 AM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:46, Abhinav Kumar wrote:
>> Introduce the dpu_encoder_phys_* for the writeback interface
>> to handle writeback specific hardware programming.
>>
>> changes in v2:
>>     - rebase on msm-next and fix related dependencies namely
>>       the irq cleanup
>>     - move cdp_cfg, aspace out of dpu_encoder_phys_wb
>>     - leave a comment about wb master
>>     - start using _dpu_hw_get_qos_lut from dpu_hw_util
>>     - replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/Makefile                       |   1 +
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751 
>> +++++++++++++++++++++
>>   3 files changed, 782 insertions(+)
>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index ca779c1..0387f22 100644
>> --- a/drivers/gpu/drm/msm/Makefile
>> +++ b/drivers/gpu/drm/msm/Makefile
>> @@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>       disp/dpu1/dpu_encoder.o \
>>       disp/dpu1/dpu_encoder_phys_cmd.o \
>>       disp/dpu1/dpu_encoder_phys_vid.o \
>> +    disp/dpu1/dpu_encoder_phys_wb.o \
>>       disp/dpu1/dpu_formats.o \
>>       disp/dpu1/dpu_hw_catalog.o \
>>       disp/dpu1/dpu_hw_ctl.o \
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> index 00951f3..5452f98 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> @@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
>>    * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
>>    * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
>>    * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
>> + * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
>>    */
>>   enum dpu_intr_idx {
>>       INTR_IDX_VSYNC,
>> @@ -157,6 +158,7 @@ enum dpu_intr_idx {
>>       INTR_IDX_UNDERRUN,
>>       INTR_IDX_CTL_START,
>>       INTR_IDX_RDPTR,
>> +    INTR_IDX_WB_DONE,
>>       INTR_IDX_MAX,
>>   };
>> @@ -224,6 +226,27 @@ static inline int 
>> dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
>>   }
>>   /**
>> + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to 
>> handle command
>> + *    mode specific operations
>> + * @base:    Baseclass physical encoder structure
>> + * @wbirq_refcount:     Reference count of writeback interrupt
>> + * @wb_done_timeout_cnt: number of wb done irq timeout errors
>> + * @wb_cfg:  writeback block config to store fb related details
>> + * @wb_conn: backpointer to writeback connector
>> + * @wb_job: backpointer to current writeback job
>> + * @dest:   dpu buffer layout for current writeback output buffer
>> + */
>> +struct dpu_encoder_phys_wb {
>> +    struct dpu_encoder_phys base;
>> +    atomic_t wbirq_refcount;
>> +    int wb_done_timeout_cnt;
>> +    struct dpu_hw_wb_cfg wb_cfg;
>> +    struct drm_writeback_connector *wb_conn;
>> +    struct drm_writeback_job *wb_job;
>> +    struct dpu_hw_fmt_layout dest;
>> +};
>> +
>> +/**
>>    * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to 
>> handle command
>>    *    mode specific operations
>>    * @base:    Baseclass physical encoder structure
>> @@ -291,6 +314,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
>>           struct dpu_enc_phys_init_params *p);
>>   /**
>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>> + * @init:    Pointer to init info structure with initialization params
>> + */
>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>> +        struct dpu_enc_phys_init_params *p);
>> +
>> +/**
>>    * dpu_encoder_helper_trigger_start - control start helper function
>>    *    This helper function may be optionally specified by physical
>>    *    encoders if they require ctl_start triggering.
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>> new file mode 100644
>> index 0000000..128317fe
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>> @@ -0,0 +1,751 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + */
>> +
>> +#define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
>> +
>> +#include <linux/debugfs.h>
>> +
>> +#include "dpu_encoder_phys.h"
>> +#include "dpu_formats.h"
>> +#include "dpu_hw_top.h"
>> +#include "dpu_hw_wb.h"
>> +#include "dpu_hw_lm.h"
>> +#include "dpu_hw_blk.h"
>> +#include "dpu_hw_merge3d.h"
>> +#include "dpu_hw_interrupts.h"
>> +#include "dpu_core_irq.h"
>> +#include "dpu_vbif.h"
>> +#include "dpu_crtc.h"
>> +#include "disp/msm_disp_snapshot.h"
>> +
>> +#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
>> +
>> +#define to_dpu_encoder_phys_wb(x) \
>> +    container_of(x, struct dpu_encoder_phys_wb, base)
>> +
>> +/**
>> + * dpu_encoder_phys_wb_is_master - report wb always as master encoder
>> + */
>> +static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    /* there is only one physical enc for dpu_writeback */
>> +    return true;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback 
>> interface
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_set_ot_limit(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>> +    struct dpu_vbif_set_ot_params ot_params;
>> +
>> +    memset(&ot_params, 0, sizeof(ot_params));
>> +    ot_params.xin_id = hw_wb->caps->xin_id;
>> +    ot_params.num = hw_wb->idx - WB_0;
>> +    ot_params.width = phys_enc->cached_mode.hdisplay;
>> +    ot_params.height = phys_enc->cached_mode.vdisplay;
>> +    ot_params.is_wfd = true;
>> +    ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
>> +    ot_params.vbif_idx = hw_wb->caps->vbif_idx;
>> +    ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>> +    ot_params.rd = false;
>> +
>> +    dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_set_qos_remap(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb;
>> +    struct dpu_vbif_set_qos_params qos_params;
>> +
>> +    if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
>> +        DPU_ERROR("invalid arguments\n");
>> +        return;
>> +    }
>> +
>> +    if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
>> +        DPU_ERROR("invalid writeback hardware\n");
>> +        return;
>> +    }
>> +
>> +    hw_wb = phys_enc->hw_wb;
>> +
>> +    memset(&qos_params, 0, sizeof(qos_params));
>> +    qos_params.vbif_idx = hw_wb->caps->vbif_idx;
>> +    qos_params.xin_id = hw_wb->caps->xin_id;
>> +    qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>> +    qos_params.num = hw_wb->idx - WB_0;
>> +    qos_params.is_rt = false;
>> +
>> +    DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
>> +            qos_params.num,
>> +            qos_params.vbif_idx,
>> +            qos_params.xin_id, qos_params.is_rt);
>> +
>> +    dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb;
>> +    struct dpu_hw_wb_qos_cfg qos_cfg;
>> +    struct dpu_mdss_cfg *catalog;
>> +    struct dpu_qos_lut_tbl *qos_lut_tb;
>> +
>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>> !phys_enc->dpu_kms->catalog) {
>> +        DPU_ERROR("invalid parameter(s)\n");
>> +        return;
>> +    }
>> +
>> +    catalog = phys_enc->dpu_kms->catalog;
>> +
>> +    hw_wb = phys_enc->hw_wb;
>> +
>> +    memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
>> +    qos_cfg.danger_safe_en = true;
>> +    qos_cfg.danger_lut =
>> +        catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>> +
>> +    qos_cfg.safe_lut = 
>> catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>> +
>> +    qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>> +    qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
>> +
>> +    if (hw_wb->ops.setup_qos_lut)
>> +        hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
>> + * @phys_enc:    Pointer to physical encoder
>> + * @fb:        Pointer to output framebuffer
>> + * @wb_roi:    Pointer to output region of interest
>> + */
>> +static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys 
>> *phys_enc,
>> +        struct drm_framebuffer *fb)
>> +{
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +    struct dpu_hw_wb *hw_wb;
>> +    struct dpu_hw_wb_cfg *wb_cfg;
>> +    struct dpu_hw_wb_cdp_cfg cdp_cfg;
>> +
>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>> !phys_enc->dpu_kms->catalog) {
>> +        DPU_ERROR("invalid encoder\n");
>> +        return;
>> +    }
>> +
>> +    hw_wb = phys_enc->hw_wb;
>> +    wb_cfg = &wb_enc->wb_cfg;
>> +
>> +    wb_cfg->intf_mode = phys_enc->intf_mode;
>> +    wb_cfg->roi.x1 = 0;
>> +    wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
>> +    wb_cfg->roi.y1 = 0;
>> +    wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
>> +
>> +    if (hw_wb->ops.setup_roi)
>> +        hw_wb->ops.setup_roi(hw_wb, wb_cfg);
>> +
>> +    if (hw_wb->ops.setup_outformat)
>> +        hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
>> +
>> +    if (hw_wb->ops.setup_cdp) {
>> +        memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
>> +
>> +        cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
>> +                [DPU_PERF_CDP_USAGE_NRT].wr_enable;
>> +        cdp_cfg.ubwc_meta_enable =
>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
>> +        cdp_cfg.tile_amortize_enable =
>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
>> +                DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
>> +        cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
>> +
>> +        hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
>> +    }
>> +
>> +    if (hw_wb->ops.setup_outaddress)
>> +        hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
>> + * @phys_enc:Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb;
>> +    struct dpu_hw_ctl *ctl;
>> +
>> +    if (!phys_enc) {
>> +        DPU_ERROR("invalid encoder\n");
>> +        return;
>> +    }
>> +
>> +    hw_wb = phys_enc->hw_wb;
>> +    ctl = phys_enc->hw_ctl;
>> +
>> +    if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
>> +        (phys_enc->hw_ctl &&
>> +         phys_enc->hw_ctl->ops.setup_intf_cfg)) {
>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>> +        struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
>> +        enum dpu_3d_blend_mode mode_3d;
>> +
>> +        mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>> +
>> +        intf_cfg.intf = DPU_NONE;
>> +        intf_cfg.wb = hw_wb->idx;
>> +
>> +        if (mode_3d && hw_pp && hw_pp->merge_3d)
>> +            intf_cfg.merge_3d = hw_pp->merge_3d->idx;
>> +
>> +        if (phys_enc->hw_pp->merge_3d && 
>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
>> +            
>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
>> +                    mode_3d);
>> +
>> +        /* setup which pp blk will connect to this wb */
>> +        if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, 
>> true,
>> +                    phys_enc->hw_pp->idx);
>> +
>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>> &intf_cfg);
>> +    } else if (phys_enc->hw_ctl && 
>> phys_enc->hw_ctl->ops.setup_intf_cfg) {
>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>> +
>> +        intf_cfg.intf = DPU_NONE;
>> +        intf_cfg.wb = hw_wb->idx;
>> +        intf_cfg.mode_3d =
>> +            dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>> &intf_cfg);
>> +    }
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic 
>> states
>> + * @phys_enc:    Pointer to physical encoder
>> + * @crtc_state:    Pointer to CRTC atomic state
>> + * @conn_state:    Pointer to connector atomic state
>> + */
>> +static int dpu_encoder_phys_wb_atomic_check(
>> +        struct dpu_encoder_phys *phys_enc,
>> +        struct drm_crtc_state *crtc_state,
>> +        struct drm_connector_state *conn_state)
>> +{
>> +    struct drm_framebuffer *fb;
>> +    const struct drm_display_mode *mode;
>> +
>> +    DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
>> +            phys_enc->intf_idx, mode->name, mode->hdisplay, 
>> mode->vdisplay);
>> +
>> +    if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
>> +        return 0;
>> +
>> +    fb = conn_state->writeback_job->fb;
>> +    mode = &crtc_state->mode;
>> +
>> +    if (!conn_state || !conn_state->connector) {
>> +        DPU_ERROR("invalid connector state\n");
>> +        return -EINVAL;
>> +    } else if (conn_state->connector->status !=
>> +            connector_status_connected) {
>> +        DPU_ERROR("connector not connected %d\n",
>> +                conn_state->connector->status);
>> +        return -EINVAL;
>> +    }
>> +
>> +    DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
>> +            fb->width, fb->height);
>> +
>> +    if (fb->width != mode->hdisplay) {
>> +        DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
>> +                mode->hdisplay);
>> +        return -EINVAL;
>> +    } else if (fb->height != mode->vdisplay) {
>> +        DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
>> +                  mode->vdisplay);
>> +        return -EINVAL;
>> +    } else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
>> +        DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
>> +                  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
>> +        return -EINVAL;
>> +    }
> 
> Being not an expert in the DRM WB API, I have a question: are those 
> hardware limitations or the DRM requirement that we can not write to the 
> region inside the attached FB?

Both. The first two checks are indeed DRM requirement and infact igt 
writeback tests this to ensure that the driver rejects any invalid FBs 
which are not equal to the mode that was set.

https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tests/kms_writeback.c#L409

The last check is coming from our hardware.

> 
>> +
>> +    return 0;
>> +}
>> +
>> +
>> +/**
>> + * _dpu_encoder_phys_wb_update_flush - flush hardware update
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void _dpu_encoder_phys_wb_update_flush(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb;
>> +    struct dpu_hw_ctl *hw_ctl;
>> +    struct dpu_hw_pingpong *hw_pp;
>> +    u32 pending_flush = 0;
>> +
>> +    if (!phys_enc)
>> +        return;
>> +
>> +    hw_wb = phys_enc->hw_wb;
>> +    hw_pp = phys_enc->hw_pp;
>> +    hw_ctl = phys_enc->hw_ctl;
>> +
>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>> +
>> +    if (!hw_ctl) {
>> +        DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
>> +        return;
>> +    }
>> +
>> +    if (hw_ctl->ops.update_pending_flush_wb)
>> +        hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
>> +
>> +    if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && 
>> hw_pp->merge_3d)
>> +        hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
>> +                hw_pp->merge_3d->idx);
>> +
>> +    if (hw_ctl->ops.get_pending_flush)
>> +        pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
>> +
>> +    DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
>> +            hw_ctl->idx - CTL_0, pending_flush,
>> +            hw_wb->idx - WB_0);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_setup - setup writeback encoder
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_setup(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>> +    struct drm_display_mode mode = phys_enc->cached_mode;
>> +    struct drm_framebuffer *fb = NULL;
>> +
>> +    DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
>> +            hw_wb->idx - WB_0, mode.name,
>> +            mode.hdisplay, mode.vdisplay);
>> +
>> +    dpu_encoder_phys_wb_set_ot_limit(phys_enc);
>> +
>> +    dpu_encoder_phys_wb_set_qos_remap(phys_enc);
>> +
>> +    dpu_encoder_phys_wb_set_qos(phys_enc);
>> +
>> +    dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
>> +
>> +    dpu_encoder_phys_wb_setup_cdp(phys_enc);
>> +
>> +}
>> +
>> +static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
>> +{
>> +    struct dpu_encoder_phys *phys_enc = arg;
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +
>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>> +    unsigned long lock_flags;
>> +    u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
>> +
>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>> +
>> +    if (phys_enc->parent_ops->handle_frame_done)
>> +        phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
>> +                phys_enc, event);
>> +
>> +    if (phys_enc->parent_ops->handle_vblank_virt)
>> +        phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
>> +                phys_enc);
>> +
>> +    spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>> +    spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
>> +
>> +    if (wb_enc->wb_conn)
>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>> +
>> +    /* Signal any waiting atomic commit thread */
>> +    wake_up_all(&phys_enc->pending_kickoff_wq);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
>> + * @arg:    Pointer to writeback encoder
>> + * @irq_idx:    interrupt index
>> + */
>> +static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
>> +{
>> +    _dpu_encoder_phys_wb_frame_done_helper(arg);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
>> + * @phys:    Pointer to physical encoder
>> + * @enable:    indicates enable or disable interrupts
>> + */
>> +static void dpu_encoder_phys_wb_irq_ctrl(
>> +        struct dpu_encoder_phys *phys, bool enable)
>> +{
>> +
>> +    struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
>> +    int ret = 0;
>> +    int refcount;
>> +
>> +    refcount = atomic_read(&wb_enc->wbirq_refcount);
>> +
>> +    if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
>> +        dpu_core_irq_register_callback(phys->dpu_kms,
>> +                phys->irq[INTR_IDX_WB_DONE], 
>> dpu_encoder_phys_wb_done_irq, phys);
>> +        if (ret)
>> +            atomic_dec_return(&wb_enc->wbirq_refcount);
>> +    } else if (!enable &&
>> +            atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
>> +        dpu_core_irq_unregister_callback(phys->dpu_kms, 
>> phys->irq[INTR_IDX_WB_DONE]);
>> +        if (ret)
>> +            atomic_inc_return(&wb_enc->wbirq_refcount);
>> +    }
>> +}
>> +
>> +static void dpu_encoder_phys_wb_atomic_mode_set(
>> +        struct dpu_encoder_phys *phys_enc,
>> +        struct drm_crtc_state *crtc_state,
>> +        struct drm_connector_state *conn_state)
>> +{
>> +
>> +    phys_enc->irq[INTR_IDX_WB_DONE] = 
>> phys_enc->hw_wb->caps->intr_wb_done;
>> +}
>> +
>> +static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +    u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
>> +
>> +    wb_enc->wb_done_timeout_cnt++;
>> +
>> +    if (wb_enc->wb_done_timeout_cnt == 1)
>> +        msm_disp_snapshot_state(phys_enc->parent->dev);
>> +
>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>> +
>> +    /* request a ctl reset before the next kickoff */
>> +    phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
>> +
>> +    if (wb_enc->wb_conn)
>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>> +
>> +    if (phys_enc->parent_ops->handle_frame_done)
>> +        phys_enc->parent_ops->handle_frame_done(
>> +                phys_enc->parent, phys_enc, frame_event);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is 
>> committed
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static int dpu_encoder_phys_wb_wait_for_commit_done(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    unsigned long ret;
>> +    struct dpu_encoder_wait_info wait_info;
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +
>> +    wait_info.wq = &phys_enc->pending_kickoff_wq;
>> +    wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
>> +    wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
>> +
>> +    ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
>> +            dpu_encoder_phys_wb_done_irq, &wait_info);
>> +    if (ret == -ETIMEDOUT)
>> +        _dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
>> +    else if (!ret)
>> +        wb_enc->wb_done_timeout_cnt = 0;
>> +
>> +    return ret;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
>> + * @phys_enc:    Pointer to physical encoder
>> + * Returns:    Zero on success
>> + */
>> +static void dpu_encoder_phys_wb_prepare_for_kickoff(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +    struct drm_connector *drm_conn;
>> +    struct drm_connector_state *state;
>> +
>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>> +
>> +    if (!wb_enc->wb_conn || !wb_enc->wb_job) {
>> +        DPU_ERROR("invalid wb_conn or wb_job\n");
>> +        return;
>> +    }
>> +
>> +    drm_conn = &wb_enc->wb_conn->base;
>> +    state = drm_conn->state;
>> +
>> +    if (wb_enc->wb_conn && wb_enc->wb_job)
>> +        drm_writeback_queue_job(wb_enc->wb_conn, state);
>> +
>> +    dpu_encoder_phys_wb_setup(phys_enc);
>> +
>> +    _dpu_encoder_phys_wb_update_flush(phys_enc);
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static bool dpu_encoder_phys_wb_needs_single_flush(struct 
>> dpu_encoder_phys *phys_enc)
>> +{
>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>> +    return false;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_handle_post_kickoff(
>> +        struct dpu_encoder_phys *phys_enc)
>> +{
>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>> +
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_enable - enable writeback encoder
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>> +    phys_enc->enable_state = DPU_ENC_ENABLED;
>> +}
>> +/**
>> + * dpu_encoder_phys_wb_disable - disable writeback encoder
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>> +    struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
>> +
>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>> +
>> +    if (phys_enc->enable_state == DPU_ENC_DISABLED) {
>> +        DPU_ERROR("encoder is already disabled\n");
>> +        return;
>> +    }
>> +
>> +    /* reset h/w before final flush */
>> +    if (phys_enc->hw_ctl->ops.clear_pending_flush)
>> +        phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
>> +
>> +    /*
>> +     * New CTL reset sequence from 5.0 MDP onwards.
>> +     * If has_3d_merge_reset is not set, legacy reset
>> +     * sequence is executed.
>> +     *
>> +     * Legacy reset sequence has not been implemented yet.
>> +     * Any target earlier than SM8150 will need it and when
>> +     * WB support is added to those targets will need to add
>> +     * the legacy teardown sequence as well.
>> +     */
>> +    if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
>> +        dpu_encoder_helper_phys_cleanup(phys_enc);
>> +
>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_destroy - destroy writeback encoder
>> + * @phys_enc:    Pointer to physical encoder
>> + */
>> +static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
>> +
>> +    if (!phys_enc)
>> +        return;
>> +
>> +    kfree(phys_enc);
>> +}
>> +
>> +static void dpu_encoder_phys_wb_prepare_wb_job(struct 
>> dpu_encoder_phys *phys_enc,
>> +        struct drm_writeback_job *job)
>> +{
>> +    const struct msm_format *format;
>> +    struct msm_gem_address_space *aspace;
>> +    struct dpu_hw_wb_cfg *wb_cfg;
>> +    int ret;
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +
>> +    if (!job->fb)
>> +        return;
>> +
>> +    wb_enc->wb_job = job;
>> +    wb_enc->wb_conn = job->connector;
>> +    aspace = phys_enc->dpu_kms->base.aspace;
>> +
>> +    wb_cfg = &wb_enc->wb_cfg;
>> +
>> +    memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
>> +
>> +    ret = msm_framebuffer_prepare(job->fb, aspace, false);
>> +    if (ret) {
>> +        DPU_ERROR("prep fb failed, %d\n", ret);
>> +        return;
>> +    }
>> +
>> +    format = msm_framebuffer_format(job->fb);
>> +
>> +    wb_cfg->dest.format = dpu_get_dpu_format_ext(
>> +            format->pixel_format, job->fb->modifier);
>> +    if (!wb_cfg->dest.format) {
>> +        /* this error should be detected during atomic_check */
>> +        DPU_ERROR("failed to get format %x\n", format->pixel_format);
>> +        return;
>> +    }
>> +
>> +    ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
>> +    if (ret) {
>> +        DPU_DEBUG("failed to populate layout %d\n", ret);
>> +        return;
>> +    }
>> +
>> +    wb_cfg->dest.width = job->fb->width;
>> +    wb_cfg->dest.height = job->fb->height;
>> +    wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
>> +
>> +    if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
>> +            (wb_cfg->dest.format->element[0] == C1_B_Cb))
>> +        swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
>> +
>> +    DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>> +            wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
>> +            wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
>> +
>> +    DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>> +            wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
>> +            wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
>> +}
>> +
>> +static void dpu_encoder_phys_wb_cleanup_wb_job(struct 
>> dpu_encoder_phys *phys_enc,
>> +        struct drm_writeback_job *job)
>> +{
>> +    struct dpu_encoder_phys_wb *wb_enc = 
>> to_dpu_encoder_phys_wb(phys_enc);
>> +    struct msm_gem_address_space *aspace;
>> +
>> +    if (!job->fb)
>> +        return;
>> +
>> +    aspace = phys_enc->dpu_kms->base.aspace;
>> +
>> +    msm_framebuffer_cleanup(job->fb, aspace, false);
>> +    wb_enc->wb_job = NULL;
>> +    wb_enc->wb_conn = NULL;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_init_ops - initialize writeback operations
>> + * @ops:    Pointer to encoder operation table
>> + */
>> +static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops 
>> *ops)
>> +{
>> +    ops->is_master = dpu_encoder_phys_wb_is_master;
>> +    ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
>> +    ops->enable = dpu_encoder_phys_wb_enable;
>> +    ops->disable = dpu_encoder_phys_wb_disable;
>> +    ops->destroy = dpu_encoder_phys_wb_destroy;
>> +    ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
>> +    ops->wait_for_commit_done = 
>> dpu_encoder_phys_wb_wait_for_commit_done;
>> +    ops->prepare_for_kickoff = dpu_encoder_phys_wb_prepare_for_kickoff;
>> +    ops->handle_post_kickoff = dpu_encoder_phys_wb_handle_post_kickoff;
>> +    ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
>> +    ops->trigger_start = dpu_encoder_helper_trigger_start;
>> +    ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
>> +    ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
>> +    ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
>> +}
>> +
>> +/**
>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>> + * @init:    Pointer to init info structure with initialization params
>> + */
>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>> +        struct dpu_enc_phys_init_params *p)
>> +{
>> +    struct dpu_encoder_phys *phys_enc = NULL;
>> +    struct dpu_encoder_phys_wb *wb_enc = NULL;
>> +    int ret = 0;
>> +    int i;
>> +
>> +    DPU_DEBUG("\n");
>> +
>> +    if (!p || !p->parent) {
>> +        DPU_ERROR("invalid params\n");
>> +        ret = -EINVAL;
>> +        goto fail_alloc;
>> +    }
>> +
>> +    wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
>> +    if (!wb_enc) {
>> +        DPU_ERROR("failed to allocate wb phys_enc enc\n");
>> +        ret = -ENOMEM;
>> +        goto fail_alloc;
>> +    }
>> +
>> +    phys_enc = &wb_enc->base;
>> +    phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
>> +    phys_enc->intf_idx = p->intf_idx;
>> +
>> +    dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
>> +    phys_enc->parent = p->parent;
>> +    phys_enc->parent_ops = p->parent_ops;
>> +    phys_enc->dpu_kms = p->dpu_kms;
>> +    phys_enc->split_role = p->split_role;
>> +    phys_enc->intf_mode = INTF_MODE_WB_LINE;
>> +    phys_enc->intf_idx = p->intf_idx;
>> +    phys_enc->enc_spinlock = p->enc_spinlock;
>> +
>> +    atomic_set(&wb_enc->wbirq_refcount, 0);
>> +
>> +    for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
>> +        phys_enc->irq[i] = -EINVAL;
>> +
>> +    atomic_set(&phys_enc->pending_kickoff_cnt, 0);
>> +    atomic_set(&phys_enc->vblank_refcount, 0);
>> +    wb_enc->wb_done_timeout_cnt = 0;
>> +
>> +    init_waitqueue_head(&phys_enc->pending_kickoff_wq);
>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>> +
>> +    DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
>> +            phys_enc->intf_idx);
>> +
>> +    return phys_enc;
>> +
>> +fail_alloc:
>> +    return ERR_PTR(ret);
>> +}
> 
> 

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

* Re: [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20 17:41     ` Abhinav Kumar
@ 2022-04-20 18:37       ` Dmitry Baryshkov
  2022-04-20 18:46         ` [Freedreno] " Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 18:37 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On Wed, 20 Apr 2022 at 20:41, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>
>
>
> On 4/20/2022 12:44 AM, Dmitry Baryshkov wrote:
> > On 20/04/2022 04:46, Abhinav Kumar wrote:
> >> Make changes to dpu_encoder to support virtual encoder needed
> >> to support writeback for dpu.
> >>
> >> changes in v2:
> >>     - add the writeback parts to dpu_encoder_helper_phys_cleanup
> >>     - rebase on tip of msm-next and fix related dependencies
> >>     - get the writeback blocks directly from RM
> >>
> >> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >> ---
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71
> >> +++++++++++++++++-------
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
> >>   2 files changed, 54 insertions(+), 20 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> >> index 0e31ad3..06b8631 100644
> >> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> >> @@ -928,6 +928,7 @@ static void
> >> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
> >>       struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
> >>       int num_lm, num_ctl, num_pp, num_dsc;
> >>       unsigned int dsc_mask = 0;
> >> +    enum dpu_hw_blk_type blk_type;
> >>       int i;
> >>       if (!drm_enc) {
> >> @@ -1009,12 +1010,21 @@ static void
> >> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
> >>           phys->hw_pp = dpu_enc->hw_pp[i];
> >>           phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
> >> -        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
> >> -            phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
> >> phys->intf_idx);
> >> +        if (dpu_encoder_get_intf_mode(&dpu_enc->base) ==
> >> INTF_MODE_WB_LINE)
> >> +            blk_type = DPU_HW_BLK_WB;
> >> +        else
> >> +            blk_type = DPU_HW_BLK_INTF;
> >> +
> >> +        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
> >> +            if (blk_type == DPU_HW_BLK_INTF)
> >> +                phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
> >> phys->intf_idx);
> >> +            else if (blk_type == DPU_HW_BLK_WB)
> >> +                phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm,
> >> phys->intf_idx);
> >> +        }
> >> -        if (!phys->hw_intf) {
> >> +        if (!phys->hw_intf && !phys->hw_wb) {
> >>               DPU_ERROR_ENC(dpu_enc,
> >> -                      "no intf block assigned at idx: %d\n", i);
> >> +                      "no intf ow wb block assigned at idx: %d\n", i);
> >
> > or wb
> ack
> >
> >>               return;
> >>           }
> >> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct
> >> drm_encoder *drm_enc)
> >>       mutex_unlock(&dpu_enc->enc_lock);
> >>   }
> >> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
> >> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg
> >> *catalog,
> >>           enum dpu_intf_type type, u32 controller_id)
> >>   {
> >>       int i = 0;
> >> -    for (i = 0; i < catalog->intf_count; i++) {
> >> -        if (catalog->intf[i].type == type
> >> -            && catalog->intf[i].controller_id == controller_id) {
> >> -            return catalog->intf[i].id;
> >> +    if (type != INTF_WB) {
> >> +        for (i = 0; i < catalog->intf_count; i++) {
> >> +            if (catalog->intf[i].type == type
> >> +                && catalog->intf[i].controller_id == controller_id) {
> >> +                return catalog->intf[i].id;
> >> +            }
> >> +        }
> >> +    } else {
> >> +        for (i = 0; i < catalog->wb_count; i++) {
> >> +            if (catalog->wb[i].id == controller_id)
> >> +                return catalog->wb[i].id;
> >>           }
> >>       }
> >> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct
> >> dpu_encoder_phys *phys_enc)
> >>       dpu_encoder_helper_reset_mixers(phys_enc);
> >> -    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> >> -        if (dpu_enc->phys_encs[i] &&
> >> phys_enc->hw_intf->ops.bind_pingpong_blk)
> >> -            phys_enc->hw_intf->ops.bind_pingpong_blk(
> >> -                    dpu_enc->phys_encs[i]->hw_intf, false,
> >> -                    dpu_enc->phys_encs[i]->hw_pp->idx);
> >> +    if (phys_enc->hw_wb) {
> >
> > I think this adds a hidden knowledge here. That there is always just a
> > single phys_enc for the WB encoder. I'd still do this cleanup in a loop
> > together with the INTF cleanup.
> alright, I can make this change.
> >
> >> +        /* disable the PP block */
> >> +        if (phys_enc->hw_wb->ops.bind_pingpong_blk)
> >> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb,
> >> false,
> >> +                    phys_enc->hw_pp->idx);
> >> -        /* mark INTF flush as pending */
> >> -        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
> >> -
> >> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
> >> -                    dpu_enc->phys_encs[i]->hw_intf->idx);
> >> +        /* mark WB flush as pending */
> >> +        if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
> >> +            phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl,
> >> phys_enc->hw_wb->idx);
> >> +    } else {
> >> +        for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> >> +            if (dpu_enc->phys_encs[i] &&
> >> phys_enc->hw_intf->ops.bind_pingpong_blk)
> >> +                phys_enc->hw_intf->ops.bind_pingpong_blk(
> >> +                        dpu_enc->phys_encs[i]->hw_intf, false,
> >> +                        dpu_enc->phys_encs[i]->hw_pp->idx);
> >> +
> >> +            /* mark INTF flush as pending */
> >> +            if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
> >> +
> >> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
> >> +                        dpu_enc->phys_encs[i]->hw_intf->idx);
> >> +        }
> >>       }
> >>       /* reset the merge 3D HW block */
> >> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct
> >> dpu_encoder_virt *dpu_enc,
> >>       case DRM_MODE_ENCODER_TMDS:
> >>           intf_type = INTF_DP;
> >>           break;
> >> +    case DRM_MODE_ENCODER_VIRTUAL:
> >> +        intf_type = INTF_WB;
> >> +        break;
> >>       }
> >>       WARN_ON(disp_info->num_of_h_tiles < 1);
> >> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct
> >> dpu_encoder_virt *dpu_enc,
> >>           DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
> >>                   i, controller_id, phys_params.split_role);
> >> -        phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
> >> +        phys_params.intf_idx =
> >> dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
> >>                                                       intf_type,
> >>                                                       controller_id);
> >
> > I'd prefer to be more explicit here. intf_idx is of type enum dpu_intf,
> > while WBs are enumerated with enum dpu_wb.
> >
> > I's suggest adding a separate phys_params.wb_idx and a
> > dpu_encoder_get_wb() and calling one here depending on intf_type.
> >
> > Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check
> > intf_mode, but get both intf and wb and report an error if none was
> > provided.
>
> Your suggestion is valid and I also thought about it.
>
> Let me explain what prevented me from making the change here and put it
> in a to-do bucket.
>
> 1) This needs a slighly bigger cleanup including the traces, debug
> prints and some helpers as none of them are aware of the wb_idx
>
> 2) Some of the checks need to be adjusted like this one
>
> if (phys_params.intf_idx == INTF_MAX) {
>              DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d,
> id %d\n",
>                            intf_type, controller_id);
>              ret = -EINVAL;
>          }
>
> So, when we have a separate intf_idx and a wb_idx, having either one of
> them is enough . Consider a case where a board has no physical display.
> That would have no intf_idx but wb_idx can be valid.
>
> So i think this needs a little careful cleanup.
>
> Considering that I need to test that out more, I decided that for a
> short-term we can live with the fact that wb_idx is of type enum
> dpu_intf because dpu_encoder layer understands only that.
>
> Let me know if you agree on this separation to go as a follow-up.

Actually both your points vote for the immediate separation rather
than doing it in a follow-up.
For example, if you see an error reported against INTF_2 on a board
where INTF_2 is actually used, you can not determine whether it is an
issue with INTF_2 or with WB_2.

In fact even reporting an error (or a trace) against INTF_NONE (or
INTF_MAX) might be a better option. It would clearly denote that the
issue is related to the non-INTF.

I think we can merge the patch as is, but I'd strongly prefer either
to see an update or a (nearly) immediate followup.

What do you think about an interim solution? We split the
intf_idx/wb_idx in these series, but all the debugging can be updated
later. This way once we see the report against INTF_NONE, we can
deduce that it's WB.

> >
> >>           if (phys_params.intf_idx == INTF_MAX) {
> >> -            DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id
> >> %d\n",
> >> +            DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type
> >> %d, id %d\n",
> >>                             intf_type, controller_id);
> >>               ret = -EINVAL;
> >>           }
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >> index 544a9a4..0b80af4 100644
> >> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >> @@ -11,6 +11,7 @@
> >>   #include "dpu_kms.h"
> >>   #include "dpu_hw_intf.h"
> >> +#include "dpu_hw_wb.h"
> >>   #include "dpu_hw_pingpong.h"
> >>   #include "dpu_hw_ctl.h"
> >>   #include "dpu_hw_top.h"
> >> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
> >>    * @hw_ctl:        Hardware interface to the ctl registers
> >>    * @hw_pp:        Hardware interface to the ping pong registers
> >>    * @hw_intf:        Hardware interface to the intf registers
> >> + * @hw_wb:        Hardware interface to the wb registers
> >>    * @dpu_kms:        Pointer to the dpu_kms top level
> >>    * @cached_mode:    DRM mode cached at mode_set time, acted on in
> >> enable
> >>    * @enabled:        Whether the encoder has enabled and running a mode
> >> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
> >>       struct dpu_hw_ctl *hw_ctl;
> >>       struct dpu_hw_pingpong *hw_pp;
> >>       struct dpu_hw_intf *hw_intf;
> >> +    struct dpu_hw_wb *hw_wb;
> >>       struct dpu_kms *dpu_kms;
> >>       struct drm_display_mode cached_mode;
> >>       enum dpu_enc_split_role split_role;
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [Freedreno] [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20 18:37       ` Dmitry Baryshkov
@ 2022-04-20 18:46         ` Abhinav Kumar
  2022-04-20 22:06           ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 18:46 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno



On 4/20/2022 11:37 AM, Dmitry Baryshkov wrote:
> On Wed, 20 Apr 2022 at 20:41, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>>
>>
>>
>> On 4/20/2022 12:44 AM, Dmitry Baryshkov wrote:
>>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>>> Make changes to dpu_encoder to support virtual encoder needed
>>>> to support writeback for dpu.
>>>>
>>>> changes in v2:
>>>>      - add the writeback parts to dpu_encoder_helper_phys_cleanup
>>>>      - rebase on tip of msm-next and fix related dependencies
>>>>      - get the writeback blocks directly from RM
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71
>>>> +++++++++++++++++-------
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
>>>>    2 files changed, 54 insertions(+), 20 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>> index 0e31ad3..06b8631 100644
>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>> @@ -928,6 +928,7 @@ static void
>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>        struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
>>>>        int num_lm, num_ctl, num_pp, num_dsc;
>>>>        unsigned int dsc_mask = 0;
>>>> +    enum dpu_hw_blk_type blk_type;
>>>>        int i;
>>>>        if (!drm_enc) {
>>>> @@ -1009,12 +1010,21 @@ static void
>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>            phys->hw_pp = dpu_enc->hw_pp[i];
>>>>            phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>>>> -        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
>>>> -            phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>> phys->intf_idx);
>>>> +        if (dpu_encoder_get_intf_mode(&dpu_enc->base) ==
>>>> INTF_MODE_WB_LINE)
>>>> +            blk_type = DPU_HW_BLK_WB;
>>>> +        else
>>>> +            blk_type = DPU_HW_BLK_INTF;
>>>> +
>>>> +        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
>>>> +            if (blk_type == DPU_HW_BLK_INTF)
>>>> +                phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>> phys->intf_idx);
>>>> +            else if (blk_type == DPU_HW_BLK_WB)
>>>> +                phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm,
>>>> phys->intf_idx);
>>>> +        }
>>>> -        if (!phys->hw_intf) {
>>>> +        if (!phys->hw_intf && !phys->hw_wb) {
>>>>                DPU_ERROR_ENC(dpu_enc,
>>>> -                      "no intf block assigned at idx: %d\n", i);
>>>> +                      "no intf ow wb block assigned at idx: %d\n", i);
>>>
>>> or wb
>> ack
>>>
>>>>                return;
>>>>            }
>>>> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct
>>>> drm_encoder *drm_enc)
>>>>        mutex_unlock(&dpu_enc->enc_lock);
>>>>    }
>>>> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog,
>>>> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg
>>>> *catalog,
>>>>            enum dpu_intf_type type, u32 controller_id)
>>>>    {
>>>>        int i = 0;
>>>> -    for (i = 0; i < catalog->intf_count; i++) {
>>>> -        if (catalog->intf[i].type == type
>>>> -            && catalog->intf[i].controller_id == controller_id) {
>>>> -            return catalog->intf[i].id;
>>>> +    if (type != INTF_WB) {
>>>> +        for (i = 0; i < catalog->intf_count; i++) {
>>>> +            if (catalog->intf[i].type == type
>>>> +                && catalog->intf[i].controller_id == controller_id) {
>>>> +                return catalog->intf[i].id;
>>>> +            }
>>>> +        }
>>>> +    } else {
>>>> +        for (i = 0; i < catalog->wb_count; i++) {
>>>> +            if (catalog->wb[i].id == controller_id)
>>>> +                return catalog->wb[i].id;
>>>>            }
>>>>        }
>>>> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct
>>>> dpu_encoder_phys *phys_enc)
>>>>        dpu_encoder_helper_reset_mixers(phys_enc);
>>>> -    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>> -        if (dpu_enc->phys_encs[i] &&
>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>> -            phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>> -                    dpu_enc->phys_encs[i]->hw_intf, false,
>>>> -                    dpu_enc->phys_encs[i]->hw_pp->idx);
>>>> +    if (phys_enc->hw_wb) {
>>>
>>> I think this adds a hidden knowledge here. That there is always just a
>>> single phys_enc for the WB encoder. I'd still do this cleanup in a loop
>>> together with the INTF cleanup.
>> alright, I can make this change.
>>>
>>>> +        /* disable the PP block */
>>>> +        if (phys_enc->hw_wb->ops.bind_pingpong_blk)
>>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb,
>>>> false,
>>>> +                    phys_enc->hw_pp->idx);
>>>> -        /* mark INTF flush as pending */
>>>> -        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>> -
>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>> -                    dpu_enc->phys_encs[i]->hw_intf->idx);
>>>> +        /* mark WB flush as pending */
>>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
>>>> +            phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl,
>>>> phys_enc->hw_wb->idx);
>>>> +    } else {
>>>> +        for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>> +            if (dpu_enc->phys_encs[i] &&
>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>> +                phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>> +                        dpu_enc->phys_encs[i]->hw_intf, false,
>>>> +                        dpu_enc->phys_encs[i]->hw_pp->idx);
>>>> +
>>>> +            /* mark INTF flush as pending */
>>>> +            if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>> +
>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>> +                        dpu_enc->phys_encs[i]->hw_intf->idx);
>>>> +        }
>>>>        }
>>>>        /* reset the merge 3D HW block */
>>>> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct
>>>> dpu_encoder_virt *dpu_enc,
>>>>        case DRM_MODE_ENCODER_TMDS:
>>>>            intf_type = INTF_DP;
>>>>            break;
>>>> +    case DRM_MODE_ENCODER_VIRTUAL:
>>>> +        intf_type = INTF_WB;
>>>> +        break;
>>>>        }
>>>>        WARN_ON(disp_info->num_of_h_tiles < 1);
>>>> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct
>>>> dpu_encoder_virt *dpu_enc,
>>>>            DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>>>>                    i, controller_id, phys_params.split_role);
>>>> -        phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
>>>> +        phys_params.intf_idx =
>>>> dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
>>>>                                                        intf_type,
>>>>                                                        controller_id);
>>>
>>> I'd prefer to be more explicit here. intf_idx is of type enum dpu_intf,
>>> while WBs are enumerated with enum dpu_wb.
>>>
>>> I's suggest adding a separate phys_params.wb_idx and a
>>> dpu_encoder_get_wb() and calling one here depending on intf_type.
>>>
>>> Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check
>>> intf_mode, but get both intf and wb and report an error if none was
>>> provided.
>>
>> Your suggestion is valid and I also thought about it.
>>
>> Let me explain what prevented me from making the change here and put it
>> in a to-do bucket.
>>
>> 1) This needs a slighly bigger cleanup including the traces, debug
>> prints and some helpers as none of them are aware of the wb_idx
>>
>> 2) Some of the checks need to be adjusted like this one
>>
>> if (phys_params.intf_idx == INTF_MAX) {
>>               DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d,
>> id %d\n",
>>                             intf_type, controller_id);
>>               ret = -EINVAL;
>>           }
>>
>> So, when we have a separate intf_idx and a wb_idx, having either one of
>> them is enough . Consider a case where a board has no physical display.
>> That would have no intf_idx but wb_idx can be valid.
>>
>> So i think this needs a little careful cleanup.
>>
>> Considering that I need to test that out more, I decided that for a
>> short-term we can live with the fact that wb_idx is of type enum
>> dpu_intf because dpu_encoder layer understands only that.
>>
>> Let me know if you agree on this separation to go as a follow-up.
> 
> Actually both your points vote for the immediate separation rather
> than doing it in a follow-up.
> For example, if you see an error reported against INTF_2 on a board
> where INTF_2 is actually used, you can not determine whether it is an
> issue with INTF_2 or with WB_2.
> 
> In fact even reporting an error (or a trace) against INTF_NONE (or
> INTF_MAX) might be a better option. It would clearly denote that the
> issue is related to the non-INTF.
> 
> I think we can merge the patch as is, but I'd strongly prefer either
> to see an update or a (nearly) immediate followup.
> 
> What do you think about an interim solution? We split the
> intf_idx/wb_idx in these series, but all the debugging can be updated
> later. This way once we see the report against INTF_NONE, we can
> deduce that it's WB.

Thank you for your consideration.

I will post a follow-up change to fix this no later than next week.
I will even try doing it this week itself.

I will fix everything together so that it looks like a separate series 
of "separating intf_idx and wb_idx" and there is no intermediate mismatch.

Will leave a FIXME here as well.

> 
>>>
>>>>            if (phys_params.intf_idx == INTF_MAX) {
>>>> -            DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id
>>>> %d\n",
>>>> +            DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type
>>>> %d, id %d\n",
>>>>                              intf_type, controller_id);
>>>>                ret = -EINVAL;
>>>>            }
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> index 544a9a4..0b80af4 100644
>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> @@ -11,6 +11,7 @@
>>>>    #include "dpu_kms.h"
>>>>    #include "dpu_hw_intf.h"
>>>> +#include "dpu_hw_wb.h"
>>>>    #include "dpu_hw_pingpong.h"
>>>>    #include "dpu_hw_ctl.h"
>>>>    #include "dpu_hw_top.h"
>>>> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
>>>>     * @hw_ctl:        Hardware interface to the ctl registers
>>>>     * @hw_pp:        Hardware interface to the ping pong registers
>>>>     * @hw_intf:        Hardware interface to the intf registers
>>>> + * @hw_wb:        Hardware interface to the wb registers
>>>>     * @dpu_kms:        Pointer to the dpu_kms top level
>>>>     * @cached_mode:    DRM mode cached at mode_set time, acted on in
>>>> enable
>>>>     * @enabled:        Whether the encoder has enabled and running a mode
>>>> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
>>>>        struct dpu_hw_ctl *hw_ctl;
>>>>        struct dpu_hw_pingpong *hw_pp;
>>>>        struct dpu_hw_intf *hw_intf;
>>>> +    struct dpu_hw_wb *hw_wb;
>>>>        struct dpu_kms *dpu_kms;
>>>>        struct drm_display_mode cached_mode;
>>>>        enum dpu_enc_split_role split_role;
>>>
>>>
> 
> 
> 

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

* Re: [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl
  2022-04-20 17:16     ` Abhinav Kumar
@ 2022-04-20 18:48       ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 18:48 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On Wed, 20 Apr 2022 at 20:16, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>
>
>
> On 4/19/2022 11:59 PM, Dmitry Baryshkov wrote:
> > On 20/04/2022 04:46, Abhinav Kumar wrote:
> >> Add changes to support writeback module in the dpu_hw_ctl
> >> interface.
> >>
> >> changes in v2:
> >>     - keep only the wb specific changes to reset_intf_cfg
> >>     - use cfg->intf / cfg->wb to identify intf or wb
> >>     - use bit-wise OR for the wb bits while programming
> >>
> >> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >> ---
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 43
> >> +++++++++++++++++++++++++++---
> >>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 15 ++++++++++-
> >>   2 files changed, 53 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> >> index 524f024..495a9cd 100644
> >> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c
> >> @@ -1,5 +1,6 @@
> >>   // SPDX-License-Identifier: GPL-2.0-only
> >> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> >> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >> reserved.
> >> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> >>    */
> >>   #include <linux/delay.h>
> >> @@ -23,10 +24,12 @@
> >>   #define   CTL_SW_RESET                  0x030
> >>   #define   CTL_LAYER_EXTN_OFFSET         0x40
> >>   #define   CTL_MERGE_3D_ACTIVE           0x0E4
> >> +#define   CTL_WB_ACTIVE                 0x0EC
> >>   #define   CTL_INTF_ACTIVE               0x0F4
> >>   #define   CTL_MERGE_3D_FLUSH            0x100
> >>   #define   CTL_DSC_ACTIVE                0x0E8
> >>   #define   CTL_DSC_FLUSH                0x104
> >> +#define   CTL_WB_FLUSH                  0x108
> >>   #define   CTL_INTF_FLUSH                0x110
> >>   #define   CTL_INTF_MASTER               0x134
> >>   #define   CTL_FETCH_PIPE_ACTIVE         0x0FC
> >> @@ -38,6 +41,7 @@
> >>   #define  MERGE_3D_IDX   23
> >>   #define  DSC_IDX        22
> >>   #define  INTF_IDX       31
> >> +#define WB_IDX          16
> >>   #define CTL_INVALID_BIT                 0xffff
> >>   #define CTL_DEFAULT_GROUP_ID        0xf
> >> @@ -135,6 +139,9 @@ static inline void
> >> dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx)
> >>       if (ctx->pending_flush_mask & BIT(INTF_IDX))
> >>           DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH,
> >>                   ctx->pending_intf_flush_mask);
> >> +    if (ctx->pending_flush_mask & BIT(WB_IDX))
> >> +        DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH,
> >> +                ctx->pending_wb_flush_mask);
> >>       DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
> >>   }
> >> @@ -255,6 +262,13 @@ static void
> >> dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx,
> >>       }
> >>   }
> >> +static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl
> >> *ctx,
> >> +        enum dpu_wb wb)
> >> +{
> >> +    ctx->pending_wb_flush_mask |= BIT(wb - WB_0);
> >> +    ctx->pending_flush_mask |= BIT(WB_IDX);
> >> +}
> >> +
> >>   static void dpu_hw_ctl_update_pending_flush_intf_v1(struct
> >> dpu_hw_ctl *ctx,
> >>           enum dpu_intf intf)
> >>   {
> >> @@ -504,6 +518,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct
> >> dpu_hw_ctl *ctx,
> >>   {
> >>       struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>       u32 intf_active = 0;
> >> +    u32 wb_active = 0;
> >>       u32 mode_sel = 0;
> >>       /* CTL_TOP[31:28] carries group_id to collate CTL paths
> >> @@ -519,11 +534,20 @@ static void dpu_hw_ctl_intf_cfg_v1(struct
> >> dpu_hw_ctl *ctx,
> >>       if (cfg->intf_mode_sel == DPU_CTL_MODE_SEL_CMD)
> >>           mode_sel |= BIT(17);
> >> -    intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
> >> -    intf_active |= BIT(cfg->intf - INTF_0);
> >> +    if (cfg->intf) {
> >> +        intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE);
> >> +        intf_active |= BIT(cfg->intf - INTF_0);
> >> +    }
> >> +
> >> +    if (cfg->wb) {
> >> +        wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
> >> +        wb_active |= BIT(cfg->wb - WB_0);
> >> +    }
> >>       DPU_REG_WRITE(c, CTL_TOP, mode_sel);
> >>       DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
> >> +    DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
> >
> > This will not work as expected. If cfg->intf is not set, CTL_INTF_ACTIVE
> > will be reset to 0 (while it should have been retained). Please change
> > this to always read CTL_INTF_ACTIVE/CTL_WB_ACTIVE.
>
> ack, and thanks for catching this.
> Yes, i need to add the always read part back.
>
> >
> >> +
> >>       if (cfg->merge_3d)
> >>           DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE,
> >>                     BIT(cfg->merge_3d - MERGE_3D_0));
> >> @@ -546,6 +570,9 @@ static void dpu_hw_ctl_intf_cfg(struct dpu_hw_ctl
> >> *ctx,
> >>           intf_cfg |= (cfg->mode_3d - 0x1) << 20;
> >>       }
> >> +    if (cfg->wb)
> >> +        intf_cfg |= (cfg->wb & 0x3) + 2;
> >> +
> >
> > Ugh. I see that we have the same code in downstream driver. And that we
> > do not support WB_0 at all. But maybe we should be more explicit here.
>
> Sorry, I didnt follow this comment. Why is this related to WB_0?
>
> All this code is doing is that its programming the lower bits of CTL_TOP
> register to be used for WB mode.
>
> The correct value of this register for linear wb mode which we use is 0x5.
>
> Which will still be correct now because cfg->wb will be 0x3.
>
> Coming to other non-WB_2 values, this code is still correct.
>
> Lets say cfg->wb was 0x1 ( for WB_0), then the register will be
> programmed to 0x3 which is the correct value to use because then we will
> be using rotation and not linear writeback.
>
> Perhaps, you need a comment here to explain this?

IIRC, at least for 8916 WB_0 must be used with this field set to 0x1
or 0x3 depending on other settings.
Thus I thought it might be better to be explicit here.

As a second thought, let's keep it as is (and if somebody works on
WB_0/rotation support, he will know what to set here anyway).

>
> >
> >>       switch (cfg->intf_mode_sel) {
> >>       case DPU_CTL_MODE_SEL_VID:
> >>           intf_cfg &= ~BIT(17);
> >> @@ -568,12 +595,13 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct
> >> dpu_hw_ctl *ctx,
> >>   {
> >>       struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>       u32 intf_active = 0;
> >> +    u32 wb_active = 0;
> >>       u32 merge3d_active = 0;
> >>       /*
> >>        * This API resets each portion of the CTL path namely,
> >>        * clearing the sspps staged on the lm, merge_3d block,
> >> -     * interfaces etc to ensure clean teardown of the pipeline.
> >> +     * interfaces , writeback etc to ensure clean teardown of the
> >> pipeline.
> >>        * This will be used for writeback to begin with to have a
> >>        * proper teardown of the writeback session but upon further
> >>        * validation, this can be extended to all interfaces.
> >> @@ -592,6 +620,12 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct
> >> dpu_hw_ctl *ctx,
> >>           intf_active &= ~BIT(cfg->intf - INTF_0);
> >>           DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active);
> >>       }
> >> +
> >> +    if (cfg->wb) {
> >> +        wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE);
> >> +        wb_active &= ~BIT(cfg->wb - WB_0);
> >> +        DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active);
> >> +    }
> >>   }
> >>   static void dpu_hw_ctl_set_fetch_pipe_active(struct dpu_hw_ctl *ctx,
> >> @@ -622,6 +656,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops
> >> *ops,
> >>               dpu_hw_ctl_update_pending_flush_intf_v1;
> >>           ops->update_pending_flush_merge_3d =
> >>               dpu_hw_ctl_update_pending_flush_merge_3d_v1;
> >> +        ops->update_pending_flush_wb =
> >> dpu_hw_ctl_update_pending_flush_wb_v1;
> >
> > Do we also need the update_pending_flush_wb for non-ACTIVE_CTL case? I
> > think we do.
>
> Yes, but the bits will be different. I can update it.

Yes, please.

>
> >
> >>       } else {
> >>           ops->trigger_flush = dpu_hw_ctl_trigger_flush;
> >>           ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg;
> >> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> >> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> >> index c61a8fd..df8f8e9 100644
> >> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> >> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h
> >> @@ -1,5 +1,6 @@
> >>   /* SPDX-License-Identifier: GPL-2.0-only */
> >> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> >> +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >> reserved.
> >> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> >>    */
> >>   #ifndef _DPU_HW_CTL_H
> >> @@ -44,6 +45,7 @@ struct dpu_hw_stage_cfg {
> >>    */
> >>   struct dpu_hw_intf_cfg {
> >>       enum dpu_intf intf;
> >> +    enum dpu_wb wb;
> >>       enum dpu_3d_blend_mode mode_3d;
> >>       enum dpu_merge_3d merge_3d;
> >>       enum dpu_ctl_mode_sel intf_mode_sel;
> >> @@ -102,6 +104,15 @@ struct dpu_hw_ctl_ops {
> >>           u32 flushbits);
> >>       /**
> >> +     * OR in the given flushbits to the cached pending_(wb_)flush_mask
> >> +     * No effect on hardware
> >> +     * @ctx       : ctl path ctx pointer
> >> +     * @blk       : writeback block index
> >> +     */
> >> +    void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx,
> >> +        enum dpu_wb blk);
> >> +
> >> +    /**
> >>        * OR in the given flushbits to the cached
> >> pending_(intf_)flush_mask
> >>        * No effect on hardware
> >>        * @ctx       : ctl path ctx pointer
> >> @@ -199,6 +210,7 @@ struct dpu_hw_ctl_ops {
> >>    * @mixer_hw_caps: mixer hardware capabilities
> >>    * @pending_flush_mask: storage for pending ctl_flush managed via ops
> >>    * @pending_intf_flush_mask: pending INTF flush
> >> + * @pending_wb_flush_mask: pending WB flush
> >>    * @ops: operation list
> >>    */
> >>   struct dpu_hw_ctl {
> >> @@ -212,6 +224,7 @@ struct dpu_hw_ctl {
> >>       const struct dpu_lm_cfg *mixer_hw_caps;
> >>       u32 pending_flush_mask;
> >>       u32 pending_intf_flush_mask;
> >> +    u32 pending_wb_flush_mask;
> >>       u32 pending_merge_3d_flush_mask;
> >>       /* ops */
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
  2022-04-20 18:11         ` Abhinav Kumar
@ 2022-04-20 18:49           ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 18:49 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On Wed, 20 Apr 2022 at 21:11, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>
> Hi Dmitry
>
> Sorry, I missed answering one question.
>
> On 4/20/2022 10:49 AM, Dmitry Baryshkov wrote:
> > On Wed, 20 Apr 2022 at 20:01, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
> >>
> >>
> >>
> >> On 4/20/2022 12:20 AM, Dmitry Baryshkov wrote:
> >>> On 20/04/2022 04:45, Abhinav Kumar wrote:
> >>>> Add the dpu_hw_wb abstraction to program registers related to the
> >>>> writeback block. These will be invoked once all the configuration
> >>>> is set and ready to be programmed to the registers.
> >>>>
> >>>> changes in v2:
> >>>>      - remove multiple empty lines at the end of the file
> >>>>      - change dpu_hw_wb_bind_pingpong_blk to preserve upper bits
> >>>>
> >>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >>>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> >>>
> >>> It's still Reviewed-by, few nits below.
> >>>
> >>>> ---
> >>>>    drivers/gpu/drm/msm/Makefile              |   1 +
> >>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 273
> >>>> ++++++++++++++++++++++++++++++
> >>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 131 ++++++++++++++
> >>>>    3 files changed, 405 insertions(+)
> >>>>    create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >>>>    create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >>>>
> >>>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> >>>> index d5ca2e6..ca779c1 100644
> >>>> --- a/drivers/gpu/drm/msm/Makefile
> >>>> +++ b/drivers/gpu/drm/msm/Makefile
> >>>> @@ -74,6 +74,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> >>>>        disp/dpu1/dpu_hw_top.o \
> >>>>        disp/dpu1/dpu_hw_util.o \
> >>>>        disp/dpu1/dpu_hw_vbif.o \
> >>>> +    disp/dpu1/dpu_hw_wb.o \
> >>>>        disp/dpu1/dpu_kms.o \
> >>>>        disp/dpu1/dpu_plane.o \
> >>>>        disp/dpu1/dpu_rm.o \
> >>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >>>> new file mode 100644
> >>>> index 0000000..afa8aab
> >>>> --- /dev/null
> >>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
> >>>> @@ -0,0 +1,273 @@
> >>>> +// SPDX-License-Identifier: GPL-2.0-only
> >>>> + /*
> >>>> +  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >>>> reserved
> >>>> +  */
> >>>> +
> >>>> +#include "dpu_hw_mdss.h"
> >>>> +#include "dpu_hwio.h"
> >>>> +#include "dpu_hw_catalog.h"
> >>>> +#include "dpu_hw_wb.h"
> >>>> +#include "dpu_formats.h"
> >>>> +#include "dpu_kms.h"
> >>>> +
> >>>> +#define WB_DST_FORMAT                         0x000
> >>>> +#define WB_DST_OP_MODE                        0x004
> >>>> +#define WB_DST_PACK_PATTERN                   0x008
> >>>> +#define WB_DST0_ADDR                          0x00C
> >>>> +#define WB_DST1_ADDR                          0x010
> >>>> +#define WB_DST2_ADDR                          0x014
> >>>> +#define WB_DST3_ADDR                          0x018
> >>>> +#define WB_DST_YSTRIDE0                       0x01C
> >>>> +#define WB_DST_YSTRIDE1                       0x020
> >>>> +#define WB_DST_YSTRIDE1                       0x020
> >>>> +#define WB_DST_DITHER_BITDEPTH                0x024
> >>>> +#define WB_DST_MATRIX_ROW0                    0x030
> >>>> +#define WB_DST_MATRIX_ROW1                    0x034
> >>>> +#define WB_DST_MATRIX_ROW2                    0x038
> >>>> +#define WB_DST_MATRIX_ROW3                    0x03C
> >>>> +#define WB_DST_WRITE_CONFIG                   0x048
> >>>> +#define WB_ROTATION_DNSCALER                  0x050
> >>>> +#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
> >>>> +#define WB_N16_INIT_PHASE_X_C03               0x060
> >>>> +#define WB_N16_INIT_PHASE_X_C12               0x064
> >>>> +#define WB_N16_INIT_PHASE_Y_C03               0x068
> >>>> +#define WB_N16_INIT_PHASE_Y_C12               0x06C
> >>>> +#define WB_OUT_SIZE                           0x074
> >>>> +#define WB_ALPHA_X_VALUE                      0x078
> >>>> +#define WB_DANGER_LUT                         0x084
> >>>> +#define WB_SAFE_LUT                           0x088
> >>>> +#define WB_QOS_CTRL                           0x090
> >>>> +#define WB_CREQ_LUT_0                         0x098
> >>>> +#define WB_CREQ_LUT_1                         0x09C
> >>>> +#define WB_UBWC_STATIC_CTRL                   0x144
> >>>> +#define WB_MUX                                0x150
> >>>> +#define WB_CROP_CTRL                          0x154
> >>>> +#define WB_CROP_OFFSET                        0x158
> >>>> +#define WB_CSC_BASE                           0x260
> >>>> +#define WB_DST_ADDR_SW_STATUS                 0x2B0
> >>>> +#define WB_CDP_CNTL                           0x2B4
> >>>> +#define WB_OUT_IMAGE_SIZE                     0x2C0
> >>>> +#define WB_OUT_XY                             0x2C4
> >>>> +
> >>>> +/* WB_QOS_CTRL */
> >>>> +#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)
> >>>> +
> >>>> +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
> >>>> +        const struct dpu_mdss_cfg *m, void __iomem *addr,
> >>>> +        struct dpu_hw_blk_reg_map *b)
> >>>> +{
> >>>> +    int i;
> >>>> +
> >>>> +    for (i = 0; i < m->wb_count; i++) {
> >>>> +        if (wb == m->wb[i].id) {
> >>>> +            b->base_off = addr;
> >>>> +            b->blk_off = m->wb[i].base;
> >>>> +            b->length = m->wb[i].len;
> >>>> +            b->hwversion = m->hwversion;
> >>>> +            return &m->wb[i];
> >>>> +        }
> >>>> +    }
> >>>> +    return ERR_PTR(-EINVAL);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
> >>>> +        struct dpu_hw_wb_cfg *data)
> >>>> +{
> >>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
> >>>> +    DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
> >>>> +    DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
> >>>> +    DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
> >>>> +        struct dpu_hw_wb_cfg *data)
> >>>> +{
> >>>
> >>> This function shares significant logic with dpu_hw_sspp_setup_format().
> >>>
> >>> We should consider splitting the common code to the helper at some point
> >>> (later).
> >>
> >> Agreed, I do see some similarities. Will take this up in another change.
> >
> > As I wrote, this can be a separate update.
> >
> >>
> >>>
> >>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>>> +    const struct dpu_format *fmt = data->dest.format;
> >>>> +    u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
> >>>> +    u32 write_config = 0;
> >>>> +    u32 opmode = 0;
> >>>> +    u32 dst_addr_sw = 0;
> >>>> +
> >>>> +    chroma_samp = fmt->chroma_sample;
> >>>> +
> >>>> +    dst_format = (chroma_samp << 23) |
> >>>> +        (fmt->fetch_planes << 19) |
> >>>> +        (fmt->bits[C3_ALPHA] << 6) |
> >>>> +        (fmt->bits[C2_R_Cr] << 4) |
> >>>> +        (fmt->bits[C1_B_Cb] << 2) |
> >>>> +        (fmt->bits[C0_G_Y] << 0);
> >>>> +
> >>>> +    if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
> >>>> +        dst_format |= BIT(8); /* DSTC3_EN */
> >>>> +        if (!fmt->alpha_enable ||
> >>>> +            !(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
> >>>> +            dst_format |= BIT(14); /* DST_ALPHA_X */
> >>>> +    }
> >>>> +
> >>>> +    pattern = (fmt->element[3] << 24) |
> >>>> +        (fmt->element[2] << 16) |
> >>>> +        (fmt->element[1] << 8)  |
> >>>> +        (fmt->element[0] << 0);
> >>>> +
> >>>> +    dst_format |= (fmt->unpack_align_msb << 18) |
> >>>> +        (fmt->unpack_tight << 17) |
> >>>> +        ((fmt->unpack_count - 1) << 12) |
> >>>> +        ((fmt->bpp - 1) << 9);
> >>>> +
> >>>> +    ystride0 = data->dest.plane_pitch[0] |
> >>>> +        (data->dest.plane_pitch[1] << 16);
> >>>> +    ystride1 = data->dest.plane_pitch[2] |
> >>>> +    (data->dest.plane_pitch[3] << 16);
> >>>> +
> >>>> +    if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
> >>>> +        outsize = (drm_rect_height(&data->roi) << 16) |
> >>>> drm_rect_width(&data->roi);
> >>>> +    else
> >>>> +        outsize = (data->dest.height << 16) | data->dest.width;
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
> >>>> +    DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
> >>>> +    DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
> >>>> +    DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
> >>>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
> >>>> +    DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
> >>>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
> >>>> +    DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
> >>>> +    DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg
> >>>> *wb)
> >>>> +{
> >>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>>> +    u32 image_size, out_size, out_xy;
> >>>> +
> >>>> +    image_size = (wb->dest.height << 16) | wb->dest.width;
> >>>> +    out_xy = 0;
> >>>> +    out_size = (drm_rect_height(&wb->roi) << 16) |
> >>>> drm_rect_width(&wb->roi);
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
> >>>> +    DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
> >>>> +    DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
> >>>> +        struct dpu_hw_wb_qos_cfg *cfg)
> >>> I like the single call approach. Maybe we should adopt it for the SSPP
> >>> QoS LUT too.
> >>
> >> Perhaps yes.
> >>
> >> I had an overall question on this. all the dpu_hw_*** APIs accept their
> >> own unique ctx (which is the *** hardware they are programming). What is
> >> the approach you are suggesting to unify these?
> >>
> >> For helper calls I understood and already have addressed it in this series.
> >>
> >> But looking ahead for dpu_hw_*** calls, I am still unclear on the
> >> unification plan.
> >>
> >> Again, this is for a follow-up change but I am just trying to understand it.
> >
> > I liked that this function programs all QoS LUT params. (compared to
> > dpu_hw_sspp_setup_qos_ctrl() + dpu_hw_sspp_setup_creq_lut()
> >
> >>
> >>>
> >>>> +{
> >>>> +    struct dpu_hw_blk_reg_map *c = &ctx->hw;
> >>>> +    u32 qos_ctrl = 0;
> >>>> +
> >>>> +    if (!ctx || !cfg)
> >>>> +        return;
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
> >>>> +    DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);
> >>>> +
> >>>> +    if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
> >>>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
> >>>> +        DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
> >>>> +    }
> >>>
> >>> Is there a plain WB_CREQ_LUT for the non-8LVL case?
> >
> > I was comparing the SSPP with WB programming. So if possible could you
> > please check?
>
> No, there is no plain WB_CREQ_LUT. I checked the register set.
>
> So this programming is correct. I confirmed it.

Thanks for the confirmation!

> >>>> +
> >>>> +    if (cfg->danger_safe_en)
> >>>> +        qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
> >>>> +        struct dpu_hw_wb_cdp_cfg *cfg)
> >>>
> >>> Can we use dpu_hw_pipe_cdp_cfg here? Maybe after renaming it to more
> >>> generic dpu_hw_cdp_cfg.
> >>
> >>
> >> ack, I can absorb this in this series itself and re-spin this.
> >> It will save us one more new structure.
> >
> > Good!
> >
> >>>> +{
> >>>> +    struct dpu_hw_blk_reg_map *c;
> >>>> +    u32 cdp_cntl = 0;
> >>>> +
> >>>> +    if (!ctx || !cfg)
> >>>> +        return;
> >>>> +
> >>>> +    c = &ctx->hw;
> >>>> +
> >>>> +    if (cfg->enable)
> >>>> +        cdp_cntl |= BIT(0);
> >>>> +    if (cfg->ubwc_meta_enable)
> >>>> +        cdp_cntl |= BIT(1);
> >>>> +    if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
> >>>> +        cdp_cntl |= BIT(3);
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
> >>>> +}
> >>>> +
> >>>> +static void dpu_hw_wb_bind_pingpong_blk(
> >>>> +        struct dpu_hw_wb *ctx,
> >>>> +        bool enable, const enum dpu_pingpong pp)
> >>>> +{
> >>>> +    struct dpu_hw_blk_reg_map *c;
> >>>> +    int mux_cfg;
> >>>> +
> >>>> +    if (!ctx)
> >>>> +        return;
> >>>> +
> >>>> +    c = &ctx->hw;
> >>>> +
> >>>> +    mux_cfg = DPU_REG_READ(c, WB_MUX);
> >>>> +    mux_cfg &= ~0xf;
> >>>> +
> >>>> +    if (enable)
> >>>> +        mux_cfg |= (pp - PINGPONG_0) & 0x7;
> >>>> +    else
> >>>> +        mux_cfg |= 0xf;
> >>>> +
> >>>> +    DPU_REG_WRITE(c, WB_MUX, mux_cfg);
> >>>> +}
> >>>> +
> >>>> +static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
> >>>> +        unsigned long features)
> >>>> +{
> >>>> +    ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
> >>>> +    ops->setup_outformat = dpu_hw_wb_setup_format;
> >>>> +
> >>>> +    if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
> >>>> +        ops->setup_roi = dpu_hw_wb_roi;
> >>>> +
> >>>> +    if (test_bit(DPU_WB_QOS, &features))
> >>>> +        ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
> >>>> +
> >>>> +    if (test_bit(DPU_WB_CDP, &features))
> >>>> +        ops->setup_cdp = dpu_hw_wb_setup_cdp;
> >>>> +
> >>>> +    if (test_bit(DPU_WB_INPUT_CTRL, &features))
> >>>> +        ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
> >>>> +}
> >>>> +
> >>>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> >>>> +        void __iomem *addr, const struct dpu_mdss_cfg *m)
> >>>> +{
> >>>> +    struct dpu_hw_wb *c;
> >>>> +    const struct dpu_wb_cfg *cfg;
> >>>> +
> >>>> +    if (!addr || !m)
> >>>> +        return ERR_PTR(-EINVAL);
> >>>> +
> >>>> +    c = kzalloc(sizeof(*c), GFP_KERNEL);
> >>>> +    if (!c)
> >>>> +        return ERR_PTR(-ENOMEM);
> >>>> +
> >>>> +    cfg = _wb_offset(idx, m, addr, &c->hw);
> >>>> +    if (IS_ERR(cfg)) {
> >>>> +        WARN(1, "Unable to find wb idx=%d\n", idx);
> >>>> +        kfree(c);
> >>>> +        return ERR_PTR(-EINVAL);
> >>>> +    }
> >>>> +
> >>>> +    /* Assign ops */
> >>>> +    c->mdp = &m->mdp[0];
> >>>> +    c->idx = idx;
> >>>> +    c->caps = cfg;
> >>>> +    _setup_wb_ops(&c->ops, c->caps->features);
> >>>> +
> >>>> +    return c;
> >>>> +}
> >>>> +
> >>>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
> >>>> +{
> >>>> +    kfree(hw_wb);
> >>>> +}
> >>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >>>> new file mode 100644
> >>>> index 0000000..80def96
> >>>> --- /dev/null
> >>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h
> >>>> @@ -0,0 +1,131 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0-only */
> >>>> +/*
> >>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >>>> reserved
> >>>> + */
> >>>> +
> >>>> +#ifndef _DPU_HW_WB_H
> >>>> +#define _DPU_HW_WB_H
> >>>> +
> >>>> +#include "dpu_hw_catalog.h"
> >>>> +#include "dpu_hw_mdss.h"
> >>>> +#include "dpu_hw_top.h"
> >>>> +#include "dpu_hw_util.h"
> >>>> +#include "dpu_hw_pingpong.h"
> >>>> +
> >>>> +struct dpu_hw_wb;
> >>>> +
> >>>> +struct dpu_hw_wb_cfg {
> >>>> +    struct dpu_hw_fmt_layout dest;
> >>>> +    enum dpu_intf_mode intf_mode;
> >>>> +    struct drm_rect roi;
> >>>> +    struct drm_rect crop;
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * enum CDP preload ahead address size
> >>>> + */
> >>>> +enum {
> >>>> +    DPU_WB_CDP_PRELOAD_AHEAD_32,
> >>>> +    DPU_WB_CDP_PRELOAD_AHEAD_64
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * struct dpu_hw_wb_cdp_cfg : CDP configuration
> >>>> + * @enable: true to enable CDP
> >>>> + * @ubwc_meta_enable: true to enable ubwc metadata preload
> >>>> + * @tile_amortize_enable: true to enable amortization control for
> >>>> tile format
> >>>> + * @preload_ahead: number of request to preload ahead
> >>>> + * SDE_WB_CDP_PRELOAD_AHEAD_32,
> >>>> + * SDE_WB_CDP_PRELOAD_AHEAD_64
> >>>> + */
> >>>> +struct dpu_hw_wb_cdp_cfg {
> >>>> +    bool enable;
> >>>> +    bool ubwc_meta_enable;
> >>>> +    bool tile_amortize_enable;
> >>>> +    u32 preload_ahead;
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * struct dpu_hw_wb_qos_cfg : Writeback pipe QoS configuration
> >>>> + * @danger_lut: LUT for generate danger level based on fill level
> >>>> + * @safe_lut: LUT for generate safe level based on fill level
> >>>> + * @creq_lut: LUT for generate creq level based on fill level
> >>>> + * @danger_safe_en: enable danger safe generation
> >>>> + */
> >>>> +struct dpu_hw_wb_qos_cfg {
> >>>> +    u32 danger_lut;
> >>>> +    u32 safe_lut;
> >>>> +    u64 creq_lut;
> >>>> +    bool danger_safe_en;
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + *
> >>>> + * struct dpu_hw_wb_ops : Interface to the wb hw driver functions
> >>>> + *  Assumption is these functions will be called after clocks are
> >>>> enabled
> >>>> + *  @setup_outaddress: setup output address from the writeback job
> >>>> + *  @setup_outformat: setup output format of writeback block from
> >>>> writeback job
> >>>> + *  @setup_qos_lut:   setup qos LUT for writeback block based on input
> >>>> + *  @setup_cdp:       setup chroma down prefetch block for writeback
> >>>> block
> >>>> + *  @bind_pingpong_blk: enable/disable the connection with ping-pong
> >>>> block
> >>>> + */
> >>>> +struct dpu_hw_wb_ops {
> >>>> +    void (*setup_outaddress)(struct dpu_hw_wb *ctx,
> >>>> +            struct dpu_hw_wb_cfg *wb);
> >>>> +
> >>>> +    void (*setup_outformat)(struct dpu_hw_wb *ctx,
> >>>> +            struct dpu_hw_wb_cfg *wb);
> >>>> +
> >>>> +    void (*setup_roi)(struct dpu_hw_wb *ctx,
> >>>> +            struct dpu_hw_wb_cfg *wb);
> >>>> +
> >>>> +    void (*setup_qos_lut)(struct dpu_hw_wb *ctx,
> >>>> +            struct dpu_hw_wb_qos_cfg *cfg);
> >>>> +
> >>>> +    void (*setup_cdp)(struct dpu_hw_wb *ctx,
> >>>> +            struct dpu_hw_wb_cdp_cfg *cfg);
> >>>> +
> >>>> +    void (*bind_pingpong_blk)(struct dpu_hw_wb *ctx,
> >>>> +            bool enable, const enum dpu_pingpong pp);
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * struct dpu_hw_wb : WB driver object
> >>>> + * @hw: block hardware details
> >>>> + * @mdp: pointer to associated mdp portion of the catalog
> >>>> + * @idx: hardware index number within type
> >>>> + * @wb_hw_caps: hardware capabilities
> >>>> + * @ops: function pointers
> >>>> + * @hw_mdp: MDP top level hardware block
> >>>> + */
> >>>> +struct dpu_hw_wb {
> >>>> +    struct dpu_hw_blk_reg_map hw;
> >>>> +    const struct dpu_mdp_cfg *mdp;
> >>>> +
> >>>> +    /* wb path */
> >>>> +    int idx;
> >>>> +    const struct dpu_wb_cfg *caps;
> >>>> +
> >>>> +    /* ops */
> >>>> +    struct dpu_hw_wb_ops ops;
> >>>> +
> >>>> +    struct dpu_hw_mdp *hw_mdp;
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * dpu_hw_wb_init(): Initializes and return writeback hw driver object.
> >>>> + * @idx:  wb_path index for which driver object is required
> >>>> + * @addr: mapped register io address of MDP
> >>>> + * @m :   pointer to mdss catalog data
> >>>> + */
> >>>> +struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
> >>>> +        void __iomem *addr,
> >>>> +        const struct dpu_mdss_cfg *m);
> >>>> +
> >>>> +/**
> >>>> + * dpu_hw_wb_destroy(): Destroy writeback hw driver object.
> >>>> + * @hw_wb:  Pointer to writeback hw driver object
> >>>> + */
> >>>> +void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb);
> >>>> +
> >>>> +#endif /*_DPU_HW_WB_H */
> >>>
> >>>
> >
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer
  2022-04-20  7:52   ` Dmitry Baryshkov
@ 2022-04-20 19:10     ` Abhinav Kumar
  2022-04-20 19:26       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 19:10 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:52 AM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:46, Abhinav Kumar wrote:
>> Introduce the dpu_writeback module which serves as the
>> interface between dpu operations and the drm_writeback.
>>
>> This module manages the connector related operations for
>> dpu writeback.
>>
>> changes in v2:
>>     - start using drm_writeback_connector_init_with_encoder()
>>     - drop unnecessary arguments from dpu_writeback_init()
>>     - rebase on msm-next tip and remove usage of priv->connectors
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/Makefile                  |  1 +
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 68 
>> +++++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 25 ++++++++++
>>   3 files changed, 94 insertions(+)
>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index 0387f22..66395ee 100644
>> --- a/drivers/gpu/drm/msm/Makefile
>> +++ b/drivers/gpu/drm/msm/Makefile
>> @@ -80,6 +80,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>       disp/dpu1/dpu_plane.o \
>>       disp/dpu1/dpu_rm.o \
>>       disp/dpu1/dpu_vbif.o \
>> +    disp/dpu1/dpu_writeback.o
>>   msm-$(CONFIG_DRM_MSM_MDSS) += \
>>       msm_mdss.o \
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>> new file mode 100644
>> index 0000000..526d884
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>> @@ -0,0 +1,68 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + */
>> +
>> +#include "dpu_writeback.h"
>> +
>> +static int dpu_wb_conn_get_modes(struct drm_connector *connector)
>> +{
>> +    struct drm_device *dev = connector->dev;
>> +
>> +    return drm_add_modes_noedid(connector, dev->mode_config.max_width,
>> +            dev->mode_config.max_height);
>> +}
>> +
>> +static const struct drm_connector_funcs dpu_wb_conn_funcs = {
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .fill_modes = drm_helper_probe_single_connector_modes,
>> +    .destroy = drm_connector_cleanup,
>> +    .atomic_duplicate_state = 
>> drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector 
>> *connector,
>> +        struct drm_writeback_job *job)
>> +{
>> +    if (!job->fb)
>> +        return 0;
>> +
>> +    dpu_encoder_prepare_wb_job(connector->encoder, job);
>> +
>> +    return 0;
>> +}
>> +
>> +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector 
>> *connector,
>> +        struct drm_writeback_job *job)
>> +{
>> +    if (!job->fb)
>> +        return;
>> +
>> +    dpu_encoder_cleanup_wb_job(connector->encoder, job);
>> +}
>> +
>> +static const struct drm_connector_helper_funcs 
>> dpu_wb_conn_helper_funcs = {
>> +    .get_modes = dpu_wb_conn_get_modes,
>> +    .prepare_writeback_job = dpu_wb_conn_prepare_job,
>> +    .cleanup_writeback_job = dpu_wb_conn_cleanup_job,
>> +};
>> +
>> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
>> +        const u32 *format_list, u32 num_formats)
>> +{
>> +    struct dpu_wb_connector *dpu_wb_conn;
>> +    int rc = 0;
>> +
>> +    dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), 
>> GFP_KERNEL);
>> +
>> +    drm_connector_helper_add(&dpu_wb_conn->base.base, 
>> &dpu_wb_conn_helper_funcs);
>> +
>> +    /* DPU initializes the encoder and sets it up completely for 
>> writeback
>> +     * cases and hence should use the new API 
>> drm_writeback_connector_init_with_encoder
>> +     * to initialize the writeback connector
>> +     */
>> +    rc = drm_writeback_connector_init_with_encoder(dev, 
>> &dpu_wb_conn->base, enc,
>> +            &dpu_wb_conn_funcs, format_list, num_formats);
>> +
>> +    return rc;
>> +}
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>> new file mode 100644
>> index 0000000..05aff05
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>> @@ -0,0 +1,25 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>> + */
>> +
>> +#ifndef _DPU_WRITEBACK_H
>> +#define _DPU_WRITEBACK_H
>> +
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_file.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_writeback.h>
>> +
>> +#include "msm_drv.h"
>> +#include "dpu_kms.h"
>> +#include "dpu_encoder_phys.h"
>> +
>> +struct dpu_wb_connector {
>> +    struct drm_writeback_connector base;
>> +};
> 
> Do you plan to add more fields to this struct? If not, we can probably 
> drop it and use struct drm_writeback_connector directly.

Glad you asked about it. I was expecting this question because it looks 
like a very "light" struct.

Yes, we do plan to expand this as we will keep adding writeback features 
sequentially now to make it ready for this to be absorbed downstream 
completely.

> 
>> +
>> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
>> +        const u32 *format_list, u32 num_formats);
>> +
>> +#endif /*_DPU_WRITEBACK_H */
> 
> 

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

* Re: [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20 18:17     ` Abhinav Kumar
@ 2022-04-20 19:26       ` Dmitry Baryshkov
  2022-04-20 19:36         ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 19:26 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 21:17, Abhinav Kumar wrote:
> 
> 
> On 4/20/2022 12:49 AM, Dmitry Baryshkov wrote:
>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>> Introduce the dpu_encoder_phys_* for the writeback interface
>>> to handle writeback specific hardware programming.
>>>
>>> changes in v2:
>>>     - rebase on msm-next and fix related dependencies namely
>>>       the irq cleanup
>>>     - move cdp_cfg, aspace out of dpu_encoder_phys_wb
>>>     - leave a comment about wb master
>>>     - start using _dpu_hw_get_qos_lut from dpu_hw_util
>>>     - replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG
>>>
>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>> ---
>>>   drivers/gpu/drm/msm/Makefile                       |   1 +
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
>>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751 
>>> +++++++++++++++++++++
>>>   3 files changed, 782 insertions(+)
>>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>>
>>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>>> index ca779c1..0387f22 100644
>>> --- a/drivers/gpu/drm/msm/Makefile
>>> +++ b/drivers/gpu/drm/msm/Makefile
>>> @@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>>       disp/dpu1/dpu_encoder.o \
>>>       disp/dpu1/dpu_encoder_phys_cmd.o \
>>>       disp/dpu1/dpu_encoder_phys_vid.o \
>>> +    disp/dpu1/dpu_encoder_phys_wb.o \
>>>       disp/dpu1/dpu_formats.o \
>>>       disp/dpu1/dpu_hw_catalog.o \
>>>       disp/dpu1/dpu_hw_ctl.o \
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> index 00951f3..5452f98 100644
>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> @@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
>>>    * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
>>>    * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
>>>    * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
>>> + * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
>>>    */
>>>   enum dpu_intr_idx {
>>>       INTR_IDX_VSYNC,
>>> @@ -157,6 +158,7 @@ enum dpu_intr_idx {
>>>       INTR_IDX_UNDERRUN,
>>>       INTR_IDX_CTL_START,
>>>       INTR_IDX_RDPTR,
>>> +    INTR_IDX_WB_DONE,
>>>       INTR_IDX_MAX,
>>>   };
>>> @@ -224,6 +226,27 @@ static inline int 
>>> dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
>>>   }
>>>   /**
>>> + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to 
>>> handle command
>>> + *    mode specific operations
>>> + * @base:    Baseclass physical encoder structure
>>> + * @wbirq_refcount:     Reference count of writeback interrupt
>>> + * @wb_done_timeout_cnt: number of wb done irq timeout errors
>>> + * @wb_cfg:  writeback block config to store fb related details
>>> + * @wb_conn: backpointer to writeback connector
>>> + * @wb_job: backpointer to current writeback job
>>> + * @dest:   dpu buffer layout for current writeback output buffer
>>> + */
>>> +struct dpu_encoder_phys_wb {
>>> +    struct dpu_encoder_phys base;
>>> +    atomic_t wbirq_refcount;
>>> +    int wb_done_timeout_cnt;
>>> +    struct dpu_hw_wb_cfg wb_cfg;
>>> +    struct drm_writeback_connector *wb_conn;
>>> +    struct drm_writeback_job *wb_job;
>>> +    struct dpu_hw_fmt_layout dest;
>>> +};
>>> +
>>> +/**
>>>    * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to 
>>> handle command
>>>    *    mode specific operations
>>>    * @base:    Baseclass physical encoder structure
>>> @@ -291,6 +314,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
>>>           struct dpu_enc_phys_init_params *p);
>>>   /**
>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>>> + * @init:    Pointer to init info structure with initialization params
>>> + */
>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>>> +        struct dpu_enc_phys_init_params *p);
>>> +
>>> +/**
>>>    * dpu_encoder_helper_trigger_start - control start helper function
>>>    *    This helper function may be optionally specified by physical
>>>    *    encoders if they require ctl_start triggering.
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>> new file mode 100644
>>> index 0000000..128317fe
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>> @@ -0,0 +1,751 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>> reserved.
>>> + */
>>> +
>>> +#define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
>>> +
>>> +#include <linux/debugfs.h>
>>> +
>>> +#include "dpu_encoder_phys.h"
>>> +#include "dpu_formats.h"
>>> +#include "dpu_hw_top.h"
>>> +#include "dpu_hw_wb.h"
>>> +#include "dpu_hw_lm.h"
>>> +#include "dpu_hw_blk.h"
>>> +#include "dpu_hw_merge3d.h"
>>> +#include "dpu_hw_interrupts.h"
>>> +#include "dpu_core_irq.h"
>>> +#include "dpu_vbif.h"
>>> +#include "dpu_crtc.h"
>>> +#include "disp/msm_disp_snapshot.h"
>>> +
>>> +#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
>>> +
>>> +#define to_dpu_encoder_phys_wb(x) \
>>> +    container_of(x, struct dpu_encoder_phys_wb, base)
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_is_master - report wb always as master encoder
>>> + */
>>> +static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    /* there is only one physical enc for dpu_writeback */
>>> +    return true;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback 
>>> interface
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_set_ot_limit(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>> +    struct dpu_vbif_set_ot_params ot_params;
>>> +
>>> +    memset(&ot_params, 0, sizeof(ot_params));
>>> +    ot_params.xin_id = hw_wb->caps->xin_id;
>>> +    ot_params.num = hw_wb->idx - WB_0;
>>> +    ot_params.width = phys_enc->cached_mode.hdisplay;
>>> +    ot_params.height = phys_enc->cached_mode.vdisplay;
>>> +    ot_params.is_wfd = true;
>>> +    ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
>>> +    ot_params.vbif_idx = hw_wb->caps->vbif_idx;
>>> +    ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>>> +    ot_params.rd = false;
>>> +
>>> +    dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_set_qos_remap(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb;
>>> +    struct dpu_vbif_set_qos_params qos_params;
>>> +
>>> +    if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
>>> +        DPU_ERROR("invalid arguments\n");
>>> +        return;
>>> +    }
>>> +
>>> +    if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
>>> +        DPU_ERROR("invalid writeback hardware\n");
>>> +        return;
>>> +    }
>>> +
>>> +    hw_wb = phys_enc->hw_wb;
>>> +
>>> +    memset(&qos_params, 0, sizeof(qos_params));
>>> +    qos_params.vbif_idx = hw_wb->caps->vbif_idx;
>>> +    qos_params.xin_id = hw_wb->caps->xin_id;
>>> +    qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>>> +    qos_params.num = hw_wb->idx - WB_0;
>>> +    qos_params.is_rt = false;
>>> +
>>> +    DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
>>> +            qos_params.num,
>>> +            qos_params.vbif_idx,
>>> +            qos_params.xin_id, qos_params.is_rt);
>>> +
>>> +    dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for writeback
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb;
>>> +    struct dpu_hw_wb_qos_cfg qos_cfg;
>>> +    struct dpu_mdss_cfg *catalog;
>>> +    struct dpu_qos_lut_tbl *qos_lut_tb;
>>> +
>>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>>> !phys_enc->dpu_kms->catalog) {
>>> +        DPU_ERROR("invalid parameter(s)\n");
>>> +        return;
>>> +    }
>>> +
>>> +    catalog = phys_enc->dpu_kms->catalog;
>>> +
>>> +    hw_wb = phys_enc->hw_wb;
>>> +
>>> +    memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
>>> +    qos_cfg.danger_safe_en = true;
>>> +    qos_cfg.danger_lut =
>>> +        catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>> +
>>> +    qos_cfg.safe_lut = 
>>> catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>> +
>>> +    qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>> +    qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
>>> +
>>> +    if (hw_wb->ops.setup_qos_lut)
>>> +        hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
>>> + * @phys_enc:    Pointer to physical encoder
>>> + * @fb:        Pointer to output framebuffer
>>> + * @wb_roi:    Pointer to output region of interest
>>> + */
>>> +static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys 
>>> *phys_enc,
>>> +        struct drm_framebuffer *fb)
>>> +{
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +    struct dpu_hw_wb *hw_wb;
>>> +    struct dpu_hw_wb_cfg *wb_cfg;
>>> +    struct dpu_hw_wb_cdp_cfg cdp_cfg;
>>> +
>>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>>> !phys_enc->dpu_kms->catalog) {
>>> +        DPU_ERROR("invalid encoder\n");
>>> +        return;
>>> +    }
>>> +
>>> +    hw_wb = phys_enc->hw_wb;
>>> +    wb_cfg = &wb_enc->wb_cfg;
>>> +
>>> +    wb_cfg->intf_mode = phys_enc->intf_mode;
>>> +    wb_cfg->roi.x1 = 0;
>>> +    wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
>>> +    wb_cfg->roi.y1 = 0;
>>> +    wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
>>> +
>>> +    if (hw_wb->ops.setup_roi)
>>> +        hw_wb->ops.setup_roi(hw_wb, wb_cfg);
>>> +
>>> +    if (hw_wb->ops.setup_outformat)
>>> +        hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
>>> +
>>> +    if (hw_wb->ops.setup_cdp) {
>>> +        memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
>>> +
>>> +        cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
>>> +                [DPU_PERF_CDP_USAGE_NRT].wr_enable;
>>> +        cdp_cfg.ubwc_meta_enable =
>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
>>> +        cdp_cfg.tile_amortize_enable =
>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
>>> +                DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
>>> +        cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
>>> +
>>> +        hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
>>> +    }
>>> +
>>> +    if (hw_wb->ops.setup_outaddress)
>>> +        hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
>>> + * @phys_enc:Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb;
>>> +    struct dpu_hw_ctl *ctl;
>>> +
>>> +    if (!phys_enc) {
>>> +        DPU_ERROR("invalid encoder\n");
>>> +        return;
>>> +    }
>>> +
>>> +    hw_wb = phys_enc->hw_wb;
>>> +    ctl = phys_enc->hw_ctl;
>>> +
>>> +    if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
>>> +        (phys_enc->hw_ctl &&
>>> +         phys_enc->hw_ctl->ops.setup_intf_cfg)) {
>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>>> +        struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
>>> +        enum dpu_3d_blend_mode mode_3d;
>>> +
>>> +        mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>>> +
>>> +        intf_cfg.intf = DPU_NONE;
>>> +        intf_cfg.wb = hw_wb->idx;
>>> +
>>> +        if (mode_3d && hw_pp && hw_pp->merge_3d)
>>> +            intf_cfg.merge_3d = hw_pp->merge_3d->idx;
>>> +
>>> +        if (phys_enc->hw_pp->merge_3d && 
>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
>>> + 
>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
>>> +                    mode_3d);
>>> +
>>> +        /* setup which pp blk will connect to this wb */
>>> +        if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, 
>>> true,
>>> +                    phys_enc->hw_pp->idx);
>>> +
>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>>> &intf_cfg);
>>> +    } else if (phys_enc->hw_ctl && 
>>> phys_enc->hw_ctl->ops.setup_intf_cfg) {
>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>>> +
>>> +        intf_cfg.intf = DPU_NONE;
>>> +        intf_cfg.wb = hw_wb->idx;
>>> +        intf_cfg.mode_3d =
>>> +            dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>>> &intf_cfg);
>>> +    }
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic 
>>> states
>>> + * @phys_enc:    Pointer to physical encoder
>>> + * @crtc_state:    Pointer to CRTC atomic state
>>> + * @conn_state:    Pointer to connector atomic state
>>> + */
>>> +static int dpu_encoder_phys_wb_atomic_check(
>>> +        struct dpu_encoder_phys *phys_enc,
>>> +        struct drm_crtc_state *crtc_state,
>>> +        struct drm_connector_state *conn_state)
>>> +{
>>> +    struct drm_framebuffer *fb;
>>> +    const struct drm_display_mode *mode;
>>> +
>>> +    DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
>>> +            phys_enc->intf_idx, mode->name, mode->hdisplay, 
>>> mode->vdisplay);
>>> +
>>> +    if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
>>> +        return 0;
>>> +
>>> +    fb = conn_state->writeback_job->fb;
>>> +    mode = &crtc_state->mode;
>>> +
>>> +    if (!conn_state || !conn_state->connector) {
>>> +        DPU_ERROR("invalid connector state\n");
>>> +        return -EINVAL;
>>> +    } else if (conn_state->connector->status !=
>>> +            connector_status_connected) {
>>> +        DPU_ERROR("connector not connected %d\n",
>>> +                conn_state->connector->status);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
>>> +            fb->width, fb->height);
>>> +
>>> +    if (fb->width != mode->hdisplay) {
>>> +        DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
>>> +                mode->hdisplay);
>>> +        return -EINVAL;
>>> +    } else if (fb->height != mode->vdisplay) {
>>> +        DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
>>> +                  mode->vdisplay);
>>> +        return -EINVAL;
>>> +    } else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
>>> +        DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
>>> +                  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
>>> +        return -EINVAL;
>>> +    }
>>
>> Being not an expert in the DRM WB API, I have a question: are those 
>> hardware limitations or the DRM requirement that we can not write to 
>> the region inside the attached FB?
> 
> Both. The first two checks are indeed DRM requirement and infact igt 
> writeback tests this to ensure that the driver rejects any invalid FBs 
> which are not equal to the mode that was set.
> 
> https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tests/kms_writeback.c#L409 

The test is writing to the fb which is smaller than the used video mode 
(which is an obvious mistake). Is it correst to write back to the FB 
that is bigger than the selected resolution?

> The last check is coming from our hardware >
>>
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +
>>> +/**
>>> + * _dpu_encoder_phys_wb_update_flush - flush hardware update
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void _dpu_encoder_phys_wb_update_flush(struct 
>>> dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb;
>>> +    struct dpu_hw_ctl *hw_ctl;
>>> +    struct dpu_hw_pingpong *hw_pp;
>>> +    u32 pending_flush = 0;
>>> +
>>> +    if (!phys_enc)
>>> +        return;
>>> +
>>> +    hw_wb = phys_enc->hw_wb;
>>> +    hw_pp = phys_enc->hw_pp;
>>> +    hw_ctl = phys_enc->hw_ctl;
>>> +
>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>> +
>>> +    if (!hw_ctl) {
>>> +        DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
>>> +        return;
>>> +    }
>>> +
>>> +    if (hw_ctl->ops.update_pending_flush_wb)
>>> +        hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
>>> +
>>> +    if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && 
>>> hw_pp->merge_3d)
>>> +        hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
>>> +                hw_pp->merge_3d->idx);
>>> +
>>> +    if (hw_ctl->ops.get_pending_flush)
>>> +        pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
>>> +
>>> +    DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
>>> +            hw_ctl->idx - CTL_0, pending_flush,
>>> +            hw_wb->idx - WB_0);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_setup - setup writeback encoder
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_setup(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>> +    struct drm_display_mode mode = phys_enc->cached_mode;
>>> +    struct drm_framebuffer *fb = NULL;
>>> +
>>> +    DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
>>> +            hw_wb->idx - WB_0, mode.name,
>>> +            mode.hdisplay, mode.vdisplay);
>>> +
>>> +    dpu_encoder_phys_wb_set_ot_limit(phys_enc);
>>> +
>>> +    dpu_encoder_phys_wb_set_qos_remap(phys_enc);
>>> +
>>> +    dpu_encoder_phys_wb_set_qos(phys_enc);
>>> +
>>> +    dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
>>> +
>>> +    dpu_encoder_phys_wb_setup_cdp(phys_enc);
>>> +
>>> +}
>>> +
>>> +static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
>>> +{
>>> +    struct dpu_encoder_phys *phys_enc = arg;
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +
>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>> +    unsigned long lock_flags;
>>> +    u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
>>> +
>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>> +
>>> +    if (phys_enc->parent_ops->handle_frame_done)
>>> +        phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
>>> +                phys_enc, event);
>>> +
>>> +    if (phys_enc->parent_ops->handle_vblank_virt)
>>> +        phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
>>> +                phys_enc);
>>> +
>>> +    spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>>> +    spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
>>> +
>>> +    if (wb_enc->wb_conn)
>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>>> +
>>> +    /* Signal any waiting atomic commit thread */
>>> +    wake_up_all(&phys_enc->pending_kickoff_wq);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
>>> + * @arg:    Pointer to writeback encoder
>>> + * @irq_idx:    interrupt index
>>> + */
>>> +static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
>>> +{
>>> +    _dpu_encoder_phys_wb_frame_done_helper(arg);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
>>> + * @phys:    Pointer to physical encoder
>>> + * @enable:    indicates enable or disable interrupts
>>> + */
>>> +static void dpu_encoder_phys_wb_irq_ctrl(
>>> +        struct dpu_encoder_phys *phys, bool enable)
>>> +{
>>> +
>>> +    struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
>>> +    int ret = 0;
>>> +    int refcount;
>>> +
>>> +    refcount = atomic_read(&wb_enc->wbirq_refcount);
>>> +
>>> +    if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
>>> +        dpu_core_irq_register_callback(phys->dpu_kms,
>>> +                phys->irq[INTR_IDX_WB_DONE], 
>>> dpu_encoder_phys_wb_done_irq, phys);
>>> +        if (ret)
>>> +            atomic_dec_return(&wb_enc->wbirq_refcount);
>>> +    } else if (!enable &&
>>> +            atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
>>> +        dpu_core_irq_unregister_callback(phys->dpu_kms, 
>>> phys->irq[INTR_IDX_WB_DONE]);
>>> +        if (ret)
>>> +            atomic_inc_return(&wb_enc->wbirq_refcount);
>>> +    }
>>> +}
>>> +
>>> +static void dpu_encoder_phys_wb_atomic_mode_set(
>>> +        struct dpu_encoder_phys *phys_enc,
>>> +        struct drm_crtc_state *crtc_state,
>>> +        struct drm_connector_state *conn_state)
>>> +{
>>> +
>>> +    phys_enc->irq[INTR_IDX_WB_DONE] = 
>>> phys_enc->hw_wb->caps->intr_wb_done;
>>> +}
>>> +
>>> +static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +    u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
>>> +
>>> +    wb_enc->wb_done_timeout_cnt++;
>>> +
>>> +    if (wb_enc->wb_done_timeout_cnt == 1)
>>> +        msm_disp_snapshot_state(phys_enc->parent->dev);
>>> +
>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>>> +
>>> +    /* request a ctl reset before the next kickoff */
>>> +    phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
>>> +
>>> +    if (wb_enc->wb_conn)
>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>>> +
>>> +    if (phys_enc->parent_ops->handle_frame_done)
>>> +        phys_enc->parent_ops->handle_frame_done(
>>> +                phys_enc->parent, phys_enc, frame_event);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is 
>>> committed
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static int dpu_encoder_phys_wb_wait_for_commit_done(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    unsigned long ret;
>>> +    struct dpu_encoder_wait_info wait_info;
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +
>>> +    wait_info.wq = &phys_enc->pending_kickoff_wq;
>>> +    wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
>>> +    wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
>>> +
>>> +    ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
>>> +            dpu_encoder_phys_wb_done_irq, &wait_info);
>>> +    if (ret == -ETIMEDOUT)
>>> +        _dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
>>> +    else if (!ret)
>>> +        wb_enc->wb_done_timeout_cnt = 0;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
>>> + * @phys_enc:    Pointer to physical encoder
>>> + * Returns:    Zero on success
>>> + */
>>> +static void dpu_encoder_phys_wb_prepare_for_kickoff(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +    struct drm_connector *drm_conn;
>>> +    struct drm_connector_state *state;
>>> +
>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>> +
>>> +    if (!wb_enc->wb_conn || !wb_enc->wb_job) {
>>> +        DPU_ERROR("invalid wb_conn or wb_job\n");
>>> +        return;
>>> +    }
>>> +
>>> +    drm_conn = &wb_enc->wb_conn->base;
>>> +    state = drm_conn->state;
>>> +
>>> +    if (wb_enc->wb_conn && wb_enc->wb_job)
>>> +        drm_writeback_queue_job(wb_enc->wb_conn, state);
>>> +
>>> +    dpu_encoder_phys_wb_setup(phys_enc);
>>> +
>>> +    _dpu_encoder_phys_wb_update_flush(phys_enc);
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static bool dpu_encoder_phys_wb_needs_single_flush(struct 
>>> dpu_encoder_phys *phys_enc)
>>> +{
>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>> +    return false;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_handle_post_kickoff(
>>> +        struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>> +
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_enable - enable writeback encoder
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>> +    phys_enc->enable_state = DPU_ENC_ENABLED;
>>> +}
>>> +/**
>>> + * dpu_encoder_phys_wb_disable - disable writeback encoder
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>> +    struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
>>> +
>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>> +
>>> +    if (phys_enc->enable_state == DPU_ENC_DISABLED) {
>>> +        DPU_ERROR("encoder is already disabled\n");
>>> +        return;
>>> +    }
>>> +
>>> +    /* reset h/w before final flush */
>>> +    if (phys_enc->hw_ctl->ops.clear_pending_flush)
>>> +        phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
>>> +
>>> +    /*
>>> +     * New CTL reset sequence from 5.0 MDP onwards.
>>> +     * If has_3d_merge_reset is not set, legacy reset
>>> +     * sequence is executed.
>>> +     *
>>> +     * Legacy reset sequence has not been implemented yet.
>>> +     * Any target earlier than SM8150 will need it and when
>>> +     * WB support is added to those targets will need to add
>>> +     * the legacy teardown sequence as well.
>>> +     */
>>> +    if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
>>> +        dpu_encoder_helper_phys_cleanup(phys_enc);
>>> +
>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_destroy - destroy writeback encoder
>>> + * @phys_enc:    Pointer to physical encoder
>>> + */
>>> +static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
>>> +
>>> +    if (!phys_enc)
>>> +        return;
>>> +
>>> +    kfree(phys_enc);
>>> +}
>>> +
>>> +static void dpu_encoder_phys_wb_prepare_wb_job(struct 
>>> dpu_encoder_phys *phys_enc,
>>> +        struct drm_writeback_job *job)
>>> +{
>>> +    const struct msm_format *format;
>>> +    struct msm_gem_address_space *aspace;
>>> +    struct dpu_hw_wb_cfg *wb_cfg;
>>> +    int ret;
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +
>>> +    if (!job->fb)
>>> +        return;
>>> +
>>> +    wb_enc->wb_job = job;
>>> +    wb_enc->wb_conn = job->connector;
>>> +    aspace = phys_enc->dpu_kms->base.aspace;
>>> +
>>> +    wb_cfg = &wb_enc->wb_cfg;
>>> +
>>> +    memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
>>> +
>>> +    ret = msm_framebuffer_prepare(job->fb, aspace, false);
>>> +    if (ret) {
>>> +        DPU_ERROR("prep fb failed, %d\n", ret);
>>> +        return;
>>> +    }
>>> +
>>> +    format = msm_framebuffer_format(job->fb);
>>> +
>>> +    wb_cfg->dest.format = dpu_get_dpu_format_ext(
>>> +            format->pixel_format, job->fb->modifier);
>>> +    if (!wb_cfg->dest.format) {
>>> +        /* this error should be detected during atomic_check */
>>> +        DPU_ERROR("failed to get format %x\n", format->pixel_format);
>>> +        return;
>>> +    }
>>> +
>>> +    ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
>>> +    if (ret) {
>>> +        DPU_DEBUG("failed to populate layout %d\n", ret);
>>> +        return;
>>> +    }
>>> +
>>> +    wb_cfg->dest.width = job->fb->width;
>>> +    wb_cfg->dest.height = job->fb->height;
>>> +    wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
>>> +
>>> +    if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
>>> +            (wb_cfg->dest.format->element[0] == C1_B_Cb))
>>> +        swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
>>> +
>>> +    DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>>> +            wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
>>> +            wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
>>> +
>>> +    DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>>> +            wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
>>> +            wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
>>> +}
>>> +
>>> +static void dpu_encoder_phys_wb_cleanup_wb_job(struct 
>>> dpu_encoder_phys *phys_enc,
>>> +        struct drm_writeback_job *job)
>>> +{
>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>> to_dpu_encoder_phys_wb(phys_enc);
>>> +    struct msm_gem_address_space *aspace;
>>> +
>>> +    if (!job->fb)
>>> +        return;
>>> +
>>> +    aspace = phys_enc->dpu_kms->base.aspace;
>>> +
>>> +    msm_framebuffer_cleanup(job->fb, aspace, false);
>>> +    wb_enc->wb_job = NULL;
>>> +    wb_enc->wb_conn = NULL;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_init_ops - initialize writeback operations
>>> + * @ops:    Pointer to encoder operation table
>>> + */
>>> +static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops 
>>> *ops)
>>> +{
>>> +    ops->is_master = dpu_encoder_phys_wb_is_master;
>>> +    ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
>>> +    ops->enable = dpu_encoder_phys_wb_enable;
>>> +    ops->disable = dpu_encoder_phys_wb_disable;
>>> +    ops->destroy = dpu_encoder_phys_wb_destroy;
>>> +    ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
>>> +    ops->wait_for_commit_done = 
>>> dpu_encoder_phys_wb_wait_for_commit_done;
>>> +    ops->prepare_for_kickoff = dpu_encoder_phys_wb_prepare_for_kickoff;
>>> +    ops->handle_post_kickoff = dpu_encoder_phys_wb_handle_post_kickoff;
>>> +    ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
>>> +    ops->trigger_start = dpu_encoder_helper_trigger_start;
>>> +    ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
>>> +    ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
>>> +    ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
>>> +}
>>> +
>>> +/**
>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>>> + * @init:    Pointer to init info structure with initialization params
>>> + */
>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>>> +        struct dpu_enc_phys_init_params *p)
>>> +{
>>> +    struct dpu_encoder_phys *phys_enc = NULL;
>>> +    struct dpu_encoder_phys_wb *wb_enc = NULL;
>>> +    int ret = 0;
>>> +    int i;
>>> +
>>> +    DPU_DEBUG("\n");
>>> +
>>> +    if (!p || !p->parent) {
>>> +        DPU_ERROR("invalid params\n");
>>> +        ret = -EINVAL;
>>> +        goto fail_alloc;
>>> +    }
>>> +
>>> +    wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
>>> +    if (!wb_enc) {
>>> +        DPU_ERROR("failed to allocate wb phys_enc enc\n");
>>> +        ret = -ENOMEM;
>>> +        goto fail_alloc;
>>> +    }
>>> +
>>> +    phys_enc = &wb_enc->base;
>>> +    phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
>>> +    phys_enc->intf_idx = p->intf_idx;
>>> +
>>> +    dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
>>> +    phys_enc->parent = p->parent;
>>> +    phys_enc->parent_ops = p->parent_ops;
>>> +    phys_enc->dpu_kms = p->dpu_kms;
>>> +    phys_enc->split_role = p->split_role;
>>> +    phys_enc->intf_mode = INTF_MODE_WB_LINE;
>>> +    phys_enc->intf_idx = p->intf_idx;
>>> +    phys_enc->enc_spinlock = p->enc_spinlock;
>>> +
>>> +    atomic_set(&wb_enc->wbirq_refcount, 0);
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
>>> +        phys_enc->irq[i] = -EINVAL;
>>> +
>>> +    atomic_set(&phys_enc->pending_kickoff_cnt, 0);
>>> +    atomic_set(&phys_enc->vblank_refcount, 0);
>>> +    wb_enc->wb_done_timeout_cnt = 0;
>>> +
>>> +    init_waitqueue_head(&phys_enc->pending_kickoff_wq);
>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>>> +
>>> +    DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
>>> +            phys_enc->intf_idx);
>>> +
>>> +    return phys_enc;
>>> +
>>> +fail_alloc:
>>> +    return ERR_PTR(ret);
>>> +}
>>
>>


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer
  2022-04-20 19:10     ` Abhinav Kumar
@ 2022-04-20 19:26       ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 19:26 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 22:10, Abhinav Kumar wrote:
> 
> 
> On 4/20/2022 12:52 AM, Dmitry Baryshkov wrote:
>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>> Introduce the dpu_writeback module which serves as the
>>> interface between dpu operations and the drm_writeback.
>>>
>>> This module manages the connector related operations for
>>> dpu writeback.
>>>
>>> changes in v2:
>>>     - start using drm_writeback_connector_init_with_encoder()
>>>     - drop unnecessary arguments from dpu_writeback_init()
>>>     - rebase on msm-next tip and remove usage of priv->connectors
>>>
>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>> ---
>>>   drivers/gpu/drm/msm/Makefile                  |  1 +
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 68 
>>> +++++++++++++++++++++++++++
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 25 ++++++++++
>>>   3 files changed, 94 insertions(+)
>>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>>>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>>>
>>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>>> index 0387f22..66395ee 100644
>>> --- a/drivers/gpu/drm/msm/Makefile
>>> +++ b/drivers/gpu/drm/msm/Makefile
>>> @@ -80,6 +80,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>>       disp/dpu1/dpu_plane.o \
>>>       disp/dpu1/dpu_rm.o \
>>>       disp/dpu1/dpu_vbif.o \
>>> +    disp/dpu1/dpu_writeback.o
>>>   msm-$(CONFIG_DRM_MSM_MDSS) += \
>>>       msm_mdss.o \
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>>> new file mode 100644
>>> index 0000000..526d884
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>>> @@ -0,0 +1,68 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>> reserved.
>>> + */
>>> +
>>> +#include "dpu_writeback.h"
>>> +
>>> +static int dpu_wb_conn_get_modes(struct drm_connector *connector)
>>> +{
>>> +    struct drm_device *dev = connector->dev;
>>> +
>>> +    return drm_add_modes_noedid(connector, dev->mode_config.max_width,
>>> +            dev->mode_config.max_height);
>>> +}
>>> +
>>> +static const struct drm_connector_funcs dpu_wb_conn_funcs = {
>>> +    .reset = drm_atomic_helper_connector_reset,
>>> +    .fill_modes = drm_helper_probe_single_connector_modes,
>>> +    .destroy = drm_connector_cleanup,
>>> +    .atomic_duplicate_state = 
>>> drm_atomic_helper_connector_duplicate_state,
>>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>>> +};
>>> +
>>> +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector 
>>> *connector,
>>> +        struct drm_writeback_job *job)
>>> +{
>>> +    if (!job->fb)
>>> +        return 0;
>>> +
>>> +    dpu_encoder_prepare_wb_job(connector->encoder, job);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector 
>>> *connector,
>>> +        struct drm_writeback_job *job)
>>> +{
>>> +    if (!job->fb)
>>> +        return;
>>> +
>>> +    dpu_encoder_cleanup_wb_job(connector->encoder, job);
>>> +}
>>> +
>>> +static const struct drm_connector_helper_funcs 
>>> dpu_wb_conn_helper_funcs = {
>>> +    .get_modes = dpu_wb_conn_get_modes,
>>> +    .prepare_writeback_job = dpu_wb_conn_prepare_job,
>>> +    .cleanup_writeback_job = dpu_wb_conn_cleanup_job,
>>> +};
>>> +
>>> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
>>> +        const u32 *format_list, u32 num_formats)
>>> +{
>>> +    struct dpu_wb_connector *dpu_wb_conn;
>>> +    int rc = 0;
>>> +
>>> +    dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), 
>>> GFP_KERNEL);
>>> +
>>> +    drm_connector_helper_add(&dpu_wb_conn->base.base, 
>>> &dpu_wb_conn_helper_funcs);
>>> +
>>> +    /* DPU initializes the encoder and sets it up completely for 
>>> writeback
>>> +     * cases and hence should use the new API 
>>> drm_writeback_connector_init_with_encoder
>>> +     * to initialize the writeback connector
>>> +     */
>>> +    rc = drm_writeback_connector_init_with_encoder(dev, 
>>> &dpu_wb_conn->base, enc,
>>> +            &dpu_wb_conn_funcs, format_list, num_formats);
>>> +
>>> +    return rc;
>>> +}
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>>> new file mode 100644
>>> index 0000000..05aff05
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
>>> @@ -0,0 +1,25 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>> +/*
>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>> reserved.
>>> + */
>>> +
>>> +#ifndef _DPU_WRITEBACK_H
>>> +#define _DPU_WRITEBACK_H
>>> +
>>> +#include <drm/drm_crtc.h>
>>> +#include <drm/drm_file.h>
>>> +#include <drm/drm_probe_helper.h>
>>> +#include <drm/drm_writeback.h>
>>> +
>>> +#include "msm_drv.h"
>>> +#include "dpu_kms.h"
>>> +#include "dpu_encoder_phys.h"
>>> +
>>> +struct dpu_wb_connector {
>>> +    struct drm_writeback_connector base;
>>> +};
>>
>> Do you plan to add more fields to this struct? If not, we can probably 
>> drop it and use struct drm_writeback_connector directly.
> 
> Glad you asked about it. I was expecting this question because it looks 
> like a very "light" struct.
> 
> Yes, we do plan to expand this as we will keep adding writeback features 
> sequentially now to make it ready for this to be absorbed downstream 
> completely.

Then a separate struct is fine with me.

> 
>>
>>> +
>>> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
>>> +        const u32 *format_list, u32 num_formats);
>>> +
>>> +#endif /*_DPU_WRITEBACK_H */
>>
>>


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer
  2022-04-20  1:46 ` [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer Abhinav Kumar
  2022-04-20  7:52   ` Dmitry Baryshkov
@ 2022-04-20 19:26   ` Dmitry Baryshkov
  1 sibling, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 19:26 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 20/04/2022 04:46, Abhinav Kumar wrote:
> Introduce the dpu_writeback module which serves as the
> interface between dpu operations and the drm_writeback.
> 
> This module manages the connector related operations for
> dpu writeback.
> 
> changes in v2:
> 	- start using drm_writeback_connector_init_with_encoder()
> 	- drop unnecessary arguments from dpu_writeback_init()
> 	- rebase on msm-next tip and remove usage of priv->connectors
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/msm/Makefile                  |  1 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 68 +++++++++++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 25 ++++++++++
>   3 files changed, 94 insertions(+)
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
>   create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 0387f22..66395ee 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -80,6 +80,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>   	disp/dpu1/dpu_plane.o \
>   	disp/dpu1/dpu_rm.o \
>   	disp/dpu1/dpu_vbif.o \
> +	disp/dpu1/dpu_writeback.o
>   
>   msm-$(CONFIG_DRM_MSM_MDSS) += \
>   	msm_mdss.o \
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
> new file mode 100644
> index 0000000..526d884
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include "dpu_writeback.h"
> +
> +static int dpu_wb_conn_get_modes(struct drm_connector *connector)
> +{
> +	struct drm_device *dev = connector->dev;
> +
> +	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
> +			dev->mode_config.max_height);
> +}
> +
> +static const struct drm_connector_funcs dpu_wb_conn_funcs = {
> +	.reset = drm_atomic_helper_connector_reset,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = drm_connector_cleanup,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector,
> +		struct drm_writeback_job *job)
> +{
> +	if (!job->fb)
> +		return 0;
> +
> +	dpu_encoder_prepare_wb_job(connector->encoder, job);
> +
> +	return 0;
> +}
> +
> +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector,
> +		struct drm_writeback_job *job)
> +{
> +	if (!job->fb)
> +		return;
> +
> +	dpu_encoder_cleanup_wb_job(connector->encoder, job);
> +}
> +
> +static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = {
> +	.get_modes = dpu_wb_conn_get_modes,
> +	.prepare_writeback_job = dpu_wb_conn_prepare_job,
> +	.cleanup_writeback_job = dpu_wb_conn_cleanup_job,
> +};
> +
> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
> +		const u32 *format_list, u32 num_formats)
> +{
> +	struct dpu_wb_connector *dpu_wb_conn;
> +	int rc = 0;
> +
> +	dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL);
> +
> +	drm_connector_helper_add(&dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs);
> +
> +	/* DPU initializes the encoder and sets it up completely for writeback
> +	 * cases and hence should use the new API drm_writeback_connector_init_with_encoder
> +	 * to initialize the writeback connector
> +	 */
> +	rc = drm_writeback_connector_init_with_encoder(dev, &dpu_wb_conn->base, enc,
> +			&dpu_wb_conn_funcs, format_list, num_formats);
> +
> +	return rc;
> +}
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> new file mode 100644
> index 0000000..05aff05
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#ifndef _DPU_WRITEBACK_H
> +#define _DPU_WRITEBACK_H
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_writeback.h>
> +
> +#include "msm_drv.h"
> +#include "dpu_kms.h"
> +#include "dpu_encoder_phys.h"
> +
> +struct dpu_wb_connector {
> +	struct drm_writeback_connector base;
> +};
> +
> +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc,
> +		const u32 *format_list, u32 num_formats);
> +
> +#endif /*_DPU_WRITEBACK_H */


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20 19:26       ` Dmitry Baryshkov
@ 2022-04-20 19:36         ` Abhinav Kumar
  2022-04-20 19:37           ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 19:36 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:26 PM, Dmitry Baryshkov wrote:
> On 20/04/2022 21:17, Abhinav Kumar wrote:
>>
>>
>> On 4/20/2022 12:49 AM, Dmitry Baryshkov wrote:
>>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>>> Introduce the dpu_encoder_phys_* for the writeback interface
>>>> to handle writeback specific hardware programming.
>>>>
>>>> changes in v2:
>>>>     - rebase on msm-next and fix related dependencies namely
>>>>       the irq cleanup
>>>>     - move cdp_cfg, aspace out of dpu_encoder_phys_wb
>>>>     - leave a comment about wb master
>>>>     - start using _dpu_hw_get_qos_lut from dpu_hw_util
>>>>     - replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> ---
>>>>   drivers/gpu/drm/msm/Makefile                       |   1 +
>>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
>>>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751 
>>>> +++++++++++++++++++++
>>>>   3 files changed, 782 insertions(+)
>>>>   create mode 100644 
>>>> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/Makefile 
>>>> b/drivers/gpu/drm/msm/Makefile
>>>> index ca779c1..0387f22 100644
>>>> --- a/drivers/gpu/drm/msm/Makefile
>>>> +++ b/drivers/gpu/drm/msm/Makefile
>>>> @@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
>>>>       disp/dpu1/dpu_encoder.o \
>>>>       disp/dpu1/dpu_encoder_phys_cmd.o \
>>>>       disp/dpu1/dpu_encoder_phys_vid.o \
>>>> +    disp/dpu1/dpu_encoder_phys_wb.o \
>>>>       disp/dpu1/dpu_formats.o \
>>>>       disp/dpu1/dpu_hw_catalog.o \
>>>>       disp/dpu1/dpu_hw_ctl.o \
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> index 00951f3..5452f98 100644
>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>> @@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
>>>>    * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
>>>>    * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode 
>>>> panel
>>>>    * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
>>>> + * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
>>>>    */
>>>>   enum dpu_intr_idx {
>>>>       INTR_IDX_VSYNC,
>>>> @@ -157,6 +158,7 @@ enum dpu_intr_idx {
>>>>       INTR_IDX_UNDERRUN,
>>>>       INTR_IDX_CTL_START,
>>>>       INTR_IDX_RDPTR,
>>>> +    INTR_IDX_WB_DONE,
>>>>       INTR_IDX_MAX,
>>>>   };
>>>> @@ -224,6 +226,27 @@ static inline int 
>>>> dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
>>>>   }
>>>>   /**
>>>> + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to 
>>>> handle command
>>>> + *    mode specific operations
>>>> + * @base:    Baseclass physical encoder structure
>>>> + * @wbirq_refcount:     Reference count of writeback interrupt
>>>> + * @wb_done_timeout_cnt: number of wb done irq timeout errors
>>>> + * @wb_cfg:  writeback block config to store fb related details
>>>> + * @wb_conn: backpointer to writeback connector
>>>> + * @wb_job: backpointer to current writeback job
>>>> + * @dest:   dpu buffer layout for current writeback output buffer
>>>> + */
>>>> +struct dpu_encoder_phys_wb {
>>>> +    struct dpu_encoder_phys base;
>>>> +    atomic_t wbirq_refcount;
>>>> +    int wb_done_timeout_cnt;
>>>> +    struct dpu_hw_wb_cfg wb_cfg;
>>>> +    struct drm_writeback_connector *wb_conn;
>>>> +    struct drm_writeback_job *wb_job;
>>>> +    struct dpu_hw_fmt_layout dest;
>>>> +};
>>>> +
>>>> +/**
>>>>    * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to 
>>>> handle command
>>>>    *    mode specific operations
>>>>    * @base:    Baseclass physical encoder structure
>>>> @@ -291,6 +314,13 @@ struct dpu_encoder_phys 
>>>> *dpu_encoder_phys_cmd_init(
>>>>           struct dpu_enc_phys_init_params *p);
>>>>   /**
>>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>>>> + * @init:    Pointer to init info structure with initialization params
>>>> + */
>>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>>>> +        struct dpu_enc_phys_init_params *p);
>>>> +
>>>> +/**
>>>>    * dpu_encoder_helper_trigger_start - control start helper function
>>>>    *    This helper function may be optionally specified by physical
>>>>    *    encoders if they require ctl_start triggering.
>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c 
>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>>> new file mode 100644
>>>> index 0000000..128317fe
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
>>>> @@ -0,0 +1,751 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> +/*
>>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>>> reserved.
>>>> + */
>>>> +
>>>> +#define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
>>>> +
>>>> +#include <linux/debugfs.h>
>>>> +
>>>> +#include "dpu_encoder_phys.h"
>>>> +#include "dpu_formats.h"
>>>> +#include "dpu_hw_top.h"
>>>> +#include "dpu_hw_wb.h"
>>>> +#include "dpu_hw_lm.h"
>>>> +#include "dpu_hw_blk.h"
>>>> +#include "dpu_hw_merge3d.h"
>>>> +#include "dpu_hw_interrupts.h"
>>>> +#include "dpu_core_irq.h"
>>>> +#include "dpu_vbif.h"
>>>> +#include "dpu_crtc.h"
>>>> +#include "disp/msm_disp_snapshot.h"
>>>> +
>>>> +#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
>>>> +
>>>> +#define to_dpu_encoder_phys_wb(x) \
>>>> +    container_of(x, struct dpu_encoder_phys_wb, base)
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_is_master - report wb always as master encoder
>>>> + */
>>>> +static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    /* there is only one physical enc for dpu_writeback */
>>>> +    return true;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback 
>>>> interface
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_set_ot_limit(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>>> +    struct dpu_vbif_set_ot_params ot_params;
>>>> +
>>>> +    memset(&ot_params, 0, sizeof(ot_params));
>>>> +    ot_params.xin_id = hw_wb->caps->xin_id;
>>>> +    ot_params.num = hw_wb->idx - WB_0;
>>>> +    ot_params.width = phys_enc->cached_mode.hdisplay;
>>>> +    ot_params.height = phys_enc->cached_mode.vdisplay;
>>>> +    ot_params.is_wfd = true;
>>>> +    ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
>>>> +    ot_params.vbif_idx = hw_wb->caps->vbif_idx;
>>>> +    ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>>>> +    ot_params.rd = false;
>>>> +
>>>> +    dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_set_qos_remap(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb;
>>>> +    struct dpu_vbif_set_qos_params qos_params;
>>>> +
>>>> +    if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
>>>> +        DPU_ERROR("invalid arguments\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
>>>> +        DPU_ERROR("invalid writeback hardware\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    hw_wb = phys_enc->hw_wb;
>>>> +
>>>> +    memset(&qos_params, 0, sizeof(qos_params));
>>>> +    qos_params.vbif_idx = hw_wb->caps->vbif_idx;
>>>> +    qos_params.xin_id = hw_wb->caps->xin_id;
>>>> +    qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
>>>> +    qos_params.num = hw_wb->idx - WB_0;
>>>> +    qos_params.is_rt = false;
>>>> +
>>>> +    DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
>>>> +            qos_params.num,
>>>> +            qos_params.vbif_idx,
>>>> +            qos_params.xin_id, qos_params.is_rt);
>>>> +
>>>> +    dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for 
>>>> writeback
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb;
>>>> +    struct dpu_hw_wb_qos_cfg qos_cfg;
>>>> +    struct dpu_mdss_cfg *catalog;
>>>> +    struct dpu_qos_lut_tbl *qos_lut_tb;
>>>> +
>>>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>>>> !phys_enc->dpu_kms->catalog) {
>>>> +        DPU_ERROR("invalid parameter(s)\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    catalog = phys_enc->dpu_kms->catalog;
>>>> +
>>>> +    hw_wb = phys_enc->hw_wb;
>>>> +
>>>> +    memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
>>>> +    qos_cfg.danger_safe_en = true;
>>>> +    qos_cfg.danger_lut =
>>>> +        catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>>> +
>>>> +    qos_cfg.safe_lut = 
>>>> catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>>> +
>>>> +    qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
>>>> +    qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
>>>> +
>>>> +    if (hw_wb->ops.setup_qos_lut)
>>>> +        hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + * @fb:        Pointer to output framebuffer
>>>> + * @wb_roi:    Pointer to output region of interest
>>>> + */
>>>> +static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys 
>>>> *phys_enc,
>>>> +        struct drm_framebuffer *fb)
>>>> +{
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +    struct dpu_hw_wb *hw_wb;
>>>> +    struct dpu_hw_wb_cfg *wb_cfg;
>>>> +    struct dpu_hw_wb_cdp_cfg cdp_cfg;
>>>> +
>>>> +    if (!phys_enc || !phys_enc->dpu_kms || 
>>>> !phys_enc->dpu_kms->catalog) {
>>>> +        DPU_ERROR("invalid encoder\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    hw_wb = phys_enc->hw_wb;
>>>> +    wb_cfg = &wb_enc->wb_cfg;
>>>> +
>>>> +    wb_cfg->intf_mode = phys_enc->intf_mode;
>>>> +    wb_cfg->roi.x1 = 0;
>>>> +    wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
>>>> +    wb_cfg->roi.y1 = 0;
>>>> +    wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
>>>> +
>>>> +    if (hw_wb->ops.setup_roi)
>>>> +        hw_wb->ops.setup_roi(hw_wb, wb_cfg);
>>>> +
>>>> +    if (hw_wb->ops.setup_outformat)
>>>> +        hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
>>>> +
>>>> +    if (hw_wb->ops.setup_cdp) {
>>>> +        memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
>>>> +
>>>> +        cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
>>>> +                [DPU_PERF_CDP_USAGE_NRT].wr_enable;
>>>> +        cdp_cfg.ubwc_meta_enable =
>>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
>>>> +        cdp_cfg.tile_amortize_enable =
>>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
>>>> +                DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
>>>> +        cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
>>>> +
>>>> +        hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
>>>> +    }
>>>> +
>>>> +    if (hw_wb->ops.setup_outaddress)
>>>> +        hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
>>>> + * @phys_enc:Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb;
>>>> +    struct dpu_hw_ctl *ctl;
>>>> +
>>>> +    if (!phys_enc) {
>>>> +        DPU_ERROR("invalid encoder\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    hw_wb = phys_enc->hw_wb;
>>>> +    ctl = phys_enc->hw_ctl;
>>>> +
>>>> +    if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
>>>> +        (phys_enc->hw_ctl &&
>>>> +         phys_enc->hw_ctl->ops.setup_intf_cfg)) {
>>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>>>> +        struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
>>>> +        enum dpu_3d_blend_mode mode_3d;
>>>> +
>>>> +        mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>>>> +
>>>> +        intf_cfg.intf = DPU_NONE;
>>>> +        intf_cfg.wb = hw_wb->idx;
>>>> +
>>>> +        if (mode_3d && hw_pp && hw_pp->merge_3d)
>>>> +            intf_cfg.merge_3d = hw_pp->merge_3d->idx;
>>>> +
>>>> +        if (phys_enc->hw_pp->merge_3d && 
>>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
>>>> + 
>>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
>>>> +                    mode_3d);
>>>> +
>>>> +        /* setup which pp blk will connect to this wb */
>>>> +        if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
>>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, 
>>>> true,
>>>> +                    phys_enc->hw_pp->idx);
>>>> +
>>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>>>> &intf_cfg);
>>>> +    } else if (phys_enc->hw_ctl && 
>>>> phys_enc->hw_ctl->ops.setup_intf_cfg) {
>>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
>>>> +
>>>> +        intf_cfg.intf = DPU_NONE;
>>>> +        intf_cfg.wb = hw_wb->idx;
>>>> +        intf_cfg.mode_3d =
>>>> +            dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, 
>>>> &intf_cfg);
>>>> +    }
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic 
>>>> states
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + * @crtc_state:    Pointer to CRTC atomic state
>>>> + * @conn_state:    Pointer to connector atomic state
>>>> + */
>>>> +static int dpu_encoder_phys_wb_atomic_check(
>>>> +        struct dpu_encoder_phys *phys_enc,
>>>> +        struct drm_crtc_state *crtc_state,
>>>> +        struct drm_connector_state *conn_state)
>>>> +{
>>>> +    struct drm_framebuffer *fb;
>>>> +    const struct drm_display_mode *mode;
>>>> +
>>>> +    DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
>>>> +            phys_enc->intf_idx, mode->name, mode->hdisplay, 
>>>> mode->vdisplay);
>>>> +
>>>> +    if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
>>>> +        return 0;
>>>> +
>>>> +    fb = conn_state->writeback_job->fb;
>>>> +    mode = &crtc_state->mode;
>>>> +
>>>> +    if (!conn_state || !conn_state->connector) {
>>>> +        DPU_ERROR("invalid connector state\n");
>>>> +        return -EINVAL;
>>>> +    } else if (conn_state->connector->status !=
>>>> +            connector_status_connected) {
>>>> +        DPU_ERROR("connector not connected %d\n",
>>>> +                conn_state->connector->status);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
>>>> +            fb->width, fb->height);
>>>> +
>>>> +    if (fb->width != mode->hdisplay) {
>>>> +        DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
>>>> +                mode->hdisplay);
>>>> +        return -EINVAL;
>>>> +    } else if (fb->height != mode->vdisplay) {
>>>> +        DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
>>>> +                  mode->vdisplay);
>>>> +        return -EINVAL;
>>>> +    } else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
>>>> +        DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
>>>> +                  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
>>>> +        return -EINVAL;
>>>> +    }
>>>
>>> Being not an expert in the DRM WB API, I have a question: are those 
>>> hardware limitations or the DRM requirement that we can not write to 
>>> the region inside the attached FB?
>>
>> Both. The first two checks are indeed DRM requirement and infact igt 
>> writeback tests this to ensure that the driver rejects any invalid FBs 
>> which are not equal to the mode that was set.
>>
>> https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tests/kms_writeback.c#L409 
> 
> 
> The test is writing to the fb which is smaller than the used video mode 
> (which is an obvious mistake). Is it correst to write back to the FB 
> that is bigger than the selected resolution?

Ah, so your question is whether writeback can just write to a portion of 
the larger FB.

Yes, its possible . So thats another follow-up feature to support WB_ROI.

WB can be made to program a smaller ROI in the larger buffer. It is 
possible. Just not supported in this version and even downstream even 
though the code is present, its not verified extensively.

When we add support for it, we will relax these conditions based on the 
feature.

> 
>> The last check is coming from our hardware >
>>>
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +
>>>> +/**
>>>> + * _dpu_encoder_phys_wb_update_flush - flush hardware update
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void _dpu_encoder_phys_wb_update_flush(struct 
>>>> dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb;
>>>> +    struct dpu_hw_ctl *hw_ctl;
>>>> +    struct dpu_hw_pingpong *hw_pp;
>>>> +    u32 pending_flush = 0;
>>>> +
>>>> +    if (!phys_enc)
>>>> +        return;
>>>> +
>>>> +    hw_wb = phys_enc->hw_wb;
>>>> +    hw_pp = phys_enc->hw_pp;
>>>> +    hw_ctl = phys_enc->hw_ctl;
>>>> +
>>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>>> +
>>>> +    if (!hw_ctl) {
>>>> +        DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (hw_ctl->ops.update_pending_flush_wb)
>>>> +        hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
>>>> +
>>>> +    if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp && 
>>>> hw_pp->merge_3d)
>>>> +        hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
>>>> +                hw_pp->merge_3d->idx);
>>>> +
>>>> +    if (hw_ctl->ops.get_pending_flush)
>>>> +        pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
>>>> +
>>>> +    DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
>>>> +            hw_ctl->idx - CTL_0, pending_flush,
>>>> +            hw_wb->idx - WB_0);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_setup - setup writeback encoder
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_setup(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>>> +    struct drm_display_mode mode = phys_enc->cached_mode;
>>>> +    struct drm_framebuffer *fb = NULL;
>>>> +
>>>> +    DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
>>>> +            hw_wb->idx - WB_0, mode.name,
>>>> +            mode.hdisplay, mode.vdisplay);
>>>> +
>>>> +    dpu_encoder_phys_wb_set_ot_limit(phys_enc);
>>>> +
>>>> +    dpu_encoder_phys_wb_set_qos_remap(phys_enc);
>>>> +
>>>> +    dpu_encoder_phys_wb_set_qos(phys_enc);
>>>> +
>>>> +    dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
>>>> +
>>>> +    dpu_encoder_phys_wb_setup_cdp(phys_enc);
>>>> +
>>>> +}
>>>> +
>>>> +static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
>>>> +{
>>>> +    struct dpu_encoder_phys *phys_enc = arg;
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +
>>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>>> +    unsigned long lock_flags;
>>>> +    u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
>>>> +
>>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>>> +
>>>> +    if (phys_enc->parent_ops->handle_frame_done)
>>>> +        phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
>>>> +                phys_enc, event);
>>>> +
>>>> +    if (phys_enc->parent_ops->handle_vblank_virt)
>>>> +        phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
>>>> +                phys_enc);
>>>> +
>>>> +    spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
>>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>>>> +    spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
>>>> +
>>>> +    if (wb_enc->wb_conn)
>>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>>>> +
>>>> +    /* Signal any waiting atomic commit thread */
>>>> +    wake_up_all(&phys_enc->pending_kickoff_wq);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
>>>> + * @arg:    Pointer to writeback encoder
>>>> + * @irq_idx:    interrupt index
>>>> + */
>>>> +static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
>>>> +{
>>>> +    _dpu_encoder_phys_wb_frame_done_helper(arg);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
>>>> + * @phys:    Pointer to physical encoder
>>>> + * @enable:    indicates enable or disable interrupts
>>>> + */
>>>> +static void dpu_encoder_phys_wb_irq_ctrl(
>>>> +        struct dpu_encoder_phys *phys, bool enable)
>>>> +{
>>>> +
>>>> +    struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
>>>> +    int ret = 0;
>>>> +    int refcount;
>>>> +
>>>> +    refcount = atomic_read(&wb_enc->wbirq_refcount);
>>>> +
>>>> +    if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
>>>> +        dpu_core_irq_register_callback(phys->dpu_kms,
>>>> +                phys->irq[INTR_IDX_WB_DONE], 
>>>> dpu_encoder_phys_wb_done_irq, phys);
>>>> +        if (ret)
>>>> +            atomic_dec_return(&wb_enc->wbirq_refcount);
>>>> +    } else if (!enable &&
>>>> +            atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
>>>> +        dpu_core_irq_unregister_callback(phys->dpu_kms, 
>>>> phys->irq[INTR_IDX_WB_DONE]);
>>>> +        if (ret)
>>>> +            atomic_inc_return(&wb_enc->wbirq_refcount);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void dpu_encoder_phys_wb_atomic_mode_set(
>>>> +        struct dpu_encoder_phys *phys_enc,
>>>> +        struct drm_crtc_state *crtc_state,
>>>> +        struct drm_connector_state *conn_state)
>>>> +{
>>>> +
>>>> +    phys_enc->irq[INTR_IDX_WB_DONE] = 
>>>> phys_enc->hw_wb->caps->intr_wb_done;
>>>> +}
>>>> +
>>>> +static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +    u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
>>>> +
>>>> +    wb_enc->wb_done_timeout_cnt++;
>>>> +
>>>> +    if (wb_enc->wb_done_timeout_cnt == 1)
>>>> +        msm_disp_snapshot_state(phys_enc->parent->dev);
>>>> +
>>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
>>>> +
>>>> +    /* request a ctl reset before the next kickoff */
>>>> +    phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
>>>> +
>>>> +    if (wb_enc->wb_conn)
>>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
>>>> +
>>>> +    if (phys_enc->parent_ops->handle_frame_done)
>>>> +        phys_enc->parent_ops->handle_frame_done(
>>>> +                phys_enc->parent, phys_enc, frame_event);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is 
>>>> committed
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static int dpu_encoder_phys_wb_wait_for_commit_done(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    unsigned long ret;
>>>> +    struct dpu_encoder_wait_info wait_info;
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +
>>>> +    wait_info.wq = &phys_enc->pending_kickoff_wq;
>>>> +    wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
>>>> +    wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
>>>> +
>>>> +    ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
>>>> +            dpu_encoder_phys_wb_done_irq, &wait_info);
>>>> +    if (ret == -ETIMEDOUT)
>>>> +        _dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
>>>> +    else if (!ret)
>>>> +        wb_enc->wb_done_timeout_cnt = 0;
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + * Returns:    Zero on success
>>>> + */
>>>> +static void dpu_encoder_phys_wb_prepare_for_kickoff(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +    struct drm_connector *drm_conn;
>>>> +    struct drm_connector_state *state;
>>>> +
>>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>>> +
>>>> +    if (!wb_enc->wb_conn || !wb_enc->wb_job) {
>>>> +        DPU_ERROR("invalid wb_conn or wb_job\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    drm_conn = &wb_enc->wb_conn->base;
>>>> +    state = drm_conn->state;
>>>> +
>>>> +    if (wb_enc->wb_conn && wb_enc->wb_job)
>>>> +        drm_writeback_queue_job(wb_enc->wb_conn, state);
>>>> +
>>>> +    dpu_encoder_phys_wb_setup(phys_enc);
>>>> +
>>>> +    _dpu_encoder_phys_wb_update_flush(phys_enc);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static bool dpu_encoder_phys_wb_needs_single_flush(struct 
>>>> dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>>> +    return false;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_handle_post_kickoff(
>>>> +        struct dpu_encoder_phys *phys_enc)
>>>> +{
>>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>>> +
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_enable - enable writeback encoder
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
>>>> +    phys_enc->enable_state = DPU_ENC_ENABLED;
>>>> +}
>>>> +/**
>>>> + * dpu_encoder_phys_wb_disable - disable writeback encoder
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
>>>> +    struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
>>>> +
>>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
>>>> +
>>>> +    if (phys_enc->enable_state == DPU_ENC_DISABLED) {
>>>> +        DPU_ERROR("encoder is already disabled\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /* reset h/w before final flush */
>>>> +    if (phys_enc->hw_ctl->ops.clear_pending_flush)
>>>> +        phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
>>>> +
>>>> +    /*
>>>> +     * New CTL reset sequence from 5.0 MDP onwards.
>>>> +     * If has_3d_merge_reset is not set, legacy reset
>>>> +     * sequence is executed.
>>>> +     *
>>>> +     * Legacy reset sequence has not been implemented yet.
>>>> +     * Any target earlier than SM8150 will need it and when
>>>> +     * WB support is added to those targets will need to add
>>>> +     * the legacy teardown sequence as well.
>>>> +     */
>>>> +    if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
>>>> +        dpu_encoder_helper_phys_cleanup(phys_enc);
>>>> +
>>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_destroy - destroy writeback encoder
>>>> + * @phys_enc:    Pointer to physical encoder
>>>> + */
>>>> +static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys 
>>>> *phys_enc)
>>>> +{
>>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
>>>> +
>>>> +    if (!phys_enc)
>>>> +        return;
>>>> +
>>>> +    kfree(phys_enc);
>>>> +}
>>>> +
>>>> +static void dpu_encoder_phys_wb_prepare_wb_job(struct 
>>>> dpu_encoder_phys *phys_enc,
>>>> +        struct drm_writeback_job *job)
>>>> +{
>>>> +    const struct msm_format *format;
>>>> +    struct msm_gem_address_space *aspace;
>>>> +    struct dpu_hw_wb_cfg *wb_cfg;
>>>> +    int ret;
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +
>>>> +    if (!job->fb)
>>>> +        return;
>>>> +
>>>> +    wb_enc->wb_job = job;
>>>> +    wb_enc->wb_conn = job->connector;
>>>> +    aspace = phys_enc->dpu_kms->base.aspace;
>>>> +
>>>> +    wb_cfg = &wb_enc->wb_cfg;
>>>> +
>>>> +    memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
>>>> +
>>>> +    ret = msm_framebuffer_prepare(job->fb, aspace, false);
>>>> +    if (ret) {
>>>> +        DPU_ERROR("prep fb failed, %d\n", ret);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    format = msm_framebuffer_format(job->fb);
>>>> +
>>>> +    wb_cfg->dest.format = dpu_get_dpu_format_ext(
>>>> +            format->pixel_format, job->fb->modifier);
>>>> +    if (!wb_cfg->dest.format) {
>>>> +        /* this error should be detected during atomic_check */
>>>> +        DPU_ERROR("failed to get format %x\n", format->pixel_format);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
>>>> +    if (ret) {
>>>> +        DPU_DEBUG("failed to populate layout %d\n", ret);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    wb_cfg->dest.width = job->fb->width;
>>>> +    wb_cfg->dest.height = job->fb->height;
>>>> +    wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
>>>> +
>>>> +    if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
>>>> +            (wb_cfg->dest.format->element[0] == C1_B_Cb))
>>>> +        swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
>>>> +
>>>> +    DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>>>> +            wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
>>>> +            wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
>>>> +
>>>> +    DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
>>>> +            wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
>>>> +            wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
>>>> +}
>>>> +
>>>> +static void dpu_encoder_phys_wb_cleanup_wb_job(struct 
>>>> dpu_encoder_phys *phys_enc,
>>>> +        struct drm_writeback_job *job)
>>>> +{
>>>> +    struct dpu_encoder_phys_wb *wb_enc = 
>>>> to_dpu_encoder_phys_wb(phys_enc);
>>>> +    struct msm_gem_address_space *aspace;
>>>> +
>>>> +    if (!job->fb)
>>>> +        return;
>>>> +
>>>> +    aspace = phys_enc->dpu_kms->base.aspace;
>>>> +
>>>> +    msm_framebuffer_cleanup(job->fb, aspace, false);
>>>> +    wb_enc->wb_job = NULL;
>>>> +    wb_enc->wb_conn = NULL;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_init_ops - initialize writeback operations
>>>> + * @ops:    Pointer to encoder operation table
>>>> + */
>>>> +static void dpu_encoder_phys_wb_init_ops(struct 
>>>> dpu_encoder_phys_ops *ops)
>>>> +{
>>>> +    ops->is_master = dpu_encoder_phys_wb_is_master;
>>>> +    ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
>>>> +    ops->enable = dpu_encoder_phys_wb_enable;
>>>> +    ops->disable = dpu_encoder_phys_wb_disable;
>>>> +    ops->destroy = dpu_encoder_phys_wb_destroy;
>>>> +    ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
>>>> +    ops->wait_for_commit_done = 
>>>> dpu_encoder_phys_wb_wait_for_commit_done;
>>>> +    ops->prepare_for_kickoff = 
>>>> dpu_encoder_phys_wb_prepare_for_kickoff;
>>>> +    ops->handle_post_kickoff = 
>>>> dpu_encoder_phys_wb_handle_post_kickoff;
>>>> +    ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
>>>> +    ops->trigger_start = dpu_encoder_helper_trigger_start;
>>>> +    ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
>>>> +    ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
>>>> +    ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
>>>> + * @init:    Pointer to init info structure with initialization params
>>>> + */
>>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
>>>> +        struct dpu_enc_phys_init_params *p)
>>>> +{
>>>> +    struct dpu_encoder_phys *phys_enc = NULL;
>>>> +    struct dpu_encoder_phys_wb *wb_enc = NULL;
>>>> +    int ret = 0;
>>>> +    int i;
>>>> +
>>>> +    DPU_DEBUG("\n");
>>>> +
>>>> +    if (!p || !p->parent) {
>>>> +        DPU_ERROR("invalid params\n");
>>>> +        ret = -EINVAL;
>>>> +        goto fail_alloc;
>>>> +    }
>>>> +
>>>> +    wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
>>>> +    if (!wb_enc) {
>>>> +        DPU_ERROR("failed to allocate wb phys_enc enc\n");
>>>> +        ret = -ENOMEM;
>>>> +        goto fail_alloc;
>>>> +    }
>>>> +
>>>> +    phys_enc = &wb_enc->base;
>>>> +    phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
>>>> +    phys_enc->intf_idx = p->intf_idx;
>>>> +
>>>> +    dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
>>>> +    phys_enc->parent = p->parent;
>>>> +    phys_enc->parent_ops = p->parent_ops;
>>>> +    phys_enc->dpu_kms = p->dpu_kms;
>>>> +    phys_enc->split_role = p->split_role;
>>>> +    phys_enc->intf_mode = INTF_MODE_WB_LINE;
>>>> +    phys_enc->intf_idx = p->intf_idx;
>>>> +    phys_enc->enc_spinlock = p->enc_spinlock;
>>>> +
>>>> +    atomic_set(&wb_enc->wbirq_refcount, 0);
>>>> +
>>>> +    for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
>>>> +        phys_enc->irq[i] = -EINVAL;
>>>> +
>>>> +    atomic_set(&phys_enc->pending_kickoff_cnt, 0);
>>>> +    atomic_set(&phys_enc->vblank_refcount, 0);
>>>> +    wb_enc->wb_done_timeout_cnt = 0;
>>>> +
>>>> +    init_waitqueue_head(&phys_enc->pending_kickoff_wq);
>>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
>>>> +
>>>> +    DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
>>>> +            phys_enc->intf_idx);
>>>> +
>>>> +    return phys_enc;
>>>> +
>>>> +fail_alloc:
>>>> +    return ERR_PTR(ret);
>>>> +}
>>>
>>>
> 
> 

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

* Re: [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
  2022-04-20 19:36         ` Abhinav Kumar
@ 2022-04-20 19:37           ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 19:37 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On Wed, 20 Apr 2022 at 22:36, Abhinav Kumar <quic_abhinavk@quicinc.com> wrote:
>
>
>
> On 4/20/2022 12:26 PM, Dmitry Baryshkov wrote:
> > On 20/04/2022 21:17, Abhinav Kumar wrote:
> >>
> >>
> >> On 4/20/2022 12:49 AM, Dmitry Baryshkov wrote:
> >>> On 20/04/2022 04:46, Abhinav Kumar wrote:
> >>>> Introduce the dpu_encoder_phys_* for the writeback interface
> >>>> to handle writeback specific hardware programming.
> >>>>
> >>>> changes in v2:
> >>>>     - rebase on msm-next and fix related dependencies namely
> >>>>       the irq cleanup
> >>>>     - move cdp_cfg, aspace out of dpu_encoder_phys_wb
> >>>>     - leave a comment about wb master
> >>>>     - start using _dpu_hw_get_qos_lut from dpu_hw_util
> >>>>     - replace hw_pp->merge_3d check with DPU_CTL_ACTIVE_CFG
> >>>>
> >>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >>>> ---
> >>>>   drivers/gpu/drm/msm/Makefile                       |   1 +
> >>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |  30 +
> >>>>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c    | 751
> >>>> +++++++++++++++++++++
> >>>>   3 files changed, 782 insertions(+)
> >>>>   create mode 100644
> >>>> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> >>>>
> >>>> diff --git a/drivers/gpu/drm/msm/Makefile
> >>>> b/drivers/gpu/drm/msm/Makefile
> >>>> index ca779c1..0387f22 100644
> >>>> --- a/drivers/gpu/drm/msm/Makefile
> >>>> +++ b/drivers/gpu/drm/msm/Makefile
> >>>> @@ -60,6 +60,7 @@ msm-$(CONFIG_DRM_MSM_DPU) += \
> >>>>       disp/dpu1/dpu_encoder.o \
> >>>>       disp/dpu1/dpu_encoder_phys_cmd.o \
> >>>>       disp/dpu1/dpu_encoder_phys_vid.o \
> >>>> +    disp/dpu1/dpu_encoder_phys_wb.o \
> >>>>       disp/dpu1/dpu_formats.o \
> >>>>       disp/dpu1/dpu_hw_catalog.o \
> >>>>       disp/dpu1/dpu_hw_ctl.o \
> >>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >>>> index 00951f3..5452f98 100644
> >>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> >>>> @@ -150,6 +150,7 @@ struct dpu_encoder_phys_ops {
> >>>>    * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
> >>>>    * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode
> >>>> panel
> >>>>    * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
> >>>> + * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
> >>>>    */
> >>>>   enum dpu_intr_idx {
> >>>>       INTR_IDX_VSYNC,
> >>>> @@ -157,6 +158,7 @@ enum dpu_intr_idx {
> >>>>       INTR_IDX_UNDERRUN,
> >>>>       INTR_IDX_CTL_START,
> >>>>       INTR_IDX_RDPTR,
> >>>> +    INTR_IDX_WB_DONE,
> >>>>       INTR_IDX_MAX,
> >>>>   };
> >>>> @@ -224,6 +226,27 @@ static inline int
> >>>> dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
> >>>>   }
> >>>>   /**
> >>>> + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to
> >>>> handle command
> >>>> + *    mode specific operations
> >>>> + * @base:    Baseclass physical encoder structure
> >>>> + * @wbirq_refcount:     Reference count of writeback interrupt
> >>>> + * @wb_done_timeout_cnt: number of wb done irq timeout errors
> >>>> + * @wb_cfg:  writeback block config to store fb related details
> >>>> + * @wb_conn: backpointer to writeback connector
> >>>> + * @wb_job: backpointer to current writeback job
> >>>> + * @dest:   dpu buffer layout for current writeback output buffer
> >>>> + */
> >>>> +struct dpu_encoder_phys_wb {
> >>>> +    struct dpu_encoder_phys base;
> >>>> +    atomic_t wbirq_refcount;
> >>>> +    int wb_done_timeout_cnt;
> >>>> +    struct dpu_hw_wb_cfg wb_cfg;
> >>>> +    struct drm_writeback_connector *wb_conn;
> >>>> +    struct drm_writeback_job *wb_job;
> >>>> +    struct dpu_hw_fmt_layout dest;
> >>>> +};
> >>>> +
> >>>> +/**
> >>>>    * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to
> >>>> handle command
> >>>>    *    mode specific operations
> >>>>    * @base:    Baseclass physical encoder structure
> >>>> @@ -291,6 +314,13 @@ struct dpu_encoder_phys
> >>>> *dpu_encoder_phys_cmd_init(
> >>>>           struct dpu_enc_phys_init_params *p);
> >>>>   /**
> >>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
> >>>> + * @init:    Pointer to init info structure with initialization params
> >>>> + */
> >>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
> >>>> +        struct dpu_enc_phys_init_params *p);
> >>>> +
> >>>> +/**
> >>>>    * dpu_encoder_helper_trigger_start - control start helper function
> >>>>    *    This helper function may be optionally specified by physical
> >>>>    *    encoders if they require ctl_start triggering.
> >>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> >>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> >>>> new file mode 100644
> >>>> index 0000000..128317fe
> >>>> --- /dev/null
> >>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
> >>>> @@ -0,0 +1,751 @@
> >>>> +// SPDX-License-Identifier: GPL-2.0-only
> >>>> +/*
> >>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights
> >>>> reserved.
> >>>> + */
> >>>> +
> >>>> +#define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
> >>>> +
> >>>> +#include <linux/debugfs.h>
> >>>> +
> >>>> +#include "dpu_encoder_phys.h"
> >>>> +#include "dpu_formats.h"
> >>>> +#include "dpu_hw_top.h"
> >>>> +#include "dpu_hw_wb.h"
> >>>> +#include "dpu_hw_lm.h"
> >>>> +#include "dpu_hw_blk.h"
> >>>> +#include "dpu_hw_merge3d.h"
> >>>> +#include "dpu_hw_interrupts.h"
> >>>> +#include "dpu_core_irq.h"
> >>>> +#include "dpu_vbif.h"
> >>>> +#include "dpu_crtc.h"
> >>>> +#include "disp/msm_disp_snapshot.h"
> >>>> +
> >>>> +#define DEFAULT_MAX_WRITEBACK_WIDTH 2048
> >>>> +
> >>>> +#define to_dpu_encoder_phys_wb(x) \
> >>>> +    container_of(x, struct dpu_encoder_phys_wb, base)
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_is_master - report wb always as master encoder
> >>>> + */
> >>>> +static bool dpu_encoder_phys_wb_is_master(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    /* there is only one physical enc for dpu_writeback */
> >>>> +    return true;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_set_ot_limit - set OT limit for writeback
> >>>> interface
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_set_ot_limit(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> >>>> +    struct dpu_vbif_set_ot_params ot_params;
> >>>> +
> >>>> +    memset(&ot_params, 0, sizeof(ot_params));
> >>>> +    ot_params.xin_id = hw_wb->caps->xin_id;
> >>>> +    ot_params.num = hw_wb->idx - WB_0;
> >>>> +    ot_params.width = phys_enc->cached_mode.hdisplay;
> >>>> +    ot_params.height = phys_enc->cached_mode.vdisplay;
> >>>> +    ot_params.is_wfd = true;
> >>>> +    ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
> >>>> +    ot_params.vbif_idx = hw_wb->caps->vbif_idx;
> >>>> +    ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
> >>>> +    ot_params.rd = false;
> >>>> +
> >>>> +    dpu_vbif_set_ot_limit(phys_enc->dpu_kms, &ot_params);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_set_qos_remap(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb;
> >>>> +    struct dpu_vbif_set_qos_params qos_params;
> >>>> +
> >>>> +    if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
> >>>> +        DPU_ERROR("invalid arguments\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    if (!phys_enc->hw_wb || !phys_enc->hw_wb->caps) {
> >>>> +        DPU_ERROR("invalid writeback hardware\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    hw_wb = phys_enc->hw_wb;
> >>>> +
> >>>> +    memset(&qos_params, 0, sizeof(qos_params));
> >>>> +    qos_params.vbif_idx = hw_wb->caps->vbif_idx;
> >>>> +    qos_params.xin_id = hw_wb->caps->xin_id;
> >>>> +    qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
> >>>> +    qos_params.num = hw_wb->idx - WB_0;
> >>>> +    qos_params.is_rt = false;
> >>>> +
> >>>> +    DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n",
> >>>> +            qos_params.num,
> >>>> +            qos_params.vbif_idx,
> >>>> +            qos_params.xin_id, qos_params.is_rt);
> >>>> +
> >>>> +    dpu_vbif_set_qos_remap(phys_enc->dpu_kms, &qos_params);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_set_qos - set QoS/danger/safe LUTs for
> >>>> writeback
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb;
> >>>> +    struct dpu_hw_wb_qos_cfg qos_cfg;
> >>>> +    struct dpu_mdss_cfg *catalog;
> >>>> +    struct dpu_qos_lut_tbl *qos_lut_tb;
> >>>> +
> >>>> +    if (!phys_enc || !phys_enc->dpu_kms ||
> >>>> !phys_enc->dpu_kms->catalog) {
> >>>> +        DPU_ERROR("invalid parameter(s)\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    catalog = phys_enc->dpu_kms->catalog;
> >>>> +
> >>>> +    hw_wb = phys_enc->hw_wb;
> >>>> +
> >>>> +    memset(&qos_cfg, 0, sizeof(struct dpu_hw_wb_qos_cfg));
> >>>> +    qos_cfg.danger_safe_en = true;
> >>>> +    qos_cfg.danger_lut =
> >>>> +        catalog->perf.danger_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> >>>> +
> >>>> +    qos_cfg.safe_lut =
> >>>> catalog->perf.safe_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> >>>> +
> >>>> +    qos_lut_tb = &catalog->perf.qos_lut_tbl[DPU_QOS_LUT_USAGE_NRT];
> >>>> +    qos_cfg.creq_lut = _dpu_hw_get_qos_lut(qos_lut_tb, 0);
> >>>> +
> >>>> +    if (hw_wb->ops.setup_qos_lut)
> >>>> +        hw_wb->ops.setup_qos_lut(hw_wb, &qos_cfg);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_setup_fb - setup output framebuffer
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + * @fb:        Pointer to output framebuffer
> >>>> + * @wb_roi:    Pointer to output region of interest
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys
> >>>> *phys_enc,
> >>>> +        struct drm_framebuffer *fb)
> >>>> +{
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +    struct dpu_hw_wb *hw_wb;
> >>>> +    struct dpu_hw_wb_cfg *wb_cfg;
> >>>> +    struct dpu_hw_wb_cdp_cfg cdp_cfg;
> >>>> +
> >>>> +    if (!phys_enc || !phys_enc->dpu_kms ||
> >>>> !phys_enc->dpu_kms->catalog) {
> >>>> +        DPU_ERROR("invalid encoder\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    hw_wb = phys_enc->hw_wb;
> >>>> +    wb_cfg = &wb_enc->wb_cfg;
> >>>> +
> >>>> +    wb_cfg->intf_mode = phys_enc->intf_mode;
> >>>> +    wb_cfg->roi.x1 = 0;
> >>>> +    wb_cfg->roi.x2 = phys_enc->cached_mode.hdisplay;
> >>>> +    wb_cfg->roi.y1 = 0;
> >>>> +    wb_cfg->roi.y2 = phys_enc->cached_mode.vdisplay;
> >>>> +
> >>>> +    if (hw_wb->ops.setup_roi)
> >>>> +        hw_wb->ops.setup_roi(hw_wb, wb_cfg);
> >>>> +
> >>>> +    if (hw_wb->ops.setup_outformat)
> >>>> +        hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
> >>>> +
> >>>> +    if (hw_wb->ops.setup_cdp) {
> >>>> +        memset(&cdp_cfg, 0, sizeof(struct dpu_hw_wb_cdp_cfg));
> >>>> +
> >>>> +        cdp_cfg.enable = phys_enc->dpu_kms->catalog->perf.cdp_cfg
> >>>> +                [DPU_PERF_CDP_USAGE_NRT].wr_enable;
> >>>> +        cdp_cfg.ubwc_meta_enable =
> >>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format);
> >>>> +        cdp_cfg.tile_amortize_enable =
> >>>> +                DPU_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
> >>>> +                DPU_FORMAT_IS_TILE(wb_cfg->dest.format);
> >>>> +        cdp_cfg.preload_ahead = DPU_WB_CDP_PRELOAD_AHEAD_64;
> >>>> +
> >>>> +        hw_wb->ops.setup_cdp(hw_wb, &cdp_cfg);
> >>>> +    }
> >>>> +
> >>>> +    if (hw_wb->ops.setup_outaddress)
> >>>> +        hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
> >>>> + * @phys_enc:Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_setup_cdp(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb;
> >>>> +    struct dpu_hw_ctl *ctl;
> >>>> +
> >>>> +    if (!phys_enc) {
> >>>> +        DPU_ERROR("invalid encoder\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    hw_wb = phys_enc->hw_wb;
> >>>> +    ctl = phys_enc->hw_ctl;
> >>>> +
> >>>> +    if (test_bit(DPU_CTL_ACTIVE_CFG, &ctl->caps->features) &&
> >>>> +        (phys_enc->hw_ctl &&
> >>>> +         phys_enc->hw_ctl->ops.setup_intf_cfg)) {
> >>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
> >>>> +        struct dpu_hw_pingpong *hw_pp = phys_enc->hw_pp;
> >>>> +        enum dpu_3d_blend_mode mode_3d;
> >>>> +
> >>>> +        mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
> >>>> +
> >>>> +        intf_cfg.intf = DPU_NONE;
> >>>> +        intf_cfg.wb = hw_wb->idx;
> >>>> +
> >>>> +        if (mode_3d && hw_pp && hw_pp->merge_3d)
> >>>> +            intf_cfg.merge_3d = hw_pp->merge_3d->idx;
> >>>> +
> >>>> +        if (phys_enc->hw_pp->merge_3d &&
> >>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode)
> >>>> +
> >>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
> >>>> +                    mode_3d);
> >>>> +
> >>>> +        /* setup which pp blk will connect to this wb */
> >>>> +        if (hw_pp && phys_enc->hw_wb->ops.bind_pingpong_blk)
> >>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb,
> >>>> true,
> >>>> +                    phys_enc->hw_pp->idx);
> >>>> +
> >>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl,
> >>>> &intf_cfg);
> >>>> +    } else if (phys_enc->hw_ctl &&
> >>>> phys_enc->hw_ctl->ops.setup_intf_cfg) {
> >>>> +        struct dpu_hw_intf_cfg intf_cfg = {0};
> >>>> +
> >>>> +        intf_cfg.intf = DPU_NONE;
> >>>> +        intf_cfg.wb = hw_wb->idx;
> >>>> +        intf_cfg.mode_3d =
> >>>> +            dpu_encoder_helper_get_3d_blend_mode(phys_enc);
> >>>> +        phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl,
> >>>> &intf_cfg);
> >>>> +    }
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_atomic_check - verify and fixup given atomic
> >>>> states
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + * @crtc_state:    Pointer to CRTC atomic state
> >>>> + * @conn_state:    Pointer to connector atomic state
> >>>> + */
> >>>> +static int dpu_encoder_phys_wb_atomic_check(
> >>>> +        struct dpu_encoder_phys *phys_enc,
> >>>> +        struct drm_crtc_state *crtc_state,
> >>>> +        struct drm_connector_state *conn_state)
> >>>> +{
> >>>> +    struct drm_framebuffer *fb;
> >>>> +    const struct drm_display_mode *mode;
> >>>> +
> >>>> +    DPU_DEBUG("[atomic_check:%d, \"%s\",%d,%d]\n",
> >>>> +            phys_enc->intf_idx, mode->name, mode->hdisplay,
> >>>> mode->vdisplay);
> >>>> +
> >>>> +    if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
> >>>> +        return 0;
> >>>> +
> >>>> +    fb = conn_state->writeback_job->fb;
> >>>> +    mode = &crtc_state->mode;
> >>>> +
> >>>> +    if (!conn_state || !conn_state->connector) {
> >>>> +        DPU_ERROR("invalid connector state\n");
> >>>> +        return -EINVAL;
> >>>> +    } else if (conn_state->connector->status !=
> >>>> +            connector_status_connected) {
> >>>> +        DPU_ERROR("connector not connected %d\n",
> >>>> +                conn_state->connector->status);
> >>>> +        return -EINVAL;
> >>>> +    }
> >>>> +
> >>>> +    DPU_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
> >>>> +            fb->width, fb->height);
> >>>> +
> >>>> +    if (fb->width != mode->hdisplay) {
> >>>> +        DPU_ERROR("invalid fb w=%d, mode w=%d\n", fb->width,
> >>>> +                mode->hdisplay);
> >>>> +        return -EINVAL;
> >>>> +    } else if (fb->height != mode->vdisplay) {
> >>>> +        DPU_ERROR("invalid fb h=%d, mode h=%d\n", fb->height,
> >>>> +                  mode->vdisplay);
> >>>> +        return -EINVAL;
> >>>> +    } else if (fb->width > DEFAULT_MAX_WRITEBACK_WIDTH) {
> >>>> +        DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
> >>>> +                  fb->width, DEFAULT_MAX_WRITEBACK_WIDTH);
> >>>> +        return -EINVAL;
> >>>> +    }
> >>>
> >>> Being not an expert in the DRM WB API, I have a question: are those
> >>> hardware limitations or the DRM requirement that we can not write to
> >>> the region inside the attached FB?
> >>
> >> Both. The first two checks are indeed DRM requirement and infact igt
> >> writeback tests this to ensure that the driver rejects any invalid FBs
> >> which are not equal to the mode that was set.
> >>
> >> https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tests/kms_writeback.c#L409
> >
> >
> > The test is writing to the fb which is smaller than the used video mode
> > (which is an obvious mistake). Is it correst to write back to the FB
> > that is bigger than the selected resolution?
>
> Ah, so your question is whether writeback can just write to a portion of
> the larger FB.
>
> Yes, its possible . So thats another follow-up feature to support WB_ROI.
>
> WB can be made to program a smaller ROI in the larger buffer. It is
> possible. Just not supported in this version and even downstream even
> though the code is present, its not verified extensively.
>
> When we add support for it, we will relax these conditions based on the
> feature.

Ok, thanks for the info.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

>
> >
> >> The last check is coming from our hardware >
> >>>
> >>>> +
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +
> >>>> +/**
> >>>> + * _dpu_encoder_phys_wb_update_flush - flush hardware update
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void _dpu_encoder_phys_wb_update_flush(struct
> >>>> dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb;
> >>>> +    struct dpu_hw_ctl *hw_ctl;
> >>>> +    struct dpu_hw_pingpong *hw_pp;
> >>>> +    u32 pending_flush = 0;
> >>>> +
> >>>> +    if (!phys_enc)
> >>>> +        return;
> >>>> +
> >>>> +    hw_wb = phys_enc->hw_wb;
> >>>> +    hw_pp = phys_enc->hw_pp;
> >>>> +    hw_ctl = phys_enc->hw_ctl;
> >>>> +
> >>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> >>>> +
> >>>> +    if (!hw_ctl) {
> >>>> +        DPU_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    if (hw_ctl->ops.update_pending_flush_wb)
> >>>> +        hw_ctl->ops.update_pending_flush_wb(hw_ctl, hw_wb->idx);
> >>>> +
> >>>> +    if (hw_ctl->ops.update_pending_flush_merge_3d && hw_pp &&
> >>>> hw_pp->merge_3d)
> >>>> +        hw_ctl->ops.update_pending_flush_merge_3d(hw_ctl,
> >>>> +                hw_pp->merge_3d->idx);
> >>>> +
> >>>> +    if (hw_ctl->ops.get_pending_flush)
> >>>> +        pending_flush = hw_ctl->ops.get_pending_flush(hw_ctl);
> >>>> +
> >>>> +    DPU_DEBUG("Pending flush mask for CTL_%d is 0x%x, WB %d\n",
> >>>> +            hw_ctl->idx - CTL_0, pending_flush,
> >>>> +            hw_wb->idx - WB_0);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_setup - setup writeback encoder
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_setup(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> >>>> +    struct drm_display_mode mode = phys_enc->cached_mode;
> >>>> +    struct drm_framebuffer *fb = NULL;
> >>>> +
> >>>> +    DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
> >>>> +            hw_wb->idx - WB_0, mode.name,
> >>>> +            mode.hdisplay, mode.vdisplay);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_set_ot_limit(phys_enc);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_set_qos_remap(phys_enc);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_set_qos(phys_enc);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_setup_cdp(phys_enc);
> >>>> +
> >>>> +}
> >>>> +
> >>>> +static void _dpu_encoder_phys_wb_frame_done_helper(void *arg)
> >>>> +{
> >>>> +    struct dpu_encoder_phys *phys_enc = arg;
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +
> >>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> >>>> +    unsigned long lock_flags;
> >>>> +    u32 event = DPU_ENCODER_FRAME_EVENT_DONE;
> >>>> +
> >>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> >>>> +
> >>>> +    if (phys_enc->parent_ops->handle_frame_done)
> >>>> +        phys_enc->parent_ops->handle_frame_done(phys_enc->parent,
> >>>> +                phys_enc, event);
> >>>> +
> >>>> +    if (phys_enc->parent_ops->handle_vblank_virt)
> >>>> +        phys_enc->parent_ops->handle_vblank_virt(phys_enc->parent,
> >>>> +                phys_enc);
> >>>> +
> >>>> +    spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
> >>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
> >>>> +    spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
> >>>> +
> >>>> +    if (wb_enc->wb_conn)
> >>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
> >>>> +
> >>>> +    /* Signal any waiting atomic commit thread */
> >>>> +    wake_up_all(&phys_enc->pending_kickoff_wq);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_done_irq - writeback interrupt handler
> >>>> + * @arg:    Pointer to writeback encoder
> >>>> + * @irq_idx:    interrupt index
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_done_irq(void *arg, int irq_idx)
> >>>> +{
> >>>> +    _dpu_encoder_phys_wb_frame_done_helper(arg);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_irq_ctrl - irq control of WB
> >>>> + * @phys:    Pointer to physical encoder
> >>>> + * @enable:    indicates enable or disable interrupts
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_irq_ctrl(
> >>>> +        struct dpu_encoder_phys *phys, bool enable)
> >>>> +{
> >>>> +
> >>>> +    struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys);
> >>>> +    int ret = 0;
> >>>> +    int refcount;
> >>>> +
> >>>> +    refcount = atomic_read(&wb_enc->wbirq_refcount);
> >>>> +
> >>>> +    if (enable && atomic_inc_return(&wb_enc->wbirq_refcount) == 1) {
> >>>> +        dpu_core_irq_register_callback(phys->dpu_kms,
> >>>> +                phys->irq[INTR_IDX_WB_DONE],
> >>>> dpu_encoder_phys_wb_done_irq, phys);
> >>>> +        if (ret)
> >>>> +            atomic_dec_return(&wb_enc->wbirq_refcount);
> >>>> +    } else if (!enable &&
> >>>> +            atomic_dec_return(&wb_enc->wbirq_refcount) == 0) {
> >>>> +        dpu_core_irq_unregister_callback(phys->dpu_kms,
> >>>> phys->irq[INTR_IDX_WB_DONE]);
> >>>> +        if (ret)
> >>>> +            atomic_inc_return(&wb_enc->wbirq_refcount);
> >>>> +    }
> >>>> +}
> >>>> +
> >>>> +static void dpu_encoder_phys_wb_atomic_mode_set(
> >>>> +        struct dpu_encoder_phys *phys_enc,
> >>>> +        struct drm_crtc_state *crtc_state,
> >>>> +        struct drm_connector_state *conn_state)
> >>>> +{
> >>>> +
> >>>> +    phys_enc->irq[INTR_IDX_WB_DONE] =
> >>>> phys_enc->hw_wb->caps->intr_wb_done;
> >>>> +}
> >>>> +
> >>>> +static void _dpu_encoder_phys_wb_handle_wbdone_timeout(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +    u32 frame_event = DPU_ENCODER_FRAME_EVENT_ERROR;
> >>>> +
> >>>> +    wb_enc->wb_done_timeout_cnt++;
> >>>> +
> >>>> +    if (wb_enc->wb_done_timeout_cnt == 1)
> >>>> +        msm_disp_snapshot_state(phys_enc->parent->dev);
> >>>> +
> >>>> +    atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
> >>>> +
> >>>> +    /* request a ctl reset before the next kickoff */
> >>>> +    phys_enc->enable_state = DPU_ENC_ERR_NEEDS_HW_RESET;
> >>>> +
> >>>> +    if (wb_enc->wb_conn)
> >>>> +        drm_writeback_signal_completion(wb_enc->wb_conn, 0);
> >>>> +
> >>>> +    if (phys_enc->parent_ops->handle_frame_done)
> >>>> +        phys_enc->parent_ops->handle_frame_done(
> >>>> +                phys_enc->parent, phys_enc, frame_event);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_wait_for_commit_done - wait until request is
> >>>> committed
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static int dpu_encoder_phys_wb_wait_for_commit_done(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    unsigned long ret;
> >>>> +    struct dpu_encoder_wait_info wait_info;
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +
> >>>> +    wait_info.wq = &phys_enc->pending_kickoff_wq;
> >>>> +    wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
> >>>> +    wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
> >>>> +
> >>>> +    ret = dpu_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
> >>>> +            dpu_encoder_phys_wb_done_irq, &wait_info);
> >>>> +    if (ret == -ETIMEDOUT)
> >>>> +        _dpu_encoder_phys_wb_handle_wbdone_timeout(phys_enc);
> >>>> +    else if (!ret)
> >>>> +        wb_enc->wb_done_timeout_cnt = 0;
> >>>> +
> >>>> +    return ret;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + * Returns:    Zero on success
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_prepare_for_kickoff(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +    struct drm_connector *drm_conn;
> >>>> +    struct drm_connector_state *state;
> >>>> +
> >>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> >>>> +
> >>>> +    if (!wb_enc->wb_conn || !wb_enc->wb_job) {
> >>>> +        DPU_ERROR("invalid wb_conn or wb_job\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    drm_conn = &wb_enc->wb_conn->base;
> >>>> +    state = drm_conn->state;
> >>>> +
> >>>> +    if (wb_enc->wb_conn && wb_enc->wb_job)
> >>>> +        drm_writeback_queue_job(wb_enc->wb_conn, state);
> >>>> +
> >>>> +    dpu_encoder_phys_wb_setup(phys_enc);
> >>>> +
> >>>> +    _dpu_encoder_phys_wb_update_flush(phys_enc);
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_needs_single_flush - trigger flush processing
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static bool dpu_encoder_phys_wb_needs_single_flush(struct
> >>>> dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> >>>> +    return false;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_handle_post_kickoff(
> >>>> +        struct dpu_encoder_phys *phys_enc)
> >>>> +{
> >>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> >>>> +
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_enable - enable writeback encoder
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_enable(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->hw_wb->idx - WB_0);
> >>>> +    phys_enc->enable_state = DPU_ENC_ENABLED;
> >>>> +}
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_disable - disable writeback encoder
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_disable(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
> >>>> +    struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
> >>>> +
> >>>> +    DPU_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
> >>>> +
> >>>> +    if (phys_enc->enable_state == DPU_ENC_DISABLED) {
> >>>> +        DPU_ERROR("encoder is already disabled\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    /* reset h/w before final flush */
> >>>> +    if (phys_enc->hw_ctl->ops.clear_pending_flush)
> >>>> +        phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
> >>>> +
> >>>> +    /*
> >>>> +     * New CTL reset sequence from 5.0 MDP onwards.
> >>>> +     * If has_3d_merge_reset is not set, legacy reset
> >>>> +     * sequence is executed.
> >>>> +     *
> >>>> +     * Legacy reset sequence has not been implemented yet.
> >>>> +     * Any target earlier than SM8150 will need it and when
> >>>> +     * WB support is added to those targets will need to add
> >>>> +     * the legacy teardown sequence as well.
> >>>> +     */
> >>>> +    if (hw_ctl->caps->features & BIT(DPU_CTL_ACTIVE_CFG))
> >>>> +        dpu_encoder_helper_phys_cleanup(phys_enc);
> >>>> +
> >>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_destroy - destroy writeback encoder
> >>>> + * @phys_enc:    Pointer to physical encoder
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_destroy(struct dpu_encoder_phys
> >>>> *phys_enc)
> >>>> +{
> >>>> +    DPU_DEBUG("[wb:%d]\n", phys_enc->intf_idx - INTF_0);
> >>>> +
> >>>> +    if (!phys_enc)
> >>>> +        return;
> >>>> +
> >>>> +    kfree(phys_enc);
> >>>> +}
> >>>> +
> >>>> +static void dpu_encoder_phys_wb_prepare_wb_job(struct
> >>>> dpu_encoder_phys *phys_enc,
> >>>> +        struct drm_writeback_job *job)
> >>>> +{
> >>>> +    const struct msm_format *format;
> >>>> +    struct msm_gem_address_space *aspace;
> >>>> +    struct dpu_hw_wb_cfg *wb_cfg;
> >>>> +    int ret;
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +
> >>>> +    if (!job->fb)
> >>>> +        return;
> >>>> +
> >>>> +    wb_enc->wb_job = job;
> >>>> +    wb_enc->wb_conn = job->connector;
> >>>> +    aspace = phys_enc->dpu_kms->base.aspace;
> >>>> +
> >>>> +    wb_cfg = &wb_enc->wb_cfg;
> >>>> +
> >>>> +    memset(wb_cfg, 0, sizeof(struct dpu_hw_wb_cfg));
> >>>> +
> >>>> +    ret = msm_framebuffer_prepare(job->fb, aspace, false);
> >>>> +    if (ret) {
> >>>> +        DPU_ERROR("prep fb failed, %d\n", ret);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    format = msm_framebuffer_format(job->fb);
> >>>> +
> >>>> +    wb_cfg->dest.format = dpu_get_dpu_format_ext(
> >>>> +            format->pixel_format, job->fb->modifier);
> >>>> +    if (!wb_cfg->dest.format) {
> >>>> +        /* this error should be detected during atomic_check */
> >>>> +        DPU_ERROR("failed to get format %x\n", format->pixel_format);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
> >>>> +    if (ret) {
> >>>> +        DPU_DEBUG("failed to populate layout %d\n", ret);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    wb_cfg->dest.width = job->fb->width;
> >>>> +    wb_cfg->dest.height = job->fb->height;
> >>>> +    wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
> >>>> +
> >>>> +    if ((wb_cfg->dest.format->fetch_planes == DPU_PLANE_PLANAR) &&
> >>>> +            (wb_cfg->dest.format->element[0] == C1_B_Cb))
> >>>> +        swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
> >>>> +
> >>>> +    DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",
> >>>> +            wb_cfg->dest.plane_addr[0], wb_cfg->dest.plane_addr[1],
> >>>> +            wb_cfg->dest.plane_addr[2], wb_cfg->dest.plane_addr[3]);
> >>>> +
> >>>> +    DPU_DEBUG("[fb_stride:%8.8x,%8.8x,%8.8x,%8.8x]\n",
> >>>> +            wb_cfg->dest.plane_pitch[0], wb_cfg->dest.plane_pitch[1],
> >>>> +            wb_cfg->dest.plane_pitch[2], wb_cfg->dest.plane_pitch[3]);
> >>>> +}
> >>>> +
> >>>> +static void dpu_encoder_phys_wb_cleanup_wb_job(struct
> >>>> dpu_encoder_phys *phys_enc,
> >>>> +        struct drm_writeback_job *job)
> >>>> +{
> >>>> +    struct dpu_encoder_phys_wb *wb_enc =
> >>>> to_dpu_encoder_phys_wb(phys_enc);
> >>>> +    struct msm_gem_address_space *aspace;
> >>>> +
> >>>> +    if (!job->fb)
> >>>> +        return;
> >>>> +
> >>>> +    aspace = phys_enc->dpu_kms->base.aspace;
> >>>> +
> >>>> +    msm_framebuffer_cleanup(job->fb, aspace, false);
> >>>> +    wb_enc->wb_job = NULL;
> >>>> +    wb_enc->wb_conn = NULL;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_init_ops - initialize writeback operations
> >>>> + * @ops:    Pointer to encoder operation table
> >>>> + */
> >>>> +static void dpu_encoder_phys_wb_init_ops(struct
> >>>> dpu_encoder_phys_ops *ops)
> >>>> +{
> >>>> +    ops->is_master = dpu_encoder_phys_wb_is_master;
> >>>> +    ops->atomic_mode_set = dpu_encoder_phys_wb_atomic_mode_set;
> >>>> +    ops->enable = dpu_encoder_phys_wb_enable;
> >>>> +    ops->disable = dpu_encoder_phys_wb_disable;
> >>>> +    ops->destroy = dpu_encoder_phys_wb_destroy;
> >>>> +    ops->atomic_check = dpu_encoder_phys_wb_atomic_check;
> >>>> +    ops->wait_for_commit_done =
> >>>> dpu_encoder_phys_wb_wait_for_commit_done;
> >>>> +    ops->prepare_for_kickoff =
> >>>> dpu_encoder_phys_wb_prepare_for_kickoff;
> >>>> +    ops->handle_post_kickoff =
> >>>> dpu_encoder_phys_wb_handle_post_kickoff;
> >>>> +    ops->needs_single_flush = dpu_encoder_phys_wb_needs_single_flush;
> >>>> +    ops->trigger_start = dpu_encoder_helper_trigger_start;
> >>>> +    ops->prepare_wb_job = dpu_encoder_phys_wb_prepare_wb_job;
> >>>> +    ops->cleanup_wb_job = dpu_encoder_phys_wb_cleanup_wb_job;
> >>>> +    ops->irq_control = dpu_encoder_phys_wb_irq_ctrl;
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * dpu_encoder_phys_wb_init - initialize writeback encoder
> >>>> + * @init:    Pointer to init info structure with initialization params
> >>>> + */
> >>>> +struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
> >>>> +        struct dpu_enc_phys_init_params *p)
> >>>> +{
> >>>> +    struct dpu_encoder_phys *phys_enc = NULL;
> >>>> +    struct dpu_encoder_phys_wb *wb_enc = NULL;
> >>>> +    int ret = 0;
> >>>> +    int i;
> >>>> +
> >>>> +    DPU_DEBUG("\n");
> >>>> +
> >>>> +    if (!p || !p->parent) {
> >>>> +        DPU_ERROR("invalid params\n");
> >>>> +        ret = -EINVAL;
> >>>> +        goto fail_alloc;
> >>>> +    }
> >>>> +
> >>>> +    wb_enc = kzalloc(sizeof(*wb_enc), GFP_KERNEL);
> >>>> +    if (!wb_enc) {
> >>>> +        DPU_ERROR("failed to allocate wb phys_enc enc\n");
> >>>> +        ret = -ENOMEM;
> >>>> +        goto fail_alloc;
> >>>> +    }
> >>>> +
> >>>> +    phys_enc = &wb_enc->base;
> >>>> +    phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
> >>>> +    phys_enc->intf_idx = p->intf_idx;
> >>>> +
> >>>> +    dpu_encoder_phys_wb_init_ops(&phys_enc->ops);
> >>>> +    phys_enc->parent = p->parent;
> >>>> +    phys_enc->parent_ops = p->parent_ops;
> >>>> +    phys_enc->dpu_kms = p->dpu_kms;
> >>>> +    phys_enc->split_role = p->split_role;
> >>>> +    phys_enc->intf_mode = INTF_MODE_WB_LINE;
> >>>> +    phys_enc->intf_idx = p->intf_idx;
> >>>> +    phys_enc->enc_spinlock = p->enc_spinlock;
> >>>> +
> >>>> +    atomic_set(&wb_enc->wbirq_refcount, 0);
> >>>> +
> >>>> +    for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
> >>>> +        phys_enc->irq[i] = -EINVAL;
> >>>> +
> >>>> +    atomic_set(&phys_enc->pending_kickoff_cnt, 0);
> >>>> +    atomic_set(&phys_enc->vblank_refcount, 0);
> >>>> +    wb_enc->wb_done_timeout_cnt = 0;
> >>>> +
> >>>> +    init_waitqueue_head(&phys_enc->pending_kickoff_wq);
> >>>> +    phys_enc->enable_state = DPU_ENC_DISABLED;
> >>>> +
> >>>> +    DPU_DEBUG("Created dpu_encoder_phys for wb %d\n",
> >>>> +            phys_enc->intf_idx);
> >>>> +
> >>>> +    return phys_enc;
> >>>> +
> >>>> +fail_alloc:
> >>>> +    return ERR_PTR(ret);
> >>>> +}
> >>>
> >>>
> >
> >



-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks
  2022-04-20  7:23   ` Dmitry Baryshkov
@ 2022-04-20 21:28     ` Abhinav Kumar
  2022-04-20 22:42       ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 21:28 UTC (permalink / raw)
  To: Dmitry Baryshkov, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh



On 4/20/2022 12:23 AM, Dmitry Baryshkov wrote:
> On 20/04/2022 04:46, Abhinav Kumar wrote:
>> Add an API to reset the encoder related hw blocks to ensure
>> a proper teardown of the pipeline. At the moment this is being
>> used only for the writeback encoder but eventually we can start
>> using this for all interfaces.
>>
>> changes in v2:
>>     - split the writeback part to another commit
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> 
> A minor question: do we need to also reset the DSPP alongside resetting 
> the LM?

So this function is mostly doing what the downstream equivalent of it 
does which is to disable all the sspp blend stages, the ping-pong 
binding and 3d-merge connection.

For DSPP, there is no ability to *bind* it or connect it. Its a 
hard-wired connection. Its just a question of whether to enable it or not.

When the CTL path connections are removed, there is no need to 
explicitly disable the DSPP.

Thats why even downstream doesnt do it today.

> 
>> ---
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 82 
>> ++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  7 ++
>>   2 files changed, 89 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> index 4523693..0e31ad3 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>> @@ -1,5 +1,6 @@
>>   // SPDX-License-Identifier: GPL-2.0-only
>>   /*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>>    * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All 
>> rights reserved.
>>    * Copyright (C) 2013 Red Hat
>>    * Author: Rob Clark <robdclark@gmail.com>
>> @@ -22,6 +23,7 @@
>>   #include "dpu_hw_ctl.h"
>>   #include "dpu_hw_dspp.h"
>>   #include "dpu_hw_dsc.h"
>> +#include "dpu_hw_merge3d.h"
>>   #include "dpu_formats.h"
>>   #include "dpu_encoder_phys.h"
>>   #include "dpu_crtc.h"
>> @@ -1838,6 +1840,86 @@ void dpu_encoder_kickoff(struct drm_encoder 
>> *drm_enc)
>>       DPU_ATRACE_END("encoder_kickoff");
>>   }
>> +static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys 
>> *phys_enc)
>> +{
>> +    struct dpu_hw_mixer_cfg mixer;
>> +    int i, num_lm;
>> +    u32 flush_mask = 0;
>> +    struct dpu_global_state *global_state;
>> +    struct dpu_hw_blk *hw_lm[2];
>> +    struct dpu_hw_mixer *hw_mixer[2];
>> +    struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
>> +
>> +    memset(&mixer, 0, sizeof(mixer));
>> +
>> +    /* reset all mixers for this encoder */
>> +    if (phys_enc->hw_ctl->ops.clear_all_blendstages)
>> +        phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
>> +
>> +    global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
>> +
>> +    num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, 
>> global_state,
>> +        phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, 
>> ARRAY_SIZE(hw_lm));
>> +
>> +    for (i = 0; i < num_lm; i++) {
>> +        hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
>> +        flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, 
>> hw_mixer[i]->idx);
>> +        if (phys_enc->hw_ctl->ops.update_pending_flush)
>> +            phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask);
>> +
>> +        /* clear all blendstages */
>> +        if (phys_enc->hw_ctl->ops.setup_blendstage)
>> +            phys_enc->hw_ctl->ops.setup_blendstage(ctl, 
>> hw_mixer[i]->idx, NULL);
>> +    }
>> +}
>> +
>> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
>> +{
>> +    struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
>> +    struct dpu_hw_intf_cfg intf_cfg = { 0 };
>> +    int i;
>> +    struct dpu_encoder_virt *dpu_enc;
>> +
>> +    dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
>> +
>> +    phys_enc->hw_ctl->ops.reset(ctl);
>> +
>> +    dpu_encoder_helper_reset_mixers(phys_enc);
>> +
>> +    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>> +        if (dpu_enc->phys_encs[i] && 
>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>> +            phys_enc->hw_intf->ops.bind_pingpong_blk(
>> +                    dpu_enc->phys_encs[i]->hw_intf, false,
>> +                    dpu_enc->phys_encs[i]->hw_pp->idx);
>> +
>> +        /* mark INTF flush as pending */
>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>> +            
>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>> +                    dpu_enc->phys_encs[i]->hw_intf->idx);
>> +    }
>> +
>> +    /* reset the merge 3D HW block */
>> +    if (phys_enc->hw_pp->merge_3d) {
>> +        
>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
>> +                BLEND_3D_NONE);
>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d)
>> +            phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl,
>> +                    phys_enc->hw_pp->merge_3d->idx);
>> +    }
>> +
>> +    intf_cfg.stream_sel = 0; /* Don't care value for video mode */
>> +    intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>> +    if (phys_enc->hw_pp->merge_3d)
>> +        intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
>> +
>> +    if (ctl->ops.reset_intf_cfg)
>> +        ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
>> +
>> +    ctl->ops.trigger_flush(ctl);
>> +    ctl->ops.trigger_start(ctl);
>> +    ctl->ops.clear_pending_flush(ctl);
>> +}
>> +
>>   void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc)
>>   {
>>       struct dpu_encoder_virt *dpu_enc;
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> index 706b566..544a9a4 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>> @@ -1,5 +1,6 @@
>>   /* SPDX-License-Identifier: GPL-2.0-only */
>>   /*
>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>> reserved.
>>    * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
>>    */
>> @@ -350,4 +351,10 @@ int dpu_encoder_helper_wait_for_irq(struct 
>> dpu_encoder_phys *phys_enc,
>>           void (*func)(void *arg, int irq_idx),
>>           struct dpu_encoder_wait_info *wait_info);
>> +/**
>> + * dpu_encoder_helper_phys_cleanup - helper to cleanup dpu pipeline
>> + * @phys_enc: Pointer to physical encoder structure
>> + */
>> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc);
>> +
>>   #endif /* __dpu_encoder_phys_H__ */
> 
> 

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

* Re: [Freedreno] [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20 18:46         ` [Freedreno] " Abhinav Kumar
@ 2022-04-20 22:06           ` Abhinav Kumar
  2022-04-20 22:38             ` Dmitry Baryshkov
  0 siblings, 1 reply; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-20 22:06 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

Hi Dmitry

On 4/20/2022 11:46 AM, Abhinav Kumar wrote:
> 
> 
> On 4/20/2022 11:37 AM, Dmitry Baryshkov wrote:
>> On Wed, 20 Apr 2022 at 20:41, Abhinav Kumar 
>> <quic_abhinavk@quicinc.com> wrote:
>>>
>>>
>>>
>>> On 4/20/2022 12:44 AM, Dmitry Baryshkov wrote:
>>>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>>>> Make changes to dpu_encoder to support virtual encoder needed
>>>>> to support writeback for dpu.
>>>>>
>>>>> changes in v2:
>>>>>      - add the writeback parts to dpu_encoder_helper_phys_cleanup
>>>>>      - rebase on tip of msm-next and fix related dependencies
>>>>>      - get the writeback blocks directly from RM
>>>>>
>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>> ---
>>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71
>>>>> +++++++++++++++++-------
>>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
>>>>>    2 files changed, 54 insertions(+), 20 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>> index 0e31ad3..06b8631 100644
>>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>> @@ -928,6 +928,7 @@ static void
>>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>>        struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
>>>>>        int num_lm, num_ctl, num_pp, num_dsc;
>>>>>        unsigned int dsc_mask = 0;
>>>>> +    enum dpu_hw_blk_type blk_type;
>>>>>        int i;
>>>>>        if (!drm_enc) {
>>>>> @@ -1009,12 +1010,21 @@ static void
>>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>>            phys->hw_pp = dpu_enc->hw_pp[i];
>>>>>            phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>>>>> -        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
>>>>> -            phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>>> phys->intf_idx);
>>>>> +        if (dpu_encoder_get_intf_mode(&dpu_enc->base) ==
>>>>> INTF_MODE_WB_LINE)
>>>>> +            blk_type = DPU_HW_BLK_WB;
>>>>> +        else
>>>>> +            blk_type = DPU_HW_BLK_INTF;
>>>>> +
>>>>> +        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
>>>>> +            if (blk_type == DPU_HW_BLK_INTF)
>>>>> +                phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>>> phys->intf_idx);
>>>>> +            else if (blk_type == DPU_HW_BLK_WB)
>>>>> +                phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm,
>>>>> phys->intf_idx);
>>>>> +        }
>>>>> -        if (!phys->hw_intf) {
>>>>> +        if (!phys->hw_intf && !phys->hw_wb) {
>>>>>                DPU_ERROR_ENC(dpu_enc,
>>>>> -                      "no intf block assigned at idx: %d\n", i);
>>>>> +                      "no intf ow wb block assigned at idx: %d\n", 
>>>>> i);
>>>>
>>>> or wb
>>> ack
>>>>
>>>>>                return;
>>>>>            }
>>>>> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct
>>>>> drm_encoder *drm_enc)
>>>>>        mutex_unlock(&dpu_enc->enc_lock);
>>>>>    }
>>>>> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg 
>>>>> *catalog,
>>>>> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg
>>>>> *catalog,
>>>>>            enum dpu_intf_type type, u32 controller_id)
>>>>>    {
>>>>>        int i = 0;
>>>>> -    for (i = 0; i < catalog->intf_count; i++) {
>>>>> -        if (catalog->intf[i].type == type
>>>>> -            && catalog->intf[i].controller_id == controller_id) {
>>>>> -            return catalog->intf[i].id;
>>>>> +    if (type != INTF_WB) {
>>>>> +        for (i = 0; i < catalog->intf_count; i++) {
>>>>> +            if (catalog->intf[i].type == type
>>>>> +                && catalog->intf[i].controller_id == controller_id) {
>>>>> +                return catalog->intf[i].id;
>>>>> +            }
>>>>> +        }
>>>>> +    } else {
>>>>> +        for (i = 0; i < catalog->wb_count; i++) {
>>>>> +            if (catalog->wb[i].id == controller_id)
>>>>> +                return catalog->wb[i].id;
>>>>>            }
>>>>>        }
>>>>> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct
>>>>> dpu_encoder_phys *phys_enc)
>>>>>        dpu_encoder_helper_reset_mixers(phys_enc);
>>>>> -    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>>> -        if (dpu_enc->phys_encs[i] &&
>>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>>> -            phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>>> -                    dpu_enc->phys_encs[i]->hw_intf, false,
>>>>> -                    dpu_enc->phys_encs[i]->hw_pp->idx);
>>>>> +    if (phys_enc->hw_wb) {
>>>>
>>>> I think this adds a hidden knowledge here. That there is always just a
>>>> single phys_enc for the WB encoder. I'd still do this cleanup in a loop
>>>> together with the INTF cleanup.
>>> alright, I can make this change.

I dug into the history of dpu_encoder_helper_phys_cleanup() API more in 
downstream.

So this API seems to be a bit poorly designed in downstream too.

This gets called from phys->ops.disable() today which is anyway called 
from within a loop.

  for (i = 0; i < dpu_enc->num_phys_encs; i++) {
         struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];

         if (phys->ops.disable)
             phys->ops.disable(phys);
     }

So technically we dont need a loop even for the intf cases because of that.

But, while calling for interfaces, downstream makes sure to call this 
just once for the master and skip for the same.

Here the loop is needed just to set the flush bits even for the slave 
and let the master do the actual flush.

Since today, we are not calling this for interface yet, I think i can 
get rid of the loop even for the interface case for uniformity.

When I extend this API to be called for interface cases, we can decide 
how to handle master/slave cases.

Let me know what you think.

>>>>
>>>>> +        /* disable the PP block */
>>>>> +        if (phys_enc->hw_wb->ops.bind_pingpong_blk)
>>>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb,
>>>>> false,
>>>>> +                    phys_enc->hw_pp->idx);
>>>>> -        /* mark INTF flush as pending */
>>>>> -        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>>> -
>>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>>> -                    dpu_enc->phys_encs[i]->hw_intf->idx);
>>>>> +        /* mark WB flush as pending */
>>>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
>>>>> +            phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl,
>>>>> phys_enc->hw_wb->idx);
>>>>> +    } else {
>>>>> +        for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>>> +            if (dpu_enc->phys_encs[i] &&
>>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>>> +                phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>>> +                        dpu_enc->phys_encs[i]->hw_intf, false,
>>>>> +                        dpu_enc->phys_encs[i]->hw_pp->idx);
>>>>> +
>>>>> +            /* mark INTF flush as pending */
>>>>> +            if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>>> +
>>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>>> +                        dpu_enc->phys_encs[i]->hw_intf->idx);
>>>>> +        }
>>>>>        }
>>>>>        /* reset the merge 3D HW block */
>>>>> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct
>>>>> dpu_encoder_virt *dpu_enc,
>>>>>        case DRM_MODE_ENCODER_TMDS:
>>>>>            intf_type = INTF_DP;
>>>>>            break;
>>>>> +    case DRM_MODE_ENCODER_VIRTUAL:
>>>>> +        intf_type = INTF_WB;
>>>>> +        break;
>>>>>        }
>>>>>        WARN_ON(disp_info->num_of_h_tiles < 1);
>>>>> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct
>>>>> dpu_encoder_virt *dpu_enc,
>>>>>            DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>>>>>                    i, controller_id, phys_params.split_role);
>>>>> -        phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog,
>>>>> +        phys_params.intf_idx =
>>>>> dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
>>>>>                                                        intf_type,
>>>>>                                                        controller_id);
>>>>
>>>> I'd prefer to be more explicit here. intf_idx is of type enum dpu_intf,
>>>> while WBs are enumerated with enum dpu_wb.
>>>>
>>>> I's suggest adding a separate phys_params.wb_idx and a
>>>> dpu_encoder_get_wb() and calling one here depending on intf_type.
>>>>
>>>> Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check
>>>> intf_mode, but get both intf and wb and report an error if none was
>>>> provided.
>>>
>>> Your suggestion is valid and I also thought about it.
>>>
>>> Let me explain what prevented me from making the change here and put it
>>> in a to-do bucket.
>>>
>>> 1) This needs a slighly bigger cleanup including the traces, debug
>>> prints and some helpers as none of them are aware of the wb_idx
>>>
>>> 2) Some of the checks need to be adjusted like this one
>>>
>>> if (phys_params.intf_idx == INTF_MAX) {
>>>               DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type %d,
>>> id %d\n",
>>>                             intf_type, controller_id);
>>>               ret = -EINVAL;
>>>           }
>>>
>>> So, when we have a separate intf_idx and a wb_idx, having either one of
>>> them is enough . Consider a case where a board has no physical display.
>>> That would have no intf_idx but wb_idx can be valid.
>>>
>>> So i think this needs a little careful cleanup.
>>>
>>> Considering that I need to test that out more, I decided that for a
>>> short-term we can live with the fact that wb_idx is of type enum
>>> dpu_intf because dpu_encoder layer understands only that.
>>>
>>> Let me know if you agree on this separation to go as a follow-up.
>>
>> Actually both your points vote for the immediate separation rather
>> than doing it in a follow-up.
>> For example, if you see an error reported against INTF_2 on a board
>> where INTF_2 is actually used, you can not determine whether it is an
>> issue with INTF_2 or with WB_2.
>>
>> In fact even reporting an error (or a trace) against INTF_NONE (or
>> INTF_MAX) might be a better option. It would clearly denote that the
>> issue is related to the non-INTF.
>>
>> I think we can merge the patch as is, but I'd strongly prefer either
>> to see an update or a (nearly) immediate followup.
>>
>> What do you think about an interim solution? We split the
>> intf_idx/wb_idx in these series, but all the debugging can be updated
>> later. This way once we see the report against INTF_NONE, we can
>> deduce that it's WB.
> 
> Thank you for your consideration.
> 
> I will post a follow-up change to fix this no later than next week.
> I will even try doing it this week itself.
> 
> I will fix everything together so that it looks like a separate series 
> of "separating intf_idx and wb_idx" and there is no intermediate mismatch.
> 
> Will leave a FIXME here as well.
> 
>>
>>>>
>>>>>            if (phys_params.intf_idx == INTF_MAX) {
>>>>> -            DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id
>>>>> %d\n",
>>>>> +            DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type
>>>>> %d, id %d\n",
>>>>>                              intf_type, controller_id);
>>>>>                ret = -EINVAL;
>>>>>            }
>>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>> index 544a9a4..0b80af4 100644
>>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>> @@ -11,6 +11,7 @@
>>>>>    #include "dpu_kms.h"
>>>>>    #include "dpu_hw_intf.h"
>>>>> +#include "dpu_hw_wb.h"
>>>>>    #include "dpu_hw_pingpong.h"
>>>>>    #include "dpu_hw_ctl.h"
>>>>>    #include "dpu_hw_top.h"
>>>>> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
>>>>>     * @hw_ctl:        Hardware interface to the ctl registers
>>>>>     * @hw_pp:        Hardware interface to the ping pong registers
>>>>>     * @hw_intf:        Hardware interface to the intf registers
>>>>> + * @hw_wb:        Hardware interface to the wb registers
>>>>>     * @dpu_kms:        Pointer to the dpu_kms top level
>>>>>     * @cached_mode:    DRM mode cached at mode_set time, acted on in
>>>>> enable
>>>>>     * @enabled:        Whether the encoder has enabled and running 
>>>>> a mode
>>>>> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
>>>>>        struct dpu_hw_ctl *hw_ctl;
>>>>>        struct dpu_hw_pingpong *hw_pp;
>>>>>        struct dpu_hw_intf *hw_intf;
>>>>> +    struct dpu_hw_wb *hw_wb;
>>>>>        struct dpu_kms *dpu_kms;
>>>>>        struct drm_display_mode cached_mode;
>>>>>        enum dpu_enc_split_role split_role;
>>>>
>>>>
>>
>>
>>

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

* Re: [Freedreno] [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
  2022-04-20 22:06           ` Abhinav Kumar
@ 2022-04-20 22:38             ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 22:38 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh, freedreno

On 21/04/2022 01:06, Abhinav Kumar wrote:
> Hi Dmitry
> 
> On 4/20/2022 11:46 AM, Abhinav Kumar wrote:
>>
>>
>> On 4/20/2022 11:37 AM, Dmitry Baryshkov wrote:
>>> On Wed, 20 Apr 2022 at 20:41, Abhinav Kumar 
>>> <quic_abhinavk@quicinc.com> wrote:
>>>>
>>>>
>>>>
>>>> On 4/20/2022 12:44 AM, Dmitry Baryshkov wrote:
>>>>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>>>>> Make changes to dpu_encoder to support virtual encoder needed
>>>>>> to support writeback for dpu.
>>>>>>
>>>>>> changes in v2:
>>>>>>      - add the writeback parts to dpu_encoder_helper_phys_cleanup
>>>>>>      - rebase on tip of msm-next and fix related dependencies
>>>>>>      - get the writeback blocks directly from RM
>>>>>>
>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>> ---
>>>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 71
>>>>>> +++++++++++++++++-------
>>>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  3 +
>>>>>>    2 files changed, 54 insertions(+), 20 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>>> index 0e31ad3..06b8631 100644
>>>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>>>>> @@ -928,6 +928,7 @@ static void
>>>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>>>        struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
>>>>>>        int num_lm, num_ctl, num_pp, num_dsc;
>>>>>>        unsigned int dsc_mask = 0;
>>>>>> +    enum dpu_hw_blk_type blk_type;
>>>>>>        int i;
>>>>>>        if (!drm_enc) {
>>>>>> @@ -1009,12 +1010,21 @@ static void
>>>>>> dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>>>>>>            phys->hw_pp = dpu_enc->hw_pp[i];
>>>>>>            phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>>>>>> -        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
>>>>>> -            phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>>>> phys->intf_idx);
>>>>>> +        if (dpu_encoder_get_intf_mode(&dpu_enc->base) ==
>>>>>> INTF_MODE_WB_LINE)
>>>>>> +            blk_type = DPU_HW_BLK_WB;
>>>>>> +        else
>>>>>> +            blk_type = DPU_HW_BLK_INTF;
>>>>>> +
>>>>>> +        if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX) {
>>>>>> +            if (blk_type == DPU_HW_BLK_INTF)
>>>>>> +                phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm,
>>>>>> phys->intf_idx);
>>>>>> +            else if (blk_type == DPU_HW_BLK_WB)
>>>>>> +                phys->hw_wb = dpu_rm_get_wb(&dpu_kms->rm,
>>>>>> phys->intf_idx);
>>>>>> +        }
>>>>>> -        if (!phys->hw_intf) {
>>>>>> +        if (!phys->hw_intf && !phys->hw_wb) {
>>>>>>                DPU_ERROR_ENC(dpu_enc,
>>>>>> -                      "no intf block assigned at idx: %d\n", i);
>>>>>> +                      "no intf ow wb block assigned at idx: 
>>>>>> %d\n", i);
>>>>>
>>>>> or wb
>>>> ack
>>>>>
>>>>>>                return;
>>>>>>            }
>>>>>> @@ -1157,15 +1167,22 @@ static void dpu_encoder_virt_disable(struct
>>>>>> drm_encoder *drm_enc)
>>>>>>        mutex_unlock(&dpu_enc->enc_lock);
>>>>>>    }
>>>>>> -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg 
>>>>>> *catalog,
>>>>>> +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg
>>>>>> *catalog,
>>>>>>            enum dpu_intf_type type, u32 controller_id)
>>>>>>    {
>>>>>>        int i = 0;
>>>>>> -    for (i = 0; i < catalog->intf_count; i++) {
>>>>>> -        if (catalog->intf[i].type == type
>>>>>> -            && catalog->intf[i].controller_id == controller_id) {
>>>>>> -            return catalog->intf[i].id;
>>>>>> +    if (type != INTF_WB) {
>>>>>> +        for (i = 0; i < catalog->intf_count; i++) {
>>>>>> +            if (catalog->intf[i].type == type
>>>>>> +                && catalog->intf[i].controller_id == 
>>>>>> controller_id) {
>>>>>> +                return catalog->intf[i].id;
>>>>>> +            }
>>>>>> +        }
>>>>>> +    } else {
>>>>>> +        for (i = 0; i < catalog->wb_count; i++) {
>>>>>> +            if (catalog->wb[i].id == controller_id)
>>>>>> +                return catalog->wb[i].id;
>>>>>>            }
>>>>>>        }
>>>>>> @@ -1886,16 +1903,27 @@ void dpu_encoder_helper_phys_cleanup(struct
>>>>>> dpu_encoder_phys *phys_enc)
>>>>>>        dpu_encoder_helper_reset_mixers(phys_enc);
>>>>>> -    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>>>> -        if (dpu_enc->phys_encs[i] &&
>>>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>>>> -            phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>>>> -                    dpu_enc->phys_encs[i]->hw_intf, false,
>>>>>> -                    dpu_enc->phys_encs[i]->hw_pp->idx);
>>>>>> +    if (phys_enc->hw_wb) {
>>>>>
>>>>> I think this adds a hidden knowledge here. That there is always just a
>>>>> single phys_enc for the WB encoder. I'd still do this cleanup in a 
>>>>> loop
>>>>> together with the INTF cleanup.
>>>> alright, I can make this change.
> 
> I dug into the history of dpu_encoder_helper_phys_cleanup() API more in 
> downstream.
> 
> So this API seems to be a bit poorly designed in downstream too.
> 
> This gets called from phys->ops.disable() today which is anyway called 
> from within a loop.
> 
>   for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>          struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
> 
>          if (phys->ops.disable)
>              phys->ops.disable(phys);
>      }
> 
> So technically we dont need a loop even for the intf cases because of that.
> 
> But, while calling for interfaces, downstream makes sure to call this 
> just once for the master and skip for the same.
> 
> Here the loop is needed just to set the flush bits even for the slave 
> and let the master do the actual flush.
> 
> Since today, we are not calling this for interface yet, I think i can 
> get rid of the loop even for the interface case for uniformity.
> 
> When I extend this API to be called for interface cases, we can decide 
> how to handle master/slave cases.
> 
> Let me know what you think.

This correspons more or less the the idea that I have been toying for 
quite some time but never had time to finish: a higher level that would 
receive the topology (a list of all LMs, CTLs, INTFs, etc) and wrap 
around all CTL  calls, taking care of ACTIVE vs non-active CTL, single 
flush vs multiple flushes, etc. I postponed this ideas, as I could not 
foresee what would be the requirements of WB support.

In the disable case I think you'd want to call a disable_pipe function 
in a loop and then to flush all the pending changes. I'd prefer to have 
a directly called function rather than a callback.

Does that answer your question?

> 
>>>>>
>>>>>> +        /* disable the PP block */
>>>>>> +        if (phys_enc->hw_wb->ops.bind_pingpong_blk)
>>>>>> +            phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb,
>>>>>> false,
>>>>>> +                    phys_enc->hw_pp->idx);
>>>>>> -        /* mark INTF flush as pending */
>>>>>> -        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>>>> -
>>>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>>>> -                    dpu_enc->phys_encs[i]->hw_intf->idx);
>>>>>> +        /* mark WB flush as pending */
>>>>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_wb)
>>>>>> +            phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl,
>>>>>> phys_enc->hw_wb->idx);
>>>>>> +    } else {
>>>>>> +        for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>>>>> +            if (dpu_enc->phys_encs[i] &&
>>>>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>>>>> +                phys_enc->hw_intf->ops.bind_pingpong_blk(
>>>>>> +                        dpu_enc->phys_encs[i]->hw_intf, false,
>>>>>> +                        dpu_enc->phys_encs[i]->hw_pp->idx);
>>>>>> +
>>>>>> +            /* mark INTF flush as pending */
>>>>>> +            if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>>>>> +
>>>>>> phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>>>>> +                        dpu_enc->phys_encs[i]->hw_intf->idx);
>>>>>> +        }
>>>>>>        }
>>>>>>        /* reset the merge 3D HW block */
>>>>>> @@ -2111,6 +2139,9 @@ static int dpu_encoder_setup_display(struct
>>>>>> dpu_encoder_virt *dpu_enc,
>>>>>>        case DRM_MODE_ENCODER_TMDS:
>>>>>>            intf_type = INTF_DP;
>>>>>>            break;
>>>>>> +    case DRM_MODE_ENCODER_VIRTUAL:
>>>>>> +        intf_type = INTF_WB;
>>>>>> +        break;
>>>>>>        }
>>>>>>        WARN_ON(disp_info->num_of_h_tiles < 1);
>>>>>> @@ -2145,11 +2176,11 @@ static int dpu_encoder_setup_display(struct
>>>>>> dpu_encoder_virt *dpu_enc,
>>>>>>            DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>>>>>>                    i, controller_id, phys_params.split_role);
>>>>>> -        phys_params.intf_idx = 
>>>>>> dpu_encoder_get_intf(dpu_kms->catalog,
>>>>>> +        phys_params.intf_idx =
>>>>>> dpu_encoder_get_intf_or_wb(dpu_kms->catalog,
>>>>>>                                                        intf_type,
>>>>>>                                                        
>>>>>> controller_id);
>>>>>
>>>>> I'd prefer to be more explicit here. intf_idx is of type enum 
>>>>> dpu_intf,
>>>>> while WBs are enumerated with enum dpu_wb.
>>>>>
>>>>> I's suggest adding a separate phys_params.wb_idx and a
>>>>> dpu_encoder_get_wb() and calling one here depending on intf_type.
>>>>>
>>>>> Then at the dpu_encoder_virt_atomic_mode_set() you don't need to check
>>>>> intf_mode, but get both intf and wb and report an error if none was
>>>>> provided.
>>>>
>>>> Your suggestion is valid and I also thought about it.
>>>>
>>>> Let me explain what prevented me from making the change here and put it
>>>> in a to-do bucket.
>>>>
>>>> 1) This needs a slighly bigger cleanup including the traces, debug
>>>> prints and some helpers as none of them are aware of the wb_idx
>>>>
>>>> 2) Some of the checks need to be adjusted like this one
>>>>
>>>> if (phys_params.intf_idx == INTF_MAX) {
>>>>               DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type 
>>>> %d,
>>>> id %d\n",
>>>>                             intf_type, controller_id);
>>>>               ret = -EINVAL;
>>>>           }
>>>>
>>>> So, when we have a separate intf_idx and a wb_idx, having either one of
>>>> them is enough . Consider a case where a board has no physical display.
>>>> That would have no intf_idx but wb_idx can be valid.
>>>>
>>>> So i think this needs a little careful cleanup.
>>>>
>>>> Considering that I need to test that out more, I decided that for a
>>>> short-term we can live with the fact that wb_idx is of type enum
>>>> dpu_intf because dpu_encoder layer understands only that.
>>>>
>>>> Let me know if you agree on this separation to go as a follow-up.
>>>
>>> Actually both your points vote for the immediate separation rather
>>> than doing it in a follow-up.
>>> For example, if you see an error reported against INTF_2 on a board
>>> where INTF_2 is actually used, you can not determine whether it is an
>>> issue with INTF_2 or with WB_2.
>>>
>>> In fact even reporting an error (or a trace) against INTF_NONE (or
>>> INTF_MAX) might be a better option. It would clearly denote that the
>>> issue is related to the non-INTF.
>>>
>>> I think we can merge the patch as is, but I'd strongly prefer either
>>> to see an update or a (nearly) immediate followup.
>>>
>>> What do you think about an interim solution? We split the
>>> intf_idx/wb_idx in these series, but all the debugging can be updated
>>> later. This way once we see the report against INTF_NONE, we can
>>> deduce that it's WB.
>>
>> Thank you for your consideration.
>>
>> I will post a follow-up change to fix this no later than next week.
>> I will even try doing it this week itself.
>>
>> I will fix everything together so that it looks like a separate series 
>> of "separating intf_idx and wb_idx" and there is no intermediate 
>> mismatch.
>>
>> Will leave a FIXME here as well.
>>
>>>
>>>>>
>>>>>>            if (phys_params.intf_idx == INTF_MAX) {
>>>>>> -            DPU_ERROR_ENC(dpu_enc, "could not get intf: type %d, id
>>>>>> %d\n",
>>>>>> +            DPU_ERROR_ENC(dpu_enc, "could not get intf or wb: type
>>>>>> %d, id %d\n",
>>>>>>                              intf_type, controller_id);
>>>>>>                ret = -EINVAL;
>>>>>>            }
>>>>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>>> index 544a9a4..0b80af4 100644
>>>>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>>>>> @@ -11,6 +11,7 @@
>>>>>>    #include "dpu_kms.h"
>>>>>>    #include "dpu_hw_intf.h"
>>>>>> +#include "dpu_hw_wb.h"
>>>>>>    #include "dpu_hw_pingpong.h"
>>>>>>    #include "dpu_hw_ctl.h"
>>>>>>    #include "dpu_hw_top.h"
>>>>>> @@ -165,6 +166,7 @@ enum dpu_intr_idx {
>>>>>>     * @hw_ctl:        Hardware interface to the ctl registers
>>>>>>     * @hw_pp:        Hardware interface to the ping pong registers
>>>>>>     * @hw_intf:        Hardware interface to the intf registers
>>>>>> + * @hw_wb:        Hardware interface to the wb registers
>>>>>>     * @dpu_kms:        Pointer to the dpu_kms top level
>>>>>>     * @cached_mode:    DRM mode cached at mode_set time, acted on in
>>>>>> enable
>>>>>>     * @enabled:        Whether the encoder has enabled and running 
>>>>>> a mode
>>>>>> @@ -193,6 +195,7 @@ struct dpu_encoder_phys {
>>>>>>        struct dpu_hw_ctl *hw_ctl;
>>>>>>        struct dpu_hw_pingpong *hw_pp;
>>>>>>        struct dpu_hw_intf *hw_intf;
>>>>>> +    struct dpu_hw_wb *hw_wb;
>>>>>>        struct dpu_kms *dpu_kms;
>>>>>>        struct drm_display_mode cached_mode;
>>>>>>        enum dpu_enc_split_role split_role;
>>>>>
>>>>>
>>>
>>>
>>>


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks
  2022-04-20 21:28     ` Abhinav Kumar
@ 2022-04-20 22:42       ` Dmitry Baryshkov
  0 siblings, 0 replies; 51+ messages in thread
From: Dmitry Baryshkov @ 2022-04-20 22:42 UTC (permalink / raw)
  To: Abhinav Kumar, freedreno
  Cc: markyacoub, liviu.dudau, dri-devel, swboyd, seanpaul,
	laurent.pinchart, quic_jesszhan, quic_aravindh

On 21/04/2022 00:28, Abhinav Kumar wrote:
> 
> 
> On 4/20/2022 12:23 AM, Dmitry Baryshkov wrote:
>> On 20/04/2022 04:46, Abhinav Kumar wrote:
>>> Add an API to reset the encoder related hw blocks to ensure
>>> a proper teardown of the pipeline. At the moment this is being
>>> used only for the writeback encoder but eventually we can start
>>> using this for all interfaces.
>>>
>>> changes in v2:
>>>     - split the writeback part to another commit
>>>
>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>>
>> A minor question: do we need to also reset the DSPP alongside 
>> resetting the LM?
> 
> So this function is mostly doing what the downstream equivalent of it 
> does which is to disable all the sspp blend stages, the ping-pong 
> binding and 3d-merge connection.
> 
> For DSPP, there is no ability to *bind* it or connect it. Its a 
> hard-wired connection. Its just a question of whether to enable it or not.
> 
> When the CTL path connections are removed, there is no need to 
> explicitly disable the DSPP.
> 
> Thats why even downstream doesnt do it today.

Ack, thanks.


> 
>>
>>> ---
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c      | 82 
>>> ++++++++++++++++++++++++
>>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h |  7 ++
>>>   2 files changed, 89 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>> index 4523693..0e31ad3 100644
>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
>>> @@ -1,5 +1,6 @@
>>>   // SPDX-License-Identifier: GPL-2.0-only
>>>   /*
>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>> reserved.
>>>    * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All 
>>> rights reserved.
>>>    * Copyright (C) 2013 Red Hat
>>>    * Author: Rob Clark <robdclark@gmail.com>
>>> @@ -22,6 +23,7 @@
>>>   #include "dpu_hw_ctl.h"
>>>   #include "dpu_hw_dspp.h"
>>>   #include "dpu_hw_dsc.h"
>>> +#include "dpu_hw_merge3d.h"
>>>   #include "dpu_formats.h"
>>>   #include "dpu_encoder_phys.h"
>>>   #include "dpu_crtc.h"
>>> @@ -1838,6 +1840,86 @@ void dpu_encoder_kickoff(struct drm_encoder 
>>> *drm_enc)
>>>       DPU_ATRACE_END("encoder_kickoff");
>>>   }
>>> +static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys 
>>> *phys_enc)
>>> +{
>>> +    struct dpu_hw_mixer_cfg mixer;
>>> +    int i, num_lm;
>>> +    u32 flush_mask = 0;
>>> +    struct dpu_global_state *global_state;
>>> +    struct dpu_hw_blk *hw_lm[2];
>>> +    struct dpu_hw_mixer *hw_mixer[2];
>>> +    struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
>>> +
>>> +    memset(&mixer, 0, sizeof(mixer));
>>> +
>>> +    /* reset all mixers for this encoder */
>>> +    if (phys_enc->hw_ctl->ops.clear_all_blendstages)
>>> +        phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
>>> +
>>> +    global_state = 
>>> dpu_kms_get_existing_global_state(phys_enc->dpu_kms);
>>> +
>>> +    num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, 
>>> global_state,
>>> +        phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, 
>>> ARRAY_SIZE(hw_lm));
>>> +
>>> +    for (i = 0; i < num_lm; i++) {
>>> +        hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]);
>>> +        flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, 
>>> hw_mixer[i]->idx);
>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush)
>>> +            phys_enc->hw_ctl->ops.update_pending_flush(ctl, 
>>> flush_mask);
>>> +
>>> +        /* clear all blendstages */
>>> +        if (phys_enc->hw_ctl->ops.setup_blendstage)
>>> +            phys_enc->hw_ctl->ops.setup_blendstage(ctl, 
>>> hw_mixer[i]->idx, NULL);
>>> +    }
>>> +}
>>> +
>>> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc)
>>> +{
>>> +    struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
>>> +    struct dpu_hw_intf_cfg intf_cfg = { 0 };
>>> +    int i;
>>> +    struct dpu_encoder_virt *dpu_enc;
>>> +
>>> +    dpu_enc = to_dpu_encoder_virt(phys_enc->parent);
>>> +
>>> +    phys_enc->hw_ctl->ops.reset(ctl);
>>> +
>>> +    dpu_encoder_helper_reset_mixers(phys_enc);
>>> +
>>> +    for (i = 0; i < dpu_enc->num_phys_encs; i++) {
>>> +        if (dpu_enc->phys_encs[i] && 
>>> phys_enc->hw_intf->ops.bind_pingpong_blk)
>>> +            phys_enc->hw_intf->ops.bind_pingpong_blk(
>>> +                    dpu_enc->phys_encs[i]->hw_intf, false,
>>> +                    dpu_enc->phys_encs[i]->hw_pp->idx);
>>> +
>>> +        /* mark INTF flush as pending */
>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_intf)
>>> + phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl,
>>> +                    dpu_enc->phys_encs[i]->hw_intf->idx);
>>> +    }
>>> +
>>> +    /* reset the merge 3D HW block */
>>> +    if (phys_enc->hw_pp->merge_3d) {
>>> + 
>>> phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d,
>>> +                BLEND_3D_NONE);
>>> +        if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d)
>>> +            phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl,
>>> +                    phys_enc->hw_pp->merge_3d->idx);
>>> +    }
>>> +
>>> +    intf_cfg.stream_sel = 0; /* Don't care value for video mode */
>>> +    intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
>>> +    if (phys_enc->hw_pp->merge_3d)
>>> +        intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx;
>>> +
>>> +    if (ctl->ops.reset_intf_cfg)
>>> +        ctl->ops.reset_intf_cfg(ctl, &intf_cfg);
>>> +
>>> +    ctl->ops.trigger_flush(ctl);
>>> +    ctl->ops.trigger_start(ctl);
>>> +    ctl->ops.clear_pending_flush(ctl);
>>> +}
>>> +
>>>   void dpu_encoder_prepare_commit(struct drm_encoder *drm_enc)
>>>   {
>>>       struct dpu_encoder_virt *dpu_enc;
>>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 
>>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> index 706b566..544a9a4 100644
>>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
>>> @@ -1,5 +1,6 @@
>>>   /* SPDX-License-Identifier: GPL-2.0-only */
>>>   /*
>>> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights 
>>> reserved.
>>>    * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
>>>    */
>>> @@ -350,4 +351,10 @@ int dpu_encoder_helper_wait_for_irq(struct 
>>> dpu_encoder_phys *phys_enc,
>>>           void (*func)(void *arg, int irq_idx),
>>>           struct dpu_encoder_wait_info *wait_info);
>>> +/**
>>> + * dpu_encoder_helper_phys_cleanup - helper to cleanup dpu pipeline
>>> + * @phys_enc: Pointer to physical encoder structure
>>> + */
>>> +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys 
>>> *phys_enc);
>>> +
>>>   #endif /* __dpu_encoder_phys_H__ */
>>
>>


-- 
With best wishes
Dmitry

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

* Re: [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
  2022-04-20  1:45 ` [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog Abhinav Kumar
@ 2022-04-21 12:16   ` Liviu Dudau
  2022-04-22 23:08     ` Abhinav Kumar
  0 siblings, 1 reply; 51+ messages in thread
From: Liviu Dudau @ 2022-04-21 12:16 UTC (permalink / raw)
  To: Abhinav Kumar
  Cc: markyacoub, dri-devel, swboyd, seanpaul, laurent.pinchart,
	dmitry.baryshkov, quic_jesszhan, quic_aravindh, freedreno

On Tue, Apr 19, 2022 at 06:45:56PM -0700, Abhinav Kumar wrote:
> Add writeback blocks to the sm8250 DPU hardware catalog. Other
> chipsets support writeback too but add it to sm8250 to prototype
> the feature so that it can be easily extended to other chipsets.
> 
> changes in v2:
> 	- none
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> ---
>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 74 +++++++++++++++++++++++++-
>  drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 ++++++++++++++++++++++-
>  2 files changed, 138 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> index b0a0ef7..bcb5273 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
> @@ -1,5 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0-only
> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> +/* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.

Hi Abhinav,

Nit: Order should be historical (i.e. QIC copyright comes last). Comment applies to
all other copyright years additions.

Best regards,
Liviu

>   */
>  
>  #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
> @@ -120,6 +121,16 @@
>  			  BIT(MDP_AD4_0_INTR) | \
>  			  BIT(MDP_AD4_1_INTR))
>  
> +#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \
> +			 BIT(DPU_WB_UBWC) | \
> +			 BIT(DPU_WB_YUV_CONFIG) | \
> +			 BIT(DPU_WB_PIPE_ALPHA) | \
> +			 BIT(DPU_WB_XY_ROI_OFFSET) | \
> +			 BIT(DPU_WB_QOS) | \
> +			 BIT(DPU_WB_QOS_8LVL) | \
> +			 BIT(DPU_WB_CDP) | \
> +			 BIT(DPU_WB_INPUT_CTRL))
> +
>  #define DEFAULT_PIXEL_RAM_SIZE		(50 * 1024)
>  #define DEFAULT_DPU_LINE_WIDTH		2048
>  #define DEFAULT_DPU_OUTPUT_LINE_WIDTH	2560
> @@ -211,6 +222,40 @@ static const u32 rotation_v2_formats[] = {
>  	/* TODO add formats after validation */
>  };
>  
> +static const uint32_t wb2_formats[] = {
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_RGB888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_RGBX8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ARGB1555,
> +	DRM_FORMAT_RGBA5551,
> +	DRM_FORMAT_XRGB1555,
> +	DRM_FORMAT_RGBX5551,
> +	DRM_FORMAT_ARGB4444,
> +	DRM_FORMAT_RGBA4444,
> +	DRM_FORMAT_RGBX4444,
> +	DRM_FORMAT_XRGB4444,
> +	DRM_FORMAT_BGR565,
> +	DRM_FORMAT_BGR888,
> +	DRM_FORMAT_ABGR8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_BGRX8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_ABGR1555,
> +	DRM_FORMAT_BGRA5551,
> +	DRM_FORMAT_XBGR1555,
> +	DRM_FORMAT_BGRX5551,
> +	DRM_FORMAT_ABGR4444,
> +	DRM_FORMAT_BGRA4444,
> +	DRM_FORMAT_BGRX4444,
> +	DRM_FORMAT_XBGR4444,
> +};
> +
>  /*************************************************************
>   * DPU sub blocks config
>   *************************************************************/
> @@ -448,6 +493,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = {
>  			.reg_off = 0x2C4, .bit_off = 8},
>  	.clk_ctrls[DPU_CLK_CTRL_REG_DMA] = {
>  			.reg_off = 0x2BC, .bit_off = 20},
> +	.clk_ctrls[DPU_CLK_CTRL_WB2] = {
> +			.reg_off = 0x3B8, .bit_off = 24},
>  	},
>  };
>  
> @@ -1235,6 +1282,29 @@ static const struct dpu_intf_cfg qcm2290_intf[] = {
>  };
>  
>  /*************************************************************
> + * Writeback blocks config
> + *************************************************************/
> +#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \
> +		__xin_id, vbif_id, _reg, _wb_done_bit) \
> +	{ \
> +	.name = _name, .id = _id, \
> +	.base = _base, .len = 0x2c8, \
> +	.features = _features, \
> +	.format_list = wb2_formats, \
> +	.num_formats = ARRAY_SIZE(wb2_formats), \
> +	.clk_ctrl = _clk_ctrl, \
> +	.xin_id = __xin_id, \
> +	.vbif_idx = vbif_id, \
> +	.maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \
> +	.intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \
> +	}
> +
> +static const struct dpu_wb_cfg sm8250_wb[] = {
> +	WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6,
> +			VBIF_RT, MDP_SSPP_TOP0_INTR, 4),
> +};
> +
> +/*************************************************************
>   * VBIF sub blocks config
>   *************************************************************/
>  /* VBIF QOS remap */
> @@ -1832,6 +1902,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg)
>  		.intf = sm8150_intf,
>  		.vbif_count = ARRAY_SIZE(sdm845_vbif),
>  		.vbif = sdm845_vbif,
> +		.wb_count = ARRAY_SIZE(sm8250_wb),
> +		.wb = sm8250_wb,
>  		.reg_dma_count = 1,
>  		.dma_cfg = sm8250_regdma,
>  		.perf = sm8250_perf_data,
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> index 866fd7a..8cb6d1f 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
> @@ -1,5 +1,7 @@
>  /* SPDX-License-Identifier: GPL-2.0-only */
> -/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
> +/*
> + * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
>   */
>  
>  #ifndef _DPU_HW_CATALOG_H
> @@ -214,6 +216,42 @@ enum {
>  };
>  
>  /**
> +  * WB sub-blocks and features
> +  * @DPU_WB_LINE_MODE        Writeback module supports line/linear mode
> +  * @DPU_WB_BLOCK_MODE       Writeback module supports block mode read
> +  * @DPU_WB_CHROMA_DOWN,     Writeback chroma down block,
> +  * @DPU_WB_DOWNSCALE,       Writeback integer downscaler,
> +  * @DPU_WB_DITHER,          Dither block
> +  * @DPU_WB_TRAFFIC_SHAPER,  Writeback traffic shaper bloc
> +  * @DPU_WB_UBWC,            Writeback Universal bandwidth compression
> +  * @DPU_WB_YUV_CONFIG       Writeback supports output of YUV colorspace
> +  * @DPU_WB_PIPE_ALPHA       Writeback supports pipe alpha
> +  * @DPU_WB_XY_ROI_OFFSET    Writeback supports x/y-offset of out ROI in
> +  *                          the destination image
> +  * @DPU_WB_QOS,             Writeback supports QoS control, danger/safe/creq
> +  * @DPU_WB_QOS_8LVL,        Writeback supports 8-level QoS control
> +  * @DPU_WB_CDP              Writeback supports client driven prefetch
> +  * @DPU_WB_INPUT_CTRL       Writeback supports from which pp block input pixel
> +  *                          data arrives.
> +  * @DPU_WB_CROP             CWB supports cropping
> +  * @DPU_WB_MAX              maximum value
> +  */
> +enum {
> +	DPU_WB_LINE_MODE = 0x1,
> +	DPU_WB_BLOCK_MODE,
> +	DPU_WB_UBWC,
> +	DPU_WB_YUV_CONFIG,
> +	DPU_WB_PIPE_ALPHA,
> +	DPU_WB_XY_ROI_OFFSET,
> +	DPU_WB_QOS,
> +	DPU_WB_QOS_8LVL,
> +	DPU_WB_CDP,
> +	DPU_WB_INPUT_CTRL,
> +	DPU_WB_CROP,
> +	DPU_WB_MAX
> +};
> +
> +/**
>   * VBIF sub-blocks and features
>   * @DPU_VBIF_QOS_OTLIM        VBIF supports OT Limit
>   * @DPU_VBIF_QOS_REMAP        VBIF supports QoS priority remap
> @@ -460,6 +498,7 @@ enum dpu_clk_ctrl_type {
>  	DPU_CLK_CTRL_CURSOR1,
>  	DPU_CLK_CTRL_INLINE_ROT0_SSPP,
>  	DPU_CLK_CTRL_REG_DMA,
> +	DPU_CLK_CTRL_WB2,
>  	DPU_CLK_CTRL_MAX,
>  };
>  
> @@ -608,6 +647,28 @@ struct dpu_intf_cfg  {
>  };
>  
>  /**
> + * struct dpu_wb_cfg - information of writeback blocks
> + * @DPU_HW_BLK_INFO:    refer to the description above for DPU_HW_BLK_INFO
> + * @vbif_idx:           vbif client index
> + * @maxlinewidth:       max line width supported by writeback block
> + * @xin_id:             bus client identifier
> + * @intr_wb_done:       interrupt index for WB_DONE
> + * @format_list:	    list of formats supported by this writeback block
> + * @num_formats:	    number of formats supported by this writeback block
> + * @clk_ctrl:	        clock control identifier
> + */
> +struct dpu_wb_cfg {
> +	DPU_HW_BLK_INFO;
> +	u8 vbif_idx;
> +	u32 maxlinewidth;
> +	u32 xin_id;
> +	s32 intr_wb_done;
> +	const u32 *format_list;
> +	u32 num_formats;
> +	enum dpu_clk_ctrl_type clk_ctrl;
> +};
> +
> +/**
>   * struct dpu_vbif_dynamic_ot_cfg - dynamic OT setting
>   * @pps                pixel per seconds
>   * @ot_limit           OT limit to use up to specified pixel per second
> @@ -792,6 +853,9 @@ struct dpu_mdss_cfg {
>  	u32 vbif_count;
>  	const struct dpu_vbif_cfg *vbif;
>  
> +	u32 wb_count;
> +	const struct dpu_wb_cfg *wb;
> +
>  	u32 reg_dma_count;
>  	struct dpu_reg_dma_cfg dma_cfg;
>  
> -- 
> 2.7.4
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯

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

* Re: [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
  2022-04-21 12:16   ` Liviu Dudau
@ 2022-04-22 23:08     ` Abhinav Kumar
  0 siblings, 0 replies; 51+ messages in thread
From: Abhinav Kumar @ 2022-04-22 23:08 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: markyacoub, dri-devel, swboyd, seanpaul, laurent.pinchart,
	dmitry.baryshkov, quic_jesszhan, quic_aravindh, freedreno

Hi Liviu

Thank you for the feedback.

I have fixed the order of copyright years in all the changes in the next 
version.

Thanks

Abhinav

On 4/21/2022 5:16 AM, Liviu Dudau wrote:
> On Tue, Apr 19, 2022 at 06:45:56PM -0700, Abhinav Kumar wrote:
>> Add writeback blocks to the sm8250 DPU hardware catalog. Other
>> chipsets support writeback too but add it to sm8250 to prototype
>> the feature so that it can be easily extended to other chipsets.
>>
>> changes in v2:
>> 	- none
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>> ---
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 74 +++++++++++++++++++++++++-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 ++++++++++++++++++++++-
>>   2 files changed, 138 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
>> index b0a0ef7..bcb5273 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
>> @@ -1,5 +1,6 @@
>>   // SPDX-License-Identifier: GPL-2.0-only
>> -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
>> +/* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
>> + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> 
> Hi Abhinav,
> 
> Nit: Order should be historical (i.e. QIC copyright comes last). Comment applies to
> all other copyright years additions.
> 
> Best regards,
> Liviu
> 
>>    */
>>   
>>   #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
>> @@ -120,6 +121,16 @@
>>   			  BIT(MDP_AD4_0_INTR) | \
>>   			  BIT(MDP_AD4_1_INTR))
>>   
>> +#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \
>> +			 BIT(DPU_WB_UBWC) | \
>> +			 BIT(DPU_WB_YUV_CONFIG) | \
>> +			 BIT(DPU_WB_PIPE_ALPHA) | \
>> +			 BIT(DPU_WB_XY_ROI_OFFSET) | \
>> +			 BIT(DPU_WB_QOS) | \
>> +			 BIT(DPU_WB_QOS_8LVL) | \
>> +			 BIT(DPU_WB_CDP) | \
>> +			 BIT(DPU_WB_INPUT_CTRL))
>> +
>>   #define DEFAULT_PIXEL_RAM_SIZE		(50 * 1024)
>>   #define DEFAULT_DPU_LINE_WIDTH		2048
>>   #define DEFAULT_DPU_OUTPUT_LINE_WIDTH	2560
>> @@ -211,6 +222,40 @@ static const u32 rotation_v2_formats[] = {
>>   	/* TODO add formats after validation */
>>   };
>>   
>> +static const uint32_t wb2_formats[] = {
>> +	DRM_FORMAT_RGB565,
>> +	DRM_FORMAT_BGR565,
>> +	DRM_FORMAT_RGB888,
>> +	DRM_FORMAT_ARGB8888,
>> +	DRM_FORMAT_RGBA8888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_XRGB8888,
>> +	DRM_FORMAT_RGBX8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_ARGB1555,
>> +	DRM_FORMAT_RGBA5551,
>> +	DRM_FORMAT_XRGB1555,
>> +	DRM_FORMAT_RGBX5551,
>> +	DRM_FORMAT_ARGB4444,
>> +	DRM_FORMAT_RGBA4444,
>> +	DRM_FORMAT_RGBX4444,
>> +	DRM_FORMAT_XRGB4444,
>> +	DRM_FORMAT_BGR565,
>> +	DRM_FORMAT_BGR888,
>> +	DRM_FORMAT_ABGR8888,
>> +	DRM_FORMAT_BGRA8888,
>> +	DRM_FORMAT_BGRX8888,
>> +	DRM_FORMAT_XBGR8888,
>> +	DRM_FORMAT_ABGR1555,
>> +	DRM_FORMAT_BGRA5551,
>> +	DRM_FORMAT_XBGR1555,
>> +	DRM_FORMAT_BGRX5551,
>> +	DRM_FORMAT_ABGR4444,
>> +	DRM_FORMAT_BGRA4444,
>> +	DRM_FORMAT_BGRX4444,
>> +	DRM_FORMAT_XBGR4444,
>> +};
>> +
>>   /*************************************************************
>>    * DPU sub blocks config
>>    *************************************************************/
>> @@ -448,6 +493,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = {
>>   			.reg_off = 0x2C4, .bit_off = 8},
>>   	.clk_ctrls[DPU_CLK_CTRL_REG_DMA] = {
>>   			.reg_off = 0x2BC, .bit_off = 20},
>> +	.clk_ctrls[DPU_CLK_CTRL_WB2] = {
>> +			.reg_off = 0x3B8, .bit_off = 24},
>>   	},
>>   };
>>   
>> @@ -1235,6 +1282,29 @@ static const struct dpu_intf_cfg qcm2290_intf[] = {
>>   };
>>   
>>   /*************************************************************
>> + * Writeback blocks config
>> + *************************************************************/
>> +#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \
>> +		__xin_id, vbif_id, _reg, _wb_done_bit) \
>> +	{ \
>> +	.name = _name, .id = _id, \
>> +	.base = _base, .len = 0x2c8, \
>> +	.features = _features, \
>> +	.format_list = wb2_formats, \
>> +	.num_formats = ARRAY_SIZE(wb2_formats), \
>> +	.clk_ctrl = _clk_ctrl, \
>> +	.xin_id = __xin_id, \
>> +	.vbif_idx = vbif_id, \
>> +	.maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \
>> +	.intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \
>> +	}
>> +
>> +static const struct dpu_wb_cfg sm8250_wb[] = {
>> +	WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6,
>> +			VBIF_RT, MDP_SSPP_TOP0_INTR, 4),
>> +};
>> +
>> +/*************************************************************
>>    * VBIF sub blocks config
>>    *************************************************************/
>>   /* VBIF QOS remap */
>> @@ -1832,6 +1902,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg)
>>   		.intf = sm8150_intf,
>>   		.vbif_count = ARRAY_SIZE(sdm845_vbif),
>>   		.vbif = sdm845_vbif,
>> +		.wb_count = ARRAY_SIZE(sm8250_wb),
>> +		.wb = sm8250_wb,
>>   		.reg_dma_count = 1,
>>   		.dma_cfg = sm8250_regdma,
>>   		.perf = sm8250_perf_data,
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> index 866fd7a..8cb6d1f 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
>> @@ -1,5 +1,7 @@
>>   /* SPDX-License-Identifier: GPL-2.0-only */
>> -/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
>> +/*
>> + * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
>> + * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
>>    */
>>   
>>   #ifndef _DPU_HW_CATALOG_H
>> @@ -214,6 +216,42 @@ enum {
>>   };
>>   
>>   /**
>> +  * WB sub-blocks and features
>> +  * @DPU_WB_LINE_MODE        Writeback module supports line/linear mode
>> +  * @DPU_WB_BLOCK_MODE       Writeback module supports block mode read
>> +  * @DPU_WB_CHROMA_DOWN,     Writeback chroma down block,
>> +  * @DPU_WB_DOWNSCALE,       Writeback integer downscaler,
>> +  * @DPU_WB_DITHER,          Dither block
>> +  * @DPU_WB_TRAFFIC_SHAPER,  Writeback traffic shaper bloc
>> +  * @DPU_WB_UBWC,            Writeback Universal bandwidth compression
>> +  * @DPU_WB_YUV_CONFIG       Writeback supports output of YUV colorspace
>> +  * @DPU_WB_PIPE_ALPHA       Writeback supports pipe alpha
>> +  * @DPU_WB_XY_ROI_OFFSET    Writeback supports x/y-offset of out ROI in
>> +  *                          the destination image
>> +  * @DPU_WB_QOS,             Writeback supports QoS control, danger/safe/creq
>> +  * @DPU_WB_QOS_8LVL,        Writeback supports 8-level QoS control
>> +  * @DPU_WB_CDP              Writeback supports client driven prefetch
>> +  * @DPU_WB_INPUT_CTRL       Writeback supports from which pp block input pixel
>> +  *                          data arrives.
>> +  * @DPU_WB_CROP             CWB supports cropping
>> +  * @DPU_WB_MAX              maximum value
>> +  */
>> +enum {
>> +	DPU_WB_LINE_MODE = 0x1,
>> +	DPU_WB_BLOCK_MODE,
>> +	DPU_WB_UBWC,
>> +	DPU_WB_YUV_CONFIG,
>> +	DPU_WB_PIPE_ALPHA,
>> +	DPU_WB_XY_ROI_OFFSET,
>> +	DPU_WB_QOS,
>> +	DPU_WB_QOS_8LVL,
>> +	DPU_WB_CDP,
>> +	DPU_WB_INPUT_CTRL,
>> +	DPU_WB_CROP,
>> +	DPU_WB_MAX
>> +};
>> +
>> +/**
>>    * VBIF sub-blocks and features
>>    * @DPU_VBIF_QOS_OTLIM        VBIF supports OT Limit
>>    * @DPU_VBIF_QOS_REMAP        VBIF supports QoS priority remap
>> @@ -460,6 +498,7 @@ enum dpu_clk_ctrl_type {
>>   	DPU_CLK_CTRL_CURSOR1,
>>   	DPU_CLK_CTRL_INLINE_ROT0_SSPP,
>>   	DPU_CLK_CTRL_REG_DMA,
>> +	DPU_CLK_CTRL_WB2,
>>   	DPU_CLK_CTRL_MAX,
>>   };
>>   
>> @@ -608,6 +647,28 @@ struct dpu_intf_cfg  {
>>   };
>>   
>>   /**
>> + * struct dpu_wb_cfg - information of writeback blocks
>> + * @DPU_HW_BLK_INFO:    refer to the description above for DPU_HW_BLK_INFO
>> + * @vbif_idx:           vbif client index
>> + * @maxlinewidth:       max line width supported by writeback block
>> + * @xin_id:             bus client identifier
>> + * @intr_wb_done:       interrupt index for WB_DONE
>> + * @format_list:	    list of formats supported by this writeback block
>> + * @num_formats:	    number of formats supported by this writeback block
>> + * @clk_ctrl:	        clock control identifier
>> + */
>> +struct dpu_wb_cfg {
>> +	DPU_HW_BLK_INFO;
>> +	u8 vbif_idx;
>> +	u32 maxlinewidth;
>> +	u32 xin_id;
>> +	s32 intr_wb_done;
>> +	const u32 *format_list;
>> +	u32 num_formats;
>> +	enum dpu_clk_ctrl_type clk_ctrl;
>> +};
>> +
>> +/**
>>    * struct dpu_vbif_dynamic_ot_cfg - dynamic OT setting
>>    * @pps                pixel per seconds
>>    * @ot_limit           OT limit to use up to specified pixel per second
>> @@ -792,6 +853,9 @@ struct dpu_mdss_cfg {
>>   	u32 vbif_count;
>>   	const struct dpu_vbif_cfg *vbif;
>>   
>> +	u32 wb_count;
>> +	const struct dpu_wb_cfg *wb;
>> +
>>   	u32 reg_dma_count;
>>   	struct dpu_reg_dma_cfg dma_cfg;
>>   
>> -- 
>> 2.7.4
>>
> 

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

end of thread, other threads:[~2022-04-22 23:08 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-20  1:45 [PATCH v2 00/17] Add writeback block support for DPU Abhinav Kumar
2022-04-20  1:45 ` [PATCH v2 01/17] drm: allow passing possible_crtcs to drm_writeback_connector_init() Abhinav Kumar
2022-04-20  1:45 ` [PATCH v2 02/17] drm: introduce drm_writeback_connector_init_with_encoder() API Abhinav Kumar
2022-04-20  1:45 ` [PATCH v2 03/17] drm: allow real encoder to be passed for drm_writeback_connector Abhinav Kumar
2022-04-20  1:45 ` [PATCH v2 04/17] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog Abhinav Kumar
2022-04-21 12:16   ` Liviu Dudau
2022-04-22 23:08     ` Abhinav Kumar
2022-04-20  1:45 ` [PATCH v2 05/17] drm/msm/dpu: add reset_intf_cfg operation for dpu_hw_ctl Abhinav Kumar
2022-04-20  7:02   ` Dmitry Baryshkov
2022-04-20  1:45 ` [PATCH v2 06/17] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks Abhinav Kumar
2022-04-20  7:20   ` Dmitry Baryshkov
2022-04-20 17:01     ` Abhinav Kumar
2022-04-20 17:49       ` Dmitry Baryshkov
2022-04-20 18:11         ` Abhinav Kumar
2022-04-20 18:49           ` Dmitry Baryshkov
2022-04-20  1:45 ` [PATCH v2 07/17] drm/msm/dpu: add writeback blocks to DPU RM Abhinav Kumar
2022-04-20  6:47   ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 08/17] drm/msm/dpu: add changes to support writeback in hw_ctl Abhinav Kumar
2022-04-20  6:59   ` Dmitry Baryshkov
2022-04-20 17:16     ` Abhinav Kumar
2022-04-20 18:48       ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 09/17] drm/msm/dpu: add an API to reset the encoder related hw blocks Abhinav Kumar
2022-04-20  7:23   ` Dmitry Baryshkov
2022-04-20 21:28     ` Abhinav Kumar
2022-04-20 22:42       ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 10/17] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder Abhinav Kumar
2022-04-20  7:44   ` Dmitry Baryshkov
2022-04-20 17:41     ` Abhinav Kumar
2022-04-20 18:37       ` Dmitry Baryshkov
2022-04-20 18:46         ` [Freedreno] " Abhinav Kumar
2022-04-20 22:06           ` Abhinav Kumar
2022-04-20 22:38             ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 11/17] drm/msm/dpu: add encoder operations to prepare/cleanup wb job Abhinav Kumar
2022-04-20  1:46 ` [PATCH v2 12/17] drm/msm/dpu: move _dpu_plane_get_qos_lut to dpu_hw_util file Abhinav Kumar
2022-04-20  7:26   ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 13/17] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback Abhinav Kumar
2022-04-20  7:49   ` Dmitry Baryshkov
2022-04-20 18:17     ` Abhinav Kumar
2022-04-20 19:26       ` Dmitry Baryshkov
2022-04-20 19:36         ` Abhinav Kumar
2022-04-20 19:37           ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 14/17] drm/msm/dpu: add the writeback connector layer Abhinav Kumar
2022-04-20  7:52   ` Dmitry Baryshkov
2022-04-20 19:10     ` Abhinav Kumar
2022-04-20 19:26       ` Dmitry Baryshkov
2022-04-20 19:26   ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 15/17] drm/msm/dpu: initialize dpu encoder and connector for writeback Abhinav Kumar
2022-04-20  7:54   ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 16/17] drm/msm/dpu: gracefully handle null fb commits " Abhinav Kumar
2022-04-20  7:55   ` Dmitry Baryshkov
2022-04-20  1:46 ` [PATCH v2 17/17] drm/msm/dpu: add writeback blocks to the display snapshot Abhinav Kumar

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.