Skip to content

Commit

Permalink
drm/tilcdc: Set framebuffer DMA address to HW only if CRTC is enabled
Browse files Browse the repository at this point in the history
Touching HW while clocks are off is a serious error and for instance
breaks suspend functionality. After this patch tilcdc_crtc_update_fb()
always updates the primary plane's framebuffer pointer, increases fb's
reference count and stores vblank event. tilcdc_crtc_update_fb() only
writes the fb's DMA address to HW if the crtc is enabled, as
tilcdc_crtc_enable() takes care of writing the address on enable.

This patch also refactors the tilcdc_crtc_update_fb() a bit. Number of
subsequent small changes had made it almost unreadable. There should
be no other functional changes but checking the CRTC's enable
state. However, the locking goes a bit differently and some of the
redundant checks have been removed in this new version.

The enable_lock should be enough to protect the access to
tilcdc_crtc->enabled. The irq_lock protects the access to last_vblank
and next_fb. The check for vrefresh and last_vblank being valid is
redundant, as the vrefresh should be always valid if the CRTC is
enabled and now last_vblank should be too, because it is initialized
to current time when CRTC raster is enabled. If for some reason the
values are not correctly initialized the division by zero warning is
quite appropriate.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
  • Loading branch information
Jyri Sarha committed Mar 14, 2017
1 parent abf8315 commit 11abbc9
Showing 1 changed file with 23 additions and 12 deletions.
35 changes: 23 additions & 12 deletions drivers/gpu/drm/tilcdc/tilcdc_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
unsigned long flags;

WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
mutex_lock(&tilcdc_crtc->enable_lock);
Expand All @@ -484,7 +485,17 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG,
LCDC_PALETTE_LOAD_MODE(DATA_ONLY),
LCDC_PALETTE_LOAD_MODE_MASK);

/* There is no real chance for a race here as the time stamp
* is taken before the raster DMA is started. The spin-lock is
* taken to have a memory barrier after taking the time-stamp
* and to avoid a context switch between taking the stamp and
* enabling the raster.
*/
spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
tilcdc_crtc->last_vblank = ktime_get();
tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);

drm_crtc_vblank_on(crtc);

Expand Down Expand Up @@ -539,7 +550,6 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown)
}

drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
tilcdc_crtc->last_vblank = 0;

tilcdc_crtc->enabled = false;
mutex_unlock(&tilcdc_crtc->enable_lock);
Expand Down Expand Up @@ -602,7 +612,6 @@ int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
{
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
struct drm_device *dev = crtc->dev;
unsigned long flags;

WARN_ON(!drm_modeset_is_locked(&crtc->mutex));

Expand All @@ -614,28 +623,30 @@ int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
drm_framebuffer_reference(fb);

crtc->primary->fb = fb;
tilcdc_crtc->event = event;

spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
mutex_lock(&tilcdc_crtc->enable_lock);

if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) {
if (tilcdc_crtc->enabled) {
unsigned long flags;
ktime_t next_vblank;
s64 tdiff;

next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
1000000 / crtc->hwmode.vrefresh);
spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);

next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
1000000 / crtc->hwmode.vrefresh);
tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));

if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US)
tilcdc_crtc->next_fb = fb;
}

if (tilcdc_crtc->next_fb != fb)
set_scanout(crtc, fb);
else
set_scanout(crtc, fb);

tilcdc_crtc->event = event;
spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
}

spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
mutex_unlock(&tilcdc_crtc->enable_lock);

return 0;
}
Expand Down

0 comments on commit 11abbc9

Please sign in to comment.