Merge "drm/msm: teardown all modes in lastclose handler" into msm-4.8
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index c97d097..2605562 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -24,7 +24,6 @@
#include "display_manager.h"
#include "sde_wb.h"
-
/*
* MSM driver version:
* - 1.0.0 - initial interface
@@ -35,6 +34,8 @@
#define MSM_VERSION_MINOR 2
#define MSM_VERSION_PATCHLEVEL 0
+#define TEARDOWN_DEADLOCK_RETRY_MAX 5
+
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
struct msm_drm_private *priv = dev->dev_private;
@@ -562,7 +563,20 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
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;
struct msm_file_private *ctx = file->driver_priv;
+ struct msm_kms *kms = priv->kms;
+
+ if (kms && kms->funcs && kms->funcs->postclose)
+ kms->funcs->postclose(kms, file);
mutex_lock(&dev->struct_mutex);
if (ctx == priv->lastctx)
@@ -572,11 +586,111 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
kfree(ctx);
}
+static int msm_disable_all_modes_commit(
+ struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ unsigned int plane_mask;
+ int ret;
+
+ plane_mask = 0;
+ drm_for_each_plane(plane, dev) {
+ struct drm_plane_state *plane_state;
+
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state)) {
+ ret = PTR_ERR(plane_state);
+ goto fail;
+ }
+
+ plane_state->rotation = BIT(DRM_ROTATE_0);
+
+ plane->old_fb = plane->fb;
+ plane_mask |= 1 << drm_plane_index(plane);
+
+ /* disable non-primary: */
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ DRM_DEBUG("disabling plane %d\n", plane->base.id);
+
+ ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+ if (ret != 0)
+ DRM_ERROR("error %d disabling plane %d\n", ret,
+ plane->base.id);
+ }
+
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_mode_set mode_set;
+
+ memset(&mode_set, 0, sizeof(struct drm_mode_set));
+ mode_set.crtc = crtc;
+
+ DRM_DEBUG("disabling crtc %d\n", crtc->base.id);
+
+ ret = __drm_atomic_helper_set_config(&mode_set, state);
+ if (ret != 0)
+ DRM_ERROR("error %d disabling crtc %d\n", ret,
+ crtc->base.id);
+ }
+
+ DRM_DEBUG("committing disables\n");
+ ret = drm_atomic_commit(state);
+
+fail:
+ drm_atomic_clean_old_fb(dev, plane_mask, ret);
+ DRM_DEBUG("disables result %d\n", ret);
+ return ret;
+}
+
+/**
+ * msm_clear_all_modes - disables all planes and crtcs via an atomic commit
+ * based on restore_fbdev_mode_atomic in drm_fb_helper.c
+ * @dev: device pointer
+ * @Return: 0 on success, otherwise -error
+ */
+static int msm_disable_all_modes(struct drm_device *dev)
+{
+ struct drm_atomic_state *state;
+ int ret, i;
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ state->acquire_ctx = dev->mode_config.acquire_ctx;
+
+ for (i = 0; i < TEARDOWN_DEADLOCK_RETRY_MAX; i++) {
+ ret = msm_disable_all_modes_commit(dev, state);
+ if (ret != -EDEADLK)
+ break;
+ drm_atomic_state_clear(state);
+ drm_atomic_legacy_backoff(state);
+ }
+
+ /* on successful atomic commit state ownership transfers to framework */
+ if (ret != 0)
+ drm_atomic_state_free(state);
+
+ return ret;
+}
+
static void msm_lastclose(struct drm_device *dev)
{
struct msm_drm_private *priv = dev->dev_private;
- if (priv->fbdev)
+ struct msm_kms *kms = priv->kms;
+
+ if (priv->fbdev) {
drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
+ } else {
+ drm_modeset_lock_all(dev);
+ msm_disable_all_modes(dev);
+ drm_modeset_unlock_all(dev);
+ if (kms && kms->funcs && kms->funcs->lastclose)
+ kms->funcs->lastclose(kms);
+ }
}
static irqreturn_t msm_irq(int irq, void *arg)
@@ -836,6 +950,7 @@ static struct drm_driver msm_driver = {
DRIVER_MODESET,
.open = msm_open,
.preclose = msm_preclose,
+ .postclose = msm_postclose,
.lastclose = msm_lastclose,
.irq_handler = msm_irq,
.irq_preinstall = msm_irq_preinstall,
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 22e7441..2eb5b2a 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -78,6 +78,9 @@ 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);
/* cleanup: */
void (*destroy)(struct msm_kms *kms);
};
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 84d8baf..9896ebd 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -830,6 +830,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
return -EINVAL;
}
+ if (!state->enable || !state->active) {
+ SDE_DEBUG("crtc%d -> enable %d, active %d, skip atomic_check\n",
+ crtc->base.id, state->enable, state->active);
+ return 0;
+ }
+
sde_crtc = to_sde_crtc(crtc);
mode = &state->adjusted_mode;
SDE_DEBUG("%s: check", sde_crtc->name);
@@ -924,6 +930,14 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
return 0;
}
+void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)
+{
+ struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+
+ SDE_DEBUG("%s: cancel: %p", sde_crtc->name, file);
+ complete_flip(crtc, file);
+}
+
/**
* sde_crtc_install_properties - install all drm properties for crtc
* @crtc: Pointer to drm crtc structure
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index d11c1e8..414374c 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -357,18 +357,31 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc = NULL;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
int i = 0;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
return;
+ } else if (!drm_enc->dev) {
+ SDE_ERROR("invalid dev\n");
+ return;
+ } else if (!drm_enc->dev->dev_private) {
+ SDE_ERROR("invalid dev_private\n");
+ return;
}
sde_enc = to_sde_encoder_virt(drm_enc);
+ priv = drm_enc->dev->dev_private;
+ sde_kms = to_sde_kms(priv->kms);
+
SDE_DEBUG_ENC(sde_enc, "\n");
MSM_EVT(drm_enc->dev, 0, 0);
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+
bs_set(sde_enc, 1);
for (i = 0; i < sde_enc->num_phys_encs; i++) {
@@ -402,6 +415,12 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
return;
+ } else if (!drm_enc->dev) {
+ SDE_ERROR("invalid dev\n");
+ return;
+ } else if (!drm_enc->dev->dev_private) {
+ SDE_ERROR("invalid dev_private\n");
+ return;
}
sde_enc = to_sde_encoder_virt(drm_enc);
@@ -415,10 +434,13 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
- if (phys && phys->ops.disable)
+ if (phys && phys->ops.disable && !phys->ops.is_master(phys))
phys->ops.disable(phys);
}
+ if (sde_enc->cur_master && sde_enc->cur_master->ops.disable)
+ sde_enc->cur_master->ops.disable(sde_enc->cur_master);
+
sde_enc->cur_master = NULL;
SDE_DEBUG_ENC(sde_enc, "cleared master\n");
@@ -426,6 +448,8 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
sde_cp_crtc_destroy_properties(drm_enc->crtc);
sde_rm_release(&sde_kms->rm, drm_enc);
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
}
static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index d6c0b75..3c90115 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -174,12 +174,24 @@ static void sde_debugfs_destroy(struct sde_kms *sde_kms)
static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
{
+ struct sde_kms *sde_kms = to_sde_kms(kms);
+ struct drm_device *dev = sde_kms->dev;
+ struct msm_drm_private *priv = dev->dev_private;
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+
return sde_crtc_vblank(crtc, true);
}
static void sde_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
{
+ struct sde_kms *sde_kms = to_sde_kms(kms);
+ struct drm_device *dev = sde_kms->dev;
+ struct msm_drm_private *priv = dev->dev_private;
+
sde_crtc_vblank(crtc, false);
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
}
static void sde_prepare_commit(struct msm_kms *kms,
@@ -355,34 +367,6 @@ static long sde_round_pixclk(struct msm_kms *kms, unsigned long rate,
return rate;
}
-static void sde_postopen(struct msm_kms *kms, struct drm_file *file)
-{
- struct sde_kms *sde_kms;
- struct msm_drm_private *priv;
-
- if (!kms)
- return;
-
- sde_kms = to_sde_kms(kms);
- priv = sde_kms->dev->dev_private;
-
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
-}
-
-static void sde_preclose(struct msm_kms *kms, struct drm_file *file)
-{
- struct sde_kms *sde_kms;
- struct msm_drm_private *priv;
-
- if (!kms)
- return;
-
- sde_kms = to_sde_kms(kms);
- priv = sde_kms->dev->dev_private;
-
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
-}
-
static void sde_destroy(struct msm_kms *kms)
{
struct sde_kms *sde_kms = to_sde_kms(kms);
@@ -401,12 +385,24 @@ static void sde_destroy(struct msm_kms *kms)
kfree(sde_kms);
}
+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;
+
+ for (i = 0; i < priv->num_crtcs; i++)
+ sde_crtc_cancel_pending_flip(priv->crtcs[i], file);
+}
+
static const struct msm_kms_funcs kms_funcs = {
.hw_init = sde_hw_init,
.irq_preinstall = sde_irq_preinstall,
.irq_postinstall = sde_irq_postinstall,
.irq_uninstall = sde_irq_uninstall,
.irq = sde_irq,
+ .preclose = sde_kms_preclose,
.prepare_fence = sde_kms_prepare_fence,
.prepare_commit = sde_prepare_commit,
.commit = sde_commit,
@@ -417,8 +413,6 @@ static const struct msm_kms_funcs kms_funcs = {
.check_modified_format = sde_format_check_modified_format,
.get_format = sde_get_msm_format,
.round_pixclk = sde_round_pixclk,
- .postopen = sde_postopen,
- .preclose = sde_preclose,
.destroy = sde_destroy,
};
@@ -791,7 +785,9 @@ struct msm_kms *sde_kms_init(struct drm_device *dev)
goto catalog_err;
}
- sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
+ if (IS_ERR_OR_NULL(sde_kms->hw_intr))
+ goto catalog_err;
/*
* Now we need to read the HW catalog and initialize resources such as
@@ -830,9 +826,7 @@ struct msm_kms *sde_kms_init(struct drm_device *dev)
*/
dev->mode_config.allow_fb_modifiers = true;
- sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
- if (IS_ERR_OR_NULL(sde_kms->hw_intr))
- goto clk_rate_err;
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
return &sde_kms->base;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index b4bf05f..17a4101 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -449,6 +449,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
*/
void sde_crtc_complete_commit(struct drm_crtc *crtc);
+void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
+
/**
* sde_encoder_get_hw_resources - Populate table of required hardware resources
* @encoder: encoder pointer