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
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);
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);
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:
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;
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),
.reg_dma_count = 1, .dma_cfg = sm8250_regdma, .perf = sm8250_perf_data,.wb = sm8250_wb,
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
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),
.reg_dma_count = 1, .dma_cfg = sm8250_regdma, .perf = sm8250_perf_data,.wb = sm8250_wb,
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
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);
/*
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->update_pending_flush_intf = dpu_hw_ctl_update_pending_flush_intf_v1; ops->update_pending_flush_merge_3d =ops->reset_intf_cfg = dpu_hw_ctl_reset_intf_cfg_v1;
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);
/*
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 */
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 */
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 */
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 */
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 */
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 */
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__ */
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
*/ struct dpu_rm {
- @hw_wb: array of wb hardware resources
- @dspp_blks: array of dspp hardware resources
@@ -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__ */
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 */
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
*/ struct dpu_hw_ctl {
- @pending_wb_flush_mask: pending WB flush
- @ops: operation list
@@ -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 */
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 */
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.
*/ #include <linux/delay.h>
- Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
@@ -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.
*/ #ifndef _DPU_HW_CTL_H
- Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
@@ -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
*/ struct dpu_hw_ctl {
- @pending_wb_flush_mask: pending WB flush
- @ops: operation list
@@ -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 */
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__ */
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__ */
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__ */
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__ */
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;
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;
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;
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.
- This needs a slighly bigger cleanup including the traces, debug
prints and some helpers as none of them are aware of the wb_idx
- 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;
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.
- This needs a slighly bigger cleanup including the traces, debug
prints and some helpers as none of them are aware of the wb_idx
- 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;
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.
- This needs a slighly bigger cleanup including the traces, debug
prints and some helpers as none of them are aware of the wb_idx
- 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;
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.
- This needs a slighly bigger cleanup including the traces, debug
prints and some helpers as none of them are aware of the wb_idx
- 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;
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); };
/**
_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,
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,
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); +}
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
*/ enum dpu_intr_idx { INTR_IDX_VSYNC,
- @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector
@@ -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);
+}
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_wri...
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); +}
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_wri...
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); +}
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_wri...
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); +}
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
*/ enum dpu_intr_idx { INTR_IDX_VSYNC,
- @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector
@@ -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_wri...
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);
+}
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 */
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 */
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 */
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 */
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 */
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; }
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 dpu_encoder_virt *dpu_enc, struct dpu_enc_phys_init_params *params) {struct msm_display_info *disp_info,
@@ -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);
if (rc) { devm_kfree(dev->dev, dpu_enc); return ERR_PTR(rc);drm_enc_mode, NULL);
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; }
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; + }
/**
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;
}
/**
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");
dri-devel@lists.freedesktop.org