@@ -1163,6 +1163,78 @@ static bool dpu_crtc_needs_dirtyfb(struct drm_crtc_state *cstate)
return false;
}
+#define MAX_HDISPLAY_SPLIT 1080
+
+static struct msm_display_topology dpu_crtc_get_topology(
+ struct drm_crtc *crtc,
+ struct dpu_kms *dpu_kms,
+ struct drm_crtc_state *crtc_state)
+{
+ struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ struct msm_display_topology topology = {0};
+ struct drm_encoder *drm_enc;
+
+ drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask)
+ dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state,
+ &crtc_state->adjusted_mode);
+
+ /*
+ * Datapath topology selection
+ *
+ * Dual display
+ * 2 LM, 2 INTF ( Split display using 2 interfaces)
+ *
+ * Single display
+ * 1 LM, 1 INTF
+ * 2 LM, 1 INTF (stream merge to support high resolution interfaces)
+ *
+ * Add dspps to the reservation requirements if ctm is requested
+ */
+
+ if (topology.num_intf == 2)
+ topology.num_lm = 2;
+ else if (topology.num_dsc == 2)
+ topology.num_lm = 2;
+ else if (dpu_kms->catalog->caps->has_3d_merge)
+ topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
+ else
+ topology.num_lm = 1;
+
+ if (crtc_state->ctm)
+ topology.num_dspp = topology.num_lm;
+
+ return topology;
+}
+
+static int dpu_crtc_assign_resources(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
+{
+ struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc);
+ struct dpu_global_state *global_state;
+ struct msm_display_topology topology;
+ int ret;
+
+ /*
+ * Release and Allocate resources on every modeset
+ * Dont allocate when enable is false.
+ */
+ global_state = dpu_kms_get_existing_global_state(dpu_kms);
+ if (IS_ERR(global_state))
+ return PTR_ERR(global_state);
+
+ dpu_rm_release(global_state, crtc);
+
+ if (!crtc_state->enable)
+ return 0;
+
+ topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state);
+ ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
+ crtc, &topology);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
@@ -1174,10 +1246,24 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
const struct drm_plane_state *pstate;
struct drm_plane *plane;
+ struct drm_encoder *drm_enc;
+
int rc = 0;
bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state);
+ /* there might be cases where encoder needs a modeset too */
+ drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) {
+ if (dpu_encoder_needs_modeset(drm_enc, crtc_state->state))
+ crtc_state->mode_changed = true;
+ }
+
+ if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+ rc = dpu_crtc_assign_resources(crtc, crtc_state);
+ if (rc < 0)
+ return rc;
+ }
+
if (!crtc_state->enable || !drm_atomic_crtc_effectively_active(crtc_state)) {
DRM_DEBUG_ATOMIC("crtc%d -> enable %d, active %d, skip atomic_check\n",
crtc->base.id, crtc_state->enable,
@@ -58,8 +58,6 @@
#define IDLE_SHORT_TIMEOUT 1
-#define MAX_HDISPLAY_SPLIT 1080
-
/* timeout in frames waiting for frame done */
#define DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES 5
@@ -544,159 +542,117 @@ void dpu_encoder_helper_split_config(
}
}
-bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
+void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
+ struct msm_display_topology *topology,
+ struct drm_atomic_state *state,
+ const struct drm_display_mode *adj_mode)
{
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
- int i, intf_count = 0, num_dsc = 0;
+ struct drm_connector *connector;
+ struct drm_connector_state *conn_state;
+ struct msm_display_info *disp_info;
+ struct drm_framebuffer *fb;
+ struct msm_drm_private *priv;
+ int i;
for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
if (dpu_enc->phys_encs[i])
- intf_count++;
+ topology->num_intf++;
- /* See dpu_encoder_get_topology, we only support 2:2:1 topology */
+ /* We only support 2 DSC mode (with 2 LM and 1 INTF) */
if (dpu_enc->dsc)
- num_dsc = 2;
+ topology->num_dsc += 2;
- return (num_dsc > 0) && (num_dsc > intf_count);
-}
+ connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc);
+ if (!connector)
+ return;
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (!conn_state)
+ return;
-struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc)
-{
- struct msm_drm_private *priv = drm_enc->dev->dev_private;
- struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
- int index = dpu_enc->disp_info.h_tile_instance[0];
+ disp_info = &dpu_enc->disp_info;
- if (dpu_enc->disp_info.intf_type == INTF_DSI)
- return msm_dsi_get_dsc_config(priv->dsi[index]);
+ priv = drm_enc->dev->dev_private;
- return NULL;
+ /*
+ * Use CDM only for writeback or DP at the moment as other interfaces cannot handle it.
+ * If writeback itself cannot handle cdm for some reason it will fail in its atomic_check()
+ * earlier.
+ */
+ if (disp_info->intf_type == INTF_WB && conn_state->writeback_job) {
+ fb = conn_state->writeback_job->fb;
+
+ if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb)))
+ topology->needs_cdm = true;
+ } else if (disp_info->intf_type == INTF_DP) {
+ if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], adj_mode))
+ topology->needs_cdm = true;
+ }
}
-static struct msm_display_topology dpu_encoder_get_topology(
- struct dpu_encoder_virt *dpu_enc,
- struct dpu_kms *dpu_kms,
- struct drm_display_mode *mode,
- struct drm_crtc_state *crtc_state,
- struct drm_dsc_config *dsc)
+static bool dpu_encoder_needs_dsc_merge(struct drm_encoder *drm_enc)
{
- struct msm_display_topology topology = {0};
- int i, intf_count = 0;
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
+ u32 num_intf = 0;
+ u32 num_dsc = 0;
+ int i;
for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
if (dpu_enc->phys_encs[i])
- intf_count++;
-
- /* Datapath topology selection
- *
- * Dual display
- * 2 LM, 2 INTF ( Split display using 2 interfaces)
- *
- * Single display
- * 1 LM, 1 INTF
- * 2 LM, 1 INTF (stream merge to support high resolution interfaces)
- *
- * Add dspps to the reservation requirements if ctm is requested
- */
- if (intf_count == 2)
- topology.num_lm = 2;
- else if (!dpu_kms->catalog->caps->has_3d_merge)
- topology.num_lm = 1;
- else
- topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
-
- if (crtc_state->ctm)
- topology.num_dspp = topology.num_lm;
-
- topology.num_intf = intf_count;
+ num_intf++;
- if (dsc) {
- /*
- * In case of Display Stream Compression (DSC), we would use
- * 2 DSC encoders, 2 layer mixers and 1 interface
- * this is power optimal and can drive up to (including) 4k
- * screens
- */
- topology.num_dsc = 2;
- topology.num_lm = 2;
- topology.num_intf = 1;
- }
+ /* We only support 2 DSC mode (with 2 LM and 1 INTF) */
+ if (dpu_enc->dsc)
+ num_dsc += 2;
- return topology;
+ return (num_dsc > 0) && (num_dsc > num_intf);
}
-static int dpu_encoder_virt_atomic_check(
- struct drm_encoder *drm_enc,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
+bool dpu_encoder_needs_modeset(struct drm_encoder *drm_enc, struct drm_atomic_state *state)
{
- struct dpu_encoder_virt *dpu_enc;
- struct msm_drm_private *priv;
- struct dpu_kms *dpu_kms;
- struct drm_display_mode *adj_mode;
- struct msm_display_topology topology;
- struct msm_display_info *disp_info;
- struct dpu_global_state *global_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *conn_state;
struct drm_framebuffer *fb;
- struct drm_dsc_config *dsc;
- int ret = 0;
-
- if (!drm_enc || !crtc_state || !conn_state) {
- DPU_ERROR("invalid arg(s), drm_enc %d, crtc/conn state %d/%d\n",
- drm_enc != NULL, crtc_state != NULL, conn_state != NULL);
- return -EINVAL;
- }
-
- dpu_enc = to_dpu_encoder_virt(drm_enc);
- DPU_DEBUG_ENC(dpu_enc, "\n");
-
- priv = drm_enc->dev->dev_private;
- disp_info = &dpu_enc->disp_info;
- dpu_kms = to_dpu_kms(priv->kms);
- adj_mode = &crtc_state->adjusted_mode;
- global_state = dpu_kms_get_global_state(crtc_state->state);
- if (IS_ERR(global_state))
- return PTR_ERR(global_state);
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
- trace_dpu_enc_atomic_check(DRMID(drm_enc));
+ if (!drm_enc || !state)
+ return false;
- dsc = dpu_encoder_get_dsc_config(drm_enc);
+ connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc);
+ if (!connector)
+ return false;
- topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, crtc_state, dsc);
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
- /*
- * Use CDM only for writeback or DP at the moment as other interfaces cannot handle it.
- * If writeback itself cannot handle cdm for some reason it will fail in its atomic_check()
- * earlier.
- */
- if (disp_info->intf_type == INTF_WB && conn_state->writeback_job) {
+ if (dpu_enc->disp_info.intf_type == INTF_WB && conn_state->writeback_job) {
fb = conn_state->writeback_job->fb;
-
- if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb)))
- topology.needs_cdm = true;
- } else if (disp_info->intf_type == INTF_DP) {
- if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], adj_mode))
- topology.needs_cdm = true;
+ if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) {
+ if (!dpu_enc->cur_master->hw_cdm)
+ return true;
+ } else {
+ if (dpu_enc->cur_master->hw_cdm)
+ return true;
+ }
}
- if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm)
- crtc_state->mode_changed = true;
- else if (!topology.needs_cdm && dpu_enc->cur_master->hw_cdm)
- crtc_state->mode_changed = true;
- /*
- * Release and Allocate resources on every modeset
- * Dont allocate when active is false.
- */
- if (drm_atomic_crtc_needs_modeset(crtc_state)) {
- dpu_rm_release(global_state, crtc_state->crtc);
+ return false;
+}
- if (!crtc_state->active_changed || crtc_state->enable)
- ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
- crtc_state->crtc, &topology);
- }
+struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc)
+{
+ struct msm_drm_private *priv = drm_enc->dev->dev_private;
+ struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
+ int index = dpu_enc->disp_info.h_tile_instance[0];
- trace_dpu_enc_atomic_check_flags(DRMID(drm_enc), adj_mode->flags);
+ if (dpu_enc->disp_info.intf_type == INTF_DSI)
+ return msm_dsi_get_dsc_config(priv->dsi[index]);
- return ret;
+ return NULL;
+}
+
+bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
+{
+ return dpu_encoder_needs_dsc_merge(drm_enc);
}
static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
@@ -2449,7 +2405,6 @@ static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = {
.atomic_mode_set = dpu_encoder_virt_atomic_mode_set,
.atomic_disable = dpu_encoder_virt_atomic_disable,
.atomic_enable = dpu_encoder_virt_atomic_enable,
- .atomic_check = dpu_encoder_virt_atomic_check,
};
static const struct drm_encoder_funcs dpu_encoder_funcs = {
@@ -169,6 +169,25 @@ int dpu_encoder_get_crc(const struct drm_encoder *drm_enc, u32 *crcs, int pos);
*/
bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc);
+/**
+ * dpu_encoder_update_topology - update topology with the requirements for the encoder
+ * @drm_enc: Pointer to previously created drm encoder structure
+ * @topology: Topology to be updated
+ * @state: Current DRM atomic state
+ * @adj_mode: Current DRM display mode associated with the crtc
+ */
+void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
+ struct msm_display_topology *topology,
+ struct drm_atomic_state *state,
+ const struct drm_display_mode *adj_mode);
+
+/**
+ * dpu_encoder_update_topology - update topology with the requirements for the encoder
+ * @drm_enc: Pointer to previously created drm encoder structure
+ * @topology: Current DRM atomic state
+ */
+bool dpu_encoder_needs_modeset(struct drm_encoder *drm_enc, struct drm_atomic_state *state);
+
/**
* dpu_encoder_prepare_wb_job - prepare writeback job for the encoder.
* @drm_enc: Pointer to previously created drm encoder structure