From cab792ba29b839f83aa42ffccb30d9135981cdb9 Mon Sep 17 00:00:00 2001 From: Andhavarapu Karthik Date: Wed, 18 Nov 2020 15:44:54 +0530 Subject: [PATCH 1/9] disp: msm: sde: aggregate all register dump memory allocation In current implementation, register dump memory is allocated separately for each range and block. When register dumps are added to minidump, this will generate around 32 minidump bin files. Changes are made to allocate all the required register dump memory once. When register dumps are added to minidump, this will generate only one minidump file. Change-Id: I04d4d8db559f21f862f8989d3dfcfa7d9aef6dc5 Signed-off-by: Andhavarapu Karthik --- msm/sde_dbg.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index 89715d376f24..eea83f4d1a82 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -266,6 +266,7 @@ struct sde_dbg_base { struct sde_dbg_evtlog *evtlog; struct sde_dbg_reglog *reglog; struct list_head reg_base_list; + void *reg_dump_addr; struct device *dev; struct mutex mutex; @@ -488,6 +489,7 @@ static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, char *base_addr, char *addr, size_t len_bytes, u32 **dump_mem) { u32 in_log, in_mem, len_align, len_padded; + struct sde_dbg_base *dbg_base = &sde_dbg_base; u32 *dump_addr = NULL; char *end_addr; int i; @@ -515,9 +517,10 @@ static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, end_addr = addr + len_bytes; if (in_mem) { - if (dump_mem && !(*dump_mem)) - *dump_mem = devm_kzalloc(sde_dbg_base.dev, len_padded, - GFP_KERNEL); + if (dump_mem && !(*dump_mem)) { + *dump_mem = dbg_base->reg_dump_addr; + dbg_base->reg_dump_addr += len_padded; + } if (dump_mem && *dump_mem) { dump_addr = *dump_mem; @@ -590,6 +593,49 @@ static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node, return length; } +static u32 _sde_dbg_get_reg_blk_size(struct sde_dbg_reg_base *dbg) +{ + u32 len, len_align, len_padded; + u32 size = 0; + struct sde_dbg_reg_range *range_node; + + if (!dbg || !dbg->base) { + pr_err("dbg base is null!\n"); + return 0; + } + + if (!list_empty(&dbg->sub_range_list)) { + list_for_each_entry(range_node, &dbg->sub_range_list, head) { + len = _sde_dbg_get_dump_range(&range_node->offset, + dbg->max_offset); + len_align = (len + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + size += REG_BASE_NAME_LEN + RANGE_NAME_LEN + len_padded; + } + } else { + len = dbg->max_offset; + len_align = (len + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + size += REG_BASE_NAME_LEN + RANGE_NAME_LEN + len_padded; + } + return size; +} + +static u32 _sde_dbg_get_reg_dump_size(void) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + u32 size = 0; + + if (!dbg_base) + return 0; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) { + size += _sde_dbg_get_reg_blk_size(blk_base); + } + return size; +} + static int _sde_dump_reg_range_cmp(void *priv, struct list_head *a, struct list_head *b) { @@ -635,6 +681,7 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, char *addr; size_t len; struct sde_dbg_reg_range *range_node; + struct sde_dbg_base *dbg_base = &sde_dbg_base; if (!dbg || !(dbg->base || dbg->cb)) { pr_err("dbg base is null!\n"); @@ -664,6 +711,12 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, addr, range_node->offset.start, range_node->offset.end); + scnprintf(dbg_base->reg_dump_addr, REG_BASE_NAME_LEN, + dbg->name); + dbg_base->reg_dump_addr += REG_BASE_NAME_LEN; + scnprintf(dbg_base->reg_dump_addr, RANGE_NAME_LEN, + range_node->range_name); + dbg_base->reg_dump_addr += RANGE_NAME_LEN; _sde_dump_reg(range_node->range_name, reg_dump_flag, dbg->base, addr, len, &range_node->reg_dump); @@ -676,6 +729,10 @@ static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, dbg->max_offset); addr = dbg->base; len = dbg->max_offset; + scnprintf(dbg_base->reg_dump_addr, REG_BASE_NAME_LEN, + dbg->name); + dbg_base->reg_dump_addr += REG_BASE_NAME_LEN; + dbg_base->reg_dump_addr += RANGE_NAME_LEN; _sde_dump_reg(dbg->name, reg_dump_flag, dbg->base, addr, len, &dbg->reg_dump); } @@ -1286,9 +1343,15 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], bool dump_secure) { int i; + u32 reg_dump_size; + struct sde_dbg_base *dbg_base = &sde_dbg_base; mutex_lock(&sde_dbg_base.mutex); + reg_dump_size = _sde_dbg_get_reg_dump_size(); + dbg_base->reg_dump_addr = devm_kzalloc(sde_dbg_base.dev, + reg_dump_size, GFP_KERNEL); + if (dump_all) sde_evtlog_dump_all(sde_dbg_base.evtlog); From b7d7dbc6e7251b7e89b1a235d6637a54d47b7a24 Mon Sep 17 00:00:00 2001 From: Andhavarapu Karthik Date: Tue, 4 May 2021 18:47:25 +0530 Subject: [PATCH 2/9] disp: msm: sde: add register dumps to minidump Add register dumps partition to minidump. Change-Id: I587e5169747eb60780a698b0f7756bb52c9ef853 Signed-off-by: Andhavarapu Karthik --- msm/sde_dbg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index eea83f4d1a82..12ca18734ebc 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -1352,6 +1352,11 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], dbg_base->reg_dump_addr = devm_kzalloc(sde_dbg_base.dev, reg_dump_size, GFP_KERNEL); + if (dbg_base->reg_dump_addr && + sde_mini_dump_add_region("reg_dump", + reg_dump_size, dbg_base->reg_dump_addr) < 0) + pr_err("minidump add regdump failed\n"); + if (dump_all) sde_evtlog_dump_all(sde_dbg_base.evtlog); From 2c8aa84b336ff5c16319d52f48c14f5ad0bc583f Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Mon, 21 Dec 2020 15:25:30 +0530 Subject: [PATCH 3/9] Revert "disp: msm: sde: remove all preclose logic" This change reverts commit ae2dceb0b690 ("disp: msm: sde: remove all preclose logic"). This change also modifies the force disable plane logic since __drm_atomic_helper_disable_plane is not exposed to drivers. Change-Id: I89e19dead9ade724798952b1934b45b5663e1a42 Signed-off-by: Jayaprakash Madisetty --- msm/msm_drv.c | 10 ++++ msm/msm_kms.h | 1 + msm/sde/sde_kms.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 40b5e1064d96..f145a7fcb762 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -989,6 +989,15 @@ static void context_close(struct msm_file_private *ctx) kfree(ctx); } +static void msm_preclose(struct drm_device *dev, struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + struct msm_kms *kms = priv->kms; + + if (kms && kms->funcs && kms->funcs->preclose) + kms->funcs->preclose(kms, file); +} + static void msm_postclose(struct drm_device *dev, struct drm_file *file) { struct msm_drm_private *priv = dev->dev_private; @@ -1717,6 +1726,7 @@ static struct drm_driver msm_driver = { DRIVER_ATOMIC | DRIVER_MODESET, .open = msm_open, + .preclose = msm_preclose, .postclose = msm_postclose, .lastclose = msm_lastclose, .irq_handler = msm_irq, diff --git a/msm/msm_kms.h b/msm/msm_kms.h index e56407afb241..058f97cdf53a 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -93,6 +93,7 @@ struct msm_kms_funcs { struct drm_encoder *slave_encoder, bool is_cmd_mode); void (*postopen)(struct msm_kms *kms, struct drm_file *file); + void (*preclose)(struct msm_kms *kms, struct drm_file *file); void (*postclose)(struct msm_kms *kms, struct drm_file *file); void (*lastclose)(struct msm_kms *kms); int (*register_events)(struct msm_kms *kms, diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index e48a1e20f8b3..e783238e84c3 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2368,6 +2368,143 @@ static void sde_kms_destroy(struct msm_kms *kms) kfree(sde_kms); } +static void _sde_kms_plane_force_remove(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state; + int ret = 0; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + SDE_ERROR("error %d getting plane %d state\n", + ret, plane->base.id); + return; + } + + plane->old_fb = plane->fb; + + SDE_DEBUG("disabling plane %d\n", plane->base.id); + + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret != 0) + SDE_ERROR("error %d disabling plane %d\n", ret, + plane->base.id); + + drm_atomic_set_fb_for_plane(plane_state, NULL); +} + +static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, + struct drm_atomic_state *state) +{ + struct drm_device *dev = sde_kms->dev; + struct drm_framebuffer *fb, *tfb; + struct list_head fbs; + struct drm_plane *plane; + int ret = 0; + u32 plane_mask = 0; + + INIT_LIST_HEAD(&fbs); + + list_for_each_entry_safe(fb, tfb, &file->fbs, filp_head) { + if (drm_framebuffer_read_refcount(fb) > 1) { + list_move_tail(&fb->filp_head, &fbs); + + drm_for_each_plane(plane, dev) { + if (plane->fb == fb) { + plane_mask |= + 1 << drm_plane_index(plane); + _sde_kms_plane_force_remove( + plane, state); + } + } + } else { + list_del_init(&fb->filp_head); + drm_framebuffer_put(fb); + } + } + + if (list_empty(&fbs)) { + SDE_DEBUG("skip commit as no fb(s)\n"); + drm_atomic_state_put(state); + return 0; + } + + SDE_DEBUG("committing after removing all the pipes\n"); + ret = drm_atomic_commit(state); + + if (ret) { + /* + * move the fbs back to original list, so it would be + * handled during drm_release + */ + list_for_each_entry_safe(fb, tfb, &fbs, filp_head) + list_move_tail(&fb->filp_head, &file->fbs); + + SDE_ERROR("atomic commit failed in preclose, ret:%d\n", ret); + goto end; + } + + while (!list_empty(&fbs)) { + fb = list_first_entry(&fbs, typeof(*fb), filp_head); + + list_del_init(&fb->filp_head); + drm_framebuffer_put(fb); + } + +end: + return ret; +} + +static void sde_kms_preclose(struct msm_kms *kms, struct drm_file *file) +{ + struct sde_kms *sde_kms = to_sde_kms(kms); + struct drm_device *dev = sde_kms->dev; + struct msm_drm_private *priv = dev->dev_private; + unsigned int i; + struct drm_atomic_state *state = NULL; + struct drm_modeset_acquire_ctx ctx; + int ret = 0; + + /* cancel pending flip event */ + for (i = 0; i < priv->num_crtcs; i++) + sde_crtc_complete_flip(priv->crtcs[i], file); + + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock_all_ctx(dev, &ctx); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else if (WARN_ON(ret)) { + goto end; + } + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto end; + } + + state->acquire_ctx = &ctx; + + for (i = 0; i < TEARDOWN_DEADLOCK_RETRY_MAX; i++) { + ret = _sde_kms_remove_fbs(sde_kms, file, state); + if (ret != -EDEADLK) + break; + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + } + +end: + if (state) + drm_atomic_state_put(state); + + SDE_DEBUG("sde preclose done, ret:%d\n", ret); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + static int _sde_kms_helper_reset_custom_properties(struct sde_kms *sde_kms, struct drm_atomic_state *state) { @@ -3748,6 +3885,7 @@ static const struct msm_kms_funcs kms_funcs = { .irq_postinstall = sde_irq_postinstall, .irq_uninstall = sde_irq_uninstall, .irq = sde_irq, + .preclose = sde_kms_preclose, .lastclose = sde_kms_lastclose, .prepare_fence = sde_kms_prepare_fence, .prepare_commit = sde_kms_prepare_commit, From 6cddd238e8902ae902beb9a41d65a3b15fd38c8c Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Mon, 21 Dec 2020 16:48:50 +0530 Subject: [PATCH 4/9] disp: msm: sde: remove fb's attached to a drm_file in preclose This change avoids upstream drm issuing drm_atomic_commit in drm_fb_release which is leading to artifacts on screen or atomic_check failures due to atomically unstaging each fb from plane_state and committing remaining planes on hardware. a) This patch moves the state operations for setting crtc to connector state to a helper api. b) This patch clears any dim_layers present in the crtc_state as part of null commit. c) This patch removes any framebuffers attached to a drm_file in msm_preclose whose refcount is not managed by composer kill inadvertently and issues null flush to hardware in such cases. d) This patch handles msm_preclose as part of msm_release operation since legacy feature is not supported for msm_driver. Change-Id: Ib2068d74d4b23b73b7c84544858c9f6bb6adfa67 Signed-off-by: Jayaprakash Madisetty --- msm/msm_drv.c | 10 +++- msm/sde/sde_crtc.c | 13 +++-- msm/sde/sde_crtc.h | 6 +++ msm/sde/sde_kms.c | 127 ++++++++++++++++++++++++++++++++------------- 4 files changed, 111 insertions(+), 45 deletions(-) diff --git a/msm/msm_drv.c b/msm/msm_drv.c index f145a7fcb762..00c321821e43 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include "msm_drv.h" @@ -1522,6 +1523,14 @@ static int msm_release(struct inode *inode, struct file *filp) kfree(node); } + /** + * Handle preclose operation here for removing fb's whose + * refcount > 1. This operation is not triggered from upstream + * drm as msm_driver does not support DRIVER_LEGACY feature. + */ + if (drm_is_current_master(file_priv)) + msm_preclose(dev, file_priv); + return drm_release(inode, filp); } @@ -1726,7 +1735,6 @@ static struct drm_driver msm_driver = { DRIVER_ATOMIC | DRIVER_MODESET, .open = msm_open, - .preclose = msm_preclose, .postclose = msm_postclose, .lastclose = msm_lastclose, .irq_handler = msm_irq, diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 5f04f8e19188..260e713c34d2 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2574,17 +2574,16 @@ static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate) cstate->input_fence_timeout_ns *= NSEC_PER_MSEC; } -/** - * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings - * @cstate: Pointer to sde crtc state - */ -static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate) +void _sde_crtc_clear_dim_layers_v1(struct drm_crtc_state *state) { u32 i; + struct sde_crtc_state *cstate; - if (!cstate) + if (!state) return; + cstate = to_sde_crtc_state(state); + for (i = 0; i < cstate->num_dim_layers; i++) memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i])); @@ -2613,7 +2612,7 @@ static void _sde_crtc_set_dim_layer_v1(struct drm_crtc *crtc, if (!usr_ptr) { /* usr_ptr is null when setting the default property value */ - _sde_crtc_clear_dim_layers_v1(cstate); + _sde_crtc_clear_dim_layers_v1(&cstate->base); SDE_DEBUG("dim_layer data removed\n"); goto clear; } diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 98ee9a46090c..09646cc718ea 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -957,4 +957,10 @@ int sde_crtc_get_num_datapath(struct drm_crtc *crtc, */ void sde_crtc_reset_sw_state(struct drm_crtc *crtc); +/** + * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings + * @cstate: Pointer to drm crtc state + */ +void _sde_crtc_clear_dim_layers_v1(struct drm_crtc_state *state); + #endif /* _SDE_CRTC_H_ */ diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index e783238e84c3..e0df384d81cd 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2368,6 +2368,71 @@ static void sde_kms_destroy(struct msm_kms *kms) kfree(sde_kms); } +static void sde_kms_helper_clear_dim_layers(struct drm_atomic_state *state, struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state = NULL; + struct sde_crtc_state *c_state; + + if (!state || !crtc) { + SDE_ERROR("invalid params\n"); + return; + } + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + c_state = to_sde_crtc_state(crtc_state); + + _sde_crtc_clear_dim_layers_v1(crtc_state); + set_bit(SDE_CRTC_DIRTY_DIM_LAYERS, c_state->dirty); +} + +static int sde_kms_set_crtc_for_conn(struct drm_device *dev, + struct drm_encoder *enc, struct drm_atomic_state *state) +{ + struct drm_connector *conn = NULL; + struct drm_connector *tmp_conn = NULL; + struct drm_connector_list_iter conn_iter; + struct drm_crtc_state *crtc_state = NULL; + struct drm_connector_state *conn_state = NULL; + int ret = 0; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(tmp_conn, &conn_iter) { + if (enc == tmp_conn->state->best_encoder) { + conn = tmp_conn; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + + if (!conn || !enc->crtc) { + SDE_ERROR("invalid params for enc:%d\n", DRMID(enc)); + return -EINVAL; + } + + crtc_state = drm_atomic_get_crtc_state(state, enc->crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + SDE_ERROR("error %d getting crtc %d state\n", + ret, DRMID(enc->crtc)); + return ret; + } + + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + SDE_ERROR("error %d getting connector %d state\n", + ret, DRMID(conn)); + return ret; + } + + crtc_state->active = true; + ret = drm_atomic_set_crtc_for_connector(conn_state, enc->crtc); + if (ret) + SDE_ERROR("error %d setting the crtc\n", ret); + + return ret; +} + static void _sde_kms_plane_force_remove(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -2401,8 +2466,9 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, struct drm_framebuffer *fb, *tfb; struct list_head fbs; struct drm_plane *plane; + struct drm_crtc *crtc = NULL; + unsigned int crtc_mask = 0; int ret = 0; - u32 plane_mask = 0; INIT_LIST_HEAD(&fbs); @@ -2411,11 +2477,10 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, list_move_tail(&fb->filp_head, &fbs); drm_for_each_plane(plane, dev) { - if (plane->fb == fb) { - plane_mask |= - 1 << drm_plane_index(plane); - _sde_kms_plane_force_remove( - plane, state); + if (plane->state && plane->state->fb == fb) { + if (plane->state->crtc) + crtc_mask |= drm_crtc_mask(plane->state->crtc); + _sde_kms_plane_force_remove(plane, state); } } } else { @@ -2426,13 +2491,28 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, if (list_empty(&fbs)) { SDE_DEBUG("skip commit as no fb(s)\n"); - drm_atomic_state_put(state); return 0; } - SDE_DEBUG("committing after removing all the pipes\n"); + drm_for_each_crtc(crtc, dev) { + if ((crtc_mask & drm_crtc_mask(crtc)) && crtc->state->active) { + struct drm_encoder *drm_enc; + + drm_for_each_encoder_mask(drm_enc, crtc->dev, + crtc->state->encoder_mask) { + ret = sde_kms_set_crtc_for_conn(dev, drm_enc, state); + if (ret) + goto error; + } + sde_kms_helper_clear_dim_layers(state, crtc); + } + } + + SDE_EVT32(state, crtc_mask); + SDE_DEBUG("null commit after removing all the pipes\n"); ret = drm_atomic_commit(state); +error: if (ret) { /* * move the fbs back to original list, so it would be @@ -3525,12 +3605,7 @@ static void _sde_kms_null_commit(struct drm_device *dev, struct drm_encoder *enc) { struct drm_modeset_acquire_ctx ctx; - struct drm_connector *conn = NULL; - struct drm_connector *tmp_conn = NULL; - struct drm_connector_list_iter conn_iter; struct drm_atomic_state *state = NULL; - struct drm_crtc_state *crtc_state = NULL; - struct drm_connector_state *conn_state = NULL; int retry_cnt = 0; int ret = 0; @@ -3554,32 +3629,10 @@ retry: } state->acquire_ctx = &ctx; - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(tmp_conn, &conn_iter) { - if (enc == tmp_conn->state->best_encoder) { - conn = tmp_conn; - break; - } - } - drm_connector_list_iter_end(&conn_iter); - if (!conn) { - SDE_ERROR("error in finding conn for enc:%d\n", DRMID(enc)); - goto end; - } - - crtc_state = drm_atomic_get_crtc_state(state, enc->crtc); - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - SDE_ERROR("error %d getting connector %d state\n", - ret, DRMID(conn)); - goto end; - } - - crtc_state->active = true; - ret = drm_atomic_set_crtc_for_connector(conn_state, enc->crtc); + ret = sde_kms_set_crtc_for_conn(dev, enc, state); if (ret) - SDE_ERROR("error %d setting the crtc\n", ret); + goto end; ret = drm_atomic_commit(state); if (ret) From b7db20d3ca92938e4b09952814bd397f4ce62d89 Mon Sep 17 00:00:00 2001 From: Andhavarapu Karthik Date: Mon, 31 May 2021 16:18:24 +0530 Subject: [PATCH 5/9] disp: msm: sde: update perf cpu dma latency votes on idle in video mode In video mode for fps greater than 60, perf cpu dma latency votes are not removed during idle fallback. Made changes to remove and add the perf cpu dma latency votes during idle fallback and idle exit scenarios respectively. Change-Id: I8dffc743bd48c96a6022935f71057a0223d9696e Signed-off-by: Andhavarapu Karthik --- msm/sde/sde_encoder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index c4d7c021d311..994bf8a18da3 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1721,6 +1721,7 @@ static int _sde_encoder_rc_kickoff(struct drm_encoder *drm_enc, if (is_vid_mode && sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) { sde_encoder_irq_control(drm_enc, true); + _sde_encoder_pm_qos_add_request(drm_enc); } else { /* enable all the clks and resources */ ret = _sde_encoder_resource_control_helper(drm_enc, @@ -1959,6 +1960,7 @@ static int _sde_encoder_rc_idle(struct drm_encoder *drm_enc, if (is_vid_mode) { sde_encoder_irq_control(drm_enc, false); + _sde_encoder_pm_qos_remove_request(drm_enc); } else { /* disable all the clks and resources */ _sde_encoder_update_rsc_client(drm_enc, false); From dbe227922765e50ef66a7af9e535ac308d70a7ac Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Thu, 3 Jun 2021 15:26:51 +0530 Subject: [PATCH 6/9] disp: msm: sde: fix imbalance power vote during cwb and dms concurrency Imbalance power vote caused with below scenario: 1. Primary display running with CWB enabled. 2. Idle-PC has kicked in causing primary and cwb encoder to rc_idle state. 3. Post idle-PC with DMS switch enabled along with CWB. Changes made are to avoid pre_modeset and post_modeset callbacks for CWB encoder as it not required and it puts a unnecessary power vote causing power issues in suspend usecase. Change-Id: I95b7ba093484f50be5f575cafa63a02e8fbca441 Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index c4d7c021d311..0fbb0a83b07a 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -2272,9 +2272,9 @@ static int sde_encoder_virt_modeset_rc(struct drm_encoder *drm_enc, if (pre_modeset) { intf_mode = sde_encoder_get_intf_mode(drm_enc); - if (msm_is_mode_seamless_dms(adj_mode) || - (msm_is_mode_seamless_dyn_clk(adj_mode) && - is_cmd_mode)) { + if ((msm_is_mode_seamless_dms(adj_mode) || + msm_is_mode_seamless_dyn_clk(adj_mode)) && + is_cmd_mode) { /* restore resource state before releasing them */ ret = sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_PRE_MODESET); @@ -2298,9 +2298,9 @@ static int sde_encoder_virt_modeset_rc(struct drm_encoder *drm_enc, adj_mode); } } else { - if (msm_is_mode_seamless_dms(adj_mode) || - (msm_is_mode_seamless_dyn_clk(adj_mode) && - is_cmd_mode)) + if ((msm_is_mode_seamless_dms(adj_mode) || + msm_is_mode_seamless_dyn_clk(adj_mode)) && + is_cmd_mode) sde_encoder_resource_control(&sde_enc->base, SDE_ENC_RC_EVENT_POST_MODESET); else if (msm_is_mode_seamless_poms(adj_mode)) From 42ccb141543a2844e8e32ec7da4b7095c94461cd Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Fri, 28 May 2021 16:23:59 +0530 Subject: [PATCH 7/9] disp: msm: dsi: Remove unused for loop in ESD reg read validation In ESD register read validation, a for loop is used to increment a local variable with the length of ESD command. But this is unused in the validation of the read data. This change removes the unused for loop. Change-Id: I6d1a9233061ee33a2b5390e2f922a2412af7d605 Signed-off-by: Ritesh Kumar --- msm/dsi/dsi_display.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index a4bfad36c655..260be3e257f2 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -595,9 +595,6 @@ static bool dsi_display_validate_reg_read(struct dsi_panel *panel) for (i = 0; i < count; i++) len += lenp[i]; - for (i = 0; i < len; i++) - j += len; - for (j = 0; j < config->groups; ++j) { for (i = 0; i < len; ++i) { if (config->return_buf[i] != From 587be9368e6eab221e203e53ac0cd539c1588bda Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Fri, 4 Jun 2021 14:58:17 +0530 Subject: [PATCH 8/9] disp: msm: dsi: Add support to enable LP11 insertion feature Some panels may require that the DSI link returns to the low-power stop state (LP11) after transmission of every line. If a panel requires that the DSI link returns to LP11, apply the LP11 insertion between lines feature. This change adds support to - Disable the command mdp burst mode - Enable mdp idle ctrl - Program the No. of dsi pclk cycles of idle time to insert between command mode mdp packets. The idle time must be long enough to cover the time link takes to switch between HS to LP11 mode. Change-Id: Ie718d334f05ce6c1ecd1a05b379bbbe18dec6330 Signed-off-by: Ritesh Kumar --- msm/dsi/dsi_ctrl_hw_cmn.c | 16 ++++++++++++---- msm/dsi/dsi_defs.h | 7 ++++++- msm/dsi/dsi_panel.c | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/msm/dsi/dsi_ctrl_hw_cmn.c b/msm/dsi/dsi_ctrl_hw_cmn.c index ead4fcb290ed..3f0affef1305 100644 --- a/msm/dsi/dsi_ctrl_hw_cmn.c +++ b/msm/dsi/dsi_ctrl_hw_cmn.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ #include @@ -615,15 +615,23 @@ void dsi_ctrl_hw_cmn_cmd_engine_setup(struct dsi_ctrl_hw *ctrl, reg |= cmd_mode_format_map[common_cfg->dst_format]; DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL, reg); - reg = DSI_R32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2); - reg |= BIT(16); - DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2, reg); + if (!cfg->mdp_idle_ctrl_en) { + reg = DSI_R32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2); + reg |= BIT(16); + DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2, reg); + } reg = cfg->wr_mem_start & 0xFF; reg |= (cfg->wr_mem_continue & 0xFF) << 8; reg |= (cfg->insert_dcs_command ? BIT(16) : 0); DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL, reg); + if (cfg->mdp_idle_ctrl_en) { + reg = cfg->mdp_idle_ctrl_len & 0x3FF; + reg |= BIT(12); + DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_IDLE_CTRL, reg); + } + DSI_CTRL_HW_DBG(ctrl, "Cmd engine setup done\n"); } diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 9f424781b78f..41dee2f61823 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #ifndef _DSI_DEFS_H_ @@ -542,12 +542,17 @@ struct dsi_video_engine_cfg { * @wr_mem_continue: DCS command for write_memory_continue. * @insert_dcs_command: Insert DCS command as first byte of payload * of the pixel data. + * @mdp_idle_ctrl_en: Enable idle insertion between command mode mdp packets. + * @mdp_idle_ctrl_len: No. of dsi pclk cycles of idle time to insert between + * command mode mdp packets. */ struct dsi_cmd_engine_cfg { u32 max_cmd_packets_interleave; u32 wr_mem_start; u32 wr_mem_continue; bool insert_dcs_command; + bool mdp_idle_ctrl_en; + u32 mdp_idle_ctrl_len; }; /** diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 3830a7eb8aa4..cdde192e19ac 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1590,6 +1590,21 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg, goto error; } + cfg->mdp_idle_ctrl_en = + utils->read_bool(utils->data, "qcom,mdss-dsi-mdp-idle-ctrl-en"); + + if (cfg->mdp_idle_ctrl_en) { + val = 0; + rc = utils->read_u32(utils->data, "qcom,mdss-dsi-mdp-idle-ctrl-len", &val); + if (rc) { + DSI_DEBUG("[%s] mdp idle ctrl len is not defined\n", name); + cfg->mdp_idle_ctrl_len = 0; + cfg->mdp_idle_ctrl_en = false; + rc = 0; + } else { + cfg->mdp_idle_ctrl_len = val; + } + } error: return rc; } From 85ea12542fc7c102ed494929e94a211faa0538cd Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Tue, 1 Jun 2021 12:28:40 +0530 Subject: [PATCH 9/9] disp: msm: sde: add changes to fix the sde fence signaled logic This change allows signalling of sde fences when sequence number is less than done_count. In current scenario, fence is not signaled as the subtraction result is not typecasted to int before updating the status causing the UI thread to block on sde fence. Change-Id: I3ca62222e0c67223b9eaf299474a2c282d761d3a Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_fence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_fence.c b/msm/sde/sde_fence.c index 5007c2dffbfd..dd9cc295f4e9 100644 --- a/msm/sde/sde_fence.c +++ b/msm/sde/sde_fence.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -131,7 +131,7 @@ static bool sde_fence_signaled(struct dma_fence *fence) struct sde_fence *f = to_sde_fence(fence); bool status; - status = (int)((fence->seqno - f->ctx->done_count) <= 0); + status = ((int)(fence->seqno - f->ctx->done_count) <= 0); SDE_DEBUG("status:%d fence seq:%d and timeline:%d\n", status, fence->seqno, f->ctx->done_count); return status;