Merge "disp: msm: sde: remove fb's attached to a drm_file in preclose"

This commit is contained in:
qctecmdr
2021-07-15 13:14:46 -07:00
committed by Gerrit - the friendly Code Review server
5 changed files with 251 additions and 36 deletions

View File

@@ -45,6 +45,7 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_vblank.h>
#include <drm/drm_drv.h>
#include <drm/drm_auth.h>
#include <drm/drm_probe_helper.h>
#include "msm_drv.h"
@@ -967,6 +968,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;
@@ -1471,6 +1481,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);
}

View File

@@ -97,6 +97,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,

View File

@@ -2837,17 +2837,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]));
@@ -2876,7 +2875,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;
}

View File

@@ -1043,4 +1043,10 @@ void sde_crtc_reset_sw_state(struct drm_crtc *crtc);
*/
void sde_crtc_disable_cp_features(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_ */

View File

@@ -2397,6 +2397,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)
{
@@ -3448,12 +3665,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;
@@ -3477,32 +3689,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)
@@ -3819,6 +4009,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,