X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Frcar-du%2Frcar_du_crtc.c;h=7d0b8ef9bea21ca04a7fc678f3913dc1768615fd;hb=3515a9a20abc9248beadea0af468f753d67a9965;hp=25c7a998fc2cf075fe1ecb6c8fe603f439abab7f;hpb=d582cb792647dfc82345e53cff15b6ab87e2540f;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 25c7a998fc2c..7d0b8ef9bea2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include @@ -99,9 +101,13 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) clk_disable_unprepare(rcrtc->clock); } +/* ----------------------------------------------------------------------------- + * Hardware Setup + */ + static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { - const struct drm_display_mode *mode = &rcrtc->crtc.mode; + const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; unsigned long mode_clock = mode->clock * 1000; unsigned long clk; u32 value; @@ -187,9 +193,19 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, rcdu->dpad0_source = rcrtc->index; } -void rcar_du_crtc_update_planes(struct drm_crtc *crtc) +static unsigned int plane_zpos(struct rcar_du_plane *plane) +{ + return to_rcar_du_plane_state(plane->plane.state)->zpos; +} + +static const struct rcar_du_format_info * +plane_format(struct rcar_du_plane *plane) +{ + return to_rcar_du_plane_state(plane->plane.state)->format; +} + +static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) { - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; unsigned int num_planes = 0; unsigned int prio = 0; @@ -201,29 +217,30 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; unsigned int j; - if (plane->crtc != &rcrtc->crtc || !plane->enabled) + if (plane->plane.state->crtc != &rcrtc->crtc) continue; /* Insert the plane in the sorted planes array. */ for (j = num_planes++; j > 0; --j) { - if (planes[j-1]->zpos <= plane->zpos) + if (plane_zpos(planes[j-1]) <= plane_zpos(plane)) break; planes[j] = planes[j-1]; } planes[j] = plane; - prio += plane->format->planes * 4; + prio += plane_format(plane)->planes * 4; } for (i = 0; i < num_planes; ++i) { struct rcar_du_plane *plane = planes[i]; - unsigned int index = plane->hwindex; + struct drm_plane_state *state = plane->plane.state; + unsigned int index = to_rcar_du_plane_state(state)->hwindex; prio -= 4; dspr |= (index + 1) << prio; dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index); - if (plane->format->planes == 2) { + if (plane_format(plane)->planes == 2) { index = (index + 1) % 8; prio -= 4; @@ -236,8 +253,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) * with superposition controller 2. */ if (rcrtc->index % 2) { - u32 value = rcar_du_group_read(rcrtc->group, DPTSR); - /* The DPTSR register is updated when the display controller is * stopped. We thus need to restart the DU. Once again, sorry * for the flicker. One way to mitigate the issue would be to @@ -245,29 +260,104 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) * split, or through a module parameter). Flicker would then * occur only if we need to break the pre-association. */ - if (value != dptsr) { + mutex_lock(&rcrtc->group->lock); + if (rcar_du_group_read(rcrtc->group, DPTSR) != dptsr) { rcar_du_group_write(rcrtc->group, DPTSR, dptsr); if (rcrtc->group->used_crtcs) rcar_du_group_restart(rcrtc->group); } + mutex_unlock(&rcrtc->group->lock); } rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, dspr); } +/* ----------------------------------------------------------------------------- + * Page Flip + */ + +void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, + struct drm_file *file) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + /* Destroy the pending vertical blanking event associated with the + * pending page flip, if any, and disable vertical blanking interrupts. + */ + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + if (event && event->base.file_priv == file) { + rcrtc->event = NULL; + event->base.destroy(&event->base); + drm_crtc_vblank_put(&rcrtc->crtc); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) +{ + struct drm_pending_vblank_event *event; + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + event = rcrtc->event; + rcrtc->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (event == NULL) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, rcrtc->index, event); + wake_up(&rcrtc->flip_wait); + spin_unlock_irqrestore(&dev->event_lock, flags); + + drm_crtc_vblank_put(&rcrtc->crtc); +} + +static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc) +{ + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; + bool pending; + + spin_lock_irqsave(&dev->event_lock, flags); + pending = rcrtc->event != NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return pending; +} + +static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc) +{ + struct rcar_du_device *rcdu = rcrtc->group->dev; + + if (wait_event_timeout(rcrtc->flip_wait, + !rcar_du_crtc_page_flip_pending(rcrtc), + msecs_to_jiffies(50))) + return; + + dev_warn(rcdu->dev, "page flip timeout\n"); + + rcar_du_crtc_finish_page_flip(rcrtc); +} + +/* ----------------------------------------------------------------------------- + * Start/Stop and Suspend/Resume + */ + static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; bool interlaced; - unsigned int i; if (rcrtc->started) return; - if (WARN_ON(rcrtc->plane->format == NULL)) - return; - /* Set display off and background to black */ rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0)); rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0)); @@ -276,20 +366,8 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_crtc_set_display_timing(rcrtc); rcar_du_group_set_routing(rcrtc->group); - mutex_lock(&rcrtc->group->planes.lock); - rcrtc->plane->enabled = true; - rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcrtc->group->planes.lock); - - /* Setup planes. */ - for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; - - if (plane->crtc != crtc || !plane->enabled) - continue; - - rcar_du_plane_setup(plane); - } + /* Start with all planes disabled. */ + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0); /* Select master sync mode. This enables display operation in master * sync mode (with the HSYNC and VSYNC signals configured as outputs and @@ -302,6 +380,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_group_start_stop(rcrtc->group, true); + /* Turn vertical blanking interrupt reporting back on. */ + drm_crtc_vblank_on(crtc); + rcrtc->started = true; } @@ -312,10 +393,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) if (!rcrtc->started) return; - mutex_lock(&rcrtc->group->planes.lock); - rcrtc->plane->enabled = false; - rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcrtc->group->planes.lock); + /* Disable vertical blanking interrupt reporting. We first need to wait + * for page flip completion before stopping the CRTC as userspace + * expects page flips to eventually complete. + */ + rcar_du_crtc_wait_page_flip(rcrtc); + drm_crtc_vblank_off(crtc); /* Select switch sync mode. This stops display operation and configures * the HSYNC and VSYNC signals as inputs. @@ -335,196 +418,109 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { - if (rcrtc->dpms != DRM_MODE_DPMS_ON) + unsigned int i; + + if (!rcrtc->enabled) return; rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); -} - -static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) -{ - struct drm_crtc *crtc = &rcrtc->crtc; - - rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb); - rcar_du_plane_update_base(rcrtc->plane); -} - -static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; + /* Commit the planes state. */ + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; - if (rcrtc->dpms == mode) - return; + if (plane->plane.state->crtc != &rcrtc->crtc) + continue; - if (mode == DRM_MODE_DPMS_ON) { - rcar_du_crtc_get(rcrtc); - rcar_du_crtc_start(rcrtc); - } else { - rcar_du_crtc_stop(rcrtc); - rcar_du_crtc_put(rcrtc); + rcar_du_plane_setup(plane); } - rcrtc->dpms = mode; + rcar_du_crtc_update_planes(rcrtc); } -static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - /* TODO Fixup modes */ - return true; -} +/* ----------------------------------------------------------------------------- + * CRTC Functions + */ -static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) +static void rcar_du_crtc_enable(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - /* We need to access the hardware during mode set, acquire a reference - * to the CRTC. - */ - rcar_du_crtc_get(rcrtc); + if (rcrtc->enabled) + return; - /* Stop the CRTC and release the plane. Force the DPMS mode to off as a - * result. - */ - rcar_du_crtc_stop(rcrtc); - rcar_du_plane_release(rcrtc->plane); + rcar_du_crtc_get(rcrtc); + rcar_du_crtc_start(rcrtc); - rcrtc->dpms = DRM_MODE_DPMS_OFF; + rcrtc->enabled = true; } -static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static void rcar_du_crtc_disable(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct rcar_du_device *rcdu = rcrtc->group->dev; - const struct rcar_du_format_info *format; - int ret; - - format = rcar_du_format_info(crtc->primary->fb->pixel_format); - if (format == NULL) { - dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n", - crtc->primary->fb->pixel_format); - ret = -EINVAL; - goto error; - } - ret = rcar_du_plane_reserve(rcrtc->plane, format); - if (ret < 0) - goto error; - - rcrtc->plane->format = format; - - rcrtc->plane->src_x = x; - rcrtc->plane->src_y = y; - rcrtc->plane->width = mode->hdisplay; - rcrtc->plane->height = mode->vdisplay; + if (!rcrtc->enabled) + return; - rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb); + rcar_du_crtc_stop(rcrtc); + rcar_du_crtc_put(rcrtc); + rcrtc->enabled = false; rcrtc->outputs = 0; - - return 0; - -error: - /* There's no rollback/abort operation to clean up in case of error. We - * thus need to release the reference to the CRTC acquired in prepare() - * here. - */ - rcar_du_crtc_put(rcrtc); - return ret; } -static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) +static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - - /* We're done, restart the CRTC and set the DPMS mode to on. The - * reference to the DU acquired at prepare() time will thus be released - * by the DPMS handler (possibly called by the disable() handler). - */ - rcar_du_crtc_start(rcrtc); - rcrtc->dpms = DRM_MODE_DPMS_ON; + /* TODO Fixup modes */ + return true; } -static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) +static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) { + struct drm_pending_vblank_event *event = crtc->state->event; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct drm_device *dev = rcrtc->crtc.dev; + unsigned long flags; - rcrtc->plane->src_x = x; - rcrtc->plane->src_y = y; - - rcar_du_crtc_update_base(rcrtc); + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); - return 0; + spin_lock_irqsave(&dev->event_lock, flags); + rcrtc->event = event; + spin_unlock_irqrestore(&dev->event_lock, flags); + } } -static void rcar_du_crtc_disable(struct drm_crtc *crtc) +static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - rcar_du_plane_release(rcrtc->plane); + rcar_du_crtc_update_planes(rcrtc); } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { - .dpms = rcar_du_crtc_dpms, .mode_fixup = rcar_du_crtc_mode_fixup, - .prepare = rcar_du_crtc_mode_prepare, - .commit = rcar_du_crtc_mode_commit, - .mode_set = rcar_du_crtc_mode_set, - .mode_set_base = rcar_du_crtc_mode_set_base, .disable = rcar_du_crtc_disable, + .enable = rcar_du_crtc_enable, + .atomic_begin = rcar_du_crtc_atomic_begin, + .atomic_flush = rcar_du_crtc_atomic_flush, }; -void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, - struct drm_file *file) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - /* Destroy the pending vertical blanking event associated with the - * pending page flip, if any, and disable vertical blanking interrupts. - */ - spin_lock_irqsave(&dev->event_lock, flags); - event = rcrtc->event; - if (event && event->base.file_priv == file) { - rcrtc->event = NULL; - event->base.destroy(&event->base); - drm_vblank_put(dev, rcrtc->index); - } - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - event = rcrtc->event; - rcrtc->event = NULL; - spin_unlock_irqrestore(&dev->event_lock, flags); - - if (event == NULL) - return; - - spin_lock_irqsave(&dev->event_lock, flags); - drm_send_vblank_event(dev, rcrtc->index, event); - spin_unlock_irqrestore(&dev->event_lock, flags); +static const struct drm_crtc_funcs crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; - drm_vblank_put(dev, rcrtc->index); -} +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) { @@ -544,41 +540,9 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) return ret; } -static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) -{ - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - struct drm_device *dev = rcrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - if (rcrtc->event != NULL) { - spin_unlock_irqrestore(&dev->event_lock, flags); - return -EBUSY; - } - spin_unlock_irqrestore(&dev->event_lock, flags); - - crtc->primary->fb = fb; - rcar_du_crtc_update_base(rcrtc); - - if (event) { - event->pipe = rcrtc->index; - drm_vblank_get(dev, rcrtc->index); - spin_lock_irqsave(&dev->event_lock, flags); - rcrtc->event = event; - spin_unlock_irqrestore(&dev->event_lock, flags); - } - - return 0; -} - -static const struct drm_crtc_funcs crtc_funcs = { - .destroy = drm_crtc_cleanup, - .set_config = drm_crtc_helper_set_config, - .page_flip = rcar_du_crtc_page_flip, -}; +/* ----------------------------------------------------------------------------- + * Initialization + */ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { @@ -620,20 +584,24 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) return -EPROBE_DEFER; } + init_waitqueue_head(&rcrtc->flip_wait); + rcrtc->group = rgrp; rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; - rcrtc->dpms = DRM_MODE_DPMS_OFF; - rcrtc->plane = &rgrp->planes.planes[index % 2]; - - rcrtc->plane->crtc = crtc; + rcrtc->enabled = false; - ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs); + ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, + &rgrp->planes.planes[index % 2].plane, + NULL, &crtc_funcs); if (ret < 0) return ret; drm_crtc_helper_add(crtc, &crtc_helper_funcs); + /* Start with vertical blanking interrupt reporting disabled. */ + drm_crtc_vblank_off(crtc); + /* Register the interrupt handler. */ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { irq = platform_get_irq(pdev, index);