Commit 2d53a180 authored by Jyri Sarha's avatar Jyri Sarha

drm/tilcdc: Fix race from forced shutdown of crtc in unload

Fix race from forced shutdown of crtc in unload by adding internal
locking and a boolean telling if device is going to be shutdown.
Signed-off-by: default avatarJyri Sarha <jsarha@ti.com>
parent 9e79e062
...@@ -33,7 +33,9 @@ struct tilcdc_crtc { ...@@ -33,7 +33,9 @@ struct tilcdc_crtc {
struct drm_plane primary; struct drm_plane primary;
const struct tilcdc_panel_info *info; const struct tilcdc_panel_info *info;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
struct mutex enable_lock;
bool enabled; bool enabled;
bool shutdown;
wait_queue_head_t frame_done_wq; wait_queue_head_t frame_done_wq;
bool frame_done; bool frame_done;
spinlock_t irq_lock; spinlock_t irq_lock;
...@@ -158,9 +160,11 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc) ...@@ -158,9 +160,11 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
mutex_lock(&tilcdc_crtc->enable_lock);
if (tilcdc_crtc->enabled) if (tilcdc_crtc->enabled || tilcdc_crtc->shutdown) {
mutex_unlock(&tilcdc_crtc->enable_lock);
return; return;
}
pm_runtime_get_sync(dev->dev); pm_runtime_get_sync(dev->dev);
...@@ -175,17 +179,22 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc) ...@@ -175,17 +179,22 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
drm_crtc_vblank_on(crtc); drm_crtc_vblank_on(crtc);
tilcdc_crtc->enabled = true; tilcdc_crtc->enabled = true;
mutex_unlock(&tilcdc_crtc->enable_lock);
} }
void tilcdc_crtc_off(struct drm_crtc *crtc) static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown)
{ {
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_drm_private *priv = dev->dev_private;
if (!tilcdc_crtc->enabled) mutex_lock(&tilcdc_crtc->enable_lock);
if (shutdown)
tilcdc_crtc->shutdown = true;
if (!tilcdc_crtc->enabled) {
mutex_unlock(&tilcdc_crtc->enable_lock);
return; return;
}
tilcdc_crtc->frame_done = false; tilcdc_crtc->frame_done = false;
tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
...@@ -224,12 +233,18 @@ void tilcdc_crtc_off(struct drm_crtc *crtc) ...@@ -224,12 +233,18 @@ void tilcdc_crtc_off(struct drm_crtc *crtc)
tilcdc_crtc->last_vblank = ktime_set(0, 0); tilcdc_crtc->last_vblank = ktime_set(0, 0);
tilcdc_crtc->enabled = false; tilcdc_crtc->enabled = false;
mutex_unlock(&tilcdc_crtc->enable_lock);
} }
static void tilcdc_crtc_disable(struct drm_crtc *crtc) static void tilcdc_crtc_disable(struct drm_crtc *crtc)
{ {
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
tilcdc_crtc_off(crtc); tilcdc_crtc_off(crtc, false);
}
void tilcdc_crtc_shutdown(struct drm_crtc *crtc)
{
tilcdc_crtc_off(crtc, true);
} }
static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) static bool tilcdc_crtc_is_on(struct drm_crtc *crtc)
...@@ -857,6 +872,8 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) ...@@ -857,6 +872,8 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
if (ret < 0) if (ret < 0)
goto fail; goto fail;
mutex_init(&tilcdc_crtc->enable_lock);
init_waitqueue_head(&tilcdc_crtc->frame_done_wq); init_waitqueue_head(&tilcdc_crtc->frame_done_wq);
drm_flip_work_init(&tilcdc_crtc->unref_work, drm_flip_work_init(&tilcdc_crtc->unref_work,
......
...@@ -197,7 +197,7 @@ static void tilcdc_fini(struct drm_device *dev) ...@@ -197,7 +197,7 @@ static void tilcdc_fini(struct drm_device *dev)
struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_drm_private *priv = dev->dev_private;
if (priv->crtc) if (priv->crtc)
tilcdc_crtc_off(priv->crtc); tilcdc_crtc_shutdown(priv->crtc);
if (priv->is_registered) if (priv->is_registered)
drm_dev_unregister(dev); drm_dev_unregister(dev);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
#include <drm/drm_bridge.h>
/* Defaulting to pixel clock defined on AM335x */ /* Defaulting to pixel clock defined on AM335x */
#define TILCDC_DEFAULT_MAX_PIXELCLOCK 126000 #define TILCDC_DEFAULT_MAX_PIXELCLOCK 126000
...@@ -173,7 +174,7 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, ...@@ -173,7 +174,7 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,
bool simulate_vesa_sync); bool simulate_vesa_sync);
int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode);
int tilcdc_crtc_max_width(struct drm_crtc *crtc); int tilcdc_crtc_max_width(struct drm_crtc *crtc);
void tilcdc_crtc_off(struct drm_crtc *crtc); void tilcdc_crtc_shutdown(struct drm_crtc *crtc);
int tilcdc_crtc_update_fb(struct drm_crtc *crtc, int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event); struct drm_pending_vblank_event *event);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment