From 6118efe5968ce65411996f3a40d9cc0487efe46f Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Fri, 23 May 2014 13:45:51 -0700 Subject: drm/i915: move psr_setup_done to psr struct "Because our driver assumes only one panel is PSR capable, and we already have other PSR information on dev_priv instead of intel_dp. If we ever support multiple PSR panels, we'll have to move struct i915_psr to intel_dp anyway." (by Paulo) v2: Avoid more than one setup. Removing initialization and trusting allocation. (By Paulo Zanoni). v3: rebase. v4: Adding comment. Reviewed-by: Paulo Zanoni Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 49414d3..ef7ab0a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -638,6 +638,7 @@ struct i915_drrs { struct i915_psr { bool sink_support; bool source_ok; + bool setup_done; }; enum intel_pch { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 52fda95..82edfbc 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1665,7 +1665,7 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; struct edp_vsc_psr psr_vsc; - if (intel_dp->psr_setup_done) + if (dev_priv->psr.setup_done) return; /* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */ @@ -1680,7 +1680,7 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp) I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP | EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP); - intel_dp->psr_setup_done = true; + dev_priv->psr.setup_done = true; } static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp) @@ -4279,8 +4279,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_aux_init(intel_dp, intel_connector); - intel_dp->psr_setup_done = false; - if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) { drm_dp_aux_unregister(&intel_dp->aux); if (is_edp(intel_dp)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bda0ae3..fbf032d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -537,7 +537,6 @@ struct intel_dp { unsigned long last_power_cycle; unsigned long last_power_on; unsigned long last_backlight_off; - bool psr_setup_done; bool use_tps3; struct intel_connector *attached_connector; -- cgit v0.10.2 From 38807746fa2ce44b79957ff07813d10fcaf3d311 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Fri, 23 May 2014 21:00:15 +0530 Subject: drm/i915/chv: Enable Render Standby (RC6) for Cherryview v2: Configure PCBR if BIOS fails allocate pcbr (deepak) v3: Fix PCBR condition check during CHV RC6 Enable flag set v4: Fixup PCBR comment msg. (Chris) Rebase against latest code (Deak) Fixup Spurious hunk (Ben) v5: Fix PCBR and commentis msg (mika) v6: Rebase patch on latest nightly (Deepak) Signed-off-by: Deepak S Acked-by: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e691b30..15653d4 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1167,6 +1167,8 @@ enum punit_power_well { #define VLV_IMR (VLV_DISPLAY_BASE + 0x20a8) #define VLV_ISR (VLV_DISPLAY_BASE + 0x20ac) #define VLV_PCBR (VLV_DISPLAY_BASE + 0x2120) +#define VLV_PCBR_ADDR_SHIFT 12 + #define DISPLAY_PLANE_FLIP_PENDING(plane) (1<<(11-(plane))) /* A and B only */ #define EIR 0x020b0 #define EMR 0x020b4 diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d1e53ab..9209290 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3341,6 +3341,13 @@ static void gen6_disable_rps(struct drm_device *dev) gen6_disable_rps_interrupts(dev); } +static void cherryview_disable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(GEN6_RC_CONTROL, 0); +} + static void valleyview_disable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3763,6 +3770,35 @@ static void valleyview_check_pctx(struct drm_i915_private *dev_priv) dev_priv->vlv_pctx->stolen->start); } + +/* Check that the pcbr address is not empty. */ +static void cherryview_check_pctx(struct drm_i915_private *dev_priv) +{ + unsigned long pctx_addr = I915_READ(VLV_PCBR) & ~4095; + + WARN_ON((pctx_addr >> VLV_PCBR_ADDR_SHIFT) == 0); +} + +static void cherryview_setup_pctx(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long pctx_paddr, paddr; + struct i915_gtt *gtt = &dev_priv->gtt; + u32 pcbr; + int pctx_size = 32*1024; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + pcbr = I915_READ(VLV_PCBR); + if ((pcbr >> VLV_PCBR_ADDR_SHIFT) == 0) { + paddr = (dev_priv->mm.stolen_base + + (gtt->stolen_size - pctx_size)); + + pctx_paddr = (paddr & (~4095)); + I915_WRITE(VLV_PCBR, pctx_paddr); + } +} + static void valleyview_setup_pctx(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3852,11 +3888,70 @@ static void valleyview_init_gt_powersave(struct drm_device *dev) mutex_unlock(&dev_priv->rps.hw_lock); } +static void cherryview_init_gt_powersave(struct drm_device *dev) +{ + cherryview_setup_pctx(dev); +} + static void valleyview_cleanup_gt_powersave(struct drm_device *dev) { valleyview_cleanup_pctx(dev); } +static void cherryview_enable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring; + u32 gtfifodbg, rc6_mode = 0, pcbr; + int i; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + gtfifodbg = I915_READ(GTFIFODBG); + if (gtfifodbg) { + DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n", + gtfifodbg); + I915_WRITE(GTFIFODBG, gtfifodbg); + } + + cherryview_check_pctx(dev_priv); + + /* 1a & 1b: Get forcewake during program sequence. Although the driver + * hasn't enabled a state yet where we need forcewake, BIOS may have.*/ + gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); + + /* 2a: Program RC6 thresholds.*/ + I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16); + I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */ + I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); /* 25 * 1280ns */ + + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); + I915_WRITE(GEN6_RC_SLEEP, 0); + + I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */ + + /* allows RC6 residency counter to work */ + I915_WRITE(VLV_COUNTER_CONTROL, + _MASKED_BIT_ENABLE(VLV_COUNT_RANGE_HIGH | + VLV_MEDIA_RC6_COUNT_EN | + VLV_RENDER_RC6_COUNT_EN)); + + /* For now we assume BIOS is allocating and populating the PCBR */ + pcbr = I915_READ(VLV_PCBR); + + DRM_DEBUG_DRIVER("PCBR offset : 0x%x\n", pcbr); + + /* 3: Enable RC6 */ + if ((intel_enable_rc6(dev) & INTEL_RC6_ENABLE) && + (pcbr >> VLV_PCBR_ADDR_SHIFT)) + rc6_mode = GEN6_RC_CTL_EI_MODE(1); + + I915_WRITE(GEN6_RC_CONTROL, rc6_mode); + + gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); +} + static void valleyview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -4665,13 +4760,17 @@ void intel_init_gt_powersave(struct drm_device *dev) { i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6); - if (IS_VALLEYVIEW(dev)) + if (IS_CHERRYVIEW(dev)) + cherryview_init_gt_powersave(dev); + else if (IS_VALLEYVIEW(dev)) valleyview_init_gt_powersave(dev); } void intel_cleanup_gt_powersave(struct drm_device *dev) { - if (IS_VALLEYVIEW(dev)) + if (IS_CHERRYVIEW(dev)) + return; + else if (IS_VALLEYVIEW(dev)) valleyview_cleanup_gt_powersave(dev); } @@ -4685,13 +4784,15 @@ void intel_disable_gt_powersave(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); ironlake_disable_rc6(dev); - } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) { + } else if (INTEL_INFO(dev)->gen >= 6) { if (cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work)) intel_runtime_pm_put(dev_priv); cancel_work_sync(&dev_priv->rps.work); mutex_lock(&dev_priv->rps.hw_lock); - if (IS_VALLEYVIEW(dev)) + if (IS_CHERRYVIEW(dev)) + cherryview_disable_rps(dev); + else if (IS_VALLEYVIEW(dev)) valleyview_disable_rps(dev); else gen6_disable_rps(dev); @@ -4709,7 +4810,9 @@ static void intel_gen6_powersave_work(struct work_struct *work) mutex_lock(&dev_priv->rps.hw_lock); - if (IS_VALLEYVIEW(dev)) { + if (IS_CHERRYVIEW(dev)) { + cherryview_enable_rps(dev); + } else if (IS_VALLEYVIEW(dev)) { valleyview_enable_rps(dev); } else if (IS_BROADWELL(dev)) { gen8_enable_rps(dev); @@ -4734,7 +4837,7 @@ void intel_enable_gt_powersave(struct drm_device *dev) ironlake_enable_rc6(dev); intel_init_emon(dev); mutex_unlock(&dev->struct_mutex); - } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) { + } else if (INTEL_INFO(dev)->gen >= 6) { /* * PCU communication is slow and this doesn't need to be * done at any specific time, so do this out of our fast path -- cgit v0.10.2 From 1938e59ab719f38f07cb02120349c480857ce992 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Fri, 23 May 2014 21:00:16 +0530 Subject: drm/i915/chv: Added CHV specific register read and write and Streamline CHV forcewake stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support to individually control Media/Render well based on the register access. Add CHV specific write function to habdle difference between registers that are sadowed vs those that need forcewake even for writes. Streamline the CHV forcewake functions just like was done for VLV. This will also fix a bug in accessing the common well registers, where we'd end up trying to wake up the wells too many times since we'd call force_wake_get/put twice per register access, with FORCEFAKE_ALL both times. v2: Drop write FIFO for CHV and add comman well forcewake (Ville) Re-factor CHV/VLV Forcewake offsets (Ben) v3: Fix for decrementing fw count in chv read/write. (Deepak) v4: Squash the patches (Mika) Reviewed-by: Mika Kuoppala [vsyrjala: Move the register range macros into intel_uncore.c] Signed-off-by: Ville Syrjälä Signed-off-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 79cba59..a0e3586 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -468,16 +468,43 @@ void assert_force_wake_inactive(struct drm_i915_private *dev_priv) #define NEEDS_FORCE_WAKE(dev_priv, reg) \ ((reg) < 0x40000 && (reg) != FORCEWAKE) -#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \ - (((reg) >= 0x2000 && (reg) < 0x4000) ||\ - ((reg) >= 0x5000 && (reg) < 0x8000) ||\ - ((reg) >= 0xB000 && (reg) < 0x12000) ||\ - ((reg) >= 0x2E000 && (reg) < 0x30000)) +#define REG_RANGE(reg, start, end) ((reg) >= (start) && (reg) < (end)) -#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\ - (((reg) >= 0x12000 && (reg) < 0x14000) ||\ - ((reg) >= 0x22000 && (reg) < 0x24000) ||\ - ((reg) >= 0x30000 && (reg) < 0x40000)) +#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \ + (REG_RANGE((reg), 0x2000, 0x4000) || \ + REG_RANGE((reg), 0x5000, 0x8000) || \ + REG_RANGE((reg), 0xB000, 0x12000) || \ + REG_RANGE((reg), 0x2E000, 0x30000)) + +#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg) \ + (REG_RANGE((reg), 0x12000, 0x14000) || \ + REG_RANGE((reg), 0x22000, 0x24000) || \ + REG_RANGE((reg), 0x30000, 0x40000)) + +#define FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg) \ + (REG_RANGE((reg), 0x2000, 0x4000) || \ + REG_RANGE((reg), 0x5000, 0x8000) || \ + REG_RANGE((reg), 0x8300, 0x8500) || \ + REG_RANGE((reg), 0xB000, 0xC000) || \ + REG_RANGE((reg), 0xE000, 0xE800)) + +#define FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg) \ + (REG_RANGE((reg), 0x8800, 0x8900) || \ + REG_RANGE((reg), 0xD000, 0xD800) || \ + REG_RANGE((reg), 0x12000, 0x14000) || \ + REG_RANGE((reg), 0x1A000, 0x1C000) || \ + REG_RANGE((reg), 0x1E800, 0x1EA00) || \ + REG_RANGE((reg), 0x30000, 0x40000)) + +#define FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg) \ + (REG_RANGE((reg), 0x4000, 0x5000) || \ + REG_RANGE((reg), 0x8000, 0x8300) || \ + REG_RANGE((reg), 0x8500, 0x8600) || \ + REG_RANGE((reg), 0x9000, 0xB000) || \ + REG_RANGE((reg), 0xC000, 0xC800) || \ + REG_RANGE((reg), 0xF000, 0x10000) || \ + REG_RANGE((reg), 0x14000, 0x14400) || \ + REG_RANGE((reg), 0x22000, 0x24000)) static void ilk_dummy_write(struct drm_i915_private *dev_priv) @@ -572,7 +599,35 @@ vlv_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ REG_READ_FOOTER; \ } +#define __chv_read(x) \ +static u##x \ +chv_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ + unsigned fwengine = 0; \ + REG_READ_HEADER(x); \ + if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_rendercount == 0) \ + fwengine = FORCEWAKE_RENDER; \ + } else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_mediacount == 0) \ + fwengine = FORCEWAKE_MEDIA; \ + } else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_rendercount == 0) \ + fwengine |= FORCEWAKE_RENDER; \ + if (dev_priv->uncore.fw_mediacount == 0) \ + fwengine |= FORCEWAKE_MEDIA; \ + } \ + if (fwengine) \ + dev_priv->uncore.funcs.force_wake_get(dev_priv, fwengine); \ + val = __raw_i915_read##x(dev_priv, reg); \ + if (fwengine) \ + dev_priv->uncore.funcs.force_wake_put(dev_priv, fwengine); \ + REG_READ_FOOTER; \ +} +__chv_read(8) +__chv_read(16) +__chv_read(32) +__chv_read(64) __vlv_read(8) __vlv_read(16) __vlv_read(32) @@ -590,6 +645,7 @@ __gen4_read(16) __gen4_read(32) __gen4_read(64) +#undef __chv_read #undef __vlv_read #undef __gen6_read #undef __gen5_read @@ -694,6 +750,38 @@ gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace REG_WRITE_FOOTER; \ } +#define __chv_write(x) \ +static void \ +chv_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \ + unsigned fwengine = 0; \ + bool shadowed = is_gen8_shadowed(dev_priv, reg); \ + REG_WRITE_HEADER; \ + if (!shadowed) { \ + if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_rendercount == 0) \ + fwengine = FORCEWAKE_RENDER; \ + } else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_mediacount == 0) \ + fwengine = FORCEWAKE_MEDIA; \ + } else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg)) { \ + if (dev_priv->uncore.fw_rendercount == 0) \ + fwengine |= FORCEWAKE_RENDER; \ + if (dev_priv->uncore.fw_mediacount == 0) \ + fwengine |= FORCEWAKE_MEDIA; \ + } \ + } \ + if (fwengine) \ + dev_priv->uncore.funcs.force_wake_get(dev_priv, fwengine); \ + __raw_i915_write##x(dev_priv, reg, val); \ + if (fwengine) \ + dev_priv->uncore.funcs.force_wake_put(dev_priv, fwengine); \ + REG_WRITE_FOOTER; \ +} + +__chv_write(8) +__chv_write(16) +__chv_write(32) +__chv_write(64) __gen8_write(8) __gen8_write(16) __gen8_write(32) @@ -715,6 +803,7 @@ __gen4_write(16) __gen4_write(32) __gen4_write(64) +#undef __chv_write #undef __gen8_write #undef __hsw_write #undef __gen6_write @@ -778,14 +867,26 @@ void intel_uncore_init(struct drm_device *dev) switch (INTEL_INFO(dev)->gen) { default: - dev_priv->uncore.funcs.mmio_writeb = gen8_write8; - dev_priv->uncore.funcs.mmio_writew = gen8_write16; - dev_priv->uncore.funcs.mmio_writel = gen8_write32; - dev_priv->uncore.funcs.mmio_writeq = gen8_write64; - dev_priv->uncore.funcs.mmio_readb = gen6_read8; - dev_priv->uncore.funcs.mmio_readw = gen6_read16; - dev_priv->uncore.funcs.mmio_readl = gen6_read32; - dev_priv->uncore.funcs.mmio_readq = gen6_read64; + if (IS_CHERRYVIEW(dev)) { + dev_priv->uncore.funcs.mmio_writeb = chv_write8; + dev_priv->uncore.funcs.mmio_writew = chv_write16; + dev_priv->uncore.funcs.mmio_writel = chv_write32; + dev_priv->uncore.funcs.mmio_writeq = chv_write64; + dev_priv->uncore.funcs.mmio_readb = chv_read8; + dev_priv->uncore.funcs.mmio_readw = chv_read16; + dev_priv->uncore.funcs.mmio_readl = chv_read32; + dev_priv->uncore.funcs.mmio_readq = chv_read64; + + } else { + dev_priv->uncore.funcs.mmio_writeb = gen8_write8; + dev_priv->uncore.funcs.mmio_writew = gen8_write16; + dev_priv->uncore.funcs.mmio_writel = gen8_write32; + dev_priv->uncore.funcs.mmio_writeq = gen8_write64; + dev_priv->uncore.funcs.mmio_readb = gen6_read8; + dev_priv->uncore.funcs.mmio_readw = gen6_read16; + dev_priv->uncore.funcs.mmio_readl = gen6_read32; + dev_priv->uncore.funcs.mmio_readq = gen6_read64; + } break; case 7: case 6: -- cgit v0.10.2 From f98551ae9222ce6cbfe5e9644fa129acd8264a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 22 May 2014 17:48:06 +0300 Subject: drm/i915: Keep vblank interrupts enabled while enabling/disabling planes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because of the upcoming vblank interrupt driven watermark update mechanism we will have use for vblank interrupts during plane enabling/disabling. So don't call drm_vblank_off() until planes are off, and call drm_vblank_on() just before we start to enable the planes. v2: Pimp commit message (Paulo) Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index efd3cf5..0c693d8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3883,6 +3883,8 @@ static void intel_crtc_enable_planes(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; + drm_vblank_on(dev, pipe); + intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); /* The fixup needs to happen before cursor is enabled */ @@ -3908,7 +3910,6 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) int plane = intel_crtc->plane; intel_crtc_wait_for_pending_flips(crtc); - drm_crtc_vblank_off(crtc); if (dev_priv->fbc.plane == plane) intel_disable_fbc(dev); @@ -3919,6 +3920,8 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, false); intel_disable_planes(crtc); intel_disable_primary_hw_plane(dev_priv, plane, pipe); + + drm_vblank_off(dev, pipe); } static void ironlake_crtc_enable(struct drm_crtc *crtc) @@ -3997,8 +4000,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) cpt_verify_modeset(dev, intel_crtc->pipe); intel_crtc_enable_planes(crtc); - - drm_crtc_vblank_on(crtc); } /* IPS only exists on ULT machines and is tied to pipe A. */ @@ -4112,8 +4113,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) * to change the workaround. */ haswell_mode_set_planes_workaround(intel_crtc); intel_crtc_enable_planes(crtc); - - drm_crtc_vblank_on(crtc); } static void ironlake_pfit_disable(struct intel_crtc *crtc) @@ -4623,8 +4622,6 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_crtc_enable_planes(crtc); - drm_crtc_vblank_on(crtc); - /* Underruns don't raise interrupts, so check manually. */ i9xx_check_fifo_underruns(dev); } @@ -4717,8 +4714,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) if (IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); - drm_crtc_vblank_on(crtc); - /* Underruns don't raise interrupts, so check manually. */ i9xx_check_fifo_underruns(dev); } -- cgit v0.10.2 From 2b6b3a09915f852ecca4317af6b55d2610e6b8d6 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Tue, 27 May 2014 15:59:30 +0530 Subject: drm/i915/chv: Enable RPS (Turbo) for Cherryview v2: Disable media turbo and Add DOWN_IDLE_AVG support (Ville) v3: Mass rename of the dev_priv->rps variables in upstream. v4: Rebase against latest code. (Deepak) v5: Rebase against latest nightly code. (Deepak) v6: Rename the variables to match the spec (Mika) v7: change min/max freq variable naming to match spec (Mika) Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 15653d4..53db3a2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -529,6 +529,16 @@ enum punit_power_well { #define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ #define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ +#define PUNIT_GPU_STATUS_REG 0xdb +#define PUNIT_GPU_STATUS_MAX_FREQ_SHIFT 16 +#define PUNIT_GPU_STATUS_MAX_FREQ_MASK 0xff +#define PUNIT_GPU_STATIS_GFX_MIN_FREQ_SHIFT 8 +#define PUNIT_GPU_STATUS_GFX_MIN_FREQ_MASK 0xff + +#define PUNIT_GPU_DUTYCYCLE_REG 0xdf +#define PUNIT_GPU_DUTYCYCLE_RPE_FREQ_SHIFT 8 +#define PUNIT_GPU_DUTYCYCLE_RPE_FREQ_MASK 0xff + #define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c #define FB_GFX_MAX_FREQ_FUSE_SHIFT 3 #define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8 @@ -933,6 +943,7 @@ enum punit_power_well { #define SANDYBRIDGE_FENCE_PITCH_SHIFT 32 #define GEN7_FENCE_MAX_PITCH_VAL 0x0800 + /* control register for cpu gtt access */ #define TILECTL 0x101000 #define TILECTL_SWZCTL (1 << 0) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 9209290..ee27d74 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3731,6 +3731,35 @@ void gen6_update_ring_freq(struct drm_device *dev) mutex_unlock(&dev_priv->rps.hw_lock); } +int cherryview_rps_max_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rp0; + + val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG); + rp0 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK; + + return rp0; +} + +static int cherryview_rps_rpe_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rpe; + + val = vlv_punit_read(dev_priv, PUNIT_GPU_DUTYCYCLE_REG); + rpe = (val >> PUNIT_GPU_DUTYCYCLE_RPE_FREQ_SHIFT) & PUNIT_GPU_DUTYCYCLE_RPE_FREQ_MASK; + + return rpe; +} + +int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rpn; + + val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG); + rpn = (val >> PUNIT_GPU_STATIS_GFX_MIN_FREQ_SHIFT) & PUNIT_GPU_STATUS_GFX_MIN_FREQ_MASK; + return rpn; +} + int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; @@ -3890,7 +3919,36 @@ static void valleyview_init_gt_powersave(struct drm_device *dev) static void cherryview_init_gt_powersave(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + cherryview_setup_pctx(dev); + + mutex_lock(&dev_priv->rps.hw_lock); + + dev_priv->rps.max_freq = cherryview_rps_max_freq(dev_priv); + dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; + DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq), + dev_priv->rps.max_freq); + + dev_priv->rps.efficient_freq = cherryview_rps_rpe_freq(dev_priv); + DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), + dev_priv->rps.efficient_freq); + + dev_priv->rps.min_freq = cherryview_rps_min_freq(dev_priv); + DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), + dev_priv->rps.min_freq); + + /* Preserve min/max settings in case of re-init */ + if (dev_priv->rps.max_freq_softlimit == 0) + dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; + + if (dev_priv->rps.min_freq_softlimit == 0) + dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; + + mutex_unlock(&dev_priv->rps.hw_lock); } static void valleyview_cleanup_gt_powersave(struct drm_device *dev) @@ -3902,7 +3960,7 @@ static void cherryview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; - u32 gtfifodbg, rc6_mode = 0, pcbr; + u32 gtfifodbg, val, rc6_mode = 0, pcbr; int i; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); @@ -3949,6 +4007,38 @@ static void cherryview_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RC_CONTROL, rc6_mode); + /* 4 Program defaults and thresholds for RPS*/ + I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); + I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000); + I915_WRITE(GEN6_RP_UP_EI, 66000); + I915_WRITE(GEN6_RP_DOWN_EI, 350000); + + I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + + /* 5: Enable RPS */ + I915_WRITE(GEN6_RP_CONTROL, + GEN6_RP_MEDIA_HW_NORMAL_MODE | + GEN6_RP_MEDIA_IS_GFX | + GEN6_RP_ENABLE | + GEN6_RP_UP_BUSY_AVG | + GEN6_RP_DOWN_IDLE_AVG); + + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); + + DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); + DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); + + dev_priv->rps.cur_freq = (val >> 8) & 0xff; + DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + dev_priv->rps.cur_freq); + + DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), + dev_priv->rps.efficient_freq); + + valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq); + gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); } -- cgit v0.10.2 From 3f4e349587cd840f01bd24ab6911bbcc790f2b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 23 May 2014 21:00:18 +0530 Subject: drm/i915/chv: CHV doesn't need WaRsForcewakeWaitTC0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip __gen6_gt_wait_for_thread_c0() on CHV. Reviewed-by: Mika Kuoppala Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index a0e3586..bc18444 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -231,8 +231,8 @@ static void __vlv_force_wake_get(struct drm_i915_private *dev_priv, } /* WaRsForcewakeWaitTC0:vlv */ - __gen6_gt_wait_for_thread_c0(dev_priv); - + if (!IS_CHERRYVIEW(dev_priv->dev)) + __gen6_gt_wait_for_thread_c0(dev_priv); } static void __vlv_force_wake_put(struct drm_i915_private *dev_priv, -- cgit v0.10.2 From ab53c267f28f1830c660aad1a8cc063b34fa1cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 23 May 2014 21:00:19 +0530 Subject: drm/i915/chv: Skip gen6_gt_check_fifodbg() on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHV uses the gen8 shadow register mechanism so we shouldn't be checking the GT FIFO status. This effectively removes the posting read, so add an explicit posting read using FORCEWAKE_ACK_VLV (which is what use in vlv_forcewake_reset()). Reviewed-by: Mika Kuoppala Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index bc18444..871c284 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -250,9 +250,10 @@ static void __vlv_force_wake_put(struct drm_i915_private *dev_priv, __raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); - /* The below doubles as a POSTING_READ */ - gen6_gt_check_fifodbg(dev_priv); - + /* something from same cacheline, but !FORCEWAKE_VLV */ + __raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV); + if (!IS_CHERRYVIEW(dev_priv->dev)) + gen6_gt_check_fifodbg(dev_priv); } static void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine) -- cgit v0.10.2 From 13a5660c137e4c9ca88e0bb20d518eff016bcfbc Mon Sep 17 00:00:00 2001 From: Deepak S Date: Fri, 23 May 2014 21:00:21 +0530 Subject: drm/i915/chv: Freq(opcode) request for CHV. On CHV, All the freq request should be even. So, we need to make sure we request the opcode accordingly. v2: Avoid vairable for freq request (ville) Signed-off-by: Deepak S Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cf288a9..218ef08 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1252,8 +1252,10 @@ static void gen6_pm_rps_work(struct work_struct *work) if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) { if (adj > 0) adj *= 2; - else - adj = 1; + else { + /* CHV needs even encode values */ + adj = IS_CHERRYVIEW(dev_priv->dev) ? 2 : 1; + } new_delay = dev_priv->rps.cur_freq + adj; /* @@ -1271,8 +1273,10 @@ static void gen6_pm_rps_work(struct work_struct *work) } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) { if (adj < 0) adj *= 2; - else - adj = -1; + else { + /* CHV needs even encode values */ + adj = IS_CHERRYVIEW(dev_priv->dev) ? -2 : -1; + } new_delay = dev_priv->rps.cur_freq + adj; } else { /* unknown event */ new_delay = dev_priv->rps.cur_freq; -- cgit v0.10.2 From 9197c88bf946cf792ad5124f00bd51a0bc18f8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 9 Apr 2014 13:29:05 +0300 Subject: drm/i915/chv: Try to program the PHY used clock channel overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These should make it possible to feed port C from pipe A or port B from pipe B. Didn't quite seem to work though. Signed-off-by: Ville Syrjälä Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 53db3a2..fd541fc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -771,6 +771,8 @@ enum punit_power_well { #define _VLV_PCS_DW8_CH0 0x8220 #define _VLV_PCS_DW8_CH1 0x8420 +#define CHV_PCS_USEDCLKCHANNEL_OVRRIDE (1 << 20) +#define CHV_PCS_USEDCLKCHANNEL (1 << 21) #define VLV_PCS_DW8(ch) _PORT(ch, _VLV_PCS_DW8_CH0, _VLV_PCS_DW8_CH1) #define _VLV_PCS01_DW8_CH0 0x0220 @@ -895,6 +897,11 @@ enum punit_power_well { #define DPIO_DCLKP_EN (1 << 13) #define CHV_CMN_DW14(ch) _PIPE(ch, _CHV_CMN_DW14_CH0, _CHV_CMN_DW1_CH1) +#define _CHV_CMN_DW19_CH0 0x814c +#define _CHV_CMN_DW6_CH1 0x8098 +#define CHV_CMN_USEDCLKCHANNEL (1 << 13) +#define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1) + #define CHV_CMN_DW30 0x8178 #define DPIO_LRC_BYPASS (1 << 3) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 82edfbc..709f049 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2119,6 +2119,51 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) vlv_wait_port_ready(dev_priv, dport); } +static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(encoder->base.crtc); + enum dpio_channel ch = vlv_dport_to_channel(dport); + enum pipe pipe = intel_crtc->pipe; + u32 val; + + mutex_lock(&dev_priv->dpio_lock); + + /* program clock channel usage */ + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + + /* + * This a a bit weird since generally CL + * matches the pipe, but here we need to + * pick the CL based on the port. + */ + val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch)); + if (pipe != PIPE_B) + val &= ~CHV_CMN_USEDCLKCHANNEL; + else + val |= CHV_CMN_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val); + + mutex_unlock(&dev_priv->dpio_lock); +} + /* * Native read with retry for link status and receiver capability reads for * cases where the sink may still be asleep. @@ -4335,6 +4380,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->get_hw_state = intel_dp_get_hw_state; intel_encoder->get_config = intel_dp_get_config; if (IS_CHERRYVIEW(dev)) { + intel_encoder->pre_pll_enable = chv_dp_pre_pll_enable; intel_encoder->pre_enable = chv_pre_enable_dp; intel_encoder->enable = vlv_enable_dp; intel_encoder->post_disable = chv_post_disable_dp; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index eee2bbe..8da29d1 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1229,6 +1229,51 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); } +static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(encoder->base.crtc); + enum dpio_channel ch = vlv_dport_to_channel(dport); + enum pipe pipe = intel_crtc->pipe; + u32 val; + + mutex_lock(&dev_priv->dpio_lock); + + /* program clock channel usage */ + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + + /* + * This a a bit weird since generally CL + * matches the pipe, but here we need to + * pick the CL based on the port. + */ + val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch)); + if (pipe != PIPE_B) + val &= ~CHV_CMN_USEDCLKCHANNEL; + else + val |= CHV_CMN_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val); + + mutex_unlock(&dev_priv->dpio_lock); +} + static void vlv_hdmi_post_disable(struct intel_encoder *encoder) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); @@ -1528,6 +1573,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->get_hw_state = intel_hdmi_get_hw_state; intel_encoder->get_config = intel_hdmi_get_config; if (IS_CHERRYVIEW(dev)) { + intel_encoder->pre_pll_enable = chv_hdmi_pre_pll_enable; intel_encoder->pre_enable = chv_hdmi_pre_enable; intel_encoder->enable = vlv_enable_hdmi; intel_encoder->post_disable = chv_hdmi_post_disable; -- cgit v0.10.2 From b9e5ac3c181e4709e3e3bf3e280900186f5e0412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 27 May 2014 16:30:18 +0300 Subject: drm/i915/chv: Force clock buffer enables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Try to force the PHY clock buffer enables to make the clock routing work. v2: Fix the pipe B case to actually enable CH0 clock buffers Signed-off-by: Ville Syrjälä Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fd541fc..fa9b03c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -881,6 +881,16 @@ enum punit_power_well { #define DPIO_CHV_PROP_COEFF_SHIFT 0 #define CHV_PLL_DW6(ch) _PIPE(ch, _CHV_PLL_DW6_CH0, _CHV_PLL_DW6_CH1) +#define _CHV_CMN_DW5_CH0 0x8114 +#define CHV_BUFRIGHTENA1_DISABLE (0 << 20) +#define CHV_BUFRIGHTENA1_NORMAL (1 << 20) +#define CHV_BUFRIGHTENA1_FORCE (3 << 20) +#define CHV_BUFRIGHTENA1_MASK (3 << 20) +#define CHV_BUFLEFTENA1_DISABLE (0 << 22) +#define CHV_BUFLEFTENA1_NORMAL (1 << 22) +#define CHV_BUFLEFTENA1_FORCE (3 << 22) +#define CHV_BUFLEFTENA1_MASK (3 << 22) + #define _CHV_CMN_DW13_CH0 0x8134 #define _CHV_CMN_DW0_CH1 0x8080 #define DPIO_CHV_S1_DIV_SHIFT 21 @@ -895,6 +905,14 @@ enum punit_power_well { #define _CHV_CMN_DW1_CH1 0x8084 #define DPIO_AFC_RECAL (1 << 14) #define DPIO_DCLKP_EN (1 << 13) +#define CHV_BUFLEFTENA2_DISABLE (0 << 17) /* CL2 DW1 only */ +#define CHV_BUFLEFTENA2_NORMAL (1 << 17) /* CL2 DW1 only */ +#define CHV_BUFLEFTENA2_FORCE (3 << 17) /* CL2 DW1 only */ +#define CHV_BUFLEFTENA2_MASK (3 << 17) /* CL2 DW1 only */ +#define CHV_BUFRIGHTENA2_DISABLE (0 << 19) /* CL2 DW1 only */ +#define CHV_BUFRIGHTENA2_NORMAL (1 << 19) /* CL2 DW1 only */ +#define CHV_BUFRIGHTENA2_FORCE (3 << 19) /* CL2 DW1 only */ +#define CHV_BUFRIGHTENA2_MASK (3 << 19) /* CL2 DW1 only */ #define CHV_CMN_DW14(ch) _PIPE(ch, _CHV_CMN_DW14_CH0, _CHV_CMN_DW1_CH1) #define _CHV_CMN_DW19_CH0 0x814c diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 709f049..b3f97f2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2132,6 +2132,25 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) mutex_lock(&dev_priv->dpio_lock); + /* program left/right clock distribution */ + if (pipe != PIPE_B) { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); + if (ch == DPIO_CH0) + val |= CHV_BUFLEFTENA1_FORCE; + if (ch == DPIO_CH1) + val |= CHV_BUFRIGHTENA1_FORCE; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + } else { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); + if (ch == DPIO_CH0) + val |= CHV_BUFLEFTENA2_FORCE; + if (ch == DPIO_CH1) + val |= CHV_BUFRIGHTENA2_FORCE; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + } + /* program clock channel usage */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8da29d1..318b150 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1242,6 +1242,25 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) mutex_lock(&dev_priv->dpio_lock); + /* program left/right clock distribution */ + if (pipe != PIPE_B) { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); + if (ch == DPIO_CH0) + val |= CHV_BUFLEFTENA1_FORCE; + if (ch == DPIO_CH1) + val |= CHV_BUFRIGHTENA1_FORCE; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + } else { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); + if (ch == DPIO_CH0) + val |= CHV_BUFLEFTENA2_FORCE; + if (ch == DPIO_CH1) + val |= CHV_BUFRIGHTENA2_FORCE; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + } + /* program clock channel usage */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; -- cgit v0.10.2 From 61407f6dd3c75ab900d52da4c29f9a52ed6b2acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 27 May 2014 16:32:55 +0300 Subject: drm/i915/chv: Force PHY clock buffers off after PLL disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we forced the clock buffers on in .pre_pll_enable() we should probably undo the damage after we've turned the PLL off. We do the clock buffer force enable in the .pre_pll_enable() hook as we need to know which port is going to be used, but in the disable case we don't need the port since we just disable the clock buffers to both channels. So we can do this in chv_disable_pll() instead of having to add any kind of .post_pll_disable() hook. v2: Improve the commit message Signed-off-by: Ville Syrjälä Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0c693d8..e7e95c3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1712,6 +1712,17 @@ static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) val &= ~DPIO_DCLKP_EN; vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val); + /* disable left/right clock distribution */ + if (pipe != PIPE_B) { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + } else { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + } + mutex_unlock(&dev_priv->dpio_lock); } -- cgit v0.10.2 From 2d401b175f9026d839d3ad1cf444802c986c32bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 9 Apr 2014 13:29:08 +0300 Subject: drm/i915: Don't use pipe_offset stuff for DPLL registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are just single registers so wasting space for the pipe offsets seems a bit pointless. So just use the _PIPE3() macro instead. Also rewrite the _PIPE3() macro to be more obvious, and protect the arguments properly. Signed-off-by: Ville Syrjälä Reviewed-by: Damien Lespiau [danvet: Frob conflict.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 651e65e..615b62f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -46,8 +46,6 @@ static struct drm_driver driver; PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \ .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \ TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \ - .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET }, \ - .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \ .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET } #define GEN_CHV_PIPEOFFSETS \ @@ -55,10 +53,6 @@ static struct drm_driver driver; CHV_PIPE_C_OFFSET }, \ .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \ CHV_TRANSCODER_C_OFFSET, }, \ - .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET, \ - CHV_DPLL_C_OFFSET }, \ - .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET, \ - CHV_DPLL_C_MD_OFFSET }, \ .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \ CHV_PALETTE_C_OFFSET } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ef7ab0a..bea9ab40 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -552,8 +552,6 @@ struct intel_device_info { /* Register offsets for the various display pipes and transcoders */ int pipe_offsets[I915_MAX_TRANSCODERS]; int trans_offsets[I915_MAX_TRANSCODERS]; - int dpll_offsets[I915_MAX_PIPES]; - int dpll_md_offsets[I915_MAX_PIPES]; int palette_offsets[I915_MAX_PIPES]; int cursor_offsets[I915_MAX_PIPES]; }; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fa9b03c..5798082 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -29,8 +29,8 @@ #define _TRANSCODER(tran, a, b) ((a) + (tran)*((b)-(a))) #define _PORT(port, a, b) ((a) + (port)*((b)-(a))) -#define _PIPE3(pipe, a, b, c) (pipe < 2 ? _PIPE(pipe, a, b) : c) -#define _PORT3(port, a, b, c) (port < 2 ? _PORT(port, a, b) : c) +#define _PIPE3(pipe, a, b, c) ((pipe) == PIPE_A ? (a) : \ + (pipe) == PIPE_B ? (b) : (c)) #define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a)) #define _MASKED_BIT_DISABLE(a) ((a) << 16) @@ -1605,11 +1605,10 @@ enum punit_power_well { /* * Clock control & power management */ -#define DPLL_A_OFFSET 0x6014 -#define DPLL_B_OFFSET 0x6018 -#define CHV_DPLL_C_OFFSET 0x6030 -#define DPLL(pipe) (dev_priv->info.dpll_offsets[pipe] + \ - dev_priv->info.display_mmio_offset) +#define _DPLL_A (dev_priv->info.display_mmio_offset + 0x6014) +#define _DPLL_B (dev_priv->info.display_mmio_offset + 0x6018) +#define _CHV_DPLL_C (dev_priv->info.display_mmio_offset + 0x6030) +#define DPLL(pipe) _PIPE3((pipe), _DPLL_A, _DPLL_B, _CHV_DPLL_C) #define VGA0 0x6000 #define VGA1 0x6004 @@ -1697,11 +1696,10 @@ enum punit_power_well { #define SDVO_MULTIPLIER_SHIFT_HIRES 4 #define SDVO_MULTIPLIER_SHIFT_VGA 0 -#define DPLL_A_MD_OFFSET 0x601c /* 965+ only */ -#define DPLL_B_MD_OFFSET 0x6020 /* 965+ only */ -#define CHV_DPLL_C_MD_OFFSET 0x603c -#define DPLL_MD(pipe) (dev_priv->info.dpll_md_offsets[pipe] + \ - dev_priv->info.display_mmio_offset) +#define _DPLL_A_MD (dev_priv->info.display_mmio_offset + 0x601c) +#define _DPLL_B_MD (dev_priv->info.display_mmio_offset + 0x6020) +#define _CHV_DPLL_C_MD (dev_priv->info.display_mmio_offset + 0x603c) +#define DPLL_MD(pipe) _PIPE3((pipe), _DPLL_A_MD, _DPLL_B_MD, _CHV_DPLL_C_MD) /* * UDI pixel divider, controlling how many pixels are stuffed into a packet. @@ -6449,9 +6447,5 @@ enum punit_power_well { /* For UMS only (deprecated): */ #define _PALETTE_A (dev_priv->info.display_mmio_offset + 0xa000) #define _PALETTE_B (dev_priv->info.display_mmio_offset + 0xa800) -#define _DPLL_A (dev_priv->info.display_mmio_offset + 0x6014) -#define _DPLL_B (dev_priv->info.display_mmio_offset + 0x6018) -#define _DPLL_A_MD (dev_priv->info.display_mmio_offset + 0x601c) -#define _DPLL_B_MD (dev_priv->info.display_mmio_offset + 0x6020) #endif /* _I915_REG_H_ */ -- cgit v0.10.2 From 2dcbc34d12ba463d5c0be5d96762911707844098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 9 Apr 2014 13:29:09 +0300 Subject: drm/i915/chv: Handle video DIP registers on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DIP registers are a mess on VLV and CHV. The register block on pipe A is different than the register block on pipes B and C. In order to handle that using the pipe offsets, we'd need a new pipe offset per register, which seems wasteful. So instead just use the _PIPE3() macro to handle these registers. Signed-off-by: Ville Syrjälä Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5798082..242df99 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4909,8 +4909,7 @@ enum punit_power_well { #define _PCH_TRANSA_LINK_M2 0xe0048 #define _PCH_TRANSA_LINK_N2 0xe004c -/* Per-transcoder DIP controls */ - +/* Per-transcoder DIP controls (PCH) */ #define _VIDEO_DIP_CTL_A 0xe0200 #define _VIDEO_DIP_DATA_A 0xe0208 #define _VIDEO_DIP_GCP_A 0xe0210 @@ -4923,6 +4922,7 @@ enum punit_power_well { #define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B) #define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B) +/* Per-transcoder DIP controls (VLV) */ #define VLV_VIDEO_DIP_CTL_A (VLV_DISPLAY_BASE + 0x60200) #define VLV_VIDEO_DIP_DATA_A (VLV_DISPLAY_BASE + 0x60208) #define VLV_VIDEO_DIP_GDCP_PAYLOAD_A (VLV_DISPLAY_BASE + 0x60210) @@ -4931,12 +4931,19 @@ enum punit_power_well { #define VLV_VIDEO_DIP_DATA_B (VLV_DISPLAY_BASE + 0x61174) #define VLV_VIDEO_DIP_GDCP_PAYLOAD_B (VLV_DISPLAY_BASE + 0x61178) +#define CHV_VIDEO_DIP_CTL_C (VLV_DISPLAY_BASE + 0x611f0) +#define CHV_VIDEO_DIP_DATA_C (VLV_DISPLAY_BASE + 0x611f4) +#define CHV_VIDEO_DIP_GDCP_PAYLOAD_C (VLV_DISPLAY_BASE + 0x611f8) + #define VLV_TVIDEO_DIP_CTL(pipe) \ - _PIPE(pipe, VLV_VIDEO_DIP_CTL_A, VLV_VIDEO_DIP_CTL_B) + _PIPE3((pipe), VLV_VIDEO_DIP_CTL_A, \ + VLV_VIDEO_DIP_CTL_B, CHV_VIDEO_DIP_CTL_C) #define VLV_TVIDEO_DIP_DATA(pipe) \ - _PIPE(pipe, VLV_VIDEO_DIP_DATA_A, VLV_VIDEO_DIP_DATA_B) + _PIPE3((pipe), VLV_VIDEO_DIP_DATA_A, \ + VLV_VIDEO_DIP_DATA_B, CHV_VIDEO_DIP_DATA_C) #define VLV_TVIDEO_DIP_GCP(pipe) \ - _PIPE(pipe, VLV_VIDEO_DIP_GDCP_PAYLOAD_A, VLV_VIDEO_DIP_GDCP_PAYLOAD_B) + _PIPE3((pipe), VLV_VIDEO_DIP_GDCP_PAYLOAD_A, \ + VLV_VIDEO_DIP_GDCP_PAYLOAD_B, CHV_VIDEO_DIP_GDCP_PAYLOAD_C) /* Haswell DIP controls */ #define HSW_VIDEO_DIP_CTL_A 0x60200 -- cgit v0.10.2 From b5fbcd98975529d256c4d33823bad3b5324b56ab Mon Sep 17 00:00:00 2001 From: Shobhit Kumar Date: Tue, 27 May 2014 19:23:46 +0530 Subject: drm/i915: Fix checkpatch errors Fix warnings introduced by the following commit - commit 9c92da2c7c17eea79b6321b37592df0a002d24df Author: Shobhit Kumar Date: Fri May 23 21:35:27 2014 +0530 drm/i915: Add support for Generic MIPI panel driver Fixed all except the DRM logging which go beyond line 80 Signed-off-by: Shobhit Kumar Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c index 21a0d34..47c7584 100644 --- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -143,7 +143,7 @@ static u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, u8 *data) case MIPI_DSI_DCS_LONG_WRITE: dsi_vc_dcs_write(intel_dsi, vc, data, len); break; - }; + } data += len; @@ -294,7 +294,8 @@ static bool generic_init(struct intel_dsi_device *dsi) intel_dsi->rst_timer_val = mipi_config->device_reset_timer; intel_dsi->init_count = mipi_config->master_init_timer; intel_dsi->bw_timer = mipi_config->dbi_bw_timer; - intel_dsi->video_frmt_cfg_bits = mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0; + intel_dsi->video_frmt_cfg_bits = + mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0; switch (intel_dsi->escape_clk_div) { case 0: @@ -351,7 +352,8 @@ static bool generic_init(struct intel_dsi_device *dsi) * * prepare count */ - ths_prepare_ns = max(mipi_config->ths_prepare, mipi_config->tclk_prepare); + ths_prepare_ns = max(mipi_config->ths_prepare, + mipi_config->tclk_prepare); prepare_cnt = DIV_ROUND_UP(ths_prepare_ns * ui_den, ui_num * 2); /* exit zero count */ -- cgit v0.10.2 From 4328633d6553558471c260137019c245c2e9d7c6 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 28 May 2014 12:30:56 +0100 Subject: drm/i915: Make intel_dsi_init() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions that can't fail are such a bliss to work with, it'd be shame to miss the occasion. The "failure" mode is the DSI connector not being created, the rest of the initialization can carry on happily. We weren't even checking that value anyway. Suggested-by: Ville Syrjälä Suggested-by: Shobhit Kumar Cc: Ville Syrjälä Cc: Shobhit Kumar Signed-off-by: Damien Lespiau [danvet: Also convert the missed return statement due to other patches merged meanwhile.] [danvet2: Squash in fixup from Damien to remove empty return; at the end of intel_dsi_init.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index fbf032d..cfed575 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -834,7 +834,7 @@ void intel_edp_psr_update(struct drm_device *dev); void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); /* intel_dsi.c */ -bool intel_dsi_init(struct drm_device *dev); +void intel_dsi_init(struct drm_device *dev); /* intel_dvo.c */ diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 02f99d7..bd89de5 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -657,7 +657,7 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, }; -bool intel_dsi_init(struct drm_device *dev) +void intel_dsi_init(struct drm_device *dev) { struct intel_dsi *intel_dsi; struct intel_encoder *intel_encoder; @@ -673,16 +673,16 @@ bool intel_dsi_init(struct drm_device *dev) /* There is no detection method for MIPI so rely on VBT */ if (!dev_priv->vbt.has_mipi) - return false; + return; intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL); if (!intel_dsi) - return false; + return; intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); if (!intel_connector) { kfree(intel_dsi); - return false; + return; } intel_encoder = &intel_dsi->base; @@ -693,7 +693,7 @@ bool intel_dsi_init(struct drm_device *dev) dev_priv->mipi_mmio_base = VLV_MIPI_BASE; } else { DRM_ERROR("Unsupported Mipi device to reg base"); - return false; + return; } connector = &intel_connector->base; @@ -753,12 +753,10 @@ bool intel_dsi_init(struct drm_device *dev) fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; intel_panel_init(&intel_connector->panel, fixed_mode, NULL); - return true; + return; err: drm_encoder_cleanup(&intel_encoder->base); kfree(intel_dsi); kfree(intel_connector); - - return false; } -- cgit v0.10.2 From be19f0ff482bd5f800d916683784a7e1474d5d6b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 28 May 2014 16:16:42 +0100 Subject: drm/i915: Check for a NULL shared dpll before dereferencing This doesn't look possible but a little extra defense against the improbable is worth it - an oops here could lockup the machine. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e7e95c3..8635bb7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1760,6 +1760,9 @@ static void intel_prepare_shared_dpll(struct intel_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + if (WARN_ON(pll == NULL)) + return; + WARN_ON(!pll->refcount); if (pll->active == 0) { DRM_DEBUG_DRIVER("setting up %s\n", pll->name); -- cgit v0.10.2 From f3d28878da307c394a870252094136ea8387144d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 May 2014 23:23:08 +0200 Subject: drm/i915: Fix context locking in debugfs This goes all the way back to the introduction of this debugfs file, even though back then no locking really was required. None of the intermediate patches fixed this. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 601caa8..c2e211a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1721,7 +1721,7 @@ static int i915_context_status(struct seq_file *m, void *unused) struct intel_context *ctx; int ret, i; - ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; @@ -1751,7 +1751,7 @@ static int i915_context_status(struct seq_file *m, void *unused) seq_putc(m, '\n'); } - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->struct_mutex); return 0; } -- cgit v0.10.2 From 75e2acf24ab5e4bbf6a19b0f5460456ea16a2e5d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 May 2014 23:27:00 +0200 Subject: drm/i915: Drop locking around fbdev-fb in debugfs All the date we print is invariant for the lifetime of the driver. And none of it would be protected by the mode_config.mutex anyway. So drop it. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index c2e211a..7e884ea 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1675,9 +1675,6 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) #ifdef CONFIG_DRM_I915_FBDEV struct drm_i915_private *dev_priv = dev->dev_private; - int ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (ret) - return ret; ifbdev = dev_priv->fbdev; fb = to_intel_framebuffer(ifbdev->helper.fb); @@ -1690,7 +1687,6 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_putc(m, '\n'); - mutex_unlock(&dev->mode_config.mutex); #endif mutex_lock(&dev->mode_config.fb_lock); -- cgit v0.10.2 From 3b150f08e5013297729627a222e410284832effe Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Thu, 29 May 2014 08:06:53 -0700 Subject: drm/i915: don't force full modeset if primary plane is disabled (v2) In a future patch, we'll allow the primary plane to be disabled by userspace via the universal plane API. If a modeset is requested while the primary plane is disabled, crtc->primary->fb will be NULL which generally triggers a full modeset (except in fastboot situations). If we detect that the crtc is active, but there's no primary plane fb, we should still allow a simple plane update rather than a full modeset if the mode isn't actually changing (after re-enabling the primary plane of course). v2: - Enable plane after set_base to avoid enabling the plane if set_base fails, and to make flip+enable atomic (suggested by Ville) - Drop BUG to WARN if we somehow enter the 'fb_changed' modeset case with the crtc disabled (suggested by Ville) Reviewed-by: Chon Ming Lee Signed-off-by: Matt Roper Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8635bb7..d99be95 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10562,12 +10562,17 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, if (is_crtc_connector_off(set)) { config->mode_changed = true; } else if (set->crtc->primary->fb != set->fb) { - /* If we have no fb then treat it as a full mode set */ + /* + * If we have no fb, we can only flip as long as the crtc is + * active, otherwise we need a full mode set. The crtc may + * be active if we've only disabled the primary plane, or + * in fastboot situations. + */ if (set->crtc->primary->fb == NULL) { struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc); - if (intel_crtc->active && i915.fastboot) { + if (intel_crtc->active) { DRM_DEBUG_KMS("crtc has no fb, will flip\n"); config->fb_changed = true; } else { @@ -10805,10 +10810,24 @@ static int intel_crtc_set_config(struct drm_mode_set *set) ret = intel_set_mode(set->crtc, set->mode, set->x, set->y, set->fb); } else if (config->fb_changed) { + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc); + intel_crtc_wait_for_pending_flips(set->crtc); ret = intel_pipe_set_base(set->crtc, set->x, set->y, set->fb); + + /* + * We need to make sure the primary plane is re-enabled if it + * has previously been turned off. + */ + if (!intel_crtc->primary_enabled && ret == 0) { + WARN_ON(!intel_crtc->active); + intel_enable_primary_hw_plane(dev_priv, intel_crtc->plane, + intel_crtc->pipe); + } + /* * In the fastboot case this may be our only check of the * state after boot. It would be better to only do it on -- cgit v0.10.2 From 465c120c4c252e127b57cd133c4611b075e0a237 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Thu, 29 May 2014 08:06:54 -0700 Subject: drm/i915: Intel-specific primary plane handling (v8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intel hardware allows the primary plane to be disabled independently of the CRTC. Provide custom primary plane handling to allow this. v8: - Pin/unpin properly when clipping causes the primary plane to be disabled when it has previously been enabled. - s/drm_primary_helper_check_update/drm_plane_helper_check_update/ v7: - Clip primary plane to invisible when crtc is disabled since intel_crtc->config.pipe_src_{w,h} may be garbage otherwise. - Unpin old fb before pinning new one in the "just pin and return" case that is used when the crtc is disabled. - Don't treat implicit disabling of the primary plane (caused by clipping) the same way as explicit disabling (caused by fb=0). For implicit disables, we should leave the fb set and pinned, whereas for explicit disables we need to unpin the fb before primary->fb is cleared. v6: - Pass rectangles to primary helper check function and get plane visibility back. - Wait for pending pageflips on primary plane update/disable. - Allow primary plane to be updated while the crtc is disabled (changes will take effect when the crtc is re-enabled if modeset passes -1 for the fb id). - Drop WARN() if we try to disable the primary plane when it's already been disabled. This will happen if the crtc gets disabled after the primary plane has already been disabled independently. v5: - Use new drm_primary_helper_check_update() helper function to check setplane parameter validity. - Swap primary plane's pipe for pre-gen4 FBC (caught by Ville Syrjälä) - Cleanup primary plane properly on crtc init failure v4: - Don't add a primary_plane field to intel_crtc; that was left over from a much earlier iteration of this patch series, but is no longer needed/used now that the DRM core primary plane support has been merged. v3: - Provide gen-specific primary plane format lists (suggested by Daniel Vetter). - If the primary plane is already enabled, go ahead and just call the primary plane helper to do the update (suggested by Daniel Vetter). - Don't try to disable the primary plane on destruction; the DRM layer should have already taken care of this for us. v2: - Unpin fb properly on primary plane disable - Provide an Intel-specific set of primary plane formats - Additional sanity checks on setplane (in line with the checks currently being done by the DRM core primary plane helper) Reviewed-by: Chon Ming Lee Signed-off-by: Matt Roper Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d99be95..e6a2a75 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -39,10 +39,37 @@ #include "i915_trace.h" #include #include +#include +#include #include +/* Primary plane formats supported by all gen */ +#define COMMON_PRIMARY_FORMATS \ + DRM_FORMAT_C8, \ + DRM_FORMAT_RGB565, \ + DRM_FORMAT_XRGB8888, \ + DRM_FORMAT_ARGB8888 + +/* Primary plane formats for gen <= 3 */ +static const uint32_t intel_primary_formats_gen2[] = { + COMMON_PRIMARY_FORMATS, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, +}; + +/* Primary plane formats for gen >= 4 */ +static const uint32_t intel_primary_formats_gen4[] = { + COMMON_PRIMARY_FORMATS, \ + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ABGR2101010, +}; + #define DIV_ROUND_CLOSEST_ULL(ll, d) \ - ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) +({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); @@ -10977,17 +11004,216 @@ static void intel_shared_dpll_init(struct drm_device *dev) BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); } +static int +intel_primary_plane_disable(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_crtc *intel_crtc; + + if (!plane->fb) + return 0; + + BUG_ON(!plane->crtc); + + intel_crtc = to_intel_crtc(plane->crtc); + + /* + * Even though we checked plane->fb above, it's still possible that + * the primary plane has been implicitly disabled because the crtc + * coordinates given weren't visible, or because we detected + * that it was 100% covered by a sprite plane. Or, the CRTC may be + * off and we've set a fb, but haven't actually turned on the CRTC yet. + * In either case, we need to unpin the FB and let the fb pointer get + * updated, but otherwise we don't need to touch the hardware. + */ + if (!intel_crtc->primary_enabled) + goto disable_unpin; + + intel_crtc_wait_for_pending_flips(plane->crtc); + intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, + intel_plane->pipe); + +disable_unpin: + intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + plane->fb = NULL; + + return 0; +} + +static int +intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane *intel_plane = to_intel_plane(plane); + struct drm_rect dest = { + /* integer pixels */ + .x1 = crtc_x, + .y1 = crtc_y, + .x2 = crtc_x + crtc_w, + .y2 = crtc_y + crtc_h, + }; + struct drm_rect src = { + /* 16.16 fixed point */ + .x1 = src_x, + .y1 = src_y, + .x2 = src_x + src_w, + .y2 = src_y + src_h, + }; + const struct drm_rect clip = { + /* integer pixels */ + .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0, + .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0, + }; + bool visible; + int ret; + + ret = drm_plane_helper_check_update(plane, crtc, fb, + &src, &dest, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true, &visible); + + if (ret) + return ret; + + /* + * If the CRTC isn't enabled, we're just pinning the framebuffer, + * updating the fb pointer, and returning without touching the + * hardware. This allows us to later do a drmModeSetCrtc with fb=-1 to + * turn on the display with all planes setup as desired. + */ + if (!crtc->enabled) { + /* + * If we already called setplane while the crtc was disabled, + * we may have an fb pinned; unpin it. + */ + if (plane->fb) + intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + + /* Pin and return without programming hardware */ + return intel_pin_and_fence_fb_obj(dev, + to_intel_framebuffer(fb)->obj, + NULL); + } + + intel_crtc_wait_for_pending_flips(crtc); + + /* + * If clipping results in a non-visible primary plane, we'll disable + * the primary plane. Note that this is a bit different than what + * happens if userspace explicitly disables the plane by passing fb=0 + * because plane->fb still gets set and pinned. + */ + if (!visible) { + /* + * Try to pin the new fb first so that we can bail out if we + * fail. + */ + if (plane->fb != fb) { + ret = intel_pin_and_fence_fb_obj(dev, + to_intel_framebuffer(fb)->obj, + NULL); + if (ret) + return ret; + } + + if (intel_crtc->primary_enabled) + intel_disable_primary_hw_plane(dev_priv, + intel_plane->plane, + intel_plane->pipe); + + + if (plane->fb != fb) + if (plane->fb) + intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + + return 0; + } + + ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb); + if (ret) + return ret; + + if (!intel_crtc->primary_enabled) + intel_enable_primary_hw_plane(dev_priv, intel_crtc->plane, + intel_crtc->pipe); + + return 0; +} + +static void intel_primary_plane_destroy(struct drm_plane *plane) +{ + struct intel_plane *intel_plane = to_intel_plane(plane); + drm_plane_cleanup(plane); + kfree(intel_plane); +} + +static const struct drm_plane_funcs intel_primary_plane_funcs = { + .update_plane = intel_primary_plane_setplane, + .disable_plane = intel_primary_plane_disable, + .destroy = intel_primary_plane_destroy, +}; + +static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, + int pipe) +{ + struct intel_plane *primary; + const uint32_t *intel_primary_formats; + int num_formats; + + primary = kzalloc(sizeof(*primary), GFP_KERNEL); + if (primary == NULL) + return NULL; + + primary->can_scale = false; + primary->max_downscale = 1; + primary->pipe = pipe; + primary->plane = pipe; + if (HAS_FBC(dev) && INTEL_INFO(dev)->gen < 4) + primary->plane = !pipe; + + if (INTEL_INFO(dev)->gen <= 3) { + intel_primary_formats = intel_primary_formats_gen2; + num_formats = ARRAY_SIZE(intel_primary_formats_gen2); + } else { + intel_primary_formats = intel_primary_formats_gen4; + num_formats = ARRAY_SIZE(intel_primary_formats_gen4); + } + + drm_universal_plane_init(dev, &primary->base, 0, + &intel_primary_plane_funcs, + intel_primary_formats, num_formats, + DRM_PLANE_TYPE_PRIMARY); + return &primary->base; +} + static void intel_crtc_init(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc; - int i; + struct drm_plane *primary; + int i, ret; intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL); if (intel_crtc == NULL) return; - drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); + primary = intel_primary_plane_create(dev, pipe); + ret = drm_crtc_init_with_planes(dev, &intel_crtc->base, primary, + NULL, &intel_crtc_funcs); + if (ret) { + drm_plane_cleanup(primary); + kfree(intel_crtc); + return; + } drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); for (i = 0; i < 256; i++) { -- cgit v0.10.2 From f461d1be22239053919543ec9b048095407bf4bd Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 May 2014 14:34:10 +0200 Subject: drm/i915: use shmem helpers if possible Instead of shuffling gfp-masks all the time, use the shmem_read_mapping_page() helper. Note that __GFP_IO and __GFP_WAIT are set in mapping_gfp_mask() for i915, so the behavior is still the same. Cc: Daniel Vetter Cc: Jani Nikula Signed-off-by: David Herrmann Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f361263..d86b77e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2059,16 +2059,10 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) * our own buffer, now let the real VM do its job and * go down in flames if truly OOM. */ - gfp &= ~(__GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD); - gfp |= __GFP_IO | __GFP_WAIT; - i915_gem_shrink_all(dev_priv); - page = shmem_read_mapping_page_gfp(mapping, i, gfp); + page = shmem_read_mapping_page(mapping, i); if (IS_ERR(page)) goto err_pages; - - gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; - gfp &= ~(__GFP_IO | __GFP_WAIT); } #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { -- cgit v0.10.2 From 1a5036bfd40bd7138fef8bb35adce7d76d8da924 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Mon, 2 Jun 2014 16:59:39 +0200 Subject: drivers/gpu/drm/i915/dma: style fixes Fixed several double space pointer notations, and added one newline Signed-off-by: Robin Schroer Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 4c22a5b..1c5df4a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -136,7 +136,7 @@ static void i915_free_hws(struct drm_device *dev) I915_WRITE(HWS_PGA, 0x1ffff000); } -void i915_kernel_lost_context(struct drm_device * dev) +void i915_kernel_lost_context(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; @@ -164,7 +164,7 @@ void i915_kernel_lost_context(struct drm_device * dev) master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY; } -static int i915_dma_cleanup(struct drm_device * dev) +static int i915_dma_cleanup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int i; @@ -188,7 +188,7 @@ static int i915_dma_cleanup(struct drm_device * dev) return 0; } -static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) +static int i915_initialize(struct drm_device *dev, drm_i915_init_t *init) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; @@ -233,7 +233,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) return 0; } -static int i915_dma_resume(struct drm_device * dev) +static int i915_dma_resume(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring = LP_RING(dev_priv); @@ -357,7 +357,7 @@ static int validate_cmd(int cmd) return 0; } -static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords) +static int i915_emit_cmds(struct drm_device *dev, int *buffer, int dwords) { struct drm_i915_private *dev_priv = dev->dev_private; int i, ret; @@ -367,6 +367,7 @@ static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords) for (i = 0; i < dwords;) { int sz = validate_cmd(buffer[i]); + if (sz == 0 || i + sz > dwords) return -EINVAL; i += sz; @@ -451,7 +452,7 @@ static void i915_emit_breadcrumb(struct drm_device *dev) } } -static int i915_dispatch_cmdbuffer(struct drm_device * dev, +static int i915_dispatch_cmdbuffer(struct drm_device *dev, drm_i915_cmdbuffer_t *cmd, struct drm_clip_rect *cliprects, void *cmdbuf) @@ -485,8 +486,8 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev, return 0; } -static int i915_dispatch_batchbuffer(struct drm_device * dev, - drm_i915_batchbuffer_t * batch, +static int i915_dispatch_batchbuffer(struct drm_device *dev, + drm_i915_batchbuffer_t *batch, struct drm_clip_rect *cliprects) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -547,7 +548,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev, return 0; } -static int i915_dispatch_flip(struct drm_device * dev) +static int i915_dispatch_flip(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = @@ -753,7 +754,7 @@ fail_batch_free: return ret; } -static int i915_emit_irq(struct drm_device * dev) +static int i915_emit_irq(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; @@ -779,7 +780,7 @@ static int i915_emit_irq(struct drm_device * dev) return dev_priv->dri1.counter; } -static int i915_wait_irq(struct drm_device * dev, int irq_nr) +static int i915_wait_irq(struct drm_device *dev, int irq_nr) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; @@ -1264,6 +1265,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ { struct drm_device *dev = pci_get_drvdata(pdev); pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + if (state == VGA_SWITCHEROO_ON) { pr_info("switched on\n"); dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; @@ -1895,7 +1897,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file) * and DMA structures, since the kernel won't be using them, and clea * up any GEM state. */ -void i915_driver_lastclose(struct drm_device * dev) +void i915_driver_lastclose(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1916,7 +1918,7 @@ void i915_driver_lastclose(struct drm_device * dev) i915_dma_cleanup(dev); } -void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) +void i915_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) { mutex_lock(&dev->struct_mutex); i915_gem_context_close(dev, file_priv); @@ -1993,7 +1995,7 @@ int i915_max_ioctl = ARRAY_SIZE(i915_ioctls); * manage the gtt, we need to claim that all intel devices are agp. For * otherwise the drm core refuses to initialize the agp support code. */ -int i915_driver_device_is_agp(struct drm_device * dev) +int i915_driver_device_is_agp(struct drm_device *dev) { return 1; } -- cgit v0.10.2 From 4ad83e94059969b5b3ac107ee002d9401e8ac5b3 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 2 Jun 2014 18:07:47 +0530 Subject: drm/i915: Change Mipi register definitions Re-define MIPI register definitions in such a way that most of the existing DSI code can be re-used for future platforms. Register definitions are re-written using MMIO offset variable, so that without changing the existing sequence, same code can be generically applied. V4: Addressing review comments by Damien and Ville, splitting into two patches This patch removes all the un-necessary formatting changes from previous patch. V5: Removed 80 char limit formatting for existing MIPI regs V6: Removed extra space, change one definition Signed-off-by: Shashank Sharma Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 242df99..d3c2b9f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -6092,12 +6092,12 @@ enum punit_power_well { #define TEARING_EFFECT_DELAY_MASK (0xffff << 0) /* XXX: all bits reserved */ -#define _MIPIA_AUTOPWG (VLV_DISPLAY_BASE + 0x611a0) +#define _MIPIA_AUTOPWG (VLV_DISPLAY_BASE + 0x611a0) /* MIPI DSI Controller and D-PHY registers */ -#define _MIPIA_DEVICE_READY (VLV_DISPLAY_BASE + 0xb000) -#define _MIPIB_DEVICE_READY (VLV_DISPLAY_BASE + 0xb800) +#define _MIPIA_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb000) +#define _MIPIB_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb800) #define MIPI_DEVICE_READY(pipe) _PIPE(pipe, _MIPIA_DEVICE_READY, _MIPIB_DEVICE_READY) #define BUS_POSSESSION (1 << 3) /* set to give bus to receiver */ #define ULPS_STATE_MASK (3 << 1) @@ -6106,11 +6106,11 @@ enum punit_power_well { #define ULPS_STATE_NORMAL_OPERATION (0 << 1) #define DEVICE_READY (1 << 0) -#define _MIPIA_INTR_STAT (VLV_DISPLAY_BASE + 0xb004) -#define _MIPIB_INTR_STAT (VLV_DISPLAY_BASE + 0xb804) +#define _MIPIA_INTR_STAT (dev_priv->mipi_mmio_base + 0xb004) +#define _MIPIB_INTR_STAT (dev_priv->mipi_mmio_base + 0xb804) #define MIPI_INTR_STAT(pipe) _PIPE(pipe, _MIPIA_INTR_STAT, _MIPIB_INTR_STAT) -#define _MIPIA_INTR_EN (VLV_DISPLAY_BASE + 0xb008) -#define _MIPIB_INTR_EN (VLV_DISPLAY_BASE + 0xb808) +#define _MIPIA_INTR_EN (dev_priv->mipi_mmio_base + 0xb008) +#define _MIPIB_INTR_EN (dev_priv->mipi_mmio_base + 0xb808) #define MIPI_INTR_EN(pipe) _PIPE(pipe, _MIPIA_INTR_EN, _MIPIB_INTR_EN) #define TEARING_EFFECT (1 << 31) #define SPL_PKT_SENT_INTERRUPT (1 << 30) @@ -6145,8 +6145,8 @@ enum punit_power_well { #define RXSOT_SYNC_ERROR (1 << 1) #define RXSOT_ERROR (1 << 0) -#define _MIPIA_DSI_FUNC_PRG (VLV_DISPLAY_BASE + 0xb00c) -#define _MIPIB_DSI_FUNC_PRG (VLV_DISPLAY_BASE + 0xb80c) +#define _MIPIA_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb00c) +#define _MIPIB_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb80c) #define MIPI_DSI_FUNC_PRG(pipe) _PIPE(pipe, _MIPIA_DSI_FUNC_PRG, _MIPIB_DSI_FUNC_PRG) #define CMD_MODE_DATA_WIDTH_MASK (7 << 13) #define CMD_MODE_NOT_SUPPORTED (0 << 13) @@ -6168,77 +6168,78 @@ enum punit_power_well { #define DATA_LANES_PRG_REG_SHIFT 0 #define DATA_LANES_PRG_REG_MASK (7 << 0) -#define _MIPIA_HS_TX_TIMEOUT (VLV_DISPLAY_BASE + 0xb010) -#define _MIPIB_HS_TX_TIMEOUT (VLV_DISPLAY_BASE + 0xb810) +#define _MIPIA_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb010) +#define _MIPIB_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb810) #define MIPI_HS_TX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_HS_TX_TIMEOUT, _MIPIB_HS_TX_TIMEOUT) #define HIGH_SPEED_TX_TIMEOUT_COUNTER_MASK 0xffffff -#define _MIPIA_LP_RX_TIMEOUT (VLV_DISPLAY_BASE + 0xb014) -#define _MIPIB_LP_RX_TIMEOUT (VLV_DISPLAY_BASE + 0xb814) +#define _MIPIA_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb014) +#define _MIPIB_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb814) #define MIPI_LP_RX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_LP_RX_TIMEOUT, _MIPIB_LP_RX_TIMEOUT) #define LOW_POWER_RX_TIMEOUT_COUNTER_MASK 0xffffff -#define _MIPIA_TURN_AROUND_TIMEOUT (VLV_DISPLAY_BASE + 0xb018) -#define _MIPIB_TURN_AROUND_TIMEOUT (VLV_DISPLAY_BASE + 0xb818) +#define _MIPIA_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb018) +#define _MIPIB_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb818) #define MIPI_TURN_AROUND_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT) #define TURN_AROUND_TIMEOUT_MASK 0x3f -#define _MIPIA_DEVICE_RESET_TIMER (VLV_DISPLAY_BASE + 0xb01c) -#define _MIPIB_DEVICE_RESET_TIMER (VLV_DISPLAY_BASE + 0xb81c) +#define _MIPIA_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb01c) +#define _MIPIB_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb81c) #define MIPI_DEVICE_RESET_TIMER(pipe) _PIPE(pipe, _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER) #define DEVICE_RESET_TIMER_MASK 0xffff -#define _MIPIA_DPI_RESOLUTION (VLV_DISPLAY_BASE + 0xb020) -#define _MIPIB_DPI_RESOLUTION (VLV_DISPLAY_BASE + 0xb820) +#define _MIPIA_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb020) +#define _MIPIB_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb820) #define MIPI_DPI_RESOLUTION(pipe) _PIPE(pipe, _MIPIA_DPI_RESOLUTION, _MIPIB_DPI_RESOLUTION) #define VERTICAL_ADDRESS_SHIFT 16 #define VERTICAL_ADDRESS_MASK (0xffff << 16) #define HORIZONTAL_ADDRESS_SHIFT 0 #define HORIZONTAL_ADDRESS_MASK 0xffff -#define _MIPIA_DBI_FIFO_THROTTLE (VLV_DISPLAY_BASE + 0xb024) -#define _MIPIB_DBI_FIFO_THROTTLE (VLV_DISPLAY_BASE + 0xb824) +#define _MIPIA_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb024) +#define _MIPIB_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb824) #define MIPI_DBI_FIFO_THROTTLE(pipe) _PIPE(pipe, _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE) #define DBI_FIFO_EMPTY_HALF (0 << 0) #define DBI_FIFO_EMPTY_QUARTER (1 << 0) #define DBI_FIFO_EMPTY_7_LOCATIONS (2 << 0) /* regs below are bits 15:0 */ -#define _MIPIA_HSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb028) -#define _MIPIB_HSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb828) +#define _MIPIA_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb028) +#define _MIPIB_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb828) #define MIPI_HSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT) -#define _MIPIA_HBP_COUNT (VLV_DISPLAY_BASE + 0xb02c) -#define _MIPIB_HBP_COUNT (VLV_DISPLAY_BASE + 0xb82c) +#define _MIPIA_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb02c) +#define _MIPIB_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb82c) #define MIPI_HBP_COUNT(pipe) _PIPE(pipe, _MIPIA_HBP_COUNT, _MIPIB_HBP_COUNT) -#define _MIPIA_HFP_COUNT (VLV_DISPLAY_BASE + 0xb030) -#define _MIPIB_HFP_COUNT (VLV_DISPLAY_BASE + 0xb830) +#define _MIPIA_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb030) +#define _MIPIB_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb830) #define MIPI_HFP_COUNT(pipe) _PIPE(pipe, _MIPIA_HFP_COUNT, _MIPIB_HFP_COUNT) -#define _MIPIA_HACTIVE_AREA_COUNT (VLV_DISPLAY_BASE + 0xb034) -#define _MIPIB_HACTIVE_AREA_COUNT (VLV_DISPLAY_BASE + 0xb834) +#define _MIPIA_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb034) +#define _MIPIB_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb834) #define MIPI_HACTIVE_AREA_COUNT(pipe) _PIPE(pipe, _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT) -#define _MIPIA_VSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb038) -#define _MIPIB_VSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb838) +#define _MIPIA_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb038) +#define _MIPIB_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb838) #define MIPI_VSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT) -#define _MIPIA_VBP_COUNT (VLV_DISPLAY_BASE + 0xb03c) -#define _MIPIB_VBP_COUNT (VLV_DISPLAY_BASE + 0xb83c) +#define _MIPIA_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb03c) +#define _MIPIB_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb83c) #define MIPI_VBP_COUNT(pipe) _PIPE(pipe, _MIPIA_VBP_COUNT, _MIPIB_VBP_COUNT) -#define _MIPIA_VFP_COUNT (VLV_DISPLAY_BASE + 0xb040) -#define _MIPIB_VFP_COUNT (VLV_DISPLAY_BASE + 0xb840) +#define _MIPIA_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb040) +#define _MIPIB_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb840) #define MIPI_VFP_COUNT(pipe) _PIPE(pipe, _MIPIA_VFP_COUNT, _MIPIB_VFP_COUNT) -#define _MIPIA_HIGH_LOW_SWITCH_COUNT (VLV_DISPLAY_BASE + 0xb044) -#define _MIPIB_HIGH_LOW_SWITCH_COUNT (VLV_DISPLAY_BASE + 0xb844) +#define _MIPIA_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb044) +#define _MIPIB_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb844) #define MIPI_HIGH_LOW_SWITCH_COUNT(pipe) _PIPE(pipe, _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT) + /* regs above are bits 15:0 */ -#define _MIPIA_DPI_CONTROL (VLV_DISPLAY_BASE + 0xb048) -#define _MIPIB_DPI_CONTROL (VLV_DISPLAY_BASE + 0xb848) +#define _MIPIA_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb048) +#define _MIPIB_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb848) #define MIPI_DPI_CONTROL(pipe) _PIPE(pipe, _MIPIA_DPI_CONTROL, _MIPIB_DPI_CONTROL) #define DPI_LP_MODE (1 << 6) #define BACKLIGHT_OFF (1 << 5) @@ -6248,26 +6249,26 @@ enum punit_power_well { #define TURN_ON (1 << 1) #define SHUTDOWN (1 << 0) -#define _MIPIA_DPI_DATA (VLV_DISPLAY_BASE + 0xb04c) -#define _MIPIB_DPI_DATA (VLV_DISPLAY_BASE + 0xb84c) +#define _MIPIA_DPI_DATA (dev_priv->mipi_mmio_base + 0xb04c) +#define _MIPIB_DPI_DATA (dev_priv->mipi_mmio_base + 0xb84c) #define MIPI_DPI_DATA(pipe) _PIPE(pipe, _MIPIA_DPI_DATA, _MIPIB_DPI_DATA) #define COMMAND_BYTE_SHIFT 0 #define COMMAND_BYTE_MASK (0x3f << 0) -#define _MIPIA_INIT_COUNT (VLV_DISPLAY_BASE + 0xb050) -#define _MIPIB_INIT_COUNT (VLV_DISPLAY_BASE + 0xb850) +#define _MIPIA_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb050) +#define _MIPIB_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb850) #define MIPI_INIT_COUNT(pipe) _PIPE(pipe, _MIPIA_INIT_COUNT, _MIPIB_INIT_COUNT) #define MASTER_INIT_TIMER_SHIFT 0 #define MASTER_INIT_TIMER_MASK (0xffff << 0) -#define _MIPIA_MAX_RETURN_PKT_SIZE (VLV_DISPLAY_BASE + 0xb054) -#define _MIPIB_MAX_RETURN_PKT_SIZE (VLV_DISPLAY_BASE + 0xb854) +#define _MIPIA_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb054) +#define _MIPIB_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb854) #define MIPI_MAX_RETURN_PKT_SIZE(pipe) _PIPE(pipe, _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE) #define MAX_RETURN_PKT_SIZE_SHIFT 0 #define MAX_RETURN_PKT_SIZE_MASK (0x3ff << 0) -#define _MIPIA_VIDEO_MODE_FORMAT (VLV_DISPLAY_BASE + 0xb058) -#define _MIPIB_VIDEO_MODE_FORMAT (VLV_DISPLAY_BASE + 0xb858) +#define _MIPIA_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb058) +#define _MIPIB_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb858) #define MIPI_VIDEO_MODE_FORMAT(pipe) _PIPE(pipe, _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT) #define RANDOM_DPI_DISPLAY_RESOLUTION (1 << 4) #define DISABLE_VIDEO_BTA (1 << 3) @@ -6276,8 +6277,8 @@ enum punit_power_well { #define VIDEO_MODE_NON_BURST_WITH_SYNC_EVENTS (2 << 0) #define VIDEO_MODE_BURST (3 << 0) -#define _MIPIA_EOT_DISABLE (VLV_DISPLAY_BASE + 0xb05c) -#define _MIPIB_EOT_DISABLE (VLV_DISPLAY_BASE + 0xb85c) +#define _MIPIA_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb05c) +#define _MIPIB_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb85c) #define MIPI_EOT_DISABLE(pipe) _PIPE(pipe, _MIPIA_EOT_DISABLE, _MIPIB_EOT_DISABLE) #define LP_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 7) #define HS_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 6) @@ -6288,27 +6289,27 @@ enum punit_power_well { #define CLOCKSTOP (1 << 1) #define EOT_DISABLE (1 << 0) -#define _MIPIA_LP_BYTECLK (VLV_DISPLAY_BASE + 0xb060) -#define _MIPIB_LP_BYTECLK (VLV_DISPLAY_BASE + 0xb860) +#define _MIPIA_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb060) +#define _MIPIB_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb860) #define MIPI_LP_BYTECLK(pipe) _PIPE(pipe, _MIPIA_LP_BYTECLK, _MIPIB_LP_BYTECLK) #define LP_BYTECLK_SHIFT 0 #define LP_BYTECLK_MASK (0xffff << 0) /* bits 31:0 */ -#define _MIPIA_LP_GEN_DATA (VLV_DISPLAY_BASE + 0xb064) -#define _MIPIB_LP_GEN_DATA (VLV_DISPLAY_BASE + 0xb864) +#define _MIPIA_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb064) +#define _MIPIB_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb864) #define MIPI_LP_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_LP_GEN_DATA, _MIPIB_LP_GEN_DATA) /* bits 31:0 */ -#define _MIPIA_HS_GEN_DATA (VLV_DISPLAY_BASE + 0xb068) -#define _MIPIB_HS_GEN_DATA (VLV_DISPLAY_BASE + 0xb868) +#define _MIPIA_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb068) +#define _MIPIB_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb868) #define MIPI_HS_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_HS_GEN_DATA, _MIPIB_HS_GEN_DATA) -#define _MIPIA_LP_GEN_CTRL (VLV_DISPLAY_BASE + 0xb06c) -#define _MIPIB_LP_GEN_CTRL (VLV_DISPLAY_BASE + 0xb86c) +#define _MIPIA_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb06c) +#define _MIPIB_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb86c) #define MIPI_LP_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_LP_GEN_CTRL, _MIPIB_LP_GEN_CTRL) -#define _MIPIA_HS_GEN_CTRL (VLV_DISPLAY_BASE + 0xb070) -#define _MIPIB_HS_GEN_CTRL (VLV_DISPLAY_BASE + 0xb870) +#define _MIPIA_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb070) +#define _MIPIB_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb870) #define MIPI_HS_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_HS_GEN_CTRL, _MIPIB_HS_GEN_CTRL) #define LONG_PACKET_WORD_COUNT_SHIFT 8 #define LONG_PACKET_WORD_COUNT_MASK (0xffff << 8) @@ -6320,8 +6321,8 @@ enum punit_power_well { #define DATA_TYPE_MASK (3f << 0) /* data type values, see include/video/mipi_display.h */ -#define _MIPIA_GEN_FIFO_STAT (VLV_DISPLAY_BASE + 0xb074) -#define _MIPIB_GEN_FIFO_STAT (VLV_DISPLAY_BASE + 0xb874) +#define _MIPIA_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb074) +#define _MIPIB_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb874) #define MIPI_GEN_FIFO_STAT(pipe) _PIPE(pipe, _MIPIA_GEN_FIFO_STAT, _MIPIB_GEN_FIFO_STAT) #define DPI_FIFO_EMPTY (1 << 28) #define DBI_FIFO_EMPTY (1 << 27) @@ -6338,15 +6339,15 @@ enum punit_power_well { #define HS_DATA_FIFO_HALF_EMPTY (1 << 1) #define HS_DATA_FIFO_FULL (1 << 0) -#define _MIPIA_HS_LS_DBI_ENABLE (VLV_DISPLAY_BASE + 0xb078) -#define _MIPIB_HS_LS_DBI_ENABLE (VLV_DISPLAY_BASE + 0xb878) +#define _MIPIA_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb078) +#define _MIPIB_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb878) #define MIPI_HS_LP_DBI_ENABLE(pipe) _PIPE(pipe, _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE) #define DBI_HS_LP_MODE_MASK (1 << 0) #define DBI_LP_MODE (1 << 0) #define DBI_HS_MODE (0 << 0) -#define _MIPIA_DPHY_PARAM (VLV_DISPLAY_BASE + 0xb080) -#define _MIPIB_DPHY_PARAM (VLV_DISPLAY_BASE + 0xb880) +#define _MIPIA_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb080) +#define _MIPIB_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb880) #define MIPI_DPHY_PARAM(pipe) _PIPE(pipe, _MIPIA_DPHY_PARAM, _MIPIB_DPHY_PARAM) #define EXIT_ZERO_COUNT_SHIFT 24 #define EXIT_ZERO_COUNT_MASK (0x3f << 24) @@ -6358,34 +6359,36 @@ enum punit_power_well { #define PREPARE_COUNT_MASK (0x3f << 0) /* bits 31:0 */ -#define _MIPIA_DBI_BW_CTRL (VLV_DISPLAY_BASE + 0xb084) -#define _MIPIB_DBI_BW_CTRL (VLV_DISPLAY_BASE + 0xb884) +#define _MIPIA_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb084) +#define _MIPIB_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb884) #define MIPI_DBI_BW_CTRL(pipe) _PIPE(pipe, _MIPIA_DBI_BW_CTRL, _MIPIB_DBI_BW_CTRL) -#define _MIPIA_CLK_LANE_SWITCH_TIME_CNT (VLV_DISPLAY_BASE + 0xb088) -#define _MIPIB_CLK_LANE_SWITCH_TIME_CNT (VLV_DISPLAY_BASE + 0xb888) +#define _MIPIA_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \ + + 0xb088) +#define _MIPIB_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \ + + 0xb888) #define MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe) _PIPE(pipe, _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT) #define LP_HS_SSW_CNT_SHIFT 16 #define LP_HS_SSW_CNT_MASK (0xffff << 16) #define HS_LP_PWR_SW_CNT_SHIFT 0 #define HS_LP_PWR_SW_CNT_MASK (0xffff << 0) -#define _MIPIA_STOP_STATE_STALL (VLV_DISPLAY_BASE + 0xb08c) -#define _MIPIB_STOP_STATE_STALL (VLV_DISPLAY_BASE + 0xb88c) +#define _MIPIA_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb08c) +#define _MIPIB_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb88c) #define MIPI_STOP_STATE_STALL(pipe) _PIPE(pipe, _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL) #define STOP_STATE_STALL_COUNTER_SHIFT 0 #define STOP_STATE_STALL_COUNTER_MASK (0xff << 0) -#define _MIPIA_INTR_STAT_REG_1 (VLV_DISPLAY_BASE + 0xb090) -#define _MIPIB_INTR_STAT_REG_1 (VLV_DISPLAY_BASE + 0xb890) +#define _MIPIA_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb090) +#define _MIPIB_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb890) #define MIPI_INTR_STAT_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1) -#define _MIPIA_INTR_EN_REG_1 (VLV_DISPLAY_BASE + 0xb094) -#define _MIPIB_INTR_EN_REG_1 (VLV_DISPLAY_BASE + 0xb894) +#define _MIPIA_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb094) +#define _MIPIB_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb894) #define MIPI_INTR_EN_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_EN_REG_1, _MIPIB_INTR_EN_REG_1) #define RX_CONTENTION_DETECTED (1 << 0) /* XXX: only pipe A ?!? */ -#define MIPIA_DBI_TYPEC_CTRL (VLV_DISPLAY_BASE + 0xb100) +#define MIPIA_DBI_TYPEC_CTRL (dev_priv->mipi_mmio_base + 0xb100) #define DBI_TYPEC_ENABLE (1 << 31) #define DBI_TYPEC_WIP (1 << 30) #define DBI_TYPEC_OPTION_SHIFT 28 @@ -6399,8 +6402,8 @@ enum punit_power_well { /* MIPI adapter registers */ -#define _MIPIA_CTRL (VLV_DISPLAY_BASE + 0xb104) -#define _MIPIB_CTRL (VLV_DISPLAY_BASE + 0xb904) +#define _MIPIA_CTRL (dev_priv->mipi_mmio_base + 0xb104) +#define _MIPIB_CTRL (dev_priv->mipi_mmio_base + 0xb904) #define MIPI_CTRL(pipe) _PIPE(pipe, _MIPIA_CTRL, _MIPIB_CTRL) #define ESCAPE_CLOCK_DIVIDER_SHIFT 5 /* A only */ #define ESCAPE_CLOCK_DIVIDER_MASK (3 << 5) @@ -6413,21 +6416,21 @@ enum punit_power_well { #define READ_REQUEST_PRIORITY_HIGH (3 << 3) #define RGB_FLIP_TO_BGR (1 << 2) -#define _MIPIA_DATA_ADDRESS (VLV_DISPLAY_BASE + 0xb108) -#define _MIPIB_DATA_ADDRESS (VLV_DISPLAY_BASE + 0xb908) +#define _MIPIA_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb108) +#define _MIPIB_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb908) #define MIPI_DATA_ADDRESS(pipe) _PIPE(pipe, _MIPIA_DATA_ADDRESS, _MIPIB_DATA_ADDRESS) #define DATA_MEM_ADDRESS_SHIFT 5 #define DATA_MEM_ADDRESS_MASK (0x7ffffff << 5) #define DATA_VALID (1 << 0) -#define _MIPIA_DATA_LENGTH (VLV_DISPLAY_BASE + 0xb10c) -#define _MIPIB_DATA_LENGTH (VLV_DISPLAY_BASE + 0xb90c) +#define _MIPIA_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb10c) +#define _MIPIB_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb90c) #define MIPI_DATA_LENGTH(pipe) _PIPE(pipe, _MIPIA_DATA_LENGTH, _MIPIB_DATA_LENGTH) #define DATA_LENGTH_SHIFT 0 #define DATA_LENGTH_MASK (0xfffff << 0) -#define _MIPIA_COMMAND_ADDRESS (VLV_DISPLAY_BASE + 0xb110) -#define _MIPIB_COMMAND_ADDRESS (VLV_DISPLAY_BASE + 0xb910) +#define _MIPIA_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb110) +#define _MIPIB_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb910) #define MIPI_COMMAND_ADDRESS(pipe) _PIPE(pipe, _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS) #define COMMAND_MEM_ADDRESS_SHIFT 5 #define COMMAND_MEM_ADDRESS_MASK (0x7ffffff << 5) @@ -6435,19 +6438,19 @@ enum punit_power_well { #define MEMORY_WRITE_DATA_FROM_PIPE_RENDERING (1 << 1) #define COMMAND_VALID (1 << 0) -#define _MIPIA_COMMAND_LENGTH (VLV_DISPLAY_BASE + 0xb114) -#define _MIPIB_COMMAND_LENGTH (VLV_DISPLAY_BASE + 0xb914) +#define _MIPIA_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb114) +#define _MIPIB_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb914) #define MIPI_COMMAND_LENGTH(pipe) _PIPE(pipe, _MIPIA_COMMAND_LENGTH, _MIPIB_COMMAND_LENGTH) #define COMMAND_LENGTH_SHIFT(n) (8 * (n)) /* n: 0...3 */ #define COMMAND_LENGTH_MASK(n) (0xff << (8 * (n))) -#define _MIPIA_READ_DATA_RETURN0 (VLV_DISPLAY_BASE + 0xb118) -#define _MIPIB_READ_DATA_RETURN0 (VLV_DISPLAY_BASE + 0xb918) +#define _MIPIA_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb118) +#define _MIPIB_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb918) #define MIPI_READ_DATA_RETURN(pipe, n) \ (_PIPE(pipe, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) + 4 * (n)) /* n: 0...7 */ -#define _MIPIA_READ_DATA_VALID (VLV_DISPLAY_BASE + 0xb138) -#define _MIPIB_READ_DATA_VALID (VLV_DISPLAY_BASE + 0xb938) +#define _MIPIA_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb138) +#define _MIPIB_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb938) #define MIPI_READ_DATA_VALID(pipe) _PIPE(pipe, _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID) #define READ_DATA_VALID(n) (1 << (n)) -- cgit v0.10.2 From a2560a66af86f19f3e84122a1887cca81e83dec3 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 2 Jun 2014 18:07:48 +0530 Subject: drm/i915: Use transcoder as index to MIPI regs Conceptually, the MIPI registers are addressed by the MIPI transcoder index, not the pipe. It doesn't matter right now, because there's a 1:1 relationship between pipes and MIPI transcoders, but that change allows us to break that link in the future V1: Created new patch to address Damien's review comment. Replacing _PIPE calls to _TRANSCODER calls V2: Re-basing on patch 2 V3: Re-basing on patch 2 V4: Re-basing on patch 2 Signed-off-by: Shashank Sharma Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d3c2b9f..247be2a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -6045,7 +6045,8 @@ enum punit_power_well { #define _MIPIA_PORT_CTRL (VLV_DISPLAY_BASE + 0x61190) #define _MIPIB_PORT_CTRL (VLV_DISPLAY_BASE + 0x61700) -#define MIPI_PORT_CTRL(pipe) _PIPE(pipe, _MIPIA_PORT_CTRL, _MIPIB_PORT_CTRL) +#define MIPI_PORT_CTRL(tc) _TRANSCODER(tc, _MIPIA_PORT_CTRL, \ + _MIPIB_PORT_CTRL) #define DPI_ENABLE (1 << 31) /* A + B */ #define MIPIA_MIPI4DPHY_DELAY_COUNT_SHIFT 27 #define MIPIA_MIPI4DPHY_DELAY_COUNT_MASK (0xf << 27) @@ -6087,7 +6088,8 @@ enum punit_power_well { #define _MIPIA_TEARING_CTRL (VLV_DISPLAY_BASE + 0x61194) #define _MIPIB_TEARING_CTRL (VLV_DISPLAY_BASE + 0x61704) -#define MIPI_TEARING_CTRL(pipe) _PIPE(pipe, _MIPIA_TEARING_CTRL, _MIPIB_TEARING_CTRL) +#define MIPI_TEARING_CTRL(tc) _TRANSCODER(tc, \ + _MIPIA_TEARING_CTRL, _MIPIB_TEARING_CTRL) #define TEARING_EFFECT_DELAY_SHIFT 0 #define TEARING_EFFECT_DELAY_MASK (0xffff << 0) @@ -6098,7 +6100,8 @@ enum punit_power_well { #define _MIPIA_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb000) #define _MIPIB_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb800) -#define MIPI_DEVICE_READY(pipe) _PIPE(pipe, _MIPIA_DEVICE_READY, _MIPIB_DEVICE_READY) +#define MIPI_DEVICE_READY(tc) _TRANSCODER(tc, _MIPIA_DEVICE_READY, \ + _MIPIB_DEVICE_READY) #define BUS_POSSESSION (1 << 3) /* set to give bus to receiver */ #define ULPS_STATE_MASK (3 << 1) #define ULPS_STATE_ENTER (2 << 1) @@ -6108,10 +6111,12 @@ enum punit_power_well { #define _MIPIA_INTR_STAT (dev_priv->mipi_mmio_base + 0xb004) #define _MIPIB_INTR_STAT (dev_priv->mipi_mmio_base + 0xb804) -#define MIPI_INTR_STAT(pipe) _PIPE(pipe, _MIPIA_INTR_STAT, _MIPIB_INTR_STAT) +#define MIPI_INTR_STAT(tc) _TRANSCODER(tc, _MIPIA_INTR_STAT, \ + _MIPIB_INTR_STAT) #define _MIPIA_INTR_EN (dev_priv->mipi_mmio_base + 0xb008) #define _MIPIB_INTR_EN (dev_priv->mipi_mmio_base + 0xb808) -#define MIPI_INTR_EN(pipe) _PIPE(pipe, _MIPIA_INTR_EN, _MIPIB_INTR_EN) +#define MIPI_INTR_EN(tc) _TRANSCODER(tc, _MIPIA_INTR_EN, \ + _MIPIB_INTR_EN) #define TEARING_EFFECT (1 << 31) #define SPL_PKT_SENT_INTERRUPT (1 << 30) #define GEN_READ_DATA_AVAIL (1 << 29) @@ -6147,7 +6152,8 @@ enum punit_power_well { #define _MIPIA_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb00c) #define _MIPIB_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb80c) -#define MIPI_DSI_FUNC_PRG(pipe) _PIPE(pipe, _MIPIA_DSI_FUNC_PRG, _MIPIB_DSI_FUNC_PRG) +#define MIPI_DSI_FUNC_PRG(tc) _TRANSCODER(tc, _MIPIA_DSI_FUNC_PRG, \ + _MIPIB_DSI_FUNC_PRG) #define CMD_MODE_DATA_WIDTH_MASK (7 << 13) #define CMD_MODE_NOT_SUPPORTED (0 << 13) #define CMD_MODE_DATA_WIDTH_16_BIT (1 << 13) @@ -6170,27 +6176,32 @@ enum punit_power_well { #define _MIPIA_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb010) #define _MIPIB_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb810) -#define MIPI_HS_TX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_HS_TX_TIMEOUT, _MIPIB_HS_TX_TIMEOUT) +#define MIPI_HS_TX_TIMEOUT(tc) _TRANSCODER(tc, _MIPIA_HS_TX_TIMEOUT, \ + _MIPIB_HS_TX_TIMEOUT) #define HIGH_SPEED_TX_TIMEOUT_COUNTER_MASK 0xffffff #define _MIPIA_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb014) #define _MIPIB_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb814) -#define MIPI_LP_RX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_LP_RX_TIMEOUT, _MIPIB_LP_RX_TIMEOUT) +#define MIPI_LP_RX_TIMEOUT(tc) _TRANSCODER(tc, _MIPIA_LP_RX_TIMEOUT, \ + _MIPIB_LP_RX_TIMEOUT) #define LOW_POWER_RX_TIMEOUT_COUNTER_MASK 0xffffff #define _MIPIA_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb018) #define _MIPIB_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb818) -#define MIPI_TURN_AROUND_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT) +#define MIPI_TURN_AROUND_TIMEOUT(tc) _TRANSCODER(tc, \ + _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT) #define TURN_AROUND_TIMEOUT_MASK 0x3f #define _MIPIA_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb01c) #define _MIPIB_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb81c) -#define MIPI_DEVICE_RESET_TIMER(pipe) _PIPE(pipe, _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER) +#define MIPI_DEVICE_RESET_TIMER(tc) _TRANSCODER(tc, \ + _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER) #define DEVICE_RESET_TIMER_MASK 0xffff #define _MIPIA_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb020) #define _MIPIB_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb820) -#define MIPI_DPI_RESOLUTION(pipe) _PIPE(pipe, _MIPIA_DPI_RESOLUTION, _MIPIB_DPI_RESOLUTION) +#define MIPI_DPI_RESOLUTION(tc) _TRANSCODER(tc, _MIPIA_DPI_RESOLUTION, \ + _MIPIB_DPI_RESOLUTION) #define VERTICAL_ADDRESS_SHIFT 16 #define VERTICAL_ADDRESS_MASK (0xffff << 16) #define HORIZONTAL_ADDRESS_SHIFT 0 @@ -6198,7 +6209,8 @@ enum punit_power_well { #define _MIPIA_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb024) #define _MIPIB_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb824) -#define MIPI_DBI_FIFO_THROTTLE(pipe) _PIPE(pipe, _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE) +#define MIPI_DBI_FIFO_THROTTLE(tc) _TRANSCODER(tc, \ + _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE) #define DBI_FIFO_EMPTY_HALF (0 << 0) #define DBI_FIFO_EMPTY_QUARTER (1 << 0) #define DBI_FIFO_EMPTY_7_LOCATIONS (2 << 0) @@ -6206,41 +6218,50 @@ enum punit_power_well { /* regs below are bits 15:0 */ #define _MIPIA_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb028) #define _MIPIB_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb828) -#define MIPI_HSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT) +#define MIPI_HSYNC_PADDING_COUNT(tc) _TRANSCODER(tc, \ + _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT) #define _MIPIA_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb02c) #define _MIPIB_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb82c) -#define MIPI_HBP_COUNT(pipe) _PIPE(pipe, _MIPIA_HBP_COUNT, _MIPIB_HBP_COUNT) +#define MIPI_HBP_COUNT(tc) _TRANSCODER(tc, _MIPIA_HBP_COUNT, \ + _MIPIB_HBP_COUNT) #define _MIPIA_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb030) #define _MIPIB_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb830) -#define MIPI_HFP_COUNT(pipe) _PIPE(pipe, _MIPIA_HFP_COUNT, _MIPIB_HFP_COUNT) +#define MIPI_HFP_COUNT(tc) _TRANSCODER(tc, _MIPIA_HFP_COUNT, \ + _MIPIB_HFP_COUNT) #define _MIPIA_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb034) #define _MIPIB_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb834) -#define MIPI_HACTIVE_AREA_COUNT(pipe) _PIPE(pipe, _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT) +#define MIPI_HACTIVE_AREA_COUNT(tc) _TRANSCODER(tc, \ + _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT) #define _MIPIA_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb038) #define _MIPIB_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb838) -#define MIPI_VSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT) +#define MIPI_VSYNC_PADDING_COUNT(tc) _TRANSCODER(tc, \ + _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT) #define _MIPIA_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb03c) #define _MIPIB_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb83c) -#define MIPI_VBP_COUNT(pipe) _PIPE(pipe, _MIPIA_VBP_COUNT, _MIPIB_VBP_COUNT) +#define MIPI_VBP_COUNT(tc) _TRANSCODER(tc, _MIPIA_VBP_COUNT, \ + _MIPIB_VBP_COUNT) #define _MIPIA_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb040) #define _MIPIB_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb840) -#define MIPI_VFP_COUNT(pipe) _PIPE(pipe, _MIPIA_VFP_COUNT, _MIPIB_VFP_COUNT) +#define MIPI_VFP_COUNT(tc) _TRANSCODER(tc, _MIPIA_VFP_COUNT, \ + _MIPIB_VFP_COUNT) #define _MIPIA_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb044) #define _MIPIB_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb844) -#define MIPI_HIGH_LOW_SWITCH_COUNT(pipe) _PIPE(pipe, _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT) +#define MIPI_HIGH_LOW_SWITCH_COUNT(tc) _TRANSCODER(tc, \ + _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT) /* regs above are bits 15:0 */ #define _MIPIA_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb048) #define _MIPIB_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb848) -#define MIPI_DPI_CONTROL(pipe) _PIPE(pipe, _MIPIA_DPI_CONTROL, _MIPIB_DPI_CONTROL) +#define MIPI_DPI_CONTROL(tc) _TRANSCODER(tc, _MIPIA_DPI_CONTROL, \ + _MIPIB_DPI_CONTROL) #define DPI_LP_MODE (1 << 6) #define BACKLIGHT_OFF (1 << 5) #define BACKLIGHT_ON (1 << 4) @@ -6251,25 +6272,29 @@ enum punit_power_well { #define _MIPIA_DPI_DATA (dev_priv->mipi_mmio_base + 0xb04c) #define _MIPIB_DPI_DATA (dev_priv->mipi_mmio_base + 0xb84c) -#define MIPI_DPI_DATA(pipe) _PIPE(pipe, _MIPIA_DPI_DATA, _MIPIB_DPI_DATA) +#define MIPI_DPI_DATA(tc) _TRANSCODER(tc, _MIPIA_DPI_DATA, \ + _MIPIB_DPI_DATA) #define COMMAND_BYTE_SHIFT 0 #define COMMAND_BYTE_MASK (0x3f << 0) #define _MIPIA_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb050) #define _MIPIB_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb850) -#define MIPI_INIT_COUNT(pipe) _PIPE(pipe, _MIPIA_INIT_COUNT, _MIPIB_INIT_COUNT) +#define MIPI_INIT_COUNT(tc) _TRANSCODER(tc, _MIPIA_INIT_COUNT, \ + _MIPIB_INIT_COUNT) #define MASTER_INIT_TIMER_SHIFT 0 #define MASTER_INIT_TIMER_MASK (0xffff << 0) #define _MIPIA_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb054) #define _MIPIB_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb854) -#define MIPI_MAX_RETURN_PKT_SIZE(pipe) _PIPE(pipe, _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE) +#define MIPI_MAX_RETURN_PKT_SIZE(tc) _TRANSCODER(tc, \ + _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE) #define MAX_RETURN_PKT_SIZE_SHIFT 0 #define MAX_RETURN_PKT_SIZE_MASK (0x3ff << 0) #define _MIPIA_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb058) #define _MIPIB_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb858) -#define MIPI_VIDEO_MODE_FORMAT(pipe) _PIPE(pipe, _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT) +#define MIPI_VIDEO_MODE_FORMAT(tc) _TRANSCODER(tc, \ + _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT) #define RANDOM_DPI_DISPLAY_RESOLUTION (1 << 4) #define DISABLE_VIDEO_BTA (1 << 3) #define IP_TG_CONFIG (1 << 2) @@ -6279,7 +6304,8 @@ enum punit_power_well { #define _MIPIA_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb05c) #define _MIPIB_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb85c) -#define MIPI_EOT_DISABLE(pipe) _PIPE(pipe, _MIPIA_EOT_DISABLE, _MIPIB_EOT_DISABLE) +#define MIPI_EOT_DISABLE(tc) _TRANSCODER(tc, _MIPIA_EOT_DISABLE, \ + _MIPIB_EOT_DISABLE) #define LP_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 7) #define HS_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 6) #define LOW_CONTENTION_RECOVERY_DISABLE (1 << 5) @@ -6291,26 +6317,31 @@ enum punit_power_well { #define _MIPIA_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb060) #define _MIPIB_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb860) -#define MIPI_LP_BYTECLK(pipe) _PIPE(pipe, _MIPIA_LP_BYTECLK, _MIPIB_LP_BYTECLK) +#define MIPI_LP_BYTECLK(tc) _TRANSCODER(tc, _MIPIA_LP_BYTECLK, \ + _MIPIB_LP_BYTECLK) #define LP_BYTECLK_SHIFT 0 #define LP_BYTECLK_MASK (0xffff << 0) /* bits 31:0 */ #define _MIPIA_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb064) #define _MIPIB_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb864) -#define MIPI_LP_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_LP_GEN_DATA, _MIPIB_LP_GEN_DATA) +#define MIPI_LP_GEN_DATA(tc) _TRANSCODER(tc, _MIPIA_LP_GEN_DATA, \ + _MIPIB_LP_GEN_DATA) /* bits 31:0 */ #define _MIPIA_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb068) #define _MIPIB_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb868) -#define MIPI_HS_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_HS_GEN_DATA, _MIPIB_HS_GEN_DATA) +#define MIPI_HS_GEN_DATA(tc) _TRANSCODER(tc, _MIPIA_HS_GEN_DATA, \ + _MIPIB_HS_GEN_DATA) #define _MIPIA_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb06c) #define _MIPIB_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb86c) -#define MIPI_LP_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_LP_GEN_CTRL, _MIPIB_LP_GEN_CTRL) +#define MIPI_LP_GEN_CTRL(tc) _TRANSCODER(tc, _MIPIA_LP_GEN_CTRL, \ + _MIPIB_LP_GEN_CTRL) #define _MIPIA_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb070) #define _MIPIB_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb870) -#define MIPI_HS_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_HS_GEN_CTRL, _MIPIB_HS_GEN_CTRL) +#define MIPI_HS_GEN_CTRL(tc) _TRANSCODER(tc, _MIPIA_HS_GEN_CTRL, \ + _MIPIB_HS_GEN_CTRL) #define LONG_PACKET_WORD_COUNT_SHIFT 8 #define LONG_PACKET_WORD_COUNT_MASK (0xffff << 8) #define SHORT_PACKET_PARAM_SHIFT 8 @@ -6323,7 +6354,8 @@ enum punit_power_well { #define _MIPIA_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb074) #define _MIPIB_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb874) -#define MIPI_GEN_FIFO_STAT(pipe) _PIPE(pipe, _MIPIA_GEN_FIFO_STAT, _MIPIB_GEN_FIFO_STAT) +#define MIPI_GEN_FIFO_STAT(tc) _TRANSCODER(tc, _MIPIA_GEN_FIFO_STAT, \ + _MIPIB_GEN_FIFO_STAT) #define DPI_FIFO_EMPTY (1 << 28) #define DBI_FIFO_EMPTY (1 << 27) #define LP_CTRL_FIFO_EMPTY (1 << 26) @@ -6341,14 +6373,16 @@ enum punit_power_well { #define _MIPIA_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb078) #define _MIPIB_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb878) -#define MIPI_HS_LP_DBI_ENABLE(pipe) _PIPE(pipe, _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE) +#define MIPI_HS_LP_DBI_ENABLE(tc) _TRANSCODER(tc, \ + _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE) #define DBI_HS_LP_MODE_MASK (1 << 0) #define DBI_LP_MODE (1 << 0) #define DBI_HS_MODE (0 << 0) #define _MIPIA_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb080) #define _MIPIB_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb880) -#define MIPI_DPHY_PARAM(pipe) _PIPE(pipe, _MIPIA_DPHY_PARAM, _MIPIB_DPHY_PARAM) +#define MIPI_DPHY_PARAM(tc) _TRANSCODER(tc, _MIPIA_DPHY_PARAM, \ + _MIPIB_DPHY_PARAM) #define EXIT_ZERO_COUNT_SHIFT 24 #define EXIT_ZERO_COUNT_MASK (0x3f << 24) #define TRAIL_COUNT_SHIFT 16 @@ -6361,13 +6395,15 @@ enum punit_power_well { /* bits 31:0 */ #define _MIPIA_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb084) #define _MIPIB_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb884) -#define MIPI_DBI_BW_CTRL(pipe) _PIPE(pipe, _MIPIA_DBI_BW_CTRL, _MIPIB_DBI_BW_CTRL) +#define MIPI_DBI_BW_CTRL(tc) _TRANSCODER(tc, _MIPIA_DBI_BW_CTRL, \ + _MIPIB_DBI_BW_CTRL) #define _MIPIA_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \ + 0xb088) #define _MIPIB_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \ + 0xb888) -#define MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe) _PIPE(pipe, _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT) +#define MIPI_CLK_LANE_SWITCH_TIME_CNT(tc) _TRANSCODER(tc, \ + _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT) #define LP_HS_SSW_CNT_SHIFT 16 #define LP_HS_SSW_CNT_MASK (0xffff << 16) #define HS_LP_PWR_SW_CNT_SHIFT 0 @@ -6375,16 +6411,19 @@ enum punit_power_well { #define _MIPIA_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb08c) #define _MIPIB_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb88c) -#define MIPI_STOP_STATE_STALL(pipe) _PIPE(pipe, _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL) +#define MIPI_STOP_STATE_STALL(tc) _TRANSCODER(tc, \ + _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL) #define STOP_STATE_STALL_COUNTER_SHIFT 0 #define STOP_STATE_STALL_COUNTER_MASK (0xff << 0) #define _MIPIA_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb090) #define _MIPIB_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb890) -#define MIPI_INTR_STAT_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1) +#define MIPI_INTR_STAT_REG_1(tc) _TRANSCODER(tc, \ + _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1) #define _MIPIA_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb094) #define _MIPIB_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb894) -#define MIPI_INTR_EN_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_EN_REG_1, _MIPIB_INTR_EN_REG_1) +#define MIPI_INTR_EN_REG_1(tc) _TRANSCODER(tc, _MIPIA_INTR_EN_REG_1, \ + _MIPIB_INTR_EN_REG_1) #define RX_CONTENTION_DETECTED (1 << 0) /* XXX: only pipe A ?!? */ @@ -6404,7 +6443,8 @@ enum punit_power_well { #define _MIPIA_CTRL (dev_priv->mipi_mmio_base + 0xb104) #define _MIPIB_CTRL (dev_priv->mipi_mmio_base + 0xb904) -#define MIPI_CTRL(pipe) _PIPE(pipe, _MIPIA_CTRL, _MIPIB_CTRL) +#define MIPI_CTRL(tc) _TRANSCODER(tc, _MIPIA_CTRL, \ + _MIPIB_CTRL) #define ESCAPE_CLOCK_DIVIDER_SHIFT 5 /* A only */ #define ESCAPE_CLOCK_DIVIDER_MASK (3 << 5) #define ESCAPE_CLOCK_DIVIDER_1 (0 << 5) @@ -6418,20 +6458,23 @@ enum punit_power_well { #define _MIPIA_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb108) #define _MIPIB_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb908) -#define MIPI_DATA_ADDRESS(pipe) _PIPE(pipe, _MIPIA_DATA_ADDRESS, _MIPIB_DATA_ADDRESS) +#define MIPI_DATA_ADDRESS(tc) _TRANSCODER(tc, _MIPIA_DATA_ADDRESS, \ + _MIPIB_DATA_ADDRESS) #define DATA_MEM_ADDRESS_SHIFT 5 #define DATA_MEM_ADDRESS_MASK (0x7ffffff << 5) #define DATA_VALID (1 << 0) #define _MIPIA_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb10c) #define _MIPIB_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb90c) -#define MIPI_DATA_LENGTH(pipe) _PIPE(pipe, _MIPIA_DATA_LENGTH, _MIPIB_DATA_LENGTH) +#define MIPI_DATA_LENGTH(tc) _TRANSCODER(tc, _MIPIA_DATA_LENGTH, \ + _MIPIB_DATA_LENGTH) #define DATA_LENGTH_SHIFT 0 #define DATA_LENGTH_MASK (0xfffff << 0) #define _MIPIA_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb110) #define _MIPIB_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb910) -#define MIPI_COMMAND_ADDRESS(pipe) _PIPE(pipe, _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS) +#define MIPI_COMMAND_ADDRESS(tc) _TRANSCODER(tc, \ + _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS) #define COMMAND_MEM_ADDRESS_SHIFT 5 #define COMMAND_MEM_ADDRESS_MASK (0x7ffffff << 5) #define AUTO_PWG_ENABLE (1 << 2) @@ -6440,18 +6483,21 @@ enum punit_power_well { #define _MIPIA_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb114) #define _MIPIB_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb914) -#define MIPI_COMMAND_LENGTH(pipe) _PIPE(pipe, _MIPIA_COMMAND_LENGTH, _MIPIB_COMMAND_LENGTH) +#define MIPI_COMMAND_LENGTH(tc) _TRANSCODER(tc, _MIPIA_COMMAND_LENGTH, \ + _MIPIB_COMMAND_LENGTH) #define COMMAND_LENGTH_SHIFT(n) (8 * (n)) /* n: 0...3 */ #define COMMAND_LENGTH_MASK(n) (0xff << (8 * (n))) #define _MIPIA_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb118) #define _MIPIB_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb918) -#define MIPI_READ_DATA_RETURN(pipe, n) \ - (_PIPE(pipe, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) + 4 * (n)) /* n: 0...7 */ +#define MIPI_READ_DATA_RETURN(tc, n) \ + (_TRANSCODER(tc, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) \ + + 4 * (n)) /* n: 0...7 */ #define _MIPIA_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb138) #define _MIPIB_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb938) -#define MIPI_READ_DATA_VALID(pipe) _PIPE(pipe, _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID) +#define MIPI_READ_DATA_VALID(tc) _TRANSCODER(tc, \ + _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID) #define READ_DATA_VALID(n) (1 << (n)) /* For UMS only (deprecated): */ -- cgit v0.10.2 From 7365fb78965725c8ddd5e4d5c061d6d8fbb617e7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 29 May 2014 14:33:21 -0700 Subject: drm/i915: enable PPGTT on VLV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Working for real this time. i915_ppgtt_info has all sorts of good stuff in it and X is running nicely on top. Signed-off-by: Jesse Barnes Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bea9ab40..8631fb3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1936,10 +1936,8 @@ struct drm_i915_cmd_table { #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) #define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) -#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6 && \ - (!IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))) -#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 \ - && !IS_GEN8(dev)) +#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6) +#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 && !IS_GEN8(dev)) #define USES_PPGTT(dev) intel_enable_ppgtt(dev, false) #define USES_FULL_PPGTT(dev) intel_enable_ppgtt(dev, true) -- cgit v0.10.2 From 0e572fe7383a376992364914694c39aa7fe44c1d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Apr 2014 23:55:42 +0200 Subject: drm/i915: runtime PM support for DPMS Keeping track of the power domains is a bit messy since crtc->active is currently updated by the platform hooks, but we need to be aware of which state transition exactly is going on. Maybe we simply need to shovel all the power domain handling down into platform code to simplify this. But doing that requires some more auditing since currently the ->mode_set callbacks still read some random registers (to e.g. figure out the reference clocks). Also note that intel_crtc_update_dpms is always call first/last even for encoders which have their own dpms functions. Hence we really only need to update this place here. Being a quick "does it blow up?" run not really tested yet. v2: Don't do runtime PM in the DPMS hooks for HAS_DDI platforms since that is stalled. Also add a comment to explain what's going on. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e6a2a75..b5cbb28 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4876,16 +4876,43 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *intel_encoder; + enum intel_display_power_domain domain; + unsigned long domains; bool enable = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) enable |= intel_encoder->connectors_active; - if (enable) - dev_priv->display.crtc_enable(crtc); - else - dev_priv->display.crtc_disable(crtc); + if (enable) { + if (!intel_crtc->active) { + /* + * FIXME: DDI plls and relevant code isn't converted + * yet, so do runtime PM for DPMS only for all other + * platforms for now. + */ + if (!HAS_DDI(dev)) { + domains = get_crtc_power_domains(crtc); + for_each_power_domain(domain, domains) + intel_display_power_get(dev_priv, domain); + intel_crtc->enabled_power_domains = domains; + } + + dev_priv->display.crtc_enable(crtc); + } + } else { + if (intel_crtc->active) { + dev_priv->display.crtc_disable(crtc); + + if (!HAS_DDI(dev)) { + domains = intel_crtc->enabled_power_domains; + for_each_power_domain(domain, domains) + intel_display_power_put(dev_priv, domain); + intel_crtc->enabled_power_domains = 0; + } + } + } intel_crtc_update_sarea(crtc, enable); } -- cgit v0.10.2 From d5ec26394c6633d82cc5e7ae6813206ba20fe8f5 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 2 Jun 2014 10:12:06 -0700 Subject: drm/i915: Drop unused lut tables from intel_plane Those LUT where defined in the original sprite patch introducing intel_plane, but were never used. commit b840d907fcf6d5d5ef91af4518b3dab3a5da0f75 Author: Jesse Barnes Date: Tue Dec 13 13:19:38 2011 -0800 drm/i915: add SNB and IVB video sprite support v6 Signed-off-by: Matt Roper Reviewed-by: Damien Lespiau [danvet: Pimp commit message as suggested by Damien] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index cfed575..78d4124 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -428,7 +428,6 @@ struct intel_plane { struct drm_i915_gem_object *obj; bool can_scale; int max_downscale; - u32 lut_r[1024], lut_g[1024], lut_b[1024]; int crtc_x, crtc_y; unsigned int crtc_w, crtc_h; uint32_t src_x, src_y; -- cgit v0.10.2 From 542c184f0ea08ee65794c1f0b044e1db4f8aab1b Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Tue, 3 Jun 2014 10:08:26 +0200 Subject: drm/i915: Simplify intel_gpu_reset Replaced ever growing switch for gen version with chained conditionals. Futre gen's only need to add a new one if they require something different. Reviewed-by: Damien Lespiau Signed-off-by: Robert Beckett [danvet: Picked from internal tree and white-wash commit message.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 871c284..c90222d 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -1154,18 +1154,16 @@ static int gen6_do_reset(struct drm_device *dev) int intel_gpu_reset(struct drm_device *dev) { - switch (INTEL_INFO(dev)->gen) { - case 8: - case 7: - case 6: return gen6_do_reset(dev); - case 5: return ironlake_do_reset(dev); - case 4: - if (IS_G4X(dev)) - return g4x_do_reset(dev); - else - return i965_do_reset(dev); - default: return -ENODEV; - } + if (INTEL_INFO(dev)->gen >= 6) + return gen6_do_reset(dev); + else if (IS_GEN5(dev)) + return ironlake_do_reset(dev); + else if (IS_G4X(dev)) + return g4x_do_reset(dev); + else if (IS_GEN4(dev)) + return i965_do_reset(dev); + else + return -ENODEV; } void intel_uncore_check_errors(struct drm_device *dev) -- cgit v0.10.2 From a211b497eb95b0bd5a55d472600532c8437f0a75 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jun 2014 09:36:23 +0200 Subject: drm/i915: Add #defines for short/long pulse on gmch platforms For no reason at all the public docs lack them, and Dave needs them for his hpd interrupt rework. Cc: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 247be2a..286f05c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2566,8 +2566,14 @@ enum punit_power_well { #define PORTC_HOTPLUG_LIVE_STATUS_VLV (1 << 28) #define PORTB_HOTPLUG_LIVE_STATUS_VLV (1 << 29) #define PORTD_HOTPLUG_INT_STATUS (3 << 21) +#define PORTD_HOTPLUG_INT_LONG_PULSE (2 << 21) +#define PORTD_HOTPLUG_INT_SHORT_PULSE (1 << 21) #define PORTC_HOTPLUG_INT_STATUS (3 << 19) +#define PORTC_HOTPLUG_INT_LONG_PULSE (2 << 19) +#define PORTC_HOTPLUG_INT_SHORT_PULSE (1 << 19) #define PORTB_HOTPLUG_INT_STATUS (3 << 17) +#define PORTB_HOTPLUG_INT_LONG_PULSE (2 << 17) +#define PORTB_HOTPLUG_INT_SHORT_PLUSE (1 << 17) /* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) -- cgit v0.10.2 From 85e90679335f56d162f4a0ff525573818e17ce44 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Thu, 12 Jun 2014 08:35:44 -0700 Subject: drm/i915: disable power wells on suspend We want to make sure everything is disabled and at its lowest power when freezing. Reviewed-by: Imre Deak Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 651e65e..458b778 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -557,6 +557,8 @@ static int i915_drm_freeze(struct drm_device *dev) dev_priv->suspend_count++; + intel_display_set_init_power(dev_priv, false); + return 0; } -- cgit v0.10.2 From 156c7ca08185886329113f20249446af4fb07f60 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 12 Jun 2014 08:35:45 -0700 Subject: drm/i915: leave rc6 enabled at suspend time v4 This allows the system to enter the lowest power mode during system freeze. v2: delete force wake timer at suspend (Imre) v3: add GT work suspend function (Imre) v4: use uncore forcewake reset (Daniel) Reviewed-by: Imre Deak Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 458b778..704dfe3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -529,7 +529,7 @@ static int i915_drm_freeze(struct drm_device *dev) drm_irq_uninstall(dev); dev_priv->enable_hotplug_processing = false; - intel_disable_gt_powersave(dev); + intel_suspend_gt_powersave(dev); /* * Disable CRTCs directly since we want to preserve sw state @@ -548,8 +548,8 @@ static int i915_drm_freeze(struct drm_device *dev) i915_save_state(dev); + intel_uncore_forcewake_reset(dev, false); intel_opregion_fini(dev); - intel_uncore_fini(dev); console_lock(); intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 49414d3..be4cd5e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2085,6 +2085,7 @@ extern void intel_uncore_early_sanitize(struct drm_device *dev); extern void intel_uncore_init(struct drm_device *dev); extern void intel_uncore_check_errors(struct drm_device *dev); extern void intel_uncore_fini(struct drm_device *dev); +extern void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore); void i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bda0ae3..a23b83d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -961,6 +961,7 @@ void intel_init_gt_powersave(struct drm_device *dev); void intel_cleanup_gt_powersave(struct drm_device *dev); void intel_enable_gt_powersave(struct drm_device *dev); void intel_disable_gt_powersave(struct drm_device *dev); +void intel_suspend_gt_powersave(struct drm_device *dev); void intel_reset_gt_powersave(struct drm_device *dev); void ironlake_teardown_rc6(struct drm_device *dev); void gen6_update_ring_freq(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d1e53ab..685b491 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4675,6 +4675,26 @@ void intel_cleanup_gt_powersave(struct drm_device *dev) valleyview_cleanup_gt_powersave(dev); } +/** + * intel_suspend_gt_powersave - suspend PM work and helper threads + * @dev: drm device + * + * We don't want to disable RC6 or other features here, we just want + * to make sure any work we've queued has finished and won't bother + * us while we're suspended. + */ +void intel_suspend_gt_powersave(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Interrupts should be disabled already to avoid re-arming. */ + WARN_ON(dev->irq_enabled); + + flush_delayed_work(&dev_priv->rps.delayed_resume_work); + + cancel_work_sync(&dev_priv->rps.work); +} + void intel_disable_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 79cba59..53e13ae 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -315,7 +315,7 @@ static void gen6_force_wake_timer(unsigned long arg) intel_runtime_pm_put(dev_priv); } -static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) +void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) { struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; -- cgit v0.10.2 From 10d8d366790ff8da28c1853b2c795f3a74a4fdaa Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 12 Jun 2014 17:48:52 +0200 Subject: drm/i915: Unifiy GT powersave suspend logic Jesse's patch to only quiescent our rps work and Imre's fix to address a race with runtime pm and the forcewake reference held by the used diverging means to address the same bug: Jesse's patch uses flush_delayed_work while (since we want to make sure rps is set up) while Imre's used a cancel+manuel refcount adjustment. Unify them again by simply reusing intel_suspend_gt_powersave in intel_disable_gt_powersave. Cc: Imre Deak Cc: Jesse Barnes Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 685b491..4912220 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4706,10 +4706,8 @@ void intel_disable_gt_powersave(struct drm_device *dev) ironlake_disable_drps(dev); ironlake_disable_rc6(dev); } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) { - if (cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work)) - intel_runtime_pm_put(dev_priv); + intel_suspend_gt_powersave(dev); - cancel_work_sync(&dev_priv->rps.work); mutex_lock(&dev_priv->rps.hw_lock); if (IS_VALLEYVIEW(dev)) valleyview_disable_rps(dev); -- cgit v0.10.2 From e5747e3adcd67ae27105003ec99fb58cba180105 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 12 Jun 2014 08:35:47 -0700 Subject: drm/i915: send proper opregion notifications on suspend/resume This indicates to the firmware that it can power down various other components or bring them back up, depending on the target system state. Reviewed-by: Imre Deak Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 704dfe3..43b8043 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -28,6 +28,7 @@ */ #include +#include #include #include #include "i915_drv.h" @@ -499,6 +500,7 @@ static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; + pci_power_t opregion_target_state; intel_runtime_pm_get(dev_priv); @@ -548,6 +550,12 @@ static int i915_drm_freeze(struct drm_device *dev) i915_save_state(dev); + if (acpi_target_system_state() >= ACPI_STATE_S3) + opregion_target_state = PCI_D3cold; + else + opregion_target_state = PCI_D1; + intel_opregion_notify_adapter(dev, opregion_target_state); + intel_uncore_forcewake_reset(dev, false); intel_opregion_fini(dev); @@ -680,6 +688,8 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) dev_priv->modeset_restore = MODESET_DONE; mutex_unlock(&dev_priv->modeset_restore_lock); + intel_opregion_notify_adapter(dev, PCI_D0); + intel_runtime_pm_put(dev_priv); return 0; } -- cgit v0.10.2 From 8abdc17941c71b37311bb93876ac83dce58160c8 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Thu, 12 Jun 2014 08:35:48 -0700 Subject: drm/i915: make sure PC8 is enabled on suspend and disabled on resume v4 This matches the runtime suspend paths and allows the system to enter the lowest power mode at freeze time. v2: move disable_pc8 call to thaw_early (Imre) move enable_pc8 to freeze_late (Imre/Jesse) v3: drop spurious hunk from _freeze now that we have freeze_late (Jesse) v4: move back to suspend_late (Imre was right) Reviewed-by: Imre Deak Signed-off-by: Kristen Carlson Accardi Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 43b8043..3536bfe 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -616,6 +616,9 @@ static int i915_drm_thaw_early(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + hsw_disable_pc8(dev_priv); + intel_uncore_early_sanitize(dev); intel_uncore_sanitize(dev); intel_power_domains_init_hw(dev_priv); @@ -899,6 +902,7 @@ static int i915_pm_suspend_late(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_i915_private *dev_priv = drm_dev->dev_private; /* * We have a suspedn ordering issue with the snd-hda driver also @@ -912,6 +916,9 @@ static int i915_pm_suspend_late(struct device *dev) if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + if (IS_HASWELL(drm_dev) || IS_BROADWELL(drm_dev)) + hsw_enable_pc8(dev_priv); + pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); -- cgit v0.10.2 From a33d7105c4e905c895c41d8caf6bd8f85b2e3ce7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 6 Jun 2014 08:22:08 +0200 Subject: drm/i915: Only wait one vblank when disabling crc if the pipe is on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we incur an unsightly WARNING. The mutex locking is a bit overkill, but it curbs races and eventially we might grow a locking check in the vblank wait code to make sure the right crtc lock is held. This is fallout from commit 9393707190194eb8b42e412b444a03331db6862f Author: Jesse Barnes AuthorDate: Fri Apr 4 16:12:09 2014 -0700 drm/i915: warn when a vblank wait times out Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=79612 Tested-by: Guo Jinxian Reviewed-by: Ville Syrjälä [danvet: Rebase on top of drm core ww locking changes.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7e884ea..252c4bc 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2923,11 +2923,16 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, /* real source -> none transition */ if (source == INTEL_PIPE_CRC_SOURCE_NONE) { struct intel_pipe_crc_entry *entries; + struct intel_crtc *crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n", pipe_name(pipe)); - intel_wait_for_vblank(dev, pipe); + drm_modeset_lock(&crtc->base.mutex, NULL); + if (crtc->active) + intel_wait_for_vblank(dev, pipe); + drm_modeset_unlock(&crtc->base.mutex); spin_lock_irq(&pipe_crc->lock); entries = pipe_crc->entries; -- cgit v0.10.2 From ef0cf27c4d0209d0c38c2ddf8f2c529706912861 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 6 Jun 2014 10:22:54 +0100 Subject: drm/i915: Use the .release hook to drop the stolen drm_mm tracking Now that we have a release hook into i915_gem_object_free, we can move the explicit call to the internal stolen function and hook it up throught the callback instead. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8631fb3..c565e73 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2439,7 +2439,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, u32 stolen_offset, u32 gtt_offset, u32 size); -void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d86b77e..3768199 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4444,7 +4444,6 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) obj->madv = I915_MADV_DONTNEED; i915_gem_object_put_pages(obj); i915_gem_object_free_mmap_offset(obj); - i915_gem_object_release_stolen(obj); BUG_ON(obj->pages); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 62ef55b..6441178 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -292,9 +292,20 @@ static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj) kfree(obj->pages); } + +static void +i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) +{ + if (obj->stolen) { + drm_mm_remove_node(obj->stolen); + kfree(obj->stolen); + obj->stolen = NULL; + } +} static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { .get_pages = i915_gem_object_get_pages_stolen, .put_pages = i915_gem_object_put_pages_stolen, + .release = i915_gem_object_release_stolen, }; static struct drm_i915_gem_object * @@ -452,13 +463,3 @@ err_out: drm_gem_object_unreference(&obj->base); return NULL; } - -void -i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) -{ - if (obj->stolen) { - drm_mm_remove_node(obj->stolen); - kfree(obj->stolen); - obj->stolen = NULL; - } -} -- cgit v0.10.2 From e0f0273eb9a61a99fed3e49c1e673bb73a45d8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 5 Jun 2014 19:15:50 +0300 Subject: drm/i915: Use named initializers for gmch wm params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using names initializers when filling out the watermark structs saves you from having go look up the struct definition every single time. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ee27d74..f83d1ff 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -865,95 +865,95 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane) /* Pineview has different values for various configs */ static const struct intel_watermark_params pineview_display_wm = { - PINEVIEW_DISPLAY_FIFO, - PINEVIEW_MAX_WM, - PINEVIEW_DFT_WM, - PINEVIEW_GUARD_WM, - PINEVIEW_FIFO_LINE_SIZE + .fifo_size = PINEVIEW_DISPLAY_FIFO, + .max_wm = PINEVIEW_MAX_WM, + .default_wm = PINEVIEW_DFT_WM, + .guard_size = PINEVIEW_GUARD_WM, + .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, }; static const struct intel_watermark_params pineview_display_hplloff_wm = { - PINEVIEW_DISPLAY_FIFO, - PINEVIEW_MAX_WM, - PINEVIEW_DFT_HPLLOFF_WM, - PINEVIEW_GUARD_WM, - PINEVIEW_FIFO_LINE_SIZE + .fifo_size = PINEVIEW_DISPLAY_FIFO, + .max_wm = PINEVIEW_MAX_WM, + .default_wm = PINEVIEW_DFT_HPLLOFF_WM, + .guard_size = PINEVIEW_GUARD_WM, + .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, }; static const struct intel_watermark_params pineview_cursor_wm = { - PINEVIEW_CURSOR_FIFO, - PINEVIEW_CURSOR_MAX_WM, - PINEVIEW_CURSOR_DFT_WM, - PINEVIEW_CURSOR_GUARD_WM, - PINEVIEW_FIFO_LINE_SIZE, + .fifo_size = PINEVIEW_CURSOR_FIFO, + .max_wm = PINEVIEW_CURSOR_MAX_WM, + .default_wm = PINEVIEW_CURSOR_DFT_WM, + .guard_size = PINEVIEW_CURSOR_GUARD_WM, + .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, }; static const struct intel_watermark_params pineview_cursor_hplloff_wm = { - PINEVIEW_CURSOR_FIFO, - PINEVIEW_CURSOR_MAX_WM, - PINEVIEW_CURSOR_DFT_WM, - PINEVIEW_CURSOR_GUARD_WM, - PINEVIEW_FIFO_LINE_SIZE + .fifo_size = PINEVIEW_CURSOR_FIFO, + .max_wm = PINEVIEW_CURSOR_MAX_WM, + .default_wm = PINEVIEW_CURSOR_DFT_WM, + .guard_size = PINEVIEW_CURSOR_GUARD_WM, + .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, }; static const struct intel_watermark_params g4x_wm_info = { - G4X_FIFO_SIZE, - G4X_MAX_WM, - G4X_MAX_WM, - 2, - G4X_FIFO_LINE_SIZE, + .fifo_size = G4X_FIFO_SIZE, + .max_wm = G4X_MAX_WM, + .default_wm = G4X_MAX_WM, + .guard_size = 2, + .cacheline_size = G4X_FIFO_LINE_SIZE, }; static const struct intel_watermark_params g4x_cursor_wm_info = { - I965_CURSOR_FIFO, - I965_CURSOR_MAX_WM, - I965_CURSOR_DFT_WM, - 2, - G4X_FIFO_LINE_SIZE, + .fifo_size = I965_CURSOR_FIFO, + .max_wm = I965_CURSOR_MAX_WM, + .default_wm = I965_CURSOR_DFT_WM, + .guard_size = 2, + .cacheline_size = G4X_FIFO_LINE_SIZE, }; static const struct intel_watermark_params valleyview_wm_info = { - VALLEYVIEW_FIFO_SIZE, - VALLEYVIEW_MAX_WM, - VALLEYVIEW_MAX_WM, - 2, - G4X_FIFO_LINE_SIZE, + .fifo_size = VALLEYVIEW_FIFO_SIZE, + .max_wm = VALLEYVIEW_MAX_WM, + .default_wm = VALLEYVIEW_MAX_WM, + .guard_size = 2, + .cacheline_size = G4X_FIFO_LINE_SIZE, }; static const struct intel_watermark_params valleyview_cursor_wm_info = { - I965_CURSOR_FIFO, - VALLEYVIEW_CURSOR_MAX_WM, - I965_CURSOR_DFT_WM, - 2, - G4X_FIFO_LINE_SIZE, + .fifo_size = I965_CURSOR_FIFO, + .max_wm = VALLEYVIEW_CURSOR_MAX_WM, + .default_wm = I965_CURSOR_DFT_WM, + .guard_size = 2, + .cacheline_size = G4X_FIFO_LINE_SIZE, }; static const struct intel_watermark_params i965_cursor_wm_info = { - I965_CURSOR_FIFO, - I965_CURSOR_MAX_WM, - I965_CURSOR_DFT_WM, - 2, - I915_FIFO_LINE_SIZE, + .fifo_size = I965_CURSOR_FIFO, + .max_wm = I965_CURSOR_MAX_WM, + .default_wm = I965_CURSOR_DFT_WM, + .guard_size = 2, + .cacheline_size = I915_FIFO_LINE_SIZE, }; static const struct intel_watermark_params i945_wm_info = { - I945_FIFO_SIZE, - I915_MAX_WM, - 1, - 2, - I915_FIFO_LINE_SIZE + .fifo_size = I945_FIFO_SIZE, + .max_wm = I915_MAX_WM, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I915_FIFO_LINE_SIZE, }; static const struct intel_watermark_params i915_wm_info = { - I915_FIFO_SIZE, - I915_MAX_WM, - 1, - 2, - I915_FIFO_LINE_SIZE + .fifo_size = I915_FIFO_SIZE, + .max_wm = I915_MAX_WM, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I915_FIFO_LINE_SIZE, }; static const struct intel_watermark_params i830_wm_info = { - I855GM_FIFO_SIZE, - I915_MAX_WM, - 1, - 2, - I830_FIFO_LINE_SIZE + .fifo_size = I855GM_FIFO_SIZE, + .max_wm = I915_MAX_WM, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I830_FIFO_LINE_SIZE, }; static const struct intel_watermark_params i845_wm_info = { - I830_FIFO_SIZE, - I915_MAX_WM, - 1, - 2, - I830_FIFO_LINE_SIZE + .fifo_size = I830_FIFO_SIZE, + .max_wm = I915_MAX_WM, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I830_FIFO_LINE_SIZE, }; /** -- cgit v0.10.2 From f417c11b8987245826aa8b2b606ce2f9d5edbd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 5 Jun 2014 19:15:52 +0300 Subject: drm/i915: Don't get hw state from DVO chip unless DVO is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Certain DVO chips (ns2501 for example) don't like to be accessed unless the PLL is running. Simply skip the DVO get_hw_state if the DVO port is disabled. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index a3631c0..5d7d975 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -112,7 +112,15 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector) static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector) { + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base); + u32 tmp; + + tmp = I915_READ(intel_dvo->dev.dvo_reg); + + if (!(tmp & DVO_ENABLE)) + return false; return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev); } -- cgit v0.10.2 From daa3afb27d3026ac15389bcb040b946f1784a97a Mon Sep 17 00:00:00 2001 From: Tom O'Rourke Date: Fri, 30 May 2014 16:22:10 -0700 Subject: drm/i915/bdw: Add Broadwell support for debugfs rps freq info Add Broadwell support to i915_frequency_info and extend i915_max|min_freq_get|set to (gen >= 6). v2: generalized support for i915_max|min_freq_get|set (Daniel). Signed-off-by: Tom O'Rourke Reviewed-by: Jeff McGee [danvet: Fix checkpatch fail.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 252c4bc..7b83297 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1027,7 +1027,8 @@ static int i915_frequency_info(struct seq_file *m, void *unused) MEMSTAT_VID_SHIFT); seq_printf(m, "Current P-state: %d\n", (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT); - } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) { + } else if (IS_GEN6(dev) || (IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) || + IS_BROADWELL(dev)) { u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); @@ -1046,7 +1047,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) reqf = I915_READ(GEN6_RPNSWREQ); reqf &= ~GEN6_TURBO_DISABLE; - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) reqf >>= 24; else reqf >>= 25; @@ -1063,7 +1064,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI); rpcurdown = I915_READ(GEN6_RP_CUR_DOWN); rpprevdown = I915_READ(GEN6_RP_PREV_DOWN); - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; else cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; @@ -3505,7 +3506,7 @@ i915_max_freq_get(void *data, u64 *val) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (INTEL_INFO(dev)->gen < 6) return -ENODEV; flush_delayed_work(&dev_priv->rps.delayed_resume_work); @@ -3531,7 +3532,7 @@ i915_max_freq_set(void *data, u64 val) u32 rp_state_cap, hw_max, hw_min; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (INTEL_INFO(dev)->gen < 6) return -ENODEV; flush_delayed_work(&dev_priv->rps.delayed_resume_work); @@ -3586,7 +3587,7 @@ i915_min_freq_get(void *data, u64 *val) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (INTEL_INFO(dev)->gen < 6) return -ENODEV; flush_delayed_work(&dev_priv->rps.delayed_resume_work); @@ -3612,7 +3613,7 @@ i915_min_freq_set(void *data, u64 val) u32 rp_state_cap, hw_max, hw_min; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (INTEL_INFO(dev)->gen < 6) return -ENODEV; flush_delayed_work(&dev_priv->rps.delayed_resume_work); -- cgit v0.10.2 From fdaf66de9e4e242bb1ce8b41db49551830336b44 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 6 Jun 2014 18:22:06 +0100 Subject: drm/i915: Use %c in a format string for the pipe name pipe_name() returns an ascii character. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 088fe93..27975c3 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -417,7 +417,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, } crtcs[i] = new_crtc; - DRM_DEBUG_KMS("connector %s on pipe %d [CRTC:%d]: %dx%d%s\n", + DRM_DEBUG_KMS("connector %s on pipe %c [CRTC:%d]: %dx%d%s\n", connector->name, pipe_name(to_intel_crtc(encoder->crtc)->pipe), encoder->crtc->base.id, -- cgit v0.10.2 From 9e31c2a590f27309fb8a419ba2943879af26a8cd Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 6 Jun 2014 14:04:37 +0300 Subject: drm/i915: fix possible refcount leak when resetting forcewake If the timer putting the last forcewake refcount was pending and we canceled it, we'll leak the corresponding forcewake and RPM references. v2: - do the ptr casting at the caller instead of adding a separate helper for this (Chris) Signed-off-by: Imre Deak Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index c90222d..6a73078 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -321,7 +321,8 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; - del_timer_sync(&dev_priv->uncore.force_wake_timer); + if (del_timer_sync(&dev_priv->uncore.force_wake_timer)) + gen6_force_wake_timer((unsigned long)dev_priv); /* Hold uncore.lock across reset to prevent any register access * with forcewake not set correctly -- cgit v0.10.2 From 10018603a481e4c1067e825e449f2e19dd737095 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 6 Jun 2014 12:59:39 +0300 Subject: drm/i915: preserve user forcewake over system suspend/resume Atm, the forcewake refcount will be incorrectly set to zero during system suspend if there is any reference held via the i915_forcewake_user debugfs entry. Fix this by simply not zeroing the sw counters during suspend and restoring the original state using them. Note that the only other places where we zeroed the counters were driver load and unload time, where it was redundant anyway. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=78059 Signed-off-by: Imre Deak Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 615b62f..5a08c86 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -600,7 +600,7 @@ static int i915_drm_thaw_early(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - intel_uncore_early_sanitize(dev); + intel_uncore_early_sanitize(dev, true); intel_uncore_sanitize(dev); intel_power_domains_init_hw(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c565e73..eea98f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2078,7 +2078,8 @@ extern void intel_irq_init(struct drm_device *dev); extern void intel_hpd_init(struct drm_device *dev); extern void intel_uncore_sanitize(struct drm_device *dev); -extern void intel_uncore_early_sanitize(struct drm_device *dev); +extern void intel_uncore_early_sanitize(struct drm_device *dev, + bool restore_forcewake); extern void intel_uncore_init(struct drm_device *dev); extern void intel_uncore_check_errors(struct drm_device *dev); extern void intel_uncore_fini(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 6a73078..11cc792 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -358,16 +358,12 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) dev_priv->uncore.fifo_count = __raw_i915_read32(dev_priv, GTFIFOCTL) & GT_FIFO_FREE_ENTRIES_MASK; - } else { - dev_priv->uncore.forcewake_count = 0; - dev_priv->uncore.fw_rendercount = 0; - dev_priv->uncore.fw_mediacount = 0; } spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } -void intel_uncore_early_sanitize(struct drm_device *dev) +void intel_uncore_early_sanitize(struct drm_device *dev, bool restore_forcewake) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -390,7 +386,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev) __raw_i915_write32(dev_priv, GTFIFODBG, __raw_i915_read32(dev_priv, GTFIFODBG)); - intel_uncore_forcewake_reset(dev, false); + intel_uncore_forcewake_reset(dev, restore_forcewake); } void intel_uncore_sanitize(struct drm_device *dev) @@ -821,7 +817,7 @@ void intel_uncore_init(struct drm_device *dev) setup_timer(&dev_priv->uncore.force_wake_timer, gen6_force_wake_timer, (unsigned long)dev_priv); - intel_uncore_early_sanitize(dev); + intel_uncore_early_sanitize(dev, false); if (IS_VALLEYVIEW(dev)) { dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get; -- cgit v0.10.2 From 75a91c975b7d127182ac5742277d33a310560be8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 6 Jun 2014 22:40:42 +0200 Subject: drm/i915: Update DRIVER_DATE to 20140606 Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index eea98f4..506386e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -53,7 +53,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20080730" +#define DRIVER_DATE "20140606" enum pipe { INVALID_PIPE = -1, -- cgit v0.10.2 From a8aab8bd5efe1377ea2b0d2b51b26989eea6906d Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 5 Jun 2014 14:28:17 -0700 Subject: drm/i915: Fix VLV CRC reading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding missing Display mmio reg offset. Credits-to: Laws, Philip Cc: He, Shuang Signed-off-by: Rodrigo Vivi Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 286f05c..05e2541 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2627,7 +2627,7 @@ enum punit_power_well { #define PORT_DFT_I9XX 0x61150 #define DC_BALANCE_RESET (1 << 25) -#define PORT_DFT2_G4X 0x61154 +#define PORT_DFT2_G4X (dev_priv->info.display_mmio_offset + 0x61154) #define DC_BALANCE_RESET_VLV (1 << 31) #define PIPE_SCRAMBLE_RESET_MASK (0x3 << 0) #define PIPE_B_SCRAMBLE_RESET (1 << 1) -- cgit v0.10.2 From b90b91d87038f6b257b40a02b42ed4f9705e06f0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 10 Jun 2014 12:14:40 +0100 Subject: drm/i915: Prefault the entire object on first page fault Inserting additional PTEs has no side-effect for us as the pfn are fixed for the entire time the object is resident in the global GTT. The downside is that we pay the entire cost of faulting the object upon the first hit, for which we in return receive the benefit of removing the per-page faulting overhead. On an Ivybridge i7-3720qm with 1600MHz DDR3, with 32 fences, Upload rate for 2 linear surfaces: 8127MiB/s -> 8134MiB/s Upload rate for 2 tiled surfaces: 8607MiB/s -> 8625MiB/s Upload rate for 4 linear surfaces: 8127MiB/s -> 8127MiB/s Upload rate for 4 tiled surfaces: 8611MiB/s -> 8602MiB/s Upload rate for 8 linear surfaces: 8114MiB/s -> 8124MiB/s Upload rate for 8 tiled surfaces: 8601MiB/s -> 8603MiB/s Upload rate for 16 linear surfaces: 8110MiB/s -> 8123MiB/s Upload rate for 16 tiled surfaces: 8595MiB/s -> 8606MiB/s Upload rate for 32 linear surfaces: 8104MiB/s -> 8121MiB/s Upload rate for 32 tiled surfaces: 8589MiB/s -> 8605MiB/s Upload rate for 64 linear surfaces: 8107MiB/s -> 8121MiB/s Upload rate for 64 tiled surfaces: 2013MiB/s -> 3017MiB/s Signed-off-by: Chris Wilson Cc: "Goel, Akash" Testcasee: igt/gem_fence_upload/performance Reviewed-by: Brad Volkin Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3768199..c313cb2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1561,14 +1561,26 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (ret) goto unpin; - obj->fault_mappable = true; - + /* Finally, remap it using the new GTT offset */ pfn = dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj); pfn >>= PAGE_SHIFT; - pfn += page_offset; - /* Finally, remap it using the new GTT offset */ - ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); + if (!obj->fault_mappable) { + int i; + + for (i = 0; i < obj->base.size >> PAGE_SHIFT; i++) { + ret = vm_insert_pfn(vma, + (unsigned long)vma->vm_start + i * PAGE_SIZE, + pfn + i); + if (ret) + break; + } + + obj->fault_mappable = true; + } else + ret = vm_insert_pfn(vma, + (unsigned long)vmf->virtual_address, + pfn + page_offset); unpin: i915_gem_object_ggtt_unpin(obj); unlock: -- cgit v0.10.2 From 3b2cc8ab64bf5d643ac9a917653e0cb6fce32ffc Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Wed, 11 Jun 2014 16:17:16 +0100 Subject: drm/i915/bdw: Do not write the Semaphore Sync Registers in GEN8+ These do not exist anymore. Spotted while reading through intel_ringbuffer.c Signed-off-by: Oscar Mateo Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 279488a..0eaaaec 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1746,14 +1746,15 @@ int intel_ring_cacheline_align(struct intel_engine_cs *ring) void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno) { - struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; BUG_ON(ring->outstanding_lazy_seqno); - if (INTEL_INFO(ring->dev)->gen >= 6) { + if (INTEL_INFO(dev)->gen == 6 || INTEL_INFO(dev)->gen == 7) { I915_WRITE(RING_SYNC_0(ring->mmio_base), 0); I915_WRITE(RING_SYNC_1(ring->mmio_base), 0); - if (HAS_VEBOX(ring->dev)) + if (HAS_VEBOX(dev)) I915_WRITE(RING_SYNC_2(ring->mmio_base), 0); } -- cgit v0.10.2 From 7405f42c790f123f5301dbd2a9ebb69ccd909f47 Mon Sep 17 00:00:00 2001 From: Tom O'Rourke Date: Tue, 10 Jun 2014 16:26:34 -0700 Subject: drm/i915/chv: Fix "drm/i915/chv: Add a bunch of pre production workarounds" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct a merge mishap in commit e4443e459ccf43f2c139358400365fd6a839d40d. Wa*:chv belongs in cherryview_enable_rps, not gen8_enable_rps. Signed-off-by: Tom O'Rourke Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f83d1ff..537a7ee 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3513,15 +3513,11 @@ static void gen8_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); - /* WaDisablePwrmtrEvent:chv (pre-production hw) */ - I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff); - I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00); - /* 5: Enable RPS */ I915_WRITE(GEN6_RP_CONTROL, GEN6_RP_MEDIA_TURBO | GEN6_RP_MEDIA_HW_NORMAL_MODE | - GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */ + GEN6_RP_MEDIA_IS_GFX | GEN6_RP_ENABLE | GEN6_RP_UP_BUSY_AVG | GEN6_RP_DOWN_IDLE_AVG); @@ -4015,10 +4011,14 @@ static void cherryview_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + /* WaDisablePwrmtrEvent:chv (pre-production hw) */ + I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff); + I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00); + /* 5: Enable RPS */ I915_WRITE(GEN6_RP_CONTROL, GEN6_RP_MEDIA_HW_NORMAL_MODE | - GEN6_RP_MEDIA_IS_GFX | + GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */ GEN6_RP_ENABLE | GEN6_RP_UP_BUSY_AVG | GEN6_RP_DOWN_IDLE_AVG); -- cgit v0.10.2 From c394c2b08e247c32ef292b75fd8b34312465f8ae Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:08 -0700 Subject: drm: Refactor framebuffer creation to allow internal use (v2) Refactor DRM framebuffer creation into a new function that returns a struct drm_framebuffer directly. The upcoming universal cursor support will want to create framebuffers internally to wrap cursor buffers, so we want to be able to share that framebuffer creation with the drm_mode_addfb2 ioctl handler. v2: Take struct drm_mode_fb_cmd2 parameter directly rather than void* Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fe94cc1..5a88267 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -41,6 +41,10 @@ #include "drm_crtc_internal.h" +static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv); + /** * drm_modeset_lock_all - take all modeset locks * @dev: drm device @@ -2827,56 +2831,38 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) return 0; } -/** - * drm_mode_addfb2 - add an FB to the graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Add a new FB to the specified CRTC, given a user request with format. This is - * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers - * and uses fourcc codes as pixel format specifiers. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, errno on failure. - */ -int drm_mode_addfb2(struct drm_device *dev, - void *data, struct drm_file *file_priv) +static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv) { - struct drm_mode_fb_cmd2 *r = data; struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; int ret; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - if (r->flags & ~DRM_MODE_FB_INTERLACED) { DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); - return -EINVAL; + return ERR_PTR(-EINVAL); } if ((config->min_width > r->width) || (r->width > config->max_width)) { DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", r->width, config->min_width, config->max_width); - return -EINVAL; + return ERR_PTR(-EINVAL); } if ((config->min_height > r->height) || (r->height > config->max_height)) { DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", r->height, config->min_height, config->max_height); - return -EINVAL; + return ERR_PTR(-EINVAL); } ret = framebuffer_check(r); if (ret) - return ret; + return ERR_PTR(ret); fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - return PTR_ERR(fb); + return fb; } mutex_lock(&file_priv->fbs_lock); @@ -2885,8 +2871,37 @@ int drm_mode_addfb2(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); mutex_unlock(&file_priv->fbs_lock); + return fb; +} - return ret; +/** + * drm_mode_addfb2 - add an FB to the graphics configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Add a new FB to the specified CRTC, given a user request with format. This is + * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers + * and uses fourcc codes as pixel format specifiers. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_addfb2(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_framebuffer *fb; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = add_framebuffer_internal(dev, data, file_priv); + if (IS_ERR(fb)) + return PTR_ERR(fb); + + return 0; } /** -- cgit v0.10.2 From b36552b32aa9c69e83a3a20bda56379fb9e52435 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:09 -0700 Subject: drm: Refactor setplane to allow internal use (v3) Refactor DRM setplane code into a new setplane_internal() function that takes DRM objects directly as parameters rather than looking them up by ID. We'll use this in a future patch when we implement legacy cursor ioctls on top of the universal plane interface. v3: - Move integer overflow checking from setplane_internal to setplane ioctl. The upcoming legacy cursor support via universal planes needs to maintain current cursor ioctl semantics and not return error for these extreme values (found via intel-gpu-tools kms_cursor_crc test). v2: - Allow planes to be disabled without a valid crtc again (and add mention of this to setplane's kerneldoc, since it doesn't seem to be mentioned anywhere else). - Reformat some parameter line wrap Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5a88267..27eae03 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2122,45 +2122,32 @@ out: return ret; } -/** - * drm_mode_setplane - configure a plane's configuration - * @dev: DRM device - * @data: ioctl data* - * @file_priv: DRM file info +/* + * setplane_internal - setplane handler for internal callers * - * Set plane configuration, including placement, fb, scaling, and other factors. - * Or pass a NULL fb to disable. + * Note that we assume an extra reference has already been taken on fb. If the + * update fails, this reference will be dropped before return; if it succeeds, + * the previous framebuffer (if any) will be unreferenced instead. * - * Returns: - * Zero on success, errno on failure. + * src_{x,y,w,h} are provided in 16.16 fixed point format */ -int drm_mode_setplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) +static int setplane_internal(struct drm_crtc *crtc, + struct drm_plane *plane, + struct drm_framebuffer *fb, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + /* src_{x,y,w,h} values are 16.16 fixed point */ + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) { - struct drm_mode_set_plane *plane_req = data; - struct drm_plane *plane; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; + struct drm_device *dev = crtc->dev; + struct drm_framebuffer *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; int i; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ - plane = drm_plane_find(dev, plane_req->plane_id); - if (!plane) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - plane_req->plane_id); - return -ENOENT; - } - /* No fb means shut it down */ - if (!plane_req->fb_id) { + if (!fb) { drm_modeset_lock_all(dev); old_fb = plane->fb; ret = plane->funcs->disable_plane(plane); @@ -2174,14 +2161,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } - crtc = drm_crtc_find(dev, plane_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - ret = -ENOENT; - goto out; - } - /* Check whether this plane is usable on this CRTC */ if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { DRM_DEBUG_KMS("Invalid crtc for plane\n"); @@ -2189,14 +2168,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - ret = -ENOENT; - goto out; - } - /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) if (fb->pixel_format == plane->format_types[i]) @@ -2212,43 +2183,25 @@ int drm_mode_setplane(struct drm_device *dev, void *data, fb_height = fb->height << 16; /* Make sure source coordinates are inside the fb. */ - if (plane_req->src_w > fb_width || - plane_req->src_x > fb_width - plane_req->src_w || - plane_req->src_h > fb_height || - plane_req->src_y > fb_height - plane_req->src_h) { + if (src_w > fb_width || + src_x > fb_width - src_w || + src_h > fb_height || + src_y > fb_height - src_h) { DRM_DEBUG_KMS("Invalid source coordinates " "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - plane_req->src_w >> 16, - ((plane_req->src_w & 0xffff) * 15625) >> 10, - plane_req->src_h >> 16, - ((plane_req->src_h & 0xffff) * 15625) >> 10, - plane_req->src_x >> 16, - ((plane_req->src_x & 0xffff) * 15625) >> 10, - plane_req->src_y >> 16, - ((plane_req->src_y & 0xffff) * 15625) >> 10); + src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, + src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, + src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, + src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); ret = -ENOSPC; goto out; } - /* Give drivers some help against integer overflows */ - if (plane_req->crtc_w > INT_MAX || - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || - plane_req->crtc_h > INT_MAX || - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - plane_req->crtc_w, plane_req->crtc_h, - plane_req->crtc_x, plane_req->crtc_y); - ret = -ERANGE; - goto out; - } - drm_modeset_lock_all(dev); old_fb = plane->fb; ret = plane->funcs->update_plane(plane, crtc, fb, - plane_req->crtc_x, plane_req->crtc_y, - plane_req->crtc_w, plane_req->crtc_h, - plane_req->src_x, plane_req->src_y, - plane_req->src_w, plane_req->src_h); + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); if (!ret) { plane->crtc = crtc; plane->fb = fb; @@ -2265,6 +2218,81 @@ out: drm_framebuffer_unreference(old_fb); return ret; + +} + +/** + * drm_mode_setplane - configure a plane's configuration + * @dev: DRM device + * @data: ioctl data* + * @file_priv: DRM file info + * + * Set plane configuration, including placement, fb, scaling, and other factors. + * Or pass a NULL fb to disable (planes may be disabled without providing a + * valid crtc). + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_setplane(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_set_plane *plane_req = data; + struct drm_mode_object *obj; + struct drm_plane *plane; + struct drm_crtc *crtc = NULL; + struct drm_framebuffer *fb = NULL; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + /* Give drivers some help against integer overflows */ + if (plane_req->crtc_w > INT_MAX || + plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || + plane_req->crtc_h > INT_MAX || + plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { + DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", + plane_req->crtc_w, plane_req->crtc_h, + plane_req->crtc_x, plane_req->crtc_y); + return -ERANGE; + } + + /* + * First, find the plane, crtc, and fb objects. If not available, + * we don't bother to call the driver. + */ + obj = drm_mode_object_find(dev, plane_req->plane_id, + DRM_MODE_OBJECT_PLANE); + if (!obj) { + DRM_DEBUG_KMS("Unknown plane ID %d\n", + plane_req->plane_id); + return -ENOENT; + } + plane = obj_to_plane(obj); + + if (plane_req->fb_id) { + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + if (!fb) { + DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", + plane_req->fb_id); + return -ENOENT; + } + + obj = drm_mode_object_find(dev, plane_req->crtc_id, + DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_DEBUG_KMS("Unknown crtc ID %d\n", + plane_req->crtc_id); + return -ENOENT; + } + crtc = obj_to_crtc(obj); + } + + return setplane_internal(crtc, plane, fb, + plane_req->crtc_x, plane_req->crtc_y, + plane_req->crtc_w, plane_req->crtc_h, + plane_req->src_x, plane_req->src_y, + plane_req->src_w, plane_req->src_h); } /** -- cgit v0.10.2 From 161d0dc1dccb17ff7a38f462c7c0d4ef8bcc5662 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:10 -0700 Subject: drm: Support legacy cursor ioctls via universal planes when possible (v4) If drivers support universal planes and have registered a cursor plane with the DRM core, we should use that universal plane support when handling legacy cursor ioctls. Drivers that transition to universal planes won't have to maintain separate legacy ioctl handling; drivers that don't transition to universal planes will continue to operate without any change to behavior. Note that there's a bit of a mismatch between the legacy cursor ioctls and the universal plane API's --- legacy ioctl's use driver buffer handles directly whereas the universal plane API takes drm_framebuffers. Since there's no way to recover the driver handle from a drm_framebuffer, we can implement legacy ioctl's in terms of universal plane interfaces, but cannot implement universal plane interfaces in terms of legacy ioctls. Specifically, there's no way to create a general cursor helper in the way we previously created a primary plane helper. It's important to land this patch before any patches that add universal cursor support to individual drivers so that drivers don't have to worry about juggling two different styles of reference counting for cursor buffers when userspace mixes and matches legacy and universal cursor calls. With this patch, a driver that switches to universal cursor support may assume that all cursor buffers are wrapped in a drm_framebuffer and can rely on framebuffer reference counting for all cursor operations. v4: - Add comments pointing out setplane_internal's reference-eating semantics. v3: - Drop drm_mode_rmfb() call that is no longer needed now that we're using setplane_internal(), which takes care of deref'ing the appropriate framebuffer. v2: - Use new add_framebuffer_internal() function to create framebuffer rather than trying to call directly into the ioctl interface and look up the handle returned. - Use new setplane_internal() function to update the cursor plane rather than calling through the ioctl interface. Note that since we're no longer looking up an fb_id, no extra reference will be taken here. - Grab extra reference to fb under lock in !BO case to avoid issues where racing userspace could cause the fb to be destroyed out from under us after we grab the fb pointer. Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 27eae03..b5bce5b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2288,6 +2288,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data, crtc = obj_to_crtc(obj); } + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ return setplane_internal(crtc, plane, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, @@ -2541,6 +2545,102 @@ out: return ret; } +/** + * drm_mode_cursor_universal - translate legacy cursor ioctl call into a + * universal plane handler call + * @crtc: crtc to update cursor for + * @req: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Legacy cursor ioctl's work directly with driver buffer handles. To + * translate legacy ioctl calls into universal plane handler calls, we need to + * wrap the native buffer handle in a drm_framebuffer. + * + * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB + * buffer with a pitch of 4*width; the universal plane interface should be used + * directly in cases where the hardware can support other buffer settings and + * userspace wants to make use of these capabilities. + * + * Returns: + * Zero on success, errno on failure. + */ +static int drm_mode_cursor_universal(struct drm_crtc *crtc, + struct drm_mode_cursor2 *req, + struct drm_file *file_priv) +{ + struct drm_device *dev = crtc->dev; + struct drm_framebuffer *fb = NULL; + struct drm_mode_fb_cmd2 fbreq = { + .width = req->width, + .height = req->height, + .pixel_format = DRM_FORMAT_ARGB8888, + .pitches = { req->width * 4 }, + .handles = { req->handle }, + }; + int32_t crtc_x, crtc_y; + uint32_t crtc_w = 0, crtc_h = 0; + uint32_t src_w = 0, src_h = 0; + int ret = 0; + + BUG_ON(!crtc->cursor); + + /* + * Obtain fb we'll be using (either new or existing) and take an extra + * reference to it if fb != null. setplane will take care of dropping + * the reference if the plane update fails. + */ + if (req->flags & DRM_MODE_CURSOR_BO) { + if (req->handle) { + fb = add_framebuffer_internal(dev, &fbreq, file_priv); + if (IS_ERR(fb)) { + DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); + return PTR_ERR(fb); + } + + drm_framebuffer_reference(fb); + } else { + fb = NULL; + } + } else { + mutex_lock(&dev->mode_config.mutex); + fb = crtc->cursor->fb; + if (fb) + drm_framebuffer_reference(fb); + mutex_unlock(&dev->mode_config.mutex); + } + + if (req->flags & DRM_MODE_CURSOR_MOVE) { + crtc_x = req->x; + crtc_y = req->y; + } else { + crtc_x = crtc->cursor_x; + crtc_y = crtc->cursor_y; + } + + if (fb) { + crtc_w = fb->width; + crtc_h = fb->height; + src_w = fb->width << 16; + src_h = fb->height << 16; + } + + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ + ret = setplane_internal(crtc, crtc->cursor, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + 0, 0, src_w, src_h); + + /* Update successful; save new cursor position, if necessary */ + if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { + crtc->cursor_x = req->x; + crtc->cursor_y = req->y; + } + + return ret; +} + static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) @@ -2560,6 +2660,13 @@ static int drm_mode_cursor_common(struct drm_device *dev, return -ENOENT; } + /* + * If this crtc has a universal cursor plane, call that plane's update + * handler rather than using legacy cursor handlers. + */ + if (crtc->cursor) + return drm_mode_cursor_universal(crtc, req, file_priv); + drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e..b8c7a9a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -331,6 +331,10 @@ struct drm_crtc { struct drm_plane *primary; struct drm_plane *cursor; + /* position of cursor plane on crtc */ + int cursor_x; + int cursor_y; + /* Temporary tracking of the old fb while a modeset is ongoing. Used * by drm_mode_set_config_internal to implement correct refcounting. */ struct drm_framebuffer *old_fb; -- cgit v0.10.2 From 17cfd91f3985f23f92b8c7cba3307a2b630feb95 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 13 Jun 2014 15:22:28 +0100 Subject: drm: Avoid NULL deference when disabling a plane from userspace To disable a plane, userspace passes in an framebuffer id of 0. This causes us to pass CRTC == NULL to setplane_internal, who promptly deferences it to grab the struct drm_device. Oops. [ 1296.467327] BUG: unable to handle kernel NULL pointer dereference at (null) [ 1296.467332] IP: [] setplane_internal+0x11/0x280 [ 1296.467338] *pde = 00000000 [ 1296.467341] Oops: 0000 [#1] SMP [ 1296.467344] Modules linked in: ccm bnep bluetooth snd_hda_codec_hdmi snd_hda_codec_idt snd_hda_codec_generic snd_hda_intel arc4 iwldvm snd_hda_controller snd_hda_codec mac80211 snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer iwlwifi sdhci_pci snd cfg80211 x86_pkg_temp_thermal hp_wmi sdhci sparse_keymap mmc_core crc32c_intel rfkill microcode hp_accel lpc_ich lis3lv02d wmi mfd_core serio_raw input_polldev soundcore e1000e ptp pps_core [ 1296.467367] CPU: 1 PID: 672 Comm: Xorg Tainted: G W 3.15.0-rc8+ #351 [ 1296.467369] Hardware name: Hewlett-Packard HP ProBook 6360b/1620, BIOS 68SCF Ver. B.42 12/29/2010 [ 1296.467371] task: f423b5c0 ti: c2332000 task.ti: c2332000 [ 1296.467374] EIP: 0060:[] EFLAGS: 00013286 CPU: 1 [ 1296.467376] EIP is at setplane_internal+0x11/0x280 [ 1296.467378] EAX: 00000000 EBX: c2333e90 ECX: 00000000 EDX: f3165600 [ 1296.467380] ESI: f430f400 EDI: 00000000 EBP: c2333e14 ESP: c2333dd4 [ 1296.467382] DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 [ 1296.467384] CR0: 80050033 CR2: 00000000 CR3: 00159000 CR4: 000407d0 [ 1296.467385] Stack: [ 1296.467387] 000200da 00000002 c2333de8 c15dc4a0 f430f400 c2333e00 c134c54f eeeeeeee [ 1296.467391] f430f400 00000007 f416b480 c2333e14 00000000 c2333e90 f430f400 00000000 [ 1296.467396] c2333e4c c1350aed 00000000 00000000 00000000 00000000 00000000 00000000 [ 1296.467400] Call Trace: [ 1296.467406] [] ? mutex_lock+0x10/0x28 [ 1296.467408] [] ? _object_find+0x5f/0x90 [ 1296.467413] [] drm_mode_setplane+0x10d/0x1f0 [ 1296.467416] [] ? drm_mode_getplane+0x100/0x100 [ 1296.467420] [] drm_ioctl+0x1bd/0x4f0 [ 1296.467423] [] ? drm_mode_getplane+0x100/0x100 [ 1296.467427] [] ? handle_mm_fault+0x5d3/0xb30 [ 1296.467431] [] ? tlb_finish_mmu+0x11/0x40 [ 1296.467435] [] ? drm_ioctl_flags+0x40/0x40 [ 1296.467438] [] do_vfs_ioctl+0x2f2/0x4d0 [ 1296.467443] [] ? inode_has_perm.isra.32+0x32/0x40 [ 1296.467446] [] ? file_has_perm+0x7f/0x90 [ 1296.467449] [] ? selinux_file_ioctl+0x4c/0xf0 [ 1296.467452] [] SyS_ioctl+0x60/0x90 [ 1296.467456] [] sysenter_do_call+0x12/0x22 [ 1296.467457] Code: 3f cf ff eb dd ba 3f 00 00 00 b8 d9 c9 7f c1 e8 e6 3f cf ff eb d9 8d 74 26 00 55 89 e5 57 56 53 83 ec 34 66 66 66 66 90 89 45 f0 <8b> 00 85 c9 89 d6 89 cb 89 45 ec 0f 84 16 01 00 00 8b 45 f0 e8 [ 1296.467485] EIP: [] setplane_internal+0x11/0x280 SS:ESP 0068:c2 Fixes regression from commit b02fd7fd8a541c3d590bfdda23365a927b507ceb Author: Matt Roper Date: Tue Jun 10 08:28:10 2014 -0700 drm: Support legacy cursor ioctls via universal planes when possible (v4) While at it move the plane parameter to the first position in setplane_internal since that's the main object we're manipulating. Signed-off-by: Chris Wilson Cc: Daniel Vetter Cc: Pallavi G Cc: Matt Roper Reviewed-by: Matt Roper [danvet: Add note about parameter reordering.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b5bce5b..d86d254 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2131,8 +2131,8 @@ out: * * src_{x,y,w,h} are provided in 16.16 fixed point format */ -static int setplane_internal(struct drm_crtc *crtc, - struct drm_plane *plane, +static int setplane_internal(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h, @@ -2140,7 +2140,7 @@ static int setplane_internal(struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = plane->dev; struct drm_framebuffer *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; @@ -2292,7 +2292,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * setplane_internal will take care of deref'ing either the old or new * framebuffer depending on success. */ - return setplane_internal(crtc, plane, fb, + return setplane_internal(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, @@ -2628,7 +2628,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, * setplane_internal will take care of deref'ing either the old or new * framebuffer depending on success. */ - ret = setplane_internal(crtc, crtc->cursor, fb, + ret = setplane_internal(crtc->cursor, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, 0, 0, src_w, src_h); -- cgit v0.10.2 From fc1d3e44ef7c1db93384150fdbf8948dcf949f15 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:11 -0700 Subject: drm: Allow drivers to register cursor planes with crtc Universal plane support had placeholders for cursor planes, but didn't actually do anything with them. Save the cursor plane reference inside the crtc and update the cursor plane parameter from void* to drm_plane. Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d86d254..41c7212 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -727,7 +727,7 @@ DEFINE_WW_CLASS(crtc_ww_class); */ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, - void *cursor, + struct drm_plane *cursor, const struct drm_crtc_funcs *funcs) { struct drm_mode_config *config = &dev->mode_config; @@ -752,8 +752,11 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, config->num_crtc++; crtc->primary = primary; + crtc->cursor = cursor; if (primary) primary->possible_crtcs = 1 << drm_crtc_index(crtc); + if (cursor) + cursor->possible_crtcs = 1 << drm_crtc_index(crtc); out: drm_modeset_unlock_all(dev); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b8c7a9a..4ee7e26 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -856,7 +856,7 @@ struct drm_prop_enum_list { extern int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, - void *cursor, + struct drm_plane *cursor, const struct drm_crtc_funcs *funcs); extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, -- cgit v0.10.2 From e328795139b30c9d49d8fde37e1d6efc845ac46b Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:12 -0700 Subject: drm/i915: Add intel_crtc_cursor_set_obj() to set cursor buffer (v2) Refactor cursor buffer setting such that the code to actually update the cursor lives in a new function, intel_crtc_cursor_set_obj(), and takes a GEM object as a parameter. The existing legacy cursor ioctl handler, intel_crtc_cursor_set() will now perform the userspace handle lookup and then call this new function. This refactoring is in preparation for the universal plane cursor support where we'll want to update the cursor with an actual GEM buffer object (obtained via drm_framebuffer) rather than a userspace handle. v2: Drop obvious kerneldoc and replace with note about function's reference consumption Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b5cbb28..1beeb2a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8089,21 +8089,26 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, intel_crtc->cursor_base = base; } -static int intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file, - uint32_t handle, - uint32_t width, uint32_t height) +/* + * intel_crtc_cursor_set_obj - Set cursor to specified GEM object + * + * Note that the object's reference will be consumed if the update fails. If + * the update succeeds, the reference of the old object (if any) will be + * consumed. + */ +static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, + struct drm_i915_gem_object *obj, + uint32_t width, uint32_t height) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj; unsigned old_width; uint32_t addr; int ret; /* if we want to turn off the cursor ignore width and height */ - if (!handle) { + if (!obj) { DRM_DEBUG_KMS("cursor off\n"); addr = 0; obj = NULL; @@ -8119,12 +8124,8 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, return -EINVAL; } - obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); - if (&obj->base == NULL) - return -ENOENT; - if (obj->base.size < width * height * 4) { - DRM_DEBUG_KMS("buffer is to small\n"); + DRM_DEBUG_KMS("buffer is too small\n"); ret = -ENOMEM; goto fail; } @@ -8207,6 +8208,25 @@ fail: return ret; } +static int intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_gem_object *obj; + + if (handle) { + obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); + if (&obj->base == NULL) + return -ENOENT; + } else { + obj = NULL; + } + + return intel_crtc_cursor_set_obj(crtc, obj, width, height); +} + static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); -- cgit v0.10.2 From 3d7d6510e621a1422461c4958aa3276271205457 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:13 -0700 Subject: drm/i915: Switch to unified plane cursor handling (v4) The DRM core will translate calls to legacy cursor ioctls into universal cursor calls automatically, so there's no need to maintain the legacy cursor support. This greatly simplifies the transition since we don't have to handle reference counting differently depending on which cursor interface was called. The aim here is to transition to the universal plane interface with minimal code change. There's a lot of cleanup that can be done (e.g., using state stored in crtc->cursor->fb rather than intel_crtc) that is left to future patches. v4: - Drop drm_gem_object_unreference() that is no longer needed now that we receive the GEM obj directly rather than looking up the ID. v3: - Pass cursor obj to intel_crtc_cursor_set_obj() if cursor fb changes, even if 'visible' is false. intel_crtc_cursor_set_obj() will notice that the cursor isn't visible and disable it properly, but we still need to get intel_crtc->cursor_addr set properly so that we behave properly if the cursor becomes visible again in the future without changing the cursor buffer (noted by Chris Wilson and verified via i-g-t kms_cursor_crc). - s/drm_plane_init/drm_universal_plane_init/. Due to type compatibility between enum and bool, everything actually works correctly with the wrong init call, except for the type of plane that gets exposed to userspace (it shows up as type 'primary' rather than type 'cursor'). v2: - Remove duplicate dimension checks on cursor - Drop explicit cursor disable from crtc destroy (fb & plane destruction will take care of that now) - Use DRM plane helper to check update parameters Cc: intel-gfx@lists.freedesktop.org Signed-off-by: Matt Roper Reviewed-by: Pallavi G Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1beeb2a..1880c18 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -68,6 +68,11 @@ static const uint32_t intel_primary_formats_gen4[] = { DRM_FORMAT_ABGR2101010, }; +/* Cursor formats */ +static const uint32_t intel_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + #define DIV_ROUND_CLOSEST_ULL(ll, d) \ ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) @@ -8044,8 +8049,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int x = intel_crtc->cursor_x; - int y = intel_crtc->cursor_y; + int x = crtc->cursor_x; + int y = crtc->cursor_y; u32 base = 0, pos = 0; if (on) @@ -8180,7 +8185,6 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, if (intel_crtc->cursor_bo) { if (!INTEL_INFO(dev)->cursor_needs_physical) i915_gem_object_unpin_from_display_plane(intel_crtc->cursor_bo); - drm_gem_object_unreference(&intel_crtc->cursor_bo->base); } mutex_unlock(&dev->struct_mutex); @@ -8208,38 +8212,6 @@ fail: return ret; } -static int intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_gem_object *obj; - - if (handle) { - obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); - if (&obj->base == NULL) - return -ENOENT; - } else { - obj = NULL; - } - - return intel_crtc_cursor_set_obj(crtc, obj, width, height); -} - -static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - intel_crtc->cursor_x = clamp_t(int, x, SHRT_MIN, SHRT_MAX); - intel_crtc->cursor_y = clamp_t(int, y, SHRT_MIN, SHRT_MAX); - - if (intel_crtc->active) - intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL); - - return 0; -} - static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) { @@ -8885,8 +8857,6 @@ static void intel_crtc_destroy(struct drm_crtc *crtc) kfree(work); } - intel_crtc_cursor_set(crtc, NULL, 0, 0, 0); - drm_crtc_cleanup(crtc); kfree(intel_crtc); @@ -10942,8 +10912,6 @@ out_config: } static const struct drm_crtc_funcs intel_crtc_funcs = { - .cursor_set = intel_crtc_cursor_set, - .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, .set_config = intel_crtc_set_config, .destroy = intel_crtc_destroy, @@ -11196,7 +11164,8 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, return 0; } -static void intel_primary_plane_destroy(struct drm_plane *plane) +/* Common destruction function for both primary and cursor planes */ +static void intel_plane_destroy(struct drm_plane *plane) { struct intel_plane *intel_plane = to_intel_plane(plane); drm_plane_cleanup(plane); @@ -11206,7 +11175,7 @@ static void intel_primary_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs intel_primary_plane_funcs = { .update_plane = intel_primary_plane_setplane, .disable_plane = intel_primary_plane_disable, - .destroy = intel_primary_plane_destroy, + .destroy = intel_plane_destroy, }; static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, @@ -11242,11 +11211,100 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, return &primary->base; } +static int +intel_cursor_plane_disable(struct drm_plane *plane) +{ + if (!plane->fb) + return 0; + + BUG_ON(!plane->crtc); + + return intel_crtc_cursor_set_obj(plane->crtc, NULL, 0, 0); +} + +static int +intel_cursor_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_rect dest = { + /* integer pixels */ + .x1 = crtc_x, + .y1 = crtc_y, + .x2 = crtc_x + crtc_w, + .y2 = crtc_y + crtc_h, + }; + struct drm_rect src = { + /* 16.16 fixed point */ + .x1 = src_x, + .y1 = src_y, + .x2 = src_x + src_w, + .y2 = src_y + src_h, + }; + const struct drm_rect clip = { + /* integer pixels */ + .x2 = intel_crtc->config.pipe_src_w, + .y2 = intel_crtc->config.pipe_src_h, + }; + bool visible; + int ret; + + ret = drm_plane_helper_check_update(plane, crtc, fb, + &src, &dest, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true, &visible); + if (ret) + return ret; + + crtc->cursor_x = crtc_x; + crtc->cursor_y = crtc_y; + if (fb != crtc->cursor->fb) { + return intel_crtc_cursor_set_obj(crtc, obj, crtc_w, crtc_h); + } else { + intel_crtc_update_cursor(crtc, visible); + return 0; + } +} +static const struct drm_plane_funcs intel_cursor_plane_funcs = { + .update_plane = intel_cursor_plane_update, + .disable_plane = intel_cursor_plane_disable, + .destroy = intel_plane_destroy, +}; + +static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev, + int pipe) +{ + struct intel_plane *cursor; + + cursor = kzalloc(sizeof(*cursor), GFP_KERNEL); + if (cursor == NULL) + return NULL; + + cursor->can_scale = false; + cursor->max_downscale = 1; + cursor->pipe = pipe; + cursor->plane = pipe; + + drm_universal_plane_init(dev, &cursor->base, 0, + &intel_cursor_plane_funcs, + intel_cursor_formats, + ARRAY_SIZE(intel_cursor_formats), + DRM_PLANE_TYPE_CURSOR); + return &cursor->base; +} + static void intel_crtc_init(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc; - struct drm_plane *primary; + struct drm_plane *primary = NULL; + struct drm_plane *cursor = NULL; int i, ret; intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL); @@ -11254,13 +11312,17 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) return; primary = intel_primary_plane_create(dev, pipe); + if (!primary) + goto fail; + + cursor = intel_cursor_plane_create(dev, pipe); + if (!cursor) + goto fail; + ret = drm_crtc_init_with_planes(dev, &intel_crtc->base, primary, - NULL, &intel_crtc_funcs); - if (ret) { - drm_plane_cleanup(primary); - kfree(intel_crtc); - return; - } + cursor, &intel_crtc_funcs); + if (ret) + goto fail; drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); for (i = 0; i < 256; i++) { @@ -11293,6 +11355,14 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe); + return; + +fail: + if (primary) + drm_plane_cleanup(primary); + if (cursor) + drm_plane_cleanup(cursor); + kfree(intel_crtc); } enum pipe intel_get_pipe_from_connector(struct intel_connector *connector) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 78d4124..57a36ab 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -384,7 +384,6 @@ struct intel_crtc { struct drm_i915_gem_object *cursor_bo; uint32_t cursor_addr; - int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; uint32_t cursor_cntl; uint32_t cursor_base; -- cgit v0.10.2 From 4704c573fc9fee38dd30f07d02037a7edc42bacd Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:38 -0700 Subject: drm/i915: Use HAS_PSR to avoid unecessary interactions. Let's be more conservative and protect platforms that don't support PSR from unecessary interactions. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b3f97f2..da65adc 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1747,11 +1747,6 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) dev_priv->psr.source_ok = false; - if (!HAS_PSR(dev)) { - DRM_DEBUG_KMS("PSR not supported on this platform\n"); - return false; - } - if ((intel_encoder->type != INTEL_OUTPUT_EDP) || (dig_port->port != PORT_A)) { DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); @@ -1824,6 +1819,11 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + if (!HAS_PSR(dev)) { + DRM_DEBUG_KMS("PSR not supported on this platform\n"); + return; + } + if (intel_edp_psr_match_conditions(intel_dp) && !intel_edp_is_psr_enabled(dev)) intel_edp_psr_do_enable(intel_dp); @@ -1851,6 +1851,9 @@ void intel_edp_psr_update(struct drm_device *dev) struct intel_encoder *encoder; struct intel_dp *intel_dp = NULL; + if (!HAS_PSR(dev)) + return; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) if (encoder->type == INTEL_OUTPUT_EDP) { intel_dp = enc_to_intel_dp(&encoder->base); -- cgit v0.10.2 From 164872543eb9e4142d7c4b057e01c4eb00868705 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:39 -0700 Subject: drm/i915: Don't let update_psr function actually enable PSR. Being more conservative by enabling PSR only on psr_enable function. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index da65adc..a7b4ac9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1805,9 +1805,6 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) intel_edp_is_psr_enabled(dev)) return; - /* Setup PSR once */ - intel_edp_psr_setup(intel_dp); - /* Enable PSR on the panel */ intel_edp_psr_enable_sink(intel_dp); @@ -1824,6 +1821,9 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) return; } + /* Setup PSR once */ + intel_edp_psr_setup(intel_dp); + if (intel_edp_psr_match_conditions(intel_dp) && !intel_edp_is_psr_enabled(dev)) intel_edp_psr_do_enable(intel_dp); @@ -1848,12 +1848,16 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) void intel_edp_psr_update(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *encoder; struct intel_dp *intel_dp = NULL; if (!HAS_PSR(dev)) return; + if (!dev_priv->psr.setup_done) + return; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) if (encoder->type == INTEL_OUTPUT_EDP) { intel_dp = enc_to_intel_dp(&encoder->base); -- cgit v0.10.2 From 34eb7579dce8126049ae45bf27b6d4235ceaedf1 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:40 -0700 Subject: drm/i915: Do not try to enable PSR when Panel doesn't suport it. Also do not cache aux info. That info could be related to another panel. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a7b4ac9..68289ce 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1613,11 +1613,9 @@ static void intel_dp_get_config(struct intel_encoder *encoder, } } -static bool is_edp_psr(struct drm_device *dev) +static bool is_edp_psr(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dev->dev_private; - - return dev_priv->psr.sink_support; + return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED; } static bool intel_edp_is_psr_enabled(struct drm_device *dev) @@ -1821,6 +1819,11 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) return; } + if (!is_edp_psr(intel_dp)) { + DRM_DEBUG_KMS("PSR not supported by this panel\n"); + return; + } + /* Setup PSR once */ intel_edp_psr_setup(intel_dp); @@ -1862,9 +1865,6 @@ void intel_edp_psr_update(struct drm_device *dev) if (encoder->type == INTEL_OUTPUT_EDP) { intel_dp = enc_to_intel_dp(&encoder->base); - if (!is_edp_psr(dev)) - return; - if (!intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_disable(intel_dp); else -- cgit v0.10.2 From 82c562549b4c86274c564d3b3498020559683dcd Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:42 -0700 Subject: drm/i915: BDW PSR: Add single frame update support. When link is in stand by and PSR exit is triggered by a primary or sprite plane flip this mode allows only one single updated frame to be send to display than get back to PSR immediately. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 05e2541..9ce017b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2409,6 +2409,7 @@ enum punit_power_well { #define EDP_PSR_BASE(dev) (IS_HASWELL(dev) ? 0x64800 : 0x6f800) #define EDP_PSR_CTL(dev) (EDP_PSR_BASE(dev) + 0) #define EDP_PSR_ENABLE (1<<31) +#define BDW_PSR_SINGLE_FRAME (1<<30) #define EDP_PSR_LINK_DISABLE (0<<27) #define EDP_PSR_LINK_STANDBY (1<<27) #define EDP_PSR_MIN_LINK_ENTRY_TIME_MASK (3<<25) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 68289ce..73e4a5c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1723,6 +1723,7 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp) val |= EDP_PSR_TP2_TP3_TIME_0us; val |= EDP_PSR_TP1_TIME_0us; val |= EDP_PSR_SKIP_AUX_EXIT; + val |= IS_BROADWELL(dev) ? BDW_PSR_SINGLE_FRAME : 0; } else val |= EDP_PSR_LINK_DISABLE; -- cgit v0.10.2 From 4c8c7000cc54ef45d05d3519ddb9118d4c65fbd0 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:43 -0700 Subject: drm/i915: BDW PSR: Remove limitations that aren't valid for BDW. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 73e4a5c..f8ff257 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1776,6 +1776,10 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } + /* Below limitations aren't valid for Broadwell */ + if (IS_BROADWELL(dev)) + goto out; + if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) { DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n"); return false; @@ -1792,6 +1796,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } + out: dev_priv->psr.source_ok = true; return true; } -- cgit v0.10.2 From 0e0ae65236cb302208b83cdf1395f1b7ad6a7419 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:44 -0700 Subject: drm/i915: BDW PSR: Remove DDIA limitation for Broadwell. Broadwell has a PSR per transcoder, where DDIA supports link disable and link standby modes while other transcoders only support link standby. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f8ff257..fc3486a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1683,16 +1683,21 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp) static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t aux_clock_divider; int precharge = 0x3; int msg_size = 5; /* Header(4) + Message(1) */ + bool only_standby = false; aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0); + if (IS_BROADWELL(dev) && dig_port->port != PORT_A) + only_standby = true; + /* Enable PSR in sink */ - if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) + if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby) drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE); else @@ -1711,14 +1716,19 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp) static void intel_edp_psr_enable_source(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t max_sleep_time = 0x1f; uint32_t idle_frames = 1; uint32_t val = 0x0; const uint32_t link_entry_time = EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES; + bool only_standby = false; - if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) { + if (IS_BROADWELL(dev) && dig_port->port != PORT_A) + only_standby = true; + + if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby) { val |= EDP_PSR_LINK_STANDBY; val |= EDP_PSR_TP2_TP3_TIME_0us; val |= EDP_PSR_TP1_TIME_0us; @@ -1746,8 +1756,13 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) dev_priv->psr.source_ok = false; - if ((intel_encoder->type != INTEL_OUTPUT_EDP) || - (dig_port->port != PORT_A)) { + if (!HAS_PSR(dev)) { + DRM_DEBUG_KMS("PSR not supported on this platform\n"); + return false; + } + + if (IS_HASWELL(dev) && (intel_encoder->type != INTEL_OUTPUT_EDP || + dig_port->port != PORT_A)) { DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); return false; } -- cgit v0.10.2 From 642f9bb558decd546c73dfc7064f2138a3428f23 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:46 -0700 Subject: drm/i915: PSR HSW: update after enabling sprite. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the current structure HSW doesn't support PSR with sprites enabled but sprites can be enabled after PSR was enabled what would cause user to miss screen updates. v2: move it to update_plane. Cc: Ville Syrjälä Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 1b66ddc..404335d 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1051,6 +1051,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); } + intel_edp_psr_update(dev); + return 0; } -- cgit v0.10.2 From 60a5ca015ffd2aacfe5674b5a401cd2a37159e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 11:10:53 +0300 Subject: drm/i915: Add locking around framebuffer_references-- MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit obj->framebuffer_references isn't an atomic_t so the decrement needs to be protected by some lock. struct_mutex seems like the appropriate lock here, and we may already take it for the obj unref anyway. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1880c18..b9251c8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11594,11 +11594,14 @@ static void intel_setup_outputs(struct drm_device *dev) static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) { + struct drm_device *dev = fb->dev; struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); drm_framebuffer_cleanup(fb); + mutex_lock(&dev->struct_mutex); WARN_ON(!intel_fb->obj->framebuffer_references--); - drm_gem_object_unreference_unlocked(&intel_fb->obj->base); + drm_gem_object_unreference(&intel_fb->obj->base); + mutex_unlock(&dev->struct_mutex); kfree(intel_fb); } -- cgit v0.10.2 From 19c656a173db57a2a1dee342fe667603ccc6dac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 15:39:56 +0300 Subject: drm/i915: Print PCI revision in i915_dump_device_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Knowing the device stepping may be crucial in analyzing problems. Since we always ask bug reporters for dmegs with drm.debug=0xe (or something) it would be nice if the PCI revision is already included in the dump. Avoids having to ask for lspci output as well. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 1c5df4a..0f98957 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1460,10 +1460,11 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv) #define SEP_EMPTY #define PRINT_FLAG(name) info->name ? #name "," : "" #define SEP_COMMA , - DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags=" + DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x rev=0x%02x flags=" DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY), info->gen, dev_priv->dev->pdev->device, + dev_priv->dev->pdev->revision, DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA)); #undef PRINT_S #undef SEP_EMPTY -- cgit v0.10.2 From d593d992a99c4945a183611354dded7578abab7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 16:42:51 +0300 Subject: drm/i915: Fix __user sparse warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHECK linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1529:47: warning: incorrect type in initializer (different address spaces) linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1529:47: expected struct drm_i915_gem_exec_object2 *user_exec_list linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1529:47: got void [noderef] * linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1533:61: warning: incorrect type in argument 1 (different address spaces) linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1533:61: expected void [noderef] *dst linux/drivers/gpu/drm/i915/i915_gem_execbuffer.c:1533:61: got unsigned long long * Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 3a30133..93d7f72 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1525,7 +1525,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list); if (!ret) { /* Copy the new buffer offsets back to the user's exec list. */ - struct drm_i915_gem_exec_object2 *user_exec_list = + struct drm_i915_gem_exec_object2 __user *user_exec_list = to_user_ptr(args->buffers_ptr); int i; -- cgit v0.10.2 From 62942ed7279d3e06dc15ae3d47665eff3b373327 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 13 Jun 2014 09:28:33 -0700 Subject: drm/i915/vlv: disable PPGTT on early revs v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Early revs didn't have PPGTT support, so disable there. v2: add debug msg when disabling on early stepping v3: enable on other B3 packages as well (untested) (Ville) References: https://bugs.freedesktop.org/show_bug.cgi?id=79669 References: https://bugs.freedesktop.org/show_bug.cgi?id=79670 Signed-off-by: Jesse Barnes Acked-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index eec820a..6f5d5a1 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -63,6 +63,12 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt) } #endif + /* Early VLV doesn't have this */ + if (IS_VALLEYVIEW(dev) && dev->pdev->revision < 0xb) { + DRM_DEBUG_DRIVER("disabling PPGTT on pre-B3 step VLV\n"); + return 0; + } + return HAS_ALIASING_PPGTT(dev) ? 1 : 0; } -- cgit v0.10.2 From 7c8f8a7007cf0069488e0b4e3db5f89d715f297e Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Fri, 13 Jun 2014 05:10:03 -0700 Subject: drm/i915: Force PSR exit by inactivating it. The perfect solution for psr_exit is the hardware tracking the changes and doing the psr exit by itself. This scenario works for HSW and BDW with some environments like Gnome and Wayland. However there are many other scenarios that this isn't true. Mainly one right now is KDE users on HSW and BDW with PSR on. User would miss many screen updates. For instances any key typed could be seen only when mouse cursor is moved. So this patch introduces the ability of trigger PSR exit on kernel side on some common cases that. Most of the cases are coverred by psr_exit at set_domain. The remaining cases are coverred by triggering it at set_domain, busy_ioctl, sw_finish and mark_busy. The downside here might be reducing the residency time on the cases this already work very wall like Gnome environment. But so far let's get focused on fixinge issues sio PSR couild be used for everybody and we could even get it enabled by default. Later we can add some alternatives to choose the level of PSR efficiency over boot flag of even over crtc property. v2: remove exit from connector_dpms. Daniel pointed this is the wrong way and also this isn't needed for BDW and HSW anyway. Cc: Daniel Vetter Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 506386e..faef035 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -637,6 +637,9 @@ struct i915_psr { bool sink_support; bool source_ok; bool setup_done; + bool enabled; + bool active; + struct delayed_work work; }; enum intel_pch { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c313cb2..1794a04 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1395,6 +1395,8 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, goto unlock; } + intel_edp_psr_exit(dev, true); + /* Try to flush the object off the GPU without holding the lock. * We will repeat the flush holding the lock in the normal manner * to catch cases where we are gazumped. @@ -1440,6 +1442,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, if (ret) return ret; + intel_edp_psr_exit(dev, true); + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { ret = -ENOENT; @@ -4236,6 +4240,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (ret) return ret; + intel_edp_psr_exit(dev, true); + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { ret = -ENOENT; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b9251c8..2fdbedf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8818,12 +8818,15 @@ out: intel_runtime_pm_put(dev_priv); } + void intel_mark_fb_busy(struct drm_i915_gem_object *obj, struct intel_engine_cs *ring) { struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; + intel_edp_psr_exit(dev, true); + if (!i915.powersave) return; @@ -9290,6 +9293,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (work == NULL) return -ENOMEM; + /* Exit PSR early in page flip */ + intel_edp_psr_exit(dev, true); + work->event = event; work->crtc = crtc; work->old_fb_obj = to_intel_framebuffer(old_fb)->obj; @@ -11581,6 +11587,8 @@ static void intel_setup_outputs(struct drm_device *dev) if (SUPPORTS_TV(dev)) intel_tv_init(dev); + intel_edp_psr_init(dev); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { encoder->base.possible_crtcs = encoder->crtc_mask; encoder->base.possible_clones = diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fc3486a..b6b2640 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1818,10 +1818,11 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; - if (!intel_edp_psr_match_conditions(intel_dp) || - intel_edp_is_psr_enabled(dev)) + if (intel_edp_is_psr_enabled(dev)) return; /* Enable PSR on the panel */ @@ -1829,6 +1830,9 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) /* Enable PSR on the host */ intel_edp_psr_enable_source(intel_dp); + + dev_priv->psr.enabled = true; + dev_priv->psr.active = true; } void intel_edp_psr_enable(struct intel_dp *intel_dp) @@ -1848,8 +1852,7 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) /* Setup PSR once */ intel_edp_psr_setup(intel_dp); - if (intel_edp_psr_match_conditions(intel_dp) && - !intel_edp_is_psr_enabled(dev)) + if (intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_do_enable(intel_dp); } @@ -1858,7 +1861,7 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - if (!intel_edp_is_psr_enabled(dev)) + if (!dev_priv->psr.enabled) return; I915_WRITE(EDP_PSR_CTL(dev), @@ -1868,13 +1871,13 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) & EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10)) DRM_ERROR("Timed out waiting for PSR Idle State\n"); + + dev_priv->psr.enabled = false; } void intel_edp_psr_update(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *encoder; - struct intel_dp *intel_dp = NULL; if (!HAS_PSR(dev)) return; @@ -1882,6 +1885,17 @@ void intel_edp_psr_update(struct drm_device *dev) if (!dev_priv->psr.setup_done) return; + intel_edp_psr_exit(dev, true); +} + +void intel_edp_psr_work(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, typeof(*dev_priv), psr.work.work); + struct drm_device *dev = dev_priv->dev; + struct intel_encoder *encoder; + struct intel_dp *intel_dp = NULL; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) if (encoder->type == INTEL_OUTPUT_EDP) { intel_dp = enc_to_intel_dp(&encoder->base); @@ -1889,9 +1903,66 @@ void intel_edp_psr_update(struct drm_device *dev) if (!intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_disable(intel_dp); else - if (!intel_edp_is_psr_enabled(dev)) - intel_edp_psr_do_enable(intel_dp); + intel_edp_psr_do_enable(intel_dp); + } +} + +void intel_edp_psr_inactivate(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_connector *connector; + struct intel_encoder *encoder; + struct intel_crtc *intel_crtc; + struct intel_dp *intel_dp = NULL; + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + + if (connector->base.dpms != DRM_MODE_DPMS_ON) + continue; + + encoder = to_intel_encoder(connector->base.encoder); + if (encoder->type == INTEL_OUTPUT_EDP) { + + intel_dp = enc_to_intel_dp(&encoder->base); + intel_crtc = to_intel_crtc(encoder->base.crtc); + + dev_priv->psr.active = false; + + I915_WRITE(EDP_PSR_CTL(dev), I915_READ(EDP_PSR_CTL(dev)) + & ~EDP_PSR_ENABLE); } + } +} + +void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!HAS_PSR(dev)) + return; + + if (!dev_priv->psr.setup_done) + return; + + cancel_delayed_work_sync(&dev_priv->psr.work); + + if (dev_priv->psr.active) + intel_edp_psr_inactivate(dev); + + if (schedule_back) + schedule_delayed_work(&dev_priv->psr.work, + msecs_to_jiffies(100)); +} + +void intel_edp_psr_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!HAS_PSR(dev)) + return; + + INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work); } static void intel_disable_dp(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 57a36ab..9aa95f0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -830,6 +830,9 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); void intel_edp_psr_update(struct drm_device *dev); void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); +void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back); +void intel_edp_psr_init(struct drm_device *dev); + /* intel_dsi.c */ void intel_dsi_init(struct drm_device *dev); -- cgit v0.10.2 From 5755c78f771b1c71974c466cfece9b4524898a4c Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 12 Jun 2014 10:16:45 -0700 Subject: drm/i915: Improve PSR debugfs status. Now we have the active/inactive state for exit and this actually changes the HW enable bit the status was a bit confusing for users. So let's provide more info. Reviewed-by: Vijay Purushothaman Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7b83297..a8b8140 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1973,10 +1973,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support)); seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok)); + seq_printf(m, "Enabled: %s\n", yesno(dev_priv->psr.enabled)); + seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active)); enabled = HAS_PSR(dev) && I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE; - seq_printf(m, "Enabled: %s\n", yesno(enabled)); + seq_printf(m, "HW Enabled & Active bit: %s\n", yesno(enabled)); if (HAS_PSR(dev)) psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) & -- cgit v0.10.2 From 868d665b43473e230d560d5186535270a3d57a19 Mon Sep 17 00:00:00 2001 From: Christoph Jaeger Date: Fri, 13 Jun 2014 21:51:22 +0200 Subject: drm/i915: Fix memory leak in intel_dsi_init() error path intel_dsi_init() bails out without freeing the memory 'intel_dsi' and 'intel_connector' point to. Simply bail out before allocating memory. Picked up by Coverity - CID 1222750. Signed-off-by: Christoph Jaeger Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index bd89de5..a9a7fb4 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -675,6 +675,13 @@ void intel_dsi_init(struct drm_device *dev) if (!dev_priv->vbt.has_mipi) return; + if (IS_VALLEYVIEW(dev)) { + dev_priv->mipi_mmio_base = VLV_MIPI_BASE; + } else { + DRM_ERROR("Unsupported Mipi device to reg base"); + return; + } + intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL); if (!intel_dsi) return; @@ -689,13 +696,6 @@ void intel_dsi_init(struct drm_device *dev) encoder = &intel_encoder->base; intel_dsi->attached_connector = intel_connector; - if (IS_VALLEYVIEW(dev)) { - dev_priv->mipi_mmio_base = VLV_MIPI_BASE; - } else { - DRM_ERROR("Unsupported Mipi device to reg base"); - return; - } - connector = &intel_connector->base; drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI); -- cgit v0.10.2 From 6254b2042c794a8c2e14dc49b575a0708c823f88 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 16 Jun 2014 08:57:44 +0100 Subject: drm/i915: Simplify i915_gem_release_all_mmaps() An object can only have an active gtt mapping if it is currently bound into the global gtt. Therefore we can simply walk the list of all bound objects and check the flag upon those for an active gtt mapping. From commit 48018a57a8f5900e7e53ffaa0adeb784095accfb Author: Paulo Zanoni Date: Fri Dec 13 15:22:31 2013 -0200 drm/i915: release the GTT mmaps when going into D3 Also note that the WARN is inappropriate for this function as GPU activity is orthogonal to GTT mmap status. Rather it is the caller that relies upon this condition and so it should assert that the GPU is idle itself. References: https://bugs.freedesktop.org/show_bug.cgi?id=80081 Signed-off-by: Chris Wilson Cc: Paulo Zanoni Cc: Rodrigo Vivi Cc: Daniel Vetter Reviewed-by: Paulo Zanoni Tested-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1794a04..234aa1c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1632,22 +1632,6 @@ out: return ret; } -void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv) -{ - struct i915_vma *vma; - - /* - * Only the global gtt is relevant for gtt memory mappings, so restrict - * list traversal to objects bound into the global address space. Note - * that the active list should be empty, but better safe than sorry. - */ - WARN_ON(!list_empty(&dev_priv->gtt.base.active_list)); - list_for_each_entry(vma, &dev_priv->gtt.base.active_list, mm_list) - i915_gem_release_mmap(vma->obj); - list_for_each_entry(vma, &dev_priv->gtt.base.inactive_list, mm_list) - i915_gem_release_mmap(vma->obj); -} - /** * i915_gem_release_mmap - remove physical page mappings * @obj: obj in question @@ -1673,6 +1657,15 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } +void +i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *obj; + + list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) + i915_gem_release_mmap(obj); +} + uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { -- cgit v0.10.2 From 1ce826d436f33c8fc0cf8b51d213b496ea73e0a6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 10 Jun 2014 11:23:33 +0100 Subject: drm/i915: Simplify processing of the golden render context state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite i915_gem_render_state.c for the purposes of clarity and compactness, in the process we can eliminate some dodgy math that did not handle 64bit addresses correctly. Signed-off-by: Chris Wilson Cc: Ville Syrjälä Cc: Damien Lespiau Cc: Mika Kuoppala Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index 3521f99..e60be3f 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -28,64 +28,13 @@ #include "i915_drv.h" #include "intel_renderstate.h" -struct i915_render_state { +struct render_state { + const struct intel_renderstate_rodata *rodata; struct drm_i915_gem_object *obj; - unsigned long ggtt_offset; - void *batch; - u32 size; - u32 len; + u64 ggtt_offset; + int gen; }; -static struct i915_render_state *render_state_alloc(struct drm_device *dev) -{ - struct i915_render_state *so; - struct page *page; - int ret; - - so = kzalloc(sizeof(*so), GFP_KERNEL); - if (!so) - return ERR_PTR(-ENOMEM); - - so->obj = i915_gem_alloc_object(dev, 4096); - if (so->obj == NULL) { - ret = -ENOMEM; - goto free; - } - so->size = 4096; - - ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0); - if (ret) - goto free_gem; - - BUG_ON(so->obj->pages->nents != 1); - page = sg_page(so->obj->pages->sgl); - - so->batch = kmap(page); - if (!so->batch) { - ret = -ENOMEM; - goto unpin; - } - - so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj); - - return so; -unpin: - i915_gem_object_ggtt_unpin(so->obj); -free_gem: - drm_gem_object_unreference(&so->obj->base); -free: - kfree(so); - return ERR_PTR(ret); -} - -static void render_state_free(struct i915_render_state *so) -{ - kunmap(so->batch); - i915_gem_object_ggtt_unpin(so->obj); - drm_gem_object_unreference(&so->obj->base); - kfree(so); -} - static const struct intel_renderstate_rodata * render_state_get_rodata(struct drm_device *dev, const int gen) { @@ -101,98 +50,120 @@ render_state_get_rodata(struct drm_device *dev, const int gen) return NULL; } -static int render_state_setup(const int gen, - const struct intel_renderstate_rodata *rodata, - struct i915_render_state *so) +static int render_state_init(struct render_state *so, struct drm_device *dev) { - const u64 goffset = i915_gem_obj_ggtt_offset(so->obj); - u32 reloc_index = 0; - u32 * const d = so->batch; - unsigned int i = 0; int ret; - if (!rodata || rodata->batch_items * 4 > so->size) + so->gen = INTEL_INFO(dev)->gen; + so->rodata = render_state_get_rodata(dev, so->gen); + if (so->rodata == NULL) + return 0; + + if (so->rodata->batch_items * 4 > 4096) return -EINVAL; + so->obj = i915_gem_alloc_object(dev, 4096); + if (so->obj == NULL) + return -ENOMEM; + + ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0); + if (ret) + goto free_gem; + + so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj); + return 0; + +free_gem: + drm_gem_object_unreference(&so->obj->base); + return ret; +} + +static int render_state_setup(struct render_state *so) +{ + const struct intel_renderstate_rodata *rodata = so->rodata; + unsigned int i = 0, reloc_index = 0; + struct page *page; + u32 *d; + int ret; + ret = i915_gem_object_set_to_cpu_domain(so->obj, true); if (ret) return ret; + page = sg_page(so->obj->pages->sgl); + d = kmap(page); + while (i < rodata->batch_items) { u32 s = rodata->batch[i]; - if (reloc_index < rodata->reloc_items && - i * 4 == rodata->reloc[reloc_index]) { - - s += goffset & 0xffffffff; - - /* We keep batch offsets max 32bit */ - if (gen >= 8) { + if (i * 4 == rodata->reloc[reloc_index]) { + u64 r = s + so->ggtt_offset; + s = lower_32_bits(r); + if (so->gen >= 8) { if (i + 1 >= rodata->batch_items || rodata->batch[i + 1] != 0) return -EINVAL; - d[i] = s; - i++; - s = (goffset & 0xffffffff00000000ull) >> 32; + d[i++] = s; + s = upper_32_bits(r); } reloc_index++; } - d[i] = s; - i++; + d[i++] = s; } + kunmap(page); ret = i915_gem_object_set_to_gtt_domain(so->obj, false); if (ret) return ret; - if (rodata->reloc_items != reloc_index) { - DRM_ERROR("not all relocs resolved, %d out of %d\n", - reloc_index, rodata->reloc_items); + if (rodata->reloc[reloc_index] != -1) { + DRM_ERROR("only %d relocs resolved\n", reloc_index); return -EINVAL; } - so->len = rodata->batch_items * 4; - return 0; } +static void render_state_fini(struct render_state *so) +{ + i915_gem_object_ggtt_unpin(so->obj); + drm_gem_object_unreference(&so->obj->base); +} + int i915_gem_render_state_init(struct intel_engine_cs *ring) { - const int gen = INTEL_INFO(ring->dev)->gen; - struct i915_render_state *so; - const struct intel_renderstate_rodata *rodata; + struct render_state so; int ret; if (WARN_ON(ring->id != RCS)) return -ENOENT; - rodata = render_state_get_rodata(ring->dev, gen); - if (rodata == NULL) - return 0; + ret = render_state_init(&so, ring->dev); + if (ret) + return ret; - so = render_state_alloc(ring->dev); - if (IS_ERR(so)) - return PTR_ERR(so); + if (so.rodata == NULL) + return 0; - ret = render_state_setup(gen, rodata, so); + ret = render_state_setup(&so); if (ret) goto out; ret = ring->dispatch_execbuffer(ring, - i915_gem_obj_ggtt_offset(so->obj), - so->len, + so.ggtt_offset, + so.rodata->batch_items * 4, I915_DISPATCH_SECURE); if (ret) goto out; - i915_vma_move_to_active(i915_gem_obj_to_ggtt(so->obj), ring); + i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), ring); - ret = __i915_add_request(ring, NULL, so->obj, NULL); + ret = __i915_add_request(ring, NULL, so.obj, NULL); /* __i915_add_request moves object to inactive if it fails */ out: - render_state_free(so); + render_state_fini(&so); return ret; } diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h index a5e783a..fd4f662 100644 --- a/drivers/gpu/drm/i915/intel_renderstate.h +++ b/drivers/gpu/drm/i915/intel_renderstate.h @@ -28,7 +28,6 @@ struct intel_renderstate_rodata { const u32 *reloc; - const u32 reloc_items; const u32 *batch; const u32 batch_items; }; @@ -40,7 +39,6 @@ extern const struct intel_renderstate_rodata gen8_null_state; #define RO_RENDERSTATE(_g) \ const struct intel_renderstate_rodata gen ## _g ## _null_state = { \ .reloc = gen ## _g ## _null_state_relocs, \ - .reloc_items = sizeof(gen ## _g ## _null_state_relocs)/4, \ .batch = gen ## _g ## _null_state_batch, \ .batch_items = sizeof(gen ## _g ## _null_state_batch)/4, \ } diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen6.c b/drivers/gpu/drm/i915/intel_renderstate_gen6.c index 740538a..56c1429 100644 --- a/drivers/gpu/drm/i915/intel_renderstate_gen6.c +++ b/drivers/gpu/drm/i915/intel_renderstate_gen6.c @@ -6,6 +6,7 @@ static const u32 gen6_null_state_relocs[] = { 0x0000002c, 0x000001e0, 0x000001e4, + -1, }; static const u32 gen6_null_state_batch[] = { diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen7.c b/drivers/gpu/drm/i915/intel_renderstate_gen7.c index 6fa7ff2..419e35a 100644 --- a/drivers/gpu/drm/i915/intel_renderstate_gen7.c +++ b/drivers/gpu/drm/i915/intel_renderstate_gen7.c @@ -5,6 +5,7 @@ static const u32 gen7_null_state_relocs[] = { 0x00000010, 0x00000018, 0x000001ec, + -1, }; static const u32 gen7_null_state_batch[] = { diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen8.c b/drivers/gpu/drm/i915/intel_renderstate_gen8.c index 5c87561..75ef1b5d 100644 --- a/drivers/gpu/drm/i915/intel_renderstate_gen8.c +++ b/drivers/gpu/drm/i915/intel_renderstate_gen8.c @@ -5,6 +5,7 @@ static const u32 gen8_null_state_relocs[] = { 0x00000050, 0x00000060, 0x000003ec, + -1, }; static const u32 gen8_null_state_batch[] = { -- cgit v0.10.2 From 9576c27f5287f873505583350049100896a48299 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 13 Jun 2014 18:45:40 -0300 Subject: drm/i915: update BDW DDI buffer translations Two BSpec updates changed the recommended values for BDW eDP and DP DDI buffer translations. Now the signal levels also match the HSW signal levels, which simplify things a little bit. It seems some DP sinks don't work properly without voltage level 0 and pre-emphasis level 3, so this patch may fix some bugs on panels/monitors that happen on BDW but not on HSW. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9ce017b..e1fb0f2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5821,7 +5821,6 @@ enum punit_power_well { #define DDI_BUF_CTL_B 0x64100 #define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B) #define DDI_BUF_CTL_ENABLE (1<<31) -/* Haswell */ #define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */ #define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ #define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */ @@ -5831,16 +5830,6 @@ enum punit_power_well { #define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */ #define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ #define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ -/* Broadwell */ -#define DDI_BUF_EMP_400MV_0DB_BDW (0<<24) /* Sel0 */ -#define DDI_BUF_EMP_400MV_3_5DB_BDW (1<<24) /* Sel1 */ -#define DDI_BUF_EMP_400MV_6DB_BDW (2<<24) /* Sel2 */ -#define DDI_BUF_EMP_600MV_0DB_BDW (3<<24) /* Sel3 */ -#define DDI_BUF_EMP_600MV_3_5DB_BDW (4<<24) /* Sel4 */ -#define DDI_BUF_EMP_600MV_6DB_BDW (5<<24) /* Sel5 */ -#define DDI_BUF_EMP_800MV_0DB_BDW (6<<24) /* Sel6 */ -#define DDI_BUF_EMP_800MV_3_5DB_BDW (7<<24) /* Sel7 */ -#define DDI_BUF_EMP_1200MV_0DB_BDW (8<<24) /* Sel8 */ #define DDI_BUF_EMP_MASK (0xf<<24) #define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index b17b9c7..ded6013 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -76,12 +76,12 @@ static const u32 bdw_ddi_translations_edp[] = { 0x00FFFFFF, 0x00000012, /* eDP parameters */ 0x00EBAFFF, 0x00020011, 0x00C71FFF, 0x0006000F, + 0x00AAAFFF, 0x000E000A, 0x00FFFFFF, 0x00020011, 0x00DB6FFF, 0x0005000F, 0x00BEEFFF, 0x000A000C, 0x00FFFFFF, 0x0005000F, 0x00DB6FFF, 0x000A000C, - 0x00FFFFFF, 0x000A000C, 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/ }; @@ -89,12 +89,12 @@ static const u32 bdw_ddi_translations_dp[] = { 0x00FFFFFF, 0x0007000E, /* DP parameters */ 0x00D75FFF, 0x000E000A, 0x00BEFFFF, 0x00140006, + 0x80B2CFFF, 0x001B0002, 0x00FFFFFF, 0x000E000A, 0x00D75FFF, 0x00180004, 0x80CB2FFF, 0x001B0002, 0x00F7DFFF, 0x00180004, 0x80D75FFF, 0x001B0002, - 0x80FFFFFF, 0x001B0002, 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/ }; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b6b2640..dc58044 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2330,7 +2330,7 @@ intel_dp_voltage_max(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); enum port port = dp_to_dig_port(intel_dp)->port; - if (IS_VALLEYVIEW(dev) || IS_BROADWELL(dev)) + if (IS_VALLEYVIEW(dev)) return DP_TRAIN_VOLTAGE_SWING_1200; else if (IS_GEN7(dev) && port == PORT_A) return DP_TRAIN_VOLTAGE_SWING_800; @@ -2346,18 +2346,7 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) struct drm_device *dev = intel_dp_to_dev(intel_dp); enum port port = dp_to_dig_port(intel_dp)->port; - if (IS_BROADWELL(dev)) { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: - default: - return DP_TRAIN_PRE_EMPHASIS_0; - } - } else if (IS_HASWELL(dev)) { + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: return DP_TRAIN_PRE_EMPHASIS_9_5; @@ -2829,41 +2818,6 @@ intel_hsw_signal_levels(uint8_t train_set) } } -static uint32_t -intel_bdw_signal_levels(uint8_t train_set) -{ - int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | - DP_TRAIN_PRE_EMPHASIS_MASK); - switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_400MV_0DB_BDW; /* Sel0 */ - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_400MV_3_5DB_BDW; /* Sel1 */ - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_400MV_6DB_BDW; /* Sel2 */ - - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_600MV_0DB_BDW; /* Sel3 */ - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_600MV_3_5DB_BDW; /* Sel4 */ - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_600MV_6DB_BDW; /* Sel5 */ - - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_800MV_0DB_BDW; /* Sel6 */ - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_800MV_3_5DB_BDW; /* Sel7 */ - - case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_1200MV_0DB_BDW; /* Sel8 */ - - default: - DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" - "0x%x\n", signal_levels); - return DDI_BUF_EMP_400MV_0DB_BDW; /* Sel0 */ - } -} - /* Properly updates "DP" with the correct signal levels. */ static void intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) @@ -2874,10 +2828,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) uint32_t signal_levels, mask; uint8_t train_set = intel_dp->train_set[0]; - if (IS_BROADWELL(dev)) { - signal_levels = intel_bdw_signal_levels(train_set); - mask = DDI_BUF_EMP_MASK; - } else if (IS_HASWELL(dev)) { + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { signal_levels = intel_hsw_signal_levels(train_set); mask = DDI_BUF_EMP_MASK; } else if (IS_CHERRYVIEW(dev)) { -- cgit v0.10.2 From 1100244e31f5b8ce9d09ec6b965536c15efc6148 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 13 Jun 2014 18:45:41 -0300 Subject: drm/i915: update intel_dp_voltage_max comment Any comment containing "current Intel hardware supports" quickly becomes obsolete, so remove it and let people discover the information by looking at the function implementation. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index dc58044..6381d4c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2319,11 +2319,7 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE; } -/* - * These are source-specific values; current Intel hardware supports - * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB - */ - +/* These are source-specific values. */ static uint8_t intel_dp_voltage_max(struct intel_dp *intel_dp) { -- cgit v0.10.2 From 8c0f92e1f87149bd2429015c9d0600a13a34d607 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 16 Jun 2014 02:08:26 +0200 Subject: drm/i915: Fix comment about our plane remapping on gen2/3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spotted while crawling around in the area. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2fdbedf..a514779 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11339,7 +11339,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) /* * On gen2/3 only plane A can do fbc, but the panel fitter and lvds port - * is hooked to plane B. Hence we want plane A feeding pipe B. + * is hooked to pipe B. Hence we want plane A feeding pipe B. */ intel_crtc->pipe = pipe; intel_crtc->plane = pipe; -- cgit v0.10.2 From 5aa8a937730940b48b70b21e05cf03e4cea31058 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 16 Jun 2014 10:12:55 -0700 Subject: drm/i915: Handle disabled primary plane in debugfs i915_display_info (v2) Now that the primary plane can be disabled independently of the CRTC, the debugfs code needs to be updated to recognize when the primary plane is disabled and not try to return information about the primary plane's framebuffer. This change prevents a NULL dereference when reading i915_display_info with a disabled primary plane. v2: Replace a seq_printf() with seq_puts() (suggested by Damien) Signed-off-by: Matt Roper Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a8b8140..8057fd4 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2220,9 +2220,12 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc) struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder; - seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", - crtc->primary->fb->base.id, crtc->x, crtc->y, - crtc->primary->fb->width, crtc->primary->fb->height); + if (crtc->primary->fb) + seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", + crtc->primary->fb->base.id, crtc->x, crtc->y, + crtc->primary->fb->width, crtc->primary->fb->height); + else + seq_puts(m, "\tprimary plane disabled\n"); for_each_encoder_on_crtc(dev, crtc, intel_encoder) intel_encoder_info(m, intel_crtc, intel_encoder); } -- cgit v0.10.2 From 24f3a8cf7766e52a087904b4346794c7b410f957 Mon Sep 17 00:00:00 2001 From: Akash Goel Date: Tue, 17 Jun 2014 10:59:42 +0530 Subject: drm/i915: Added write-enable pte bit supportt This adds support for a write-enable bit in the entry of GTT. This is handled via a read-only flag in the GEM buffer object which is then used to see how to set the bit when writing the GTT entries. Currently by default the Batch buffer & Ring buffers are marked as read only. v2: Moved the pte override code for read-only bit to 'byt_pte_encode'. (Chris) Fixed the issue of leaving 'gt_old_ro' as unused. (Chris) v3: Removed the 'gt_old_ro' field, now setting RO bit only for Ring Buffers(Daniel). v4: Added a new 'flags' parameter to all the pte(gen6) encode & insert_entries functions, in lieu of overloading the cache_level enum (Daniel). v5: Removed the superfluous VLV check & changed the definition location of PTE_READ_ONLY flag (Imre) Reviewed-by: Imre Deak Signed-off-by: Akash Goel Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index faef035..c5b0215 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1661,6 +1661,12 @@ struct drm_i915_gem_object { unsigned int pin_display:1; /* + * Is the object to be mapped as read-only to the GPU + * Only honoured if hardware has relevant pte bit + */ + unsigned long gt_ro:1; + + /* * Is the GPU currently using a fence to access this buffer, */ unsigned int pending_fenced_gpu_access:1; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 6f5d5a1..743ddfc 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -116,7 +116,7 @@ static inline gen8_ppgtt_pde_t gen8_pde_encode(struct drm_device *dev, static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr, enum i915_cache_level level, - bool valid) + bool valid, u32 unused) { gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0; pte |= GEN6_PTE_ADDR_ENCODE(addr); @@ -138,7 +138,7 @@ static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr, static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr, enum i915_cache_level level, - bool valid) + bool valid, u32 unused) { gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0; pte |= GEN6_PTE_ADDR_ENCODE(addr); @@ -162,7 +162,7 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr, static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr, enum i915_cache_level level, - bool valid) + bool valid, u32 flags) { gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0; pte |= GEN6_PTE_ADDR_ENCODE(addr); @@ -170,7 +170,8 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr, /* Mark the page as writeable. Other platforms don't have a * setting for read-only/writable, so this matches that behavior. */ - pte |= BYT_PTE_WRITEABLE; + if (!(flags & PTE_READ_ONLY)) + pte |= BYT_PTE_WRITEABLE; if (level != I915_CACHE_NONE) pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES; @@ -180,7 +181,7 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr, static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr, enum i915_cache_level level, - bool valid) + bool valid, u32 unused) { gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0; pte |= HSW_PTE_ADDR_ENCODE(addr); @@ -193,7 +194,7 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr, static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr, enum i915_cache_level level, - bool valid) + bool valid, u32 unused) { gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0; pte |= HSW_PTE_ADDR_ENCODE(addr); @@ -307,7 +308,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, struct sg_table *pages, uint64_t start, - enum i915_cache_level cache_level) + enum i915_cache_level cache_level, u32 unused) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); @@ -645,7 +646,7 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) uint32_t pd_entry; int pte, pde; - scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true); + scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0); pd_addr = (gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + ppgtt->pd_offset / sizeof(gen6_gtt_pte_t); @@ -947,7 +948,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm, unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; unsigned last_pte, i; - scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true); + scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0); while (num_entries) { last_pte = first_pte + num_entries; @@ -970,7 +971,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm, static void gen6_ppgtt_insert_entries(struct i915_address_space *vm, struct sg_table *pages, uint64_t start, - enum i915_cache_level cache_level) + enum i915_cache_level cache_level, u32 flags) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); @@ -987,7 +988,8 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm, pt_vaddr[act_pte] = vm->pte_encode(sg_page_iter_dma_address(&sg_iter), - cache_level, true); + cache_level, true, flags); + if (++act_pte == I915_PPGTT_PT_ENTRIES) { kunmap_atomic(pt_vaddr); pt_vaddr = NULL; @@ -1224,8 +1226,12 @@ ppgtt_bind_vma(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags) { + /* Currently applicable only to VLV */ + if (vma->obj->gt_ro) + flags |= PTE_READ_ONLY; + vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start, - cache_level); + cache_level, flags); } static void ppgtt_unbind_vma(struct i915_vma *vma) @@ -1400,7 +1406,7 @@ static inline void gen8_set_pte(void __iomem *addr, gen8_gtt_pte_t pte) static void gen8_ggtt_insert_entries(struct i915_address_space *vm, struct sg_table *st, uint64_t start, - enum i915_cache_level level) + enum i915_cache_level level, u32 unused) { struct drm_i915_private *dev_priv = vm->dev->dev_private; unsigned first_entry = start >> PAGE_SHIFT; @@ -1446,7 +1452,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm, static void gen6_ggtt_insert_entries(struct i915_address_space *vm, struct sg_table *st, uint64_t start, - enum i915_cache_level level) + enum i915_cache_level level, u32 flags) { struct drm_i915_private *dev_priv = vm->dev->dev_private; unsigned first_entry = start >> PAGE_SHIFT; @@ -1458,7 +1464,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm, for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) { addr = sg_page_iter_dma_address(&sg_iter); - iowrite32(vm->pte_encode(addr, level, true), >t_entries[i]); + iowrite32(vm->pte_encode(addr, level, true, flags), >t_entries[i]); i++; } @@ -1470,7 +1476,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm, */ if (i != 0) WARN_ON(readl(>t_entries[i-1]) != - vm->pte_encode(addr, level, true)); + vm->pte_encode(addr, level, true, flags)); /* This next bit makes the above posting read even more important. We * want to flush the TLBs only after we're certain all the PTE updates @@ -1524,7 +1530,7 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch); + scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch, 0); for (i = 0; i < num_entries; i++) iowrite32(scratch_pte, >t_base[i]); @@ -1573,6 +1579,10 @@ static void ggtt_bind_vma(struct i915_vma *vma, struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj = vma->obj; + /* Currently applicable only to VLV */ + if (obj->gt_ro) + flags |= PTE_READ_ONLY; + /* If there is no aliasing PPGTT, or the caller needs a global mapping, * or we have a global mapping already but the cacheability flags have * changed, set the global PTEs. @@ -1589,7 +1599,7 @@ static void ggtt_bind_vma(struct i915_vma *vma, (cache_level != obj->cache_level)) { vma->vm->insert_entries(vma->vm, obj->pages, vma->node.start, - cache_level); + cache_level, flags); obj->has_global_gtt_mapping = 1; } } @@ -1601,7 +1611,7 @@ static void ggtt_bind_vma(struct i915_vma *vma, appgtt->base.insert_entries(&appgtt->base, vma->obj->pages, vma->node.start, - cache_level); + cache_level, flags); vma->obj->has_aliasing_ppgtt_mapping = 1; } } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 1b96a06..8d6f7c1 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -154,6 +154,7 @@ struct i915_vma { void (*unbind_vma)(struct i915_vma *vma); /* Map an object into an address space with the given cache flags. */ #define GLOBAL_BIND (1<<0) +#define PTE_READ_ONLY (1<<1) void (*bind_vma)(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags); @@ -197,7 +198,7 @@ struct i915_address_space { /* FIXME: Need a more generic return type */ gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr, enum i915_cache_level level, - bool valid); /* Create a valid PTE */ + bool valid, u32 flags); /* Create a valid PTE */ void (*clear_range)(struct i915_address_space *vm, uint64_t start, uint64_t length, @@ -205,7 +206,7 @@ struct i915_address_space { void (*insert_entries)(struct i915_address_space *vm, struct sg_table *st, uint64_t start, - enum i915_cache_level cache_level); + enum i915_cache_level cache_level, u32 flags); void (*cleanup)(struct i915_address_space *vm); }; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 0eaaaec..b96edaf 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1397,6 +1397,9 @@ static int allocate_ring_buffer(struct intel_engine_cs *ring) if (obj == NULL) return -ENOMEM; + /* mark ring buffers as read-only from GPU side by default */ + obj->gt_ro = 1; + ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE); if (ret) goto err_unref; -- cgit v0.10.2 From 032843a5a5733cbb50daca20a76ead2c55d8f51a Mon Sep 17 00:00:00 2001 From: Daisy Sun Date: Mon, 16 Jun 2014 15:48:18 -0700 Subject: drm/i915: Broaden FBC resolution limit to 4096*4096 Staring from HSW, the resolution limit of FBC has increased to 4096*4096 Issue: VIZ-2813 Change-Id: I842f64e3cf2c0d18d29ef1bcfef3b9bb1f1764ac Signed-off-by: Daisy Sun Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 537a7ee..6cd11aa 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -530,7 +530,10 @@ void intel_update_fbc(struct drm_device *dev) goto out_disable; } - if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { + if (INTEL_INFO(dev)->gen >= 8 || IS_HASWELL(dev)) { + max_width = 4096; + max_height = 4096; + } else if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { max_width = 4096; max_height = 2048; } else { -- cgit v0.10.2 From f02a326e32edae40e7837824db6d1b3aae7c29b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 16 Jun 2014 19:51:21 +0200 Subject: drm/i915: Add missing statics to recent psr functions Cc: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6381d4c..912e9c4 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1888,7 +1888,7 @@ void intel_edp_psr_update(struct drm_device *dev) intel_edp_psr_exit(dev, true); } -void intel_edp_psr_work(struct work_struct *work) +static void intel_edp_psr_work(struct work_struct *work) { struct drm_i915_private *dev_priv = container_of(work, typeof(*dev_priv), psr.work.work); @@ -1907,7 +1907,7 @@ void intel_edp_psr_work(struct work_struct *work) } } -void intel_edp_psr_inactivate(struct drm_device *dev) +static void intel_edp_psr_inactivate(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_connector *connector; -- cgit v0.10.2 From 84c33a64b4e29757d04ba1b285b96ee160d5ff93 Mon Sep 17 00:00:00 2001 From: Sourab Gupta Date: Mon, 2 Jun 2014 16:47:17 +0530 Subject: drm/i915: Replaced Blitter ring based flips with MMIO flips This patch enables the framework for using MMIO based flip calls, in contrast with the CS based flip calls which are being used currently. MMIO based flip calls can be enabled on architectures where Render and Blitter engines reside in different power wells. The decision to use MMIO flips can be made based on workloads to give 100% residency for Media power well. v2: The MMIO flips now use the interrupt driven mechanism for issuing the flips when target seqno is reached. (Incorporating Ville's idea) v3: Rebasing on latest code. Code restructuring after incorporating Damien's comments v4: Addressing Ville's review comments -general cleanup -updating only base addr instead of calling update_primary_plane -extending patch for gen5+ platforms v5: Addressed Ville's review comments -Making mmio flip vs cs flip selection based on module parameter -Adding check for DRIVER_MODESET feature in notify_ring before calling notify mmio flip. -Other changes mostly in function arguments v6: -Having a seperate function to check condition for using mmio flips (Ville) -propogating error code from i915_gem_check_olr (Ville) v7: -Adding __must_check with i915_gem_check_olr (Chris) -Renaming mmio_flip_data to mmio_flip (Chris) -Rebasing on latest nightly v8: -Rebasing on latest code -squash 3rd patch in series(mmio setbase vs page flip race) with this patch -Added new tiling mode update in intel_do_mmio_flip (Chris) v9: -check for obj->last_write_seqno being 0 instead of obj->ring being NULL in intel_postpone_flip, as this is a more restrictive condition (Chris) v10: -Applied Chris's suggestions for squashing patches 2,3 into this patch. These patches make the selection of CS vs MMIO flip at the page flip time, and make the module parameter for using mmio flips as tristate, the states being 'force CS flips', 'force mmio flips', 'driver discretion'. Changed the logic for driver discretion (Chris) v11: Minor code cleanup(better readability, fixing whitespace errors, using lockdep to check mutex locked status in postpone_flip, removal of __must_check in function definition) (Chris) Reviewed-by: Chris Wilson Signed-off-by: Sourab Gupta Signed-off-by: Akash Goel Tested-by: Chris Wilson # snb, ivb [danvet: Fix up parameter alignement checkpatch spotted.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0f98957..10009fc 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1575,6 +1575,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->backlight_lock); spin_lock_init(&dev_priv->uncore.lock); spin_lock_init(&dev_priv->mm.object_stat_lock); + spin_lock_init(&dev_priv->mmio_flip_lock); mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->modeset_restore_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c5b0215..9bfc242 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1370,6 +1370,9 @@ struct drm_i915_private { /* protects the irq masks */ spinlock_t irq_lock; + /* protects the mmio flip data */ + spinlock_t mmio_flip_lock; + bool display_irqs_enabled; /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */ @@ -2043,6 +2046,7 @@ struct i915_params { bool reset; bool disable_display; bool disable_vtd_wa; + int use_mmio_flip; }; extern struct i915_params i915 __read_mostly; @@ -2239,6 +2243,8 @@ bool i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_engine_cs *ring); int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); +int __must_check i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno); + static inline bool i915_reset_in_progress(struct i915_gpu_error *error) { return unlikely(atomic_read(&error->reset_counter) @@ -2608,6 +2614,8 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data, int i915_get_reset_stats_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +void intel_notify_mmio_flip(struct intel_engine_cs *ring); + /* overlay */ extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev); extern void intel_overlay_print_error_state(struct drm_i915_error_state_buf *e, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 234aa1c..7f643db 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1095,7 +1095,7 @@ i915_gem_check_wedge(struct i915_gpu_error *error, * Compare seqno against outstanding lazy request. Emit a request if they are * equal. */ -static int +int i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno) { int ret; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 218ef08..c7d4cb7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1218,6 +1218,9 @@ static void notify_ring(struct drm_device *dev, trace_i915_gem_request_complete(ring); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + intel_notify_mmio_flip(ring); + wake_up_all(&ring->irq_queue); i915_queue_hangcheck(dev); } diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index d05a2af..8145729 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -48,6 +48,7 @@ struct i915_params i915 __read_mostly = { .disable_display = 0, .enable_cmd_parser = 1, .disable_vtd_wa = 0, + .use_mmio_flip = 0, }; module_param_named(modeset, i915.modeset, int, 0400); @@ -156,3 +157,7 @@ MODULE_PARM_DESC(disable_vtd_wa, "Disable all VT-d workarounds (default: false)" module_param_named(enable_cmd_parser, i915.enable_cmd_parser, int, 0600); MODULE_PARM_DESC(enable_cmd_parser, "Enable command parsing (1=enabled [default], 0=disabled)"); + +module_param_named(use_mmio_flip, i915.use_mmio_flip, int, 0600); +MODULE_PARM_DESC(use_mmio_flip, + "use MMIO flips (-1=never, 0=driver discretion [default], 1=always)"); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a514779..5e8e711 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9248,6 +9248,147 @@ static int intel_gen7_queue_flip(struct drm_device *dev, return 0; } +static bool use_mmio_flip(struct intel_engine_cs *ring, + struct drm_i915_gem_object *obj) +{ + /* + * This is not being used for older platforms, because + * non-availability of flip done interrupt forces us to use + * CS flips. Older platforms derive flip done using some clever + * tricks involving the flip_pending status bits and vblank irqs. + * So using MMIO flips there would disrupt this mechanism. + */ + + if (INTEL_INFO(ring->dev)->gen < 5) + return false; + + if (i915.use_mmio_flip < 0) + return false; + else if (i915.use_mmio_flip > 0) + return true; + else + return ring != obj->ring; +} + +static void intel_do_mmio_flip(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_framebuffer *intel_fb = + to_intel_framebuffer(intel_crtc->base.primary->fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + u32 dspcntr; + u32 reg; + + intel_mark_page_flip_active(intel_crtc); + + reg = DSPCNTR(intel_crtc->plane); + dspcntr = I915_READ(reg); + + if (INTEL_INFO(dev)->gen >= 4) { + if (obj->tiling_mode != I915_TILING_NONE) + dspcntr |= DISPPLANE_TILED; + else + dspcntr &= ~DISPPLANE_TILED; + } + I915_WRITE(reg, dspcntr); + + I915_WRITE(DSPSURF(intel_crtc->plane), + intel_crtc->unpin_work->gtt_offset); + POSTING_READ(DSPSURF(intel_crtc->plane)); +} + +static int intel_postpone_flip(struct drm_i915_gem_object *obj) +{ + struct intel_engine_cs *ring; + int ret; + + lockdep_assert_held(&obj->base.dev->struct_mutex); + + if (!obj->last_write_seqno) + return 0; + + ring = obj->ring; + + if (i915_seqno_passed(ring->get_seqno(ring, true), + obj->last_write_seqno)) + return 0; + + ret = i915_gem_check_olr(ring, obj->last_write_seqno); + if (ret) + return ret; + + if (WARN_ON(!ring->irq_get(ring))) + return 0; + + return 1; +} + +void intel_notify_mmio_flip(struct intel_engine_cs *ring) +{ + struct drm_i915_private *dev_priv = to_i915(ring->dev); + struct intel_crtc *intel_crtc; + unsigned long irq_flags; + u32 seqno; + + seqno = ring->get_seqno(ring, false); + + spin_lock_irqsave(&dev_priv->mmio_flip_lock, irq_flags); + for_each_intel_crtc(ring->dev, intel_crtc) { + struct intel_mmio_flip *mmio_flip; + + mmio_flip = &intel_crtc->mmio_flip; + if (mmio_flip->seqno == 0) + continue; + + if (ring->id != mmio_flip->ring_id) + continue; + + if (i915_seqno_passed(seqno, mmio_flip->seqno)) { + intel_do_mmio_flip(intel_crtc); + mmio_flip->seqno = 0; + ring->irq_put(ring); + } + } + spin_unlock_irqrestore(&dev_priv->mmio_flip_lock, irq_flags); +} + +static int intel_queue_mmio_flip(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_i915_gem_object *obj, + struct intel_engine_cs *ring, + uint32_t flags) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long irq_flags; + int ret; + + if (WARN_ON(intel_crtc->mmio_flip.seqno)) + return -EBUSY; + + ret = intel_postpone_flip(obj); + if (ret < 0) + return ret; + if (ret == 0) { + intel_do_mmio_flip(intel_crtc); + return 0; + } + + spin_lock_irqsave(&dev_priv->mmio_flip_lock, irq_flags); + intel_crtc->mmio_flip.seqno = obj->last_write_seqno; + intel_crtc->mmio_flip.ring_id = obj->ring->id; + spin_unlock_irqrestore(&dev_priv->mmio_flip_lock, irq_flags); + + /* + * Double check to catch cases where irq fired before + * mmio flip data was ready + */ + intel_notify_mmio_flip(obj->ring); + return 0; +} + static int intel_default_queue_flip(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -9358,7 +9499,12 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->gtt_offset = i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset; - ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, page_flip_flags); + if (use_mmio_flip(ring, obj)) + ret = intel_queue_mmio_flip(dev, crtc, fb, obj, ring, + page_flip_flags); + else + ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, + page_flip_flags); if (ret) goto cleanup_unpin; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9aa95f0..cac7539 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -358,6 +358,11 @@ struct intel_pipe_wm { bool sprites_scaled; }; +struct intel_mmio_flip { + u32 seqno; + u32 ring_id; +}; + struct intel_crtc { struct drm_crtc base; enum pipe pipe; @@ -411,6 +416,7 @@ struct intel_crtc { wait_queue_head_t vbl_wait; int scanline_offset; + struct intel_mmio_flip mmio_flip; }; struct intel_plane_wm_parameters { -- cgit v0.10.2 From 4661270797f0c172da4b54415574421cd27f1bf3 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 May 2014 12:24:42 +0200 Subject: drm/i915: use shmem helpers if possible Instead of shuffling gfp-masks all the time, use the shmem_read_mapping_page() helper. Note that __GFP_IO and __GFP_WAIT are set in mapping_gfp_mask() for i915, so the behavior is still the same. Cc: Daniel Vetter Cc: Jani Nikula Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f361263..d86b77e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2059,16 +2059,10 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) * our own buffer, now let the real VM do its job and * go down in flames if truly OOM. */ - gfp &= ~(__GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD); - gfp |= __GFP_IO | __GFP_WAIT; - i915_gem_shrink_all(dev_priv); - page = shmem_read_mapping_page_gfp(mapping, i, gfp); + page = shmem_read_mapping_page(mapping, i); if (IS_ERR(page)) goto err_pages; - - gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; - gfp &= ~(__GFP_IO | __GFP_WAIT); } #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { -- cgit v0.10.2 From beff0d0f6121f6a2a818a050a1e4d91706b3f190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 17 Jun 2014 21:03:00 +0300 Subject: drm/i915: Don't prefault the entire obj if the vma is smaller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Take the minimum of the object size and the vma size and prefault only that much. Avoids a SIGBUS when mmapping only a portion of the object. Prefaulting was introduced here: commit b90b91d87038f6b257b40a02b42ed4f9705e06f0 Author: Chris Wilson Date: Tue Jun 10 12:14:40 2014 +0100 drm/i915: Prefault the entire object on first page fault Cc: Chris Wilson Signed-off-by: Ville Syrjälä Testcase: igt/gem_mmap/short-mmap Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 7f643db..d836413 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1570,9 +1570,12 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) pfn >>= PAGE_SHIFT; if (!obj->fault_mappable) { + unsigned long size = min_t(unsigned long, + vma->vm_end - vma->vm_start, + obj->base.size); int i; - for (i = 0; i < obj->base.size >> PAGE_SHIFT; i++) { + for (i = 0; i < size >> PAGE_SHIFT; i++) { ret = vm_insert_pfn(vma, (unsigned long)vma->vm_start + i * PAGE_SIZE, pfn + i); -- cgit v0.10.2 From c9224faa59c3071ecfa2d4b24592f4eb61e57069 Mon Sep 17 00:00:00 2001 From: Brad Volkin Date: Tue, 17 Jun 2014 14:10:34 -0700 Subject: drm/i915: Add some L3 registers to the parser whitelist Beignet needs these in order to program the L3 cache config for OpenCL workloads, particularly when using SLM. Signed-off-by: Brad Volkin Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index 9d79543..dea99d9 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -426,6 +426,9 @@ static const u32 gen7_render_regs[] = { GEN7_SO_WRITE_OFFSET(1), GEN7_SO_WRITE_OFFSET(2), GEN7_SO_WRITE_OFFSET(3), + GEN7_L3SQCREG1, + GEN7_L3CNTLREG2, + GEN7_L3CNTLREG3, }; static const u32 gen7_blt_regs[] = { diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e1fb0f2..3488567 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4670,6 +4670,8 @@ enum punit_power_well { #define GEN7_L3CNTLREG1 0xB01C #define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C47FF8C #define GEN7_L3AGDIS (1<<19) +#define GEN7_L3CNTLREG2 0xB020 +#define GEN7_L3CNTLREG3 0xB024 #define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030 #define GEN7_WA_L3_CHICKEN_MODE 0x20000000 -- cgit v0.10.2 From 8a270ebf341105b85aaf4f5e4336bb53b3c10177 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 17 Jun 2014 22:34:37 +0200 Subject: drm/i915: Grab dev->struct_mutex in i915_gem_pageflip_info We could walk of a bad list otherwise when someone concurrently unbinds stuff for fun. I've suspected this as the root-cause behind seemingly inconsistent state, but alas it's not. Acked-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8057fd4..76c2572 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -513,6 +513,11 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; unsigned long flags; struct intel_crtc *crtc; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; for_each_intel_crtc(dev, crtc) { const char pipe = pipe_name(crtc->pipe); @@ -554,6 +559,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) spin_unlock_irqrestore(&dev->event_lock, flags); } + mutex_unlock(&dev->struct_mutex); + return 0; } -- cgit v0.10.2 From f25748ea7342bcc44866befcbf0222cd1d89d851 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 17 Jun 2014 22:34:38 +0200 Subject: drm/i915: Don't BUG_ON in i915_gem_obj_offset A WARN_ON is perfectly fine. The BUG in here seems to be the cause behind hard-hangs when I cat the i915_gem_pageflip debugfs file (which calls this from an irq spinlock). But only while running a full igt run after a while. I still need to root cause the underlying issue. I'll also start reject patches which add new BUG_ON but don't come with a really good justification for it. The general rule really should be to just WARN and hope the driver survives for long enough. v2: Make the WARN a bit more useful per Chris' suggestion. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d836413..d857f58 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5078,12 +5078,13 @@ unsigned long i915_gem_obj_offset(struct drm_i915_gem_object *o, vm == &dev_priv->mm.aliasing_ppgtt->base) vm = &dev_priv->gtt.base; - BUG_ON(list_empty(&o->vma_list)); list_for_each_entry(vma, &o->vma_list, vma_link) { if (vma->vm == vm) return vma->node.start; } + WARN(1, "%s vma for this object not found.\n", + i915_is_ggtt(vm) ? "global" : "ppgtt"); return -1; } -- cgit v0.10.2 From 72c90f625ccf7aa348f3beae236e09cc837308ee Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Mon, 16 Jun 2014 16:10:57 +0100 Subject: drm/i915: Ack interrupts before handling them (GEN5 - GEN7) Otherwise, we might receive a new interrupt before we have time to ack the first one, eventually missing it. According to BSPec, the right order should be: 1 - Disable Master Interrupt Control. 2 - Find the source(s) of the interrupt. 3 - Clear the Interrupt Identity bits (IIR). 4 - Process the interrupt(s) that had bits set in the IIRs. 5 - Re-enable Master Interrupt Control. Without an atomic XCHG operation with mmio space, the above merely reduces the window in which we can miss an interrupt (especially when you consider how heavyweight the I915_READ/I915_WRITE operations are). We maintain the "disable SDE interrupts when handling" hack since apparently it works. Spotted by Bob Beckett . v2: Add warning to commit message and comments to the code as per Chris Wilson's request. v3: Improve the source comments. Signed-off-by: Oscar Mateo Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c7d4cb7..35c7e3a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2139,6 +2139,14 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) } } +/* + * To handle irqs with the minimum potential races with fresh interrupts, we: + * 1 - Disable Master Interrupt Control. + * 2 - Find the source(s) of the interrupt. + * 3 - Clear the Interrupt Identity bits (IIR). + * 4 - Process the interrupt(s) that had bits set in the IIRs. + * 5 - Re-enable Master Interrupt Control. + */ static irqreturn_t ironlake_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; @@ -2166,32 +2174,34 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) POSTING_READ(SDEIER); } + /* Find, clear, then process each source of interrupt */ + gt_iir = I915_READ(GTIIR); if (gt_iir) { + I915_WRITE(GTIIR, gt_iir); + ret = IRQ_HANDLED; if (INTEL_INFO(dev)->gen >= 6) snb_gt_irq_handler(dev, dev_priv, gt_iir); else ilk_gt_irq_handler(dev, dev_priv, gt_iir); - I915_WRITE(GTIIR, gt_iir); - ret = IRQ_HANDLED; } de_iir = I915_READ(DEIIR); if (de_iir) { + I915_WRITE(DEIIR, de_iir); + ret = IRQ_HANDLED; if (INTEL_INFO(dev)->gen >= 7) ivb_display_irq_handler(dev, de_iir); else ilk_display_irq_handler(dev, de_iir); - I915_WRITE(DEIIR, de_iir); - ret = IRQ_HANDLED; } if (INTEL_INFO(dev)->gen >= 6) { u32 pm_iir = I915_READ(GEN6_PMIIR); if (pm_iir) { - gen6_rps_irq_handler(dev_priv, pm_iir); I915_WRITE(GEN6_PMIIR, pm_iir); ret = IRQ_HANDLED; + gen6_rps_irq_handler(dev_priv, pm_iir); } } -- cgit v0.10.2 From 3ff60f89bc4836583f5bd195062f16c563bd97aa Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Mon, 16 Jun 2014 16:10:58 +0100 Subject: drm/i915/vlv: Ack interrupts before handling them (VLV) Otherwise, we might receive a new interrupt before we have time to ack the first one, eventually missing it. Without an atomic XCHG operation with mmio space, this patch merely reduces the window in which we can miss an interrupt (especially when you consider how heavyweight the I915_READ/I915_WRITE operations are). Notice that, before clearing a port-sourced interrupt in the IIR, the corresponding interrupt source status in the PORT_HOTPLUG_STAT must be cleared. Spotted by Bob Beckett . v2: - Reorder the IIR clearing to reduce the window even further. - Add warning to commit message and comments to the code as per Chris Wilson's request. - Imre Deak pointed out that the pipe underrun flag might not be signaled in IIR, so do not make valleyview_pipestat_irq_handler depend on it. v3: Improve the source code comment. Signed-off-by: Oscar Mateo Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 35c7e3a..c434a68 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1816,26 +1816,28 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - if (IS_G4X(dev)) { - u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; + if (hotplug_status) { + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + /* + * Make sure hotplug status is cleared before we clear IIR, or else we + * may miss hotplug events. + */ + POSTING_READ(PORT_HOTPLUG_STAT); - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x); - } else { - u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; + if (IS_G4X(dev)) { + u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); - } + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x); + } else { + u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; - if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && - hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X) - dp_aux_irq_handler(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); + } - I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); - /* - * Make sure hotplug status is cleared before we clear IIR, or else we - * may miss hotplug events. - */ - POSTING_READ(PORT_HOTPLUG_STAT); + if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && + hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X) + dp_aux_irq_handler(dev); + } } static irqreturn_t valleyview_irq_handler(int irq, void *arg) @@ -1846,29 +1848,36 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) irqreturn_t ret = IRQ_NONE; while (true) { - iir = I915_READ(VLV_IIR); + /* Find, clear, then process each source of interrupt */ + gt_iir = I915_READ(GTIIR); + if (gt_iir) + I915_WRITE(GTIIR, gt_iir); + pm_iir = I915_READ(GEN6_PMIIR); + if (pm_iir) + I915_WRITE(GEN6_PMIIR, pm_iir); + + iir = I915_READ(VLV_IIR); + if (iir) { + /* Consume port before clearing IIR or we'll miss events */ + if (iir & I915_DISPLAY_PORT_INTERRUPT) + i9xx_hpd_irq_handler(dev); + I915_WRITE(VLV_IIR, iir); + } if (gt_iir == 0 && pm_iir == 0 && iir == 0) goto out; ret = IRQ_HANDLED; - snb_gt_irq_handler(dev, dev_priv, gt_iir); - - valleyview_pipestat_irq_handler(dev, iir); - - /* Consume port. Then clear IIR or we'll miss events */ - if (iir & I915_DISPLAY_PORT_INTERRUPT) - i9xx_hpd_irq_handler(dev); - + if (gt_iir) + snb_gt_irq_handler(dev, dev_priv, gt_iir); if (pm_iir) gen6_rps_irq_handler(dev_priv, pm_iir); - - I915_WRITE(GTIIR, gt_iir); - I915_WRITE(GEN6_PMIIR, pm_iir); - I915_WRITE(VLV_IIR, iir); + /* Call regardless, as some status bits might not be + * signalled in iir */ + valleyview_pipestat_irq_handler(dev, iir); } out: -- cgit v0.10.2 From 38cc46d73ed99dd7002f1406002e52d7975d16cc Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Mon, 16 Jun 2014 16:10:59 +0100 Subject: drm/i915/bdw: Ack interrupts before handling them (GEN8) Otherwise, we might receive a new interrupt before we have time to ack the first one, eventually missing it. The right order should be: 1 - Disable Master Interrupt Control. 2 - Find the category of interrupt that is pending. 3 - Find the source(s) of the interrupt and clear the Interrupt Identity bits (IIR) 4 - Process the interrupt(s) that had bits set in the IIRs. 5 - Re-enable Master Interrupt Control. Without an atomic XCHG operation with mmio space, the above merely reduces the window in which we can miss an interrupt (especially when you consider how heavyweight the I915_READ/I915_WRITE operations are). Spotted by Bob Beckett . v2: Add warning to commit message and comments to the code as per Chris Wilson's request. v3: Improve the source code comment. Signed-off-by: Oscar Mateo Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c434a68..b6debdc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1465,6 +1465,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) { tmp = I915_READ(GEN8_GT_IIR(0)); if (tmp) { + I915_WRITE(GEN8_GT_IIR(0), tmp); ret = IRQ_HANDLED; rcs = tmp >> GEN8_RCS_IRQ_SHIFT; bcs = tmp >> GEN8_BCS_IRQ_SHIFT; @@ -1472,7 +1473,6 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, notify_ring(dev, &dev_priv->ring[RCS]); if (bcs & GT_RENDER_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[BCS]); - I915_WRITE(GEN8_GT_IIR(0), tmp); } else DRM_ERROR("The master control interrupt lied (GT0)!\n"); } @@ -1480,6 +1480,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) { tmp = I915_READ(GEN8_GT_IIR(1)); if (tmp) { + I915_WRITE(GEN8_GT_IIR(1), tmp); ret = IRQ_HANDLED; vcs = tmp >> GEN8_VCS1_IRQ_SHIFT; if (vcs & GT_RENDER_USER_INTERRUPT) @@ -1487,7 +1488,6 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, vcs = tmp >> GEN8_VCS2_IRQ_SHIFT; if (vcs & GT_RENDER_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS2]); - I915_WRITE(GEN8_GT_IIR(1), tmp); } else DRM_ERROR("The master control interrupt lied (GT1)!\n"); } @@ -1495,10 +1495,10 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (master_ctl & GEN8_GT_PM_IRQ) { tmp = I915_READ(GEN8_GT_IIR(2)); if (tmp & dev_priv->pm_rps_events) { - ret = IRQ_HANDLED; - gen8_rps_irq_handler(dev_priv, tmp); I915_WRITE(GEN8_GT_IIR(2), tmp & dev_priv->pm_rps_events); + ret = IRQ_HANDLED; + gen8_rps_irq_handler(dev_priv, tmp); } else DRM_ERROR("The master control interrupt lied (PM)!\n"); } @@ -1506,11 +1506,11 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (master_ctl & GEN8_GT_VECS_IRQ) { tmp = I915_READ(GEN8_GT_IIR(3)); if (tmp) { + I915_WRITE(GEN8_GT_IIR(3), tmp); ret = IRQ_HANDLED; vcs = tmp >> GEN8_VECS_IRQ_SHIFT; if (vcs & GT_RENDER_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VECS]); - I915_WRITE(GEN8_GT_IIR(3), tmp); } else DRM_ERROR("The master control interrupt lied (GT3)!\n"); } @@ -2241,36 +2241,36 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) I915_WRITE(GEN8_MASTER_IRQ, 0); POSTING_READ(GEN8_MASTER_IRQ); + /* Find, clear, then process each source of interrupt */ + ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl); if (master_ctl & GEN8_DE_MISC_IRQ) { tmp = I915_READ(GEN8_DE_MISC_IIR); - if (tmp & GEN8_DE_MISC_GSE) - intel_opregion_asle_intr(dev); - else if (tmp) - DRM_ERROR("Unexpected DE Misc interrupt\n"); - else - DRM_ERROR("The master control interrupt lied (DE MISC)!\n"); - if (tmp) { I915_WRITE(GEN8_DE_MISC_IIR, tmp); ret = IRQ_HANDLED; + if (tmp & GEN8_DE_MISC_GSE) + intel_opregion_asle_intr(dev); + else + DRM_ERROR("Unexpected DE Misc interrupt\n"); } + else + DRM_ERROR("The master control interrupt lied (DE MISC)!\n"); } if (master_ctl & GEN8_DE_PORT_IRQ) { tmp = I915_READ(GEN8_DE_PORT_IIR); - if (tmp & GEN8_AUX_CHANNEL_A) - dp_aux_irq_handler(dev); - else if (tmp) - DRM_ERROR("Unexpected DE Port interrupt\n"); - else - DRM_ERROR("The master control interrupt lied (DE PORT)!\n"); - if (tmp) { I915_WRITE(GEN8_DE_PORT_IIR, tmp); ret = IRQ_HANDLED; + if (tmp & GEN8_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + else + DRM_ERROR("Unexpected DE Port interrupt\n"); } + else + DRM_ERROR("The master control interrupt lied (DE PORT)!\n"); } for_each_pipe(pipe) { @@ -2280,33 +2280,32 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) continue; pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe)); - if (pipe_iir & GEN8_PIPE_VBLANK) - intel_pipe_handle_vblank(dev, pipe); - - if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) { - intel_prepare_page_flip(dev, pipe); - intel_finish_page_flip_plane(dev, pipe); - } + if (pipe_iir) { + ret = IRQ_HANDLED; + I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir); + if (pipe_iir & GEN8_PIPE_VBLANK) + intel_pipe_handle_vblank(dev, pipe); - if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE) - hsw_pipe_crc_irq_handler(dev, pipe); + if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) { + intel_prepare_page_flip(dev, pipe); + intel_finish_page_flip_plane(dev, pipe); + } - if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) { - if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, - false)) - DRM_ERROR("Pipe %c FIFO underrun\n", - pipe_name(pipe)); - } + if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE) + hsw_pipe_crc_irq_handler(dev, pipe); - if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) { - DRM_ERROR("Fault errors on pipe %c\n: 0x%08x", - pipe_name(pipe), - pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS); - } + if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) { + if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, + false)) + DRM_ERROR("Pipe %c FIFO underrun\n", + pipe_name(pipe)); + } - if (pipe_iir) { - ret = IRQ_HANDLED; - I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir); + if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) { + DRM_ERROR("Fault errors on pipe %c\n: 0x%08x", + pipe_name(pipe), + pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS); + } } else DRM_ERROR("The master control interrupt lied (DE PIPE)!\n"); } @@ -2318,13 +2317,13 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) * on older pch-split platforms. But this needs testing. */ u32 pch_iir = I915_READ(SDEIIR); - - cpt_irq_handler(dev, pch_iir); - if (pch_iir) { I915_WRITE(SDEIIR, pch_iir); ret = IRQ_HANDLED; - } + cpt_irq_handler(dev, pch_iir); + } else + DRM_ERROR("The master control interrupt lied (SDE)!\n"); + } I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); -- cgit v0.10.2 From 27b6c122512ca30399bb1b39cc42eda83901f304 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Mon, 16 Jun 2014 16:11:00 +0100 Subject: drm/i915/chv: Ack interrupts before handling them (CHV) Otherwise, we might receive a new interrupt before we have time to ack the first one, eventually missing it. Without an atomic XCHG operation with mmio space, this patch merely reduces the window in which we can miss an interrupt (especially when you consider how heavyweight the I915_READ/I915_WRITE operations are). Notice that, before clearing a port-sourced interrupt in the IIR, the corresponding interrupt source status in the PORT_HOTPLUG_STAT must be cleared. Spotted by Bob Beckett . v2: - Add warning to commit message and comments to the code as per Chris Wilson's request. - Imre Deak pointed out that the pipe underrun flag might not be signaled in IIR, so do not make valleyview_pipestat_irq_handler depend on it. v3: Improve the source code comment. Signed-off-by: Oscar Mateo Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b6debdc..73f1849 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1898,21 +1898,27 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) if (master_ctl == 0 && iir == 0) break; + ret = IRQ_HANDLED; + I915_WRITE(GEN8_MASTER_IRQ, 0); - gen8_gt_irq_handler(dev, dev_priv, master_ctl); + /* Find, clear, then process each source of interrupt */ - valleyview_pipestat_irq_handler(dev, iir); + if (iir) { + /* Consume port before clearing IIR or we'll miss events */ + if (iir & I915_DISPLAY_PORT_INTERRUPT) + i9xx_hpd_irq_handler(dev); + I915_WRITE(VLV_IIR, iir); + } - /* Consume port. Then clear IIR or we'll miss events */ - i9xx_hpd_irq_handler(dev); + gen8_gt_irq_handler(dev, dev_priv, master_ctl); - I915_WRITE(VLV_IIR, iir); + /* Call regardless, as some status bits might not be + * signalled in iir */ + valleyview_pipestat_irq_handler(dev, iir); I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL); POSTING_READ(GEN8_MASTER_IRQ); - - ret = IRQ_HANDLED; } return ret; -- cgit v0.10.2 From a1bc07c5205193ff1d771f4ef23402ae1400d42a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 May 2014 19:25:55 +0200 Subject: drm: Don't export internal module variables Drivers really have no business touching these. Noticed because exynose _did_ touch the vblank off delay, which could potentially affect other drivers. drm_debug is an exception since it's used in macros and inline functions. Note that this reduces the timeout on exynos from 50s to 5s. Apparently this was done to paper over a vblank get/put race in exynos, but really should be fixed properly somewhere else. Spotted by David. v2: Drop bonghits changes. Note to self: Don't submit patches before first coffee. Cc: Inki Dae Reviewed-by: David Herrmann Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 14d1646..f4148bf 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -38,17 +38,13 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */ -EXPORT_SYMBOL(drm_rnodes); /* 1 to allow user space to request universal planes (experimental) */ unsigned int drm_universal_planes = 0; -EXPORT_SYMBOL(drm_universal_planes); unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ -EXPORT_SYMBOL(drm_vblank_offdelay); unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ -EXPORT_SYMBOL(drm_timestamp_precision); /* * Default to use monotonic timestamps for wait-for-vblank and page-flip diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index d91f277..ddb5003 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -39,8 +39,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -#define VBLANK_OFF_DELAY 50000 - static struct platform_device *exynos_drm_pdev; static DEFINE_MUTEX(drm_component_lock); @@ -103,8 +101,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) /* setup possible_clones. */ exynos_drm_encoder_setup(dev); - drm_vblank_offdelay = VBLANK_OFF_DELAY; - platform_set_drvdata(dev->platformdev, dev); /* Try to bind all sub drivers. */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 36535f3..fca4b98 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -40,8 +40,6 @@ struct drm_device; struct exynos_drm_overlay; struct drm_connector; -extern unsigned int drm_vblank_offdelay; - /* This enumerates device type. */ enum exynos_drm_device_type { EXYNOS_DEVICE_TYPE_NONE, -- cgit v0.10.2 From 735b9ffada0563afa52d0879cf6d630819ee68ad Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 9 Jun 2014 14:39:50 +0100 Subject: drm: Driver-specific ioctls range from 0x40 to 0x9f DRM_COMMAND_END is 0xa0, so the last driver ioctl is 0x9f, not 0x99. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 9abbeb9..b0b8556 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -780,7 +780,7 @@ struct drm_prime_handle { /** * Device specific ioctls should only be in their respective headers - * The device specific ioctl range is from 0x40 to 0x99. + * The device specific ioctl range is from 0x40 to 0x9f. * Generic IOCTLS restart at 0xA0. * * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and -- cgit v0.10.2 From c3e25ae34fab3557b17a585446b5576e49d2f203 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 7 Jun 2014 10:55:39 -0400 Subject: drm: fix uninitialized acquire_ctx fields (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The acquire ctx will typically be declared on the stack, which means we could have garbage values for any uninitialized field. In this case, it was triggering WARN_ON()s because 'contended' had garbage value. Go ahead and use memset() to be more future-proof. v2: now with extra brown paper bag Reported-by: Ville Syrjälä Signed-off-by: Rob Clark Reviewed-by: Ville Syrjälä Tested-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 7c2497d..0dc57d5 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -64,6 +64,7 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, uint32_t flags) { + memset(ctx, 0, sizeof(*ctx)); ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); INIT_LIST_HEAD(&ctx->locked); } -- cgit v0.10.2 From 14d8ec544f2351a5da33e233b75f44cf6a5bea8e Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Wed, 18 Jun 2014 17:16:03 +0100 Subject: drm/i915: Remove ctx->last_ring The original comment that introduced it said: commit 0009e46cd54324c4af20b0b52b89973b1b914167 Author: Ben Widawsky Date: Fri Dec 6 14:11:02 2013 -0800 drm/i915: Track which ring a context ran on Previously we dropped the association of a context to a ring. It is however very important to know which ring a context ran on (we could have reused the other member, but I was nitpicky). This is very important when we switch address spaces, which unlike context objects, do change per ring. As an example, if we have: RCS BCS ctx A ctx A ctx B ctx B Without tracking the last ring B ran on, we wouldn't know to switch the address space on BCS in the last row. But this is not really true, because we are already checking to != from (with "from" being = ring->last_context) and that should be enough to make sure we switch to the right address space. We would have a problem if we switched the context object for every ring (since then we would fail to do it in some situations) but we only switch it for the render ring, so we don't care. Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0640071..4fea8a0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -591,7 +591,6 @@ struct intel_context { bool is_initialized; uint8_t remap_slice; struct drm_i915_file_private *file_priv; - struct intel_engine_cs *last_ring; struct drm_i915_gem_object *obj; struct i915_ctx_hang_stats hang_stats; struct i915_address_space *vm; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 3ffe308..21eda88 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -605,7 +605,7 @@ static int do_switch(struct intel_engine_cs *ring, BUG_ON(!i915_gem_obj_is_pinned(from->obj)); } - if (from == to && from->last_ring == ring && !to->remap_slice) + if (from == to && !to->remap_slice) return 0; /* Trying to pin first makes error handling easier. */ @@ -699,7 +699,6 @@ static int do_switch(struct intel_engine_cs *ring, done: i915_gem_context_reference(to); ring->last_context = to; - to->last_ring = ring; if (ring->id == RCS && !to->is_initialized && from == NULL) { ret = i915_gem_render_state_init(ring); -- cgit v0.10.2 From 34ea3d386347cd6de4c2fa2491dd85c9e753e7e4 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Thu, 29 May 2014 16:57:41 +0100 Subject: drm: add register and unregister functions for connectors Introduce generic functions to register and unregister connectors. This provides a common place to add and remove associated user space interfaces. Signed-off-by: Thomas Wood Reviewed-by: David Herrmann Signed-off-by: Daniel Vetter diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7df3134..b314a42 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1610,7 +1610,7 @@ int max_width, max_height; The connector is then registered with a call to drm_connector_init with a pointer to the connector functions and a connector type, and exposed through sysfs with a call to - drm_sysfs_connector_add. + drm_connector_register. Supported connector types are @@ -1768,7 +1768,7 @@ int max_width, max_height; (drm_encoder_cleanup) and connectors (drm_connector_cleanup). Furthermore, connectors that have been added to sysfs must be removed by a call to - drm_sysfs_connector_remove before calling + drm_connector_unregister before calling drm_connector_cleanup. @@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev) drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); }]]> In the example above (taken from the i915 driver), a CRTC, connector and diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c index d685a54..abbc309 100644 --- a/drivers/gpu/drm/armada/armada_output.c +++ b/drivers/gpu/drm/armada/armada_output.c @@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn) { struct armada_connector *dconn = drm_to_armada_conn(conn); - drm_sysfs_connector_remove(conn); + drm_connector_unregister(conn); drm_connector_cleanup(conn); kfree(dconn); } @@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev, if (ret) goto err_conn; - ret = drm_sysfs_connector_add(&dconn->conn); + ret = drm_connector_register(&dconn->conn); if (ret) goto err_sysfs; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee9..9896286 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -829,7 +829,7 @@ static void ast_connector_destroy(struct drm_connector *connector) { struct ast_connector *ast_connector = to_ast_connector(connector); ast_i2c_destroy(ast_connector->i2c); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -871,7 +871,7 @@ static int ast_connector_init(struct drm_device *dev) connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); connector->polled = DRM_CONNECTOR_POLL_CONNECT; diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 98fd17a..d466696 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, } drm_connector_helper_add(&ptn_bridge->connector, &ptn3460_connector_helper_funcs); - drm_sysfs_connector_add(&ptn_bridge->connector); + drm_connector_register(&ptn_bridge->connector); drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); return 0; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fe94cc1..c50c827 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -921,6 +921,34 @@ void drm_connector_cleanup(struct drm_connector *connector) EXPORT_SYMBOL(drm_connector_cleanup); /** + * drm_connector_register - register a connector + * @connector: the connector to register + * + * Register userspace interfaces for a connector + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_register(struct drm_connector *connector) +{ + return drm_sysfs_connector_add(connector); +} +EXPORT_SYMBOL(drm_connector_register); + +/** + * drm_connector_unregister - unregister a connector + * @connector: the connector to unregister + * + * Unregister userspace interfaces for a connector + */ +void drm_connector_unregister(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); +} +EXPORT_SYMBOL(drm_connector_unregister); + + +/** * drm_connector_unplug_all - unregister connector userspace interfaces * @dev: drm device * @@ -934,7 +962,7 @@ void drm_connector_unplug_all(struct drm_device *dev) /* taking the mode config mutex ends up in a clash with sysfs */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); } EXPORT_SYMBOL(drm_connector_unplug_all); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 369b262..7827dad 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -438,7 +438,6 @@ err_out_files: out: return ret; } -EXPORT_SYMBOL(drm_sysfs_connector_add); /** * drm_sysfs_connector_remove - remove an connector device from sysfs @@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector) device_unregister(connector->kdev); connector->kdev = NULL; } -EXPORT_SYMBOL(drm_sysfs_connector_remove); /** * drm_sysfs_hotplug_event - generate a DRM uevent diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index a8ffc8c..86dc69d 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -1018,7 +1018,7 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 9a16dbe..25c7888 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -185,7 +185,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector) struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(exynos_connector); } @@ -230,7 +230,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, drm_connector_init(dev, connector, &exynos_connector_funcs, type); drm_connector_helper_add(connector, &exynos_connector_helper_funcs); - err = drm_sysfs_connector_add(connector); + err = drm_connector_register(connector); if (err) goto err_connector; @@ -250,7 +250,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, return connector; err_sysfs: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); err_connector: drm_connector_cleanup(connector); kfree(exynos_connector); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 482127f..4693531 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force) static void exynos_dpi_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6302aa6..2df3592 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1246,7 +1246,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 2fb8705..9528d81 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -562,7 +562,7 @@ static int vidi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &vidi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index c104d0c..dd565c4 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1129,7 +1129,7 @@ static int hdmi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index c18268c..248c33a 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -192,7 +192,7 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector) struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->ddc_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -304,7 +304,7 @@ void cdv_intel_crt_init(struct drm_device *dev, drm_connector_helper_add(connector, &cdv_intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_ddc: diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9ff30c2..a4cc0e6 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1713,7 +1713,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector) } } i2c_del_adapter(&intel_dp->adapter); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -1847,7 +1847,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); /* Set up the DDC bus. */ switch (output_reg) { diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index b99084b..4268bf2 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -248,7 +248,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector) if (gma_encoder->i2c_bus) psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -356,7 +356,7 @@ void cdv_hdmi_init(struct drm_device *dev, hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter); hdmi_priv->dev = dev; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_ddc: diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 8ecc920..0b77039 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -446,7 +446,7 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector) if (gma_encoder->i2c_bus) psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -774,7 +774,7 @@ void cdv_intel_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 6e91b20..abf2248 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -318,7 +318,7 @@ static void mdfld_dsi_connector_destroy(struct drm_connector *connector) if (!dsi_connector) return; - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); sender = dsi_connector->pkg_sender; mdfld_dsi_pkg_sender_destroy(sender); @@ -597,7 +597,7 @@ void mdfld_dsi_output_init(struct drm_device *dev, dsi_config->encoder = encoder; encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; /*TODO: add code to destroy outputs on error*/ diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index cf018dd..e6f5c62 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -665,7 +665,7 @@ void oaktrail_hdmi_init(struct drm_device *dev, connector->display_info.subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); dev_info(dev->dev, "HDMI initialised.\n"); return; diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 9b09946..0d39da6 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -404,7 +404,7 @@ void oaktrail_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index d7778d0..88aad95 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -563,7 +563,7 @@ void psb_intel_lvds_destroy(struct drm_connector *connector) if (lvds_priv->ddc_bus) psb_intel_i2c_destroy(lvds_priv->ddc_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -829,7 +829,7 @@ void psb_intel_lvds_init(struct drm_device *dev, */ out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index deeb082..0be96fd 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1682,7 +1682,7 @@ static void psb_intel_sdvo_destroy(struct drm_connector *connector) psb_intel_sdvo_connector->tv_format); psb_intel_sdvo_destroy_enhance_property(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -2071,7 +2071,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector, connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; gma_connector_attach_encoder(&connector->base, &encoder->base); - drm_sysfs_connector_add(&connector->base.base); + drm_connector_register(&connector->base.base); } static void diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 5a045d3..8da5ef9 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -869,7 +869,7 @@ void intel_crt_init(struct drm_device *dev) drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (!I915_HAS_HOTPLUG(dev)) intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index efd3cf5..dbabaec 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12234,7 +12234,7 @@ void intel_connector_unregister(struct intel_connector *intel_connector) struct drm_connector *connector = &intel_connector->base; intel_panel_destroy_backlight(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); } void intel_modeset_cleanup(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 52fda95..99f033f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4246,7 +4246,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, edp_panel_vdd_work); intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; @@ -4289,7 +4289,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, edp_panel_vdd_off_sync(intel_dp); drm_modeset_unlock(&dev->mode_config.connection_mutex); } - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); return false; } diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 02f99d7..7c07ee0 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -742,7 +742,7 @@ bool intel_dsi_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev); if (!fixed_mode) { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index a3631c0..3fb71a0 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -558,7 +558,7 @@ void intel_dvo_init(struct drm_device *dev) intel_dvo->panel_wants_dither = true; } - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index eee2bbe..0b60310 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1490,7 +1490,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi_add_properties(intel_hdmi, connector); intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written * 0xd. Failure to do so will result in spurious interrupts being diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 2312602..4d29a83 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -1097,7 +1097,7 @@ out: DRM_DEBUG_KMS("lid notifier registration failed\n"); lvds_connector->lid_notifier.notifier_call = NULL; } - drm_sysfs_connector_add(connector); + drm_connector_register(connector); intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); intel_panel_setup_backlight(connector); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6a4d5bc..e0be8ae 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2431,7 +2431,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, connector->base.unregister = intel_sdvo_connector_unregister; intel_connector_attach_encoder(&connector->base, &encoder->base); - ret = drm_sysfs_connector_add(drm_connector); + ret = drm_connector_register(drm_connector); if (ret < 0) goto err1; @@ -2444,7 +2444,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, return 0; err2: - drm_sysfs_connector_remove(drm_connector); + drm_connector_unregister(drm_connector); err1: drm_connector_cleanup(drm_connector); @@ -2557,7 +2557,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) return true; err: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); return false; } @@ -2636,7 +2636,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) return true; err: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); return false; } @@ -2709,7 +2709,7 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo) list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { if (intel_attached_encoder(connector) == &intel_sdvo->base) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); } } diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 67c6c9a..e211eef 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1680,5 +1680,5 @@ intel_tv_init(struct drm_device *dev) drm_object_attach_property(&connector->base, dev->mode_config.tv_bottom_margin_property, intel_tv->margin[TV_MARGIN_BOTTOM]); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a034ed4..f9fe390 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1621,7 +1621,7 @@ static struct drm_connector *mga_vga_init(struct drm_device *dev) drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); mga_connector->i2c = mgag200_i2c_create(dev); if (!mga_connector->i2c) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index e56a619..56c64c1 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -298,7 +298,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector) hdp_disable(hdmi_connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); hdmi_unreference(hdmi_connector->hdmi); @@ -408,7 +408,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi) connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); ret = hpd_enable(hdmi_connector); if (ret) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1fa222e..680f46d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -104,7 +104,7 @@ nouveau_connector_destroy(struct drm_connector *connector) struct nouveau_connector *nv_connector = nouveau_connector(connector); nouveau_event_ref(NULL, &nv_connector->hpd); kfree(nv_connector->edid); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); if (nv_connector->aux.transfer) drm_dp_aux_unregister(&nv_connector->aux); @@ -1236,6 +1236,6 @@ nouveau_connector_create(struct drm_device *dev, int index) INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; } diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 86f4ead..36bc5cc 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -130,7 +130,7 @@ static void omap_connector_destroy(struct drm_connector *connector) struct omap_dss_device *dssdev = omap_connector->dssdev; DBG("%s", omap_connector->dssdev->name); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(omap_connector); @@ -307,7 +307,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 5d7ea24..b8ced08 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -835,7 +835,7 @@ static void qxl_conn_destroy(struct drm_connector *connector) struct qxl_output *qxl_output = drm_connector_to_qxl_output(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(qxl_output); } @@ -902,7 +902,7 @@ static int qdev_output_init(struct drm_device *dev, int num_output) drm_object_attach_property(&connector->base, qdev->hotplug_mode_update_property, 0); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 933c5c3..5b1a611 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -737,7 +737,7 @@ static void radeon_connector_destroy(struct drm_connector *connector) if (radeon_connector->edid) kfree(radeon_connector->edid); kfree(radeon_connector->con_priv); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -2038,7 +2038,7 @@ radeon_add_atom_connector(struct drm_device *dev, connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (has_aux) radeon_dp_aux_init(radeon_connector); @@ -2199,5 +2199,5 @@ radeon_add_legacy_connector(struct drm_device *dev, } else connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 289048d..21426bd 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -64,7 +64,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -105,7 +105,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index ccfe64c..8af3944 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -32,7 +32,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { static void rcar_du_vga_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -70,7 +70,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index faf176b..47875de 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -692,7 +692,7 @@ static void shmob_drm_connector_destroy(struct drm_connector *connector) struct shmob_drm_connector *scon = to_shmob_connector(connector); shmob_drm_backlight_exit(scon); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -726,7 +726,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) goto err_cleanup; @@ -749,7 +749,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, err_backlight: shmob_drm_backlight_exit(&sdev->connector); err_sysfs: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); err_cleanup: drm_connector_cleanup(connector); return ret; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index a3e4f1e..446837e 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -105,7 +105,7 @@ static void drm_connector_clear(struct drm_connector *connector) static void tegra_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); drm_connector_clear(connector); } @@ -318,7 +318,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs); drm_mode_connector_attach_encoder(&output->connector, &output->encoder); - drm_sysfs_connector_add(&output->connector); + drm_connector_register(&output->connector); output->encoder.possible_crtcs = 0x3; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 86c6732..0c0bce7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -247,7 +247,7 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index 595068b..c578d14 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -261,7 +261,7 @@ static struct drm_connector *slave_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index c38b56b..5324dfd 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -261,7 +261,7 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index b44d548..dea38ab 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -124,7 +124,7 @@ static int udl_connector_set_property(struct drm_connector *connector, static void udl_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -154,7 +154,7 @@ int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); drm_connector_helper_add(connector, &udl_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8f3edc4..1a024e3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -75,7 +75,7 @@ void vmw_display_unit_cleanup(struct vmw_display_unit *du) vmw_surface_unreference(&du->cursor_surface); if (du->cursor_dmabuf) vmw_dmabuf_unreference(&du->cursor_dmabuf); - drm_sysfs_connector_remove(&du->connector); + drm_connector_unregister(&du->connector); drm_crtc_cleanup(&du->crtc); drm_encoder_cleanup(&du->encoder); drm_connector_cleanup(&du->connector); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b2b9bd2..15e185a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -371,7 +371,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_sysfs_connector_add(connector); + (void) drm_connector_register(connector); drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index a95d3a0..b295463 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -467,7 +467,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_sysfs_connector_add(connector); + (void) drm_connector_register(connector); drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index def8280..6f54ff4 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -202,7 +202,7 @@ static const struct file_operations imx_drm_driver_fops = { void imx_drm_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); @@ -293,10 +293,10 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) * userspace will expect to be able to access DRM at this point. */ list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret) { dev_err(drm->dev, - "[CONNECTOR:%d:%s] drm_sysfs_connector_add failed: %d\n", + "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", connector->base.id, connector->name, ret); goto err_unbind; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e..5512c99 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -878,6 +878,8 @@ extern int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type); +int drm_connector_register(struct drm_connector *connector); +void drm_connector_unregister(struct drm_connector *connector); extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ -- cgit v0.10.2 From 30f6570798f6c897df4f1f2c676d803728bfec27 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Wed, 18 Jun 2014 17:52:32 +0100 Subject: drm/debugfs: add a "force" file per connector Add a file to debugfs for each connector to enable modification of the "force" connector attribute. This allows connectors to be enabled or disabled for testing and debugging purposes. v2: Add stricter value checking and clean up debugfs_entry if file creation fails in drm_debugfs_connector_add. (David Herrmann) Signed-off-by: Thomas Wood Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c50c827..c304e56 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -881,6 +881,8 @@ int drm_connector_init(struct drm_device *dev, drm_object_attach_property(&connector->base, dev->mode_config.dpms_property, 0); + connector->debugfs_entry = NULL; + out_put: if (ret) drm_mode_object_put(dev, &connector->base); @@ -931,7 +933,19 @@ EXPORT_SYMBOL(drm_connector_cleanup); */ int drm_connector_register(struct drm_connector *connector) { - return drm_sysfs_connector_add(connector); + int ret; + + ret = drm_sysfs_connector_add(connector); + if (ret) + return ret; + + ret = drm_debugfs_connector_add(connector); + if (ret) { + drm_sysfs_connector_remove(connector); + return ret; + } + + return 0; } EXPORT_SYMBOL(drm_connector_register); @@ -944,6 +958,7 @@ EXPORT_SYMBOL(drm_connector_register); void drm_connector_unregister(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); + drm_debugfs_connector_remove(connector); } EXPORT_SYMBOL(drm_connector_unregister); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b4b51d4..8ab3f3e 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -237,5 +237,119 @@ int drm_debugfs_cleanup(struct drm_minor *minor) return 0; } +static int connector_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + const char *status; + + switch (connector->force) { + case DRM_FORCE_ON: + status = "on\n"; + break; + + case DRM_FORCE_ON_DIGITAL: + status = "digital\n"; + break; + + case DRM_FORCE_OFF: + status = "off\n"; + break; + + case DRM_FORCE_UNSPECIFIED: + status = "unspecified\n"; + break; + + default: + return 0; + } + + seq_puts(m, status); + + return 0; +} + +static int connector_open(struct inode *inode, struct file *file) +{ + struct drm_connector *dev = inode->i_private; + + return single_open(file, connector_show, dev); +} + +static ssize_t connector_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_connector *connector = m->private; + char buf[12]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (!strcmp(buf, "on")) + connector->force = DRM_FORCE_ON; + else if (!strcmp(buf, "digital")) + connector->force = DRM_FORCE_ON_DIGITAL; + else if (!strcmp(buf, "off")) + connector->force = DRM_FORCE_OFF; + else if (!strcmp(buf, "unspecified")) + connector->force = DRM_FORCE_UNSPECIFIED; + else + return -EINVAL; + + return len; +} + +static const struct file_operations drm_connector_fops = { + .owner = THIS_MODULE, + .open = connector_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = connector_write +}; + +int drm_debugfs_connector_add(struct drm_connector *connector) +{ + struct drm_minor *minor = connector->dev->primary; + struct dentry *root, *ent; + + if (!minor->debugfs_root) + return -1; + + root = debugfs_create_dir(connector->name, minor->debugfs_root); + if (!root) + return -ENOMEM; + + connector->debugfs_entry = root; + + /* force */ + ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, + &drm_connector_fops); + if (!ent) + goto error; + + return 0; + +error: + debugfs_remove_recursive(connector->debugfs_entry); + connector->debugfs_entry = NULL; + return -ENOMEM; +} + +void drm_debugfs_connector_remove(struct drm_connector *connector) +{ + if (!connector->debugfs_entry) + return; + + debugfs_remove_recursive(connector->debugfs_entry); + + connector->debugfs_entry = NULL; +} + #endif /* CONFIG_DEBUG_FS */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8af71a8..57ecc42 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1419,6 +1419,8 @@ extern int drm_debugfs_create_files(const struct drm_info_list *files, extern int drm_debugfs_remove_files(const struct drm_info_list *files, int count, struct drm_minor *minor); extern int drm_debugfs_cleanup(struct drm_minor *minor); +extern int drm_debugfs_connector_add(struct drm_connector *connector); +extern void drm_debugfs_connector_remove(struct drm_connector *connector); #else static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) @@ -1443,6 +1445,15 @@ static inline int drm_debugfs_cleanup(struct drm_minor *minor) { return 0; } + +static inline int drm_debugfs_connector_add(struct drm_connector *connector) +{ + return 0; +} +static inline void drm_debugfs_connector_remove(struct drm_connector *connector) +{ +} + #endif /* Info file support */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5512c99..d4b0a6a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -545,6 +545,8 @@ struct drm_connector { int audio_latency[2]; int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; + + struct dentry *debugfs_entry; }; /** -- cgit v0.10.2 From 4cf2b28146713d39f78bd62002a8ab00075c63b7 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Wed, 18 Jun 2014 17:52:33 +0100 Subject: drm/debugfs: add an "edid_override" file per connector Add a file to debugfs for each connector to allow the EDID to be overridden. v2: Copy ubuf before accessing it and reject invalid length data. (David Herrmann) Ensure override_edid is reset when a new EDID value is written. (David Herrmann) Fix the debugfs file permissions. (David Herrmann) Signed-off-by: Thomas Wood Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c304e56..7db6251 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3763,6 +3763,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct drm_device *dev = connector->dev; int ret, size; + /* ignore requests to set edid when overridden */ + if (connector->override_edid) + return 0; + if (connector->edid_blob_ptr) drm_property_destroy_blob(dev, connector->edid_blob_ptr); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 8ab3f3e..13bd429 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -35,6 +35,7 @@ #include #include #include +#include #if defined(CONFIG_DEBUG_FS) @@ -304,6 +305,67 @@ static ssize_t connector_write(struct file *file, const char __user *ubuf, return len; } +static int edid_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct drm_property_blob *edid = connector->edid_blob_ptr; + + if (connector->override_edid && edid) + seq_write(m, edid->data, edid->length); + + return 0; +} + +static int edid_open(struct inode *inode, struct file *file) +{ + struct drm_connector *dev = inode->i_private; + + return single_open(file, edid_show, dev); +} + +static ssize_t edid_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_connector *connector = m->private; + char *buf; + struct edid *edid; + int ret; + + buf = memdup_user(ubuf, len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + edid = (struct edid *) buf; + + if (len == 5 && !strncmp(buf, "reset", 5)) { + connector->override_edid = false; + ret = drm_mode_connector_update_edid_property(connector, NULL); + } else if (len < EDID_LENGTH || + EDID_LENGTH * (1 + edid->extensions) > len) + ret = -EINVAL; + else { + connector->override_edid = false; + ret = drm_mode_connector_update_edid_property(connector, edid); + if (!ret) + connector->override_edid = true; + } + + kfree(buf); + + return (ret) ? ret : len; +} + +static const struct file_operations drm_edid_fops = { + .owner = THIS_MODULE, + .open = edid_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = edid_write +}; + + static const struct file_operations drm_connector_fops = { .owner = THIS_MODULE, .open = connector_open, @@ -333,6 +395,12 @@ int drm_debugfs_connector_add(struct drm_connector *connector) if (!ent) goto error; + /* edid */ + ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, + connector, &drm_edid_fops); + if (!ent) + goto error; + return 0; error: diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d22676b..db7d250 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect count = drm_load_edid_firmware(connector); if (count == 0) #endif - count = (*connector_funcs->get_modes)(connector); + { + if (connector->override_edid) { + struct edid *edid = (struct edid *) connector->edid_blob_ptr->data; + + count = drm_add_edid_modes(connector, edid); + } else + count = (*connector_funcs->get_modes)(connector); + } if (count == 0 && connector->status == connector_status_connected) count = drm_add_modes_noedid(connector, 1024, 768); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d4b0a6a..857bbb1 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -533,6 +533,7 @@ struct drm_connector { /* forced on connector */ enum drm_connector_force force; + bool override_edid; uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; struct drm_encoder *encoder; /* currently active encoder */ -- cgit v0.10.2 From 30bfa4eec7e813825f7537149eb394605c41dbe8 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 19 Jun 2014 15:14:37 +0800 Subject: drm/fb-helper: Redundant info->fix.type_aux setting in drm_fb_helper_fill_fix() The variable info->fix.type_aux is set to zero twice in the function drm_fb_helper_fill_fix(). This patch removes one redundant. Signed-off-by: Liu Ying Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d5d8cea..7ff98063 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1056,7 +1056,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, info->fix.ypanstep = 1; /* doing it in hw */ info->fix.ywrapstep = 0; info->fix.accel = FB_ACCEL_NONE; - info->fix.type_aux = 0; info->fix.line_length = pitch; return; -- cgit v0.10.2 From 77c70c56670875207d7ffd3c83cba7357ff4ff2e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 13:59:02 +0200 Subject: drm/i915: Drop unecessary complexity from psr_inactivate It's not needed and further more will get in the way of a sane locking scheme - psr_exit _can't_ take modeset locks due to lock inversion, and at least once dp mst hits the connector list is no longer static. But since we track all state in dev_priv->psr there is no need at all. Cc: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 912e9c4..74e194d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1910,29 +1910,11 @@ static void intel_edp_psr_work(struct work_struct *work) static void intel_edp_psr_inactivate(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_connector *connector; - struct intel_encoder *encoder; - struct intel_crtc *intel_crtc; - struct intel_dp *intel_dp = NULL; - - list_for_each_entry(connector, &dev->mode_config.connector_list, - base.head) { - if (connector->base.dpms != DRM_MODE_DPMS_ON) - continue; - - encoder = to_intel_encoder(connector->base.encoder); - if (encoder->type == INTEL_OUTPUT_EDP) { + dev_priv->psr.active = false; - intel_dp = enc_to_intel_dp(&encoder->base); - intel_crtc = to_intel_crtc(encoder->base.crtc); - - dev_priv->psr.active = false; - - I915_WRITE(EDP_PSR_CTL(dev), I915_READ(EDP_PSR_CTL(dev)) - & ~EDP_PSR_ENABLE); - } - } + I915_WRITE(EDP_PSR_CTL(dev), I915_READ(EDP_PSR_CTL(dev)) + & ~EDP_PSR_ENABLE); } void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back) -- cgit v0.10.2 From e6e559d4a90b539b711d8b7a70b7673031c48191 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 13:59:03 +0200 Subject: drm/i915: Ditch intel_edp_psr_update We have _enable/_disable interfaces now for the modeset sequence and intel_edp_psr_exit for workarounds. The callsites in intel_display.c are all redundant with the modeset sequence enable/disable calls in intel_ddi.c. The one in intel_sprite.c is real and needs to be switched to psr_exit. If this breaks anything then we need to augment the enable/disable functions accordingly. Cc: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5e8e711..88d9d81 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2763,7 +2763,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); return 0; @@ -3943,7 +3942,6 @@ static void intel_crtc_enable_planes(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); } @@ -4236,7 +4234,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); } @@ -4284,7 +4281,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); } @@ -4836,7 +4832,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 74e194d..b373b89 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1875,19 +1875,6 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) dev_priv->psr.enabled = false; } -void intel_edp_psr_update(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (!HAS_PSR(dev)) - return; - - if (!dev_priv->psr.setup_done) - return; - - intel_edp_psr_exit(dev, true); -} - static void intel_edp_psr_work(struct work_struct *work) { struct drm_i915_private *dev_priv = diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ab5962b..e92354c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -834,7 +834,6 @@ void intel_edp_panel_on(struct intel_dp *intel_dp); void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); -void intel_edp_psr_update(struct drm_device *dev); void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back); void intel_edp_psr_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 404335d..2a211c6 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1051,7 +1051,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); } - intel_edp_psr_update(dev); + intel_edp_psr_exit(dev, true); return 0; } -- cgit v0.10.2 From 3108e99ea94fa1cb80c08ebcdcf60e8dea718438 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 13:59:05 +0200 Subject: drm/i915: Drop schedule_back from psr_exit It doesn't make sense to never again schedule the work, since by the time we might want to re-enable psr the world might have changed and we can do it again. The only exception is when we shut down the pipe, but that's an entirely different thing and needs to be handled in psr_disable. Note that later patch will again split psr_exit into psr_invalidate and psr_flush. But the split is different and this simplification helps with the transition. v2: Improve the commit message a bit. Cc: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d857f58..903f8f5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1395,7 +1395,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, goto unlock; } - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); /* Try to flush the object off the GPU without holding the lock. * We will repeat the flush holding the lock in the normal manner @@ -1442,7 +1442,7 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { @@ -4236,7 +4236,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 88d9d81..548161d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8820,7 +8820,7 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); if (!i915.powersave) return; @@ -9430,7 +9430,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, return -ENOMEM; /* Exit PSR early in page flip */ - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); work->event = event; work->crtc = crtc; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b373b89..23b62b0 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1904,7 +1904,7 @@ static void intel_edp_psr_inactivate(struct drm_device *dev) & ~EDP_PSR_ENABLE); } -void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back) +void intel_edp_psr_exit(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1919,9 +1919,8 @@ void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back) if (dev_priv->psr.active) intel_edp_psr_inactivate(dev); - if (schedule_back) - schedule_delayed_work(&dev_priv->psr.work, - msecs_to_jiffies(100)); + schedule_delayed_work(&dev_priv->psr.work, + msecs_to_jiffies(100)); } void intel_edp_psr_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e92354c..5d20f71 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -835,7 +835,7 @@ void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); -void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back); +void intel_edp_psr_exit(struct drm_device *dev); void intel_edp_psr_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 2a211c6..9038e2a 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1051,7 +1051,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); } - intel_edp_psr_exit(dev, true); + intel_edp_psr_exit(dev); return 0; } -- cgit v0.10.2 From a071fa00647bc9a3c53f917b236fff9aea175e3a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 23:28:09 +0200 Subject: drm/i915: Introduce accurate frontbuffer tracking So from just a quick look we seem to have enough information to accurately figure out whether a given gem bo is used as a frontbuffer and where exactly: We have obj->pin_count as a first check with no false negatives and only negligible false positives. And then we can just walk the modeset objects and figure out where exactly a buffer is used as scanout. Except that we can't due to locking order: If we already hold dev->struct_mutex we can't acquire any modeset locks, so could potential chase freed pointers and other evil stuff. So we need something else. For that introduce a new set of bits obj->frontbuffer_bits to track where a buffer object is used. That we can then chase without grabbing any modeset locks. Of course the consumers of this (DRRS, PSR, FBC, ...) still need to be able to do their magic both when called from modeset and from gem code. But that can be easily achieved by adding locks for these specific subsystems which always nest within either kms or gem locking. This patch just adds the relevant update code to all places. Note that if we ever support multi-planar scanout targets then we need one frontbuffer tracking bit per attachment point that we expose to userspace. v2: - Fix more oopsen. Oops. - WARN if we leak obj->frontbuffer_bits when freeing a gem buffer. Fix the bugs this brought to light. - s/update_frontbuffer_bits/update_fb_bits/. More consistent with the fb tracking functions (fb for gem object, frontbuffer for raw bits). And the function name was way too long. v3: Size obj->frontbuffer_bits correctly so that all pipes fit in. v4: Don't update fb bits in set_base on failure. Noticed by Chris. v5: s/i915_gem_update_fb_bits/i915_gem_track_fb/ Also remove a few local enum pipe variables which are now no longer needed to make the function arguments no drop over the 80 char limit. Cc: Rodrigo Vivi Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4fea8a0..264de5b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1593,6 +1593,26 @@ struct drm_i915_gem_object_ops { void (*release)(struct drm_i915_gem_object *); }; +/* + * Frontbuffer tracking bits. Set in obj->frontbuffer_bits while a gem bo is + * considered to be the frontbuffer for the given plane interface-vise. This + * doesn't mean that the hw necessarily already scans it out, but that any + * rendering (by the cpu or gpu) will land in the frontbuffer eventually. + * + * We have one bit per pipe and per scanout plane type. + */ +#define INTEL_FRONTBUFFER_BITS_PER_PIPE 4 +#define INTEL_FRONTBUFFER_BITS \ + (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES) +#define INTEL_FRONTBUFFER_PRIMARY(pipe) \ + (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))) +#define INTEL_FRONTBUFFER_CURSOR(pipe) \ + (1 << (1 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))) +#define INTEL_FRONTBUFFER_SPRITE(pipe) \ + (1 << (2 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))) +#define INTEL_FRONTBUFFER_OVERLAY(pipe) \ + (1 << (3 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))) + struct drm_i915_gem_object { struct drm_gem_object base; @@ -1680,6 +1700,8 @@ struct drm_i915_gem_object { unsigned int has_global_gtt_mapping:1; unsigned int has_dma_mapping:1; + unsigned int frontbuffer_bits:INTEL_FRONTBUFFER_BITS; + struct sg_table *pages; int pages_pin_count; @@ -1726,6 +1748,10 @@ struct drm_i915_gem_object { }; #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base) +void i915_gem_track_fb(struct drm_i915_gem_object *old, + struct drm_i915_gem_object *new, + unsigned frontbuffer_bits); + /** * Request queue structure. * diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 903f8f5..caed662 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4452,6 +4452,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) if (obj->stolen) i915_gem_object_unpin_pages(obj); + WARN_ON(obj->frontbuffer_bits); + if (WARN_ON(obj->pages_pin_count)) obj->pages_pin_count = 0; if (discard_backing_storage(obj)) @@ -4996,6 +4998,23 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file) return ret; } +void i915_gem_track_fb(struct drm_i915_gem_object *old, + struct drm_i915_gem_object *new, + unsigned frontbuffer_bits) +{ + if (old) { + WARN_ON(!mutex_is_locked(&old->base.dev->struct_mutex)); + WARN_ON(!(old->frontbuffer_bits & frontbuffer_bits)); + old->frontbuffer_bits &= ~frontbuffer_bits; + } + + if (new) { + WARN_ON(!mutex_is_locked(&new->base.dev->struct_mutex)); + WARN_ON(new->frontbuffer_bits & frontbuffer_bits); + new->frontbuffer_bits |= frontbuffer_bits; + } +} + static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) { if (!mutex_is_locked(mutex)) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 548161d..ae24829f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2351,6 +2351,7 @@ static bool intel_alloc_plane_obj(struct intel_crtc *crtc, goto out_unref_obj; } + obj->frontbuffer_bits = INTEL_FRONTBUFFER_PRIMARY(crtc->pipe); mutex_unlock(&dev->struct_mutex); DRM_DEBUG_KMS("plane fb obj %p\n", obj); @@ -2396,6 +2397,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc, if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) { drm_framebuffer_reference(c->primary->fb); intel_crtc->base.primary->fb = c->primary->fb; + fb->obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); break; } } @@ -2684,7 +2686,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum pipe pipe = intel_crtc->pipe; struct drm_framebuffer *old_fb; + struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; int ret; if (intel_crtc_has_pending_flip(crtc)) { @@ -2705,10 +2709,13 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return -EINVAL; } + old_fb = crtc->primary->fb; + mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, - to_intel_framebuffer(fb)->obj, - NULL); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + if (ret == 0) + i915_gem_track_fb(to_intel_framebuffer(old_fb)->obj, obj, + INTEL_FRONTBUFFER_PRIMARY(pipe)); mutex_unlock(&dev->struct_mutex); if (ret != 0) { DRM_ERROR("pin & fence failed\n"); @@ -2748,7 +2755,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, dev_priv->display.update_primary_plane(crtc, fb, x, y); - old_fb = crtc->primary->fb; crtc->primary->fb = fb; crtc->x = x; crtc->y = y; @@ -4922,6 +4928,8 @@ static void intel_crtc_disable(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *old_obj; + enum pipe pipe = to_intel_crtc(crtc)->pipe; /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); @@ -4931,12 +4939,15 @@ static void intel_crtc_disable(struct drm_crtc *crtc) dev_priv->display.off(crtc); assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); - assert_cursor_disabled(dev_priv, to_intel_crtc(crtc)->pipe); - assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe); + assert_cursor_disabled(dev_priv, pipe); + assert_pipe_disabled(dev->dev_private, pipe); if (crtc->primary->fb) { + old_obj = to_intel_framebuffer(crtc->primary->fb)->obj; mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(to_intel_framebuffer(crtc->primary->fb)->obj); + intel_unpin_fb_obj(old_obj); + i915_gem_track_fb(old_obj, NULL, + INTEL_FRONTBUFFER_PRIMARY(pipe)); mutex_unlock(&dev->struct_mutex); crtc->primary->fb = NULL; } @@ -8103,6 +8114,7 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum pipe pipe = intel_crtc->pipe; unsigned old_width; uint32_t addr; int ret; @@ -8182,6 +8194,8 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, i915_gem_object_unpin_from_display_plane(intel_crtc->cursor_bo); } + i915_gem_track_fb(intel_crtc->cursor_bo, obj, + INTEL_FRONTBUFFER_CURSOR(pipe)); mutex_unlock(&dev->struct_mutex); old_width = intel_crtc->cursor_width; @@ -9404,6 +9418,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *old_fb = crtc->primary->fb; struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum pipe pipe = intel_crtc->pipe; struct intel_unpin_work *work; struct intel_engine_cs *ring; unsigned long flags; @@ -9475,7 +9490,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev)) - work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(intel_crtc->pipe)) + 1; + work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(pipe)) + 1; if (IS_VALLEYVIEW(dev)) { ring = &dev_priv->ring[BCS]; @@ -9503,6 +9518,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (ret) goto cleanup_unpin; + i915_gem_track_fb(work->old_fb_obj, obj, + INTEL_FRONTBUFFER_PRIMARY(pipe)); + intel_disable_fbc(dev); intel_mark_fb_busy(obj, NULL); mutex_unlock(&dev->struct_mutex); @@ -9534,7 +9552,7 @@ out_hang: intel_crtc_wait_for_pending_flips(crtc); ret = intel_pipe_set_base(crtc, crtc->x, crtc->y, fb); if (ret == 0 && event) - drm_send_vblank_event(dev, intel_crtc->pipe, event); + drm_send_vblank_event(dev, pipe, event); } return ret; } @@ -10569,10 +10587,13 @@ static int __intel_set_mode(struct drm_crtc *crtc, */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { struct drm_framebuffer *old_fb; + struct drm_i915_gem_object *old_obj = NULL; + struct drm_i915_gem_object *obj = + to_intel_framebuffer(fb)->obj; mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, - to_intel_framebuffer(fb)->obj, + obj, NULL); if (ret != 0) { DRM_ERROR("pin & fence failed\n"); @@ -10580,8 +10601,12 @@ static int __intel_set_mode(struct drm_crtc *crtc, goto done; } old_fb = crtc->primary->fb; - if (old_fb) - intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); + if (old_fb) { + old_obj = to_intel_framebuffer(old_fb)->obj; + intel_unpin_fb_obj(old_obj); + } + i915_gem_track_fb(old_obj, obj, + INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); mutex_unlock(&dev->struct_mutex); crtc->primary->fb = fb; @@ -11196,8 +11221,9 @@ intel_primary_plane_disable(struct drm_plane *plane) intel_crtc_wait_for_pending_flips(plane->crtc); intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, intel_plane->pipe); - disable_unpin: + i915_gem_track_fb(to_intel_framebuffer(plane->fb)->obj, NULL, + INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); plane->fb = NULL; @@ -11215,6 +11241,7 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); + struct drm_i915_gem_object *obj, *old_obj = NULL; struct drm_rect dest = { /* integer pixels */ .x1 = crtc_x, @@ -11246,6 +11273,10 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, if (ret) return ret; + if (plane->fb) + old_obj = to_intel_framebuffer(plane->fb)->obj; + obj = to_intel_framebuffer(fb)->obj; + /* * If the CRTC isn't enabled, we're just pinning the framebuffer, * updating the fb pointer, and returning without touching the @@ -11258,12 +11289,13 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, * we may have an fb pinned; unpin it. */ if (plane->fb) - intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + intel_unpin_fb_obj(old_obj); + + i915_gem_track_fb(old_obj, obj, + INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); /* Pin and return without programming hardware */ - return intel_pin_and_fence_fb_obj(dev, - to_intel_framebuffer(fb)->obj, - NULL); + return intel_pin_and_fence_fb_obj(dev, obj, NULL); } intel_crtc_wait_for_pending_flips(crtc); @@ -11280,13 +11312,14 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, * fail. */ if (plane->fb != fb) { - ret = intel_pin_and_fence_fb_obj(dev, - to_intel_framebuffer(fb)->obj, - NULL); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); if (ret) return ret; } + i915_gem_track_fb(old_obj, obj, + INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); + if (intel_crtc->primary_enabled) intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, @@ -11295,7 +11328,7 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, if (plane->fb != fb) if (plane->fb) - intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + intel_unpin_fb_obj(old_obj); return 0; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index daa1189..99b6c14 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -415,6 +415,10 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) } intel_overlay_release_old_vid_tail(overlay); + + + i915_gem_track_fb(overlay->old_vid_bo, NULL, + INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe)); return 0; } @@ -686,6 +690,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, bool scale_changed = false; struct drm_device *dev = overlay->dev; u32 swidth, swidthsw, sheight, ostride; + enum pipe pipe = overlay->crtc->pipe; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); @@ -713,7 +718,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, oconfig = OCONF_CC_OUT_8BIT; if (IS_GEN4(overlay->dev)) oconfig |= OCONF_CSC_MODE_BT709; - oconfig |= overlay->crtc->pipe == 0 ? + oconfig |= pipe == 0 ? OCONF_PIPE_A : OCONF_PIPE_B; iowrite32(oconfig, ®s->OCONFIG); intel_overlay_unmap_regs(overlay, regs); @@ -776,6 +781,9 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret) goto out_unpin; + i915_gem_track_fb(overlay->vid_bo, new_bo, + INTEL_FRONTBUFFER_OVERLAY(pipe)); + overlay->old_vid_bo = overlay->vid_bo; overlay->vid_bo = new_bo; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 9038e2a..140bd83 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -811,6 +811,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_device *dev = plane->dev; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); + enum pipe pipe = intel_crtc->pipe; struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj = intel_fb->obj; struct drm_i915_gem_object *old_obj = intel_plane->obj; @@ -998,6 +999,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, */ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + i915_gem_track_fb(old_obj, obj, + INTEL_FRONTBUFFER_SPRITE(pipe)); mutex_unlock(&dev->struct_mutex); if (ret) @@ -1062,6 +1065,7 @@ intel_disable_plane(struct drm_plane *plane) struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); struct intel_crtc *intel_crtc; + enum pipe pipe; if (!plane->fb) return 0; @@ -1070,6 +1074,7 @@ intel_disable_plane(struct drm_plane *plane) return -EINVAL; intel_crtc = to_intel_crtc(plane->crtc); + pipe = intel_crtc->pipe; if (intel_crtc->active) { bool primary_was_enabled = intel_crtc->primary_enabled; @@ -1088,6 +1093,8 @@ intel_disable_plane(struct drm_plane *plane) mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(intel_plane->obj); + i915_gem_track_fb(intel_plane->obj, NULL, + INTEL_FRONTBUFFER_SPRITE(pipe)); mutex_unlock(&dev->struct_mutex); intel_plane->obj = NULL; -- cgit v0.10.2 From d5a81ef187043eac6642d7f7584f896a33b68c7c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 14:46:49 +0200 Subject: drm/i915: Print obj->frontbuffer_bits in debugfs output Can be useful to figure out imbalances and bugs in the frontbuffer tracking. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 76c2572..6b7b32b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -170,6 +170,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) } if (obj->ring != NULL) seq_printf(m, " (%s)", obj->ring->name); + if (obj->frontbuffer_bits) + seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits); } static void describe_ctx(struct seq_file *m, struct intel_context *ctx) -- cgit v0.10.2 From 2a8c6edfd2a888085ae0ddaf6c3ea3f529fdc4e8 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 19 Jun 2014 16:01:44 +0800 Subject: drm/fb-helper: Remove unnecessary list empty check in drm_fb_helper_debug_enter() The following list empty check is unnecessary because we would still do nothing real and return 'val' if my_list is empty. if (list_empty(&my_list)) return val; list_for_each_entry(pos, &my_list, member) { ... } return val; This patch removes the unnecessary check in drm_fb_helper_debug_enter(). Signed-off-by: Liu Ying Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7ff98063..5aab5ab 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -199,9 +199,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info) struct drm_crtc_helper_funcs *funcs; int i; - if (list_empty(&kernel_fb_helper_list)) - return false; - list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { for (i = 0; i < helper->crtc_count; i++) { struct drm_mode_set *mode_set = -- cgit v0.10.2 From e991077ec67e08bd345fcee4f810e59740359da5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 15:05:19 +0200 Subject: drm/i915: Properly track domain of the fbcon fb X could end up putting the fbcon fb into other domains, e.g. for smooth take-overs. Also we want this for accurate frontbuffer tracking: The set_config is an implicit flush and will re-enable psr and similar features, so we need to bring the bo back into the gtt domain. v2: Add FIXME comment about fbcon locking fun in atomic context, requested by Chris. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 27975c3..226fbc7 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -43,10 +43,36 @@ #include #include "i915_drv.h" +static int intel_fbdev_set_par(struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct intel_fbdev *ifbdev = + container_of(fb_helper, struct intel_fbdev, helper); + int ret; + + ret = drm_fb_helper_set_par(info); + + if (ret == 0) { + /* + * FIXME: fbdev presumes that all callbacks also work from + * atomic contexts and relies on that for emergency oops + * printing. KMS totally doesn't do that and the locking here is + * by far not the only place this goes wrong. Ignore this for + * now until we solve this for real. + */ + mutex_lock(&fb_helper->dev->struct_mutex); + ret = i915_gem_object_set_to_gtt_domain(ifbdev->fb->obj, + true); + mutex_unlock(&fb_helper->dev->struct_mutex); + } + + return ret; +} + static struct fb_ops intelfb_ops = { .owner = THIS_MODULE, .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, + .fb_set_par = intel_fbdev_set_par, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, -- cgit v0.10.2 From e11aa362308f5de467ce355a2a2471321b15a35c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 18 Jun 2014 09:52:55 -0700 Subject: drm/i915: use runtime irq suspend/resume in freeze/thaw We don't need to uninstall the full handler, simply disabling interrupts ought to be enough. Signed-off-by: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7ae4e2a..be97741 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -522,7 +522,7 @@ static int i915_drm_freeze(struct drm_device *dev) return error; } - drm_irq_uninstall(dev); + intel_runtime_pm_disable_interrupts(dev); dev_priv->enable_hotplug_processing = false; intel_suspend_gt_powersave(dev); @@ -646,8 +646,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) } mutex_unlock(&dev->struct_mutex); - /* We need working interrupts for modeset enabling ... */ - drm_irq_install(dev, dev->pdev->irq); + intel_runtime_pm_restore_interrupts(dev); intel_modeset_init_hw(dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8148d06..1cce0a5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4880,7 +4880,7 @@ void intel_suspend_gt_powersave(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* Interrupts should be disabled already to avoid re-arming. */ - WARN_ON(dev->irq_enabled); + WARN_ON(dev->irq_enabled && !dev_priv->pm.irqs_disabled); flush_delayed_work(&dev_priv->rps.delayed_resume_work); @@ -4892,7 +4892,7 @@ void intel_disable_gt_powersave(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* Interrupts should be disabled already to avoid re-arming. */ - WARN_ON(dev->irq_enabled); + WARN_ON(dev->irq_enabled && !dev_priv->pm.irqs_disabled); if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); -- cgit v0.10.2 From 395a5abbd97d7d76a7a26da52f33daebe279b3b3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 18 Jun 2014 09:52:56 -0700 Subject: drm/i915: don't take runtime PM reference around freeze/thaw We should be taking the right power well refs these days, so this shouldn't be necessary. It also gets in the way of re-using these routines for S0iX states, as those need all the power saving features enabled. Signed-off-by: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index be97741..6eb45ac 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -496,8 +496,6 @@ static int i915_drm_freeze(struct drm_device *dev) struct drm_crtc *crtc; pci_power_t opregion_target_state; - intel_runtime_pm_get(dev_priv); - /* ignore lid events during suspend */ mutex_lock(&dev_priv->modeset_restore_lock); dev_priv->modeset_restore = MODESET_SUSPENDED; @@ -686,7 +684,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) intel_opregion_notify_adapter(dev, PCI_D0); - intel_runtime_pm_put(dev_priv); return 0; } -- cgit v0.10.2 From cc36513ca36a03e4fd8693d127bac5fe48185786 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jun 2014 13:59:13 +0200 Subject: drm/i915: Use new frontbuffer bits to increase pll clock The downclocking checks a few more things, so not that simple to convert. Also, this should get unified with the drrs handling and also use the locking of that. Otoh the drrs locking is about as hapzardous as no locking, at least on first sight. For easier conversion ditch the upclocking on unload - we'll turn off everything anyway. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 264de5b..17e90b3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1612,6 +1612,8 @@ struct drm_i915_gem_object_ops { (1 << (2 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))) #define INTEL_FRONTBUFFER_OVERLAY(pipe) \ (1 << (3 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))) +#define INTEL_FRONTBUFFER_ALL_MASK(pipe) \ + (0xf << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))) struct drm_i915_gem_object { struct drm_gem_object base; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ae24829f..9a3fa90 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -76,7 +76,8 @@ static const uint32_t intel_cursor_formats[] = { #define DIV_ROUND_CLOSEST_ULL(ll, d) \ ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) -static void intel_increase_pllclock(struct drm_crtc *crtc); +static void intel_increase_pllclock(struct drm_device *dev, + enum pipe pipe); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); static void i9xx_crtc_clock_get(struct intel_crtc *crtc, @@ -2585,7 +2586,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, if (dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); - intel_increase_pllclock(crtc); + intel_increase_pllclock(dev, to_intel_crtc(crtc)->pipe); dev_priv->display.update_primary_plane(crtc, fb, x, y); @@ -8721,12 +8722,10 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, return mode; } -static void intel_increase_pllclock(struct drm_crtc *crtc) +static void intel_increase_pllclock(struct drm_device *dev, + enum pipe pipe) { - struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; int dpll_reg = DPLL(pipe); int dpll; @@ -8832,21 +8831,19 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj, struct intel_engine_cs *ring) { struct drm_device *dev = obj->base.dev; - struct drm_crtc *crtc; + enum pipe pipe; intel_edp_psr_exit(dev); if (!i915.powersave) return; - for_each_crtc(dev, crtc) { - if (!crtc->primary->fb) - continue; - - if (to_intel_framebuffer(crtc->primary->fb)->obj != obj) + for_each_pipe(pipe) { + if (!(obj->frontbuffer_bits & + INTEL_FRONTBUFFER_ALL_MASK(pipe))) continue; - intel_increase_pllclock(crtc); + intel_increase_pllclock(dev, pipe); if (ring && intel_fbc_enabled(dev)) ring->fbc_dirty = true; } @@ -12796,7 +12793,6 @@ void intel_connector_unregister(struct intel_connector *intel_connector) void intel_modeset_cleanup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc; struct drm_connector *connector; /* @@ -12816,14 +12812,6 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_unregister_dsm_handler(); - for_each_crtc(dev, crtc) { - /* Skip inactive CRTCs */ - if (!crtc->primary->fb) - continue; - - intel_increase_pllclock(crtc); - } - intel_disable_fbc(dev); intel_disable_gt_powersave(dev); -- cgit v0.10.2 From f99d70690e0755696e4e68404c8993431efda469 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 19 Jun 2014 16:01:59 +0200 Subject: drm/i915: Track frontbuffer invalidation/flushing So these are the guts of the new beast. This tracks when a frontbuffer gets invalidated (due to frontbuffer rendering) and hence should be constantly scaned out, and when it's flushed again and can be compressed/one-shot-upload. Rules for flushing are simple: The frontbuffer needs one more full upload starting from the next vblank. Which means that the flushing can _only_ be called once the frontbuffer update has been latched. But this poses a problem for pageflips: We can't just delay the flushing until the pageflip is latched, since that would pose the risk that we override frontbuffer rendering that has been scheduled in-between the pageflip ioctl and the actual latching. To handle this track asynchronous invalidations (and also pageflip) state per-ring and delay any in-between flushing until the rendering has completed. And also cancel any delayed flushing if we get a new invalidation request (whether delayed or not). Also call intel_mark_fb_busy in both cases in all cases to make sure that we keep the screen at the highest refresh rate both on flips, synchronous plane updates and for frontbuffer rendering. v2: Lots of improvements Suggestions from Chris: - Move invalidate/flush in flush_*_domain and set_to_*_domain. - Drop the flush in busy_ioctl since it's redundant. Was a leftover from an earlier concept to track flips/delayed flushes. - Don't forget about the initial modeset enable/final disable. Suggested by Chris. Track flips accurately, too. Since flips complete independently of rendering we need to track pending flips in a separate mask. Again if an invalidate happens we need to cancel the evenutal flush to avoid races. v3: Provide correct header declarations for flip functions. Currently not needed outside of intel_display.c, but part of the proper interface. v4: Add proper domain management to fbcon so that the fbcon buffer is also tracked correctly. v5: Fixup locking around the fbcon set_to_gtt_domain call. v6: More comments from Chris: - Split out fbcon changes. - Drop superflous checks for potential scanout before calling intel_fb functions - we can micro-optimize this later. - s/intel_fb_/intel_fb_obj_/ to make it clear that this deals in gem object. We already have precedence for fb_obj in the pin_and_fence functions. v7: Clarify the semantics of the flip flush handling by renaming things a bit: - Don't go through a gem object but take the relevant frontbuffer bits directly. These functions center on the plane, the actual object is irrelevant - even a flip to the same object as already active should cause a flush. - Add a new intel_frontbuffer_flip for synchronous plane updates. It currently just calls intel_frontbuffer_flush since the implemenation differs. This way we achieve a clear split between one-shot update events on one side and frontbuffer rendering with potentially a very long delay between the invalidate and flush. Chris and I also had some discussions about mark_busy and whether it is appropriate to call from flush. But mark busy is a state which should be derived from the 3 events (invalidate, flush, flip) we now have by the users, like psr does by tracking relevant information in psr.busy_frontbuffer_bits. DRRS (the only real use of mark_busy for frontbuffer) needs to have similar logic. With that the overall mark_busy in the core could be removed. v8: Only when retiring gpu buffers only flush frontbuffer bits we actually invalidated in a batch. Just for safety since before any additional usage/invalidate we should always retire current rendering. Suggested by Chris Wilson. v9: Actually use intel_frontbuffer_flip in all appropriate places. Spotted by Chris. v10: Address more comments from Chris: - Don't call _flip in set_base when the crtc is inactive, avoids redunancy in the modeset case with the initial enabling of all planes. - Add comments explaining that the initial/final plane enable/disable still has work left to do before it's fully generic. v11: Only invalidate for gtt/cpu access when writing. Spotted by Chris. v12: s/_flush/_flip/ in intel_overlay.c per Chris' comment. Cc: Rodrigo Vivi Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 17e90b3..aa849f2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1330,6 +1330,17 @@ struct intel_pipe_crc { wait_queue_head_t wq; }; +struct i915_frontbuffer_tracking { + struct mutex lock; + + /* + * Tracking bits for delayed frontbuffer flushing du to gpu activity or + * scheduled flips. + */ + unsigned busy_bits; + unsigned flip_bits; +}; + struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -1476,6 +1487,9 @@ struct drm_i915_private { bool lvds_downclock_avail; /* indicates the reduced downclock for LVDS*/ int lvds_downclock; + + struct i915_frontbuffer_tracking fb_tracking; + u16 orig_clock; bool mchbar_need_disable; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index caed662..f6d1238 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1395,8 +1395,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, goto unlock; } - intel_edp_psr_exit(dev); - /* Try to flush the object off the GPU without holding the lock. * We will repeat the flush holding the lock in the normal manner * to catch cases where we are gazumped. @@ -1442,8 +1440,6 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - intel_edp_psr_exit(dev); - obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { ret = -ENOENT; @@ -2223,6 +2219,8 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) list_move_tail(&vma->mm_list, &vm->inactive_list); } + intel_fb_obj_flush(obj, true); + list_del_init(&obj->ring_list); obj->ring = NULL; @@ -3552,6 +3550,8 @@ i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj) old_write_domain = obj->base.write_domain; obj->base.write_domain = 0; + intel_fb_obj_flush(obj, false); + trace_i915_gem_object_change_domain(obj, obj->base.read_domains, old_write_domain); @@ -3573,6 +3573,8 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj, old_write_domain = obj->base.write_domain; obj->base.write_domain = 0; + intel_fb_obj_flush(obj, false); + trace_i915_gem_object_change_domain(obj, obj->base.read_domains, old_write_domain); @@ -3626,6 +3628,9 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) obj->dirty = 1; } + if (write) + intel_fb_obj_invalidate(obj, NULL); + trace_i915_gem_object_change_domain(obj, old_read_domains, old_write_domain); @@ -3962,6 +3967,9 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write) obj->base.write_domain = I915_GEM_DOMAIN_CPU; } + if (write) + intel_fb_obj_invalidate(obj, NULL); + trace_i915_gem_object_change_domain(obj, old_read_domains, old_write_domain); @@ -4236,8 +4244,6 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - intel_edp_psr_exit(dev); - obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { ret = -ENOENT; @@ -4937,6 +4943,8 @@ i915_gem_load(struct drm_device *dev) dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom; register_oom_notifier(&dev_priv->mm.oom_notifier); + + mutex_init(&dev_priv->fb_tracking.lock); } void i915_gem_release(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 93d7f72..d815ef5 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -975,10 +975,8 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas, if (obj->base.write_domain) { obj->dirty = 1; obj->last_write_seqno = intel_ring_get_seqno(ring); - /* check for potential scanout */ - if (i915_gem_obj_ggtt_bound(obj) && - i915_gem_obj_to_ggtt(obj)->pin_count) - intel_mark_fb_busy(obj, ring); + + intel_fb_obj_invalidate(obj, ring); /* update for the implicit flush after a batch */ obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9a3fa90..ff63367 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2756,6 +2756,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, dev_priv->display.update_primary_plane(crtc, fb, x, y); + if (intel_crtc->active) + intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_PRIMARY(pipe)); + crtc->primary->fb = fb; crtc->x = x; crtc->y = y; @@ -3950,6 +3953,13 @@ static void intel_crtc_enable_planes(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); + + /* + * FIXME: Once we grow proper nuclear flip support out of this we need + * to compute the mask of flip planes precisely. For the time being + * consider this a flip from a NULL plane. + */ + intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe)); } static void intel_crtc_disable_planes(struct drm_crtc *crtc) @@ -3972,6 +3982,13 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) intel_disable_planes(crtc); intel_disable_primary_hw_plane(dev_priv, plane, pipe); + /* + * FIXME: Once we grow proper nuclear flip support out of this we need + * to compute the mask of flip planes precisely. For the time being + * consider this a flip to a NULL plane. + */ + intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe)); + drm_vblank_off(dev, pipe); } @@ -8212,6 +8229,8 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL); } + intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_CURSOR(pipe)); + return 0; fail_unpin: i915_gem_object_unpin_from_display_plane(obj); @@ -8827,20 +8846,26 @@ out: } -void intel_mark_fb_busy(struct drm_i915_gem_object *obj, - struct intel_engine_cs *ring) +/** + * intel_mark_fb_busy - mark given planes as busy + * @dev: DRM device + * @frontbuffer_bits: bits for the affected planes + * @ring: optional ring for asynchronous commands + * + * This function gets called every time the screen contents change. It can be + * used to keep e.g. the update rate at the nominal refresh rate with DRRS. + */ +static void intel_mark_fb_busy(struct drm_device *dev, + unsigned frontbuffer_bits, + struct intel_engine_cs *ring) { - struct drm_device *dev = obj->base.dev; enum pipe pipe; - intel_edp_psr_exit(dev); - if (!i915.powersave) return; for_each_pipe(pipe) { - if (!(obj->frontbuffer_bits & - INTEL_FRONTBUFFER_ALL_MASK(pipe))) + if (!(frontbuffer_bits & INTEL_FRONTBUFFER_ALL_MASK(pipe))) continue; intel_increase_pllclock(dev, pipe); @@ -8849,6 +8874,150 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj, } } +/** + * intel_fb_obj_invalidate - invalidate frontbuffer object + * @obj: GEM object to invalidate + * @ring: set for asynchronous rendering + * + * This function gets called every time rendering on the given object starts and + * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must + * be invalidated. If @ring is non-NULL any subsequent invalidation will be delayed + * until the rendering completes or a flip on this frontbuffer plane is + * scheduled. + */ +void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, + struct intel_engine_cs *ring) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + if (!obj->frontbuffer_bits) + return; + + if (ring) { + mutex_lock(&dev_priv->fb_tracking.lock); + dev_priv->fb_tracking.busy_bits + |= obj->frontbuffer_bits; + dev_priv->fb_tracking.flip_bits + &= ~obj->frontbuffer_bits; + mutex_unlock(&dev_priv->fb_tracking.lock); + } + + intel_mark_fb_busy(dev, obj->frontbuffer_bits, ring); + + intel_edp_psr_exit(dev); +} + +/** + * intel_frontbuffer_flush - flush frontbuffer + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called every time rendering on the given planes has + * completed and frontbuffer caching can be started again. Flushes will get + * delayed if they're blocked by some oustanding asynchronous rendering. + * + * Can be called without any locks held. + */ +void intel_frontbuffer_flush(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Delay flushing when rings are still busy.*/ + mutex_lock(&dev_priv->fb_tracking.lock); + frontbuffer_bits &= ~dev_priv->fb_tracking.busy_bits; + mutex_unlock(&dev_priv->fb_tracking.lock); + + intel_mark_fb_busy(dev, frontbuffer_bits, NULL); + + intel_edp_psr_exit(dev); +} + +/** + * intel_fb_obj_flush - flush frontbuffer object + * @obj: GEM object to flush + * @retire: set when retiring asynchronous rendering + * + * This function gets called every time rendering on the given object has + * completed and frontbuffer caching can be started again. If @retire is true + * then any delayed flushes will be unblocked. + */ +void intel_fb_obj_flush(struct drm_i915_gem_object *obj, + bool retire) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned frontbuffer_bits; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + if (!obj->frontbuffer_bits) + return; + + frontbuffer_bits = obj->frontbuffer_bits; + + if (retire) { + mutex_lock(&dev_priv->fb_tracking.lock); + /* Filter out new bits since rendering started. */ + frontbuffer_bits &= dev_priv->fb_tracking.busy_bits; + + dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits; + mutex_unlock(&dev_priv->fb_tracking.lock); + } + + intel_frontbuffer_flush(dev, frontbuffer_bits); +} + +/** + * intel_frontbuffer_flip_prepare - prepare asnychronous frontbuffer flip + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called after scheduling a flip on @obj. The actual + * frontbuffer flushing will be delayed until completion is signalled with + * intel_frontbuffer_flip_complete. If an invalidate happens in between this + * flush will be cancelled. + * + * Can be called without any locks held. + */ +void intel_frontbuffer_flip_prepare(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + mutex_lock(&dev_priv->fb_tracking.lock); + dev_priv->fb_tracking.flip_bits + |= frontbuffer_bits; + mutex_unlock(&dev_priv->fb_tracking.lock); +} + +/** + * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flush + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called after the flip has been latched and will complete + * on the next vblank. It will execute the fush if it hasn't been cancalled yet. + * + * Can be called without any locks held. + */ +void intel_frontbuffer_flip_complete(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + mutex_lock(&dev_priv->fb_tracking.lock); + /* Mask any cancelled flips. */ + frontbuffer_bits &= dev_priv->fb_tracking.flip_bits; + dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits; + mutex_unlock(&dev_priv->fb_tracking.lock); + + intel_frontbuffer_flush(dev, frontbuffer_bits); +} + static void intel_crtc_destroy(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -8876,6 +9045,7 @@ static void intel_unpin_work_fn(struct work_struct *__work) struct intel_unpin_work *work = container_of(__work, struct intel_unpin_work, work); struct drm_device *dev = work->crtc->dev; + enum pipe pipe = to_intel_crtc(work->crtc)->pipe; mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(work->old_fb_obj); @@ -8885,6 +9055,8 @@ static void intel_unpin_work_fn(struct work_struct *__work) intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); + intel_frontbuffer_flip_complete(dev, INTEL_FRONTBUFFER_PRIMARY(pipe)); + BUG_ON(atomic_read(&to_intel_crtc(work->crtc)->unpin_work_count) == 0); atomic_dec(&to_intel_crtc(work->crtc)->unpin_work_count); @@ -9441,9 +9613,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (work == NULL) return -ENOMEM; - /* Exit PSR early in page flip */ - intel_edp_psr_exit(dev); - work->event = event; work->crtc = crtc; work->old_fb_obj = to_intel_framebuffer(old_fb)->obj; @@ -9519,7 +9688,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, INTEL_FRONTBUFFER_PRIMARY(pipe)); intel_disable_fbc(dev); - intel_mark_fb_busy(obj, NULL); + intel_frontbuffer_flip_prepare(dev, INTEL_FRONTBUFFER_PRIMARY(pipe)); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5d20f71..bd0d10e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -724,8 +724,33 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev); int intel_pch_rawclk(struct drm_device *dev); int valleyview_cur_cdclk(struct drm_i915_private *dev_priv); void intel_mark_busy(struct drm_device *dev); -void intel_mark_fb_busy(struct drm_i915_gem_object *obj, - struct intel_engine_cs *ring); +void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, + struct intel_engine_cs *ring); +void intel_frontbuffer_flip_prepare(struct drm_device *dev, + unsigned frontbuffer_bits); +void intel_frontbuffer_flip_complete(struct drm_device *dev, + unsigned frontbuffer_bits); +void intel_frontbuffer_flush(struct drm_device *dev, + unsigned frontbuffer_bits); +/** + * intel_frontbuffer_flip - prepare frontbuffer flip + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called after scheduling a flip on @obj. This is for + * synchronous plane updates which will happen on the next vblank and which will + * not get delayed by pending gpu rendering. + * + * Can be called without any locks held. + */ +static inline +void intel_frontbuffer_flip(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + intel_frontbuffer_flush(dev, frontbuffer_bits); +} + +void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire); void intel_mark_idle(struct drm_device *dev); void intel_crtc_restore_mode(struct drm_crtc *crtc); void intel_crtc_update_dpms(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 99b6c14..307c2f1 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -787,6 +787,9 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, overlay->old_vid_bo = overlay->vid_bo; overlay->vid_bo = new_bo; + intel_frontbuffer_flip(dev, + INTEL_FRONTBUFFER_OVERLAY(pipe)); + return 0; out_unpin: diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 140bd83..0e3fd5c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1034,6 +1034,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, else intel_plane->disable_plane(plane, crtc); + intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_SPRITE(pipe)); + if (!primary_was_enabled && primary_enabled) intel_post_enable_primary(crtc); } @@ -1054,8 +1056,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); } - intel_edp_psr_exit(dev); - return 0; } -- cgit v0.10.2 From 9c33baa6b3bbb01c1a88dceba986b20e6642cf31 Mon Sep 17 00:00:00 2001 From: Konrad Zapalowicz Date: Thu, 19 Jun 2014 19:07:15 +0200 Subject: drivers/i915: Fix unnoticed failure of init_ring_common() This commit add check for return value of init_ring_common() in the init_render_ring(). Now, when failure is detected the error code is propagated to the caller instead of being ignored. Signed-off-by: Konrad Zapalowicz Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index b96edaf..2faef26 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -604,6 +604,8 @@ static int init_render_ring(struct intel_engine_cs *ring) struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; int ret = init_ring_common(ring); + if (ret) + return ret; /* WaTimedSingleVertexDispatch:cl,bw,ctg,elk,ilk,snb */ if (INTEL_INFO(dev)->gen >= 4 && INTEL_INFO(dev)->gen < 7) -- cgit v0.10.2 From 34882298b93e998d5fccde852b860e8fbe6c8f6b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 20 Jun 2014 10:36:06 +0200 Subject: drm/i915: Update DRIVER_DATE to 20140620 Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index aa849f2..b00eae8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -53,7 +53,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20140606" +#define DRIVER_DATE "20140620" enum pipe { INVALID_PIPE = -1, -- cgit v0.10.2 From 91565c85b66db820f01894a971d39aaef60c4325 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 24 Jun 2014 17:05:02 -0700 Subject: drm/i915: Don't try to look up object for non-existent fb crtc->primary->fb may be NULL upon entry to intel_pipe_set_base() if the primary plane has previously been disabled via the universal plane interface. We need to check for NULL before trying to reference old_fb's obj. This fixes a regression introduced in commit a071fa00647bc9a3c53f917b236fff9aea175e3a Author: Daniel Vetter Date: Wed Jun 18 23:28:09 2014 +0200 drm/i915: Introduce accurate frontbuffer tracking Testcase: igt/kms_universal_plane Cc: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ff63367..f4eb752 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2690,6 +2690,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, enum pipe pipe = intel_crtc->pipe; struct drm_framebuffer *old_fb; struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + struct drm_i915_gem_object *old_obj; int ret; if (intel_crtc_has_pending_flip(crtc)) { @@ -2711,11 +2712,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, } old_fb = crtc->primary->fb; + old_obj = old_fb ? to_intel_framebuffer(old_fb)->obj : NULL; mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); if (ret == 0) - i915_gem_track_fb(to_intel_framebuffer(old_fb)->obj, obj, + i915_gem_track_fb(old_obj, obj, INTEL_FRONTBUFFER_PRIMARY(pipe)); mutex_unlock(&dev->struct_mutex); if (ret != 0) { -- cgit v0.10.2 From 84a2ab8ec76b4fa47f490a01c8186e4b4d757bfe Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 27 Jun 2014 18:51:51 -0300 Subject: drm/i915: flush delayed_resume_work when suspending It is possible that, by the time we run i915_drm_freeze(), delayed_resume_work was already queued but did not run yet. If it still didn't run after intel_runtime_pm_disable_interrupts(), by the time it runs it will try to change the interrupt registers with the interrupts already disabled, which will trigger a WARN. We can reliably reproduce this with the pm_rpm system-suspend test case. In order to avoid the problem, we have to flush the work before disabling the interrupts. We could also cancel the work instead of flushing it, but that would require us to put a runtime PM reference - and any other resource we may need in the future - in case the work was already queued, so I believe flushing the work is more future-proof, although less efficient. But I can also change this part if someone requests. Another thing I tried was to move the intel_suspend_gt_powersave() call to before intel_runtime_pm_disable_interrupts(), but since that function needs to be called after the interrupts are already disabled, due to dev_priv->rps.work, this strategy didn't work. Testcase: igt/pm_rpm/system-suspend Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80517 Signed-off-by: Paulo Zanoni Reviewed-by: Rodrigo Vivi Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 6eb45ac..8a0cb0c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -520,6 +520,8 @@ static int i915_drm_freeze(struct drm_device *dev) return error; } + flush_delayed_work(&dev_priv->rps.delayed_resume_work); + intel_runtime_pm_disable_interrupts(dev); dev_priv->enable_hotplug_processing = false; -- cgit v0.10.2 From 0d68b25e9ceb344fe2f93373b1c0311d33814265 Mon Sep 17 00:00:00 2001 From: Tom O'Rourke Date: Wed, 9 Apr 2014 11:44:06 -0700 Subject: drm/i915/bdw: Use timeout mode for RC6 on bdw Higher RC6 residency is observed using timeout mode instead of EI mode. This applies to Broadwell only. The difference is particularly noticeable with video playback. Signed-off-by: Tom O'Rourke Reviewed-by: Ben Widawsky Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1cce0a5..cb26145 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3486,15 +3486,23 @@ static void gen8_enable_rps(struct drm_device *dev) for_each_ring(ring, dev_priv, unused) I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); I915_WRITE(GEN6_RC_SLEEP, 0); - I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */ + if (IS_BROADWELL(dev)) + I915_WRITE(GEN6_RC6_THRESHOLD, 625); /* 800us/1.28 for TO */ + else + I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */ /* 3: Enable RC6 */ if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE) rc6_mask = GEN6_RC_CTL_RC6_ENABLE; intel_print_rc6_info(dev, rc6_mask); - I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | - GEN6_RC_CTL_EI_MODE(1) | - rc6_mask); + if (IS_BROADWELL(dev)) + I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | + GEN7_RC_CTL_TO_MODE | + rc6_mask); + else + I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE | + GEN6_RC_CTL_EI_MODE(1) | + rc6_mask); /* 4 Program defaults and thresholds for RPS*/ I915_WRITE(GEN6_RPNSWREQ, -- cgit v0.10.2 From 1267a26b22be45a09584ff016f33609e026a804c Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 1 Jul 2014 20:39:41 +0200 Subject: drm/i915: replace ALIGN(PAGE_SIZE) by PAGE_ALIGN use mm.h definition Cc: Daniel Vetter Cc: Jani Nikula Cc: intel-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Signed-off-by: Fabian Frederick Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f4eb752..48d3758 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6209,8 +6209,8 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc, aligned_height = intel_align_height(dev, crtc->base.primary->fb->height, plane_config->tiled); - plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] * - aligned_height, PAGE_SIZE); + plane_config->size = PAGE_ALIGN(crtc->base.primary->fb->pitches[0] * + aligned_height); DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe, plane, crtc->base.primary->fb->width, @@ -7229,8 +7229,8 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc, aligned_height = intel_align_height(dev, crtc->base.primary->fb->height, plane_config->tiled); - plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] * - aligned_height, PAGE_SIZE); + plane_config->size = PAGE_ALIGN(crtc->base.primary->fb->pitches[0] * + aligned_height); DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe, plane, crtc->base.primary->fb->width, @@ -8318,7 +8318,7 @@ static u32 intel_framebuffer_size_for_mode(struct drm_display_mode *mode, int bpp) { u32 pitch = intel_framebuffer_pitch_for_width(mode->hdisplay, bpp); - return ALIGN(pitch * mode->vdisplay, PAGE_SIZE); + return PAGE_ALIGN(pitch * mode->vdisplay); } static struct drm_framebuffer * diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 226fbc7..b8eac8c 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -107,7 +107,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper, sizes->surface_depth); size = mode_cmd.pitches[0] * mode_cmd.height; - size = ALIGN(size, PAGE_SIZE); + size = PAGE_ALIGN(size); obj = i915_gem_object_create_stolen(dev, size); if (obj == NULL) obj = i915_gem_alloc_object(dev, size); -- cgit v0.10.2 From de4bf3d51fe38eaf90cb587e4eae1f3f0e056a54 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 19 Jun 2014 10:47:32 +0200 Subject: drm/i2c: tda998x: fix lack of required reg in DT documentation The I2C address (reg) is required for the TDA998x driver to be loaded and initialized. Signed-off-by: Jean-Francois Moine Signed-off-by: Russell King diff --git a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt index d7df01c..e9e4bce 100644 --- a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt +++ b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt @@ -3,6 +3,8 @@ Device-Tree bindings for the NXP TDA998x HDMI transmitter Required properties; - compatible: must be "nxp,tda998x" + - reg: I2C address + Optional properties: - interrupts: interrupt number and trigger type default: polling -- cgit v0.10.2 From c4213885cd01c6ec15f44b4d06d67790601e1731 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 19 Jun 2014 12:06:10 -0700 Subject: drm/i915: Move compressed_fb to static allocation We are already using the size to determine whether or not to free the object, so there is no functional change there. Almost everything else has changed to static allocations of the drm_mm_node too. Aside from bringing this inline with much of our other code, this makes error paths slightly simpler, which benefits the look of an upcoming patch. Signed-off-by: Ben Widawsky Reviewed-by: Rodrigo Vivi Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index aa849f2..7aeaaaa 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -604,7 +604,7 @@ struct i915_fbc { enum plane plane; int y; - struct drm_mm_node *compressed_fb; + struct drm_mm_node compressed_fb; struct drm_mm_node *compressed_llb; struct intel_fbc_work { diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 6441178..642fd36 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -106,27 +106,25 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) static int i915_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); + struct drm_mm_node *uninitialized_var(compressed_llb); int ret; - compressed_fb = kzalloc(sizeof(*compressed_fb), GFP_KERNEL); - if (!compressed_fb) - goto err_llb; - /* Try to over-allocate to reduce reallocations and fragmentation */ - ret = drm_mm_insert_node(&dev_priv->mm.stolen, compressed_fb, + ret = drm_mm_insert_node(&dev_priv->mm.stolen, + &dev_priv->fbc.compressed_fb, size <<= 1, 4096, DRM_MM_SEARCH_DEFAULT); if (ret) - ret = drm_mm_insert_node(&dev_priv->mm.stolen, compressed_fb, + ret = drm_mm_insert_node(&dev_priv->mm.stolen, + &dev_priv->fbc.compressed_fb, size >>= 1, 4096, DRM_MM_SEARCH_DEFAULT); if (ret) goto err_llb; if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start); else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + I915_WRITE(DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start); } else { compressed_llb = kzalloc(sizeof(*compressed_llb), GFP_KERNEL); if (!compressed_llb) @@ -140,12 +138,11 @@ static int i915_setup_compression(struct drm_device *dev, int size) dev_priv->fbc.compressed_llb = compressed_llb; I915_WRITE(FBC_CFB_BASE, - dev_priv->mm.stolen_base + compressed_fb->start); + dev_priv->mm.stolen_base + dev_priv->fbc.compressed_fb.start); I915_WRITE(FBC_LL_BASE, dev_priv->mm.stolen_base + compressed_llb->start); } - dev_priv->fbc.compressed_fb = compressed_fb; dev_priv->fbc.size = size; DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n", @@ -155,9 +152,8 @@ static int i915_setup_compression(struct drm_device *dev, int size) err_fb: kfree(compressed_llb); - drm_mm_remove_node(compressed_fb); + drm_mm_remove_node(&dev_priv->fbc.compressed_fb); err_llb: - kfree(compressed_fb); pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size); return -ENOSPC; } @@ -185,10 +181,7 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev) if (dev_priv->fbc.size == 0) return; - if (dev_priv->fbc.compressed_fb) { - drm_mm_remove_node(dev_priv->fbc.compressed_fb); - kfree(dev_priv->fbc.compressed_fb); - } + drm_mm_remove_node(&dev_priv->fbc.compressed_fb); if (dev_priv->fbc.compressed_llb) { drm_mm_remove_node(dev_priv->fbc.compressed_llb); -- cgit v0.10.2 From edc0fdbbf6d8e179a0b28b9fc7597cbadabe77a8 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 19 Jun 2014 12:06:11 -0700 Subject: drm/i915: Extract CFB threshold calculation Right now, there is no threshold (0 means fail, 1 means 1:1 compression limit). This is to split the function/non-functional change of the next patch. The next patch will start to attempt to reduce the amount of CFB space we need for dire situations. It will be contained within this function. Signed-off-by: Ben Widawsky Reviewed-by: Rodrigo Vivi Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 642fd36..a86b331 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -103,22 +103,36 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) return base; } -static int i915_setup_compression(struct drm_device *dev, int size) +static int find_compression_threshold(struct drm_device *dev, + struct drm_mm_node *node, + int size) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mm_node *uninitialized_var(compressed_llb); + const int compression_threshold = 1; int ret; /* Try to over-allocate to reduce reallocations and fragmentation */ - ret = drm_mm_insert_node(&dev_priv->mm.stolen, - &dev_priv->fbc.compressed_fb, + ret = drm_mm_insert_node(&dev_priv->mm.stolen, node, size <<= 1, 4096, DRM_MM_SEARCH_DEFAULT); if (ret) - ret = drm_mm_insert_node(&dev_priv->mm.stolen, - &dev_priv->fbc.compressed_fb, + ret = drm_mm_insert_node(&dev_priv->mm.stolen, node, size >>= 1, 4096, DRM_MM_SEARCH_DEFAULT); if (ret) + return 0; + else + return compression_threshold; +} + +static int i915_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_mm_node *uninitialized_var(compressed_llb); + int ret; + + ret = find_compression_threshold(dev, &dev_priv->fbc.compressed_fb, + size); + if (!ret) goto err_llb; if (HAS_PCH_SPLIT(dev)) -- cgit v0.10.2 From 5e59f7175f96550ede91f58d267d2b551cb6fbba Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 10:41:24 -0700 Subject: drm/i915: Try harder to get FBC The GEN FBC unit provides the ability to set a low pass on frames it attempts to compress. If a frame is less than a certain amount compressibility (2:1, 4:1) it will not bother. This allows the driver to reduce the size it requests out of stolen memory. Unluckily, a few months ago, Ville actually began using this feature for framebuffers that are 16bpp (not sure why not 8bpp). In those cases, we are already using this mechanism for a different purpose, and so we can only achieve one further level of compression (2:1 -> 4:1) FBC GEN1, ie. pre-G45 is ignored. The cleverness of the patch is Art's. The bugs are mine. v2: Update message and including missing threshold case 3 (Spotted by Arthur). Cc: Art Runyan Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7aeaaaa..07982e3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -600,6 +600,7 @@ struct intel_context { struct i915_fbc { unsigned long size; + unsigned threshold; unsigned int fb_id; enum plane plane; int y; @@ -2487,7 +2488,7 @@ static inline void i915_gem_chipset_flush(struct drm_device *dev) /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); -int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size, int fb_cpp); void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); struct drm_i915_gem_object * diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index a86b331..b695d18 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -105,35 +105,61 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) static int find_compression_threshold(struct drm_device *dev, struct drm_mm_node *node, - int size) + int size, + int fb_cpp) { struct drm_i915_private *dev_priv = dev->dev_private; - const int compression_threshold = 1; + int compression_threshold = 1; int ret; - /* Try to over-allocate to reduce reallocations and fragmentation */ + /* HACK: This code depends on what we will do in *_enable_fbc. If that + * code changes, this code needs to change as well. + * + * The enable_fbc code will attempt to use one of our 2 compression + * thresholds, therefore, in that case, we only have 1 resort. + */ + + /* Try to over-allocate to reduce reallocations and fragmentation. */ ret = drm_mm_insert_node(&dev_priv->mm.stolen, node, size <<= 1, 4096, DRM_MM_SEARCH_DEFAULT); - if (ret) - ret = drm_mm_insert_node(&dev_priv->mm.stolen, node, - size >>= 1, 4096, - DRM_MM_SEARCH_DEFAULT); - if (ret) + if (ret == 0) + return compression_threshold; + +again: + /* HW's ability to limit the CFB is 1:4 */ + if (compression_threshold > 4 || + (fb_cpp == 2 && compression_threshold == 2)) return 0; - else + + ret = drm_mm_insert_node(&dev_priv->mm.stolen, node, + size >>= 1, 4096, + DRM_MM_SEARCH_DEFAULT); + if (ret && INTEL_INFO(dev)->gen <= 4) { + return 0; + } else if (ret) { + compression_threshold <<= 1; + goto again; + } else { return compression_threshold; + } } -static int i915_setup_compression(struct drm_device *dev, int size) +static int i915_setup_compression(struct drm_device *dev, int size, int fb_cpp) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mm_node *uninitialized_var(compressed_llb); int ret; ret = find_compression_threshold(dev, &dev_priv->fbc.compressed_fb, - size); + size, fb_cpp); if (!ret) goto err_llb; + else if (ret > 1) { + DRM_INFO("Reducing the compressed framebuffer size. This may lead to less power savings than a non-reduced-size. Try to increase stolen memory size if available in BIOS.\n"); + + } + + dev_priv->fbc.threshold = ret; if (HAS_PCH_SPLIT(dev)) I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start); @@ -157,7 +183,7 @@ static int i915_setup_compression(struct drm_device *dev, int size) dev_priv->mm.stolen_base + compressed_llb->start); } - dev_priv->fbc.size = size; + dev_priv->fbc.size = size / dev_priv->fbc.threshold; DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n", size); @@ -172,7 +198,7 @@ err_llb: return -ENOSPC; } -int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size, int fb_cpp) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -185,7 +211,7 @@ int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) /* Release any current block */ i915_gem_stolen_cleanup_compression(dev); - return i915_setup_compression(dev, size); + return i915_setup_compression(dev, size, fb_cpp); } void i915_gem_stolen_cleanup_compression(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index cb26145..2ce9636 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -229,9 +229,20 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc) dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane); if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) + dev_priv->fbc.threshold++; + + switch (dev_priv->fbc.threshold) { + case 4: + case 3: + dpfc_ctl |= DPFC_CTL_LIMIT_4X; + break; + case 2: dpfc_ctl |= DPFC_CTL_LIMIT_2X; - else + break; + case 1: dpfc_ctl |= DPFC_CTL_LIMIT_1X; + break; + } dpfc_ctl |= DPFC_CTL_FENCE_EN; if (IS_GEN5(dev)) dpfc_ctl |= obj->fence_reg; @@ -285,9 +296,21 @@ static void gen7_enable_fbc(struct drm_crtc *crtc) dpfc_ctl = IVB_DPFC_CTL_PLANE(intel_crtc->plane); if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) + dev_priv->fbc.threshold++; + + switch (dev_priv->fbc.threshold) { + case 4: + case 3: + dpfc_ctl |= DPFC_CTL_LIMIT_4X; + break; + case 2: dpfc_ctl |= DPFC_CTL_LIMIT_2X; - else + break; + case 1: dpfc_ctl |= DPFC_CTL_LIMIT_1X; + break; + } + dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN; I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); @@ -567,7 +590,8 @@ void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; - if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { + if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size, + drm_format_plane_cpp(fb->pixel_format, 0))) { if (set_no_fbc_reason(dev_priv, FBC_STOLEN_TOO_SMALL)) DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); goto out_disable; -- cgit v0.10.2 From e5d9ddfbb79667fb4b16db07c3282a2304fbf3ff Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 Apr 2014 15:19:38 +0100 Subject: drm/armada: move IRQ handling into CRTC Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 81c34f9..9341a94 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -349,7 +349,7 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) +static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) { struct armada_vbl_event *e, *n; void __iomem *base = dcrtc->base; @@ -410,6 +410,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) } } +static irqreturn_t armada_drm_irq(int irq, void *arg) +{ + struct armada_crtc *dcrtc = arg; + u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); + + /* + * This is rediculous - rather than writing bits to clear, we + * have to set the actual status register value. This is racy. + */ + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); + + /* Mask out those interrupts we haven't enabled */ + v = stat & dcrtc->irq_ena; + + if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { + armada_drm_crtc_irq(dcrtc, stat); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + /* These are locked by dev->vbl_lock */ void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) { @@ -888,6 +909,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) if (!IS_ERR(dcrtc->clk)) clk_disable_unprepare(dcrtc->clk); + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); + kfree(dcrtc); } @@ -1028,7 +1051,7 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *dev, unsigned num, - struct resource *res) + struct resource *res, int irq) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1074,6 +1097,15 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); + writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); + + ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc", + dcrtc); + if (ret < 0) { + kfree(dcrtc); + return ret; + } if (priv->variant->crtc_init) { ret = priv->variant->crtc_init(dcrtc); diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 9c10a07..0e952b2 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -72,10 +72,10 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *); +int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *, + int); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); -void armada_drm_crtc_irq(struct armada_crtc *, u32); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 8ab3cd1..58884d8 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -155,10 +155,16 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) /* Create all LCD controllers */ for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { + int irq; + if (!res[n]) break; - ret = armada_drm_crtc_create(dev, n, res[n]); + irq = platform_get_irq(dev->platformdev, n); + if (irq < 0) + goto err_kms; + + ret = armada_drm_crtc_create(dev, n, res[n], irq); if (ret) goto err_kms; } @@ -173,22 +179,16 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_kms; - ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); - if (ret) - goto err_kms; - dev->vblank_disable_allowed = 1; ret = armada_fbdev_init(dev); if (ret) - goto err_irq; + goto err_kms; drm_kms_helper_poll_init(dev); return 0; - err_irq: - drm_irq_uninstall(dev); err_kms: drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -203,7 +203,6 @@ static int armada_drm_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); armada_fbdev_fini(dev); - drm_irq_uninstall(dev); drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); flush_work(&priv->fb_unref_work); @@ -259,52 +258,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc) armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); } -static irqreturn_t armada_drm_irq_handler(int irq, void *arg) -{ - struct drm_device *dev = arg; - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); - irqreturn_t handled = IRQ_NONE; - - /* - * This is rediculous - rather than writing bits to clear, we - * have to set the actual status register value. This is racy. - */ - writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); - - /* Mask out those interrupts we haven't enabled */ - v = stat & dcrtc->irq_ena; - - if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { - armada_drm_crtc_irq(dcrtc, stat); - handled = IRQ_HANDLED; - } - - return handled; -} - -static int armada_drm_irq_postinstall(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - - spin_lock_irq(&dev->vbl_lock); - writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); - writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); - spin_unlock_irq(&dev->vbl_lock); - - return 0; -} - -static void armada_drm_irq_uninstall(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - - writel(0, dcrtc->base + LCD_SPU_IRQ_ENA); -} - static struct drm_ioctl_desc armada_ioctls[] = { DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, DRM_UNLOCKED), @@ -340,9 +293,6 @@ static struct drm_driver armada_drm_driver = { .get_vblank_counter = drm_vblank_count, .enable_vblank = armada_drm_enable_vblank, .disable_vblank = armada_drm_disable_vblank, - .irq_handler = armada_drm_irq_handler, - .irq_postinstall = armada_drm_irq_postinstall, - .irq_uninstall = armada_drm_irq_uninstall, #ifdef CONFIG_DEBUG_FS .debugfs_init = armada_drm_debugfs_init, .debugfs_cleanup = armada_drm_debugfs_cleanup, @@ -362,7 +312,7 @@ static struct drm_driver armada_drm_driver = { .desc = "Armada SoC DRM", .date = "20120730", .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_HAVE_IRQ | DRIVER_PRIME, + DRIVER_PRIME, .ioctls = armada_ioctls, .fops = &armada_drm_fops, }; -- cgit v0.10.2 From d016540722bb412ffa8376a9db0de757ce915bfb Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 11:02:23 +0100 Subject: drm/armada: use number of CRTCs registered Use the number of CRTCs registered to size the vblank arrays rather than our own count. Number CRTCs using this as well. This permits us to register CRTCs as components in the near future rather than as part of a single device. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 9341a94..4336dfd 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1050,8 +1050,8 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) return 0; } -int armada_drm_crtc_create(struct drm_device *dev, unsigned num, - struct resource *res, int irq) +int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, + int irq) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1075,7 +1075,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, } dcrtc->base = base; - dcrtc->num = num; + dcrtc->num = dev->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); dcrtc->csc_yuv_mode = CSC_AUTO; dcrtc->csc_rgb_mode = CSC_AUTO; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 0e952b2..531a9b0 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -72,8 +72,7 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *, - int); +int armada_drm_crtc_create(struct drm_device *, struct resource *, int); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 58884d8..add8b10 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -164,7 +164,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, n, res[n], irq); + ret = armada_drm_crtc_create(dev, res[n], irq); if (ret) goto err_kms; } @@ -175,7 +175,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; #endif - ret = drm_vblank_init(dev, n); + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) goto err_kms; -- cgit v0.10.2 From 3ecea269959afaae50b001deb294cfb9539dbea3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 15:21:30 +0100 Subject: drm/armada: move variant initialisation to CRTC init Move the variant initialisation entirely to the CRTC init function - the variant support is really about the CRTC properties than the whole system, and we want to treat each CRTC individually when we support DT. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 59948ef..a9dabca 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -15,20 +15,19 @@ #include "armada_drm.h" #include "armada_hw.h" -static int armada510_init(struct armada_private *priv, struct device *dev) +static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) { - priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1"); + struct clk *clk; - if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT) - priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); + clk = devm_clk_get(dev, "ext_ref_clk_1"); + if (IS_ERR(clk)) + return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk); - return PTR_RET(priv->extclk[0]); -} + dcrtc->extclk[0] = clk; -static int armada510_crtc_init(struct armada_crtc *dcrtc) -{ /* Lower the watermark so to eliminate jitter at higher bandwidths */ armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); + return 0; } @@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc) static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct drm_display_mode *mode, uint32_t *sclk) { - struct armada_private *priv = dcrtc->crtc.dev->dev_private; - struct clk *clk = priv->extclk[0]; + struct clk *clk = dcrtc->extclk[0]; int ret; if (dcrtc->num == 1) @@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct armada_variant armada510_ops = { .has_spu_adv_reg = true, .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, - .init = armada510_init, .crtc_init = armada510_crtc_init, .crtc_compute_clock = armada510_crtc_compute_clock, }; diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 4336dfd..3adddab 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1108,7 +1108,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, } if (priv->variant->crtc_init) { - ret = priv->variant->crtc_init(dcrtc); + ret = priv->variant->crtc_init(dcrtc, dev->dev); if (ret) { kfree(dcrtc); return ret; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 531a9b0..3f0e70b 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -38,6 +38,7 @@ struct armada_crtc { unsigned num; void __iomem *base; struct clk *clk; + struct clk *extclk[2]; struct { uint32_t spu_v_h_total; uint32_t spu_v_porch; diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index a72cae0..a5452ae 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -59,10 +59,9 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *, struct armada_private; struct armada_variant { - bool has_spu_adv_reg; + bool has_spu_adv_reg; uint32_t spu_adv_reg; - int (*init)(struct armada_private *, struct device *); - int (*crtc_init)(struct armada_crtc *); + int (*crtc_init)(struct armada_crtc *, struct device *); int (*crtc_compute_clock)(struct armada_crtc *, const struct drm_display_mode *, uint32_t *); @@ -78,7 +77,6 @@ struct armada_private { struct drm_fb_helper *fbdev; struct armada_crtc *dcrtc[2]; struct drm_mm linear; - struct clk *extclk[2]; struct drm_property *csc_yuv_prop; struct drm_property *csc_rgb_prop; struct drm_property *colorkey_prop; diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index add8b10..4939a86 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -130,10 +130,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) priv->variant = (struct armada_variant *)id->driver_data; - ret = priv->variant->init(priv, dev->dev); - if (ret) - return ret; - INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); -- cgit v0.10.2 From 42e62ba7a484e12a77b9e330a29bbf7c99d3e60e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 15:24:03 +0100 Subject: drm/armada: make variant a CRTC thing Move the variant pointer into the armada_crtc structure, and update for the resulting changes. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index a9dabca..1bcf1a8 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -79,6 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct armada_variant armada510_ops = { .has_spu_adv_reg = true, .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, - .crtc_init = armada510_crtc_init, - .crtc_compute_clock = armada510_crtc_compute_clock, + .init = armada510_crtc_init, + .compute_clock = armada510_crtc_compute_clock, }; diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 3adddab..afa4974 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -332,17 +332,16 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc) static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adj) { - struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); int ret; /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ - if (!priv->variant->has_spu_adv_reg && + if (!dcrtc->variant->has_spu_adv_reg && adj->flags & DRM_MODE_FLAG_INTERLACE) return false; /* Check whether the display mode is possible */ - ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL); + ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL); if (ret) return false; @@ -491,7 +490,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adj, int x, int y, struct drm_framebuffer *old_fb) { - struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_regs regs[17]; uint32_t lm, rm, tm, bm, val, sclk; @@ -536,7 +534,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, } /* Now compute the divider for real */ - priv->variant->crtc_compute_clock(dcrtc, adj, &sclk); + dcrtc->variant->compute_clock(dcrtc, adj, &sclk); /* Ensure graphic fifo is enabled */ armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); @@ -558,7 +556,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, dcrtc->v[1].spu_v_porch = tm << 16 | bm; val = adj->crtc_hsync_start; dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - priv->variant->spu_adv_reg; + dcrtc->variant->spu_adv_reg; if (interlaced) { /* Odd interlaced frame */ @@ -567,7 +565,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; val = adj->crtc_hsync_start - adj->crtc_htotal / 2; dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - priv->variant->spu_adv_reg; + dcrtc->variant->spu_adv_reg; } else { dcrtc->v[0] = dcrtc->v[1]; } @@ -582,7 +580,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, LCD_SPUT_V_H_TOTAL); - if (priv->variant->has_spu_adv_reg) { + if (dcrtc->variant->has_spu_adv_reg) { armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); @@ -826,12 +824,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_private *priv = crtc->dev->dev_private; struct armada_gem_object *obj = NULL; int ret; /* If no cursor support, replicate drm's return value */ - if (!priv->variant->has_spu_adv_reg) + if (!dcrtc->variant->has_spu_adv_reg) return -ENXIO; if (handle && w > 0 && h > 0) { @@ -879,11 +876,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_private *priv = crtc->dev->dev_private; int ret; /* If no cursor support, replicate drm's return value */ - if (!priv->variant->has_spu_adv_reg) + if (!dcrtc->variant->has_spu_adv_reg) return -EFAULT; mutex_lock(&dev->struct_mutex); @@ -1051,7 +1047,7 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, - int irq) + int irq, const struct armada_variant *variant) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1074,6 +1070,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return -ENOMEM; } + dcrtc->variant = variant; dcrtc->base = base; dcrtc->num = dev->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); @@ -1107,8 +1104,8 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return ret; } - if (priv->variant->crtc_init) { - ret = priv->variant->crtc_init(dcrtc, dev->dev); + if (dcrtc->variant->init) { + ret = dcrtc->variant->init(dcrtc, dev->dev); if (ret) { kfree(dcrtc); return ret; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 3f0e70b..73efcfc 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -32,9 +32,11 @@ struct armada_regs { armada_reg_queue_mod(_r, _i, 0, 0, ~0) struct armada_frame_work; +struct armada_variant; struct armada_crtc { struct drm_crtc crtc; + const struct armada_variant *variant; unsigned num; void __iomem *base; struct clk *clk; @@ -73,7 +75,8 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, struct resource *, int); +int armada_drm_crtc_create(struct drm_device *, struct resource *, int, + const struct armada_variant *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index a5452ae..ea63c6c 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -61,17 +61,16 @@ struct armada_private; struct armada_variant { bool has_spu_adv_reg; uint32_t spu_adv_reg; - int (*crtc_init)(struct armada_crtc *, struct device *); - int (*crtc_compute_clock)(struct armada_crtc *, - const struct drm_display_mode *, - uint32_t *); + int (*init)(struct armada_crtc *, struct device *); + int (*compute_clock)(struct armada_crtc *, + const struct drm_display_mode *, + uint32_t *); }; /* Variant ops */ extern const struct armada_variant armada510_ops; struct armada_private { - const struct armada_variant *variant; struct work_struct fb_unref_work; DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); struct drm_fb_helper *fbdev; diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 4939a86..3995be3 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -85,6 +85,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev, static int armada_drm_load(struct drm_device *dev, unsigned long flags) { const struct platform_device_id *id; + const struct armada_variant *variant; struct armada_private *priv; struct resource *res[ARRAY_SIZE(priv->dcrtc)]; struct resource *mem = NULL; @@ -128,7 +129,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (!id) return -ENXIO; - priv->variant = (struct armada_variant *)id->driver_data; + variant = (const struct armada_variant *)id->driver_data; INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); @@ -160,7 +161,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, res[n], irq); + ret = armada_drm_crtc_create(dev, res[n], irq, variant); if (ret) goto err_kms; } -- cgit v0.10.2 From 18e4a4669c5023eb1157f2a3f1bf6ca2b8535572 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Mon, 9 Jun 2014 12:39:22 +0200 Subject: drm/vmwgfx: Fix compat shader namespace Contrary to the host-backed shader interface that has a per-context name-space for shaders, the compat shader namespace was per client (or rather, per file). Fix this so that the compat shader namespace is per context, and at the same time, make command buffer managed context resource management generic. Signed-off-by: Thomas Hellstrom Reviewed-by: Jakob Bornecrantz diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 458cdf6..ce0ab95 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -6,6 +6,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \ vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \ vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ - vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o + vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ + vmwgfx_cmdbuf_res.o \ obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c new file mode 100644 index 0000000..bfeb4b1 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c @@ -0,0 +1,341 @@ +/************************************************************************** + * + * Copyright © 2014 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" + +#define VMW_CMDBUF_RES_MAN_HT_ORDER 12 + +enum vmw_cmdbuf_res_state { + VMW_CMDBUF_RES_COMMITED, + VMW_CMDBUF_RES_ADD, + VMW_CMDBUF_RES_DEL +}; + +/** + * struct vmw_cmdbuf_res - Command buffer managed resource entry. + * + * @res: Refcounted pointer to a struct vmw_resource. + * @hash: Hash entry for the manager hash table. + * @head: List head used either by the staging list or the manager list + * of commited resources. + * @state: Staging state of this resource entry. + * @man: Pointer to a resource manager for this entry. + */ +struct vmw_cmdbuf_res { + struct vmw_resource *res; + struct drm_hash_item hash; + struct list_head head; + enum vmw_cmdbuf_res_state state; + struct vmw_cmdbuf_res_manager *man; +}; + +/** + * struct vmw_cmdbuf_res_manager - Command buffer resource manager. + * + * @resources: Hash table containing staged and commited command buffer + * resources + * @list: List of commited command buffer resources. + * @dev_priv: Pointer to a device private structure. + * + * @resources and @list are protected by the cmdbuf mutex for now. + */ +struct vmw_cmdbuf_res_manager { + struct drm_open_hash resources; + struct list_head list; + struct vmw_private *dev_priv; +}; + + +/** + * vmw_cmdbuf_res_lookup - Look up a command buffer resource + * + * @man: Pointer to the command buffer resource manager + * @resource_type: The resource type, that combined with the user key + * identifies the resource. + * @user_key: The user key. + * + * Returns a valid refcounted struct vmw_resource pointer on success, + * an error pointer on failure. + */ +struct vmw_resource * +vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key) +{ + struct drm_hash_item *hash; + int ret; + unsigned long key = user_key | (res_type << 24); + + ret = drm_ht_find_item(&man->resources, key, &hash); + if (unlikely(ret != 0)) + return ERR_PTR(ret); + + return vmw_resource_reference + (drm_hash_entry(hash, struct vmw_cmdbuf_res, hash)->res); +} + +/** + * vmw_cmdbuf_res_free - Free a command buffer resource. + * + * @man: Pointer to the command buffer resource manager + * @entry: Pointer to a struct vmw_cmdbuf_res. + * + * Frees a struct vmw_cmdbuf_res entry and drops its reference to the + * struct vmw_resource. + */ +static void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man, + struct vmw_cmdbuf_res *entry) +{ + list_del(&entry->head); + WARN_ON(drm_ht_remove_item(&man->resources, &entry->hash)); + vmw_resource_unreference(&entry->res); + kfree(entry); +} + +/** + * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions + * + * @list: Caller's list of command buffer resource actions. + * + * This function commits a list of command buffer resource + * additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions has commited the fifo contents to the device. + */ +void vmw_cmdbuf_res_commit(struct list_head *list) +{ + struct vmw_cmdbuf_res *entry, *next; + + list_for_each_entry_safe(entry, next, list, head) { + list_del(&entry->head); + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + entry->state = VMW_CMDBUF_RES_COMMITED; + list_add_tail(&entry->head, &entry->man->list); + break; + case VMW_CMDBUF_RES_DEL: + vmw_resource_unreference(&entry->res); + kfree(entry); + break; + default: + BUG(); + break; + } + } +} + +/** + * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions + * + * @man: Pointer to the command buffer resource manager + * @list: Caller's list of command buffer resource action + * + * This function reverts a list of command buffer resource + * additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions failed for some reason, and the command stream was never + * submitted. + */ +void vmw_cmdbuf_res_revert(struct list_head *list) +{ + struct vmw_cmdbuf_res *entry, *next; + int ret; + + list_for_each_entry_safe(entry, next, list, head) { + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + vmw_cmdbuf_res_free(entry->man, entry); + break; + case VMW_CMDBUF_RES_DEL: + ret = drm_ht_insert_item(&entry->man->resources, + &entry->hash); + list_del(&entry->head); + list_add_tail(&entry->head, &entry->man->list); + entry->state = VMW_CMDBUF_RES_COMMITED; + break; + default: + BUG(); + break; + } + } +} + +/** + * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition. + * + * @man: Pointer to the command buffer resource manager. + * @res_type: The resource type. + * @user_key: The user-space id of the resource. + * @res: Valid (refcount != 0) pointer to a struct vmw_resource. + * @list: The staging list. + * + * This function allocates a struct vmw_cmdbuf_res entry and adds the + * resource to the hash table of the manager identified by @man. The + * entry is then put on the staging list identified by @list. + */ +int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct vmw_resource *res, + struct list_head *list) +{ + struct vmw_cmdbuf_res *cres; + int ret; + + cres = kzalloc(sizeof(*cres), GFP_KERNEL); + if (unlikely(cres == NULL)) + return -ENOMEM; + + cres->hash.key = user_key | (res_type << 24); + ret = drm_ht_insert_item(&man->resources, &cres->hash); + if (unlikely(ret != 0)) + goto out_invalid_key; + + cres->state = VMW_CMDBUF_RES_ADD; + cres->res = vmw_resource_reference(res); + cres->man = man; + list_add_tail(&cres->head, list); + +out_invalid_key: + return ret; +} + +/** + * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal. + * + * @man: Pointer to the command buffer resource manager. + * @res_type: The resource type. + * @user_key: The user-space id of the resource. + * @list: The staging list. + * + * This function looks up the struct vmw_cmdbuf_res entry from the manager + * hash table and, if it exists, removes it. Depending on its current staging + * state it then either removes the entry from the staging list or adds it + * to it with a staging state of removal. + */ +int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct list_head *list) +{ + struct vmw_cmdbuf_res *entry; + struct drm_hash_item *hash; + int ret; + + ret = drm_ht_find_item(&man->resources, user_key, &hash); + if (likely(ret != 0)) + return -EINVAL; + + entry = drm_hash_entry(hash, struct vmw_cmdbuf_res, hash); + + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + vmw_cmdbuf_res_free(man, entry); + break; + case VMW_CMDBUF_RES_COMMITED: + (void) drm_ht_remove_item(&man->resources, &entry->hash); + list_del(&entry->head); + entry->state = VMW_CMDBUF_RES_DEL; + list_add_tail(&entry->head, list); + break; + default: + BUG(); + break; + } + + return 0; +} + +/** + * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource + * manager. + * + * @dev_priv: Pointer to a struct vmw_private + * + * Allocates and initializes a command buffer managed resource manager. Returns + * an error pointer on failure. + */ +struct vmw_cmdbuf_res_manager * +vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) +{ + struct vmw_cmdbuf_res_manager *man; + int ret; + + man = kzalloc(sizeof(*man), GFP_KERNEL); + if (man == NULL) + return ERR_PTR(-ENOMEM); + + man->dev_priv = dev_priv; + INIT_LIST_HEAD(&man->list); + ret = drm_ht_create(&man->resources, VMW_CMDBUF_RES_MAN_HT_ORDER); + if (ret == 0) + return man; + + kfree(man); + return ERR_PTR(ret); +} + +/** + * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource + * manager. + * + * @man: Pointer to the manager to destroy. + * + * This function destroys a command buffer managed resource manager and + * unreferences / frees all command buffer managed resources and -entries + * associated with it. + */ +void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) +{ + struct vmw_cmdbuf_res *entry, *next; + + list_for_each_entry_safe(entry, next, &man->list, head) + vmw_cmdbuf_res_free(man, entry); + + kfree(man); +} + +/** + * + * vmw_cmdbuf_res_man_size - Return the size of a command buffer managed + * resource manager + * + * Returns the approximate allocation size of a command buffer managed + * resource manager. + */ +size_t vmw_cmdbuf_res_man_size(void) +{ + static size_t res_man_size; + + if (unlikely(res_man_size == 0)) + res_man_size = + ttm_round_pot(sizeof(struct vmw_cmdbuf_res_manager)) + + ttm_round_pot(sizeof(struct hlist_head) << + VMW_CMDBUF_RES_MAN_HT_ORDER); + + return res_man_size; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c index 8bb26dc..5ac9287 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -33,6 +33,7 @@ struct vmw_user_context { struct ttm_base_object base; struct vmw_resource res; struct vmw_ctx_binding_state cbs; + struct vmw_cmdbuf_res_manager *man; }; @@ -103,7 +104,8 @@ static const vmw_scrub_func vmw_scrub_funcs[vmw_ctx_binding_max] = { static void vmw_hw_context_destroy(struct vmw_resource *res) { - + struct vmw_user_context *uctx = + container_of(res, struct vmw_user_context, res); struct vmw_private *dev_priv = res->dev_priv; struct { SVGA3dCmdHeader header; @@ -113,9 +115,9 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) if (res->func->destroy == vmw_gb_context_destroy) { mutex_lock(&dev_priv->cmdbuf_mutex); + vmw_cmdbuf_res_man_destroy(uctx->man); mutex_lock(&dev_priv->binding_mutex); - (void) vmw_context_binding_state_kill - (&container_of(res, struct vmw_user_context, res)->cbs); + (void) vmw_context_binding_state_kill(&uctx->cbs); (void) vmw_gb_context_destroy(res); mutex_unlock(&dev_priv->binding_mutex); if (dev_priv->pinned_bo != NULL && @@ -152,13 +154,16 @@ static int vmw_gb_context_init(struct vmw_private *dev_priv, ret = vmw_resource_init(dev_priv, res, true, res_free, &vmw_gb_context_func); res->backup_size = SVGA3D_CONTEXT_DATA_SIZE; + if (unlikely(ret != 0)) + goto out_err; - if (unlikely(ret != 0)) { - if (res_free) - res_free(res); - else - kfree(res); - return ret; + if (dev_priv->has_mob) { + uctx->man = vmw_cmdbuf_res_man_create(dev_priv); + if (unlikely(IS_ERR(uctx->man))) { + ret = PTR_ERR(uctx->man); + uctx->man = NULL; + goto out_err; + } } memset(&uctx->cbs, 0, sizeof(uctx->cbs)); @@ -166,6 +171,13 @@ static int vmw_gb_context_init(struct vmw_private *dev_priv, vmw_resource_activate(res, vmw_hw_context_destroy); return 0; + +out_err: + if (res_free) + res_free(res); + else + kfree(res); + return ret; } static int vmw_context_init(struct vmw_private *dev_priv, @@ -471,7 +483,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, */ if (unlikely(vmw_user_context_size == 0)) - vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128 + + ((dev_priv->has_mob) ? vmw_cmdbuf_res_man_size() : 0); ret = ttm_read_lock(&dev_priv->reservation_sem, true); if (unlikely(ret != 0)) @@ -901,3 +914,8 @@ struct list_head *vmw_context_binding_list(struct vmw_resource *ctx) { return &(container_of(ctx, struct vmw_user_context, res)->cbs.list); } + +struct vmw_cmdbuf_res_manager *vmw_context_res_man(struct vmw_resource *ctx) +{ + return container_of(ctx, struct vmw_user_context, res)->man; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 246a62b..f31a754 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -946,7 +946,6 @@ static void vmw_postclose(struct drm_device *dev, drm_master_put(&vmw_fp->locked_master); } - vmw_compat_shader_man_destroy(vmw_fp->shman); ttm_object_file_release(&vmw_fp->tfile); kfree(vmw_fp); } @@ -966,16 +965,10 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv) if (unlikely(vmw_fp->tfile == NULL)) goto out_no_tfile; - vmw_fp->shman = vmw_compat_shader_man_create(dev_priv); - if (IS_ERR(vmw_fp->shman)) - goto out_no_shman; - file_priv->driver_priv = vmw_fp; return 0; -out_no_shman: - ttm_object_file_release(&vmw_fp->tfile); out_no_tfile: kfree(vmw_fp); return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 6b252a8..c181175 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -40,10 +40,10 @@ #include #include "vmwgfx_fence.h" -#define VMWGFX_DRIVER_DATE "20140325" +#define VMWGFX_DRIVER_DATE "20140704" #define VMWGFX_DRIVER_MAJOR 2 #define VMWGFX_DRIVER_MINOR 6 -#define VMWGFX_DRIVER_PATCHLEVEL 0 +#define VMWGFX_DRIVER_PATCHLEVEL 1 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_MAX_RELOCATIONS 2048 @@ -75,14 +75,11 @@ #define VMW_RES_FENCE ttm_driver_type3 #define VMW_RES_SHADER ttm_driver_type4 -struct vmw_compat_shader_manager; - struct vmw_fpriv { struct drm_master *locked_master; struct ttm_object_file *tfile; struct list_head fence_events; bool gb_aware; - struct vmw_compat_shader_manager *shman; }; struct vmw_dma_buffer { @@ -124,6 +121,10 @@ struct vmw_resource { void (*hw_destroy) (struct vmw_resource *res); }; + +/* + * Resources that are managed using ioctls. + */ enum vmw_res_type { vmw_res_context, vmw_res_surface, @@ -132,6 +133,15 @@ enum vmw_res_type { vmw_res_max }; +/* + * Resources that are managed using command streams. + */ +enum vmw_cmdbuf_res_type { + vmw_cmdbuf_res_compat_shader +}; + +struct vmw_cmdbuf_res_manager; + struct vmw_cursor_snooper { struct drm_crtc *crtc; size_t age; @@ -341,7 +351,7 @@ struct vmw_sw_context{ bool needs_post_query_barrier; struct vmw_resource *error_resource; struct vmw_ctx_binding_state staged_bindings; - struct list_head staged_shaders; + struct list_head staged_cmd_res; }; struct vmw_legacy_display; @@ -974,7 +984,8 @@ extern void vmw_context_binding_res_list_kill(struct list_head *head); extern void vmw_context_binding_res_list_scrub(struct list_head *head); extern int vmw_context_rebind_all(struct vmw_resource *ctx); extern struct list_head *vmw_context_binding_list(struct vmw_resource *ctx); - +extern struct vmw_cmdbuf_res_manager * +vmw_context_res_man(struct vmw_resource *ctx); /* * Surface management - vmwgfx_surface.c */ @@ -1008,27 +1019,42 @@ extern int vmw_shader_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, - SVGA3dShaderType shader_type, - u32 *user_key); -extern void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, - struct list_head *list); -extern void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, - struct list_head *list); -extern int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, - u32 user_key, - SVGA3dShaderType shader_type, - struct list_head *list); -extern int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, +extern int vmw_compat_shader_add(struct vmw_private *dev_priv, + struct vmw_cmdbuf_res_manager *man, u32 user_key, const void *bytecode, SVGA3dShaderType shader_type, size_t size, - struct ttm_object_file *tfile, struct list_head *list); -extern struct vmw_compat_shader_manager * -vmw_compat_shader_man_create(struct vmw_private *dev_priv); -extern void -vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man); +extern int vmw_compat_shader_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, SVGA3dShaderType shader_type, + struct list_head *list); +extern struct vmw_resource * +vmw_compat_shader_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key, SVGA3dShaderType shader_type); + +/* + * Command buffer managed resources - vmwgfx_cmdbuf_res.c + */ + +extern struct vmw_cmdbuf_res_manager * +vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv); +extern void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man); +extern size_t vmw_cmdbuf_res_man_size(void); +extern struct vmw_resource * +vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key); +extern void vmw_cmdbuf_res_revert(struct list_head *list); +extern void vmw_cmdbuf_res_commit(struct list_head *list); +extern int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct vmw_resource *res, + struct list_head *list); +extern int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct list_head *list); /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 87df0b3..7bfdaa1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -422,28 +422,91 @@ static int vmw_resources_validate(struct vmw_sw_context *sw_context) return 0; } + +/** + * vmw_cmd_res_reloc_add - Add a resource to a software context's + * relocation- and validation lists. + * + * @dev_priv: Pointer to a struct vmw_private identifying the device. + * @sw_context: Pointer to the software context. + * @res_type: Resource type. + * @id_loc: Pointer to where the id that needs translation is located. + * @res: Valid pointer to a struct vmw_resource. + * @p_val: If non null, a pointer to the struct vmw_resource_validate_node + * used for this resource is returned here. + */ +static int vmw_cmd_res_reloc_add(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + enum vmw_res_type res_type, + uint32_t *id_loc, + struct vmw_resource *res, + struct vmw_resource_val_node **p_val) +{ + int ret; + struct vmw_resource_val_node *node; + + *p_val = NULL; + ret = vmw_resource_relocation_add(&sw_context->res_relocations, + res, + id_loc - sw_context->buf_start); + if (unlikely(ret != 0)) + goto out_err; + + ret = vmw_resource_val_add(sw_context, res, &node); + if (unlikely(ret != 0)) + goto out_err; + + if (res_type == vmw_res_context && dev_priv->has_mob && + node->first_usage) { + + /* + * Put contexts first on the list to be able to exit + * list traversal for contexts early. + */ + list_del(&node->head); + list_add(&node->head, &sw_context->resource_list); + + ret = vmw_resource_context_res_add(dev_priv, sw_context, res); + if (unlikely(ret != 0)) + goto out_err; + node->staged_bindings = + kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL); + if (node->staged_bindings == NULL) { + DRM_ERROR("Failed to allocate context binding " + "information.\n"); + goto out_err; + } + INIT_LIST_HEAD(&node->staged_bindings->list); + } + + if (p_val) + *p_val = node; + +out_err: + return ret; +} + + /** - * vmw_cmd_compat_res_check - Check that a resource is present and if so, put it + * vmw_cmd_res_check - Check that a resource is present and if so, put it * on the resource validate list unless it's already there. * * @dev_priv: Pointer to a device private structure. * @sw_context: Pointer to the software context. * @res_type: Resource type. * @converter: User-space visisble type specific information. - * @id: user-space resource id handle. * @id_loc: Pointer to the location in the command buffer currently being * parsed from where the user-space resource id handle is located. * @p_val: Pointer to pointer to resource validalidation node. Populated * on exit. */ static int -vmw_cmd_compat_res_check(struct vmw_private *dev_priv, - struct vmw_sw_context *sw_context, - enum vmw_res_type res_type, - const struct vmw_user_resource_conv *converter, - uint32_t id, - uint32_t *id_loc, - struct vmw_resource_val_node **p_val) +vmw_cmd_res_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + enum vmw_res_type res_type, + const struct vmw_user_resource_conv *converter, + uint32_t *id_loc, + struct vmw_resource_val_node **p_val) { struct vmw_res_cache_entry *rcache = &sw_context->res_cache[res_type]; @@ -451,7 +514,7 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv, struct vmw_resource_val_node *node; int ret; - if (id == SVGA3D_INVALID_ID) { + if (*id_loc == SVGA3D_INVALID_ID) { if (p_val) *p_val = NULL; if (res_type == vmw_res_context) { @@ -466,7 +529,7 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv, * resource */ - if (likely(rcache->valid && id == rcache->handle)) { + if (likely(rcache->valid && *id_loc == rcache->handle)) { const struct vmw_resource *res = rcache->res; rcache->node->first_usage = false; @@ -480,49 +543,28 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv, ret = vmw_user_resource_lookup_handle(dev_priv, sw_context->fp->tfile, - id, + *id_loc, converter, &res); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use resource 0x%08x.\n", - (unsigned) id); + (unsigned) *id_loc); dump_stack(); return ret; } rcache->valid = true; rcache->res = res; - rcache->handle = id; - - ret = vmw_resource_relocation_add(&sw_context->res_relocations, - res, - id_loc - sw_context->buf_start); - if (unlikely(ret != 0)) - goto out_no_reloc; + rcache->handle = *id_loc; - ret = vmw_resource_val_add(sw_context, res, &node); + ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, res_type, id_loc, + res, &node); if (unlikely(ret != 0)) goto out_no_reloc; rcache->node = node; if (p_val) *p_val = node; - - if (dev_priv->has_mob && node->first_usage && - res_type == vmw_res_context) { - ret = vmw_resource_context_res_add(dev_priv, sw_context, res); - if (unlikely(ret != 0)) - goto out_no_reloc; - node->staged_bindings = - kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL); - if (node->staged_bindings == NULL) { - DRM_ERROR("Failed to allocate context binding " - "information.\n"); - goto out_no_reloc; - } - INIT_LIST_HEAD(&node->staged_bindings->list); - } - vmw_resource_unreference(&res); return 0; @@ -534,31 +576,6 @@ out_no_reloc: } /** - * vmw_cmd_res_check - Check that a resource is present and if so, put it - * on the resource validate list unless it's already there. - * - * @dev_priv: Pointer to a device private structure. - * @sw_context: Pointer to the software context. - * @res_type: Resource type. - * @converter: User-space visisble type specific information. - * @id_loc: Pointer to the location in the command buffer currently being - * parsed from where the user-space resource id handle is located. - * @p_val: Pointer to pointer to resource validalidation node. Populated - * on exit. - */ -static int -vmw_cmd_res_check(struct vmw_private *dev_priv, - struct vmw_sw_context *sw_context, - enum vmw_res_type res_type, - const struct vmw_user_resource_conv *converter, - uint32_t *id_loc, - struct vmw_resource_val_node **p_val) -{ - return vmw_cmd_compat_res_check(dev_priv, sw_context, res_type, - converter, *id_loc, id_loc, p_val); -} - -/** * vmw_rebind_contexts - Rebind all resources previously bound to * referenced contexts. * @@ -572,8 +589,8 @@ static int vmw_rebind_contexts(struct vmw_sw_context *sw_context) int ret; list_for_each_entry(val, &sw_context->resource_list, head) { - if (likely(!val->staged_bindings)) - continue; + if (unlikely(!val->staged_bindings)) + break; ret = vmw_context_rebind_all(val->res); if (unlikely(ret != 0)) { @@ -1626,13 +1643,14 @@ static int vmw_cmd_shader_define(struct vmw_private *dev_priv, } *cmd; int ret; size_t size; + struct vmw_resource_val_node *val; cmd = container_of(header, struct vmw_shader_define_cmd, header); ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, user_context_converter, &cmd->body.cid, - NULL); + &val); if (unlikely(ret != 0)) return ret; @@ -1640,11 +1658,11 @@ static int vmw_cmd_shader_define(struct vmw_private *dev_priv, return 0; size = cmd->header.size - sizeof(cmd->body); - ret = vmw_compat_shader_add(sw_context->fp->shman, + ret = vmw_compat_shader_add(dev_priv, + vmw_context_res_man(val->res), cmd->body.shid, cmd + 1, cmd->body.type, size, - sw_context->fp->tfile, - &sw_context->staged_shaders); + &sw_context->staged_cmd_res); if (unlikely(ret != 0)) return ret; @@ -1672,23 +1690,24 @@ static int vmw_cmd_shader_destroy(struct vmw_private *dev_priv, SVGA3dCmdDestroyShader body; } *cmd; int ret; + struct vmw_resource_val_node *val; cmd = container_of(header, struct vmw_shader_destroy_cmd, header); ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, user_context_converter, &cmd->body.cid, - NULL); + &val); if (unlikely(ret != 0)) return ret; if (unlikely(!dev_priv->has_mob)) return 0; - ret = vmw_compat_shader_remove(sw_context->fp->shman, + ret = vmw_compat_shader_remove(vmw_context_res_man(val->res), cmd->body.shid, cmd->body.type, - &sw_context->staged_shaders); + &sw_context->staged_cmd_res); if (unlikely(ret != 0)) return ret; @@ -1715,7 +1734,9 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv, SVGA3dCmdHeader header; SVGA3dCmdSetShader body; } *cmd; - struct vmw_resource_val_node *ctx_node; + struct vmw_resource_val_node *ctx_node, *res_node = NULL; + struct vmw_ctx_bindinfo bi; + struct vmw_resource *res = NULL; int ret; cmd = container_of(header, struct vmw_set_shader_cmd, @@ -1727,32 +1748,40 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; - if (dev_priv->has_mob) { - struct vmw_ctx_bindinfo bi; - struct vmw_resource_val_node *res_node; - u32 shid = cmd->body.shid; - - if (shid != SVGA3D_INVALID_ID) - (void) vmw_compat_shader_lookup(sw_context->fp->shman, - cmd->body.type, - &shid); - - ret = vmw_cmd_compat_res_check(dev_priv, sw_context, - vmw_res_shader, - user_shader_converter, - shid, - &cmd->body.shid, &res_node); + if (!dev_priv->has_mob) + return 0; + + if (cmd->body.shid != SVGA3D_INVALID_ID) { + res = vmw_compat_shader_lookup + (vmw_context_res_man(ctx_node->res), + cmd->body.shid, + cmd->body.type); + + if (!IS_ERR(res)) { + ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, + vmw_res_shader, + &cmd->body.shid, res, + &res_node); + vmw_resource_unreference(&res); + if (unlikely(ret != 0)) + return ret; + } + } + + if (!res_node) { + ret = vmw_cmd_res_check(dev_priv, sw_context, + vmw_res_shader, + user_shader_converter, + &cmd->body.shid, &res_node); if (unlikely(ret != 0)) return ret; - - bi.ctx = ctx_node->res; - bi.res = res_node ? res_node->res : NULL; - bi.bt = vmw_ctx_binding_shader; - bi.i1.shader_type = cmd->body.type; - return vmw_context_binding_add(ctx_node->staged_bindings, &bi); } - return 0; + bi.ctx = ctx_node->res; + bi.res = res_node ? res_node->res : NULL; + bi.bt = vmw_ctx_binding_shader; + bi.i1.shader_type = cmd->body.type; + return vmw_context_binding_add(ctx_node->staged_bindings, &bi); } /** @@ -2394,6 +2423,8 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, } } + + int vmw_execbuf_process(struct drm_file *file_priv, struct vmw_private *dev_priv, void __user *user_commands, @@ -2453,7 +2484,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, goto out_unlock; sw_context->res_ht_initialized = true; } - INIT_LIST_HEAD(&sw_context->staged_shaders); + INIT_LIST_HEAD(&sw_context->staged_cmd_res); INIT_LIST_HEAD(&resource_list); ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, @@ -2548,8 +2579,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, } list_splice_init(&sw_context->resource_list, &resource_list); - vmw_compat_shaders_commit(sw_context->fp->shman, - &sw_context->staged_shaders); + vmw_cmdbuf_res_commit(&sw_context->staged_cmd_res); mutex_unlock(&dev_priv->cmdbuf_mutex); /* @@ -2576,8 +2606,7 @@ out_unlock: list_splice_init(&sw_context->resource_list, &resource_list); error_resource = sw_context->error_resource; sw_context->error_resource = NULL; - vmw_compat_shaders_revert(sw_context->fp->shman, - &sw_context->staged_shaders); + vmw_cmdbuf_res_revert(&sw_context->staged_cmd_res); mutex_unlock(&dev_priv->cmdbuf_mutex); /* diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index c1559eea..8719fb3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -29,8 +29,6 @@ #include "vmwgfx_resource_priv.h" #include "ttm/ttm_placement.h" -#define VMW_COMPAT_SHADER_HT_ORDER 12 - struct vmw_shader { struct vmw_resource res; SVGA3dShaderType type; @@ -42,49 +40,8 @@ struct vmw_user_shader { struct vmw_shader shader; }; -/** - * enum vmw_compat_shader_state - Staging state for compat shaders - */ -enum vmw_compat_shader_state { - VMW_COMPAT_COMMITED, - VMW_COMPAT_ADD, - VMW_COMPAT_DEL -}; - -/** - * struct vmw_compat_shader - Metadata for compat shaders. - * - * @handle: The TTM handle of the guest backed shader. - * @tfile: The struct ttm_object_file the guest backed shader is registered - * with. - * @hash: Hash item for lookup. - * @head: List head for staging lists or the compat shader manager list. - * @state: Staging state. - * - * The structure is protected by the cmdbuf lock. - */ -struct vmw_compat_shader { - u32 handle; - struct ttm_object_file *tfile; - struct drm_hash_item hash; - struct list_head head; - enum vmw_compat_shader_state state; -}; - -/** - * struct vmw_compat_shader_manager - Compat shader manager. - * - * @shaders: Hash table containing staged and commited compat shaders - * @list: List of commited shaders. - * @dev_priv: Pointer to a device private structure. - * - * @shaders and @list are protected by the cmdbuf mutex for now. - */ -struct vmw_compat_shader_manager { - struct drm_open_hash shaders; - struct list_head list; - struct vmw_private *dev_priv; -}; +static uint64_t vmw_user_shader_size; +static uint64_t vmw_shader_size; static void vmw_user_shader_free(struct vmw_resource *res); static struct vmw_resource * @@ -98,8 +55,6 @@ static int vmw_gb_shader_unbind(struct vmw_resource *res, struct ttm_validate_buffer *val_buf); static int vmw_gb_shader_destroy(struct vmw_resource *res); -static uint64_t vmw_user_shader_size; - static const struct vmw_user_resource_conv user_shader_conv = { .object_type = VMW_RES_SHADER, .base_obj_to_res = vmw_user_shader_base_to_res, @@ -347,6 +302,16 @@ static void vmw_user_shader_free(struct vmw_resource *res) vmw_user_shader_size); } +static void vmw_shader_free(struct vmw_resource *res) +{ + struct vmw_shader *shader = vmw_res_to_shader(res); + struct vmw_private *dev_priv = res->dev_priv; + + kfree(shader); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_shader_size); +} + /** * This function is called when user space has no more references on the * base object. It releases the base-object's reference on the resource object. @@ -371,13 +336,13 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, TTM_REF_USAGE); } -static int vmw_shader_alloc(struct vmw_private *dev_priv, - struct vmw_dma_buffer *buffer, - size_t shader_size, - size_t offset, - SVGA3dShaderType shader_type, - struct ttm_object_file *tfile, - u32 *handle) +static int vmw_user_shader_alloc(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buffer, + size_t shader_size, + size_t offset, + SVGA3dShaderType shader_type, + struct ttm_object_file *tfile, + u32 *handle) { struct vmw_user_shader *ushader; struct vmw_resource *res, *tmp; @@ -442,6 +407,56 @@ out: } +struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buffer, + size_t shader_size, + size_t offset, + SVGA3dShaderType shader_type) +{ + struct vmw_shader *shader; + struct vmw_resource *res; + int ret; + + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of shaders anyway. + */ + if (unlikely(vmw_shader_size == 0)) + vmw_shader_size = + ttm_round_pot(sizeof(struct vmw_shader)) + 128; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_shader_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for shader " + "creation.\n"); + goto out_err; + } + + shader = kzalloc(sizeof(*shader), GFP_KERNEL); + if (unlikely(shader == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_shader_size); + ret = -ENOMEM; + goto out_err; + } + + res = &shader->res; + + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_gb_shader_init(dev_priv, res, shader_size, + offset, shader_type, buffer, + vmw_shader_free); + +out_err: + return ret ? ERR_PTR(ret) : res; +} + + int vmw_shader_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -490,8 +505,8 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) goto out_bad_arg; - ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset, - shader_type, tfile, &arg->shader_handle); + ret = vmw_user_shader_alloc(dev_priv, buffer, arg->size, arg->offset, + shader_type, tfile, &arg->shader_handle); ttm_read_unlock(&dev_priv->reservation_sem); out_bad_arg: @@ -500,202 +515,83 @@ out_bad_arg: } /** - * vmw_compat_shader_lookup - Look up a compat shader - * - * @man: Pointer to the compat shader manager. - * @shader_type: The shader type, that combined with the user_key identifies - * the shader. - * @user_key: On entry, this should be a pointer to the user_key. - * On successful exit, it will contain the guest-backed shader's TTM handle. + * vmw_compat_shader_id_ok - Check whether a compat shader user key and + * shader type are within valid bounds. * - * Returns 0 on success. Non-zero on failure, in which case the value pointed - * to by @user_key is unmodified. - */ -int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, - SVGA3dShaderType shader_type, - u32 *user_key) -{ - struct drm_hash_item *hash; - int ret; - unsigned long key = *user_key | (shader_type << 24); - - ret = drm_ht_find_item(&man->shaders, key, &hash); - if (unlikely(ret != 0)) - return ret; - - *user_key = drm_hash_entry(hash, struct vmw_compat_shader, - hash)->handle; - - return 0; -} - -/** - * vmw_compat_shader_free - Free a compat shader. - * - * @man: Pointer to the compat shader manager. - * @entry: Pointer to a struct vmw_compat_shader. - * - * Frees a struct vmw_compat_shder entry and drops its reference to the - * guest backed shader. - */ -static void vmw_compat_shader_free(struct vmw_compat_shader_manager *man, - struct vmw_compat_shader *entry) -{ - list_del(&entry->head); - WARN_ON(drm_ht_remove_item(&man->shaders, &entry->hash)); - WARN_ON(ttm_ref_object_base_unref(entry->tfile, entry->handle, - TTM_REF_USAGE)); - kfree(entry); -} - -/** - * vmw_compat_shaders_commit - Commit a list of compat shader actions. - * - * @man: Pointer to the compat shader manager. - * @list: Caller's list of compat shader actions. + * @user_key: User space id of the shader. + * @shader_type: Shader type. * - * This function commits a list of compat shader additions or removals. - * It is typically called when the execbuf ioctl call triggering these - * actions has commited the fifo contents to the device. + * Returns true if valid false if not. */ -void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, - struct list_head *list) +static bool vmw_compat_shader_id_ok(u32 user_key, SVGA3dShaderType shader_type) { - struct vmw_compat_shader *entry, *next; - - list_for_each_entry_safe(entry, next, list, head) { - list_del(&entry->head); - switch (entry->state) { - case VMW_COMPAT_ADD: - entry->state = VMW_COMPAT_COMMITED; - list_add_tail(&entry->head, &man->list); - break; - case VMW_COMPAT_DEL: - ttm_ref_object_base_unref(entry->tfile, entry->handle, - TTM_REF_USAGE); - kfree(entry); - break; - default: - BUG(); - break; - } - } + return user_key <= ((1 << 20) - 1) && (unsigned) shader_type < 16; } /** - * vmw_compat_shaders_revert - Revert a list of compat shader actions + * vmw_compat_shader_key - Compute a hash key suitable for a compat shader. * - * @man: Pointer to the compat shader manager. - * @list: Caller's list of compat shader actions. + * @user_key: User space id of the shader. + * @shader_type: Shader type. * - * This function reverts a list of compat shader additions or removals. - * It is typically called when the execbuf ioctl call triggering these - * actions failed for some reason, and the command stream was never - * submitted. + * Returns a hash key suitable for a command buffer managed resource + * manager hash table. */ -void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, - struct list_head *list) +static u32 vmw_compat_shader_key(u32 user_key, SVGA3dShaderType shader_type) { - struct vmw_compat_shader *entry, *next; - int ret; - - list_for_each_entry_safe(entry, next, list, head) { - switch (entry->state) { - case VMW_COMPAT_ADD: - vmw_compat_shader_free(man, entry); - break; - case VMW_COMPAT_DEL: - ret = drm_ht_insert_item(&man->shaders, &entry->hash); - list_del(&entry->head); - list_add_tail(&entry->head, &man->list); - entry->state = VMW_COMPAT_COMMITED; - break; - default: - BUG(); - break; - } - } + return user_key | (shader_type << 20); } /** * vmw_compat_shader_remove - Stage a compat shader for removal. * - * @man: Pointer to the compat shader manager + * @man: Pointer to the compat shader manager identifying the shader namespace. * @user_key: The key that is used to identify the shader. The key is * unique to the shader type. * @shader_type: Shader type. - * @list: Caller's list of staged shader actions. - * - * This function stages a compat shader for removal and removes the key from - * the shader manager's hash table. If the shader was previously only staged - * for addition it is completely removed (But the execbuf code may keep a - * reference if it was bound to a context between addition and removal). If - * it was previously commited to the manager, it is staged for removal. + * @list: Caller's list of staged command buffer resource actions. */ -int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, +int vmw_compat_shader_remove(struct vmw_cmdbuf_res_manager *man, u32 user_key, SVGA3dShaderType shader_type, struct list_head *list) { - struct vmw_compat_shader *entry; - struct drm_hash_item *hash; - int ret; - - ret = drm_ht_find_item(&man->shaders, user_key | (shader_type << 24), - &hash); - if (likely(ret != 0)) + if (!vmw_compat_shader_id_ok(user_key, shader_type)) return -EINVAL; - entry = drm_hash_entry(hash, struct vmw_compat_shader, hash); - - switch (entry->state) { - case VMW_COMPAT_ADD: - vmw_compat_shader_free(man, entry); - break; - case VMW_COMPAT_COMMITED: - (void) drm_ht_remove_item(&man->shaders, &entry->hash); - list_del(&entry->head); - entry->state = VMW_COMPAT_DEL; - list_add_tail(&entry->head, list); - break; - default: - BUG(); - break; - } - - return 0; + return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_compat_shader, + vmw_compat_shader_key(user_key, + shader_type), + list); } /** - * vmw_compat_shader_add - Create a compat shader and add the - * key to the manager + * vmw_compat_shader_add - Create a compat shader and stage it for addition + * as a command buffer managed resource. * - * @man: Pointer to the compat shader manager + * @man: Pointer to the compat shader manager identifying the shader namespace. * @user_key: The key that is used to identify the shader. The key is * unique to the shader type. * @bytecode: Pointer to the bytecode of the shader. * @shader_type: Shader type. * @tfile: Pointer to a struct ttm_object_file that the guest-backed shader is * to be created with. - * @list: Caller's list of staged shader actions. + * @list: Caller's list of staged command buffer resource actions. * - * Note that only the key is added to the shader manager's hash table. - * The shader is not yet added to the shader manager's list of shaders. */ -int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, +int vmw_compat_shader_add(struct vmw_private *dev_priv, + struct vmw_cmdbuf_res_manager *man, u32 user_key, const void *bytecode, SVGA3dShaderType shader_type, size_t size, - struct ttm_object_file *tfile, struct list_head *list) { struct vmw_dma_buffer *buf; struct ttm_bo_kmap_obj map; bool is_iomem; - struct vmw_compat_shader *compat; - u32 handle; int ret; + struct vmw_resource *res; - if (user_key > ((1 << 24) - 1) || (unsigned) shader_type > 16) + if (!vmw_compat_shader_id_ok(user_key, shader_type)) return -EINVAL; /* Allocate and pin a DMA buffer */ @@ -703,7 +599,7 @@ int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, if (unlikely(buf == NULL)) return -ENOMEM; - ret = vmw_dmabuf_init(man->dev_priv, buf, size, &vmw_sys_ne_placement, + ret = vmw_dmabuf_init(dev_priv, buf, size, &vmw_sys_ne_placement, true, vmw_dmabuf_bo_free); if (unlikely(ret != 0)) goto out; @@ -728,84 +624,40 @@ int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, WARN_ON(ret != 0); ttm_bo_unreserve(&buf->base); - /* Create a guest-backed shader container backed by the dma buffer */ - ret = vmw_shader_alloc(man->dev_priv, buf, size, 0, shader_type, - tfile, &handle); - vmw_dmabuf_unreference(&buf); + res = vmw_shader_alloc(dev_priv, buf, size, 0, shader_type); if (unlikely(ret != 0)) goto no_reserve; - /* - * Create a compat shader structure and stage it for insertion - * in the manager - */ - compat = kzalloc(sizeof(*compat), GFP_KERNEL); - if (compat == NULL) - goto no_compat; - - compat->hash.key = user_key | (shader_type << 24); - ret = drm_ht_insert_item(&man->shaders, &compat->hash); - if (unlikely(ret != 0)) - goto out_invalid_key; - - compat->state = VMW_COMPAT_ADD; - compat->handle = handle; - compat->tfile = tfile; - list_add_tail(&compat->head, list); - - return 0; -out_invalid_key: - kfree(compat); -no_compat: - ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); + ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_compat_shader, + vmw_compat_shader_key(user_key, shader_type), + res, list); + vmw_resource_unreference(&res); no_reserve: + vmw_dmabuf_unreference(&buf); out: return ret; } /** - * vmw_compat_shader_man_create - Create a compat shader manager - * - * @dev_priv: Pointer to a device private structure. - * - * Typically done at file open time. If successful returns a pointer to a - * compat shader manager. Otherwise returns an error pointer. - */ -struct vmw_compat_shader_manager * -vmw_compat_shader_man_create(struct vmw_private *dev_priv) -{ - struct vmw_compat_shader_manager *man; - int ret; - - man = kzalloc(sizeof(*man), GFP_KERNEL); - if (man == NULL) - return ERR_PTR(-ENOMEM); - - man->dev_priv = dev_priv; - INIT_LIST_HEAD(&man->list); - ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER); - if (ret == 0) - return man; - - kfree(man); - return ERR_PTR(ret); -} - -/** - * vmw_compat_shader_man_destroy - Destroy a compat shader manager + * vmw_compat_shader_lookup - Look up a compat shader * - * @man: Pointer to the shader manager to destroy. + * @man: Pointer to the command buffer managed resource manager identifying + * the shader namespace. + * @user_key: The user space id of the shader. + * @shader_type: The shader type. * - * Typically done at file close time. + * Returns a refcounted pointer to a struct vmw_resource if the shader was + * found. An error pointer otherwise. */ -void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man) +struct vmw_resource * +vmw_compat_shader_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key, + SVGA3dShaderType shader_type) { - struct vmw_compat_shader *entry, *next; - - mutex_lock(&man->dev_priv->cmdbuf_mutex); - list_for_each_entry_safe(entry, next, &man->list, head) - vmw_compat_shader_free(man, entry); + if (!vmw_compat_shader_id_ok(user_key, shader_type)) + return ERR_PTR(-EINVAL); - mutex_unlock(&man->dev_priv->cmdbuf_mutex); - kfree(man); + return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_compat_shader, + vmw_compat_shader_key(user_key, + shader_type)); } -- cgit v0.10.2 From 57127efaf1adc5e0bb2d3fa5a01bd97f71af7836 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 4 Jul 2014 08:20:11 +0100 Subject: drm/i915: Show cursor size in debugfs/i915_display_info Inlcude the pipe-size and cursor-size in debugfs. Signed-off-by: Chris Wilson Reviewed-by: Damien Lespiau Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 6b7b32b..e415866 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2356,17 +2356,17 @@ static int i915_display_info(struct seq_file *m, void *unused) bool active; int x, y; - seq_printf(m, "CRTC %d: pipe: %c, active: %s\n", + seq_printf(m, "CRTC %d: pipe: %c, active=%s (size=%dx%d)\n", crtc->base.base.id, pipe_name(crtc->pipe), - yesno(crtc->active)); + yesno(crtc->active), crtc->config.pipe_src_w, crtc->config.pipe_src_h); if (crtc->active) { intel_crtc_info(m, crtc); active = cursor_position(dev, crtc->pipe, &x, &y); - seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n", + seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n", yesno(crtc->cursor_base), - x, y, crtc->cursor_addr, - yesno(active)); + x, y, crtc->cursor_width, crtc->cursor_height, + crtc->cursor_addr, yesno(active)); } seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s \n", -- cgit v0.10.2 From 84448288546d13d7e06fd6638fb78ddff559b399 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Apr 2014 11:20:42 +0100 Subject: drm: msm: update to use component match support Update MSM's DRM driver to use the component match support rather than add_components. Reviewed-by: Rob Clark Signed-off-by: Russell King diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9a5d87d..a322029 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -905,12 +905,41 @@ static int compare_of(struct device *dev, void *data) { return dev->of_node == data; } +#else +static int compare_dev(struct device *dev, void *data) +{ + return dev == data; +} +#endif + +static int msm_drm_bind(struct device *dev) +{ + return drm_platform_init(&msm_driver, to_platform_device(dev)); +} + +static void msm_drm_unbind(struct device *dev) +{ + drm_put_dev(platform_get_drvdata(to_platform_device(dev))); +} + +static const struct component_master_ops msm_drm_ops = { + .bind = msm_drm_bind, + .unbind = msm_drm_unbind, +}; + +/* + * Platform driver: + */ -static int msm_drm_add_components(struct device *master, struct master *m) +static int msm_pdev_probe(struct platform_device *pdev) { - struct device_node *np = master->of_node; + struct component_match *match = NULL; +#ifdef CONFIG_OF + /* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx + * (or probably any other).. so probably some room for some helpers + */ + struct device_node *np = pdev->dev.of_node; unsigned i; - int ret; for (i = 0; ; i++) { struct device_node *node; @@ -919,22 +948,9 @@ static int msm_drm_add_components(struct device *master, struct master *m) if (!node) break; - ret = component_master_add_child(m, compare_of, node); - of_node_put(node); - - if (ret) - return ret; + component_match_add(&pdev->dev, &match, compare_of, node); } - return 0; -} #else -static int compare_dev(struct device *dev, void *data) -{ - return dev == data; -} - -static int msm_drm_add_components(struct device *master, struct master *m) -{ /* For non-DT case, it kinda sucks. We don't actually have a way * to know whether or not we are waiting for certain devices (or if * they are simply not present). But for non-DT we only need to @@ -958,41 +974,12 @@ static int msm_drm_add_components(struct device *master, struct master *m) return -EPROBE_DEFER; } - ret = component_master_add_child(m, compare_dev, dev); - if (ret) { - DBG("could not add child: %d", ret); - return ret; - } + component_match_add(&pdev->dev, &match, compare_dev, dev); } - - return 0; -} #endif -static int msm_drm_bind(struct device *dev) -{ - return drm_platform_init(&msm_driver, to_platform_device(dev)); -} - -static void msm_drm_unbind(struct device *dev) -{ - drm_put_dev(platform_get_drvdata(to_platform_device(dev))); -} - -static const struct component_master_ops msm_drm_ops = { - .add_components = msm_drm_add_components, - .bind = msm_drm_bind, - .unbind = msm_drm_unbind, -}; - -/* - * Platform driver: - */ - -static int msm_pdev_probe(struct platform_device *pdev) -{ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - return component_master_add(&pdev->dev, &msm_drm_ops); + return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match); } static int msm_pdev_remove(struct platform_device *pdev) -- cgit v0.10.2 From dfcab17e57aa784f174bc3f6beb6086f9956575a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:47 +0300 Subject: drm/i915: Change vlv cdclk to use kHz units MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use kHz units in vlv cdclk code since that's more customary. Also replace the precomputed 90% values with *9/10 computation for extra clarity. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6d038c5..11a303e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4465,6 +4465,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev) intel_display_set_init_power(dev_priv, false); } +/* returns HPLL frequency in kHz */ int valleyview_get_vco(struct drm_i915_private *dev_priv) { int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 }; @@ -4475,7 +4476,7 @@ int valleyview_get_vco(struct drm_i915_private *dev_priv) CCK_FUSE_HPLL_FREQ_MASK; mutex_unlock(&dev_priv->dpio_lock); - return vco_freq[hpll_freq]; + return vco_freq[hpll_freq] * 1000; } /* Adjust CDclk dividers to allow high res or save power if possible */ @@ -4487,9 +4488,9 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) WARN_ON(valleyview_cur_cdclk(dev_priv) != dev_priv->vlv_cdclk_freq); dev_priv->vlv_cdclk_freq = cdclk; - if (cdclk >= 320) /* jump to highest voltage for 400MHz too */ + if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */ cmd = 2; - else if (cdclk == 266) + else if (cdclk == 266667) cmd = 1; else cmd = 0; @@ -4506,11 +4507,11 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) } mutex_unlock(&dev_priv->rps.hw_lock); - if (cdclk == 400) { + if (cdclk == 400000) { u32 divider, vco; vco = valleyview_get_vco(dev_priv); - divider = ((vco << 1) / cdclk) - 1; + divider = DIV_ROUND_CLOSEST(vco << 1, cdclk) - 1; mutex_lock(&dev_priv->dpio_lock); /* adjust cdclk divider */ @@ -4530,7 +4531,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) * For high bandwidth configs, we set a higher latency in the bunit * so that the core display fetch happens in time to avoid underruns. */ - if (cdclk == 400) + if (cdclk == 400000) val |= 4500 / 250; /* 4.5 usec */ else val |= 3000 / 250; /* 3.0 usec */ @@ -4554,7 +4555,7 @@ int valleyview_cur_cdclk(struct drm_i915_private *dev_priv) divider &= 0xf; - cur_cdclk = (vco << 1) / (divider + 1); + cur_cdclk = DIV_ROUND_CLOSEST(vco << 1, divider + 1); return cur_cdclk; } @@ -4571,12 +4572,12 @@ static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, * So we check to see whether we're above 90% of the lower bin and * adjust if needed. */ - if (max_pixclk > 288000) { - return 400; - } else if (max_pixclk > 240000) { - return 320; - } else - return 266; + if (max_pixclk > 320000*9/10) + return 400000; + else if (max_pixclk > 266667*9/10) + return 320000; + else + return 266667; /* Looks like the 200MHz CDclk freq doesn't work on some configs */ } diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d33b61d..9ce4f09 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -86,7 +86,7 @@ static void gmbus_set_freq(struct drm_i915_private *dev_priv) BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); - vco = valleyview_get_vco(dev_priv); + vco = valleyview_get_vco(dev_priv) / 1000; /* Get the CDCLK divide ratio */ cdclk_div = get_disp_clk_div(dev_priv, CDCLK); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d2facba..b907ee6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5596,7 +5596,7 @@ static void valleyview_init_clock_gating(struct drm_device *dev) DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); dev_priv->vlv_cdclk_freq = valleyview_cur_cdclk(dev_priv); - DRM_DEBUG_DRIVER("Current CD clock rate: %d MHz", + DRM_DEBUG_DRIVER("Current CD clock rate: %d kHz", dev_priv->vlv_cdclk_freq); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); -- cgit v0.10.2 From 9cf33db5eb6a485a16668e19dbcfdfc0f6c61090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:48 +0300 Subject: drm/i915: Give names to the CCK_DISPLAY_CLOCK_CONTROL bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using magic values for CCK frequency bits. Also the mask we were using for the requested frequency was one bit too short. Fix it up. Note: This also fixes the #define for a mask (spotted by Jesse in his review). Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes [danvet: Add note about mask change.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3488567..66151ff 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -584,6 +584,11 @@ enum punit_power_well { #define DSI_PLL_M1_DIV_SHIFT 0 #define DSI_PLL_M1_DIV_MASK (0x1ff << 0) #define CCK_DISPLAY_CLOCK_CONTROL 0x6b +#define DISPLAY_TRUNK_FORCE_ON (1 << 17) +#define DISPLAY_TRUNK_FORCE_OFF (1 << 16) +#define DISPLAY_FREQUENCY_STATUS (0x1f << 8) +#define DISPLAY_FREQUENCY_STATUS_SHIFT 8 +#define DISPLAY_FREQUENCY_VALUES (0x1f << 0) /** * DOC: DPIO diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 11a303e..3feaaba 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4516,7 +4516,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) mutex_lock(&dev_priv->dpio_lock); /* adjust cdclk divider */ val = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL); - val &= ~0xf; + val &= ~DISPLAY_FREQUENCY_VALUES; val |= divider; vlv_cck_write(dev_priv, CCK_DISPLAY_CLOCK_CONTROL, val); mutex_unlock(&dev_priv->dpio_lock); @@ -4553,7 +4553,7 @@ int valleyview_cur_cdclk(struct drm_i915_private *dev_priv) divider = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL); mutex_unlock(&dev_priv->dpio_lock); - divider &= 0xf; + divider &= DISPLAY_FREQUENCY_VALUES; cur_cdclk = DIV_ROUND_CLOSEST(vco << 1, divider + 1); -- cgit v0.10.2 From d197b7d3480b5c9a3c33b224684fa942d76d1e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:49 +0300 Subject: drm/i915: Move vlv cdclk code to .get_display_clock_speed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a standard hook for reading out the current cdclk. Move the VLV code from valleyview_cur_cdclk() to .get_display_clock_speed(). Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3feaaba..310218a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4485,7 +4485,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) struct drm_i915_private *dev_priv = dev->dev_private; u32 val, cmd; - WARN_ON(valleyview_cur_cdclk(dev_priv) != dev_priv->vlv_cdclk_freq); + WARN_ON(dev_priv->display.get_display_clock_speed(dev) != dev_priv->vlv_cdclk_freq); dev_priv->vlv_cdclk_freq = cdclk; if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */ @@ -4542,24 +4542,6 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) intel_i2c_reset(dev); } -int valleyview_cur_cdclk(struct drm_i915_private *dev_priv) -{ - int cur_cdclk, vco; - int divider; - - vco = valleyview_get_vco(dev_priv); - - mutex_lock(&dev_priv->dpio_lock); - divider = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL); - mutex_unlock(&dev_priv->dpio_lock); - - divider &= DISPLAY_FREQUENCY_VALUES; - - cur_cdclk = DIV_ROUND_CLOSEST(vco << 1, divider + 1); - - return cur_cdclk; -} - static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, int max_pixclk) { @@ -5269,7 +5251,18 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc, static int valleyview_get_display_clock_speed(struct drm_device *dev) { - return 400000; /* FIXME */ + struct drm_i915_private *dev_priv = dev->dev_private; + int vco = valleyview_get_vco(dev_priv); + u32 val; + int divider; + + mutex_lock(&dev_priv->dpio_lock); + val = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL); + mutex_unlock(&dev_priv->dpio_lock); + + divider = val & DISPLAY_FREQUENCY_VALUES; + + return DIV_ROUND_CLOSEST(vco << 1, divider + 1); } static int i945_get_display_clock_speed(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5f7c7bd..056fca9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -722,7 +722,6 @@ void intel_ddi_get_config(struct intel_encoder *encoder, const char *intel_output_name(int output); bool intel_has_pending_fb_unpin(struct drm_device *dev); int intel_pch_rawclk(struct drm_device *dev); -int valleyview_cur_cdclk(struct drm_i915_private *dev_priv); void intel_mark_busy(struct drm_device *dev); void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, struct intel_engine_cs *ring); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index b907ee6..146101c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5595,7 +5595,7 @@ static void valleyview_init_clock_gating(struct drm_device *dev) } DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); - dev_priv->vlv_cdclk_freq = valleyview_cur_cdclk(dev_priv); + dev_priv->vlv_cdclk_freq = dev_priv->display.get_display_clock_speed(dev); DRM_DEBUG_DRIVER("Current CD clock rate: %d kHz", dev_priv->vlv_cdclk_freq); -- cgit v0.10.2 From 29dc7ef3bbd3a78d35154f8b103b2f8a724f7986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:50 +0300 Subject: drm/i915: Handle 320 vs. 333 MHz cdclk on vlv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Depending on the HPLL frequency one of the supported cdclk frquencies is either 320MHz or 333MHz. Figure out which one it is to accurately pick the minimal required cdclk. This would also avoid a warning from the cdclk code where it compares the actual cdclk read out from the hardware with a value that was calculated using valleyview_calc_cdclk(). Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 310218a..5940011 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4545,19 +4545,22 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, int max_pixclk) { + int vco = valleyview_get_vco(dev_priv); + int freq_320 = (vco << 1) % 320000 != 0 ? 333333 : 320000; + /* * Really only a few cases to deal with, as only 4 CDclks are supported: * 200MHz * 267MHz - * 320MHz + * 320/333MHz (depends on HPLL freq) * 400MHz * So we check to see whether we're above 90% of the lower bin and * adjust if needed. */ - if (max_pixclk > 320000*9/10) + if (max_pixclk > freq_320*9/10) return 400000; else if (max_pixclk > 266667*9/10) - return 320000; + return freq_320; else return 266667; /* Looks like the 200MHz CDclk freq doesn't work on some configs */ -- cgit v0.10.2 From e37c67a1c5743ac7fc7f946290fee96f84248ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:51 +0300 Subject: drm/i915: Use 200MHz cdclk on vlv when all pipes are off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the cdclk frequency to 200MHz on vlv when all pipes are off. In theory we should be able to use 200MHz also when the pixel clock is at most 90% of 200MHz. However in practice all we seem to get is a solid color picture or an otherwise corrupted display. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5940011..d127e76 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4556,14 +4556,19 @@ static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, * 400MHz * So we check to see whether we're above 90% of the lower bin and * adjust if needed. + * + * We seem to get an unstable or solid color picture at 200MHz. + * Not sure what's wrong. For now use 200MHz only when all pipes + * are off. */ if (max_pixclk > freq_320*9/10) return 400000; else if (max_pixclk > 266667*9/10) return freq_320; - else + else if (max_pixclk > 0) return 266667; - /* Looks like the 200MHz CDclk freq doesn't work on some configs */ + else + return 200000; } /* compute the max pixel clock for new configuration */ -- cgit v0.10.2 From a877e8015c0d8f308fee925ee72dc6451de616ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:52 +0300 Subject: drm/i915: Wait for cdclk change to occure when going for 400MHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VLV Punit doesn't support the 400MHz cdclk option, so we bypass the Punit and poke at CCK directly. However we forgot to wait for the frequeency change to complete. Poll the CCK clock status to make sure the clock has changed before we fire up any pipes. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d127e76..251a095 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4519,6 +4519,11 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) val &= ~DISPLAY_FREQUENCY_VALUES; val |= divider; vlv_cck_write(dev_priv, CCK_DISPLAY_CLOCK_CONTROL, val); + + if (wait_for((vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL) & + DISPLAY_FREQUENCY_STATUS) == (divider << DISPLAY_FREQUENCY_STATUS_SHIFT), + 50)) + DRM_ERROR("timed out waiting for CDclk change\n"); mutex_unlock(&dev_priv->dpio_lock); } -- cgit v0.10.2 From 7d007f40596670c8fddfae583952b091b02cf0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:53 +0300 Subject: drm/i915: Warn if there's a cdclk change in progess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If someone is interested in the current cdclk frquency it should be stable and not in process of changing frquency. Warn if the current and requested cdclk don't match in .get_display_clock_spee() on vlv. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 251a095..ac3ee2c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5275,6 +5275,10 @@ static int valleyview_get_display_clock_speed(struct drm_device *dev) divider = val & DISPLAY_FREQUENCY_VALUES; + WARN((val & DISPLAY_FREQUENCY_STATUS) != + (divider << DISPLAY_FREQUENCY_STATUS_SHIFT), + "cdclk change in progress\n"); + return DIV_ROUND_CLOSEST(vco << 1, divider + 1); } -- cgit v0.10.2 From f8bf63fdcb1f82459dae7a3f22ee5ce92f3ea727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:54 +0300 Subject: drm/i915: Kill duplicated cdclk readout code from i2c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a slightly different way of readoing out the cdclk in gmbus_set_freq(). Kill that and just call .get_display_clock_speed(). Also need to remove the GMBUSFREQ update from intel_i2c_reset() since that gets called way too early. Let's do it in intel_modeset_init_hw() instead, and also pull the initial vlv_cdclk_freq update there from init_clock gating. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ac3ee2c..5155811 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4466,7 +4466,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev) } /* returns HPLL frequency in kHz */ -int valleyview_get_vco(struct drm_i915_private *dev_priv) +static int valleyview_get_vco(struct drm_i915_private *dev_priv) { int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 }; @@ -4479,6 +4479,22 @@ int valleyview_get_vco(struct drm_i915_private *dev_priv) return vco_freq[hpll_freq] * 1000; } +static void vlv_update_cdclk(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->vlv_cdclk_freq = dev_priv->display.get_display_clock_speed(dev); + DRM_DEBUG_DRIVER("Current CD clock rate: %d kHz", + dev_priv->vlv_cdclk_freq); + + /* + * Program the gmbus_freq based on the cdclk frequency. + * BSpec erroneously claims we should aim for 4MHz, but + * in fact 1MHz is the correct frequency. + */ + I915_WRITE(GMBUSFREQ_VLV, dev_priv->vlv_cdclk_freq); +} + /* Adjust CDclk dividers to allow high res or save power if possible */ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) { @@ -4486,7 +4502,6 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) u32 val, cmd; WARN_ON(dev_priv->display.get_display_clock_speed(dev) != dev_priv->vlv_cdclk_freq); - dev_priv->vlv_cdclk_freq = cdclk; if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */ cmd = 2; @@ -4543,8 +4558,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) vlv_bunit_write(dev_priv, BUNIT_REG_BISOC, val); mutex_unlock(&dev_priv->dpio_lock); - /* Since we changed the CDclk, we need to update the GMBUSFREQ too */ - intel_i2c_reset(dev); + vlv_update_cdclk(dev); } static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, @@ -12440,6 +12454,9 @@ void intel_modeset_init_hw(struct drm_device *dev) { intel_prepare_ddi(dev); + if (IS_VALLEYVIEW(dev)) + vlv_update_cdclk(dev); + intel_init_clock_gating(dev); intel_reset_dpio(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 056fca9..4d4f735 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -830,7 +830,6 @@ void hsw_disable_ips(struct intel_crtc *crtc); void intel_display_set_init_power(struct drm_i915_private *dev, bool enable); enum intel_display_power_domain intel_display_port_power_domain(struct intel_encoder *intel_encoder); -int valleyview_get_vco(struct drm_i915_private *dev_priv); void intel_mode_from_pipe_config(struct drm_display_mode *mode, struct intel_crtc_config *pipe_config); int intel_format_to_fourcc(int format); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 9ce4f09..b31088a 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -34,11 +34,6 @@ #include #include "i915_drv.h" -enum disp_clk { - CDCLK, - CZCLK -}; - struct gmbus_port { const char *name; int reg; @@ -63,60 +58,11 @@ to_intel_gmbus(struct i2c_adapter *i2c) return container_of(i2c, struct intel_gmbus, adapter); } -static int get_disp_clk_div(struct drm_i915_private *dev_priv, - enum disp_clk clk) -{ - u32 reg_val; - int clk_ratio; - - reg_val = I915_READ(CZCLK_CDCLK_FREQ_RATIO); - - if (clk == CDCLK) - clk_ratio = - ((reg_val & CDCLK_FREQ_MASK) >> CDCLK_FREQ_SHIFT) + 1; - else - clk_ratio = (reg_val & CZCLK_FREQ_MASK) + 1; - - return clk_ratio; -} - -static void gmbus_set_freq(struct drm_i915_private *dev_priv) -{ - int vco, gmbus_freq = 0, cdclk_div; - - BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); - - vco = valleyview_get_vco(dev_priv) / 1000; - - /* Get the CDCLK divide ratio */ - cdclk_div = get_disp_clk_div(dev_priv, CDCLK); - - /* - * Program the gmbus_freq based on the cdclk frequency. - * BSpec erroneously claims we should aim for 4MHz, but - * in fact 1MHz is the correct frequency. - */ - if (cdclk_div) - gmbus_freq = (vco << 1) / cdclk_div; - - if (WARN_ON(gmbus_freq == 0)) - return; - - I915_WRITE(GMBUSFREQ_VLV, gmbus_freq); -} - void intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - /* - * In BIOS-less system, program the correct gmbus frequency - * before reading edid. - */ - if (IS_VALLEYVIEW(dev)) - gmbus_set_freq(dev_priv); - I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 146101c..719db71 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5595,10 +5595,6 @@ static void valleyview_init_clock_gating(struct drm_device *dev) } DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); - dev_priv->vlv_cdclk_freq = dev_priv->display.get_display_clock_speed(dev); - DRM_DEBUG_DRIVER("Current CD clock rate: %d kHz", - dev_priv->vlv_cdclk_freq); - I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); /* WaDisableEarlyCull:vlv */ -- cgit v0.10.2 From aa519f231d4ce3ad20932cbd2bb1aae4010ff6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:55 +0300 Subject: drm/i915: Pull the cmnlane tricks into its own power well ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the clutter in __vlv_set_power_well() by moving the cmnlane handling into custom enable/disable hooks for the cmnlane. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 719db71..16a94e1 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6013,31 +6013,9 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv, void __vlv_set_power_well(struct drm_i915_private *dev_priv, enum punit_power_well power_well_id, bool enable) { - struct drm_device *dev = dev_priv->dev; u32 mask; u32 state; u32 ctrl; - enum pipe pipe; - - if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC) { - if (enable) { - /* - * Enable the CRI clock source so we can get at the - * display and the reference clock for VGA - * hotplug / manual detection. - */ - I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | - DPLL_REFA_CLK_ENABLE_VLV | - DPLL_INTEGRATED_CRI_CLK_VLV); - udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ - } else { - for_each_pipe(pipe) - assert_pll_disabled(dev_priv, pipe); - /* Assert common reset */ - I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) & - ~DPIO_CMNRST); - } - } mask = PUNIT_PWRGT_MASK(power_well_id); state = enable ? PUNIT_PWRGT_PWR_ON(power_well_id) : @@ -6065,20 +6043,6 @@ void __vlv_set_power_well(struct drm_i915_private *dev_priv, out: mutex_unlock(&dev_priv->rps.hw_lock); - - /* - * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - - * 6. De-assert cmn_reset/side_reset. Same as VLV X0. - * a. GUnit 0x2110 bit[0] set to 1 (def 0) - * b. The other bits such as sfr settings / modesel may all - * be set to 0. - * - * This should only be done on init and resume from S3 with - * both PLLs disabled, or we risk losing DPIO and PLL - * synchronization. - */ - if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC && enable) - I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST); } static void vlv_set_power_well(struct drm_i915_private *dev_priv, @@ -6178,6 +6142,53 @@ static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, vlv_set_power_well(dev_priv, power_well, false); } +static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); + + /* + * Enable the CRI clock source so we can get at the + * display and the reference clock for VGA + * hotplug / manual detection. + */ + I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | + DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); + udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ + + vlv_set_power_well(dev_priv, power_well, true); + + /* + * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - + * 6. De-assert cmn_reset/side_reset. Same as VLV X0. + * a. GUnit 0x2110 bit[0] set to 1 (def 0) + * b. The other bits such as sfr settings / modesel may all + * be set to 0. + * + * This should only be done on init and resume from S3 with + * both PLLs disabled, or we risk losing DPIO and PLL + * synchronization. + */ + I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST); +} + +static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + struct drm_device *dev = dev_priv->dev; + enum pipe pipe; + + WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); + + for_each_pipe(pipe) + assert_pll_disabled(dev_priv, pipe); + + /* Assert common reset */ + I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) & ~DPIO_CMNRST); + + vlv_set_power_well(dev_priv, power_well, false); +} + static void check_power_well_state(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -6426,6 +6437,13 @@ static const struct i915_power_well_ops vlv_display_power_well_ops = { .is_enabled = vlv_power_well_enabled, }; +static const struct i915_power_well_ops vlv_dpio_cmn_power_well_ops = { + .sync_hw = vlv_power_well_sync_hw, + .enable = vlv_dpio_cmn_power_well_enable, + .disable = vlv_dpio_cmn_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + static const struct i915_power_well_ops vlv_dpio_power_well_ops = { .sync_hw = vlv_power_well_sync_hw, .enable = vlv_power_well_enable, @@ -6486,7 +6504,7 @@ static struct i915_power_well vlv_power_wells[] = { .name = "dpio-common", .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS, .data = PUNIT_POWER_WELL_DPIO_CMN_BC, - .ops = &vlv_dpio_power_well_ops, + .ops = &vlv_dpio_cmn_power_well_ops, }, }; -- cgit v0.10.2 From d2011dc8d41b20dc0ec0bf741c61fe500dc8d0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 Jun 2014 13:37:56 +0300 Subject: drm/i915: Move VLV cmnlane workaround to intel_power_domains_init_hw() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the CMNRESET deassert is part of the cmnlane power well, intel_reset_dpio() is called too late to make any difference. We've deasserted CMNRESET by that time, and so the off+on toggle w/a will never kick in. Move the workaround to intel_power_domains_init_hw() where it gets called before we enable the init power domain. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5155811..d675df8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1514,9 +1514,6 @@ static void intel_reset_dpio(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (!IS_VALLEYVIEW(dev)) - return; - if (IS_CHERRYVIEW(dev)) { enum dpio_phy phy; u32 val; @@ -1538,26 +1535,6 @@ static void intel_reset_dpio(struct drm_device *dev) I915_WRITE(DISPLAY_PHY_CONTROL, PHY_COM_LANE_RESET_DEASSERT(phy, val)); } - - } else { - /* - * If DPIO has already been reset, e.g. by BIOS, just skip all - * this. - */ - if (I915_READ(DPIO_CTL) & DPIO_CMNRST) - return; - - /* - * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx: - * Need to assert and de-assert PHY SB reset by gating the - * common lane power, then un-gating it. - * Simply ungating isn't enough to reset the PHY enough to get - * ports and lanes running. - */ - __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC, - false); - __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC, - true); } } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4d4f735..0ef04ea 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1003,8 +1003,7 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv); void intel_init_runtime_pm(struct drm_i915_private *dev_priv); void intel_fini_runtime_pm(struct drm_i915_private *dev_priv); void ilk_wm_get_hw_state(struct drm_device *dev); -void __vlv_set_power_well(struct drm_i915_private *dev_priv, - enum punit_power_well power_well_id, bool enable); + /* intel_sdvo.c */ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 16a94e1..01498fa 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6010,9 +6010,10 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv, return true; } -void __vlv_set_power_well(struct drm_i915_private *dev_priv, - enum punit_power_well power_well_id, bool enable) +static void vlv_set_power_well(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, bool enable) { + enum punit_power_well power_well_id = power_well->data; u32 mask; u32 state; u32 ctrl; @@ -6045,14 +6046,6 @@ out: mutex_unlock(&dev_priv->rps.hw_lock); } -static void vlv_set_power_well(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, bool enable) -{ - enum punit_power_well power_well_id = power_well->data; - - __vlv_set_power_well(dev_priv, power_well_id, enable); -} - static void vlv_power_well_sync_hw(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -6508,6 +6501,21 @@ static struct i915_power_well vlv_power_wells[] = { }, }; +static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, + enum punit_power_well power_well_id) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + struct i915_power_well *power_well; + int i; + + for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) { + if (power_well->data == power_well_id) + return power_well; + } + + return NULL; +} + #define set_power_wells(power_domains, __power_wells) ({ \ (power_domains)->power_wells = (__power_wells); \ (power_domains)->power_well_count = ARRAY_SIZE(__power_wells); \ @@ -6558,11 +6566,50 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv) mutex_unlock(&power_domains->lock); } +static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) +{ + struct i915_power_well *cmn = + lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC); + struct i915_power_well *disp2d = + lookup_power_well(dev_priv, PUNIT_POWER_WELL_DISP2D); + + /* nothing to do if common lane is already off */ + if (!cmn->ops->is_enabled(dev_priv, cmn)) + return; + + /* If the display might be already active skip this */ + if (disp2d->ops->is_enabled(dev_priv, disp2d) && + I915_READ(DPIO_CTL) & DPIO_CMNRST) + return; + + DRM_DEBUG_KMS("toggling display PHY side reset\n"); + + /* cmnlane needs DPLL registers */ + disp2d->ops->enable(dev_priv, disp2d); + + /* + * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx: + * Need to assert and de-assert PHY SB reset by gating the + * common lane power, then un-gating it. + * Simply ungating isn't enough to reset the PHY enough to get + * ports and lanes running. + */ + cmn->ops->disable(dev_priv, cmn); +} + void intel_power_domains_init_hw(struct drm_i915_private *dev_priv) { + struct drm_device *dev = dev_priv->dev; struct i915_power_domains *power_domains = &dev_priv->power_domains; power_domains->initializing = true; + + if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) { + mutex_lock(&power_domains->lock); + vlv_cmnlane_wa(dev_priv); + mutex_unlock(&power_domains->lock); + } + /* For now, we need the power well to be always enabled. */ intel_display_set_init_power(dev_priv, true); intel_power_domains_resume(dev_priv); -- cgit v0.10.2 From 5209b1f4c4f8036f52f5ac2df2afc806254f247f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 1 Jul 2014 12:36:17 +0300 Subject: drm/i915: gmch: factor out intel_set_memory_cxsr This functionality will be also needed by an upcoming patch, so factor it out. As a bonus this also makes things a bit more uniform across platforms. Note that this also changes the register read-modify-write to a simple write during disabling. This is what we do during enabling anyway and according to the spec all the relevant bits are reserved-MBZ or reserved with a 0 default value. v2: - unchanged v3: - fix missing cxsr disabling on pineview (Deepak) Signed-off-by: Imre Deak Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a503a53..3836286 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2649,6 +2649,8 @@ extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void valleyview_set_rps(struct drm_device *dev, u8 val); extern int valleyview_rps_max_freq(struct drm_i915_private *dev_priv); extern int valleyview_rps_min_freq(struct drm_i915_private *dev_priv); +extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, + bool enable); extern void intel_detect_pch(struct drm_device *dev); extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); extern int intel_enable_rc6(const struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 01498fa..ded4294 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -816,12 +816,33 @@ static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, return NULL; } -static void pineview_disable_cxsr(struct drm_device *dev) +void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_device *dev = dev_priv->dev; + u32 val; - /* deactivate cxsr */ - I915_WRITE(DSPFW3, I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN); + if (IS_VALLEYVIEW(dev)) { + I915_WRITE(FW_BLC_SELF_VLV, enable ? FW_CSPWRDWNEN : 0); + } else if (IS_G4X(dev) || IS_CRESTLINE(dev)) { + I915_WRITE(FW_BLC_SELF, enable ? FW_BLC_SELF_EN : 0); + } else if (IS_PINEVIEW(dev)) { + val = I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN; + val |= enable ? PINEVIEW_SELF_REFRESH_EN : 0; + I915_WRITE(DSPFW3, val); + } else if (IS_I945G(dev) || IS_I945GM(dev)) { + val = enable ? _MASKED_BIT_ENABLE(FW_BLC_SELF_EN) : + _MASKED_BIT_DISABLE(FW_BLC_SELF_EN); + I915_WRITE(FW_BLC_SELF, val); + } else if (IS_I915GM(dev)) { + val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) : + _MASKED_BIT_DISABLE(INSTPM_SELF_EN); + I915_WRITE(INSTPM, val); + } else { + return; + } + + DRM_DEBUG_KMS("memory self-refresh is %s\n", + enable ? "enabled" : "disabled"); } /* @@ -1060,7 +1081,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) dev_priv->fsb_freq, dev_priv->mem_freq); if (!latency) { DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n"); - pineview_disable_cxsr(dev); + intel_set_memory_cxsr(dev_priv, false); return; } @@ -1111,13 +1132,9 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) I915_WRITE(DSPFW3, reg); DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg); - /* activate cxsr */ - I915_WRITE(DSPFW3, - I915_READ(DSPFW3) | PINEVIEW_SELF_REFRESH_EN); - DRM_DEBUG_KMS("Self-refresh is enabled\n"); + intel_set_memory_cxsr(dev_priv, true); } else { - pineview_disable_cxsr(dev); - DRM_DEBUG_KMS("Self-refresh is disabled\n"); + intel_set_memory_cxsr(dev_priv, false); } } @@ -1369,10 +1386,9 @@ static void valleyview_update_wm(struct drm_crtc *crtc) &valleyview_wm_info, &valleyview_cursor_wm_info, &ignore_plane_sr, &cursor_sr)) { - I915_WRITE(FW_BLC_SELF_VLV, FW_CSPWRDWNEN); + intel_set_memory_cxsr(dev_priv, true); } else { - I915_WRITE(FW_BLC_SELF_VLV, - I915_READ(FW_BLC_SELF_VLV) & ~FW_CSPWRDWNEN); + intel_set_memory_cxsr(dev_priv, false); plane_sr = cursor_sr = 0; } @@ -1421,10 +1437,9 @@ static void g4x_update_wm(struct drm_crtc *crtc) &g4x_wm_info, &g4x_cursor_wm_info, &plane_sr, &cursor_sr)) { - I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); + intel_set_memory_cxsr(dev_priv, true); } else { - I915_WRITE(FW_BLC_SELF, - I915_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN); + intel_set_memory_cxsr(dev_priv, false); plane_sr = cursor_sr = 0; } @@ -1495,13 +1510,10 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) DRM_DEBUG_KMS("self-refresh watermark: display plane %d " "cursor %d\n", srwm, cursor_sr); - if (IS_CRESTLINE(dev)) - I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); + intel_set_memory_cxsr(dev_priv, true); } else { /* Turn off self refresh if both pipes are enabled */ - if (IS_CRESTLINE(dev)) - I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) - & ~FW_BLC_SELF_EN); + intel_set_memory_cxsr(dev_priv, false); } DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n", @@ -1587,10 +1599,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) cwm = 2; /* Play safe and disable self-refresh before adjusting watermarks. */ - if (IS_I945G(dev) || IS_I945GM(dev)) - I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | 0); - else if (IS_I915GM(dev)) - I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_SELF_EN)); + intel_set_memory_cxsr(dev_priv, false); /* Calc sr entries for one plane configs */ if (HAS_FW_BLC(dev) && enabled) { @@ -1636,17 +1645,8 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) I915_WRITE(FW_BLC, fwater_lo); I915_WRITE(FW_BLC2, fwater_hi); - if (HAS_FW_BLC(dev)) { - if (enabled) { - if (IS_I945G(dev) || IS_I945GM(dev)) - I915_WRITE(FW_BLC_SELF, - FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN); - else if (IS_I915GM(dev)) - I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_SELF_EN)); - DRM_DEBUG_KMS("memory self refresh enabled\n"); - } else - DRM_DEBUG_KMS("memory self refresh disabled\n"); - } + if (enabled) + intel_set_memory_cxsr(dev_priv, true); } static void i845_update_wm(struct drm_crtc *unused_crtc) @@ -6782,7 +6782,7 @@ void intel_init_pm(struct drm_device *dev) (dev_priv->is_ddr3 == 1) ? "3" : "2", dev_priv->fsb_freq, dev_priv->mem_freq); /* Disable CxSR and never update its watermark again */ - pineview_disable_cxsr(dev); + intel_set_memory_cxsr(dev_priv, false); dev_priv->display.update_wm = NULL; } else dev_priv->display.update_wm = pineview_update_wm; -- cgit v0.10.2 From 9858425c8fe631673ef325b59bac38c26c08b004 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 13 Jun 2014 14:54:20 +0300 Subject: drm/i915: gmch: set SR WMs to valid values before enabling them Atm it's possible that we enable the memory self-refresh mode before the watermark levels used by this mode are programmed with valid values. So move the enabling after we programmed the WM levels. Signed-off-by: Imre Deak Reviewed-by: Vijay Purushothaman Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ded4294..41c1b79 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -1360,6 +1360,7 @@ static void valleyview_update_wm(struct drm_crtc *crtc) int plane_sr, cursor_sr; int ignore_plane_sr, ignore_cursor_sr; unsigned int enabled = 0; + bool cxsr_enabled; vlv_update_drain_latency(dev); @@ -1386,8 +1387,9 @@ static void valleyview_update_wm(struct drm_crtc *crtc) &valleyview_wm_info, &valleyview_cursor_wm_info, &ignore_plane_sr, &cursor_sr)) { - intel_set_memory_cxsr(dev_priv, true); + cxsr_enabled = true; } else { + cxsr_enabled = false; intel_set_memory_cxsr(dev_priv, false); plane_sr = cursor_sr = 0; } @@ -1408,6 +1410,9 @@ static void valleyview_update_wm(struct drm_crtc *crtc) I915_WRITE(DSPFW3, (I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) | (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); + + if (cxsr_enabled) + intel_set_memory_cxsr(dev_priv, true); } static void g4x_update_wm(struct drm_crtc *crtc) @@ -1418,6 +1423,7 @@ static void g4x_update_wm(struct drm_crtc *crtc) int planea_wm, planeb_wm, cursora_wm, cursorb_wm; int plane_sr, cursor_sr; unsigned int enabled = 0; + bool cxsr_enabled; if (g4x_compute_wm0(dev, PIPE_A, &g4x_wm_info, latency_ns, @@ -1437,8 +1443,9 @@ static void g4x_update_wm(struct drm_crtc *crtc) &g4x_wm_info, &g4x_cursor_wm_info, &plane_sr, &cursor_sr)) { - intel_set_memory_cxsr(dev_priv, true); + cxsr_enabled = true; } else { + cxsr_enabled = false; intel_set_memory_cxsr(dev_priv, false); plane_sr = cursor_sr = 0; } @@ -1460,6 +1467,9 @@ static void g4x_update_wm(struct drm_crtc *crtc) I915_WRITE(DSPFW3, (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) | (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); + + if (cxsr_enabled) + intel_set_memory_cxsr(dev_priv, true); } static void i965_update_wm(struct drm_crtc *unused_crtc) @@ -1469,6 +1479,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) struct drm_crtc *crtc; int srwm = 1; int cursor_sr = 16; + bool cxsr_enabled; /* Calc sr entries for one plane configs */ crtc = single_enabled_crtc(dev); @@ -1510,8 +1521,9 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) DRM_DEBUG_KMS("self-refresh watermark: display plane %d " "cursor %d\n", srwm, cursor_sr); - intel_set_memory_cxsr(dev_priv, true); + cxsr_enabled = true; } else { + cxsr_enabled = false; /* Turn off self refresh if both pipes are enabled */ intel_set_memory_cxsr(dev_priv, false); } @@ -1525,6 +1537,9 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) I915_WRITE(DSPFW2, (8 << 8) | (8 << 0)); /* update cursor SR watermark */ I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); + + if (cxsr_enabled) + intel_set_memory_cxsr(dev_priv, true); } static void i9xx_update_wm(struct drm_crtc *unused_crtc) -- cgit v0.10.2 From 564ed191f5d816d24501664296991ec70327e2bc Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 13 Jun 2014 14:54:21 +0300 Subject: drm/i915: gmch: fix stuck primary plane due to memory self-refresh mode Blanking/unblanking the console in a loop on an Asus T100 sometimes leaves the console blank. After some digging I found that applying commit 61bc95c1fbbb6a08b55bbe161fdf1ea5493fc595 Author: Egbert Eich Date: Mon Mar 4 09:24:38 2013 -0500 DRM/i915: On G45 enable cursor plane briefly after enabling the display plane. fixed VLV too. In my case the problem seemed to happen already during the previous crtc disabling and went away if I disabled self-refresh mode before disabling the primary plane. The root cause for this is that updates from the shadow to live plane control register are blocked at vblank time if the memory self-refresh mode (aka max-fifo mode on VLV) is active at that moment. The controller checks at frame start time if the CPU is in C0 and the self-refresh mode enable bit is set and if so activates self-reresh mode, otherwise deactivates it. So to make sure that the plane truly gets disabled before pipe-off we have to: 1. disable memory self-refresh mode 2. disable plane 3. wait for vblank 4. disable pipe 5. wait for pipe-off v2: - add explanation for the root cause from HW team (Cesar Mancini et al) - remove note about the CPU C7S state, in my latest tests disabling it alone didn't make a difference - add vblank between disabling plane and pipe (Ville) - apply the same workaround for all gmch platforms (Ville) Signed-off-by: Imre Deak Reviewed-by: Vijay Purushothaman Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d675df8..74b8b38 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4818,6 +4818,16 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) if (IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev, pipe, false); + /* + * Vblank time updates from the shadow to live plane control register + * are blocked if the memory self-refresh mode is active at that + * moment. So to make sure the plane gets truly disabled, disable + * first the self-refresh mode. The self-refresh enable bit in turn + * will be checked/applied by the HW only at the next frame start + * event which is after the vblank start event, so we need to have a + * wait-for-vblank between disabling the plane and the pipe. + */ + intel_set_memory_cxsr(dev_priv, false); intel_crtc_disable_planes(crtc); for_each_encoder_on_crtc(dev, crtc, encoder) @@ -4826,9 +4836,10 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) /* * On gen2 planes are double buffered but the pipe isn't, so we must * wait for planes to fully turn off before disabling the pipe. + * We also need to wait on all gmch platforms because of the + * self-refresh mode constraint explained above. */ - if (IS_GEN2(dev)) - intel_wait_for_vblank(dev, pipe); + intel_wait_for_vblank(dev, pipe); intel_disable_pipe(dev_priv, pipe); -- cgit v0.10.2 From 13cf550448b58abf8f44f5d6a560f2d20871c965 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 18 Jun 2014 11:29:35 +1000 Subject: drm/i915: rework digital port IRQ handling (v2) The digital ports from Ironlake and up have the ability to distinguish between long and short HPD pulses. Displayport 1.1 only uses the short form to request link retraining usually, so we haven't really needed support for it until now. However with DP 1.2 MST we need to handle the short irqs on their own outside the modesetting locking the long hpd's involve. This patch adds the framework to distinguish between short/long to the current code base, to lay the basis for future DP 1.2 MST work. This should mean we get better bisectability in case of regression due to the new irq handling. v2: add GM45 support (untested, due to lack of hw) Signed-off-by: Dave Airlie Reviewed-by: Todd Previte [danvet: Fix conflicts in i915_irq.c with Oscar Mateo's irq handling race fixes and a trivial one in intel_drv.h with the psr code.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3836286..b560efc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1559,6 +1559,11 @@ struct drm_i915_private { struct i915_runtime_pm pm; + struct intel_digital_port *hpd_irq_port[I915_MAX_PORTS]; + u32 long_hpd_port_mask; + u32 short_hpd_port_mask; + struct work_struct dig_port_work; + /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct i915_dri1_state dri1; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1c1ec22..c50e3b4 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1090,6 +1090,53 @@ static bool intel_hpd_irq_event(struct drm_device *dev, return true; } +static void i915_digport_work_func(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, struct drm_i915_private, dig_port_work); + unsigned long irqflags; + u32 long_port_mask, short_port_mask; + struct intel_digital_port *intel_dig_port; + int i, ret; + u32 old_bits = 0; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + long_port_mask = dev_priv->long_hpd_port_mask; + dev_priv->long_hpd_port_mask = 0; + short_port_mask = dev_priv->short_hpd_port_mask; + dev_priv->short_hpd_port_mask = 0; + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + for (i = 0; i < I915_MAX_PORTS; i++) { + bool valid = false; + bool long_hpd = false; + intel_dig_port = dev_priv->hpd_irq_port[i]; + if (!intel_dig_port || !intel_dig_port->hpd_pulse) + continue; + + if (long_port_mask & (1 << i)) { + valid = true; + long_hpd = true; + } else if (short_port_mask & (1 << i)) + valid = true; + + if (valid) { + ret = intel_dig_port->hpd_pulse(intel_dig_port, long_hpd); + if (ret == true) { + /* if we get true fallback to old school hpd */ + old_bits |= (1 << intel_dig_port->base.hpd_pin); + } + } + } + + if (old_bits) { + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + dev_priv->hpd_event_bits |= old_bits; + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + schedule_work(&dev_priv->hotplug_work); + } +} + /* * Handle hotplug events outside the interrupt handler proper. */ @@ -1521,23 +1568,104 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, #define HPD_STORM_DETECT_PERIOD 1000 #define HPD_STORM_THRESHOLD 5 +static int ilk_port_to_hotplug_shift(enum port port) +{ + switch (port) { + case PORT_A: + case PORT_E: + default: + return -1; + case PORT_B: + return 0; + case PORT_C: + return 8; + case PORT_D: + return 16; + } +} + +static int g4x_port_to_hotplug_shift(enum port port) +{ + switch (port) { + case PORT_A: + case PORT_E: + default: + return -1; + case PORT_B: + return 17; + case PORT_C: + return 19; + case PORT_D: + return 21; + } +} + +static inline enum port get_port_from_pin(enum hpd_pin pin) +{ + switch (pin) { + case HPD_PORT_B: + return PORT_B; + case HPD_PORT_C: + return PORT_C; + case HPD_PORT_D: + return PORT_D; + default: + return PORT_A; /* no hpd */ + } +} + static inline void intel_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, + u32 dig_hotplug_reg, const u32 *hpd) { struct drm_i915_private *dev_priv = dev->dev_private; int i; + enum port port; bool storm_detected = false; + bool queue_dig = false, queue_hp = false; + u32 dig_shift; + u32 dig_port_mask = 0; if (!hotplug_trigger) return; - DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", - hotplug_trigger); + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x, dig 0x%08x\n", + hotplug_trigger, dig_hotplug_reg); spin_lock(&dev_priv->irq_lock); for (i = 1; i < HPD_NUM_PINS; i++) { + if (!(hpd[i] & hotplug_trigger)) + continue; + + port = get_port_from_pin(i); + if (port && dev_priv->hpd_irq_port[port]) { + bool long_hpd; + + if (IS_G4X(dev)) { + dig_shift = g4x_port_to_hotplug_shift(port); + long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; + } else { + dig_shift = ilk_port_to_hotplug_shift(port); + long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; + } + + DRM_DEBUG_DRIVER("digital hpd port %d %d\n", port, long_hpd); + /* for long HPD pulses we want to have the digital queue happen, + but we still want HPD storm detection to function. */ + if (long_hpd) { + dev_priv->long_hpd_port_mask |= (1 << port); + dig_port_mask |= hpd[i]; + } else { + /* for short HPD just trigger the digital queue */ + dev_priv->short_hpd_port_mask |= (1 << port); + hotplug_trigger &= ~hpd[i]; + } + queue_dig = true; + } + } + for (i = 1; i < HPD_NUM_PINS; i++) { if (hpd[i] & hotplug_trigger && dev_priv->hpd_stats[i].hpd_mark == HPD_DISABLED) { /* @@ -1557,7 +1685,11 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED) continue; - dev_priv->hpd_event_bits |= (1 << i); + if (!(dig_port_mask & hpd[i])) { + dev_priv->hpd_event_bits |= (1 << i); + queue_hp = true; + } + if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies + msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) { @@ -1586,7 +1718,10 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, * queue for otherwise the flush_work in the pageflip code will * deadlock. */ - schedule_work(&dev_priv->hotplug_work); + if (queue_dig) + schedule_work(&dev_priv->dig_port_work); + if (queue_hp) + schedule_work(&dev_priv->hotplug_work); } static void gmbus_irq_handler(struct drm_device *dev) @@ -1827,11 +1962,11 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev) if (IS_G4X(dev)) { u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x); + intel_hpd_irq_handler(dev, hotplug_trigger, 0, hpd_status_g4x); } else { u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); + intel_hpd_irq_handler(dev, hotplug_trigger, 0, hpd_status_i915); } if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && @@ -1929,8 +2064,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; + u32 dig_hotplug_reg; + + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); + I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); + intel_hpd_irq_handler(dev, hotplug_trigger, dig_hotplug_reg, hpd_ibx); if (pch_iir & SDE_AUDIO_POWER_MASK) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> @@ -2036,8 +2175,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; + u32 dig_hotplug_reg; + + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); + I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); + intel_hpd_irq_handler(dev, hotplug_trigger, dig_hotplug_reg, hpd_cpt); if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> @@ -4358,6 +4501,7 @@ void intel_irq_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); + INIT_WORK(&dev_priv->dig_port_work, i915_digport_work_func); INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func); INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ded6013..efaf44b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1705,6 +1705,9 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->cloneable = 0; intel_encoder->hot_plug = intel_ddi_hot_plug; + intel_dig_port->hpd_pulse = intel_dp_hpd_pulse; + dev_priv->hpd_irq_port[port] = intel_dig_port; + if (init_dp) dp_connector = intel_ddi_init_dp_connector(intel_dig_port); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 23b62b0..2da413c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3815,6 +3815,22 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder) intel_dp_check_link_status(intel_dp); } +bool +intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) +{ + struct intel_dp *intel_dp = &intel_dig_port->dp; + + if (long_hpd) + return true; + + /* + * we'll check the link status via the normal hot plug path later - + * but for short hpds we should check it now + */ + intel_dp_check_link_status(intel_dp); + return false; +} + /* Return which DP Port should be selected for Transcoder DP control */ int intel_trans_dp_port_sel(struct drm_crtc *crtc) @@ -4387,6 +4403,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, void intel_dp_init(struct drm_device *dev, int output_reg, enum port port) { + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; @@ -4443,6 +4460,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->cloneable = 0; intel_encoder->hot_plug = intel_dp_hot_plug; + intel_dig_port->hpd_pulse = intel_dp_hpd_pulse; + dev_priv->hpd_irq_port[port] = intel_dig_port; + if (!intel_dp_init_connector(intel_dig_port, intel_connector)) { drm_encoder_cleanup(encoder); kfree(intel_dig_port); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0ef04ea..45afd25 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -567,6 +567,7 @@ struct intel_digital_port { u32 saved_port_bits; struct intel_dp dp; struct intel_hdmi hdmi; + bool (*hpd_pulse)(struct intel_digital_port *, bool); }; static inline int @@ -850,6 +851,8 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc); bool intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config); bool intel_dp_is_edp(struct drm_device *dev, enum port port); +bool intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, + bool long_hpd); void intel_edp_backlight_on(struct intel_dp *intel_dp); void intel_edp_backlight_off(struct intel_dp *intel_dp); void intel_edp_panel_vdd_on(struct intel_dp *intel_dp); @@ -861,7 +864,6 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); void intel_edp_psr_exit(struct drm_device *dev); void intel_edp_psr_init(struct drm_device *dev); - /* intel_dsi.c */ void intel_dsi_init(struct drm_device *dev); -- cgit v0.10.2 From 4bfad3ddc8cfcd9938c8e4b0b5b73780d4726438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 18 Jun 2014 22:04:48 +0300 Subject: drm/i915: Unpin last_context at reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're forgetting to unpin the last_context from the ggtt at GPU reset time. This leads to the vma pin_count leaking at every reset if the last context wasn't the ring default context. Further use of the same context will trigger the pin_count check in i915_gem_object_pin() and userspace will be faced with EBUSY as a result. This plaques kms_flip rather badly since it performs lots of resets, and every fd has its own default context these days. Fix the problem by properly unpinning the last context at reset. This regression seems to back to commit acce9ffa4807027965ebd948456fa8385bbee32e Author: Ben Widawsky Date: Fri Dec 6 14:11:03 2013 -0800 drm/i915: Better reset handling for contexts Testcase: igt/gem_ctx_exec/reset-pin-leak Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 0d2c75b..0656121 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -382,6 +382,9 @@ void i915_gem_context_reset(struct drm_device *dev) dctx->obj->active = 0; } + if (ring->last_context->obj && i == RCS) + i915_gem_object_ggtt_unpin(ring->last_context->obj); + i915_gem_context_unreference(ring->last_context); i915_gem_context_reference(dctx); ring->last_context = dctx; -- cgit v0.10.2 From 0eaa53f021847eec5b759843e0cb195322c26896 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 30 Jun 2014 04:45:01 -0700 Subject: drm/i915: Don't pretend ips is always enabled on BDW. As pointed out before we don't have a reliable way to read back ips status on BDW without the risk to disable it when reading. However now we are pretending that IPS on BDW is always on and getting people confused about it. So this patch allows people to know if ips was ever attempted to be enabled. Even if the current status is impossible to be ascertain. v2: (spotted by Paulo): * A version that at least compiles * with more clear messages * let Cheryview on the safe side until we aren't sure that checking ips state on ips won't disable it. Cc: Paulo Zanoni Signed-off-by: Rodrigo Vivi Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8da9985..8cc9936 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1523,10 +1523,17 @@ static int i915_ips_status(struct seq_file *m, void *unused) intel_runtime_pm_get(dev_priv); - if (IS_BROADWELL(dev) || I915_READ(IPS_CTL) & IPS_ENABLE) - seq_puts(m, "enabled\n"); - else - seq_puts(m, "disabled\n"); + seq_printf(m, "Enabled by kernel parameter: %s\n", + yesno(i915.enable_ips)); + + if (INTEL_INFO(dev)->gen >= 8) { + seq_puts(m, "Currently: unknown\n"); + } else { + if (I915_READ(IPS_CTL) & IPS_ENABLE) + seq_puts(m, "Currently: enabled\n"); + else + seq_puts(m, "Currently: disabled\n"); + } intel_runtime_pm_put(dev_priv); -- cgit v0.10.2 From a7de62fe94deda8339c72d03ffd4bd5586aa20cb Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Mon, 7 Jul 2014 18:20:34 +0200 Subject: DRM/i915: Remove magic to prevent blank screen on gen4 chipsets Since the root cause is understood now and with the fix commit 564ed191f5d816d24501664296991ec70327e2bc Author: Imre Deak Date: Fri Jun 13 14:54:21 2014 +0300 drm/i915: gmch: fix stuck primary plane due to memory self-refresh mode in place the magic for G4x chipsets introduced with commit commit 61bc95c1fbbb6a08b55bbe161fdf1ea5493fc595 Author: Egbert Eich Date: Mon Mar 4 09:24:38 2013 -0500 DRM/i915: On G45 enable cursor plane briefly after enabling the display plane. to avoided occasional screen blanking on mode changes can finally be removed. It's been verified that Imre's fix also resolves the said issue. Signed-off-by: Egbert Eich Tested-by: Stefan Dirsch Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 74b8b38..b961122 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3894,30 +3894,6 @@ static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) */ } -/** - * i9xx_fixup_plane - ugly workaround for G45 to fire up the hardware - * cursor plane briefly if not already running after enabling the display - * plane. - * This workaround avoids occasional blank screens when self refresh is - * enabled. - */ -static void -g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) -{ - u32 cntl = I915_READ(CURCNTR(pipe)); - - if ((cntl & CURSOR_MODE) == 0) { - u32 fw_bcl_self = I915_READ(FW_BLC_SELF); - - I915_WRITE(FW_BLC_SELF, fw_bcl_self & ~FW_BLC_SELF_EN); - I915_WRITE(CURCNTR(pipe), CURSOR_MODE_64_ARGB_AX); - intel_wait_for_vblank(dev_priv->dev, pipe); - I915_WRITE(CURCNTR(pipe), cntl); - I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe))); - I915_WRITE(FW_BLC_SELF, fw_bcl_self); - } -} - static void intel_crtc_enable_planes(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3930,9 +3906,6 @@ static void intel_crtc_enable_planes(struct drm_crtc *crtc) intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); - /* The fixup needs to happen before cursor is enabled */ - if (IS_G4X(dev)) - g4x_fixup_plane(dev_priv, pipe); intel_crtc_update_cursor(crtc, true); intel_crtc_dpms_overlay(intel_crtc, true); -- cgit v0.10.2 From b3f9ad93b7621364ed51f9c37b9cf9abc9855991 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Mon, 7 Jul 2014 12:40:17 +0100 Subject: drm/i915/bdw: 3D_CHICKEN3 has write mask bits The workaround to limit SDE poly depth FIFO to 2 is not applied because 3D Chicken-3 mask bit is not set. WaLimitSizeOfSDEPolyFifo is only for BDW-A and could be removed. Signed-off-by: Michel Thierry Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 41c1b79..f2a4056 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5375,7 +5375,7 @@ static void gen8_init_clock_gating(struct drm_device *dev) I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_BWGTLB_DISABLE)); I915_WRITE(_3D_CHICKEN3, - _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2)); + _MASKED_BIT_ENABLE(_3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2))); I915_WRITE(COMMON_SLICE_CHICKEN2, _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE)); -- cgit v0.10.2 From fabf6e513f640dac89645e1a98376eb8064394ee Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 May 2014 14:10:22 +0200 Subject: drm/i915: Support pf CRC source on haswell transcoder edp The always-on power well pixel path on haswell is routed such that it bypasses the panel fitter when we use is. Which means the pfit CRC source won't work in that configuration. Add a new disallow-bypass flags to the pfit pipe config state and set it when we want to use the pf CRC. Results in a bit of flicker, but should get the job done. We'll also undo do it afterwards to make sure other tests arent' negatively affected. Totally untested due to lack of hsw laptops around here. v2: s/disallow_bypass/force_power_well_on/ to avoid a double negative (Damien). v3: force_thru because roadsigns. v4: Don't forget the power wells! Also note that until the runtime pm for DPMS series is fully merged the simple disable/enable trick won't work since the ->crtc_mode_set callback is still required to do nasty things. This stuff is tricky, but I think by both fixing up get_crtc_power_domains and the debugfs wa code we should always grab/drop the additional power well correctly. v5: Wrap in () as suggested by Damien to avoid setting reserved values for the edp transcoder path on bdw+ References: https://bugs.freedesktop.org/show_bug.cgi?id=72864 Cc: Damien Lespiau Reviewed-by: Damien Lespiau Tested-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8cc9936..bfd0d41 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2867,7 +2867,60 @@ static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, return 0; } -static int ivb_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, +static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]); + + drm_modeset_lock_all(dev); + /* + * If we use the eDP transcoder we need to make sure that we don't + * bypass the pfit, since otherwise the pipe CRC source won't work. Only + * relevant on hsw with pipe A when using the always-on power well + * routing. + */ + if (crtc->config.cpu_transcoder == TRANSCODER_EDP && + !crtc->config.pch_pfit.enabled) { + crtc->config.pch_pfit.force_thru = true; + + intel_display_power_get(dev_priv, + POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A)); + + dev_priv->display.crtc_disable(&crtc->base); + dev_priv->display.crtc_enable(&crtc->base); + } + drm_modeset_unlock_all(dev); +} + +static void hsw_undo_trans_edp_pipe_A_crc_wa(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]); + + drm_modeset_lock_all(dev); + /* + * If we use the eDP transcoder we need to make sure that we don't + * bypass the pfit, since otherwise the pipe CRC source won't work. Only + * relevant on hsw with pipe A when using the always-on power well + * routing. + */ + if (crtc->config.pch_pfit.force_thru) { + crtc->config.pch_pfit.force_thru = false; + + dev_priv->display.crtc_disable(&crtc->base); + dev_priv->display.crtc_enable(&crtc->base); + + intel_display_power_put(dev_priv, + POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A)); + } + drm_modeset_unlock_all(dev); +} + +static int ivb_pipe_crc_ctl_reg(struct drm_device *dev, + enum pipe pipe, + enum intel_pipe_crc_source *source, uint32_t *val) { if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) @@ -2881,6 +2934,9 @@ static int ivb_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB; break; case INTEL_PIPE_CRC_SOURCE_PF: + if (IS_HASWELL(dev) && pipe == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev); + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB; break; case INTEL_PIPE_CRC_SOURCE_NONE: @@ -2913,11 +2969,11 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, else if (INTEL_INFO(dev)->gen < 5) ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val); else if (IS_VALLEYVIEW(dev)) - ret = vlv_pipe_crc_ctl_reg(dev,pipe, &source, &val); + ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val); else if (IS_GEN5(dev) || IS_GEN6(dev)) ret = ilk_pipe_crc_ctl_reg(&source, &val); else - ret = ivb_pipe_crc_ctl_reg(&source, &val); + ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val); if (ret != 0) return ret; @@ -2969,6 +3025,8 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, g4x_undo_pipe_scramble_reset(dev, pipe); else if (IS_VALLEYVIEW(dev)) vlv_undo_pipe_scramble_reset(dev, pipe); + else if (IS_HASWELL(dev) && pipe == PIPE_A) + hsw_undo_trans_edp_pipe_A_crc_wa(dev); } return 0; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index efaf44b..6cc79c8 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -995,7 +995,9 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) * eDP when not using the panel fitter, and when not * using motion blur mitigation (which we don't * support). */ - if (IS_HASWELL(dev) && intel_crtc->config.pch_pfit.enabled) + if (IS_HASWELL(dev) && + (intel_crtc->config.pch_pfit.enabled || + intel_crtc->config.pch_pfit.force_thru)) temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; else temp |= TRANS_DDI_EDP_INPUT_A_ON; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b961122..8043e4b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4350,7 +4350,6 @@ static unsigned long get_crtc_power_domains(struct drm_crtc *crtc) struct intel_encoder *intel_encoder; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; - bool pfit_enabled = intel_crtc->config.pch_pfit.enabled; unsigned long mask; enum transcoder transcoder; @@ -4358,7 +4357,8 @@ static unsigned long get_crtc_power_domains(struct drm_crtc *crtc) mask = BIT(POWER_DOMAIN_PIPE(pipe)); mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder)); - if (pfit_enabled) + if (intel_crtc->config.pch_pfit.enabled || + intel_crtc->config.pch_pfit.force_thru) mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe)); for_each_encoder_on_crtc(dev, crtc, intel_encoder) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 45afd25..9da665a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -338,6 +338,7 @@ struct intel_crtc_config { u32 pos; u32 size; bool enabled; + bool force_thru; } pch_pfit; /* FDI configuration, only valid if has_pch_encoder is set. */ -- cgit v0.10.2 From 2885f6ac070612f21d041f650a4d2bc44b02d6b6 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 26 Jun 2014 18:23:52 +0100 Subject: drm/i915: Corrected 'file_priv' to 'file' in 'i915_driver_preclose()' The 'i915_driver_preclose()' function has a parameter called 'file_priv'. However, this is misleading as the structure it points to is a 'drm_file' not a 'drm_i915_file_private'. It should be named just 'file' to avoid confusion. Signed-off-by: John Harrison Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5e583a1..ce69185 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1957,11 +1957,11 @@ void i915_driver_lastclose(struct drm_device *dev) i915_dma_cleanup(dev); } -void i915_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) +void i915_driver_preclose(struct drm_device *dev, struct drm_file *file) { mutex_lock(&dev->struct_mutex); - i915_gem_context_close(dev, file_priv); - i915_gem_release(dev, file_priv); + i915_gem_context_close(dev, file); + i915_gem_release(dev, file); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b560efc..204de40 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2104,12 +2104,12 @@ void i915_update_dri1_breadcrumb(struct drm_device *dev); extern void i915_kernel_lost_context(struct drm_device * dev); extern int i915_driver_load(struct drm_device *, unsigned long flags); extern int i915_driver_unload(struct drm_device *); -extern int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv); +extern int i915_driver_open(struct drm_device *dev, struct drm_file *file); extern void i915_driver_lastclose(struct drm_device * dev); extern void i915_driver_preclose(struct drm_device *dev, - struct drm_file *file_priv); + struct drm_file *file); extern void i915_driver_postclose(struct drm_device *dev, - struct drm_file *file_priv); + struct drm_file *file); extern int i915_driver_device_is_agp(struct drm_device * dev); #ifdef CONFIG_COMPAT extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, -- cgit v0.10.2 From f7b64236854b0862e9967090f227cf08d81c71f4 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Tue, 1 Jul 2014 02:41:36 -0700 Subject: drm/i915: Fix VCS2's ring name. It just fix a typo. v2: removing underscore to let this like all other ring names (Oscar) Cc: Oscar Mateo Reviewed-by (v1): Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 2faef26..22c2b9a 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -2224,7 +2224,7 @@ int intel_init_bsd2_ring_buffer(struct drm_device *dev) return -EINVAL; } - ring->name = "bds2_ring"; + ring->name = "bsd2 ring"; ring->id = VCS2; ring->write_tail = ring_write_tail; -- cgit v0.10.2 From ddd4dbc6c16e4719841447348e6b5ec3541c8f68 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 30 Jun 2014 09:51:11 -0700 Subject: drm/i915: Updating comments. ring index calculation table was out of date after other rings were added, although the formula is flexible and scale when adding new rings. So this patch just update the comments and add a brief explanation why to use sync_seqno[ring index]. Signed-off-by: Rodrigo Vivi Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f6d1238..e85c85c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2842,6 +2842,8 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj, idx = intel_ring_sync_index(from, to); seqno = obj->last_read_seqno; + /* Optimization: Avoid semaphore sync when we are sure we already + * waited for an object with higher seqno */ if (seqno <= from->semaphore.sync_seqno[idx]) return 0; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index e72017b..2e8b516 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -238,9 +238,11 @@ intel_ring_sync_index(struct intel_engine_cs *ring, int idx; /* - * cs -> 0 = vcs, 1 = bcs - * vcs -> 0 = bcs, 1 = cs, - * bcs -> 0 = cs, 1 = vcs. + * rcs -> 0 = vcs, 1 = bcs, 2 = vecs, 3 = vcs2; + * vcs -> 0 = bcs, 1 = vecs, 2 = vcs2, 3 = rcs; + * bcs -> 0 = vecs, 1 = vcs2. 2 = rcs, 3 = vcs; + * vecs -> 0 = vcs2, 1 = rcs, 2 = vcs, 3 = bcs; + * vcs2 -> 0 = rcs, 1 = vcs, 2 = bcs, 3 = vecs; */ idx = (other - ring) - 1; -- cgit v0.10.2 From 707d9cf9935cfba2d62dd80dc01dc5dc4530d4ca Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:36 -0700 Subject: drm/i915: gen specific ring init Gen8 has already had some differentiation with how it handles rings. Semaphores bring yet more differences, and now is as good a time as any to do the split. Also, since gen8 doesn't actually use semaphores up until this point, put the proper "NULL" values in for the mbox info. v2: v1 had a stale commit message v3: Move everything in the is_semaphore_enabled() check v4: VCS2 rebase Remove double assignment of signal in render ring (Ville) v5: Adding missed VCS2 signal init on gen8+ (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 22c2b9a..7ca1b94 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -729,7 +729,11 @@ gen6_add_request(struct intel_engine_cs *ring) { int ret; - ret = ring->semaphore.signal(ring, 4); + if (ring->semaphore.signal) + ret = ring->semaphore.signal(ring, 4); + else + ret = intel_ring_begin(ring, 4); + if (ret) return ret; @@ -1952,40 +1956,59 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->id = RCS; ring->mmio_base = RENDER_RING_BASE; - if (INTEL_INFO(dev)->gen >= 6) { + if (INTEL_INFO(dev)->gen >= 8) { + ring->add_request = gen6_add_request; + ring->flush = gen8_render_ring_flush; + ring->irq_get = gen8_ring_get_irq; + ring->irq_put = gen8_ring_put_irq; + ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT; + ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } + } else if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; ring->flush = gen7_render_ring_flush; if (INTEL_INFO(dev)->gen == 6) ring->flush = gen6_render_ring_flush; - if (INTEL_INFO(dev)->gen >= 8) { - ring->flush = gen8_render_ring_flush; - ring->irq_get = gen8_ring_get_irq; - ring->irq_put = gen8_ring_put_irq; - } else { - ring->irq_get = gen6_ring_get_irq; - ring->irq_put = gen6_ring_put_irq; - } + ring->irq_get = gen6_ring_get_irq; + ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT; ring->get_seqno = gen6_ring_get_seqno; ring->set_seqno = ring_set_seqno; - ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - /* - * The current semaphore is only applied on pre-gen8 platform. - * And there is no VCS2 ring on the pre-gen8 platform. So the - * semaphore between RCS and VCS2 is initialized as INVALID. - * Gen8 will initialize the sema between VCS2 and RCS later. - */ - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_RV; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_RB; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_RVE; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_VRSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_BRSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_VERSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + /* + * The current semaphore is only applied on pre-gen8 + * platform. And there is no VCS2 ring on the pre-gen8 + * platform. So the semaphore between RCS and VCS2 is + * initialized as INVALID. Gen8 will initialize the + * sema between VCS2 and RCS later. + */ + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_RV; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_RB; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_RVE; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_VRSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_BRSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_VERSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } else if (IS_GEN5(dev)) { ring->add_request = pc_render_add_request; ring->flush = gen4_render_ring_flush; @@ -2013,6 +2036,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->irq_enable_mask = I915_USER_INTERRUPT; } ring->write_tail = ring_write_tail; + if (IS_HASWELL(dev)) ring->dispatch_execbuffer = hsw_ring_dispatch_execbuffer; else if (IS_GEN8(dev)) @@ -2163,31 +2187,49 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + /* + * The current semaphore is only applied on + * pre-gen8 platform. And there is no VCS2 ring + * on the pre-gen8 platform. So the semaphore + * between VCS and VCS2 is initialized as + * INVALID. Gen8 will initialize the sema + * between VCS2 and VCS later. + */ + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } else { ring->irq_enable_mask = GT_BSD_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VR; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VB; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_VVE; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_RVSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_BVSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_VEVSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } - ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - /* - * The current semaphore is only applied on pre-gen8 platform. - * And there is no VCS2 ring on the pre-gen8 platform. So the - * semaphore between VCS and VCS2 is initialized as INVALID. - * Gen8 will initialize the sema between VCS2 and VCS later. - */ - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VR; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VB; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_VVE; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_RVSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_BVSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_VEVSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; } else { ring->mmio_base = BSD_RING_BASE; ring->flush = bsd_ring_flush; @@ -2283,30 +2325,47 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->irq_get = gen8_ring_get_irq; ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } else { ring->irq_enable_mask = GT_BLT_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.signal = gen6_signal; + ring->semaphore.sync_to = gen6_ring_sync; + /* + * The current semaphore is only applied on pre-gen8 + * platform. And there is no VCS2 ring on the pre-gen8 + * platform. So the semaphore between BCS and VCS2 is + * initialized as INVALID. Gen8 will initialize the + * sema between BCS and VCS2 later. + */ + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_BR; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_BV; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_BVE; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_RBSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_VBSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_VEBSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } - ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - /* - * The current semaphore is only applied on pre-gen8 platform. And - * there is no VCS2 ring on the pre-gen8 platform. So the semaphore - * between BCS and VCS2 is initialized as INVALID. - * Gen8 will initialize the sema between BCS and VCS2 later. - */ - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_BR; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_BV; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_BVE; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_RBSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_VBSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_VEBSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; ring->init = init_ring_common; return intel_init_ring_buffer(dev, ring); @@ -2333,24 +2392,40 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev) ring->irq_get = gen8_ring_get_irq; ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } else { ring->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; ring->irq_get = hsw_vebox_get_irq; ring->irq_put = hsw_vebox_put_irq; ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer; + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.signal = gen6_signal; + ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VER; + ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_VEV; + ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VEB; + ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; + ring->semaphore.mbox.signal[RCS] = GEN6_RVESYNC; + ring->semaphore.mbox.signal[VCS] = GEN6_VVESYNC; + ring->semaphore.mbox.signal[BCS] = GEN6_BVESYNC; + ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; + ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + } } - ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VER; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_VEV; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VEB; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_RVESYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_VVESYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_BVESYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; ring->init = init_ring_common; return intel_init_ring_buffer(dev, ring); -- cgit v0.10.2 From a1444b79fec2e95b705c5505c1f609306538dc49 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:35 -0700 Subject: drm/i915: Make semaphore updates more precise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the ring mask we now have an easy way to know the number of rings in the system, and therefore can accurately predict the number of dwords to emit for semaphore signalling. This was not possible (easily) previously. There should be no functional impact, simply fewer instructions emitted. While we're here, simply do the round up to 2 instead of the fancier rounding we did before, which rounding up per mbox, ie 4. This also allows us to drop the unnecessary MI_NOOP, so not really 4, 3. v2: Use 3 dwords instead of 4 (Ville) Do the proper calculation to get the number of dwords to emit (Ville) Conditionally set .sync_to when semaphores are enabled (Ville) v3: Rebased on VCS2 Replace hweight_long with hweight32 (Ville) v4: Pull out the accidentally squashed hunk from the next patch after rebase (Daniel). v5: Fix conflict after rebase (Rodrigo) Reviewed-by: Rodrigo Vivi Reviewed-by: Ville Syrjälä (v1) Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 7ca1b94..e9c8814 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -679,23 +679,16 @@ static int gen6_signal(struct intel_engine_cs *signaller, struct drm_device *dev = signaller->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *useless; - int i, ret; + int i, ret, num_rings; - /* NB: In order to be able to do semaphore MBOX updates for varying - * number of rings, it's easiest if we round up each individual update - * to a multiple of 2 (since ring updates must always be a multiple of - * 2) even though the actual update only requires 3 dwords. - */ -#define MBOX_UPDATE_DWORDS 4 - if (i915_semaphore_is_enabled(dev)) - num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS); - else - return intel_ring_begin(signaller, num_dwords); +#define MBOX_UPDATE_DWORDS 3 + num_rings = hweight32(INTEL_INFO(dev)->ring_mask); + num_dwords += round_up((num_rings-1) * MBOX_UPDATE_DWORDS, 2); +#undef MBOX_UPDATE_DWORDS ret = intel_ring_begin(signaller, num_dwords); if (ret) return ret; -#undef MBOX_UPDATE_DWORDS for_each_ring(useless, dev_priv, i) { u32 mbox_reg = signaller->semaphore.mbox.signal[i]; @@ -703,15 +696,13 @@ static int gen6_signal(struct intel_engine_cs *signaller, intel_ring_emit(signaller, MI_LOAD_REGISTER_IMM(1)); intel_ring_emit(signaller, mbox_reg); intel_ring_emit(signaller, signaller->outstanding_lazy_seqno); - intel_ring_emit(signaller, MI_NOOP); - } else { - intel_ring_emit(signaller, MI_NOOP); - intel_ring_emit(signaller, MI_NOOP); - intel_ring_emit(signaller, MI_NOOP); - intel_ring_emit(signaller, MI_NOOP); } } + /* If num_dwords was rounded, make sure the tail pointer is correct */ + if (num_rings % 2 == 0) + intel_ring_emit(signaller, MI_NOOP); + return 0; } -- cgit v0.10.2 From 3e78998a588c64d6447849deabf630e837505a24 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:37 -0700 Subject: drm/i915/bdw: implement semaphore signal Semaphore signalling works similarly to previous GENs with the exception that the per ring mailboxes no longer exist. Instead you must define your own space, somewhere in the GTT. The comments in the code define the layout I've opted for, which should be fairly future proof. Ie. I tried to define offsets in abstract terms (NUM_RINGS, seqno size, etc). NOTE: If one wanted to move this to the HWSP they could. I've decided one 4k object would be easier to deal with, and provide potential wins with cache locality, but that's all speculative. v2: Update the macro to not need the other ring's ring->id (Chris) Update the comment to use the correct formula (Chris) v3: Move the macros the ringbuffer.h to prevent churn in next patch (Ville) v4: Fixed compilation rebase conflict commit 1ec9e26ddab06459e89a890431b2de064c5d1056 Author: Daniel Vetter Date: Fri Feb 14 14:01:11 2014 +0100 drm/i915: Consolidate binding parameters into flags v5: VCS2 rebase Replace hweight_long with hweight32 v6 (Rodrigo): * Add missed VC2 gen8 ring signal init * fixing conflicst on rebase * minor fixes on address table * remove WARN_ON Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi [danvet: s/BUG_ON/WARN_ON/] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 204de40..7ef67b6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1375,6 +1375,7 @@ struct drm_i915_private { struct pci_dev *bridge_dev; struct intel_engine_cs ring[I915_NUM_RINGS]; + struct drm_i915_gem_object *semaphore_obj; uint32_t last_seqno, next_seqno; drm_dma_handle_t *status_page_dmah; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 66151ff..218ca7b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -240,7 +240,7 @@ #define MI_DISPLAY_FLIP_IVB_SPRITE_B (3 << 19) #define MI_DISPLAY_FLIP_IVB_PLANE_C (4 << 19) #define MI_DISPLAY_FLIP_IVB_SPRITE_C (5 << 19) -#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6+ */ +#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6, gen7 */ #define MI_SEMAPHORE_GLOBAL_GTT (1<<22) #define MI_SEMAPHORE_UPDATE (1<<21) #define MI_SEMAPHORE_COMPARE (1<<20) @@ -266,6 +266,8 @@ #define MI_RESTORE_EXT_STATE_EN (1<<2) #define MI_FORCE_RESTORE (1<<1) #define MI_RESTORE_INHIBIT (1<<0) +#define MI_SEMAPHORE_SIGNAL MI_INSTR(0x1b, 0) /* GEN8+ */ +#define MI_SEMAPHORE_TARGET(engine) ((engine)<<15) #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) @@ -360,6 +362,7 @@ #define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */ #define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9) #define PIPE_CONTROL_NOTIFY (1<<8) +#define PIPE_CONTROL_FLUSH_ENABLE (1<<7) /* gen7+ */ #define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4) #define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3) #define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index e9c8814..e1aac25 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -660,6 +660,13 @@ static int init_render_ring(struct intel_engine_cs *ring) static void render_ring_cleanup(struct intel_engine_cs *ring) { struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->semaphore_obj) { + i915_gem_object_ggtt_unpin(dev_priv->semaphore_obj); + drm_gem_object_unreference(&dev_priv->semaphore_obj->base); + dev_priv->semaphore_obj = NULL; + } if (ring->scratch.obj == NULL) return; @@ -673,6 +680,80 @@ static void render_ring_cleanup(struct intel_engine_cs *ring) ring->scratch.obj = NULL; } +static int gen8_rcs_signal(struct intel_engine_cs *signaller, + unsigned int num_dwords) +{ +#define MBOX_UPDATE_DWORDS 8 + struct drm_device *dev = signaller->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *waiter; + int i, ret, num_rings; + + num_rings = hweight32(INTEL_INFO(dev)->ring_mask); + num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS; +#undef MBOX_UPDATE_DWORDS + + ret = intel_ring_begin(signaller, num_dwords); + if (ret) + return ret; + + for_each_ring(waiter, dev_priv, i) { + u64 gtt_offset = signaller->semaphore.signal_ggtt[i]; + if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) + continue; + + intel_ring_emit(signaller, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(signaller, PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_FLUSH_ENABLE); + intel_ring_emit(signaller, lower_32_bits(gtt_offset)); + intel_ring_emit(signaller, upper_32_bits(gtt_offset)); + intel_ring_emit(signaller, signaller->outstanding_lazy_seqno); + intel_ring_emit(signaller, 0); + intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL | + MI_SEMAPHORE_TARGET(waiter->id)); + intel_ring_emit(signaller, 0); + } + + return 0; +} + +static int gen8_xcs_signal(struct intel_engine_cs *signaller, + unsigned int num_dwords) +{ +#define MBOX_UPDATE_DWORDS 6 + struct drm_device *dev = signaller->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *waiter; + int i, ret, num_rings; + + num_rings = hweight32(INTEL_INFO(dev)->ring_mask); + num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS; +#undef MBOX_UPDATE_DWORDS + + ret = intel_ring_begin(signaller, num_dwords); + if (ret) + return ret; + + for_each_ring(waiter, dev_priv, i) { + u64 gtt_offset = signaller->semaphore.signal_ggtt[i]; + if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) + continue; + + intel_ring_emit(signaller, (MI_FLUSH_DW + 1) | + MI_FLUSH_DW_OP_STOREDW); + intel_ring_emit(signaller, lower_32_bits(gtt_offset) | + MI_FLUSH_DW_USE_GTT); + intel_ring_emit(signaller, upper_32_bits(gtt_offset)); + intel_ring_emit(signaller, signaller->outstanding_lazy_seqno); + intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL | + MI_SEMAPHORE_TARGET(waiter->id)); + intel_ring_emit(signaller, 0); + } + + return 0; +} + static int gen6_signal(struct intel_engine_cs *signaller, unsigned int num_dwords) { @@ -1942,12 +2023,30 @@ int intel_init_render_ring_buffer(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring = &dev_priv->ring[RCS]; + struct drm_i915_gem_object *obj; + int ret; ring->name = "render ring"; ring->id = RCS; ring->mmio_base = RENDER_RING_BASE; if (INTEL_INFO(dev)->gen >= 8) { + if (i915_semaphore_is_enabled(dev)) { + obj = i915_gem_alloc_object(dev, 4096); + if (obj == NULL) { + DRM_ERROR("Failed to allocate semaphore bo. Disabling semaphores\n"); + i915.semaphores = 0; + } else { + i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); + ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_NONBLOCK); + if (ret != 0) { + drm_gem_object_unreference(&obj->base); + DRM_ERROR("Failed to pin semaphore bo. Disabling semaphores\n"); + i915.semaphores = 0; + } else + dev_priv->semaphore_obj = obj; + } + } ring->add_request = gen6_add_request; ring->flush = gen8_render_ring_flush; ring->irq_get = gen8_ring_get_irq; @@ -1956,18 +2055,10 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->get_seqno = gen6_ring_get_seqno; ring->set_seqno = ring_set_seqno; if (i915_semaphore_is_enabled(dev)) { + WARN_ON(!dev_priv->semaphore_obj); ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + ring->semaphore.signal = gen8_rcs_signal; + GEN8_RING_SEMAPHORE_INIT; } } else if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; @@ -2045,9 +2136,6 @@ int intel_init_render_ring_buffer(struct drm_device *dev) /* Workaround batchbuffer to combat CS tlb bug. */ if (HAS_BROKEN_CS_TLB(dev)) { - struct drm_i915_gem_object *obj; - int ret; - obj = i915_gem_alloc_object(dev, I830_BATCH_LIMIT); if (obj == NULL) { DRM_ERROR("Failed to allocate batch bo\n"); @@ -2180,25 +2268,8 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - /* - * The current semaphore is only applied on - * pre-gen8 platform. And there is no VCS2 ring - * on the pre-gen8 platform. So the semaphore - * between VCS and VCS2 is initialized as - * INVALID. Gen8 will initialize the sema - * between VCS2 and VCS later. - */ - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + ring->semaphore.signal = gen8_xcs_signal; + GEN8_RING_SEMAPHORE_INIT; } } else { ring->irq_enable_mask = GT_BSD_USER_INTERRUPT; @@ -2273,24 +2344,10 @@ int intel_init_bsd2_ring_buffer(struct drm_device *dev) ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - /* - * The current semaphore is only applied on the pre-gen8. And there - * is no bsd2 ring on the pre-gen8. So now the semaphore_register - * between VCS2 and other ring is initialized as invalid. - * Gen8 will initialize the sema between VCS2 and other ring later. - */ - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; - + if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.signal = gen8_xcs_signal; + GEN8_RING_SEMAPHORE_INIT; + } ring->init = init_ring_common; return intel_init_ring_buffer(dev, ring); @@ -2318,17 +2375,8 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + ring->semaphore.signal = gen8_xcs_signal; + GEN8_RING_SEMAPHORE_INIT; } } else { ring->irq_enable_mask = GT_BLT_USER_INTERRUPT; @@ -2385,17 +2433,8 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev) ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { ring->semaphore.sync_to = gen6_ring_sync; - ring->semaphore.signal = gen6_signal; - ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID; - ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC; - ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC; + ring->semaphore.signal = gen8_xcs_signal; + GEN8_RING_SEMAPHORE_INIT; } } else { ring->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 2e8b516..17ef9cd 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -40,6 +40,32 @@ struct intel_hw_status_page { #define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base)) #define I915_WRITE_MODE(ring, val) I915_WRITE(RING_MI_MODE((ring)->mmio_base), val) +/* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to + * do the writes, and that must have qw aligned offsets, simply pretend it's 8b. + */ +#define i915_semaphore_seqno_size sizeof(uint64_t) +#define GEN8_SIGNAL_OFFSET(__ring, to) \ + (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \ + ((__ring)->id * I915_NUM_RINGS * i915_semaphore_seqno_size) + \ + (i915_semaphore_seqno_size * (to))) + +#define GEN8_WAIT_OFFSET(__ring, from) \ + (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \ + ((from) * I915_NUM_RINGS * i915_semaphore_seqno_size) + \ + (i915_semaphore_seqno_size * (__ring)->id)) + +#define GEN8_RING_SEMAPHORE_INIT do { \ + if (!dev_priv->semaphore_obj) { \ + break; \ + } \ + ring->semaphore.signal_ggtt[RCS] = GEN8_SIGNAL_OFFSET(ring, RCS); \ + ring->semaphore.signal_ggtt[VCS] = GEN8_SIGNAL_OFFSET(ring, VCS); \ + ring->semaphore.signal_ggtt[BCS] = GEN8_SIGNAL_OFFSET(ring, BCS); \ + ring->semaphore.signal_ggtt[VECS] = GEN8_SIGNAL_OFFSET(ring, VECS); \ + ring->semaphore.signal_ggtt[VCS2] = GEN8_SIGNAL_OFFSET(ring, VCS2); \ + ring->semaphore.signal_ggtt[ring->id] = MI_SEMAPHORE_SYNC_INVALID; \ + } while(0) + enum intel_ring_hangcheck_action { HANGCHECK_IDLE = 0, HANGCHECK_WAIT, @@ -127,15 +153,55 @@ struct intel_engine_cs { #define I915_DISPATCH_PINNED 0x2 void (*cleanup)(struct intel_engine_cs *ring); + /* GEN8 signal/wait table - never trust comments! + * signal to signal to signal to signal to signal to + * RCS VCS BCS VECS VCS2 + * -------------------------------------------------------------------- + * RCS | NOP (0x00) | VCS (0x08) | BCS (0x10) | VECS (0x18) | VCS2 (0x20) | + * |------------------------------------------------------------------- + * VCS | RCS (0x28) | NOP (0x30) | BCS (0x38) | VECS (0x40) | VCS2 (0x48) | + * |------------------------------------------------------------------- + * BCS | RCS (0x50) | VCS (0x58) | NOP (0x60) | VECS (0x68) | VCS2 (0x70) | + * |------------------------------------------------------------------- + * VECS | RCS (0x78) | VCS (0x80) | BCS (0x88) | NOP (0x90) | VCS2 (0x98) | + * |------------------------------------------------------------------- + * VCS2 | RCS (0xa0) | VCS (0xa8) | BCS (0xb0) | VECS (0xb8) | NOP (0xc0) | + * |------------------------------------------------------------------- + * + * Generalization: + * f(x, y) := (x->id * NUM_RINGS * seqno_size) + (seqno_size * y->id) + * ie. transpose of g(x, y) + * + * sync from sync from sync from sync from sync from + * RCS VCS BCS VECS VCS2 + * -------------------------------------------------------------------- + * RCS | NOP (0x00) | VCS (0x28) | BCS (0x50) | VECS (0x78) | VCS2 (0xa0) | + * |------------------------------------------------------------------- + * VCS | RCS (0x08) | NOP (0x30) | BCS (0x58) | VECS (0x80) | VCS2 (0xa8) | + * |------------------------------------------------------------------- + * BCS | RCS (0x10) | VCS (0x38) | NOP (0x60) | VECS (0x88) | VCS2 (0xb0) | + * |------------------------------------------------------------------- + * VECS | RCS (0x18) | VCS (0x40) | BCS (0x68) | NOP (0x90) | VCS2 (0xb8) | + * |------------------------------------------------------------------- + * VCS2 | RCS (0x20) | VCS (0x48) | BCS (0x70) | VECS (0x98) | NOP (0xc0) | + * |------------------------------------------------------------------- + * + * Generalization: + * g(x, y) := (y->id * NUM_RINGS * seqno_size) + (seqno_size * x->id) + * ie. transpose of f(x, y) + */ struct { u32 sync_seqno[I915_NUM_RINGS-1]; - struct { - /* our mbox written by others */ - u32 wait[I915_NUM_RINGS]; - /* mboxes this ring signals to */ - u32 signal[I915_NUM_RINGS]; - } mbox; + union { + struct { + /* our mbox written by others */ + u32 wait[I915_NUM_RINGS]; + /* mboxes this ring signals to */ + u32 signal[I915_NUM_RINGS]; + } mbox; + u64 signal_ggtt[I915_NUM_RINGS]; + }; /* AKA wait() */ int (*sync_to)(struct intel_engine_cs *ring, -- cgit v0.10.2 From 5ee426ca135c91e071d23853e876b957526841c5 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:38 -0700 Subject: drm/i915/bdw: implement semaphore wait Semaphore waits use a new instruction, MI_SEMAPHORE_WAIT. The seqno to wait on is all well defined by the table in the previous patch. There is nothing else different from previous GEN's semaphore synchronization code. v2: Update macros to not require the other ring's ring->id (Chris) v3: Add missing VCS2 gen8_ring_wait init besides s/ring_buffer/engine_cs (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 218ca7b..8c0f70d 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -268,6 +268,9 @@ #define MI_RESTORE_INHIBIT (1<<0) #define MI_SEMAPHORE_SIGNAL MI_INSTR(0x1b, 0) /* GEN8+ */ #define MI_SEMAPHORE_TARGET(engine) ((engine)<<15) +#define MI_SEMAPHORE_WAIT MI_INSTR(0x1c, 2) /* GEN8+ */ +#define MI_SEMAPHORE_POLL (1<<15) +#define MI_SEMAPHORE_SAD_GTE_SDD (1<<12) #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index e1aac25..8819680 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -832,6 +832,31 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev, * @signaller - ring which has, or will signal * @seqno - seqno which the waiter will block on */ + +static int +gen8_ring_sync(struct intel_engine_cs *waiter, + struct intel_engine_cs *signaller, + u32 seqno) +{ + struct drm_i915_private *dev_priv = waiter->dev->dev_private; + int ret; + + ret = intel_ring_begin(waiter, 4); + if (ret) + return ret; + + intel_ring_emit(waiter, MI_SEMAPHORE_WAIT | + MI_SEMAPHORE_GLOBAL_GTT | + MI_SEMAPHORE_SAD_GTE_SDD); + intel_ring_emit(waiter, seqno); + intel_ring_emit(waiter, + lower_32_bits(GEN8_WAIT_OFFSET(waiter, signaller->id))); + intel_ring_emit(waiter, + upper_32_bits(GEN8_WAIT_OFFSET(waiter, signaller->id))); + intel_ring_advance(waiter); + return 0; +} + static int gen6_ring_sync(struct intel_engine_cs *waiter, struct intel_engine_cs *signaller, @@ -2056,7 +2081,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->set_seqno = ring_set_seqno; if (i915_semaphore_is_enabled(dev)) { WARN_ON(!dev_priv->semaphore_obj); - ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.sync_to = gen8_ring_sync; ring->semaphore.signal = gen8_rcs_signal; GEN8_RING_SEMAPHORE_INIT; } @@ -2267,7 +2292,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { - ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.sync_to = gen8_ring_sync; ring->semaphore.signal = gen8_xcs_signal; GEN8_RING_SEMAPHORE_INIT; } @@ -2343,8 +2368,8 @@ int intel_init_bsd2_ring_buffer(struct drm_device *dev) ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; - ring->semaphore.sync_to = gen6_ring_sync; if (i915_semaphore_is_enabled(dev)) { + ring->semaphore.sync_to = gen8_ring_sync; ring->semaphore.signal = gen8_xcs_signal; GEN8_RING_SEMAPHORE_INIT; } @@ -2374,7 +2399,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { - ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.sync_to = gen8_ring_sync; ring->semaphore.signal = gen8_xcs_signal; GEN8_RING_SEMAPHORE_INIT; } @@ -2432,7 +2457,7 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev) ring->irq_put = gen8_ring_put_irq; ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; if (i915_semaphore_is_enabled(dev)) { - ring->semaphore.sync_to = gen6_ring_sync; + ring->semaphore.sync_to = gen8_ring_sync; ring->semaphore.signal = gen8_xcs_signal; GEN8_RING_SEMAPHORE_INIT; } -- cgit v0.10.2 From a6cdb93a7a135b853353fffecbdc2e60ba56a016 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 30 Jun 2014 09:53:39 -0700 Subject: drm/i915: Implement MI decode for gen8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ipehr just carries Dword 0 and on Gen 8, offsets are located on Dword 2 and 3 of MI_SEMAPHORE_WAIT. This implementation was based on Ben's work and on Ville's suggestion for Ben Cc: Ville Syrjälä Cc: Ben Widawsky Signed-off-by: Rodrigo Vivi Reviewed-by: Ben Widawsky [danvet: Fixup format string.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c50e3b4..d672053 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2927,12 +2927,7 @@ static bool ipehr_is_semaphore_wait(struct drm_device *dev, u32 ipehr) { if (INTEL_INFO(dev)->gen >= 8) { - /* - * FIXME: gen8 semaphore support - currently we don't emit - * semaphores on bdw anyway, but this needs to be addressed when - * we merge that code. - */ - return false; + return (ipehr >> 23) == 0x1c; } else { ipehr &= ~MI_SEMAPHORE_SYNC_MASK; return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | @@ -2941,19 +2936,20 @@ ipehr_is_semaphore_wait(struct drm_device *dev, u32 ipehr) } static struct intel_engine_cs * -semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr) +semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr, u64 offset) { struct drm_i915_private *dev_priv = ring->dev->dev_private; struct intel_engine_cs *signaller; int i; if (INTEL_INFO(dev_priv->dev)->gen >= 8) { - /* - * FIXME: gen8 semaphore support - currently we don't emit - * semaphores on bdw anyway, but this needs to be addressed when - * we merge that code. - */ - return NULL; + for_each_ring(signaller, dev_priv, i) { + if (ring == signaller) + continue; + + if (offset == signaller->semaphore.signal_ggtt[ring->id]) + return signaller; + } } else { u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK; @@ -2966,8 +2962,8 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr) } } - DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x\n", - ring->id, ipehr); + DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x, offset 0x%016llx\n", + ring->id, ipehr, offset); return NULL; } @@ -2977,7 +2973,8 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno) { struct drm_i915_private *dev_priv = ring->dev->dev_private; u32 cmd, ipehr, head; - int i; + u64 offset = 0; + int i, backwards; ipehr = I915_READ(RING_IPEHR(ring->mmio_base)); if (!ipehr_is_semaphore_wait(ring->dev, ipehr)) @@ -2986,13 +2983,15 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno) /* * HEAD is likely pointing to the dword after the actual command, * so scan backwards until we find the MBOX. But limit it to just 3 - * dwords. Note that we don't care about ACTHD here since that might + * or 4 dwords depending on the semaphore wait command size. + * Note that we don't care about ACTHD here since that might * point at at batch, and semaphores are always emitted into the * ringbuffer itself. */ head = I915_READ_HEAD(ring) & HEAD_ADDR; + backwards = (INTEL_INFO(ring->dev)->gen >= 8) ? 5 : 4; - for (i = 4; i; --i) { + for (i = backwards; i; --i) { /* * Be paranoid and presume the hw has gone off into the wild - * our ring is smaller than what the hardware (and hence @@ -3012,7 +3011,12 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno) return NULL; *seqno = ioread32(ring->buffer->virtual_start + head + 4) + 1; - return semaphore_wait_to_signaller_ring(ring, ipehr); + if (INTEL_INFO(ring->dev)->gen >= 8) { + offset = ioread32(ring->buffer->virtual_start + head + 12); + offset <<= 32; + offset = ioread32(ring->buffer->virtual_start + head + 8); + } + return semaphore_wait_to_signaller_ring(ring, ipehr, offset); } static int semaphore_passed(struct intel_engine_cs *ring) -- cgit v0.10.2 From 87f85ebc8dd089842e9efafd811486e5997b2265 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:40 -0700 Subject: drm/i915: Extract semaphore error collection v2: s/ring_buffer/engine_cs (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 66cf417..9d42b6a 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -746,6 +746,23 @@ static void i915_gem_record_fences(struct drm_device *dev, } } + +static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv, + struct intel_engine_cs *ring, + struct drm_i915_error_ring *ering) +{ + ering->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(ring->mmio_base)); + ering->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(ring->mmio_base)); + ering->semaphore_seqno[0] = ring->semaphore.sync_seqno[0]; + ering->semaphore_seqno[1] = ring->semaphore.sync_seqno[1]; + + if (HAS_VEBOX(dev_priv->dev)) { + ering->semaphore_mboxes[2] = + I915_READ(RING_SYNC_2(ring->mmio_base)); + ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2]; + } +} + static void i915_record_ring_state(struct drm_device *dev, struct intel_engine_cs *ring, struct drm_i915_error_ring *ering) @@ -755,18 +772,7 @@ static void i915_record_ring_state(struct drm_device *dev, if (INTEL_INFO(dev)->gen >= 6) { ering->rc_psmi = I915_READ(ring->mmio_base + 0x50); ering->fault_reg = I915_READ(RING_FAULT_REG(ring)); - ering->semaphore_mboxes[0] - = I915_READ(RING_SYNC_0(ring->mmio_base)); - ering->semaphore_mboxes[1] - = I915_READ(RING_SYNC_1(ring->mmio_base)); - ering->semaphore_seqno[0] = ring->semaphore.sync_seqno[0]; - ering->semaphore_seqno[1] = ring->semaphore.sync_seqno[1]; - } - - if (HAS_VEBOX(dev)) { - ering->semaphore_mboxes[2] = - I915_READ(RING_SYNC_2(ring->mmio_base)); - ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2]; + gen6_record_semaphore_state(dev_priv, ring, ering); } if (INTEL_INFO(dev)->gen >= 4) { -- cgit v0.10.2 From 0ca36d78391dc97b7d3e3d26e4518e30f3e88c16 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:41 -0700 Subject: drm/i915/bdw: collect semaphore error state Since the semaphore information is in an object, just dump it, and let the user parse it later. NOTE: The page being used for the semaphores are incoherent with the CPU. No matter what I do, I cannot figure out a way to read anything but 0s. Note that the semaphore waits are indeed working. v2: Don't print signal, and wait (they should be the same). Instead, print sync_seqno (Chris) v3: Free the semaphore error object (Chris) v4: Fix semaphore offset calculation during error state collection (Ville) v5: VCS2 rebase Make semaphore object error capture coding style consistent (Ville) Do the proper math for the signal offset (Ville) v6: Fix small conflicts on rebase and s/ring_buffer/engine_cs (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7ef67b6..44f26eb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -324,6 +324,7 @@ struct drm_i915_error_state { u64 fence[I915_MAX_NUM_FENCES]; struct intel_overlay_error_state *overlay; struct intel_display_error_state *display; + struct drm_i915_error_object *semaphore_obj; struct drm_i915_error_ring { bool valid; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 9d42b6a..45b6191 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -327,6 +327,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, struct drm_device *dev = error_priv->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_error_state *error = error_priv->error; + struct drm_i915_error_object *obj; int i, j, offset, elt; int max_hangcheck_score; @@ -395,8 +396,6 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, error->pinned_bo_count[0]); for (i = 0; i < ARRAY_SIZE(error->ring); i++) { - struct drm_i915_error_object *obj; - obj = error->ring[i].batchbuffer; if (obj) { err_puts(m, dev_priv->ring[i].name); @@ -459,6 +458,18 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } } + if ((obj = error->semaphore_obj)) { + err_printf(m, "Semaphore page = 0x%08x\n", obj->gtt_offset); + for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { + err_printf(m, "[%04x] %08x %08x %08x %08x\n", + elt * 4, + obj->pages[0][elt], + obj->pages[0][elt+1], + obj->pages[0][elt+2], + obj->pages[0][elt+3]); + } + } + if (error->overlay) intel_overlay_print_error_state(m, error->overlay); @@ -529,6 +540,7 @@ static void i915_error_state_free(struct kref *error_ref) kfree(error->ring[i].requests); } + i915_error_object_free(error->semaphore_obj); kfree(error->active_bo); kfree(error->overlay); kfree(error->display); @@ -747,6 +759,33 @@ static void i915_gem_record_fences(struct drm_device *dev, } +static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv, + struct drm_i915_error_state *error, + struct intel_engine_cs *ring, + struct drm_i915_error_ring *ering) +{ + struct intel_engine_cs *useless; + int i; + + if (!i915_semaphore_is_enabled(dev_priv->dev)) + return; + + if (!error->semaphore_obj) + error->semaphore_obj = + i915_error_object_create(dev_priv, + dev_priv->semaphore_obj, + &dev_priv->gtt.base); + + for_each_ring(useless, dev_priv, i) { + u16 signal_offset = + (GEN8_SIGNAL_OFFSET(ring, i) & PAGE_MASK) / 4; + u32 *tmp = error->semaphore_obj->pages[0]; + + ering->semaphore_mboxes[i] = tmp[signal_offset]; + ering->semaphore_seqno[i] = ring->semaphore.sync_seqno[i]; + } +} + static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv, struct intel_engine_cs *ring, struct drm_i915_error_ring *ering) @@ -764,6 +803,7 @@ static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv, } static void i915_record_ring_state(struct drm_device *dev, + struct drm_i915_error_state *error, struct intel_engine_cs *ring, struct drm_i915_error_ring *ering) { @@ -772,7 +812,10 @@ static void i915_record_ring_state(struct drm_device *dev, if (INTEL_INFO(dev)->gen >= 6) { ering->rc_psmi = I915_READ(ring->mmio_base + 0x50); ering->fault_reg = I915_READ(RING_FAULT_REG(ring)); - gen6_record_semaphore_state(dev_priv, ring, ering); + if (INTEL_INFO(dev)->gen >= 8) + gen8_record_semaphore_state(dev_priv, error, ring, ering); + else + gen6_record_semaphore_state(dev_priv, ring, ering); } if (INTEL_INFO(dev)->gen >= 4) { @@ -901,7 +944,7 @@ static void i915_gem_record_rings(struct drm_device *dev, error->ring[i].valid = true; - i915_record_ring_state(dev, ring, &error->ring[i]); + i915_record_ring_state(dev, error, ring, &error->ring[i]); request = i915_gem_find_active_request(ring); if (request) { -- cgit v0.10.2 From e04934cf256154fd6cc88a0767e45bd1b4920419 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:42 -0700 Subject: drm/i915: semaphore debugfs Simple debugfs file to display the current state of semaphores. This is useful if you want to see the state without hanging the GPU. NOTE: This patch is optional to the series. NOTE2: Like the GPU error state collection, the reads are currently incoherent. v2 (Rodrigo): * Iterate only on active rings. * s/ring_buffer/engine_cs. Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index bfd0d41..c1b88a8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2395,6 +2395,76 @@ static int i915_display_info(struct seq_file *m, void *unused) return 0; } +static int i915_semaphore_status(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring; + int num_rings = hweight32(INTEL_INFO(dev)->ring_mask); + int i, j, ret; + + if (!i915_semaphore_is_enabled(dev)) { + seq_puts(m, "Semaphores are disabled\n"); + return 0; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (IS_BROADWELL(dev)) { + struct page *page; + uint64_t *seqno; + + page = i915_gem_object_get_page(dev_priv->semaphore_obj, 0); + + seqno = (uint64_t *)kmap_atomic(page); + for_each_ring(ring, dev_priv, i) { + uint64_t offset; + + seq_printf(m, "%s\n", ring->name); + + seq_puts(m, " Last signal:"); + for (j = 0; j < num_rings; j++) { + offset = i * I915_NUM_RINGS + j; + seq_printf(m, "0x%08llx (0x%02llx) ", + seqno[offset], offset * 8); + } + seq_putc(m, '\n'); + + seq_puts(m, " Last wait: "); + for (j = 0; j < num_rings; j++) { + offset = i + (j * I915_NUM_RINGS); + seq_printf(m, "0x%08llx (0x%02llx) ", + seqno[offset], offset * 8); + } + seq_putc(m, '\n'); + + } + kunmap_atomic(seqno); + } else { + seq_puts(m, " Last signal:"); + for_each_ring(ring, dev_priv, i) + for (j = 0; j < num_rings; j++) + seq_printf(m, "0x%08x\n", + I915_READ(ring->semaphore.mbox.signal[j])); + seq_putc(m, '\n'); + } + + seq_puts(m, "\nSync seqno:\n"); + for_each_ring(ring, dev_priv, i) { + for (j = 0; j < num_rings; j++) { + seq_printf(m, " 0x%08x ", ring->semaphore.sync_seqno[j]); + } + seq_putc(m, '\n'); + } + seq_putc(m, '\n'); + + mutex_unlock(&dev->struct_mutex); + return 0; +} + struct pipe_crc_info { const char *name; struct drm_device *dev; @@ -3904,6 +3974,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_pc8_status", i915_pc8_status, 0}, {"i915_power_domain_info", i915_power_domain_info, 0}, {"i915_display_info", i915_display_info, 0}, + {"i915_semaphore_status", i915_semaphore_status, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) -- cgit v0.10.2 From bae4fcd2c7b778a3f69e03e244221cd4932b8f67 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 30 Jun 2014 09:53:43 -0700 Subject: drm/i915/bdw: poll semaphores As Ville points out, it's possible/probable we don't actually need this. Potentially, this validates the letter of the spec, and not the spirit. Ville: > I discussed this on irc w/ Ben, and I was suggesting we don't need to > poll. Polling apparently can be used as a workaround for certain > hardware issues, but it looks like those issues shouldn't affect us, > for the momemnt at least. So my suggestion was to try w/o polling > first (since there could be some power cost to polling) and add the > poll bit if problems arise. Rodrigo: Spec suggests this as an W/A for GT3. However semaphores didn't worked in my BDW GT2 on Signal Mode. So pool mode is definitely needed. Reviewed-by: Rodrigo Vivi Tested-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 8819680..e18ed05 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -847,6 +847,7 @@ gen8_ring_sync(struct intel_engine_cs *waiter, intel_ring_emit(waiter, MI_SEMAPHORE_WAIT | MI_SEMAPHORE_GLOBAL_GTT | + MI_SEMAPHORE_POLL | MI_SEMAPHORE_SAD_GTE_SDD); intel_ring_emit(waiter, seqno); intel_ring_emit(waiter, -- cgit v0.10.2 From 521e62e49a42661a4ee0102644517dbe2f100a23 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 30 Jun 2014 09:53:44 -0700 Subject: drm/i915: Enable semaphores on BDW Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 8a0cb0c..bc19623 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -477,10 +477,6 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) if (i915.semaphores >= 0) return i915.semaphores; - /* Until we get further testing... */ - if (IS_GEN8(dev)) - return false; - #ifdef CONFIG_INTEL_IOMMU /* Enable semaphores on SNB when IO remapping is off */ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) -- cgit v0.10.2 From f7d2323c181ed5a2596494b860a99d567fd3e6cd Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 31 Mar 2014 11:13:56 -0700 Subject: drm/i915: correct BLC vs PWM enable/disable ordering With the new checks in place, we can see we're doing things backwards, so fix them up per the spec. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2da413c..e0f1bd1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1316,8 +1316,6 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power off\n"); - edp_wait_backlight_off(intel_dp); - WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); pp = ironlake_get_pp_control(intel_dp); @@ -1353,6 +1351,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) return; DRM_DEBUG_KMS("\n"); + + intel_panel_enable_backlight(intel_dp->attached_connector); + /* * If we enable the backlight right away following a panel power * on, we may see slight flicker as the panel syncs with the eDP @@ -1367,8 +1368,6 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); - - intel_panel_enable_backlight(intel_dp->attached_connector); } void intel_edp_backlight_off(struct intel_dp *intel_dp) @@ -1381,8 +1380,6 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return; - intel_panel_disable_backlight(intel_dp->attached_connector); - DRM_DEBUG_KMS("\n"); pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_BLC_ENABLE; @@ -1392,6 +1389,10 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); intel_dp->last_backlight_off = jiffies; + + edp_wait_backlight_off(intel_dp); + + intel_panel_disable_backlight(intel_dp->attached_connector); } static void ironlake_edp_pll_on(struct intel_dp *intel_dp) -- cgit v0.10.2 From ab5a60c3ee41ff22304e2bcf63c151aa2851df0c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 May 2014 12:45:39 +0200 Subject: drm/omap: use __GFP_DMA32 for shmem-backed gem OMAP requires bo-pages to be in the DMA32 zone. Explicitly request this by setting __GFP_DMA32 as mapping-gfp-mask during shmem initialization. This drops HIGHMEM from the gfp-mask and uses DMA32 instead. shmem-core takes care to relocate pages during swap-in in case they have been loaded into the wrong zone. It is _not_ possible to pass __GFP_DMA32 to shmem_read_mapping_page_gfp() as the page might have already been swapped-in at that time. The zone-mask must be set during initialization and be kept constant for now. Remove the now superfluous TODO in omap_gem.c. Reviewed-by: Rob Clark Tested-by: Tomi Valkeinen Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 95dbce2..1331fd5 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -233,10 +233,6 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) WARN_ON(omap_obj->pages); - /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the - * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably - * we actually want CMA memory for it all anyways.. - */ pages = drm_gem_get_pages(obj, GFP_KERNEL); if (IS_ERR(pages)) { dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); @@ -1347,6 +1343,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj; struct drm_gem_object *obj = NULL; + struct address_space *mapping; size_t size; int ret; @@ -1404,14 +1401,16 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, omap_obj->height = gsize.tiled.height; } - ret = 0; - if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) + if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) { drm_gem_private_object_init(dev, obj, size); - else + } else { ret = drm_gem_object_init(dev, obj, size); + if (ret) + goto fail; - if (ret) - goto fail; + mapping = file_inode(obj->filp)->i_mapping; + mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32); + } return obj; -- cgit v0.10.2 From 0cdbe8ac696b5399327f972a1c91263c1a44f1d9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 May 2014 12:59:47 +0200 Subject: drm/gem: remove misleading gfp parameter to get_pages() drm_gem_get_pages() currently allows passing a 'gfp' parameter that is passed to shmem combined with mapping_gfp_mask(). Given that the default mapping_gfp_mask() is GFP_HIGHUSER, it is _very_ unlikely that anyone will ever make use of that parameter. In fact, all drivers currently pass redundant flags or 0. This patch removes the 'gfp' parameter. The only reason to keep it is to remove flags like __GFP_WAIT. But in its current form, it can only be used to add flags. So to remove __GFP_WAIT, you'd have to drop it from the mapping_gfp_mask, which again is stupid as this mask is used by shmem-core for other allocations, too. If any driver ever requires that parameter, we can introduce a new helper that takes the raw 'gfp' parameter. The caller'd be responsible to combine it with mapping_gfp_mask() in a suitable way. The current drm_gem_get_pages() helper would then simply use mapping_gfp_mask() and call the new helper. This is what shmem_read_mapping_pages{_gfp,} does right now. Moreover, the gfp-zone flag-usage is not obvious: If you pass a modified zone, shmem core will WARN() or even BUG(). In other words, the following must be true for 'gfp' passed to shmem_read_mapping_pages_gfp(): gfp_zone(mapping_gfp_mask(mapping)) == gfp_zone(gfp) Add a comment to drm_gem_read_pages() explaining that constraint. Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index f7d7119..6adee4c 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset); * drm_gem_get_pages - helper to allocate backing pages for a GEM object * from shmem * @obj: obj in question - * @gfpmask: gfp mask of requested pages + * + * This reads the page-array of the shmem-backing storage of the given gem + * object. An array of pages is returned. If a page is not allocated or + * swapped-out, this will allocate/swap-in the required pages. Note that the + * whole object is covered by the page-array and pinned in memory. + * + * Use drm_gem_put_pages() to release the array and unpin all pages. + * + * This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()). + * If you require other GFP-masks, you have to do those allocations yourself. + * + * Note that you are not allowed to change gfp-zones during runtime. That is, + * shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as + * set during initialization. If you have special zone constraints, set them + * after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care + * to keep pages in the required zone during swap-in. */ -struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) +struct page **drm_gem_get_pages(struct drm_gem_object *obj) { - struct inode *inode; struct address_space *mapping; struct page *p, **pages; int i, npages; /* This is the shared memory object that backs the GEM resource */ - inode = file_inode(obj->filp); - mapping = inode->i_mapping; + mapping = file_inode(obj->filp)->i_mapping; /* We already BUG_ON() for non-page-aligned sizes in * drm_gem_object_init(), so we should never hit this unless @@ -466,10 +479,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) if (pages == NULL) return ERR_PTR(-ENOMEM); - gfpmask |= mapping_gfp_mask(mapping); - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + p = shmem_read_mapping_page(mapping, i); if (IS_ERR(p)) goto fail; pages[i] = p; @@ -479,7 +490,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping) * so shmem can relocate pages during swapin if required. */ - BUG_ON((gfpmask & __GFP_DMA32) && + BUG_ON((mapping_gfp_mask(mapping) & __GFP_DMA32) && (page_to_pfn(p) >= 0x00100000UL)); } diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 592d205..ce015db 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -206,7 +206,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) WARN_ON(gt->pages); - pages = drm_gem_get_pages(>->gem, 0); + pages = drm_gem_get_pages(>->gem); if (IS_ERR(pages)) return PTR_ERR(pages); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index bb8026d..6866879 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -73,7 +73,7 @@ static struct page **get_pages(struct drm_gem_object *obj) int npages = obj->size >> PAGE_SHIFT; if (iommu_present(&platform_bus_type)) - p = drm_gem_get_pages(obj, 0); + p = drm_gem_get_pages(obj); else p = get_pages_vram(obj, npages); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 1331fd5..7c68c8a 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -233,7 +233,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) WARN_ON(omap_obj->pages); - pages = drm_gem_get_pages(obj, GFP_KERNEL); + pages = drm_gem_get_pages(obj); if (IS_ERR(pages)) { dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); return PTR_ERR(pages); diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index c041cd7..8044f5f 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -107,14 +107,14 @@ int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } } -static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask) +static int udl_gem_get_pages(struct udl_gem_object *obj) { struct page **pages; if (obj->pages) return 0; - pages = drm_gem_get_pages(&obj->base, gfpmask); + pages = drm_gem_get_pages(&obj->base); if (IS_ERR(pages)) return PTR_ERR(pages); @@ -147,7 +147,7 @@ int udl_gem_vmap(struct udl_gem_object *obj) return 0; } - ret = udl_gem_get_pages(obj, GFP_KERNEL); + ret = udl_gem_get_pages(obj); if (ret) return ret; @@ -205,7 +205,7 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, } gobj = to_udl_bo(obj); - ret = udl_gem_get_pages(gobj, GFP_KERNEL); + ret = udl_gem_get_pages(gobj); if (ret) goto out; ret = drm_gem_create_mmap_offset(obj); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8af71a8..57c07cd 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1574,7 +1574,7 @@ void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size); -struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); +struct page **drm_gem_get_pages(struct drm_gem_object *obj); void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty, bool accessed); -- cgit v0.10.2 From 15f8f65751f081bd2d57627c453fe8986a3a0718 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Jun 2014 15:39:18 +0900 Subject: drm/ttm: remove declaration of ttm_tt_cache_flush ttm_tt_cache_flush's implementation was removed in 2009 by commit c9c97b8c, but its declaration has been hiding in ttm_bo_driver.h since then. It has been surviving in the dark for too long now ; give it the mercy blow. Reviewed-by: Thierry Reding Reviewed-by: David Herrmann Signed-off-by: Alexandre Courbot Signed-off-by: David Herrmann diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index a5183da..e3f8c99 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -653,18 +653,6 @@ extern void ttm_tt_unbind(struct ttm_tt *ttm); extern int ttm_tt_swapin(struct ttm_tt *ttm); /** - * ttm_tt_cache_flush: - * - * @pages: An array of pointers to struct page:s to flush. - * @num_pages: Number of pages to flush. - * - * Flush the data of the indicated pages from the cpu caches. - * This is used when changing caching attributes of the pages from - * cache-coherent. - */ -extern void ttm_tt_cache_flush(struct page *pages[], unsigned long num_pages); - -/** * ttm_tt_set_placement_caching: * * @ttm A struct ttm_tt the backing pages of which will change caching policy. -- cgit v0.10.2 From 6d6dfcfb883818b40b58bac61cc72cab428a7a03 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 16 Mar 2014 14:38:40 +0100 Subject: drm: enable render-nodes by default We introduced render-nodes about 1/2 year ago and no problems showed up. Remove the drm_rnodes argument and enable them by default now. Acked-by: Daniel Vetter Acked-by: Alex Deucher Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 14d1646..07bf701 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -37,9 +37,6 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); -unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */ -EXPORT_SYMBOL(drm_rnodes); - /* 1 to allow user space to request universal planes (experimental) */ unsigned int drm_universal_planes = 0; EXPORT_SYMBOL(drm_universal_planes); @@ -60,13 +57,11 @@ MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); -MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); -module_param_named(rnodes, drm_rnodes, int, 0600); module_param_named(universal_planes, drm_universal_planes, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); @@ -588,7 +583,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, goto err_minors; } - if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) { + if (drm_core_check_feature(dev, DRIVER_RENDER)) { ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); if (ret) goto err_minors; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 57c07cd..dc74c97 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1395,7 +1395,6 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; -extern unsigned int drm_rnodes; extern unsigned int drm_universal_planes; extern unsigned int drm_vblank_offdelay; -- cgit v0.10.2 From 2fc2dd781bec793fbc293ecc9345b233783a6382 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 27 Jun 2014 20:29:09 +0900 Subject: drm/ttm: recognize ARM arch in ioprot handler Nouveau can now be used on ARM, so add an ioprot handler for this architecture. Signed-off-by: Lucas Stach Signed-off-by: Alexandre Courbot Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 1df856f..30e5d90 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -500,7 +500,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp) pgprot_val(tmp) |= _PAGE_GUARDED; } #endif -#if defined(__ia64__) +#if defined(__ia64__) || defined(__arm__) if (caching_flags & TTM_PL_FLAG_WC) tmp = pgprot_writecombine(tmp); else -- cgit v0.10.2 From a8ba29cd337cce06899e1ad51c69b4beea7628a0 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 1 Jul 2014 21:02:03 +0200 Subject: drm/bochs: replace ALIGN(PAGE_SIZE) by PAGE_ALIGN use mm.h definition Signed-off-by: Fabian Frederick Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index b9a695d..1728a1b 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -387,7 +387,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel, *obj = NULL; - size = ALIGN(size, PAGE_SIZE); + size = PAGE_ALIGN(size); if (size == 0) return -EINVAL; -- cgit v0.10.2 From d2c87e2d2377966450cfb4271694c77dac615f98 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Fri, 4 Jul 2014 21:17:15 +0200 Subject: drm/omap: remove null test before kfree Fix checkpatch warning: WARNING: kfree(NULL) is safe this check is probably not required Cc: David Airlie Cc: Tomi Valkeinen Signed-off-by: Fabian Frederick Signed-off-by: David Herrmann diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 7c68c8a..5c36700 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1179,9 +1179,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op) } } spin_unlock(&sync_lock); - - if (waiter) - kfree(waiter); + kfree(waiter); } return ret; } -- cgit v0.10.2 From 4e4e7dc55af5aa62497ac060e3cfd143bd4b4db3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 3 Jul 2014 09:59:51 -0600 Subject: vgaarb: We can own non-decoded resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VGA arbiter does not allow devices to "own" resources that it doesn't "decode". However, it does allow devices to "lock" resources that it doesn't decode. This gets us into trouble because locking the resource goes through the same bridge routing updates regardless of whether we decode the resource. This means that when a non-decoded resource is released, the bridge is left with VGA routing enabled and locking a different device won't clear it. This happens in the following scenario: VGA device 01:00.0 (VGA1) is owned by the radeon driver, which registers a set_vga_decode function which releases legacy VGA decodes. VGA device 02:00.0 (VGA2) is any VGA device. VGA1 user locks VGA resources triggering first_use callback of set_vga_decoded, clearing "decode" and "owns" of legacy resources on VGA1. VGA1 user unlocks VGA resources. VGA2 user locks VGA resources, which skips VGA1 as conflicting as it does not "own" legacy resources, although VGA routing is still enabled for the VGA1 bridge. VGA routing is enabled on VGA2 bridge. VGA2 may or may not receive VGA transactions depending on the bus priority of VGA1 vs VGA2 bridge. To resolve this, we need to allow devices to "own" resources that they do not "decode". This way we can track bus ownership of VGA. When a device decodes VGA, it only means that we must update the command bits in cases where the conflicting device is on the same bus. Signed-off-by: Alex Williamson Cc: Ville Syrjälä Cc: Daniel Vetter Cc: Dave Airlie Signed-off-by: Dave Airlie diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index af02597..d2077f0 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -237,12 +237,10 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, if (conflict->locks & lwants) return conflict; - /* Ok, now check if he owns the resource we want. We don't need - * to check "decodes" since it should be impossible to own - * own legacy resources you don't decode unless I have a bug - * in this code... + /* Ok, now check if it owns the resource we want. We can + * lock resources that are not decoded, therefore a device + * can own resources it doesn't decode. */ - WARN_ON(conflict->owns & ~conflict->decodes); match = lwants & conflict->owns; if (!match) continue; @@ -254,13 +252,19 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, flags = 0; pci_bits = 0; + /* If we can't control legacy resources via the bridge, we + * also need to disable normal decoding. + */ if (!conflict->bridge_has_one_vga) { - vga_irq_set_state(conflict, false); - flags |= PCI_VGA_STATE_CHANGE_DECODES; - if (match & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) + if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM) pci_bits |= PCI_COMMAND_MEMORY; - if (match & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) + if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO) pci_bits |= PCI_COMMAND_IO; + + if (pci_bits) { + vga_irq_set_state(conflict, false); + flags |= PCI_VGA_STATE_CHANGE_DECODES; + } } if (change_bridge) @@ -268,18 +272,19 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, pci_set_vga_state(conflict->pdev, false, pci_bits, flags); conflict->owns &= ~match; - /* If he also owned non-legacy, that is no longer the case */ - if (match & VGA_RSRC_LEGACY_MEM) + + /* If we disabled normal decoding, reflect it in owns */ + if (pci_bits & PCI_COMMAND_MEMORY) conflict->owns &= ~VGA_RSRC_NORMAL_MEM; - if (match & VGA_RSRC_LEGACY_IO) + if (pci_bits & PCI_COMMAND_IO) conflict->owns &= ~VGA_RSRC_NORMAL_IO; } enable_them: /* ok dude, we got it, everybody conflicting has been disabled, let's - * enable us. Make sure we don't mark a bit in "owns" that we don't - * also have in "decodes". We can lock resources we don't decode but - * not own them. + * enable us. Mark any bits in "owns" regardless of whether we + * decoded them. We can lock resources we don't decode, therefore + * we must track them via "owns". */ flags = 0; pci_bits = 0; @@ -291,7 +296,7 @@ enable_them: if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) pci_bits |= PCI_COMMAND_IO; } - if (!!(wants & VGA_RSRC_LEGACY_MASK)) + if (wants & VGA_RSRC_LEGACY_MASK) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); @@ -299,7 +304,7 @@ enable_them: if (!vgadev->bridge_has_one_vga) { vga_irq_set_state(vgadev, true); } - vgadev->owns |= (wants & vgadev->decodes); + vgadev->owns |= wants; lock_them: vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); if (rsrc & VGA_RSRC_LEGACY_IO) @@ -649,7 +654,6 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev, old_decodes = vgadev->decodes; decodes_removed = ~new_decodes & old_decodes; decodes_unlocked = vgadev->locks & decodes_removed; - vgadev->owns &= ~decodes_removed; vgadev->decodes = new_decodes; pr_info("vgaarb: device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n", -- cgit v0.10.2 From e3f202798aaa808e7a38faa8c3a9f0aa93b85cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 3 Jul 2014 09:02:23 +0200 Subject: drm/ttm: fix handling of TTM_PL_FLAG_TOPDOWN v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bo->mem.placement is not initialized when ttm_bo_man_get_node is called, so the flag had no effect at all. v2: change nouveau and vmwgfx as well Signed-off-by: Christian König Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index ab0228f..7e185c1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -76,6 +76,7 @@ static int nouveau_vram_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); @@ -162,6 +163,7 @@ static int nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); @@ -242,6 +244,7 @@ static int nv04_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_mem *node; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4ab9f71..a13a100 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -784,7 +784,7 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, int ret; do { - ret = (*man->func->get_node)(man, bo, placement, mem); + ret = (*man->func->get_node)(man, bo, placement, 0, mem); if (unlikely(ret != 0)) return ret; if (mem->mm_node) @@ -897,7 +897,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (man->has_type && man->use_type) { type_found = true; - ret = (*man->func->get_node)(man, bo, placement, mem); + ret = (*man->func->get_node)(man, bo, placement, + cur_flags, mem); if (unlikely(ret)) return ret; } @@ -937,7 +938,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, ttm_flag_masked(&cur_flags, placement->busy_placement[i], ~TTM_PL_MASK_MEMTYPE); - if (mem_type == TTM_PL_SYSTEM) { mem->mem_type = mem_type; mem->placement = cur_flags; diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index bd850c9..9e103a48 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -50,6 +50,7 @@ struct ttm_range_manager { static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; @@ -67,7 +68,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, if (!node) return -ENOMEM; - if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN) + if (flags & TTM_PL_FLAG_TOPDOWN) aflags = DRM_MM_CREATE_TOP; spin_lock(&rman->lock); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index b1273e8..26f8bdd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -47,6 +47,7 @@ struct vmwgfx_gmrid_man { static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct vmwgfx_gmrid_man *gman = diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index e3f8c99..202f0a7 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -182,6 +182,7 @@ struct ttm_mem_type_manager_func { * @man: Pointer to a memory type manager. * @bo: Pointer to the buffer object we're allocating space for. * @placement: Placement details. + * @flags: Additional placement flags. * @mem: Pointer to a struct ttm_mem_reg to be filled in. * * This function should allocate space in the memory type managed @@ -206,6 +207,7 @@ struct ttm_mem_type_manager_func { int (*get_node)(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem); /** -- cgit v0.10.2 From d980b1831c14b233434555a851adf6c74fe9d19e Mon Sep 17 00:00:00 2001 From: Fabien DESSENNE Date: Tue, 1 Jul 2014 14:40:42 +0200 Subject: drm/crtc-helper: use drm_framebuffer flags The "flags" parameter of the DRM_IOCTL_MODE_ADDFB2 ioctl must be propagated and used by the driver. The only possible value of flags is DRM_MODE_FB_INTERLACED. Signed-off-by: Fabien Dessenne Reviewed-by: Benjamin GAIGNARD Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 78b37f3..6c65a0a 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -818,6 +818,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, &fb->bits_per_pixel); fb->pixel_format = mode_cmd->pixel_format; + fb->flags = mode_cmd->flags; } EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); -- cgit v0.10.2 From e396900e649b0af31161634d87fe37076f46c12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:04 -0300 Subject: drm/tilcdc: panel: fix dangling sysfs connector node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a drm_sysfs_connector_remove call when we destroy the panel to make sure the connector node in sysfs gets deleted. This is required for proper unload and re-load of this driver as a module. Without this, we would get a warning at re-load time like so: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 824 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x54/0x74() sysfs: cannot create duplicate filename '/class/drm/card0-LVDS-1' Modules linked in: [...] CPU: 0 PID: 824 Comm: modprobe Not tainted 3.15.0-rc4-00027-g6484f96-dirty #81 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x68/0x88) [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) [] (warn_slowpath_fmt) from [] (sysfs_warn_dup+0x54/0x74) [] (sysfs_warn_dup) from [] (sysfs_do_create_link_sd.isra.2+0xb0/0xb8) [] (sysfs_do_create_link_sd.isra.2) from [] (device_add+0x338/0x520) [] (device_add) from [] (device_create_groups_vargs+0xa0/0xc4) [] (device_create_groups_vargs) from [] (device_create+0x24/0x2c) [] (device_create) from [] (drm_sysfs_connector_add+0x64/0x204) [] (drm_sysfs_connector_add) from [] (panel_modeset_init+0xb8/0x134 [tilcdc]) [] (panel_modeset_init [tilcdc]) from [] (tilcdc_load+0x214/0x4c0 [tilcdc]) [] (tilcdc_load [tilcdc]) from [] (drm_dev_register+0xa4/0x104) [ .. snip .. ] ---[ end trace b2d09cd9578b0497 ]--- [drm:drm_sysfs_connector_add] *ERROR* failed to register connector device: -17 Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 0c0bce7..f5bed33 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -151,6 +151,7 @@ struct panel_connector { static void panel_connector_destroy(struct drm_connector *connector) { struct panel_connector *panel_connector = to_panel_connector(connector); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(panel_connector); } -- cgit v0.10.2 From daa15b4cd1eee58eb1322062a3320b1dbe5dc96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:05 -0300 Subject: drm/tilcdc: slave: fix dangling sysfs connector node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a drm_sysfs_connector_remove call when we destroy the panel to make sure the connector node in sysfs gets deleted. This is required for proper unload and re-load of this driver as a module. Without this, we would get a warning at re-load time like so: tda998x 0-0070: found TDA19988 ------------[ cut here ]------------ WARNING: CPU: 0 PID: 825 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x54/0x74() sysfs: cannot create duplicate filename '/class/drm/card0-HDMI-A-1' Modules linked in: [..] CPU: 0 PID: 825 Comm: modprobe Not tainted 3.15.0-rc4-00027-g9dcdef4 #82 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x68/0x88) [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) [] (warn_slowpath_fmt) from [] (sysfs_warn_dup+0x54/0x74) [] (sysfs_warn_dup) from [] (sysfs_do_create_link_sd.isra.2+0xb0/0xb8) [] (sysfs_do_create_link_sd.isra.2) from [] (device_add+0x338/0x520) [] (device_add) from [] (device_create_groups_vargs+0xa0/0xc4) [] (device_create_groups_vargs) from [] (device_create+0x24/0x2c) [] (device_create) from [] (drm_sysfs_connector_add+0x64/0x204) [] (drm_sysfs_connector_add) from [] (slave_modeset_init+0x120/0x1bc [tilcdc]) [] (slave_modeset_init [tilcdc]) from [] (tilcdc_load+0x214/0x4c0 [tilcdc]) [] (tilcdc_load [tilcdc]) from [] (drm_dev_register+0xa4/0x104) [..snip..] ---[ end trace 4df8d614936ebdee ]--- [drm:drm_sysfs_connector_add] *ERROR* failed to register connector device: -17 Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index c578d14..a5a2245 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -166,6 +166,7 @@ struct slave_connector { static void slave_connector_destroy(struct drm_connector *connector) { struct slave_connector *slave_connector = to_slave_connector(connector); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(slave_connector); } -- cgit v0.10.2 From 16dcbdef404f4e87dab985494381939fe0a2d456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:06 -0300 Subject: drm/tilcdc: tfp410: fix dangling sysfs connector node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a drm_sysfs_connector_remove call when we destroy the panel to make sure the connector node in sysfs gets deleted. This is required for proper unload and re-load of this driver, otherwise we will get a warning about a duplicate filename in sysfs. Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 5324dfd..6303606 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -167,6 +167,7 @@ struct tfp410_connector { static void tfp410_connector_destroy(struct drm_connector *connector) { struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(tfp410_connector); } -- cgit v0.10.2 From 3a49012224ca9016658a831a327ff6a7fe5bb4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:07 -0300 Subject: drm/tilcdc: panel: fix leak when unloading the module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver did not unregister the allocated framebuffer, which caused memory leaks (and memory manager WARNs) when unloading. Also, the framebuffer device under /dev still existed after unloading. Add a call to drm_fbdev_cma_fini when unloading the module to prevent both issues. Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index b20b694..490aee7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -122,6 +122,7 @@ static int tilcdc_unload(struct drm_device *dev) struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_module *mod, *cur; + drm_fbdev_cma_fini(priv->fbdev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); -- cgit v0.10.2 From eb565a2bbadc6a5030a6dbe58db1aa52453e7edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:08 -0300 Subject: drm/tilcdc: fix release order on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unregister resources in the correct order on tilcdc_drm_fini, which is the reverse order they were registered during tilcdc_drm_init. This also means unregistering the driver before releasing its resources. Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 490aee7..006a30e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -629,10 +629,10 @@ static int __init tilcdc_drm_init(void) static void __exit tilcdc_drm_fini(void) { DBG("fini"); - tilcdc_tfp410_fini(); - tilcdc_slave_fini(); - tilcdc_panel_fini(); platform_driver_unregister(&tilcdc_platform_driver); + tilcdc_panel_fini(); + tilcdc_slave_fini(); + tilcdc_tfp410_fini(); } late_initcall(tilcdc_drm_init); -- cgit v0.10.2 From c9a3ad25eddfdb898114a9d73cdb4c3472d9dfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:09 -0300 Subject: drm/tilcdc: fix double kfree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit display_timings_release calls kfree on the display_timings object passed to it. Calling kfree after it is wrong. SLUB debug showed the following warning: ============================================================================= BUG kmalloc-64 (Tainted: G W ): Object already free ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in of_get_display_timings+0x2c/0x214 age=601 cpu=0 pid=884 __slab_alloc.constprop.79+0x2e0/0x33c kmem_cache_alloc+0xac/0xdc of_get_display_timings+0x2c/0x214 panel_probe+0x7c/0x314 [tilcdc] platform_drv_probe+0x18/0x48 [..snip..] INFO: Freed in panel_destroy+0x18/0x3c [tilcdc] age=0 cpu=0 pid=907 __slab_free+0x34/0x330 panel_destroy+0x18/0x3c [tilcdc] tilcdc_unload+0xd0/0x118 [tilcdc] drm_dev_unregister+0x24/0x98 [..snip..] Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Cc: #v3.9+ Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index f5bed33..b0b9396 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -286,10 +286,8 @@ static void panel_destroy(struct tilcdc_module *mod) { struct panel_module *panel_mod = to_panel_module(mod); - if (panel_mod->timings) { + if (panel_mod->timings) display_timings_release(panel_mod->timings); - kfree(panel_mod->timings); - } tilcdc_module_cleanup(mod); kfree(panel_mod->info); -- cgit v0.10.2 From 7cdcce9f8b4c15dc55f880307b844a49127db86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:10 -0300 Subject: drm/tilcdc: remove submodule destroy calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TI tilcdc driver is designed with a notion of submodules. Currently, at unload time, these submodules are iterated and destroyed. Now that the tilcdc remove order is fixed, this can be handled perfectly by the kernel using the device infrastructure, since each submodule is a kernel driver itself, and they are only destroy()'ed at unload time. Therefore we move the destroy() functionality to each submodule's remove(). Also, remove some checks in the unloading process since the new code guarantees the resources are allocated and need a release. Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/Module.symvers b/drivers/gpu/drm/tilcdc/Module.symvers new file mode 100644 index 0000000..e69de29 diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 006a30e..2c860c4 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -120,7 +120,6 @@ static int cpufreq_transition(struct notifier_block *nb, static int tilcdc_unload(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; - struct tilcdc_module *mod, *cur; drm_fbdev_cma_fini(priv->fbdev); drm_kms_helper_poll_fini(dev); @@ -149,11 +148,6 @@ static int tilcdc_unload(struct drm_device *dev) pm_runtime_disable(dev->dev); - list_for_each_entry_safe(mod, cur, &module_list, list) { - DBG("destroying module: %s", mod->name); - mod->funcs->destroy(mod); - } - kfree(priv); return 0; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 0938036..7596c14 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -98,7 +98,6 @@ struct tilcdc_module; struct tilcdc_module_ops { /* create appropriate encoders/connectors: */ int (*modeset_init)(struct tilcdc_module *mod, struct drm_device *dev); - void (*destroy)(struct tilcdc_module *mod); #ifdef CONFIG_DEBUG_FS /* create debugfs nodes (can be NULL): */ int (*debugfs_init)(struct tilcdc_module *mod, struct drm_minor *minor); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index b0b9396..8ff72c8 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -282,21 +282,8 @@ static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) return 0; } -static void panel_destroy(struct tilcdc_module *mod) -{ - struct panel_module *panel_mod = to_panel_module(mod); - - if (panel_mod->timings) - display_timings_release(panel_mod->timings); - - tilcdc_module_cleanup(mod); - kfree(panel_mod->info); - kfree(panel_mod); -} - static const struct tilcdc_module_ops panel_module_ops = { .modeset_init = panel_modeset_init, - .destroy = panel_destroy, }; /* @@ -372,6 +359,7 @@ static int panel_probe(struct platform_device *pdev) return -ENOMEM; mod = &panel_mod->base; + pdev->dev.platform_data = mod; tilcdc_module_init(mod, "panel", &panel_module_ops); @@ -379,17 +367,16 @@ static int panel_probe(struct platform_device *pdev) if (IS_ERR(pinctrl)) dev_warn(&pdev->dev, "pins are not configured\n"); - panel_mod->timings = of_get_display_timings(node); if (!panel_mod->timings) { dev_err(&pdev->dev, "could not get panel timings\n"); - goto fail; + goto fail_free; } panel_mod->info = of_get_panel_info(node); if (!panel_mod->info) { dev_err(&pdev->dev, "could not get panel info\n"); - goto fail; + goto fail_timings; } mod->preferred_bpp = panel_mod->info->bpp; @@ -400,13 +387,26 @@ static int panel_probe(struct platform_device *pdev) return 0; -fail: - panel_destroy(mod); +fail_timings: + display_timings_release(panel_mod->timings); + +fail_free: + kfree(panel_mod); + tilcdc_module_cleanup(mod); return ret; } static int panel_remove(struct platform_device *pdev) { + struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); + struct panel_module *panel_mod = to_panel_module(mod); + + display_timings_release(panel_mod->timings); + + tilcdc_module_cleanup(mod); + kfree(panel_mod->info); + kfree(panel_mod); + return 0; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index a5a2245..f02cb7c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -296,17 +296,8 @@ static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) return 0; } -static void slave_destroy(struct tilcdc_module *mod) -{ - struct slave_module *slave_mod = to_slave_module(mod); - - tilcdc_module_cleanup(mod); - kfree(slave_mod); -} - static const struct tilcdc_module_ops slave_module_ops = { .modeset_init = slave_modeset_init, - .destroy = slave_destroy, }; /* @@ -356,10 +347,13 @@ static int slave_probe(struct platform_device *pdev) } slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL); - if (!slave_mod) - return -ENOMEM; + if (!slave_mod) { + ret = -ENOMEM; + goto fail_adapter; + } mod = &slave_mod->base; + pdev->dev.platform_data = mod; mod->preferred_bpp = slave_info.bpp; @@ -374,10 +368,20 @@ static int slave_probe(struct platform_device *pdev) tilcdc_slave_probedefer(false); return 0; + +fail_adapter: + i2c_put_adapter(slavei2c); + return ret; } static int slave_remove(struct platform_device *pdev) { + struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); + struct slave_module *slave_mod = to_slave_module(mod); + + tilcdc_module_cleanup(mod); + kfree(slave_mod); + return 0; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 6303606..82fb520 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -296,23 +296,8 @@ static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev return 0; } -static void tfp410_destroy(struct tilcdc_module *mod) -{ - struct tfp410_module *tfp410_mod = to_tfp410_module(mod); - - if (tfp410_mod->i2c) - i2c_put_adapter(tfp410_mod->i2c); - - if (!IS_ERR_VALUE(tfp410_mod->gpio)) - gpio_free(tfp410_mod->gpio); - - tilcdc_module_cleanup(mod); - kfree(tfp410_mod); -} - static const struct tilcdc_module_ops tfp410_module_ops = { .modeset_init = tfp410_modeset_init, - .destroy = tfp410_destroy, }; /* @@ -342,6 +327,7 @@ static int tfp410_probe(struct platform_device *pdev) return -ENOMEM; mod = &tfp410_mod->base; + pdev->dev.platform_data = mod; tilcdc_module_init(mod, "tfp410", &tfp410_module_ops); @@ -365,6 +351,7 @@ static int tfp410_probe(struct platform_device *pdev) tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node); if (!tfp410_mod->i2c) { dev_err(&pdev->dev, "could not get i2c\n"); + of_node_put(i2c_node); goto fail; } @@ -378,19 +365,32 @@ static int tfp410_probe(struct platform_device *pdev) ret = gpio_request(tfp410_mod->gpio, "DVI_PDn"); if (ret) { dev_err(&pdev->dev, "could not get DVI_PDn gpio\n"); - goto fail; + goto fail_adapter; } } return 0; +fail_adapter: + i2c_put_adapter(tfp410_mod->i2c); + fail: - tfp410_destroy(mod); + kfree(tfp410_mod); + tilcdc_module_cleanup(mod); return ret; } static int tfp410_remove(struct platform_device *pdev) { + struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); + struct tfp410_module *tfp410_mod = to_tfp410_module(mod); + + i2c_put_adapter(tfp410_mod->i2c); + gpio_free(tfp410_mod->gpio); + + tilcdc_module_cleanup(mod); + kfree(tfp410_mod); + return 0; } -- cgit v0.10.2 From 2023d84d2c3b7a4b8fc5c64c08c59c4e1505e2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 17 Jun 2014 11:17:11 -0300 Subject: drm/tilcdc: replace late_initcall with module_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use module_init instead of late_initcall, as is the norm for modular drivers. module_init was used until 6e8de0bd6a51fdeebd5d975c4fcc426f730b339b ("drm/tilcdc: add encoder slave (v2)") changed it to a late_initcall, but it does not explain why. Tests show it's working properly with module_init. Signed-off-by: Guido Martínez Tested-by: Darren Etheridge Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 2c860c4..6be623b 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -629,7 +629,7 @@ static void __exit tilcdc_drm_fini(void) tilcdc_tfp410_fini(); } -late_initcall(tilcdc_drm_init); +module_init(tilcdc_drm_init); module_exit(tilcdc_drm_fini); MODULE_AUTHOR("Rob Clark Date: Sun, 15 Jun 2014 02:10:39 +0200 Subject: ttm: use NULL instead of 0 for ttm_bo_reserve()'s pointer arg. Fix a sparse warning: ttm_bo_reserve()'s last argument is a pointer to a struct, so use NULL as nullpointer. Signed-off-by: Martin Kepplinger Reviewed-by: Jingoo Han Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a13a100..3da89d5 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -412,7 +412,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) int ret; spin_lock(&glob->lru_lock); - ret = __ttm_bo_reserve(bo, false, true, false, 0); + ret = __ttm_bo_reserve(bo, false, true, false, NULL); spin_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); @@ -514,7 +514,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, return ret; spin_lock(&glob->lru_lock); - ret = __ttm_bo_reserve(bo, false, true, false, 0); + ret = __ttm_bo_reserve(bo, false, true, false, NULL); /* * We raced, and lost, someone else holds the reservation now, @@ -577,11 +577,11 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) kref_get(&nentry->list_kref); } - ret = __ttm_bo_reserve(entry, false, true, false, 0); + ret = __ttm_bo_reserve(entry, false, true, false, NULL); if (remove_all && ret) { spin_unlock(&glob->lru_lock); ret = __ttm_bo_reserve(entry, false, false, - false, 0); + false, NULL); spin_lock(&glob->lru_lock); } @@ -726,7 +726,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { - ret = __ttm_bo_reserve(bo, false, true, false, 0); + ret = __ttm_bo_reserve(bo, false, true, false, NULL); if (!ret) break; } @@ -1595,7 +1595,7 @@ int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait) * Using ttm_bo_reserve makes sure the lru lists are updated. */ - ret = ttm_bo_reserve(bo, true, no_wait, false, 0); + ret = ttm_bo_reserve(bo, true, no_wait, false, NULL); if (unlikely(ret != 0)) return ret; spin_lock(&bdev->fence_lock); @@ -1630,7 +1630,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = __ttm_bo_reserve(bo, false, true, false, 0); + ret = __ttm_bo_reserve(bo, false, true, false, NULL); if (!ret) break; } -- cgit v0.10.2 From 50c3dc970a09b3b60422a58934cc27a413288bab Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Jun 2014 17:19:22 +0200 Subject: drm/fb-helper: Fix hpd vs. initial config races Some drivers need to be able to have a perfect race-free fbcon setup. Current drivers only enable hotplug processing after the call to drm_fb_helper_initial_config which leaves a tiny but important race. This race is especially noticable on embedded platforms where the driver itself enables the voltage for the hdmi output, since only then will monitors (after a bit of delay, as usual) respond by asserting the hpd pin. Most of the infrastructure is already there with the split-out drm_fb_helper_init. And drm_fb_helper_initial_config already has all the required locking to handle concurrent hpd events since commit 53f1904bced78d7c00f5d874c662ec3ac85d0f9f Author: Daniel Vetter Date: Thu Mar 20 14:26:35 2014 +0100 drm/fb-helper: improve drm_fb_helper_initial_config locking The only missing bit is making drm_fb_helper_hotplug_event save against concurrent calls of drm_fb_helper_initial_config. The only unprotected bit is the check for fb_helper->fb. With that drivers can first initialize the fb helper, then enabel hotplug processing and then set up the initial config all in a completely race-free manner. Update kerneldoc and convert i915 as a proof of concept. Feature requested by Thierry since his tegra driver atm reliably boots slowly enough to misses the hotplug event for an external hdmi screen, but also reliably boots to quickly for the hpd pin to be asserted when the fb helper calls into the hdmi ->detect function. Cc: Thierry Reding Signed-off-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 5aab5ab..6cd687a2 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1609,8 +1609,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * either the output polling work or a work item launched from the driver's * hotplug interrupt). * - * Note that the driver must ensure that this is only called _after_ the fb has - * been fully set up, i.e. after the call to drm_fb_helper_initial_config. + * Note that drivers may call this even before calling + * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows + * for a race-free fbcon setup and will make sure that the fbdev emulation will + * not miss any hotplug events. * * RETURNS: * 0 on success and a non-zero error code otherwise. @@ -1620,11 +1622,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; u32 max_width, max_height; - if (!fb_helper->fb) - return 0; - mutex_lock(&fb_helper->dev->mode_config.mutex); - if (!drm_fb_helper_is_bound(fb_helper)) { + if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { fb_helper->delayed_hotplug = true; mutex_unlock(&fb_helper->dev->mode_config.mutex); return 0; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6c65639..cac9265 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1375,9 +1375,6 @@ static int i915_load_modeset_init(struct drm_device *dev) */ intel_fbdev_initial_config(dev); - /* Only enable hotplug handling once the fbdev is fully set up. */ - dev_priv->enable_hotplug_processing = true; - drm_kms_helper_poll_init(dev); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 651e65e..d935ab3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -527,7 +527,6 @@ static int i915_drm_freeze(struct drm_device *dev) } drm_irq_uninstall(dev); - dev_priv->enable_hotplug_processing = false; intel_disable_gt_powersave(dev); @@ -655,7 +654,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) * notifications. * */ intel_hpd_init(dev); - dev_priv->enable_hotplug_processing = true; /* Config may have changed between suspend and resume */ drm_helper_hpd_irq_event(dev); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a47fbf6..5484f05 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1389,7 +1389,6 @@ struct drm_i915_private { u32 pipestat_irq_mask[I915_MAX_PIPES]; struct work_struct hotplug_work; - bool enable_hotplug_processing; struct { unsigned long hpd_last_jiffies; int hpd_cnt; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 267f069..69a7960 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1109,10 +1109,6 @@ static void i915_hotplug_work_func(struct work_struct *work) bool changed = false; u32 hpd_event_bits; - /* HPD irq before everything is fully set up. */ - if (!dev_priv->enable_hotplug_processing) - return; - mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); -- cgit v0.10.2 From 3a4938799dec32783759b0c66ee3bb8c82dd9937 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 17:19:23 +0200 Subject: drm: Constify struct drm_fb_helper_funcs There's no need for this to be modifiable. Make it const so that it can be put into the .rodata section. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Acked-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index fd166f5..a7c947c 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, return ret; } -static struct drm_fb_helper_funcs armada_fb_helper_funcs = { +static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { .gamma_set = armada_drm_crtc_gamma_set, .gamma_get = armada_drm_crtc_gamma_get, .fb_probe = armada_fb_probe, diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index a28640f..2113894 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = ast_crtc->lut_b[regno] << 8; } -static struct drm_fb_helper_funcs ast_fb_helper_funcs = { +static const struct drm_fb_helper_funcs ast_fb_helper_funcs = { .gamma_set = ast_fb_gamma_set, .gamma_get = ast_fb_gamma_get, .fb_probe = astfb_create, diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 561b844..17e5c17 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = regno; } -static struct drm_fb_helper_funcs bochs_fb_helper_funcs = { +static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = { .gamma_set = bochs_fb_gamma_set, .gamma_get = bochs_fb_gamma_get, .fb_probe = bochsfb_create, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 32bbba0..2bd0291 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, return 0; } -static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { +static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { .gamma_set = cirrus_crtc_fb_gamma_set, .gamma_get = cirrus_crtc_fb_gamma_get, .fb_probe = cirrusfb_create, diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index f27c883..cb01e16 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -327,7 +327,7 @@ err_drm_gem_cma_free_object: return ret; } -static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { +static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { .fb_probe = drm_fbdev_cma_create, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d771b46..fc25fe7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -225,7 +225,7 @@ out: return ret; } -static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { +static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { .fb_probe = exynos_drm_fbdev_create, }; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index e7fcc14..76e4d77 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, return psbfb_create(psb_fbdev, sizes); } -static struct drm_fb_helper_funcs psb_fb_helper_funcs = { +static const struct drm_fb_helper_funcs psb_fb_helper_funcs = { .gamma_set = psbfb_gamma_set, .gamma_get = psbfb_gamma_get, .fb_probe = psbfb_probe, diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 088fe93..c942d13 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -452,7 +452,7 @@ out: return true; } -static struct drm_fb_helper_funcs intel_fb_helper_funcs = { +static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { .initial_config = intel_fb_initial_config, .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 13b7dd8..a4319ab 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, return 0; } -static struct drm_fb_helper_funcs mga_fb_helper_funcs = { +static const struct drm_fb_helper_funcs mga_fb_helper_funcs = { .gamma_set = mga_crtc_fb_gamma_set, .gamma_get = mga_crtc_fb_gamma_get, .fb_probe = mgag200fb_create, diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 5107fc4..7e706c4 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -177,7 +177,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc, DBG("fbdev: get gamma"); } -static struct drm_fb_helper_funcs msm_fb_helper_funcs = { +static const struct drm_fb_helper_funcs msm_fb_helper_funcs = { .gamma_set = msm_crtc_fb_gamma_set, .gamma_get = msm_crtc_fb_gamma_get, .fb_probe = msm_fbdev_create, diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 64a42cf..8e9c07b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) info->flags |= FBINFO_HWACCEL_DISABLED; } -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { +static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .gamma_set = nouveau_fbcon_gamma_set, .gamma_get = nouveau_fbcon_gamma_get, .fb_probe = nouveau_fbcon_create, diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 1388ca7..4cb1208 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -281,7 +281,7 @@ fail: return ret; } -static struct drm_fb_helper_funcs omap_fb_helper_funcs = { +static const struct drm_fb_helper_funcs omap_fb_helper_funcs = { .fb_probe = omap_fbdev_create, }; diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index f437b30..cf89614 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev) return 0; } -static struct drm_fb_helper_funcs qxl_fb_helper_funcs = { +static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = { .fb_probe = qxl_fb_find_or_create_single, }; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 665ced3..ad97afd 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb return 0; } -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { +static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, .gamma_get = radeon_crtc_fb_gamma_get, .fb_probe = radeonfb_create, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 9798a70..f7cf47b 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -267,7 +267,7 @@ release: return err; } -static struct drm_fb_helper_funcs tegra_fb_helper_funcs = { +static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { .fb_probe = tegra_fbdev_probe, }; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 3771763..0647c8c 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -550,7 +550,7 @@ out: return ret; } -static struct drm_fb_helper_funcs udl_fb_helper_funcs = { +static const struct drm_fb_helper_funcs udl_fb_helper_funcs = { .fb_probe = udlfb_create, }; diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 7997246..a47df7c 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -87,7 +87,7 @@ struct drm_fb_helper { struct drm_fb_helper_crtc *crtc_info; int connector_count; struct drm_fb_helper_connector **connector_info; - struct drm_fb_helper_funcs *funcs; + const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; u32 pseudo_palette[17]; struct list_head kernel_fb_list; -- cgit v0.10.2 From 10a231026574f9ec9761b6435394b8830b485e4e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 17:19:24 +0200 Subject: drm: Introduce drm_fb_helper_prepare() To implement hotplug detection in a race-free manner, drivers must call drm_kms_helper_poll_init() before hotplug events can be triggered. Such events can be triggered right after any of the encoders or connectors are initialized. At the same time, if the drm_fb_helper_hotplug_event() helper is used by a driver, then the poll helper requires some parts of the FB helper to be initialized to prevent a crash. At the same time, drm_fb_helper_init() requires information that is not necessarily available at such an early stage (number of CRTCs and connectors), so it cannot be used yet. Add a new helper, drm_fb_helper_prepare(), that initializes the bare minimum needed to allow drm_kms_helper_poll_init() to execute and any subsequent hotplug events to be processed properly. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index a7c947c..7838e73 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev) priv->fbdev = fbh; - fbh->funcs = &armada_fb_helper_funcs; + drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); ret = drm_fb_helper_init(dev, fbh, 1, 1); if (ret) { diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 2113894..cba45c7 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev) return -ENOMEM; ast->fbdev = afbdev; - afbdev->helper.funcs = &ast_fb_helper_funcs; spin_lock_init(&afbdev->dirty_lock); + + drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); + ret = drm_fb_helper_init(dev, &afbdev->helper, 1, 1); if (ret) { diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 17e5c17..19cf3e9 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs) { int ret; - bochs->fb.helper.funcs = &bochs_fb_helper_funcs; + drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, + &bochs_fb_helper_funcs); ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 1, 1); diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 2bd0291..2a135f2 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return -ENOMEM; cdev->mode_info.gfbdev = gfbdev; - gfbdev->helper.funcs = &cirrus_fb_helper_funcs; spin_lock_init(&gfbdev->dirty_lock); + drm_fb_helper_prepare(cdev->dev, &gfbdev->helper, + &cirrus_fb_helper_funcs); + ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, cdev->num_crtc, CIRRUSFB_CONN_LIMIT); if (ret) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index cb01e16..cc0ae04 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, return ERR_PTR(-ENOMEM); } - fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs; helper = &fbdev_cma->fb_helper; + drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); + ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6cd687a2..bdee6eb 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list); * helper functions used by many drivers to implement the kernel mode setting * interfaces. * - * Initialization is done as a three-step process with drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). - * Drivers with fancier requirements than the default behaviour can override the - * second step with their own code. Teardown is done with drm_fb_helper_fini(). + * Initialization is done as a four-step process with drm_fb_helper_prepare(), + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the + * default behaviour can override the third step with their own code. + * Teardown is done with drm_fb_helper_fini(). * * At runtime drivers should restore the fbdev console by calling * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They @@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list); * * All other functions exported by the fb helper library can be used to * implement the fbdev driver interface by the driver. + * + * It is possible, though perhaps somewhat tricky, to implement race-free + * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() + * helper must be called first to initialize the minimum required to make + * hotplug detection work. Drivers also need to make sure to properly set up + * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init() + * it is safe to enable interrupts and start processing hotplug events. At the + * same time, drivers should initialize all modeset objects such as CRTCs, + * encoders and connectors. To finish up the fbdev helper initialization, the + * drm_fb_helper_init() function is called. To probe for all attached displays + * and set up an initial configuration using the detected hardware, drivers + * should call drm_fb_helper_single_add_all_connectors() followed by + * drm_fb_helper_initial_config(). */ /** @@ -528,6 +542,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) } /** + * drm_fb_helper_prepare - setup a drm_fb_helper structure + * @dev: DRM device + * @helper: driver-allocated fbdev helper structure to set up + * @funcs: pointer to structure of functions associate with this helper + * + * Sets up the bare minimum to make the framebuffer helper usable. This is + * useful to implement race-free initialization of the polling helpers. + */ +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, + const struct drm_fb_helper_funcs *funcs) +{ + INIT_LIST_HEAD(&helper->kernel_fb_list); + helper->funcs = funcs; + helper->dev = dev; +} +EXPORT_SYMBOL(drm_fb_helper_prepare); + +/** * drm_fb_helper_init - initialize a drm_fb_helper structure * @dev: drm device * @fb_helper: driver-allocated fbdev helper structure to initialize @@ -539,8 +571,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) * nor register the fbdev. This is only done in drm_fb_helper_initial_config() * to allow driver writes more control over the exact init sequence. * - * Drivers must set fb_helper->funcs before calling - * drm_fb_helper_initial_config(). + * Drivers must call drm_fb_helper_prepare() before calling this function. * * RETURNS: * Zero if everything went ok, nonzero otherwise. @@ -555,10 +586,6 @@ int drm_fb_helper_init(struct drm_device *dev, if (!max_conn_count) return -EINVAL; - fb_helper->dev = dev; - - INIT_LIST_HEAD(&fb_helper->kernel_fb_list); - fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); if (!fb_helper->crtc_info) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index fc25fe7..32e63f6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev) return -ENOMEM; private->fb_helper = helper = &fbdev->drm_fb_helper; - helper->funcs = &exynos_drm_fb_helper_funcs; + + drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs); num_crtc = dev->mode_config.num_crtc; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 76e4d77..d0dd3be 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev) } dev_priv->fbdev = fbdev; - fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs; + + drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs, INTELFB_CONN_LIMIT); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index c942d13..347d162 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -623,7 +623,8 @@ int intel_fbdev_init(struct drm_device *dev) if (ifbdev == NULL) return -ENOMEM; - ifbdev->helper.funcs = &intel_fb_helper_funcs; + drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs); + if (!intel_fbdev_init_bios(dev, ifbdev)) ifbdev->preferred_bpp = 32; diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index a4319ab..5451dc5 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return -ENOMEM; mdev->mfbdev = mfbdev; - mfbdev->helper.funcs = &mga_fb_helper_funcs; spin_lock_init(&mfbdev->dirty_lock); + drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs); + ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, mdev->num_crtc, MGAG200FB_CONN_LIMIT); if (ret) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 7e706c4..c437065 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -197,7 +197,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev) helper = &fbdev->base; - helper->funcs = &msm_fb_helper_funcs; + drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs); ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, priv->num_connectors); diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 8e9c07b..afe706a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev) fbcon->dev = dev; drm->fbcon = fbcon; - fbcon->helper.funcs = &nouveau_fbcon_helper_funcs; + + drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); ret = drm_fb_helper_init(dev, &fbcon->helper, dev->mode_config.num_crtc, 4); diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 4cb1208..8436c68 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) helper = &fbdev->base; - helper->funcs = &omap_fb_helper_funcs; + drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, priv->num_connectors); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index cf89614..df56788 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev) qfbdev->qdev = qdev; qdev->mode_info.qfbdev = qfbdev; - qfbdev->helper.funcs = &qxl_fb_helper_funcs; spin_lock_init(&qfbdev->delayed_ops_lock); INIT_LIST_HEAD(&qfbdev->delayed_ops); + + drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper, + &qxl_fb_helper_funcs); + ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, qxl_num_crtc /* num_crtc - QXL supports just 1 */, QXLFB_CONN_LIMIT); diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index ad97afd..db598d7 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev) rfbdev->rdev = rdev; rdev->mode_info.rfbdev = rfbdev; - rfbdev->helper.funcs = &radeon_fb_helper_funcs; + + drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper, + &radeon_fb_helper_funcs); ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, rdev->num_crtc, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7cf47b..d5d53aa 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, unsigned int num_crtc, unsigned int max_connectors) { - struct drm_fb_helper *helper; struct tegra_fbdev *fbdev; int err; @@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, return ERR_PTR(-ENOMEM); } - fbdev->base.funcs = &tegra_fb_helper_funcs; - helper = &fbdev->base; + drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); if (err < 0) { diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 0647c8c..d1da339 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev) return -ENOMEM; udl->fbdev = ufbdev; - ufbdev->helper.funcs = &udl_fb_helper_funcs; + + drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); ret = drm_fb_helper_init(dev, &ufbdev->helper, 1, 1); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index a47df7c..1cf587f 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -97,6 +97,8 @@ struct drm_fb_helper { bool delayed_hotplug; }; +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, + const struct drm_fb_helper_funcs *funcs); int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper, int crtc_count, int max_conn); -- cgit v0.10.2 From e2215321ff180f280d83837f57f3a84f8b6dda0d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 17:19:25 +0200 Subject: drm/tegra: Implement race-free hotplug detection A race condition currently exists on Tegra, where it can happen that a monitor attached via HDMI isn't detected during the initial FB helper setup, but the hotplug event happens too early to be processed by the poll helpers because they haven't been initialized yet. This happens because on some boards the HDMI driver can control the regulator that supplies the +5V pin on the HDMI connector. Therefore depending on the timing between the initialization of the HDMI driver and the rest of DRM, it's possible that the monitor returns the hotplug signal right within the window where we would miss it. Unfortunately, drm_kms_helper_poll_init() will wreak havoc when called before at least some parts of the FB helpers have been set up. This commit fixes this by splitting out the minimum of initialization required to make drm_kms_helper_poll_init() work into a separate function that can be called early. It is then safe to move all of the poll helper initialization to an earlier point in time (before the HDMI output driver has a chance to enable the +5V supply). That way if the hotplug signal is returned before the initial FB helper setup, the monitor will be forcefully detected at that point, and if the hotplug signal is returned after that it will be properly handled by the poll helpers. Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 3396f9f..fd736ef 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -40,6 +40,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm_mode_config_init(drm); + err = tegra_drm_fb_prepare(drm); + if (err < 0) + return err; + + drm_kms_helper_poll_init(drm); + err = host1x_device_init(device); if (err < 0) return err; @@ -59,8 +65,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) return err; - drm_kms_helper_poll_init(drm); - return 0; } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 6b8fe9d..0d30689 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -280,6 +280,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index); bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer); +int tegra_drm_fb_prepare(struct drm_device *drm); int tegra_drm_fb_init(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm); #ifdef CONFIG_DRM_TEGRA_FBDEV diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index d5d53aa..fc1528e 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -271,13 +271,9 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { .fb_probe = tegra_fbdev_probe, }; -static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, - unsigned int preferred_bpp, - unsigned int num_crtc, - unsigned int max_connectors) +static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm) { struct tegra_fbdev *fbdev; - int err; fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); if (!fbdev) { @@ -287,10 +283,21 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); + return fbdev; +} + +static int tegra_fbdev_init(struct tegra_fbdev *fbdev, + unsigned int preferred_bpp, + unsigned int num_crtc, + unsigned int max_connectors) +{ + struct drm_device *drm = fbdev->base.dev; + int err; + err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); if (err < 0) { dev_err(drm->dev, "failed to initialize DRM FB helper\n"); - goto free; + return err; } err = drm_fb_helper_single_add_all_connectors(&fbdev->base); @@ -299,21 +306,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, goto fini; } - drm_helper_disable_unused_functions(drm); - err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); if (err < 0) { dev_err(drm->dev, "failed to set initial configuration\n"); goto fini; } - return fbdev; + return 0; fini: drm_fb_helper_fini(&fbdev->base); -free: - kfree(fbdev); - return ERR_PTR(err); + return err; } static void tegra_fbdev_free(struct tegra_fbdev *fbdev) @@ -364,7 +367,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { #endif }; -int tegra_drm_fb_init(struct drm_device *drm) +int tegra_drm_fb_prepare(struct drm_device *drm) { #ifdef CONFIG_DRM_TEGRA_FBDEV struct tegra_drm *tegra = drm->dev_private; @@ -379,8 +382,7 @@ int tegra_drm_fb_init(struct drm_device *drm) drm->mode_config.funcs = &tegra_drm_mode_funcs; #ifdef CONFIG_DRM_TEGRA_FBDEV - tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc, - drm->mode_config.num_connector); + tegra->fbdev = tegra_fbdev_create(drm); if (IS_ERR(tegra->fbdev)) return PTR_ERR(tegra->fbdev); #endif @@ -388,6 +390,21 @@ int tegra_drm_fb_init(struct drm_device *drm) return 0; } +int tegra_drm_fb_init(struct drm_device *drm) +{ +#ifdef CONFIG_DRM_TEGRA_FBDEV + struct tegra_drm *tegra = drm->dev_private; + int err; + + err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc, + drm->mode_config.num_connector); + if (err < 0) + return err; +#endif + + return 0; +} + void tegra_drm_fb_exit(struct drm_device *drm) { #ifdef CONFIG_DRM_TEGRA_FBDEV -- cgit v0.10.2 From 26507b065b503ac1b5ba702161c2084190fc0c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Marchesin?= Date: Wed, 2 Jul 2014 15:13:42 -0700 Subject: drm/udl: add vblank support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to be able to send page flip completion events. Also while I'm at it, fix the error paths on init. Signed-off-by: Stéphane Marchesin Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 7094b92..4279567 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -306,10 +306,23 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) DRM_DEBUG("\n"); ret = udl_modeset_init(dev); + if (ret) + goto err; ret = udl_fbdev_init(dev); + if (ret) + goto err; + + ret = drm_vblank_init(dev, 1); + if (ret) + goto err_fb; + return 0; +err_fb: + udl_fbdev_cleanup(dev); err: + if (udl->urbs.count) + udl_free_urb_list(dev); kfree(udl); DRM_ERROR("%d\n", ret); return ret; @@ -325,6 +338,8 @@ int udl_driver_unload(struct drm_device *dev) { struct udl_device *udl = dev->dev_private; + drm_vblank_cleanup(dev); + if (udl->urbs.count) udl_free_urb_list(dev); -- cgit v0.10.2 From 40377ef2d9a3ddc8ba4aee11393fb207e82111e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Marchesin?= Date: Wed, 2 Jul 2014 15:13:43 -0700 Subject: drm/udl: Implement page_flip ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a very crude page_flip implementation for UDL. There are ways to make it better (make it asynchronous, make it do actual vsynced flips...) but that's for another patch. [airlied: fixup primary change] Signed-off-by: Stéphane Marchesin Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index cddc4fc..dc145d3 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -363,6 +363,26 @@ static void udl_crtc_destroy(struct drm_crtc *crtc) kfree(crtc); } +static int udl_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct udl_framebuffer *ufb = to_udl_fb(fb); + struct drm_device *dev = crtc->dev; + unsigned long flags; + + udl_handle_damage(ufb, 0, 0, fb->width, fb->height); + + spin_lock_irqsave(&dev->event_lock, flags); + if (event) + drm_send_vblank_event(dev, 0, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + crtc->primary->fb = fb; + + return 0; +} + static void udl_crtc_prepare(struct drm_crtc *crtc) { } @@ -384,6 +404,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = { static const struct drm_crtc_funcs udl_crtc_funcs = { .set_config = drm_crtc_helper_set_config, .destroy = udl_crtc_destroy, + .page_flip = udl_crtc_page_flip, }; static int udl_crtc_init(struct drm_device *dev) -- cgit v0.10.2 From 2390cd11bfbe8d2b1b28c4e0f01fe7e122f7196d Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:29 +1000 Subject: drm/crtc: add interface to reinitialise the legacy mode group This can be called to update things after dynamic connectors/encoders are created/deleted. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7db6251..f939b05 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1513,6 +1513,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_group_init_legacy_group); +void drm_reinit_primary_mode_group(struct drm_device *dev) +{ + drm_modeset_lock_all(dev); + drm_mode_group_destroy(&dev->primary->mode_group); + drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); + drm_modeset_unlock_all(dev); +} +EXPORT_SYMBOL(drm_reinit_primary_mode_group); + /** * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo * @out: drm_mode_modeinfo struct to return to the user diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 857bbb1..eb0f6f3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -942,6 +942,7 @@ extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); +extern void drm_reinit_primary_mode_group(struct drm_device *dev); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); -- cgit v0.10.2 From 65c2a89c30ed63db15fbbdba41b74e5f47278897 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:30 +1000 Subject: drm/fb_helper: allow adding/removing connectors later This is required to get fbcon probing to work on new connectors, callers should acquire the mode config lock before calling these. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index bdee6eb..3144db9 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -119,6 +119,58 @@ fail: } EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); +int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) +{ + struct drm_fb_helper_connector **temp; + struct drm_fb_helper_connector *fb_helper_connector; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { + temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1; + fb_helper->connector_info = temp; + } + + + fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); + if (!fb_helper_connector) + return -ENOMEM; + + fb_helper_connector->connector = connector; + fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_add_one_connector); + +int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + struct drm_fb_helper_connector *fb_helper_connector; + int i, j; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + + for (i = 0; i < fb_helper->connector_count; i++) { + if (fb_helper->connector_info[i]->connector == connector) + break; + } + + if (i == fb_helper->connector_count) + return -EINVAL; + fb_helper_connector = fb_helper->connector_info[i]; + + for (j = i + 1; j < fb_helper->connector_count; j++) { + fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; + } + fb_helper->connector_count--; + kfree(fb_helper_connector); + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); + static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) { struct drm_fb_helper_connector *fb_helper_conn; @@ -596,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev, kfree(fb_helper->crtc_info); return -ENOMEM; } + fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; fb_helper->connector_count = 0; for (i = 0; i < crtc_count; i++) { diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 1cf587f..bfd329d 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -86,6 +86,7 @@ struct drm_fb_helper { int crtc_count; struct drm_fb_helper_crtc *crtc_info; int connector_count; + int connector_info_alloc_count; struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; @@ -130,4 +131,7 @@ struct drm_display_mode * drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, int width, int height); +int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); +int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector); #endif -- cgit v0.10.2 From 43aba7eb0dc052d84e88f3569ea3f030868d49b2 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:31 +1000 Subject: drm: add a path blob property This property will be used by the MST code to provide userspace with a path to parse so it can recognise connectors around hotplugs. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f939b05..bd74226 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1257,6 +1257,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { struct drm_property *edid; struct drm_property *dpms; + struct drm_property *dev_path; /* * Standard properties (apply to all connectors) @@ -1271,6 +1272,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) ARRAY_SIZE(drm_dpms_enum_list)); dev->mode_config.dpms_property = dpms; + dev_path = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "PATH", 0); + dev->mode_config.path_property = dev_path; + return 0; } @@ -3755,6 +3762,25 @@ done: return ret; } +int drm_mode_connector_set_path_property(struct drm_connector *connector, + char *path) +{ + struct drm_device *dev = connector->dev; + int ret, size; + size = strlen(path) + 1; + + connector->path_blob_ptr = drm_property_create_blob(connector->dev, + size, path); + if (!connector->path_blob_ptr) + return -EINVAL; + + ret = drm_object_property_set_value(&connector->base, + dev->mode_config.path_property, + connector->path_blob_ptr->base.id); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_path_property); + /** * drm_mode_connector_update_edid_property - update the edid property of a connector * @connector: drm connector diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index eb0f6f3..4da6207 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -524,6 +524,8 @@ struct drm_connector { struct drm_property_blob *edid_blob_ptr; struct drm_object_properties properties; + struct drm_property_blob *path_blob_ptr; + uint8_t polled; /* DRM_CONNECTOR_POLL_* */ /* requested DPMS state */ @@ -803,6 +805,7 @@ struct drm_mode_config { struct list_head property_blob_list; struct drm_property *edid_property; struct drm_property *dpms_property; + struct drm_property *path_property; struct drm_property *plane_type_property; /* DVI-I properties */ @@ -952,6 +955,8 @@ extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); +extern int drm_mode_connector_set_path_property(struct drm_connector *connector, + char *path); extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid); -- cgit v0.10.2 From ad7f8a1f9ced7f049f9b66d588723f243a7034cd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:32 +1000 Subject: drm/helper: add Displayport multi-stream helper (v0.6) This is the initial import of the helper for displayport multistream. It consists of a topology manager, init/destroy/set mst state It supports DP 1.2 MST sideband msg protocol handler - via hpd irqs connector detect and edid retrieval interface. It supports i2c device over DP 1.2 sideband msg protocol (EDID reads only) bandwidth manager API via vcpi allocation and payload updating, along with a helper to check the ACT status. Objects: MST topology manager - one per toplevel MST capable GPU port - not sure if this should be higher level again MST branch unit - one instance per plugged branching unit - one at top of hierarchy - others hanging from ports MST port - one port per port reported by branching units, can have MST units hanging from them as well. Changes since initial posting: a) add a mutex responsbile for the queues, it locks the sideband and msg slots, and msgs to transmit state b) add worker to handle connection state change events, for MST device chaining and hotplug c) add a payload spinlock d) add path sideband msg support e) fixup enum path resources transmit f) reduce max dpcd msg to 16, as per DP1.2 spec. g) separate tx queue kicking from irq processing and move irq acking back to drivers. Changes since v0.2: a) reorganise code, b) drop ACT forcing code c) add connector naming interface using path property d) add topology dumper helper e) proper reference counting and lookup for ports and mstbs. f) move tx kicking into a workq g) add aux locking - this should be redone h) split teardown into two parts i) start working on documentation on interface. Changes since v0.3: a) vc payload locking and tracking fixes b) add hotplug callback into driver - replaces crazy return 1 scheme c) txmsg + mst branch device refcount fixes d) don't bail on mst shutdown if device is gone e) change irq handler to take all 4 bytes of SINK_COUNT + ESI vectors f) make DP payload updates timeout longer - observed on docking station redock g) add more info to debugfs dumper Changes since v0.4: a) suspend/resume support b) more debugging in debugfs Changes since v0.5: a) use byte * to avoid unnecessary stack usage b) fix num_sdp_streams interpretation. c) init payload state for unplug events d) remove lenovo dock sink count hack e) drop aux lock - post rebase f) call hotplug on port destroy TODO: misc features Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b314a42..4890d94 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2338,6 +2338,12 @@ void intel_crt_init(struct drm_device *dev) !Edrivers/gpu/drm/drm_dp_helper.c + Display Port MST Helper Functions Reference +!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper +!Iinclude/drm/drm_dp_mst_helper.h +!Edrivers/gpu/drm/drm_dp_mst_topology.c + + EDID Helper Functions Reference !Edrivers/gpu/drm/drm_edid.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd2ba42..af9a609 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -24,7 +24,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-usb-y := drm_usb.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ - drm_plane_helper.o + drm_plane_helper.o drm_dp_mst_topology.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c new file mode 100644 index 0000000..73bc773 --- /dev/null +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -0,0 +1,2712 @@ +/* + * Copyright © 2014 Red Hat + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * DOC: dp mst helper + * + * These functions contain parts of the DisplayPort 1.2a MultiStream Transport + * protocol. The helpers contain a topology manager and bandwidth manager. + * The helpers encapsulate the sending and received of sideband msgs. + */ +static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, + char *buf); +static int test_calc_pbn_mode(void); + +static void drm_dp_put_port(struct drm_dp_mst_port *port); + +static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload); + +static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes); + +static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb); +static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port); +static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, + u8 *guid); + +static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux); +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux); +static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); +/* sideband msg handling */ +static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = num_nibbles * 4; + u8 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x10) == 0x10) + remainder ^= 0x13; + } + + number_of_bits = 4; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x10) != 0) + remainder ^= 0x13; + } + + return remainder; +} + +static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = number_of_bytes * 8; + u16 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x100) == 0x100) + remainder ^= 0xd5; + } + + number_of_bits = 8; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x100) != 0) + remainder ^= 0xd5; + } + + return remainder & 0xff; +} +static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr) +{ + u8 size = 3; + size += (hdr->lct / 2); + return size; +} + +static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, + u8 *buf, int *len) +{ + int idx = 0; + int i; + u8 crc4; + buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf); + for (i = 0; i < (hdr->lct / 2); i++) + buf[idx++] = hdr->rad[i]; + buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) | + (hdr->msg_len & 0x3f); + buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4); + + crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1); + buf[idx - 1] |= (crc4 & 0xf); + + *len = idx; +} + +static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, + u8 *buf, int buflen, u8 *hdrlen) +{ + u8 crc4; + u8 len; + int i; + u8 idx; + if (buf[0] == 0) + return false; + len = 3; + len += ((buf[0] & 0xf0) >> 4) / 2; + if (len > buflen) + return false; + crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1); + + if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) { + DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]); + return false; + } + + hdr->lct = (buf[0] & 0xf0) >> 4; + hdr->lcr = (buf[0] & 0xf); + idx = 1; + for (i = 0; i < (hdr->lct / 2); i++) + hdr->rad[i] = buf[idx++]; + hdr->broadcast = (buf[idx] >> 7) & 0x1; + hdr->path_msg = (buf[idx] >> 6) & 0x1; + hdr->msg_len = buf[idx] & 0x3f; + idx++; + hdr->somt = (buf[idx] >> 7) & 0x1; + hdr->eomt = (buf[idx] >> 6) & 0x1; + hdr->seqno = (buf[idx] >> 4) & 0x1; + idx++; + *hdrlen = idx; + return true; +} + +static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, + struct drm_dp_sideband_msg_tx *raw) +{ + int idx = 0; + int i; + u8 *buf = raw->msg; + buf[idx++] = req->req_type & 0x7f; + + switch (req->req_type) { + case DP_ENUM_PATH_RESOURCES: + buf[idx] = (req->u.port_num.port_number & 0xf) << 4; + idx++; + break; + case DP_ALLOCATE_PAYLOAD: + buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 | + (req->u.allocate_payload.number_sdp_streams & 0xf); + idx++; + buf[idx] = (req->u.allocate_payload.vcpi & 0x7f); + idx++; + buf[idx] = (req->u.allocate_payload.pbn >> 8); + idx++; + buf[idx] = (req->u.allocate_payload.pbn & 0xff); + idx++; + for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) { + buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) | + (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf); + idx++; + } + if (req->u.allocate_payload.number_sdp_streams & 1) { + i = req->u.allocate_payload.number_sdp_streams - 1; + buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4; + idx++; + } + break; + case DP_QUERY_PAYLOAD: + buf[idx] = (req->u.query_payload.port_number & 0xf) << 4; + idx++; + buf[idx] = (req->u.query_payload.vcpi & 0x7f); + idx++; + break; + case DP_REMOTE_DPCD_READ: + buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4; + buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf; + idx++; + buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8; + idx++; + buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff); + idx++; + buf[idx] = (req->u.dpcd_read.num_bytes); + idx++; + break; + + case DP_REMOTE_DPCD_WRITE: + buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4; + buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf; + idx++; + buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8; + idx++; + buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff); + idx++; + buf[idx] = (req->u.dpcd_write.num_bytes); + idx++; + memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes); + idx += req->u.dpcd_write.num_bytes; + break; + case DP_REMOTE_I2C_READ: + buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4; + buf[idx] |= (req->u.i2c_read.num_transactions & 0x3); + idx++; + for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) { + buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f; + idx++; + buf[idx] = req->u.i2c_read.transactions[i].num_bytes; + idx++; + memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes); + idx += req->u.i2c_read.transactions[i].num_bytes; + + buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5; + buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf); + idx++; + } + buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f; + idx++; + buf[idx] = (req->u.i2c_read.num_bytes_read); + idx++; + break; + + case DP_REMOTE_I2C_WRITE: + buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4; + idx++; + buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f; + idx++; + buf[idx] = (req->u.i2c_write.num_bytes); + idx++; + memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes); + idx += req->u.i2c_write.num_bytes; + break; + } + raw->cur_len = idx; +} + +static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len) +{ + u8 crc4; + crc4 = drm_dp_msg_data_crc4(msg, len); + msg[len] = crc4; +} + +static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep, + struct drm_dp_sideband_msg_tx *raw) +{ + int idx = 0; + u8 *buf = raw->msg; + + buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f); + + raw->cur_len = idx; +} + +/* this adds a chunk of msg to the builder to get the final msg */ +static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, + u8 *replybuf, u8 replybuflen, bool hdr) +{ + int ret; + u8 crc4; + + if (hdr) { + u8 hdrlen; + struct drm_dp_sideband_msg_hdr recv_hdr; + ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen); + if (ret == false) { + print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false); + return false; + } + + /* get length contained in this portion */ + msg->curchunk_len = recv_hdr.msg_len; + msg->curchunk_hdrlen = hdrlen; + + /* we have already gotten an somt - don't bother parsing */ + if (recv_hdr.somt && msg->have_somt) + return false; + + if (recv_hdr.somt) { + memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr)); + msg->have_somt = true; + } + if (recv_hdr.eomt) + msg->have_eomt = true; + + /* copy the bytes for the remainder of this header chunk */ + msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen)); + memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx); + } else { + memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); + msg->curchunk_idx += replybuflen; + } + + if (msg->curchunk_idx >= msg->curchunk_len) { + /* do CRC */ + crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1); + /* copy chunk into bigger msg */ + memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1); + msg->curlen += msg->curchunk_len - 1; + } + return true; +} + +static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + int i; + memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16); + idx += 16; + repmsg->u.link_addr.nports = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + for (i = 0; i < repmsg->u.link_addr.nports; i++) { + if (raw->msg[idx] & 0x80) + repmsg->u.link_addr.ports[i].input_port = 1; + + repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7; + repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf); + + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1; + repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1; + if (repmsg->u.link_addr.ports[i].input_port == 0) + repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1; + idx++; + if (idx > raw->curlen) + goto fail_len; + if (repmsg->u.link_addr.ports[i].input_port == 0) { + repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]); + idx++; + if (idx > raw->curlen) + goto fail_len; + memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf; + repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf); + idx++; + + } + if (idx > raw->curlen) + goto fail_len; + } + + return true; +fail_len: + DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; + if (idx > raw->curlen) + goto fail_len; + + memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes); + return true; +fail_len: + DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + + repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf); + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; + idx++; + /* TODO check */ + memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes); + return true; +fail_len: + DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.allocate_payload.vcpi = raw->msg[idx]; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *msg) +{ + memset(msg, 0, sizeof(*msg)); + msg->reply_type = (raw->msg[0] & 0x80) >> 7; + msg->req_type = (raw->msg[0] & 0x7f); + + if (msg->reply_type) { + memcpy(msg->u.nak.guid, &raw->msg[1], 16); + msg->u.nak.reason = raw->msg[17]; + msg->u.nak.nak_data = raw->msg[18]; + return false; + } + + switch (msg->req_type) { + case DP_LINK_ADDRESS: + return drm_dp_sideband_parse_link_address(raw, msg); + case DP_QUERY_PAYLOAD: + return drm_dp_sideband_parse_query_payload_ack(raw, msg); + case DP_REMOTE_DPCD_READ: + return drm_dp_sideband_parse_remote_dpcd_read(raw, msg); + case DP_REMOTE_DPCD_WRITE: + return drm_dp_sideband_parse_remote_dpcd_write(raw, msg); + case DP_REMOTE_I2C_READ: + return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg); + case DP_ENUM_PATH_RESOURCES: + return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg); + case DP_ALLOCATE_PAYLOAD: + return drm_dp_sideband_parse_allocate_payload_ack(raw, msg); + default: + DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type); + return false; + } +} + +static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + int idx = 1; + + msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; + idx++; + if (idx > raw->curlen) + goto fail_len; + + memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + + msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1; + msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1; + msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1; + msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1; + msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7); + idx++; + return true; +fail_len: + DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + int idx = 1; + + msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; + idx++; + if (idx > raw->curlen) + goto fail_len; + + memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + + msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); + idx++; + return true; +fail_len: + DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + memset(msg, 0, sizeof(*msg)); + msg->req_type = (raw->msg[0] & 0x7f); + + switch (msg->req_type) { + case DP_CONNECTION_STATUS_NOTIFY: + return drm_dp_sideband_parse_connection_status_notify(raw, msg); + case DP_RESOURCE_STATUS_NOTIFY: + return drm_dp_sideband_parse_resource_status_notify(raw, msg); + default: + DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type); + return false; + } +} + +static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_REMOTE_DPCD_WRITE; + req.u.dpcd_write.port_number = port_num; + req.u.dpcd_write.dpcd_address = offset; + req.u.dpcd_write.num_bytes = num_bytes; + req.u.dpcd_write.bytes = bytes; + drm_dp_encode_sideband_req(&req, msg); + + return 0; +} + +static int build_link_address(struct drm_dp_sideband_msg_tx *msg) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_LINK_ADDRESS; + drm_dp_encode_sideband_req(&req, msg); + return 0; +} + +static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_ENUM_PATH_RESOURCES; + req.u.port_num.port_number = port_num; + drm_dp_encode_sideband_req(&req, msg); + msg->path_msg = true; + return 0; +} + +static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num, + u8 vcpi, uint16_t pbn) +{ + struct drm_dp_sideband_msg_req_body req; + memset(&req, 0, sizeof(req)); + req.req_type = DP_ALLOCATE_PAYLOAD; + req.u.allocate_payload.port_number = port_num; + req.u.allocate_payload.vcpi = vcpi; + req.u.allocate_payload.pbn = pbn; + drm_dp_encode_sideband_req(&req, msg); + msg->path_msg = true; + return 0; +} + +static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_vcpi *vcpi) +{ + int ret; + + mutex_lock(&mgr->payload_lock); + ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); + if (ret > mgr->max_payloads) { + ret = -EINVAL; + DRM_DEBUG_KMS("out of payload ids %d\n", ret); + goto out_unlock; + } + + set_bit(ret, &mgr->payload_mask); + vcpi->vcpi = ret; + mgr->proposed_vcpis[ret - 1] = vcpi; +out_unlock: + mutex_unlock(&mgr->payload_lock); + return ret; +} + +static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, + int id) +{ + if (id == 0) + return; + + mutex_lock(&mgr->payload_lock); + DRM_DEBUG_KMS("putting payload %d\n", id); + clear_bit(id, &mgr->payload_mask); + mgr->proposed_vcpis[id - 1] = NULL; + mutex_unlock(&mgr->payload_lock); +} + +static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + bool ret; + mutex_lock(&mgr->qlock); + ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX || + txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT); + mutex_unlock(&mgr->qlock); + return ret; +} + +static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, + struct drm_dp_sideband_msg_tx *txmsg) +{ + struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + int ret; + + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + (4 * HZ)); + mutex_lock(&mstb->mgr->qlock); + if (ret > 0) { + if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { + ret = -EIO; + goto out; + } + } else { + DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno); + + /* dump some state */ + ret = -EIO; + + /* remove from q */ + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED || + txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) { + list_del(&txmsg->next); + } + + if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND || + txmsg->state == DRM_DP_SIDEBAND_TX_SENT) { + mstb->tx_slots[txmsg->seqno] = NULL; + } + } +out: + mutex_unlock(&mgr->qlock); + + return ret; +} + +static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad) +{ + struct drm_dp_mst_branch *mstb; + + mstb = kzalloc(sizeof(*mstb), GFP_KERNEL); + if (!mstb) + return NULL; + + mstb->lct = lct; + if (lct > 1) + memcpy(mstb->rad, rad, lct / 2); + INIT_LIST_HEAD(&mstb->ports); + kref_init(&mstb->kref); + return mstb; +} + +static void drm_dp_destroy_mst_branch_device(struct kref *kref) +{ + struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); + struct drm_dp_mst_port *port, *tmp; + bool wake_tx = false; + + cancel_work_sync(&mstb->mgr->work); + + /* + * destroy all ports - don't need lock + * as there are no more references to the mst branch + * device at this point. + */ + list_for_each_entry_safe(port, tmp, &mstb->ports, next) { + list_del(&port->next); + drm_dp_put_port(port); + } + + /* drop any tx slots msg */ + mutex_lock(&mstb->mgr->qlock); + if (mstb->tx_slots[0]) { + mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + mstb->tx_slots[0] = NULL; + wake_tx = true; + } + if (mstb->tx_slots[1]) { + mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + mstb->tx_slots[1] = NULL; + wake_tx = true; + } + mutex_unlock(&mstb->mgr->qlock); + + if (wake_tx) + wake_up(&mstb->mgr->tx_waitq); + kfree(mstb); +} + +static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb) +{ + kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device); +} + + +static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt) +{ + switch (old_pdt) { + case DP_PEER_DEVICE_DP_LEGACY_CONV: + case DP_PEER_DEVICE_SST_SINK: + /* remove i2c over sideband */ + drm_dp_mst_unregister_i2c_bus(&port->aux); + break; + case DP_PEER_DEVICE_MST_BRANCHING: + drm_dp_put_mst_branch_device(port->mstb); + port->mstb = NULL; + break; + } +} + +static void drm_dp_destroy_port(struct kref *kref) +{ + struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); + struct drm_dp_mst_topology_mgr *mgr = port->mgr; + if (!port->input) { + port->vcpi.num_slots = 0; + if (port->connector) + (*port->mgr->cbs->destroy_connector)(mgr, port->connector); + drm_dp_port_teardown_pdt(port, port->pdt); + + if (!port->input && port->vcpi.vcpi > 0) + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + } + kfree(port); + + (*mgr->cbs->hotplug)(mgr); +} + +static void drm_dp_put_port(struct drm_dp_mst_port *port) +{ + kref_put(&port->kref, drm_dp_destroy_port); +} + +static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find) +{ + struct drm_dp_mst_port *port; + struct drm_dp_mst_branch *rmstb; + if (to_find == mstb) { + kref_get(&mstb->kref); + return mstb; + } + list_for_each_entry(port, &mstb->ports, next) { + if (port->mstb) { + rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find); + if (rmstb) + return rmstb; + } + } + return NULL; +} + +static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_branch *rmstb = NULL; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb); + mutex_unlock(&mgr->lock); + return rmstb; +} + +static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find) +{ + struct drm_dp_mst_port *port, *mport; + + list_for_each_entry(port, &mstb->ports, next) { + if (port == to_find) { + kref_get(&port->kref); + return port; + } + if (port->mstb) { + mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find); + if (mport) + return mport; + } + } + return NULL; +} + +static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + struct drm_dp_mst_port *rport = NULL; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port); + mutex_unlock(&mgr->lock); + return rport; +} + +static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num) +{ + struct drm_dp_mst_port *port; + + list_for_each_entry(port, &mstb->ports, next) { + if (port->port_num == port_num) { + kref_get(&port->kref); + return port; + } + } + + return NULL; +} + +/* + * calculate a new RAD for this MST branch device + * if parent has an LCT of 2 then it has 1 nibble of RAD, + * if parent has an LCT of 3 then it has 2 nibbles of RAD, + */ +static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port, + u8 *rad) +{ + int lct = port->parent->lct; + int shift = 4; + int idx = lct / 2; + if (lct > 1) { + memcpy(rad, port->parent->rad, idx); + shift = (lct % 2) ? 4 : 0; + } else + rad[0] = 0; + + rad[idx] |= port->port_num << shift; + return lct + 1; +} + +/* + * return sends link address for new mstb + */ +static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port) +{ + int ret; + u8 rad[6], lct; + bool send_link = false; + switch (port->pdt) { + case DP_PEER_DEVICE_DP_LEGACY_CONV: + case DP_PEER_DEVICE_SST_SINK: + /* add i2c over sideband */ + ret = drm_dp_mst_register_i2c_bus(&port->aux); + break; + case DP_PEER_DEVICE_MST_BRANCHING: + lct = drm_dp_calculate_rad(port, rad); + + port->mstb = drm_dp_add_mst_branch_device(lct, rad); + port->mstb->mgr = port->mgr; + port->mstb->port_parent = port; + + send_link = true; + break; + } + return send_link; +} + +static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port) +{ + int ret; + if (port->dpcd_rev >= 0x12) { + port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid); + if (!port->guid_valid) { + ret = drm_dp_send_dpcd_write(mstb->mgr, + port, + DP_GUID, + 16, port->guid); + port->guid_valid = true; + } + } +} + +static void build_mst_prop_path(struct drm_dp_mst_port *port, + struct drm_dp_mst_branch *mstb, + char *proppath) +{ + int i; + char temp[8]; + snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id); + for (i = 0; i < (mstb->lct - 1); i++) { + int shift = (i % 2) ? 0 : 4; + int port_num = mstb->rad[i / 2] >> shift; + snprintf(temp, 8, "-%d", port_num); + strncat(proppath, temp, 255); + } + snprintf(temp, 8, "-%d", port->port_num); + strncat(proppath, temp, 255); +} + +static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, + struct device *dev, + struct drm_dp_link_addr_reply_port *port_msg) +{ + struct drm_dp_mst_port *port; + bool ret; + bool created = false; + int old_pdt = 0; + int old_ddps = 0; + port = drm_dp_get_port(mstb, port_msg->port_number); + if (!port) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return; + kref_init(&port->kref); + port->parent = mstb; + port->port_num = port_msg->port_number; + port->mgr = mstb->mgr; + port->aux.name = "DPMST"; + port->aux.dev = dev; + created = true; + } else { + old_pdt = port->pdt; + old_ddps = port->ddps; + } + + port->pdt = port_msg->peer_device_type; + port->input = port_msg->input_port; + port->mcs = port_msg->mcs; + port->ddps = port_msg->ddps; + port->ldps = port_msg->legacy_device_plug_status; + port->dpcd_rev = port_msg->dpcd_revision; + port->num_sdp_streams = port_msg->num_sdp_streams; + port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks; + memcpy(port->guid, port_msg->peer_guid, 16); + + /* manage mstb port lists with mgr lock - take a reference + for this list */ + if (created) { + mutex_lock(&mstb->mgr->lock); + kref_get(&port->kref); + list_add(&port->next, &mstb->ports); + mutex_unlock(&mstb->mgr->lock); + } + + if (old_ddps != port->ddps) { + if (port->ddps) { + drm_dp_check_port_guid(mstb, port); + if (!port->input) + drm_dp_send_enum_path_resources(mstb->mgr, mstb, port); + } else { + port->guid_valid = false; + port->available_pbn = 0; + } + } + + if (old_pdt != port->pdt && !port->input) { + drm_dp_port_teardown_pdt(port, old_pdt); + + ret = drm_dp_port_setup_pdt(port); + if (ret == true) { + drm_dp_send_link_address(mstb->mgr, port->mstb); + port->mstb->link_address_sent = true; + } + } + + if (created && !port->input) { + char proppath[255]; + build_mst_prop_path(port, mstb, proppath); + port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); + } + + /* put reference to this port */ + drm_dp_put_port(port); +} + +static void drm_dp_update_port(struct drm_dp_mst_branch *mstb, + struct drm_dp_connection_status_notify *conn_stat) +{ + struct drm_dp_mst_port *port; + int old_pdt; + int old_ddps; + bool dowork = false; + port = drm_dp_get_port(mstb, conn_stat->port_number); + if (!port) + return; + + old_ddps = port->ddps; + old_pdt = port->pdt; + port->pdt = conn_stat->peer_device_type; + port->mcs = conn_stat->message_capability_status; + port->ldps = conn_stat->legacy_device_plug_status; + port->ddps = conn_stat->displayport_device_plug_status; + + if (old_ddps != port->ddps) { + if (port->ddps) { + drm_dp_check_port_guid(mstb, port); + dowork = true; + } else { + port->guid_valid = false; + port->available_pbn = 0; + } + } + if (old_pdt != port->pdt && !port->input) { + drm_dp_port_teardown_pdt(port, old_pdt); + + if (drm_dp_port_setup_pdt(port)) + dowork = true; + } + + drm_dp_put_port(port); + if (dowork) + queue_work(system_long_wq, &mstb->mgr->work); + +} + +static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr, + u8 lct, u8 *rad) +{ + struct drm_dp_mst_branch *mstb; + struct drm_dp_mst_port *port; + int i; + /* find the port by iterating down */ + mstb = mgr->mst_primary; + + for (i = 0; i < lct - 1; i++) { + int shift = (i % 2) ? 0 : 4; + int port_num = rad[i / 2] >> shift; + + list_for_each_entry(port, &mstb->ports, next) { + if (port->port_num == port_num) { + if (!port->mstb) { + DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]); + return NULL; + } + + mstb = port->mstb; + break; + } + } + } + kref_get(&mstb->kref); + return mstb; +} + +static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_port *port; + + if (!mstb->link_address_sent) { + drm_dp_send_link_address(mgr, mstb); + mstb->link_address_sent = true; + } + list_for_each_entry(port, &mstb->ports, next) { + if (port->input) + continue; + + if (!port->ddps) + continue; + + if (!port->available_pbn) + drm_dp_send_enum_path_resources(mgr, mstb, port); + + if (port->mstb) + drm_dp_check_and_send_link_address(mgr, port->mstb); + } +} + +static void drm_dp_mst_link_probe_work(struct work_struct *work) +{ + struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); + + drm_dp_check_and_send_link_address(mgr, mgr->mst_primary); + +} + +static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, + u8 *guid) +{ + static u8 zero_guid[16]; + + if (!memcmp(guid, zero_guid, 16)) { + u64 salt = get_jiffies_64(); + memcpy(&guid[0], &salt, sizeof(u64)); + memcpy(&guid[8], &salt, sizeof(u64)); + return false; + } + return true; +} + +#if 0 +static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_REMOTE_DPCD_READ; + req.u.dpcd_read.port_number = port_num; + req.u.dpcd_read.dpcd_address = offset; + req.u.dpcd_read.num_bytes = num_bytes; + drm_dp_encode_sideband_req(&req, msg); + + return 0; +} +#endif + +static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr, + bool up, u8 *msg, int len) +{ + int ret; + int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE; + int tosend, total, offset; + int retries = 0; + +retry: + total = len; + offset = 0; + do { + tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total); + + ret = drm_dp_dpcd_write(mgr->aux, regbase + offset, + &msg[offset], + tosend); + if (ret != tosend) { + if (ret == -EIO && retries < 5) { + retries++; + goto retry; + } + DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret); + WARN(1, "fail\n"); + + return -EIO; + } + offset += tosend; + total -= tosend; + } while (total > 0); + return 0; +} + +static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + struct drm_dp_mst_branch *mstb = txmsg->dst; + + /* both msg slots are full */ + if (txmsg->seqno == -1) { + if (mstb->tx_slots[0] && mstb->tx_slots[1]) { + DRM_DEBUG_KMS("%s: failed to find slot\n", __func__); + return -EAGAIN; + } + if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) { + txmsg->seqno = mstb->last_seqno; + mstb->last_seqno ^= 1; + } else if (mstb->tx_slots[0] == NULL) + txmsg->seqno = 0; + else + txmsg->seqno = 1; + mstb->tx_slots[txmsg->seqno] = txmsg; + } + hdr->broadcast = 0; + hdr->path_msg = txmsg->path_msg; + hdr->lct = mstb->lct; + hdr->lcr = mstb->lct - 1; + if (mstb->lct > 1) + memcpy(hdr->rad, mstb->rad, mstb->lct / 2); + hdr->seqno = txmsg->seqno; + return 0; +} +/* + * process a single block of the next message in the sideband queue + */ +static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg, + bool up) +{ + u8 chunk[48]; + struct drm_dp_sideband_msg_hdr hdr; + int len, space, idx, tosend; + int ret; + + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) { + txmsg->seqno = -1; + txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND; + } + + /* make hdr from dst mst - for replies use seqno + otherwise assign one */ + ret = set_hdr_from_dst_qlock(&hdr, txmsg); + if (ret < 0) + return ret; + + /* amount left to send in this message */ + len = txmsg->cur_len - txmsg->cur_offset; + + /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */ + space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr); + + tosend = min(len, space); + if (len == txmsg->cur_len) + hdr.somt = 1; + if (space >= len) + hdr.eomt = 1; + + + hdr.msg_len = tosend + 1; + drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx); + memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend); + /* add crc at end */ + drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend); + idx += tosend + 1; + + ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx); + if (ret) { + DRM_DEBUG_KMS("sideband msg failed to send\n"); + return ret; + } + + txmsg->cur_offset += tosend; + if (txmsg->cur_offset == txmsg->cur_len) { + txmsg->state = DRM_DP_SIDEBAND_TX_SENT; + return 1; + } + return 0; +} + +/* must be called holding qlock */ +static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + /* construct a chunk from the first msg in the tx_msg queue */ + if (list_empty(&mgr->tx_msg_downq)) { + mgr->tx_down_in_progress = false; + return; + } + mgr->tx_down_in_progress = true; + + txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next); + ret = process_single_tx_qlock(mgr, txmsg, false); + if (ret == 1) { + /* txmsg is sent it should be in the slots now */ + list_del(&txmsg->next); + } else if (ret) { + DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); + list_del(&txmsg->next); + if (txmsg->seqno != -1) + txmsg->dst->tx_slots[txmsg->seqno] = NULL; + txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + wake_up(&mgr->tx_waitq); + } + if (list_empty(&mgr->tx_msg_downq)) { + mgr->tx_down_in_progress = false; + return; + } +} + +/* called holding qlock */ +static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + /* construct a chunk from the first msg in the tx_msg queue */ + if (list_empty(&mgr->tx_msg_upq)) { + mgr->tx_up_in_progress = false; + return; + } + + txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next); + ret = process_single_tx_qlock(mgr, txmsg, true); + if (ret == 1) { + /* up txmsgs aren't put in slots - so free after we send it */ + list_del(&txmsg->next); + kfree(txmsg); + } else if (ret) + DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); + mgr->tx_up_in_progress = true; +} + +static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + mutex_lock(&mgr->qlock); + list_add_tail(&txmsg->next, &mgr->tx_msg_downq); + if (!mgr->tx_down_in_progress) + process_single_down_tx_qlock(mgr); + mutex_unlock(&mgr->qlock); +} + +static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + len = build_link_address(txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + int i; + + if (txmsg->reply.reply_type == 1) + DRM_DEBUG_KMS("link address nak received\n"); + else { + DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports); + for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { + DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i, + txmsg->reply.u.link_addr.ports[i].input_port, + txmsg->reply.u.link_addr.ports[i].peer_device_type, + txmsg->reply.u.link_addr.ports[i].port_number, + txmsg->reply.u.link_addr.ports[i].dpcd_revision, + txmsg->reply.u.link_addr.ports[i].mcs, + txmsg->reply.u.link_addr.ports[i].ddps, + txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status, + txmsg->reply.u.link_addr.ports[i].num_sdp_streams, + txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks); + } + for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { + drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]); + } + (*mgr->cbs->hotplug)(mgr); + } + } else + DRM_DEBUG_KMS("link address failed %d\n", ret); + + kfree(txmsg); + return 0; +} + +static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + len = build_enum_path_resources(txmsg, port->port_num); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) + DRM_DEBUG_KMS("enum path resources nak received\n"); + else { + if (port->port_num != txmsg->reply.u.path_resources.port_number) + DRM_ERROR("got incorrect port in response\n"); + DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number, + txmsg->reply.u.path_resources.avail_payload_bw_number); + port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number; + } + } + + kfree(txmsg); + return 0; +} + +int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + int pbn) +{ + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + int len, ret; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EINVAL; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto fail_put; + } + + txmsg->dst = mstb; + len = build_allocate_payload(txmsg, port->port_num, + id, + pbn); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) { + ret = -EINVAL; + } else + ret = 0; + } + kfree(txmsg); +fail_put: + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload) +{ + int ret; + + ret = drm_dp_dpcd_write_payload(mgr, id, payload); + if (ret < 0) { + payload->payload_state = 0; + return ret; + } + payload->payload_state = DP_PAYLOAD_LOCAL; + return 0; +} + +int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) +{ + int ret; + ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); + if (ret < 0) + return ret; + payload->payload_state = DP_PAYLOAD_REMOTE; + return ret; +} + +int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) +{ + DRM_DEBUG_KMS("\n"); + /* its okay for these to fail */ + if (port) { + drm_dp_payload_send_msg(mgr, port, id, 0); + } + + drm_dp_dpcd_write_payload(mgr, id, payload); + payload->payload_state = 0; + return 0; +} + +int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload) +{ + payload->payload_state = 0; + return 0; +} + +/** + * drm_dp_update_payload_part1() - Execute payload update part 1 + * @mgr: manager to use. + * + * This iterates over all proposed virtual channels, and tries to + * allocate space in the link for them. For 0->slots transitions, + * this step just writes the VCPI to the MST device. For slots->0 + * transitions, this writes the updated VCPIs and removes the + * remote VC payloads. + * + * after calling this the driver should generate ACT and payload + * packets. + */ +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) +{ + int i; + int cur_slots = 1; + struct drm_dp_payload req_payload; + struct drm_dp_mst_port *port; + + mutex_lock(&mgr->payload_lock); + for (i = 0; i < mgr->max_payloads; i++) { + /* solve the current payloads - compare to the hw ones + - update the hw view */ + req_payload.start_slot = cur_slots; + if (mgr->proposed_vcpis[i]) { + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; + } else { + port = NULL; + req_payload.num_slots = 0; + } + /* work out what is required to happen with this payload */ + if (mgr->payloads[i].start_slot != req_payload.start_slot || + mgr->payloads[i].num_slots != req_payload.num_slots) { + + /* need to push an update for this payload */ + if (req_payload.num_slots) { + drm_dp_create_payload_step1(mgr, i + 1, &req_payload); + mgr->payloads[i].num_slots = req_payload.num_slots; + } else if (mgr->payloads[i].num_slots) { + mgr->payloads[i].num_slots = 0; + drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]); + req_payload.payload_state = mgr->payloads[i].payload_state; + } else + req_payload.payload_state = 0; + + mgr->payloads[i].start_slot = req_payload.start_slot; + mgr->payloads[i].payload_state = req_payload.payload_state; + } + cur_slots += req_payload.num_slots; + } + mutex_unlock(&mgr->payload_lock); + + return 0; +} +EXPORT_SYMBOL(drm_dp_update_payload_part1); + +/** + * drm_dp_update_payload_part2() - Execute payload update part 2 + * @mgr: manager to use. + * + * This iterates over all proposed virtual channels, and tries to + * allocate space in the link for them. For 0->slots transitions, + * this step writes the remote VC payload commands. For slots->0 + * this just resets some internal state. + */ +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_mst_port *port; + int i; + int ret; + mutex_lock(&mgr->payload_lock); + for (i = 0; i < mgr->max_payloads; i++) { + + if (!mgr->proposed_vcpis[i]) + continue; + + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + + DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state); + if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { + ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]); + } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { + ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]); + } + if (ret) { + mutex_unlock(&mgr->payload_lock); + return ret; + } + } + mutex_unlock(&mgr->payload_lock); + return 0; +} +EXPORT_SYMBOL(drm_dp_update_payload_part2); + +#if 0 /* unused as of yet */ +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + len = build_dpcd_read(txmsg, port->port_num, 0, 8); + txmsg->dst = port->parent; + + drm_dp_queue_down_tx(mgr, txmsg); + + return 0; +} +#endif + +static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes) +{ + int len; + int ret; + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EINVAL; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto fail_put; + } + + len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes); + txmsg->dst = mstb; + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) { + ret = -EINVAL; + } else + ret = 0; + } + kfree(txmsg); +fail_put: + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type) +{ + struct drm_dp_sideband_msg_reply_body reply; + + reply.reply_type = 1; + reply.req_type = req_type; + drm_dp_encode_sideband_reply(&reply, msg); + return 0; +} + +static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + int req_type, int seqno, bool broadcast) +{ + struct drm_dp_sideband_msg_tx *txmsg; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + txmsg->seqno = seqno; + drm_dp_encode_up_ack_reply(txmsg, req_type); + + mutex_lock(&mgr->qlock); + list_add_tail(&txmsg->next, &mgr->tx_msg_upq); + if (!mgr->tx_up_in_progress) { + process_single_up_tx_qlock(mgr); + } + mutex_unlock(&mgr->qlock); + return 0; +} + +static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count) +{ + switch (dp_link_bw) { + case DP_LINK_BW_1_62: + return 3 * dp_link_count; + case DP_LINK_BW_2_7: + return 5 * dp_link_count; + case DP_LINK_BW_5_4: + return 10 * dp_link_count; + } + return 0; +} + +/** + * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager + * @mgr: manager to set state for + * @mst_state: true to enable MST on this connector - false to disable. + * + * This is called by the driver when it detects an MST capable device plugged + * into a DP MST capable port, or when a DP MST capable device is unplugged. + */ +int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) +{ + int ret = 0; + struct drm_dp_mst_branch *mstb = NULL; + + mutex_lock(&mgr->lock); + if (mst_state == mgr->mst_state) + goto out_unlock; + + mgr->mst_state = mst_state; + /* set the device into MST mode */ + if (mst_state) { + WARN_ON(mgr->mst_primary); + + /* get dpcd info */ + ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); + if (ret != DP_RECEIVER_CAP_SIZE) { + DRM_DEBUG_KMS("failed to read DPCD\n"); + goto out_unlock; + } + + mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK); + mgr->total_pbn = 2560; + mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div); + mgr->avail_slots = mgr->total_slots; + + /* add initial branch device at LCT 1 */ + mstb = drm_dp_add_mst_branch_device(1, NULL); + if (mstb == NULL) { + ret = -ENOMEM; + goto out_unlock; + } + mstb->mgr = mgr; + + /* give this the main reference */ + mgr->mst_primary = mstb; + kref_get(&mgr->mst_primary->kref); + + { + struct drm_dp_payload reset_pay; + reset_pay.start_slot = 0; + reset_pay.num_slots = 0x3f; + drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); + if (ret < 0) { + goto out_unlock; + } + + + /* sort out guid */ + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16); + if (ret != 16) { + DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret); + goto out_unlock; + } + + mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid); + if (!mgr->guid_valid) { + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16); + mgr->guid_valid = true; + } + + queue_work(system_long_wq, &mgr->work); + + ret = 0; + } else { + /* disable MST on the device */ + mstb = mgr->mst_primary; + mgr->mst_primary = NULL; + /* this can fail if the device is gone */ + drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); + ret = 0; + memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); + mgr->payload_mask = 0; + set_bit(0, &mgr->payload_mask); + } + +out_unlock: + mutex_unlock(&mgr->lock); + if (mstb) + drm_dp_put_mst_branch_device(mstb); + return ret; + +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst); + +/** + * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager + * @mgr: manager to suspend + * + * This function tells the MST device that we can't handle UP messages + * anymore. This should stop it from sending any since we are suspended. + */ +void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr) +{ + mutex_lock(&mgr->lock); + drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UPSTREAM_IS_SRC); + mutex_unlock(&mgr->lock); +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend); + +/** + * drm_dp_mst_topology_mgr_resume() - resume the MST manager + * @mgr: manager to resume + * + * This will fetch DPCD and see if the device is still there, + * if it is, it will rewrite the MSTM control bits, and return. + * + * if the device fails this returns -1, and the driver should do + * a full MST reprobe, in case we were undocked. + */ +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + + mutex_lock(&mgr->lock); + + if (mgr->mst_primary) { + int sret; + sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); + if (sret != DP_RECEIVER_CAP_SIZE) { + DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); + ret = -1; + goto out_unlock; + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); + if (ret < 0) { + DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n"); + ret = -1; + goto out_unlock; + } + ret = 0; + } else + ret = -1; + +out_unlock: + mutex_unlock(&mgr->lock); + return ret; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); + +static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) +{ + int len; + u8 replyblock[32]; + int replylen, origlen, curreply; + int ret; + struct drm_dp_sideband_msg_rx *msg; + int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; + msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv; + + len = min(mgr->max_dpcd_transaction_bytes, 16); + ret = drm_dp_dpcd_read(mgr->aux, basereg, + replyblock, len); + if (ret != len) { + DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); + return; + } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); + if (!ret) { + DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); + return; + } + replylen = msg->curchunk_len + msg->curchunk_hdrlen; + + origlen = replylen; + replylen -= len; + curreply = len; + while (replylen > 0) { + len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16); + ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply, + replyblock, len); + if (ret != len) { + DRM_DEBUG_KMS("failed to read a chunk\n"); + } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); + if (ret == false) + DRM_DEBUG_KMS("failed to build sideband msg\n"); + curreply += len; + replylen -= len; + } +} + +static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + + drm_dp_get_one_sb_msg(mgr, false); + + if (mgr->down_rep_recv.have_eomt) { + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + int slot = -1; + mstb = drm_dp_get_mst_branch_device(mgr, + mgr->down_rep_recv.initial_hdr.lct, + mgr->down_rep_recv.initial_hdr.rad); + + if (!mstb) { + DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct); + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + /* find the message */ + slot = mgr->down_rep_recv.initial_hdr.seqno; + mutex_lock(&mgr->qlock); + txmsg = mstb->tx_slots[slot]; + /* remove from slots */ + mutex_unlock(&mgr->qlock); + + if (!txmsg) { + DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", + mstb, + mgr->down_rep_recv.initial_hdr.seqno, + mgr->down_rep_recv.initial_hdr.lct, + mgr->down_rep_recv.initial_hdr.rad[0], + mgr->down_rep_recv.msg[0]); + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply); + if (txmsg->reply.reply_type == 1) { + DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data); + } + + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + drm_dp_put_mst_branch_device(mstb); + + mutex_lock(&mgr->qlock); + txmsg->state = DRM_DP_SIDEBAND_TX_RX; + mstb->tx_slots[slot] = NULL; + mutex_unlock(&mgr->qlock); + + wake_up(&mgr->tx_waitq); + } + return ret; +} + +static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + drm_dp_get_one_sb_msg(mgr, true); + + if (mgr->up_req_recv.have_eomt) { + struct drm_dp_sideband_msg_req_body msg; + struct drm_dp_mst_branch *mstb; + bool seqno; + mstb = drm_dp_get_mst_branch_device(mgr, + mgr->up_req_recv.initial_hdr.lct, + mgr->up_req_recv.initial_hdr.rad); + if (!mstb) { + DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + seqno = mgr->up_req_recv.initial_hdr.seqno; + drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg); + + if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { + drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false); + drm_dp_update_port(mstb, &msg.u.conn_stat); + DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type); + (*mgr->cbs->hotplug)(mgr); + + } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { + drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false); + DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn); + } + + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + } + return ret; +} + +/** + * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify + * @mgr: manager to notify irq for. + * @esi: 4 bytes from SINK_COUNT_ESI + * + * This should be called from the driver when it detects a short IRQ, + * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The + * topology manager will process the sideband messages received as a result + * of this. + */ +int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled) +{ + int ret = 0; + int sc; + *handled = false; + sc = esi[0] & 0x3f; + + if (sc != mgr->sink_count) { + mgr->sink_count = sc; + *handled = true; + } + + if (esi[1] & DP_DOWN_REP_MSG_RDY) { + ret = drm_dp_mst_handle_down_rep(mgr); + *handled = true; + } + + if (esi[1] & DP_UP_REQ_MSG_RDY) { + ret |= drm_dp_mst_handle_up_req(mgr); + *handled = true; + } + + drm_dp_mst_kick_tx(mgr); + return ret; +} +EXPORT_SYMBOL(drm_dp_mst_hpd_irq); + +/** + * drm_dp_mst_detect_port() - get connection status for an MST port + * @mgr: manager for this port + * @port: unverified pointer to a port + * + * This returns the current connection state for a port. It validates the + * port pointer still exists so the caller doesn't require a reference + */ +enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + enum drm_connector_status status = connector_status_disconnected; + + /* we need to search for the port in the mgr in case its gone */ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return connector_status_disconnected; + + if (!port->ddps) + goto out; + + switch (port->pdt) { + case DP_PEER_DEVICE_NONE: + case DP_PEER_DEVICE_MST_BRANCHING: + break; + + case DP_PEER_DEVICE_SST_SINK: + status = connector_status_connected; + break; + case DP_PEER_DEVICE_DP_LEGACY_CONV: + if (port->ldps) + status = connector_status_connected; + break; + } +out: + drm_dp_put_port(port); + return status; +} +EXPORT_SYMBOL(drm_dp_mst_detect_port); + +/** + * drm_dp_mst_get_edid() - get EDID for an MST port + * @connector: toplevel connector to get EDID for + * @mgr: manager for this port + * @port: unverified pointer to a port. + * + * This returns an EDID for the port connected to a connector, + * It validates the pointer still exists so the caller doesn't require a + * reference. + */ +struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + struct edid *edid = NULL; + + /* we need to search for the port in the mgr in case its gone */ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return NULL; + + edid = drm_get_edid(connector, &port->aux.ddc); + drm_dp_put_port(port); + return edid; +} +EXPORT_SYMBOL(drm_dp_mst_get_edid); + +/** + * drm_dp_find_vcpi_slots() - find slots for this PBN value + * @mgr: manager to use + * @pbn: payload bandwidth to convert into slots. + */ +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, + int pbn) +{ + int num_slots; + + num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); + + if (num_slots > mgr->avail_slots) + return -ENOSPC; + return num_slots; +} +EXPORT_SYMBOL(drm_dp_find_vcpi_slots); + +static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_vcpi *vcpi, int pbn) +{ + int num_slots; + int ret; + + num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); + + if (num_slots > mgr->avail_slots) + return -ENOSPC; + + vcpi->pbn = pbn; + vcpi->aligned_pbn = num_slots * mgr->pbn_div; + vcpi->num_slots = num_slots; + + ret = drm_dp_mst_assign_payload_id(mgr, vcpi); + if (ret < 0) + return ret; + return 0; +} + +/** + * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel + * @mgr: manager for this port + * @port: port to allocate a virtual channel for. + * @pbn: payload bandwidth number to request + * @slots: returned number of slots for this PBN. + */ +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots) +{ + int ret; + + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return false; + + if (port->vcpi.vcpi > 0) { + DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn); + if (pbn == port->vcpi.pbn) { + *slots = port->vcpi.num_slots; + return true; + } + } + + ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn); + if (ret) { + DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret); + goto out; + } + DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots); + *slots = port->vcpi.num_slots; + + drm_dp_put_port(port); + return true; +out: + return false; +} +EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); + +/** + * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI + * @mgr: manager for this port + * @port: unverified pointer to a port. + * + * This just resets the number of slots for the ports VCPI for later programming. + */ +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return; + port->vcpi.num_slots = 0; + drm_dp_put_port(port); +} +EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots); + +/** + * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI + * @mgr: manager for this port + * @port: unverified port to deallocate vcpi for + */ +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return; + + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + port->vcpi.num_slots = 0; + port->vcpi.pbn = 0; + port->vcpi.aligned_pbn = 0; + port->vcpi.vcpi = 0; + drm_dp_put_port(port); +} +EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi); + +static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, + int id, struct drm_dp_payload *payload) +{ + u8 payload_alloc[3], status; + int ret; + int retries = 0; + + drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, + DP_PAYLOAD_TABLE_UPDATED); + + payload_alloc[0] = id; + payload_alloc[1] = payload->start_slot; + payload_alloc[2] = payload->num_slots; + + ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3); + if (ret != 3) { + DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret); + goto fail; + } + +retry: + ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); + if (ret < 0) { + DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); + goto fail; + } + + if (!(status & DP_PAYLOAD_TABLE_UPDATED)) { + retries++; + if (retries < 20) { + usleep_range(10000, 20000); + goto retry; + } + DRM_DEBUG_KMS("status not set after read payload table status %d\n", status); + ret = -EINVAL; + goto fail; + } + ret = 0; +fail: + return ret; +} + + +/** + * drm_dp_check_act_status() - Check ACT handled status. + * @mgr: manager to use + * + * Check the payload status bits in the DPCD for ACT handled completion. + */ +int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) +{ + u8 status; + int ret; + int count = 0; + + do { + ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); + + if (ret < 0) { + DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); + goto fail; + } + + if (status & DP_PAYLOAD_ACT_HANDLED) + break; + count++; + udelay(100); + + } while (count < 30); + + if (!(status & DP_PAYLOAD_ACT_HANDLED)) { + DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count); + ret = -EINVAL; + goto fail; + } + return 0; +fail: + return ret; +} +EXPORT_SYMBOL(drm_dp_check_act_status); + +/** + * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. + * @clock: dot clock for the mode + * @bpp: bpp for the mode. + * + * This uses the formula in the spec to calculate the PBN value for a mode. + */ +int drm_dp_calc_pbn_mode(int clock, int bpp) +{ + fixed20_12 pix_bw; + fixed20_12 fbpp; + fixed20_12 result; + fixed20_12 margin, tmp; + u32 res; + + pix_bw.full = dfixed_const(clock); + fbpp.full = dfixed_const(bpp); + tmp.full = dfixed_const(8); + fbpp.full = dfixed_div(fbpp, tmp); + + result.full = dfixed_mul(pix_bw, fbpp); + margin.full = dfixed_const(54); + tmp.full = dfixed_const(64); + margin.full = dfixed_div(margin, tmp); + result.full = dfixed_div(result, margin); + + margin.full = dfixed_const(1006); + tmp.full = dfixed_const(1000); + margin.full = dfixed_div(margin, tmp); + result.full = dfixed_mul(result, margin); + + result.full = dfixed_div(result, tmp); + result.full = dfixed_ceil(result); + res = dfixed_trunc(result); + return res; +} +EXPORT_SYMBOL(drm_dp_calc_pbn_mode); + +static int test_calc_pbn_mode(void) +{ + int ret; + ret = drm_dp_calc_pbn_mode(154000, 30); + if (ret != 689) + return -EINVAL; + ret = drm_dp_calc_pbn_mode(234000, 30); + if (ret != 1047) + return -EINVAL; + return 0; +} + +/* we want to kick the TX after we've ack the up/down IRQs. */ +static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr) +{ + queue_work(system_long_wq, &mgr->tx_work); +} + +static void drm_dp_mst_dump_mstb(struct seq_file *m, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_port *port; + int tabs = mstb->lct; + char prefix[10]; + int i; + + for (i = 0; i < tabs; i++) + prefix[i] = '\t'; + prefix[i] = '\0'; + + seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports); + list_for_each_entry(port, &mstb->ports, next) { + seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector); + if (port->mstb) + drm_dp_mst_dump_mstb(m, port->mstb); + } +} + +static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, + char *buf) +{ + int ret; + int i; + for (i = 0; i < 4; i++) { + ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16); + if (ret != 16) + break; + } + if (i == 4) + return true; + return false; +} + +/** + * drm_dp_mst_dump_topology(): dump topology to seq file. + * @m: seq_file to dump output to + * @mgr: manager to dump current topology for. + * + * helper to dump MST topology to a seq file for debugfs. + */ +void drm_dp_mst_dump_topology(struct seq_file *m, + struct drm_dp_mst_topology_mgr *mgr) +{ + int i; + struct drm_dp_mst_port *port; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + drm_dp_mst_dump_mstb(m, mgr->mst_primary); + + /* dump VCPIs */ + mutex_unlock(&mgr->lock); + + mutex_lock(&mgr->payload_lock); + seq_printf(m, "vcpi: %lx\n", mgr->payload_mask); + + for (i = 0; i < mgr->max_payloads; i++) { + if (mgr->proposed_vcpis[i]) { + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots); + } else + seq_printf(m, "vcpi %d:unsed\n", i); + } + for (i = 0; i < mgr->max_payloads; i++) { + seq_printf(m, "payload %d: %d, %d, %d\n", + i, + mgr->payloads[i].payload_state, + mgr->payloads[i].start_slot, + mgr->payloads[i].num_slots); + + + } + mutex_unlock(&mgr->payload_lock); + + mutex_lock(&mgr->lock); + if (mgr->mst_primary) { + u8 buf[64]; + bool bret; + int ret; + ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE); + seq_printf(m, "dpcd: "); + for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2); + seq_printf(m, "faux/mst: "); + for (i = 0; i < 2; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1); + seq_printf(m, "mst ctrl: "); + for (i = 0; i < 1; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + + bret = dump_dp_payload_table(mgr, buf); + if (bret == true) { + seq_printf(m, "payload table: "); + for (i = 0; i < 63; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + } + + } + + mutex_unlock(&mgr->lock); + +} +EXPORT_SYMBOL(drm_dp_mst_dump_topology); + +static void drm_dp_tx_work(struct work_struct *work) +{ + struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work); + + mutex_lock(&mgr->qlock); + if (mgr->tx_down_in_progress) + process_single_down_tx_qlock(mgr); + mutex_unlock(&mgr->qlock); +} + +/** + * drm_dp_mst_topology_mgr_init - initialise a topology manager + * @mgr: manager struct to initialise + * @dev: device providing this structure - for i2c addition. + * @aux: DP helper aux channel to talk to this device + * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit + * @max_payloads: maximum number of payloads this GPU can source + * @conn_base_id: the connector object ID the MST device is connected to. + * + * Return 0 for success, or negative error code on failure + */ +int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, + struct device *dev, struct drm_dp_aux *aux, + int max_dpcd_transaction_bytes, + int max_payloads, int conn_base_id) +{ + mutex_init(&mgr->lock); + mutex_init(&mgr->qlock); + mutex_init(&mgr->payload_lock); + INIT_LIST_HEAD(&mgr->tx_msg_upq); + INIT_LIST_HEAD(&mgr->tx_msg_downq); + INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); + INIT_WORK(&mgr->tx_work, drm_dp_tx_work); + init_waitqueue_head(&mgr->tx_waitq); + mgr->dev = dev; + mgr->aux = aux; + mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes; + mgr->max_payloads = max_payloads; + mgr->conn_base_id = conn_base_id; + mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL); + if (!mgr->payloads) + return -ENOMEM; + mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL); + if (!mgr->proposed_vcpis) + return -ENOMEM; + set_bit(0, &mgr->payload_mask); + test_calc_pbn_mode(); + return 0; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); + +/** + * drm_dp_mst_topology_mgr_destroy() - destroy topology manager. + * @mgr: manager to destroy + */ +void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) +{ + mutex_lock(&mgr->payload_lock); + kfree(mgr->payloads); + mgr->payloads = NULL; + kfree(mgr->proposed_vcpis); + mgr->proposed_vcpis = NULL; + mutex_unlock(&mgr->payload_lock); + mgr->dev = NULL; + mgr->aux = NULL; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy); + +/* I2C device */ +static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct drm_dp_aux *aux = adapter->algo_data; + struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); + struct drm_dp_mst_branch *mstb; + struct drm_dp_mst_topology_mgr *mgr = port->mgr; + unsigned int i; + bool reading = false; + struct drm_dp_sideband_msg_req_body msg; + struct drm_dp_sideband_msg_tx *txmsg = NULL; + int ret; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EREMOTEIO; + + /* construct i2c msg */ + /* see if last msg is a read */ + if (msgs[num - 1].flags & I2C_M_RD) + reading = true; + + if (!reading) { + DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n"); + ret = -EIO; + goto out; + } + + msg.req_type = DP_REMOTE_I2C_READ; + msg.u.i2c_read.num_transactions = num - 1; + msg.u.i2c_read.port_number = port->port_num; + for (i = 0; i < num - 1; i++) { + msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr; + msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len; + msg.u.i2c_read.transactions[i].bytes = msgs[i].buf; + } + msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr; + msg.u.i2c_read.num_bytes_read = msgs[num - 1].len; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto out; + } + + txmsg->dst = mstb; + drm_dp_encode_sideband_req(&msg, txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + + if (txmsg->reply.reply_type == 1) { /* got a NAK back */ + ret = -EREMOTEIO; + goto out; + } + if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) { + ret = -EIO; + goto out; + } + memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len); + ret = num; + } +out: + kfree(txmsg); + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm drm_dp_mst_i2c_algo = { + .functionality = drm_dp_mst_i2c_functionality, + .master_xfer = drm_dp_mst_i2c_xfer, +}; + +/** + * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) +{ + aux->ddc.algo = &drm_dp_mst_i2c_algo; + aux->ddc.algo_data = aux; + aux->ddc.retries = 3; + + aux->ddc.class = I2C_CLASS_DDC; + aux->ddc.owner = THIS_MODULE; + aux->ddc.dev.parent = aux->dev; + aux->ddc.dev.of_node = aux->dev->of_node; + + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); + + return i2c_add_adapter(&aux->ddc); +} + +/** + * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter + * @aux: DisplayPort AUX channel + */ +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux) +{ + i2c_del_adapter(&aux->ddc); +} diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h new file mode 100644 index 0000000..9b446ad --- /dev/null +++ b/include/drm/drm_dp_mst_helper.h @@ -0,0 +1,509 @@ +/* + * Copyright © 2014 Red Hat. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#ifndef _DRM_DP_MST_HELPER_H_ +#define _DRM_DP_MST_HELPER_H_ + +#include +#include + +struct drm_dp_mst_branch; + +/** + * struct drm_dp_vcpi - Virtual Channel Payload Identifer + * @vcpi: Virtual channel ID. + * @pbn: Payload Bandwidth Number for this channel + * @aligned_pbn: PBN aligned with slot size + * @num_slots: number of slots for this PBN + */ +struct drm_dp_vcpi { + int vcpi; + int pbn; + int aligned_pbn; + int num_slots; +}; + +/** + * struct drm_dp_mst_port - MST port + * @kref: reference count for this port. + * @guid_valid: for DP 1.2 devices if we have validated the GUID. + * @guid: guid for DP 1.2 device on this port. + * @port_num: port number + * @input: if this port is an input port. + * @mcs: message capability status - DP 1.2 spec. + * @ddps: DisplayPort Device Plug Status - DP 1.2 + * @pdt: Peer Device Type + * @ldps: Legacy Device Plug Status + * @dpcd_rev: DPCD revision of device on this port + * @num_sdp_streams: Number of simultaneous streams + * @num_sdp_stream_sinks: Number of stream sinks + * @available_pbn: Available bandwidth for this port. + * @next: link to next port on this branch device + * @mstb: branch device attach below this port + * @aux: i2c aux transport to talk to device connected to this port. + * @parent: branch device parent of this port + * @vcpi: Virtual Channel Payload info for this port. + * @connector: DRM connector this port is connected to. + * @mgr: topology manager this port lives under. + * + * This structure represents an MST port endpoint on a device somewhere + * in the MST topology. + */ +struct drm_dp_mst_port { + struct kref kref; + + /* if dpcd 1.2 device is on this port - its GUID info */ + bool guid_valid; + u8 guid[16]; + + u8 port_num; + bool input; + bool mcs; + bool ddps; + u8 pdt; + bool ldps; + u8 dpcd_rev; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + uint16_t available_pbn; + struct list_head next; + struct drm_dp_mst_branch *mstb; /* pointer to an mstb if this port has one */ + struct drm_dp_aux aux; /* i2c bus for this port? */ + struct drm_dp_mst_branch *parent; + + struct drm_dp_vcpi vcpi; + struct drm_connector *connector; + struct drm_dp_mst_topology_mgr *mgr; +}; + +/** + * struct drm_dp_mst_branch - MST branch device. + * @kref: reference count for this port. + * @rad: Relative Address to talk to this branch device. + * @lct: Link count total to talk to this branch device. + * @num_ports: number of ports on the branch. + * @msg_slots: one bit per transmitted msg slot. + * @ports: linked list of ports on this branch. + * @port_parent: pointer to the port parent, NULL if toplevel. + * @mgr: topology manager for this branch device. + * @tx_slots: transmission slots for this device. + * @last_seqno: last sequence number used to talk to this. + * @link_address_sent: if a link address message has been sent to this device yet. + * + * This structure represents an MST branch device, there is one + * primary branch device at the root, along with any others connected + * to downstream ports + */ +struct drm_dp_mst_branch { + struct kref kref; + u8 rad[8]; + u8 lct; + int num_ports; + + int msg_slots; + struct list_head ports; + + /* list of tx ops queue for this port */ + struct drm_dp_mst_port *port_parent; + struct drm_dp_mst_topology_mgr *mgr; + + /* slots are protected by mstb->mgr->qlock */ + struct drm_dp_sideband_msg_tx *tx_slots[2]; + int last_seqno; + bool link_address_sent; +}; + + +/* sideband msg header - not bit struct */ +struct drm_dp_sideband_msg_hdr { + u8 lct; + u8 lcr; + u8 rad[8]; + bool broadcast; + bool path_msg; + u8 msg_len; + bool somt; + bool eomt; + bool seqno; +}; + +struct drm_dp_nak_reply { + u8 guid[16]; + u8 reason; + u8 nak_data; +}; + +struct drm_dp_link_address_ack_reply { + u8 guid[16]; + u8 nports; + struct drm_dp_link_addr_reply_port { + bool input_port; + u8 peer_device_type; + u8 port_number; + bool mcs; + bool ddps; + bool legacy_device_plug_status; + u8 dpcd_revision; + u8 peer_guid[16]; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + } ports[16]; +}; + +struct drm_dp_remote_dpcd_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_dpcd_write_ack_reply { + u8 port_number; +}; + +struct drm_dp_remote_dpcd_write_nak_reply { + u8 port_number; + u8 reason; + u8 bytes_written_before_failure; +}; + +struct drm_dp_remote_i2c_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_i2c_read_nak_reply { + u8 port_number; + u8 nak_reason; + u8 i2c_nak_transaction; +}; + +struct drm_dp_remote_i2c_write_ack_reply { + u8 port_number; +}; + + +struct drm_dp_sideband_msg_rx { + u8 chunk[48]; + u8 msg[256]; + u8 curchunk_len; + u8 curchunk_idx; /* chunk we are parsing now */ + u8 curchunk_hdrlen; + u8 curlen; /* total length of the msg */ + bool have_somt; + bool have_eomt; + struct drm_dp_sideband_msg_hdr initial_hdr; +}; + + +struct drm_dp_allocate_payload { + u8 port_number; + u8 number_sdp_streams; + u8 vcpi; + u16 pbn; + u8 sdp_stream_sink[8]; +}; + +struct drm_dp_allocate_payload_ack_reply { + u8 port_number; + u8 vcpi; + u16 allocated_pbn; +}; + +struct drm_dp_connection_status_notify { + u8 guid[16]; + u8 port_number; + bool legacy_device_plug_status; + bool displayport_device_plug_status; + bool message_capability_status; + bool input_port; + u8 peer_device_type; +}; + +struct drm_dp_remote_dpcd_read { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; +}; + +struct drm_dp_remote_dpcd_write { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; + u8 *bytes; +}; + +struct drm_dp_remote_i2c_read { + u8 num_transactions; + u8 port_number; + struct { + u8 i2c_dev_id; + u8 num_bytes; + u8 *bytes; + u8 no_stop_bit; + u8 i2c_transaction_delay; + } transactions[4]; + u8 read_i2c_device_id; + u8 num_bytes_read; +}; + +struct drm_dp_remote_i2c_write { + u8 port_number; + u8 write_i2c_device_id; + u8 num_bytes; + u8 *bytes; +}; + +/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_req { + u8 port_number; +}; + +struct drm_dp_enum_path_resources_ack_reply { + u8 port_number; + u16 full_payload_bw_number; + u16 avail_payload_bw_number; +}; + +/* covers POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_rep { + u8 port_number; +}; + +struct drm_dp_query_payload { + u8 port_number; + u8 vcpi; +}; + +struct drm_dp_resource_status_notify { + u8 port_number; + u8 guid[16]; + u16 available_pbn; +}; + +struct drm_dp_query_payload_ack_reply { + u8 port_number; + u8 allocated_pbn; +}; + +struct drm_dp_sideband_msg_req_body { + u8 req_type; + union ack_req { + struct drm_dp_connection_status_notify conn_stat; + struct drm_dp_port_number_req port_num; + struct drm_dp_resource_status_notify resource_stat; + + struct drm_dp_query_payload query_payload; + struct drm_dp_allocate_payload allocate_payload; + + struct drm_dp_remote_dpcd_read dpcd_read; + struct drm_dp_remote_dpcd_write dpcd_write; + + struct drm_dp_remote_i2c_read i2c_read; + struct drm_dp_remote_i2c_write i2c_write; + } u; +}; + +struct drm_dp_sideband_msg_reply_body { + u8 reply_type; + u8 req_type; + union ack_replies { + struct drm_dp_nak_reply nak; + struct drm_dp_link_address_ack_reply link_addr; + struct drm_dp_port_number_rep port_number; + + struct drm_dp_enum_path_resources_ack_reply path_resources; + struct drm_dp_allocate_payload_ack_reply allocate_payload; + struct drm_dp_query_payload_ack_reply query_payload; + + struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack; + struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack; + struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack; + + struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack; + struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack; + struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack; + } u; +}; + +/* msg is queued to be put into a slot */ +#define DRM_DP_SIDEBAND_TX_QUEUED 0 +/* msg has started transmitting on a slot - still on msgq */ +#define DRM_DP_SIDEBAND_TX_START_SEND 1 +/* msg has finished transmitting on a slot - removed from msgq only in slot */ +#define DRM_DP_SIDEBAND_TX_SENT 2 +/* msg has received a response - removed from slot */ +#define DRM_DP_SIDEBAND_TX_RX 3 +#define DRM_DP_SIDEBAND_TX_TIMEOUT 4 + +struct drm_dp_sideband_msg_tx { + u8 msg[256]; + u8 chunk[48]; + u8 cur_offset; + u8 cur_len; + struct drm_dp_mst_branch *dst; + struct list_head next; + int seqno; + int state; + bool path_msg; + struct drm_dp_sideband_msg_reply_body reply; +}; + +/* sideband msg handler */ +struct drm_dp_mst_topology_mgr; +struct drm_dp_mst_topology_cbs { + /* create a connector for a port */ + struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *path); + void (*destroy_connector)(struct drm_dp_mst_topology_mgr *mgr, + struct drm_connector *connector); + void (*hotplug)(struct drm_dp_mst_topology_mgr *mgr); + +}; + +#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8) + +#define DP_PAYLOAD_LOCAL 1 +#define DP_PAYLOAD_REMOTE 2 +#define DP_PAYLOAD_DELETE_LOCAL 3 + +struct drm_dp_payload { + int payload_state; + int start_slot; + int num_slots; +}; + +/** + * struct drm_dp_mst_topology_mgr - DisplayPort MST manager + * @dev: device pointer for adding i2c devices etc. + * @cbs: callbacks for connector addition and destruction. + * @max_dpcd_transaction_bytes - maximum number of bytes to read/write in one go. + * @aux: aux channel for the DP connector. + * @max_payloads: maximum number of payloads the GPU can generate. + * @conn_base_id: DRM connector ID this mgr is connected to. + * @down_rep_recv: msg receiver state for down replies. + * @up_req_recv: msg receiver state for up requests. + * @lock: protects mst state, primary, guid, dpcd. + * @mst_state: if this manager is enabled for an MST capable port. + * @mst_primary: pointer to the primary branch device. + * @guid_valid: GUID valid for the primary branch device. + * @guid: GUID for primary port. + * @dpcd: cache of DPCD for primary port. + * @pbn_div: PBN to slots divisor. + * + * This struct represents the toplevel displayport MST topology manager. + * There should be one instance of this for every MST capable DP connector + * on the GPU. + */ +struct drm_dp_mst_topology_mgr { + + struct device *dev; + struct drm_dp_mst_topology_cbs *cbs; + int max_dpcd_transaction_bytes; + struct drm_dp_aux *aux; /* auxch for this topology mgr to use */ + int max_payloads; + int conn_base_id; + + /* only ever accessed from the workqueue - which should be serialised */ + struct drm_dp_sideband_msg_rx down_rep_recv; + struct drm_dp_sideband_msg_rx up_req_recv; + + /* pointer to info about the initial MST device */ + struct mutex lock; /* protects mst_state + primary + guid + dpcd */ + + bool mst_state; + struct drm_dp_mst_branch *mst_primary; + /* primary MST device GUID */ + bool guid_valid; + u8 guid[16]; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 sink_count; + int pbn_div; + int total_slots; + int avail_slots; + int total_pbn; + + /* messages to be transmitted */ + /* qlock protects the upq/downq and in_progress, + the mstb tx_slots and txmsg->state once they are queued */ + struct mutex qlock; + struct list_head tx_msg_downq; + struct list_head tx_msg_upq; + bool tx_down_in_progress; + bool tx_up_in_progress; + + /* payload info + lock for it */ + struct mutex payload_lock; + struct drm_dp_vcpi **proposed_vcpis; + struct drm_dp_payload *payloads; + unsigned long payload_mask; + + wait_queue_head_t tx_waitq; + struct work_struct work; + + struct work_struct tx_work; +}; + +int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, struct device *dev, struct drm_dp_aux *aux, int max_dpcd_transaction_bytes, int max_payloads, int conn_base_id); + +void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); + + +int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state); + + +int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled); + + +enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + +struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + + +int drm_dp_calc_pbn_mode(int clock, int bpp); + + +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots); + + +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + + +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); + + +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, + int pbn); + + +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr); + + +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr); + +int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_dump_topology(struct seq_file *m, + struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr); +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr); +#endif -- cgit v0.10.2 From c7dbc6c9ae5c3baa3be755a228a349374d043b5b Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 7 Jul 2014 11:19:57 -0700 Subject: drm: Remove command line guard for universal planes Universal planes are ready to leave 'experimental' state so drop the kernel command line parameter that we've been hiding them behind. Userspace clients that wish to receive universal planes will still need to opt-in by setting the appropriate capability bit, so this should have no impact on existing userspace. Signed-off-by: Matt Roper Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 69c61f3..ad66f96 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) file_priv->stereo_allowed = req->value; break; case DRM_CLIENT_CAP_UNIVERSAL_PLANES: - if (!drm_universal_planes) - return -EINVAL; if (req->value > 1) return -EINVAL; file_priv->universal_planes = req->value; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 4e862b4..233ea20 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -37,9 +37,6 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); -/* 1 to allow user space to request universal planes (experimental) */ -unsigned int drm_universal_planes = 0; - unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ @@ -59,7 +56,6 @@ MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); -module_param_named(universal_planes, drm_universal_planes, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a134479..9b6a445 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1395,7 +1395,6 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; -extern unsigned int drm_universal_planes; extern unsigned int drm_vblank_offdelay; extern unsigned int drm_timestamp_precision; -- cgit v0.10.2 From ec5cc0f9b019af95e4571a9fa162d94294c8d90b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 12 Jun 2014 10:28:55 +0100 Subject: drm/i915: Restrict GPU boost to the RCS engine Make the assumption that media workloads are not as latency sensitive for __wait_seqno, and that upclocking the GPU does not affect the BLT engine. Under that assumption, we only wait to forcibly upclock the GPU when we are stalling for results from the render pipeline. Signed-off-by: Chris Wilson Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e85c85c..b622573 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1168,7 +1168,7 @@ static int __wait_seqno(struct intel_engine_cs *ring, u32 seqno, timeout_expire = timeout ? jiffies + timespec_to_jiffies_timeout(timeout) : 0; - if (INTEL_INFO(dev)->gen >= 6 && can_wait_boost(file_priv)) { + if (INTEL_INFO(dev)->gen >= 6 && ring->id == RCS && can_wait_boost(file_priv)) { gen6_rps_boost(dev_priv); if (file_priv) mod_delayed_work(dev_priv->wq, -- cgit v0.10.2 From 95fa2eeecfe82788b47366abf7c3a773cc187663 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 23 Jun 2014 15:46:02 +0300 Subject: drm/i915: make system freeze support depend on CONFIG_ACPI_SLEEP To achieve further power savings during system freeze (aka connected standby, or s0ix) we have to send a PCI_D1 opregion notification. As the information about the state we're entering (system freeze, suspend to ram or suspend to disk) is only available through the ACPI subsystem, make this support depend on the relevant kconfig option. Things will still work if this option isn't set, albeit with less than optimial power saving. This also fixes a compile breakage when the option is not set introduced in commit e5747e3adcd67ae27105003ec99fb58cba180105 Author: Jesse Barnes Date: Thu Jun 12 08:35:47 2014 -0700 drm/i915: send proper opregion notifications on suspend/resume Reported-by: Randy Dunlap Signed-off-by: Imre Deak Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index bc19623..23139aa 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -540,10 +540,11 @@ static int i915_drm_freeze(struct drm_device *dev) i915_save_state(dev); - if (acpi_target_system_state() >= ACPI_STATE_S3) - opregion_target_state = PCI_D3cold; - else + opregion_target_state = PCI_D3cold; +#if IS_ENABLED(CONFIG_ACPI_SLEEP) + if (acpi_target_system_state() < ACPI_STATE_S3) opregion_target_state = PCI_D1; +#endif intel_opregion_notify_adapter(dev, opregion_target_state); intel_uncore_forcewake_reset(dev, false); -- cgit v0.10.2 From aa0c13daada955ca86dff0b99428929841bfdbc6 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:27:58 +0100 Subject: drm/i915: Extract context backing object allocation This is preparatory work for Execlists: we plan to use it later to allocate our own context objects (since Logical Ring Contexts do not have the same kind of backing objects). No functional changes. Reviewed-by: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 0656121..e664e86 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -198,6 +198,36 @@ void i915_gem_context_free(struct kref *ctx_ref) kfree(ctx); } +static struct drm_i915_gem_object * +i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) +{ + struct drm_i915_gem_object *obj; + int ret; + + obj = i915_gem_alloc_object(dev, size); + if (obj == NULL) + return ERR_PTR(-ENOMEM); + + /* + * Try to make the context utilize L3 as well as LLC. + * + * On VLV we don't have L3 controls in the PTEs so we + * shouldn't touch the cache level, especially as that + * would make the object snooped which might have a + * negative performance impact. + */ + if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) { + ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC); + /* Failure shouldn't ever happen this early */ + if (WARN_ON(ret)) { + drm_gem_object_unreference(&obj->base); + return ERR_PTR(ret); + } + } + + return obj; +} + static struct i915_hw_ppgtt * create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx) { @@ -234,27 +264,13 @@ __create_hw_context(struct drm_device *dev, list_add_tail(&ctx->link, &dev_priv->context_list); if (dev_priv->hw_context_size) { - ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size); - if (ctx->obj == NULL) { - ret = -ENOMEM; + struct drm_i915_gem_object *obj = + i915_gem_alloc_context_obj(dev, dev_priv->hw_context_size); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); goto err_out; } - - /* - * Try to make the context utilize L3 as well as LLC. - * - * On VLV we don't have L3 controls in the PTEs so we - * shouldn't touch the cache level, especially as that - * would make the object snooped which might have a - * negative performance impact. - */ - if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) { - ret = i915_gem_object_set_cache_level(ctx->obj, - I915_CACHE_L3_LLC); - /* Failure shouldn't ever happen this early */ - if (WARN_ON(ret)) - goto err_out; - } + ctx->obj = obj; } /* Default context will never have a file_priv */ -- cgit v0.10.2 From ea0c76f8c306716a301abbf28699c4ca0a102bed Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:27:59 +0100 Subject: drm/i915: Emphasize that ctx->obj & ctx->is_initialized refer to the legacy rcs ctx We have already advanced that Logical Ring Contexts have their own kind of backing objects, but everything will be better explained in the Execlists series. For now, suffice it to say that the current backing object is only ever used with the render ring, so we're making this fact more explicit (which is a good reason on its own). As for the is_initialized flag, we only use to signify that the render state has been initialized (a.k.a. golden context, a.k.a. null context). It doesn't mean anything for the other engines, so make that distinction obvious. Done with the following Coccinelle patch (plus manual frobbing of the struct): @@ struct intel_context c; @@ - (c).obj + c.legacy_hw_ctx.rcs_state @@ struct intel_context *c; @@ - (c)->obj + c->legacy_hw_ctx.rcs_state @@ struct intel_context c; @@ - (c).is_initialized + c.legacy_hw_ctx.initialized @@ struct intel_context *c; @@ - (c)->is_initialized + c->legacy_hw_ctx.initialized This Execlists prep-work patch has been suggested by Chris Wilson and Daniel Vetter separately. Initially, it was two separate patches: drm/i915: Rename ctx->obj to ctx->rcs_state drm/i915: Make it obvious that ctx->id is merely a user handle Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson [danvet: s/id/is_initialized/ to fix the subject and resolve a conflict in i915_gem_context_reset. Also introduce a new lctx local variable to avoid overtly long lines.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index c1b88a8..31d5cdf3 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -176,7 +176,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) static void describe_ctx(struct seq_file *m, struct intel_context *ctx) { - seq_putc(m, ctx->is_initialized ? 'I' : 'i'); + seq_putc(m, ctx->legacy_hw_ctx.initialized ? 'I' : 'i'); seq_putc(m, ctx->remap_slice ? 'R' : 'r'); seq_putc(m, ' '); } @@ -1753,7 +1753,7 @@ static int i915_context_status(struct seq_file *m, void *unused) } list_for_each_entry(ctx, &dev_priv->context_list, link) { - if (ctx->obj == NULL) + if (ctx->legacy_hw_ctx.rcs_state == NULL) continue; seq_puts(m, "HW context "); @@ -1762,7 +1762,7 @@ static int i915_context_status(struct seq_file *m, void *unused) if (ring->default_context == ctx) seq_printf(m, "(default context %s) ", ring->name); - describe_obj(m, ctx->obj); + describe_obj(m, ctx->legacy_hw_ctx.rcs_state); seq_putc(m, '\n'); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 44f26eb..eeb3e4c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -589,13 +589,16 @@ struct i915_ctx_hang_stats { struct intel_context { struct kref ref; int id; - bool is_initialized; uint8_t remap_slice; struct drm_i915_file_private *file_priv; - struct drm_i915_gem_object *obj; struct i915_ctx_hang_stats hang_stats; struct i915_address_space *vm; + struct { + struct drm_i915_gem_object *rcs_state; + bool initialized; + } legacy_hw_ctx; + struct list_head link; }; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index e664e86..6b76482 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -182,14 +182,14 @@ void i915_gem_context_free(struct kref *ctx_ref) typeof(*ctx), ref); struct i915_hw_ppgtt *ppgtt = NULL; - if (ctx->obj) { + if (ctx->legacy_hw_ctx.rcs_state) { /* We refcount even the aliasing PPGTT to keep the code symmetric */ - if (USES_PPGTT(ctx->obj->base.dev)) + if (USES_PPGTT(ctx->legacy_hw_ctx.rcs_state->base.dev)) ppgtt = ctx_to_ppgtt(ctx); /* XXX: Free up the object before tearing down the address space, in * case we're bound in the PPGTT */ - drm_gem_object_unreference(&ctx->obj->base); + drm_gem_object_unreference(&ctx->legacy_hw_ctx.rcs_state->base); } if (ppgtt) @@ -270,7 +270,7 @@ __create_hw_context(struct drm_device *dev, ret = PTR_ERR(obj); goto err_out; } - ctx->obj = obj; + ctx->legacy_hw_ctx.rcs_state = obj; } /* Default context will never have a file_priv */ @@ -317,7 +317,7 @@ i915_gem_create_context(struct drm_device *dev, if (IS_ERR(ctx)) return ctx; - if (is_global_default_ctx && ctx->obj) { + if (is_global_default_ctx && ctx->legacy_hw_ctx.rcs_state) { /* We may need to do things with the shrinker which * require us to immediately switch back to the default * context. This can cause a problem as pinning the @@ -325,7 +325,7 @@ i915_gem_create_context(struct drm_device *dev, * be available. To avoid this we always pin the default * context. */ - ret = i915_gem_obj_ggtt_pin(ctx->obj, + ret = i915_gem_obj_ggtt_pin(ctx->legacy_hw_ctx.rcs_state, get_context_alignment(dev), 0); if (ret) { DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret); @@ -365,8 +365,8 @@ i915_gem_create_context(struct drm_device *dev, return ctx; err_unpin: - if (is_global_default_ctx && ctx->obj) - i915_gem_object_ggtt_unpin(ctx->obj); + if (is_global_default_ctx && ctx->legacy_hw_ctx.rcs_state) + i915_gem_object_ggtt_unpin(ctx->legacy_hw_ctx.rcs_state); err_destroy: i915_gem_context_unreference(ctx); return ERR_PTR(ret); @@ -382,26 +382,27 @@ void i915_gem_context_reset(struct drm_device *dev) for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; struct intel_context *dctx = ring->default_context; + struct intel_context *lctx = ring->last_context; /* Do a fake switch to the default context */ - if (ring->last_context == dctx) + if (lctx == dctx) continue; - if (!ring->last_context) + if (!lctx) continue; - if (dctx->obj && i == RCS) { - WARN_ON(i915_gem_obj_ggtt_pin(dctx->obj, + if (dctx->legacy_hw_ctx.rcs_state && i == RCS) { + WARN_ON(i915_gem_obj_ggtt_pin(dctx->legacy_hw_ctx.rcs_state, get_context_alignment(dev), 0)); /* Fake a finish/inactive */ - dctx->obj->base.write_domain = 0; - dctx->obj->active = 0; + dctx->legacy_hw_ctx.rcs_state->base.write_domain = 0; + dctx->legacy_hw_ctx.rcs_state->active = 0; } - if (ring->last_context->obj && i == RCS) - i915_gem_object_ggtt_unpin(ring->last_context->obj); + if (lctx->legacy_hw_ctx.rcs_state && i == RCS) + i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - i915_gem_context_unreference(ring->last_context); + i915_gem_context_unreference(lctx); i915_gem_context_reference(dctx); ring->last_context = dctx; } @@ -448,7 +449,7 @@ void i915_gem_context_fini(struct drm_device *dev) struct intel_context *dctx = dev_priv->ring[RCS].default_context; int i; - if (dctx->obj) { + if (dctx->legacy_hw_ctx.rcs_state) { /* The only known way to stop the gpu from accessing the hw context is * to reset it. Do this as the very last operation to avoid confusing * other code, leading to spurious errors. */ @@ -463,13 +464,13 @@ void i915_gem_context_fini(struct drm_device *dev) WARN_ON(!dev_priv->ring[RCS].last_context); if (dev_priv->ring[RCS].last_context == dctx) { /* Fake switch to NULL context */ - WARN_ON(dctx->obj->active); - i915_gem_object_ggtt_unpin(dctx->obj); + WARN_ON(dctx->legacy_hw_ctx.rcs_state->active); + i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state); i915_gem_context_unreference(dctx); dev_priv->ring[RCS].last_context = NULL; } - i915_gem_object_ggtt_unpin(dctx->obj); + i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state); } for (i = 0; i < I915_NUM_RINGS; i++) { @@ -589,7 +590,7 @@ mi_set_context(struct intel_engine_cs *ring, intel_ring_emit(ring, MI_NOOP); intel_ring_emit(ring, MI_SET_CONTEXT); - intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->obj) | + intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->legacy_hw_ctx.rcs_state) | MI_MM_SPACE_GTT | MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN | @@ -621,8 +622,8 @@ static int do_switch(struct intel_engine_cs *ring, int ret, i; if (from != NULL && ring == &dev_priv->ring[RCS]) { - BUG_ON(from->obj == NULL); - BUG_ON(!i915_gem_obj_is_pinned(from->obj)); + BUG_ON(from->legacy_hw_ctx.rcs_state == NULL); + BUG_ON(!i915_gem_obj_is_pinned(from->legacy_hw_ctx.rcs_state)); } if (from == to && !to->remap_slice) @@ -630,7 +631,7 @@ static int do_switch(struct intel_engine_cs *ring, /* Trying to pin first makes error handling easier. */ if (ring == &dev_priv->ring[RCS]) { - ret = i915_gem_obj_ggtt_pin(to->obj, + ret = i915_gem_obj_ggtt_pin(to->legacy_hw_ctx.rcs_state, get_context_alignment(ring->dev), 0); if (ret) return ret; @@ -663,17 +664,17 @@ static int do_switch(struct intel_engine_cs *ring, * * XXX: We need a real interface to do this instead of trickery. */ - ret = i915_gem_object_set_to_gtt_domain(to->obj, false); + ret = i915_gem_object_set_to_gtt_domain(to->legacy_hw_ctx.rcs_state, false); if (ret) goto unpin_out; - if (!to->obj->has_global_gtt_mapping) { - struct i915_vma *vma = i915_gem_obj_to_vma(to->obj, + if (!to->legacy_hw_ctx.rcs_state->has_global_gtt_mapping) { + struct i915_vma *vma = i915_gem_obj_to_vma(to->legacy_hw_ctx.rcs_state, &dev_priv->gtt.base); - vma->bind_vma(vma, to->obj->cache_level, GLOBAL_BIND); + vma->bind_vma(vma, to->legacy_hw_ctx.rcs_state->cache_level, GLOBAL_BIND); } - if (!to->is_initialized || i915_gem_context_is_default(to)) + if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to)) hw_flags |= MI_RESTORE_INHIBIT; ret = mi_set_context(ring, to, hw_flags); @@ -699,8 +700,8 @@ static int do_switch(struct intel_engine_cs *ring, * MI_SET_CONTEXT instead of when the next seqno has completed. */ if (from != NULL) { - from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; - i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->obj), ring); + from->legacy_hw_ctx.rcs_state->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; + i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->legacy_hw_ctx.rcs_state), ring); /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the * whole damn pipeline, we don't need to explicitly mark the * object dirty. The only exception is that the context must be @@ -708,16 +709,16 @@ static int do_switch(struct intel_engine_cs *ring, * able to defer doing this until we know the object would be * swapped, but there is no way to do that yet. */ - from->obj->dirty = 1; - BUG_ON(from->obj->ring != ring); + from->legacy_hw_ctx.rcs_state->dirty = 1; + BUG_ON(from->legacy_hw_ctx.rcs_state->ring != ring); /* obj is kept alive until the next request by its active ref */ - i915_gem_object_ggtt_unpin(from->obj); + i915_gem_object_ggtt_unpin(from->legacy_hw_ctx.rcs_state); i915_gem_context_unreference(from); } - uninitialized = !to->is_initialized && from == NULL; - to->is_initialized = true; + uninitialized = !to->legacy_hw_ctx.initialized && from == NULL; + to->legacy_hw_ctx.initialized = true; done: i915_gem_context_reference(to); @@ -733,7 +734,7 @@ done: unpin_out: if (ring->id == RCS) - i915_gem_object_ggtt_unpin(to->obj); + i915_gem_object_ggtt_unpin(to->legacy_hw_ctx.rcs_state); return ret; } @@ -754,7 +755,7 @@ int i915_switch_context(struct intel_engine_cs *ring, WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); - if (to->obj == NULL) { /* We have the fake context */ + if (to->legacy_hw_ctx.rcs_state == NULL) { /* We have the fake context */ if (to != ring->last_context) { i915_gem_context_reference(to); if (ring->last_context) -- cgit v0.10.2 From 821d66dd7ca3f59f68478d0d0ee1f28a1de74267 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:00 +0100 Subject: drm/i915: Emphasize that ctx->id is merely a user handle This is an Execlists preparatory patch, since they make context ID become an overloaded term: - In the software, it was used to distinguish which context userspace was trying to use. - In the BSpec, the term is used to describe the 20-bits long field the hardware uses to it to discriminate the contexts that are submitted to the ELSP and inform the driver about their current status (via Context Switch Interrupts and Context Status Buffers). Initially, I tried to make the different meanings converge, but it proved impossible: - The software ctx->id is per-filp, while the hardware one needs to be globally unique. - Also, we multiplex several backing states objects per intel_context, and all of them need unique HW IDs. - I tried adding a per-filp ID and then composing the HW context ID as: ctx->id + file_priv->id + ring->id, but the fact that the hardware only uses 20-bits means we have to artificially limit the number of filps or contexts the userspace can create. The ctx->user_handle renaming bits are done with this Cocci patch (plus manual frobbing of the struct declaration): @@ struct intel_context c; @@ - (c).id + c.user_handle @@ struct intel_context *c; @@ - (c)->id + c->user_handle Also, while we are at it, s/DEFAULT_CONTEXT_ID/DEFAULT_CONTEXT_HANDLE and change the type to unsigned 32 bits. v2: s/handle/user_handle and change the type to uint32_t as suggested by Chris Wilson. Reviewed-by: Jesse Barnes (v1) Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 31d5cdf3..b3b56c4 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1876,7 +1876,7 @@ static int per_file_ctx(int id, void *ptr, void *data) if (i915_gem_context_is_default(ctx)) seq_puts(m, " default context:\n"); else - seq_printf(m, " context %d:\n", ctx->id); + seq_printf(m, " context %d:\n", ctx->user_handle); ppgtt->debug_dump(ppgtt, m); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index eeb3e4c..dcef38b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -585,10 +585,10 @@ struct i915_ctx_hang_stats { }; /* This must match up with the value previously used for execbuf2.rsvd1. */ -#define DEFAULT_CONTEXT_ID 0 +#define DEFAULT_CONTEXT_HANDLE 0 struct intel_context { struct kref ref; - int id; + int user_handle; uint8_t remap_slice; struct drm_i915_file_private *file_priv; struct i915_ctx_hang_stats hang_stats; @@ -2469,7 +2469,7 @@ static inline void i915_gem_context_unreference(struct intel_context *ctx) static inline bool i915_gem_context_is_default(const struct intel_context *c) { - return c->id == DEFAULT_CONTEXT_ID; + return c->user_handle == DEFAULT_CONTEXT_HANDLE; } int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 6b76482..de72a28 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -276,14 +276,14 @@ __create_hw_context(struct drm_device *dev, /* Default context will never have a file_priv */ if (file_priv != NULL) { ret = idr_alloc(&file_priv->context_idr, ctx, - DEFAULT_CONTEXT_ID, 0, GFP_KERNEL); + DEFAULT_CONTEXT_HANDLE, 0, GFP_KERNEL); if (ret < 0) goto err_out; } else - ret = DEFAULT_CONTEXT_ID; + ret = DEFAULT_CONTEXT_HANDLE; ctx->file_priv = file_priv; - ctx->id = ret; + ctx->user_handle = ret; /* NB: Mark all slices as needing a remap so that when the context first * loads it will restore whatever remap state already exists. If there * is no remap info, it will be a NOP. */ @@ -793,7 +793,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, if (IS_ERR(ctx)) return PTR_ERR(ctx); - args->ctx_id = ctx->id; + args->ctx_id = ctx->user_handle; DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id); return 0; @@ -807,7 +807,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct intel_context *ctx; int ret; - if (args->ctx_id == DEFAULT_CONTEXT_ID) + if (args->ctx_id == DEFAULT_CONTEXT_HANDLE) return -ENOENT; ret = i915_mutex_lock_interruptible(dev); @@ -820,7 +820,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, return PTR_ERR(ctx); } - idr_remove(&ctx->file_priv->context_idr, ctx->id); + idr_remove(&ctx->file_priv->context_idr, ctx->user_handle); i915_gem_context_unreference(ctx); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index d815ef5..c97178e 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -938,7 +938,7 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file, struct intel_context *ctx = NULL; struct i915_ctx_hang_stats *hs; - if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_ID) + if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_HANDLE) return ERR_PTR(-EINVAL); ctx = i915_gem_context_get(file->driver_priv, ctx_id); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 29145df..e0f0843 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -1010,7 +1010,7 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev, if (args->flags || args->pad) return -EINVAL; - if (args->ctx_id == DEFAULT_CONTEXT_ID && !capable(CAP_SYS_ADMIN)) + if (args->ctx_id == DEFAULT_CONTEXT_HANDLE && !capable(CAP_SYS_ADMIN)) return -EPERM; ret = mutex_lock_interruptible(&dev->struct_mutex); -- cgit v0.10.2 From 31b7a88d354e5d7f7d2e68818e4bd8f115ab1ba6 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:01 +0100 Subject: drm/i915: Add kerneldoc comments to the intel_context struct A bit of background on the context elements. Cc: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson [danvet: Appease checkpatch.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dcef38b..8fa8172 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -586,6 +586,23 @@ struct i915_ctx_hang_stats { /* This must match up with the value previously used for execbuf2.rsvd1. */ #define DEFAULT_CONTEXT_HANDLE 0 +/** + * struct intel_context - as the name implies, represents a context. + * @ref: reference count. + * @user_handle: userspace tracking identity for this context. + * @remap_slice: l3 row remapping information. + * @file_priv: filp associated with this context (NULL for global default + * context). + * @hang_stats: information about the role of this context in possible GPU + * hangs. + * @vm: virtual memory space used by this context. + * @legacy_hw_ctx: render context backing object and whether it is correctly + * initialized (legacy ring submission mechanism only). + * @link: link in the global list of contexts. + * + * Contexts are memory images used by the hardware to store copies of their + * internal state. + */ struct intel_context { struct kref ref; int user_handle; -- cgit v0.10.2 From 2919d2913c515fcfef6d50a46aae31128d454ce9 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:02 +0100 Subject: drm/i915: Extract ringbuffer destroy & generalize alloc to take a ringbuf More prep work: with Execlists, we are going to start creating a lot of extra ringbuffers soon, so these functions are handy. No functional changes. v2: rename allocate/destroy_ring_buffer to alloc/destroy_ringbuffer_obj because the name is more meaningful and to mirror a similar function in the context world: i915_gem_alloc_context_obj(). Change suggested by Brad Volkin. Reviewed-by: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index e18ed05..40b17f2 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1482,15 +1482,25 @@ static int init_phys_status_page(struct intel_engine_cs *ring) return 0; } -static int allocate_ring_buffer(struct intel_engine_cs *ring) +static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) +{ + if (!ringbuf->obj) + return; + + iounmap(ringbuf->virtual_start); + i915_gem_object_ggtt_unpin(ringbuf->obj); + drm_gem_object_unreference(&ringbuf->obj->base); + ringbuf->obj = NULL; +} + +static int intel_alloc_ringbuffer_obj(struct drm_device *dev, + struct intel_ringbuffer *ringbuf) { - struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_ringbuffer *ringbuf = ring->buffer; struct drm_i915_gem_object *obj; int ret; - if (intel_ring_initialized(ring)) + if (ringbuf->obj) return 0; obj = NULL; @@ -1562,7 +1572,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto error; } - ret = allocate_ring_buffer(ring); + ret = intel_alloc_ringbuffer_obj(dev, ringbuf); if (ret) { DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", ring->name, ret); goto error; @@ -1603,11 +1613,7 @@ void intel_cleanup_ring_buffer(struct intel_engine_cs *ring) intel_stop_ring_buffer(ring); WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0); - iounmap(ringbuf->virtual_start); - - i915_gem_object_ggtt_unpin(ringbuf->obj); - drm_gem_object_unreference(&ringbuf->obj->base); - ringbuf->obj = NULL; + intel_destroy_ringbuffer_obj(ringbuf); ring->preallocated_lazy_request = NULL; ring->outstanding_lazy_seqno = 0; -- cgit v0.10.2 From 64c58f2c48da85223f0db438fe4854bc3ef24353 Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:03 +0100 Subject: drm/i915: Generalize ring_space to take a ringbuf It's simple enough that it doesn't need to know anything about the engine. Trivial change. Reviewed-by: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 40b17f2..738140c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -48,9 +48,8 @@ static inline int __ring_space(int head, int tail, int size) return space; } -static inline int ring_space(struct intel_engine_cs *ring) +static inline int ring_space(struct intel_ringbuffer *ringbuf) { - struct intel_ringbuffer *ringbuf = ring->buffer; return __ring_space(ringbuf->head & HEAD_ADDR, ringbuf->tail, ringbuf->size); } @@ -545,7 +544,7 @@ static int init_ring_common(struct intel_engine_cs *ring) else { ringbuf->head = I915_READ_HEAD(ring); ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR; - ringbuf->space = ring_space(ring); + ringbuf->space = ring_space(ringbuf); ringbuf->last_retired_head = -1; } @@ -1639,7 +1638,7 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n) ringbuf->head = ringbuf->last_retired_head; ringbuf->last_retired_head = -1; - ringbuf->space = ring_space(ring); + ringbuf->space = ring_space(ringbuf); if (ringbuf->space >= n) return 0; } @@ -1662,7 +1661,7 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n) ringbuf->head = ringbuf->last_retired_head; ringbuf->last_retired_head = -1; - ringbuf->space = ring_space(ring); + ringbuf->space = ring_space(ringbuf); return 0; } @@ -1691,7 +1690,7 @@ static int ring_wait_for_space(struct intel_engine_cs *ring, int n) trace_i915_ring_wait_begin(ring); do { ringbuf->head = I915_READ_HEAD(ring); - ringbuf->space = ring_space(ring); + ringbuf->space = ring_space(ringbuf); if (ringbuf->space >= n) { ret = 0; break; @@ -1743,7 +1742,7 @@ static int intel_wrap_ring_buffer(struct intel_engine_cs *ring) iowrite32(MI_NOOP, virt++); ringbuf->tail = 0; - ringbuf->space = ring_space(ring); + ringbuf->space = ring_space(ringbuf); return 0; } -- cgit v0.10.2 From 1b5d063fafeb01569ca988b1d4f45f64c67d2e5d Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:04 +0100 Subject: drm/i915: Generalize intel_ring_get_tail to take a ringbuf Again, it's low-level enough to simply take a ringbuf and nothing else. Trivial change. Reviewed-by: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b622573..e5d4d73 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2330,7 +2330,7 @@ int __i915_add_request(struct intel_engine_cs *ring, u32 request_ring_position, request_start; int ret; - request_start = intel_ring_get_tail(ring); + request_start = intel_ring_get_tail(ring->buffer); /* * Emit any outstanding flushes - execbuf can fail to emit the flush * after having emitted the batchbuffer command. Hence we need to fix @@ -2351,7 +2351,7 @@ int __i915_add_request(struct intel_engine_cs *ring, * GPU processing the request, we never over-estimate the * position of the head. */ - request_ring_position = intel_ring_get_tail(ring); + request_ring_position = intel_ring_get_tail(ring->buffer); ret = ring->add_request(ring); if (ret) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 17ef9cd..ed59410 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -386,9 +386,9 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev); u64 intel_ring_get_active_head(struct intel_engine_cs *ring); void intel_ring_setup_status_page(struct intel_engine_cs *ring); -static inline u32 intel_ring_get_tail(struct intel_engine_cs *ring) +static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf) { - return ring->buffer->tail; + return ringbuf->tail; } static inline u32 intel_ring_get_seqno(struct intel_engine_cs *ring) -- cgit v0.10.2 From 78382593e921c88371abd019aca8978db3248a8f Mon Sep 17 00:00:00 2001 From: Oscar Mateo Date: Thu, 3 Jul 2014 16:28:05 +0100 Subject: drm/i915: Extract the actual workload submission mechanism from execbuffer So that we isolate the legacy ringbuffer submission mechanism, which becomes a good candidate to be abstracted away. This is prep-work for Execlists (which will its own workload submission mechanism). No functional changes. Reviewed-by: Jesse Barnes Signed-off-by: Oscar Mateo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index c97178e..60998fc 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1026,6 +1026,163 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev, return 0; } +static int +legacy_ringbuffer_submission(struct drm_device *dev, struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags) +{ + struct drm_clip_rect *cliprects = NULL; + struct drm_i915_private *dev_priv = dev->dev_private; + u64 exec_len; + int instp_mode; + u32 instp_mask; + int i, ret = 0; + + if (args->num_cliprects != 0) { + if (ring != &dev_priv->ring[RCS]) { + DRM_DEBUG("clip rectangles are only valid with the render ring\n"); + return -EINVAL; + } + + if (INTEL_INFO(dev)->gen >= 5) { + DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); + return -EINVAL; + } + + if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) { + DRM_DEBUG("execbuf with %u cliprects\n", + args->num_cliprects); + return -EINVAL; + } + + cliprects = kcalloc(args->num_cliprects, + sizeof(*cliprects), + GFP_KERNEL); + if (cliprects == NULL) { + ret = -ENOMEM; + goto error; + } + + if (copy_from_user(cliprects, + to_user_ptr(args->cliprects_ptr), + sizeof(*cliprects)*args->num_cliprects)) { + ret = -EFAULT; + goto error; + } + } else { + if (args->DR4 == 0xffffffff) { + DRM_DEBUG("UXA submitting garbage DR4, fixing up\n"); + args->DR4 = 0; + } + + if (args->DR1 || args->DR4 || args->cliprects_ptr) { + DRM_DEBUG("0 cliprects but dirt in cliprects fields\n"); + return -EINVAL; + } + } + + ret = i915_gem_execbuffer_move_to_gpu(ring, vmas); + if (ret) + goto error; + + ret = i915_switch_context(ring, ctx); + if (ret) + goto error; + + instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK; + instp_mask = I915_EXEC_CONSTANTS_MASK; + switch (instp_mode) { + case I915_EXEC_CONSTANTS_REL_GENERAL: + case I915_EXEC_CONSTANTS_ABSOLUTE: + case I915_EXEC_CONSTANTS_REL_SURFACE: + if (instp_mode != 0 && ring != &dev_priv->ring[RCS]) { + DRM_DEBUG("non-0 rel constants mode on non-RCS\n"); + ret = -EINVAL; + goto error; + } + + if (instp_mode != dev_priv->relative_constants_mode) { + if (INTEL_INFO(dev)->gen < 4) { + DRM_DEBUG("no rel constants on pre-gen4\n"); + ret = -EINVAL; + goto error; + } + + if (INTEL_INFO(dev)->gen > 5 && + instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) { + DRM_DEBUG("rel surface constants mode invalid on gen5+\n"); + ret = -EINVAL; + goto error; + } + + /* The HW changed the meaning on this bit on gen6 */ + if (INTEL_INFO(dev)->gen >= 6) + instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; + } + break; + default: + DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode); + ret = -EINVAL; + goto error; + } + + if (ring == &dev_priv->ring[RCS] && + instp_mode != dev_priv->relative_constants_mode) { + ret = intel_ring_begin(ring, 4); + if (ret) + goto error; + + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit(ring, INSTPM); + intel_ring_emit(ring, instp_mask << 16 | instp_mode); + intel_ring_advance(ring); + + dev_priv->relative_constants_mode = instp_mode; + } + + if (args->flags & I915_EXEC_GEN7_SOL_RESET) { + ret = i915_reset_gen7_sol_offsets(dev, ring); + if (ret) + goto error; + } + + exec_len = args->batch_len; + if (cliprects) { + for (i = 0; i < args->num_cliprects; i++) { + ret = i915_emit_box(dev, &cliprects[i], + args->DR1, args->DR4); + if (ret) + goto error; + + ret = ring->dispatch_execbuffer(ring, + exec_start, exec_len, + flags); + if (ret) + goto error; + } + } else { + ret = ring->dispatch_execbuffer(ring, + exec_start, exec_len, + flags); + if (ret) + return ret; + } + + trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); + + i915_gem_execbuffer_move_to_active(vmas, ring); + i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj); + +error: + kfree(cliprects); + return ret; +} + /** * Find one BSD ring to dispatch the corresponding BSD command. * The Ring ID is returned. @@ -1085,14 +1242,13 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_private *dev_priv = dev->dev_private; struct eb_vmas *eb; struct drm_i915_gem_object *batch_obj; - struct drm_clip_rect *cliprects = NULL; struct intel_engine_cs *ring; struct intel_context *ctx; struct i915_address_space *vm; const u32 ctx_id = i915_execbuffer2_get_context_id(*args); - u64 exec_start = args->batch_start_offset, exec_len; - u32 mask, flags; - int ret, mode, i; + u64 exec_start = args->batch_start_offset; + u32 flags; + int ret; bool need_relocs; if (!i915_gem_check_execbuffer(args)) @@ -1136,87 +1292,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, return -EINVAL; } - mode = args->flags & I915_EXEC_CONSTANTS_MASK; - mask = I915_EXEC_CONSTANTS_MASK; - switch (mode) { - case I915_EXEC_CONSTANTS_REL_GENERAL: - case I915_EXEC_CONSTANTS_ABSOLUTE: - case I915_EXEC_CONSTANTS_REL_SURFACE: - if (mode != 0 && ring != &dev_priv->ring[RCS]) { - DRM_DEBUG("non-0 rel constants mode on non-RCS\n"); - return -EINVAL; - } - - if (mode != dev_priv->relative_constants_mode) { - if (INTEL_INFO(dev)->gen < 4) { - DRM_DEBUG("no rel constants on pre-gen4\n"); - return -EINVAL; - } - - if (INTEL_INFO(dev)->gen > 5 && - mode == I915_EXEC_CONSTANTS_REL_SURFACE) { - DRM_DEBUG("rel surface constants mode invalid on gen5+\n"); - return -EINVAL; - } - - /* The HW changed the meaning on this bit on gen6 */ - if (INTEL_INFO(dev)->gen >= 6) - mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; - } - break; - default: - DRM_DEBUG("execbuf with unknown constants: %d\n", mode); - return -EINVAL; - } - if (args->buffer_count < 1) { DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); return -EINVAL; } - if (args->num_cliprects != 0) { - if (ring != &dev_priv->ring[RCS]) { - DRM_DEBUG("clip rectangles are only valid with the render ring\n"); - return -EINVAL; - } - - if (INTEL_INFO(dev)->gen >= 5) { - DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); - return -EINVAL; - } - - if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) { - DRM_DEBUG("execbuf with %u cliprects\n", - args->num_cliprects); - return -EINVAL; - } - - cliprects = kcalloc(args->num_cliprects, - sizeof(*cliprects), - GFP_KERNEL); - if (cliprects == NULL) { - ret = -ENOMEM; - goto pre_mutex_err; - } - - if (copy_from_user(cliprects, - to_user_ptr(args->cliprects_ptr), - sizeof(*cliprects)*args->num_cliprects)) { - ret = -EFAULT; - goto pre_mutex_err; - } - } else { - if (args->DR4 == 0xffffffff) { - DRM_DEBUG("UXA submitting garbage DR4, fixing up\n"); - args->DR4 = 0; - } - - if (args->DR1 || args->DR4 || args->cliprects_ptr) { - DRM_DEBUG("0 cliprects but dirt in cliprects fields\n"); - return -EINVAL; - } - } - intel_runtime_pm_get(dev_priv); ret = i915_mutex_lock_interruptible(dev); @@ -1320,63 +1400,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, else exec_start += i915_gem_obj_offset(batch_obj, vm); - ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->vmas); + ret = legacy_ringbuffer_submission(dev, file, ring, ctx, + args, &eb->vmas, batch_obj, exec_start, flags); if (ret) goto err; - ret = i915_switch_context(ring, ctx); - if (ret) - goto err; - - if (ring == &dev_priv->ring[RCS] && - mode != dev_priv->relative_constants_mode) { - ret = intel_ring_begin(ring, 4); - if (ret) - goto err; - - intel_ring_emit(ring, MI_NOOP); - intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit(ring, INSTPM); - intel_ring_emit(ring, mask << 16 | mode); - intel_ring_advance(ring); - - dev_priv->relative_constants_mode = mode; - } - - if (args->flags & I915_EXEC_GEN7_SOL_RESET) { - ret = i915_reset_gen7_sol_offsets(dev, ring); - if (ret) - goto err; - } - - - exec_len = args->batch_len; - if (cliprects) { - for (i = 0; i < args->num_cliprects; i++) { - ret = i915_emit_box(dev, &cliprects[i], - args->DR1, args->DR4); - if (ret) - goto err; - - ret = ring->dispatch_execbuffer(ring, - exec_start, exec_len, - flags); - if (ret) - goto err; - } - } else { - ret = ring->dispatch_execbuffer(ring, - exec_start, exec_len, - flags); - if (ret) - goto err; - } - - trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); - - i915_gem_execbuffer_move_to_active(&eb->vmas, ring); - i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj); - err: /* the request owns the ref now */ i915_gem_context_unreference(ctx); @@ -1385,8 +1413,6 @@ err: mutex_unlock(&dev->struct_mutex); pre_mutex_err: - kfree(cliprects); - /* intel_gpu_busy should also get a ref, so it will free when the device * is really idle. */ intel_runtime_pm_put(dev_priv); -- cgit v0.10.2 From 2a92d5bca1999b69c78f3c3e97b5484985b094b9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jul 2014 10:40:29 +0100 Subject: drm/i915: Disable RCS flips on Ivybridge We currently see random GPU hangs when using RCS flips with multiple pipes on Ivybridge. Now that we have mmio flips, we can fairly cheaply fallback to using CPU driven flips instead. Signed-off-by: Chris Wilson Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=77104 Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8043e4b..8728c88 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9658,6 +9658,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev)) { ring = &dev_priv->ring[BCS]; + } else if (IS_IVYBRIDGE(dev)) { + ring = &dev_priv->ring[BCS]; } else if (INTEL_INFO(dev)->gen >= 7) { ring = obj->ring; if (ring == NULL || ring->id != RCS) -- cgit v0.10.2 From 8e09bf837f8c6b09784bf22c3a8c597df3c20b79 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jul 2014 10:40:30 +0100 Subject: drm/i915: Use mmio flips to change tiling mode on Baytrail For whatever reason, MI_DISPLAY_FLIP fails to change tiling mode on Baytrail, so just use CPU driven mmio flips instead. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76176 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8728c88..8e738d4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9435,6 +9435,9 @@ static bool use_mmio_flip(struct intel_engine_cs *ring, * So using MMIO flips there would disrupt this mechanism. */ + if (ring == NULL) + return true; + if (INTEL_INFO(ring->dev)->gen < 5) return false; @@ -9658,6 +9661,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev)) { ring = &dev_priv->ring[BCS]; + if (obj->tiling_mode != work->old_fb_obj->tiling_mode) + /* vlv: DISPLAY_FLIP fails to change tiling */ + ring = NULL; } else if (IS_IVYBRIDGE(dev)) { ring = &dev_priv->ring[BCS]; } else if (INTEL_INFO(dev)->gen >= 7) { -- cgit v0.10.2 From 31685c258e0b0ad6aa486c5ec001382cf8a64212 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 3 Jul 2014 17:33:01 -0400 Subject: drm/i915/vlv: WA for Turbo and RC6 to work together. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With RC6 enabled, BYT has an HW issue in determining the right Gfx busyness. WA for Turbo + RC6: Use SW based Gfx busy-ness detection to decide on increasing/decreasing the freq. This logic will monitor C0 counters of render/media power-wells over EI period and takes necessary action based on these values v2: Refactor duplicate code. (Ville) v3: Reformat the comments. (Ville) v4: Enable required counters and remove unwanted code (Ville) v5: Added frequency change acceleration support and remove kernel-doc style comments. (Ville) v6: Updated comment section and Fix w/a comment. (Ville) Signed-off-by: Deepak S Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8fa8172..41191f1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -902,6 +902,12 @@ struct vlv_s0ix_state { u32 clock_gate_dis2; }; +struct intel_rps_ei_calc { + u32 cz_ts_ei; + u32 render_ei_c0; + u32 media_ei_c0; +}; + struct intel_gen6_power_mgmt { /* work and pm_iir are protected by dev_priv->irq_lock */ struct work_struct work; @@ -926,6 +932,8 @@ struct intel_gen6_power_mgmt { u8 rp1_freq; /* "less than" RP0 power/freqency */ u8 rp0_freq; /* Non-overclocked max frequency. */ + u32 ei_interrupt_count; + int last_adj; enum { LOW_POWER, BETWEEN, HIGH_POWER } power; @@ -1527,6 +1535,13 @@ struct drm_i915_private { /* gen6+ rps state */ struct intel_gen6_power_mgmt rps; + /* rps wa up ei calculation */ + struct intel_rps_ei_calc rps_up_ei; + + /* rps wa down ei calculation */ + struct intel_rps_ei_calc rps_down_ei; + + /* ilk-only ips/rps state. Everything in here is protected by the global * mchdev_lock in intel_pm.c */ struct intel_ilk_power_mgmt ips; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d672053..0b4a8ed 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1272,6 +1272,131 @@ static void notify_ring(struct drm_device *dev, i915_queue_hangcheck(dev); } +static u32 vlv_c0_residency(struct drm_i915_private *dev_priv, + struct intel_rps_ei_calc *rps_ei) +{ + u32 cz_ts, cz_freq_khz; + u32 render_count, media_count; + u32 elapsed_render, elapsed_media, elapsed_time; + u32 residency = 0; + + cz_ts = vlv_punit_read(dev_priv, PUNIT_REG_CZ_TIMESTAMP); + cz_freq_khz = DIV_ROUND_CLOSEST(dev_priv->mem_freq * 1000, 4); + + render_count = I915_READ(VLV_RENDER_C0_COUNT_REG); + media_count = I915_READ(VLV_MEDIA_C0_COUNT_REG); + + if (rps_ei->cz_ts_ei == 0) { + rps_ei->cz_ts_ei = cz_ts; + rps_ei->render_ei_c0 = render_count; + rps_ei->media_ei_c0 = media_count; + + return dev_priv->rps.cur_freq; + } + + elapsed_time = cz_ts - rps_ei->cz_ts_ei; + rps_ei->cz_ts_ei = cz_ts; + + elapsed_render = render_count - rps_ei->render_ei_c0; + rps_ei->render_ei_c0 = render_count; + + elapsed_media = media_count - rps_ei->media_ei_c0; + rps_ei->media_ei_c0 = media_count; + + /* Convert all the counters into common unit of milli sec */ + elapsed_time /= VLV_CZ_CLOCK_TO_MILLI_SEC; + elapsed_render /= cz_freq_khz; + elapsed_media /= cz_freq_khz; + + /* + * Calculate overall C0 residency percentage + * only if elapsed time is non zero + */ + if (elapsed_time) { + residency = + ((max(elapsed_render, elapsed_media) * 100) + / elapsed_time); + } + + return residency; +} + +/** + * vlv_calc_delay_from_C0_counters - Increase/Decrease freq based on GPU + * busy-ness calculated from C0 counters of render & media power wells + * @dev_priv: DRM device private + * + */ +static u32 vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv) +{ + u32 residency_C0_up = 0, residency_C0_down = 0; + u8 new_delay, adj; + + dev_priv->rps.ei_interrupt_count++; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + + if (dev_priv->rps_up_ei.cz_ts_ei == 0) { + vlv_c0_residency(dev_priv, &dev_priv->rps_up_ei); + vlv_c0_residency(dev_priv, &dev_priv->rps_down_ei); + return dev_priv->rps.cur_freq; + } + + + /* + * To down throttle, C0 residency should be less than down threshold + * for continous EI intervals. So calculate down EI counters + * once in VLV_INT_COUNT_FOR_DOWN_EI + */ + if (dev_priv->rps.ei_interrupt_count == VLV_INT_COUNT_FOR_DOWN_EI) { + + dev_priv->rps.ei_interrupt_count = 0; + + residency_C0_down = vlv_c0_residency(dev_priv, + &dev_priv->rps_down_ei); + } else { + residency_C0_up = vlv_c0_residency(dev_priv, + &dev_priv->rps_up_ei); + } + + new_delay = dev_priv->rps.cur_freq; + + adj = dev_priv->rps.last_adj; + /* C0 residency is greater than UP threshold. Increase Frequency */ + if (residency_C0_up >= VLV_RP_UP_EI_THRESHOLD) { + if (adj > 0) + adj *= 2; + else + adj = 1; + + if (dev_priv->rps.cur_freq < dev_priv->rps.max_freq_softlimit) + new_delay = dev_priv->rps.cur_freq + adj; + + /* + * For better performance, jump directly + * to RPe if we're below it. + */ + if (new_delay < dev_priv->rps.efficient_freq) + new_delay = dev_priv->rps.efficient_freq; + + } else if (!dev_priv->rps.ei_interrupt_count && + (residency_C0_down < VLV_RP_DOWN_EI_THRESHOLD)) { + if (adj < 0) + adj *= 2; + else + adj = -1; + /* + * This means, C0 residency is less than down threshold over + * a period of VLV_INT_COUNT_FOR_DOWN_EI. So, reduce the freq + */ + if (dev_priv->rps.cur_freq > dev_priv->rps.min_freq_softlimit) + new_delay = dev_priv->rps.cur_freq + adj; + } + + return new_delay; +} + static void gen6_pm_rps_work(struct work_struct *work) { struct drm_i915_private *dev_priv = @@ -1320,6 +1445,8 @@ static void gen6_pm_rps_work(struct work_struct *work) else new_delay = dev_priv->rps.min_freq_softlimit; adj = 0; + } else if (pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) { + new_delay = vlv_calc_delay_from_C0_counters(dev_priv); } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) { if (adj < 0) adj *= 2; @@ -4511,7 +4638,11 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); /* Let's track the enabled rps events */ - dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS; + if (IS_VALLEYVIEW(dev)) + /* WaGsvRC0ResidenncyMethod:VLV */ + dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED; + else + dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS; setup_timer(&dev_priv->gpu_error.hangcheck_timer, i915_hangcheck_elapsed, diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8c0f70d..190d4bb 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -531,6 +531,7 @@ enum punit_power_well { #define PUNIT_REG_GPU_FREQ_STS 0xd8 #define GENFREQSTATUS (1<<0) #define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc +#define PUNIT_REG_CZ_TIMESTAMP 0xce #define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ #define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ @@ -556,6 +557,11 @@ enum punit_power_well { #define FB_FMAX_VMIN_FREQ_LO_SHIFT 27 #define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000 +#define VLV_CZ_CLOCK_TO_MILLI_SEC 100000 +#define VLV_RP_UP_EI_THRESHOLD 90 +#define VLV_RP_DOWN_EI_THRESHOLD 70 +#define VLV_INT_COUNT_FOR_DOWN_EI 5 + /* vlv2 north clock has */ #define CCK_FUSE_REG 0x8 #define CCK_FUSE_HPLL_FREQ_MASK 0x3 @@ -5394,6 +5400,7 @@ enum punit_power_well { #define VLV_GTLC_ALLOWWAKEERR (1 << 1) #define VLV_GTLC_PW_MEDIA_STATUS_MASK (1 << 5) #define VLV_GTLC_PW_RENDER_STATUS_MASK (1 << 7) +#define VLV_GTLC_SURVIVABILITY_REG 0x130098 #define FORCEWAKE_MT 0xa188 /* multi-threaded */ #define FORCEWAKE_KERNEL 0x1 #define FORCEWAKE_USER 0x2 @@ -5541,6 +5548,8 @@ enum punit_power_well { #define GEN6_GT_GFX_RC6_LOCKED 0x138104 #define VLV_COUNTER_CONTROL 0x138104 #define VLV_COUNT_RANGE_HIGH (1<<15) +#define VLV_MEDIA_RC0_COUNT_EN (1<<5) +#define VLV_RENDER_RC0_COUNT_EN (1<<4) #define VLV_MEDIA_RC6_COUNT_EN (1<<1) #define VLV_RENDER_RC6_COUNT_EN (1<<0) #define GEN6_GT_GFX_RC6 0x138108 @@ -5549,6 +5558,8 @@ enum punit_power_well { #define GEN6_GT_GFX_RC6p 0x13810C #define GEN6_GT_GFX_RC6pp 0x138110 +#define VLV_RENDER_C0_COUNT_REG 0x138118 +#define VLV_MEDIA_C0_COUNT_REG 0x13811C #define GEN6_PCODE_MAILBOX 0x138124 #define GEN6_PCODE_READY (1<<31) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f2a4056..d1af641 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3282,8 +3282,11 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) vlv_force_gfx_clock(dev_priv, false); - I915_WRITE(GEN6_PMINTRMSK, - gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); + if (dev_priv->pm_rps_events & GEN6_PM_RP_UP_EI_EXPIRED) + I915_WRITE(GEN6_PMINTRMSK, ~dev_priv->pm_rps_events); + else + I915_WRITE(GEN6_PMINTRMSK, + gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); } void gen6_rps_idle(struct drm_i915_private *dev_priv) @@ -4125,6 +4128,7 @@ static void valleyview_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_DOWN_EI, 350000); I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 0xf4240); I915_WRITE(GEN6_RP_CONTROL, GEN6_RP_MEDIA_TURBO | @@ -4145,9 +4149,11 @@ static void valleyview_enable_rps(struct drm_device *dev) /* allows RC6 residency counter to work */ I915_WRITE(VLV_COUNTER_CONTROL, - _MASKED_BIT_ENABLE(VLV_COUNT_RANGE_HIGH | + _MASKED_BIT_ENABLE(VLV_MEDIA_RC0_COUNT_EN | + VLV_RENDER_RC0_COUNT_EN | VLV_MEDIA_RC6_COUNT_EN | VLV_RENDER_RC6_COUNT_EN)); + if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE) rc6_mode = GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL; -- cgit v0.10.2 From 1f767e02d69f6a37a86aa94fd2c7e2528ae1161e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 3 Jul 2014 17:33:03 -0400 Subject: drm/i915: HWS must be in the mappable region for g33 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On g33, the documentation states "HWS_PGA: Format = Bits 28:12 of graphics memory address (bits 31:29 MBZ)." which translates to that the address of the HWS must be below 256MiB, which is conveniently the mappable aperture. This also appears to be true (but not documented as so) for gen4 and gen5. To generalise we force it into the low mappable region for all non-LLC platforms. If we locate the HWS at the top of the GTT the machine will hard hang during boot (fails on pnv, gm45, ilk and byt, but works on snb, ivb, hsw). v2: Add comments to explain why use PIN_MAPPABLE even though we have no intention of mapping the object. (Ville) Signed-off-by: Chris Wilson Cc: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 738140c..599709e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1432,6 +1432,7 @@ static int init_status_page(struct intel_engine_cs *ring) struct drm_i915_gem_object *obj; if ((obj = ring->status_page.obj) == NULL) { + unsigned flags; int ret; obj = i915_gem_alloc_object(ring->dev, 4096); @@ -1444,7 +1445,20 @@ static int init_status_page(struct intel_engine_cs *ring) if (ret) goto err_unref; - ret = i915_gem_obj_ggtt_pin(obj, 4096, 0); + flags = 0; + if (!HAS_LLC(ring->dev)) + /* On g33, we cannot place HWS above 256MiB, so + * restrict its pinning to the low mappable arena. + * Though this restriction is not documented for + * gen4, gen5, or byt, they also behave similarly + * and hang if the HWS is placed at the top of the + * GTT. To generalise, it appears that all !llc + * platforms have issues with us placing the HWS + * above the mappable region (even though we never + * actualy map it). + */ + flags |= PIN_MAPPABLE; + ret = i915_gem_obj_ggtt_pin(obj, 4096, flags); if (ret) { err_unref: drm_gem_object_unreference(&obj->base); -- cgit v0.10.2 From 1f834c32b758f0a30ae2d7a89f7bae54000faf2e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 9 Jul 2014 11:05:08 +1000 Subject: drm/tilcdc: oops a Module.symvers snuck in remove it. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/Module.symvers b/drivers/gpu/drm/tilcdc/Module.symvers deleted file mode 100644 index e69de29..0000000 -- cgit v0.10.2 From 75bc08ab276f3e6f436847328631a07f30f973c4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 9 Jul 2014 11:15:09 +1000 Subject: drm/mst: fix build with debugfs off. seq_file.h was being pulled in via drm_mm.h in that case. Reported-by: Matthew Thode (on irc) Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 73bc773..369d6c4 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 155e63696caab748da3f344f66de503038a72024 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 7 Jul 2014 18:21:47 -0700 Subject: drm/i915: Introduce intel_fb_obj() macro Add an intel_fb_obj() macro that returns the GEM object associated with a DRM framebuffer. This macro is safe to call on NULL framebuffers (a NULL object pointer will be returned in this case). Signed-off-by: Matt Roper Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9da665a..41c83ea 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -486,6 +486,7 @@ struct cxsr_latency { #define to_intel_encoder(x) container_of(x, struct intel_encoder, base) #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) #define to_intel_plane(x) container_of(x, struct intel_plane, base) +#define intel_fb_obj(x) (x ? to_intel_framebuffer(x)->obj : NULL) struct intel_hdmi { u32 hdmi_reg; -- cgit v0.10.2 From 2ff8fde1ea0992dfd735dce94f8cae2aacff8e5c Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 8 Jul 2014 07:50:07 -0700 Subject: drm/i915: Make use of intel_fb_obj() (v2) This should hopefully simplify the display code slightly and also solves at least one mistake in intel_pipe_set_base() where to_intel_framebuffer(fb)->obj is referenced during local variable initialization, before 'if (!fb)' gets checked. Potential uses of this macro were identified via the following Coccinelle patch: @@ expression E; @@ * to_intel_framebuffer(E)->obj @@ expression E; identifier I; @@ I = to_intel_framebuffer(E); ... * I->obj v2: Rewrite some NULL tests in terms of the obj rather than the fb. Also add a WARN() if trying to pageflip with a disabled primary plane. [Suggested by Chris Wilson] Signed-off-by: Matt Roper Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8e738d4..71430a0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2356,7 +2356,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc, struct drm_device *dev = intel_crtc->base.dev; struct drm_crtc *c; struct intel_crtc *i; - struct intel_framebuffer *fb; + struct drm_i915_gem_object *obj; if (!intel_crtc->base.primary->fb) return; @@ -2377,14 +2377,17 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc, if (c == &intel_crtc->base) continue; - if (!i->active || !c->primary->fb) + if (!i->active) + continue; + + obj = intel_fb_obj(c->primary->fb); + if (obj == NULL) continue; - fb = to_intel_framebuffer(c->primary->fb); - if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) { + if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) { drm_framebuffer_reference(c->primary->fb); intel_crtc->base.primary->fb = c->primary->fb; - fb->obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); + obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); break; } } @@ -2397,16 +2400,12 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; u32 reg; - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; - reg = DSPCNTR(plane); dspcntr = I915_READ(reg); /* Mask out pixel format bits in case we change it */ @@ -2487,16 +2486,12 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; u32 reg; - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; - reg = DSPCNTR(plane); dspcntr = I915_READ(reg); /* Mask out pixel format bits in case we change it */ @@ -2627,7 +2622,7 @@ void intel_display_handle_reset(struct drm_device *dev) static int intel_finish_fb(struct drm_framebuffer *old_fb) { - struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(old_fb); struct drm_i915_private *dev_priv = obj->base.dev->dev_private; bool was_interruptible = dev_priv->mm.interruptible; int ret; @@ -2674,9 +2669,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; - struct drm_framebuffer *old_fb; - struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; - struct drm_i915_gem_object *old_obj; + struct drm_framebuffer *old_fb = crtc->primary->fb; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *old_obj = intel_fb_obj(old_fb); int ret; if (intel_crtc_has_pending_flip(crtc)) { @@ -2697,9 +2692,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return -EINVAL; } - old_fb = crtc->primary->fb; - old_obj = old_fb ? to_intel_framebuffer(old_fb)->obj : NULL; - mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); if (ret == 0) @@ -2755,7 +2747,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (intel_crtc->active && old_fb != fb) intel_wait_for_vblank(dev, intel_crtc->pipe); mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); + intel_unpin_fb_obj(old_obj); mutex_unlock(&dev->struct_mutex); } @@ -4929,7 +4921,7 @@ static void intel_crtc_disable(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_object *old_obj; + struct drm_i915_gem_object *old_obj = intel_fb_obj(crtc->primary->fb); enum pipe pipe = to_intel_crtc(crtc)->pipe; /* crtc should still be enabled when we disable it. */ @@ -4944,7 +4936,6 @@ static void intel_crtc_disable(struct drm_crtc *crtc) assert_pipe_disabled(dev->dev_private, pipe); if (crtc->primary->fb) { - old_obj = to_intel_framebuffer(crtc->primary->fb)->obj; mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(old_obj); i915_gem_track_fb(old_obj, NULL, @@ -9586,7 +9577,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *old_fb = crtc->primary->fb; - struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; struct intel_unpin_work *work; @@ -9594,6 +9585,14 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, unsigned long flags; int ret; + /* + * drm_mode_page_flip_ioctl() should already catch this, but double + * check to be safe. In the future we may enable pageflipping from + * a disabled primary plane. + */ + if (WARN_ON(intel_fb_obj(old_fb) == NULL)) + return -EBUSY; + /* Can't change pixel format via MI display flips. */ if (fb->pixel_format != crtc->primary->fb->pixel_format) return -EINVAL; @@ -9616,7 +9615,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->event = event; work->crtc = crtc; - work->old_fb_obj = to_intel_framebuffer(old_fb)->obj; + work->old_fb_obj = intel_fb_obj(old_fb); INIT_WORK(&work->work, intel_unpin_work_fn); ret = drm_crtc_vblank_get(crtc); @@ -10758,10 +10757,9 @@ static int __intel_set_mode(struct drm_crtc *crtc, * on the DPLL. */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { - struct drm_framebuffer *old_fb; - struct drm_i915_gem_object *old_obj = NULL; - struct drm_i915_gem_object *obj = - to_intel_framebuffer(fb)->obj; + struct drm_framebuffer *old_fb = crtc->primary->fb; + struct drm_i915_gem_object *old_obj = intel_fb_obj(old_fb); + struct drm_i915_gem_object *obj = intel_fb_obj(fb); mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, @@ -10772,11 +10770,8 @@ static int __intel_set_mode(struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); goto done; } - old_fb = crtc->primary->fb; - if (old_fb) { - old_obj = to_intel_framebuffer(old_fb)->obj; + if (old_fb) intel_unpin_fb_obj(old_obj); - } i915_gem_track_fb(old_obj, obj, INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); mutex_unlock(&dev->struct_mutex); @@ -11394,9 +11389,9 @@ intel_primary_plane_disable(struct drm_plane *plane) intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, intel_plane->pipe); disable_unpin: - i915_gem_track_fb(to_intel_framebuffer(plane->fb)->obj, NULL, + i915_gem_track_fb(intel_fb_obj(plane->fb), NULL, INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); - intel_unpin_fb_obj(to_intel_framebuffer(plane->fb)->obj); + intel_unpin_fb_obj(intel_fb_obj(plane->fb)); plane->fb = NULL; return 0; @@ -11413,7 +11408,8 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); - struct drm_i915_gem_object *obj, *old_obj = NULL; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->fb); struct drm_rect dest = { /* integer pixels */ .x1 = crtc_x, @@ -11445,10 +11441,6 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, if (ret) return ret; - if (plane->fb) - old_obj = to_intel_framebuffer(plane->fb)->obj; - obj = to_intel_framebuffer(fb)->obj; - /* * If the CRTC isn't enabled, we're just pinning the framebuffer, * updating the fb pointer, and returning without touching the @@ -12945,7 +12937,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, void intel_modeset_gem_init(struct drm_device *dev) { struct drm_crtc *c; - struct intel_framebuffer *fb; + struct drm_i915_gem_object *obj; mutex_lock(&dev->struct_mutex); intel_init_gt_powersave(dev); @@ -12962,11 +12954,11 @@ void intel_modeset_gem_init(struct drm_device *dev) */ mutex_lock(&dev->struct_mutex); for_each_crtc(dev, c) { - if (!c->primary->fb) + obj = intel_fb_obj(c->primary->fb); + if (obj == NULL) continue; - fb = to_intel_framebuffer(c->primary->fb); - if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) { + if (intel_pin_and_fence_fb_obj(dev, obj, NULL)) { DRM_ERROR("failed to pin boot fb on pipe %d\n", to_intel_crtc(c)->pipe); drm_framebuffer_unreference(c->primary->fb); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e0f1bd1..3ca6c7c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1752,7 +1752,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dig_port->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->primary->fb)->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(crtc->primary->fb); struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; dev_priv->psr.source_ok = false; @@ -1785,7 +1785,6 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } - obj = to_intel_framebuffer(crtc->primary->fb)->obj; if (obj->tiling_mode != I915_TILING_X || obj->fence_reg == I915_FENCE_REG_NONE) { DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n"); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d1af641..19c5c26 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -93,8 +93,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; - struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int cfb_pitch; int i; @@ -150,8 +149,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; - struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 dpfc_ctl; @@ -222,8 +220,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; - struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 dpfc_ctl; @@ -289,8 +286,7 @@ static void gen7_enable_fbc(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->primary->fb; - struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 dpfc_ctl; @@ -485,7 +481,6 @@ void intel_update_fbc(struct drm_device *dev) struct drm_crtc *crtc = NULL, *tmp_crtc; struct intel_crtc *intel_crtc; struct drm_framebuffer *fb; - struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj; const struct drm_display_mode *adjusted_mode; unsigned int max_width, max_height; @@ -530,8 +525,7 @@ void intel_update_fbc(struct drm_device *dev) intel_crtc = to_intel_crtc(crtc); fb = crtc->primary->fb; - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; + obj = intel_fb_obj(fb); adjusted_mode = &intel_crtc->config.adjusted_mode; if (i915.enable_fbc < 0) { @@ -589,7 +583,7 @@ void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; - if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size, + if (i915_gem_stolen_setup_compression(dev, obj->base.size, drm_format_plane_cpp(fb->pixel_format, 0))) { if (set_no_fbc_reason(dev_priv, FBC_STOLEN_TOO_SMALL)) DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); @@ -1599,12 +1593,12 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); if (IS_I915GM(dev) && enabled) { - struct intel_framebuffer *fb; + struct drm_i915_gem_object *obj; - fb = to_intel_framebuffer(enabled->primary->fb); + obj = intel_fb_obj(enabled->primary->fb); /* self-refresh seems busted with untiled */ - if (fb->obj->tiling_mode == I915_TILING_NONE) + if (obj->tiling_mode == I915_TILING_NONE) enabled = NULL; } -- cgit v0.10.2 From 1196f662628a6bd03456c40ac18a554af52d1d76 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 9 Jul 2014 15:10:43 +0300 Subject: drm/i915: remove i915_delayedfreq_table debugfs entry CHV hard hangs on reading these registers. As these have not been used since cantiga & ilk, remove the debugfs entry. References: https://bugs.freedesktop.org/show_bug.cgi?id=80893 Suggested-by: Jesse Barnes Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index b3b56c4..6d471e1 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1158,32 +1158,6 @@ out: return ret; } -static int i915_delayfreq_table(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 delayfreq; - int ret, i; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); - - for (i = 0; i < 16; i++) { - delayfreq = I915_READ(PXVFREQ_BASE + i * 4); - seq_printf(m, "P%02dVIDFREQ: 0x%08x (VID: %d)\n", i, delayfreq, - (delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT); - } - - intel_runtime_pm_put(dev_priv); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static inline int MAP_TO_MV(int map) { return 1250 - (map * 25); @@ -3952,7 +3926,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_frequency_info", i915_frequency_info, 0}, - {"i915_delayfreq_table", i915_delayfreq_table, 0}, {"i915_inttoext_table", i915_inttoext_table, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, -- cgit v0.10.2 From ba5eaf3fb1a9de52c95bd987d07e258d1d0c8a49 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 9 Jul 2014 15:10:44 +0300 Subject: drm/i915: remove i915_inttoext_table debugfs entry CHV hard hangs on reading these registers. As these have not been used since cantiga & ilk, remove the debugfs entry. References: https://bugs.freedesktop.org/show_bug.cgi?id=80893 Suggested-by: Jesse Barnes Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 6d471e1..bab1159 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1158,35 +1158,6 @@ out: return ret; } -static inline int MAP_TO_MV(int map) -{ - return 1250 - (map * 25); -} - -static int i915_inttoext_table(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 inttoext; - int ret, i; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); - - for (i = 1; i <= 32; i++) { - inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4); - seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext); - } - - intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static int ironlake_drpc_info(struct seq_file *m) { struct drm_info_node *node = m->private; @@ -3926,7 +3897,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_frequency_info", i915_frequency_info, 0}, - {"i915_inttoext_table", i915_inttoext_table, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, {"i915_ring_freq_table", i915_ring_freq_table, 0}, -- cgit v0.10.2 From 57c9d685472f7266508e49d325fa6e791b7f8b87 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 9 Jul 2014 15:10:45 +0300 Subject: drm/i915: remove i915_gfxec debugfs entry CHV hard hangs on reading on 0x112f4. References: https://bugs.freedesktop.org/show_bug.cgi?id=80893 Suggested-by: Daniel Vetter Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index bab1159..21e1f92 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1582,26 +1582,6 @@ out: return ret; } -static int i915_gfxec(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); - - seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4)); - intel_runtime_pm_put(dev_priv); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static int i915_opregion(struct seq_file *m, void *unused) { struct drm_info_node *node = m->private; @@ -3900,7 +3880,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, {"i915_ring_freq_table", i915_ring_freq_table, 0}, - {"i915_gfxec", i915_gfxec, 0}, {"i915_fbc_status", i915_fbc_status, 0}, {"i915_ips_status", i915_ips_status, 0}, {"i915_sr_status", i915_sr_status, 0}, -- cgit v0.10.2 From d101c8fe9bda6578ae72d6021415cfaad2b422f0 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 9 Jul 2014 15:10:46 +0300 Subject: drm/i915: remove i915_rstdby_delays debugfs entry CHV hard hangs on reading on 0x11100 References: https://bugs.freedesktop.org/show_bug.cgi?id=80893 Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 21e1f92..3945191 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -994,29 +994,6 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops, i915_next_seqno_get, i915_next_seqno_set, "0x%llx\n"); -static int i915_rstdby_delays(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u16 crstanddelay; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); - - crstanddelay = I915_READ16(CRSTANDVID); - - intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev->struct_mutex); - - seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f)); - - return 0; -} - static int i915_frequency_info(struct seq_file *m, void *unused) { struct drm_info_node *node = m->private; @@ -3875,7 +3852,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, - {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_frequency_info", i915_frequency_info, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, -- cgit v0.10.2 From 62eb3e20f8f3ca9de4a17f87effe534d6a691176 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 9 Jul 2014 17:12:57 +0530 Subject: drm/tilcdc: Fix build breakage Commit 34ea3d386347 ("drm: add register and unregister functions for connectors") probably missed out converting the drm_sysfs_connector_remove instances in the following files. Without this patch we get the following compilation error: ERROR: "drm_sysfs_connector_remove" [drivers/gpu/drm/tilcdc/tilcdc.ko] undefined! Signed-off-by: Sachin Kamat CC: Thomas Wood CC: David Herrmann CC: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 8ff72c8..4c7aa1d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -151,7 +151,7 @@ struct panel_connector { static void panel_connector_destroy(struct drm_connector *connector) { struct panel_connector *panel_connector = to_panel_connector(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(panel_connector); } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index f02cb7c..3775fd4 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -166,7 +166,7 @@ struct slave_connector { static void slave_connector_destroy(struct drm_connector *connector) { struct slave_connector *slave_connector = to_slave_connector(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(slave_connector); } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 82fb520..354c47c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -167,7 +167,7 @@ struct tfp410_connector { static void tfp410_connector_destroy(struct drm_connector *connector) { struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(tfp410_connector); } -- cgit v0.10.2 From f475dadf833691e6173dee9ad05953265f6ffe21 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:59:57 -0300 Subject: drm/i915: fix hsw_write_dcomp() error message That function can be used to write anything on D_COMP, not just disable it, so print a more appropriate message. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 71430a0..aea4871 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7347,7 +7347,7 @@ static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val) mutex_lock(&dev_priv->rps.hw_lock); if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val)) - DRM_ERROR("Failed to disable D_COMP\n"); + DRM_ERROR("Failed to write to D_COMP\n"); mutex_unlock(&dev_priv->rps.hw_lock); } else { I915_WRITE(D_COMP, val); -- cgit v0.10.2 From 9ccd5aeb2901aaaabf77f185e2e5ea3a0b577e86 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:59:58 -0300 Subject: drm/i915: fix D_COMP usage on BDW On HSW, the D_COMP register can be accessed through the mailbox (read and write) or through MMIO on a MCHBAR offset (read only). On BDW, the access should be done through MMIO on another address. So to account for all these cases, create hsw_read_dcomp() with the correct implementation for reading, and also fix hsw_write_dcomp() to do the correct thing on BDW. With this patch, we can now get back from the PC8+ state on BDW. We were previously getting a black screen and lots of dmesg errors. Please notice that the bug only happens when you actually reach the PC8+ states, not when you only allow it. Testcase: igt/pm_rpm/rte Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 190d4bb..a2117a9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5984,7 +5984,10 @@ enum punit_power_well { #define LCPLL_CD_SOURCE_FCLK (1<<21) #define LCPLL_CD_SOURCE_FCLK_DONE (1<<19) -#define D_COMP (MCHBAR_MIRROR_BASE_SNB + 0x5F0C) +/* Please see hsw_read_dcomp() and hsw_write_dcomp() before using this register, + * since on HSW we can't write to it using I915_WRITE. */ +#define D_COMP_HSW (MCHBAR_MIRROR_BASE_SNB + 0x5F0C) +#define D_COMP_BDW 0x138144 #define D_COMP_RCOMP_IN_PROGRESS (1<<9) #define D_COMP_COMP_FORCE (1<<8) #define D_COMP_COMP_DISABLE (1<<0) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index aea4871..34286c6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7339,6 +7339,16 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) WARN(!dev_priv->pm.irqs_disabled, "IRQs enabled\n"); } +static uint32_t hsw_read_dcomp(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + + if (IS_HASWELL(dev)) + return I915_READ(D_COMP_HSW); + else + return I915_READ(D_COMP_BDW); +} + static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val) { struct drm_device *dev = dev_priv->dev; @@ -7350,9 +7360,9 @@ static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val) DRM_ERROR("Failed to write to D_COMP\n"); mutex_unlock(&dev_priv->rps.hw_lock); } else { - I915_WRITE(D_COMP, val); + I915_WRITE(D_COMP_BDW, val); + POSTING_READ(D_COMP_BDW); } - POSTING_READ(D_COMP); } /* @@ -7390,12 +7400,13 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv, if (wait_for((I915_READ(LCPLL_CTL) & LCPLL_PLL_LOCK) == 0, 1)) DRM_ERROR("LCPLL still locked\n"); - val = I915_READ(D_COMP); + val = hsw_read_dcomp(dev_priv); val |= D_COMP_COMP_DISABLE; hsw_write_dcomp(dev_priv, val); ndelay(100); - if (wait_for((I915_READ(D_COMP) & D_COMP_RCOMP_IN_PROGRESS) == 0, 1)) + if (wait_for((hsw_read_dcomp(dev_priv) & D_COMP_RCOMP_IN_PROGRESS) == 0, + 1)) DRM_ERROR("D_COMP RCOMP still in progress\n"); if (allow_power_down) { @@ -7444,7 +7455,7 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) POSTING_READ(LCPLL_CTL); } - val = I915_READ(D_COMP); + val = hsw_read_dcomp(dev_priv); val |= D_COMP_COMP_FORCE; val &= ~D_COMP_COMP_DISABLE; hsw_write_dcomp(dev_priv, val); -- cgit v0.10.2 From 03872064f77f4beb61d1afc22adf62da47cf087b Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 9 Jul 2014 14:31:57 -0300 Subject: drm/i915: get/put runtime PM at i915_semaphore_status Otherwise we will print some WARNs when we read registers and the machine is suspended. Testcase: igt/pm_rpm/debugfs-read Cc: Ben Widawsky Signed-off-by: Paulo Zanoni Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3945191..952f199 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2314,6 +2314,7 @@ static int i915_semaphore_status(struct seq_file *m, void *unused) ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; + intel_runtime_pm_get(dev_priv); if (IS_BROADWELL(dev)) { struct page *page; @@ -2363,6 +2364,7 @@ static int i915_semaphore_status(struct seq_file *m, void *unused) } seq_putc(m, '\n'); + intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); return 0; } -- cgit v0.10.2 From 4c34574fd9e6f3ed34ebe67fbabb0fef0b0754e3 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Wed, 9 Jul 2014 16:22:10 -0700 Subject: drm/i915: Add missing locking to primary plane handlers intel_primary_plane_{setplane,disable} were lacking struct_mutex locking around their GEM operations. Signed-off-by: Matt Roper Reported-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34286c6..0029d43 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11400,9 +11400,11 @@ intel_primary_plane_disable(struct drm_plane *plane) intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, intel_plane->pipe); disable_unpin: + mutex_lock(&dev->struct_mutex); i915_gem_track_fb(intel_fb_obj(plane->fb), NULL, INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); intel_unpin_fb_obj(intel_fb_obj(plane->fb)); + mutex_unlock(&dev->struct_mutex); plane->fb = NULL; return 0; @@ -11459,6 +11461,8 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, * turn on the display with all planes setup as desired. */ if (!crtc->enabled) { + mutex_lock(&dev->struct_mutex); + /* * If we already called setplane while the crtc was disabled, * we may have an fb pinned; unpin it. @@ -11470,7 +11474,10 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); /* Pin and return without programming hardware */ - return intel_pin_and_fence_fb_obj(dev, obj, NULL); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + mutex_unlock(&dev->struct_mutex); + + return ret; } intel_crtc_wait_for_pending_flips(crtc); @@ -11482,14 +11489,18 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, * because plane->fb still gets set and pinned. */ if (!visible) { + mutex_lock(&dev->struct_mutex); + /* * Try to pin the new fb first so that we can bail out if we * fail. */ if (plane->fb != fb) { ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); - if (ret) + if (ret) { + mutex_unlock(&dev->struct_mutex); return ret; + } } i915_gem_track_fb(old_obj, obj, @@ -11505,6 +11516,8 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, if (plane->fb) intel_unpin_fb_obj(old_obj); + mutex_unlock(&dev->struct_mutex); + return 0; } -- cgit v0.10.2 From ebcdd39eafb1d87e1aa31edfb65cd53bfc604346 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Wed, 9 Jul 2014 16:22:11 -0700 Subject: drm/i915: Add a couple WARN()'s to catch missing locks Add !mutex_is_locked() checks to intel_pin_and_fence_fb_obj() and intel_unpin_fb_obj() to help catch failures to grab struct_mutex when operating on fb objects. Signed-off-by: Matt Roper Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0029d43..f5986b2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2196,6 +2196,8 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, u32 alignment; int ret; + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + switch (obj->tiling_mode) { case I915_TILING_NONE: if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) @@ -2252,6 +2254,8 @@ err_interruptible: void intel_unpin_fb_obj(struct drm_i915_gem_object *obj) { + WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex)); + i915_gem_object_unpin_fence(obj); i915_gem_object_unpin_from_display_plane(obj); } -- cgit v0.10.2 From 8cc3e169a606ab9577a333a2017cb1acf75668e3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:46 +0300 Subject: drm/i915: Check hw state in assert_can_disable_lcpll All the other checks also check hw state, so checking our software refcounts for the plls looks a bit odd. Also this will simplify the conversion over to the shared dpll framework, which itself has massive amounts of checks to make sure that we never leave a display pll enabled when we shouldn't. So after that conversion we should stil have a good enough coverage of asserts for entering pc8/runtime pm on hsw/bdw. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f5986b2..e1b0049 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7312,7 +7312,6 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - struct intel_ddi_plls *plls = &dev_priv->ddi_plls; struct intel_crtc *crtc; for_each_intel_crtc(dev, crtc) @@ -7320,9 +7319,9 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) pipe_name(crtc->pipe)); WARN(I915_READ(HSW_PWR_WELL_DRIVER), "Power well on\n"); - WARN(plls->spll_refcount, "SPLL enabled\n"); - WARN(plls->wrpll1_refcount, "WRPLL1 enabled\n"); - WARN(plls->wrpll2_refcount, "WRPLL2 enabled\n"); + WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL enabled\n"); + WARN(I915_READ(WRPLL_CTL1) & WRPLL_PLL_ENABLE, "WRPLL1 enabled\n"); + WARN(I915_READ(WRPLL_CTL2) & WRPLL_PLL_ENABLE, "WRPLL2 enabled\n"); WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n"); WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE, "CPU PWM1 enabled\n"); -- cgit v0.10.2 From 7ca1ac135bc4d566e460230133ff959bb1bfcf88 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:47 +0300 Subject: drm/i915: Remove spll_refcount for hsw SPLL would be a reference clock we could potentially share, especially if we want to use the SSC mode. But currently we don't, so let's rip out this complexity for a simpler conversion to the new display pll framework. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 41191f1..a1650d0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -229,7 +229,6 @@ void intel_link_compute_m_n(int bpp, int nlanes, struct intel_link_m_n *m_n); struct intel_ddi_plls { - int spll_refcount; int wrpll1_refcount; int wrpll2_refcount; }; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 6cc79c8..bd8b1eb 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -394,14 +394,11 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) switch (intel_crtc->ddi_pll_sel) { case PORT_CLK_SEL_SPLL: - plls->spll_refcount--; - if (plls->spll_refcount == 0) { - DRM_DEBUG_KMS("Disabling SPLL\n"); - val = I915_READ(SPLL_CTL); - WARN_ON(!(val & SPLL_PLL_ENABLE)); - I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); - POSTING_READ(SPLL_CTL); - } + DRM_DEBUG_KMS("Disabling SPLL\n"); + val = I915_READ(SPLL_CTL); + WARN_ON(!(val & SPLL_PLL_ENABLE)); + I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); + POSTING_READ(SPLL_CTL); break; case PORT_CLK_SEL_WRPLL1: plls->wrpll1_refcount--; @@ -425,7 +422,6 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) break; } - WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n"); WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); @@ -821,16 +817,9 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) } } else if (type == INTEL_OUTPUT_ANALOG) { - if (plls->spll_refcount == 0) { - DRM_DEBUG_KMS("Using SPLL on pipe %c\n", - pipe_name(pipe)); - plls->spll_refcount++; - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; - } else { - DRM_ERROR("SPLL already in use\n"); - return false; - } - + DRM_DEBUG_KMS("Using SPLL on pipe %c\n", + pipe_name(pipe)); + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; } else { WARN(1, "Invalid DDI encoder type %d\n", type); return false; @@ -869,13 +858,13 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) return; case PORT_CLK_SEL_SPLL: - pll_name = "SPLL"; - reg = SPLL_CTL; - refcount = plls->spll_refcount; new_val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC; - break; - + WARN(I915_READ(SPLL_CTL) & enable_bit, "SPLL already enabled\n"); + I915_WRITE(SPLL_CTL, new_val); + POSTING_READ(SPLL_CTL); + udelay(20); + return; case PORT_CLK_SEL_WRPLL1: case PORT_CLK_SEL_WRPLL2: if (crtc->ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { @@ -1188,7 +1177,6 @@ void intel_ddi_setup_hw_pll_state(struct drm_device *dev) enum pipe pipe; struct intel_crtc *intel_crtc; - dev_priv->ddi_plls.spll_refcount = 0; dev_priv->ddi_plls.wrpll1_refcount = 0; dev_priv->ddi_plls.wrpll2_refcount = 0; @@ -1205,9 +1193,6 @@ void intel_ddi_setup_hw_pll_state(struct drm_device *dev) pipe); switch (intel_crtc->ddi_pll_sel) { - case PORT_CLK_SEL_SPLL: - dev_priv->ddi_plls.spll_refcount++; - break; case PORT_CLK_SEL_WRPLL1: dev_priv->ddi_plls.wrpll1_refcount++; break; -- cgit v0.10.2 From 114fe4885721d985907fbfb0d1a0c1c6676b4543 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:48 +0300 Subject: drm/i915: Clean up WRPLL/SPLL #defines Luckily the bit definitions match, but it's still confusing to use one when handling the other. So sprinkle some OCD over the #defines to make them match and use the right version in each place. Maybe we should unify these definitions completely, but that can always be done sometime in the future. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a2117a9..d829dfc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5922,9 +5922,10 @@ enum punit_power_well { #define WRPLL_CTL1 0x46040 #define WRPLL_CTL2 0x46060 #define WRPLL_PLL_ENABLE (1<<31) -#define WRPLL_PLL_SELECT_SSC (0x01<<28) -#define WRPLL_PLL_SELECT_NON_SSC (0x02<<28) -#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28) +#define WRPLL_PLL_SSC (1<<28) +#define WRPLL_PLL_NON_SSC (2<<28) +#define WRPLL_PLL_LCPLL (3<<28) +#define WRPLL_PLL_REF_MASK (3<<28) /* WRPLL divider programming */ #define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0) #define WRPLL_DIVIDER_REF_MASK (0xff) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index bd8b1eb..8c57f9a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -588,9 +588,9 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, u32 wrpll; wrpll = I915_READ(reg); - switch (wrpll & SPLL_PLL_REF_MASK) { - case SPLL_PLL_SSC: - case SPLL_PLL_NON_SSC: + switch (wrpll & WRPLL_PLL_REF_MASK) { + case WRPLL_PLL_SSC: + case WRPLL_PLL_NON_SSC: /* * We could calculate spread here, but our checking * code only cares about 5% accuracy, and spread is a max of @@ -598,7 +598,7 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, */ refclk = 135; break; - case SPLL_PLL_LCPLL: + case WRPLL_PLL_LCPLL: refclk = LC_FREQ; break; default: @@ -780,7 +780,7 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); - val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | + val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p); @@ -879,7 +879,7 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); - new_val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | + new_val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p); -- cgit v0.10.2 From 4fe9467d216af71fb456d626ee26e17178d619f9 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 25 Jun 2014 22:01:49 +0300 Subject: drm/i915: ddi: move pch setup after encoder->pre_enable This is needed by an upcoming patch that moves the PCH/CRT PLL enabling into the pre_enable hook, after which we want to keep the modeset sequence at its current state. At this point this won't have an effect since the PCH/CRT pre_enable hook is atm a NOP. Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e1b0049..8ce89c8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4100,16 +4100,15 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_crtc->active = true; intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); - if (intel_crtc->config.has_pch_encoder) - intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); - - if (intel_crtc->config.has_pch_encoder) - dev_priv->display.fdi_link_train(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->pre_enable) encoder->pre_enable(encoder); + if (intel_crtc->config.has_pch_encoder) { + intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); + dev_priv->display.fdi_link_train(crtc); + } + intel_ddi_enable_pipe_clock(intel_crtc); ironlake_pfit_enable(intel_crtc); -- cgit v0.10.2 From 97b040aa391651793e4d463408c137b81517cc90 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 25 Jun 2014 22:01:50 +0300 Subject: drm/i915: ddi: move pch cleanup before encoder->post_disable This is needed by an upcoming patch that moves the PCH/CRT PLL disabling into the post_disable hook, after which we want to keep the modeset sequence at its current state. At this point this won't have an effect since the PCH/CRT post_disable hook is atm a NOP. Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8ce89c8..6332d9d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4246,16 +4246,16 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) intel_ddi_disable_pipe_clock(intel_crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_disable) - encoder->post_disable(encoder); - if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); intel_ddi_fdi_disable(crtc); } + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->post_disable) + encoder->post_disable(encoder); + intel_crtc->active = false; intel_update_watermarks(crtc); -- cgit v0.10.2 From 082717ead9f5836fac1b2757aad38f652cc63636 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:51 +0300 Subject: drm/i915: Move the SPLL enabling into hsw_crt_pre_enable The call to intel_ddi_pll_enable in haswell_crtc_mode_set is the only function that still touches the hardware state from the crtc mode_set callback on hsw. Since the SPLL isn't ever shared we can easily take it out into the hsw crt encoder functions. Temporarily we'll loose a bit of WARN_ON coverage with this, but once the WRPLLs are switched over that will be restored. For the SPLL selection add a WARN in the hsw fdi link training code. Signed-off-by: Daniel Vetter [imre: rebased on patchset version w/o pch/crt/fdi refactoring] Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 5a045d3..d312cf8 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -137,6 +137,18 @@ static void hsw_crt_get_config(struct intel_encoder *encoder, pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder); } +static void hsw_crt_pre_enable(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL already enabled\n"); + I915_WRITE(SPLL_CTL, + SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC); + POSTING_READ(SPLL_CTL); + udelay(20); +} + /* Note: The caller is required to filter out dpms modes not supported by the * platform. */ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) @@ -860,6 +872,7 @@ void intel_crt_init(struct drm_device *dev) if (HAS_DDI(dev)) { crt->base.get_config = hsw_crt_get_config; crt->base.get_hw_state = intel_ddi_get_hw_state; + crt->base.pre_enable = hsw_crt_pre_enable; } else { crt->base.get_config = intel_crt_get_config; crt->base.get_hw_state = intel_crt_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 8c57f9a..991ad0b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -278,6 +278,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Configure Port Clock Select */ I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel); + WARN_ON(intel_crtc->ddi_pll_sel != PORT_CLK_SEL_SPLL); /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ @@ -848,23 +849,6 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) BUILD_BUG_ON(enable_bit != WRPLL_PLL_ENABLE); switch (crtc->ddi_pll_sel) { - case PORT_CLK_SEL_LCPLL_2700: - case PORT_CLK_SEL_LCPLL_1350: - case PORT_CLK_SEL_LCPLL_810: - /* - * LCPLL should always be enabled at this point of the mode set - * sequence, so nothing to do. - */ - return; - - case PORT_CLK_SEL_SPLL: - new_val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | - SPLL_PLL_SSC; - WARN(I915_READ(SPLL_CTL) & enable_bit, "SPLL already enabled\n"); - I915_WRITE(SPLL_CTL, new_val); - POSTING_READ(SPLL_CTL); - udelay(20); - return; case PORT_CLK_SEL_WRPLL1: case PORT_CLK_SEL_WRPLL2: if (crtc->ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { @@ -889,7 +873,6 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) WARN(1, "Bad selected pll: PORT_CLK_SEL_NONE\n"); return; default: - WARN(1, "Bad selected pll: 0x%08x\n", crtc->ddi_pll_sel); return; } -- cgit v0.10.2 From abfdc1e375aefb3945cdd9b408059b35f4f4f4ec Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:52 +0300 Subject: drm/i915: Move SPLL disabling into hsw_crt_post_disable Similar to how the ->crtc_mode_set hook should touch the hardware to enable anything the ->crtc_off hook should disable anything in the hardware. Otherwise runtime pm for dpms will not work. Currently the only things left int the haswell_crtc_off hook is disabling the ddi plls. We can't move the WRPLL enabling out yet because the current ddi pll sharing code used by the haswell code doesn't separately track active users and overall users. This must be fixed by porting it to the generic shared display pll framework, which is powerful enough. But the SPLL source is only used by the crt encoder and so can be moved already. We only need to make sure that the ddi port E is already off, which hsw_fdi_disable does by calling intel_ddi_post_disable. With this the code reorg to shuffle hsw fdi/lpt specific code into a new hsw-specific crt encoder type is now finally complete. Signed-off-by: Daniel Vetter [imre: rebased on patchset version w/o pch/crt/fdi refactoring] Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index d312cf8..30bfdc7 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -206,6 +206,20 @@ static void intel_disable_crt(struct intel_encoder *encoder) intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF); } + +static void hsw_crt_post_disable(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t val; + + DRM_DEBUG_KMS("Disabling SPLL\n"); + val = I915_READ(SPLL_CTL); + WARN_ON(!(val & SPLL_PLL_ENABLE)); + I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); + POSTING_READ(SPLL_CTL); +} + static void intel_enable_crt(struct intel_encoder *encoder) { struct intel_crt *crt = intel_encoder_to_crt(encoder); @@ -873,6 +887,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.get_config = hsw_crt_get_config; crt->base.get_hw_state = intel_ddi_get_hw_state; crt->base.pre_enable = hsw_crt_pre_enable; + crt->base.post_disable = hsw_crt_post_disable; } else { crt->base.get_config = intel_crt_get_config; crt->base.get_hw_state = intel_crt_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 991ad0b..fa1effc 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -394,13 +394,6 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) uint32_t val; switch (intel_crtc->ddi_pll_sel) { - case PORT_CLK_SEL_SPLL: - DRM_DEBUG_KMS("Disabling SPLL\n"); - val = I915_READ(SPLL_CTL); - WARN_ON(!(val & SPLL_PLL_ENABLE)); - I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); - POSTING_READ(SPLL_CTL); - break; case PORT_CLK_SEL_WRPLL1: plls->wrpll1_refcount--; if (plls->wrpll1_refcount == 0) { -- cgit v0.10.2 From 728e29d7f1edb7b0951a8c7a0201adf12bf9d867 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:53 +0300 Subject: drm/i915: Add a debugfs file for the shared dpll state Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 952f199..f22a81d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2369,6 +2369,31 @@ static int i915_semaphore_status(struct seq_file *m, void *unused) return 0; } +static int i915_shared_dplls_info(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + drm_modeset_lock_all(dev); + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; + + seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->name, pll->id); + seq_printf(m, " refcount: %i, active: %i, on: %s\n", pll->refcount, + pll->active, yesno(pll->on)); + seq_printf(m, " tracked hardware state:\n"); + seq_printf(m, " dpll: 0x%08x\n", pll->hw_state.dpll); + seq_printf(m, " dpll_md: 0x%08x\n", pll->hw_state.dpll_md); + seq_printf(m, " fp0: 0x%08x\n", pll->hw_state.fp0); + seq_printf(m, " fp1: 0x%08x\n", pll->hw_state.fp1); + } + drm_modeset_unlock_all(dev); + + return 0; +} + struct pipe_crc_info { const char *name; struct drm_device *dev; @@ -3875,6 +3900,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_power_domain_info", i915_power_domain_info, 0}, {"i915_display_info", i915_display_info, 0}, {"i915_semaphore_status", i915_semaphore_status, 0}, + {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) -- cgit v0.10.2 From de7cfc635e4ce20ded5ca4e40328386d9ba81922 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:54 +0300 Subject: drm/i915: Move ddi_pll_sel into the pipe config Just boring sed job for preparation. Signed-off-by: Daniel Vetter Reviewed-by: Damien Lespiau [imre: rebased on patchset version w/o pch/crt/fdi refactoring] Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index fa1effc..c96bc3b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -277,8 +277,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); /* Configure Port Clock Select */ - I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel); - WARN_ON(intel_crtc->ddi_pll_sel != PORT_CLK_SEL_SPLL); + I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->config.ddi_pll_sel); + WARN_ON(intel_crtc->config.ddi_pll_sel != PORT_CLK_SEL_SPLL); /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ @@ -393,7 +393,7 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t val; - switch (intel_crtc->ddi_pll_sel) { + switch (intel_crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: plls->wrpll1_refcount--; if (plls->wrpll1_refcount == 0) { @@ -419,7 +419,7 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; } #define LC_FREQ 2700 @@ -754,13 +754,13 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) switch (intel_dp->link_bw) { case DP_LINK_BW_1_62: - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; break; case DP_LINK_BW_2_7: - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; break; case DP_LINK_BW_5_4: - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; break; default: DRM_ERROR("Link bandwidth %d unsupported\n", @@ -804,16 +804,16 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) if (reg == WRPLL_CTL1) { plls->wrpll1_refcount++; - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL1; } else { plls->wrpll2_refcount++; - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL2; } } else if (type == INTEL_OUTPUT_ANALOG) { DRM_DEBUG_KMS("Using SPLL on pipe %c\n", pipe_name(pipe)); - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_SPLL; } else { WARN(1, "Invalid DDI encoder type %d\n", type); return false; @@ -841,10 +841,10 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) BUILD_BUG_ON(enable_bit != SPLL_PLL_ENABLE); BUILD_BUG_ON(enable_bit != WRPLL_PLL_ENABLE); - switch (crtc->ddi_pll_sel) { + switch (crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: case PORT_CLK_SEL_WRPLL2: - if (crtc->ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { + if (crtc->config.ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { pll_name = "WRPLL1"; reg = WRPLL_CTL1; refcount = plls->wrpll1_refcount; @@ -1161,14 +1161,14 @@ void intel_ddi_setup_hw_pll_state(struct drm_device *dev) to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); if (!intel_crtc->active) { - intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; continue; } - intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv, + intel_crtc->config.ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv, pipe); - switch (intel_crtc->ddi_pll_sel) { + switch (intel_crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: dev_priv->ddi_plls.wrpll1_refcount++; break; @@ -1224,8 +1224,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) intel_edp_panel_on(intel_dp); } - WARN_ON(crtc->ddi_pll_sel == PORT_CLK_SEL_NONE); - I915_WRITE(PORT_CLK_SEL(port), crtc->ddi_pll_sel); + WARN_ON(crtc->config.ddi_pll_sel == PORT_CLK_SEL_NONE); + I915_WRITE(PORT_CLK_SEL(port), crtc->config.ddi_pll_sel); if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 41c83ea..0c12558 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -307,6 +307,9 @@ struct intel_crtc_config { /* Selected dpll when shared or DPLL_ID_PRIVATE. */ enum intel_dpll_id shared_dpll; + /* PORT_CLK_SEL for DDI ports. */ + uint32_t ddi_pll_sel; + /* Actual register state of the dpll, for shared dpll cross-checking. */ struct intel_dpll_hw_state dpll_hw_state; @@ -399,8 +402,6 @@ struct intel_crtc { struct intel_crtc_config *new_config; bool new_enabled; - uint32_t ddi_pll_sel; - /* reset counter value when the last flip was submitted */ unsigned int reset_counter; -- cgit v0.10.2 From 26804afd4b3c7a1ff577db4d70df8055c7901ff4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:55 +0300 Subject: drm/i915: State readout and cross-checking for ddi_pll_sel To make things a bit more manageable extract a new function for reading out common ddi port state. This means a bit of duplication between encoders and the core since both look at the same registers, but doesn't seem worth to make a fuzz about. We can also remove the state readout code in intel_ddi_setup_hw_pll_state. That code is only called from the hardware take over and not the cross check code, and only after the crtc state is reconstructed. So we can rely on an accurate value of crtc->config.ddi_pll_sel already. Compared to the old code also trust the hw state more and don't special-case port A - we want to cross-check the actual-state, not bake in our own assumptions about how this is supposed to all be linked up. v2: Make use of the read-out ddi_pll_sel in intel_ddi_clock_get. Signed-off-by: Daniel Vetter Reviewed-by: Damien Lespiau [imre: rebased on patchset version w/o pch/crt/fdi refactoring] Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d829dfc..b2b555c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5794,6 +5794,7 @@ enum punit_power_well { #define TRANS_DDI_FUNC_ENABLE (1<<31) /* Those bits are ignored by pipe EDP since it can only connect to DDI A */ #define TRANS_DDI_PORT_MASK (7<<28) +#define TRANS_DDI_PORT_SHIFT 28 #define TRANS_DDI_SELECT_PORT(x) ((x)<<28) #define TRANS_DDI_PORT_NONE (0<<28) #define TRANS_DDI_MODE_SELECT_MASK (7<<24) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c96bc3b..1b4748b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -612,11 +612,10 @@ static void intel_ddi_clock_get(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; - enum port port = intel_ddi_get_encoder_port(encoder); int link_clock = 0; u32 val, pll; - val = I915_READ(PORT_CLK_SEL(port)); + val = pipe_config->ddi_pll_sel; switch (val & PORT_CLK_SEL_MASK) { case PORT_CLK_SEL_LCPLL_810: link_clock = 81000; @@ -1113,40 +1112,6 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, return false; } -static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - uint32_t temp, ret; - enum port port = I915_MAX_PORTS; - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); - int i; - - if (cpu_transcoder == TRANSCODER_EDP) { - port = PORT_A; - } else { - temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); - temp &= TRANS_DDI_PORT_MASK; - - for (i = PORT_B; i <= PORT_E; i++) - if (temp == TRANS_DDI_SELECT_PORT(i)) - port = i; - } - - if (port == I915_MAX_PORTS) { - WARN(1, "Pipe %c enabled on an unknown port\n", - pipe_name(pipe)); - ret = PORT_CLK_SEL_NONE; - } else { - ret = I915_READ(PORT_CLK_SEL(port)); - DRM_DEBUG_KMS("Pipe %c connected to port %c using clock " - "0x%08x\n", pipe_name(pipe), port_name(port), - ret); - } - - return ret; -} - void intel_ddi_setup_hw_pll_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1165,9 +1130,6 @@ void intel_ddi_setup_hw_pll_state(struct drm_device *dev) continue; } - intel_crtc->config.ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv, - pipe); - switch (intel_crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: dev_priv->ddi_plls.wrpll1_refcount++; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6332d9d..e26df67 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7570,6 +7570,35 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, return 0; } +static void haswell_get_ddi_port_state(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port; + uint32_t tmp; + + tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder)); + + port = (tmp & TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT; + + pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); + /* + * Haswell has only FDI/PCH transcoder A. It is which is connected to + * DDI E. So just check whether this pipe is wired to DDI E and whether + * the PCH transcoder is on. + */ + if ((port == PORT_E) && I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) { + pipe_config->has_pch_encoder = true; + + tmp = I915_READ(FDI_RX_CTL(PIPE_A)); + pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> + FDI_DP_PORT_WIDTH_SHIFT) + 1; + + ironlake_get_fdi_m_n_config(crtc, pipe_config); + } +} + static bool haswell_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -7615,22 +7644,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; - /* - * Haswell has only FDI/PCH transcoder A. It is which is connected to - * DDI E. So just check whether this pipe is wired to DDI E and whether - * the PCH transcoder is on. - */ - tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder)); - if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && - I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) { - pipe_config->has_pch_encoder = true; - - tmp = I915_READ(FDI_RX_CTL(PIPE_A)); - pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> - FDI_DP_PORT_WIDTH_SHIFT) + 1; - - ironlake_get_fdi_m_n_config(crtc, pipe_config); - } + haswell_get_ddi_port_state(crtc, pipe_config); intel_get_pipe_timings(crtc, pipe_config); @@ -10409,6 +10423,8 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(double_wide); + PIPE_CONF_CHECK_X(ddi_pll_sel); + PIPE_CONF_CHECK_I(shared_dpll); PIPE_CONF_CHECK_X(dpll_hw_state.dpll); PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md); -- cgit v0.10.2 From 3fcf305b36a7be8bfc8f9e53b0498fbba7768da6 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:26:03 -0300 Subject: drm/i915: BDW also has special-purpose DP DDI clocks Don't let it fall in the HAS_PCH_SPLIT() case. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3ca6c7c..568e532 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -756,7 +756,7 @@ intel_dp_set_clock(struct intel_encoder *encoder, if (IS_G4X(dev)) { divisor = gen4_dpll; count = ARRAY_SIZE(gen4_dpll); - } else if (IS_HASWELL(dev)) { + } else if (HAS_DDI(dev)) { /* Haswell has special-purpose DP DDI clocks. */ } else if (HAS_PCH_SPLIT(dev)) { divisor = pch_dpll; -- cgit v0.10.2 From 0e50338cf0f0009a5c9bc847a4c86a1d4438af66 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 4 Jul 2014 11:26:04 -0300 Subject: drm/i915: Precompute static ddi_pll_sel values in encoders This way only the dynamic WRPLL selection for hdmi ddi mode is done in intel_ddi_pll_select. v2: Don't clobber the precomputed values when selecting clocks fro hdmi encoders. v3 (from Paulo): Rebase on top of the s/IS_HASWELL/HAS_DDI/ patch. Signed-off-by: Daniel Vetter Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 30bfdc7..4b08561 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -315,8 +315,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder, pipe_config->pipe_bpp = 24; /* FDI must always be 2.7 GHz */ - if (HAS_DDI(dev)) + if (HAS_DDI(dev)) { + pipe_config->ddi_pll_sel = PORT_CLK_SEL_SPLL; pipe_config->port_clock = 135000 * 2; + } return true; } diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 1b4748b..195d52e 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -403,6 +403,7 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE); POSTING_READ(WRPLL_CTL1); } + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; break; case PORT_CLK_SEL_WRPLL2: plls->wrpll2_refcount--; @@ -413,13 +414,12 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE); POSTING_READ(WRPLL_CTL2); } + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; break; } WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); - - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; } #define LC_FREQ 2700 @@ -739,7 +739,6 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) { struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); - struct drm_encoder *encoder = &intel_encoder->base; struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_ddi_plls *plls = &dev_priv->ddi_plls; int type = intel_encoder->type; @@ -748,26 +747,7 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) intel_ddi_put_crtc_pll(crtc); - if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - switch (intel_dp->link_bw) { - case DP_LINK_BW_1_62: - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; - break; - case DP_LINK_BW_2_7: - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; - break; - case DP_LINK_BW_5_4: - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; - break; - default: - DRM_ERROR("Link bandwidth %d unsupported\n", - intel_dp->link_bw); - return false; - } - - } else if (type == INTEL_OUTPUT_HDMI) { + if (type == INTEL_OUTPUT_HDMI) { uint32_t reg, val; unsigned p, n2, r2; @@ -808,14 +788,6 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) plls->wrpll2_refcount++; intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL2; } - - } else if (type == INTEL_OUTPUT_ANALOG) { - DRM_DEBUG_KMS("Using SPLL on pipe %c\n", - pipe_name(pipe)); - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_SPLL; - } else { - WARN(1, "Invalid DDI encoder type %d\n", type); - return false; } return true; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 568e532..ec080e5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -746,6 +746,22 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) } static void +hsw_dp_set_ddi_pll_sel(struct intel_crtc_config *pipe_config, int link_bw) +{ + switch (link_bw) { + case DP_LINK_BW_1_62: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; + break; + case DP_LINK_BW_2_7: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; + break; + case DP_LINK_BW_5_4: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; + break; + } +} + +static void intel_dp_set_clock(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config, int link_bw) { @@ -756,8 +772,6 @@ intel_dp_set_clock(struct intel_encoder *encoder, if (IS_G4X(dev)) { divisor = gen4_dpll; count = ARRAY_SIZE(gen4_dpll); - } else if (HAS_DDI(dev)) { - /* Haswell has special-purpose DP DDI clocks. */ } else if (HAS_PCH_SPLIT(dev)) { divisor = pch_dpll; count = ARRAY_SIZE(pch_dpll); @@ -928,7 +942,10 @@ found: &pipe_config->dp_m2_n2); } - intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); + if (HAS_DDI(dev)) + hsw_dp_set_ddi_pll_sel(pipe_config, intel_dp->link_bw); + else + intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); return true; } -- cgit v0.10.2 From 9cd86933fe250fd3e698b62505dfe2d43326baaa Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:57 +0300 Subject: drm/i915: Basic shared dpll support for WRPLLs Just filing in names and ids, but not yet officially registering them so that the hw state cross checker doesn't completely freak out about them. Still since we do already read out and cross check config->shared_dpll the basics are now there to flesh out the wrpll shared dpll implementation. The idea is now to roll out all the callbacks step-by-step and then at the end switch to the shared dpll framework. This way hw and sw changes are clearly separated. Signed-off-by: Daniel Vetter [imre: added const to hsw_ddi_pll_names (Damien)] Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a1650d0..3d87838 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -184,8 +184,10 @@ struct i915_mmu_object; enum intel_dpll_id { DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ /* real shared dpll ids must be >= 0 */ - DPLL_ID_PCH_PLL_A, - DPLL_ID_PCH_PLL_B, + DPLL_ID_PCH_PLL_A = 0, + DPLL_ID_PCH_PLL_B = 1, + DPLL_ID_WRPLL1 = 0, + DPLL_ID_WRPLL2 = 1, }; #define I915_NUM_PLLS 2 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 195d52e..bf6f1c2 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -784,9 +784,11 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) if (reg == WRPLL_CTL1) { plls->wrpll1_refcount++; intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL1; + intel_crtc->config.shared_dpll = DPLL_ID_WRPLL1; } else { plls->wrpll2_refcount++; intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL2; + intel_crtc->config.shared_dpll = DPLL_ID_WRPLL2; } } @@ -1315,10 +1317,25 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) } } +static char *hsw_ddi_pll_names[] = { + "WRPLL 1", + "WRPLL 2", +}; + void intel_ddi_pll_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t val = I915_READ(LCPLL_CTL); + int i; + + /* Dummy setup until everything is moved over to avoid upsetting the hw + * state cross checker. */ + dev_priv->num_shared_dpll = 0; + + for (i = 0; i < 2; i++) { + dev_priv->shared_dplls[i].id = i; + dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i]; + } /* The LCPLL register should be turned on by the BIOS. For now let's * just check its state and print errors in case something is wrong. diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e26df67..0874f35 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7583,6 +7583,16 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc, port = (tmp & TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT; pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); + + switch (pipe_config->ddi_pll_sel) { + case PORT_CLK_SEL_WRPLL1: + pipe_config->shared_dpll = DPLL_ID_WRPLL1; + break; + case PORT_CLK_SEL_WRPLL2: + pipe_config->shared_dpll = DPLL_ID_WRPLL2; + break; + } + /* * Haswell has only FDI/PCH transcoder A. It is which is connected to * DDI E. So just check whether this pipe is wired to DDI E and whether @@ -11286,12 +11296,6 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .page_flip = intel_crtc_page_flip, }; -static void intel_cpu_pll_init(struct drm_device *dev) -{ - if (HAS_DDI(dev)) - intel_ddi_pll_init(dev); -} - static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, struct intel_dpll_hw_state *hw_state) @@ -11379,7 +11383,9 @@ static void intel_shared_dpll_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + if (HAS_DDI(dev)) + intel_ddi_pll_init(dev); + else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) ibx_pch_dpll_init(dev); else dev_priv->num_shared_dpll = 0; @@ -12536,7 +12542,6 @@ void intel_modeset_init(struct drm_device *dev) intel_init_dpio(dev); intel_reset_dpio(dev); - intel_cpu_pll_init(dev); intel_shared_dpll_init(dev); /* Just disable it once at startup */ -- cgit v0.10.2 From 96f6128cbb1cf020e5b4c53656fd6b2f588727d9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:01:58 +0300 Subject: drm/i915: Document that the pll->mode_set hook is optional The WRPLLs won't use them. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3d87838..26982d1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -206,6 +206,8 @@ struct intel_shared_dpll { /* should match the index in the dev_priv->shared_dplls array */ enum intel_dpll_id id; struct intel_dpll_hw_state hw_state; + /* The mode_set hook is optional and should be used together with the + * intel_prepare_shared_dpll function. */ void (*mode_set)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); void (*enable)(struct drm_i915_private *dev_priv, -- cgit v0.10.2 From bd2bb1b9a1c8b8f7b673db22d628ffd491669deb Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:27:38 -0300 Subject: drm/i915: add POWER_DOMAIN_PLLS And get/put it when needed. The special thing about this commit is that it will now return false in ibx_pch_dpll_get_hw_state() in case the power domain is not enabled. This will fix some WARNs we have when we run pm_rpm on SNB. Testcase: igt/pm_rpm Bugzilla:https://bugs.freedesktop.org/show_bug.cgi?id=80463 Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f22a81d..2effe1a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2043,6 +2043,8 @@ static const char *power_domain_str(enum intel_display_power_domain domain) return "VGA"; case POWER_DOMAIN_AUDIO: return "AUDIO"; + case POWER_DOMAIN_PLLS: + return "PLLS"; case POWER_DOMAIN_INIT: return "INIT"; default: diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 26982d1..479a9aa 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -129,6 +129,7 @@ enum intel_display_power_domain { POWER_DOMAIN_PORT_OTHER, POWER_DOMAIN_VGA, POWER_DOMAIN_AUDIO, + POWER_DOMAIN_PLLS, POWER_DOMAIN_INIT, POWER_DOMAIN_NUM, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0874f35..d61c5e4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1814,6 +1814,8 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc) } WARN_ON(pll->on); + intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS); + DRM_DEBUG_KMS("enabling %s\n", pll->name); pll->enable(dev_priv, pll); pll->on = true; @@ -1850,6 +1852,8 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) DRM_DEBUG_KMS("disabling %s\n", pll->name); pll->disable(dev_priv, pll); pll->on = false; + + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); } static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, @@ -11302,6 +11306,9 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, { uint32_t val; + if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PLLS)) + return false; + val = I915_READ(PCH_DPLL(pll->id)); hw_state->dpll = val; hw_state->fp0 = I915_READ(PCH_FP0(pll->id)); @@ -12867,6 +12874,9 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) DRM_DEBUG_KMS("%s hw state readout: refcount %i, on %i\n", pll->name, pll->refcount, pll->on); + + if (pll->refcount) + intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS); } list_for_each_entry(encoder, &dev->mode_config.encoder_list, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 19c5c26..55228df 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6346,6 +6346,7 @@ EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \ BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \ BIT(POWER_DOMAIN_PORT_CRT) | \ + BIT(POWER_DOMAIN_PLLS) | \ BIT(POWER_DOMAIN_INIT)) #define HSW_DISPLAY_POWER_DOMAINS ( \ (POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) | \ -- cgit v0.10.2 From d452c5b67a6e2ae9f94df223919c107a8950910a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 4 Jul 2014 11:27:39 -0300 Subject: drm/i915: State readout support for WRPLLs Still tacked onto the side, but slowly getting there. v2: Don't forget the debugfs file. v3 (from Paulo): Don't forget to check the power domains. Signed-off-by: Daniel Vetter Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 2effe1a..4a5b0f8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2390,6 +2390,7 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) seq_printf(m, " dpll_md: 0x%08x\n", pll->hw_state.dpll_md); seq_printf(m, " fp0: 0x%08x\n", pll->hw_state.fp0); seq_printf(m, " fp1: 0x%08x\n", pll->hw_state.fp1); + seq_printf(m, " wrpll: 0x%08x\n", pll->hw_state.wrpll); } drm_modeset_unlock_all(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 479a9aa..5d13c99 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -197,6 +197,7 @@ struct intel_dpll_hw_state { uint32_t dpll_md; uint32_t fp0; uint32_t fp1; + uint32_t wrpll; }; struct intel_shared_dpll { diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b2b555c..d20fadd 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5922,6 +5922,7 @@ enum punit_power_well { /* WRPLL */ #define WRPLL_CTL1 0x46040 #define WRPLL_CTL2 0x46060 +#define WRPLL_CTL(pll) (pll == 0 ? WRPLL_CTL1 : WRPLL_CTL2) #define WRPLL_PLL_ENABLE (1<<31) #define WRPLL_PLL_SSC (1<<28) #define WRPLL_PLL_NON_SSC (2<<28) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index bf6f1c2..52a9160 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -790,6 +790,8 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL2; intel_crtc->config.shared_dpll = DPLL_ID_WRPLL2; } + + intel_crtc->config.dpll_hw_state.wrpll = val; } return true; @@ -1317,6 +1319,21 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) } } +static bool hsw_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + uint32_t val; + + if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PLLS)) + return false; + + val = I915_READ(WRPLL_CTL(pll->id)); + hw_state->wrpll = val; + + return val & WRPLL_PLL_ENABLE; +} + static char *hsw_ddi_pll_names[] = { "WRPLL 1", "WRPLL 2", @@ -1335,6 +1352,8 @@ void intel_ddi_pll_init(struct drm_device *dev) for (i = 0; i < 2; i++) { dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i]; + dev_priv->shared_dplls[i].get_hw_state = + hsw_ddi_pll_get_hw_state; } /* The LCPLL register should be turned on by the BIOS. For now let's diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d61c5e4..3e0917d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7579,6 +7579,7 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_shared_dpll *pll; enum port port; uint32_t tmp; @@ -7597,6 +7598,13 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc, break; } + if (pipe_config->shared_dpll >= 0) { + pll = &dev_priv->shared_dplls[pipe_config->shared_dpll]; + + WARN_ON(!pll->get_hw_state(dev_priv, pll, + &pipe_config->dpll_hw_state)); + } + /* * Haswell has only FDI/PCH transcoder A. It is which is connected to * DDI E. So just check whether this pipe is wired to DDI E and whether @@ -10444,6 +10452,7 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md); PIPE_CONF_CHECK_X(dpll_hw_state.fp0); PIPE_CONF_CHECK_X(dpll_hw_state.fp1); + PIPE_CONF_CHECK_X(dpll_hw_state.wrpll); if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) PIPE_CONF_CHECK_I(pipe_bpp); -- cgit v0.10.2 From 12030431e9ba407b8c35b3c3bf70b67d609447f3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:02:00 +0300 Subject: drm/i915: ->disable hook for WRPLLs Currently still with a redudant WARN_ON in there, the common shared dpll code will take care of this in the future. Also we need to flip the switch for the transitional hack now to make sure that we disable the right pll. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 52a9160..7c6e8d5 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -391,28 +391,20 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_ddi_plls *plls = &dev_priv->ddi_plls; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - uint32_t val; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(intel_crtc); switch (intel_crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: plls->wrpll1_refcount--; if (plls->wrpll1_refcount == 0) { - DRM_DEBUG_KMS("Disabling WRPLL 1\n"); - val = I915_READ(WRPLL_CTL1); - WARN_ON(!(val & WRPLL_PLL_ENABLE)); - I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE); - POSTING_READ(WRPLL_CTL1); + pll->disable(dev_priv, pll); } intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; break; case PORT_CLK_SEL_WRPLL2: plls->wrpll2_refcount--; if (plls->wrpll2_refcount == 0) { - DRM_DEBUG_KMS("Disabling WRPLL 2\n"); - val = I915_READ(WRPLL_CTL2); - WARN_ON(!(val & WRPLL_PLL_ENABLE)); - I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE); - POSTING_READ(WRPLL_CTL2); + pll->disable(dev_priv, pll); } intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; break; @@ -1319,6 +1311,17 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) } } +static void hsw_ddi_pll_disable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + uint32_t val; + + val = I915_READ(WRPLL_CTL(pll->id)); + WARN_ON(!(val & WRPLL_PLL_ENABLE)); + I915_WRITE(WRPLL_CTL(pll->id), val & ~WRPLL_PLL_ENABLE); + POSTING_READ(WRPLL_CTL(pll->id)); +} + static bool hsw_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, struct intel_dpll_hw_state *hw_state) @@ -1352,6 +1355,7 @@ void intel_ddi_pll_init(struct drm_device *dev) for (i = 0; i < 2; i++) { dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i]; + dev_priv->shared_dplls[i].disable = hsw_ddi_pll_disable; dev_priv->shared_dplls[i].get_hw_state = hsw_ddi_pll_get_hw_state; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3e0917d..851221d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5224,9 +5224,11 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc, if (HAS_IPS(dev)) hsw_compute_ips_config(crtc, pipe_config); - /* XXX: PCH clock sharing is done in ->mode_set, so make sure the old - * clock survives for now. */ - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + /* + * XXX: PCH/WRPLL clock sharing is done in ->mode_set, so make sure the + * old clock survives for now. + */ + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev) || HAS_DDI(dev)) pipe_config->shared_dpll = crtc->config.shared_dpll; if (pipe_config->has_pch_encoder) -- cgit v0.10.2 From e0b01be41dcdfd28c6855f605983a61b29f65692 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:02:01 +0300 Subject: drm/i915: ->enable hook for WRPLLs This time around another cute hack to pre-fill the pll->hw_state with the right values. And also remove a bunch of checks which will be replaced by lots more checks in the common framework. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 7c6e8d5..150b64b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -740,6 +740,7 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) intel_ddi_put_crtc_pll(crtc); if (type == INTEL_OUTPUT_HDMI) { + struct intel_shared_dpll *pll; uint32_t reg, val; unsigned p, n2, r2; @@ -784,6 +785,9 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) } intel_crtc->config.dpll_hw_state.wrpll = val; + + pll = &dev_priv->shared_dplls[intel_crtc->config.shared_dpll]; + pll->hw_state.wrpll = val; } return true; @@ -798,54 +802,24 @@ void intel_ddi_pll_enable(struct intel_crtc *crtc) struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ddi_plls *plls = &dev_priv->ddi_plls; - int clock = crtc->config.port_clock; - uint32_t reg, cur_val, new_val; int refcount; - const char *pll_name; - uint32_t enable_bit = (1 << 31); - unsigned int p, n2, r2; - - BUILD_BUG_ON(enable_bit != SPLL_PLL_ENABLE); - BUILD_BUG_ON(enable_bit != WRPLL_PLL_ENABLE); + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); switch (crtc->config.ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: case PORT_CLK_SEL_WRPLL2: if (crtc->config.ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { - pll_name = "WRPLL1"; - reg = WRPLL_CTL1; refcount = plls->wrpll1_refcount; } else { - pll_name = "WRPLL2"; - reg = WRPLL_CTL2; refcount = plls->wrpll2_refcount; } - - intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); - - new_val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | - WRPLL_DIVIDER_REFERENCE(r2) | - WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p); - break; - - case PORT_CLK_SEL_NONE: - WARN(1, "Bad selected pll: PORT_CLK_SEL_NONE\n"); - return; default: return; } - cur_val = I915_READ(reg); - - WARN(refcount < 1, "Bad %s refcount: %d\n", pll_name, refcount); if (refcount == 1) { - WARN(cur_val & enable_bit, "%s already enabled\n", pll_name); - I915_WRITE(reg, new_val); - POSTING_READ(reg); - udelay(20); - } else { - WARN((cur_val & enable_bit) == 0, "%s disabled\n", pll_name); + pll->enable(dev_priv, pll); } } @@ -1311,6 +1285,18 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) } } +static void hsw_ddi_pll_enable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + uint32_t cur_val; + + cur_val = I915_READ(WRPLL_CTL(pll->id)); + WARN(cur_val & WRPLL_PLL_ENABLE, "%s already enabled\n", pll->name); + I915_WRITE(WRPLL_CTL(pll->id), pll->hw_state.wrpll); + POSTING_READ(WRPLL_CTL(pll->id)); + udelay(20); +} + static void hsw_ddi_pll_disable(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) { @@ -1356,6 +1342,7 @@ void intel_ddi_pll_init(struct drm_device *dev) dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i]; dev_priv->shared_dplls[i].disable = hsw_ddi_pll_disable; + dev_priv->shared_dplls[i].enable = hsw_ddi_pll_enable; dev_priv->shared_dplls[i].get_hw_state = hsw_ddi_pll_get_hw_state; } -- cgit v0.10.2 From 716c2e55100ff5588bd2bbca14951ef11624cba2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:02:02 +0300 Subject: drm/i915: Switch to common shared dpll framework for WRPLLs Mostly this patch is one big excersize in deleting code and asserts which are no longer needed. Note that we still abuse the shared dpll framework a bit since we call the enable/disable functions from the crtc mode_set and off hooks. But changing the actual hardware sequence will be done in the next step. Note that besides the massive amount of changes in this patch the places and order in which the low-level WRPLL code is called is absolutely unchanged. Signed-off-by: Daniel Vetter Reviewed-by: Damien Lespiau [imre: rebased on patchset version w/o pch/crt/fdi refactoring] Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 5d13c99..647ea67 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -234,11 +234,6 @@ void intel_link_compute_m_n(int bpp, int nlanes, int pixel_clock, int link_clock, struct intel_link_m_n *m_n); -struct intel_ddi_plls { - int wrpll1_refcount; - int wrpll2_refcount; -}; - /* Interface history: * * 1.1: Original. @@ -1517,7 +1512,6 @@ struct drm_i915_private { int num_shared_dpll; struct intel_shared_dpll shared_dplls[I915_NUM_PLLS]; - struct intel_ddi_plls ddi_plls; int dpio_phy_iosf_port[I915_NUM_PHYS_VLV]; /* Reclocking support */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d20fadd..2d2c4de 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5946,6 +5946,7 @@ enum punit_power_well { #define PORT_CLK_SEL_LCPLL_1350 (1<<29) #define PORT_CLK_SEL_LCPLL_810 (2<<29) #define PORT_CLK_SEL_SPLL (3<<29) +#define PORT_CLK_SEL_WRPLL(pll) (((pll)+4)<<29) #define PORT_CLK_SEL_WRPLL1 (4<<29) #define PORT_CLK_SEL_WRPLL2 (5<<29) #define PORT_CLK_SEL_NONE (7<<29) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 150b64b..60e67a1 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -388,30 +388,12 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) { - struct drm_i915_private *dev_priv = crtc->dev->dev_private; - struct intel_ddi_plls *plls = &dev_priv->ddi_plls; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(intel_crtc); - switch (intel_crtc->config.ddi_pll_sel) { - case PORT_CLK_SEL_WRPLL1: - plls->wrpll1_refcount--; - if (plls->wrpll1_refcount == 0) { - pll->disable(dev_priv, pll); - } - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; - break; - case PORT_CLK_SEL_WRPLL2: - plls->wrpll2_refcount--; - if (plls->wrpll2_refcount == 0) { - pll->disable(dev_priv, pll); - } - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; - break; - } + if (intel_crtc_to_shared_dpll(intel_crtc)) + intel_disable_shared_dpll(intel_crtc); - WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); - WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); + intel_put_shared_dpll(intel_crtc); } #define LC_FREQ 2700 @@ -731,17 +713,14 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) { struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); - struct drm_i915_private *dev_priv = crtc->dev->dev_private; - struct intel_ddi_plls *plls = &dev_priv->ddi_plls; int type = intel_encoder->type; - enum pipe pipe = intel_crtc->pipe; int clock = intel_crtc->config.port_clock; intel_ddi_put_crtc_pll(crtc); if (type == INTEL_OUTPUT_HDMI) { struct intel_shared_dpll *pll; - uint32_t reg, val; + uint32_t val; unsigned p, n2, r2; intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); @@ -750,79 +729,21 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p); - if (val == I915_READ(WRPLL_CTL1)) { - DRM_DEBUG_KMS("Reusing WRPLL 1 on pipe %c\n", - pipe_name(pipe)); - reg = WRPLL_CTL1; - } else if (val == I915_READ(WRPLL_CTL2)) { - DRM_DEBUG_KMS("Reusing WRPLL 2 on pipe %c\n", - pipe_name(pipe)); - reg = WRPLL_CTL2; - } else if (plls->wrpll1_refcount == 0) { - DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n", - pipe_name(pipe)); - reg = WRPLL_CTL1; - } else if (plls->wrpll2_refcount == 0) { - DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n", - pipe_name(pipe)); - reg = WRPLL_CTL2; - } else { - DRM_ERROR("No WRPLLs available!\n"); - return false; - } + intel_crtc->config.dpll_hw_state.wrpll = val; - DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", - clock, p, n2, r2); - - if (reg == WRPLL_CTL1) { - plls->wrpll1_refcount++; - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL1; - intel_crtc->config.shared_dpll = DPLL_ID_WRPLL1; - } else { - plls->wrpll2_refcount++; - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL2; - intel_crtc->config.shared_dpll = DPLL_ID_WRPLL2; + pll = intel_get_shared_dpll(intel_crtc); + if (pll == NULL) { + DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", + pipe_name(intel_crtc->pipe)); + return false; } - intel_crtc->config.dpll_hw_state.wrpll = val; - - pll = &dev_priv->shared_dplls[intel_crtc->config.shared_dpll]; - pll->hw_state.wrpll = val; + intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL(pll->id); } return true; } -/* - * To be called after intel_ddi_pll_select(). That one selects the PLL to be - * used, this one actually enables the PLL. - */ -void intel_ddi_pll_enable(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_ddi_plls *plls = &dev_priv->ddi_plls; - int refcount; - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - - switch (crtc->config.ddi_pll_sel) { - case PORT_CLK_SEL_WRPLL1: - case PORT_CLK_SEL_WRPLL2: - if (crtc->config.ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { - refcount = plls->wrpll1_refcount; - } else { - refcount = plls->wrpll2_refcount; - } - break; - default: - return; - } - - if (refcount == 1) { - pll->enable(dev_priv, pll); - } -} - void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; @@ -1054,35 +975,6 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, return false; } -void intel_ddi_setup_hw_pll_state(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - enum pipe pipe; - struct intel_crtc *intel_crtc; - - dev_priv->ddi_plls.wrpll1_refcount = 0; - dev_priv->ddi_plls.wrpll2_refcount = 0; - - for_each_pipe(pipe) { - intel_crtc = - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - - if (!intel_crtc->active) { - intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_NONE; - continue; - } - - switch (intel_crtc->config.ddi_pll_sel) { - case PORT_CLK_SEL_WRPLL1: - dev_priv->ddi_plls.wrpll1_refcount++; - break; - case PORT_CLK_SEL_WRPLL2: - dev_priv->ddi_plls.wrpll2_refcount++; - break; - } - } -} - void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc) { struct drm_crtc *crtc = &intel_crtc->base; @@ -1288,10 +1180,6 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) static void hsw_ddi_pll_enable(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) { - uint32_t cur_val; - - cur_val = I915_READ(WRPLL_CTL(pll->id)); - WARN(cur_val & WRPLL_PLL_ENABLE, "%s already enabled\n", pll->name); I915_WRITE(WRPLL_CTL(pll->id), pll->hw_state.wrpll); POSTING_READ(WRPLL_CTL(pll->id)); udelay(20); @@ -1303,7 +1191,6 @@ static void hsw_ddi_pll_disable(struct drm_i915_private *dev_priv, uint32_t val; val = I915_READ(WRPLL_CTL(pll->id)); - WARN_ON(!(val & WRPLL_PLL_ENABLE)); I915_WRITE(WRPLL_CTL(pll->id), val & ~WRPLL_PLL_ENABLE); POSTING_READ(WRPLL_CTL(pll->id)); } @@ -1334,11 +1221,9 @@ void intel_ddi_pll_init(struct drm_device *dev) uint32_t val = I915_READ(LCPLL_CTL); int i; - /* Dummy setup until everything is moved over to avoid upsetting the hw - * state cross checker. */ - dev_priv->num_shared_dpll = 0; + dev_priv->num_shared_dpll = 2; - for (i = 0; i < 2; i++) { + for (i = 0; i < dev_priv->num_shared_dpll; i++) { dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i]; dev_priv->shared_dplls[i].disable = hsw_ddi_pll_disable; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 851221d..a3305a0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1821,7 +1821,7 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc) pll->on = true; } -static void intel_disable_shared_dpll(struct intel_crtc *crtc) +void intel_disable_shared_dpll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3621,7 +3621,7 @@ static void lpt_pch_enable(struct drm_crtc *crtc) lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } -static void intel_put_shared_dpll(struct intel_crtc *crtc) +void intel_put_shared_dpll(struct intel_crtc *crtc) { struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); @@ -3641,7 +3641,7 @@ static void intel_put_shared_dpll(struct intel_crtc *crtc) crtc->config.shared_dpll = DPLL_ID_PRIVATE; } -static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc) +struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); @@ -7569,7 +7569,9 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (!intel_ddi_pll_select(intel_crtc)) return -EINVAL; - intel_ddi_pll_enable(intel_crtc); + + if (intel_crtc_to_shared_dpll(intel_crtc)) + intel_enable_shared_dpll(intel_crtc); intel_crtc->lowfreq_avail = false; @@ -12868,10 +12870,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) crtc->active ? "enabled" : "disabled"); } - /* FIXME: Smash this into the new shared dpll infrastructure. */ - if (HAS_DDI(dev)) - intel_ddi_setup_hw_pll_state(dev); - for (i = 0; i < dev_priv->num_shared_dpll; i++) { struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0c12558..a7d12f36 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -710,9 +710,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder); void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc); void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc); -void intel_ddi_setup_hw_pll_state(struct drm_device *dev); bool intel_ddi_pll_select(struct intel_crtc *crtc); -void intel_ddi_pll_enable(struct intel_crtc *crtc); void intel_ddi_put_crtc_pll(struct drm_crtc *crtc); void intel_ddi_set_pipe_settings(struct drm_crtc *crtc); void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder); @@ -796,12 +794,19 @@ __intel_framebuffer_create(struct drm_device *dev, void intel_prepare_page_flip(struct drm_device *dev, int plane); void intel_finish_page_flip(struct drm_device *dev, int pipe); void intel_finish_page_flip_plane(struct drm_device *dev, int plane); + +/* shared dpll functions */ struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, bool state); #define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) #define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) +void intel_disable_shared_dpll(struct intel_crtc *crtc); +struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc); +void intel_put_shared_dpll(struct intel_crtc *crtc); + +/* modesetting asserts */ void assert_pll(struct drm_i915_private *dev_priv, enum pipe pipe, bool state); #define assert_pll_enabled(d, p) assert_pll(d, p, true) -- cgit v0.10.2 From df8ad70ca34f9bdf7e070d1a430bbdcac6acee12 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:02:03 +0300 Subject: drm/i915: Only touch WRPLL hw state in enable/disable hooks To be able to do this we need to separately keep track of how many crtcs need a given WRPLL and how many actually actively use it. The common shared dpll framework already has all this, including massive state readout and cross checking. Which allows us to do this switch in a fairly small patch. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 60e67a1..b226724 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -386,16 +386,6 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) return ret; } -void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - if (intel_crtc_to_shared_dpll(intel_crtc)) - intel_disable_shared_dpll(intel_crtc); - - intel_put_shared_dpll(intel_crtc); -} - #define LC_FREQ 2700 #define LC_FREQ_2K (LC_FREQ * 2000) @@ -716,7 +706,7 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) int type = intel_encoder->type; int clock = intel_crtc->config.port_clock; - intel_ddi_put_crtc_pll(crtc); + intel_put_shared_dpll(intel_crtc); if (type == INTEL_OUTPUT_HDMI) { struct intel_shared_dpll *pll; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a3305a0..6362524 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4080,6 +4080,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) if (intel_crtc->active) return; + if (intel_crtc_to_shared_dpll(intel_crtc)) + intel_enable_shared_dpll(intel_crtc); + if (intel_crtc->config.has_dp_encoder) intel_dp_set_m_n(intel_crtc); @@ -4266,6 +4269,9 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); + + if (intel_crtc_to_shared_dpll(intel_crtc)) + intel_disable_shared_dpll(intel_crtc); } static void ironlake_crtc_off(struct drm_crtc *crtc) @@ -4274,10 +4280,6 @@ static void ironlake_crtc_off(struct drm_crtc *crtc) intel_put_shared_dpll(intel_crtc); } -static void haswell_crtc_off(struct drm_crtc *crtc) -{ - intel_ddi_put_crtc_pll(crtc); -} static void i9xx_pfit_enable(struct intel_crtc *crtc) { @@ -7570,9 +7572,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (!intel_ddi_pll_select(intel_crtc)) return -EINVAL; - if (intel_crtc_to_shared_dpll(intel_crtc)) - intel_enable_shared_dpll(intel_crtc); - intel_crtc->lowfreq_avail = false; return 0; @@ -12216,7 +12215,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; - dev_priv->display.off = haswell_crtc_off; + dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_primary_plane = ironlake_update_primary_plane; } else if (HAS_PCH_SPLIT(dev)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a7d12f36..fa19744 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -711,7 +711,6 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc); void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc); bool intel_ddi_pll_select(struct intel_crtc *crtc); -void intel_ddi_put_crtc_pll(struct drm_crtc *crtc); void intel_ddi_set_pipe_settings(struct drm_crtc *crtc); void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder); bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); @@ -802,7 +801,6 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, bool state); #define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) #define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) -void intel_disable_shared_dpll(struct intel_crtc *crtc); struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc); void intel_put_shared_dpll(struct intel_crtc *crtc); -- cgit v0.10.2 From 7709afa8198d4e0567d9fe1e922ef1d694e215bb Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:30:28 -0300 Subject: drm/i915: don't skip shared DPLL assertion on LPT Since we now have support for shared DPLLS. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6362524..15ade7e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1094,11 +1094,6 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, bool cur_state; struct intel_dpll_hw_state hw_state; - if (HAS_PCH_LPT(dev_priv->dev)) { - DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); - return; - } - if (WARN (!pll, "asserting DPLL %s with no DPLL\n", state_string(state))) return; -- cgit v0.10.2 From 930e8c9e79acc0e3f1702dcd2345f4584818c2e9 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 13:38:34 -0300 Subject: drm/i915: check the power domains in ironlake_get_pipe_config() Just like we already do in haswell_get_pipe_config(). This should prevent some WARNs when we run pm_rpm on SNB. Testcase: igt/pm_rpm Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80463 Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 15ade7e..54381d7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7240,6 +7240,10 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; uint32_t tmp; + if (!intel_display_power_enabled(dev_priv, + POWER_DOMAIN_PIPE(crtc->pipe))) + return false; + pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe; pipe_config->shared_dpll = DPLL_ID_PRIVATE; -- cgit v0.10.2 From 34a6c70f099ef33e04e7436ee962293beead7b90 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 13:38:35 -0300 Subject: drm/i915: check the power domains in intel_lvds_get_hw_state() Just like we do for the other encoders. This should fix some WARNs when running pm_rpm on SNB. Testcase: igt/pm_rpm Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80463 Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 2312602..cba04dd 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -71,8 +71,13 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + enum intel_display_power_domain power_domain; u32 tmp; + power_domain = intel_display_port_power_domain(encoder); + if (!intel_display_power_enabled(dev_priv, power_domain)) + return false; + tmp = I915_READ(lvds_encoder->reg); if (!(tmp & LVDS_PORT_EN)) -- cgit v0.10.2 From 1f835a77ac6061f346da2ce3f2c377ab2b5693c3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 13:38:36 -0300 Subject: drm/i915: don't read LVDS regs at compute_config time We may reach this point while the machine is still runtime suspended, so we'll hit a WARN. The other encoders also don't touch registers at this point, so instead of waking the machine up, write some code to keep the register always at the same state, including after we runtime suspend/resume. Testcase: igt/pm_rpm Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80463 Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index cba04dd..a3126e0 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -51,6 +51,7 @@ struct intel_lvds_encoder { bool is_dual_link; u32 reg; + u32 a3_power; struct intel_lvds_connector *attached_connector; }; @@ -170,8 +171,11 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder) /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. + * panels behave in the two modes. For now, let's just maintain the + * value we got from the BIOS. */ + temp &= ~LVDS_A3_POWER_MASK; + temp |= lvds_encoder->a3_power; /* Set the dithering flag on LVDS as needed, note that there is no * special lvds dither control bit on pch-split platforms, dithering is @@ -269,7 +273,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct intel_crtc_config *pipe_config) { struct drm_device *dev = intel_encoder->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&intel_encoder->base); struct intel_connector *intel_connector = @@ -284,8 +287,7 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, return false; } - if ((I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK) == - LVDS_A3_POWER_UP) + if (lvds_encoder->a3_power == LVDS_A3_POWER_UP) lvds_bpp = 8*3; else lvds_bpp = 6*3; @@ -1086,6 +1088,9 @@ out: DRM_DEBUG_KMS("detected %s-link lvds configuration\n", lvds_encoder->is_dual_link ? "dual" : "single"); + lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) & + LVDS_A3_POWER_MASK; + /* * Unlock registers and just * leave them unlocked -- cgit v0.10.2 From 755f68f4f32fc690957cfc94a19fff3139958299 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 10 Jul 2014 14:52:43 +0100 Subject: drm/i915: Don't cast a pointer to void* unnecessarily C is super happy to asign anything pointer to void *. Don't pretend otherwise. Signed-off-by: Damien Lespiau Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ce69185..2c0bad6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1596,7 +1596,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (dev_priv == NULL) return -ENOMEM; - dev->dev_private = (void *)dev_priv; + dev->dev_private = dev_priv; dev_priv->dev = dev; /* copy initial configuration to dev_priv->info */ -- cgit v0.10.2 From bf225f20d51fb1b77d47fce0628159a3eda027b9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 10 Jul 2014 20:31:18 +0100 Subject: drm/i915: Move RPS evaluation interval counters to i915->rps Place the RPS counters inside the RPS struct. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 647ea67..263a879 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -902,10 +902,10 @@ struct vlv_s0ix_state { u32 clock_gate_dis2; }; -struct intel_rps_ei_calc { - u32 cz_ts_ei; - u32 render_ei_c0; - u32 media_ei_c0; +struct intel_rps_ei { + u32 cz_clock; + u32 render_c0; + u32 media_c0; }; struct intel_gen6_power_mgmt { @@ -940,6 +940,9 @@ struct intel_gen6_power_mgmt { bool enabled; struct delayed_work delayed_resume_work; + /* manual wa residency calculations */ + struct intel_rps_ei up_ei, down_ei; + /* * Protects RPS/RC6 register access and PCU communication. * Must be taken after struct_mutex if nested. @@ -1534,13 +1537,6 @@ struct drm_i915_private { /* gen6+ rps state */ struct intel_gen6_power_mgmt rps; - /* rps wa up ei calculation */ - struct intel_rps_ei_calc rps_up_ei; - - /* rps wa down ei calculation */ - struct intel_rps_ei_calc rps_down_ei; - - /* ilk-only ips/rps state. Everything in here is protected by the global * mchdev_lock in intel_pm.c */ struct intel_ilk_power_mgmt ips; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0b4a8ed..30fd637 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1273,7 +1273,7 @@ static void notify_ring(struct drm_device *dev, } static u32 vlv_c0_residency(struct drm_i915_private *dev_priv, - struct intel_rps_ei_calc *rps_ei) + struct intel_rps_ei *rps_ei) { u32 cz_ts, cz_freq_khz; u32 render_count, media_count; @@ -1286,22 +1286,22 @@ static u32 vlv_c0_residency(struct drm_i915_private *dev_priv, render_count = I915_READ(VLV_RENDER_C0_COUNT_REG); media_count = I915_READ(VLV_MEDIA_C0_COUNT_REG); - if (rps_ei->cz_ts_ei == 0) { - rps_ei->cz_ts_ei = cz_ts; - rps_ei->render_ei_c0 = render_count; - rps_ei->media_ei_c0 = media_count; + if (rps_ei->cz_clock == 0) { + rps_ei->cz_clock = cz_ts; + rps_ei->render_c0 = render_count; + rps_ei->media_c0 = media_count; return dev_priv->rps.cur_freq; } - elapsed_time = cz_ts - rps_ei->cz_ts_ei; - rps_ei->cz_ts_ei = cz_ts; + elapsed_time = cz_ts - rps_ei->cz_clock; + rps_ei->cz_clock = cz_ts; - elapsed_render = render_count - rps_ei->render_ei_c0; - rps_ei->render_ei_c0 = render_count; + elapsed_render = render_count - rps_ei->render_c0; + rps_ei->render_c0 = render_count; - elapsed_media = media_count - rps_ei->media_ei_c0; - rps_ei->media_ei_c0 = media_count; + elapsed_media = media_count - rps_ei->media_c0; + rps_ei->media_c0 = media_count; /* Convert all the counters into common unit of milli sec */ elapsed_time /= VLV_CZ_CLOCK_TO_MILLI_SEC; @@ -1337,9 +1337,9 @@ static u32 vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv) WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); - if (dev_priv->rps_up_ei.cz_ts_ei == 0) { - vlv_c0_residency(dev_priv, &dev_priv->rps_up_ei); - vlv_c0_residency(dev_priv, &dev_priv->rps_down_ei); + if (dev_priv->rps.up_ei.cz_clock == 0) { + vlv_c0_residency(dev_priv, &dev_priv->rps.up_ei); + vlv_c0_residency(dev_priv, &dev_priv->rps.down_ei); return dev_priv->rps.cur_freq; } @@ -1354,10 +1354,10 @@ static u32 vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv) dev_priv->rps.ei_interrupt_count = 0; residency_C0_down = vlv_c0_residency(dev_priv, - &dev_priv->rps_down_ei); + &dev_priv->rps.down_ei); } else { residency_C0_up = vlv_c0_residency(dev_priv, - &dev_priv->rps_up_ei); + &dev_priv->rps.up_ei); } new_delay = dev_priv->rps.cur_freq; -- cgit v0.10.2 From 7b3c29f6fd374640266d82aafe6929761469e27b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 10 Jul 2014 20:31:19 +0100 Subject: drm/i915: Make the RPS interrupt generation mask handle the vlv wa We can eliminate a lot of special case code by making the computation of the interrupt mask be correct for all callers. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 55228df..780c3ab 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3183,6 +3183,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val) if (val < dev_priv->rps.max_freq_softlimit) mask |= GEN6_PM_RP_UP_THRESHOLD; + mask |= dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED); + mask &= dev_priv->pm_rps_events; + /* IVB and SNB hard hangs on looping batchbuffer * if GEN6_PM_UP_EI_EXPIRED is masked. */ @@ -3276,11 +3279,8 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) vlv_force_gfx_clock(dev_priv, false); - if (dev_priv->pm_rps_events & GEN6_PM_RP_UP_EI_EXPIRED) - I915_WRITE(GEN6_PMINTRMSK, ~dev_priv->pm_rps_events); - else - I915_WRITE(GEN6_PMINTRMSK, - gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); + I915_WRITE(GEN6_PMINTRMSK, + gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); } void gen6_rps_idle(struct drm_i915_private *dev_priv) -- cgit v0.10.2 From ca2aed6cec9ed3a11a95771d8ef4265247031157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 28 Jun 2014 02:03:56 +0300 Subject: drm/i915: Don't disable PPGTT for CHV based in PCI rev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 62942ed7279d3e06dc15ae3d47665eff3b373327 Author: Jesse Barnes Date: Fri Jun 13 09:28:33 2014 -0700 drm/i915/vlv: disable PPGTT on early revs v3 we forgot about CHV. IS_VALLEYVIEW() is true for CHV, so we need to explicitly avoid disabling PPGTT on CHV. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a4153ee..5188936 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -64,7 +64,8 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt) #endif /* Early VLV doesn't have this */ - if (IS_VALLEYVIEW(dev) && dev->pdev->revision < 0xb) { + if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && + dev->pdev->revision < 0xb) { DRM_DEBUG_DRIVER("disabling PPGTT on pre-B3 step VLV\n"); return 0; } -- cgit v0.10.2 From 03af20458a57a50735b12c1e3c23abc7ff70c6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 28 Jun 2014 02:03:53 +0300 Subject: drm/i915: Use the cached min/min/rpe values in the vlv debugfs code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to re-read the hardware rps fuses when we already have all the values tucked away in dev_priv->rps. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4a5b0f8..981ca42 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1108,20 +1108,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "Max overclocked frequency: %dMHz\n", dev_priv->rps.max_freq * GT_FREQUENCY_MULTIPLIER); } else if (IS_VALLEYVIEW(dev)) { - u32 freq_sts, val; + u32 freq_sts; mutex_lock(&dev_priv->rps.hw_lock); freq_sts = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); - val = valleyview_rps_max_freq(dev_priv); seq_printf(m, "max GPU freq: %d MHz\n", - vlv_gpu_freq(dev_priv, val)); + dev_priv->rps.max_freq); - val = valleyview_rps_min_freq(dev_priv); seq_printf(m, "min GPU freq: %d MHz\n", - vlv_gpu_freq(dev_priv, val)); + dev_priv->rps.min_freq); + + seq_printf(m, "efficient (RPe) frequency: %d MHz\n", + dev_priv->rps.efficient_freq); seq_printf(m, "current GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv, (freq_sts >> 8) & 0xff)); @@ -3632,8 +3633,8 @@ i915_max_freq_set(void *data, u64 val) if (IS_VALLEYVIEW(dev)) { val = vlv_freq_opcode(dev_priv, val); - hw_max = valleyview_rps_max_freq(dev_priv); - hw_min = valleyview_rps_min_freq(dev_priv); + hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; } else { do_div(val, GT_FREQUENCY_MULTIPLIER); @@ -3713,8 +3714,8 @@ i915_min_freq_set(void *data, u64 val) if (IS_VALLEYVIEW(dev)) { val = vlv_freq_opcode(dev_priv, val); - hw_max = valleyview_rps_max_freq(dev_priv); - hw_min = valleyview_rps_min_freq(dev_priv); + hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; } else { do_div(val, GT_FREQUENCY_MULTIPLIER); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 263a879..7031757 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2684,8 +2684,6 @@ extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void intel_init_pch_refclk(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void valleyview_set_rps(struct drm_device *dev, u8 val); -extern int valleyview_rps_max_freq(struct drm_i915_private *dev_priv); -extern int valleyview_rps_min_freq(struct drm_i915_private *dev_priv); extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable); extern void intel_detect_pch(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 780c3ab..2bc08a2 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3781,7 +3781,7 @@ void gen6_update_ring_freq(struct drm_device *dev) mutex_unlock(&dev_priv->rps.hw_lock); } -int cherryview_rps_max_freq(struct drm_i915_private *dev_priv) +static int cherryview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; @@ -3801,7 +3801,7 @@ static int cherryview_rps_rpe_freq(struct drm_i915_private *dev_priv) return rpe; } -int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) +static int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) { u32 val, rpn; @@ -3810,7 +3810,7 @@ int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) return rpn; } -int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) +static int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; @@ -3835,7 +3835,7 @@ static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) return rpe; } -int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) +static int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) { return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff; } -- cgit v0.10.2 From 7e435aad38083f378ba7889d8b8fdcdbfe7088e7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Jun 2014 11:07:12 +0100 Subject: drm: add of_graph endpoint helper to find possible CRTCs Add a helper to allow encoders to find their possible CRTCs from the OF graph without having to re-implement this functionality. We add a device_node to drm_crtc which corresponds with the port node in the DT description of the CRTC device. We can then scan the DRM device list for CRTCs to find their index, matching the appropriate CRTC using the port device_node, thus building up the possible CRTC mask. Reviewed-by: Rob Clark Signed-off-by: Russell King diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd2ba42..533d011 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o +drm-$(CONFIG_OF) += drm_of.o drm-usb-y := drm_usb.o diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c new file mode 100644 index 0000000..16150a0 --- /dev/null +++ b/drivers/gpu/drm/drm_of.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +/** + * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node + * @dev: DRM device + * @port: port OF node + * + * Given a port OF node, return the possible mask of the corresponding + * CRTC within a device's list of CRTCs. Returns zero if not found. + */ +static uint32_t drm_crtc_port_mask(struct drm_device *dev, + struct device_node *port) +{ + unsigned int index = 0; + struct drm_crtc *tmp; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp->port == port) + return 1 << index; + + index++; + } + + return 0; +} + +/** + * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port + * @dev: DRM device + * @port: encoder port to scan for endpoints + * + * Scan all endpoints attached to a port, locate their attached CRTCs, + * and generate the DRM mask of CRTCs which may be attached to this + * encoder. + * + * See Documentation/devicetree/bindings/graph.txt for the bindings. + */ +uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + struct device_node *remote_port, *ep = NULL; + uint32_t possible_crtcs = 0; + + do { + ep = of_graph_get_next_endpoint(port, ep); + if (!ep) + break; + + remote_port = of_graph_get_remote_port(ep); + if (!remote_port) { + of_node_put(ep); + return 0; + } + + possible_crtcs |= drm_crtc_port_mask(dev, remote_port); + + of_node_put(remote_port); + } while (1); + + return possible_crtcs; +} +EXPORT_SYMBOL(drm_of_find_possible_crtcs); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e..6a94909 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,6 +41,7 @@ struct drm_framebuffer; struct drm_object_properties; struct drm_file; struct drm_clip_rect; +struct device_node; #define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 @@ -314,6 +315,7 @@ struct drm_crtc_funcs { */ struct drm_crtc { struct drm_device *dev; + struct device_node *port; struct list_head head; /** diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h new file mode 100644 index 0000000..2441f71 --- /dev/null +++ b/include/drm/drm_of.h @@ -0,0 +1,18 @@ +#ifndef __DRM_OF_H__ +#define __DRM_OF_H__ + +struct drm_device; +struct device_node; + +#ifdef CONFIG_OF +extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port); +#else +static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + return 0; +} +#endif + +#endif /* __DRM_OF_H__ */ -- cgit v0.10.2 From 0101fd0043075a2855ea98f07e6f2c7a72a5cbab Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Feb 2014 11:57:06 +0000 Subject: drm/armada: convert to componentized support Convert the Armada DRM driver to use the component helpers, which will permit us to clean up the driver and move towards an implementation which is compatible with a DT description of the hardware. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 3995be3..2b6fc6c 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -6,7 +6,9 @@ * published by the Free Software Foundation. */ #include +#include #include +#include #include #include #include "armada_crtc.h" @@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = { }; #endif +static bool is_componentized(struct device *dev) +{ + return dev->of_node || dev->platform_data; +} + static void armada_drm_unref_work(struct work_struct *work) { struct armada_private *priv = @@ -166,26 +173,35 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; } + if (is_componentized(dev->dev)) { + ret = component_bind_all(dev->dev, dev); + if (ret) + goto err_kms; + } else { #ifdef CONFIG_DRM_ARMADA_TDA1998X - ret = armada_drm_connector_slave_create(dev, &tda19988_config); - if (ret) - goto err_kms; + ret = armada_drm_connector_slave_create(dev, &tda19988_config); + if (ret) + goto err_kms; #endif + } ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) - goto err_kms; + goto err_comp; dev->vblank_disable_allowed = 1; ret = armada_fbdev_init(dev); if (ret) - goto err_kms; + goto err_comp; drm_kms_helper_poll_init(dev); return 0; + err_comp: + if (is_componentized(dev->dev)) + component_unbind_all(dev->dev, dev); err_kms: drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -200,6 +216,10 @@ static int armada_drm_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); armada_fbdev_fini(dev); + + if (is_componentized(dev->dev)) + component_unbind_all(dev->dev, dev); + drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); flush_work(&priv->fb_unref_work); @@ -314,14 +334,135 @@ static struct drm_driver armada_drm_driver = { .fops = &armada_drm_fops, }; +static int armada_drm_bind(struct device *dev) +{ + return drm_platform_init(&armada_drm_driver, to_platform_device(dev)); +} + +static void armada_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int compare_dev_name(struct device *dev, void *data) +{ + const char *name = data; + return !strcmp(dev_name(dev), name); +} + +static void armada_add_endpoints(struct device *dev, + struct component_match **match, struct device_node *port) +{ + struct device_node *ep, *remote; + + for_each_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; + } + + component_match_add(dev, match, compare_of, remote); + of_node_put(remote); + } +} + +static int armada_drm_find_components(struct device *dev, + struct component_match **match) +{ + struct device_node *port; + int i; + + if (dev->of_node) { + struct device_node *np = dev->of_node; + + for (i = 0; ; i++) { + port = of_parse_phandle(np, "ports", i); + if (!port) + break; + + component_match_add(dev, match, compare_of, port); + of_node_put(port); + } + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + for (i = 0; ; i++) { + port = of_parse_phandle(np, "ports", i); + if (!port) + break; + + armada_add_endpoints(dev, match, port); + of_node_put(port); + } + } else if (dev->platform_data) { + char **devices = dev->platform_data; + struct device *d; + + for (i = 0; devices[i]; i++) + component_match_add(dev, match, compare_dev_name, + devices[i]); + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + for (i = 0; devices[i]; i++) { + d = bus_find_device_by_name(&platform_bus_type, NULL, + devices[i]); + if (d && d->of_node) { + for_each_child_of_node(d->of_node, port) + armada_add_endpoints(dev, match, port); + } + put_device(d); + } + } + + return 0; +} + +static const struct component_master_ops armada_master_ops = { + .bind = armada_drm_bind, + .unbind = armada_drm_unbind, +}; + static int armada_drm_probe(struct platform_device *pdev) { - return drm_platform_init(&armada_drm_driver, pdev); + if (is_componentized(&pdev->dev)) { + struct component_match *match = NULL; + int ret; + + ret = armada_drm_find_components(&pdev->dev, &match); + if (ret < 0) + return ret; + + return component_master_add_with_match(&pdev->dev, + &armada_master_ops, match); + } else { + return drm_platform_init(&armada_drm_driver, pdev); + } } static int armada_drm_remove(struct platform_device *pdev) { - drm_put_dev(platform_get_drvdata(pdev)); + if (is_componentized(&pdev->dev)) + component_master_del(&pdev->dev, &armada_master_ops); + else + drm_put_dev(platform_get_drvdata(pdev)); return 0; } -- cgit v0.10.2 From fe42487284efc584a43e75a835881bff2cf21b6a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 5 Jul 2014 11:16:58 +0100 Subject: drm/armada: update Armada 510 (Dove) to use "ext_ref_clk1" as the clock Remove the underscore between "clk" and "1" so that we match the name of the clock given in the documentation. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 1bcf1a8..ad3d2eb 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -19,7 +19,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) { struct clk *clk; - clk = devm_clk_get(dev, "ext_ref_clk_1"); + clk = devm_clk_get(dev, "ext_ref_clk1"); if (IS_ERR(clk)) return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk); -- cgit v0.10.2 From ad49579adfd4b8097b391f838b62b5a6fa346a4a Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 30 Jun 2014 15:38:18 +0100 Subject: dt-bindings: add Marvell Dove LCD controller documentation Add the Marvell Dove LCD controller DT binding documentation. The clock names used here are intentionally taken from the specification for the Dove SoC. Signed-off-by: Russell King diff --git a/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt new file mode 100644 index 0000000..46525ea --- /dev/null +++ b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt @@ -0,0 +1,30 @@ +Device Tree bindings for Armada DRM CRTC driver + +Required properties: + - compatible: value should be "marvell,dove-lcd". + - reg: base address and size of the LCD controller + - interrupts: single interrupt number for the LCD controller + - port: video output port with endpoints, as described by graph.txt + +Optional properties: + + - clocks: as described by clock-bindings.txt + - clock-names: as described by clock-bindings.txt + "axiclk" - axi bus clock for pixel clock + "plldivider" - pll divider clock for pixel clock + "ext_ref_clk0" - external clock 0 for pixel clock + "ext_ref_clk1" - external clock 1 for pixel clock + +Note: all clocks are optional but at least one must be specified. +Further clocks may be added in the future according to requirements of +different SoCs. + +Example: + + lcd0: lcd-controller@820000 { + compatible = "marvell,dove-lcd"; + reg = <0x820000 0x1000>; + interrupts = <47>; + clocks = <&si5351 0>; + clock-names = "ext_ref_clk_1"; + }; -- cgit v0.10.2 From e1e9fb840b684703cbc65eaef99f69194e0f161f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jun 2014 22:02:04 +0300 Subject: drm/i915: ddi: enable runtime pm during dpms Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni [danvet: Remove now bogus comment.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 54381d7..f31ad8d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4890,17 +4890,10 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) if (enable) { if (!intel_crtc->active) { - /* - * FIXME: DDI plls and relevant code isn't converted - * yet, so do runtime PM for DPMS only for all other - * platforms for now. - */ - if (!HAS_DDI(dev)) { - domains = get_crtc_power_domains(crtc); - for_each_power_domain(domain, domains) - intel_display_power_get(dev_priv, domain); - intel_crtc->enabled_power_domains = domains; - } + domains = get_crtc_power_domains(crtc); + for_each_power_domain(domain, domains) + intel_display_power_get(dev_priv, domain); + intel_crtc->enabled_power_domains = domains; dev_priv->display.crtc_enable(crtc); } @@ -4908,12 +4901,10 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) if (intel_crtc->active) { dev_priv->display.crtc_disable(crtc); - if (!HAS_DDI(dev)) { - domains = intel_crtc->enabled_power_domains; - for_each_power_domain(domain, domains) - intel_display_power_put(dev_priv, domain); - intel_crtc->enabled_power_domains = 0; - } + domains = intel_crtc->enabled_power_domains; + for_each_power_domain(domain, domains) + intel_display_power_put(dev_priv, domain); + intel_crtc->enabled_power_domains = 0; } } -- cgit v0.10.2 From a62d149758d714be4bf0d70b9bd5328e7995c552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 28 Jun 2014 02:04:01 +0300 Subject: drm/i915: Call encoder->post_disable() in intel_sanitize_encoder() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VLV and CHV disable the DP port only in the .post_disable() hook, so we need to make intel_sanitize_encoder() call that when it's trying to disable encoders without an active pipes. My bsw actaully hits this when an external display is connected. The BIOS still likes to turn on the eDP port, but leaves the pipe disabled. Signed-off-by: Ville Syrjälä Reviewed-by: Rafael Barbalho Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f31ad8d..d2b752d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12775,6 +12775,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) encoder->base.base.id, encoder->base.name); encoder->disable(encoder); + if (encoder->post_disable) + encoder->post_disable(encoder); } encoder->base.crtc = NULL; encoder->connectors_active = false; -- cgit v0.10.2 From f8f2b001a1247b54ece090c1c86193b1f3f997fe Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 10 Jul 2014 13:16:21 +0530 Subject: drm/i915: Read guaranteed freq for valleyview Reading RP1 for valleyview to help us enable "pm_rps" i-g-t testcase execution. Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala [danvet: Add missing static.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 2bc08a2..8066ca5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3810,6 +3810,17 @@ static int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) return rpn; } +static int valleyview_rps_guar_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rp1; + + val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE); + + rp1 = (val & FB_GFX_FGUARANTEED_FREQ_FUSE_MASK) >> FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT; + + return rp1; +} + static int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; @@ -3952,6 +3963,11 @@ static void valleyview_init_gt_powersave(struct drm_device *dev) vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), dev_priv->rps.efficient_freq); + dev_priv->rps.rp1_freq = valleyview_rps_guar_freq(dev_priv); + DRM_DEBUG_DRIVER("RP1(Guar Freq) GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), + dev_priv->rps.rp1_freq); + dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv); DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), -- cgit v0.10.2 From 74c4f62bcdbb0e2ee115197eafd4edc05bbcf55c Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 10 Jul 2014 13:16:22 +0530 Subject: drm/i915: Add RP0/RP1/RPn render P state thresholds in VLV sysfs This is useful for userspace utilities to verify and micromanaging the increase/decrease frequncy. Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 86ce39a..b15c8ce 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -461,11 +461,20 @@ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr mutex_unlock(&dev->struct_mutex); if (attr == &dev_attr_gt_RP0_freq_mhz) { - val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + val = vlv_gpu_freq(dev_priv, dev_priv->rps.rp0_freq); + else + val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER; } else if (attr == &dev_attr_gt_RP1_freq_mhz) { - val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + val = vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq); + else + val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER; } else if (attr == &dev_attr_gt_RPn_freq_mhz) { - val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq); + else + val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER; } else { BUG(); } @@ -486,6 +495,9 @@ static const struct attribute *vlv_attrs[] = { &dev_attr_gt_cur_freq_mhz.attr, &dev_attr_gt_max_freq_mhz.attr, &dev_attr_gt_min_freq_mhz.attr, + &dev_attr_gt_RP0_freq_mhz.attr, + &dev_attr_gt_RP1_freq_mhz.attr, + &dev_attr_gt_RPn_freq_mhz.attr, &dev_attr_vlv_rpe_freq_mhz.attr, NULL, }; -- cgit v0.10.2 From 67c3bf6f55a97a0915a0f9ea07278a3073cc9601 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 10 Jul 2014 13:16:24 +0530 Subject: drm/i915: populate mem_freq/cz_clock for chv We need mem_freq or cz clock for freq/opcode conversion Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7031757..8620ea9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -931,6 +931,7 @@ struct intel_gen6_power_mgmt { u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */ u8 rp1_freq; /* "less than" RP0 power/freqency */ u8 rp0_freq; /* Non-overclocked max frequency. */ + u32 cz_freq; u32 ei_interrupt_count; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2d2c4de..0ebe0f4 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5538,6 +5538,12 @@ enum punit_power_well { GEN6_PM_RP_DOWN_THRESHOLD | \ GEN6_PM_RP_DOWN_TIMEOUT) +#define CHV_CZ_CLOCK_FREQ_MODE_200 200 +#define CHV_CZ_CLOCK_FREQ_MODE_267 267 +#define CHV_CZ_CLOCK_FREQ_MODE_320 320 +#define CHV_CZ_CLOCK_FREQ_MODE_333 333 +#define CHV_CZ_CLOCK_FREQ_MODE_400 400 + #define GEN7_GT_SCRATCH_BASE 0x4F100 #define GEN7_GT_SCRATCH_REG_NUM 8 diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8066ca5..32ccd7a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5700,6 +5700,35 @@ static void valleyview_init_clock_gating(struct drm_device *dev) static void cherryview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; + + mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, CCK_FUSE_REG); + mutex_unlock(&dev_priv->rps.hw_lock); + switch ((val >> 2) & 0x7) { + case 0: + case 1: + dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_200; + dev_priv->mem_freq = 1600; + break; + case 2: + dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_267; + dev_priv->mem_freq = 1600; + break; + case 3: + dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_333; + dev_priv->mem_freq = 2000; + break; + case 4: + dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_320; + dev_priv->mem_freq = 1600; + break; + case 5: + dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_400; + dev_priv->mem_freq = 1600; + break; + } + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); -- cgit v0.10.2 From 22b1b2f866b2089d8264e367121c9c9ee0689da4 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Sat, 12 Jul 2014 14:54:33 +0530 Subject: drm/i915: CHV GPU frequency to opcode functions Adding chv specific fre/encode conversion. v2: Remove generic function and platform check (Daniel) Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 32ccd7a..1ec777a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6926,7 +6926,7 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } -int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val) +int byt_gpu_freq(struct drm_i915_private *dev_priv, int val) { int div; @@ -6948,7 +6948,7 @@ int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val) return DIV_ROUND_CLOSEST(dev_priv->mem_freq * (val + 6 - 0xbd), 4 * div); } -int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val) +int byt_freq_opcode(struct drm_i915_private *dev_priv, int val) { int mul; @@ -6970,6 +6970,80 @@ int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val) return DIV_ROUND_CLOSEST(4 * mul * val, dev_priv->mem_freq) + 0xbd - 6; } +int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) +{ + int div, freq; + + switch (dev_priv->rps.cz_freq) { + case 200: + div = 5; + break; + case 267: + div = 6; + break; + case 320: + case 333: + case 400: + div = 8; + break; + default: + return -1; + } + + freq = (DIV_ROUND_CLOSEST((dev_priv->rps.cz_freq * val), 2 * div) / 2); + + return freq; +} + +int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) +{ + int mul, opcode; + + switch (dev_priv->rps.cz_freq) { + case 200: + mul = 5; + break; + case 267: + mul = 6; + break; + case 320: + case 333: + case 400: + mul = 8; + break; + default: + return -1; + } + + opcode = (DIV_ROUND_CLOSEST((val * 2 * mul), dev_priv->rps.cz_freq) * 2); + + return opcode; +} + +int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val) +{ + int ret = -1; + + if (IS_CHERRYVIEW(dev_priv->dev)) + ret = chv_gpu_freq(dev_priv, val); + else if (IS_VALLEYVIEW(dev_priv->dev)) + ret = byt_gpu_freq(dev_priv, val); + + return ret; +} + +int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val) +{ + int ret = -1; + + if (IS_CHERRYVIEW(dev_priv->dev)) + ret = chv_freq_opcode(dev_priv, val); + else if (IS_VALLEYVIEW(dev_priv->dev)) + ret = byt_freq_opcode(dev_priv, val); + + return ret; +} + void intel_pm_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; -- cgit v0.10.2 From 3497a5620caec0ae25e3fa3b6828f1cdeac80ec0 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 10 Jul 2014 13:16:26 +0530 Subject: drm/i915/chv: Add basic PM interrupt support for CHV Enabled PM interrupt programming for CHV. Re-using gen8 code and extending same for CHV. Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 30fd637..188fe04 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1407,7 +1407,7 @@ static void gen6_pm_rps_work(struct work_struct *work) spin_lock_irq(&dev_priv->irq_lock); pm_iir = dev_priv->rps.pm_iir; dev_priv->rps.pm_iir = 0; - if (IS_BROADWELL(dev_priv->dev)) + if (IS_BROADWELL(dev_priv->dev) || IS_CHERRYVIEW(dev_priv->dev)) bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); else { /* Make sure not to corrupt PMIMR state used by ringbuffer */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1ec777a..58a03f8 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3392,6 +3392,8 @@ static void cherryview_disable_rps(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(GEN6_RC_CONTROL, 0); + + gen8_disable_rps_interrupts(dev); } static void valleyview_disable_rps(struct drm_device *dev) @@ -4109,6 +4111,8 @@ static void cherryview_enable_rps(struct drm_device *dev) valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq); + gen8_enable_rps_interrupts(dev); + gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); } -- cgit v0.10.2 From 7707df4ad6c73e005098c4b4db2f86494e9d404d Mon Sep 17 00:00:00 2001 From: Deepak S Date: Sat, 12 Jul 2014 18:46:14 +0530 Subject: drm/i915: Add RP1 render P state thresholds in CHV This is useful for userspace utilities to verify and micromanaging the increase/decrease frequncy. v2: Use vlv_gpu_freq to get freq (Deepak) Signed-off-by: Deepak S Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 58a03f8..97be125 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3803,6 +3803,16 @@ static int cherryview_rps_rpe_freq(struct drm_i915_private *dev_priv) return rpe; } +static int cherryview_rps_guar_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rp1; + + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); + rp1 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK; + + return rp1; +} + static int cherryview_rps_min_freq(struct drm_i915_private *dev_priv) { u32 val, rpn; @@ -4004,6 +4014,11 @@ static void cherryview_init_gt_powersave(struct drm_device *dev) vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), dev_priv->rps.efficient_freq); + dev_priv->rps.rp1_freq = cherryview_rps_guar_freq(dev_priv); + DRM_DEBUG_DRIVER("RP1(Guar) GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq), + dev_priv->rps.rp1_freq); + dev_priv->rps.min_freq = cherryview_rps_min_freq(dev_priv); DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), -- cgit v0.10.2 From d8c96083cf5e4a910c20836414e8a06ebcf07317 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 11:10:15 +0100 Subject: drm/armada: permit CRTCs to be registered as separate devices Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index afa4974..0642b5c 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -7,6 +7,9 @@ * published by the Free Software Foundation. */ #include +#include +#include +#include #include #include #include "armada_crtc.h" @@ -1046,19 +1049,19 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) return 0; } -int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, - int irq, const struct armada_variant *variant) +int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, + struct resource *res, int irq, const struct armada_variant *variant) { - struct armada_private *priv = dev->dev_private; + struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; void __iomem *base; int ret; - ret = armada_drm_crtc_create_properties(dev); + ret = armada_drm_crtc_create_properties(drm); if (ret) return ret; - base = devm_request_and_ioremap(dev->dev, res); + base = devm_request_and_ioremap(dev, res); if (!base) { DRM_ERROR("failed to ioremap register\n"); return -ENOMEM; @@ -1070,9 +1073,12 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return -ENOMEM; } + if (dev != drm->dev) + dev_set_drvdata(dev, dcrtc); + dcrtc->variant = variant; dcrtc->base = base; - dcrtc->num = dev->mode_config.num_crtc; + dcrtc->num = drm->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); dcrtc->csc_yuv_mode = CSC_AUTO; dcrtc->csc_rgb_mode = CSC_AUTO; @@ -1105,7 +1111,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, } if (dcrtc->variant->init) { - ret = dcrtc->variant->init(dcrtc, dev->dev); + ret = dcrtc->variant->init(dcrtc, dev); if (ret) { kfree(dcrtc); return ret; @@ -1117,7 +1123,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, priv->dcrtc[dcrtc->num] = dcrtc; - drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs); + drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, @@ -1125,5 +1131,94 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, dcrtc->csc_rgb_mode); - return armada_overlay_plane_create(dev, 1 << dcrtc->num); + return armada_overlay_plane_create(drm, 1 << dcrtc->num); +} + +static int +armada_lcd_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + const struct armada_variant *variant; + + if (irq < 0) + return irq; + + if (!dev->of_node) { + const struct platform_device_id *id; + + id = platform_get_device_id(pdev); + if (!id) + return -ENXIO; + + variant = (const struct armada_variant *)id->driver_data; + } else { + const struct of_device_id *match; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match) + return -ENXIO; + + variant = match->data; + } + + return armada_drm_crtc_create(drm, dev, res, irq, variant); +} + +static void +armada_lcd_unbind(struct device *dev, struct device *master, void *data) +{ + struct armada_crtc *dcrtc = dev_get_drvdata(dev); + + armada_drm_crtc_destroy(&dcrtc->crtc); +} + +static const struct component_ops armada_lcd_ops = { + .bind = armada_lcd_bind, + .unbind = armada_lcd_unbind, +}; + +static int armada_lcd_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &armada_lcd_ops); +} + +static int armada_lcd_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &armada_lcd_ops); + return 0; } + +static struct of_device_id armada_lcd_of_match[] = { + { + .compatible = "marvell,dove-lcd", + .data = &armada510_ops, + }, + {} +}; +MODULE_DEVICE_TABLE(of, armada_lcd_of_match); + +static const struct platform_device_id armada_lcd_platform_ids[] = { + { + .name = "armada-lcd", + .driver_data = (unsigned long)&armada510_ops, + }, { + .name = "armada-510-lcd", + .driver_data = (unsigned long)&armada510_ops, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); + +struct platform_driver armada_lcd_platform_driver = { + .probe = armada_lcd_probe, + .remove = armada_lcd_remove, + .driver = { + .name = "armada-lcd", + .owner = THIS_MODULE, + .of_match_table = armada_lcd_of_match, + }, + .id_table = armada_lcd_platform_ids, +}; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 73efcfc..0d9a803 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -75,12 +75,14 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, struct resource *, int, - const struct armada_variant *); +int armada_drm_crtc_create(struct drm_device *, struct device *, + struct resource *, int, const struct armada_variant *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); +extern struct platform_driver armada_lcd_platform_driver; + #endif diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 2b6fc6c..f237f44 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -115,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) return -EINVAL; } - if (!res[0] || !mem) + if (!mem) return -ENXIO; if (!devm_request_mem_region(dev->dev, mem->start, @@ -168,7 +168,8 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, res[n], irq, variant); + ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, + variant); if (ret) goto err_kms; } @@ -490,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = { static int __init armada_drm_init(void) { + int ret; + armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); - return platform_driver_register(&armada_drm_platform_driver); + + ret = platform_driver_register(&armada_lcd_platform_driver); + if (ret) + return ret; + ret = platform_driver_register(&armada_drm_platform_driver); + if (ret) + platform_driver_unregister(&armada_lcd_platform_driver); + return ret; } module_init(armada_drm_init); static void __exit armada_drm_exit(void) { platform_driver_unregister(&armada_drm_platform_driver); + platform_driver_unregister(&armada_lcd_platform_driver); } module_exit(armada_drm_exit); -- cgit v0.10.2 From 9611cb93fa65dde199f4f888bd034ffc80c7adf0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Jun 2014 11:21:23 +0100 Subject: drm/armada: register crtc with port Register the CRTC with the port node so that the DRM OF helpers can find the appropriate CRTC. This is important so that encoders can identify their corresponding possible CRTCs. Signed-off-by: Russell King diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 0642b5c..3f620e2 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -910,6 +910,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); + of_node_put(dcrtc->crtc.port); + kfree(dcrtc); } @@ -1050,7 +1052,8 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, - struct resource *res, int irq, const struct armada_variant *variant) + struct resource *res, int irq, const struct armada_variant *variant, + struct device_node *port) { struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; @@ -1123,6 +1126,7 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, priv->dcrtc[dcrtc->num] = dcrtc; + dcrtc->crtc.port = port; drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); @@ -1142,6 +1146,7 @@ armada_lcd_bind(struct device *dev, struct device *master, void *data) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); const struct armada_variant *variant; + struct device_node *port = NULL; if (irq < 0) return irq; @@ -1156,15 +1161,27 @@ armada_lcd_bind(struct device *dev, struct device *master, void *data) variant = (const struct armada_variant *)id->driver_data; } else { const struct of_device_id *match; + struct device_node *np, *parent = dev->of_node; match = of_match_device(dev->driver->of_match_table, dev); if (!match) return -ENXIO; + np = of_get_child_by_name(parent, "ports"); + if (np) + parent = np; + port = of_get_child_by_name(parent, "port"); + of_node_put(np); + if (!port) { + dev_err(dev, "no port node found in %s\n", + parent->full_name); + return -ENXIO; + } + variant = match->data; } - return armada_drm_crtc_create(drm, dev, res, irq, variant); + return armada_drm_crtc_create(drm, dev, res, irq, variant, port); } static void diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 0d9a803..98102a5 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -75,8 +75,10 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) +struct device_node; int armada_drm_crtc_create(struct drm_device *, struct device *, - struct resource *, int, const struct armada_variant *); + struct resource *, int, const struct armada_variant *, + struct device_node *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index f237f44..e2d5792 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -169,7 +169,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, - variant); + variant, NULL); if (ret) goto err_kms; } -- cgit v0.10.2 From 06596961966bfeb128bc07f250b68fb9c5f177ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:51 +0530 Subject: drm: Move DRM_ROTATE bits out of omapdrm into drm_crtc.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rotation property stuff should be standardized among all drivers. Move the bits to drm_crtc.h from omap_drv.h. Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 284b80f..b08a450 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -119,13 +119,6 @@ struct omap_drm_private { struct omap_drm_irq error_handler; }; -/* this should probably be in drm-core to standardize amongst drivers */ -#define DRM_ROTATE_0 0 -#define DRM_ROTATE_90 1 -#define DRM_ROTATE_180 2 -#define DRM_ROTATE_270 3 -#define DRM_REFLECT_X 4 -#define DRM_REFLECT_Y 5 #ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4ee7e26..bfc7235 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -75,6 +75,14 @@ static inline uint64_t I642U64(int64_t val) return (uint64_t)*((uint64_t *)&val); } +/* rotation property bits */ +#define DRM_ROTATE_0 0 +#define DRM_ROTATE_90 1 +#define DRM_ROTATE_180 2 +#define DRM_ROTATE_270 3 +#define DRM_REFLECT_X 4 +#define DRM_REFLECT_Y 5 + enum drm_connector_force { DRM_FORCE_UNSPECIFIED, DRM_FORCE_OFF, -- cgit v0.10.2 From 7689ffb32fa314cc6f128b433b5a285f2699cb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:52 +0530 Subject: drm: Add support_bits parameter to drm_property_create_bitmask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make drm_property_create_bitmask() a bit more generic by allowing the caller to specify which bits are in fact supported. This allows multiple callers to use the same enum list, but still create different versions of the same property with different list of supported bits. v2: Populate values[] array as non-sparse Make supported_bits 64bit Fix up omapdrm call site (Rob) Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Reviewed-by: Sagar Kamble Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 41c7212..2fbee61 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3395,19 +3395,28 @@ EXPORT_SYMBOL(drm_property_create_enum); struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, - int num_values) + int num_props, + uint64_t supported_bits) { struct drm_property *property; - int i, ret; + int i, ret, index = 0; + int num_values = hweight64(supported_bits); flags |= DRM_MODE_PROP_BITMASK; property = drm_property_create(dev, flags, name, num_values); if (!property) return NULL; + for (i = 0; i < num_props; i++) { + if (!(supported_bits & (1ULL << props[i].type))) + continue; - for (i = 0; i < num_values; i++) { - ret = drm_property_add_enum(property, i, + if (WARN_ON(index >= num_values)) { + drm_property_destroy(dev, property); + return NULL; + } + + ret = drm_property_add_enum(property, index++, props[i].type, props[i].name); if (ret) { diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 3cf31ee..aff06e7 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -317,7 +317,10 @@ void omap_plane_install_properties(struct drm_plane *plane, { DRM_REFLECT_Y, "reflect-y" }, }; prop = drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props)); + props, ARRAY_SIZE(props), + BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) | + BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) | + BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); if (prop == NULL) return; priv->rotation_prop = prop; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bfc7235..cb4850a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1006,7 +1006,8 @@ extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, - int num_values); + int num_props, + uint64_t supported_bits); struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); -- cgit v0.10.2 From c1df5f3c2d665f1a5b365d1e352e99832f188fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:53 +0530 Subject: drm: Add drm_mode_create_rotation_property() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to create a standards compliant rotation property. Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2fbee61..f224d4d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4959,3 +4959,21 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); + +struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, + unsigned int supported_rotations) +{ + static const struct drm_prop_enum_list props[] = { + { DRM_ROTATE_0, "rotate-0" }, + { DRM_ROTATE_90, "rotate-90" }, + { DRM_ROTATE_180, "rotate-180" }, + { DRM_ROTATE_270, "rotate-270" }, + { DRM_REFLECT_X, "reflect-x" }, + { DRM_REFLECT_Y, "reflect-y" }, + }; + + return drm_property_create_bitmask(dev, 0, "rotation", + props, ARRAY_SIZE(props), + supported_rotations); +} +EXPORT_SYMBOL(drm_mode_create_rotation_property); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cb4850a..f7b383b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1113,6 +1113,8 @@ extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format); +extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, + unsigned int supported_rotations); /* Helpers */ -- cgit v0.10.2 From a4969dd78735ea61bf67712987e1bd69d32e243c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:54 +0530 Subject: drm/omap: Switch omapdrm over to drm_mode_create_rotation_property() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new drm_mode_create_rotation_property() in omapdrm. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Rob Clark Reviewed-by: Imre Deak Reviewed-by: Sagar Kamble Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index aff06e7..da9d15d 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -308,19 +308,13 @@ void omap_plane_install_properties(struct drm_plane *plane, if (priv->has_dmm) { prop = priv->rotation_prop; if (!prop) { - const struct drm_prop_enum_list props[] = { - { DRM_ROTATE_0, "rotate-0" }, - { DRM_ROTATE_90, "rotate-90" }, - { DRM_ROTATE_180, "rotate-180" }, - { DRM_ROTATE_270, "rotate-270" }, - { DRM_REFLECT_X, "reflect-x" }, - { DRM_REFLECT_Y, "reflect-y" }, - }; - prop = drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props), - BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) | - BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); + prop = drm_mode_create_rotation_property(dev, + BIT(DRM_ROTATE_0) | + BIT(DRM_ROTATE_90) | + BIT(DRM_ROTATE_180) | + BIT(DRM_ROTATE_270) | + BIT(DRM_REFLECT_X) | + BIT(DRM_REFLECT_Y)); if (prop == NULL) return; priv->rotation_prop = prop; -- cgit v0.10.2 From 07074006cd951f7a952512c57d60788ee7ea18db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:55 +0530 Subject: drm: Add drm_rect rotation functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some helper functions to move drm_rects between different rotated coordinate spaces. One function does the forward transform and another does the inverse. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 7047ca0..631f5af 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -293,3 +293,143 @@ void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point) DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1); } EXPORT_SYMBOL(drm_rect_debug_print); + +/** + * drm_rect_rotate - Rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation to be applied + * + * Apply @rotation to the coordinates of rectangle @r. + * + * @width and @height combined with @rotation define + * the location of the new origin. + * + * @width correcsponds to the horizontal and @height + * to the vertical axis of the untransformed coordinate + * space. + */ +void drm_rect_rotate(struct drm_rect *r, + int width, int height, + unsigned int rotation) +{ + struct drm_rect tmp; + + if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + tmp = *r; + + if (rotation & BIT(DRM_REFLECT_X)) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } + + if (rotation & BIT(DRM_REFLECT_Y)) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } + } + + switch (rotation & 0xf) { + case BIT(DRM_ROTATE_0): + break; + case BIT(DRM_ROTATE_90): + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = width - tmp.x2; + r->y2 = width - tmp.x1; + break; + case BIT(DRM_ROTATE_180): + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case BIT(DRM_ROTATE_270): + tmp = *r; + r->x1 = height - tmp.y2; + r->x2 = height - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + default: + break; + } +} +EXPORT_SYMBOL(drm_rect_rotate); + +/** + * drm_rect_rotate_inv - Inverse rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation whose inverse is to be applied + * + * Apply the inverse of @rotation to the coordinates + * of rectangle @r. + * + * @width and @height combined with @rotation define + * the location of the new origin. + * + * @width correcsponds to the horizontal and @height + * to the vertical axis of the original untransformed + * coordinate space, so that you never have to flip + * them when doing a rotatation and its inverse. + * That is, if you do: + * + * drm_rotate(&r, width, height, rotation); + * drm_rotate_inv(&r, width, height, rotation); + * + * you will always get back the original rectangle. + */ +void drm_rect_rotate_inv(struct drm_rect *r, + int width, int height, + unsigned int rotation) +{ + struct drm_rect tmp; + + switch (rotation & 0xf) { + case BIT(DRM_ROTATE_0): + break; + case BIT(DRM_ROTATE_90): + tmp = *r; + r->x1 = width - tmp.y2; + r->x2 = width - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + case BIT(DRM_ROTATE_180): + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case BIT(DRM_ROTATE_270): + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = height - tmp.x2; + r->y2 = height - tmp.x1; + break; + default: + break; + } + + if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + tmp = *r; + + if (rotation & BIT(DRM_REFLECT_X)) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } + + if (rotation & BIT(DRM_REFLECT_Y)) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } + } +} +EXPORT_SYMBOL(drm_rect_rotate_inv); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index d128629..26bb55e 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -163,5 +163,11 @@ int drm_rect_calc_vscale_relaxed(struct drm_rect *src, struct drm_rect *dst, int min_vscale, int max_vscale); void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point); +void drm_rect_rotate(struct drm_rect *r, + int width, int height, + unsigned int rotation); +void drm_rect_rotate_inv(struct drm_rect *r, + int width, int height, + unsigned int rotation); #endif -- cgit v0.10.2 From 3c9855f6dc1c68f7c4028f49bd8e3df7e4faae67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 8 Jul 2014 10:31:56 +0530 Subject: drm: Add drm_rotation_simplify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_rotation_simplify() can be used to eliminate unsupported rotation flags. It will check if any unsupported flags are present, and if so it will modify the rotation to an alternate form by adding 180 degrees to rotation angle, and flipping the reflect x and y bits. The hope is that this identity transform will eliminate the unsupported flags. Of course that might not result in any more supported rotation, so the caller is still responsible for checking the result afterwards. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f224d4d..89bab66 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4842,6 +4842,36 @@ int drm_format_vert_chroma_subsampling(uint32_t format) EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); /** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified + * @supported_rotations: Supported rotations + * + * Attempt to simplify the rotation to a form that is supported. + * Eg. if the hardware supports everything except DRM_REFLECT_X + * one could call this function like this: + * + * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | + * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | + * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); + * + * to eliminate the DRM_ROTATE_X flag. Depending on what kind of + * transforms the hardware supports, this function may not + * be able to produce a supported transform, so the caller should + * check the result afterwards. + */ +unsigned int drm_rotation_simplify(unsigned int rotation, + unsigned int supported_rotations) +{ + if (rotation & ~supported_rotations) { + rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); + rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4); + } + + return rotation; +} +EXPORT_SYMBOL(drm_rotation_simplify); + +/** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f7b383b..08ed55e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1115,6 +1115,8 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format); extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, unsigned int supported_rotations); +extern unsigned int drm_rotation_simplify(unsigned int rotation, + unsigned int supported_rotations); /* Helpers */ -- cgit v0.10.2 From 4ba08faa9096653bbc2bfcd885b4c04bf7fa01f3 Mon Sep 17 00:00:00 2001 From: Sagar Kamble Date: Tue, 8 Jul 2014 10:32:02 +0530 Subject: Documentation: drm: Removing placeholders for generic drm properties description These property descriptions were kept as placeholder. Removing them for simplicity. Cc: damien.lespiau@intel.com Cc: daniel.vetter@ffwll.ch Cc: ville.syrjala@linux.intel.com Signed-off-by: Sagar Kamble Reviewed-by: Damien Lespiau Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7df3134..4a0e932 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2648,8 +2648,8 @@ void intel_crt_init(struct drm_device *dev) TBD - i915 - Generic + i915 + Generic "Broadcast RGB" ENUM { "Automatic", "Full", "Limited 16:235" } @@ -2664,13 +2664,6 @@ void intel_crt_init(struct drm_device *dev) TBD - Standard name as in DRM - Standard type as in DRM - Standard value as in DRM - Standard Object as in DRM - TBD - - SDVO-TV “mode” ENUM @@ -2799,8 +2792,8 @@ void intel_crt_init(struct drm_device *dev) TBD - CDV gma-500 - Generic + CDV gma-500 + Generic "Broadcast RGB" ENUM { “Full”, “Limited 16:235” } @@ -2815,15 +2808,8 @@ void intel_crt_init(struct drm_device *dev) TBD - Standard name as in DRM - Standard type as in DRM - Standard value as in DRM - Standard Object as in DRM - TBD - - - Poulsbo - Generic + Poulsbo + Generic “backlight” RANGE Min=0, Max=100 @@ -2831,13 +2817,6 @@ void intel_crt_init(struct drm_device *dev) TBD - Standard name as in DRM - Standard type as in DRM - Standard value as in DRM - Standard Object as in DRM - TBD - - SDVO-TV “mode” ENUM @@ -3064,7 +3043,7 @@ void intel_crt_init(struct drm_device *dev) TBD - i2c/ch7006_drv + i2c/ch7006_drv Generic “scale” RANGE @@ -3073,14 +3052,7 @@ void intel_crt_init(struct drm_device *dev) TBD - TV - Standard names as in DRM - Standard types as in DRM - Standard Values as in DRM - Standard object as in DRM - TBD - - + TV “mode” ENUM { "PAL", "PAL-M","PAL-N"}, ”PAL-Nc" @@ -3089,7 +3061,7 @@ void intel_crt_init(struct drm_device *dev) TBD - nouveau + nouveau NV10 Overlay "colorkey" RANGE @@ -3198,14 +3170,6 @@ void intel_crt_init(struct drm_device *dev) TBD - Generic - Standard name as in DRM - Standard type as in DRM - Standard value as in DRM - Standard Object as in DRM - TBD - - omap Generic “rotation” @@ -3236,7 +3200,7 @@ void intel_crt_init(struct drm_device *dev) TBD - radeon + radeon DVI-I “coherent” RANGE @@ -3308,14 +3272,6 @@ void intel_crt_init(struct drm_device *dev) TBD - Generic - Standard name as in DRM - Standard type as in DRM - Standard value as in DRM - Standard Object as in DRM - TBD - - rcar-du Generic "alpha" -- cgit v0.10.2 From d4ef41ce151988411dec8b089636d9ecbd1da559 Mon Sep 17 00:00:00 2001 From: Sagar Kamble Date: Tue, 8 Jul 2014 10:32:03 +0530 Subject: Documentation: drm: describing rotation property Cc: damien.lespiau@intel.com Cc: daniel.vetter@ffwll.ch Cc: ville.syrjala@linux.intel.com Signed-off-by: Sagar Kamble Reviewed-by: Damien Lespiau Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 4a0e932..8ab1a05 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2648,7 +2648,7 @@ void intel_crt_init(struct drm_device *dev) TBD - i915 + i915 Generic "Broadcast RGB" ENUM @@ -2664,6 +2664,14 @@ void intel_crt_init(struct drm_device *dev) TBD + Plane + “rotation” + BITMASK + { 0, "rotate-0" }, { 2, "rotate-180" } + Plane + TBD + + SDVO-TV “mode” ENUM -- cgit v0.10.2 From 542a6b205b184ec90e2108aaebaf8ba16128baec Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 9 Jul 2014 14:55:56 +0300 Subject: drm/i915/chv: calculate rc6 residency correctly The register to read cz count is different from vlv. Also the counts returned from CCK_CTL1 for BSW are (ticks in 30ns - 1). czcount_30ns of value 1 is a special case for 320Mhz. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80703 Suggested-by: Deepak S Cc: Jesse Barnes Signed-off-by: Mika Kuoppala Tested-by: Guo Jinxian Reviewed-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0ebe0f4..503da23 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2281,7 +2281,7 @@ enum punit_power_well { /* Same as Haswell, but 72064 bytes now. */ #define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE) - +#define CHV_CLK_CTL1 0x101100 #define VLV_CLK_CTL2 0x101104 #define CLK_CTL2_CZCOUNT_30NS_SHIFT 28 diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index b15c8ce..ae7fd8f 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -47,22 +47,45 @@ static u32 calc_residency(struct drm_device *dev, const u32 reg) intel_runtime_pm_get(dev_priv); - /* On VLV, residency time is in CZ units rather than 1.28us */ + /* On VLV and CHV, residency time is in CZ units rather than 1.28us */ if (IS_VALLEYVIEW(dev)) { - u32 clkctl2; + u32 reg, czcount_30ns; - clkctl2 = I915_READ(VLV_CLK_CTL2) >> - CLK_CTL2_CZCOUNT_30NS_SHIFT; - if (!clkctl2) { - WARN(!clkctl2, "bogus CZ count value"); + if (IS_CHERRYVIEW(dev)) + reg = CHV_CLK_CTL1; + else + reg = VLV_CLK_CTL2; + + czcount_30ns = I915_READ(reg) >> CLK_CTL2_CZCOUNT_30NS_SHIFT; + + if (!czcount_30ns) { + WARN(!czcount_30ns, "bogus CZ count value"); ret = 0; goto out; } - units = DIV_ROUND_UP_ULL(30ULL * bias, (u64)clkctl2); + + units = 0; + div = 1000000ULL; + + if (IS_CHERRYVIEW(dev)) { + /* Special case for 320Mhz */ + if (czcount_30ns == 1) { + div = 10000000ULL; + units = 3125ULL; + } else { + /* chv counts are one less */ + czcount_30ns += 1; + } + } + + if (units == 0) + units = DIV_ROUND_UP_ULL(30ULL * bias, + (u64)czcount_30ns); + if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH) units <<= 8; - div = 1000000ULL * bias; + div = div * bias; } raw_time = I915_READ(reg) * units; -- cgit v0.10.2 From b55dd64720919ba8d6830fbaec9e779e4bdb0ae0 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jul 2014 11:21:39 +0200 Subject: drm/i915: byt_gpu_freq() can be static CC: Deepak S CC: Daniel Vetter Signed-off-by: Fengguang Wu Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 97be125..9585f15 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6945,7 +6945,7 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } -int byt_gpu_freq(struct drm_i915_private *dev_priv, int val) +static int byt_gpu_freq(struct drm_i915_private *dev_priv, int val) { int div; @@ -6967,7 +6967,7 @@ int byt_gpu_freq(struct drm_i915_private *dev_priv, int val) return DIV_ROUND_CLOSEST(dev_priv->mem_freq * (val + 6 - 0xbd), 4 * div); } -int byt_freq_opcode(struct drm_i915_private *dev_priv, int val) +static int byt_freq_opcode(struct drm_i915_private *dev_priv, int val) { int mul; @@ -6989,7 +6989,7 @@ int byt_freq_opcode(struct drm_i915_private *dev_priv, int val) return DIV_ROUND_CLOSEST(4 * mul * val, dev_priv->mem_freq) + 0xbd - 6; } -int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) +static int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) { int div, freq; @@ -7014,7 +7014,7 @@ int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) return freq; } -int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) +static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) { int mul, opcode; -- cgit v0.10.2 From 884d6a0b558c9c31fdf0d950bd07a2c804876445 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 8 Jul 2014 21:32:11 +0900 Subject: drm/dsi: Flag for non-continuous clock behavior As per section 5.6.1 of the DSI specification, all DSI transmitters must support continuous clock behavior on the clock lane, while non-continuous mode support is only optional. Add a flag that allows devices to indicate that they support non-continuous clock mode so host drivers can adapt their behavior accordingly. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 944f33f..efa1b55 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -94,6 +94,8 @@ void mipi_dsi_host_unregister(struct mipi_dsi_host *host); #define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) /* disable EoT packets in HS mode */ #define MIPI_DSI_MODE_EOT_PACKET BIT(9) +/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ +#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, -- cgit v0.10.2 From 937ca28445644cb9a5baf95b60b3482f2b751023 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Jun 2014 14:06:11 +0100 Subject: drm/panel: consolidate unnecessary explicit dependencies DRM_PANEL_LD9040 and DRM_PANEL_S6E8AA0 both explicitly depended on DRM_PANEL && DRM, whereas DRM_PANEL_SIMPLE relies upon the dependency on the menu. We do not need to use explicit dependencies if we make the menu depend on DRM_PANEL && DRM - this will implicitly make each entry in the menu depend on DRM_PANEL && DRM without this needing to be explicitly stated against every entry. Signed-off-by: Russell King Acked-by: Arnd Bergmann Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 4ec874d..d83958d 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -5,7 +5,7 @@ config DRM_PANEL Panel registration and lookup framework. menu "Display Panels" - depends on DRM_PANEL + depends on DRM && DRM_PANEL config DRM_PANEL_SIMPLE tristate "support for simple panels" @@ -18,14 +18,12 @@ config DRM_PANEL_SIMPLE config DRM_PANEL_LD9040 tristate "LD9040 RGB/SPI panel" - depends on DRM && DRM_PANEL depends on OF select SPI select VIDEOMODE_HELPERS config DRM_PANEL_S6E8AA0 tristate "S6E8AA0 DSI video mode panel" - depends on DRM && DRM_PANEL depends on OF select DRM_MIPI_DSI select VIDEOMODE_HELPERS -- cgit v0.10.2 From 50d5ed399262f066f07daf3b6ab20be5d5b56ba7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Jun 2014 14:06:16 +0100 Subject: drm/panel: make DRM_PANEL_LD9040 depend on SPI Rather than DRM_PANEL_LD9040 selecting SPI, which then results in an increase in the probability of Kconf reporting circular dependencies (we're one "select" away from that right now), make this depend on SPI instead. This is akin to having some driver select DRM. Having some drivers depend on a subsystem, and other drivers select a subsystem is a recipe for circular dependencies, and there's really no need for it. The potential circular dependency (which can be caused today by the addition of selecting DRM_PANEL from DRM_IMX_LDB) is: symbol DMADEVICES is selected by SAMSUNG_DMADEV symbol SAMSUNG_DMADEV is selected by S3C64XX_PL080 symbol S3C64XX_PL080 is selected by SPI_S3C64XX symbol SPI_S3C64XX depends on SPI symbol SPI is selected by DRM_PANEL_LD9040 symbol DRM_PANEL_LD9040 depends on DRM_PANEL symbol DRM_PANEL is selected by DRM_IMX_LDB symbol DRM_IMX_LDB depends on MFD_SYSCON symbol MFD_SYSCON is selected by POWER_RESET_KEYSTONE symbol POWER_RESET_KEYSTONE depends on POWER_SUPPLY symbol POWER_SUPPLY is selected by HID_SONY symbol HID_SONY depends on NEW_LEDS symbol NEW_LEDS is selected by BACKLIGHT_ADP8860 symbol BACKLIGHT_ADP8860 depends on BACKLIGHT_CLASS_DEVICE symbol BACKLIGHT_CLASS_DEVICE is selected by FB_MX3 symbol FB_MX3 depends on MX3_IPU symbol MX3_IPU depends on DMADEVICES Acked-by: Arnd Bergmann Signed-off-by: Russell King Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index d83958d..bee9f72 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -18,8 +18,7 @@ config DRM_PANEL_SIMPLE config DRM_PANEL_LD9040 tristate "LD9040 RGB/SPI panel" - depends on OF - select SPI + depends on OF && SPI select VIDEOMODE_HELPERS config DRM_PANEL_S6E8AA0 -- cgit v0.10.2 From 5e4cc278625d789b72a3a74f6b90362d2436f6a5 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 8 Jul 2014 21:32:12 +0900 Subject: drm/panel: Set non-continuous clock flag on supporting panels The LG LD070WX3-SL01 and Panasonic VVX10F004B00 are DSI support non-continuous clock mode. Set the MIPI_DSI_CLOCK_NON_CONTINUOUS to their definition so host drivers become aware of this capability. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a2513613..869a84e3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -545,7 +545,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = { .height = 151, }, }, - .flags = MIPI_DSI_MODE_VIDEO, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, .format = MIPI_DSI_FMT_RGB888, .lanes = 4, }; @@ -599,7 +599,8 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = { .height = 136, }, }, - .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS, .format = MIPI_DSI_FMT_RGB888, .lanes = 4, }; -- cgit v0.10.2 From 102932b0e474bb33061e88bcf5d83e66f10c1da2 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 5 Jun 2014 15:53:32 +0200 Subject: drm/panel: add support for Foxlink FL500WVR00-A0T panel This panel is used by Atmel's SAMA5D3 Evaluation Kits (sama5d3xek) and supported by the simple-panel driver. Signed-off-by: Boris BREZILLON Signed-off-by: Thierry Reding diff --git a/Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt b/Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt new file mode 100644 index 0000000..b47f9d8 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt @@ -0,0 +1,7 @@ +Foxlink Group 5" WVGA TFT LCD panel + +Required properties: +- compatible: should be "foxlink,fl500wvr00-a0t" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 869a84e3..9961d44 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -403,6 +403,28 @@ static const struct panel_desc edt_etm0700g0dh6 = { }, }; +static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { + .clock = 32260, + .hdisplay = 800, + .hsync_start = 800 + 168, + .hsync_end = 800 + 168 + 64, + .htotal = 800 + 168 + 64 + 88, + .vdisplay = 480, + .vsync_start = 480 + 37, + .vsync_end = 480 + 37 + 2, + .vtotal = 480 + 37 + 2 + 8, + .vrefresh = 60, +}; + +static const struct panel_desc foxlink_fl500wvr00_a0t = { + .modes = &foxlink_fl500wvr00_a0t_mode, + .num_modes = 1, + .size = { + .width = 108, + .height = 65, + }, +}; + static const struct drm_display_mode lg_lp129qe_mode = { .clock = 285250, .hdisplay = 2560, @@ -470,6 +492,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "edt,etm0700g0dh6", .data = &edt_etm0700g0dh6, }, { + .compatible = "foxlink,fl500wvr00-a0t", + .data = &foxlink_fl500wvr00_a0t, + }, { .compatible = "lg,lp129qe", .data = &lg_lp129qe, }, { -- cgit v0.10.2 From 70e2626005fb21f95d1056bd0eae8d5e540d16ac Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:29:56 -0400 Subject: drm/ast: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 9896286..5389350 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -667,17 +667,9 @@ static void ast_encoder_destroy(struct drm_encoder *encoder) static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - /* pick the encoder ids */ - if (enc_id) { - obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } -- cgit v0.10.2 From fc22d96d152d7f867ac08bcd36ffcb59e1cbfb43 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:29:57 -0400 Subject: drm/bochs: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index dcf2e55..b3c6130 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -216,18 +216,9 @@ static struct drm_encoder * bochs_connector_best_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - /* pick the encoder ids */ - if (enc_id) { - obj = drm_mode_object_find(connector->dev, enc_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } -- cgit v0.10.2 From ef13aec45406b31969571f9cb3872a2654d06a66 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:29:58 -0400 Subject: drm/cirrus: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 49332c5..e1c5c32 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - /* pick the encoder ids */ - if (enc_id) { - obj = - drm_mode_object_find(connector->dev, enc_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } -- cgit v0.10.2 From 4726f1ff50e8da000a9af7bfbdb3af54d39a17bd Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:29:59 -0400 Subject: drm/exynos: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 25c7888..ba9b3d5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder( struct drm_device *dev = connector->dev; struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - struct drm_mode_object *obj; - struct drm_encoder *encoder; - - obj = drm_mode_object_find(dev, exynos_connector->encoder_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) { - DRM_DEBUG_KMS("Unknown ENCODER ID %d\n", - exynos_connector->encoder_id); - return NULL; - } - - encoder = obj_to_encoder(obj); - - return encoder; + return drm_encoder_find(dev, exynos_connector->encoder_id); } static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { -- cgit v0.10.2 From c7e951147d92319e1d2477628abfa36f277f887e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:00 -0400 Subject: drm/mgag200: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index f9fe390..45f04de 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1562,19 +1562,9 @@ static struct drm_encoder *mga_connector_best_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - /* pick the encoder ids */ - if (enc_id) { - obj = - drm_mode_object_find(connector->dev, enc_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } -- cgit v0.10.2 From bf02d87fa458a665c176eaefd58d29bc615a8875 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:01 -0400 Subject: drm/udl: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index dea38ab..e026a9e 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -105,14 +105,7 @@ static struct drm_encoder* udl_best_single_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - - obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; + return drm_encoder_find(connector->dev, enc_id); } static int udl_connector_set_property(struct drm_connector *connector, -- cgit v0.10.2 From a4cd5d68570cc7614d50da156545919af83a3548 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:02 -0400 Subject: drm/vmwgfx: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 1a024e3..991e5c8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1501,7 +1501,6 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, { struct drm_vmw_cursor_bypass_arg *arg = data; struct vmw_display_unit *du; - struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0; @@ -1519,13 +1518,12 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, return 0; } - obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, arg->crtc_id); + if (!crtc) { ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); du = vmw_crtc_to_du(crtc); du->hotspot_x = arg->xhot; -- cgit v0.10.2 From 6d385c0aa31de99da391fda0fc43462e2df20863 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:03 -0400 Subject: drm/nouveau: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 680f46d..dbdc9ad 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -63,7 +63,7 @@ find_encoder(struct drm_connector *connector, int type) { struct drm_device *dev = connector->dev; struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; + struct drm_encoder *enc; int i, id; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { @@ -71,10 +71,10 @@ find_encoder(struct drm_connector *connector, int type) if (!id) break; - obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); - if (!obj) + enc = drm_encoder_find(dev, id); + if (!enc) continue; - nv_encoder = nouveau_encoder(obj_to_encoder(obj)); + nv_encoder = nouveau_encoder(enc); if (type == DCB_OUTPUT_ANY || (nv_encoder->dcb && nv_encoder->dcb->type == type)) @@ -119,7 +119,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; + struct drm_encoder *encoder; int i, panel = -ENODEV; /* eDP panels need powering on by us (if the VBIOS doesn't default it @@ -139,10 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) if (id == 0) break; - obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(dev, id); + if (!encoder) continue; - nv_encoder = nouveau_encoder(obj_to_encoder(obj)); + nv_encoder = nouveau_encoder(encoder); if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { int ret = nouveau_dp_detect(nv_encoder); -- cgit v0.10.2 From 7707e6535f43328e05e4729ac96eee864b90e8a4 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:04 -0400 Subject: drm/i915: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 927d247..8796eb1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11738,21 +11738,20 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data; - struct drm_mode_object *drmmode_obj; + struct drm_crtc *drmmode_crtc; struct intel_crtc *crtc; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, - DRM_MODE_OBJECT_CRTC); + drmmode_crtc = drm_crtc_find(dev, pipe_from_crtc_id->crtc_id); - if (!drmmode_obj) { + if (!drmmode_crtc) { DRM_ERROR("no such CRTC id\n"); return -ENOENT; } - crtc = to_intel_crtc(obj_to_crtc(drmmode_obj)); + crtc = to_intel_crtc(drmmode_crtc); pipe_from_crtc_id->pipe = crtc->pipe; return 0; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 307c2f1..dc2f4f26 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1039,7 +1039,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, struct drm_intel_overlay_put_image *put_image_rec = data; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_overlay *overlay; - struct drm_mode_object *drmmode_obj; + struct drm_crtc *drmmode_crtc; struct intel_crtc *crtc; struct drm_i915_gem_object *new_bo; struct put_image_params *params; @@ -1068,13 +1068,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, if (!params) return -ENOMEM; - drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!drmmode_obj) { + drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id); + if (!drmmode_crtc) { ret = -ENOENT; goto out_free; } - crtc = to_intel_crtc(obj_to_crtc(drmmode_obj)); + crtc = to_intel_crtc(drmmode_crtc); new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv, put_image_rec->bo_handle)); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 985317e..6afd1cf 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1123,7 +1123,6 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *set = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; @@ -1137,13 +1136,12 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, set->plane_id); + if (!plane) { ret = -ENOENT; goto out_unlock; } - plane = obj_to_plane(obj); intel_plane = to_intel_plane(plane); ret = intel_plane->update_colorkey(plane, set); @@ -1156,7 +1154,6 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *get = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; @@ -1166,13 +1163,12 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, get->plane_id); + if (!plane) { ret = -ENOENT; goto out_unlock; } - plane = obj_to_plane(obj); intel_plane = to_intel_plane(plane); intel_plane->get_colorkey(plane, get); -- cgit v0.10.2 From b957f457fbce30cc4901dc28f2b56f2b15dfe84a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 17 Jul 2014 23:30:05 -0400 Subject: drm/radeon: use helpers Signed-off-by: Rob Clark Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 1544efc..ebdce08 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -1401,7 +1401,6 @@ int r100_cs_parse_packet0(struct radeon_cs_parser *p, */ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) { - struct drm_mode_object *obj; struct drm_crtc *crtc; struct radeon_crtc *radeon_crtc; struct radeon_cs_packet p3reloc, waitreloc; @@ -1441,12 +1440,11 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) header = radeon_get_ib_value(p, h_idx); crtc_id = radeon_get_ib_value(p, h_idx + 5); reg = R100_CP_PACKET0_GET_REG(header); - obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(p->rdev->ddev, crtc_id); + if (!crtc) { DRM_ERROR("cannot find crtc %d\n", crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj); radeon_crtc = to_radeon_crtc(crtc); crtc_id = radeon_crtc->crtc_id; diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 12511bb..c47537a 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -825,7 +825,6 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p, uint32_t *vline_start_end, uint32_t *vline_status) { - struct drm_mode_object *obj; struct drm_crtc *crtc; struct radeon_crtc *radeon_crtc; struct radeon_cs_packet p3reloc, wait_reg_mem; @@ -887,12 +886,11 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p, crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1); reg = R600_CP_PACKET0_GET_REG(header); - obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(p->rdev->ddev, crtc_id); + if (!crtc) { DRM_ERROR("cannot find crtc %d\n", crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj); radeon_crtc = to_radeon_crtc(crtc); crtc_id = radeon_crtc->crtc_id; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 8137b7a..c667c43 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -216,7 +216,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c struct drm_encoder *best_encoder = NULL; struct drm_encoder *encoder = NULL; struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; - struct drm_mode_object *obj; bool connected; int i; @@ -226,14 +225,11 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, + connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); - if ((encoder == best_encoder) && (status == connector_status_connected)) connected = true; else @@ -249,7 +245,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int encoder_type) { - struct drm_mode_object *obj; struct drm_encoder *encoder; int i; @@ -257,11 +252,10 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); if (encoder->encoder_type == encoder_type) return encoder; } @@ -271,17 +265,9 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - /* pick the encoder ids */ - if (enc_id) { - obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } @@ -1048,7 +1034,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = NULL; struct drm_encoder_helper_funcs *encoder_funcs; - struct drm_mode_object *obj; int i, r; enum drm_connector_status ret = connector_status_disconnected; bool dret = false, broken_edid = false; @@ -1153,14 +1138,11 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, + connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); - if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) continue; @@ -1225,19 +1207,16 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct drm_mode_object *obj; struct drm_encoder *encoder; int i; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); - if (radeon_connector->use_digital == true) { if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) return encoder; @@ -1252,13 +1231,8 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) /* then check use digitial */ /* pick the first one */ - if (enc_id) { - obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); - if (!obj) - return NULL; - encoder = obj_to_encoder(obj); - return encoder; - } + if (enc_id) + return drm_encoder_find(connector->dev, enc_id); return NULL; } @@ -1391,7 +1365,6 @@ static int radeon_dp_get_modes(struct drm_connector *connector) u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector) { - struct drm_mode_object *obj; struct drm_encoder *encoder; struct radeon_encoder *radeon_encoder; int i; @@ -1400,11 +1373,10 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); radeon_encoder = to_radeon_encoder(encoder); switch (radeon_encoder->encoder_id) { @@ -1421,7 +1393,6 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) { - struct drm_mode_object *obj; struct drm_encoder *encoder; struct radeon_encoder *radeon_encoder; int i; @@ -1431,11 +1402,10 @@ bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) if (connector->encoder_ids[i] == 0) break; - obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); - if (!obj) + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + if (!encoder) continue; - encoder = obj_to_encoder(obj); radeon_encoder = to_radeon_encoder(encoder); if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) found = true; -- cgit v0.10.2 From 2aa9d2bc8cc3ab32ea0d15ec9005f25b1af3465a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 26 Jun 2014 21:37:20 +0200 Subject: drm: Fix function names in kerneldoc The drm_property_create_enum(), drm_property_create_bitmask() and drm_property_create_range() contain the wrong name in the kerneldoc comment. This is probably simply a copy/paste mistake. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c808a09..1ccf5cb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3388,7 +3388,7 @@ fail: EXPORT_SYMBOL(drm_property_create); /** - * drm_property_create - create a new enumeration property type + * drm_property_create_enum - create a new enumeration property type * @dev: drm device * @flags: flags specifying the property type * @name: name of the property @@ -3434,7 +3434,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, EXPORT_SYMBOL(drm_property_create_enum); /** - * drm_property_create - create a new bitmask property type + * drm_property_create_bitmask - create a new bitmask property type * @dev: drm device * @flags: flags specifying the property type * @name: name of the property @@ -3496,7 +3496,7 @@ static struct drm_property *property_create_range(struct drm_device *dev, } /** - * drm_property_create - create a new ranged property type + * drm_property_create_range - create a new ranged property type * @dev: drm device * @flags: flags specifying the property type * @name: name of the property -- cgit v0.10.2 From 74f20788a7637f03bb877a178ef2c8f34a722c26 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 28 Jun 2014 15:20:43 +0200 Subject: drivers/gpu/drm/drm_buffer.c: remove unnecessary null test before kfree This patch removes special case of last element and loops from idx to 0. Cc: David Airlie Cc: dri-devel@lists.freedesktop.org Signed-off-by: Fabian Frederick Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/drm_buffer.c index 0406110..86a4a4a 100644 --- a/drivers/gpu/drm/drm_buffer.c +++ b/drivers/gpu/drm/drm_buffer.c @@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size) error_out: - /* Only last element can be null pointer so check for it first. */ - if ((*buf)->data[idx]) - kfree((*buf)->data[idx]); - - for (--idx; idx >= 0; --idx) + for (; idx >= 0; --idx) kfree((*buf)->data[idx]); kfree(*buf); -- cgit v0.10.2 From 3281cc7e341b6dc029965c74a8dd3be5e942a63c Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 30 Jun 2014 15:37:51 -0700 Subject: drm/plane-helper: Use proper plane init function drm_plane_init() (the legacy plane initialization function) takes a bool as its final parameter; originally this indicated whether a plane was 'private' to the driver (before the DRM core understood non-overlay planes), now it indicates whether the plane is a primary plane (private planes were used by some drivers to represent primary planes internally). The newer drm_universal_plane_init() take an 'enum drm_plane_type' as its final parameter to allow the caller to specify the specific plane type desired (primary, cursor, or overlay). Due to a rebasing mistake, the primary plane helper is currently passing DRM_PLANE_TYPE_PRIMARY (enum value = 1) for drm_plane_init()'s boolean 'is_primary' parameter. This winds up giving the correct behavior since DRM_PLANE_TYPE_PRIMARY evaluates as true, but is confusing to anyone reading the code since we're passing an enum value (one of three possible values) for a boolean parameter. Replace the primary plane helper's call to drm_plane_init() with drm_universal_plane_init() so that the parameter and value types match up as expected. Signed-off-by: Matt Roper Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 6d13314..827ec1a 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -335,9 +335,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, } /* possible_crtc's will be filled in later by crtc_init */ - ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs, - formats, num_formats, - DRM_PLANE_TYPE_PRIMARY); + ret = drm_universal_plane_init(dev, primary, 0, + &drm_primary_helper_funcs, + formats, num_formats, + DRM_PLANE_TYPE_PRIMARY); if (ret) { kfree(primary); primary = NULL; -- cgit v0.10.2 From 7389ad4b6515c2de6402bfafdfebf0b319790d16 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 14 Jul 2014 11:53:44 +0100 Subject: drm/dp-mst-helper: Avoid reading uninitialized value A static analysis tool tells me that we could try to read an uninitialized 'ret' value. Plug that. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 369d6c4..813b8d1 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1644,7 +1644,7 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) { struct drm_dp_mst_port *port; int i; - int ret; + int ret = 0; mutex_lock(&mgr->payload_lock); for (i = 0; i < mgr->max_payloads; i++) { -- cgit v0.10.2 From bf3719c04ee3322b0f6d13c078e3ca4c3b7e3ead Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 14 Jul 2014 12:13:18 +0100 Subject: drm/dp-mst-helper: Don't use uninitialized fields of the sideband message header We could be using uninitialized fields of the header in drm_dp_encode_sideband_msg_hdr(), for instance hdr->somt is set to 1 in the first patcket but never set to 0 otherwise. Always clear the header at the start then. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 813b8d1..618526d 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1290,6 +1290,8 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, int len, space, idx, tosend; int ret; + memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr)); + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) { txmsg->seqno = -1; txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND; -- cgit v0.10.2 From 008f40451d0e59f220a4fa13aaf75d04303a01a1 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 17 Jul 2014 11:25:18 -0400 Subject: drm: Check for connection_mutex in drm_select_eld drm_select_eld should check for mode_config.connection_mutex as well as mode_config.mutex: We need that since this function checks conector->encoder links. Signed-off-by: Sean Paul [danvet: Pimp commit message slightly.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index dfa9769..087d608 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3305,6 +3305,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); list_for_each_entry(connector, &dev->mode_config.connector_list, head) if (connector->encoder == encoder && connector->eld[0]) -- cgit v0.10.2 From b19729617929fff8217708f6e960cd85e6f53da2 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 21 Jul 2014 13:54:41 +1000 Subject: drm/i915: fix psr match conditions screw ups. Not enough brown paper bags, you'll have to share one. (oops below). The initial match condition code was racy (locking is coming I hear). then along came: cd234b0bfd5ab012e42274b24aae420fa1823d58 drm/i915: Do not dereference NULL crtc or fb until after checking Chris made an attempt to fix it, Ben "reviewed" it. Daniel merged it. Then drm/i915: Make use of intel_fb_obj() (v2) 2ff8fde1ea0992dfd735dce94f8cae2aacff8e5c made it worse by removing the obj check later. All in all, my laptop can't barely turn off the display without hitting this. Posted to #intel-gfx out of niceness, but I've merged this already into drm-next. Here's an oops. [ 11.528185] BUG: unable to handle kernel NULL pointer dereference at 00000000000000d0 [ 11.528233] IP: [] intel_edp_psr_match_conditions+0x1e/0x2e0 [i915] [ 11.528294] PGD 35bc0067 PUD c997c067 PMD 0 [ 11.528321] Oops: 0000 [#1] SMP [ 11.528916] CPU: 3 PID: 244 Comm: kworker/3:2 Not tainted 3.16.0-rc4+ #17 [ 11.528949] Hardware name: LENOVO 20ARS25701/20ARS25701, BIOS GJET72WW (2.22 ) 02/21/2014 [ 11.529004] Workqueue: events intel_edp_psr_work [i915] [ 11.529031] task: ffff8803079fdaa0 ti: ffff8803079c4000 task.ti: ffff8803079c4000 [ 11.529067] RIP: 0010:[] [] intel_edp_psr_match_conditions+0x1e/0x2e0 [i915] [ 11.529129] RSP: 0018:ffff8803079c7d40 EFLAGS: 00010246 [ 11.529155] RAX: 0000000000000000 RBX: ffff88030c11c000 RCX: c000000000000000 [ 11.529189] RDX: 0000000000000001 RSI: 1df0000000000000 RDI: ffff88030c1190d8 [ 11.529222] RBP: ffff8803079c7d60 R08: ffffffff82691140 R09: 0000000000000000 [ 11.529256] R10: ffff8803079fdaa0 R11: 3e00000000000000 R12: ffff88030c11c728 [ 11.529290] R13: ffff88030c1190d8 R14: ffff88031e2d8e00 R15: 00000000000000c0 [ 11.529324] FS: 0000000000000000(0000) GS:ffff88031e2c0000(0000) knlGS:0000000000000000 [ 11.529361] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 11.529389] CR2: 00000000000000d0 CR3: 00000000c8d9d000 CR4: 00000000001407e0 [ 11.529423] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 11.529457] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 11.529489] Stack: [ 11.529500] ffff88030c119000 ffff88030c11c728 ffff88030c1190d8 ffff88031e2d8e00 [ 11.529541] ffff8803079c7d88 ffffffffa01679b2 ffff880035b29a80 ffff880307909f00 [ 11.529583] ffff88031e2d4740 ffff8803079c7df8 ffffffff810a78ab ffffffff810a7849 [ 11.529624] Call Trace: [ 11.529654] [] intel_edp_psr_work+0x52/0x90 [i915] [ 11.529689] [] process_one_work+0x1db/0x540 [ 11.529719] [] ? process_one_work+0x179/0x540 [ 11.529750] [] worker_thread+0x11d/0x520 [ 11.529779] [] ? create_and_start_worker+0x60/0x60 [ 11.529810] [] kthread+0xe4/0x100 [ 11.529836] [] ? kthread_create_on_node+0x200/0x200 [ 11.529870] [] ret_from_fork+0x7c/0xb0 [ 11.529896] [] ? kthread_create_on_node+0x200/0x200 [ 11.529926] Code: ba 31 13 f0 c9 85 f6 75 84 eb d0 66 90 0f 1f 44 00 00 55 48 89 e5 41 56 41 55 41 54 53 48 8b 87 68 ff ff ff 48 8b 9f 28 ff ff ff <48> 8b 80 d0 00 00 00 4c 8b 63 28 48 8b 40 48 48 85 c0 0f 84 1a [ 11.530110] RIP [] intel_edp_psr_match_conditions+0x1e/0x2e0 [i915] [ 11.530163] RSP [ 11.530180] CR2: 00000000000000d0 Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c06a62a..30816b3 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1767,9 +1767,9 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = dig_port->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(crtc->primary->fb); + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + struct drm_i915_gem_object *obj; struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; dev_priv->psr.source_ok = false; @@ -1802,6 +1802,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } + obj = intel_fb_obj(crtc->primary->fb); if (obj->tiling_mode != I915_TILING_X || obj->fence_reg == I915_FENCE_REG_NONE) { DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n"); -- cgit v0.10.2 From e7c36347130fc3b9eec3572d00c53533f07a7347 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 3 Jul 2014 17:33:33 +0530 Subject: drm: gem_cma: Use ERR_CAST helper Makes the code a bit more readable. Signed-off-by: Sachin Kamat Signed-off-by: Sachin Kamat Acked-by: Laurent Pinchart Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 05c97c5..e467e67 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -327,7 +327,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, /* Create a CMA GEM buffer. */ cma_obj = __drm_gem_cma_create(dev, size); if (IS_ERR(cma_obj)) - return ERR_PTR(PTR_ERR(cma_obj)); + return ERR_CAST(cma_obj); cma_obj->paddr = sg_dma_address(sgt->sgl); cma_obj->sgt = sgt; -- cgit v0.10.2 From 01b887c36e12845bb9162e65504de623f1a17f10 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 11:17:41 +1000 Subject: drm/i915: add some registers need for displayport MST support. These are just from the Haswell spec. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2d2c4de..b0036cd 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5815,6 +5815,7 @@ enum punit_power_well { #define TRANS_DDI_EDP_INPUT_A_ONOFF (4<<12) #define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12) #define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12) +#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8) #define TRANS_DDI_BFI_ENABLE (1<<4) /* DisplayPort Transport Control */ @@ -5824,6 +5825,7 @@ enum punit_power_well { #define DP_TP_CTL_ENABLE (1<<31) #define DP_TP_CTL_MODE_SST (0<<27) #define DP_TP_CTL_MODE_MST (1<<27) +#define DP_TP_CTL_FORCE_ACT (1<<25) #define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18) #define DP_TP_CTL_FDI_AUTOTRAIN (1<<15) #define DP_TP_CTL_LINK_TRAIN_MASK (7<<8) @@ -5838,8 +5840,13 @@ enum punit_power_well { #define DP_TP_STATUS_A 0x64044 #define DP_TP_STATUS_B 0x64144 #define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B) -#define DP_TP_STATUS_IDLE_DONE (1<<25) -#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12) +#define DP_TP_STATUS_IDLE_DONE (1<<25) +#define DP_TP_STATUS_ACT_SENT (1<<24) +#define DP_TP_STATUS_MODE_STATUS_MST (1<<23) +#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12) +#define DP_TP_STATUS_PAYLOAD_MAPPING_VC2 (3 << 8) +#define DP_TP_STATUS_PAYLOAD_MAPPING_VC1 (3 << 4) +#define DP_TP_STATUS_PAYLOAD_MAPPING_VC0 (3 << 0) /* DDI Buffer Control */ #define DDI_BUF_CTL_A 0x64000 -- cgit v0.10.2 From 44905a27dd01f2bddd96664d0ab7da43a07f4d5a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 13:36:43 +1000 Subject: i915: split some DP modesetting code into a separate function this is just prep work for mst support. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index b226724..1aec425 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -365,6 +365,18 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DRM_ERROR("FDI link training failed!\n"); } +void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(&encoder->base); + + intel_dp->DP = intel_dig_port->saved_port_bits | + DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; + intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); + +} + static struct intel_encoder * intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) { @@ -1015,12 +1027,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct intel_digital_port *intel_dig_port = - enc_to_dig_port(encoder); - intel_dp->DP = intel_dig_port->saved_port_bits | - DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; - intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); + intel_ddi_init_dp_buf_reg(intel_encoder); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index fa19744..40086e1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -718,6 +718,7 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc); void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config); +void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder); /* intel_display.c */ const char *intel_output_name(int output); -- cgit v0.10.2 From 36cd7444c044806cd2a4e450a8385597221d5d25 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 13:44:18 +1000 Subject: drm/i915: check connector->encoder before using it. DP MST will need connectors that aren't connected to specific encoders, add some checks in advance to avoid oopses. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4a5b0f8..f27daf7 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2209,13 +2209,15 @@ static void intel_connector_info(struct seq_file *m, seq_printf(m, "\tCEA rev: %d\n", connector->display_info.cea_rev); } - if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || - intel_encoder->type == INTEL_OUTPUT_EDP) - intel_dp_info(m, intel_connector); - else if (intel_encoder->type == INTEL_OUTPUT_HDMI) - intel_hdmi_info(m, intel_connector); - else if (intel_encoder->type == INTEL_OUTPUT_LVDS) - intel_lvds_info(m, intel_connector); + if (intel_encoder) { + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || + intel_encoder->type == INTEL_OUTPUT_EDP) + intel_dp_info(m, intel_connector); + else if (intel_encoder->type == INTEL_OUTPUT_HDMI) + intel_hdmi_info(m, intel_connector); + else if (intel_encoder->type == INTEL_OUTPUT_LVDS) + intel_lvds_info(m, intel_connector); + } seq_printf(m, "\tmodes:\n"); list_for_each_entry(mode, &connector->modes, head) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 026f0a3..12d77b5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1165,6 +1165,8 @@ static void i915_hotplug_work_func(struct work_struct *work) dev_priv->hpd_event_bits = 0; list_for_each_entry(connector, &mode_config->connector_list, head) { intel_connector = to_intel_connector(connector); + if (!intel_connector->encoder) + continue; intel_encoder = intel_connector->encoder; if (intel_encoder->hpd_pin > HPD_NONE && dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED && @@ -1195,6 +1197,8 @@ static void i915_hotplug_work_func(struct work_struct *work) list_for_each_entry(connector, &mode_config->connector_list, head) { intel_connector = to_intel_connector(connector); + if (!intel_connector->encoder) + continue; intel_encoder = intel_connector->encoder; if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) { if (intel_encoder->hot_plug) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e07e6b5..008bb3d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5003,20 +5003,23 @@ static void intel_connector_check_state(struct intel_connector *connector) "wrong connector dpms state\n"); WARN(connector->base.encoder != &encoder->base, "active connector not linked to encoder\n"); - WARN(!encoder->connectors_active, - "encoder->connectors_active not set\n"); - encoder_enabled = encoder->get_hw_state(encoder, &pipe); - WARN(!encoder_enabled, "encoder not enabled\n"); - if (WARN_ON(!encoder->base.crtc)) - return; + if (encoder) { + WARN(!encoder->connectors_active, + "encoder->connectors_active not set\n"); + + encoder_enabled = encoder->get_hw_state(encoder, &pipe); + WARN(!encoder_enabled, "encoder not enabled\n"); + if (WARN_ON(!encoder->base.crtc)) + return; - crtc = encoder->base.crtc; + crtc = encoder->base.crtc; - WARN(!crtc->enabled, "crtc not enabled\n"); - WARN(!to_intel_crtc(crtc)->active, "crtc not active\n"); - WARN(pipe != to_intel_crtc(crtc)->pipe, - "encoder active on the wrong pipe\n"); + WARN(!crtc->enabled, "crtc not enabled\n"); + WARN(!to_intel_crtc(crtc)->active, "crtc not active\n"); + WARN(pipe != to_intel_crtc(crtc)->pipe, + "encoder active on the wrong pipe\n"); + } } } -- cgit v0.10.2 From d05410f9a450df8848196ce87f1c9ef14ff89cb5 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 13:22:59 +1000 Subject: drm/i915: split conversion function out into separate function for MST I need to reuse this, so just move it now. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 008bb3d..421ea71 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4300,6 +4300,23 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc) I915_WRITE(BCLRPAT(crtc->pipe), 0); } +static enum intel_display_power_domain port_to_power_domain(enum port port) +{ + switch (port) { + case PORT_A: + return POWER_DOMAIN_PORT_DDI_A_4_LANES; + case PORT_B: + return POWER_DOMAIN_PORT_DDI_B_4_LANES; + case PORT_C: + return POWER_DOMAIN_PORT_DDI_C_4_LANES; + case PORT_D: + return POWER_DOMAIN_PORT_DDI_D_4_LANES; + default: + WARN_ON_ONCE(1); + return POWER_DOMAIN_PORT_OTHER; + } +} + #define for_each_power_domain(domain, mask) \ for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \ if ((1 << (domain)) & (mask)) @@ -4318,19 +4335,7 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder) case INTEL_OUTPUT_HDMI: case INTEL_OUTPUT_EDP: intel_dig_port = enc_to_dig_port(&intel_encoder->base); - switch (intel_dig_port->port) { - case PORT_A: - return POWER_DOMAIN_PORT_DDI_A_4_LANES; - case PORT_B: - return POWER_DOMAIN_PORT_DDI_B_4_LANES; - case PORT_C: - return POWER_DOMAIN_PORT_DDI_C_4_LANES; - case PORT_D: - return POWER_DOMAIN_PORT_DDI_D_4_LANES; - default: - WARN_ON_ONCE(1); - return POWER_DOMAIN_PORT_OTHER; - } + return port_to_power_domain(intel_dig_port->port); case INTEL_OUTPUT_ANALOG: return POWER_DOMAIN_PORT_CRT; case INTEL_OUTPUT_DSI: -- cgit v0.10.2 From d3cf40ed7fa1b56bd53d3b52eddf44d0e3c3ec20 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 13:24:37 +0200 Subject: drm: Add missing __user annotation The drm_copy_field() function copies strings into userspace buffers, so the first parameter needs to have a __user annotation to avoid warnings from the sparse checker. Signed-off-by: Thierry Reding Reviewed-by: David Herrmann Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 8218078..0cc1827 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -233,7 +233,7 @@ module_exit(drm_core_exit); /** * Copy and IOCTL return string to user space */ -static int drm_copy_field(char *buf, size_t *buf_len, const char *value) +static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value) { int len; -- cgit v0.10.2 From ee3939e07920f4a5405aba5f40529fd0fdd1dfb5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 13:15:51 +0200 Subject: drm/ttm: Fix a few sparse warnings The final parameter to ttm_bo_reserve() is a pointer, therefore callers should use NULL instead of 0. Fixes a bunch of sparse warnings of this type: warning: Using plain integer as NULL pointer Signed-off-by: Thierry Reding Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5d6a875..957d4fa 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -362,7 +362,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait) { int ret; - ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL); if (ret) { if (ret != -ERESTARTSYS && ret != -EBUSY) DRM_ERROR("reserve failed %p\n", bo); diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 19cf3e9..fe95d31 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper, bo = gem_to_bochs_bo(gobj); - ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL); if (ret) return ret; diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index b3c6130..9d7346b 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (old_fb) { bochs_fb = to_bochs_framebuffer(old_fb); bo = gem_to_bochs_bo(bochs_fb->obj); - ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL); if (ret) { DRM_ERROR("failed to reserve old_fb bo\n"); } else { @@ -67,7 +67,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, bochs_fb = to_bochs_framebuffer(crtc->primary->fb); bo = gem_to_bochs_bo(bochs_fb->obj); - ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 117d3ec..401c890 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -241,7 +241,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait) { int ret; - ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL); if (ret) { if (ret != -ERESTARTSYS && ret != -EBUSY) DRM_ERROR("reserve failed %p\n", bo); diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index cf11ee6..80de23d 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -280,7 +280,7 @@ static inline int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait) { int ret; - ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL); if (ret) { if (ret != -ERESTARTSYS && ret != -EBUSY) DRM_ERROR("reserve failed %p\n", bo); diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index b6dc85c..ba29a70 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -309,7 +309,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) struct ttm_buffer_object *bo = &nvbo->bo; int ret; - ret = ttm_bo_reserve(bo, false, false, false, 0); + ret = ttm_bo_reserve(bo, false, false, false, NULL); if (ret) goto out; @@ -350,7 +350,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) struct ttm_buffer_object *bo = &nvbo->bo; int ret, ref; - ret = ttm_bo_reserve(bo, false, false, false, 0); + ret = ttm_bo_reserve(bo, false, false, false, NULL); if (ret) return ret; @@ -385,7 +385,7 @@ nouveau_bo_map(struct nouveau_bo *nvbo) { int ret; - ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index c90c0dc..df9d451 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -61,7 +61,7 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) if (!cli->base.vm) return 0; - ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); if (ret) return ret; @@ -132,7 +132,7 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) if (!cli->base.vm) return; - ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); + ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL); if (ret) return; diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index d458a14..83a4232 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -31,7 +31,7 @@ static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait) { int r; - r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; @@ -67,7 +67,7 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, { int r; - r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0); + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c index 70ddce835..ed1d510 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -61,7 +61,7 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv, vmw_execbuf_release_pinned_bo(dev_priv); - ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + ret = ttm_bo_reserve(bo, interruptible, false, false, NULL); if (unlikely(ret != 0)) goto err; @@ -105,7 +105,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, if (pin) vmw_execbuf_release_pinned_bo(dev_priv); - ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + ret = ttm_bo_reserve(bo, interruptible, false, false, NULL); if (unlikely(ret != 0)) goto err; @@ -212,7 +212,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv, if (pin) vmw_execbuf_release_pinned_bo(dev_priv); - ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + ret = ttm_bo_reserve(bo, interruptible, false, false, NULL); if (unlikely(ret != 0)) goto err_unlock; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index f31a754..18b54ac 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -316,7 +316,7 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) if (unlikely(ret != 0)) return ret; - ret = ttm_bo_reserve(bo, false, true, false, 0); + ret = ttm_bo_reserve(bo, false, true, false, NULL); BUG_ON(ret != 0); ret = ttm_bo_kmap(bo, 0, 1, &map); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 991e5c8..d2bc2b0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -136,7 +136,7 @@ int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, kmap_offset = 0; kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT; - ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0); + ret = ttm_bo_reserve(&dmabuf->base, true, false, false, NULL); if (unlikely(ret != 0)) { DRM_ERROR("reserve failed\n"); return -EINVAL; @@ -343,7 +343,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT; kmap_num = (64*64*4) >> PAGE_SHIFT; - ret = ttm_bo_reserve(bo, true, false, false, 0); + ret = ttm_bo_reserve(bo, true, false, false, NULL); if (unlikely(ret != 0)) { DRM_ERROR("reserve failed\n"); return; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 01d68f0..a432c0d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -127,7 +127,7 @@ static void vmw_resource_release(struct kref *kref) if (res->backup) { struct ttm_buffer_object *bo = &res->backup->base; - ttm_bo_reserve(bo, false, false, false, 0); + ttm_bo_reserve(bo, false, false, false, NULL); if (!list_empty(&res->mob_head) && res->func->unbind != NULL) { struct ttm_validate_buffer val_buf; -- cgit v0.10.2 From 8fa6a4255e80537774126440a056c8e4fa7e30a0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 13:23:57 +0200 Subject: drm/dp: Staticize a couple of DP utility functions sparse complains about these functions missing a prototype, but looking closer they aren't (and likely not supposed to be) used outside of this source file, so they can be made static. Signed-off-by: Thierry Reding Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 618526d..ac3c273 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1484,10 +1484,10 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, return 0; } -int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - int pbn) +static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + int pbn) { struct drm_dp_sideband_msg_tx *txmsg; struct drm_dp_mst_branch *mstb; @@ -1538,10 +1538,10 @@ static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, return 0; } -int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - struct drm_dp_payload *payload) +static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) { int ret; ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); @@ -1551,10 +1551,10 @@ int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, return ret; } -int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int id, - struct drm_dp_payload *payload) +static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) { DRM_DEBUG_KMS("\n"); /* its okay for these to fail */ @@ -1567,9 +1567,9 @@ int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, return 0; } -int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, - int id, - struct drm_dp_payload *payload) +static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload) { payload->payload_state = 0; return 0; -- cgit v0.10.2 From 0e32b39ceed665bfa4a77a4bc307b6652b991632 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 14:02:48 +1000 Subject: drm/i915: add DP 1.2 MST support (v0.7) This adds DP 1.2 MST support on Haswell systems. Notes: a) this reworks irq handling for DP MST ports, so that we can avoid the mode config locking in the current hpd handlers, as we need to process up/down msgs at a better time. Changes since v0.1: use PORT_PCH_HOTPLUG to detect short vs long pulses add a workqueue to deal with digital events as they can get blocked on the main workqueue beyong mode_config mutex fix a bunch of modeset checker warnings acks irqs in the driver cleanup the MST encoders Changes since v0.2: check irq status again in work handler move around bring up and tear down to fix DPMS on/off use path properties. Changes since v0.3: updates for mst apis more state checker fixes irq handling improvements fbcon handling support improved reference counting of link - fixes redocking. Changes since v0.4: handle gpu reset hpd reinit without oopsing check link status on HPD irqs fix suspend/resume Changes since v0.5: use proper functions to get max link/lane counts fix another checker backtrace - due to connectors disappearing. set output type in more places fro, unknown->displayport don't talk to devices if no HPD asserted check mst on short irqs only check link status properly rebase onto prepping irq changes. drop unsued force_act Changes since v0.6: cleanup unused struct entry. [airlied: fix some sparse warnings]. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index cad1683..91bd167 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -59,6 +59,7 @@ i915-y += dvo_ch7017.o \ intel_crt.o \ intel_ddi.o \ intel_dp.o \ + intel_dp_mst.o \ intel_dsi_cmd.o \ intel_dsi.o \ intel_dsi_pll.o \ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6df6506..d335c46 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1717,6 +1717,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_mtrrfree; } + dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0); + if (dev_priv->dp_wq == NULL) { + DRM_ERROR("Failed to create our dp workqueue.\n"); + ret = -ENOMEM; + goto out_freewq; + } + intel_irq_init(dev); intel_uncore_sanitize(dev); @@ -1792,6 +1799,8 @@ out_gem_unload: intel_teardown_gmbus(dev); intel_teardown_mchbar(dev); pm_qos_remove_request(&dev_priv->pm_qos); + destroy_workqueue(dev_priv->dp_wq); +out_freewq: destroy_workqueue(dev_priv->wq); out_mtrrfree: arch_phys_wc_del(dev_priv->gtt.mtrr); @@ -1892,6 +1901,7 @@ int i915_driver_unload(struct drm_device *dev) intel_teardown_gmbus(dev); intel_teardown_mchbar(dev); + destroy_workqueue(dev_priv->dp_wq); destroy_workqueue(dev_priv->wq); pm_qos_remove_request(&dev_priv->pm_qos); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 83cb43a..a361bb9 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -518,7 +518,6 @@ static int i915_drm_freeze(struct drm_device *dev) flush_delayed_work(&dev_priv->rps.delayed_resume_work); - intel_runtime_pm_disable_interrupts(dev); intel_suspend_gt_powersave(dev); @@ -532,6 +531,9 @@ static int i915_drm_freeze(struct drm_device *dev) } drm_modeset_unlock_all(dev); + intel_dp_mst_suspend(dev); + intel_runtime_pm_disable_interrupts(dev); + intel_modeset_suspend_hw(dev); } @@ -646,6 +648,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) intel_modeset_init_hw(dev); + { + unsigned long irqflags; + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + } + + intel_dp_mst_resume(dev); drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); drm_modeset_unlock_all(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2dc3a92..7db16be 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1595,6 +1595,15 @@ struct drm_i915_private { u32 short_hpd_port_mask; struct work_struct dig_port_work; + /* + * if we get a HPD irq from DP and a HPD irq from non-DP + * the non-DP HPD could block the workqueue on a mode config + * mutex getting, that userspace may have taken. However + * userspace is waiting on the DP workqueue to run which is + * blocked behind the non-DP one. + */ + struct workqueue_struct *dp_wq; + /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct i915_dri1_state dri1; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 12d77b5..07e1a40 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1846,7 +1846,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, * deadlock. */ if (queue_dig) - schedule_work(&dev_priv->dig_port_work); + queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work); if (queue_hp) schedule_work(&dev_priv->hotplug_work); } @@ -4739,7 +4739,9 @@ void intel_hpd_init(struct drm_device *dev) list_for_each_entry(connector, &mode_config->connector_list, head) { struct intel_connector *intel_connector = to_intel_connector(connector); connector->polled = intel_connector->polled; - if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE) + if (connector->encoder && !connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE) + connector->polled = DRM_CONNECTOR_POLL_HPD; + if (intel_connector->mst_port) connector->polled = DRM_CONNECTOR_POLL_HPD; } diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 1aec425..9b1542f 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) struct drm_encoder *encoder = &intel_encoder->base; int type = intel_encoder->type; - if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || + if (type == INTEL_OUTPUT_DP_MST) { + struct intel_digital_port *intel_dig_port = enc_to_mst(encoder)->primary; + return intel_dig_port->port; + } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) { struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); @@ -584,8 +587,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, return (refclk * n * 100) / (p * r); } -static void intel_ddi_clock_get(struct intel_encoder *encoder, - struct intel_crtc_config *pipe_config) +void intel_ddi_clock_get(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; int link_clock = 0; @@ -755,8 +758,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) int type = intel_encoder->type; uint32_t temp; - if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { - + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP_MST) { temp = TRANS_MSA_SYNC_CLK; switch (intel_crtc->config.pipe_bpp) { case 18: @@ -778,6 +780,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) } } +void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; + uint32_t temp; + temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + if (state == true) + temp |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC; + else + temp &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC; + I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp); +} + void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -857,7 +874,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - temp |= TRANS_DDI_MODE_SELECT_DP_SST; + if (intel_dp->is_mst) { + temp |= TRANS_DDI_MODE_SELECT_DP_MST; + } else + temp |= TRANS_DDI_MODE_SELECT_DP_SST; + + temp |= DDI_PORT_WIDTH(intel_dp->lane_count); + } else if (type == INTEL_OUTPUT_DP_MST) { + struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp; + + if (intel_dp->is_mst) { + temp |= TRANS_DDI_MODE_SELECT_DP_MST; + } else + temp |= TRANS_DDI_MODE_SELECT_DP_SST; temp |= DDI_PORT_WIDTH(intel_dp->lane_count); } else { @@ -874,7 +903,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); uint32_t val = I915_READ(reg); - val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK); + val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK | TRANS_DDI_DP_VC_PAYLOAD_ALLOC); val |= TRANS_DDI_PORT_NONE; I915_WRITE(reg, val); } @@ -913,8 +942,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) case TRANS_DDI_MODE_SELECT_DP_SST: if (type == DRM_MODE_CONNECTOR_eDP) return true; - case TRANS_DDI_MODE_SELECT_DP_MST: return (type == DRM_MODE_CONNECTOR_DisplayPort); + case TRANS_DDI_MODE_SELECT_DP_MST: + /* if the transcoder is in MST state then + * connector isn't connected */ + return false; case TRANS_DDI_MODE_SELECT_FDI: return (type == DRM_MODE_CONNECTOR_VGA); @@ -966,6 +998,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(port)) { + if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST) + return false; + *pipe = i; return true; } @@ -1272,10 +1307,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) intel_wait_ddi_buf_idle(dev_priv, port); } - val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST | + val = DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE; - if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) - val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + if (intel_dp->is_mst) + val |= DP_TP_CTL_MODE_MST; + else { + val |= DP_TP_CTL_MODE_SST; + if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) + val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + } I915_WRITE(DP_TP_CTL(port), val); POSTING_READ(DP_TP_CTL(port)); @@ -1314,11 +1354,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc) static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); - int type = intel_encoder->type; + struct intel_digital_port *intel_dig_port = enc_to_dig_port(&intel_encoder->base); + int type = intel_dig_port->base.type; + + if (type != INTEL_OUTPUT_DISPLAYPORT && + type != INTEL_OUTPUT_EDP && + type != INTEL_OUTPUT_UNKNOWN) { + return; + } - if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) - intel_dp_check_link_status(intel_dp); + intel_dp_hot_plug(intel_encoder); } void intel_ddi_get_config(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 421ea71..7b542b4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -101,6 +101,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc); static void intel_set_pipe_csc(struct drm_crtc *crtc); static void vlv_prepare_pll(struct intel_crtc *crtc); +static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe) +{ + if (!connector->mst_port) + return connector->encoder; + else + return &connector->mst_port->mst_encoders[pipe]->base; +} + typedef struct { int min, max; } intel_range_t; @@ -4130,6 +4138,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) lpt_pch_enable(crtc); + if (intel_crtc->config.dp_encoder_is_mst) + intel_ddi_set_vc_payload_alloc(crtc, true); + for_each_encoder_on_crtc(dev, crtc, encoder) { encoder->enable(encoder); intel_opregion_notify_encoder(encoder, true); @@ -4178,6 +4189,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_disable_pipe(dev_priv, pipe); + if (intel_crtc->config.dp_encoder_is_mst) + intel_ddi_set_vc_payload_alloc(crtc, false); + ironlake_pfit_disable(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) @@ -4336,6 +4350,9 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder) case INTEL_OUTPUT_EDP: intel_dig_port = enc_to_dig_port(&intel_encoder->base); return port_to_power_domain(intel_dig_port->port); + case INTEL_OUTPUT_DP_MST: + intel_dig_port = enc_to_mst(&intel_encoder->base)->primary; + return port_to_power_domain(intel_dig_port->port); case INTEL_OUTPUT_ANALOG: return POWER_DOMAIN_PORT_CRT; case INTEL_OUTPUT_DSI: @@ -5004,6 +5021,10 @@ static void intel_connector_check_state(struct intel_connector *connector) connector->base.base.id, connector->base.name); + /* there is no real hw state for MST connectors */ + if (connector->mst_port) + return; + WARN(connector->base.dpms == DRM_MODE_DPMS_OFF, "wrong connector dpms state\n"); WARN(connector->base.encoder != &encoder->base, @@ -10524,6 +10545,14 @@ check_encoder_state(struct drm_device *dev) if (connector->base.dpms != DRM_MODE_DPMS_OFF) active = true; } + /* + * for MST connectors if we unplug the connector is gone + * away but the encoder is still connected to a crtc + * until a modeset happens in response to the hotplug. + */ + if (!enabled && encoder->base.encoder_type == DRM_MODE_ENCODER_DPMST) + continue; + WARN(!!encoder->base.crtc != enabled, "encoder's enabled state mismatch " "(expected %i, found %i)\n", @@ -11069,7 +11098,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, * for them. */ for (ro = 0; ro < set->num_connectors; ro++) { if (set->connectors[ro] == &connector->base) { - connector->new_encoder = connector->encoder; + connector->new_encoder = intel_find_encoder(connector, to_intel_crtc(set->crtc)->pipe); break; } } @@ -11115,7 +11144,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, new_crtc)) { return -EINVAL; } - connector->encoder->new_crtc = to_intel_crtc(new_crtc); + connector->new_encoder->new_crtc = to_intel_crtc(new_crtc); DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", connector->base.base.id, @@ -11149,7 +11178,12 @@ intel_modeset_stage_output_state(struct drm_device *dev, } } /* Now we've also updated encoder->new_crtc for all encoders. */ - + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (connector->new_encoder) + if (connector->new_encoder != connector->encoder) + connector->encoder = connector->new_encoder; + } for_each_intel_crtc(dev, crtc) { crtc->new_enabled = false; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 30816b3..e7a7953 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -112,7 +112,7 @@ static void intel_dp_link_down(struct intel_dp *intel_dp); static bool _edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); -static int +int intel_dp_max_link_bw(struct intel_dp *intel_dp) { int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; @@ -740,8 +740,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) { struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base); - sysfs_remove_link(&intel_connector->base.kdev->kobj, - intel_dp->aux.ddc.dev.kobj.name); + if (!intel_connector->mst_port) + sysfs_remove_link(&intel_connector->base.kdev->kobj, + intel_dp->aux.ddc.dev.kobj.name); intel_connector_unregister(intel_connector); } @@ -3309,6 +3310,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) edp_panel_vdd_off(intel_dp, false); } +static bool +intel_dp_probe_mst(struct intel_dp *intel_dp) +{ + u8 buf[1]; + + if (!intel_dp->can_mst) + return false; + + if (intel_dp->dpcd[DP_DPCD_REV] < 0x12) + return false; + + _edp_panel_vdd_on(intel_dp); + if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) { + if (buf[0] & DP_MST_CAP) { + DRM_DEBUG_KMS("Sink is MST capable\n"); + intel_dp->is_mst = true; + } else { + DRM_DEBUG_KMS("Sink is not MST capable\n"); + intel_dp->is_mst = false; + } + } + edp_panel_vdd_off(intel_dp, false); + + drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); + return intel_dp->is_mst; +} + int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -3346,6 +3374,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) sink_irq_vector, 1) == 1; } +static bool +intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector) +{ + int ret; + + ret = intel_dp_dpcd_read_wake(&intel_dp->aux, + DP_SINK_COUNT_ESI, + sink_irq_vector, 14); + if (ret != 14) + return false; + + return true; +} + static void intel_dp_handle_test_request(struct intel_dp *intel_dp) { @@ -3353,6 +3395,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK); } +static int +intel_dp_check_mst_status(struct intel_dp *intel_dp) +{ + bool bret; + + if (intel_dp->is_mst) { + u8 esi[16] = { 0 }; + int ret = 0; + int retry; + bool handled; + bret = intel_dp_get_sink_irq_esi(intel_dp, esi); +go_again: + if (bret == true) { + + /* check link status - esi[10] = 0x200c */ + if (intel_dp->active_mst_links && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) { + DRM_DEBUG_KMS("channel EQ not ok, retraining\n"); + intel_dp_start_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); + intel_dp_stop_link_train(intel_dp); + } + + DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]); + ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled); + + if (handled) { + for (retry = 0; retry < 3; retry++) { + int wret; + wret = drm_dp_dpcd_write(&intel_dp->aux, + DP_SINK_COUNT_ESI+1, + &esi[1], 3); + if (wret == 3) { + break; + } + } + + bret = intel_dp_get_sink_irq_esi(intel_dp, esi); + if (bret == true) { + DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]); + goto go_again; + } + } else + ret = 0; + + return ret; + } else { + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + DRM_DEBUG_KMS("failed to get ESI - device may have failed\n"); + intel_dp->is_mst = false; + drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); + /* send a hotplug event */ + drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev); + } + } + return -EINVAL; +} + /* * According to DP spec * 5.1.2: @@ -3361,7 +3460,6 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) * 3. Use Link Training from 2.5.3.3 and 3.5.1.3 * 4. Check link status on receipt of hot-plug interrupt */ - void intel_dp_check_link_status(struct intel_dp *intel_dp) { @@ -3581,6 +3679,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) enum drm_connector_status status; enum intel_display_power_domain power_domain; struct edid *edid = NULL; + bool ret; intel_runtime_pm_get(dev_priv); @@ -3590,6 +3689,14 @@ intel_dp_detect(struct drm_connector *connector, bool force) DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + if (intel_dp->is_mst) { + /* MST devices are disconnected from a monitor POV */ + if (intel_encoder->type != INTEL_OUTPUT_EDP) + intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + status = connector_status_disconnected; + goto out; + } + intel_dp->has_audio = false; if (HAS_PCH_SPLIT(dev)) @@ -3602,6 +3709,16 @@ intel_dp_detect(struct drm_connector *connector, bool force) intel_dp_probe_oui(intel_dp); + ret = intel_dp_probe_mst(intel_dp); + if (ret) { + /* if we are in MST mode then this connector + won't appear connected or have anything with EDID on it */ + if (intel_encoder->type != INTEL_OUTPUT_EDP) + intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + status = connector_status_disconnected; + goto out; + } + if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); } else { @@ -3797,6 +3914,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) struct drm_device *dev = intel_dp_to_dev(intel_dp); drm_dp_aux_unregister(&intel_dp->aux); + intel_dp_mst_encoder_cleanup(intel_dig_port); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); @@ -3825,28 +3943,62 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = { .destroy = intel_dp_encoder_destroy, }; -static void +void intel_dp_hot_plug(struct intel_encoder *intel_encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); - - intel_dp_check_link_status(intel_dp); + return; } bool intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) { struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + if (intel_dig_port->base.type != INTEL_OUTPUT_EDP) + intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT; - if (long_hpd) - return true; + DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port, + long_hpd ? "long" : "short"); - /* - * we'll check the link status via the normal hot plug path later - - * but for short hpds we should check it now - */ - intel_dp_check_link_status(intel_dp); + if (long_hpd) { + if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + goto mst_fail; + + if (!intel_dp_get_dpcd(intel_dp)) { + goto mst_fail; + } + + intel_dp_probe_oui(intel_dp); + + if (!intel_dp_probe_mst(intel_dp)) + goto mst_fail; + + } else { + if (intel_dp->is_mst) { + ret = intel_dp_check_mst_status(intel_dp); + if (ret == -EINVAL) + goto mst_fail; + } + + if (!intel_dp->is_mst) { + /* + * we'll check the link status via the normal hot plug path later - + * but for short hpds we should check it now + */ + intel_dp_check_link_status(intel_dp); + } + } return false; +mst_fail: + /* if we were in MST mode, and device is not there get out of MST mode */ + if (intel_dp->is_mst) { + DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", intel_dp->is_mst, intel_dp->mst_mgr.mst_state); + intel_dp->is_mst = false; + drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); + } + return true; } /* Return which DP Port should be selected for Transcoder DP control */ @@ -3897,7 +4049,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port) return false; } -static void +void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); @@ -4391,6 +4543,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_aux_init(intel_dp, intel_connector); + /* init MST on ports that can support it */ + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { + if (port == PORT_B || port == PORT_C || port == PORT_D) { + intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id); + } + } + if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) { drm_dp_aux_unregister(&intel_dp->aux); if (is_edp(intel_dp)) { @@ -4487,3 +4646,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) kfree(intel_connector); } } + +void intel_dp_mst_suspend(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + /* disable MST */ + for (i = 0; i < I915_MAX_PORTS; i++) { + struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i]; + if (!intel_dig_port) + continue; + + if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) { + if (!intel_dig_port->dp.can_mst) + continue; + if (intel_dig_port->dp.is_mst) + drm_dp_mst_topology_mgr_suspend(&intel_dig_port->dp.mst_mgr); + } + } +} + +void intel_dp_mst_resume(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < I915_MAX_PORTS; i++) { + struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i]; + if (!intel_dig_port) + continue; + if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) { + int ret; + + if (!intel_dig_port->dp.can_mst) + continue; + + ret = drm_dp_mst_topology_mgr_resume(&intel_dig_port->dp.mst_mgr); + if (ret != 0) { + intel_dp_check_mst_status(&intel_dig_port->dp); + } + } + } +} diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c new file mode 100644 index 0000000..ec48d56 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -0,0 +1,534 @@ +/* + * Copyright © 2008 Intel Corporation + * 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include +#include "i915_drv.h" +#include "intel_drv.h" +#include +#include + +static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = encoder->base.dev; + int bpp; + int lane_count, slots; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + struct intel_connector *found = NULL, *intel_connector; + int mst_pbn; + + pipe_config->dp_encoder_is_mst = true; + pipe_config->has_pch_encoder = false; + pipe_config->has_dp_encoder = true; + bpp = 24; + /* + * for MST we always configure max link bw - the spec doesn't + * seem to suggest we should do otherwise. + */ + lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + intel_dp->link_bw = intel_dp_max_link_bw(intel_dp); + intel_dp->lane_count = lane_count; + + pipe_config->pipe_bpp = 24; + pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw); + + list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) { + if (intel_connector->new_encoder == encoder) { + found = intel_connector; + break; + } + } + + if (!found) { + DRM_ERROR("can't find connector\n"); + return false; + } + + mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp); + + pipe_config->pbn = mst_pbn; + slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn); + + intel_link_compute_m_n(bpp, lane_count, + adjusted_mode->crtc_clock, + pipe_config->port_clock, + &pipe_config->dp_m_n); + + pipe_config->dp_m_n.tu = slots; + return true; + +} + +static void intel_mst_disable_dp(struct intel_encoder *encoder) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + int ret; + + DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); + + drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->port); + + ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); + if (ret) { + DRM_ERROR("failed to update payload %d\n", ret); + } +} + +static void intel_mst_post_disable_dp(struct intel_encoder *encoder) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + + DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); + + /* this can fail */ + drm_dp_check_act_status(&intel_dp->mst_mgr); + /* and this can also fail */ + drm_dp_update_payload_part2(&intel_dp->mst_mgr); + + drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->port); + + intel_dp->active_mst_links--; + intel_mst->port = NULL; + if (intel_dp->active_mst_links == 0) { + intel_dig_port->base.post_disable(&intel_dig_port->base); + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); + } +} + +static void intel_mst_pre_enable_dp(struct intel_encoder *encoder) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dig_port->port; + int ret; + uint32_t temp; + struct intel_connector *found = NULL, *intel_connector; + int slots; + struct drm_crtc *crtc = encoder->base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) { + if (intel_connector->new_encoder == encoder) { + found = intel_connector; + break; + } + } + + if (!found) { + DRM_ERROR("can't find connector\n"); + return; + } + + DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); + intel_mst->port = found->port; + + if (intel_dp->active_mst_links == 0) { + enum port port = intel_ddi_get_encoder_port(encoder); + + I915_WRITE(PORT_CLK_SEL(port), intel_crtc->config.ddi_pll_sel); + + intel_ddi_init_dp_buf_reg(&intel_dig_port->base); + + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + + + intel_dp_start_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); + intel_dp_stop_link_train(intel_dp); + } + + ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr, + intel_mst->port, intel_crtc->config.pbn, &slots); + if (ret == false) { + DRM_ERROR("failed to allocate vcpi\n"); + return; + } + + + intel_dp->active_mst_links++; + temp = I915_READ(DP_TP_STATUS(port)); + I915_WRITE(DP_TP_STATUS(port), temp); + + ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); +} + +static void intel_mst_enable_dp(struct intel_encoder *encoder) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dig_port->port; + int ret; + + DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); + + if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT), + 1)) + DRM_ERROR("Timed out waiting for ACT sent\n"); + + ret = drm_dp_check_act_status(&intel_dp->mst_mgr); + + ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr); +} + +static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + *pipe = intel_mst->pipe; + if (intel_mst->port) + return true; + return false; +} + +static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + u32 temp, flags = 0; + + pipe_config->has_dp_encoder = true; + + temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + if (temp & TRANS_DDI_PHSYNC) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + if (temp & TRANS_DDI_PVSYNC) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + switch (temp & TRANS_DDI_BPC_MASK) { + case TRANS_DDI_BPC_6: + pipe_config->pipe_bpp = 18; + break; + case TRANS_DDI_BPC_8: + pipe_config->pipe_bpp = 24; + break; + case TRANS_DDI_BPC_10: + pipe_config->pipe_bpp = 30; + break; + case TRANS_DDI_BPC_12: + pipe_config->pipe_bpp = 36; + break; + default: + break; + } + pipe_config->adjusted_mode.flags |= flags; + intel_dp_get_m_n(crtc, pipe_config); + + intel_ddi_clock_get(&intel_dig_port->base, pipe_config); +} + +static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_dp *intel_dp = intel_connector->mst_port; + struct edid *edid; + int ret; + + edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port); + if (!edid) + return 0; + + ret = intel_connector_update_modes(connector, edid); + kfree(edid); + + return ret; +} + +static enum drm_connector_status +intel_mst_port_dp_detect(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_dp *intel_dp = intel_connector->mst_port; + + return drm_dp_mst_detect_port(&intel_dp->mst_mgr, intel_connector->port); +} + +static enum drm_connector_status +intel_dp_mst_detect(struct drm_connector *connector, bool force) +{ + enum drm_connector_status status; + status = intel_mst_port_dp_detect(connector); + return status; +} + +static int +intel_dp_mst_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + return 0; +} + +static void +intel_dp_mst_connector_destroy(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + + if (!IS_ERR_OR_NULL(intel_connector->edid)) + kfree(intel_connector->edid); + + drm_connector_cleanup(connector); + kfree(connector); +} + +static const struct drm_connector_funcs intel_dp_mst_connector_funcs = { + .dpms = intel_connector_dpms, + .detect = intel_dp_mst_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = intel_dp_mst_set_property, + .destroy = intel_dp_mst_connector_destroy, +}; + +static int intel_dp_mst_get_modes(struct drm_connector *connector) +{ + return intel_dp_mst_get_ddc_modes(connector); +} + +static enum drm_mode_status +intel_dp_mst_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* TODO - validate mode against available PBN for link */ + if (mode->clock < 10000) + return MODE_CLOCK_LOW; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + return MODE_H_ILLEGAL; + + return MODE_OK; +} + +static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_dp *intel_dp = intel_connector->mst_port; + return &intel_dp->mst_encoders[0]->base.base; +} + +static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = { + .get_modes = intel_dp_mst_get_modes, + .mode_valid = intel_dp_mst_mode_valid, + .best_encoder = intel_mst_best_encoder, +}; + +static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder) +{ + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder); + + drm_encoder_cleanup(encoder); + kfree(intel_mst); +} + +static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = { + .destroy = intel_dp_mst_encoder_destroy, +}; + +static bool intel_dp_mst_get_hw_state(struct intel_connector *connector) +{ + if (connector->encoder) { + enum pipe pipe; + if (!connector->encoder->get_hw_state(connector->encoder, &pipe)) + return false; + return true; + } + return false; +} + +static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop) +{ + struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_connector *intel_connector; + struct drm_connector *connector; + int i; + + intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); + if (!intel_connector) + return NULL; + + connector = &intel_connector->base; + drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); + drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs); + + intel_connector->unregister = intel_connector_unregister; + intel_connector->get_hw_state = intel_dp_mst_get_hw_state; + intel_connector->mst_port = intel_dp; + intel_connector->port = port; + + for (i = PIPE_A; i <= PIPE_C; i++) { + drm_mode_connector_attach_encoder(&intel_connector->base, + &intel_dp->mst_encoders[i]->base.base); + } + intel_dp_add_properties(intel_dp, connector); + + drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0); + drm_mode_connector_set_path_property(connector, pathprop); + drm_reinit_primary_mode_group(dev); + mutex_lock(&dev->mode_config.mutex); + drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, connector); + mutex_unlock(&dev->mode_config.mutex); + drm_connector_register(&intel_connector->base); + return connector; +} + +static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + /* need to nuke the connector */ + mutex_lock(&dev->mode_config.mutex); + intel_connector_dpms(connector, DRM_MODE_DPMS_OFF); + mutex_unlock(&dev->mode_config.mutex); + + intel_connector->unregister(intel_connector); + + mutex_lock(&dev->mode_config.mutex); + drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, connector); + drm_connector_cleanup(connector); + mutex_unlock(&dev->mode_config.mutex); + + drm_reinit_primary_mode_group(dev); + + kfree(intel_connector); + DRM_DEBUG_KMS("\n"); +} + +static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) +{ + struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + + drm_kms_helper_hotplug_event(dev); +} + +static struct drm_dp_mst_topology_cbs mst_cbs = { + .add_connector = intel_dp_add_mst_connector, + .destroy_connector = intel_dp_destroy_mst_connector, + .hotplug = intel_dp_mst_hotplug, +}; + +static struct intel_dp_mst_encoder * +intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe) +{ + struct intel_dp_mst_encoder *intel_mst; + struct intel_encoder *intel_encoder; + struct drm_device *dev = intel_dig_port->base.base.dev; + + intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL); + + if (!intel_mst) + return NULL; + + intel_mst->pipe = pipe; + intel_encoder = &intel_mst->base; + intel_mst->primary = intel_dig_port; + + drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs, + DRM_MODE_ENCODER_DPMST); + + intel_encoder->type = INTEL_OUTPUT_DP_MST; + intel_encoder->crtc_mask = 0x7; + intel_encoder->cloneable = 0; + + intel_encoder->compute_config = intel_dp_mst_compute_config; + intel_encoder->disable = intel_mst_disable_dp; + intel_encoder->post_disable = intel_mst_post_disable_dp; + intel_encoder->pre_enable = intel_mst_pre_enable_dp; + intel_encoder->enable = intel_mst_enable_dp; + intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state; + intel_encoder->get_config = intel_dp_mst_enc_get_config; + + return intel_mst; + +} + +static bool +intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port) +{ + int i; + struct intel_dp *intel_dp = &intel_dig_port->dp; + + for (i = PIPE_A; i <= PIPE_C; i++) + intel_dp->mst_encoders[i] = intel_dp_create_fake_mst_encoder(intel_dig_port, i); + return true; +} + +int +intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id) +{ + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_device *dev = intel_dig_port->base.base.dev; + int ret; + + intel_dp->can_mst = true; + intel_dp->mst_mgr.cbs = &mst_cbs; + + /* create encoders */ + intel_dp_create_fake_mst_encoders(intel_dig_port); + ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id); + if (ret) { + intel_dp->can_mst = false; + return ret; + } + return 0; +} + +void +intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port) +{ + struct intel_dp *intel_dp = &intel_dig_port->dp; + + if (!intel_dp->can_mst) + return; + + drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr); + /* encoders will get killed by normal cleanup */ +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 40086e1..1dfd1e5 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include /** * _wait_for - magic (register) wait macro @@ -100,6 +100,7 @@ #define INTEL_OUTPUT_EDP 8 #define INTEL_OUTPUT_DSI 9 #define INTEL_OUTPUT_UNKNOWN 10 +#define INTEL_OUTPUT_DP_MST 11 #define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_LVDS 1 @@ -207,6 +208,10 @@ struct intel_connector { /* since POLL and HPD connectors may use the same HPD line keep the native state of connector->polled in case hotplug storm detection changes it */ u8 polled; + + void *port; /* store this opaque as its illegal to dereference it */ + + struct intel_dp *mst_port; }; typedef struct dpll { @@ -351,6 +356,9 @@ struct intel_crtc_config { bool ips_enabled; bool double_wide; + + bool dp_encoder_is_mst; + int pbn; }; struct intel_pipe_wm { @@ -506,6 +514,7 @@ struct intel_hdmi { struct drm_display_mode *adjusted_mode); }; +struct intel_dp_mst_encoder; #define DP_MAX_DOWNSTREAM_PORTS 0x10 /** @@ -545,8 +554,16 @@ struct intel_dp { unsigned long last_power_on; unsigned long last_backlight_off; bool use_tps3; + bool can_mst; /* this port supports mst */ + bool is_mst; + int active_mst_links; + /* connector directly attached - won't be use for modeset in mst world */ struct intel_connector *attached_connector; + /* mst connector list */ + struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES]; + struct drm_dp_mst_topology_mgr mst_mgr; + uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index); /* * This function returns the value we have to program the AUX_CTL @@ -573,6 +590,13 @@ struct intel_digital_port { bool (*hpd_pulse)(struct intel_digital_port *, bool); }; +struct intel_dp_mst_encoder { + struct intel_encoder base; + enum pipe pipe; + struct intel_digital_port *primary; + void *port; /* store this opaque as its illegal to dereference it */ +}; + static inline int vlv_dport_to_channel(struct intel_digital_port *dport) { @@ -657,6 +681,12 @@ enc_to_dig_port(struct drm_encoder *encoder) return container_of(encoder, struct intel_digital_port, base.base); } +static inline struct intel_dp_mst_encoder * +enc_to_mst(struct drm_encoder *encoder) +{ + return container_of(encoder, struct intel_dp_mst_encoder, base.base); +} + static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) { return &enc_to_dig_port(encoder)->dp; @@ -719,6 +749,9 @@ void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config); void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder); +void intel_ddi_clock_get(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config); +void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state); /* intel_display.c */ const char *intel_output_name(int output); @@ -871,6 +904,15 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); void intel_edp_psr_exit(struct drm_device *dev); void intel_edp_psr_init(struct drm_device *dev); +int intel_dp_handle_hpd_irq(struct intel_digital_port *digport, bool long_hpd); +void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector); +void intel_dp_mst_suspend(struct drm_device *dev); +void intel_dp_mst_resume(struct drm_device *dev); +int intel_dp_max_link_bw(struct intel_dp *intel_dp); +void intel_dp_hot_plug(struct intel_encoder *intel_encoder); +/* intel_dp_mst.c */ +int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); +void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port); /* intel_dsi.c */ void intel_dsi_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index f475414..7cdb886 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -375,6 +375,11 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, } encoder = connector->encoder; + if (!encoder) { + struct drm_connector_helper_funcs *connector_funcs; + connector_funcs = connector->helper_private; + encoder = connector_funcs->best_encoder(connector); + } if (!encoder || WARN_ON(!encoder->crtc)) { DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", connector->name); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 2e2c71f..d37934b 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, case INTEL_OUTPUT_UNKNOWN: case INTEL_OUTPUT_DISPLAYPORT: case INTEL_OUTPUT_HDMI: + case INTEL_OUTPUT_DP_MST: type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL; break; case INTEL_OUTPUT_EDP: -- cgit v0.10.2 From 11bed958b72e15fd12d78c30ce49047b94817840 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 12 May 2014 15:22:27 +1000 Subject: drm/i915: mst topology dumper in debugfs (v0.2) use the mst helper code to dump the topology in debugfs. v0.2: drop is_mst check - as we want to dump other info Reviewed-by: Todd Previte Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f27daf7..e6ff0ec 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2405,6 +2405,28 @@ struct pipe_crc_info { enum pipe pipe; }; +static int i915_dp_mst_info(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_encoder *encoder; + struct intel_encoder *intel_encoder; + struct intel_digital_port *intel_dig_port; + drm_modeset_lock_all(dev); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + intel_encoder = to_intel_encoder(encoder); + if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT) + continue; + intel_dig_port = enc_to_dig_port(encoder); + if (!intel_dig_port->dp.can_mst) + continue; + + drm_dp_mst_dump_topology(m, &intel_dig_port->dp.mst_mgr); + } + drm_modeset_unlock_all(dev); + return 0; +} + static int i915_pipe_crc_open(struct inode *inode, struct file *filep) { struct pipe_crc_info *info = inode->i_private; @@ -3906,6 +3928,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_display_info", i915_display_info, 0}, {"i915_semaphore_status", i915_semaphore_status, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, + {"i915_dp_mst_info", i915_dp_mst_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) -- cgit v0.10.2 From 89fc868674ba36d3a332dd2298fbfda5be724162 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 7 Jul 2014 17:59:51 +0200 Subject: drm/i2c: tda998x: Remove useless test In tda998x_encoder_destroy(), priv->cec is never NULL, so, remove its test. Signed-off-by: Jean-Francois Moine Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index ac357b0..a8f1e03 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -1196,8 +1196,7 @@ tda998x_encoder_destroy(struct drm_encoder *encoder) if (priv->hdmi->irq) free_irq(priv->hdmi->irq, priv); - if (priv->cec) - i2c_unregister_device(priv->cec); + i2c_unregister_device(priv->cec); drm_i2c_encoder_destroy(encoder); kfree(priv); } -- cgit v0.10.2 From 396d7a24a9b3ddbce3335c57a234227eb7762d14 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 12:18:58 +0100 Subject: drm: rcar-du: fix warnings drivers/gpu/drm/rcar-du/rcar_du_drv.c:190:5: warning: "CONFIG_PM_SLEEP" is not defined [-Wundef] Always use #ifdef with CONFIG symbols, never just bare #if Signed-off-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 792fd1d..fda64b7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -187,7 +187,7 @@ static struct drm_driver rcar_du_driver = { * Power management */ -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int rcar_du_pm_suspend(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); -- cgit v0.10.2 From 681ff260bf10c558c3aaa5255e25e13d4517fff3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 12:19:03 +0100 Subject: drm: shmobile: fix warnings drivers/gpu/drm/shmobile/shmob_drm_drv.c:300:5: warning: "CONFIG_PM_SLEEP" is not defined [-Wundef] Always use #ifdef with CONFIG symbols, never just bare #if Signed-off-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 82c84c7..ff4ba48 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -297,7 +297,7 @@ static struct drm_driver shmob_drm_driver = { * Power management */ -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int shmob_drm_pm_suspend(struct device *dev) { struct shmob_drm_device *sdev = dev_get_drvdata(dev); -- cgit v0.10.2 From 8f8e7e14637a07d8bde6e4b0dc385b72d1a674fb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 12 Jul 2014 11:01:43 +0100 Subject: drm: cirrus: fix warnings Regular nightly randconfig build testing discovered these warnings: drivers/gpu/drm/cirrus/cirrus_drv.c:79:12: warning: 'cirrus_pm_suspend' defined but not used [-Wunused-function] drivers/gpu/drm/cirrus/cirrus_drv.c:96:12: warning: 'cirrus_pm_resume' defined but not used [-Wunused-function] Fix these by adding the same condition that SET_SYSTEM_SLEEP_PM_OPS() uses. Signed-off-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 08ce520..4516b05 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -76,6 +76,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev) drm_put_dev(dev); } +#ifdef CONFIG_PM_SLEEP static int cirrus_pm_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -110,6 +111,7 @@ static int cirrus_pm_resume(struct device *dev) drm_kms_helper_poll_enable(drm_dev); return 0; } +#endif static const struct file_operations cirrus_driver_fops = { .owner = THIS_MODULE, -- cgit v0.10.2 From 150cee9cfd749b1a9f4e995d63761891d46c56de Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 12 Jul 2014 11:01:38 +0100 Subject: drm: bochs: fix warnings Regular nightly randconfig build testing discovered these warnings: drivers/gpu/drm/bochs/bochs_drv.c:100:12: warning: 'bochs_pm_suspend' defined but not used [-Wunused-function] drivers/gpu/drm/bochs/bochs_drv.c:117:12: warning: 'bochs_pm_resume' defined but not used [-Wunused-function] Fix these by adding the same condition that SET_SYSTEM_SLEEP_PM_OPS() uses. Signed-off-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index 9c13df2..f5e0ead 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -97,6 +97,7 @@ static struct drm_driver bochs_driver = { /* ---------------------------------------------------------------------- */ /* pm interface */ +#ifdef CONFIG_PM_SLEEP static int bochs_pm_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -131,6 +132,7 @@ static int bochs_pm_resume(struct device *dev) drm_kms_helper_poll_enable(drm_dev); return 0; } +#endif static const struct dev_pm_ops bochs_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend, -- cgit v0.10.2 From 2d31ca3ad7d5d44c8adc7f253c96ce33f3a2e931 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 12 Jul 2014 10:53:41 +0100 Subject: drm: omapdrm: fix compiler errors Regular randconfig nightly testing has detected problems with omapdrm. omapdrm fails to build when the kernel is built to support 64-bit DMA addresses and/or 64-bit physical addresses due to an assumption about the width of these types. Use %pad to print DMA addresses, rather than %x or %Zx (which is even more wrong than %x). Avoid passing a uint32_t pointer into a function which expects dma_addr_t pointer. drivers/gpu/drm/omapdrm/omap_plane.c: In function 'omap_plane_pre_apply': drivers/gpu/drm/omapdrm/omap_plane.c:145:2: error: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Werror=format] drivers/gpu/drm/omapdrm/omap_plane.c:145:2: error: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'dma_addr_t' [-Werror=format] make[5]: *** [drivers/gpu/drm/omapdrm/omap_plane.o] Error 1 drivers/gpu/drm/omapdrm/omap_gem.c: In function 'omap_gem_get_paddr': drivers/gpu/drm/omapdrm/omap_gem.c:794:4: error: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'dma_addr_t' [-Werror=format] drivers/gpu/drm/omapdrm/omap_gem.c: In function 'omap_gem_describe': drivers/gpu/drm/omapdrm/omap_gem.c:991:4: error: format '%Zx' expects argument of type 'size_t', but argument 7 has type 'dma_addr_t' [-Werror=format] drivers/gpu/drm/omapdrm/omap_gem.c: In function 'omap_gem_init': drivers/gpu/drm/omapdrm/omap_gem.c:1470:4: error: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t' [-Werror=format] make[5]: *** [drivers/gpu/drm/omapdrm/omap_gem.o] Error 1 drivers/gpu/drm/omapdrm/omap_dmm_tiler.c: In function 'dmm_txn_append': drivers/gpu/drm/omapdrm/omap_dmm_tiler.c:226:2: error: passing argument 3 of 'alloc_dma' from incompatible pointer type [-Werror] make[5]: *** [drivers/gpu/drm/omapdrm/omap_dmm_tiler.o] Error 1 make[5]: Target `__build' not remade because of errors. make[4]: *** [drivers/gpu/drm/omapdrm] Error 2 Signed-off-by: Russell King Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index f926b4c..56c6055 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -199,7 +199,7 @@ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm) static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, struct page **pages, uint32_t npages, uint32_t roll) { - dma_addr_t pat_pa = 0; + dma_addr_t pat_pa = 0, data_pa = 0; uint32_t *data; struct pat *pat; struct refill_engine *engine = txn->engine_handle; @@ -223,7 +223,9 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, .lut_id = engine->tcm->lut_id, }; - data = alloc_dma(txn, 4*i, &pat->data_pa); + data = alloc_dma(txn, 4*i, &data_pa); + /* FIXME: what if data_pa is more than 32-bit ? */ + pat->data_pa = data_pa; while (i--) { int n = i + roll; diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 5c36700..e484941 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -787,7 +787,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, omap_obj->paddr = tiler_ssptr(block); omap_obj->block = block; - DBG("got paddr: %08x", omap_obj->paddr); + DBG("got paddr: %pad", &omap_obj->paddr); } omap_obj->paddr_cnt++; @@ -981,9 +981,9 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) off = drm_vma_node_start(&obj->vma_node); - seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d", + seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d", omap_obj->flags, obj->name, obj->refcount.refcount.counter, - off, omap_obj->paddr, omap_obj->paddr_cnt, + off, &omap_obj->paddr, omap_obj->paddr_cnt, omap_obj->vaddr, omap_obj->roll); if (omap_obj->flags & OMAP_BO_TILED) { @@ -1464,8 +1464,8 @@ void omap_gem_init(struct drm_device *dev) entry->paddr = tiler_ssptr(block); entry->block = block; - DBG("%d:%d: %dx%d: paddr=%08x stride=%d", i, j, w, h, - entry->paddr, + DBG("%d:%d: %dx%d: paddr=%pad stride=%d", i, j, w, h, + &entry->paddr, usergart[i].stride_pfn << PAGE_SHIFT); } } diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 3cf31ee..6af3398 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -142,8 +142,8 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, info->out_height, info->screen_width); - DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, - info->paddr, info->p_uv_addr); + DBG("%d,%d %pad %pad", info->pos_x, info->pos_y, + &info->paddr, &info->p_uv_addr); /* TODO: */ ilace = false; -- cgit v0.10.2 From 3b9c214a28e3c611a5322257574dc7b725558e04 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 22 Jul 2014 12:55:39 +1000 Subject: drm/ttm: use gfp_t instead of int for flags. fixes sparse warning. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 863bef9..beb8e75 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -790,7 +790,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags, return 0; } -static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, int flags, +static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, gfp_t flags, char *name) { spin_lock_init(&pool->lock); -- cgit v0.10.2 From 4133d018039637a066b0fee9bcaff214e7d2e051 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 22 Jul 2014 12:59:32 +1000 Subject: drm/ttm: make device_released static fixes sparse warning, and removes potentially dangerous name from namespace. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ttm/ttm_module.c b/drivers/gpu/drm/ttm/ttm_module.c index d7f92fe..66fc639 100644 --- a/drivers/gpu/drm/ttm/ttm_module.c +++ b/drivers/gpu/drm/ttm/ttm_module.c @@ -35,7 +35,7 @@ #include static DECLARE_WAIT_QUEUE_HEAD(exit_q); -atomic_t device_released; +static atomic_t device_released; static struct device_type ttm_drm_class_type = { .name = "ttm", -- cgit v0.10.2 From 371c359f83b4d19d2688e975a07dc44add307e24 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 12:22:26 +0200 Subject: drm/dsi: Make mipi_dsi_dcs_write() return ssize_t This function returns the value of the struct mipi_dsi_host_ops' .transfer() so make sure the return types are consistent. Acked-by: Andrzej Hajda Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index e633df2..6d2fd20 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -205,8 +205,8 @@ EXPORT_SYMBOL(mipi_dsi_detach); * @data: pointer to the command followed by parameters * @len: length of @data */ -int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len) +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, + const void *data, size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index 06e57a2..beb4349 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -133,14 +133,14 @@ static int s6e8aa0_clear_error(struct s6e8aa0 *ctx) static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) { struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); - int ret; + ssize_t ret; if (ctx->error < 0) return; ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len); if (ret < 0) { - dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, + dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, data); ctx->error = ret; } diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index efa1b55..4b01127 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -127,8 +127,8 @@ struct mipi_dsi_device { int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len); +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, + const void *data, size_t len); ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, u8 cmd, void *data, size_t len); -- cgit v0.10.2 From 3c523d7d38a17b17371a9ea952c04a9b65c8a357 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 12:28:25 +0200 Subject: drm/dsi: Use peripheral's channel for DCS commands When executing DCS commands, use the channel associated with the DSI peripheral rather than one explicitly specified in the function call. Devices shouldn't be able to step on each others' toes like this. Acked-by: Andrzej Hajda Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 6d2fd20..6aa6a9e 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -201,16 +201,15 @@ EXPORT_SYMBOL(mipi_dsi_detach); /** * mipi_dsi_dcs_write - send DCS write command * @dsi: DSI device - * @channel: virtual channel * @data: pointer to the command followed by parameters * @len: length of @data */ -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len) +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, + size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { - .channel = channel, + .channel = dsi->channel, .tx_buf = data, .tx_len = len }; @@ -239,19 +238,18 @@ EXPORT_SYMBOL(mipi_dsi_dcs_write); /** * mipi_dsi_dcs_read - send DCS read request command * @dsi: DSI device - * @channel: virtual channel * @cmd: DCS read command * @data: pointer to read buffer * @len: length of @data * * Function returns number of read bytes or error code. */ -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, - u8 cmd, void *data, size_t len) +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { - .channel = channel, + .channel = dsi->channel, .type = MIPI_DSI_DCS_READ, .tx_buf = &cmd, .tx_len = 1, diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index beb4349..5502ef6 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -138,7 +138,7 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) if (ctx->error < 0) return; - ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len); + ret = mipi_dsi_dcs_write(dsi, data, len); if (ret < 0) { dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, data); @@ -154,7 +154,7 @@ static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len) if (ctx->error < 0) return ctx->error; - ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); + ret = mipi_dsi_dcs_read(dsi, cmd, data, len); if (ret < 0) { dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd); ctx->error = ret; diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 4b01127..7b5e1a9 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -127,10 +127,10 @@ struct mipi_dsi_device { int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len); -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, - u8 cmd, void *data, size_t len); +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, + size_t len); +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len); /** * struct mipi_dsi_driver - DSI driver -- cgit v0.10.2 From 9d90ce5c1002928d3bbc1f6014b04bf36c94d803 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 15:51:17 +0200 Subject: drm/panel: s6e8aa0: Use static inline for upcasting Use a static inline function for upcasting a struct drm_panel to the driver-specific structure. The advantage over using a macro is that it gives us additional type checking. Acked-by: Andrzej Hajda Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index 5502ef6..4fe0bbb 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -120,7 +120,10 @@ struct s6e8aa0 { int error; }; -#define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel) +static inline struct s6e8aa0 *panel_to_s6e8aa0(struct drm_panel *panel) +{ + return container_of(panel, struct s6e8aa0, panel); +} static int s6e8aa0_clear_error(struct s6e8aa0 *ctx) { -- cgit v0.10.2 From ea44739db37f7e187a2e684c1f9d5662b9dba94a Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Tue, 22 Jul 2014 08:38:55 +0200 Subject: drm/panel: simple: add support for InnoLux N156BGE-L21 panel This panel is used by the Medcom Wide and supported by the simple-panel driver. Signed-off-by: Alban Bedel Signed-off-by: Thierry Reding diff --git a/Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt b/Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt new file mode 100644 index 0000000..7825844 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt @@ -0,0 +1,7 @@ +InnoLux 15.6" WXGA TFT LCD panel + +Required properties: +- compatible: should be "innolux,n156bge-l21" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 9961d44..357712c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -425,6 +425,28 @@ static const struct panel_desc foxlink_fl500wvr00_a0t = { }, }; +static const struct drm_display_mode innolux_n156bge_l21_mode = { + .clock = 69300, + .hdisplay = 1366, + .hsync_start = 1366 + 16, + .hsync_end = 1366 + 16 + 34, + .htotal = 1366 + 16 + 34 + 50, + .vdisplay = 768, + .vsync_start = 768 + 2, + .vsync_end = 768 + 2 + 6, + .vtotal = 768 + 2 + 6 + 12, + .vrefresh = 60, +}; + +static const struct panel_desc innolux_n156bge_l21 = { + .modes = &innolux_n156bge_l21_mode, + .num_modes = 1, + .size = { + .width = 344, + .height = 193, + }, +}; + static const struct drm_display_mode lg_lp129qe_mode = { .clock = 285250, .hdisplay = 2560, @@ -495,6 +517,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "foxlink,fl500wvr00-a0t", .data = &foxlink_fl500wvr00_a0t, }, { + .compatible = "innolux,n156bge-l21", + .data = &innolux_n156bge_l21, + }, { .compatible = "lg,lp129qe", .data = &lg_lp129qe, }, { -- cgit v0.10.2 From 7296c849bf2eca2bd7d34a4686a53e3089150ac1 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 22 Jul 2014 20:10:28 +1000 Subject: drm/i915: fix build without fbdev. Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index ec48d56..d9a7a78 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -377,12 +377,27 @@ static bool intel_dp_mst_get_hw_state(struct intel_connector *connector) return false; } +static void intel_connector_add_to_fbdev(struct intel_connector *connector) +{ +#ifdef CONFIG_DRM_I915_FBDEV + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, &connector->base); +#endif +} + +static void intel_connector_remove_from_fbdev(struct intel_connector *connector) +{ +#ifdef CONFIG_DRM_I915_FBDEV + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, &connector->base); +#endif +} + static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop) { struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_connector *intel_connector; struct drm_connector *connector; int i; @@ -410,7 +425,7 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo drm_mode_connector_set_path_property(connector, pathprop); drm_reinit_primary_mode_group(dev); mutex_lock(&dev->mode_config.mutex); - drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, connector); + intel_connector_add_to_fbdev(intel_connector); mutex_unlock(&dev->mode_config.mutex); drm_connector_register(&intel_connector->base); return connector; @@ -421,7 +436,6 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, { struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; /* need to nuke the connector */ mutex_lock(&dev->mode_config.mutex); intel_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -430,7 +444,7 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, intel_connector->unregister(intel_connector); mutex_lock(&dev->mode_config.mutex); - drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, connector); + intel_connector_remove_from_fbdev(intel_connector); drm_connector_cleanup(connector); mutex_unlock(&dev->mode_config.mutex); -- cgit v0.10.2 From b04c5bd6fda54703e56f29569e4bca489d6c5a5c Mon Sep 17 00:00:00 2001 From: Borun Fu Date: Sat, 12 Jul 2014 10:02:27 +0530 Subject: drm/i915: Power gating display wells during i915_pm_suspend On VLV, after i915_pm_suspend display power wells are staying power ungated. So, after initiating mem sleep "echo mem > /sys/power/state" Display is staing D0 State. There might be better way/place to power gate these wells. Also, we need to make sure that if wells are power gated due to DPMS OFF sequence, they need not be turned off by i915_pm_suspend again. v2: Extracted helper for intel_crtc_disable and power gating CRTC power wells. [Daniel] Cc: Imre Deak Cc: Paulo Zanoni Cc: Daniel Vetter Cc: Jani Nikula Change-Id: I34c80da66aa24c423a5576c68aa1f3a8d0f43848 Signed-off-by: Borun Fu Signed-off-by: Sagar Kamble Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 23139aa..de7d9a7 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -525,12 +525,11 @@ static int i915_drm_freeze(struct drm_device *dev) /* * Disable CRTCs directly since we want to preserve sw state - * for _thaw. + * for _thaw. Also, power gate the CRTC power wells. */ drm_modeset_lock_all(dev); - for_each_crtc(dev, crtc) { - dev_priv->display.crtc_disable(crtc); - } + for_each_crtc(dev, crtc) + intel_crtc_control(crtc, false); drm_modeset_unlock_all(dev); intel_modeset_suspend_hw(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8620ea9..1c66401 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -179,6 +179,10 @@ enum hpd_pin { list_for_each_entry((intel_connector), &(dev)->mode_config.connector_list, base.head) \ if ((intel_connector)->base.encoder == (__encoder)) +#define for_each_power_domain(domain, mask) \ + for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \ + if ((1 << (domain)) & (mask)) + struct drm_i915_private; struct i915_mmu_object; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2b752d..7e0dc46 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4300,10 +4300,6 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc) I915_WRITE(BCLRPAT(crtc->pipe), 0); } -#define for_each_power_domain(domain, mask) \ - for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \ - if ((1 << (domain)) & (mask)) - enum intel_display_power_domain intel_display_port_power_domain(struct intel_encoder *intel_encoder) { @@ -4872,21 +4868,14 @@ static void intel_crtc_update_sarea(struct drm_crtc *crtc, } } -/** - * Sets the power management mode of the pipe and plane. - */ -void intel_crtc_update_dpms(struct drm_crtc *crtc) +/* Master function to enable/disable CRTC and corresponding power wells */ +void intel_crtc_control(struct drm_crtc *crtc, bool enable) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *intel_encoder; enum intel_display_power_domain domain; unsigned long domains; - bool enable = false; - - for_each_encoder_on_crtc(dev, crtc, intel_encoder) - enable |= intel_encoder->connectors_active; if (enable) { if (!intel_crtc->active) { @@ -4907,6 +4896,21 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) intel_crtc->enabled_power_domains = 0; } } +} + +/** + * Sets the power management mode of the pipe and plane. + */ +void intel_crtc_update_dpms(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_encoder *intel_encoder; + bool enable = false; + + for_each_encoder_on_crtc(dev, crtc, intel_encoder) + enable |= intel_encoder->connectors_active; + + intel_crtc_control(crtc, enable); intel_crtc_update_sarea(crtc, enable); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index fa19744..b9540c0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -753,6 +753,7 @@ void intel_frontbuffer_flip(struct drm_device *dev, void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire); void intel_mark_idle(struct drm_device *dev); void intel_crtc_restore_mode(struct drm_crtc *crtc); +void intel_crtc_control(struct drm_crtc *crtc, bool enable); void intel_crtc_update_dpms(struct drm_crtc *crtc); void intel_encoder_destroy(struct drm_encoder *encoder); void intel_connector_dpms(struct drm_connector *, int mode); -- cgit v0.10.2 From 9a603f48fa2e0760d043bd66f4dc62055ea8c745 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:09 -0700 Subject: drm/i915: Run psr_setup unconditionally Due to runtime pm and system s/r we need to restore hw state every time we enable a pipe again. Hence trying to avoid that is just pointless book-keeping which Rodrigo then tried to work around by manually adding psr_setup calls to our resume code. Much simpler to just remove code instead. v2: Properly bail out of psr exit if psr isn't enabled. Spotted by Rodrigo. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1c66401..c5d157b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -661,7 +661,6 @@ struct i915_drrs { struct i915_psr { bool sink_support; bool source_ok; - bool setup_done; bool enabled; bool active; struct delayed_work work; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ec080e5..6359005 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1681,9 +1681,6 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; struct edp_vsc_psr psr_vsc; - if (dev_priv->psr.setup_done) - return; - /* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */ memset(&psr_vsc, 0, sizeof(psr_vsc)); psr_vsc.sdp_header.HB0 = 0; @@ -1695,8 +1692,6 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp) /* Avoid continuous PSR exit by masking memup and hpd */ I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP | EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP); - - dev_priv->psr.setup_done = true; } static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp) @@ -1928,7 +1923,7 @@ void intel_edp_psr_exit(struct drm_device *dev) if (!HAS_PSR(dev)) return; - if (!dev_priv->psr.setup_done) + if (!dev_priv->psr.enabled) return; cancel_delayed_work_sync(&dev_priv->psr.work); -- cgit v0.10.2 From 1fcc9d1cf3c72ec7c7a3253b50b8e44f95f3f616 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:10 -0700 Subject: drm/i915: Add a FIXME about drrs/psr interactions Can't review this right now due to lack of DRRS code. Reviewed-by: Rodrigo Vivi Cc: Vandana Kannan Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6359005..4017406 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4104,6 +4104,11 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) return; } + /* + * FIXME: This needs proper synchronization with psr state. But really + * hard to tell without seeing the user of this function of this code. + * Check locking and ordering once that lands. + */ if (INTEL_INFO(dev)->gen < 8 && intel_edp_is_psr_enabled(dev)) { DRM_DEBUG_KMS("DRRS is disabled as PSR is enabled\n"); return; -- cgit v0.10.2 From 2807cf69df89defb024ac74708b6e43c231d0d47 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:11 -0700 Subject: drm/i915: Track the psr dp connector in dev_priv->psr.enabled Trying to fish that one out through looping is a bit a locking nightmare. So just set it and use it in the work struct. v2: - Don't Oops in psr_work, spotted by Rodrigo. - Fix compile warning. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 981ca42..62ea619 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1894,7 +1894,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support)); seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok)); - seq_printf(m, "Enabled: %s\n", yesno(dev_priv->psr.enabled)); + seq_printf(m, "Enabled: %s\n", yesno((bool)dev_priv->psr.enabled)); seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active)); enabled = HAS_PSR(dev) && diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c5d157b..d8115c3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -658,10 +658,11 @@ struct i915_drrs { struct intel_connector *connector; }; +struct intel_dp; struct i915_psr { bool sink_support; bool source_ok; - bool enabled; + struct intel_dp *enabled; bool active; struct delayed_work work; }; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 4017406..457bd82 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1843,7 +1843,7 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) /* Enable PSR on the host */ intel_edp_psr_enable_source(intel_dp); - dev_priv->psr.enabled = true; + dev_priv->psr.enabled = intel_dp; dev_priv->psr.active = true; } @@ -1884,26 +1884,22 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10)) DRM_ERROR("Timed out waiting for PSR Idle State\n"); - dev_priv->psr.enabled = false; + dev_priv->psr.enabled = NULL; } static void intel_edp_psr_work(struct work_struct *work) { struct drm_i915_private *dev_priv = container_of(work, typeof(*dev_priv), psr.work.work); - struct drm_device *dev = dev_priv->dev; - struct intel_encoder *encoder; - struct intel_dp *intel_dp = NULL; + struct intel_dp *intel_dp = dev_priv->psr.enabled; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) - if (encoder->type == INTEL_OUTPUT_EDP) { - intel_dp = enc_to_intel_dp(&encoder->base); + if (!intel_dp) + return; - if (!intel_edp_psr_match_conditions(intel_dp)) - intel_edp_psr_disable(intel_dp); - else - intel_edp_psr_do_enable(intel_dp); - } + if (!intel_edp_psr_match_conditions(intel_dp)) + intel_edp_psr_disable(intel_dp); + else + intel_edp_psr_do_enable(intel_dp); } static void intel_edp_psr_inactivate(struct drm_device *dev) -- cgit v0.10.2 From e921bcbfba1f973c60e3c537adeb32e0cdf2eb77 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:12 -0700 Subject: drm/i915: Don't try to disable psr harder from the work item It's disabled already except when we've raced. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 457bd82..0184188 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1896,9 +1896,7 @@ static void intel_edp_psr_work(struct work_struct *work) if (!intel_dp) return; - if (!intel_edp_psr_match_conditions(intel_dp)) - intel_edp_psr_disable(intel_dp); - else + if (intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_do_enable(intel_dp); } -- cgit v0.10.2 From 3638379cfe83604f2759399a998fa90ad0fd56ca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:13 -0700 Subject: drm/i915: Lock down psr sw/hw state tracking Make sure we track the sw side (psr.active) correctly and WARN everywhere it might get out of sync with the hw. v2: Fixup WARN_ON logic inversion, reported by Rodrigo. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0184188..f630c23 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1834,8 +1834,8 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (intel_edp_is_psr_enabled(dev)) - return; + WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE); + WARN_ON(dev_priv->psr.active); /* Enable PSR on the panel */ intel_edp_psr_enable_sink(intel_dp); @@ -1876,13 +1876,19 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) if (!dev_priv->psr.enabled) return; - I915_WRITE(EDP_PSR_CTL(dev), - I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE); + if (dev_priv->psr.active) { + I915_WRITE(EDP_PSR_CTL(dev), + I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE); + + /* Wait till PSR is idle */ + if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) & + EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10)) + DRM_ERROR("Timed out waiting for PSR Idle State\n"); - /* Wait till PSR is idle */ - if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) & - EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10)) - DRM_ERROR("Timed out waiting for PSR Idle State\n"); + dev_priv->psr.active = false; + } else { + WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE); + } dev_priv->psr.enabled = NULL; } @@ -1900,16 +1906,6 @@ static void intel_edp_psr_work(struct work_struct *work) intel_edp_psr_do_enable(intel_dp); } -static void intel_edp_psr_inactivate(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - dev_priv->psr.active = false; - - I915_WRITE(EDP_PSR_CTL(dev), I915_READ(EDP_PSR_CTL(dev)) - & ~EDP_PSR_ENABLE); -} - void intel_edp_psr_exit(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1922,8 +1918,15 @@ void intel_edp_psr_exit(struct drm_device *dev) cancel_delayed_work_sync(&dev_priv->psr.work); - if (dev_priv->psr.active) - intel_edp_psr_inactivate(dev); + if (dev_priv->psr.active) { + u32 val = I915_READ(EDP_PSR_CTL(dev)); + + WARN_ON(!(val & EDP_PSR_ENABLE)); + + I915_WRITE(EDP_PSR_CTL(dev), val & ~EDP_PSR_ENABLE); + + dev_priv->psr.active = false; + } schedule_delayed_work(&dev_priv->psr.work, msecs_to_jiffies(100)); -- cgit v0.10.2 From 109fc2adec3adf1a8c84533b2828da7016bf2abd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:14 -0700 Subject: drm/i915: More checks for psr.enabled We need to make sure that no one else is using this in the enable function and also that the work item hasn't raced with the disabled function. v2: Improve bisectability by moving one hunk to an earlier patch. v3: added missing dev_priv declaration (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter (v2) Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f630c23..fd92a81 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1850,6 +1850,7 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) void intel_edp_psr_enable(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; if (!HAS_PSR(dev)) { DRM_DEBUG_KMS("PSR not supported on this platform\n"); @@ -1861,6 +1862,11 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) return; } + if (dev_priv->psr.enabled) { + DRM_DEBUG_KMS("PSR already in use\n"); + return; + } + /* Setup PSR once */ intel_edp_psr_setup(intel_dp); -- cgit v0.10.2 From f0355c4a9eaf4cb803930d9fe6a26fb46846e576 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:15 -0700 Subject: drm/i915: Add locking to psr code It's not really optional to have locking ... The ugly part is how much locking the psr work needs since it has to recheck everything. Which is way too much. But we need to ditch the psr work in it's current form anyway and implement proper frontbuffer tracking. The other nasty bit that had to go was the delayed work cancle in psr_exit. Which means a bunch of races just became a bit more likely, but mea culpa. v2: Fixup HAS_PSR checks, resulting in uninitialized mutex issues. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d8115c3..faa27d0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -660,6 +660,7 @@ struct i915_drrs { struct intel_dp; struct i915_psr { + struct mutex lock; bool sink_support; bool source_ok; struct intel_dp *enabled; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fd92a81..3a3bb09 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1767,6 +1767,11 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) struct drm_i915_gem_object *obj = intel_fb_obj(crtc->primary->fb); struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; + lockdep_assert_held(&dev_priv->psr.lock); + lockdep_assert_held(&dev->struct_mutex); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + dev_priv->psr.source_ok = false; if (!HAS_PSR(dev)) { @@ -1836,6 +1841,7 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE); WARN_ON(dev_priv->psr.active); + lockdep_assert_held(&dev_priv->psr.lock); /* Enable PSR on the panel */ intel_edp_psr_enable_sink(intel_dp); @@ -1862,8 +1868,10 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) return; } + mutex_lock(&dev_priv->psr.lock); if (dev_priv->psr.enabled) { DRM_DEBUG_KMS("PSR already in use\n"); + mutex_unlock(&dev_priv->psr.lock); return; } @@ -1872,6 +1880,7 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) if (intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_do_enable(intel_dp); + mutex_unlock(&dev_priv->psr.lock); } void intel_edp_psr_disable(struct intel_dp *intel_dp) @@ -1879,9 +1888,15 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - if (!dev_priv->psr.enabled) + if (!HAS_PSR(dev)) return; + mutex_lock(&dev_priv->psr.lock); + if (!dev_priv->psr.enabled) { + mutex_unlock(&dev_priv->psr.lock); + return; + } + if (dev_priv->psr.active) { I915_WRITE(EDP_PSR_CTL(dev), I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE); @@ -1897,19 +1912,30 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) } dev_priv->psr.enabled = NULL; + mutex_unlock(&dev_priv->psr.lock); } static void intel_edp_psr_work(struct work_struct *work) { struct drm_i915_private *dev_priv = container_of(work, typeof(*dev_priv), psr.work.work); + struct drm_device *dev = dev_priv->dev; struct intel_dp *intel_dp = dev_priv->psr.enabled; + drm_modeset_lock_all(dev); + mutex_lock(&dev->struct_mutex); + mutex_lock(&dev_priv->psr.lock); + intel_dp = dev_priv->psr.enabled; + if (!intel_dp) - return; + goto unlock; if (intel_edp_psr_match_conditions(intel_dp)) intel_edp_psr_do_enable(intel_dp); +unlock: + mutex_unlock(&dev_priv->psr.lock); + mutex_unlock(&dev->struct_mutex); + drm_modeset_unlock_all(dev); } void intel_edp_psr_exit(struct drm_device *dev) @@ -1922,8 +1948,7 @@ void intel_edp_psr_exit(struct drm_device *dev) if (!dev_priv->psr.enabled) return; - cancel_delayed_work_sync(&dev_priv->psr.work); - + mutex_lock(&dev_priv->psr.lock); if (dev_priv->psr.active) { u32 val = I915_READ(EDP_PSR_CTL(dev)); @@ -1936,16 +1961,15 @@ void intel_edp_psr_exit(struct drm_device *dev) schedule_delayed_work(&dev_priv->psr.work, msecs_to_jiffies(100)); + mutex_unlock(&dev_priv->psr.lock); } void intel_edp_psr_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (!HAS_PSR(dev)) - return; - INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work); + mutex_init(&dev_priv->psr.lock); } static void intel_disable_dp(struct intel_encoder *encoder) -- cgit v0.10.2 From 9ca153017e00550dbeda2718cfd69ca37de9c523 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:16 -0700 Subject: drm/i915: Fix up PSR frontbuffer tracking I've tried to split this up, but all the changes are so tightly related that I didn't find a good way to do this without breaking bisecting. Essentially this completely changes how psr is glued into the overall driver, and there's not much you can do to soften such a paradigm change. - Use frontbuffer tracking bits stuff to separate disable and re-enable. - Don't re-check everything in the psr work. We have now accurate tracking for everything, so no need to check for sprites or tiling really. Allows us to ditch tons of locks. - That in turn allows us to properly cancel the work in the disable function - no more deadlocks. - Add a check for HSW sprites and force a flush. Apparently the hardware doesn't forward the flushing when updating the sprite base address. We can do the same trick everywhere else we have such issues, e.g. on baytrail with ... everything. - Don't re-enable psr with a delay in psr_exit. It really must be turned off forever if we detect a gtt write. At least with the current frontbuffer render tracking. Userspace can do a busy ioctl call or no-op pageflip to re-enable psr. - Drop redundant checks for crtc and crtc->active - now that they're only called from enable this is guaranteed. - Fix up the hsw port check. eDP can also happen on port D, but the issue is exactly that it doesn't work there. So an || check is wrong. - We still schedule the psr work with a delay. The frontbuffer flushing interface mandates that we upload the next full frame, so need to wait a bit. Once we have single-shot frame uploads we can do better here. v2: Don't enable psr initially, rely upon the fb flush of the initial plane setup for that. Gives us more unified code flow and makes the crtc enable sequence less a special case. v3: s/psr_exit/psr_invalidate/ for consistency v4: Fixup whitespace. v5: Correctly bail out of psr_invalidate/flush when dev_priv->psr.enabled is NULL. Spotted by Rodrigo. v6: - Only schedule work when there's work to do. Fixes WARNINGs reported by Rodrigo. - Comments Chris requested to clarify the code. v7: Fix conflict on rebase (Rodrigo) Cc: Chris Wilson Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter (v6) Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index faa27d0..0b9f789 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -666,6 +666,7 @@ struct i915_psr { struct intel_dp *enabled; bool active; struct delayed_work work; + unsigned busy_frontbuffer_bits; }; enum intel_pch { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7e0dc46..9064dd9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8942,7 +8942,7 @@ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, intel_mark_fb_busy(dev, obj->frontbuffer_bits, ring); - intel_edp_psr_exit(dev); + intel_edp_psr_invalidate(dev, obj->frontbuffer_bits); } /** @@ -8968,7 +8968,7 @@ void intel_frontbuffer_flush(struct drm_device *dev, intel_mark_fb_busy(dev, frontbuffer_bits, NULL); - intel_edp_psr_exit(dev); + intel_edp_psr_flush(dev, frontbuffer_bits); } /** diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3a3bb09..333471c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1764,8 +1764,6 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dig_port->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(crtc->primary->fb); - struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; lockdep_assert_held(&dev_priv->psr.lock); lockdep_assert_held(&dev->struct_mutex); @@ -1779,8 +1777,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } - if (IS_HASWELL(dev) && (intel_encoder->type != INTEL_OUTPUT_EDP || - dig_port->port != PORT_A)) { + if (IS_HASWELL(dev) && dig_port->port != PORT_A) { DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); return false; } @@ -1790,33 +1787,10 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; } - crtc = dig_port->base.base.crtc; - if (crtc == NULL) { - DRM_DEBUG_KMS("crtc not active for PSR\n"); - return false; - } - - intel_crtc = to_intel_crtc(crtc); - if (!intel_crtc_active(crtc)) { - DRM_DEBUG_KMS("crtc not active for PSR\n"); - return false; - } - - if (obj->tiling_mode != I915_TILING_X || - obj->fence_reg == I915_FENCE_REG_NONE) { - DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n"); - return false; - } - /* Below limitations aren't valid for Broadwell */ if (IS_BROADWELL(dev)) goto out; - if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) { - DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n"); - return false; - } - if (I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config.cpu_transcoder)) & S3D_ENABLE) { DRM_DEBUG_KMS("PSR condition failed: Stereo 3D is Enabled\n"); @@ -1849,7 +1823,6 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp) /* Enable PSR on the host */ intel_edp_psr_enable_source(intel_dp); - dev_priv->psr.enabled = intel_dp; dev_priv->psr.active = true; } @@ -1875,11 +1848,13 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp) return; } + dev_priv->psr.busy_frontbuffer_bits = 0; + /* Setup PSR once */ intel_edp_psr_setup(intel_dp); if (intel_edp_psr_match_conditions(intel_dp)) - intel_edp_psr_do_enable(intel_dp); + dev_priv->psr.enabled = intel_dp; mutex_unlock(&dev_priv->psr.lock); } @@ -1913,42 +1888,39 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) dev_priv->psr.enabled = NULL; mutex_unlock(&dev_priv->psr.lock); + + cancel_delayed_work_sync(&dev_priv->psr.work); } static void intel_edp_psr_work(struct work_struct *work) { struct drm_i915_private *dev_priv = container_of(work, typeof(*dev_priv), psr.work.work); - struct drm_device *dev = dev_priv->dev; struct intel_dp *intel_dp = dev_priv->psr.enabled; - drm_modeset_lock_all(dev); - mutex_lock(&dev->struct_mutex); mutex_lock(&dev_priv->psr.lock); intel_dp = dev_priv->psr.enabled; if (!intel_dp) goto unlock; - if (intel_edp_psr_match_conditions(intel_dp)) - intel_edp_psr_do_enable(intel_dp); + /* + * The delayed work can race with an invalidate hence we need to + * recheck. Since psr_flush first clears this and then reschedules we + * won't ever miss a flush when bailing out here. + */ + if (dev_priv->psr.busy_frontbuffer_bits) + goto unlock; + + intel_edp_psr_do_enable(intel_dp); unlock: mutex_unlock(&dev_priv->psr.lock); - mutex_unlock(&dev->struct_mutex); - drm_modeset_unlock_all(dev); } -void intel_edp_psr_exit(struct drm_device *dev) +static void intel_edp_psr_do_exit(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (!HAS_PSR(dev)) - return; - - if (!dev_priv->psr.enabled) - return; - - mutex_lock(&dev_priv->psr.lock); if (dev_priv->psr.active) { u32 val = I915_READ(EDP_PSR_CTL(dev)); @@ -1959,8 +1931,68 @@ void intel_edp_psr_exit(struct drm_device *dev) dev_priv->psr.active = false; } - schedule_delayed_work(&dev_priv->psr.work, - msecs_to_jiffies(100)); +} + +void intel_edp_psr_invalidate(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + enum pipe pipe; + + if (!HAS_PSR(dev)) + return; + + mutex_lock(&dev_priv->psr.lock); + if (!dev_priv->psr.enabled) { + mutex_unlock(&dev_priv->psr.lock); + return; + } + + crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc; + pipe = to_intel_crtc(crtc)->pipe; + + intel_edp_psr_do_exit(dev); + + frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); + + dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits; + mutex_unlock(&dev_priv->psr.lock); +} + +void intel_edp_psr_flush(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + enum pipe pipe; + + if (!HAS_PSR(dev)) + return; + + mutex_lock(&dev_priv->psr.lock); + if (!dev_priv->psr.enabled) { + mutex_unlock(&dev_priv->psr.lock); + return; + } + + crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc; + pipe = to_intel_crtc(crtc)->pipe; + dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits; + + /* + * On Haswell sprite plane updates don't result in a psr invalidating + * signal in the hardware. Which means we need to manually fake this in + * software for all flushes, not just when we've seen a preceding + * invalidation through frontbuffer rendering. + */ + if (IS_HASWELL(dev) && + (frontbuffer_bits & INTEL_FRONTBUFFER_SPRITE(pipe))) + intel_edp_psr_do_exit(dev); + + if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits) + schedule_delayed_work(&dev_priv->psr.work, + msecs_to_jiffies(100)); mutex_unlock(&dev_priv->psr.lock); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b9540c0..3adcdd1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -868,7 +868,10 @@ void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); -void intel_edp_psr_exit(struct drm_device *dev); +void intel_edp_psr_invalidate(struct drm_device *dev, + unsigned frontbuffer_bits); +void intel_edp_psr_flush(struct drm_device *dev, + unsigned frontbuffer_bits); void intel_edp_psr_init(struct drm_device *dev); /* intel_dsi.c */ -- cgit v0.10.2 From fa128fa647b81734ff83c3c5ee0d60ebf69e6ee5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:17 -0700 Subject: drm/i915: Improve PSR debugfs output Add busy_frontbuffer_bits and locking. Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 62ea619..fc39610 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1892,10 +1892,15 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) intel_runtime_pm_get(dev_priv); + mutex_lock(&dev_priv->psr.lock); seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support)); seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok)); seq_printf(m, "Enabled: %s\n", yesno((bool)dev_priv->psr.enabled)); seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active)); + seq_printf(m, "Busy frontbuffer bits: 0x%03x\n", + dev_priv->psr.busy_frontbuffer_bits); + seq_printf(m, "Re-enable work scheduled: %s\n", + yesno(work_busy(&dev_priv->psr.work.work))); enabled = HAS_PSR(dev) && I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE; @@ -1905,6 +1910,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) & EDP_PSR_PERF_CNT_MASK; seq_printf(m, "Performance_Counter: %u\n", psrperf); + mutex_unlock(&dev_priv->psr.lock); intel_runtime_pm_put(dev_priv); return 0; -- cgit v0.10.2 From eeefa889cddb8d7e4ee6ce0212e685dd624d66a1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Jul 2014 10:30:18 -0700 Subject: drm/i915: Remove redundant HAS_PSR checks We only need to check for this in psr_enable, everything else is already protect by the dev_priv->psr.enabled checks. Those need the psr locking, but these functions are called infrequent enough that the locking overhead is negligible. Suggested by Chris Wilson. Reviewed-by: Rodrigo Vivi Cc: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 333471c..86b5e97 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1772,11 +1772,6 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) dev_priv->psr.source_ok = false; - if (!HAS_PSR(dev)) { - DRM_DEBUG_KMS("PSR not supported on this platform\n"); - return false; - } - if (IS_HASWELL(dev) && dig_port->port != PORT_A) { DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); return false; @@ -1863,9 +1858,6 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - if (!HAS_PSR(dev)) - return; - mutex_lock(&dev_priv->psr.lock); if (!dev_priv->psr.enabled) { mutex_unlock(&dev_priv->psr.lock); @@ -1940,9 +1932,6 @@ void intel_edp_psr_invalidate(struct drm_device *dev, struct drm_crtc *crtc; enum pipe pipe; - if (!HAS_PSR(dev)) - return; - mutex_lock(&dev_priv->psr.lock); if (!dev_priv->psr.enabled) { mutex_unlock(&dev_priv->psr.lock); @@ -1967,9 +1956,6 @@ void intel_edp_psr_flush(struct drm_device *dev, struct drm_crtc *crtc; enum pipe pipe; - if (!HAS_PSR(dev)) - return; - mutex_lock(&dev_priv->psr.lock); if (!dev_priv->psr.enabled) { mutex_unlock(&dev_priv->psr.lock); -- cgit v0.10.2 From b6d547791fd3ef4ccc89ad2556ab01045640aef7 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Fri, 11 Jul 2014 10:30:19 -0700 Subject: drm/i915: Enable PSR by default. Panel Self Refresh is an eDP power saving feature specified by VESA's eDP v1.3, that allows some panel componets to shutdown while you still see static images on the screen. Besides being supported on the platform it must be supported by the eDP panel itself. Now that we have the propper frontbuffer tracking support and correct locks on place we can enabled this feature by default. Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 8145729..bbdee21 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -37,7 +37,7 @@ struct i915_params i915 __read_mostly = { .enable_fbc = -1, .enable_hangcheck = true, .enable_ppgtt = -1, - .enable_psr = 0, + .enable_psr = 1, .preliminary_hw_support = IS_ENABLED(CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT), .disable_power_well = 1, .enable_ips = 1, @@ -118,7 +118,7 @@ MODULE_PARM_DESC(enable_ppgtt, "(-1=auto [default], 0=disabled, 1=aliasing, 2=full)"); module_param_named(enable_psr, i915.enable_psr, int, 0600); -MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)"); +MODULE_PARM_DESC(enable_psr, "Enable PSR (default: true)"); module_param_named(preliminary_hw_support, i915.preliminary_hw_support, int, 0600); MODULE_PARM_DESC(preliminary_hw_support, -- cgit v0.10.2 From f96de58fc7e7d3d717c7c63975c3b896c906b5e3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 16 Dec 2013 15:57:40 +0000 Subject: drm/i915: Handle failure to kick out a conflicting fb driver Signed-off-by: Chris Wilson Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2c0bad6..eb3d9c0 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1427,15 +1427,16 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) } #if IS_ENABLED(CONFIG_FB) -static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) +static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) { struct apertures_struct *ap; struct pci_dev *pdev = dev_priv->dev->pdev; bool primary; + int ret; ap = alloc_apertures(1); if (!ap) - return; + return -ENOMEM; ap->ranges[0].base = dev_priv->gtt.mappable_base; ap->ranges[0].size = dev_priv->gtt.mappable_end; @@ -1443,13 +1444,16 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; - remove_conflicting_framebuffers(ap, "inteldrmfb", primary); + ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary); kfree(ap); + + return ret; } #else -static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) +static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) { + return 0; } #endif @@ -1667,7 +1671,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_gtt; } - i915_kick_out_firmware_fb(dev_priv); + ret = i915_kick_out_firmware_fb(dev_priv); + if (ret) { + DRM_ERROR("failed to remove conflicting framebuffer drivers\n"); + goto out_gtt; + } } pci_set_master(dev->pdev); -- cgit v0.10.2 From b47adc1792422f4caf6c1db0a2b235bfd4f02521 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Fri, 20 Jun 2014 20:03:02 +0530 Subject: drm/i915: Force GPU Freq to lowest while suspending. We might be leaving the GPU Frequency (and thus vnn) high during the suspend. Force gt to move to lowest freq while suspending. v2: Fixed typo in commit message (Deepak) v3: Force gt to lowest freq in suspend_gt_powersave (Daniel) v4: Add GPU min freq set _after_ we've cancelled the rps works (Daniel) Signed-off-by: Deepak S Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 9585f15..f55347f 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4974,6 +4974,9 @@ void intel_suspend_gt_powersave(struct drm_device *dev) flush_delayed_work(&dev_priv->rps.delayed_resume_work); cancel_work_sync(&dev_priv->rps.work); + + /* Force GPU to min freq during suspend */ + gen6_rps_idle(dev_priv); } void intel_disable_gt_powersave(struct drm_device *dev) -- cgit v0.10.2 From 4651fb23f6f1d86700b07a27ad7f137d28492342 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 24 Apr 2014 10:50:56 -0300 Subject: drm/i915: remove useless runtime PM get calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already call intel_display_power_get, which will get a power domain, and every power domain should get a runtime PM reference, which will wake up the machine. v2: - Also touch intel_crt_detect() (Ville). Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä [danvet: Fixup commit message as spotted by Ville.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 4b08561..133573c 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -660,8 +660,6 @@ intel_crt_detect(struct drm_connector *connector, bool force) struct intel_load_detect_pipe tmp; struct drm_modeset_acquire_ctx ctx; - intel_runtime_pm_get(dev_priv); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", connector->base.id, connector->name, force); @@ -713,8 +711,6 @@ intel_crt_detect(struct drm_connector *connector, bool force) out: intel_display_power_put(dev_priv, power_domain); - intel_runtime_pm_put(dev_priv); - return status; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 86b5e97..9274ddf 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3621,8 +3621,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) enum intel_display_power_domain power_domain; struct edid *edid = NULL; - intel_runtime_pm_get(dev_priv); - power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); @@ -3657,9 +3655,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) out: intel_display_power_put(dev_priv, power_domain); - - intel_runtime_pm_put(dev_priv); - return status; } -- cgit v0.10.2 From 34638118f987c3f4136e442b65de22d73a0458cb Mon Sep 17 00:00:00 2001 From: Deepak S Date: Sat, 28 Jun 2014 11:26:26 +0530 Subject: drm/i915/chv: Drop WaGsvBringDownFreqInRc6 Drop WaGsvBringDownFreq on CHV. When in RC6 requesting the min freq should be fine to bring the voltage down. Signed-off-by: Deepak S Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f55347f..be2d5bf 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3289,7 +3289,9 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv) mutex_lock(&dev_priv->rps.hw_lock); if (dev_priv->rps.enabled) { - if (IS_VALLEYVIEW(dev)) + if (IS_CHERRYVIEW(dev)) + valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); + else if (IS_VALLEYVIEW(dev)) vlv_set_rps_idle(dev_priv); else gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); -- cgit v0.10.2 From ff587e45a1a1690f5cd713a2782672c579460365 Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Wed, 11 Jun 2014 10:46:48 +0530 Subject: drm/crtc: Add property for aspect ratio Added a property to enable user space to set aspect ratio. This patch contains declaration of the property and code to create the property. v2: Thierry's review comments. - Made aspect ratio enum generic instead of HDMI/CEA specfic - Removed usage of temporary aspect_ratio variable v3: Thierry's review comments. - Fixed indentation v4: Thierry's review comments. - Return ENOMEM when property creation fails Signed-off-by: Vandana Kannan Cc: Thierry Reding Reviewed-by: Thierry Reding Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 89bab66..f0a7777 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -182,6 +182,12 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { DRM_MODE_SCALE_ASPECT, "Full aspect" }, }; +static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { + { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, + { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, + { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, +}; + /* * Non-global properties, but "required" for certain connectors. */ @@ -1391,6 +1397,33 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev) EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); /** + * drm_mode_create_aspect_ratio_property - create aspect ratio property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_create_aspect_ratio_property(struct drm_device *dev) +{ + if (dev->mode_config.aspect_ratio_property) + return 0; + + dev->mode_config.aspect_ratio_property = + drm_property_create_enum(dev, 0, "aspect ratio", + drm_aspect_ratio_enum_list, + ARRAY_SIZE(drm_aspect_ratio_enum_list)); + + if (dev->mode_config.aspect_ratio_property == NULL) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); + +/** * drm_mode_create_dirty_property - create dirty property * @dev: DRM device * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 08ed55e..be7114e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -835,6 +835,7 @@ struct drm_mode_config { /* Optional properties */ struct drm_property *scaling_mode_property; + struct drm_property *aspect_ratio_property; struct drm_property *dirty_info_property; /* dumb ioctl parameters */ @@ -1023,6 +1024,7 @@ extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, char *formats[]); extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); +extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev); extern int drm_mode_create_dirty_info_property(struct drm_device *dev); extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index def54f9..a0db2d4a 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -88,6 +88,11 @@ #define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ #define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 + /* Dithering mode options */ #define DRM_MODE_DITHERING_OFF 0 #define DRM_MODE_DITHERING_ON 1 -- cgit v0.10.2 From 69ab6d35f699e9e8df068d701e166529c6b4bab8 Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Thu, 5 Jun 2014 14:45:29 +0530 Subject: drm/edid: Check for user aspect ratio input In case user has specified an input for aspect ratio through the property, then the user space value for PAR would take preference over the value from CEA mode list. v2: Thierry's review comments. - Modified the comment "Populate..." as per review comments v3: Thierry's review comments. - Modified the comment to block comment format. Signed-off-by: Vandana Kannan Cc: Thierry Reding Reviewed-by: Thierry Reding Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index dfa9769..58a4a9d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3775,8 +3775,14 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; - /* Populate picture aspect ratio from CEA mode list */ - if (frame->video_code > 0) + /* + * Populate picture aspect ratio from either + * user input (if specified) or from the CEA mode list. + */ + if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || + mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) + frame->picture_aspect = mode->picture_aspect_ratio; + else if (frame->video_code > 0) frame->picture_aspect = drm_get_cea_aspect_ratio( frame->video_code); -- cgit v0.10.2 From 94a11ddcec9fbd27209da4de7d7c8250c6170f2e Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Wed, 11 Jun 2014 11:06:01 +0530 Subject: drm/i915: Add aspect ratio property for HDMI Create and attach the drm property to set aspect ratio. If there is no user specified value, then PAR_NONE/Automatic option is set by default. User can select aspect ratio 4:3 or 16:9. The aspect ratio selected by user would come into effect with a mode set. v2: Modified switch case to include aspect ratio enum changes v3: Modified the patch according the change in the earlier patch to return errno in case property creation fails. With this change, property will be attached only if creation is successful Signed-off-by: Vandana Kannan Cc: Thierry Reding Cc: Daniel Vetter Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3adcdd1..719f8d2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -498,6 +498,7 @@ struct intel_hdmi { bool has_audio; enum hdmi_force_audio force_audio; bool rgb_quant_range_selectable; + enum hdmi_picture_aspect aspect_ratio; void (*write_infoframe)(struct drm_encoder *encoder, enum hdmi_infoframe_type type, const void *frame, ssize_t len); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 318b150..228489c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -367,6 +367,9 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, union hdmi_infoframe frame; int ret; + /* Set user selected PAR to incoming mode's member */ + adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio; + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, adjusted_mode); if (ret < 0) { @@ -1124,6 +1127,23 @@ intel_hdmi_set_property(struct drm_connector *connector, goto done; } + if (property == connector->dev->mode_config.aspect_ratio_property) { + switch (val) { + case DRM_MODE_PICTURE_ASPECT_NONE: + intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; + break; + case DRM_MODE_PICTURE_ASPECT_4_3: + intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3; + break; + case DRM_MODE_PICTURE_ASPECT_16_9: + intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9; + break; + default: + return -EINVAL; + } + goto done; + } + return -EINVAL; done: @@ -1480,11 +1500,22 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { }; static void +intel_attach_aspect_ratio_property(struct drm_connector *connector) +{ + if (!drm_mode_create_aspect_ratio_property(connector->dev)) + drm_object_attach_property(&connector->base, + connector->dev->mode_config.aspect_ratio_property, + DRM_MODE_PICTURE_ASPECT_NONE); +} + +static void intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector) { intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); intel_hdmi->color_range_auto = true; + intel_attach_aspect_ratio_property(connector); + intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; } void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, -- cgit v0.10.2 From 726a280deb03991f50c2ce1f7b815cf9cd0319ca Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Wed, 11 Jun 2014 14:33:05 +0530 Subject: Documentation/drm: Describing aspect ratio property Updated drm documentation to include desscription of aspect ratio property. v2: Updated aspect ratio specific documentation on top of the HTML table created. Signed-off-by: Vandana Kannan Cc: Sagar Kamble Cc: Daniel Vetter Reviewed-by: Sagar Kamble Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 8ab1a05..f5d1b18 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2502,7 +2502,7 @@ void intel_crt_init(struct drm_device *dev) Description/Restrictions - DRM + DRM Generic “EDID” BLOB | IMMUTABLE @@ -2633,7 +2633,7 @@ void intel_crt_init(struct drm_device *dev) TBD - Optional + Optional “scaling mode” ENUM { "None", "Full", "Center", "Full aspect" } @@ -2641,6 +2641,15 @@ void intel_crt_init(struct drm_device *dev) TBD + "aspect ratio" + ENUM + { "None", "4:3", "16:9" } + Connector + DRM property to set aspect ratio from user space app. + This enum is made generic to allow addition of custom aspect + ratios. + + “dirty” ENUM | IMMUTABLE { "Off", "On", "Annotate" } -- cgit v0.10.2 From 6af257cde0c536f69872cd335f186c3fdaa5f26a Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 15 Jul 2014 09:17:41 +0200 Subject: drm/i915: PM irq enabling is generic on gen8, too No need to list all the platforms explicitly. The prefix is a bit inconsistent since we usually pick gen8_ for GT related functions. But this anti-pattern is already established with snb, so material for a different patch. Cc: Damien Lespiau Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 188fe04..7d61ca2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1407,7 +1407,7 @@ static void gen6_pm_rps_work(struct work_struct *work) spin_lock_irq(&dev_priv->irq_lock); pm_iir = dev_priv->rps.pm_iir; dev_priv->rps.pm_iir = 0; - if (IS_BROADWELL(dev_priv->dev) || IS_CHERRYVIEW(dev_priv->dev)) + if (INTEL_INFO(dev_priv->dev)->gen >= 8) bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); else { /* Make sure not to corrupt PMIMR state used by ringbuffer */ -- cgit v0.10.2 From ed57cb8a5c697680cdac3bc7ddfafff7594bd98e Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 15 Jul 2014 09:21:24 +0200 Subject: drm/i915: Also give the sprite width for WM computation In the future, we'll need the height of the fb to fetch from memory for WM computation. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0b9f789..7f4f2b7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -440,8 +440,8 @@ struct drm_i915_display_funcs { void (*update_wm)(struct drm_crtc *crtc); void (*update_sprite_wm)(struct drm_plane *plane, struct drm_crtc *crtc, - uint32_t sprite_width, int pixel_size, - bool enable, bool scaled); + uint32_t sprite_width, uint32_t sprite_height, + int pixel_size, bool enable, bool scaled); void (*modeset_global_resources)(struct drm_device *dev); /* Returns the active state of the crtc, and if the crtc is active, * fills out the pipe-config with the hw state. */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 719f8d2..6093ebd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -423,6 +423,7 @@ struct intel_crtc { struct intel_plane_wm_parameters { uint32_t horiz_pixels; + uint32_t vert_pixels; uint8_t bytes_per_pixel; bool enabled; bool scaled; @@ -979,7 +980,9 @@ int ilk_wm_max_level(const struct drm_device *dev); void intel_update_watermarks(struct drm_crtc *crtc); void intel_update_sprite_watermarks(struct drm_plane *plane, struct drm_crtc *crtc, - uint32_t sprite_width, int pixel_size, + uint32_t sprite_width, + uint32_t sprite_height, + int pixel_size, bool enabled, bool scaled); void intel_init_pm(struct drm_device *dev); void intel_pm_setup(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index be2d5bf..6e03851 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2743,10 +2743,11 @@ static void ilk_update_wm(struct drm_crtc *crtc) ilk_write_wm_values(dev_priv, &results); } -static void ilk_update_sprite_wm(struct drm_plane *plane, - struct drm_crtc *crtc, - uint32_t sprite_width, int pixel_size, - bool enabled, bool scaled) +static void +ilk_update_sprite_wm(struct drm_plane *plane, + struct drm_crtc *crtc, + uint32_t sprite_width, uint32_t sprite_height, + int pixel_size, bool enabled, bool scaled) { struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); @@ -2754,6 +2755,7 @@ static void ilk_update_sprite_wm(struct drm_plane *plane, intel_plane->wm.enabled = enabled; intel_plane->wm.scaled = scaled; intel_plane->wm.horiz_pixels = sprite_width; + intel_plane->wm.vert_pixels = sprite_width; intel_plane->wm.bytes_per_pixel = pixel_size; /* @@ -2888,13 +2890,16 @@ void intel_update_watermarks(struct drm_crtc *crtc) void intel_update_sprite_watermarks(struct drm_plane *plane, struct drm_crtc *crtc, - uint32_t sprite_width, int pixel_size, + uint32_t sprite_width, + uint32_t sprite_height, + int pixel_size, bool enabled, bool scaled) { struct drm_i915_private *dev_priv = plane->dev->dev_private; if (dev_priv->display.update_sprite_wm) - dev_priv->display.update_sprite_wm(plane, crtc, sprite_width, + dev_priv->display.update_sprite_wm(plane, crtc, + sprite_width, sprite_height, pixel_size, enabled, scaled); } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 985317e..396c1e84 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -218,7 +218,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, sprctl |= SP_ENABLE; - intel_update_sprite_watermarks(dplane, crtc, src_w, pixel_size, true, + intel_update_sprite_watermarks(dplane, crtc, src_w, src_h, + pixel_size, true, src_w != crtc_w || src_h != crtc_h); /* Sizes are 0 based */ @@ -283,7 +284,7 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc) if (atomic_update) intel_pipe_update_end(intel_crtc, start_vbl_count); - intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false); + intel_update_sprite_watermarks(dplane, crtc, 0, 0, 0, false, false); } static int @@ -406,7 +407,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (IS_HASWELL(dev) || IS_BROADWELL(dev)) sprctl |= SPRITE_PIPE_CSC_ENABLE; - intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true, + intel_update_sprite_watermarks(plane, crtc, src_w, src_h, pixel_size, + true, src_w != crtc_w || src_h != crtc_h); /* Sizes are 0 based */ @@ -486,7 +488,7 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc) */ intel_wait_for_vblank(dev, pipe); - intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); + intel_update_sprite_watermarks(plane, crtc, 0, 0, 0, false, false); } static int @@ -606,7 +608,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, dvscntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */ dvscntr |= DVS_ENABLE; - intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true, + intel_update_sprite_watermarks(plane, crtc, src_w, src_h, + pixel_size, true, src_w != crtc_w || src_h != crtc_h); /* Sizes are 0 based */ @@ -681,7 +684,7 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc) */ intel_wait_for_vblank(dev, pipe); - intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); + intel_update_sprite_watermarks(plane, crtc, 0, 0, 0, false, false); } static void -- cgit v0.10.2 From a1db2fa7c8325a380792d66908b2f982cf047837 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 Jul 2014 11:28:00 +0100 Subject: drm/i915: Abandon oom quickly if killed by a signal Whilst waiting to obtain our locks for the last resort shrinking before an oom, we check whether or not a fatal signal was pending. If there was, we do not need to keep waiting as the oom will be aborted. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e5d4d73..ef047bc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5194,8 +5194,11 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr) bool was_interruptible; bool unlock; - while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout) + while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout) { schedule_timeout_killable(1); + if (fatal_signal_pending(current)) + return NOTIFY_DONE; + } if (timeout == 0) { pr_err("Unable to purge GPU memory due lock contention.\n"); return NOTIFY_DONE; -- cgit v0.10.2 From 6c308fecb4d1f928d52f9586d976f79b37149388 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 Jul 2014 11:28:01 +0100 Subject: drm/i915: Initialise userptr mmu_notifier serial to 1 During the range invalidate, we walk the list of buffers associated with the mmu_notifer and find the ones that overlap the range. An optimisation is made to speed up the iteration by assuming the previous iter is still valid whilst the tree is unmodified. This exposes a bug when a range invalidate is triggered after we have just created the mmu_notifier, but before attaching any buffers. In that case, we presume we have an unmodified list and start walking from the last iter which is NULL. Oops. The easiest fix is then to initialise the serial of the tree to 1. Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Testecase: igt/gem_userptr_blts/stress-mm Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 21ea928..b41614d 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -150,7 +150,7 @@ i915_mmu_notifier_get(struct drm_device *dev, struct mm_struct *mm) mmu->mm = mm; mmu->objects = RB_ROOT; mmu->count = 0; - mmu->serial = 0; + mmu->serial = 1; /* Protected by mmap_sem (write-lock) */ ret = __mmu_notifier_register(&mmu->mn, mm); -- cgit v0.10.2 From ca1381b55b715ae3435a0d600a345bad90233a9b Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 15 Jul 2014 15:05:33 +0100 Subject: drm/i915: Make the WRPLL names const Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index b226724..8f36750 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1200,7 +1200,7 @@ static bool hsw_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, return val & WRPLL_PLL_ENABLE; } -static char *hsw_ddi_pll_names[] = { +static const char * const hsw_ddi_pll_names[] = { "WRPLL 1", "WRPLL 2", }; -- cgit v0.10.2 From 480c80338618867851659710d1a27c9cc85833d2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 16 Jul 2014 09:49:40 +0200 Subject: drm/i915: Use genX_ prefix for gt irq enable/disable functions Traditionally we use genX_ for GT/render stuff and the codenames for display stuff. But the gt and pm interrupt handling functions on gen5/6+ stuck out as exceptions, so convert them. Looking at the diff this nicely realigns our ducks since almost all the callers are already platform-specific functions following the genX_ pattern. Spotted while reviewing some internal rps patches. No function change in this patch. Acked-by: Damien Lespiau Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7d61ca2..dfe923a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -182,12 +182,12 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv, POSTING_READ(GTIMR); } -void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask) { ilk_update_gt_irq(dev_priv, mask, mask); } -void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask) { ilk_update_gt_irq(dev_priv, mask, 0); } @@ -220,12 +220,12 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv, } } -void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen6_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) { snb_update_pm_irq(dev_priv, mask, mask); } -void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) { snb_update_pm_irq(dev_priv, mask, 0); } @@ -278,12 +278,12 @@ static void bdw_update_pm_irq(struct drm_i915_private *dev_priv, } } -void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen8_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) { bdw_update_pm_irq(dev_priv, mask, mask); } -void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) +void gen8_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask) { bdw_update_pm_irq(dev_priv, mask, 0); } @@ -1408,10 +1408,10 @@ static void gen6_pm_rps_work(struct work_struct *work) pm_iir = dev_priv->rps.pm_iir; dev_priv->rps.pm_iir = 0; if (INTEL_INFO(dev_priv->dev)->gen >= 8) - bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); + gen8_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); else { /* Make sure not to corrupt PMIMR state used by ringbuffer */ - snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); + gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); } spin_unlock_irq(&dev_priv->irq_lock); @@ -1553,7 +1553,7 @@ static void ivybridge_parity_work(struct work_struct *work) out: WARN_ON(dev_priv->l3_parity.which_slice); spin_lock_irqsave(&dev_priv->irq_lock, flags); - ilk_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev)); + gen5_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev)); spin_unlock_irqrestore(&dev_priv->irq_lock, flags); mutex_unlock(&dev_priv->dev->struct_mutex); @@ -1567,7 +1567,7 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir) return; spin_lock(&dev_priv->irq_lock); - ilk_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev)); + gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev)); spin_unlock(&dev_priv->irq_lock); iir &= GT_PARITY_ERROR(dev); @@ -1622,7 +1622,7 @@ static void gen8_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir) spin_lock(&dev_priv->irq_lock); dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events; - bdw_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events); + gen8_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events); spin_unlock(&dev_priv->irq_lock); queue_work(dev_priv->wq, &dev_priv->rps.work); @@ -1969,7 +1969,7 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir) if (pm_iir & dev_priv->pm_rps_events) { spin_lock(&dev_priv->irq_lock); dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events; - snb_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events); + gen6_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events); spin_unlock(&dev_priv->irq_lock); queue_work(dev_priv->wq, &dev_priv->rps.work); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6093ebd..9d97a50 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -683,12 +683,12 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, enum transcoder pch_transcoder, bool enable); -void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen6_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen8_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); +void gen8_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void intel_runtime_pm_disable_interrupts(struct drm_device *dev); void intel_runtime_pm_restore_interrupts(struct drm_device *dev); int intel_get_crtc_scanline(struct intel_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6e03851..25ae4e6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3474,7 +3474,7 @@ static void gen8_enable_rps_interrupts(struct drm_device *dev) spin_lock_irq(&dev_priv->irq_lock); WARN_ON(dev_priv->rps.pm_iir); - bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); + gen8_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events); spin_unlock_irq(&dev_priv->irq_lock); } @@ -3485,7 +3485,7 @@ static void gen6_enable_rps_interrupts(struct drm_device *dev) spin_lock_irq(&dev_priv->irq_lock); WARN_ON(dev_priv->rps.pm_iir); - snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); + gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); I915_WRITE(GEN6_PMIIR, dev_priv->pm_rps_events); spin_unlock_irq(&dev_priv->irq_lock); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 599709e..b3d8f76 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1004,7 +1004,7 @@ gen5_ring_get_irq(struct intel_engine_cs *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) - ilk_enable_gt_irq(dev_priv, ring->irq_enable_mask); + gen5_enable_gt_irq(dev_priv, ring->irq_enable_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; @@ -1019,7 +1019,7 @@ gen5_ring_put_irq(struct intel_engine_cs *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) - ilk_disable_gt_irq(dev_priv, ring->irq_enable_mask); + gen5_disable_gt_irq(dev_priv, ring->irq_enable_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } @@ -1212,7 +1212,7 @@ gen6_ring_get_irq(struct intel_engine_cs *ring) GT_PARITY_ERROR(dev))); else I915_WRITE_IMR(ring, ~ring->irq_enable_mask); - ilk_enable_gt_irq(dev_priv, ring->irq_enable_mask); + gen5_enable_gt_irq(dev_priv, ring->irq_enable_mask); } spin_unlock_irqrestore(&dev_priv->irq_lock, flags); @@ -1232,7 +1232,7 @@ gen6_ring_put_irq(struct intel_engine_cs *ring) I915_WRITE_IMR(ring, ~GT_PARITY_ERROR(dev)); else I915_WRITE_IMR(ring, ~0); - ilk_disable_gt_irq(dev_priv, ring->irq_enable_mask); + gen5_disable_gt_irq(dev_priv, ring->irq_enable_mask); } spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } @@ -1250,7 +1250,7 @@ hsw_vebox_get_irq(struct intel_engine_cs *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { I915_WRITE_IMR(ring, ~ring->irq_enable_mask); - snb_enable_pm_irq(dev_priv, ring->irq_enable_mask); + gen6_enable_pm_irq(dev_priv, ring->irq_enable_mask); } spin_unlock_irqrestore(&dev_priv->irq_lock, flags); @@ -1270,7 +1270,7 @@ hsw_vebox_put_irq(struct intel_engine_cs *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { I915_WRITE_IMR(ring, ~0); - snb_disable_pm_irq(dev_priv, ring->irq_enable_mask); + gen6_disable_pm_irq(dev_priv, ring->irq_enable_mask); } spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } -- cgit v0.10.2 From 813bde438c575e2c84eed9702143d915702847e3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:50:29 -0300 Subject: drm/i915: don't write powered down IRQ registers on Gen 8 If we enable unclaimed register reporting on Gen 8, we will discover that the IRQ registers for pipes B and C are also on the power well, so writes to them when the power well is disabled result in unclaimed register errors. Also, hsw_power_well_post_enable() already takes care of re-enabling them once the power well is enabled. Testcase: igt/pm_rpm/rte Signed-off-by: Paulo Zanoni Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index dfe923a..6f19420 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3467,7 +3467,9 @@ static void gen8_irq_reset(struct drm_device *dev) gen8_gt_irq_reset(dev_priv); for_each_pipe(pipe) - GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); + if (intel_display_power_enabled(dev_priv, + POWER_DOMAIN_PIPE(pipe))) + GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); GEN5_IRQ_RESET(GEN8_DE_PORT_); GEN5_IRQ_RESET(GEN8_DE_MISC_); @@ -3800,8 +3802,11 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked; for_each_pipe(pipe) - GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, dev_priv->de_irq_mask[pipe], - de_pipe_enables); + if (intel_display_power_enabled(dev_priv, + POWER_DOMAIN_PIPE(pipe))) + GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, + dev_priv->de_irq_mask[pipe], + de_pipe_enables); GEN5_IRQ_INIT(GEN8_DE_PORT_, ~GEN8_AUX_CHANNEL_A, GEN8_AUX_CHANNEL_A); } -- cgit v0.10.2 From c5107b875a84f0b25d1d6b8fbc9acb22440b746f Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:50:30 -0300 Subject: drm/i915: HSW_BLC_PWM2_CTL doesn't exist on BDW So don't write it, otherwise we will trigger unclaimed register errors. Testcase: igt/pm_rpm/rte Signed-off-by: Paulo Zanoni Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9064dd9..06566d6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7326,8 +7326,9 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n"); WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE, "CPU PWM1 enabled\n"); - WARN(I915_READ(HSW_BLC_PWM2_CTL) & BLM_PWM_ENABLE, - "CPU PWM2 enabled\n"); + if (IS_HASWELL(dev)) + WARN(I915_READ(HSW_BLC_PWM2_CTL) & BLM_PWM_ENABLE, + "CPU PWM2 enabled\n"); WARN(I915_READ(BLC_PWM_PCH_CTL1) & BLM_PCH_PWM_ENABLE, "PCH PWM1 enabled\n"); WARN(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE, -- cgit v0.10.2 From d49bdb0e1054d022cc6f88fcecf9c79bae66eab0 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 Jul 2014 11:50:31 -0300 Subject: drm/i915: extract and improve gen8_irq_power_well_post_enable Move it from hsw_power_well_post_enable() (intel_pm.c) to i915_irq.c so we can reuse the nice IRQ macros we have there. The main difference is that now we're going to check if the IIR register is non-zero when we try to re-enable the interrupts. Signed-off-by: Paulo Zanoni Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6f19420..e2e9bb8f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3478,6 +3478,18 @@ static void gen8_irq_reset(struct drm_device *dev) ibx_irq_reset(dev); } +void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, dev_priv->de_irq_mask[PIPE_B], + ~dev_priv->de_irq_mask[PIPE_B]); + GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, dev_priv->de_irq_mask[PIPE_C], + ~dev_priv->de_irq_mask[PIPE_C]); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); +} + static void cherryview_irq_preinstall(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9d97a50..bf415df 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -693,6 +693,7 @@ void intel_runtime_pm_disable_interrupts(struct drm_device *dev); void intel_runtime_pm_restore_interrupts(struct drm_device *dev); int intel_get_crtc_scanline(struct intel_crtc *crtc); void i9xx_check_fifo_underruns(struct drm_device *dev); +void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv); /* intel_crt.c */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 25ae4e6..d23ba37 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5998,7 +5998,6 @@ bool intel_display_power_enabled(struct drm_i915_private *dev_priv, static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - unsigned long irqflags; /* * After we re-enable the power well, if we touch VGA register 0x3d5 @@ -6014,21 +6013,8 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) outb(inb(VGA_MSR_READ), VGA_MSR_WRITE); vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); - if (IS_BROADWELL(dev)) { - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - I915_WRITE(GEN8_DE_PIPE_IMR(PIPE_B), - dev_priv->de_irq_mask[PIPE_B]); - I915_WRITE(GEN8_DE_PIPE_IER(PIPE_B), - ~dev_priv->de_irq_mask[PIPE_B] | - GEN8_PIPE_VBLANK); - I915_WRITE(GEN8_DE_PIPE_IMR(PIPE_C), - dev_priv->de_irq_mask[PIPE_C]); - I915_WRITE(GEN8_DE_PIPE_IER(PIPE_C), - ~dev_priv->de_irq_mask[PIPE_C] | - GEN8_PIPE_VBLANK); - POSTING_READ(GEN8_DE_PIPE_IER(PIPE_C)); - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - } + if (IS_BROADWELL(dev)) + gen8_irq_power_well_post_enable(dev_priv); } static void hsw_set_power_well(struct drm_i915_private *dev_priv, -- cgit v0.10.2 From 9a76e4956b719ec32acdd37c80944423e4825bbf Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Jun 2014 09:29:19 -0700 Subject: drm/i915: don't warn if IRQs are disabled when shutting down display IRQs This was always the case on our suspend path, but it was recently exposed by the change to use our runtime IRQ disable routine rather than the full DRM IRQ disable. Keep the warning on the enable side, as that really would indicate a bug. Signed-off-by: Jesse Barnes Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index e2e9bb8f..3ae33e7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -151,7 +151,7 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (dev_priv->pm.irqs_disabled) return; if ((dev_priv->irq_mask & mask) != mask) { -- cgit v0.10.2 From 9df7575f1c751a333b1989d1c7e2b9fc884d9105 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Jun 2014 09:29:20 -0700 Subject: drm/i915: add helper for checking whether IRQs are enabled Now that we use the runtime IRQ enable/disable functions in our suspend path, we can simply check the pm._irqs_disabled flag everywhere. So rename it to catch the users, and add an inline for it to make the checks clear everywhere. Signed-off-by: Jesse Barnes Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index fc39610..7e72800 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1996,7 +1996,7 @@ static int i915_pc8_status(struct seq_file *m, void *unused) seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); seq_printf(m, "IRQs disabled: %s\n", - yesno(dev_priv->pm.irqs_disabled)); + yesno(!intel_irqs_enabled(dev_priv))); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7f4f2b7..8b781f8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1339,7 +1339,7 @@ struct ilk_wm_values { */ struct i915_runtime_pm { bool suspended; - bool irqs_disabled; + bool _irqs_disabled; }; enum intel_pipe_crc_source { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ef047bc..ed0b5fc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1161,7 +1161,7 @@ static int __wait_seqno(struct intel_engine_cs *ring, u32 seqno, unsigned long timeout_expire; int ret; - WARN(dev_priv->pm.irqs_disabled, "IRQs disabled\n"); + WARN(!intel_irqs_enabled(dev_priv), "IRQs disabled"); if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) return 0; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3ae33e7..6774f88 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -136,7 +136,7 @@ ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; if ((dev_priv->irq_mask & mask) != 0) { @@ -151,7 +151,7 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pm.irqs_disabled) + if (!intel_irqs_enabled(dev_priv)) return; if ((dev_priv->irq_mask & mask) != mask) { @@ -173,7 +173,7 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv, { assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; dev_priv->gt_irq_mask &= ~interrupt_mask; @@ -206,7 +206,7 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv, assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; new_val = dev_priv->pm_irq_mask; @@ -264,7 +264,7 @@ static void bdw_update_pm_irq(struct drm_i915_private *dev_priv, assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; new_val = dev_priv->pm_irq_mask; @@ -420,7 +420,7 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv, assert_spin_locked(&dev_priv->irq_lock); - if (WARN_ON(dev_priv->pm.irqs_disabled)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; I915_WRITE(SDEIMR, sdeimr); @@ -4774,7 +4774,7 @@ void intel_runtime_pm_disable_interrupts(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; dev->driver->irq_uninstall(dev); - dev_priv->pm.irqs_disabled = true; + dev_priv->pm._irqs_disabled = true; } /* Restore interrupts so we can recover from runtime PM. */ @@ -4782,7 +4782,7 @@ void intel_runtime_pm_restore_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - dev_priv->pm.irqs_disabled = false; + dev_priv->pm._irqs_disabled = false; dev->driver->irq_preinstall(dev); dev->driver->irq_postinstall(dev); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 06566d6..1cdd1c1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7341,7 +7341,7 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) * gen-specific and since we only disable LCPLL after we fully disable * the interrupts, the check below should be enough. */ - WARN(!dev_priv->pm.irqs_disabled, "IRQs enabled\n"); + WARN(intel_irqs_enabled(dev_priv), "IRQs enabled\n"); } static uint32_t hsw_read_dcomp(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bf415df..8fc68c7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -691,11 +691,19 @@ void gen8_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void gen8_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void intel_runtime_pm_disable_interrupts(struct drm_device *dev); void intel_runtime_pm_restore_interrupts(struct drm_device *dev); +static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) +{ + /* + * We only use drm_irq_uninstall() at unload and VT switch, so + * this is the only thing we need to check. + */ + return !dev_priv->pm._irqs_disabled; +} + int intel_get_crtc_scanline(struct intel_crtc *crtc); void i9xx_check_fifo_underruns(struct drm_device *dev); void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv); - /* intel_crt.c */ void intel_crt_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d23ba37..3f88f29 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4976,7 +4976,7 @@ void intel_suspend_gt_powersave(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* Interrupts should be disabled already to avoid re-arming. */ - WARN_ON(dev->irq_enabled && !dev_priv->pm.irqs_disabled); + WARN_ON(intel_irqs_enabled(dev_priv)); flush_delayed_work(&dev_priv->rps.delayed_resume_work); @@ -4991,7 +4991,7 @@ void intel_disable_gt_powersave(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* Interrupts should be disabled already to avoid re-arming. */ - WARN_ON(dev->irq_enabled && !dev_priv->pm.irqs_disabled); + WARN_ON(intel_irqs_enabled(dev_priv)); if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); @@ -7069,5 +7069,5 @@ void intel_pm_setup(struct drm_device *dev) intel_gen6_powersave_work); dev_priv->pm.suspended = false; - dev_priv->pm.irqs_disabled = false; + dev_priv->pm._irqs_disabled = false; } -- cgit v0.10.2 From 95f25beddba2ec9510b249740bacc11eca70cf75 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Jun 2014 09:29:22 -0700 Subject: drm/i915: set pm._irqs_disabled at IRQ init time Before we've installed the handler, we can set this and avoid confusing init code that then thinks IRQs are enabled and spews complaints everywhere. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6774f88..6cdaecf 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -4669,6 +4669,9 @@ void intel_irq_init(struct drm_device *dev) pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + /* Haven't installed the IRQ handler yet */ + dev_priv->pm._irqs_disabled = true; + if (IS_GEN2(dev)) { dev->max_vblank_count = 0; dev->driver->get_vblank_counter = i8xx_get_vblank_counter; -- cgit v0.10.2 From ed2e6df18935beb3d63613c50103bf9757b2aa85 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Jun 2014 09:39:36 -0700 Subject: drm/i915: clear pm._irqs_disabled field after installing IRQs After this point, we'll modify it with the runtime routines. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index eb3d9c0..f510c34 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1340,6 +1340,8 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_gem_stolen; + dev_priv->pm._irqs_disabled = false; + /* Important: The output setup functions called by modeset_init need * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); -- cgit v0.10.2 From eb21b92ba0f591e7cc061440c14354fcd5762e45 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Jun 2014 11:57:33 -0700 Subject: drm/i915: mark IRQs as disabled on unload To avoid more spew with the new warnings. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1cdd1c1..f2416d7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13050,6 +13050,8 @@ void intel_modeset_cleanup(struct drm_device *dev) */ drm_irq_uninstall(dev); cancel_work_sync(&dev_priv->hotplug_work); + dev_priv->pm._irqs_disabled = true; + /* * Due to the hpd irq storm handling the hotplug work can re-arm the * poll handlers. Hence disable polling after hpd handling is shut down. -- cgit v0.10.2 From 5978118c39c2f72fd8b39ef9c086723542384809 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 16 Jul 2014 17:49:29 -0300 Subject: drm/i915: reorganize the unclaimed register detection code The current code only runs when we do an I915_WRITE operation. It checks if the unclaimed register flag is set before we do the operation, and then it checks it again after we do the operation. This double check allows us to find out if the I915_WRITE operation in question is the bad one, or if some previous code is the bad one. When it finds a problem, our code uses DRM_ERROR to signal it. The good thing about the current code is that it detects the problem, so at least we can know we did something wrong. The problem is that even though we find the problem, we don't really have much information to actually debug it. So whenever I see one of these DRM_ERROR messages on my systems, the first thing I do is apply a patch to change the DRM_ERROR to a WARN and also check for unclaimed registers on I915_READ operations. This local patch makes things even slower, but it usually helps a lot in finding the bad code. The first point here is that since the current code is only useful to detect whether we have a problem or not, but it is not really good to find the cause of the problem, I don't think we should be checking both before and after every I915_WRITE operation: just doing the check once should be enough for us to quickly detect problems. With this change, the code that runs by default for every single user will only do 1 read operation for every single I915_WRITE, instead of 2. This patch does this change. The second point is that the local patch I have should be upstream, but since it makes things slower it should be disabled by default. So I added the i915.mmio_debug option to enable it. So after this patch, this is what will happen: - By default, we will try to detect unclaimed registers once after every I915_WRITE operation. Previously we tried twice for every I915_WRITE. - When we find an unclaimed register we will still print a DRM_ERROR message, but we will now tell the user to try again with i915.mmio_debug=1. - When we use i915.mmio_debug=1 we will try to find unclaimed registers both before and after every I915_READ and I915_WRITE operation, and we will print stack traces in case we find them. This should really help locating the exact point of the bad code (or at least finding out that i915.ko is not the problem). This commit also opens space for really-slow register debugging operations on other platforms. In theory we can now add lots and lots of debug code behind i915.mmio_debug, enable this option on our tests, and catch more problems. v2: - Remove not-so-useful comments (Daniel) - Fix the param definition macros (Rodrigo) Reviewed-by: Rodrigo Vivi Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8b781f8..46ee0a90 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2135,6 +2135,7 @@ struct i915_params { bool disable_display; bool disable_vtd_wa; int use_mmio_flip; + bool mmio_debug; }; extern struct i915_params i915 __read_mostly; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index bbdee21..62ee830 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -49,6 +49,7 @@ struct i915_params i915 __read_mostly = { .enable_cmd_parser = 1, .disable_vtd_wa = 0, .use_mmio_flip = 0, + .mmio_debug = 0, }; module_param_named(modeset, i915.modeset, int, 0400); @@ -161,3 +162,8 @@ MODULE_PARM_DESC(enable_cmd_parser, module_param_named(use_mmio_flip, i915.use_mmio_flip, int, 0600); MODULE_PARM_DESC(use_mmio_flip, "use MMIO flips (-1=never, 0=driver discretion [default], 1=always)"); + +module_param_named(mmio_debug, i915.mmio_debug, bool, 0600); +MODULE_PARM_DESC(mmio_debug, + "Enable the MMIO debug code (default: false). This may negatively " + "affect performance."); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index e0f0843..6fee122 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -514,20 +514,30 @@ ilk_dummy_write(struct drm_i915_private *dev_priv) } static void -hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) +hsw_unclaimed_reg_debug(struct drm_i915_private *dev_priv, u32 reg, bool read, + bool before) { + const char *op = read ? "reading" : "writing to"; + const char *when = before ? "before" : "after"; + + if (!i915.mmio_debug) + return; + if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) { - DRM_ERROR("Unknown unclaimed register before writing to %x\n", - reg); + WARN(1, "Unclaimed register detected %s %s register 0x%x\n", + when, op, reg); __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM); } } static void -hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg) +hsw_unclaimed_reg_detect(struct drm_i915_private *dev_priv) { + if (i915.mmio_debug) + return; + if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) { - DRM_ERROR("Unclaimed write to %x\n", reg); + DRM_ERROR("Unclaimed register detected. Please use the i915.mmio_debug=1 to debug this problem."); __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM); } } @@ -564,6 +574,7 @@ gen5_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ static u##x \ gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ REG_READ_HEADER(x); \ + hsw_unclaimed_reg_debug(dev_priv, reg, true, true); \ if (dev_priv->uncore.forcewake_count == 0 && \ NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ dev_priv->uncore.funcs.force_wake_get(dev_priv, \ @@ -574,6 +585,7 @@ gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ } else { \ val = __raw_i915_read##x(dev_priv, reg); \ } \ + hsw_unclaimed_reg_debug(dev_priv, reg, true, false); \ REG_READ_FOOTER; \ } @@ -700,12 +712,13 @@ hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \ } \ - hsw_unclaimed_reg_clear(dev_priv, reg); \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \ __raw_i915_write##x(dev_priv, reg, val); \ if (unlikely(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ - hsw_unclaimed_reg_check(dev_priv, reg); \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \ + hsw_unclaimed_reg_detect(dev_priv); \ REG_WRITE_FOOTER; \ } -- cgit v0.10.2 From 66bc2cab191816d931f4d660d2f162130dd202b2 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 16 Jul 2014 17:49:30 -0300 Subject: drm/i915: BDW can also detect unclaimed registers By the time I wrote this patch, it allowed me to catch some problems. But due to patch reordering - in order to prevent fake "regression" reports - this patch may be merged after the fixes of the problems identified by this patch. Reviewed-by: Rodrigo Vivi Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index de7d9a7..3872796 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -303,6 +303,7 @@ static const struct intel_device_info intel_broadwell_d_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, @@ -314,6 +315,7 @@ static const struct intel_device_info intel_broadwell_m_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, @@ -325,6 +327,7 @@ static const struct intel_device_info intel_broadwell_gt3d_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, @@ -336,6 +339,7 @@ static const struct intel_device_info intel_broadwell_gt3m_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 6fee122..e81bc3b 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -747,6 +747,7 @@ static bool is_gen8_shadowed(struct drm_i915_private *dev_priv, u32 reg) static void \ gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \ REG_WRITE_HEADER; \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \ if (reg < 0x40000 && !is_gen8_shadowed(dev_priv, reg)) { \ if (dev_priv->uncore.forcewake_count == 0) \ dev_priv->uncore.funcs.force_wake_get(dev_priv, \ @@ -758,6 +759,8 @@ gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace } else { \ __raw_i915_write##x(dev_priv, reg, val); \ } \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \ + hsw_unclaimed_reg_detect(dev_priv); \ REG_WRITE_FOOTER; \ } -- cgit v0.10.2 From b2435c94b0f1c09ecb71520e3ee4bbc342d5e8e3 Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 17 Jul 2014 14:21:14 +0530 Subject: drm/i915: Fix printing proper min/min/rpe values in debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was fumbled while trying to use the cached min/min/rpe values in the vlv debugfs code. This is a regression from commit 03af20458a57a50735b12c1e3c23abc7ff70c6fa Author: Ville Syrjälä Date: Sat Jun 28 02:03:53 2014 +0300 drm/i915: Use the cached min/min/rpe values in the vlv debugfs code Signed-off-by: Deepak S Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7e72800..083683c 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1116,13 +1116,13 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); seq_printf(m, "max GPU freq: %d MHz\n", - dev_priv->rps.max_freq); + vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq)); seq_printf(m, "min GPU freq: %d MHz\n", - dev_priv->rps.min_freq); + vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq)); seq_printf(m, "efficient (RPe) frequency: %d MHz\n", - dev_priv->rps.efficient_freq); + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); seq_printf(m, "current GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv, (freq_sts >> 8) & 0xff)); -- cgit v0.10.2 From 36362ad3b64acd5e4d438db480bdb80a4ff75eb2 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 1 Jul 2014 11:17:41 -0700 Subject: drm/i915/error: Check the potential ctx obj's vm The bound list is global (all objects which back the VMAs are stored here). Recently the BUG() in the offset lookup was demoted to a WARN, but the fault actually lies in the caller, here. This bug has existed since the initial introduction of PPGTT (however, it was fixed in unmerged patches to fix up the error state). Note: The reason for the BUG_ON to WARN_ON demotion was _not_ to duct-tape over this bug here but another but triggerable without ppgtt. See the commit for details: commit f25748ea7342bcc44866befcbf0222cd1d89d851 Author: Daniel Vetter Date: Tue Jun 17 22:34:38 2014 +0200 drm/i915: Don't BUG_ON in i915_gem_obj_offset A WARN_ON is perfectly fine. The BUG in here seems to be the cause behind hard-hangs when I cat the i915_gem_pageflip debugfs file (which calls this from an irq spinlock). But only while running a full igt run after a while. I still need to root cause the underlying issue. I'll also start reject patches which add new BUG_ON but don't come with a really good justification for it. The general rule really should be to just WARN and hope the driver survives for long enough. v2: Make the WARN a bit more useful per Chris' suggestion. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter Signed-off-by: Ben Widawsky [danvet: Clarfy that the WARN_ON (former BUG_ON) in ggtt_offset caught more than just this bug fixed in this patch here.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 45b6191..9faebbc 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -920,6 +920,9 @@ static void i915_gem_record_active_context(struct intel_engine_cs *ring, return; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { + if (!i915_gem_obj_ggtt_bound(obj)) + continue; + if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) { ering->ctx = i915_error_ggtt_object_create(dev_priv, obj); break; -- cgit v0.10.2 From 2f295791312937d077cecec4cd8f453ce2ce93df Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 1 Jul 2014 11:17:47 -0700 Subject: drm/i915: Reorder ctx unref on ppgtt cleanup The comment [which was mine] is wrong. The context object can never be bound in a PPGTT because it is only capable of living in the Global GTT. So, remove the comment, and reorder the unref. What's nice about the latter is it keeps the context object alive past the PPGTT. This makes the destroy ordering symmetric with the creation ordering. Create: 1. Create context 2. Create PPGTT Destroy: 1. Destroy PPGTT 2. Destroy context As far as I know, this does not fix a bug. The code previously kept the context data structure, only the object was gone. As the code was, nothing tried to use the object after this point. NOTE: If in the future we have cases where the PPGTT can/should outlive the context (which doesn't occur today, but the code permits it), this ordering does not matter. Even if this occurs, as it stands now, we do not expect that to be the normal case, and having this order makes debugging a bit easier if we're tracking object lifetimes for the context vs ppgtt Signed-off-by: Ben Widawsky [danvet: Resolve conflict with Oscar's execlist prep patches.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index de72a28..3b99390 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -186,14 +186,12 @@ void i915_gem_context_free(struct kref *ctx_ref) /* We refcount even the aliasing PPGTT to keep the code symmetric */ if (USES_PPGTT(ctx->legacy_hw_ctx.rcs_state->base.dev)) ppgtt = ctx_to_ppgtt(ctx); - - /* XXX: Free up the object before tearing down the address space, in - * case we're bound in the PPGTT */ - drm_gem_object_unreference(&ctx->legacy_hw_ctx.rcs_state->base); } if (ppgtt) kref_put(&ppgtt->ref, ppgtt_release); + if (ctx->legacy_hw_ctx.rcs_state) + drm_gem_object_unreference(&ctx->legacy_hw_ctx.rcs_state->base); list_del(&ctx->link); kfree(ctx); } -- cgit v0.10.2 From c17c654d18d0c14fb366bdad00e49fdf82a73aa4 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 17 Jul 2014 11:16:39 -0300 Subject: drm/i915: remove plane/cursor/pipe assertions from intel_crtc_disable Since we merged runtime PM support for DPMS, it is possible that these assertions will be called when the power wells are disabled but a mode is "set", resulting in "failed assertion" and "device suspended while reading register" WARNs. To reproduce the bug: disable all screens using mode unset, do a modeset on one screen, disable it using DPMS, then try to do a mode unset on it again to see the WARNs. v2: The first version of this patch changed the assertions to also check the power domains. Daniel suggested that it would be better to just remove the assertions: "The modeset state checker will already notice when we've failed to turn off the pipe. And we check cursors and plane state in the enable sequence, too. Since we use these asserts a lot to lock down the precise modeset sequence I actually prefer if they're a bit dumb and don't check the power wells." Testcase: igt/rpm_rpm/dpms-mode-unset-lpsp Testcase: igt/rpm_rpm/dpms-mode-unset-non-lpsp Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f2416d7..c18c2a7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4930,10 +4930,6 @@ static void intel_crtc_disable(struct drm_crtc *crtc) intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); - assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); - assert_cursor_disabled(dev_priv, pipe); - assert_pipe_disabled(dev->dev_private, pipe); - if (crtc->primary->fb) { mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(old_obj); -- cgit v0.10.2 From 9490edb58865536e91baf489eff4e67709cf134c Mon Sep 17 00:00:00 2001 From: Armin Reese Date: Fri, 11 Jul 2014 10:20:07 -0700 Subject: drm/i915: Do not unmap object unless no other VMAs reference it When using an IOMMU, GEM objects are mapped by their DMA address as the physical address is unknown. This depends on the underlying IOMMU driver to map and unmap the physical pages properly as defined in intel_iommu.c. The current code will tell the IOMMU to unmap the GEM BO's pages on the destruction of the first VMA that "maps" that BO. This is clearly wrong as there may be other VMAs "mapping" that BO (using flink). The scanout is one such example. The patch fixes this issue by only unmapping the DMA maps when there are no more VMAs mapping that object. This is equivalent to when an object is considered unbound as can be seen by the code. On the first VMA that again because bound, we will remap. An alternate solution would be to move the dma mapping to object creation and destrubtion. I am not sure if this is considered an unfriendly thing to do. Some notes to backporters trying to backport full PPGTT: The bug can never be hit without enabling the IOMMU. The existing code will also do the right thing when the object is shared via dmabuf. The failure should be demonstrable with flink. In cases when not using intel_iommu_strict it is likely (likely, as defined by: off the top of my head) on current workloads to *not* hit this bug since we often teardown all VMAs for an object shared across multiple VMs. We also finish access to that object before the first dma_unmapping. intel_iommu_strict with flinked buffers is likely to hit this issue. Signed-off-by: Armin Reese [danvet: Add the excellent commit message provided by Ben.] Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ed0b5fc..dcd8d7b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2927,8 +2927,6 @@ int i915_vma_unbind(struct i915_vma *vma) vma->unbind_vma(vma); - i915_gem_gtt_finish_object(obj); - list_del_init(&vma->mm_list); /* Avoid an unnecessary call to unbind on rebind. */ if (i915_is_ggtt(vma->vm)) @@ -2939,8 +2937,10 @@ int i915_vma_unbind(struct i915_vma *vma) /* Since the unbound list is global, only move to that list if * no more VMAs exist. */ - if (list_empty(&obj->vma_list)) + if (list_empty(&obj->vma_list)) { + i915_gem_gtt_finish_object(obj); list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list); + } /* And finally now the object is completely decoupled from this vma, * we can drop its hold on the backing storage and allow it to be -- cgit v0.10.2 From b4558b46d58e872114e96647cbd6a75ff7280f86 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Fri, 18 Jul 2014 02:19:40 -0700 Subject: drm/i915: Fix possible overflow when recording semaphore states. semaphore _sync_seqno, _seqno and _mbox are smaller than number of rings. This optimization is to remove the ring itself from the list and the logic to do that is at intel_ring_sync_index as below: /* * rcs -> 0 = vcs, 1 = bcs, 2 = vecs, 3 = vcs2; * vcs -> 0 = bcs, 1 = vecs, 2 = vcs2, 3 = rcs; * bcs -> 0 = vecs, 1 = vcs2. 2 = rcs, 3 = vcs; * vecs -> 0 = vcs2, 1 = rcs, 2 = vcs, 3 = bcs; * vcs2 -> 0 = rcs, 1 = vcs, 2 = bcs, 3 = vecs; */ v2: Skip when from == to (Damien). v3: avoid computing idx when from == to (Damien). use ring == to instead of ring->id == to->id (Damien). use continue instead of return (Rodrigo). v4: avoid all unecessary computation (Damien). reduce idx to loop scope (Damien). Cc: Damien Lespiau Cc: Ben Widawsky Signed-off-by: Rodrigo Vivi Reviewed-by: Damien Lespiau Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 9faebbc..0b3f694 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -764,7 +764,7 @@ static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv, struct intel_engine_cs *ring, struct drm_i915_error_ring *ering) { - struct intel_engine_cs *useless; + struct intel_engine_cs *to; int i; if (!i915_semaphore_is_enabled(dev_priv->dev)) @@ -776,13 +776,20 @@ static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv, dev_priv->semaphore_obj, &dev_priv->gtt.base); - for_each_ring(useless, dev_priv, i) { - u16 signal_offset = - (GEN8_SIGNAL_OFFSET(ring, i) & PAGE_MASK) / 4; - u32 *tmp = error->semaphore_obj->pages[0]; + for_each_ring(to, dev_priv, i) { + int idx; + u16 signal_offset; + u32 *tmp; - ering->semaphore_mboxes[i] = tmp[signal_offset]; - ering->semaphore_seqno[i] = ring->semaphore.sync_seqno[i]; + if (ring == to) + continue; + + signal_offset = (GEN8_SIGNAL_OFFSET(ring, i) & PAGE_MASK) / 4; + tmp = error->semaphore_obj->pages[0]; + idx = intel_ring_sync_index(ring, to); + + ering->semaphore_mboxes[idx] = tmp[signal_offset]; + ering->semaphore_seqno[idx] = ring->semaphore.sync_seqno[idx]; } } -- cgit v0.10.2 From 5fafe292953649a992f04694393207a6e6453a34 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 21 Jul 2014 15:23:38 +0530 Subject: drm/i915: Adding HAS_GMCH_DISPLAY macro Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 46ee0a90..9bc6267 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2092,6 +2092,8 @@ struct drm_i915_cmd_table { #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP) #define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE) +#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)) + /* DPF == dynamic parity feature */ #define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) #define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev)) -- cgit v0.10.2 From 9ad0257c3bfdd0ce78a268c73cb886ecf7cdd097 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 21 Jul 2014 15:23:39 +0530 Subject: drm/i915: Allowing changing of wm latencies for valid platforms Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 083683c..652464c 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -3267,7 +3267,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; - if (!HAS_PCH_SPLIT(dev)) + if (HAS_GMCH_DISPLAY(dev)) return -ENODEV; return single_open(file, pri_wm_latency_show, dev); @@ -3277,7 +3277,7 @@ static int spr_wm_latency_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; - if (!HAS_PCH_SPLIT(dev)) + if (HAS_GMCH_DISPLAY(dev)) return -ENODEV; return single_open(file, spr_wm_latency_show, dev); @@ -3287,7 +3287,7 @@ static int cur_wm_latency_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; - if (!HAS_PCH_SPLIT(dev)) + if (HAS_GMCH_DISPLAY(dev)) return -ENODEV; return single_open(file, cur_wm_latency_show, dev); -- cgit v0.10.2 From 92e23b997332c5aa21a94c3bf81b2546df99089b Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 21 Jul 2014 15:23:40 +0530 Subject: drm/i915: Returning the right VGA control reg for platforms Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9bc6267..30b23bf 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2803,10 +2803,10 @@ int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val); static inline uint32_t i915_vgacntrl_reg(struct drm_device *dev) { - if (HAS_PCH_SPLIT(dev)) - return CPU_VGACNTRL; - else if (IS_VALLEYVIEW(dev)) + if (IS_VALLEYVIEW(dev)) return VLV_VGACNTRL; + else if (INTEL_INFO(dev)->gen >= 5) + return CPU_VGACNTRL; else return VGACNTRL; } -- cgit v0.10.2 From 3abfce77f7229116fa56aedfe3b79ce5ccde3d72 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 21 Jul 2014 15:23:43 +0530 Subject: drm/i915: Writing proper check for reading of pipe status reg Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c18c2a7..2df6860 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13225,7 +13225,7 @@ intel_display_capture_error_state(struct drm_device *dev) error->pipe[i].source = I915_READ(PIPESRC(i)); - if (!HAS_PCH_SPLIT(dev)) + if (HAS_GMCH_DISPLAY(dev)) error->pipe[i].stat = I915_READ(PIPESTAT(i)); } -- cgit v0.10.2 From f227ae9e7692b2dbb4a7d868cdcbdbfee9dffc6d Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 21 Jul 2014 15:23:45 +0530 Subject: drm/i915: Avoid incorrect returning for some platforms Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 228489c..c6601c1 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -882,7 +882,7 @@ static bool hdmi_12bpc_possible(struct intel_crtc *crtc) struct intel_encoder *encoder; int count = 0, count_hdmi = 0; - if (!HAS_PCH_SPLIT(dev)) + if (HAS_GMCH_DISPLAY(dev)) return false; list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { -- cgit v0.10.2 From 7a1db49af64e1b622a1d155f6b375439337cc355 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Tue, 22 Jul 2014 11:18:27 +0530 Subject: drm/i915: Setting legacy palette correctly for different platforms v2: Adding !HAS_GMCH_DISPLAY(dev) Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2df6860..fea68f3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3847,7 +3847,7 @@ static void intel_crtc_load_lut(struct drm_crtc *crtc) } /* use legacy palette for Ironlake */ - if (HAS_PCH_SPLIT(dev)) + if (!HAS_GMCH_DISPLAY(dev)) palreg = LGC_PALETTE(pipe); /* Workaround : Do not read or write the pipe palette/gamma data while -- cgit v0.10.2 From baff296cbe4e7337bdaaa04959b062df1acb22c2 Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Tue, 22 Jul 2014 11:16:35 +0530 Subject: drm/i915: Returning from increase/decrease of pllclock when invalid v2: Adding !HAS_GMCH_DISPLAY(dev) Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fea68f3..1389d8a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8780,7 +8780,7 @@ static void intel_increase_pllclock(struct drm_device *dev, int dpll_reg = DPLL(pipe); int dpll; - if (HAS_PCH_SPLIT(dev)) + if (!HAS_GMCH_DISPLAY(dev)) return; if (!dev_priv->lvds_downclock_avail) @@ -8808,7 +8808,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (HAS_PCH_SPLIT(dev)) + if (!HAS_GMCH_DISPLAY(dev)) return; if (!dev_priv->lvds_downclock_avail) -- cgit v0.10.2 From b98856a86b0a44ef9d0ab61f6855baf6d941fe6f Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Tue, 22 Jul 2014 11:13:46 +0530 Subject: drm/i915: Replace HAS_PCH_SPLIT which incorrectly lets some platforms in v2: Adding IS_G4X instead of gen < 5 as suggested by Daniel Signed-off-by: Sonika Jindal Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index c6601c1..d189e95 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1562,7 +1562,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, if (IS_VALLEYVIEW(dev)) { intel_hdmi->write_infoframe = vlv_write_infoframe; intel_hdmi->set_infoframes = vlv_set_infoframes; - } else if (!HAS_PCH_SPLIT(dev)) { + } else if (IS_G4X(dev)) { intel_hdmi->write_infoframe = g4x_write_infoframe; intel_hdmi->set_infoframes = g4x_set_infoframes; } else if (HAS_DDI(dev)) { -- cgit v0.10.2 From 1de6068eb755cf8f87b985277fc396903d6bccd2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 24 Jun 2014 18:27:39 +0300 Subject: drm/i915: extract backlight minimum brightness from VBT Reviewed-by: Jesse Barnes Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 30b23bf..12ab320 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1270,6 +1270,7 @@ struct intel_vbt_data { u16 pwm_freq_hz; bool present; bool active_low_pwm; + u8 min_brightness; /* min_brightness/255 of max */ } backlight; /* MIPI DSI */ diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 827498e..608ed30 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -336,11 +336,12 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb) dev_priv->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; dev_priv->vbt.backlight.active_low_pwm = entry->active_low_pwm; + dev_priv->vbt.backlight.min_brightness = entry->min_brightness; DRM_DEBUG_KMS("VBT backlight PWM modulation frequency %u Hz, " "active %s, min brightness %u, level %u\n", dev_priv->vbt.backlight.pwm_freq_hz, dev_priv->vbt.backlight.active_low_pwm ? "low" : "high", - entry->min_brightness, + dev_priv->vbt.backlight.min_brightness, backlight_data->level[panel_type]); } -- cgit v0.10.2 From 6dda730e55f412a6dfb181cae6784822ba463847 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 24 Jun 2014 18:27:40 +0300 Subject: drm/i915: respect the VBT minimum backlight brightness Historically we've exposed the full backlight PWM duty cycle range to the userspace, in the name of "mechanism, not policy". However, it turns out there are both panels and board designs where there is a minimum duty cycle that is required for proper operation. The minimum duty cycle is available in the VBT. The backlight class sysfs interface does not make any promises to the userspace about the physical meaning of the range 0..max_brightness. Specifically there is no guarantee that 0 means off; indeed for acpi_backlight 0 usually is not off, but the minimum acceptable value. Respect the minimum backlight, and expose the range acceptable to the hardware as 0..max_brightness to the userspace via the backlight class device; 0 means the minimum acceptable enabled value. To switch off the backlight, the user must disable the encoder. As a side effect, make the backlight class device max brightness and physical PWM modulation frequency (i.e. max duty cycle) independent. This allows a follow-up patch to virtualize the max value exposed to the userspace. Signed-off-by: Jani Nikula Reviewed-by: Jesse Barnes [danvet: s/BUG_ON/WARN_ON/] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc68c7..bb9042b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -165,6 +165,7 @@ struct intel_panel { struct { bool present; u32 level; + u32 min; u32 max; bool enabled; bool combination_mode; /* gen 2/4 only */ @@ -969,8 +970,8 @@ void intel_pch_panel_fitting(struct intel_crtc *crtc, void intel_gmch_panel_fitting(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config, int fitting_mode); -void intel_panel_set_backlight(struct intel_connector *connector, u32 level, - u32 max); +void intel_panel_set_backlight_acpi(struct intel_connector *connector, + u32 level, u32 max); int intel_panel_setup_backlight(struct drm_connector *connector); void intel_panel_enable_backlight(struct intel_connector *connector); void intel_panel_disable_backlight(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 2e2c71f..5a979b7 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -418,7 +418,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) */ DRM_DEBUG_KMS("updating opregion backlight %d/255\n", bclp); list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) - intel_panel_set_backlight(intel_connector, bclp, 255); + intel_panel_set_backlight_acpi(intel_connector, bclp, 255); iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv); drm_modeset_unlock(&dev->mode_config.connection_mutex); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 38a9857..f2d5f2e 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -398,6 +398,69 @@ intel_panel_detect(struct drm_device *dev) } } +/** + * scale - scale values from one range to another + * + * @source_val: value in range [@source_min..@source_max] + * + * Return @source_val in range [@source_min..@source_max] scaled to range + * [@target_min..@target_max]. + */ +static uint32_t scale(uint32_t source_val, + uint32_t source_min, uint32_t source_max, + uint32_t target_min, uint32_t target_max) +{ + uint64_t target_val; + + WARN_ON(source_min > source_max); + WARN_ON(target_min > target_max); + + /* defensive */ + source_val = clamp(source_val, source_min, source_max); + + /* avoid overflows */ + target_val = (uint64_t)(source_val - source_min) * + (target_max - target_min); + do_div(target_val, source_max - source_min); + target_val += target_min; + + return target_val; +} + +/* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */ +static inline u32 scale_user_to_hw(struct intel_connector *connector, + u32 user_level, u32 user_max) +{ + struct intel_panel *panel = &connector->panel; + + return scale(user_level, 0, user_max, + panel->backlight.min, panel->backlight.max); +} + +/* Scale user_level in range [0..user_max] to [0..hw_max], clamping the result + * to [hw_min..hw_max]. */ +static inline u32 clamp_user_to_hw(struct intel_connector *connector, + u32 user_level, u32 user_max) +{ + struct intel_panel *panel = &connector->panel; + u32 hw_level; + + hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max); + hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max); + + return hw_level; +} + +/* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */ +static inline u32 scale_hw_to_user(struct intel_connector *connector, + u32 hw_level, u32 user_max) +{ + struct intel_panel *panel = &connector->panel; + + return scale(hw_level, panel->backlight.min, panel->backlight.max, + 0, user_max); +} + static u32 intel_panel_compute_brightness(struct intel_connector *connector, u32 val) { @@ -557,17 +620,16 @@ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) dev_priv->display.set_backlight(connector, level); } -/* set backlight brightness to level in range [0..max] */ -void intel_panel_set_backlight(struct intel_connector *connector, u32 level, - u32 max) +/* set backlight brightness to level in range [0..max], scaling wrt hw min */ +static void intel_panel_set_backlight(struct intel_connector *connector, + u32 user_level, u32 user_max) { struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_panel *panel = &connector->panel; enum pipe pipe = intel_get_pipe_from_connector(connector); - u32 freq; + u32 hw_level; unsigned long flags; - u64 n; if (!panel->backlight.present || pipe == INVALID_PIPE) return; @@ -576,18 +638,46 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, WARN_ON(panel->backlight.max == 0); - /* scale to hardware max, but be careful to not overflow */ - freq = panel->backlight.max; - n = (u64)level * freq; - do_div(n, max); - level = n; + hw_level = scale_user_to_hw(connector, user_level, user_max); + panel->backlight.level = hw_level; + + if (panel->backlight.enabled) + intel_panel_actually_set_backlight(connector, hw_level); + + spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); +} + +/* set backlight brightness to level in range [0..max], assuming hw min is + * respected. + */ +void intel_panel_set_backlight_acpi(struct intel_connector *connector, + u32 user_level, u32 user_max) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_panel *panel = &connector->panel; + enum pipe pipe = intel_get_pipe_from_connector(connector); + u32 hw_level; + unsigned long flags; + + if (!panel->backlight.present || pipe == INVALID_PIPE) + return; + + spin_lock_irqsave(&dev_priv->backlight_lock, flags); + + WARN_ON(panel->backlight.max == 0); + + hw_level = clamp_user_to_hw(connector, user_level, user_max); + panel->backlight.level = hw_level; - panel->backlight.level = level; if (panel->backlight.device) - panel->backlight.device->props.brightness = level; + panel->backlight.device->props.brightness = + scale_hw_to_user(connector, + panel->backlight.level, + panel->backlight.device->props.max_brightness); if (panel->backlight.enabled) - intel_panel_actually_set_backlight(connector, level); + intel_panel_actually_set_backlight(connector, hw_level); spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); } @@ -860,7 +950,9 @@ void intel_panel_enable_backlight(struct intel_connector *connector) panel->backlight.level = panel->backlight.max; if (panel->backlight.device) panel->backlight.device->props.brightness = - panel->backlight.level; + scale_hw_to_user(connector, + panel->backlight.level, + panel->backlight.device->props.max_brightness); } dev_priv->display.enable_backlight(connector); @@ -889,11 +981,15 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd) struct intel_connector *connector = bl_get_data(bd); struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + u32 hw_level; int ret; intel_runtime_pm_get(dev_priv); drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - ret = intel_panel_get_backlight(connector); + + hw_level = intel_panel_get_backlight(connector); + ret = scale_hw_to_user(connector, hw_level, bd->props.max_brightness); + drm_modeset_unlock(&dev->mode_config.connection_mutex); intel_runtime_pm_put(dev_priv); @@ -913,12 +1009,19 @@ static int intel_backlight_device_register(struct intel_connector *connector) if (WARN_ON(panel->backlight.device)) return -ENODEV; - BUG_ON(panel->backlight.max == 0); + WARN_ON(panel->backlight.max == 0); memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; - props.brightness = panel->backlight.level; + + /* + * Note: Everything should work even if the backlight device max + * presented to the userspace is arbitrarily chosen. + */ props.max_brightness = panel->backlight.max; + props.brightness = scale_hw_to_user(connector, + panel->backlight.level, + props.max_brightness); /* * Note: using the same name independent of the connector prevents @@ -964,6 +1067,19 @@ static void intel_backlight_device_unregister(struct intel_connector *connector) * XXX: Query mode clock or hardware clock and program PWM modulation frequency * appropriately when it's 0. Use VBT and/or sane defaults. */ +static u32 get_backlight_min_vbt(struct intel_connector *connector) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_panel *panel = &connector->panel; + + WARN_ON(panel->backlight.max == 0); + + /* vbt value is a coefficient in range [0..255] */ + return scale(dev_priv->vbt.backlight.min_brightness, 0, 255, + 0, panel->backlight.max); +} + static int bdw_setup_backlight(struct intel_connector *connector) { struct drm_device *dev = connector->base.dev; @@ -979,6 +1095,8 @@ static int bdw_setup_backlight(struct intel_connector *connector) if (!panel->backlight.max) return -ENODEV; + panel->backlight.min = get_backlight_min_vbt(connector); + val = bdw_get_backlight(connector); panel->backlight.level = intel_panel_compute_brightness(connector, val); @@ -1003,6 +1121,8 @@ static int pch_setup_backlight(struct intel_connector *connector) if (!panel->backlight.max) return -ENODEV; + panel->backlight.min = get_backlight_min_vbt(connector); + val = pch_get_backlight(connector); panel->backlight.level = intel_panel_compute_brightness(connector, val); @@ -1035,6 +1155,8 @@ static int i9xx_setup_backlight(struct intel_connector *connector) if (!panel->backlight.max) return -ENODEV; + panel->backlight.min = get_backlight_min_vbt(connector); + val = i9xx_get_backlight(connector); panel->backlight.level = intel_panel_compute_brightness(connector, val); @@ -1062,6 +1184,8 @@ static int i965_setup_backlight(struct intel_connector *connector) if (!panel->backlight.max) return -ENODEV; + panel->backlight.min = get_backlight_min_vbt(connector); + val = i9xx_get_backlight(connector); panel->backlight.level = intel_panel_compute_brightness(connector, val); @@ -1099,6 +1223,8 @@ static int vlv_setup_backlight(struct intel_connector *connector) if (!panel->backlight.max) return -ENODEV; + panel->backlight.min = get_backlight_min_vbt(connector); + val = _vlv_get_backlight(dev, PIPE_A); panel->backlight.level = intel_panel_compute_brightness(connector, val); -- cgit v0.10.2 From 09b64267c1f72f2670fcde9f11e5453ce365ca23 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 23 Jul 2014 14:25:24 +1000 Subject: drm/i915: don't suspend gt until after we disable irqs and display (v2) When I moved the irq disable down to after display disable, I didn't realise the gt suspend also required irqs off, so move it down as well. Fixes WARNs seen at suspend/resume time. v2: moved the rps flush down as well. Tested-by: Paulo Zanoni Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a361bb9..2a83833 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -516,11 +516,6 @@ static int i915_drm_freeze(struct drm_device *dev) return error; } - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - - intel_suspend_gt_powersave(dev); - /* * Disable CRTCs directly since we want to preserve sw state * for _thaw. @@ -532,8 +527,13 @@ static int i915_drm_freeze(struct drm_device *dev) drm_modeset_unlock_all(dev); intel_dp_mst_suspend(dev); + + flush_delayed_work(&dev_priv->rps.delayed_resume_work); + intel_runtime_pm_disable_interrupts(dev); + intel_suspend_gt_powersave(dev); + intel_modeset_suspend_hw(dev); } -- cgit v0.10.2 From e05444be705b5c7c7f85d7722b6f97f3a6732d54 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 23 Jul 2014 14:26:12 +1000 Subject: drm/i915: fix initial fbdev setup warnings This chunk was no longer required from what I can see, or at least it is doing the wrong thing, as I confused intel_connector->encoder and connector->encoder. Drop it for now, to remove the warnings at bootup. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 7cdb886..f475414 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -375,11 +375,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, } encoder = connector->encoder; - if (!encoder) { - struct drm_connector_helper_funcs *connector_funcs; - connector_funcs = connector->helper_private; - encoder = connector_funcs->best_encoder(connector); - } if (!encoder || WARN_ON(!encoder->crtc)) { DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", connector->name); -- cgit v0.10.2 From 03dae59c72ffffd8ef6e005f48ba356c863e0587 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 23 Jul 2014 16:27:25 +0200 Subject: drm/i915: Ditch UMS config option Let's march ahead with the deprecation plan laid out in commit b30324adaf8d2e5950a602bde63030d15a61826f Author: Daniel Vetter Date: Wed Nov 13 22:11:25 2013 +0100 drm/i915: Deprecated UMS support Thus far no regression report yet, so the transparent fallback plan seems to pan out. Cc: Dave Airlie Cc: David Herrmann Suggested-by: David Herrmann Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 437e182..4e39ab3 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -69,15 +69,3 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT option changes the default for that module option. If in doubt, say "N". - -config DRM_I915_UMS - bool "Enable userspace modesetting on Intel hardware (DEPRECATED)" - depends on DRM_I915 && BROKEN - default n - help - Choose this option if you still need userspace modesetting. - - Userspace modesetting is deprecated for quite some time now, so - enable this only if you have ancient versions of the DDX drivers. - - If in doubt, say "N". -- cgit v0.10.2 From ec8b0dd51c50e33c3831bbf57ee4ca3e9f35460d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 21 Jul 2014 13:21:23 +0100 Subject: drm/i915: Allow overlapping userptr objects Whilst I strongly advise against doing so for the implicit coherency issues between the multiple buffer objects accessing the same backing store, it nevertheless is a valid use case, akin to mmaping the same file multiple times. The reason why we forbade it earlier was that our use of the interval tree for fast invalidation upon vma changes excluded overlapping objects. So in the case where the user wishes to create such pairs of overlapping objects, we degrade the range invalidation to walkin the linear list of objects associated with the mm. A situation where overlapping objects could arise is the lax implementation of MIT-SHM Pixmaps in the xserver. A second situation is where the user wishes to have different access modes to a region of memory (e.g. access through a read-only userptr buffer and through a normal userptr buffer). v2: Compile for mmu-notifiers after tweaking v3: Rename is_linear/has_linear Signed-off-by: Chris Wilson Cc: "Li, Victor Y" Cc: "Kelley, Sean V" Cc: Tvrtko Ursulin Cc: "Gong, Zhipeng" Cc: Akash Goel Cc: "Volkin, Bradley D" Reviewed-by: Tvrtko Ursulin Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index b41614d..74c45da 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -40,19 +40,87 @@ struct i915_mmu_notifier { struct hlist_node node; struct mmu_notifier mn; struct rb_root objects; + struct list_head linear; struct drm_device *dev; struct mm_struct *mm; struct work_struct work; unsigned long count; unsigned long serial; + bool has_linear; }; struct i915_mmu_object { struct i915_mmu_notifier *mmu; struct interval_tree_node it; + struct list_head link; struct drm_i915_gem_object *obj; + bool is_linear; }; +static unsigned long cancel_userptr(struct drm_i915_gem_object *obj) +{ + struct drm_device *dev = obj->base.dev; + unsigned long end; + + mutex_lock(&dev->struct_mutex); + /* Cancel any active worker and force us to re-evaluate gup */ + obj->userptr.work = NULL; + + if (obj->pages != NULL) { + struct drm_i915_private *dev_priv = to_i915(dev); + struct i915_vma *vma, *tmp; + bool was_interruptible; + + was_interruptible = dev_priv->mm.interruptible; + dev_priv->mm.interruptible = false; + + list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) { + int ret = i915_vma_unbind(vma); + WARN_ON(ret && ret != -EIO); + } + WARN_ON(i915_gem_object_put_pages(obj)); + + dev_priv->mm.interruptible = was_interruptible; + } + + end = obj->userptr.ptr + obj->base.size; + + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + + return end; +} + +static void invalidate_range__linear(struct i915_mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct i915_mmu_object *mmu; + unsigned long serial; + +restart: + serial = mn->serial; + list_for_each_entry(mmu, &mn->linear, link) { + struct drm_i915_gem_object *obj; + + if (mmu->it.last < start || mmu->it.start > end) + continue; + + obj = mmu->obj; + drm_gem_object_reference(&obj->base); + spin_unlock(&mn->lock); + + cancel_userptr(obj); + + spin_lock(&mn->lock); + if (serial != mn->serial) + goto restart; + } + + spin_unlock(&mn->lock); +} + static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, struct mm_struct *mm, unsigned long start, @@ -60,16 +128,19 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, { struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn); struct interval_tree_node *it = NULL; + unsigned long next = start; unsigned long serial = 0; end--; /* interval ranges are inclusive, but invalidate range is exclusive */ - while (start < end) { + while (next < end) { struct drm_i915_gem_object *obj; obj = NULL; spin_lock(&mn->lock); + if (mn->has_linear) + return invalidate_range__linear(mn, mm, start, end); if (serial == mn->serial) - it = interval_tree_iter_next(it, start, end); + it = interval_tree_iter_next(it, next, end); else it = interval_tree_iter_first(&mn->objects, start, end); if (it != NULL) { @@ -81,31 +152,7 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, if (obj == NULL) return; - mutex_lock(&mn->dev->struct_mutex); - /* Cancel any active worker and force us to re-evaluate gup */ - obj->userptr.work = NULL; - - if (obj->pages != NULL) { - struct drm_i915_private *dev_priv = to_i915(mn->dev); - struct i915_vma *vma, *tmp; - bool was_interruptible; - - was_interruptible = dev_priv->mm.interruptible; - dev_priv->mm.interruptible = false; - - list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) { - int ret = i915_vma_unbind(vma); - WARN_ON(ret && ret != -EIO); - } - WARN_ON(i915_gem_object_put_pages(obj)); - - dev_priv->mm.interruptible = was_interruptible; - } - - start = obj->userptr.ptr + obj->base.size; - - drm_gem_object_unreference(&obj->base); - mutex_unlock(&mn->dev->struct_mutex); + next = cancel_userptr(obj); } } @@ -151,6 +198,8 @@ i915_mmu_notifier_get(struct drm_device *dev, struct mm_struct *mm) mmu->objects = RB_ROOT; mmu->count = 0; mmu->serial = 1; + INIT_LIST_HEAD(&mmu->linear); + mmu->has_linear = false; /* Protected by mmap_sem (write-lock) */ ret = __mmu_notifier_register(&mmu->mn, mm); @@ -197,6 +246,17 @@ static void __i915_mmu_notifier_update_serial(struct i915_mmu_notifier *mmu) mmu->serial = 1; } +static bool i915_mmu_notifier_has_linear(struct i915_mmu_notifier *mmu) +{ + struct i915_mmu_object *mn; + + list_for_each_entry(mn, &mmu->linear, link) + if (mn->is_linear) + return true; + + return false; +} + static void i915_mmu_notifier_del(struct i915_mmu_notifier *mmu, struct i915_mmu_object *mn) @@ -204,7 +264,11 @@ i915_mmu_notifier_del(struct i915_mmu_notifier *mmu, lockdep_assert_held(&mmu->dev->struct_mutex); spin_lock(&mmu->lock); - interval_tree_remove(&mn->it, &mmu->objects); + list_del(&mn->link); + if (mn->is_linear) + mmu->has_linear = i915_mmu_notifier_has_linear(mmu); + else + interval_tree_remove(&mn->it, &mmu->objects); __i915_mmu_notifier_update_serial(mmu); spin_unlock(&mmu->lock); @@ -230,7 +294,6 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu, */ i915_gem_retire_requests(mmu->dev); - /* Disallow overlapping userptr objects */ spin_lock(&mmu->lock); it = interval_tree_iter_first(&mmu->objects, mn->it.start, mn->it.last); @@ -243,14 +306,22 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu, * to flush their object references upon which the object will * be removed from the interval-tree, or the the range is * still in use by another client and the overlap is invalid. + * + * If we do have an overlap, we cannot use the interval tree + * for fast range invalidation. */ obj = container_of(it, struct i915_mmu_object, it)->obj; - ret = obj->userptr.workers ? -EAGAIN : -EINVAL; - } else { + if (!obj->userptr.workers) + mmu->has_linear = mn->is_linear = true; + else + ret = -EAGAIN; + } else interval_tree_insert(&mn->it, &mmu->objects); + + if (ret == 0) { + list_add(&mn->link, &mmu->linear); __i915_mmu_notifier_update_serial(mmu); - ret = 0; } spin_unlock(&mmu->lock); mutex_unlock(&mmu->dev->struct_mutex); @@ -611,12 +682,11 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { * We impose several restrictions upon the memory being mapped * into the GPU. * 1. It must be page aligned (both start/end addresses, i.e ptr and size). - * 2. It cannot overlap any other userptr object in the same address space. - * 3. It must be normal system memory, not a pointer into another map of IO + * 2. It must be normal system memory, not a pointer into another map of IO * space (e.g. it must not be a GTT mmapping of another object). - * 4. We only allow a bo as large as we could in theory map into the GTT, + * 3. We only allow a bo as large as we could in theory map into the GTT, * that is we limit the size to the total size of the GTT. - * 5. The bo is marked as being snoopable. The backing pages are left + * 4. The bo is marked as being snoopable. The backing pages are left * accessible directly by the CPU, but reads and writes by the GPU may * incur the cost of a snoop (unless you have an LLC architecture). * -- cgit v0.10.2 From 487777673e355ab9f0b7cac4ad1207be9d36156f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 24 Jul 2014 13:28:44 +0100 Subject: drm/i915/userptr: Keep spin_lock/unlock in the same block Move the code around in order to acquire and release the spinlock in the same function and in the same block. This keeps static analysers happy and the reader sane. Suggested-by: Julia Lawall Signed-off-by: Chris Wilson Cc: Julia Lawall Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 74c45da..fe69fc8 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -91,10 +91,10 @@ static unsigned long cancel_userptr(struct drm_i915_gem_object *obj) return end; } -static void invalidate_range__linear(struct i915_mmu_notifier *mn, - struct mm_struct *mm, - unsigned long start, - unsigned long end) +static void *invalidate_range__linear(struct i915_mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) { struct i915_mmu_object *mmu; unsigned long serial; @@ -118,7 +118,7 @@ restart: goto restart; } - spin_unlock(&mn->lock); + return NULL; } static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, @@ -133,13 +133,12 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, end--; /* interval ranges are inclusive, but invalidate range is exclusive */ while (next < end) { - struct drm_i915_gem_object *obj; + struct drm_i915_gem_object *obj = NULL; - obj = NULL; spin_lock(&mn->lock); if (mn->has_linear) - return invalidate_range__linear(mn, mm, start, end); - if (serial == mn->serial) + it = invalidate_range__linear(mn, mm, start, end); + else if (serial == mn->serial) it = interval_tree_iter_next(it, next, end); else it = interval_tree_iter_first(&mn->objects, start, end); -- cgit v0.10.2 From 8bed5cc765ffdd61b59f8405d38b377f5a7f0920 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 28 Jul 2014 20:18:36 +0200 Subject: drm/rcar-du: Fix maximum frame buffer pitch computation The maximum pitch constraint for the hardware is expressed in pixels. Convert it to bytes to validate frame buffer creation, as frame buffer pitches are expressed in bytes. Reported-by: Phil Edworthy Signed-off-by: Laurent Pinchart Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index a87edfa..7602610 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -135,7 +135,9 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, { struct rcar_du_device *rcdu = dev->dev_private; const struct rcar_du_format_info *format; + unsigned int max_pitch; unsigned int align; + unsigned int bpp; format = rcar_du_format_info(mode_cmd->pixel_format); if (format == NULL) { @@ -144,13 +146,20 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); } + /* + * The pitch and alignment constraints are expressed in pixels on the + * hardware side and in bytes in the DRM API. + */ + bpp = format->planes == 2 ? 1 : format->bpp / 8; + max_pitch = 4096 * bpp; + if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B)) align = 128; else - align = 16 * format->bpp / 8; + align = 16 * bpp; if (mode_cmd->pitches[0] & (align - 1) || - mode_cmd->pitches[0] >= 8192) { + mode_cmd->pitches[0] >= max_pitch) { dev_dbg(dev->dev, "invalid pitch value %u\n", mode_cmd->pitches[0]); return ERR_PTR(-EINVAL); -- cgit v0.10.2 From 30ebb9088c50181e0f8a2013f7d7579aa3480833 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Mon, 28 Jul 2014 10:23:12 +0200 Subject: drm: sti: add bindings for DRM driver Add DRM/KMS driver bindings documentation. Describe the required properties for each of the hardware IPs drivers. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/Documentation/devicetree/bindings/gpu/st,stih4xx.txt b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt new file mode 100644 index 0000000..2d150c3 --- /dev/null +++ b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt @@ -0,0 +1,189 @@ +STMicroelectronics stih4xx platforms + +- sti-vtg: video timing generator + Required properties: + - compatible: "st,vtg" + - reg: Physical base address of the IP registers and length of memory mapped region. + Optional properties: + - interrupts : VTG interrupt number to the CPU. + - st,slave: phandle on a slave vtg + +- sti-vtac: video timing advanced inter dye communication Rx and TX + Required properties: + - compatible: "st,vtac-main" or "st,vtac-aux" + - reg: Physical base address of the IP registers and length of memory mapped region. + - clocks: from common clock binding: handle hardware IP needed clocks, the + number of clocks may depend of the SoC type. + See ../clocks/clock-bindings.txt for details. + - clock-names: names of the clocks listed in clocks property in the same + order. + +- sti-display-subsystem: Master device for DRM sub-components + This device must be the parent of all the sub-components and is responsible + of bind them. + Required properties: + - compatible: "st,sti-display-subsystem" + - ranges: to allow probing of subdevices + +- sti-compositor: frame compositor engine + must be a child of sti-display-subsystem + Required properties: + - compatible: "st,stih-compositor" + - reg: Physical base address of the IP registers and length of memory mapped region. + - clocks: from common clock binding: handle hardware IP needed clocks, the + number of clocks may depend of the SoC type. + See ../clocks/clock-bindings.txt for details. + - clock-names: names of the clocks listed in clocks property in the same + order. + - resets: resets to be used by the device + See ../reset/reset.txt for details. + - reset-names: names of the resets listed in resets property in the same + order. + - st,vtg: phandle(s) on vtg device (main and aux) nodes. + +- sti-tvout: video out hardware block + must be a child of sti-display-subsystem + Required properties: + - compatible: "st,stih-tvout" + - reg: Physical base address of the IP registers and length of memory mapped region. + - reg-names: names of the mapped memory regions listed in regs property in + the same order. + - resets: resets to be used by the device + See ../reset/reset.txt for details. + - reset-names: names of the resets listed in resets property in the same + order. + - ranges: to allow probing of subdevices + +- sti-hdmi: hdmi output block + must be a child of sti-tvout + Required properties: + - compatible: "st,stih-hdmi"; + - reg: Physical base address of the IP registers and length of memory mapped region. + - reg-names: names of the mapped memory regions listed in regs property in + the same order. + - interrupts : HDMI interrupt number to the CPU. + - interrupt-names: name of the interrupts listed in interrupts property in + the same order + - clocks: from common clock binding: handle hardware IP needed clocks, the + number of clocks may depend of the SoC type. + - clock-names: names of the clocks listed in clocks property in the same + order. + - hdmi,hpd-gpio: gpio id to detect if an hdmi cable is plugged or not. + +sti-hda: + Required properties: + must be a child of sti-tvout + - compatible: "st,stih-hda" + - reg: Physical base address of the IP registers and length of memory mapped region. + - reg-names: names of the mapped memory regions listed in regs property in + the same order. + - clocks: from common clock binding: handle hardware IP needed clocks, the + number of clocks may depend of the SoC type. + See ../clocks/clock-bindings.txt for details. + - clock-names: names of the clocks listed in clocks property in the same + order. + +Example: + +/ { + ... + + vtg_main_slave: sti-vtg-main-slave@fe85A800 { + compatible = "st,vtg"; + reg = <0xfe85A800 0x300>; + interrupts = ; + }; + + vtg_main: sti-vtg-main-master@fd348000 { + compatible = "st,vtg"; + reg = <0xfd348000 0x400>; + st,slave = <&vtg_main_slave>; + }; + + vtg_aux_slave: sti-vtg-aux-slave@fd348400 { + compatible = "st,vtg"; + reg = <0xfe858200 0x300>; + interrupts = ; + }; + + vtg_aux: sti-vtg-aux-master@fd348400 { + compatible = "st,vtg"; + reg = <0xfd348400 0x400>; + st,slave = <&vtg_aux_slave>; + }; + + + sti-vtac-rx-main@fee82800 { + compatible = "st,vtac-main"; + reg = <0xfee82800 0x200>; + clock-names = "vtac"; + clocks = <&clk_m_a2_div0 CLK_M_VTAC_MAIN_PHY>; + }; + + sti-vtac-rx-aux@fee82a00 { + compatible = "st,vtac-aux"; + reg = <0xfee82a00 0x200>; + clock-names = "vtac"; + clocks = <&clk_m_a2_div0 CLK_M_VTAC_AUX_PHY>; + }; + + sti-vtac-tx-main@fd349000 { + compatible = "st,vtac-main"; + reg = <0xfd349000 0x200>, <0xfd320000 0x10000>; + clock-names = "vtac"; + clocks = <&clk_s_a1_hs CLK_S_VTAC_TX_PHY>; + }; + + sti-vtac-tx-aux@fd349200 { + compatible = "st,vtac-aux"; + reg = <0xfd349200 0x200>, <0xfd320000 0x10000>; + clock-names = "vtac"; + clocks = <&clk_s_a1_hs CLK_S_VTAC_TX_PHY>; + }; + + sti-display-subsystem { + compatible = "st,sti-display-subsystem"; + ranges; + + sti-compositor@fd340000 { + compatible = "st,stih416-compositor"; + reg = <0xfd340000 0x1000>; + clock-names = "compo_main", "compo_aux", + "pix_main", "pix_aux"; + clocks = <&clk_m_a2_div1 CLK_M_COMPO_MAIN>, <&clk_m_a2_div1 CLK_M_COMPO_AUX>, + <&clockgen_c_vcc CLK_S_PIX_MAIN>, <&clockgen_c_vcc CLK_S_PIX_AUX>; + reset-names = "compo-main", "compo-aux"; + resets = <&softreset STIH416_COMPO_M_SOFTRESET>, <&softreset STIH416_COMPO_A_SOFTRESET>; + st,vtg = <&vtg_main>, <&vtg_aux>; + }; + + sti-tvout@fe000000 { + compatible = "st,stih416-tvout"; + reg = <0xfe000000 0x1000>, <0xfe85a000 0x400>, <0xfe830000 0x10000>; + reg-names = "tvout-reg", "hda-reg", "syscfg"; + reset-names = "tvout"; + resets = <&softreset STIH416_HDTVOUT_SOFTRESET>; + ranges; + + sti-hdmi@fe85c000 { + compatible = "st,stih416-hdmi"; + reg = <0xfe85c000 0x1000>, <0xfe830000 0x10000>; + reg-names = "hdmi-reg", "syscfg"; + interrupts = ; + interrupt-names = "irq"; + clock-names = "pix", "tmds", "phy", "audio"; + clocks = <&clockgen_c_vcc CLK_S_PIX_HDMI>, <&clockgen_c_vcc CLK_S_TMDS_HDMI>, <&clockgen_c_vcc CLK_S_HDMI_REJECT_PLL>, <&clockgen_b1 CLK_S_PCM_0>; + hdmi,hpd-gpio = <&PIO2 5>; + }; + + sti-hda@fe85a000 { + compatible = "st,stih416-hda"; + reg = <0xfe85a000 0x400>, <0xfe83085c 0x4>; + reg-names = "hda-reg", "video-dacs-ctrl"; + clock-names = "pix", "hddac"; + clocks = <&clockgen_c_vcc CLK_S_PIX_HD>, <&clockgen_c_vcc CLK_S_HDDAC>; + }; + }; + }; + ... +}; diff --git a/drivers/gpu/drm/sti/NOTES b/drivers/gpu/drm/sti/NOTES new file mode 100644 index 0000000..57e2579 --- /dev/null +++ b/drivers/gpu/drm/sti/NOTES @@ -0,0 +1,58 @@ +1. stiH display hardware IP +--------------------------- +The STMicroelectronics stiH SoCs use a common chain of HW display IP blocks: +- The High Quality Video Display Processor (HQVDP) gets video frames from a + video decoder and does high quality video processing, including scaling. + +- The Compositor is a multiplane, dual-mixer (Main & Aux) digital processor. It + has several inputs: + - The graphics planes are internally processed by the Generic Display + Pipeline (GDP). + - The video plug (VID) connects to the HQVDP output. + - The cursor handles ... a cursor. +- The TV OUT pre-formats (convert, clip, round) the compositor output data +- The HDMI / DVO / HD Analog / SD analog IP builds the video signals + - DVO (Digital Video Output) handles a 24bits parallel signal + - The HD analog signal is typically driven by a YCbCr cable, supporting up to + 1080i mode. + - The SD analog signal is typically used for legacy TV +- The VTG (Video Timing Generators) build Vsync signals used by the other HW IP +Note that some stiH drivers support only a subset of thee HW IP. + + .-------------. .-----------. .-----------. +GPU >-------------+GDP Main | | +---+ HDMI +--> HDMI +GPU >-------------+GDP mixer+---+ | :===========: +GPU >-------------+Cursor | | +---+ DVO +--> 24b// + ------- | COMPOSITOR | | TV OUT | :===========: + | | | | | +---+ HD analog +--> YCbCr +Vid >--+ HQVDP +--+VID Aux +---+ | :===========: +dec | | | mixer| | +---+ SD analog +--> CVBS + '-------' '-------------' '-----------' '-----------' + .-----------. + | main+--> Vsync + | VTG | + | aux+--> Vsync + '-----------' + +2. DRM / HW mapping +------------------- +These IP are mapped to the DRM objects as following: +- The CRTCs are mapped to the Compositor Main and Aux Mixers +- The Framebuffers and planes are mapped to the Compositor GDP (non video + buffers) and to HQVDP+VID (video buffers) +- The Cursor is mapped to the Compositor Cursor +- The Encoders are mapped to the TVOut +- The Bridges/Connectors are mapped to the HDMI / DVO / HD Analog / SD analog + +FB & planes Cursor CRTC Encoders Bridges/Connectors + | | | | | + | | | | | + | .-------------. | .-----------. .-----------. | + +------------> |GDP | Main | | | +-> | | HDMI | <-+ + +------------> |GDP v mixer|<+ | | | :===========: | + | |Cursor | | | +-> | | DVO | <-+ + | ------- | COMPOSITOR | | |TV OUT | | :===========: | + | | | | | | | +-> | | HD analog | <-+ + +-> | HQVDP | |VID Aux |<+ | | | :===========: | + | | | mixer| | +-> | | SD analog | <-+ + '-------' '-------------' '-----------' '-----------' -- cgit v0.10.2 From f2cb3148642533f6c162ce61806b25b6c622ab90 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 18:20:56 +0200 Subject: drm: sti: add VTG driver Video Time Generator drivers are used to synchronize the compositor and tvout hardware IPs by providing line count, sample count, synchronization signals (HSYNC, VSYNC) and top and bottom fields indication. VTG are used by pair for each data path (main or auxiliary) one for master and one for slave. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f512004..31894c8 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -201,3 +201,5 @@ source "drivers/gpu/drm/msm/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" source "drivers/gpu/drm/panel/Kconfig" + +source "drivers/gpu/drm/sti/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 61d9e9c..60eb5d4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_STI) += sti/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig new file mode 100644 index 0000000..a18df02 --- /dev/null +++ b/drivers/gpu/drm/sti/Kconfig @@ -0,0 +1,5 @@ +config DRM_STI + tristate "DRM Support for STMicroelectronics SoC stiH41x Series" + depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) + help + Choose this option to enable DRM on STM stiH41x chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile new file mode 100644 index 0000000..1759c20 --- /dev/null +++ b/drivers/gpu/drm/sti/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_DRM_STI) = \ + sti_vtg.o diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c new file mode 100644 index 0000000..740d6e3 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * Vincent Abriou + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include + +#include + +#include "sti_vtg.h" + +#define VTG_TYPE_MASTER 0 +#define VTG_TYPE_SLAVE_BY_EXT0 1 + +/* registers offset */ +#define VTG_MODE 0x0000 +#define VTG_CLKLN 0x0008 +#define VTG_HLFLN 0x000C +#define VTG_DRST_AUTOC 0x0010 +#define VTG_VID_TFO 0x0040 +#define VTG_VID_TFS 0x0044 +#define VTG_VID_BFO 0x0048 +#define VTG_VID_BFS 0x004C + +#define VTG_HOST_ITS 0x0078 +#define VTG_HOST_ITS_BCLR 0x007C +#define VTG_HOST_ITM_BCLR 0x0088 +#define VTG_HOST_ITM_BSET 0x008C + +#define VTG_H_HD_1 0x00C0 +#define VTG_TOP_V_VD_1 0x00C4 +#define VTG_BOT_V_VD_1 0x00C8 +#define VTG_TOP_V_HD_1 0x00CC +#define VTG_BOT_V_HD_1 0x00D0 + +#define VTG_H_HD_2 0x00E0 +#define VTG_TOP_V_VD_2 0x00E4 +#define VTG_BOT_V_VD_2 0x00E8 +#define VTG_TOP_V_HD_2 0x00EC +#define VTG_BOT_V_HD_2 0x00F0 + +#define VTG_H_HD_3 0x0100 +#define VTG_TOP_V_VD_3 0x0104 +#define VTG_BOT_V_VD_3 0x0108 +#define VTG_TOP_V_HD_3 0x010C +#define VTG_BOT_V_HD_3 0x0110 + +#define VTG_IRQ_BOTTOM BIT(0) +#define VTG_IRQ_TOP BIT(1) +#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) + +/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ +#define AWG_DELAY_HD (-9) +#define AWG_DELAY_ED (-8) +#define AWG_DELAY_SD (-7) + +LIST_HEAD(vtg_lookup); + +/** + * STI VTG structure + * + * @dev: pointer to device driver + * @data: data associated to the device + * @irq: VTG irq + * @type: VTG type (main or aux) + * @notifier_list: notifier callback + * @crtc_id: the crtc id for vblank event + * @slave: slave vtg + * @link: List node to link the structure in lookup list + */ +struct sti_vtg { + struct device *dev; + struct device_node *np; + void __iomem *regs; + int irq; + u32 irq_status; + struct raw_notifier_head notifier_list; + int crtc_id; + struct sti_vtg *slave; + struct list_head link; +}; + +static void vtg_register(struct sti_vtg *vtg) +{ + list_add_tail(&vtg->link, &vtg_lookup); +} + +struct sti_vtg *of_vtg_find(struct device_node *np) +{ + struct sti_vtg *vtg; + + list_for_each_entry(vtg, &vtg_lookup, link) { + if (vtg->np == np) + return vtg; + } + return NULL; +} +EXPORT_SYMBOL(of_vtg_find); + +static void vtg_reset(struct sti_vtg *vtg) +{ + /* reset slave and then master */ + if (vtg->slave) + vtg_reset(vtg->slave); + + writel(1, vtg->regs + VTG_DRST_AUTOC); +} + +static void vtg_set_mode(struct sti_vtg *vtg, + int type, const struct drm_display_mode *mode) +{ + u32 tmp; + + if (vtg->slave) + vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode); + + writel(mode->htotal, vtg->regs + VTG_CLKLN); + writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN); + + tmp = (mode->vtotal - mode->vsync_start + 1) << 16; + tmp |= mode->htotal - mode->hsync_start; + writel(tmp, vtg->regs + VTG_VID_TFO); + writel(tmp, vtg->regs + VTG_VID_BFO); + + tmp = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16; + tmp |= mode->hdisplay + mode->htotal - mode->hsync_start; + writel(tmp, vtg->regs + VTG_VID_TFS); + writel(tmp, vtg->regs + VTG_VID_BFS); + + /* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */ + tmp = (mode->hsync_end - mode->hsync_start) << 16; + writel(tmp, vtg->regs + VTG_H_HD_1); + writel(tmp, vtg->regs + VTG_H_HD_2); + + tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; + tmp |= 1; + writel(tmp, vtg->regs + VTG_TOP_V_VD_1); + writel(tmp, vtg->regs + VTG_BOT_V_VD_1); + writel(0, vtg->regs + VTG_TOP_V_HD_1); + writel(0, vtg->regs + VTG_BOT_V_HD_1); + + /* prepare VTG set 2 for for HD DCS */ + writel(tmp, vtg->regs + VTG_TOP_V_VD_2); + writel(tmp, vtg->regs + VTG_BOT_V_VD_2); + writel(0, vtg->regs + VTG_TOP_V_HD_2); + writel(0, vtg->regs + VTG_BOT_V_HD_2); + + /* prepare VTG set 3 for HD Analog in HD mode */ + tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; + tmp |= mode->htotal + AWG_DELAY_HD; + writel(tmp, vtg->regs + VTG_H_HD_3); + + tmp = (mode->vsync_end - mode->vsync_start) << 16; + tmp |= mode->vtotal; + writel(tmp, vtg->regs + VTG_TOP_V_VD_3); + writel(tmp, vtg->regs + VTG_BOT_V_VD_3); + + tmp = (mode->htotal + AWG_DELAY_HD) << 16; + tmp |= mode->htotal + AWG_DELAY_HD; + writel(tmp, vtg->regs + VTG_TOP_V_HD_3); + writel(tmp, vtg->regs + VTG_BOT_V_HD_3); + + /* mode */ + writel(type, vtg->regs + VTG_MODE); +} + +static void vtg_enable_irq(struct sti_vtg *vtg) +{ + /* clear interrupt status and mask */ + writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR); + writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR); + writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET); +} + +void sti_vtg_set_config(struct sti_vtg *vtg, + const struct drm_display_mode *mode) +{ + /* write configuration */ + vtg_set_mode(vtg, VTG_TYPE_MASTER, mode); + + vtg_reset(vtg); + + /* enable irq for the vtg vblank synchro */ + if (vtg->slave) + vtg_enable_irq(vtg->slave); + else + vtg_enable_irq(vtg); +} +EXPORT_SYMBOL(sti_vtg_set_config); + +/** + * sti_vtg_get_line_number + * + * @mode: display mode to be used + * @y: line + * + * Return the line number according to the display mode taking + * into account the Sync and Back Porch information. + * Video frame line numbers start at 1, y starts at 0. + * In interlaced modes the start line is the field line number of the odd + * field, but y is still defined as a progressive frame. + */ +u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) +{ + u32 start_line = mode.vtotal - mode.vsync_start + 1; + + if (mode.flags & DRM_MODE_FLAG_INTERLACE) + start_line *= 2; + + return start_line + y; +} +EXPORT_SYMBOL(sti_vtg_get_line_number); + +/** + * sti_vtg_get_pixel_number + * + * @mode: display mode to be used + * @x: row + * + * Return the pixel number according to the display mode taking + * into account the Sync and Back Porch information. + * Pixels are counted from 0. + */ +u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) +{ + return mode.htotal - mode.hsync_start + x; +} +EXPORT_SYMBOL(sti_vtg_get_pixel_number); + +int sti_vtg_register_client(struct sti_vtg *vtg, + struct notifier_block *nb, int crtc_id) +{ + if (vtg->slave) + return sti_vtg_register_client(vtg->slave, nb, crtc_id); + + vtg->crtc_id = crtc_id; + return raw_notifier_chain_register(&vtg->notifier_list, nb); +} +EXPORT_SYMBOL(sti_vtg_register_client); + +int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) +{ + if (vtg->slave) + return sti_vtg_unregister_client(vtg->slave, nb); + + return raw_notifier_chain_unregister(&vtg->notifier_list, nb); +} +EXPORT_SYMBOL(sti_vtg_unregister_client); + +static irqreturn_t vtg_irq_thread(int irq, void *arg) +{ + struct sti_vtg *vtg = arg; + u32 event; + + event = (vtg->irq_status & VTG_IRQ_TOP) ? + VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT; + + raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id); + + return IRQ_HANDLED; +} + +static irqreturn_t vtg_irq(int irq, void *arg) +{ + struct sti_vtg *vtg = arg; + + vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS); + + writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR); + + /* force sync bus write */ + readl(vtg->regs + VTG_HOST_ITS); + + return IRQ_WAKE_THREAD; +} + +static int vtg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np; + struct sti_vtg *vtg; + struct resource *res; + char irq_name[32]; + int ret; + + vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); + if (!vtg) + return -ENOMEM; + + vtg->dev = dev; + vtg->np = pdev->dev.of_node; + + /* Get Memory ressources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + DRM_ERROR("Get memory resource failed\n"); + return -ENOMEM; + } + vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + + np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); + if (np) { + vtg->slave = of_vtg_find(np); + + if (!vtg->slave) + return -EPROBE_DEFER; + } else { + vtg->irq = platform_get_irq(pdev, 0); + if (IS_ERR_VALUE(vtg->irq)) { + DRM_ERROR("Failed to get VTG interrupt\n"); + return vtg->irq; + } + + snprintf(irq_name, sizeof(irq_name), "vsync-%s", + dev_name(vtg->dev)); + + RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); + + ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, + vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg); + if (IS_ERR_VALUE(ret)) { + DRM_ERROR("Failed to register VTG interrupt\n"); + return ret; + } + } + + vtg_register(vtg); + platform_set_drvdata(pdev, vtg); + + DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev)); + + return 0; +} + +static int vtg_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id vtg_of_match[] = { + { .compatible = "st,vtg", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vtg_of_match); + +struct platform_driver sti_vtg_driver = { + .driver = { + .name = "sti-vtg", + .owner = THIS_MODULE, + .of_match_table = vtg_of_match, + }, + .probe = vtg_probe, + .remove = vtg_remove, +}; + +module_platform_driver(sti_vtg_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h new file mode 100644 index 0000000..e84d23f --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_VTG_H_ +#define _STI_VTG_H_ + +#define VTG_TOP_FIELD_EVENT 1 +#define VTG_BOTTOM_FIELD_EVENT 2 + +struct sti_vtg; +struct drm_display_mode; +struct notifier_block; + +struct sti_vtg *of_vtg_find(struct device_node *np); +void sti_vtg_set_config(struct sti_vtg *vtg, + const struct drm_display_mode *mode); +int sti_vtg_register_client(struct sti_vtg *vtg, + struct notifier_block *nb, int crtc_id); +int sti_vtg_unregister_client(struct sti_vtg *vtg, + struct notifier_block *nb); + +u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y); +u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x); + +#endif -- cgit v0.10.2 From 9ed68fa74e34c1bc36b76f1c4abf4dd4744ecce9 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 19:23:49 +0200 Subject: drm: sti: add VTAC drivers Video Traffic Advance Communication Rx and Tx drivers are designed for inter-die communication. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 1759c20..1534d4c 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_DRM_STI) = \ - sti_vtg.o + sti_vtg.o \ + sti_vtac.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_vtac.c b/drivers/gpu/drm/sti/sti_vtac.c new file mode 100644 index 0000000..82a51d4 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtac.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include + +#include + +/* registers offset */ +#define VTAC_CONFIG 0x00 +#define VTAC_RX_FIFO_CONFIG 0x04 +#define VTAC_FIFO_CONFIG_VAL 0x04 + +#define VTAC_SYS_CFG8521 0x824 +#define VTAC_SYS_CFG8522 0x828 + +/* Number of phyts per pixel */ +#define VTAC_2_5_PPP 0x0005 +#define VTAC_3_PPP 0x0006 +#define VTAC_4_PPP 0x0008 +#define VTAC_5_PPP 0x000A +#define VTAC_6_PPP 0x000C +#define VTAC_13_PPP 0x001A +#define VTAC_14_PPP 0x001C +#define VTAC_15_PPP 0x001E +#define VTAC_16_PPP 0x0020 +#define VTAC_17_PPP 0x0022 +#define VTAC_18_PPP 0x0024 + +/* enable bits */ +#define VTAC_ENABLE 0x3003 + +#define VTAC_TX_PHY_ENABLE_CLK_PHY BIT(0) +#define VTAC_TX_PHY_ENABLE_CLK_DLL BIT(1) +#define VTAC_TX_PHY_PLL_NOT_OSC_MODE BIT(3) +#define VTAC_TX_PHY_RST_N_DLL_SWITCH BIT(4) +#define VTAC_TX_PHY_PROG_N3 BIT(9) + + +/** + * VTAC mode structure + * + * @vid_in_width: Video Data Resolution + * @phyts_width: Width of phyt buses(phyt low and phyt high). + * @phyts_per_pixel: Number of phyts sent per pixel + */ +struct sti_vtac_mode { + u32 vid_in_width; + u32 phyts_width; + u32 phyts_per_pixel; +}; + +static const struct sti_vtac_mode vtac_mode_main = {0x2, 0x2, VTAC_5_PPP}; +static const struct sti_vtac_mode vtac_mode_aux = {0x1, 0x0, VTAC_17_PPP}; + +/** + * VTAC structure + * + * @dev: pointer to device structure + * @regs: ioremapped registers for RX and TX devices + * @phy_regs: phy registers for TX device + * @clk: clock + * @mode: main or auxillary configuration mode + */ +struct sti_vtac { + struct device *dev; + void __iomem *regs; + void __iomem *phy_regs; + struct clk *clk; + const struct sti_vtac_mode *mode; +}; + +static void sti_vtac_rx_set_config(struct sti_vtac *vtac) +{ + u32 config; + + /* Enable VTAC clock */ + if (clk_prepare_enable(vtac->clk)) + DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n"); + + writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG); + + config = VTAC_ENABLE; + config |= vtac->mode->vid_in_width << 4; + config |= vtac->mode->phyts_width << 16; + config |= vtac->mode->phyts_per_pixel << 23; + writel(config, vtac->regs + VTAC_CONFIG); +} + +static void sti_vtac_tx_set_config(struct sti_vtac *vtac) +{ + u32 phy_config; + u32 config; + + /* Enable VTAC clock */ + if (clk_prepare_enable(vtac->clk)) + DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n"); + + /* Configure vtac phy */ + phy_config = 0x00000000; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522); + phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config |= VTAC_TX_PHY_PROG_N3; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); + phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE; + writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); + + /* Configure vtac tx */ + config = VTAC_ENABLE; + config |= vtac->mode->vid_in_width << 4; + config |= vtac->mode->phyts_width << 16; + config |= vtac->mode->phyts_per_pixel << 23; + writel(config, vtac->regs + VTAC_CONFIG); +} + +static const struct of_device_id vtac_of_match[] = { + { + .compatible = "st,vtac-main", + .data = &vtac_mode_main, + }, { + .compatible = "st,vtac-aux", + .data = &vtac_mode_aux, + }, { + /* end node */ + } +}; +MODULE_DEVICE_TABLE(of, vtac_of_match); + +static int sti_vtac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct of_device_id *id; + struct sti_vtac *vtac; + struct resource *res; + + vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL); + if (!vtac) + return -ENOMEM; + + vtac->dev = dev; + + id = of_match_node(vtac_of_match, np); + if (!id) + return -ENOMEM; + + vtac->mode = id->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + DRM_ERROR("Invalid resource\n"); + return -ENOMEM; + } + vtac->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(vtac->regs)) + return PTR_ERR(vtac->regs); + + + vtac->clk = devm_clk_get(dev, "vtac"); + if (IS_ERR(vtac->clk)) { + DRM_ERROR("Cannot get vtac clock\n"); + return PTR_ERR(vtac->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + vtac->phy_regs = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + sti_vtac_tx_set_config(vtac); + } else { + + sti_vtac_rx_set_config(vtac); + } + + platform_set_drvdata(pdev, vtac); + DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev)); + + return 0; +} + +static int sti_vtac_remove(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver sti_vtac_driver = { + .driver = { + .name = "sti-vtac", + .owner = THIS_MODULE, + .of_match_table = vtac_of_match, + }, + .probe = sti_vtac_probe, + .remove = sti_vtac_remove, +}; + +module_platform_driver(sti_vtac_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 5402626c83a2f19da14859e2bab231a53e16ee74 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 19:24:55 +0200 Subject: drm: sti: add HDMI driver Add driver for HDMI output. HDMI PHY registers are mixed into HDMI device registers and their is only one IRQ for all this hardware block. That is why PHYs aren't using phy framework but only a thin hdmi_phy_ops structure with start and stop functions. HDMI driver is mapped on drm_bridge and drm_connector structures. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 1534d4c..12e1a07 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,3 +1,8 @@ +stihdmi-y := sti_hdmi.o \ + sti_hdmi_tx3g0c55phy.o \ + sti_hdmi_tx3g4c28phy.o \ + obj-$(CONFIG_DRM_STI) = \ sti_vtg.o \ - sti_vtac.o \ No newline at end of file + sti_vtac.o \ + stihdmi.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c new file mode 100644 index 0000000..284e541 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Vincent Abriou for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sti_hdmi.h" +#include "sti_hdmi_tx3g4c28phy.h" +#include "sti_hdmi_tx3g0c55phy.h" +#include "sti_vtg.h" + +#define HDMI_CFG 0x0000 +#define HDMI_INT_EN 0x0004 +#define HDMI_INT_STA 0x0008 +#define HDMI_INT_CLR 0x000C +#define HDMI_STA 0x0010 +#define HDMI_ACTIVE_VID_XMIN 0x0100 +#define HDMI_ACTIVE_VID_XMAX 0x0104 +#define HDMI_ACTIVE_VID_YMIN 0x0108 +#define HDMI_ACTIVE_VID_YMAX 0x010C +#define HDMI_DFLT_CHL0_DAT 0x0110 +#define HDMI_DFLT_CHL1_DAT 0x0114 +#define HDMI_DFLT_CHL2_DAT 0x0118 +#define HDMI_SW_DI_1_HEAD_WORD 0x0210 +#define HDMI_SW_DI_1_PKT_WORD0 0x0214 +#define HDMI_SW_DI_1_PKT_WORD1 0x0218 +#define HDMI_SW_DI_1_PKT_WORD2 0x021C +#define HDMI_SW_DI_1_PKT_WORD3 0x0220 +#define HDMI_SW_DI_1_PKT_WORD4 0x0224 +#define HDMI_SW_DI_1_PKT_WORD5 0x0228 +#define HDMI_SW_DI_1_PKT_WORD6 0x022C +#define HDMI_SW_DI_CFG 0x0230 + +#define HDMI_IFRAME_SLOT_AVI 1 + +#define XCAT(prefix, x, suffix) prefix ## x ## suffix +#define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) +#define HDMI_SW_DI_N_PKT_WORD0(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD0) +#define HDMI_SW_DI_N_PKT_WORD1(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD1) +#define HDMI_SW_DI_N_PKT_WORD2(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD2) +#define HDMI_SW_DI_N_PKT_WORD3(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD3) +#define HDMI_SW_DI_N_PKT_WORD4(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD4) +#define HDMI_SW_DI_N_PKT_WORD5(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD5) +#define HDMI_SW_DI_N_PKT_WORD6(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD6) + +#define HDMI_IFRAME_DISABLED 0x0 +#define HDMI_IFRAME_SINGLE_SHOT 0x1 +#define HDMI_IFRAME_FIELD 0x2 +#define HDMI_IFRAME_FRAME 0x3 +#define HDMI_IFRAME_MASK 0x3 +#define HDMI_IFRAME_CFG_DI_N(x, n) ((x) << ((n-1)*4)) /* n from 1 to 6 */ + +#define HDMI_CFG_DEVICE_EN BIT(0) +#define HDMI_CFG_HDMI_NOT_DVI BIT(1) +#define HDMI_CFG_HDCP_EN BIT(2) +#define HDMI_CFG_ESS_NOT_OESS BIT(3) +#define HDMI_CFG_H_SYNC_POL_NEG BIT(4) +#define HDMI_CFG_SINK_TERM_DET_EN BIT(5) +#define HDMI_CFG_V_SYNC_POL_NEG BIT(6) +#define HDMI_CFG_422_EN BIT(8) +#define HDMI_CFG_FIFO_OVERRUN_CLR BIT(12) +#define HDMI_CFG_FIFO_UNDERRUN_CLR BIT(13) +#define HDMI_CFG_SW_RST_EN BIT(31) + +#define HDMI_INT_GLOBAL BIT(0) +#define HDMI_INT_SW_RST BIT(1) +#define HDMI_INT_PIX_CAP BIT(3) +#define HDMI_INT_HOT_PLUG BIT(4) +#define HDMI_INT_DLL_LCK BIT(5) +#define HDMI_INT_NEW_FRAME BIT(6) +#define HDMI_INT_GENCTRL_PKT BIT(7) +#define HDMI_INT_SINK_TERM_PRESENT BIT(11) + +#define HDMI_DEFAULT_INT (HDMI_INT_SINK_TERM_PRESENT \ + | HDMI_INT_DLL_LCK \ + | HDMI_INT_HOT_PLUG \ + | HDMI_INT_GLOBAL) + +#define HDMI_WORKING_INT (HDMI_INT_SINK_TERM_PRESENT \ + | HDMI_INT_GENCTRL_PKT \ + | HDMI_INT_NEW_FRAME \ + | HDMI_INT_DLL_LCK \ + | HDMI_INT_HOT_PLUG \ + | HDMI_INT_PIX_CAP \ + | HDMI_INT_SW_RST \ + | HDMI_INT_GLOBAL) + +#define HDMI_STA_SW_RST BIT(1) + +struct sti_hdmi_connector { + struct drm_connector drm_connector; + struct drm_encoder *encoder; + struct sti_hdmi *hdmi; +}; + +#define to_sti_hdmi_connector(x) \ + container_of(x, struct sti_hdmi_connector, drm_connector) + +u32 hdmi_read(struct sti_hdmi *hdmi, int offset) +{ + return readl(hdmi->regs + offset); +} + +void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset) +{ + writel(val, hdmi->regs + offset); +} + +/** + * HDMI interrupt handler threaded + * + * @irq: irq number + * @arg: connector structure + */ +static irqreturn_t hdmi_irq_thread(int irq, void *arg) +{ + struct sti_hdmi *hdmi = arg; + + /* Hot plug/unplug IRQ */ + if (hdmi->irq_status & HDMI_INT_HOT_PLUG) { + /* read gpio to get the status */ + hdmi->hpd = gpio_get_value(hdmi->hpd_gpio); + if (hdmi->drm_dev) + drm_helper_hpd_irq_event(hdmi->drm_dev); + } + + /* Sw reset and PLL lock are exclusive so we can use the same + * event to signal them + */ + if (hdmi->irq_status & (HDMI_INT_SW_RST | HDMI_INT_DLL_LCK)) { + hdmi->event_received = true; + wake_up_interruptible(&hdmi->wait_event); + } + + return IRQ_HANDLED; +} + +/** + * HDMI interrupt handler + * + * @irq: irq number + * @arg: connector structure + */ +static irqreturn_t hdmi_irq(int irq, void *arg) +{ + struct sti_hdmi *hdmi = arg; + + /* read interrupt status */ + hdmi->irq_status = hdmi_read(hdmi, HDMI_INT_STA); + + /* clear interrupt status */ + hdmi_write(hdmi, hdmi->irq_status, HDMI_INT_CLR); + + /* force sync bus write */ + hdmi_read(hdmi, HDMI_INT_STA); + + return IRQ_WAKE_THREAD; +} + +/** + * Set hdmi active area depending on the drm display mode selected + * + * @hdmi: pointer on the hdmi internal structure + */ +static void hdmi_active_area(struct sti_hdmi *hdmi) +{ + u32 xmin, xmax; + u32 ymin, ymax; + + xmin = sti_vtg_get_pixel_number(hdmi->mode, 0); + xmax = sti_vtg_get_pixel_number(hdmi->mode, hdmi->mode.hdisplay - 1); + ymin = sti_vtg_get_line_number(hdmi->mode, 0); + ymax = sti_vtg_get_line_number(hdmi->mode, hdmi->mode.vdisplay - 1); + + hdmi_write(hdmi, xmin, HDMI_ACTIVE_VID_XMIN); + hdmi_write(hdmi, xmax, HDMI_ACTIVE_VID_XMAX); + hdmi_write(hdmi, ymin, HDMI_ACTIVE_VID_YMIN); + hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX); +} + +/** + * Overall hdmi configuration + * + * @hdmi: pointer on the hdmi internal structure + */ +static void hdmi_config(struct sti_hdmi *hdmi) +{ + u32 conf; + + DRM_DEBUG_DRIVER("\n"); + + /* Clear overrun and underrun fifo */ + conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; + + /* Enable HDMI mode not DVI */ + conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS; + + /* Enable sink term detection */ + conf |= HDMI_CFG_SINK_TERM_DET_EN; + + /* Set Hsync polarity */ + if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) { + DRM_DEBUG_DRIVER("H Sync Negative\n"); + conf |= HDMI_CFG_H_SYNC_POL_NEG; + } + + /* Set Vsync polarity */ + if (hdmi->mode.flags & DRM_MODE_FLAG_NVSYNC) { + DRM_DEBUG_DRIVER("V Sync Negative\n"); + conf |= HDMI_CFG_V_SYNC_POL_NEG; + } + + /* Enable HDMI */ + conf |= HDMI_CFG_DEVICE_EN; + + hdmi_write(hdmi, conf, HDMI_CFG); +} + +/** + * Prepare and configure the AVI infoframe + * + * AVI infoframe are transmitted at least once per two video field and + * contains information about HDMI transmission mode such as color space, + * colorimetry, ... + * + * @hdmi: pointer on the hdmi internal structure + * + * Return negative value if error occurs + */ +static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) +{ + struct drm_display_mode *mode = &hdmi->mode; + struct hdmi_avi_infoframe infoframe; + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE; + u32 val; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, mode); + if (ret < 0) { + DRM_ERROR("failed to setup AVI infoframe: %d\n", ret); + return ret; + } + + /* fixed infoframe configuration not linked to the mode */ + infoframe.colorspace = HDMI_COLORSPACE_RGB; + infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; + infoframe.colorimetry = HDMI_COLORIMETRY_NONE; + + ret = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer)); + if (ret < 0) { + DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); + return ret; + } + + /* Disable transmission slot for AVI infoframe */ + val = hdmi_read(hdmi, HDMI_SW_DI_CFG); + val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI); + hdmi_write(hdmi, val, HDMI_SW_DI_CFG); + + /* Infoframe header */ + val = buffer[0x0]; + val |= buffer[0x1] << 8; + val |= buffer[0x2] << 16; + hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI)); + + /* Infoframe packet bytes */ + val = frame[0x0]; + val |= frame[0x1] << 8; + val |= frame[0x2] << 16; + val |= frame[0x3] << 24; + hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI)); + + val = frame[0x4]; + val |= frame[0x5] << 8; + val |= frame[0x6] << 16; + val |= frame[0x7] << 24; + hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI)); + + val = frame[0x8]; + val |= frame[0x9] << 8; + val |= frame[0xA] << 16; + val |= frame[0xB] << 24; + hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI)); + + val = frame[0xC]; + val |= frame[0xD] << 8; + hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI)); + + /* Enable transmission slot for AVI infoframe + * According to the hdmi specification, AVI infoframe should be + * transmitted at least once per two video fields + */ + val = hdmi_read(hdmi, HDMI_SW_DI_CFG); + val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI); + hdmi_write(hdmi, val, HDMI_SW_DI_CFG); + + return 0; +} + +/** + * Software reset of the hdmi subsystem + * + * @hdmi: pointer on the hdmi internal structure + * + */ +#define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */ +static void hdmi_swreset(struct sti_hdmi *hdmi) +{ + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + /* Enable hdmi_audio clock only during hdmi reset */ + if (clk_prepare_enable(hdmi->clk_audio)) + DRM_INFO("Failed to prepare/enable hdmi_audio clk\n"); + + /* Sw reset */ + hdmi->event_received = false; + + val = hdmi_read(hdmi, HDMI_CFG); + val |= HDMI_CFG_SW_RST_EN; + hdmi_write(hdmi, val, HDMI_CFG); + + /* Wait reset completed */ + wait_event_interruptible_timeout(hdmi->wait_event, + hdmi->event_received == true, + msecs_to_jiffies + (HDMI_TIMEOUT_SWRESET)); + + /* + * HDMI_STA_SW_RST bit is set to '1' when SW_RST bit in HDMI_CFG is + * set to '1' and clk_audio is running. + */ + if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_SW_RST) == 0) + DRM_DEBUG_DRIVER("Warning: HDMI sw reset timeout occurs\n"); + + val = hdmi_read(hdmi, HDMI_CFG); + val &= ~HDMI_CFG_SW_RST_EN; + hdmi_write(hdmi, val, HDMI_CFG); + + /* Disable hdmi_audio clock. Not used anymore for drm purpose */ + clk_disable_unprepare(hdmi->clk_audio); +} + +static void sti_hdmi_disable(struct drm_bridge *bridge) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + u32 val = hdmi_read(hdmi, HDMI_CFG); + + if (!hdmi->enabled) + return; + + DRM_DEBUG_DRIVER("\n"); + + /* Disable HDMI */ + val &= ~HDMI_CFG_DEVICE_EN; + hdmi_write(hdmi, val, HDMI_CFG); + + hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR); + + /* Stop the phy */ + hdmi->phy_ops->stop(hdmi); + + /* Set the default channel data to be a dark red */ + hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); + hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); + hdmi_write(hdmi, 0x0060, HDMI_DFLT_CHL2_DAT); + + /* Disable/unprepare hdmi clock */ + clk_disable_unprepare(hdmi->clk_phy); + clk_disable_unprepare(hdmi->clk_tmds); + clk_disable_unprepare(hdmi->clk_pix); + + hdmi->enabled = false; +} + +static void sti_hdmi_pre_enable(struct drm_bridge *bridge) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + DRM_DEBUG_DRIVER("\n"); + + if (hdmi->enabled) + return; + + /* Prepare/enable clocks */ + if (clk_prepare_enable(hdmi->clk_pix)) + DRM_ERROR("Failed to prepare/enable hdmi_pix clk\n"); + if (clk_prepare_enable(hdmi->clk_tmds)) + DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n"); + if (clk_prepare_enable(hdmi->clk_phy)) + DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n"); + + hdmi->enabled = true; + + /* Program hdmi serializer and start phy */ + if (!hdmi->phy_ops->start(hdmi)) { + DRM_ERROR("Unable to start hdmi phy\n"); + return; + } + + /* Program hdmi active area */ + hdmi_active_area(hdmi); + + /* Enable working interrupts */ + hdmi_write(hdmi, HDMI_WORKING_INT, HDMI_INT_EN); + + /* Program hdmi config */ + hdmi_config(hdmi); + + /* Program AVI infoframe */ + if (hdmi_avi_infoframe_config(hdmi)) + DRM_ERROR("Unable to configure AVI infoframe\n"); + + /* Sw reset */ + hdmi_swreset(hdmi); +} + +static void sti_hdmi_set_mode(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + /* Copy the drm display mode in the connector local structure */ + memcpy(&hdmi->mode, mode, sizeof(struct drm_display_mode)); + + /* Update clock framerate according to the selected mode */ + ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000); + if (ret < 0) { + DRM_ERROR("Cannot set rate (%dHz) for hdmi_pix clk\n", + mode->clock * 1000); + return; + } + ret = clk_set_rate(hdmi->clk_phy, mode->clock * 1000); + if (ret < 0) { + DRM_ERROR("Cannot set rate (%dHz) for hdmi_rejection_pll clk\n", + mode->clock * 1000); + return; + } +} + +static void sti_hdmi_bridge_nope(struct drm_bridge *bridge) +{ + /* do nothing */ +} + +static void sti_hdmi_brigde_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); + kfree(bridge); +} + +static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = { + .pre_enable = sti_hdmi_pre_enable, + .enable = sti_hdmi_bridge_nope, + .disable = sti_hdmi_disable, + .post_disable = sti_hdmi_bridge_nope, + .mode_set = sti_hdmi_set_mode, + .destroy = sti_hdmi_brigde_destroy, +}; + +static int sti_hdmi_connector_get_modes(struct drm_connector *connector) +{ + struct i2c_adapter *i2c_adap; + struct edid *edid; + int count; + + DRM_DEBUG_DRIVER("\n"); + + i2c_adap = i2c_get_adapter(1); + if (!i2c_adap) + goto fail; + + edid = drm_get_edid(connector, i2c_adap); + if (!edid) + goto fail; + + count = drm_add_edid_modes(connector, edid); + drm_mode_connector_update_edid_property(connector, edid); + + kfree(edid); + return count; + +fail: + DRM_ERROR("Can not read HDMI EDID\n"); + return 0; +} + +#define CLK_TOLERANCE_HZ 50 + +static int sti_hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int target = mode->clock * 1000; + int target_min = target - CLK_TOLERANCE_HZ; + int target_max = target + CLK_TOLERANCE_HZ; + int result; + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + struct sti_hdmi *hdmi = hdmi_connector->hdmi; + + + result = clk_round_rate(hdmi->clk_pix, target); + + DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n", + target, result); + + if ((result < target_min) || (result > target_max)) { + DRM_DEBUG_DRIVER("hdmi pixclk=%d not supported\n", target); + return MODE_BAD; + } + + return MODE_OK; +} + +struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector) +{ + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + + /* Best encoder is the one associated during connector creation */ + return hdmi_connector->encoder; +} + +static struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = { + .get_modes = sti_hdmi_connector_get_modes, + .mode_valid = sti_hdmi_connector_mode_valid, + .best_encoder = sti_hdmi_best_encoder, +}; + +/* get detection status of display device */ +static enum drm_connector_status +sti_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + struct sti_hdmi *hdmi = hdmi_connector->hdmi; + + DRM_DEBUG_DRIVER("\n"); + + if (hdmi->hpd) { + DRM_DEBUG_DRIVER("hdmi cable connected\n"); + return connector_status_connected; + } + + DRM_DEBUG_DRIVER("hdmi cable disconnected\n"); + return connector_status_disconnected; +} + +static void sti_hdmi_connector_destroy(struct drm_connector *connector) +{ + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(hdmi_connector); +} + +static struct drm_connector_funcs sti_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = sti_hdmi_connector_detect, + .destroy = sti_hdmi_connector_destroy, +}; + +static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) + return encoder; + } + + return NULL; +} + +static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) +{ + struct sti_hdmi *hdmi = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct drm_encoder *encoder; + struct sti_hdmi_connector *connector; + struct drm_connector *drm_connector; + struct drm_bridge *bridge; + struct i2c_adapter *i2c_adap; + int err; + + i2c_adap = i2c_get_adapter(1); + if (!i2c_adap) + return -EPROBE_DEFER; + + /* Set the drm device handle */ + hdmi->drm_dev = drm_dev; + + encoder = sti_hdmi_find_encoder(drm_dev); + if (!encoder) + return -ENOMEM; + + connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); + if (!connector) + return -ENOMEM; + + connector->hdmi = hdmi; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + bridge->driver_private = hdmi; + drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs); + + encoder->bridge = bridge; + connector->encoder = encoder; + + drm_connector = (struct drm_connector *)connector; + + drm_connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_init(drm_dev, drm_connector, + &sti_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(drm_connector, + &sti_hdmi_connector_helper_funcs); + + err = drm_connector_register(drm_connector); + if (err) + goto err_connector; + + err = drm_mode_connector_attach_encoder(drm_connector, encoder); + if (err) { + DRM_ERROR("Failed to attach a connector to a encoder\n"); + goto err_sysfs; + } + + /* Enable default interrupts */ + hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); + + return 0; + +err_sysfs: + drm_connector_unregister(drm_connector); +err_connector: + drm_bridge_cleanup(bridge); + drm_connector_cleanup(drm_connector); + return -EINVAL; +} + +static void sti_hdmi_unbind(struct device *dev, + struct device *master, void *data) +{ + /* do nothing */ +} + +static const struct component_ops sti_hdmi_ops = { + .bind = sti_hdmi_bind, + .unbind = sti_hdmi_unbind, +}; + +static struct of_device_id hdmi_of_match[] = { + { + .compatible = "st,stih416-hdmi", + .data = &tx3g0c55phy_ops, + }, { + .compatible = "st,stih407-hdmi", + .data = &tx3g4c28phy_ops, + }, { + /* end node */ + } +}; +MODULE_DEVICE_TABLE(of, hdmi_of_match); + +static int sti_hdmi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sti_hdmi *hdmi; + struct device_node *np = dev->of_node; + struct resource *res; + int ret; + + DRM_INFO("%s\n", __func__); + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->dev = pdev->dev; + + /* Get resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg"); + if (!res) { + DRM_ERROR("Invalid hdmi resource\n"); + return -ENOMEM; + } + hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); + + if (of_device_is_compatible(np, "st,stih416-hdmi")) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "syscfg"); + if (!res) { + DRM_ERROR("Invalid syscfg resource\n"); + return -ENOMEM; + } + hdmi->syscfg = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (IS_ERR(hdmi->syscfg)) + return PTR_ERR(hdmi->syscfg); + + } + + hdmi->phy_ops = (struct hdmi_phy_ops *) + of_match_node(hdmi_of_match, np)->data; + + /* Get clock resources */ + hdmi->clk_pix = devm_clk_get(dev, "pix"); + if (IS_ERR(hdmi->clk_pix)) { + DRM_ERROR("Cannot get hdmi_pix clock\n"); + return PTR_ERR(hdmi->clk_pix); + } + + hdmi->clk_tmds = devm_clk_get(dev, "tmds"); + if (IS_ERR(hdmi->clk_tmds)) { + DRM_ERROR("Cannot get hdmi_tmds clock\n"); + return PTR_ERR(hdmi->clk_tmds); + } + + hdmi->clk_phy = devm_clk_get(dev, "phy"); + if (IS_ERR(hdmi->clk_phy)) { + DRM_ERROR("Cannot get hdmi_phy clock\n"); + return PTR_ERR(hdmi->clk_phy); + } + + hdmi->clk_audio = devm_clk_get(dev, "audio"); + if (IS_ERR(hdmi->clk_audio)) { + DRM_ERROR("Cannot get hdmi_audio clock\n"); + return PTR_ERR(hdmi->clk_audio); + } + + hdmi->hpd_gpio = of_get_named_gpio(np, "hdmi,hpd-gpio", 0); + if (hdmi->hpd_gpio < 0) { + DRM_ERROR("Failed to get hdmi hpd-gpio\n"); + return -EIO; + } + + hdmi->hpd = gpio_get_value(hdmi->hpd_gpio); + + init_waitqueue_head(&hdmi->wait_event); + + hdmi->irq = platform_get_irq_byname(pdev, "irq"); + + ret = devm_request_threaded_irq(dev, hdmi->irq, hdmi_irq, + hdmi_irq_thread, IRQF_ONESHOT, dev_name(dev), hdmi); + if (ret) { + DRM_ERROR("Failed to register HDMI interrupt\n"); + return ret; + } + + hdmi->reset = devm_reset_control_get(dev, "hdmi"); + /* Take hdmi out of reset */ + if (!IS_ERR(hdmi->reset)) + reset_control_deassert(hdmi->reset); + + platform_set_drvdata(pdev, hdmi); + + return component_add(&pdev->dev, &sti_hdmi_ops); +} + +static int sti_hdmi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sti_hdmi_ops); + return 0; +} + +struct platform_driver sti_hdmi_driver = { + .driver = { + .name = "sti-hdmi", + .owner = THIS_MODULE, + .of_match_table = hdmi_of_match, + }, + .probe = sti_hdmi_probe, + .remove = sti_hdmi_remove, +}; + +module_platform_driver(sti_hdmi_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h new file mode 100644 index 0000000..61bec65 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Vincent Abriou for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_HDMI_H_ +#define _STI_HDMI_H_ + +#include + +#include + +#define HDMI_STA 0x0010 +#define HDMI_STA_DLL_LCK BIT(5) + +struct sti_hdmi; + +struct hdmi_phy_ops { + bool (*start)(struct sti_hdmi *hdmi); + void (*stop)(struct sti_hdmi *hdmi); +}; + +/** + * STI hdmi structure + * + * @dev: driver device + * @drm_dev: pointer to drm device + * @mode: current display mode selected + * @regs: hdmi register + * @syscfg: syscfg register for pll rejection configuration + * @clk_pix: hdmi pixel clock + * @clk_tmds: hdmi tmds clock + * @clk_phy: hdmi phy clock + * @clk_audio: hdmi audio clock + * @irq: hdmi interrupt number + * @irq_status: interrupt status register + * @phy_ops: phy start/stop operations + * @enabled: true if hdmi is enabled else false + * @hpd_gpio: hdmi hot plug detect gpio number + * @hpd: hot plug detect status + * @wait_event: wait event + * @event_received: wait event status + * @reset: reset control of the hdmi phy + */ +struct sti_hdmi { + struct device dev; + struct drm_device *drm_dev; + struct drm_display_mode mode; + void __iomem *regs; + void __iomem *syscfg; + struct clk *clk_pix; + struct clk *clk_tmds; + struct clk *clk_phy; + struct clk *clk_audio; + int irq; + u32 irq_status; + struct hdmi_phy_ops *phy_ops; + bool enabled; + int hpd_gpio; + bool hpd; + wait_queue_head_t wait_event; + bool event_received; + struct reset_control *reset; +}; + +u32 hdmi_read(struct sti_hdmi *hdmi, int offset); +void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset); + +/** + * hdmi phy config structure + * + * A pointer to an array of these structures is passed to a TMDS (HDMI) output + * via the control interface to provide board and SoC specific + * configurations of the HDMI PHY. Each entry in the array specifies a hardware + * specific configuration for a given TMDS clock frequency range. + * + * @min_tmds_freq: Lower bound of TMDS clock frequency this entry applies to + * @max_tmds_freq: Upper bound of TMDS clock frequency this entry applies to + * @config: SoC specific register configuration + */ +struct hdmi_phy_config { + u32 min_tmds_freq; + u32 max_tmds_freq; + u32 config[4]; +}; + +#endif diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c new file mode 100644 index 0000000..49ae8e4 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Vincent Abriou for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "sti_hdmi_tx3g0c55phy.h" + +#define HDMI_SRZ_PLL_CFG 0x0504 +#define HDMI_SRZ_TAP_1 0x0508 +#define HDMI_SRZ_TAP_2 0x050C +#define HDMI_SRZ_TAP_3 0x0510 +#define HDMI_SRZ_CTRL 0x0514 + +#define HDMI_SRZ_PLL_CFG_POWER_DOWN BIT(0) +#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT 1 +#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ 0 +#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ 1 +#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ 2 +#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ 3 +#define HDMI_SRZ_PLL_CFG_VCOR_MASK 3 +#define HDMI_SRZ_PLL_CFG_VCOR(x) (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT) +#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT 8 +#define HDMI_SRZ_PLL_CFG_NDIV_MASK (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT) +#define HDMI_SRZ_PLL_CFG_MODE_SHIFT 16 +#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ 0x1 +#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ 0x4 +#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ 0x5 +#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6 +#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ 0x7 +#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ 0x8 +#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ 0x9 +#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA +#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ 0xB +#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ 0xC +#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ 0xD +#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE +#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ 0xF +#define HDMI_SRZ_PLL_CFG_MODE_MASK 0xF +#define HDMI_SRZ_PLL_CFG_MODE(x) (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT) + +#define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0) +#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1) + +/* sysconf registers */ +#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */ +#define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */ + +#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0 +#define REJECTION_PLL_HDMI_ENABLE_MASK (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT) +#define REJECTION_PLL_HDMI_PDIV_SHIFT 24 +#define REJECTION_PLL_HDMI_PDIV_MASK (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT) +#define REJECTION_PLL_HDMI_NDIV_SHIFT 16 +#define REJECTION_PLL_HDMI_NDIV_MASK (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT) +#define REJECTION_PLL_HDMI_MDIV_SHIFT 8 +#define REJECTION_PLL_HDMI_MDIV_MASK (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT) + +#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0) + +#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ + +/** + * pll mode structure + * + * A pointer to an array of these structures is passed to a TMDS (HDMI) output + * via the control interface to provide board and SoC specific + * configurations of the HDMI PHY. Each entry in the array specifies a hardware + * specific configuration for a given TMDS clock frequency range. The array + * should be terminated with an entry that has all fields set to zero. + * + * @min: Lower bound of TMDS clock frequency this entry applies to + * @max: Upper bound of TMDS clock frequency this entry applies to + * @mode: SoC specific register configuration + */ +struct pllmode { + u32 min; + u32 max; + u32 mode; +}; + +#define NB_PLL_MODE 7 +static struct pllmode pllmodes[NB_PLL_MODE] = { + {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ}, + {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ}, + {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ}, + {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ}, + {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ}, + {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ}, + {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ} +}; + +#define NB_HDMI_PHY_CONFIG 5 +static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { + {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} }, + {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} }, + {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} }, + {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} }, + {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} }, +}; + +#define PLL_CHANGE_DELAY 1 /* ms */ + +/** + * Disable the pll rejection + * + * @hdmi: pointer on the hdmi internal structure + * + * return true if the pll has been disabled + */ +static bool disable_pll_rejection(struct sti_hdmi *hdmi) +{ + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); + val &= ~REJECTION_PLL_HDMI_ENABLE_MASK; + writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); + + msleep(PLL_CHANGE_DELAY); + val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); + + return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); +} + +/** + * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL + * clock input to the new PHY PLL that generates the serializer clock + * (TMDS*10) and the TMDS clock which is now fed back into the HDMI + * formatter instead of the TMDS clock line from ClockGenB. + * + * @hdmi: pointer on the hdmi internal structure + * + * return true if pll has been correctly set + */ +static bool enable_pll_rejection(struct sti_hdmi *hdmi) +{ + unsigned int inputclock; + u32 mdiv, ndiv, pdiv, val; + + DRM_DEBUG_DRIVER("\n"); + + if (!disable_pll_rejection(hdmi)) + return false; + + inputclock = hdmi->mode.clock * 1000; + + DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock); + + + /* Power up the HDMI rejection PLL + * Note: On this SoC (stiH416) we are forced to have the input clock + * be equal to the HDMI pixel clock. + * + * The values here have been suggested by validation however they are + * still provisional and subject to change. + * + * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv) + */ + if (inputclock < 50000000) { + /* + * For slower clocks we need to multiply more to keep the + * internal VCO frequency within the physical specification + * of the PLL. + */ + pdiv = 4; + ndiv = 240; + mdiv = 30; + } else { + pdiv = 2; + ndiv = 60; + mdiv = 30; + } + + val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); + + val &= ~(REJECTION_PLL_HDMI_PDIV_MASK | + REJECTION_PLL_HDMI_NDIV_MASK | + REJECTION_PLL_HDMI_MDIV_MASK | + REJECTION_PLL_HDMI_ENABLE_MASK); + + val |= (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) | + (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) | + (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) | + (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT); + + writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); + + msleep(PLL_CHANGE_DELAY); + val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); + + return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); +} + +/** + * Start hdmi phy macro cell tx3g0c55 + * + * @hdmi: pointer on the hdmi internal structure + * + * Return false if an error occur + */ +static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi) +{ + u32 ckpxpll = hdmi->mode.clock * 1000; + u32 val, tmdsck, freqvco, pllctrl = 0; + unsigned int i; + + if (!enable_pll_rejection(hdmi)) + return false; + + DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); + + /* Assuming no pixel repetition and 24bits color */ + tmdsck = ckpxpll; + pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT; + + /* + * Setup the PLL mode parameter based on the ckpxpll. If we haven't got + * a clock frequency supported by one of the specific PLL modes then we + * will end up using the generic mode (0) which only supports a 10x + * multiplier, hence only 24bit color. + */ + for (i = 0; i < NB_PLL_MODE; i++) { + if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max) + pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode); + } + + freqvco = tmdsck * 10; + if (freqvco <= 425000000UL) + pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ); + else if (freqvco <= 850000000UL) + pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ); + else if (freqvco <= 1700000000UL) + pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ); + else if (freqvco <= 2970000000UL) + pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ); + else { + DRM_ERROR("PHY serializer clock out of range\n"); + goto err; + } + + /* + * Configure and power up the PHY PLL + */ + hdmi->event_received = false; + DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); + hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG); + + /* wait PLL interrupt */ + wait_event_interruptible_timeout(hdmi->wait_event, + hdmi->event_received == true, + msecs_to_jiffies + (HDMI_TIMEOUT_PLL_LOCK)); + + if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { + DRM_ERROR("hdmi phy pll not locked\n"); + goto err; + } + + DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); + + /* + * To configure the source termination and pre-emphasis appropriately + * for different high speed TMDS clock frequencies a phy configuration + * table must be provided, tailored to the SoC and board combination. + */ + for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { + if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && + (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { + val = hdmiphy_config[i].config[0]; + hdmi_write(hdmi, val, HDMI_SRZ_TAP_1); + val = hdmiphy_config[i].config[1]; + hdmi_write(hdmi, val, HDMI_SRZ_TAP_2); + val = hdmiphy_config[i].config[2]; + hdmi_write(hdmi, val, HDMI_SRZ_TAP_3); + val = hdmiphy_config[i].config[3]; + val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN; + val &= ~HDMI_SRZ_CTRL_POWER_DOWN; + hdmi_write(hdmi, val, HDMI_SRZ_CTRL); + + DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n", + hdmiphy_config[i].config[0], + hdmiphy_config[i].config[1], + hdmiphy_config[i].config[2], + hdmiphy_config[i].config[3]); + return true; + } + } + + /* + * Default, power up the serializer with no pre-emphasis or source + * termination. + */ + hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1); + hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2); + hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3); + hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL); + + return true; + +err: + disable_pll_rejection(hdmi); + + return false; +} + +/** + * Stop hdmi phy macro cell tx3g0c55 + * + * @hdmi: pointer on the hdmi internal structure + */ +static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi) +{ + DRM_DEBUG_DRIVER("\n"); + + hdmi->event_received = false; + + hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL); + hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG); + + /* wait PLL interrupt */ + wait_event_interruptible_timeout(hdmi->wait_event, + hdmi->event_received == true, + msecs_to_jiffies + (HDMI_TIMEOUT_PLL_LOCK)); + + if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) + DRM_ERROR("hdmi phy pll not well disabled\n"); + + disable_pll_rejection(hdmi); +} + +struct hdmi_phy_ops tx3g0c55phy_ops = { + .start = sti_hdmi_tx3g0c55phy_start, + .stop = sti_hdmi_tx3g0c55phy_stop, +}; diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h new file mode 100644 index 0000000..068237b --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_HDMI_TX3G0C55PHY_H_ +#define _STI_HDMI_TX3G0C55PHY_H_ + +#include "sti_hdmi.h" + +extern struct hdmi_phy_ops tx3g0c55phy_ops; + +#endif diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c new file mode 100644 index 0000000..8e0ceb0 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Vincent Abriou for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "sti_hdmi_tx3g4c28phy.h" + +#define HDMI_SRZ_CFG 0x504 +#define HDMI_SRZ_PLL_CFG 0x510 +#define HDMI_SRZ_ICNTL 0x518 +#define HDMI_SRZ_CALCODE_EXT 0x520 + +#define HDMI_SRZ_CFG_EN BIT(0) +#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1) +#define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16) +#define HDMI_SRZ_CFG_RBIAS_EXT BIT(17) +#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18) +#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19) +#define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24) + +#define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \ + HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \ + HDMI_SRZ_CFG_EXTERNAL_DATA | \ + HDMI_SRZ_CFG_RBIAS_EXT | \ + HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \ + HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \ + HDMI_SRZ_CFG_EN_SRC_TERMINATION) + +#define PLL_CFG_EN BIT(0) +#define PLL_CFG_NDIV_SHIFT (8) +#define PLL_CFG_IDF_SHIFT (16) +#define PLL_CFG_ODF_SHIFT (24) + +#define ODF_DIV_1 (0) +#define ODF_DIV_2 (1) +#define ODF_DIV_4 (2) +#define ODF_DIV_8 (3) + +#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ + +struct plldividers_s { + uint32_t min; + uint32_t max; + uint32_t idf; + uint32_t odf; +}; + +/* + * Functional specification recommended values + */ +#define NB_PLL_MODE 5 +static struct plldividers_s plldividers[NB_PLL_MODE] = { + {0, 20000000, 1, ODF_DIV_8}, + {20000000, 42500000, 2, ODF_DIV_8}, + {42500000, 85000000, 4, ODF_DIV_4}, + {85000000, 170000000, 8, ODF_DIV_2}, + {170000000, 340000000, 16, ODF_DIV_1} +}; + +#define NB_HDMI_PHY_CONFIG 2 +static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { + {0, 250000000, {0x0, 0x0, 0x0, 0x0} }, + {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} }, +}; + +/** + * Start hdmi phy macro cell tx3g4c28 + * + * @hdmi: pointer on the hdmi internal structure + * + * Return false if an error occur + */ +static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi) +{ + u32 ckpxpll = hdmi->mode.clock * 1000; + u32 val, tmdsck, idf, odf, pllctrl = 0; + bool foundplldivides = false; + int i; + + DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); + + for (i = 0; i < NB_PLL_MODE; i++) { + if (ckpxpll >= plldividers[i].min && + ckpxpll < plldividers[i].max) { + idf = plldividers[i].idf; + odf = plldividers[i].odf; + foundplldivides = true; + break; + } + } + + if (!foundplldivides) { + DRM_ERROR("input TMDS clock speed (%d) not supported\n", + ckpxpll); + goto err; + } + + /* Assuming no pixel repetition and 24bits color */ + tmdsck = ckpxpll; + pllctrl |= 40 << PLL_CFG_NDIV_SHIFT; + + if (tmdsck > 340000000) { + DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck); + goto err; + } + + pllctrl |= idf << PLL_CFG_IDF_SHIFT; + pllctrl |= odf << PLL_CFG_ODF_SHIFT; + + /* + * Configure and power up the PHY PLL + */ + hdmi->event_received = false; + DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); + hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG); + + /* wait PLL interrupt */ + wait_event_interruptible_timeout(hdmi->wait_event, + hdmi->event_received == true, + msecs_to_jiffies + (HDMI_TIMEOUT_PLL_LOCK)); + + if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { + DRM_ERROR("hdmi phy pll not locked\n"); + goto err; + } + + DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); + + val = (HDMI_SRZ_CFG_EN | + HDMI_SRZ_CFG_EXTERNAL_DATA | + HDMI_SRZ_CFG_EN_BIASRES_DETECTION | + HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION); + + if (tmdsck > 165000000) + val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION; + + /* + * To configure the source termination and pre-emphasis appropriately + * for different high speed TMDS clock frequencies a phy configuration + * table must be provided, tailored to the SoC and board combination. + */ + for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { + if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && + (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { + val |= (hdmiphy_config[i].config[0] + & ~HDMI_SRZ_CFG_INTERNAL_MASK); + hdmi_write(hdmi, val, HDMI_SRZ_CFG); + + val = hdmiphy_config[i].config[1]; + hdmi_write(hdmi, val, HDMI_SRZ_ICNTL); + + val = hdmiphy_config[i].config[2]; + hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT); + + DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n", + hdmiphy_config[i].config[0], + hdmiphy_config[i].config[1], + hdmiphy_config[i].config[2]); + return true; + } + } + + /* + * Default, power up the serializer with no pre-emphasis or + * output swing correction + */ + hdmi_write(hdmi, val, HDMI_SRZ_CFG); + hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL); + hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT); + + return true; + +err: + return false; +} + +/** + * Stop hdmi phy macro cell tx3g4c28 + * + * @hdmi: pointer on the hdmi internal structure + */ +static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi) +{ + int val = 0; + + DRM_DEBUG_DRIVER("\n"); + + hdmi->event_received = false; + + val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION; + val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION; + + hdmi_write(hdmi, val, HDMI_SRZ_CFG); + hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG); + + /* wait PLL interrupt */ + wait_event_interruptible_timeout(hdmi->wait_event, + hdmi->event_received == true, + msecs_to_jiffies + (HDMI_TIMEOUT_PLL_LOCK)); + + if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) + DRM_ERROR("hdmi phy pll not well disabled\n"); +} + +struct hdmi_phy_ops tx3g4c28phy_ops = { + .start = sti_hdmi_tx3g4c28phy_start, + .stop = sti_hdmi_tx3g4c28phy_stop, +}; diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h new file mode 100644 index 0000000..f99a7ff --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_HDMI_TX3G4C28PHY_H_ +#define _STI_HDMI_TX3G4C28PHY_H_ + +#include "sti_hdmi.h" + +extern struct hdmi_phy_ops tx3g4c28phy_ops; + +#endif -- cgit v0.10.2 From c86a5f6e1b9c84064231ec867aca281d9e8514a2 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 19:25:30 +0200 Subject: drm: sti: add HDA driver Add driver to support analog TV ouput. HDA driver is mapped on drm_bridge and drm_connector structures. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 12e1a07..4cf0c55 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -5,4 +5,5 @@ stihdmi-y := sti_hdmi.o \ obj-$(CONFIG_DRM_STI) = \ sti_vtg.o \ sti_vtac.o \ - stihdmi.o \ No newline at end of file + stihdmi.o \ + sti_hda.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c new file mode 100644 index 0000000..72d957f --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -0,0 +1,794 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include + +#include +#include + +/* HDformatter registers */ +#define HDA_ANA_CFG 0x0000 +#define HDA_ANA_SCALE_CTRL_Y 0x0004 +#define HDA_ANA_SCALE_CTRL_CB 0x0008 +#define HDA_ANA_SCALE_CTRL_CR 0x000C +#define HDA_ANA_ANC_CTRL 0x0010 +#define HDA_ANA_SRC_Y_CFG 0x0014 +#define HDA_COEFF_Y_PH1_TAP123 0x0018 +#define HDA_COEFF_Y_PH1_TAP456 0x001C +#define HDA_COEFF_Y_PH2_TAP123 0x0020 +#define HDA_COEFF_Y_PH2_TAP456 0x0024 +#define HDA_COEFF_Y_PH3_TAP123 0x0028 +#define HDA_COEFF_Y_PH3_TAP456 0x002C +#define HDA_COEFF_Y_PH4_TAP123 0x0030 +#define HDA_COEFF_Y_PH4_TAP456 0x0034 +#define HDA_ANA_SRC_C_CFG 0x0040 +#define HDA_COEFF_C_PH1_TAP123 0x0044 +#define HDA_COEFF_C_PH1_TAP456 0x0048 +#define HDA_COEFF_C_PH2_TAP123 0x004C +#define HDA_COEFF_C_PH2_TAP456 0x0050 +#define HDA_COEFF_C_PH3_TAP123 0x0054 +#define HDA_COEFF_C_PH3_TAP456 0x0058 +#define HDA_COEFF_C_PH4_TAP123 0x005C +#define HDA_COEFF_C_PH4_TAP456 0x0060 +#define HDA_SYNC_AWGI 0x0300 + +/* HDA_ANA_CFG */ +#define CFG_AWG_ASYNC_EN BIT(0) +#define CFG_AWG_ASYNC_HSYNC_MTD BIT(1) +#define CFG_AWG_ASYNC_VSYNC_MTD BIT(2) +#define CFG_AWG_SYNC_DEL BIT(3) +#define CFG_AWG_FLTR_MODE_SHIFT 4 +#define CFG_AWG_FLTR_MODE_MASK (0xF << CFG_AWG_FLTR_MODE_SHIFT) +#define CFG_AWG_FLTR_MODE_SD (0 << CFG_AWG_FLTR_MODE_SHIFT) +#define CFG_AWG_FLTR_MODE_ED (1 << CFG_AWG_FLTR_MODE_SHIFT) +#define CFG_AWG_FLTR_MODE_HD (2 << CFG_AWG_FLTR_MODE_SHIFT) +#define CFG_SYNC_ON_PBPR_MASK BIT(8) +#define CFG_PREFILTER_EN_MASK BIT(9) +#define CFG_PBPR_SYNC_OFF_SHIFT 16 +#define CFG_PBPR_SYNC_OFF_MASK (0x7FF << CFG_PBPR_SYNC_OFF_SHIFT) +#define CFG_PBPR_SYNC_OFF_VAL 0x117 /* Voltage dependent. stiH416 */ + +/* Default scaling values */ +#define SCALE_CTRL_Y_DFLT 0x00C50256 +#define SCALE_CTRL_CB_DFLT 0x00DB0249 +#define SCALE_CTRL_CR_DFLT 0x00DB0249 + +/* Video DACs control */ +#define VIDEO_DACS_CONTROL_MASK 0x0FFF +#define VIDEO_DACS_CONTROL_SYSCFG2535 0x085C /* for stih416 */ +#define DAC_CFG_HD_OFF_SHIFT 5 +#define DAC_CFG_HD_OFF_MASK (0x7 << DAC_CFG_HD_OFF_SHIFT) +#define VIDEO_DACS_CONTROL_SYSCFG5072 0x0120 /* for stih407 */ +#define DAC_CFG_HD_HZUVW_OFF_MASK BIT(1) + + +/* Upsampler values for the alternative 2X Filter */ +#define SAMPLER_COEF_NB 8 +#define HDA_ANA_SRC_Y_CFG_ALT_2X 0x01130000 +static u32 coef_y_alt_2x[] = { + 0x00FE83FB, 0x1F900401, 0x00000000, 0x00000000, + 0x00F408F9, 0x055F7C25, 0x00000000, 0x00000000 +}; + +#define HDA_ANA_SRC_C_CFG_ALT_2X 0x01750004 +static u32 coef_c_alt_2x[] = { + 0x001305F7, 0x05274BD0, 0x00000000, 0x00000000, + 0x0004907C, 0x09C80B9D, 0x00000000, 0x00000000 +}; + +/* Upsampler values for the 4X Filter */ +#define HDA_ANA_SRC_Y_CFG_4X 0x01ED0005 +#define HDA_ANA_SRC_C_CFG_4X 0x01ED0004 +static u32 coef_yc_4x[] = { + 0x00FC827F, 0x008FE20B, 0x00F684FC, 0x050F7C24, + 0x00F4857C, 0x0A1F402E, 0x00FA027F, 0x0E076E1D +}; + +/* AWG instructions for some video modes */ +#define AWG_MAX_INST 64 + +/* 720p@50 */ +static u32 AWGi_720p_50[] = { + 0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA, + 0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B, + 0x00000D8E, 0x00000104, 0x00001804, 0x00000971, + 0x00000C26, 0x0000003B, 0x00000FB4, 0x00000FB5, + 0x00000104, 0x00001AE8 +}; + +#define NN_720p_50 ARRAY_SIZE(AWGi_720p_50) + +/* 720p@60 */ +static u32 AWGi_720p_60[] = { + 0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA, + 0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B, + 0x00000C44, 0x00000104, 0x00001804, 0x00000971, + 0x00000C26, 0x0000003B, 0x00000F0F, 0x00000F10, + 0x00000104, 0x00001AE8 +}; + +#define NN_720p_60 ARRAY_SIZE(AWGi_720p_60) + +/* 1080p@30 */ +static u32 AWGi_1080p_30[] = { + 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56, + 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B, + 0x00000C2A, 0x00000104, 0x00001804, 0x00000971, + 0x00000C2A, 0x0000003B, 0x00000EBE, 0x00000EBF, + 0x00000EBF, 0x00000104, 0x00001A2F, 0x00001C4B, + 0x00001C52 +}; + +#define NN_1080p_30 ARRAY_SIZE(AWGi_1080p_30) + +/* 1080p@25 */ +static u32 AWGi_1080p_25[] = { + 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56, + 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B, + 0x00000DE2, 0x00000104, 0x00001804, 0x00000971, + 0x00000C2A, 0x0000003B, 0x00000F51, 0x00000F51, + 0x00000F52, 0x00000104, 0x00001A2F, 0x00001C4B, + 0x00001C52 +}; + +#define NN_1080p_25 ARRAY_SIZE(AWGi_1080p_25) + +/* 1080p@24 */ +static u32 AWGi_1080p_24[] = { + 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56, + 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B, + 0x00000E50, 0x00000104, 0x00001804, 0x00000971, + 0x00000C2A, 0x0000003B, 0x00000F76, 0x00000F76, + 0x00000F76, 0x00000104, 0x00001A2F, 0x00001C4B, + 0x00001C52 +}; + +#define NN_1080p_24 ARRAY_SIZE(AWGi_1080p_24) + +/* 720x480p@60 */ +static u32 AWGi_720x480p_60[] = { + 0x00000904, 0x00000F18, 0x0000013B, 0x00001805, + 0x00000904, 0x00000C3D, 0x0000003B, 0x00001A06 +}; + +#define NN_720x480p_60 ARRAY_SIZE(AWGi_720x480p_60) + +/* Video mode category */ +enum sti_hda_vid_cat { + VID_SD, + VID_ED, + VID_HD_74M, + VID_HD_148M +}; + +struct sti_hda_video_config { + struct drm_display_mode mode; + u32 *awg_instr; + int nb_instr; + enum sti_hda_vid_cat vid_cat; +}; + +/* HD analog supported modes + * Interlaced modes may be added when supported by the whole display chain + */ +static const struct sti_hda_video_config hda_supported_modes[] = { + /* 1080p30 74.250Mhz */ + {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_1080p_30, NN_1080p_30, VID_HD_74M}, + /* 1080p30 74.176Mhz */ + {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_1080p_30, NN_1080p_30, VID_HD_74M}, + /* 1080p24 74.250Mhz */ + {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, + 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_1080p_24, NN_1080p_24, VID_HD_74M}, + /* 1080p24 74.176Mhz */ + {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2558, + 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_1080p_24, NN_1080p_24, VID_HD_74M}, + /* 1080p25 74.250Mhz */ + {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_1080p_25, NN_1080p_25, VID_HD_74M}, + /* 720p60 74.250Mhz */ + {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_720p_60, NN_720p_60, VID_HD_74M}, + /* 720p60 74.176Mhz */ + {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74176, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_720p_60, NN_720p_60, VID_HD_74M}, + /* 720p50 74.250Mhz */ + {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, + 1760, 1980, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)}, + AWGi_720p_50, NN_720p_50, VID_HD_74M}, + /* 720x480p60 27.027Mhz */ + {{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27027, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)}, + AWGi_720x480p_60, NN_720x480p_60, VID_ED}, + /* 720x480p60 27.000Mhz */ + {{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)}, + AWGi_720x480p_60, NN_720x480p_60, VID_ED} +}; + +/** + * STI hd analog structure + * + * @dev: driver device + * @drm_dev: pointer to drm device + * @mode: current display mode selected + * @regs: HD analog register + * @video_dacs_ctrl: video DACS control register + * @enabled: true if HD analog is enabled else false + */ +struct sti_hda { + struct device dev; + struct drm_device *drm_dev; + struct drm_display_mode mode; + void __iomem *regs; + void __iomem *video_dacs_ctrl; + struct clk *clk_pix; + struct clk *clk_hddac; + bool enabled; +}; + +struct sti_hda_connector { + struct drm_connector drm_connector; + struct drm_encoder *encoder; + struct sti_hda *hda; +}; + +#define to_sti_hda_connector(x) \ + container_of(x, struct sti_hda_connector, drm_connector) + +static u32 hda_read(struct sti_hda *hda, int offset) +{ + return readl(hda->regs + offset); +} + +static void hda_write(struct sti_hda *hda, u32 val, int offset) +{ + writel(val, hda->regs + offset); +} + +/** + * Search for a video mode in the supported modes table + * + * @mode: mode being searched + * @idx: index of the found mode + * + * Return true if mode is found + */ +static bool hda_get_mode_idx(struct drm_display_mode mode, int *idx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++) + if (drm_mode_equal(&hda_supported_modes[i].mode, &mode)) { + *idx = i; + return true; + } + return false; +} + +/** + * Enable the HD DACS + * + * @hda: pointer to HD analog structure + * @enable: true if HD DACS need to be enabled, else false + */ +static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable) +{ + u32 mask; + + if (hda->video_dacs_ctrl) { + u32 val; + + switch ((u32)hda->video_dacs_ctrl & VIDEO_DACS_CONTROL_MASK) { + case VIDEO_DACS_CONTROL_SYSCFG2535: + mask = DAC_CFG_HD_OFF_MASK; + break; + case VIDEO_DACS_CONTROL_SYSCFG5072: + mask = DAC_CFG_HD_HZUVW_OFF_MASK; + break; + default: + DRM_INFO("Video DACS control register not supported!"); + return; + } + + val = readl(hda->video_dacs_ctrl); + if (enable) + val &= ~mask; + else + val |= mask; + + writel(val, hda->video_dacs_ctrl); + } +} + +/** + * Configure AWG, writing instructions + * + * @hda: pointer to HD analog structure + * @awg_instr: pointer to AWG instructions table + * @nb: nb of AWG instructions + */ +static void sti_hda_configure_awg(struct sti_hda *hda, u32 *awg_instr, int nb) +{ + unsigned int i; + + DRM_DEBUG_DRIVER("\n"); + + for (i = 0; i < nb; i++) + hda_write(hda, awg_instr[i], HDA_SYNC_AWGI + i * 4); + for (i = nb; i < AWG_MAX_INST; i++) + hda_write(hda, 0, HDA_SYNC_AWGI + i * 4); +} + +static void sti_hda_disable(struct drm_bridge *bridge) +{ + struct sti_hda *hda = bridge->driver_private; + u32 val; + + if (!hda->enabled) + return; + + DRM_DEBUG_DRIVER("\n"); + + /* Disable HD DAC and AWG */ + val = hda_read(hda, HDA_ANA_CFG); + val &= ~CFG_AWG_ASYNC_EN; + hda_write(hda, val, HDA_ANA_CFG); + hda_write(hda, 0, HDA_ANA_ANC_CTRL); + + hda_enable_hd_dacs(hda, false); + + /* Disable/unprepare hda clock */ + clk_disable_unprepare(hda->clk_hddac); + clk_disable_unprepare(hda->clk_pix); + + hda->enabled = false; +} + +static void sti_hda_pre_enable(struct drm_bridge *bridge) +{ + struct sti_hda *hda = bridge->driver_private; + u32 val, i, mode_idx; + u32 src_filter_y, src_filter_c; + u32 *coef_y, *coef_c; + u32 filter_mode; + + DRM_DEBUG_DRIVER("\n"); + + if (hda->enabled) + return; + + /* Prepare/enable clocks */ + if (clk_prepare_enable(hda->clk_pix)) + DRM_ERROR("Failed to prepare/enable hda_pix clk\n"); + if (clk_prepare_enable(hda->clk_hddac)) + DRM_ERROR("Failed to prepare/enable hda_hddac clk\n"); + + if (!hda_get_mode_idx(hda->mode, &mode_idx)) { + DRM_ERROR("Undefined mode\n"); + return; + } + + switch (hda_supported_modes[mode_idx].vid_cat) { + case VID_HD_148M: + DRM_ERROR("Beyond HD analog capabilities\n"); + return; + case VID_HD_74M: + /* HD use alternate 2x filter */ + filter_mode = CFG_AWG_FLTR_MODE_HD; + src_filter_y = HDA_ANA_SRC_Y_CFG_ALT_2X; + src_filter_c = HDA_ANA_SRC_C_CFG_ALT_2X; + coef_y = coef_y_alt_2x; + coef_c = coef_c_alt_2x; + break; + case VID_ED: + /* ED uses 4x filter */ + filter_mode = CFG_AWG_FLTR_MODE_ED; + src_filter_y = HDA_ANA_SRC_Y_CFG_4X; + src_filter_c = HDA_ANA_SRC_C_CFG_4X; + coef_y = coef_yc_4x; + coef_c = coef_yc_4x; + break; + case VID_SD: + DRM_ERROR("Not supported\n"); + return; + default: + DRM_ERROR("Undefined resolution\n"); + return; + } + DRM_DEBUG_DRIVER("Using HDA mode #%d\n", mode_idx); + + /* Enable HD Video DACs */ + hda_enable_hd_dacs(hda, true); + + /* Configure scaler */ + hda_write(hda, SCALE_CTRL_Y_DFLT, HDA_ANA_SCALE_CTRL_Y); + hda_write(hda, SCALE_CTRL_CB_DFLT, HDA_ANA_SCALE_CTRL_CB); + hda_write(hda, SCALE_CTRL_CR_DFLT, HDA_ANA_SCALE_CTRL_CR); + + /* Configure sampler */ + hda_write(hda , src_filter_y, HDA_ANA_SRC_Y_CFG); + hda_write(hda, src_filter_c, HDA_ANA_SRC_C_CFG); + for (i = 0; i < SAMPLER_COEF_NB; i++) { + hda_write(hda, coef_y[i], HDA_COEFF_Y_PH1_TAP123 + i * 4); + hda_write(hda, coef_c[i], HDA_COEFF_C_PH1_TAP123 + i * 4); + } + + /* Configure main HDFormatter */ + val = 0; + val |= (hda->mode.flags & DRM_MODE_FLAG_INTERLACE) ? + 0 : CFG_AWG_ASYNC_VSYNC_MTD; + val |= (CFG_PBPR_SYNC_OFF_VAL << CFG_PBPR_SYNC_OFF_SHIFT); + val |= filter_mode; + hda_write(hda, val, HDA_ANA_CFG); + + /* Configure AWG */ + sti_hda_configure_awg(hda, hda_supported_modes[mode_idx].awg_instr, + hda_supported_modes[mode_idx].nb_instr); + + /* Enable AWG */ + val = hda_read(hda, HDA_ANA_CFG); + val |= CFG_AWG_ASYNC_EN; + hda_write(hda, val, HDA_ANA_CFG); + + hda->enabled = true; +} + +static void sti_hda_set_mode(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sti_hda *hda = bridge->driver_private; + u32 mode_idx; + int hddac_rate; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + memcpy(&hda->mode, mode, sizeof(struct drm_display_mode)); + + if (!hda_get_mode_idx(hda->mode, &mode_idx)) { + DRM_ERROR("Undefined mode\n"); + return; + } + + switch (hda_supported_modes[mode_idx].vid_cat) { + case VID_HD_74M: + /* HD use alternate 2x filter */ + hddac_rate = mode->clock * 1000 * 2; + break; + case VID_ED: + /* ED uses 4x filter */ + hddac_rate = mode->clock * 1000 * 4; + break; + default: + DRM_ERROR("Undefined mode\n"); + return; + } + + /* HD DAC = 148.5Mhz or 108 Mhz */ + ret = clk_set_rate(hda->clk_hddac, hddac_rate); + if (ret < 0) + DRM_ERROR("Cannot set rate (%dHz) for hda_hddac clk\n", + hddac_rate); + + /* HDformatter clock = compositor clock */ + ret = clk_set_rate(hda->clk_pix, mode->clock * 1000); + if (ret < 0) + DRM_ERROR("Cannot set rate (%dHz) for hda_pix clk\n", + mode->clock * 1000); +} + +static void sti_hda_bridge_nope(struct drm_bridge *bridge) +{ + /* do nothing */ +} + +static void sti_hda_brigde_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); + kfree(bridge); +} + +static const struct drm_bridge_funcs sti_hda_bridge_funcs = { + .pre_enable = sti_hda_pre_enable, + .enable = sti_hda_bridge_nope, + .disable = sti_hda_disable, + .post_disable = sti_hda_bridge_nope, + .mode_set = sti_hda_set_mode, + .destroy = sti_hda_brigde_destroy, +}; + +static int sti_hda_connector_get_modes(struct drm_connector *connector) +{ + unsigned int i; + int count = 0; + struct sti_hda_connector *hda_connector + = to_sti_hda_connector(connector); + struct sti_hda *hda = hda_connector->hda; + + DRM_DEBUG_DRIVER("\n"); + + for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++) { + struct drm_display_mode *mode = + drm_mode_duplicate(hda->drm_dev, + &hda_supported_modes[i].mode); + if (!mode) + continue; + mode->vrefresh = drm_mode_vrefresh(mode); + + /* the first mode is the preferred mode */ + if (i == 0) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + count++; + } + + drm_mode_sort(&connector->modes); + + return count; +} + +#define CLK_TOLERANCE_HZ 50 + +static int sti_hda_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int target = mode->clock * 1000; + int target_min = target - CLK_TOLERANCE_HZ; + int target_max = target + CLK_TOLERANCE_HZ; + int result; + int idx; + struct sti_hda_connector *hda_connector + = to_sti_hda_connector(connector); + struct sti_hda *hda = hda_connector->hda; + + if (!hda_get_mode_idx(*mode, &idx)) { + return MODE_BAD; + } else { + result = clk_round_rate(hda->clk_pix, target); + + DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n", + target, result); + + if ((result < target_min) || (result > target_max)) { + DRM_DEBUG_DRIVER("hda pixclk=%d not supported\n", + target); + return MODE_BAD; + } + } + + return MODE_OK; +} + +struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector) +{ + struct sti_hda_connector *hda_connector + = to_sti_hda_connector(connector); + + /* Best encoder is the one associated during connector creation */ + return hda_connector->encoder; +} + +static struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = { + .get_modes = sti_hda_connector_get_modes, + .mode_valid = sti_hda_connector_mode_valid, + .best_encoder = sti_hda_best_encoder, +}; + +static enum drm_connector_status +sti_hda_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void sti_hda_connector_destroy(struct drm_connector *connector) +{ + struct sti_hda_connector *hda_connector + = to_sti_hda_connector(connector); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(hda_connector); +} + +static struct drm_connector_funcs sti_hda_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = sti_hda_connector_detect, + .destroy = sti_hda_connector_destroy, +}; + +static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) + return encoder; + } + + return NULL; +} + +static int sti_hda_bind(struct device *dev, struct device *master, void *data) +{ + struct sti_hda *hda = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct drm_encoder *encoder; + struct sti_hda_connector *connector; + struct drm_connector *drm_connector; + struct drm_bridge *bridge; + int err; + + /* Set the drm device handle */ + hda->drm_dev = drm_dev; + + encoder = sti_hda_find_encoder(drm_dev); + if (!encoder) + return -ENOMEM; + + connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); + if (!connector) + return -ENOMEM; + + connector->hda = hda; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + bridge->driver_private = hda; + drm_bridge_init(drm_dev, bridge, &sti_hda_bridge_funcs); + + encoder->bridge = bridge; + connector->encoder = encoder; + + drm_connector = (struct drm_connector *)connector; + + drm_connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_init(drm_dev, drm_connector, + &sti_hda_connector_funcs, DRM_MODE_CONNECTOR_Component); + drm_connector_helper_add(drm_connector, + &sti_hda_connector_helper_funcs); + + err = drm_connector_register(drm_connector); + if (err) + goto err_connector; + + err = drm_mode_connector_attach_encoder(drm_connector, encoder); + if (err) { + DRM_ERROR("Failed to attach a connector to a encoder\n"); + goto err_sysfs; + } + + return 0; + +err_sysfs: + drm_connector_unregister(drm_connector); +err_connector: + drm_bridge_cleanup(bridge); + drm_connector_cleanup(drm_connector); + return -EINVAL; +} + +static void sti_hda_unbind(struct device *dev, + struct device *master, void *data) +{ + /* do nothing */ +} + +static const struct component_ops sti_hda_ops = { + .bind = sti_hda_bind, + .unbind = sti_hda_unbind, +}; + +static int sti_hda_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sti_hda *hda; + struct resource *res; + + DRM_INFO("%s\n", __func__); + + hda = devm_kzalloc(dev, sizeof(*hda), GFP_KERNEL); + if (!hda) + return -ENOMEM; + + hda->dev = pdev->dev; + + /* Get resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hda-reg"); + if (!res) { + DRM_ERROR("Invalid hda resource\n"); + return -ENOMEM; + } + hda->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (IS_ERR(hda->regs)) + return PTR_ERR(hda->regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "video-dacs-ctrl"); + if (res) { + hda->video_dacs_ctrl = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (IS_ERR(hda->video_dacs_ctrl)) + return PTR_ERR(hda->video_dacs_ctrl); + } else { + /* If no existing video-dacs-ctrl resource continue the probe */ + DRM_DEBUG_DRIVER("No video-dacs-ctrl resource\n"); + hda->video_dacs_ctrl = NULL; + } + + /* Get clock resources */ + hda->clk_pix = devm_clk_get(dev, "pix"); + if (IS_ERR(hda->clk_pix)) { + DRM_ERROR("Cannot get hda_pix clock\n"); + return PTR_ERR(hda->clk_pix); + } + + hda->clk_hddac = devm_clk_get(dev, "hddac"); + if (IS_ERR(hda->clk_hddac)) { + DRM_ERROR("Cannot get hda_hddac clock\n"); + return PTR_ERR(hda->clk_hddac); + } + + platform_set_drvdata(pdev, hda); + + return component_add(&pdev->dev, &sti_hda_ops); +} + +static int sti_hda_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sti_hda_ops); + return 0; +} + +static struct of_device_id hda_of_match[] = { + { .compatible = "st,stih416-hda", }, + { .compatible = "st,stih407-hda", }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, hda_of_match); + +struct platform_driver sti_hda_driver = { + .driver = { + .name = "sti-hda", + .owner = THIS_MODULE, + .of_match_table = hda_of_match, + }, + .probe = sti_hda_probe, + .remove = sti_hda_remove, +}; + +module_platform_driver(sti_hda_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From cdfbff788ec9d488cab6450a439c2421ef28b0a4 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 19:26:17 +0200 Subject: drm: sti: add TVOut driver TVout hardware block is responsible to dispatch the data flow coming from compositor block to any of the output (HDMI or Analog TV). It control when output are start/stop and configure according the require flow path. TVout is the parent of HDMI and HDA drivers and bind them at runtime. Tvout is mapped on drm_encoder structure. One encoder is created for each of the sub-devices and link to their connector/bridge Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 4cf0c55..5f6d96d 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_DRM_STI) = \ sti_vtg.o \ sti_vtac.o \ stihdmi.o \ - sti_hda.o \ No newline at end of file + sti_hda.o \ + sti_tvout.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c new file mode 100644 index 0000000..b69e26f --- /dev/null +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Vincent Abriou + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* glue registers */ +#define TVO_CSC_MAIN_M0 0x000 +#define TVO_CSC_MAIN_M1 0x004 +#define TVO_CSC_MAIN_M2 0x008 +#define TVO_CSC_MAIN_M3 0x00c +#define TVO_CSC_MAIN_M4 0x010 +#define TVO_CSC_MAIN_M5 0x014 +#define TVO_CSC_MAIN_M6 0x018 +#define TVO_CSC_MAIN_M7 0x01c +#define TVO_MAIN_IN_VID_FORMAT 0x030 +#define TVO_CSC_AUX_M0 0x100 +#define TVO_CSC_AUX_M1 0x104 +#define TVO_CSC_AUX_M2 0x108 +#define TVO_CSC_AUX_M3 0x10c +#define TVO_CSC_AUX_M4 0x110 +#define TVO_CSC_AUX_M5 0x114 +#define TVO_CSC_AUX_M6 0x118 +#define TVO_CSC_AUX_M7 0x11c +#define TVO_AUX_IN_VID_FORMAT 0x130 +#define TVO_VIP_HDF 0x400 +#define TVO_HD_SYNC_SEL 0x418 +#define TVO_HD_DAC_CFG_OFF 0x420 +#define TVO_VIP_HDMI 0x500 +#define TVO_HDMI_FORCE_COLOR_0 0x504 +#define TVO_HDMI_FORCE_COLOR_1 0x508 +#define TVO_HDMI_CLIP_VALUE_B_CB 0x50c +#define TVO_HDMI_CLIP_VALUE_Y_G 0x510 +#define TVO_HDMI_CLIP_VALUE_R_CR 0x514 +#define TVO_HDMI_SYNC_SEL 0x518 +#define TVO_HDMI_DFV_OBS 0x540 + +#define TVO_IN_FMT_SIGNED BIT(0) +#define TVO_SYNC_EXT BIT(4) + +#define TVO_VIP_REORDER_R_SHIFT 24 +#define TVO_VIP_REORDER_G_SHIFT 20 +#define TVO_VIP_REORDER_B_SHIFT 16 +#define TVO_VIP_REORDER_MASK 0x3 +#define TVO_VIP_REORDER_Y_G_SEL 0 +#define TVO_VIP_REORDER_CB_B_SEL 1 +#define TVO_VIP_REORDER_CR_R_SEL 2 + +#define TVO_VIP_CLIP_SHIFT 8 +#define TVO_VIP_CLIP_MASK 0x7 +#define TVO_VIP_CLIP_DISABLED 0 +#define TVO_VIP_CLIP_EAV_SAV 1 +#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2 +#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3 +#define TVO_VIP_CLIP_PROG_RANGE 4 + +#define TVO_VIP_RND_SHIFT 4 +#define TVO_VIP_RND_MASK 0x3 +#define TVO_VIP_RND_8BIT_ROUNDED 0 +#define TVO_VIP_RND_10BIT_ROUNDED 1 +#define TVO_VIP_RND_12BIT_ROUNDED 2 + +#define TVO_VIP_SEL_INPUT_MASK 0xf +#define TVO_VIP_SEL_INPUT_MAIN 0x0 +#define TVO_VIP_SEL_INPUT_AUX 0x8 +#define TVO_VIP_SEL_INPUT_FORCE_COLOR 0xf +#define TVO_VIP_SEL_INPUT_BYPASS_MASK 0x1 +#define TVO_VIP_SEL_INPUT_BYPASSED 1 + +#define TVO_SYNC_MAIN_VTG_SET_REF 0x00 +#define TVO_SYNC_MAIN_VTG_SET_1 0x01 +#define TVO_SYNC_MAIN_VTG_SET_2 0x02 +#define TVO_SYNC_MAIN_VTG_SET_3 0x03 +#define TVO_SYNC_MAIN_VTG_SET_4 0x04 +#define TVO_SYNC_MAIN_VTG_SET_5 0x05 +#define TVO_SYNC_MAIN_VTG_SET_6 0x06 +#define TVO_SYNC_AUX_VTG_SET_REF 0x10 +#define TVO_SYNC_AUX_VTG_SET_1 0x11 +#define TVO_SYNC_AUX_VTG_SET_2 0x12 +#define TVO_SYNC_AUX_VTG_SET_3 0x13 +#define TVO_SYNC_AUX_VTG_SET_4 0x14 +#define TVO_SYNC_AUX_VTG_SET_5 0x15 +#define TVO_SYNC_AUX_VTG_SET_6 0x16 + +#define TVO_SYNC_HD_DCS_SHIFT 8 + +#define ENCODER_MAIN_CRTC_MASK BIT(0) + +/* enum listing the supported output data format */ +enum sti_tvout_video_out_type { + STI_TVOUT_VIDEO_OUT_RGB, + STI_TVOUT_VIDEO_OUT_YUV, +}; + +struct sti_tvout { + struct device *dev; + struct drm_device *drm_dev; + void __iomem *regs; + struct reset_control *reset; + struct drm_encoder *hdmi; + struct drm_encoder *hda; +}; + +struct sti_tvout_encoder { + struct drm_encoder encoder; + struct sti_tvout *tvout; +}; + +#define to_sti_tvout_encoder(x) \ + container_of(x, struct sti_tvout_encoder, encoder) + +#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout + +/* preformatter conversion matrix */ +static const u32 rgb_to_ycbcr_601[8] = { + 0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D, + 0x0000082E, 0x00002000, 0x00002000, 0x00000000 +}; + +/* 709 RGB to YCbCr */ +static const u32 rgb_to_ycbcr_709[8] = { + 0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20, + 0x0000082F, 0x00002000, 0x00002000, 0x00000000 +}; + +static u32 tvout_read(struct sti_tvout *tvout, int offset) +{ + return readl(tvout->regs + offset); +} + +static void tvout_write(struct sti_tvout *tvout, u32 val, int offset) +{ + writel(val, tvout->regs + offset); +} + +/** + * Set the clipping mode of a VIP + * + * @tvout: tvout structure + * @cr_r: + * @y_g: + * @cb_b: + */ +static void tvout_vip_set_color_order(struct sti_tvout *tvout, + u32 cr_r, u32 y_g, u32 cb_b) +{ + u32 val = tvout_read(tvout, TVO_VIP_HDMI); + + val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT); + val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT); + val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT); + val |= cr_r << TVO_VIP_REORDER_R_SHIFT; + val |= y_g << TVO_VIP_REORDER_G_SHIFT; + val |= cb_b << TVO_VIP_REORDER_B_SHIFT; + + tvout_write(tvout, val, TVO_VIP_HDMI); +} + +/** + * Set the clipping mode of a VIP + * + * @tvout: tvout structure + * @range: clipping range + */ +static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range) +{ + u32 val = tvout_read(tvout, TVO_VIP_HDMI); + + val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT); + val |= range << TVO_VIP_CLIP_SHIFT; + tvout_write(tvout, val, TVO_VIP_HDMI); +} + +/** + * Set the rounded value of a VIP + * + * @tvout: tvout structure + * @rnd: rounded val per component + */ +static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd) +{ + u32 val = tvout_read(tvout, TVO_VIP_HDMI); + + val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT); + val |= rnd << TVO_VIP_RND_SHIFT; + tvout_write(tvout, val, TVO_VIP_HDMI); +} + +/** + * Select the VIP input + * + * @tvout: tvout structure + * @sel_input: selected_input (main/aux + conv) + */ +static void tvout_vip_set_sel_input(struct sti_tvout *tvout, + bool main_path, + bool sel_input_logic_inverted, + enum sti_tvout_video_out_type video_out) +{ + u32 sel_input; + u32 val = tvout_read(tvout, TVO_VIP_HDMI); + + if (main_path) + sel_input = TVO_VIP_SEL_INPUT_MAIN; + else + sel_input = TVO_VIP_SEL_INPUT_AUX; + + switch (video_out) { + case STI_TVOUT_VIDEO_OUT_RGB: + sel_input |= TVO_VIP_SEL_INPUT_BYPASSED; + break; + case STI_TVOUT_VIDEO_OUT_YUV: + sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED; + break; + } + + /* on stih407 chip the sel_input bypass mode logic is inverted */ + if (sel_input_logic_inverted) + sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; + + val &= ~TVO_VIP_SEL_INPUT_MASK; + val |= sel_input; + tvout_write(tvout, val, TVO_VIP_HDMI); +} + +/** + * Select the input video signed or unsigned + * + * @tvout: tvout structure + * @in_vid_signed: used video input format + */ +static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt) +{ + u32 val = tvout_read(tvout, TVO_VIP_HDMI); + + val &= ~TVO_IN_FMT_SIGNED; + val |= in_vid_fmt; + tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT); +} + +/** + * Start VIP block for HDMI output + * + * @tvout: pointer on tvout structure + * @main_path: true if main path has to be used in the vip configuration + * else aux path is used. + */ +static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) +{ + struct device_node *node = tvout->dev->of_node; + bool sel_input_logic_inverted = false; + + dev_dbg(tvout->dev, "%s\n", __func__); + + if (main_path) { + DRM_DEBUG_DRIVER("main vip for hdmi\n"); + /* select the input sync for hdmi = VTG set 1 */ + tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); + } else { + DRM_DEBUG_DRIVER("aux vip for hdmi\n"); + /* select the input sync for hdmi = VTG set 1 */ + tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); + } + + /* set color channel order */ + tvout_vip_set_color_order(tvout, + TVO_VIP_REORDER_CR_R_SEL, + TVO_VIP_REORDER_Y_G_SEL, + TVO_VIP_REORDER_CB_B_SEL); + + /* set clipping mode (Limited range RGB/Y) */ + tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); + + /* set round mode (rounded to 8-bit per component) */ + tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED); + + if (of_device_is_compatible(node, "st,stih407-tvout")) { + /* set input video format */ + tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT, + TVO_IN_FMT_SIGNED); + sel_input_logic_inverted = true; + } + + /* input selection */ + tvout_vip_set_sel_input(tvout, main_path, + sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); +} + +/** + * Start HDF VIP and HD DAC + * + * @tvout: pointer on tvout structure + * @main_path: true if main path has to be used in the vip configuration + * else aux path is used. + */ +static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) +{ + struct device_node *node = tvout->dev->of_node; + bool sel_input_logic_inverted = false; + + dev_dbg(tvout->dev, "%s\n", __func__); + + if (!main_path) { + DRM_ERROR("HD Analog on aux not implemented\n"); + return; + } + + DRM_DEBUG_DRIVER("main vip for HDF\n"); + + /* set color channel order */ + tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF, + TVO_VIP_REORDER_CR_R_SEL, + TVO_VIP_REORDER_Y_G_SEL, + TVO_VIP_REORDER_CB_B_SEL); + + /* set clipping mode (Limited range RGB/Y) */ + tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF, + TVO_VIP_CLIP_LIMITED_RANGE_CB_CR); + + /* set round mode (rounded to 10-bit per component) */ + tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); + + if (of_device_is_compatible(node, "st,stih407-tvout")) { + /* set input video format */ + tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED); + sel_input_logic_inverted = true; + } + + /* Input selection */ + tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF, + main_path, + sel_input_logic_inverted, + STI_TVOUT_VIDEO_OUT_YUV); + + /* select the input sync for HD analog = VTG set 3 + * and HD DCS = VTG set 2 */ + tvout_write(tvout, + (TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT) + | TVO_SYNC_MAIN_VTG_SET_3, + TVO_HD_SYNC_SEL); + + /* power up HD DAC */ + tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); +} + +static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool sti_tvout_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void sti_tvout_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) +{ + struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); + + drm_encoder_cleanup(encoder); + kfree(sti_encoder); +} + +static const struct drm_encoder_funcs sti_tvout_encoder_funcs = { + .destroy = sti_tvout_encoder_destroy, +}; + +static void sti_hda_encoder_commit(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + + tvout_hda_start(tvout, true); +} + +static void sti_hda_encoder_disable(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + + /* reset VIP register */ + tvout_write(tvout, 0x0, TVO_VIP_HDF); + + /* power down HD DAC */ + tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF); +} + +static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { + .dpms = sti_tvout_encoder_dpms, + .mode_fixup = sti_tvout_encoder_mode_fixup, + .mode_set = sti_tvout_encoder_mode_set, + .prepare = sti_tvout_encoder_prepare, + .commit = sti_hda_encoder_commit, + .disable = sti_hda_encoder_disable, +}; + +static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, + struct sti_tvout *tvout) +{ + struct sti_tvout_encoder *encoder; + struct drm_encoder *drm_encoder; + + encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); + if (!encoder) + return NULL; + + encoder->tvout = tvout; + + drm_encoder = (struct drm_encoder *) encoder; + + drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK; + drm_encoder->possible_clones = 1 << 0; + + drm_encoder_init(dev, drm_encoder, + &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC); + + drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs); + + return drm_encoder; +} + +static void sti_hdmi_encoder_commit(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + + tvout_hdmi_start(tvout, true); +} + +static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct sti_tvout *tvout = to_sti_tvout(encoder); + + /* reset VIP register */ + tvout_write(tvout, 0x0, TVO_VIP_HDMI); +} + +static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { + .dpms = sti_tvout_encoder_dpms, + .mode_fixup = sti_tvout_encoder_mode_fixup, + .mode_set = sti_tvout_encoder_mode_set, + .prepare = sti_tvout_encoder_prepare, + .commit = sti_hdmi_encoder_commit, + .disable = sti_hdmi_encoder_disable, +}; + +static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev, + struct sti_tvout *tvout) +{ + struct sti_tvout_encoder *encoder; + struct drm_encoder *drm_encoder; + + encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); + if (!encoder) + return NULL; + + encoder->tvout = tvout; + + drm_encoder = (struct drm_encoder *) encoder; + + drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK; + drm_encoder->possible_clones = 1 << 1; + + drm_encoder_init(dev, drm_encoder, + &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs); + + return drm_encoder; +} + +static void sti_tvout_create_encoders(struct drm_device *dev, + struct sti_tvout *tvout) +{ + tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout); + tvout->hda = sti_tvout_create_hda_encoder(dev, tvout); +} + +static void sti_tvout_destroy_encoders(struct sti_tvout *tvout) +{ + if (tvout->hdmi) + drm_encoder_cleanup(tvout->hdmi); + tvout->hdmi = NULL; + + if (tvout->hda) + drm_encoder_cleanup(tvout->hda); + tvout->hda = NULL; +} + +static int sti_tvout_bind(struct device *dev, struct device *master, void *data) +{ + struct sti_tvout *tvout = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + unsigned int i; + int ret; + + tvout->drm_dev = drm_dev; + + /* set preformatter matrix */ + for (i = 0; i < 8; i++) { + tvout_write(tvout, rgb_to_ycbcr_601[i], + TVO_CSC_MAIN_M0 + (i * 4)); + tvout_write(tvout, rgb_to_ycbcr_601[i], + TVO_CSC_AUX_M0 + (i * 4)); + } + + sti_tvout_create_encoders(drm_dev, tvout); + + ret = component_bind_all(dev, drm_dev); + if (ret) + sti_tvout_destroy_encoders(tvout); + + return ret; +} + +static void sti_tvout_unbind(struct device *dev, struct device *master, + void *data) +{ + /* do nothing */ +} + +static const struct component_ops sti_tvout_ops = { + .bind = sti_tvout_bind, + .unbind = sti_tvout_unbind, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int sti_tvout_master_bind(struct device *dev) +{ + return 0; +} + +static void sti_tvout_master_unbind(struct device *dev) +{ + /* do nothing */ +} + +static const struct component_master_ops sti_tvout_master_ops = { + .bind = sti_tvout_master_bind, + .unbind = sti_tvout_master_unbind, +}; + +static int sti_tvout_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct sti_tvout *tvout; + struct resource *res; + struct device_node *child_np; + struct component_match *match = NULL; + + DRM_INFO("%s\n", __func__); + + if (!node) + return -ENODEV; + + tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL); + if (!tvout) + return -ENOMEM; + + tvout->dev = dev; + + /* get Memory ressources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg"); + if (!res) { + DRM_ERROR("Invalid glue resource\n"); + return -ENOMEM; + } + tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (IS_ERR(tvout->regs)) + return PTR_ERR(tvout->regs); + + /* get reset resources */ + tvout->reset = devm_reset_control_get(dev, "tvout"); + /* take tvout out of reset */ + if (!IS_ERR(tvout->reset)) + reset_control_deassert(tvout->reset); + + platform_set_drvdata(pdev, tvout); + + of_platform_populate(node, NULL, NULL, dev); + + child_np = of_get_next_available_child(node, NULL); + + while (child_np) { + component_match_add(dev, &match, compare_of, child_np); + of_node_put(child_np); + child_np = of_get_next_available_child(node, child_np); + } + + component_master_add_with_match(dev, &sti_tvout_master_ops, match); + + return component_add(dev, &sti_tvout_ops); +} + +static int sti_tvout_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &sti_tvout_master_ops); + component_del(&pdev->dev, &sti_tvout_ops); + return 0; +} + +static struct of_device_id tvout_of_match[] = { + { .compatible = "st,stih416-tvout", }, + { .compatible = "st,stih407-tvout", }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, tvout_of_match); + +struct platform_driver sti_tvout_driver = { + .driver = { + .name = "sti-tvout", + .owner = THIS_MODULE, + .of_match_table = tvout_of_match, + }, + .probe = sti_tvout_probe, + .remove = sti_tvout_remove, +}; + +module_platform_driver(sti_tvout_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From ba2d53fb6f16f1f854923a8c6fa452e235d608b0 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 18:48:35 +0200 Subject: drm: sti: add GDP layer Generic Display Pipeline are one of the compositor input sub-devices. GDP are dedicated to graphic input like RGB plans. GDP is part of Compositor hardware block which will be introduce later. A sti_layer structure is used to abstract GDP calls from Compositor. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 5f6d96d..7de757f 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,3 +1,6 @@ +sticompositor-y := \ + sti_gdp.o + stihdmi-y := sti_hdmi.o \ sti_hdmi_tx3g0c55phy.o \ sti_hdmi_tx3g4c28phy.o \ @@ -7,4 +10,5 @@ obj-$(CONFIG_DRM_STI) = \ sti_vtac.o \ stihdmi.o \ sti_hda.o \ - sti_tvout.o \ No newline at end of file + sti_tvout.o \ + sticompositor.o diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c new file mode 100644 index 0000000..7f55767 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -0,0 +1,516 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include + +#include "sti_gdp.h" +#include "sti_layer.h" +#include "sti_vtg.h" + +#define ENA_COLOR_FILL BIT(8) +#define WAIT_NEXT_VSYNC BIT(31) + +/* GDP color formats */ +#define GDP_RGB565 0x00 +#define GDP_RGB888 0x01 +#define GDP_RGB888_32 0x02 +#define GDP_ARGB8565 0x04 +#define GDP_ARGB8888 0x05 +#define GDP_ARGB1555 0x06 +#define GDP_ARGB4444 0x07 +#define GDP_CLUT8 0x0B +#define GDP_YCBR888 0x10 +#define GDP_YCBR422R 0x12 +#define GDP_AYCBR8888 0x15 + +#define GAM_GDP_CTL_OFFSET 0x00 +#define GAM_GDP_AGC_OFFSET 0x04 +#define GAM_GDP_VPO_OFFSET 0x0C +#define GAM_GDP_VPS_OFFSET 0x10 +#define GAM_GDP_PML_OFFSET 0x14 +#define GAM_GDP_PMP_OFFSET 0x18 +#define GAM_GDP_SIZE_OFFSET 0x1C +#define GAM_GDP_NVN_OFFSET 0x24 +#define GAM_GDP_KEY1_OFFSET 0x28 +#define GAM_GDP_KEY2_OFFSET 0x2C +#define GAM_GDP_PPT_OFFSET 0x34 +#define GAM_GDP_CML_OFFSET 0x3C +#define GAM_GDP_MST_OFFSET 0x68 + +#define GAM_GDP_ALPHARANGE_255 BIT(5) +#define GAM_GDP_AGC_FULL_RANGE 0x00808080 +#define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0)) +#define GAM_GDP_SIZE_MAX 0x7FF + +#define GDP_NODE_NB_BANK 2 +#define GDP_NODE_PER_FIELD 2 + +struct sti_gdp_node { + u32 gam_gdp_ctl; + u32 gam_gdp_agc; + u32 reserved1; + u32 gam_gdp_vpo; + u32 gam_gdp_vps; + u32 gam_gdp_pml; + u32 gam_gdp_pmp; + u32 gam_gdp_size; + u32 reserved2; + u32 gam_gdp_nvn; + u32 gam_gdp_key1; + u32 gam_gdp_key2; + u32 reserved3; + u32 gam_gdp_ppt; + u32 reserved4; + u32 gam_gdp_cml; +}; + +struct sti_gdp_node_list { + struct sti_gdp_node *top_field; + struct sti_gdp_node *btm_field; +}; + +/** + * STI GDP structure + * + * @layer: layer structure + * @clk_pix: pixel clock for the current gdp + * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification + * @is_curr_top: true if the current node processed is the top field + * @node_list: array of node list + */ +struct sti_gdp { + struct sti_layer layer; + struct clk *clk_pix; + struct notifier_block vtg_field_nb; + bool is_curr_top; + struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; +}; + +#define to_sti_gdp(x) container_of(x, struct sti_gdp, layer) + +static const uint32_t gdp_supported_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_AYUV, + DRM_FORMAT_YUV444, + DRM_FORMAT_VYUY, + DRM_FORMAT_C8, +}; + +static const uint32_t *sti_gdp_get_formats(struct sti_layer *layer) +{ + return gdp_supported_formats; +} + +static unsigned int sti_gdp_get_nb_formats(struct sti_layer *layer) +{ + return ARRAY_SIZE(gdp_supported_formats); +} + +static int sti_gdp_fourcc2format(int fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_XRGB8888: + return GDP_RGB888_32; + case DRM_FORMAT_ARGB8888: + return GDP_ARGB8888; + case DRM_FORMAT_ARGB4444: + return GDP_ARGB4444; + case DRM_FORMAT_ARGB1555: + return GDP_ARGB1555; + case DRM_FORMAT_RGB565: + return GDP_RGB565; + case DRM_FORMAT_RGB888: + return GDP_RGB888; + case DRM_FORMAT_AYUV: + return GDP_AYCBR8888; + case DRM_FORMAT_YUV444: + return GDP_YCBR888; + case DRM_FORMAT_VYUY: + return GDP_YCBR422R; + case DRM_FORMAT_C8: + return GDP_CLUT8; + } + return -1; +} + +static int sti_gdp_get_alpharange(int format) +{ + switch (format) { + case GDP_ARGB8565: + case GDP_ARGB8888: + case GDP_AYCBR8888: + return GAM_GDP_ALPHARANGE_255; + } + return 0; +} + +/** + * sti_gdp_get_free_nodes + * @layer: gdp layer + * + * Look for a GDP node list that is not currently read by the HW. + * + * RETURNS: + * Pointer to the free GDP node list + */ +static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer) +{ + int hw_nvn; + void *virt_nvn; + struct sti_gdp *gdp = to_sti_gdp(layer); + unsigned int i; + + hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET); + if (!hw_nvn) + goto end; + + virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn); + + for (i = 0; i < GDP_NODE_NB_BANK; i++) + if ((virt_nvn != gdp->node_list[i].btm_field) && + (virt_nvn != gdp->node_list[i].top_field)) + return &gdp->node_list[i]; + +end: + return &gdp->node_list[0]; +} + +/** + * sti_gdp_get_current_nodes + * @layer: GDP layer + * + * Look for GDP nodes that are currently read by the HW. + * + * RETURNS: + * Pointer to the current GDP node list + */ +static +struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer) +{ + int hw_nvn; + void *virt_nvn; + struct sti_gdp *gdp = to_sti_gdp(layer); + unsigned int i; + + hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET); + if (!hw_nvn) + goto end; + + virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn); + + for (i = 0; i < GDP_NODE_NB_BANK; i++) + if ((virt_nvn == gdp->node_list[i].btm_field) || + (virt_nvn == gdp->node_list[i].top_field)) + return &gdp->node_list[i]; + +end: + return NULL; +} + +/** + * sti_gdp_prepare_layer + * @lay: gdp layer + * @first_prepare: true if it is the first time this function is called + * + * Update the free GDP node list according to the layer properties. + * + * RETURNS: + * 0 on success. + */ +static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) +{ + struct sti_gdp_node_list *list; + struct sti_gdp_node *top_field, *btm_field; + struct drm_display_mode *mode = layer->mode; + struct device *dev = layer->dev; + struct sti_gdp *gdp = to_sti_gdp(layer); + int format; + unsigned int depth, bpp; + int rate = mode->clock * 1000; + int res; + u32 ydo, xdo, yds, xds; + + list = sti_gdp_get_free_nodes(layer); + top_field = list->top_field; + btm_field = list->btm_field; + + /* Build the top field from layer params */ + top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; + top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; + format = sti_gdp_fourcc2format(layer->format); + if (format == -1) { + DRM_ERROR("Format not supported by GDP %.4s\n", + (char *)&layer->format); + return 1; + } + top_field->gam_gdp_ctl |= format; + top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); + top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; + + /* pixel memory location */ + drm_fb_get_bpp_depth(layer->format, &depth, &bpp); + top_field->gam_gdp_pml = (u32) layer->paddr + layer->offsets[0]; + top_field->gam_gdp_pml += layer->src_x * (bpp >> 3); + top_field->gam_gdp_pml += layer->src_y * layer->pitches[0]; + + /* input parameters */ + top_field->gam_gdp_pmp = layer->pitches[0]; + top_field->gam_gdp_size = + clamp_val(layer->src_h, 0, GAM_GDP_SIZE_MAX) << 16 | + clamp_val(layer->src_w, 0, GAM_GDP_SIZE_MAX); + + /* output parameters */ + ydo = sti_vtg_get_line_number(*mode, layer->dst_y); + yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1); + xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x); + xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1); + top_field->gam_gdp_vpo = (ydo << 16) | xdo; + top_field->gam_gdp_vps = (yds << 16) | xds; + + /* Same content and chained together */ + memcpy(btm_field, top_field, sizeof(*btm_field)); + top_field->gam_gdp_nvn = virt_to_dma(dev, btm_field); + btm_field->gam_gdp_nvn = virt_to_dma(dev, top_field); + + /* Interlaced mode */ + if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) + btm_field->gam_gdp_pml = top_field->gam_gdp_pml + + layer->pitches[0]; + + if (first_prepare) { + /* Set and enable gdp clock */ + if (gdp->clk_pix) { + res = clk_set_rate(gdp->clk_pix, rate); + if (res < 0) { + DRM_ERROR("Cannot set rate (%dHz) for gdp\n", + rate); + return 1; + } + + if (clk_prepare_enable(gdp->clk_pix)) { + DRM_ERROR("Failed to prepare/enable gdp\n"); + return 1; + } + } + } + + return 0; +} + +/** + * sti_gdp_commit_layer + * @lay: gdp layer + * + * Update the NVN field of the 'right' field of the current GDP node (being + * used by the HW) with the address of the updated ('free') top field GDP node. + * - In interlaced mode the 'right' field is the bottom field as we update + * frames starting from their top field + * - In progressive mode, we update both bottom and top fields which are + * equal nodes. + * At the next VSYNC, the updated node list will be used by the HW. + * + * RETURNS: + * 0 on success. + */ +static int sti_gdp_commit_layer(struct sti_layer *layer) +{ + struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(layer); + struct sti_gdp_node *updated_top_node = updated_list->top_field; + struct sti_gdp_node *updated_btm_node = updated_list->btm_field; + struct sti_gdp *gdp = to_sti_gdp(layer); + u32 dma_updated_top = virt_to_dma(layer->dev, updated_top_node); + u32 dma_updated_btm = virt_to_dma(layer->dev, updated_btm_node); + struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer); + + dev_dbg(layer->dev, "Current NVN:0x%X\n", + readl(layer->regs + GAM_GDP_NVN_OFFSET)); + dev_dbg(layer->dev, "Posted buff: %lx current buff: %x\n", + (unsigned long)layer->paddr, + readl(layer->regs + GAM_GDP_PML_OFFSET)); + + if (curr_list == NULL) { + /* First update or invalid node should directly write in the + * hw register */ + writel(gdp->is_curr_top == true ? + dma_updated_btm : dma_updated_top, + layer->regs + GAM_GDP_NVN_OFFSET); + return 0; + } + + if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) { + if (gdp->is_curr_top == true) { + /* Do not update in the middle of the frame, but + * postpone the update after the bottom field has + * been displayed */ + curr_list->btm_field->gam_gdp_nvn = dma_updated_top; + } else { + /* Direct update to avoid one frame delay */ + writel(dma_updated_top, + layer->regs + GAM_GDP_NVN_OFFSET); + } + } else { + /* Direct update for progressive to avoid one frame delay */ + writel(dma_updated_top, layer->regs + GAM_GDP_NVN_OFFSET); + } + + return 0; +} + +/** + * sti_gdp_disable_layer + * @lay: gdp layer + * + * Disable a GDP. + * + * RETURNS: + * 0 on success. + */ +static int sti_gdp_disable_layer(struct sti_layer *layer) +{ + unsigned int i; + struct sti_gdp *gdp = to_sti_gdp(layer); + + /* Set the nodes as 'to be ignored on mixer' */ + for (i = 0; i < GDP_NODE_NB_BANK; i++) { + gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; + gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; + } + + if (gdp->clk_pix) + clk_disable_unprepare(gdp->clk_pix); + + return 0; +} + +/** + * sti_gdp_field_cb + * @nb: notifier block + * @event: event message + * @data: private data + * + * Handle VTG top field and bottom field event. + * + * RETURNS: + * 0 on success. + */ +int sti_gdp_field_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb); + + switch (event) { + case VTG_TOP_FIELD_EVENT: + gdp->is_curr_top = true; + break; + case VTG_BOTTOM_FIELD_EVENT: + gdp->is_curr_top = false; + break; + default: + DRM_ERROR("unsupported event: %lu\n", event); + break; + } + + return 0; +} + +static void sti_gdp_init(struct sti_layer *layer) +{ + struct sti_gdp *gdp = to_sti_gdp(layer); + struct device_node *np = layer->dev->of_node; + dma_addr_t dma; + void *base; + unsigned int i, size; + + /* Allocate all the nodes within a single memory page */ + size = sizeof(struct sti_gdp_node) * + GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; + + base = dma_alloc_writecombine(layer->dev, + size, &dma, GFP_KERNEL | GFP_DMA); + if (!base) { + DRM_ERROR("Failed to allocate memory for GDP node\n"); + return; + } + memset(base, 0, size); + + for (i = 0; i < GDP_NODE_NB_BANK; i++) { + if (virt_to_dma(layer->dev, base) & 0xF) { + DRM_ERROR("Mem alignment failed\n"); + return; + } + gdp->node_list[i].top_field = base; + DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base); + base += sizeof(struct sti_gdp_node); + + if (virt_to_dma(layer->dev, base) & 0xF) { + DRM_ERROR("Mem alignment failed\n"); + return; + } + gdp->node_list[i].btm_field = base; + DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base); + base += sizeof(struct sti_gdp_node); + } + + if (of_device_is_compatible(np, "st,stih407-compositor")) { + /* GDP of STiH407 chip have its own pixel clock */ + char *clk_name; + + switch (layer->desc) { + case STI_GDP_0: + clk_name = "pix_gdp1"; + break; + case STI_GDP_1: + clk_name = "pix_gdp2"; + break; + case STI_GDP_2: + clk_name = "pix_gdp3"; + break; + case STI_GDP_3: + clk_name = "pix_gdp4"; + break; + default: + DRM_ERROR("GDP id not recognized\n"); + return; + } + + gdp->clk_pix = devm_clk_get(layer->dev, clk_name); + if (IS_ERR(gdp->clk_pix)) + DRM_ERROR("Cannot get %s clock\n", clk_name); + } +} + +static const struct sti_layer_funcs gdp_ops = { + .get_formats = sti_gdp_get_formats, + .get_nb_formats = sti_gdp_get_nb_formats, + .init = sti_gdp_init, + .prepare = sti_gdp_prepare_layer, + .commit = sti_gdp_commit_layer, + .disable = sti_gdp_disable_layer, +}; + +struct sti_layer *sti_gdp_create(struct device *dev, int id) +{ + struct sti_gdp *gdp; + + gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL); + if (!gdp) { + DRM_ERROR("Failed to allocate memory for GDP\n"); + return NULL; + } + + gdp->layer.ops = &gdp_ops; + gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb; + + return (struct sti_layer *)gdp; +} diff --git a/drivers/gpu/drm/sti/sti_gdp.h b/drivers/gpu/drm/sti/sti_gdp.h new file mode 100644 index 0000000..1dab682 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_gdp.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_GDP_H_ +#define _STI_GDP_H_ + +#include + +struct sti_layer *sti_gdp_create(struct device *dev, int id); + +#endif diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h new file mode 100644 index 0000000..198c377 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_layer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_LAYER_H_ +#define _STI_LAYER_H_ + +#include + +#define to_sti_layer(x) container_of(x, struct sti_layer, plane) + +#define STI_LAYER_TYPE_SHIFT 8 +#define STI_LAYER_TYPE_MASK (~((1< Date: Mon, 28 Jul 2014 10:30:02 +0200 Subject: drm: sti: add VID layer VIDeo plug are one of the compositor input sub-devices. VID are dedicated to video inputs like YUV plans. Like GDP, VID are part of Compositor hardware block and use sti_layer structure to provide an abstraction for Compositor calls. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 7de757f..9b613d2 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,5 +1,6 @@ sticompositor-y := \ - sti_gdp.o + sti_gdp.o \ + sti_vid.o stihdmi-y := sti_hdmi.o \ sti_hdmi_tx3g0c55phy.o \ diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c new file mode 100644 index 0000000..10ced6a --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vid.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#include "sti_layer.h" +#include "sti_vid.h" +#include "sti_vtg.h" + +/* Registers */ +#define VID_CTL 0x00 +#define VID_ALP 0x04 +#define VID_CLF 0x08 +#define VID_VPO 0x0C +#define VID_VPS 0x10 +#define VID_KEY1 0x28 +#define VID_KEY2 0x2C +#define VID_MPR0 0x30 +#define VID_MPR1 0x34 +#define VID_MPR2 0x38 +#define VID_MPR3 0x3C +#define VID_MST 0x68 +#define VID_BC 0x70 +#define VID_TINT 0x74 +#define VID_CSAT 0x78 + +/* Registers values */ +#define VID_CTL_IGNORE (BIT(31) | BIT(30)) +#define VID_CTL_PSI_ENABLE (BIT(2) | BIT(1) | BIT(0)) +#define VID_ALP_OPAQUE 0x00000080 +#define VID_BC_DFLT 0x00008000 +#define VID_TINT_DFLT 0x00000000 +#define VID_CSAT_DFLT 0x00000080 +/* YCbCr to RGB BT709: + * R = Y+1.5391Cr + * G = Y-0.4590Cr-0.1826Cb + * B = Y+1.8125Cb */ +#define VID_MPR0_BT709 0x0A800000 +#define VID_MPR1_BT709 0x0AC50000 +#define VID_MPR2_BT709 0x07150545 +#define VID_MPR3_BT709 0x00000AE8 + +static int sti_vid_prepare_layer(struct sti_layer *vid, bool first_prepare) +{ + u32 val; + + /* Unmask */ + val = readl(vid->regs + VID_CTL); + val &= ~VID_CTL_IGNORE; + writel(val, vid->regs + VID_CTL); + + return 0; +} + +static int sti_vid_commit_layer(struct sti_layer *vid) +{ + struct drm_display_mode *mode = vid->mode; + u32 ydo, xdo, yds, xds; + + ydo = sti_vtg_get_line_number(*mode, vid->dst_y); + yds = sti_vtg_get_line_number(*mode, vid->dst_y + vid->dst_h - 1); + xdo = sti_vtg_get_pixel_number(*mode, vid->dst_x); + xds = sti_vtg_get_pixel_number(*mode, vid->dst_x + vid->dst_w - 1); + + writel((ydo << 16) | xdo, vid->regs + VID_VPO); + writel((yds << 16) | xds, vid->regs + VID_VPS); + + return 0; +} + +static int sti_vid_disable_layer(struct sti_layer *vid) +{ + u32 val; + + /* Mask */ + val = readl(vid->regs + VID_CTL); + val |= VID_CTL_IGNORE; + writel(val, vid->regs + VID_CTL); + + return 0; +} + +static const uint32_t *sti_vid_get_formats(struct sti_layer *layer) +{ + return NULL; +} + +static unsigned int sti_vid_get_nb_formats(struct sti_layer *layer) +{ + return 0; +} + +static void sti_vid_init(struct sti_layer *vid) +{ + /* Enable PSI, Mask layer */ + writel(VID_CTL_PSI_ENABLE | VID_CTL_IGNORE, vid->regs + VID_CTL); + + /* Opaque */ + writel(VID_ALP_OPAQUE, vid->regs + VID_ALP); + + /* Color conversion parameters */ + writel(VID_MPR0_BT709, vid->regs + VID_MPR0); + writel(VID_MPR1_BT709, vid->regs + VID_MPR1); + writel(VID_MPR2_BT709, vid->regs + VID_MPR2); + writel(VID_MPR3_BT709, vid->regs + VID_MPR3); + + /* Brightness, contrast, tint, saturation */ + writel(VID_BC_DFLT, vid->regs + VID_BC); + writel(VID_TINT_DFLT, vid->regs + VID_TINT); + writel(VID_CSAT_DFLT, vid->regs + VID_CSAT); +} + +static const struct sti_layer_funcs vid_ops = { + .get_formats = sti_vid_get_formats, + .get_nb_formats = sti_vid_get_nb_formats, + .init = sti_vid_init, + .prepare = sti_vid_prepare_layer, + .commit = sti_vid_commit_layer, + .disable = sti_vid_disable_layer, +}; + +struct sti_layer *sti_vid_create(struct device *dev) +{ + struct sti_layer *vid; + + vid = devm_kzalloc(dev, sizeof(*vid), GFP_KERNEL); + if (!vid) { + DRM_ERROR("Failed to allocate memory for VID\n"); + return NULL; + } + + vid->ops = &vid_ops; + + return vid; +} diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h new file mode 100644 index 0000000..2c0aecd --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vid.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_VID_H_ +#define _STI_VID_H_ + +struct sti_layer *sti_vid_create(struct device *dev); + +#endif -- cgit v0.10.2 From e21e21939ce0031c11605cc4a7fed83c8ed42b52 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Mon, 28 Jul 2014 10:30:18 +0200 Subject: drm: sti: add Mixer Mixer hardware IP is responsible of mixing the different inputs layers. Z-order is managed by the mixer. We could 2 mixers: one for main path and one for auxillary path Mixers are part of Compositor hardware block Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 9b613d2..4a3a816 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,4 +1,5 @@ sticompositor-y := \ + sti_mixer.o \ sti_gdp.o \ sti_vid.o @@ -12,4 +13,4 @@ obj-$(CONFIG_DRM_STI) = \ stihdmi.o \ sti_hda.o \ sti_tvout.o \ - sticompositor.o + sticompositor.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c new file mode 100644 index 0000000..5fa6dc5 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "sti_mixer.h" +#include "sti_vtg.h" + +/* Identity: G=Y , B=Cb , R=Cr */ +static const u32 mixerColorSpaceMatIdentity[] = { + 0x10000000, 0x00000000, 0x10000000, 0x00001000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +/* regs offset */ +#define GAM_MIXER_CTL 0x00 +#define GAM_MIXER_BKC 0x04 +#define GAM_MIXER_BCO 0x0C +#define GAM_MIXER_BCS 0x10 +#define GAM_MIXER_AVO 0x28 +#define GAM_MIXER_AVS 0x2C +#define GAM_MIXER_CRB 0x34 +#define GAM_MIXER_ACT 0x38 +#define GAM_MIXER_MBP 0x3C +#define GAM_MIXER_MX0 0x80 + +/* id for depth of CRB reg */ +#define GAM_DEPTH_VID0_ID 1 +#define GAM_DEPTH_VID1_ID 2 +#define GAM_DEPTH_GDP0_ID 3 +#define GAM_DEPTH_GDP1_ID 4 +#define GAM_DEPTH_GDP2_ID 5 +#define GAM_DEPTH_GDP3_ID 6 +#define GAM_DEPTH_MASK_ID 7 + +/* mask in CTL reg */ +#define GAM_CTL_BACK_MASK BIT(0) +#define GAM_CTL_VID0_MASK BIT(1) +#define GAM_CTL_VID1_MASK BIT(2) +#define GAM_CTL_GDP0_MASK BIT(3) +#define GAM_CTL_GDP1_MASK BIT(4) +#define GAM_CTL_GDP2_MASK BIT(5) +#define GAM_CTL_GDP3_MASK BIT(6) + +const char *sti_mixer_to_str(struct sti_mixer *mixer) +{ + switch (mixer->id) { + case STI_MIXER_MAIN: + return "MAIN_MIXER"; + case STI_MIXER_AUX: + return "AUX_MIXER"; + default: + return ""; + } +} + +static inline u32 sti_mixer_reg_read(struct sti_mixer *mixer, u32 reg_id) +{ + return readl(mixer->regs + reg_id); +} + +static inline void sti_mixer_reg_write(struct sti_mixer *mixer, + u32 reg_id, u32 val) +{ + writel(val, mixer->regs + reg_id); +} + +void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable) +{ + u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); + + val &= ~GAM_CTL_BACK_MASK; + val |= enable; + sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val); +} + +static void sti_mixer_set_background_color(struct sti_mixer *mixer, + u8 red, u8 green, u8 blue) +{ + u32 val = (red << 16) | (green << 8) | blue; + + sti_mixer_reg_write(mixer, GAM_MIXER_BKC, val); +} + +static void sti_mixer_set_background_area(struct sti_mixer *mixer, + struct drm_display_mode *mode) +{ + u32 ydo, xdo, yds, xds; + + ydo = sti_vtg_get_line_number(*mode, 0); + yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); + xdo = sti_vtg_get_pixel_number(*mode, 0); + xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); + + sti_mixer_reg_write(mixer, GAM_MIXER_BCO, ydo << 16 | xdo); + sti_mixer_reg_write(mixer, GAM_MIXER_BCS, yds << 16 | xds); +} + +int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer) +{ + int layer_id = 0, depth = layer->zorder; + u32 mask, val; + + if (depth >= GAM_MIXER_NB_DEPTH_LEVEL) + return 1; + + switch (layer->desc) { + case STI_GDP_0: + layer_id = GAM_DEPTH_GDP0_ID; + break; + case STI_GDP_1: + layer_id = GAM_DEPTH_GDP1_ID; + break; + case STI_GDP_2: + layer_id = GAM_DEPTH_GDP2_ID; + break; + case STI_GDP_3: + layer_id = GAM_DEPTH_GDP3_ID; + break; + case STI_VID_0: + layer_id = GAM_DEPTH_VID0_ID; + break; + case STI_VID_1: + layer_id = GAM_DEPTH_VID1_ID; + break; + default: + DRM_ERROR("Unknown layer %d\n", layer->desc); + return 1; + } + mask = GAM_DEPTH_MASK_ID << (3 * depth); + layer_id = layer_id << (3 * depth); + + dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n", + layer_id, mask); + + val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB); + val &= ~mask; + val |= layer_id; + sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val); + + dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n", + sti_mixer_reg_read(mixer, GAM_MIXER_CRB)); + return 0; +} + +int sti_mixer_active_video_area(struct sti_mixer *mixer, + struct drm_display_mode *mode) +{ + u32 ydo, xdo, yds, xds; + + ydo = sti_vtg_get_line_number(*mode, 0); + yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); + xdo = sti_vtg_get_pixel_number(*mode, 0); + xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); + + DRM_DEBUG_DRIVER("%s active video area xdo:%d ydo:%d xds:%d yds:%d\n", + sti_mixer_to_str(mixer), xdo, ydo, xds, yds); + sti_mixer_reg_write(mixer, GAM_MIXER_AVO, ydo << 16 | xdo); + sti_mixer_reg_write(mixer, GAM_MIXER_AVS, yds << 16 | xds); + + sti_mixer_set_background_color(mixer, 0xFF, 0, 0); + + sti_mixer_set_background_area(mixer, mode); + sti_mixer_set_background_status(mixer, true); + return 0; +} + +static u32 sti_mixer_get_layer_mask(struct sti_layer *layer) +{ + switch (layer->desc) { + case STI_BACK: + return GAM_CTL_BACK_MASK; + case STI_GDP_0: + return GAM_CTL_GDP0_MASK; + case STI_GDP_1: + return GAM_CTL_GDP1_MASK; + case STI_GDP_2: + return GAM_CTL_GDP2_MASK; + case STI_GDP_3: + return GAM_CTL_GDP3_MASK; + case STI_VID_0: + return GAM_CTL_VID0_MASK; + case STI_VID_1: + return GAM_CTL_VID1_MASK; + default: + return 0; + } +} + +int sti_mixer_set_layer_status(struct sti_mixer *mixer, + struct sti_layer *layer, bool status) +{ + u32 mask, val; + + mask = sti_mixer_get_layer_mask(layer); + if (!mask) { + DRM_ERROR("Can not find layer mask\n"); + return -EINVAL; + } + + val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL); + val &= ~mask; + val |= status ? mask : 0; + sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val); + + return 0; +} + +void sti_mixer_set_matrix(struct sti_mixer *mixer) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mixerColorSpaceMatIdentity); i++) + sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4), + mixerColorSpaceMatIdentity[i]); +} + +struct sti_mixer *sti_mixer_create(struct device *dev, int id, + void __iomem *baseaddr) +{ + struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); + struct device_node *np = dev->of_node; + + dev_dbg(dev, "%s\n", __func__); + if (!mixer) { + DRM_ERROR("Failed to allocated memory for mixer\n"); + return NULL; + } + mixer->regs = baseaddr; + mixer->dev = dev; + mixer->id = id; + + if (of_device_is_compatible(np, "st,stih416-compositor")) + sti_mixer_set_matrix(mixer); + + DRM_DEBUG_DRIVER("%s created. Regs=%p\n", + sti_mixer_to_str(mixer), mixer->regs); + + return mixer; +} diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h new file mode 100644 index 0000000..8743721 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_mixer.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_MIXER_H_ +#define _STI_MIXER_H_ + +#include + +#include "sti_layer.h" + +#define to_sti_mixer(x) container_of(x, struct sti_mixer, drm_crtc) + +/** + * STI Mixer subdevice structure + * + * @dev: driver device + * @regs: mixer registers + * @id: id of the mixer + * @drm_crtc: crtc object link to the mixer + * @pending_event: set if a flip event is pending on crtc + */ +struct sti_mixer { + struct device *dev; + void __iomem *regs; + int id; + struct drm_crtc drm_crtc; + struct drm_pending_vblank_event *pending_event; +}; + +const char *sti_mixer_to_str(struct sti_mixer *mixer); + +struct sti_mixer *sti_mixer_create(struct device *dev, int id, + void __iomem *baseaddr); + +int sti_mixer_set_layer_status(struct sti_mixer *mixer, + struct sti_layer *layer, bool status); +int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer); +int sti_mixer_active_video_area(struct sti_mixer *mixer, + struct drm_display_mode *mode); + +void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable); + +/* depth in Cross-bar control = z order */ +#define GAM_MIXER_NB_DEPTH_LEVEL 7 + +#define STI_MIXER_MAIN 0 +#define STI_MIXER_AUX 1 + +#endif -- cgit v0.10.2 From d219673d8437ff1073c11d30ac496fa42b09662c Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 30 Jul 2014 19:28:27 +0200 Subject: drm: sti: add Compositor Compositor control all the input sub-device (VID, GDP) and the mixer(s). It is the main entry point for composition. Layer interface is used to control the abstracted layers. Add debug in mixer and GDP. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig index a18df02..11d372c 100644 --- a/drivers/gpu/drm/sti/Kconfig +++ b/drivers/gpu/drm/sti/Kconfig @@ -1,5 +1,6 @@ config DRM_STI tristate "DRM Support for STMicroelectronics SoC stiH41x Series" depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) + select DRM_KMS_CMA_HELPER help Choose this option to enable DRM on STM stiH41x chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 4a3a816..c5838f2 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -1,7 +1,9 @@ sticompositor-y := \ + sti_layer.o \ sti_mixer.o \ sti_gdp.o \ - sti_vid.o + sti_vid.o \ + sti_compositor.o stihdmi-y := sti_hdmi.o \ sti_hdmi_tx3g0c55phy.o \ diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c new file mode 100644 index 0000000..a4164cf --- /dev/null +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include + +#include + +#include "sti_compositor.h" +#include "sti_gdp.h" +#include "sti_vtg.h" + +/* + * stiH407 compositor properties + */ +struct sti_compositor_data stih407_compositor_data = { + .nb_subdev = 6, + .subdev_desc = { + {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100}, + {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200}, + {STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300}, + {STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400}, + {STI_VID_SUBDEV, (int)STI_VID_0, 0x700}, + {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00} + }, +}; + +/* + * stiH416 compositor properties + * Note: + * on stih416 MIXER_AUX has a different base address from MIXER_MAIN + * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does + * not fit for stiH416 if we want to enable the MIXER_AUX. + */ +struct sti_compositor_data stih416_compositor_data = { + .nb_subdev = 3, + .subdev_desc = { + {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100}, + {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200}, + {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00} + }, +}; + +static int sti_compositor_init_subdev(struct sti_compositor *compo, + struct sti_compositor_subdev_descriptor *desc, + unsigned int array_size) +{ + unsigned int i, mixer_id = 0, layer_id = 0; + + for (i = 0; i < array_size; i++) { + switch (desc[i].type) { + case STI_MIXER_MAIN_SUBDEV: + case STI_MIXER_AUX_SUBDEV: + compo->mixer[mixer_id++] = + sti_mixer_create(compo->dev, desc[i].id, + compo->regs + desc[i].offset); + break; + case STI_GPD_SUBDEV: + case STI_VID_SUBDEV: + compo->layer[layer_id++] = + sti_layer_create(compo->dev, desc[i].id, + compo->regs + desc[i].offset); + break; + /* case STI_CURSOR_SUBDEV : TODO */ + default: + DRM_ERROR("Unknow subdev compoment type\n"); + return 1; + } + + } + compo->nb_mixers = mixer_id; + compo->nb_layers = layer_id; + + return 0; +} + +static int sti_compositor_bind(struct device *dev, struct device *master, + void *data) +{ + struct sti_compositor *compo = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + unsigned int i, crtc = 0, plane = 0; + + drm_vblank_init(drm_dev, crtc); + /* Allow usage of vblank without having to call drm_irq_install */ + drm_dev->irq_enabled = 1; + + + DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n", + crtc, plane); + DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n"); + + return 0; +} + +static void sti_compositor_unbind(struct device *dev, struct device *master, + void *data) +{ + /* do nothing */ +} + +static const struct component_ops sti_compositor_ops = { + .bind = sti_compositor_bind, + .unbind = sti_compositor_unbind, +}; + +static const struct of_device_id compositor_of_match[] = { + { + .compatible = "st,stih416-compositor", + .data = &stih416_compositor_data, + }, { + .compatible = "st,stih407-compositor", + .data = &stih407_compositor_data, + }, { + /* end node */ + } +}; +MODULE_DEVICE_TABLE(of, compositor_of_match); + +static int sti_compositor_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *vtg_np; + struct sti_compositor *compo; + struct resource *res; + int err; + + compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL); + if (!compo) { + DRM_ERROR("Failed to allocate compositor context\n"); + return -ENOMEM; + } + compo->dev = dev; + + /* populate data structure depending on compatibility */ + BUG_ON(!of_match_node(compositor_of_match, np)->data); + + memcpy(&compo->data, of_match_node(compositor_of_match, np)->data, + sizeof(struct sti_compositor_data)); + + /* Get Memory ressources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + DRM_ERROR("Get memory resource failed\n"); + return -ENXIO; + } + compo->regs = devm_ioremap(dev, res->start, resource_size(res)); + if (compo->regs == NULL) { + DRM_ERROR("Register mapping failed\n"); + return -ENXIO; + } + + /* Get clock resources */ + compo->clk_compo_main = devm_clk_get(dev, "compo_main"); + if (IS_ERR(compo->clk_compo_main)) { + DRM_ERROR("Cannot get compo_main clock\n"); + return PTR_ERR(compo->clk_compo_main); + } + + compo->clk_compo_aux = devm_clk_get(dev, "compo_aux"); + if (IS_ERR(compo->clk_compo_aux)) { + DRM_ERROR("Cannot get compo_aux clock\n"); + return PTR_ERR(compo->clk_compo_aux); + } + + compo->clk_pix_main = devm_clk_get(dev, "pix_main"); + if (IS_ERR(compo->clk_pix_main)) { + DRM_ERROR("Cannot get pix_main clock\n"); + return PTR_ERR(compo->clk_pix_main); + } + + compo->clk_pix_aux = devm_clk_get(dev, "pix_aux"); + if (IS_ERR(compo->clk_pix_aux)) { + DRM_ERROR("Cannot get pix_aux clock\n"); + return PTR_ERR(compo->clk_pix_aux); + } + + /* Get reset resources */ + compo->rst_main = devm_reset_control_get(dev, "compo-main"); + /* Take compo main out of reset */ + if (!IS_ERR(compo->rst_main)) + reset_control_deassert(compo->rst_main); + + compo->rst_aux = devm_reset_control_get(dev, "compo-aux"); + /* Take compo aux out of reset */ + if (!IS_ERR(compo->rst_aux)) + reset_control_deassert(compo->rst_aux); + + vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0); + if (vtg_np) + compo->vtg_main = of_vtg_find(vtg_np); + + vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1); + if (vtg_np) + compo->vtg_aux = of_vtg_find(vtg_np); + + /* Initialize compositor subdevices */ + err = sti_compositor_init_subdev(compo, compo->data.subdev_desc, + compo->data.nb_subdev); + if (err) + return err; + + platform_set_drvdata(pdev, compo); + + return component_add(&pdev->dev, &sti_compositor_ops); +} + +static int sti_compositor_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sti_compositor_ops); + return 0; +} + +static struct platform_driver sti_compositor_driver = { + .driver = { + .name = "sti-compositor", + .owner = THIS_MODULE, + .of_match_table = compositor_of_match, + }, + .probe = sti_compositor_probe, + .remove = sti_compositor_remove, +}; + +module_platform_driver(sti_compositor_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h new file mode 100644 index 0000000..3ea19db --- /dev/null +++ b/drivers/gpu/drm/sti/sti_compositor.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_COMPOSITOR_H_ +#define _STI_COMPOSITOR_H_ + +#include +#include + +#include "sti_layer.h" +#include "sti_mixer.h" + +#define WAIT_NEXT_VSYNC_MS 50 /*ms*/ + +#define STI_MAX_LAYER 8 +#define STI_MAX_MIXER 2 + +enum sti_compositor_subdev_type { + STI_MIXER_MAIN_SUBDEV, + STI_MIXER_AUX_SUBDEV, + STI_GPD_SUBDEV, + STI_VID_SUBDEV, + STI_CURSOR_SUBDEV, +}; + +struct sti_compositor_subdev_descriptor { + enum sti_compositor_subdev_type type; + int id; + unsigned int offset; +}; + +/** + * STI Compositor data structure + * + * @nb_subdev: number of subdevices supported by the compositor + * @subdev_desc: subdev list description + */ +#define MAX_SUBDEV 9 +struct sti_compositor_data { + unsigned int nb_subdev; + struct sti_compositor_subdev_descriptor subdev_desc[MAX_SUBDEV]; +}; + +/** + * STI Compositor structure + * + * @dev: driver device + * @regs: registers (main) + * @data: device data + * @clk_compo_main: clock for main compo + * @clk_compo_aux: clock for aux compo + * @clk_pix_main: pixel clock for main path + * @clk_pix_aux: pixel clock for aux path + * @rst_main: reset control of the main path + * @rst_aux: reset control of the aux path + * @mixer: array of mixers + * @vtg_main: vtg for main data path + * @vtg_aux: vtg for auxillary data path + * @layer: array of layers + * @nb_mixers: number of mixers for this compositor + * @nb_layers: number of layers (GDP,VID,...) for this compositor + * @enable: true if compositor is enable else false + * @vtg_vblank_nb: callback for VTG VSYNC notification + */ +struct sti_compositor { + struct device *dev; + void __iomem *regs; + struct sti_compositor_data data; + struct clk *clk_compo_main; + struct clk *clk_compo_aux; + struct clk *clk_pix_main; + struct clk *clk_pix_aux; + struct reset_control *rst_main; + struct reset_control *rst_aux; + struct sti_mixer *mixer[STI_MAX_MIXER]; + struct sti_vtg *vtg_main; + struct sti_vtg *vtg_aux; + struct sti_layer *layer[STI_MAX_LAYER]; + int nb_mixers; + int nb_layers; + bool enable; + struct notifier_block vtg_vblank_nb; +}; + +#endif diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index 7f55767..4e30b74 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -9,6 +9,7 @@ #include #include +#include "sti_compositor.h" #include "sti_gdp.h" #include "sti_layer.h" #include "sti_vtg.h" @@ -182,6 +183,10 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer) (virt_nvn != gdp->node_list[i].top_field)) return &gdp->node_list[i]; + /* in hazardious cases restart with the first node */ + DRM_ERROR("inconsistent NVN for %s: 0x%08X\n", + sti_layer_to_str(layer), hw_nvn); + end: return &gdp->node_list[0]; } @@ -215,6 +220,9 @@ struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer) return &gdp->node_list[i]; end: + DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n", + hw_nvn, sti_layer_to_str(layer)); + return NULL; } @@ -235,6 +243,7 @@ static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) struct drm_display_mode *mode = layer->mode; struct device *dev = layer->dev; struct sti_gdp *gdp = to_sti_gdp(layer); + struct sti_compositor *compo = dev_get_drvdata(dev); int format; unsigned int depth, bpp; int rate = mode->clock * 1000; @@ -245,6 +254,9 @@ static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) top_field = list->top_field; btm_field = list->btm_field; + dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, + sti_layer_to_str(layer), top_field, btm_field); + /* Build the top field from layer params */ top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; @@ -289,6 +301,14 @@ static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) layer->pitches[0]; if (first_prepare) { + /* Register gdp callback */ + if (sti_vtg_register_client(layer->mixer_id == STI_MIXER_MAIN ? + compo->vtg_main : compo->vtg_aux, + &gdp->vtg_field_nb, layer->mixer_id)) { + DRM_ERROR("Cannot register VTG notifier\n"); + return 1; + } + /* Set and enable gdp clock */ if (gdp->clk_pix) { res = clk_set_rate(gdp->clk_pix, rate); @@ -333,6 +353,9 @@ static int sti_gdp_commit_layer(struct sti_layer *layer) u32 dma_updated_btm = virt_to_dma(layer->dev, updated_btm_node); struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer); + dev_dbg(layer->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__, + sti_layer_to_str(layer), + updated_top_node, updated_btm_node); dev_dbg(layer->dev, "Current NVN:0x%X\n", readl(layer->regs + GAM_GDP_NVN_OFFSET)); dev_dbg(layer->dev, "Posted buff: %lx current buff: %x\n", @@ -342,6 +365,9 @@ static int sti_gdp_commit_layer(struct sti_layer *layer) if (curr_list == NULL) { /* First update or invalid node should directly write in the * hw register */ + DRM_DEBUG_DRIVER("%s first update (or invalid node)", + sti_layer_to_str(layer)); + writel(gdp->is_curr_top == true ? dma_updated_btm : dma_updated_top, layer->regs + GAM_GDP_NVN_OFFSET); @@ -380,6 +406,9 @@ static int sti_gdp_disable_layer(struct sti_layer *layer) { unsigned int i; struct sti_gdp *gdp = to_sti_gdp(layer); + struct sti_compositor *compo = dev_get_drvdata(layer->dev); + + DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer)); /* Set the nodes as 'to be ignored on mixer' */ for (i = 0; i < GDP_NODE_NB_BANK; i++) { @@ -387,6 +416,10 @@ static int sti_gdp_disable_layer(struct sti_layer *layer) gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; } + if (sti_vtg_unregister_client(layer->mixer_id == STI_MIXER_MAIN ? + compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb)) + DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); + if (gdp->clk_pix) clk_disable_unprepare(gdp->clk_pix); diff --git a/drivers/gpu/drm/sti/sti_layer.c b/drivers/gpu/drm/sti/sti_layer.c new file mode 100644 index 0000000..06a587c --- /dev/null +++ b/drivers/gpu/drm/sti/sti_layer.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include + +#include "sti_compositor.h" +#include "sti_gdp.h" +#include "sti_layer.h" +#include "sti_vid.h" + +const char *sti_layer_to_str(struct sti_layer *layer) +{ + switch (layer->desc) { + case STI_GDP_0: + return "GDP0"; + case STI_GDP_1: + return "GDP1"; + case STI_GDP_2: + return "GDP2"; + case STI_GDP_3: + return "GDP3"; + case STI_VID_0: + return "VID0"; + case STI_VID_1: + return "VID1"; + case STI_CURSOR: + return "CURSOR"; + default: + return ""; + } +} + +struct sti_layer *sti_layer_create(struct device *dev, int desc, + void __iomem *baseaddr) +{ + + struct sti_layer *layer = NULL; + + switch (desc & STI_LAYER_TYPE_MASK) { + case STI_GDP: + layer = sti_gdp_create(dev, desc); + break; + case STI_VID: + layer = sti_vid_create(dev); + break; + } + + if (!layer) { + DRM_ERROR("Failed to create layer\n"); + return NULL; + } + + layer->desc = desc; + layer->dev = dev; + layer->regs = baseaddr; + + layer->ops->init(layer); + + DRM_DEBUG_DRIVER("%s created\n", sti_layer_to_str(layer)); + + return layer; +} + +int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb, + struct drm_display_mode *mode, int mixer_id, + int dest_x, int dest_y, int dest_w, int dest_h, + int src_x, int src_y, int src_w, int src_h) +{ + int ret; + unsigned int i; + struct drm_gem_cma_object *cma_obj; + + if (!layer || !fb || !mode) { + DRM_ERROR("Null fb, layer or mode\n"); + return 1; + } + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + if (!cma_obj) { + DRM_ERROR("Can't get CMA GEM object for fb\n"); + return 1; + } + + layer->fb = fb; + layer->mode = mode; + layer->mixer_id = mixer_id; + layer->dst_x = dest_x; + layer->dst_y = dest_y; + layer->dst_w = clamp_val(dest_w, 0, mode->crtc_hdisplay - dest_x); + layer->dst_h = clamp_val(dest_h, 0, mode->crtc_vdisplay - dest_y); + layer->src_x = src_x; + layer->src_y = src_y; + layer->src_w = src_w; + layer->src_h = src_h; + layer->format = fb->pixel_format; + layer->paddr = cma_obj->paddr; + for (i = 0; i < 4; i++) { + layer->pitches[i] = fb->pitches[i]; + layer->offsets[i] = fb->offsets[i]; + } + + DRM_DEBUG_DRIVER("%s is associated with mixer_id %d\n", + sti_layer_to_str(layer), + layer->mixer_id); + DRM_DEBUG_DRIVER("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", + sti_layer_to_str(layer), + layer->dst_w, layer->dst_h, layer->dst_x, layer->dst_y, + layer->src_w, layer->src_h, layer->src_x, + layer->src_y); + + DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, + (char *)&layer->format, (unsigned long)layer->paddr); + + if (!layer->ops->prepare) + goto err_no_prepare; + + ret = layer->ops->prepare(layer, !layer->enabled); + if (!ret) + layer->enabled = true; + + return ret; + +err_no_prepare: + DRM_ERROR("Cannot prepare\n"); + return 1; +} + +int sti_layer_commit(struct sti_layer *layer) +{ + if (!layer) + return 1; + + if (!layer->ops->commit) + goto err_no_commit; + + return layer->ops->commit(layer); + +err_no_commit: + DRM_ERROR("Cannot commit\n"); + return 1; +} + +int sti_layer_disable(struct sti_layer *layer) +{ + int ret; + + DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer)); + if (!layer) + return 1; + + if (!layer->enabled) + return 0; + + if (!layer->ops->disable) + goto err_no_disable; + + ret = layer->ops->disable(layer); + if (!ret) + layer->enabled = false; + else + DRM_ERROR("Disable failed\n"); + + return ret; + +err_no_disable: + DRM_ERROR("Cannot disable\n"); + return 1; +} + +const uint32_t *sti_layer_get_formats(struct sti_layer *layer) +{ + if (!layer) + return NULL; + + if (!layer->ops->get_formats) + return NULL; + + return layer->ops->get_formats(layer); +} + +unsigned int sti_layer_get_nb_formats(struct sti_layer *layer) +{ + if (!layer) + return 0; + + if (!layer->ops->get_nb_formats) + return 0; + + return layer->ops->get_nb_formats(layer); +} diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 5fa6dc5..79f369d 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -6,6 +6,7 @@ * License terms: GNU General Public License (GPL), version 2 */ +#include "sti_compositor.h" #include "sti_mixer.h" #include "sti_vtg.h" @@ -133,6 +134,8 @@ int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer) mask = GAM_DEPTH_MASK_ID << (3 * depth); layer_id = layer_id << (3 * depth); + DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer), + sti_layer_to_str(layer), depth); dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n", layer_id, mask); @@ -195,6 +198,9 @@ int sti_mixer_set_layer_status(struct sti_mixer *mixer, { u32 mask, val; + DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable", + sti_mixer_to_str(mixer), sti_layer_to_str(layer)); + mask = sti_mixer_get_layer_mask(layer); if (!mask) { DRM_ERROR("Can not find layer mask\n"); -- cgit v0.10.2 From 9bbf86fe874cce0169f0e1152d96f0591e680874 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 31 Jul 2014 09:39:11 +0200 Subject: drm: sti: Add DRM driver itself Make the link between all the hardware drivers and DRM/KMS interface. Create the driver itself and make it register all the sub-components. Use GEM CMA helpers for buffer allocation. Signed-off-by: Benjamin Gaignard Reviewed-by: Rob Clark diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig index 11d372c..2d9d425 100644 --- a/drivers/gpu/drm/sti/Kconfig +++ b/drivers/gpu/drm/sti/Kconfig @@ -1,6 +1,14 @@ config DRM_STI tristate "DRM Support for STMicroelectronics SoC stiH41x Series" depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER help Choose this option to enable DRM on STM stiH41x chipset + +config DRM_STI_FBDEV + bool "DRM frame buffer device for STMicroelectronics SoC stiH41x Serie" + depends on DRM_STI + help + Choose this option to enable FBDEV on top of DRM for STM stiH41x chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index c5838f2..04ac2ce 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -3,7 +3,9 @@ sticompositor-y := \ sti_mixer.o \ sti_gdp.o \ sti_vid.o \ - sti_compositor.o + sti_compositor.o \ + sti_drm_crtc.o \ + sti_drm_plane.o stihdmi-y := sti_hdmi.o \ sti_hdmi_tx3g0c55phy.o \ @@ -15,4 +17,5 @@ obj-$(CONFIG_DRM_STI) = \ stihdmi.o \ sti_hda.o \ sti_tvout.o \ - sticompositor.o \ No newline at end of file + sticompositor.o \ + sti_drm_drv.o \ No newline at end of file diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index a4164cf..770a725 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -14,6 +14,9 @@ #include #include "sti_compositor.h" +#include "sti_drm_crtc.h" +#include "sti_drm_drv.h" +#include "sti_drm_plane.h" #include "sti_gdp.h" #include "sti_vtg.h" @@ -87,12 +90,51 @@ static int sti_compositor_bind(struct device *dev, struct device *master, struct sti_compositor *compo = dev_get_drvdata(dev); struct drm_device *drm_dev = data; unsigned int i, crtc = 0, plane = 0; + struct sti_drm_private *dev_priv = drm_dev->dev_private; + struct drm_plane *cursor = NULL; + struct drm_plane *primary = NULL; + + dev_priv->compo = compo; + + for (i = 0; i < compo->nb_layers; i++) { + if (compo->layer[i]) { + enum sti_layer_desc desc = compo->layer[i]->desc; + enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK; + enum drm_plane_type plane_type = DRM_PLANE_TYPE_OVERLAY; + + if (compo->mixer[crtc]) + plane_type = DRM_PLANE_TYPE_PRIMARY; + + switch (type) { + case STI_CUR: + cursor = sti_drm_plane_init(drm_dev, + compo->layer[i], + (1 << crtc) - 1, + DRM_PLANE_TYPE_CURSOR); + break; + case STI_GDP: + case STI_VID: + primary = sti_drm_plane_init(drm_dev, + compo->layer[i], + (1 << crtc) - 1, plane_type); + plane++; + break; + } + + /* The first planes are reserved for primary planes*/ + if (compo->mixer[crtc]) { + sti_drm_crtc_init(drm_dev, compo->mixer[crtc], + primary, cursor); + crtc++; + cursor = NULL; + } + } + } drm_vblank_init(drm_dev, crtc); /* Allow usage of vblank without having to call drm_irq_install */ drm_dev->irq_enabled = 1; - DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n", crtc, plane); DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n"); @@ -139,6 +181,7 @@ static int sti_compositor_probe(struct platform_device *pdev) return -ENOMEM; } compo->dev = dev; + compo->vtg_vblank_nb.notifier_call = sti_drm_crtc_vblank_cb; /* populate data structure depending on compatibility */ BUG_ON(!of_match_node(compositor_of_match, np)->data); diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c new file mode 100644 index 0000000..6eba648 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c @@ -0,0 +1,423 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#include +#include + +#include "sti_compositor.h" +#include "sti_drm_drv.h" +#include "sti_drm_crtc.h" +#include "sti_vtg.h" + +static void sti_drm_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + DRM_DEBUG_KMS("\n"); +} + +static void sti_drm_crtc_prepare(struct drm_crtc *crtc) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct device *dev = mixer->dev; + struct sti_compositor *compo = dev_get_drvdata(dev); + + compo->enable = true; + + /* Prepare and enable the compo IP clock */ + if (mixer->id == STI_MIXER_MAIN) { + if (clk_prepare_enable(compo->clk_compo_main)) + DRM_INFO("Failed to prepare/enable compo_main clk\n"); + } else { + if (clk_prepare_enable(compo->clk_compo_aux)) + DRM_INFO("Failed to prepare/enable compo_aux clk\n"); + } +} + +static void sti_drm_crtc_commit(struct drm_crtc *crtc) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct device *dev = mixer->dev; + struct sti_compositor *compo = dev_get_drvdata(dev); + struct sti_layer *layer; + + if ((!mixer || !compo)) { + DRM_ERROR("Can not find mixer or compositor)\n"); + return; + } + + /* get GDP which is reserved to the CRTC FB */ + layer = to_sti_layer(crtc->primary); + if (layer) + sti_layer_commit(layer); + else + DRM_ERROR("Can not find CRTC dedicated plane (GDP0)\n"); + + /* Enable layer on mixer */ + if (sti_mixer_set_layer_status(mixer, layer, true)) + DRM_ERROR("Can not enable layer at mixer\n"); +} + +static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* accept the provided drm_display_mode, do not fix it up */ + return true; +} + +static int +sti_drm_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) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct device *dev = mixer->dev; + struct sti_compositor *compo = dev_get_drvdata(dev); + struct sti_layer *layer; + struct clk *clk; + int rate = mode->clock * 1000; + int res; + unsigned int w, h; + + DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n", + crtc->base.id, sti_mixer_to_str(mixer), + crtc->primary->fb->base.id, mode->base.id, mode->name); + + DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", + mode->vrefresh, mode->clock, + mode->hdisplay, + mode->hsync_start, mode->hsync_end, + mode->htotal, + mode->vdisplay, + mode->vsync_start, mode->vsync_end, + mode->vtotal, mode->type, mode->flags); + + /* Set rate and prepare/enable pixel clock */ + if (mixer->id == STI_MIXER_MAIN) + clk = compo->clk_pix_main; + else + clk = compo->clk_pix_aux; + + res = clk_set_rate(clk, rate); + if (res < 0) { + DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate); + return -EINVAL; + } + if (clk_prepare_enable(clk)) { + DRM_ERROR("Failed to prepare/enable pix clk\n"); + return -EINVAL; + } + + sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ? + compo->vtg_main : compo->vtg_aux, &crtc->mode); + + /* a GDP is reserved to the CRTC FB */ + layer = to_sti_layer(crtc->primary); + if (!layer) { + DRM_ERROR("Can not find GDP0)\n"); + return -EINVAL; + } + + /* copy the mode data adjusted by mode_fixup() into crtc->mode + * so that hardware can be set to proper mode + */ + memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); + + res = sti_mixer_set_layer_depth(mixer, layer); + if (res) { + DRM_ERROR("Can not set layer depth\n"); + return -EINVAL; + } + res = sti_mixer_active_video_area(mixer, &crtc->mode); + if (res) { + DRM_ERROR("Can not set active video area\n"); + return -EINVAL; + } + + w = crtc->primary->fb->width - x; + h = crtc->primary->fb->height - y; + + return sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode, + mixer->id, 0, 0, w, h, x, y, w, h); +} + +static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct device *dev = mixer->dev; + struct sti_compositor *compo = dev_get_drvdata(dev); + struct sti_layer *layer; + unsigned int w, h; + int ret; + + DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n", + crtc->base.id, sti_mixer_to_str(mixer), + crtc->primary->fb->base.id, x, y); + + /* GDP is reserved to the CRTC FB */ + layer = to_sti_layer(crtc->primary); + if (!layer) { + DRM_ERROR("Can not find GDP0)\n"); + ret = -EINVAL; + goto out; + } + + w = crtc->primary->fb->width - crtc->x; + h = crtc->primary->fb->height - crtc->y; + + ret = sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode, + mixer->id, 0, 0, w, h, + crtc->x, crtc->y, w, h); + if (ret) { + DRM_ERROR("Can not prepare layer\n"); + goto out; + } + + sti_drm_crtc_commit(crtc); +out: + return ret; +} + +static void sti_drm_crtc_load_lut(struct drm_crtc *crtc) +{ + /* do nothing */ +} + +static void sti_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + struct device *dev = mixer->dev; + struct sti_compositor *compo = dev_get_drvdata(dev); + struct sti_layer *layer; + + if (!compo->enable) + return; + + DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer)); + + /* Disable Background */ + sti_mixer_set_background_status(mixer, false); + + /* Disable GDP */ + layer = to_sti_layer(crtc->primary); + if (!layer) { + DRM_ERROR("Cannot find GDP0\n"); + return; + } + + /* Disable layer at mixer level */ + if (sti_mixer_set_layer_status(mixer, layer, false)) + DRM_ERROR("Can not disable %s layer at mixer\n", + sti_layer_to_str(layer)); + + /* Wait a while to be sure that a Vsync event is received */ + msleep(WAIT_NEXT_VSYNC_MS); + + /* Then disable layer itself */ + sti_layer_disable(layer); + + drm_vblank_off(crtc->dev, mixer->id); + + /* Disable pixel clock and compo IP clocks */ + if (mixer->id == STI_MIXER_MAIN) { + clk_disable_unprepare(compo->clk_pix_main); + clk_disable_unprepare(compo->clk_compo_main); + } else { + clk_disable_unprepare(compo->clk_pix_aux); + clk_disable_unprepare(compo->clk_compo_aux); + } + + compo->enable = false; +} + +static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { + .dpms = sti_drm_crtc_dpms, + .prepare = sti_drm_crtc_prepare, + .commit = sti_drm_crtc_commit, + .mode_fixup = sti_drm_crtc_mode_fixup, + .mode_set = sti_drm_crtc_mode_set, + .mode_set_base = sti_drm_crtc_mode_set_base, + .load_lut = sti_drm_crtc_load_lut, + .disable = sti_drm_crtc_disable, +}; + +static int sti_drm_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct drm_device *drm_dev = crtc->dev; + struct drm_framebuffer *old_fb; + struct sti_mixer *mixer = to_sti_mixer(crtc); + unsigned long flags; + int ret; + + DRM_DEBUG_KMS("fb %d --> fb %d\n", + crtc->primary->fb->base.id, fb->base.id); + + mutex_lock(&drm_dev->struct_mutex); + + old_fb = crtc->primary->fb; + crtc->primary->fb = fb; + ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); + if (ret) { + DRM_ERROR("failed\n"); + crtc->primary->fb = old_fb; + goto out; + } + + if (event) { + event->pipe = mixer->id; + + ret = drm_vblank_get(drm_dev, event->pipe); + if (ret) { + DRM_ERROR("Cannot get vblank\n"); + goto out; + } + + spin_lock_irqsave(&drm_dev->event_lock, flags); + if (mixer->pending_event) { + drm_vblank_put(drm_dev, event->pipe); + ret = -EBUSY; + } else { + mixer->pending_event = event; + } + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + } +out: + mutex_unlock(&drm_dev->struct_mutex); + return ret; +} + +static void sti_drm_crtc_destroy(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("\n"); + drm_crtc_cleanup(crtc); +} + +static int sti_drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + DRM_DEBUG_KMS("\n"); + return 0; +} + +int sti_drm_crtc_vblank_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct drm_device *drm_dev; + struct sti_compositor *compo = + container_of(nb, struct sti_compositor, vtg_vblank_nb); + int *crtc = data; + unsigned long flags; + struct sti_drm_private *priv; + + drm_dev = compo->mixer[*crtc]->drm_crtc.dev; + priv = drm_dev->dev_private; + + if ((event != VTG_TOP_FIELD_EVENT) && + (event != VTG_BOTTOM_FIELD_EVENT)) { + DRM_ERROR("unknown event: %lu\n", event); + return -EINVAL; + } + + drm_handle_vblank(drm_dev, *crtc); + + spin_lock_irqsave(&drm_dev->event_lock, flags); + if (compo->mixer[*crtc]->pending_event) { + drm_send_vblank_event(drm_dev, -1, + compo->mixer[*crtc]->pending_event); + drm_vblank_put(drm_dev, *crtc); + compo->mixer[*crtc]->pending_event = NULL; + } + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + return 0; +} + +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) +{ + struct sti_drm_private *dev_priv = dev->dev_private; + struct sti_compositor *compo = dev_priv->compo; + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; + + if (sti_vtg_register_client(crtc == STI_MIXER_MAIN ? + compo->vtg_main : compo->vtg_aux, + vtg_vblank_nb, crtc)) { + DRM_ERROR("Cannot register VTG notifier\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(sti_drm_crtc_enable_vblank); + +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) +{ + struct sti_drm_private *priv = dev->dev_private; + struct sti_compositor *compo = priv->compo; + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; + unsigned long flags; + + DRM_DEBUG_DRIVER("\n"); + + if (sti_vtg_unregister_client(crtc == STI_MIXER_MAIN ? + compo->vtg_main : compo->vtg_aux, vtg_vblank_nb)) + DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); + + /* free the resources of the pending requests */ + spin_lock_irqsave(&dev->event_lock, flags); + if (compo->mixer[crtc]->pending_event) { + drm_vblank_put(dev, crtc); + compo->mixer[crtc]->pending_event = NULL; + } + spin_unlock_irqrestore(&dev->event_lock, flags); + +} +EXPORT_SYMBOL(sti_drm_crtc_disable_vblank); + +static struct drm_crtc_funcs sti_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .page_flip = sti_drm_crtc_page_flip, + .destroy = sti_drm_crtc_destroy, + .set_property = sti_drm_crtc_set_property, +}; + +bool sti_drm_crtc_is_main(struct drm_crtc *crtc) +{ + struct sti_mixer *mixer = to_sti_mixer(crtc); + + if (mixer->id == STI_MIXER_MAIN) + return true; + + return false; +} + +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer, + struct drm_plane *primary, struct drm_plane *cursor) +{ + struct drm_crtc *crtc = &mixer->drm_crtc; + int res; + + res = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, + &sti_crtc_funcs); + if (res) { + DRM_ERROR("Can not initialze CRTC\n"); + return -EINVAL; + } + + drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs); + + DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n", + crtc->base.id, sti_mixer_to_str(mixer)); + + return 0; +} diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.h b/drivers/gpu/drm/sti/sti_drm_crtc.h new file mode 100644 index 0000000..caca8b1 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_crtc.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_DRM_CRTC_H_ +#define _STI_DRM_CRTC_H_ + +#include + +struct sti_mixer; + +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer, + struct drm_plane *primary, struct drm_plane *cursor); +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); +int sti_drm_crtc_vblank_cb(struct notifier_block *nb, + unsigned long event, void *data); +bool sti_drm_crtc_is_main(struct drm_crtc *drm_crtc); + +#endif diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c new file mode 100644 index 0000000..a7cc249 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_drv.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sti_drm_drv.h" +#include "sti_drm_crtc.h" + +#define DRIVER_NAME "sti" +#define DRIVER_DESC "STMicroelectronics SoC DRM" +#define DRIVER_DATE "20140601" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +#define STI_MAX_FB_HEIGHT 4096 +#define STI_MAX_FB_WIDTH 4096 + +static struct drm_mode_config_funcs sti_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, +}; + +static void sti_drm_mode_config_init(struct drm_device *dev) +{ + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + /* + * set max width and height as default value. + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + dev->mode_config.max_width = STI_MAX_FB_HEIGHT; + dev->mode_config.max_height = STI_MAX_FB_WIDTH; + + dev->mode_config.funcs = &sti_drm_mode_config_funcs; +} + +static int sti_drm_load(struct drm_device *dev, unsigned long flags) +{ + struct sti_drm_private *private; + int ret; + + private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL); + if (!private) { + DRM_ERROR("Failed to allocate private\n"); + return -ENOMEM; + } + dev->dev_private = (void *)private; + private->drm_dev = dev; + + drm_mode_config_init(dev); + drm_kms_helper_poll_init(dev); + + sti_drm_mode_config_init(dev); + + ret = component_bind_all(dev->dev, dev); + if (ret) + return ret; + + drm_helper_disable_unused_functions(dev); + +#ifdef CONFIG_DRM_STI_FBDEV + drm_fbdev_cma_init(dev, 32, + dev->mode_config.num_crtc, + dev->mode_config.num_connector); +#endif + return 0; +} + +static const struct file_operations sti_drm_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .release = drm_release, +}; + +static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags) +{ + /* we want to be able to write in mmapped buffer */ + flags |= O_RDWR; + return drm_gem_prime_export(dev, obj, flags); +} + +static struct drm_driver sti_drm_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | + DRIVER_GEM | DRIVER_PRIME, + .load = sti_drm_load, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + .fops = &sti_drm_driver_fops, + + .get_vblank_counter = drm_vblank_count, + .enable_vblank = sti_drm_crtc_enable_vblank, + .disable_vblank = sti_drm_crtc_disable_vblank, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = sti_drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int sti_drm_bind(struct device *dev) +{ + return drm_platform_init(&sti_drm_driver, to_platform_device(dev)); +} + +static void sti_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +static const struct component_master_ops sti_drm_ops = { + .bind = sti_drm_bind, + .unbind = sti_drm_unbind, +}; + +static int sti_drm_master_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->parent->of_node; + struct device_node *child_np; + struct component_match *match = NULL; + + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + + child_np = of_get_next_available_child(node, NULL); + + while (child_np) { + component_match_add(dev, &match, compare_of, child_np); + of_node_put(child_np); + child_np = of_get_next_available_child(node, child_np); + } + + return component_master_add_with_match(dev, &sti_drm_ops, match); +} + +static int sti_drm_master_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &sti_drm_ops); + return 0; +} + +static struct platform_driver sti_drm_master_driver = { + .probe = sti_drm_master_probe, + .remove = sti_drm_master_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME "__master", + }, +}; + +static int sti_drm_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct platform_device *master; + + of_platform_populate(node, NULL, NULL, dev); + + platform_driver_register(&sti_drm_master_driver); + master = platform_device_register_resndata(dev, + DRIVER_NAME "__master", -1, + NULL, 0, NULL, 0); + if (!master) + return -EINVAL; + + platform_set_drvdata(pdev, master); + return 0; +} + +static int sti_drm_platform_remove(struct platform_device *pdev) +{ + struct platform_device *master = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + platform_device_unregister(master); + platform_driver_unregister(&sti_drm_master_driver); + return 0; +} + +static const struct of_device_id sti_drm_dt_ids[] = { + { .compatible = "st,sti-display-subsystem", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, sti_drm_dt_ids); + +static struct platform_driver sti_drm_platform_driver = { + .probe = sti_drm_platform_probe, + .remove = sti_drm_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = sti_drm_dt_ids, + }, +}; + +module_platform_driver(sti_drm_platform_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_drm_drv.h b/drivers/gpu/drm/sti/sti_drm_drv.h new file mode 100644 index 0000000..ec5e2eb --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_drv.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_DRM_DRV_H_ +#define _STI_DRM_DRV_H_ + +#include + +struct sti_compositor; +struct sti_tvout; + +/** + * STI drm private structure + * This structure is stored as private in the drm_device + * + * @compo: compositor + * @plane_zorder_property: z-order property for CRTC planes + * @drm_dev: drm device + */ +struct sti_drm_private { + struct sti_compositor *compo; + struct drm_property *plane_zorder_property; + struct drm_device *drm_dev; +}; + +#endif diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c new file mode 100644 index 0000000..f4118d4 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_plane.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "sti_compositor.h" +#include "sti_drm_drv.h" +#include "sti_drm_plane.h" +#include "sti_vtg.h" + +enum sti_layer_desc sti_layer_default_zorder[] = { + STI_GDP_0, + STI_VID_0, + STI_GDP_1, + STI_VID_1, + STI_GDP_2, + STI_GDP_3, +}; + +/* (Background) < GDP0 < VID0 < GDP1 < VID1 < GDP2 < GDP3 < (ForeGround) */ + +static int +sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct sti_layer *layer = to_sti_layer(plane); + struct sti_mixer *mixer = to_sti_mixer(crtc); + int res; + + DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n", + crtc->base.id, sti_mixer_to_str(mixer), + plane->base.id, sti_layer_to_str(layer), fb->base.id); + DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y); + + res = sti_mixer_set_layer_depth(mixer, layer); + if (res) { + DRM_ERROR("Can not set layer depth\n"); + return res; + } + + /* src_x are in 16.16 format. */ + res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x >> 16, src_y >> 16, + src_w >> 16, src_h >> 16); + if (res) { + DRM_ERROR("Layer prepare failed\n"); + return res; + } + + res = sti_layer_commit(layer); + if (res) { + DRM_ERROR("Layer commit failed\n"); + return res; + } + + res = sti_mixer_set_layer_status(mixer, layer, true); + if (res) { + DRM_ERROR("Can not enable layer at mixer\n"); + return res; + } + + return 0; +} + +static int sti_drm_disable_plane(struct drm_plane *plane) +{ + struct sti_layer *layer; + struct sti_mixer *mixer; + int lay_res, mix_res; + + if (!plane->crtc) { + DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", plane->base.id); + return 0; + } + layer = to_sti_layer(plane); + mixer = to_sti_mixer(plane->crtc); + + DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", + plane->crtc->base.id, sti_mixer_to_str(mixer), + plane->base.id, sti_layer_to_str(layer)); + + /* Disable layer at mixer level */ + mix_res = sti_mixer_set_layer_status(mixer, layer, false); + if (mix_res) + DRM_ERROR("Can not disable layer at mixer\n"); + + /* Wait a while to be sure that a Vsync event is received */ + msleep(WAIT_NEXT_VSYNC_MS); + + /* Then disable layer itself */ + lay_res = sti_layer_disable(layer); + if (lay_res) + DRM_ERROR("Layer disable failed\n"); + + if (lay_res || mix_res) + return -EINVAL; + + return 0; +} + +static void sti_drm_plane_destroy(struct drm_plane *plane) +{ + DRM_DEBUG_DRIVER("\n"); + + sti_drm_disable_plane(plane); + drm_plane_cleanup(plane); +} + +static int sti_drm_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct sti_drm_private *private = dev->dev_private; + struct sti_layer *layer = to_sti_layer(plane); + + DRM_DEBUG_DRIVER("\n"); + + if (property == private->plane_zorder_property) { + layer->zorder = val; + return 0; + } + + return -EINVAL; +} + +static struct drm_plane_funcs sti_drm_plane_funcs = { + .update_plane = sti_drm_update_plane, + .disable_plane = sti_drm_disable_plane, + .destroy = sti_drm_plane_destroy, + .set_property = sti_drm_plane_set_property, +}; + +static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane, + uint64_t default_val) +{ + struct drm_device *dev = plane->dev; + struct sti_drm_private *private = dev->dev_private; + struct drm_property *prop; + struct sti_layer *layer = to_sti_layer(plane); + + prop = private->plane_zorder_property; + if (!prop) { + prop = drm_property_create_range(dev, 0, "zpos", 0, + GAM_MIXER_NB_DEPTH_LEVEL - 1); + if (!prop) + return; + + private->plane_zorder_property = prop; + } + + drm_object_attach_property(&plane->base, prop, default_val); + layer->zorder = default_val; +} + +struct drm_plane *sti_drm_plane_init(struct drm_device *dev, + struct sti_layer *layer, + unsigned int possible_crtcs, + enum drm_plane_type type) +{ + int err, i; + uint64_t default_zorder = 0; + + err = drm_universal_plane_init(dev, &layer->plane, possible_crtcs, + &sti_drm_plane_funcs, + sti_layer_get_formats(layer), + sti_layer_get_nb_formats(layer), type); + if (err) { + DRM_ERROR("Failed to initialize plane\n"); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++) + if (sti_layer_default_zorder[i] == layer->desc) + break; + + default_zorder = i; + + if (type == DRM_PLANE_TYPE_OVERLAY) + sti_drm_plane_attach_zorder_property(&layer->plane, + default_zorder); + + DRM_DEBUG_DRIVER("drm plane:%d mapped to %s with zorder:%llu\n", + layer->plane.base.id, + sti_layer_to_str(layer), default_zorder); + + return &layer->plane; +} diff --git a/drivers/gpu/drm/sti/sti_drm_plane.h b/drivers/gpu/drm/sti/sti_drm_plane.h new file mode 100644 index 0000000..4f19183 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drm_plane.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_DRM_PLANE_H_ +#define _STI_DRM_PLANE_H_ + +#include + +struct sti_layer; + +struct drm_plane *sti_drm_plane_init(struct drm_device *dev, + struct sti_layer *layer, + unsigned int possible_crtcs, + enum drm_plane_type type); +#endif -- cgit v0.10.2 From 0208d51112b65e27490bc21ab6872358903f7d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Marchesin?= Date: Thu, 19 Jun 2014 18:18:28 -0700 Subject: drm/panel: simple: Add bits-per-color support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bits-per-color is provided by the EDID normally, but if we're using panels, we need to store it somewhere. So we add a field to the panel descriptor for it. Signed-off-by: Stéphane Marchesin Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 357712c..fa2aea0 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -37,6 +37,8 @@ struct panel_desc { const struct drm_display_mode *modes; unsigned int num_modes; + unsigned int bpc; + struct { unsigned int width; unsigned int height; @@ -87,6 +89,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) num++; } + connector->display_info.bpc = panel->desc->bpc; connector->display_info.width_mm = panel->desc->size.width; connector->display_info.height_mm = panel->desc->size.height; @@ -285,6 +288,7 @@ static const struct drm_display_mode auo_b101aw03_mode = { static const struct panel_desc auo_b101aw03 = { .modes = &auo_b101aw03_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 223, .height = 125, @@ -307,6 +311,7 @@ static const struct drm_display_mode auo_b133xtn01_mode = { static const struct panel_desc auo_b133xtn01 = { .modes = &auo_b133xtn01_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 293, .height = 165, @@ -329,6 +334,7 @@ static const struct drm_display_mode chunghwa_claa101wa01a_mode = { static const struct panel_desc chunghwa_claa101wa01a = { .modes = &chunghwa_claa101wa01a_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 220, .height = 120, @@ -351,6 +357,7 @@ static const struct drm_display_mode chunghwa_claa101wb01_mode = { static const struct panel_desc chunghwa_claa101wb01 = { .modes = &chunghwa_claa101wb01_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 223, .height = 125, @@ -374,6 +381,7 @@ static const struct drm_display_mode edt_et057090dhu_mode = { static const struct panel_desc edt_et057090dhu = { .modes = &edt_et057090dhu_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 115, .height = 86, @@ -397,6 +405,7 @@ static const struct drm_display_mode edt_etm0700g0dh6_mode = { static const struct panel_desc edt_etm0700g0dh6 = { .modes = &edt_etm0700g0dh6_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 152, .height = 91, @@ -441,6 +450,7 @@ static const struct drm_display_mode innolux_n156bge_l21_mode = { static const struct panel_desc innolux_n156bge_l21 = { .modes = &innolux_n156bge_l21_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 344, .height = 193, @@ -463,6 +473,7 @@ static const struct drm_display_mode lg_lp129qe_mode = { static const struct panel_desc lg_lp129qe = { .modes = &lg_lp129qe_mode, .num_modes = 1, + .bpc = 8, .size = { .width = 272, .height = 181, @@ -485,6 +496,7 @@ static const struct drm_display_mode samsung_ltn101nt05_mode = { static const struct panel_desc samsung_ltn101nt05 = { .modes = &samsung_ltn101nt05_mode, .num_modes = 1, + .bpc = 6, .size = { .width = 1024, .height = 600, -- cgit v0.10.2 From a8f4d4d63739e4bca459ff40636f1d9e4b7ef5e6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 7 Feb 2014 19:17:21 +0000 Subject: drm/i2c: tda998x: allow re-use of tda998x support code Re-jig the TDA998x code so that we separate the functionality from the drm slave encoder implementation. In several places, this is pretty clearly the correct thing to do, because we can avoid repetitively having to convert from the drm_encoder to the TDA998x private structure, particularly with the driver internal functions. The main motivation behind this change is to allow the code to be re-used with a standard drm_encoder and drm_connector implementation based on the component helpers, rather than the slave_encoder system. The addition of this will be in the following patch. We keep the slave_encoder interface as there are existing users of this; we need to give them time to convert and test. Tested-by: Darren Etheridge Signed-off-by: Russell King diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 5a738ad..64e120c 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -730,12 +730,9 @@ tda998x_configure_audio(struct tda998x_priv *priv, /* DRM encoder functions */ -static void -tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) +static void tda998x_encoder_set_config(struct tda998x_priv *priv, + const struct tda998x_encoder_params *p) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); - struct tda998x_encoder_params *p = params; - priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) | (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) | VIP_CNTRL_0_SWAP_B(p->swap_b) | @@ -752,11 +749,8 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) priv->params = *p; } -static void -tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) +static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); - /* we only care about on or off: */ if (mode != DRM_MODE_DPMS_ON) mode = DRM_MODE_DPMS_OFF; @@ -806,9 +800,8 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder, return true; } -static int -tda998x_encoder_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) +static int tda998x_encoder_mode_valid(struct tda998x_priv *priv, + struct drm_display_mode *mode) { if (mode->clock > 150000) return MODE_CLOCK_HIGH; @@ -820,11 +813,10 @@ tda998x_encoder_mode_valid(struct drm_encoder *encoder, } static void -tda998x_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +tda998x_encoder_mode_set(struct tda998x_priv *priv, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); uint16_t ref_pix, ref_line, n_pix, n_line; uint16_t hs_pix_s, hs_pix_e; uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e; @@ -1012,20 +1004,16 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, } static enum drm_connector_status -tda998x_encoder_detect(struct drm_encoder *encoder, - struct drm_connector *connector) +tda998x_encoder_detect(struct tda998x_priv *priv) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV); return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : connector_status_disconnected; } -static int -read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) +static int read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); uint8_t offset, segptr; int ret, i; @@ -1079,10 +1067,8 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) return 0; } -static uint8_t * -do_get_edid(struct drm_encoder *encoder) +static uint8_t *do_get_edid(struct tda998x_priv *priv) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); int j, valid_extensions = 0; uint8_t *block, *new; bool print_bad_edid = drm_debug & DRM_UT_KMS; @@ -1094,7 +1080,7 @@ do_get_edid(struct drm_encoder *encoder) reg_clear(priv, REG_TX4, TX4_PD_RAM); /* base block fetch */ - if (read_edid_block(encoder, block, 0)) + if (read_edid_block(priv, block, 0)) goto fail; if (!drm_edid_block_valid(block, 0, print_bad_edid)) @@ -1111,7 +1097,7 @@ do_get_edid(struct drm_encoder *encoder) for (j = 1; j <= block[0x7e]; j++) { uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH; - if (read_edid_block(encoder, ext_block, j)) + if (read_edid_block(priv, ext_block, j)) goto fail; if (!drm_edid_block_valid(ext_block, j, print_bad_edid)) @@ -1144,11 +1130,10 @@ fail: } static int -tda998x_encoder_get_modes(struct drm_encoder *encoder, - struct drm_connector *connector) +tda998x_encoder_get_modes(struct tda998x_priv *priv, + struct drm_connector *connector) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); - struct edid *edid = (struct edid *)do_get_edid(encoder); + struct edid *edid = (struct edid *)do_get_edid(priv); int n = 0; if (edid) { @@ -1161,18 +1146,14 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder, return n; } -static int -tda998x_encoder_create_resources(struct drm_encoder *encoder, - struct drm_connector *connector) +static void tda998x_encoder_set_polling(struct tda998x_priv *priv, + struct drm_connector *connector) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); - if (priv->hdmi->irq) connector->polled = DRM_CONNECTOR_POLL_HPD; else connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - return 0; } static int @@ -1185,11 +1166,8 @@ tda998x_encoder_set_property(struct drm_encoder *encoder, return 0; } -static void -tda998x_encoder_destroy(struct drm_encoder *encoder) +static void tda998x_destroy(struct tda998x_priv *priv) { - struct tda998x_priv *priv = to_tda998x_priv(encoder); - /* disable all IRQs and free the IRQ handler */ cec_write(priv, REG_CEC_RXSHPDINTENA, 0); reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); @@ -1198,22 +1176,77 @@ tda998x_encoder_destroy(struct drm_encoder *encoder) if (priv->cec) i2c_unregister_device(priv->cec); +} + +/* Slave encoder support */ + +static void +tda998x_encoder_slave_set_config(struct drm_encoder *encoder, void *params) +{ + tda998x_encoder_set_config(to_tda998x_priv(encoder), params); +} + +static void tda998x_encoder_slave_destroy(struct drm_encoder *encoder) +{ + struct tda998x_priv *priv = to_tda998x_priv(encoder); + + tda998x_destroy(priv); drm_i2c_encoder_destroy(encoder); kfree(priv); } -static struct drm_encoder_slave_funcs tda998x_encoder_funcs = { - .set_config = tda998x_encoder_set_config, - .destroy = tda998x_encoder_destroy, - .dpms = tda998x_encoder_dpms, +static void tda998x_encoder_slave_dpms(struct drm_encoder *encoder, int mode) +{ + tda998x_encoder_dpms(to_tda998x_priv(encoder), mode); +} + +static int tda998x_encoder_slave_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + return tda998x_encoder_mode_valid(to_tda998x_priv(encoder), mode); +} + +static void +tda998x_encoder_slave_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + tda998x_encoder_mode_set(to_tda998x_priv(encoder), mode, adjusted_mode); +} + +static enum drm_connector_status +tda998x_encoder_slave_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + return tda998x_encoder_detect(to_tda998x_priv(encoder)); +} + +static int tda998x_encoder_slave_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + return tda998x_encoder_get_modes(to_tda998x_priv(encoder), connector); +} + +static int +tda998x_encoder_slave_create_resources(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + tda998x_encoder_set_polling(to_tda998x_priv(encoder), connector); + return 0; +} + +static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = { + .set_config = tda998x_encoder_slave_set_config, + .destroy = tda998x_encoder_slave_destroy, + .dpms = tda998x_encoder_slave_dpms, .save = tda998x_encoder_save, .restore = tda998x_encoder_restore, .mode_fixup = tda998x_encoder_mode_fixup, - .mode_valid = tda998x_encoder_mode_valid, - .mode_set = tda998x_encoder_mode_set, - .detect = tda998x_encoder_detect, - .get_modes = tda998x_encoder_get_modes, - .create_resources = tda998x_encoder_create_resources, + .mode_valid = tda998x_encoder_slave_mode_valid, + .mode_set = tda998x_encoder_slave_mode_set, + .detect = tda998x_encoder_slave_detect, + .get_modes = tda998x_encoder_slave_get_modes, + .create_resources = tda998x_encoder_slave_create_resources, .set_property = tda998x_encoder_set_property, }; @@ -1231,20 +1264,12 @@ tda998x_remove(struct i2c_client *client) return 0; } -static int -tda998x_encoder_init(struct i2c_client *client, - struct drm_device *dev, - struct drm_encoder_slave *encoder_slave) +static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) { - struct tda998x_priv *priv; struct device_node *np = client->dev.of_node; u32 video; int rev_lo, rev_hi, ret; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3); priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1); priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5); @@ -1252,17 +1277,11 @@ tda998x_encoder_init(struct i2c_client *client, priv->current_page = 0xff; priv->hdmi = client; priv->cec = i2c_new_dummy(client->adapter, 0x34); - if (!priv->cec) { - kfree(priv); + if (!priv->cec) return -ENODEV; - } - priv->encoder = &encoder_slave->base; priv->dpms = DRM_MODE_DPMS_OFF; - encoder_slave->slave_priv = priv; - encoder_slave->slave_funcs = &tda998x_encoder_funcs; - /* wake up the device: */ cec_write(priv, REG_CEC_ENAMODS, CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); @@ -1365,12 +1384,34 @@ fail: */ if (priv->cec) i2c_unregister_device(priv->cec); - kfree(priv); - encoder_slave->slave_priv = NULL; - encoder_slave->slave_funcs = NULL; return -ENXIO; } +static int tda998x_encoder_init(struct i2c_client *client, + struct drm_device *dev, + struct drm_encoder_slave *encoder_slave) +{ + struct tda998x_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->encoder = &encoder_slave->base; + + ret = tda998x_create(client, priv); + if (ret) { + kfree(priv); + return ret; + } + + encoder_slave->slave_priv = priv; + encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs; + + return 0; +} + #ifdef CONFIG_OF static const struct of_device_id tda998x_dt_ids[] = { { .compatible = "nxp,tda998x", }, -- cgit v0.10.2 From c707c3619ca81f499a5ce032021405e989a96ff0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 7 Feb 2014 19:49:44 +0000 Subject: drm/i2c: tda998x: add component support Add component helper support to the tda998x driver. This permits the TDA998x to be declared as a separate device in device tree, and bound at the appropriate moment with a co-operating card driver. The existing slave_encoder interfaces are kept while there are existing users of it in order to prevent regressions. Tested-by: Darren Etheridge Signed-off-by: Russell King diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 64e120c..3b07ff7 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -15,8 +15,7 @@ * this program. If not, see . */ - - +#include #include #include #include @@ -1252,18 +1251,6 @@ static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = { /* I2C driver functions */ -static int -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - return 0; -} - -static int -tda998x_remove(struct i2c_client *client) -{ - return 0; -} - static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) { struct device_node *np = client->dev.of_node; @@ -1412,6 +1399,203 @@ static int tda998x_encoder_init(struct i2c_client *client, return 0; } +struct tda998x_priv2 { + struct tda998x_priv base; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +#define conn_to_tda998x_priv2(x) \ + container_of(x, struct tda998x_priv2, connector); + +#define enc_to_tda998x_priv2(x) \ + container_of(x, struct tda998x_priv2, encoder); + +static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode) +{ + struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); + + tda998x_encoder_dpms(&priv->base, mode); +} + +static void tda998x_encoder_prepare(struct drm_encoder *encoder) +{ + tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void tda998x_encoder_commit(struct drm_encoder *encoder) +{ + tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static void tda998x_encoder2_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); + + tda998x_encoder_mode_set(&priv->base, mode, adjusted_mode); +} + +static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = { + .dpms = tda998x_encoder2_dpms, + .save = tda998x_encoder_save, + .restore = tda998x_encoder_restore, + .mode_fixup = tda998x_encoder_mode_fixup, + .prepare = tda998x_encoder_prepare, + .commit = tda998x_encoder_commit, + .mode_set = tda998x_encoder2_mode_set, +}; + +static void tda998x_encoder_destroy(struct drm_encoder *encoder) +{ + struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder); + + tda998x_destroy(&priv->base); + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs tda998x_encoder_funcs = { + .destroy = tda998x_encoder_destroy, +}; + +static int tda998x_connector_get_modes(struct drm_connector *connector) +{ + struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); + + return tda998x_encoder_get_modes(&priv->base, connector); +} + +static int tda998x_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); + + return tda998x_encoder_mode_valid(&priv->base, mode); +} + +static struct drm_encoder * +tda998x_connector_best_encoder(struct drm_connector *connector) +{ + struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); + + return &priv->encoder; +} + +static +const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = { + .get_modes = tda998x_connector_get_modes, + .mode_valid = tda998x_connector_mode_valid, + .best_encoder = tda998x_connector_best_encoder, +}; + +static enum drm_connector_status +tda998x_connector_detect(struct drm_connector *connector, bool force) +{ + struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector); + + return tda998x_encoder_detect(&priv->base); +} + +static void tda998x_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs tda998x_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = tda998x_connector_detect, + .destroy = tda998x_connector_destroy, +}; + +static int tda998x_bind(struct device *dev, struct device *master, void *data) +{ + struct tda998x_encoder_params *params = dev->platform_data; + struct i2c_client *client = to_i2c_client(dev); + struct drm_device *drm = data; + struct tda998x_priv2 *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + priv->base.encoder = &priv->encoder; + priv->connector.interlace_allowed = 1; + priv->encoder.possible_crtcs = 1 << 0; + + ret = tda998x_create(client, &priv->base); + if (ret) + return ret; + + if (!dev->of_node && params) + tda998x_encoder_set_config(&priv->base, params); + + tda998x_encoder_set_polling(&priv->base, &priv->connector); + + drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs); + ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + if (ret) + goto err_encoder; + + drm_connector_helper_add(&priv->connector, + &tda998x_connector_helper_funcs); + ret = drm_connector_init(drm, &priv->connector, + &tda998x_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) + goto err_connector; + + ret = drm_sysfs_connector_add(&priv->connector); + if (ret) + goto err_sysfs; + + priv->connector.encoder = &priv->encoder; + drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder); + + return 0; + +err_sysfs: + drm_connector_cleanup(&priv->connector); +err_connector: + drm_encoder_cleanup(&priv->encoder); +err_encoder: + tda998x_destroy(&priv->base); + return ret; +} + +static void tda998x_unbind(struct device *dev, struct device *master, + void *data) +{ + struct tda998x_priv2 *priv = dev_get_drvdata(dev); + + drm_connector_cleanup(&priv->connector); + drm_encoder_cleanup(&priv->encoder); + tda998x_destroy(&priv->base); +} + +static const struct component_ops tda998x_ops = { + .bind = tda998x_bind, + .unbind = tda998x_unbind, +}; + +static int +tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + return component_add(&client->dev, &tda998x_ops); +} + +static int tda998x_remove(struct i2c_client *client) +{ + component_del(&client->dev, &tda998x_ops); + return 0; +} + #ifdef CONFIG_OF static const struct of_device_id tda998x_dt_ids[] = { { .compatible = "nxp,tda998x", }, -- cgit v0.10.2 From b008c0fc95140ca7aad65861a77f0338ce31e8b5 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Jul 2014 17:26:36 +0200 Subject: drm: remove unused "struct drm_freelist" This object is not used except for static fields in drm_bufs *cough*. Inline the watermark fields and drop the unused structure definition. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 68175b5..61acb8f 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1217,7 +1217,6 @@ int drm_infobufs(struct drm_device *dev, void *data, struct drm_buf_desc __user *to = &request->list[count]; struct drm_buf_entry *from = &dma->bufs[i]; - struct drm_freelist *list = &dma->bufs[i].freelist; if (copy_to_user(&to->count, &from->buf_count, sizeof(from->buf_count)) || @@ -1225,19 +1224,19 @@ int drm_infobufs(struct drm_device *dev, void *data, &from->buf_size, sizeof(from->buf_size)) || copy_to_user(&to->low_mark, - &list->low_mark, - sizeof(list->low_mark)) || + &from->low_mark, + sizeof(from->low_mark)) || copy_to_user(&to->high_mark, - &list->high_mark, - sizeof(list->high_mark))) + &from->high_mark, + sizeof(from->high_mark))) return -EFAULT; DRM_DEBUG("%d %d %d %d %d\n", i, dma->bufs[i].buf_count, dma->bufs[i].buf_size, - dma->bufs[i].freelist.low_mark, - dma->bufs[i].freelist.high_mark); + dma->bufs[i].low_mark, + dma->bufs[i].high_mark); ++count; } } @@ -1290,8 +1289,8 @@ int drm_markbufs(struct drm_device *dev, void *data, if (request->high_mark < 0 || request->high_mark > entry->buf_count) return -EINVAL; - entry->freelist.low_mark = request->low_mark; - entry->freelist.high_mark = request->high_mark; + entry->low_mark = request->low_mark; + entry->high_mark = request->high_mark; return 0; } diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 86feedd..ecaf0fa 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -132,7 +132,7 @@ int drm_bufs_info(struct seq_file *m, void *data) i, dma->bufs[i].buf_size, dma->bufs[i].buf_count, - atomic_read(&dma->bufs[i].freelist.count), + 0, dma->bufs[i].seg_count, seg_pages, seg_pages * PAGE_SIZE / 1024); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 9b6a445..335b7b8 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -346,18 +346,6 @@ struct drm_waitlist { spinlock_t write_lock; }; -struct drm_freelist { - int initialized; /**< Freelist in use */ - atomic_t count; /**< Number of free buffers */ - struct drm_buf *next; /**< End pointer */ - - wait_queue_head_t waiting; /**< Processes waiting on free bufs */ - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ - atomic_t wfh; /**< If waiting for high mark */ - spinlock_t lock; -}; - typedef struct drm_dma_handle { dma_addr_t busaddr; void *vaddr; @@ -375,7 +363,8 @@ struct drm_buf_entry { int page_order; struct drm_dma_handle **seglist; - struct drm_freelist freelist; + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ }; /* Event queued up for userspace to read */ -- cgit v0.10.2 From 344f4b0f4c14a12206b05aa033f3174cefbd282f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Jul 2014 17:26:37 +0200 Subject: drm: drop unused "struct drm_queue" This object is unused, drop it. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 335b7b8..d3d9be6 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -430,23 +430,6 @@ struct drm_file { struct drm_prime_file_private prime; }; -/** Wait queue */ -struct drm_queue { - atomic_t use_count; /**< Outstanding uses (+1) */ - atomic_t finalization; /**< Finalization in progress */ - atomic_t block_count; /**< Count of processes waiting */ - atomic_t block_read; /**< Queue blocked for reads */ - wait_queue_head_t read_queue; /**< Processes waiting on block_read */ - atomic_t block_write; /**< Queue blocked for writes */ - wait_queue_head_t write_queue; /**< Processes waiting on block_write */ - atomic_t total_queued; /**< Total queued statistic */ - atomic_t total_flushed; /**< Total flushes statistic */ - atomic_t total_locks; /**< Total locks statistics */ - enum drm_ctx_flags flags; /**< Context preserving and 2D-only */ - struct drm_waitlist waitlist; /**< Pending buffers */ - wait_queue_head_t flush_queue; /**< Processes waiting until flush */ -}; - /** * Lock data. */ -- cgit v0.10.2 From 1eac887772928a61808339f51166be7f8b23babd Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Jul 2014 17:26:40 +0200 Subject: drm: drop i386 verification Linux doesn't run on i386, anymore. See: commit d55c5a93db2d5fa95f233ab153f594365d95b777 Author: H. Peter Anvin Date: Wed Nov 28 11:50:24 2012 -0800 x86, 386 removal: Remove CONFIG_CMPXCHG All 486+ CPUs support CMPXCHG, so remove the fallback 386 support code. Furthermore, as the commit-message states, all 486+ CPUs support the CMPXCHG instruction and thus even legacy DRM can run fine. Drop the now superfluous "x86 == 3" check. Signed-off-by: David Herrmann Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 021fe5d..bc583fe 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -157,10 +157,6 @@ out_unlock: */ static int drm_cpu_valid(void) { -#if defined(__i386__) - if (boot_cpu_data.x86 == 3) - return 0; /* No cmpxchg on a 386 */ -#endif #if defined(__sparc__) && !defined(__sparc_v9__) return 0; /* No cmpxchg before v9 sparc. */ #endif -- cgit v0.10.2 From 08d645c181e9b9482c249e82a061c279a986db15 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sun, 15 Jun 2014 23:02:45 +0200 Subject: agp: remove read/write stubs The VFS layer handles those in the very same way, if unset. No need for additional stubs. Signed-off-by: Mathias Krause Cc: Alexander Viro Signed-off-by: Dave Airlie diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index b297033..09f17eb 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -710,19 +710,6 @@ static int agp_open(struct inode *inode, struct file *file) return 0; } - -static ssize_t agp_read(struct file *file, char __user *buf, - size_t count, loff_t * ppos) -{ - return -EINVAL; -} - -static ssize_t agp_write(struct file *file, const char __user *buf, - size_t count, loff_t * ppos) -{ - return -EINVAL; -} - static int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg) { struct agp_info userinfo; @@ -1047,8 +1034,6 @@ static const struct file_operations agp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, - .read = agp_read, - .write = agp_write, .unlocked_ioctl = agp_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_agp_ioctl, -- cgit v0.10.2 From d50a1907a3675300da14105add721826d73df63f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 30 Jun 2014 15:25:44 +0200 Subject: drm/exynos: Fix NULL pointer exception when suspending without components Fix a NULL pointer exception when main exynos drm driver was probed successfully but no components were added (e.g. by incomplete DTS). In such case the exynos_drm_load() is never called and drvdata is NULL. The NULL pointer exception may theoretically also happen as a effect of race between adding components and main driver: if suspend of the driver happens before adding components. Trace: [ 1.190295] [drm] Initialized drm 1.1.0 20060810 [ 1.195209] exynos-drm-ipp exynos-drm-ipp: drm ipp registered successfully. (...) [ 24.001743] PM: Syncing filesystems ... done. [ 24.002177] Freezing user space processes ... (elapsed 0.000 seconds) done. [ 24.007403] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done. [ 24.032559] Unable to handle kernel NULL pointer dereference at virtual address 00000134 [ 24.035007] pgd = dedd8000 [ 24.037734] [00000134] *pgd=5ee13831, *pte=00000000, *ppte=00000000 [ 24.043953] Internal error: Oops: 17 [#1] PREEMPT SMP ARM [ 24.049329] Modules linked in: [ 24.052371] CPU: 0 PID: 1 Comm: sh Not tainted 3.16.0-rc3-00035-geba20bbdde04-dirty #51 [ 24.060354] task: df478000 ti: df480000 task.ti: df480000 [ 24.065743] PC is at mutex_lock+0x10/0x50 [ 24.069733] LR is at drm_modeset_lock_all+0x30/0xbc [ 24.074590] pc : [] lr : [] psr: a0000013 [ 24.074590] sp : df481db8 ip : 00000000 fp : c05e524c [ 24.086045] r10: 00000002 r9 : c02c1fe4 r8 : deca5e44 [ 24.091253] r7 : 00000000 r6 : 00000000 r5 : 0000014c r4 : 00000134 [ 24.097763] r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : 00000134 [ 24.104275] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 24.111391] Control: 10c53c7d Table: 5edd806a DAC: 00000015 [ 24.117120] Process sh (pid: 1, stack limit = 0xdf480240) [ 24.122502] Stack: (0xdf481db8 to 0xdf482000) [ 24.126843] 1da0: dee01d80 c02a14b4 [ 24.135004] 1dc0: 00000000 00000000 c07aff98 c02aec7c 00000002 00000000 00000000 c07aff98 [ 24.143164] 1de0: deca5e10 c02aecf4 c02aecd4 c02c2010 00000000 c02c9470 00000000 00000000 [ 24.151322] 1e00: 00000000 00000000 deca5e10 deca5e10 00000000 c07aff98 00000002 deca5e44 [ 24.159482] 1e20: c06d8f78 c06fb800 deca5e78 c02ca660 df7baf00 007b0aa0 deca5e10 c06fb7c8 [ 24.167641] 1e40: c07aff98 00000000 00000002 c02cbe18 9757aec5 00000005 9757aec5 00000005 [ 24.175801] 1e60: ded1d380 00000003 00000003 c05c74d8 ded1d380 c07209d4 c05c7514 c07105d8 [ 24.183960] 1e80: 01e2a738 c0068a74 00000000 c05c7514 ded1d380 c071c6e0 00000004 c07105d8 [ 24.192119] 1ea0: 01e2a738 c047f1e0 c0600cc0 df481ec4 00000003 00000000 00000003 c05c74d8 [ 24.200278] 1ec0: ded1d380 c071c6e0 c05c7514 c07105d8 01e2a738 c0069444 c06d905c 00000003 [ 24.208438] 1ee0: 00000003 ded1d380 c06d9064 00000004 c05c3fc0 c0067d4c df535ab0 ded1d380 [ 24.216596] 1f00: df481f80 ded1d380 00000004 ded1d1cc ded1d1c0 c0221724 00000004 c016ca6c [ 24.224756] 1f20: c016ca28 00000000 00000000 c016c1d4 00000000 00000000 b6f37000 df481f80 [ 24.232915] 1f40: decedd80 00000004 df480000 df480000 b6f37000 c0110920 df47839c 60000013 [ 24.241074] 1f60: 00000000 00000000 decedd80 decedd80 00000004 df480000 b6f37000 c0110da8 [ 24.249233] 1f80: 00000000 00000000 00000004 b6edf5d8 00000004 b6f37000 00000004 c000f2a8 [ 24.257393] 1fa0: 00001000 c000f0e0 b6edf5d8 00000004 00000001 b6f37000 00000004 00000000 [ 24.265551] 1fc0: b6edf5d8 00000004 b6f37000 00000004 00000004 00000001 00000000 01e2a738 [ 24.273711] 1fe0: 00000000 beba0a20 b6e1f4f0 b6e7022c 60000010 00000001 ffffffff ffffffff [ 24.281885] [] (mutex_lock) from [] (drm_modeset_lock_all+0x30/0xbc) [ 24.289950] [] (drm_modeset_lock_all) from [] (exynos_drm_suspend+0xc/0x64) [ 24.298627] [] (exynos_drm_suspend) from [] (exynos_drm_sys_suspend+0x20/0x34) [ 24.307568] [] (exynos_drm_sys_suspend) from [] (platform_pm_suspend+0x2c/0x54) [ 24.316597] [] (platform_pm_suspend) from [] (dpm_run_callback+0x48/0x170) [ 24.325188] [] (dpm_run_callback) from [] (__device_suspend+0x128/0x39c) [ 24.333606] [] (__device_suspend) from [] (dpm_suspend+0x5c/0x314) [ 24.341506] [] (dpm_suspend) from [] (suspend_devices_and_enter+0x8c/0x598) [ 24.350185] [] (suspend_devices_and_enter) from [] (pm_suspend+0x4c4/0x5d0) [ 24.358862] [] (pm_suspend) from [] (state_store+0x70/0xd4) [ 24.366156] [] (state_store) from [] (kobj_attr_store+0x14/0x20) [ 24.373885] [] (kobj_attr_store) from [] (sysfs_kf_write+0x44/0x48) [ 24.381867] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xc0/0x17c) [ 24.390027] [] (kernfs_fop_write) from [] (vfs_write+0xa0/0x1c4) [ 24.397750] [] (vfs_write) from [] (SyS_write+0x40/0x8c) [ 24.404782] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x3c) [ 24.412332] Code: e92d4010 e1a04000 f57ff05b f590f000 (e1903f9f) [ 24.418448] ---[ end trace cfa06690eabe8dd5 ]--- [ 24.423032] Kernel panic - not syncing: Fatal exception [ 24.428220] CPU1: stopping [ 24.430905] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G D 3.16.0-rc3-00035-geba20bbdde04-dirty #51 [ 24.440549] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 24.448269] [] (show_stack) from [] (dump_stack+0x80/0xcc) [ 24.455472] [] (dump_stack) from [] (handle_IPI+0x130/0x15c) [ 24.462850] [] (handle_IPI) from [] (gic_handle_irq+0x60/0x68) [ 24.470400] [] (gic_handle_irq) from [] (__irq_svc+0x40/0x70) [ 24.477860] Exception stack(0xdf4bdf88 to 0xdf4bdfd0) [ 24.482898] df80: ffffffed 00000000 00000000 00000000 df4bc000 c06d042c [ 24.491058] dfa0: 00000000 ffffffed c06d03c0 00000000 c070c288 00000000 00000000 df4bdfd0 [ 24.499214] dfc0: c0010324 c0010328 60000013 ffffffff [ 24.504254] [] (__irq_svc) from [] (arch_cpu_idle+0x28/0x30) [ 24.511634] [] (arch_cpu_idle) from [] (cpu_startup_entry+0x2c4/0x3f0) [ 24.519878] [] (cpu_startup_entry) from [<400086c4>] (0x400086c4) [ 24.526821] ---[ end Kernel panic - not syncing: Fatal exception Signed-off-by: Krzysztof Kozlowski Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index d82e3cb..0d74e9b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -358,7 +358,7 @@ static int exynos_drm_sys_suspend(struct device *dev) struct drm_device *drm_dev = dev_get_drvdata(dev); pm_message_t message; - if (pm_runtime_suspended(dev)) + if (pm_runtime_suspended(dev) || !drm_dev) return 0; message.event = PM_EVENT_SUSPEND; @@ -369,7 +369,7 @@ static int exynos_drm_sys_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - if (pm_runtime_suspended(dev)) + if (pm_runtime_suspended(dev) || !drm_dev) return 0; return exynos_drm_resume(drm_dev); -- cgit v0.10.2 From 7a9bf6e5c4c6448283b8087802bd5c8b3ebb528c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jul 2014 09:33:07 +0530 Subject: drm/exynos: Remove unused variable from exynos_hdmi.c 'frame_size_code' is not used in the function. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 81df11d..738f2d7 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1241,14 +1241,13 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr) static void hdmi_audio_init(struct hdmi_context *hdata) { - u32 sample_rate, bits_per_sample, frame_size_code; + u32 sample_rate, bits_per_sample; u32 data_num, bit_ch, sample_frq; u32 val; u8 acr[7]; sample_rate = 44100; bits_per_sample = 16; - frame_size_code = 0; switch (bits_per_sample) { case 20: -- cgit v0.10.2 From 4a958d38277936427500e741d346ec784e1d23f1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jul 2014 09:33:08 +0530 Subject: drm/exynos: Remove unused variable in exynos_drm_gem.c 'exynos_gem_obj' is not used in the function. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 163a054..15db801 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -301,7 +301,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, unsigned int gem_handle, struct drm_file *filp) { - struct exynos_drm_gem_obj *exynos_gem_obj; struct drm_gem_object *obj; obj = drm_gem_object_lookup(dev, filp, gem_handle); @@ -310,8 +309,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, return; } - exynos_gem_obj = to_exynos_gem_obj(obj); - drm_gem_object_unreference_unlocked(obj); /* -- cgit v0.10.2 From 8f589bba25201b4574ad56ff56b153b10595e3ef Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 3 Jun 2014 21:46:11 +0900 Subject: drm/exynos: dp: Use correct module license According to the header comment in the source file, the driver is licensed under GPL v2, so update MODULE_LICENSE() to match that. Signed-off-by: Jingoo Han Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 86dc69d..845d766 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -1390,4 +1390,4 @@ struct platform_driver dp_driver = { MODULE_AUTHOR("Jingoo Han "); MODULE_DESCRIPTION("Samsung SoC DP Driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 05fdf98742e41e89816d115decaf478c28477ab9 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 1 Jul 2014 10:10:06 +0200 Subject: drm/exynos: hdmi: make 'hdmi-en' regulator optional and keep it enabled HDMI_EN regulator is additional regulator for providing voltage source for DCC lines available on HDMI connector. When there is no power provided for DDC epprom, some TV-sets do not pulls up HPD (hot plug detect) line, what causes HDMI block to stay turned off. This patch enables HDMI_EN regulator (if available) on driver probe and keep it enabled all the time to let TV-set correctly signal HPD event. Signed-off-by: Marek Szyprowski Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 738f2d7..9ec787f 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -84,6 +84,7 @@ struct hdmi_resources { struct clk *sclk_hdmiphy; struct clk *mout_hdmi; struct regulator_bulk_data *regul_bulk; + struct regulator *reg_hdmi_en; int regul_count; }; @@ -2167,7 +2168,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata) struct device *dev = hdata->dev; struct hdmi_resources *res = &hdata->res; static char *supply[] = { - "hdmi-en", "vdd", "vdd_osc", "vdd_pll", @@ -2227,6 +2227,20 @@ static int hdmi_resources_init(struct hdmi_context *hdata) } res->regul_count = ARRAY_SIZE(supply); + res->reg_hdmi_en = devm_regulator_get(dev, "hdmi-en"); + if (IS_ERR(res->reg_hdmi_en) && PTR_ERR(res->reg_hdmi_en) != -ENOENT) { + DRM_ERROR("failed to get hdmi-en regulator\n"); + return PTR_ERR(res->reg_hdmi_en); + } + if (!IS_ERR(res->reg_hdmi_en)) { + ret = regulator_enable(res->reg_hdmi_en); + if (ret) { + DRM_ERROR("failed to enable hdmi-en regulator\n"); + return ret; + } + } else + res->reg_hdmi_en = NULL; + return ret; fail: DRM_ERROR("HDMI resource init - failed\n"); @@ -2493,6 +2507,9 @@ static int hdmi_remove(struct platform_device *pdev) cancel_delayed_work_sync(&hdata->hotplug_work); + if (hdata->res.reg_hdmi_en) + regulator_disable(hdata->res.reg_hdmi_en); + put_device(&hdata->hdmiphy_port->dev); put_device(&hdata->ddc_adpt->dev); -- cgit v0.10.2 From ff830c961d44cd0b3cf483a6c7a5a175c3419427 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 1 Jul 2014 10:10:07 +0200 Subject: drm/exynos: hdmi: enable exynos 4210 and 4x12 soc support Configuration sets for Exynos 4210 and 4x12 SoC were already defined in Exynos HDMI and Mixed drivers, but they lacked proper linking to device tree 'compatible' values. This patch fixes this issue adding support for following compatible values: samsung,exynos4210-mixer, samsung,exynos4212-mixer and samsung,exynos4210-hdmi. It also corrects access to sclk_mixer clock, which is available only on Exynos 4210. Signed-off-by: Marek Szyprowski Signed-off-by: Inki Dae diff --git a/Documentation/devicetree/bindings/video/exynos_mixer.txt b/Documentation/devicetree/bindings/video/exynos_mixer.txt index 7bfde9c..08b394b 100644 --- a/Documentation/devicetree/bindings/video/exynos_mixer.txt +++ b/Documentation/devicetree/bindings/video/exynos_mixer.txt @@ -4,8 +4,9 @@ Required properties: - compatible: value should be one of the following: 1) "samsung,exynos5-mixer" 2) "samsung,exynos4210-mixer" - 3) "samsung,exynos5250-mixer" - 4) "samsung,exynos5420-mixer" + 3) "samsung,exynos4212-mixer" + 4) "samsung,exynos5250-mixer" + 5) "samsung,exynos5420-mixer" - reg: physical base address of the mixer and length of memory mapped region. diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 9ec787f..fd8141f 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -593,6 +593,13 @@ static struct hdmi_driver_data exynos4212_hdmi_driver_data = { .is_apb_phy = 0, }; +static struct hdmi_driver_data exynos4210_hdmi_driver_data = { + .type = HDMI_TYPE13, + .phy_confs = hdmiphy_v13_configs, + .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs), + .is_apb_phy = 0, +}; + static struct hdmi_driver_data exynos5_hdmi_driver_data = { .type = HDMI_TYPE14, .phy_confs = hdmiphy_v13_configs, @@ -2276,6 +2283,9 @@ static struct of_device_id hdmi_match_types[] = { .compatible = "samsung,exynos5-hdmi", .data = &exynos5_hdmi_driver_data, }, { + .compatible = "samsung,exynos4210-hdmi", + .data = &exynos4210_hdmi_driver_data, + }, { .compatible = "samsung,exynos4212-hdmi", .data = &exynos4212_hdmi_driver_data, }, { diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 7529946..9d0c21a 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -76,7 +76,7 @@ struct mixer_resources { struct clk *vp; struct clk *sclk_mixer; struct clk *sclk_hdmi; - struct clk *sclk_dac; + struct clk *mout_mixer; }; enum mixer_version_id { @@ -93,6 +93,7 @@ struct mixer_context { bool interlace; bool powered; bool vp_enabled; + bool has_sclk; u32 int_en; struct mutex mixer_mutex; @@ -106,6 +107,7 @@ struct mixer_context { struct mixer_drv_data { enum mixer_version_id version; bool is_vp_enabled; + bool has_sclk; }; static const u8 filter_y_horiz_tap8[] = { @@ -809,19 +811,23 @@ static int vp_resources_init(struct mixer_context *mixer_ctx) dev_err(dev, "failed to get clock 'vp'\n"); return -ENODEV; } - mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer"); - if (IS_ERR(mixer_res->sclk_mixer)) { - dev_err(dev, "failed to get clock 'sclk_mixer'\n"); - return -ENODEV; - } - mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac"); - if (IS_ERR(mixer_res->sclk_dac)) { - dev_err(dev, "failed to get clock 'sclk_dac'\n"); - return -ENODEV; - } - if (mixer_res->sclk_hdmi) - clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); + if (mixer_ctx->has_sclk) { + mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer"); + if (IS_ERR(mixer_res->sclk_mixer)) { + dev_err(dev, "failed to get clock 'sclk_mixer'\n"); + return -ENODEV; + } + mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer"); + if (IS_ERR(mixer_res->mout_mixer)) { + dev_err(dev, "failed to get clock 'mout_mixer'\n"); + return -ENODEV; + } + + if (mixer_res->sclk_hdmi && mixer_res->mout_mixer) + clk_set_parent(mixer_res->mout_mixer, + mixer_res->sclk_hdmi); + } res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1); if (res == NULL) { @@ -1082,7 +1088,8 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) clk_prepare_enable(res->mixer); if (ctx->vp_enabled) { clk_prepare_enable(res->vp); - clk_prepare_enable(res->sclk_mixer); + if (ctx->has_sclk) + clk_prepare_enable(res->sclk_mixer); } mutex_lock(&ctx->mixer_mutex); @@ -1121,7 +1128,8 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr) clk_disable_unprepare(res->mixer); if (ctx->vp_enabled) { clk_disable_unprepare(res->vp); - clk_disable_unprepare(res->sclk_mixer); + if (ctx->has_sclk) + clk_disable_unprepare(res->sclk_mixer); } pm_runtime_put_sync(ctx->dev); @@ -1189,9 +1197,15 @@ static struct mixer_drv_data exynos5250_mxr_drv_data = { .is_vp_enabled = 0, }; +static struct mixer_drv_data exynos4212_mxr_drv_data = { + .version = MXR_VER_0_0_0_16, + .is_vp_enabled = 1, +}; + static struct mixer_drv_data exynos4210_mxr_drv_data = { .version = MXR_VER_0_0_0_16, .is_vp_enabled = 1, + .has_sclk = 1, }; static struct platform_device_id mixer_driver_types[] = { @@ -1208,6 +1222,12 @@ static struct platform_device_id mixer_driver_types[] = { static struct of_device_id mixer_match_types[] = { { + .compatible = "samsung,exynos4210-mixer", + .data = &exynos4210_mxr_drv_data, + }, { + .compatible = "samsung,exynos4212-mixer", + .data = &exynos4212_mxr_drv_data, + }, { .compatible = "samsung,exynos5-mixer", .data = &exynos5250_mxr_drv_data, }, { @@ -1251,6 +1271,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ctx->pdev = pdev; ctx->dev = dev; ctx->vp_enabled = drv->is_vp_enabled; + ctx->has_sclk = drv->has_sclk; ctx->mxr_ver = drv->version; init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0); -- cgit v0.10.2 From 2f36e33aef211815f172a2310e5759cb0d0843bf Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Thu, 17 Jul 2014 18:01:16 +0900 Subject: drm/exynos: dsi: move the EoT packets configuration point This configuration could be used in MIPI DSI command mode also. And adds user manual description for display configuration. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Reviewed-by: Andrzej Hajda Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 2df3592..58bfb2a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -468,13 +468,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) /* DSI configuration */ reg = 0; + /* + * The first bit of mode_flags specifies display configuration. + * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video + * mode, otherwise it will support command mode. + */ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { reg |= DSIM_VIDEO_MODE; + /* + * The user manual describes that following bits are ignored in + * command mode. + */ if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) reg |= DSIM_MFLUSH_VS; - if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) - reg |= DSIM_EOT_DISABLE; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) reg |= DSIM_SYNC_INFORM; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) @@ -491,6 +498,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) reg |= DSIM_HSA_MODE; } + if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) + reg |= DSIM_EOT_DISABLE; + switch (dsi->format) { case MIPI_DSI_FMT_RGB888: reg |= DSIM_MAIN_PIX_FORMAT_RGB888; -- cgit v0.10.2 From e35d7223de18e01df8922676464572db8b1453a1 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Thu, 17 Jul 2014 18:01:17 +0900 Subject: drm/exynos: use wait_event_timeout() for safety usage There could be the case that the page flip operation isn't finished correctly with some abnormal condition such as panel reset. So this patch replaces wait_event() with wait_event_timeout() to avoid waiting for page flip completion infinitely. And clears exynos_crtc->pending_flip in exynos_drm_crtc_page_flip() when exynos_drm_crtc_mode_set_commit() is failed. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Reviewed-by: Andrzej Hajda Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 95c9435..3bf091d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -69,8 +69,10 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) if (mode > DRM_MODE_DPMS_ON) { /* wait for the completion of page flip. */ - wait_event(exynos_crtc->pending_flip_queue, - atomic_read(&exynos_crtc->pending_flip) == 0); + if (!wait_event_timeout(exynos_crtc->pending_flip_queue, + !atomic_read(&exynos_crtc->pending_flip), + HZ/20)) + atomic_set(&exynos_crtc->pending_flip, 0); drm_vblank_off(crtc->dev, exynos_crtc->pipe); } @@ -259,6 +261,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, spin_lock_irq(&dev->event_lock); drm_vblank_put(dev, exynos_crtc->pipe); list_del(&event->base.link); + atomic_set(&exynos_crtc->pending_flip, 0); spin_unlock_irq(&dev->event_lock); goto out; -- cgit v0.10.2 From 8e1c06cf65819d9e0fff061324f9f7edff83583e Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Thu, 17 Jul 2014 18:01:18 +0900 Subject: ARM: dts: samsung-fimd: add LCD I80 interface specific properties In case of using MIPI DSI based I80 interface panel, the relevant registers should be set. So this patch adds relevant DT bindings. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Signed-off-by: Inki Dae diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..8428fcf 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,34 @@ Optional Properties: - display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel. +- samsung,sysreg: handle to syscon used to control the system registers +- i80-if-timings: timing configuration for lcd i80 interface support. + - cs-setup: clock cycles for the active period of address signal is enabled + until chip select is enabled. + If not specified, the default value(0) will be used. + - wr-setup: clock cycles for the active period of CS signal is enabled until + write signal is enabled. + If not specified, the default value(0) will be used. + - wr-active: clock cycles for the active period of CS is enabled. + If not specified, the default value(1) will be used. + - wr-hold: clock cycles for the active period of CS is disabled until write + signal is disabled. + If not specified, the default value(0) will be used. + + The parameters are defined as: + + VCLK(internal) __|??????|_____|??????|_____|??????|_____|??????|_____|?? + : : : : : + Address Output --:| : : : + Chip Select ???????????????|____________:____________:____________|?? + | wr-setup+1 | | wr-hold+1 | + |<---------->| |<---------->| + Write Enable ????????????????????????????|____________|??????????????? + | wr-active+1| + |<---------->| + Video Data ------------------------------ The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes: -- cgit v0.10.2 From 5595d4d821033d4d7c4cc394b3bbc76b020f0342 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Thu, 17 Jul 2014 18:01:19 +0900 Subject: drm/exynos: add TE handler to support LCD I80 interface To support LCD I80 interface, the panel should generate Tearing Effect synchronization signal between MCU and FB to display video images. And the display controller should trigger to transfer video image at this signal. So the panel receives the TE IRQ, then calls these handler chains to notify it to the display controller. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 3bf091d..b68e58f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -511,3 +511,11 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, return -EPERM; } + +void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + + if (manager->ops->te_handler) + manager->ops->te_handler(manager); +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 9f74b10..690dcdd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -36,4 +36,11 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos); int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, unsigned int out_type); +/* + * This function calls the crtc device(manager)'s te_handler() callback + * to trigger to transfer video image at the tearing effect synchronization + * signal. + */ +void exynos_drm_crtc_te_handler(struct drm_crtc *crtc); + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 02f3b3d..13be498 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -186,6 +186,8 @@ struct exynos_drm_display { * @win_commit: apply hardware specific overlay data to registers. * @win_enable: enable hardware specific overlay. * @win_disable: disable hardware specific overlay. + * @te_handler: trigger to transfer video image at the tearing effect + * synchronization signal if there is a page flip request. */ struct exynos_drm_manager; struct exynos_drm_manager_ops { @@ -204,6 +206,7 @@ struct exynos_drm_manager_ops { void (*win_commit)(struct exynos_drm_manager *mgr, int zpos); void (*win_enable)(struct exynos_drm_manager *mgr, int zpos); void (*win_disable)(struct exynos_drm_manager *mgr, int zpos); + void (*te_handler)(struct exynos_drm_manager *mgr); }; /* -- cgit v0.10.2 From e17ddecc3aa519b7e59edf490e34ac036be1f8b8 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Tue, 22 Jul 2014 19:49:44 +0900 Subject: drm/exynos: dsi: add TE interrupt handler to support LCD I80 interface This is a temporary solution and should be made by more generic way. To support LCD I80 interface, the DSI host should register TE interrupt handler from the TE GPIO of attached panel. So the panel generates a tearing effect synchronization signal then the DSI host calls the CRTC device manager to trigger to transfer video image. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Signed-off-by: Inki Dae diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 58bfb2a..a19107a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include #include #include @@ -24,6 +26,7 @@ #include