summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLloyd Atkinson <latkinso@codeaurora.org>2016-09-15 18:21:18 -0400
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-11 18:59:28 -0700
commit74b9a89af13ae475c07f01ff950c7bec410bc623 (patch)
tree715affb97ecbcc4feab72a8e9969e503fb41a8be /drivers/gpu
parentb0b958f2062abb09fa6fc6dbb9c5f0355552310c (diff)
drm/msm: teardown all modes in lastclose handler
Need to teardown all active crtcs during lastclose to avoid causing faults by turning off clocks on running hardware. To do this, implement the lastclose handler, and have it send in a disabling commit for all planes and crtcs. To support this, need to move the clock enable / disable out of the device open and close handlers, and into the use case enable / disable in the encoder, and into the vblank enable / disable handlers. As well, needed to remove fence refcounting logic from duplicate state function since the fd at that time is not valid, and the ref counting itself is unnecessary. Change-Id: I841bbbdec0e64bb76470007e09066663c13134e3 Signed-off-by: Lloyd Atkinson <latkinso@codeaurora.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c118
-rw-r--r--drivers/gpu/drm/msm/msm_kms.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c14
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c26
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c62
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.h2
6 files changed, 186 insertions, 38 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index d33cb70c6638..c822fa5a42f4 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -22,6 +22,8 @@
#include "display_manager.h"
#include "sde_wb.h"
+#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;
@@ -502,11 +504,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_file_private *ctx = file->driver_priv;
struct msm_kms *kms = priv->kms;
- if (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)
@@ -516,11 +527,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 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)
@@ -1358,6 +1469,7 @@ static struct drm_driver msm_driver = {
.unload = msm_unload,
.open = msm_open,
.preclose = msm_preclose,
+ .postclose = msm_postclose,
.lastclose = msm_lastclose,
.set_busid = drm_platform_set_busid,
.irq_handler = msm_irq,
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index f30df61ae307..803086c36373 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -80,6 +80,8 @@ struct msm_kms_funcs {
void (*postopen)(struct msm_kms *kms, struct drm_file *file);
/* cleanup: */
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);
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 08ec3c0d6ffc..46375e025598 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -829,6 +829,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);
@@ -923,6 +929,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 7a43e4496843..62ca3b721ee2 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 033b23088fb3..168475e8dbc9 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 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 7f2c7f3cf3b3..c7664d626723 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