X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Frcar-du%2Frcar_du_crtc.c;h=7f5ae0269a618638dee585ec424008b25e8e8cea;hb=36693f3c3254d9361095f6b225d5e69bd8da5c32;hp=23cc910951f430a279b6b01ae88e5f03d7de3e74;hpb=940288b6a560710d4c63bc84d9570779dd7dec2b;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 23cc910951f4..7f5ae0269a61 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -74,39 +74,81 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) if (ret < 0) return ret; + ret = clk_prepare_enable(rcrtc->extclock); + if (ret < 0) + goto error_clock; + ret = rcar_du_group_get(rcrtc->group); if (ret < 0) - clk_disable_unprepare(rcrtc->clock); + goto error_group; + + return 0; +error_group: + clk_disable_unprepare(rcrtc->extclock); +error_clock: + clk_disable_unprepare(rcrtc->clock); return ret; } static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) { rcar_du_group_put(rcrtc->group); + + clk_disable_unprepare(rcrtc->extclock); 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; + unsigned long mode_clock = mode->clock * 1000; unsigned long clk; u32 value; + u32 escr; u32 div; - /* Dot clock */ + /* Compute the clock divisor and select the internal or external dot + * clock based on the requested frequency. + */ clk = clk_get_rate(rcrtc->clock); - div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); + div = DIV_ROUND_CLOSEST(clk, mode_clock); div = clamp(div, 1U, 64U) - 1; + escr = div | ESCR_DCLKSEL_CLKS; + + if (rcrtc->extclock) { + unsigned long extclk; + unsigned long extrate; + unsigned long rate; + u32 extdiv; + + extclk = clk_get_rate(rcrtc->extclock); + extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); + extdiv = clamp(extdiv, 1U, 64U) - 1; + + rate = clk / (div + 1); + extrate = extclk / (extdiv + 1); + + if (abs((long)extrate - (long)mode_clock) < + abs((long)rate - (long)mode_clock)) { + dev_dbg(rcrtc->group->dev->dev, + "crtc%u: using external clock\n", rcrtc->index); + escr = extdiv | ESCR_DCLKSEL_DCLKIN; + } + } rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, - ESCR_DCLKSEL_CLKS | div); + escr); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); /* Signal polarities */ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL) - | DSMR_DIPM_DE; + | DSMR_DIPM_DE | DSMR_CSPM; rcar_du_crtc_write(rcrtc, DSMR, value); /* Display timings */ @@ -117,12 +159,15 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) mode->hsync_start - 1); rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1); - rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2); - rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end + - mode->vdisplay - 2); - rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end + - mode->vsync_start - 1); - rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1); + rcar_du_crtc_write(rcrtc, VDSR, mode->crtc_vtotal - + mode->crtc_vsync_end - 2); + rcar_du_crtc_write(rcrtc, VDER, mode->crtc_vtotal - + mode->crtc_vsync_end + + mode->crtc_vdisplay - 2); + rcar_du_crtc_write(rcrtc, VSPR, mode->crtc_vtotal - + mode->crtc_vsync_end + + mode->crtc_vsync_start - 1); + rcar_du_crtc_write(rcrtc, VCR, mode->crtc_vtotal - 1); rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start); rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); @@ -139,9 +184,10 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, */ rcrtc->outputs |= BIT(output); - /* Store RGB routing to DPAD0 for R8A7790. */ - if (rcar_du_has(rcdu, RCAR_DU_FEATURE_DEFR8) && - output == RCAR_DU_OUTPUT_DPAD0) + /* Store RGB routing to DPAD0, the hardware will be configured when + * starting the CRTC. + */ + if (output == RCAR_DU_OUTPUT_DPAD0) rcdu->dpad0_source = rcrtc->index; } @@ -214,9 +260,87 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) 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_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); + wake_up(&rcrtc->flip_wait); + spin_unlock_irqrestore(&dev->event_lock, flags); + + drm_vblank_put(dev, rcrtc->index); +} + +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) @@ -252,7 +376,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) * sync mode (with the HSYNC and VSYNC signals configured as outputs and * actively driven). */ - rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER); + interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE; + rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK | DSYSR_SCM_MASK, + (interlaced ? DSYSR_SCM_INT_VIDEO : 0) | + DSYSR_TVM_MASTER); rcar_du_group_start_stop(rcrtc->group, true); @@ -266,6 +393,11 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) if (!rcrtc->started) return; + /* Wait for page flip completion before stopping the CRTC as userspace + * excepts page flips to eventually complete. + */ + rcar_du_crtc_wait_page_flip(rcrtc); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = false; rcar_du_crtc_update_planes(crtc); @@ -304,10 +436,17 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) rcar_du_plane_update_base(rcrtc->plane); } +/* ----------------------------------------------------------------------------- + * CRTC Functions + */ + 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; + if (rcrtc->dpms == mode) return; @@ -436,65 +575,6 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .disable = rcar_du_crtc_disable, }; -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); - - drm_vblank_put(dev, rcrtc->index); -} - -static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) -{ - struct rcar_du_crtc *rcrtc = arg; - irqreturn_t ret = IRQ_NONE; - u32 status; - - status = rcar_du_crtc_read(rcrtc, DSSR); - rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); - - if (status & DSSR_VBK) { - drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); - rcar_du_crtc_finish_page_flip(rcrtc); - ret = IRQ_HANDLED; - } - - return ret; -} - static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -531,6 +611,32 @@ static const struct drm_crtc_funcs crtc_funcs = { .page_flip = rcar_du_crtc_page_flip, }; +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) +{ + struct rcar_du_crtc *rcrtc = arg; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = rcar_du_crtc_read(rcrtc, DSSR); + rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); + + if (status & DSSR_FRM) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + ret = IRQ_HANDLED; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Initialization + */ + int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { static const unsigned int mmio_offsets[] = { @@ -542,12 +648,13 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; unsigned int irqflags; - char clk_name[5]; + struct clk *clk; + char clk_name[9]; char *name; int irq; int ret; - /* Get the CRTC clock. */ + /* Get the CRTC clock and the optional external clock. */ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { sprintf(clk_name, "du.%u", index); name = clk_name; @@ -561,6 +668,17 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) return PTR_ERR(rcrtc->clock); } + sprintf(clk_name, "dclkin.%u", index); + clk = devm_clk_get(rcdu->dev, clk_name); + if (!IS_ERR(clk)) { + rcrtc->extclock = clk; + } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) { + dev_info(rcdu->dev, "can't get external clock %u\n", index); + return -EPROBE_DEFER; + } + + init_waitqueue_head(&rcrtc->flip_wait); + rcrtc->group = rgrp; rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index;