From c4f5050e13df861d03f91e1224611c995af494ff Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 29 Aug 2019 19:35:31 -0700 Subject: [PATCH] disp: msm: add VDC topology related changes Add support to configure the DPU pipeline to support VDC-m topologies. Change-Id: Ib8ce9a0eaeaa838759fb09cb2ee164d4765e4989 Signed-off-by: Abhinav Kumar --- msm/dsi/dsi_drm.c | 8 +- msm/msm_drv.h | 5 + msm/sde/sde_connector.c | 4 +- msm/sde/sde_encoder.c | 10 + msm/sde/sde_encoder.h | 4 +- msm/sde/sde_encoder_dce.c | 351 +++++++++++++++++++++++++++++++-- msm/sde/sde_encoder_phys.h | 3 +- msm/sde/sde_encoder_phys_vid.c | 6 +- msm/sde/sde_hw_catalog.h | 1 + msm/sde/sde_hw_ctl.c | 39 ++++ msm/sde/sde_hw_ctl.h | 16 +- msm/sde/sde_rm.c | 2 + msm/sde/sde_rm.h | 25 +++ msm/sde_vdc_helper.c | 3 +- 14 files changed, 453 insertions(+), 24 deletions(-) diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 42cc009601..43984cb9e4 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ @@ -503,6 +503,12 @@ int dsi_conn_get_mode_info(struct drm_connector *connector, sizeof(dsi_mode.priv_info->dsc)); mode_info->comp_info.comp_ratio = MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1; + } else if (dsi_mode.priv_info->vdc_enabled) { + mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_VDC; + memcpy(&mode_info->comp_info.vdc_info, &dsi_mode.priv_info->vdc, + sizeof(dsi_mode.priv_info->vdc)); + mode_info->comp_info.comp_ratio = + MSM_DISPLAY_COMPRESSION_RATIO_4_TO_1; } if (dsi_mode.priv_info->roi_caps.enabled) { diff --git a/msm/msm_drv.h b/msm/msm_drv.h index ac93e1f28d..4bf64fa57c 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -233,11 +233,13 @@ enum msm_display_compression_type { * @MSM_DISPLAY_COMPRESSION_NONE: no compression * @MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1: 2 to 1 compression * @MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1: 3 to 1 compression + * @MSM_DISPLAY_COMPRESSION_RATIO_4_TO_1: 4 to 1 compression */ enum msm_display_compression_ratio { MSM_DISPLAY_COMPRESSION_RATIO_NONE, MSM_DISPLAY_COMPRESSION_RATIO_2_TO_1, MSM_DISPLAY_COMPRESSION_RATIO_3_TO_1, + MSM_DISPLAY_COMPRESSION_RATIO_4_TO_1, MSM_DISPLAY_COMPRESSION_RATIO_MAX, }; @@ -599,6 +601,8 @@ struct msm_display_vdc_info { * @comp_ratio: compression ratio * @dsc_info: dsc configuration if the compression * supported is DSC + * @vdc_info: vdc configuration if the compression + * supported is VDC */ struct msm_compression_info { enum msm_display_compression_type comp_type; @@ -606,6 +610,7 @@ struct msm_compression_info { union{ struct msm_display_dsc_info dsc_info; + struct msm_display_vdc_info vdc_info; }; }; diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 9ab79336bc..939b4d03d6 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -37,10 +37,12 @@ static const struct drm_prop_enum_list e_topology_name[] = { {SDE_RM_TOPOLOGY_NONE, "sde_none"}, {SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"}, {SDE_RM_TOPOLOGY_SINGLEPIPE_DSC, "sde_singlepipe_dsc"}, + {SDE_RM_TOPOLOGY_SINGLEPIPE_VDC, "sde_singlepipe_vdc"}, {SDE_RM_TOPOLOGY_DUALPIPE, "sde_dualpipe"}, {SDE_RM_TOPOLOGY_DUALPIPE_DSC, "sde_dualpipe_dsc"}, {SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE, "sde_dualpipemerge"}, {SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC, "sde_dualpipemerge_dsc"}, + {SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC, "sde_dualpipemerge_vdc"}, {SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE, "sde_dualpipe_dscmerge"}, {SDE_RM_TOPOLOGY_PPSPLIT, "sde_ppsplit"}, }; diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index f41c180ef3..c75cba867f 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -74,6 +74,7 @@ #define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \ (((x) == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) || \ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || \ + ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC) || \ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC)) /** @@ -2040,6 +2041,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct list_head *connector_list; struct drm_connector *conn = NULL, *conn_iter; struct sde_rm_hw_iter dsc_iter, pp_iter, qdss_iter; + struct sde_rm_hw_iter vdc_iter; struct sde_rm_hw_request request_hw; enum sde_intf_mode intf_mode; bool is_cmd_mode = false; @@ -2160,6 +2162,14 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, sde_enc->hw_dsc[i] = (struct sde_hw_dsc *) dsc_iter.hw; } + sde_rm_init_hw_iter(&vdc_iter, drm_enc->base.id, SDE_HW_BLK_VDC); + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + sde_enc->hw_vdc[i] = NULL; + if (!sde_rm_get_hw(&sde_kms->rm, &vdc_iter)) + break; + sde_enc->hw_vdc[i] = (struct sde_hw_vdc *) vdc_iter.hw; + } + /* Get PP for DSC configuration */ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { struct sde_hw_pingpong *pp = NULL; diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index 599ef3a133..806d050b15 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -135,6 +135,7 @@ struct sde_encoder_ops { * @hw_pp Handle to the pingpong blocks used for the display. No. * pingpong blocks can be different than num_phys_encs. * @hw_dsc: Array of DSC block handles used for the display. + * @hw_vdc: Array of VDC block handles used for the display. * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush * @intfs_swapped Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong @@ -205,9 +206,10 @@ struct sde_encoder_virt { struct sde_encoder_phys *cur_master; struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; + struct sde_hw_vdc *hw_vdc[MAX_CHANNELS_PER_ENC]; struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; - + enum sde_vdc dirty_vdc_ids[MAX_CHANNELS_PER_ENC]; bool intfs_swapped; bool qdss_status; diff --git a/msm/sde/sde_encoder_dce.c b/msm/sde/sde_encoder_dce.c index 4e63aac8ec..8c8e9aaa6b 100644 --- a/msm/sde/sde_encoder_dce.c +++ b/msm/sde/sde_encoder_dce.c @@ -20,10 +20,12 @@ #include "sde_encoder_phys.h" #include "sde_power_handle.h" #include "sde_hw_dsc.h" +#include "sde_hw_vdc.h" #include "sde_crtc.h" #include "sde_trace.h" #include "sde_core_irq.h" #include "sde_dsc_helper.h" +#include "sde_vdc_helper.h" #define SDE_DEBUG_DCE(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\ (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) @@ -65,7 +67,7 @@ static int _dce_dsc_update_pic_dim(struct msm_display_dsc_info *dsc, } if ((pic_width % dsc->config.slice_width) || - (pic_height % dsc->config.slice_height)) { + (pic_height % dsc->config.slice_height)) { SDE_ERROR("pic_dim=%dx%d has to be multiple of slice=%dx%d\n", pic_width, pic_height, dsc->config.slice_width, dsc->config.slice_height); @@ -78,6 +80,29 @@ static int _dce_dsc_update_pic_dim(struct msm_display_dsc_info *dsc, return 0; } +static int _dce_vdc_update_pic_dim(struct msm_display_vdc_info *vdc, + int frame_width, int frame_height) +{ + if (!vdc || !frame_width || !frame_height) { + SDE_ERROR("invalid input: frame_width=%d frame_height=%d\n", + frame_width, frame_height); + return -EINVAL; + } + + if ((frame_width % vdc->slice_width) || + (frame_height % vdc->slice_height)) { + SDE_ERROR("pic_dim=%dx%d has to be multiple of slice=%dx%d\n", + frame_width, frame_height, + vdc->slice_width, vdc->slice_height); + return -EINVAL; + } + + vdc->frame_width = frame_width; + vdc->frame_height = frame_height; + + return 0; +} + static int _dce_dsc_initial_line_calc(struct msm_display_dsc_info *dsc, int enc_ip_width, int dsc_cmn_mode) @@ -223,24 +248,59 @@ static void _dce_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc, hw_dsc_pp->ops.enable_dsc(hw_dsc_pp); } -static inline bool _dce_check_half_panel_update(int num_dsc, - bool merge_3d, +static void _dce_vdc_pipe_cfg(struct sde_hw_vdc *hw_vdc, + struct sde_hw_pingpong *hw_pp, + struct msm_display_vdc_info *vdc, + enum sde_3d_blend_mode mode_3d, + bool disable_merge_3d, bool enable) +{ + + if (!vdc || !hw_vdc || !hw_pp) { + SDE_ERROR("invalid params %d %d %d\n", !vdc, !hw_vdc, + !hw_pp); + return; + } + + if (!enable) { + if (hw_vdc->ops.vdc_disable) + hw_vdc->ops.vdc_disable(hw_vdc); + + if (hw_vdc->ops.bind_pingpong_blk) + hw_vdc->ops.bind_pingpong_blk(hw_vdc, false, + PINGPONG_MAX); + + if (mode_3d && hw_pp->ops.reset_3d_mode) + hw_pp->ops.reset_3d_mode(hw_pp); + return; + } + + if (hw_vdc->ops.vdc_config) + hw_vdc->ops.vdc_config(hw_vdc, vdc); + + if (mode_3d && disable_merge_3d && hw_pp->ops.reset_3d_mode) { + SDE_DEBUG("disabling 3d mux\n"); + hw_pp->ops.reset_3d_mode(hw_pp); + } + + if (mode_3d && !disable_merge_3d && hw_pp->ops.setup_3d_mode) { + SDE_DEBUG("enabling 3d mux\n"); + hw_pp->ops.setup_3d_mode(hw_pp, mode_3d); + } + if (hw_vdc->ops.bind_pingpong_blk) + hw_vdc->ops.bind_pingpong_blk(hw_vdc, true, hw_pp->idx); + +} + +static inline bool _dce_check_half_panel_update(int num_lm, unsigned long affected_displays) { /** * partial update logic is currently supported only upto dual * pipe configurations. */ - - if (merge_3d) { - int num_mixers = 2; - - return (hweight_long(affected_displays) != num_mixers); - } else if (num_dsc > 1) { - return (hweight_long(affected_displays) != num_dsc); - } - return false; + return (hweight_long(affected_displays) != num_lm); } + static int _dce_dsc_setup(struct sde_encoder_virt *sde_enc, struct sde_encoder_kickoff_params *params) { @@ -263,7 +323,7 @@ static int _dce_dsc_setup(struct sde_encoder_virt *sde_enc, bool disable_merge_3d = false; int this_frame_slices; int intf_ip_w, enc_ip_w; - int num_intf, num_dsc; + int num_intf, num_dsc, num_lm; int ich_res; int dsc_common_mode = 0; int i; @@ -314,6 +374,7 @@ static int _dce_dsc_setup(struct sde_encoder_virt *sde_enc, num_intf = def->num_intf; mode_3d = (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE; + num_lm = def->num_lm; /* * If this encoder is driving more than one DSC encoder, they @@ -323,9 +384,8 @@ static int _dce_dsc_setup(struct sde_encoder_virt *sde_enc, _dce_dsc_update_pic_dim(dsc, roi->w, roi->h); merge_3d = (mode_3d != BLEND_3D_NONE) ? true: false; dsc_merge = (num_dsc > num_intf) ? true : false; - - half_panel_partial_update = _dce_check_half_panel_update( - num_dsc, merge_3d, params->affected_displays); + half_panel_partial_update = _dce_check_half_panel_update(num_lm, + params->affected_displays); if (half_panel_partial_update && merge_3d) disable_merge_3d = true; @@ -445,6 +505,176 @@ static int _dce_dsc_setup(struct sde_encoder_virt *sde_enc, return 0; } +static int _dce_vdc_setup(struct sde_encoder_virt *sde_enc, + struct sde_encoder_kickoff_params *params) +{ + struct drm_connector *drm_conn; + struct sde_kms *sde_kms; + struct msm_drm_private *priv; + struct drm_encoder *drm_enc; + struct sde_encoder_phys *enc_master; + struct sde_hw_vdc *hw_vdc[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; + struct msm_display_vdc_info *vdc = NULL; + enum sde_rm_topology_name topology; + const struct sde_rect *roi; + struct sde_hw_ctl *hw_ctl; + struct sde_hw_intf_cfg_v1 cfg; + enum sde_3d_blend_mode mode_3d; + bool half_panel_partial_update, merge_3d; + bool disable_merge_3d = false; + int this_frame_slices; + int intf_ip_w, enc_ip_w; + const struct sde_rm_topology_def *def; + int num_intf, num_vdc, num_lm; + int i; + int ret = 0; + + if (!sde_enc || !params || !sde_enc->phys_encs[0] || + !sde_enc->phys_encs[0]->connector) + return -EINVAL; + + drm_conn = sde_enc->phys_encs[0]->connector; + + topology = sde_connector_get_topology_name(drm_conn); + if (topology == SDE_RM_TOPOLOGY_NONE) { + SDE_ERROR_DCE(sde_enc, "topology not set yet\n"); + return -EINVAL; + } + + SDE_DEBUG_DCE(sde_enc, "topology:%d\n", topology); + SDE_EVT32(DRMID(&sde_enc->base), topology, + sde_enc->cur_conn_roi.x, + sde_enc->cur_conn_roi.y, + sde_enc->cur_conn_roi.w, + sde_enc->cur_conn_roi.h, + sde_enc->prv_conn_roi.x, + sde_enc->prv_conn_roi.y, + sde_enc->prv_conn_roi.w, + sde_enc->prv_conn_roi.h, + sde_enc->cur_master->cached_mode.hdisplay, + sde_enc->cur_master->cached_mode.vdisplay); + + if (sde_kms_rect_is_equal(&sde_enc->cur_conn_roi, + &sde_enc->prv_conn_roi)) + return ret; + + enc_master = sde_enc->cur_master; + roi = &sde_enc->cur_conn_roi; + hw_ctl = enc_master->hw_ctl; + vdc = &sde_enc->mode_info.comp_info.vdc_info; + + drm_enc = &sde_enc->base; + priv = drm_enc->dev->dev_private; + sde_kms = to_sde_kms(priv->kms); + + def = sde_rm_topology_get_topology_def(&sde_kms->rm, topology); + if (IS_ERR_OR_NULL(def)) + return -EINVAL; + + num_vdc = def->num_comp_enc; + num_intf = def->num_intf; + mode_3d = (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC) ? + BLEND_3D_H_ROW_INT : BLEND_3D_NONE; + num_lm = def->num_lm; + + /* + * If this encoder is driving more than one VDC encoder, they + * operate in tandem, same pic dimension needs to be used by + * each of them.(pp-split is assumed to be not supported) + */ + _dce_vdc_update_pic_dim(vdc, roi->w, roi->h); + merge_3d = (mode_3d != BLEND_3D_NONE) ? true : false; + half_panel_partial_update = _dce_check_half_panel_update(num_lm, + params->affected_displays); + + if (half_panel_partial_update && merge_3d) + disable_merge_3d = true; + + this_frame_slices = roi->w / vdc->slice_width; + intf_ip_w = this_frame_slices * vdc->slice_width; + + sde_vdc_populate_config(vdc, intf_ip_w, vdc->traffic_mode); + + enc_ip_w = intf_ip_w; + + SDE_DEBUG_DCE(sde_enc, "pic_w: %d pic_h: %d\n", + roi->w, roi->h); + + for (i = 0; i < num_vdc; i++) { + bool active = !!((1 << i) & params->affected_displays); + + /* + * if half_panel partial update vdc should be bound to the pp + * that is driving the update, in other case when both the + * layer mixers are driving the update, vdc should be bound + * to left side pp + */ + if (merge_3d && half_panel_partial_update) + hw_pp[i] = (active) ? sde_enc->hw_pp[0] : + sde_enc->hw_pp[1]; + else + hw_pp[i] = sde_enc->hw_pp[i]; + hw_vdc[i] = sde_enc->hw_vdc[i]; + + if (!hw_vdc[i]) { + SDE_ERROR_DCE(sde_enc, "invalid params for VDC\n"); + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, + i, active); + return -EINVAL; + } + + _dce_vdc_pipe_cfg(hw_vdc[i], hw_pp[i], + vdc, mode_3d, disable_merge_3d, active); + + memset(&cfg, 0, sizeof(cfg)); + cfg.vdc[cfg.vdc_count++] = hw_vdc[i]->idx; + + if (hw_ctl->ops.update_intf_cfg) + hw_ctl->ops.update_intf_cfg(hw_ctl, + &cfg, + active); + + if (hw_ctl->ops.update_bitmask_vdc) + hw_ctl->ops.update_bitmask_vdc(hw_ctl, + hw_vdc[i]->idx, active); + + SDE_DEBUG_DCE(sde_enc, + "update_intf_cfg hw_ctl[%d], vdc:%d, %s", + hw_ctl->idx, + cfg.vdc[0], + active ? "enabled" : "disabled"); + + if (mode_3d) { + memset(&cfg, 0, sizeof(cfg)); + + cfg.merge_3d[cfg.merge_3d_count++] = + hw_pp[i]->merge_3d->idx; + + if (hw_ctl->ops.update_intf_cfg) + hw_ctl->ops.update_intf_cfg(hw_ctl, + &cfg, + !disable_merge_3d); + + if (hw_ctl->ops.update_bitmask_merge3d) + hw_ctl->ops.update_bitmask_merge3d( + hw_ctl, + hw_pp[i]->merge_3d->idx, true); + + SDE_DEBUG("mode_3d %s, on CTL_%d PP-%d merge3d:%d\n", + disable_merge_3d ? + "disabled" : "enabled", + hw_ctl->idx - CTL_0, + hw_pp[i]->idx - PINGPONG_0, + hw_pp[i]->merge_3d ? + hw_pp[i]->merge_3d->idx - MERGE_3D_0 : + -1); + } + } + + return 0; +} + static void _dce_dsc_disable(struct sde_encoder_virt *sde_enc) { int i; @@ -494,7 +724,55 @@ static void _dce_dsc_disable(struct sde_encoder_virt *sde_enc) */ } -static bool _dce_dsc_is_dirty(struct sde_encoder_virt *sde_enc) + +static void _dce_vdc_disable(struct sde_encoder_virt *sde_enc) +{ + int i; + struct sde_hw_pingpong *hw_pp = NULL; + struct sde_hw_vdc *hw_vdc = NULL; + struct sde_hw_ctl *hw_ctl = NULL; + struct sde_hw_intf_cfg_v1 cfg; + + if (!sde_enc || !sde_enc->phys_encs[0] || + !sde_enc->phys_encs[0]->connector) { + SDE_ERROR("invalid params %d %d\n", + !sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1); + return; + } + + if (sde_enc->cur_master) + hw_ctl = sde_enc->cur_master->hw_ctl; + + memset(&cfg, 0, sizeof(cfg)); + + /* Disable VDC for all the pp's present in this topology */ + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_pp = sde_enc->hw_pp[i]; + hw_vdc = sde_enc->hw_vdc[i]; + + _dce_vdc_pipe_cfg(hw_vdc, hw_pp, NULL, + BLEND_3D_NONE, false, + false); + + if (hw_vdc) { + sde_enc->dirty_vdc_ids[i] = hw_vdc->idx; + cfg.vdc[cfg.vdc_count++] = hw_vdc->idx; + } + } + + /* Clear the VDC ACTIVE config for this CTL */ + if (hw_ctl && hw_ctl->ops.update_intf_cfg) + hw_ctl->ops.update_intf_cfg(hw_ctl, &cfg, false); + + /** + * Since pending flushes from previous commit get cleared + * sometime after this point, setting VDC flush bits now + * will have no effect. Therefore dirty_vdc_ids track which + * VDC blocks must be flushed for the next trigger. + */ +} + +bool _dce_dsc_is_dirty(struct sde_encoder_virt *sde_enc) { int i; @@ -510,6 +788,21 @@ static bool _dce_dsc_is_dirty(struct sde_encoder_virt *sde_enc) return false; } +bool _dce_vdc_is_dirty(struct sde_encoder_virt *sde_enc) +{ + int i; + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + /** + * This dirty_vdc_hw field is set during VDC disable to + * indicate which VDC blocks need to be flushed + */ + if (sde_enc->dirty_vdc_ids[i]) + return true; + } + + return false; +} static void _dce_helper_flush_dsc(struct sde_encoder_virt *sde_enc) { @@ -529,6 +822,24 @@ static void _dce_helper_flush_dsc(struct sde_encoder_virt *sde_enc) } } +void _dce_helper_flush_vdc(struct sde_encoder_virt *sde_enc) +{ + int i; + struct sde_hw_ctl *hw_ctl = NULL; + enum sde_vdc vdc_idx; + + if (sde_enc->cur_master) + hw_ctl = sde_enc->cur_master->hw_ctl; + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + vdc_idx = sde_enc->dirty_vdc_ids[i]; + if (vdc_idx && hw_ctl && hw_ctl->ops.update_bitmask_vdc) + hw_ctl->ops.update_bitmask_vdc(hw_ctl, vdc_idx, 1); + + sde_enc->dirty_vdc_ids[i] = VDC_NONE; + } +} + void sde_encoder_dce_disable(struct sde_encoder_virt *sde_enc) { enum msm_display_compression_type comp_type; @@ -540,6 +851,8 @@ void sde_encoder_dce_disable(struct sde_encoder_virt *sde_enc) if (comp_type == MSM_DISPLAY_COMPRESSION_DSC) _dce_dsc_disable(sde_enc); + else if (comp_type == MSM_DISPLAY_COMPRESSION_VDC) + _dce_vdc_disable(sde_enc); } int sde_encoder_dce_flush(struct sde_encoder_virt *sde_enc) @@ -551,6 +864,8 @@ int sde_encoder_dce_flush(struct sde_encoder_virt *sde_enc) if (_dce_dsc_is_dirty(sde_enc)) _dce_helper_flush_dsc(sde_enc); + else if (_dce_vdc_is_dirty(sde_enc)) + _dce_helper_flush_vdc(sde_enc); return rc; } @@ -568,6 +883,8 @@ int sde_encoder_dce_setup(struct sde_encoder_virt *sde_enc, if (comp_type == MSM_DISPLAY_COMPRESSION_DSC) rc = _dce_dsc_setup(sde_enc, params); + else if (comp_type == MSM_DISPLAY_COMPRESSION_VDC) + rc = _dce_vdc_setup(sde_enc, params); return rc; } diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index fa887a3f7f..59ea2cc1ae 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -610,7 +610,8 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode( topology = sde_connector_get_topology_name(phys_enc->connector); if (phys_enc->split_role == ENC_ROLE_SOLO && (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE || - topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC)) + topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC || + topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC)) return BLEND_3D_H_ROW_INT; return BLEND_3D_NONE; diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index b2d043a98e..b41e49e457 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -89,6 +89,10 @@ static void drm_mode_to_intf_timing_params( timing->width = DIV_ROUND_UP(timing->width, 2); else timing->width = DIV_ROUND_UP(timing->width, 3); + } else if (phys_enc->hw_intf->cap->type != INTF_DP && + vid_enc->base.comp_type == MSM_DISPLAY_COMPRESSION_VDC) { + comp_ratio = vid_enc->base.comp_ratio; + timing->width = DIV_ROUND_UP(timing->width, comp_ratio); } timing->height = mode->vdisplay; /* active height */ diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index ac8f81fd4e..0b1367feec 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -87,6 +87,7 @@ #define MAX_MERGE_3D_PER_CTL_V1 2 #define MAX_WB_PER_CTL_V1 1 #define MAX_CDM_PER_CTL_V1 1 +#define MAX_VDC_PER_CTL_V1 1 #define IS_SDE_CTL_REV_100(rev) \ ((rev) == SDE_CTL_CFG_VERSION_1_0_0) diff --git a/msm/sde/sde_hw_ctl.c b/msm/sde/sde_hw_ctl.c index 7b98b1b0ca..f635ca4bd9 100644 --- a/msm/sde/sde_hw_ctl.c +++ b/msm/sde/sde_hw_ctl.c @@ -65,6 +65,8 @@ #define CTL_INVALID_BIT 0xffff +#define VDC_IDX(i) ((i) + 16) + #define UPDATE_ACTIVE(r, idx, en) UPDATE_MASK((r), (idx), (en)) /** @@ -138,6 +140,11 @@ static const u32 intf_flush_tbl[INTF_MAX] = {SDE_NONE, 0, 1, 2, 3, 4, 5}; */ static const u32 dsc_flush_tbl[DSC_MAX] = {SDE_NONE, 0, 1, 2, 3, 4, 5}; +/** + * list of VDC bits in CTL_DSC_FLUSH + */ +static const u32 vdc_flush_tbl[DSC_MAX] = {SDE_NONE, 16, 17}; + /** * list of MERGE_3D bits in CTL_MERGE_3D_FLUSH */ @@ -557,6 +564,26 @@ static inline int sde_hw_ctl_update_bitmask_dsc_v1(struct sde_hw_ctl *ctx, return 0; } +static inline int sde_hw_ctl_update_bitmask_vdc(struct sde_hw_ctl *ctx, + enum sde_vdc vdc, bool enable) +{ + if (!ctx) + return -EINVAL; + + if (!(vdc > SDE_NONE) || !(vdc < VDC_MAX)) { + SDE_ERROR("Unsupported vdc %d\n", vdc); + return -EINVAL; + } + + UPDATE_MASK(ctx->flush.pending_dsc_flush_mask, vdc_flush_tbl[vdc], + enable); + if (ctx->flush.pending_dsc_flush_mask) + UPDATE_MASK(ctx->flush.pending_flush_mask, DSC_IDX, 1); + else + UPDATE_MASK(ctx->flush.pending_flush_mask, DSC_IDX, 0); + return 0; +} + static inline int sde_hw_ctl_update_bitmask_merge3d_v1(struct sde_hw_ctl *ctx, enum sde_merge_3d merge_3d, bool enable) { @@ -1169,6 +1196,7 @@ static int sde_hw_ctl_update_intf_cfg(struct sde_hw_ctl *ctx, u32 merge_3d_active = 0; u32 wb_active = 0; u32 dsc_active = 0; + u32 vdc_active = 0; struct sde_hw_blk_reg_map *c; if (!ctx) @@ -1213,6 +1241,16 @@ static int sde_hw_ctl_update_intf_cfg(struct sde_hw_ctl *ctx, SDE_REG_WRITE(c, CTL_DSC_ACTIVE, dsc_active); } + if (cfg->vdc_count) { + vdc_active = SDE_REG_READ(c, CTL_DSC_ACTIVE); + for (i = 0; i < cfg->vdc_count; i++) { + if (cfg->vdc[i]) + UPDATE_ACTIVE(vdc_active, + VDC_IDX(cfg->vdc[i] - VDC_0), enable); + } + + SDE_REG_WRITE(c, CTL_DSC_ACTIVE, vdc_active); + } return 0; } @@ -1367,6 +1405,7 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->update_bitmask_wb = sde_hw_ctl_update_bitmask_wb_v1; ops->update_bitmask_intf = sde_hw_ctl_update_bitmask_intf_v1; ops->update_bitmask_dsc = sde_hw_ctl_update_bitmask_dsc_v1; + ops->update_bitmask_vdc = sde_hw_ctl_update_bitmask_vdc; ops->update_bitmask_merge3d = sde_hw_ctl_update_bitmask_merge3d_v1; ops->update_bitmask_cwb = sde_hw_ctl_update_bitmask_cwb_v1; diff --git a/msm/sde/sde_hw_ctl.h b/msm/sde/sde_hw_ctl.h index 94e809d081..515c271c88 100644 --- a/msm/sde/sde_hw_ctl.h +++ b/msm/sde/sde_hw_ctl.h @@ -85,6 +85,8 @@ struct sde_hw_intf_cfg { * @cdm: Id of active cdm blocks * @dsc_count: No. of active dsc blocks * @dsc: Id of active dsc blocks + * @vdc_count: No. of active vdc blocks + * @vdc: Id of active vdc blocks */ struct sde_hw_intf_cfg_v1 { uint32_t intf_count; @@ -106,6 +108,9 @@ struct sde_hw_intf_cfg_v1 { uint32_t dsc_count; enum sde_dsc dsc[MAX_DSC_PER_CTL_V1]; + + uint32_t vdc_count; + enum sde_vdc vdc[MAX_VDC_PER_CTL_V1]; }; /** @@ -260,7 +265,8 @@ struct sde_hw_ctl_ops { int (*setup_intf_cfg_v1)(struct sde_hw_ctl *ctx, struct sde_hw_intf_cfg_v1 *cfg); - /** Update the interface selection with input WB config + /** + * Update the interface selection with input WB config * @ctx : ctl path ctx pointer * @cfg : pointer to input wb config * @enable : set if true, clear otherwise @@ -386,6 +392,14 @@ struct sde_hw_ctl_ops { int (*update_bitmask_dsc)(struct sde_hw_ctl *ctx, enum sde_dsc blk, bool enable); + /** + * update_bitmask_vdc: updates mask corresponding to vdc + * @blk : blk id + * @enable : true to enable, 0 to disable + */ + int (*update_bitmask_vdc)(struct sde_hw_ctl *ctx, + enum sde_vdc blk, bool enable); + /** * update_bitmask_merge3d: updates mask corresponding to merge_3d * @blk : blk id diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index 370f16365a..9b4e592a92 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -57,10 +57,12 @@ static const struct sde_rm_topology_def g_ctl_ver_1_top_table[] = { { SDE_RM_TOPOLOGY_NONE, 0, 0, 0, 0, false }, { SDE_RM_TOPOLOGY_SINGLEPIPE, 1, 0, 1, 1, false }, { SDE_RM_TOPOLOGY_SINGLEPIPE_DSC, 1, 1, 1, 1, false }, + { SDE_RM_TOPOLOGY_SINGLEPIPE_VDC, 1, 1, 1, 1, false }, { SDE_RM_TOPOLOGY_DUALPIPE, 2, 0, 2, 1, true }, { SDE_RM_TOPOLOGY_DUALPIPE_DSC, 2, 2, 2, 1, true }, { SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE, 2, 0, 1, 1, false }, { SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC, 2, 1, 1, 1, false }, + { SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC, 2, 1, 1, 1, false }, { SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE, 2, 2, 1, 1, false }, { SDE_RM_TOPOLOGY_PPSPLIT, 1, 0, 2, 1, true }, }; diff --git a/msm/sde/sde_rm.h b/msm/sde/sde_rm.h index 01d66dddaa..df1fb5ffa5 100644 --- a/msm/sde/sde_rm.h +++ b/msm/sde/sde_rm.h @@ -19,10 +19,12 @@ * @SDE_RM_TOPOLOGY_NONE: No topology in use currently * @SDE_RM_TOPOLOGY_SINGLEPIPE: 1 LM, 1 PP, 1 INTF/WB * @SDE_RM_TOPOLOGY_SINGLEPIPE_DSC: 1 LM, 1 DSC, 1 PP, 1 INTF/WB + * @SDE_RM_TOPOLOGY_SINGLEPIPE_VDC: 1 LM, 1 VDC, 1 PP, 1 INTF/WB * @SDE_RM_TOPOLOGY_DUALPIPE: 2 LM, 2 PP, 2 INTF/WB * @SDE_RM_TOPOLOGY_DUALPIPE_DSC: 2 LM, 2 DSC, 2 PP, 2 INTF/WB * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE: 2 LM, 2 PP, 3DMux, 1 INTF/WB * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC: 2 LM, 2 PP, 3DMux, 1 DSC, 1 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC: 2 LM, 2 PP, 3DMux, 1 VDC, 1 INTF/WB * @SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE: 2 LM, 2 PP, 2 DSC Merge, 1 INTF/WB * @SDE_RM_TOPOLOGY_PPSPLIT: 1 LM, 2 PPs, 2 INTF/WB */ @@ -30,10 +32,12 @@ enum sde_rm_topology_name { SDE_RM_TOPOLOGY_NONE = 0, SDE_RM_TOPOLOGY_SINGLEPIPE, SDE_RM_TOPOLOGY_SINGLEPIPE_DSC, + SDE_RM_TOPOLOGY_SINGLEPIPE_VDC, SDE_RM_TOPOLOGY_DUALPIPE, SDE_RM_TOPOLOGY_DUALPIPE_DSC, SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE, SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC, + SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC, SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE, SDE_RM_TOPOLOGY_PPSPLIT, SDE_RM_TOPOLOGY_MAX, @@ -299,6 +303,27 @@ static inline const struct sde_rm_topology_def* return &rm->topology_tbl[topology]; } +/** + * sde_rm_topology_get_num_lm - returns number of mixers + * used for this topology + * @rm: SDE Resource Manager handle + * @topology: topology selected for the display + * @return: number of lms + */ +static inline int sde_rm_topology_get_num_lm(struct sde_rm *rm, + enum sde_rm_topology_name topology) +{ + if ((!rm) || (topology <= SDE_RM_TOPOLOGY_NONE) || + (topology >= SDE_RM_TOPOLOGY_MAX)) { + pr_err("invalid arguments: rm:%d topology:%d\n", + rm == NULL, topology); + + return -EINVAL; + } + + return rm->topology_tbl[topology].num_lm; +} + /** * sde_rm_ext_blk_create_reserve - Create external HW blocks * in resource manager and reserve for specific encoder. diff --git a/msm/sde_vdc_helper.c b/msm/sde_vdc_helper.c index fea7a68066..3101a7ea7a 100644 --- a/msm/sde_vdc_helper.c +++ b/msm/sde_vdc_helper.c @@ -607,7 +607,8 @@ void sde_vdc_intf_prog_params(struct msm_display_vdc_info *vdc_info, total_bytes_per_intf = bytes_in_slice * slice_per_intf; vdc_info->eol_byte_num = total_bytes_per_intf % 3; - vdc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3); + vdc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, + 3); vdc_info->bytes_in_slice = bytes_in_slice; vdc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt; vdc_info->pkt_per_line = slice_per_intf / slice_per_pkt;