Loading msm/msm_drv.c +18 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <linux/kthread.h> #include <uapi/linux/sched/types.h> #include <drm/drm_of.h> #include <drm/drm_auth.h> #include <drm/drm_probe_helper.h> #include "msm_drv.h" Loading Loading @@ -989,6 +990,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; Loading Loading @@ -1513,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); } Loading msm/msm_kms.h +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading msm/sde/sde_crtc.c +6 −7 Original line number Diff line number Diff line Loading @@ -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])); Loading Loading @@ -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; } Loading msm/sde/sde_crtc.h +6 −0 Original line number Diff line number Diff line Loading @@ -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_ */ msm/sde/sde_kms.c +220 −29 Original line number Diff line number Diff line Loading @@ -2368,6 +2368,223 @@ 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) { 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; struct drm_crtc *crtc = NULL; unsigned int crtc_mask = 0; int ret = 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->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 { list_del_init(&fb->filp_head); drm_framebuffer_put(fb); } } if (list_empty(&fbs)) { SDE_DEBUG("skip commit as no fb(s)\n"); return 0; } 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 * 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) { Loading Loading @@ -3388,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; Loading @@ -3417,32 +3629,10 @@ static void _sde_kms_null_commit(struct drm_device *dev, } 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) Loading Loading @@ -3748,6 +3938,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, Loading Loading
msm/msm_drv.c +18 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <linux/kthread.h> #include <uapi/linux/sched/types.h> #include <drm/drm_of.h> #include <drm/drm_auth.h> #include <drm/drm_probe_helper.h> #include "msm_drv.h" Loading Loading @@ -989,6 +990,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; Loading Loading @@ -1513,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); } Loading
msm/msm_kms.h +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
msm/sde/sde_crtc.c +6 −7 Original line number Diff line number Diff line Loading @@ -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])); Loading Loading @@ -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; } Loading
msm/sde/sde_crtc.h +6 −0 Original line number Diff line number Diff line Loading @@ -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_ */
msm/sde/sde_kms.c +220 −29 Original line number Diff line number Diff line Loading @@ -2368,6 +2368,223 @@ 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) { 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; struct drm_crtc *crtc = NULL; unsigned int crtc_mask = 0; int ret = 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->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 { list_del_init(&fb->filp_head); drm_framebuffer_put(fb); } } if (list_empty(&fbs)) { SDE_DEBUG("skip commit as no fb(s)\n"); return 0; } 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 * 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) { Loading Loading @@ -3388,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; Loading @@ -3417,32 +3629,10 @@ static void _sde_kms_null_commit(struct drm_device *dev, } 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) Loading Loading @@ -3748,6 +3938,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, Loading