diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index f9df3b872c16..7c7af25b330c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1653,6 +1653,8 @@ void intel_crt_init(struct drm_device *dev) KMS API Functions !Edrivers/gpu/drm/drm_crtc.c +!Edrivers/gpu/drm/drm_rect.c +!Finclude/drm/drm_rect.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1c9f24396002..1ecbe5b7312d 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o drm_prime.o + drm_trace_points.o drm_global.o drm_prime.o \ + drm_rect.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c new file mode 100644 index 000000000000..7047ca025787 --- /dev/null +++ b/drivers/gpu/drm/drm_rect.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2011-2013 Intel Corporation + * + * 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 +#include +#include +#include + +/** + * drm_rect_intersect - intersect two rectangles + * @r1: first rectangle + * @r2: second rectangle + * + * Calculate the intersection of rectangles @r1 and @r2. + * @r1 will be overwritten with the intersection. + * + * RETURNS: + * %true if rectangle @r1 is still visible after the operation, + * %false otherwise. + */ +bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2) +{ + r1->x1 = max(r1->x1, r2->x1); + r1->y1 = max(r1->y1, r2->y1); + r1->x2 = min(r1->x2, r2->x2); + r1->y2 = min(r1->y2, r2->y2); + + return drm_rect_visible(r1); +} +EXPORT_SYMBOL(drm_rect_intersect); + +/** + * drm_rect_clip_scaled - perform a scaled clip operation + * @src: source window rectangle + * @dst: destination window rectangle + * @clip: clip rectangle + * @hscale: horizontal scaling factor + * @vscale: vertical scaling factor + * + * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the + * same amounts multiplied by @hscale and @vscale. + * + * RETURNS: + * %true if rectangle @dst is still visible after being clipped, + * %false otherwise + */ +bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, + const struct drm_rect *clip, + int hscale, int vscale) +{ + int diff; + + diff = clip->x1 - dst->x1; + if (diff > 0) { + int64_t tmp = src->x1 + (int64_t) diff * hscale; + src->x1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = clip->y1 - dst->y1; + if (diff > 0) { + int64_t tmp = src->y1 + (int64_t) diff * vscale; + src->y1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = dst->x2 - clip->x2; + if (diff > 0) { + int64_t tmp = src->x2 - (int64_t) diff * hscale; + src->x2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = dst->y2 - clip->y2; + if (diff > 0) { + int64_t tmp = src->y2 - (int64_t) diff * vscale; + src->y2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + + return drm_rect_intersect(dst, clip); +} +EXPORT_SYMBOL(drm_rect_clip_scaled); + +static int drm_calc_scale(int src, int dst) +{ + int scale = 0; + + if (src < 0 || dst < 0) + return -EINVAL; + + if (dst == 0) + return 0; + + scale = src / dst; + + return scale; +} + +/** + * drm_rect_calc_hscale - calculate the horizontal scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_hscale: minimum allowed horizontal scaling factor + * @max_hscale: maximum allowed horizontal scaling factor + * + * Calculate the horizontal scaling factor as + * (@src width) / (@dst width). + * + * RETURNS: + * The horizontal scaling factor, or errno of out of limits. + */ +int drm_rect_calc_hscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_hscale, int max_hscale) +{ + int src_w = drm_rect_width(src); + int dst_w = drm_rect_width(dst); + int hscale = drm_calc_scale(src_w, dst_w); + + if (hscale < 0 || dst_w == 0) + return hscale; + + if (hscale < min_hscale || hscale > max_hscale) + return -ERANGE; + + return hscale; +} +EXPORT_SYMBOL(drm_rect_calc_hscale); + +/** + * drm_rect_calc_vscale - calculate the vertical scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_vscale: minimum allowed vertical scaling factor + * @max_vscale: maximum allowed vertical scaling factor + * + * Calculate the vertical scaling factor as + * (@src height) / (@dst height). + * + * RETURNS: + * The vertical scaling factor, or errno of out of limits. + */ +int drm_rect_calc_vscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_vscale, int max_vscale) +{ + int src_h = drm_rect_height(src); + int dst_h = drm_rect_height(dst); + int vscale = drm_calc_scale(src_h, dst_h); + + if (vscale < 0 || dst_h == 0) + return vscale; + + if (vscale < min_vscale || vscale > max_vscale) + return -ERANGE; + + return vscale; +} +EXPORT_SYMBOL(drm_rect_calc_vscale); + +/** + * drm_calc_hscale_relaxed - calculate the horizontal scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_hscale: minimum allowed horizontal scaling factor + * @max_hscale: maximum allowed horizontal scaling factor + * + * Calculate the horizontal scaling factor as + * (@src width) / (@dst width). + * + * If the calculated scaling factor is below @min_vscale, + * decrease the height of rectangle @dst to compensate. + * + * If the calculated scaling factor is above @max_vscale, + * decrease the height of rectangle @src to compensate. + * + * RETURNS: + * The horizontal scaling factor. + */ +int drm_rect_calc_hscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_hscale, int max_hscale) +{ + int src_w = drm_rect_width(src); + int dst_w = drm_rect_width(dst); + int hscale = drm_calc_scale(src_w, dst_w); + + if (hscale < 0 || dst_w == 0) + return hscale; + + if (hscale < min_hscale) { + int max_dst_w = src_w / min_hscale; + + drm_rect_adjust_size(dst, max_dst_w - dst_w, 0); + + return min_hscale; + } + + if (hscale > max_hscale) { + int max_src_w = dst_w * max_hscale; + + drm_rect_adjust_size(src, max_src_w - src_w, 0); + + return max_hscale; + } + + return hscale; +} +EXPORT_SYMBOL(drm_rect_calc_hscale_relaxed); + +/** + * drm_rect_calc_vscale_relaxed - calculate the vertical scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_vscale: minimum allowed vertical scaling factor + * @max_vscale: maximum allowed vertical scaling factor + * + * Calculate the vertical scaling factor as + * (@src height) / (@dst height). + * + * If the calculated scaling factor is below @min_vscale, + * decrease the height of rectangle @dst to compensate. + * + * If the calculated scaling factor is above @max_vscale, + * decrease the height of rectangle @src to compensate. + * + * RETURNS: + * The vertical scaling factor. + */ +int drm_rect_calc_vscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_vscale, int max_vscale) +{ + int src_h = drm_rect_height(src); + int dst_h = drm_rect_height(dst); + int vscale = drm_calc_scale(src_h, dst_h); + + if (vscale < 0 || dst_h == 0) + return vscale; + + if (vscale < min_vscale) { + int max_dst_h = src_h / min_vscale; + + drm_rect_adjust_size(dst, 0, max_dst_h - dst_h); + + return min_vscale; + } + + if (vscale > max_vscale) { + int max_src_h = dst_h * max_vscale; + + drm_rect_adjust_size(src, 0, max_src_h - src_h); + + return max_vscale; + } + + return vscale; +} +EXPORT_SYMBOL(drm_rect_calc_vscale_relaxed); + +/** + * drm_rect_debug_print - print the rectangle information + * @r: rectangle to print + * @fixed_point: rectangle is in 16.16 fixed point format + */ +void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point) +{ + int w = drm_rect_width(r); + int h = drm_rect_height(r); + + if (fixed_point) + DRM_DEBUG_KMS("%d.%06ux%d.%06u%+d.%06u%+d.%06u\n", + w >> 16, ((w & 0xffff) * 15625) >> 10, + h >> 16, ((h & 0xffff) * 15625) >> 10, + r->x1 >> 16, ((r->x1 & 0xffff) * 15625) >> 10, + r->y1 >> 16, ((r->y1 & 0xffff) * 15625) >> 10); + else + DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1); +} +EXPORT_SYMBOL(drm_rect_debug_print); diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 3edd981e0770..757e0fa11043 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -32,12 +32,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define CH7xxx_REG_DID 0x4b #define CH7011_VID 0x83 /* 7010 as well */ +#define CH7010B_VID 0x05 #define CH7009A_VID 0x84 #define CH7009B_VID 0x85 #define CH7301_VID 0x95 #define CH7xxx_VID 0x84 #define CH7xxx_DID 0x17 +#define CH7010_DID 0x16 #define CH7xxx_NUM_REGS 0x4c @@ -87,11 +89,20 @@ static struct ch7xxx_id_struct { char *name; } ch7xxx_ids[] = { { CH7011_VID, "CH7011" }, + { CH7010B_VID, "CH7010B" }, { CH7009A_VID, "CH7009A" }, { CH7009B_VID, "CH7009B" }, { CH7301_VID, "CH7301" }, }; +static struct ch7xxx_did_struct { + uint8_t did; + char *name; +} ch7xxx_dids[] = { + { CH7xxx_DID, "CH7XXX" }, + { CH7010_DID, "CH7010B" }, +}; + struct ch7xxx_priv { bool quiet; }; @@ -108,6 +119,18 @@ static char *ch7xxx_get_id(uint8_t vid) return NULL; } +static char *ch7xxx_get_did(uint8_t did) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) { + if (ch7xxx_dids[i].did == did) + return ch7xxx_dids[i].name; + } + + return NULL; +} + /** Reads an 8 bit register */ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { @@ -179,7 +202,7 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo, /* this will detect the CH7xxx chip on the specified i2c bus */ struct ch7xxx_priv *ch7xxx; uint8_t vendor, device; - char *name; + char *name, *devid; ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); if (ch7xxx == NULL) @@ -204,7 +227,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo, if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) goto out; - if (device != CH7xxx_DID) { + devid = ch7xxx_get_did(device); + if (!devid) { DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s " "slave %d.\n", vendor, adapter->name, dvo->slave_addr); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e913d325d5b8..a55630a80f83 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -61,11 +61,11 @@ static int i915_capabilities(struct seq_file *m, void *data) seq_printf(m, "gen: %d\n", info->gen); seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev)); -#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) -#define DEV_INFO_SEP ; - DEV_INFO_FLAGS; -#undef DEV_INFO_FLAG -#undef DEV_INFO_SEP +#define PRINT_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) +#define SEP_SEMICOLON ; + DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON); +#undef PRINT_FLAG +#undef SEP_SEMICOLON return 0; } @@ -941,7 +941,7 @@ static int i915_cur_delayinfo(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)) { + } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(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); @@ -1009,6 +1009,27 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) seq_printf(m, "Max overclocked frequency: %dMHz\n", dev_priv->rps.hw_max * GT_FREQUENCY_MULTIPLIER); + } else if (IS_VALLEYVIEW(dev)) { + u32 freq_sts, val; + + mutex_lock(&dev_priv->rps.hw_lock); + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, + &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); + + valleyview_punit_read(dev_priv, PUNIT_FUSE_BUS1, &val); + seq_printf(m, "max GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, val)); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + seq_printf(m, "min GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, val)); + + seq_printf(m, "current GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, + (freq_sts >> 8) & 0xff)); + mutex_unlock(&dev_priv->rps.hw_lock); } else { seq_printf(m, "no P-state info available\n"); } @@ -1812,7 +1833,11 @@ i915_max_freq_get(void *data, u64 *val) if (ret) return ret; - *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + *val = vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.max_delay); + else + *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1837,9 +1862,16 @@ i915_max_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go above the set value. */ - do_div(val, GT_FREQUENCY_MULTIPLIER); - dev_priv->rps.max_delay = val; - gen6_set_rps(dev, val); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + dev_priv->rps.max_delay = val; + gen6_set_rps(dev, val); + } else { + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.max_delay = val; + gen6_set_rps(dev, val); + } + mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1863,7 +1895,11 @@ i915_min_freq_get(void *data, u64 *val) if (ret) return ret; - *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + *val = vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.min_delay); + else + *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1888,9 +1924,15 @@ i915_min_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go below the set value. */ - do_div(val, GT_FREQUENCY_MULTIPLIER); - dev_priv->rps.min_delay = val; - gen6_set_rps(dev, val); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + dev_priv->rps.min_delay = val; + valleyview_set_rps(dev, val); + } else { + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.min_delay = val; + gen6_set_rps(dev, val); + } mutex_unlock(&dev_priv->rps.hw_lock); return 0; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3b315ba85a3e..f5addac7c154 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1445,15 +1445,19 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv) { const struct intel_device_info *info = dev_priv->info; -#define DEV_INFO_FLAG(name) info->name ? #name "," : "" -#define DEV_INFO_SEP , +#define PRINT_S(name) "%s" +#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=" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY), info->gen, dev_priv->dev->pdev->device, - DEV_INFO_FLAGS); -#undef DEV_INFO_FLAG -#undef DEV_INFO_SEP + DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA)); +#undef PRINT_S +#undef SEP_EMPTY +#undef PRINT_FLAG +#undef SEP_COMMA } /** @@ -1468,7 +1472,7 @@ static void intel_early_sanitize_regs(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (IS_HASWELL(dev)) + if (HAS_FPGA_DBG_UNCLAIMED(dev)) I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); } @@ -1629,6 +1633,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); + spin_lock_init(&dev_priv->backlight.lock); mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); @@ -1737,10 +1742,10 @@ int i915_driver_unload(struct drm_device *dev) * free the memory space allocated for the child device * config parsed from VBT */ - if (dev_priv->child_dev && dev_priv->child_dev_num) { - kfree(dev_priv->child_dev); - dev_priv->child_dev = NULL; - dev_priv->child_dev_num = 0; + if (dev_priv->vbt.child_dev && dev_priv->vbt.child_dev_num) { + kfree(dev_priv->vbt.child_dev); + dev_priv->vbt.child_dev = NULL; + dev_priv->vbt.child_dev_num = 0; } vga_switcheroo_unregister_client(dev->pdev); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a2e4953b8e8d..b7c3b98f7858 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -280,6 +280,7 @@ static const struct intel_device_info intel_ivybridge_m_info = { GEN7_FEATURES, .is_ivybridge = 1, .is_mobile = 1, + .has_fbc = 1, }; static const struct intel_device_info intel_ivybridge_q_info = { @@ -308,12 +309,17 @@ static const struct intel_device_info intel_valleyview_d_info = { static const struct intel_device_info intel_haswell_d_info = { GEN7_FEATURES, .is_haswell = 1, + .has_ddi = 1, + .has_fpga_dbg = 1, }; static const struct intel_device_info intel_haswell_m_info = { GEN7_FEATURES, .is_haswell = 1, .is_mobile = 1, + .has_ddi = 1, + .has_fpga_dbg = 1, + .has_fbc = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ @@ -549,6 +555,8 @@ static int i915_drm_freeze(struct drm_device *dev) */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) dev_priv->display.crtc_disable(crtc); + + intel_modeset_suspend_hw(dev); } i915_save_state(dev); @@ -984,12 +992,6 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct intel_device_info *intel_info = (struct intel_device_info *) ent->driver_data; - if (intel_info->is_valleyview) - if(!i915_preliminary_hw_support) { - DRM_ERROR("Preliminary hardware support disabled\n"); - return -ENODEV; - } - /* Only bind to function 0 of the device. Early generations * used function 1 as a placeholder for multi-head. This causes * us confusion instead, especially on the systems where both @@ -1218,16 +1220,16 @@ MODULE_LICENSE("GPL and additional rights"); static void ilk_dummy_write(struct drm_i915_private *dev_priv) { - /* WaIssueDummyWriteToWakeupFromRC6: Issue a dummy write to wake up the - * chip from rc6 before touching it for real. MI_MODE is masked, hence - * harmless to write 0 into. */ + /* WaIssueDummyWriteToWakeupFromRC6:ilk Issue a dummy write to wake up + * the chip from rc6 before touching it for real. MI_MODE is masked, + * hence harmless to write 0 into. */ I915_WRITE_NOTRACE(MI_MODE, 0); } static void hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) { - if (IS_HASWELL(dev_priv->dev) && + if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) && (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { DRM_ERROR("Unknown unclaimed register before writing to %x\n", reg); @@ -1238,7 +1240,7 @@ hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) static void hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg) { - if (IS_HASWELL(dev_priv->dev) && + if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) && (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { DRM_ERROR("Unclaimed write to %x\n", reg); I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b9d00dcf9a2d..dfb5ade15149 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -76,6 +76,8 @@ enum plane { }; #define plane_name(p) ((p) + 'A') +#define sprite_name(p, s) ((p) * dev_priv->num_plane + (s) + 'A') + enum port { PORT_A = 0, PORT_B, @@ -86,6 +88,24 @@ enum port { }; #define port_name(p) ((p) + 'A') +enum intel_display_power_domain { + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_C, + POWER_DOMAIN_PIPE_A_PANEL_FITTER, + POWER_DOMAIN_PIPE_B_PANEL_FITTER, + POWER_DOMAIN_PIPE_C_PANEL_FITTER, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_TRANSCODER_C, + POWER_DOMAIN_TRANSCODER_EDP = POWER_DOMAIN_TRANSCODER_A + 0xF, +}; + +#define POWER_DOMAIN_PIPE(pipe) ((pipe) + POWER_DOMAIN_PIPE_A) +#define POWER_DOMAIN_PIPE_PANEL_FITTER(pipe) \ + ((pipe) + POWER_DOMAIN_PIPE_A_PANEL_FITTER) +#define POWER_DOMAIN_TRANSCODER(tran) ((tran) + POWER_DOMAIN_TRANSCODER_A) + enum hpd_pin { HPD_NONE = 0, HPD_PORT_A = HPD_NONE, /* PORT_A is internal */ @@ -331,68 +351,55 @@ struct drm_i915_gt_funcs { void (*force_wake_put)(struct drm_i915_private *dev_priv); }; -#define DEV_INFO_FLAGS \ - DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \ - DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \ - DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \ - DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \ - DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_llc) +#define DEV_INFO_FOR_EACH_FLAG(func, sep) \ + func(is_mobile) sep \ + func(is_i85x) sep \ + func(is_i915g) sep \ + func(is_i945gm) sep \ + func(is_g33) sep \ + func(need_gfx_hws) sep \ + func(is_g4x) sep \ + func(is_pineview) sep \ + func(is_broadwater) sep \ + func(is_crestline) sep \ + func(is_ivybridge) sep \ + func(is_valleyview) sep \ + func(is_haswell) sep \ + func(has_force_wake) sep \ + func(has_fbc) sep \ + func(has_pipe_cxsr) sep \ + func(has_hotplug) sep \ + func(cursor_needs_physical) sep \ + func(has_overlay) sep \ + func(overlay_needs_physical) sep \ + func(supports_tv) sep \ + func(has_bsd_ring) sep \ + func(has_blt_ring) sep \ + func(has_llc) sep \ + func(has_ddi) sep \ + func(has_fpga_dbg) + +#define DEFINE_FLAG(name) u8 name:1 +#define SEP_SEMICOLON ; struct intel_device_info { u32 display_mmio_offset; u8 num_pipes:3; u8 gen; - u8 is_mobile:1; - u8 is_i85x:1; - u8 is_i915g:1; - u8 is_i945gm:1; - u8 is_g33:1; - u8 need_gfx_hws:1; - u8 is_g4x:1; - u8 is_pineview:1; - u8 is_broadwater:1; - u8 is_crestline:1; - u8 is_ivybridge:1; - u8 is_valleyview:1; - u8 has_force_wake:1; - u8 is_haswell:1; - u8 has_fbc:1; - u8 has_pipe_cxsr:1; - u8 has_hotplug:1; - u8 cursor_needs_physical:1; - u8 has_overlay:1; - u8 overlay_needs_physical:1; - u8 supports_tv:1; - u8 has_bsd_ring:1; - u8 has_blt_ring:1; - u8 has_llc:1; + DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON); }; +#undef DEFINE_FLAG +#undef SEP_SEMICOLON + enum i915_cache_level { I915_CACHE_NONE = 0, I915_CACHE_LLC, I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ }; +typedef uint32_t gen6_gtt_pte_t; + /* The Graphics Translation Table is the way in which GEN hardware translates a * Graphics Virtual Address into a Physical Address. In addition to the normal * collateral associated with any va->pa translations GEN hardware also has a @@ -428,6 +435,9 @@ struct i915_gtt { struct sg_table *st, unsigned int pg_start, enum i915_cache_level cache_level); + gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level); }; #define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT) @@ -449,6 +459,9 @@ struct i915_hw_ppgtt { struct sg_table *st, unsigned int pg_start, enum i915_cache_level cache_level); + gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level); int (*enable)(struct drm_device *dev); void (*cleanup)(struct i915_hw_ppgtt *ppgtt); }; @@ -457,6 +470,7 @@ struct i915_hw_ppgtt { /* This must match up with the value previously used for execbuf2.rsvd1. */ #define DEFAULT_CONTEXT_ID 0 struct i915_hw_context { + struct kref ref; int id; bool is_initialized; struct drm_i915_file_private *file_priv; @@ -658,6 +672,7 @@ struct i915_suspend_saved_registers { struct intel_gen6_power_mgmt { struct work_struct work; + struct delayed_work vlv_work; u32 pm_iir; /* lock - irqsave spinlock that protectects the work_struct and * pm_iir. */ @@ -668,6 +683,7 @@ struct intel_gen6_power_mgmt { u8 cur_delay; u8 min_delay; u8 max_delay; + u8 rpe_delay; u8 hw_max; struct delayed_work delayed_resume_work; @@ -875,6 +891,37 @@ enum modeset_restore { MODESET_SUSPENDED, }; +struct intel_vbt_data { + struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ + struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ + + /* Feature bits */ + unsigned int int_tv_support:1; + unsigned int lvds_dither:1; + unsigned int lvds_vbt:1; + unsigned int int_crt_support:1; + unsigned int lvds_use_ssc:1; + unsigned int display_clock_mode:1; + unsigned int fdi_rx_polarity_inverted:1; + int lvds_ssc_freq; + unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ + + /* eDP */ + int edp_rate; + int edp_lanes; + int edp_preemphasis; + int edp_vswing; + bool edp_initialized; + bool edp_support; + int edp_bpp; + struct edp_power_seq edp_pps; + + int crt_ddc_pin; + + int child_dev_num; + struct child_device_config *child_dev; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -941,6 +988,7 @@ typedef struct drm_i915_private { HPD_MARK_DISABLED = 2 } hpd_mark; } hpd_stats[HPD_NUM_PINS]; + u32 hpd_event_bits; struct timer_list hotplug_reenable_timer; int num_pch_pll; @@ -953,6 +1001,7 @@ typedef struct drm_i915_private { struct intel_fbc_work *fbc_work; struct intel_opregion opregion; + struct intel_vbt_data vbt; /* overlay */ struct intel_overlay *overlay; @@ -962,37 +1011,15 @@ typedef struct drm_i915_private { struct { int level; bool enabled; + spinlock_t lock; /* bl registers and the above bl fields */ struct backlight_device *device; } backlight; /* LVDS info */ struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ - - /* Feature bits from the VBIOS */ - unsigned int int_tv_support:1; - unsigned int lvds_dither:1; - unsigned int lvds_vbt:1; - unsigned int int_crt_support:1; - unsigned int lvds_use_ssc:1; - unsigned int display_clock_mode:1; - unsigned int fdi_rx_polarity_inverted:1; - int lvds_ssc_freq; - unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ - struct { - int rate; - int lanes; - int preemphasis; - int vswing; - - bool initialized; - bool support; - int bpp; - struct edp_power_seq pps; - } edp; bool no_aux_handshake; - int crt_ddc_pin; struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ @@ -1020,10 +1047,6 @@ typedef struct drm_i915_private { /* Kernel Modesetting */ struct sdvo_device_mapping sdvo_mappings[2]; - /* indicate whether the LVDS_BORDER should be enabled or not */ - unsigned int lvds_border_bits; - /* Panel fitter placement and size for Ironlake+ */ - u32 pch_pf_pos, pch_pf_size; struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; @@ -1038,8 +1061,6 @@ typedef struct drm_i915_private { /* indicates the reduced downclock for LVDS*/ int lvds_downclock; u16 orig_clock; - int child_dev_num; - struct child_device_config *child_dev; bool mchbar_need_disable; @@ -1059,6 +1080,8 @@ typedef struct drm_i915_private { struct i915_gpu_error gpu_error; + struct drm_i915_gem_object *vlv_pctx; + /* list of fbdev register on this device */ struct intel_fbdev *fbdev; @@ -1274,6 +1297,9 @@ struct drm_i915_gem_request { /** Postion in the ringbuffer of the end of the request */ u32 tail; + /** Context related to this request */ + struct i915_hw_context *ctx; + /** Time at which this request was emitted, in jiffies. */ unsigned long emitted_jiffies; @@ -1373,8 +1399,9 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) -#define HAS_DDI(dev) (IS_HASWELL(dev)) +#define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) #define HAS_POWER_WELL(dev) (IS_HASWELL(dev)) +#define HAS_FPGA_DBG_UNCLAIMED(dev) (INTEL_INFO(dev)->has_fpga_dbg) #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 @@ -1486,8 +1513,6 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); -void intel_enable_asle(struct drm_device *dev); - #ifdef CONFIG_DEBUG_FS extern void i915_destroy_error_state(struct drm_device *dev); #else @@ -1703,6 +1728,17 @@ void i915_gem_context_fini(struct drm_device *dev); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct intel_ring_buffer *ring, struct drm_file *file, int to_id); +void i915_gem_context_free(struct kref *ctx_ref); +static inline void i915_gem_context_reference(struct i915_hw_context *ctx) +{ + kref_get(&ctx->ref); +} + +static inline void i915_gem_context_unreference(struct i915_hw_context *ctx) +{ + kref_put(&ctx->ref, i915_gem_context_free); +} + int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, @@ -1800,7 +1836,7 @@ void i915_teardown_sysfs(struct drm_device *dev_priv); /* intel_i2c.c */ extern int intel_setup_gmbus(struct drm_device *dev); extern void intel_teardown_gmbus(struct drm_device *dev); -extern inline bool intel_gmbus_is_port_valid(unsigned port) +static inline bool intel_gmbus_is_port_valid(unsigned port) { return (port >= GMBUS_PORT_SSC && port <= GMBUS_PORT_DPD); } @@ -1809,7 +1845,7 @@ extern struct i2c_adapter *intel_gmbus_get_adapter( struct drm_i915_private *dev_priv, unsigned port); extern void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed); extern void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit); -extern inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) +static inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) { return container_of(adapter, struct intel_gmbus, adapter)->force_bit; } @@ -1821,14 +1857,10 @@ extern int intel_opregion_setup(struct drm_device *dev); extern void intel_opregion_init(struct drm_device *dev); extern void intel_opregion_fini(struct drm_device *dev); extern void intel_opregion_asle_intr(struct drm_device *dev); -extern void intel_opregion_gse_intr(struct drm_device *dev); -extern void intel_opregion_enable_asle(struct drm_device *dev); #else static inline void intel_opregion_init(struct drm_device *dev) { return; } static inline void intel_opregion_fini(struct drm_device *dev) { return; } static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; } -static inline void intel_opregion_gse_intr(struct drm_device *dev) { return; } -static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; } #endif /* intel_acpi.c */ @@ -1842,6 +1874,7 @@ static inline void intel_unregister_dsm_handler(void) { return; } /* modesetting */ extern void intel_modeset_init_hw(struct drm_device *dev); +extern void intel_modeset_suspend_hw(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); @@ -1854,6 +1887,9 @@ extern void intel_disable_fbc(struct drm_device *dev); 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_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); @@ -1885,6 +1921,10 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); +int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); + +int vlv_gpu_freq(int ddr_freq, int val); +int vlv_freq_opcode(int ddr_freq, int val); #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a6cf8e843973..2b51fa7e3477 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2042,6 +2042,11 @@ i915_add_request(struct intel_ring_buffer *ring, request->seqno = intel_ring_get_seqno(ring); request->ring = ring; request->tail = request_ring_position; + request->ctx = ring->last_context; + + if (request->ctx) + i915_gem_context_reference(request->ctx); + request->emitted_jiffies = jiffies; was_empty = list_empty(&ring->request_list); list_add_tail(&request->list, &ring->request_list); @@ -2094,6 +2099,17 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) spin_unlock(&file_priv->mm.lock); } +static void i915_gem_free_request(struct drm_i915_gem_request *request) +{ + list_del(&request->list); + i915_gem_request_remove_from_client(request); + + if (request->ctx) + i915_gem_context_unreference(request->ctx); + + kfree(request); +} + static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct intel_ring_buffer *ring) { @@ -2104,9 +2120,7 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct drm_i915_gem_request, list); - list_del(&request->list); - i915_gem_request_remove_from_client(request); - kfree(request); + i915_gem_free_request(request); } while (!list_empty(&ring->active_list)) { @@ -2198,9 +2212,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) */ ring->last_retired_head = request->tail; - list_del(&request->list); - i915_gem_request_remove_from_client(request); - kfree(request); + i915_gem_free_request(request); } /* Move any buffers on the active list that are no longer referenced diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a1e8ecb6adf6..64cb1909a0ce 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -124,10 +124,10 @@ static int get_context_size(struct drm_device *dev) return ret; } -static void do_destroy(struct i915_hw_context *ctx) +void i915_gem_context_free(struct kref *ctx_ref) { - if (ctx->file_priv) - idr_remove(&ctx->file_priv->context_idr, ctx->id); + struct i915_hw_context *ctx = container_of(ctx_ref, + typeof(*ctx), ref); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); @@ -145,6 +145,7 @@ create_hw_context(struct drm_device *dev, if (ctx == NULL) return ERR_PTR(-ENOMEM); + kref_init(&ctx->ref); ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size); if (ctx->obj == NULL) { kfree(ctx); @@ -169,18 +170,18 @@ create_hw_context(struct drm_device *dev, if (file_priv == NULL) return ctx; - ctx->file_priv = file_priv; - ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, GFP_KERNEL); if (ret < 0) goto err_out; + + ctx->file_priv = file_priv; ctx->id = ret; return ctx; err_out: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ERR_PTR(ret); } @@ -226,7 +227,7 @@ static int create_default_context(struct drm_i915_private *dev_priv) err_unpin: i915_gem_object_unpin(ctx->obj); err_destroy: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ret; } @@ -262,6 +263,7 @@ void i915_gem_context_init(struct drm_device *dev) void i915_gem_context_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context; if (dev_priv->hw_contexts_disabled) return; @@ -271,9 +273,16 @@ void i915_gem_context_fini(struct drm_device *dev) * other code, leading to spurious errors. */ intel_gpu_reset(dev); - i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj); + i915_gem_object_unpin(dctx->obj); - do_destroy(dev_priv->ring[RCS].default_context); + /* When default context is created and switched to, base object refcount + * will be 2 (+1 from object creation and +1 from do_switch()). + * i915_gem_context_fini() will be called after gpu_idle() has switched + * to default context. So we need to unreference the base object once + * to offset the do_switch part, so that i915_gem_context_unreference() + * can then free the base object correctly. */ + drm_gem_object_unreference(&dctx->obj->base); + i915_gem_context_unreference(dctx); } static int context_idr_cleanup(int id, void *p, void *data) @@ -282,8 +291,7 @@ static int context_idr_cleanup(int id, void *p, void *data) BUG_ON(id == DEFAULT_CONTEXT_ID); - do_destroy(ctx); - + i915_gem_context_unreference(ctx); return 0; } @@ -325,6 +333,7 @@ mi_set_context(struct intel_ring_buffer *ring, if (ret) return ret; + /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */ if (IS_GEN7(ring->dev)) intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE); else @@ -353,13 +362,13 @@ mi_set_context(struct intel_ring_buffer *ring, static int do_switch(struct i915_hw_context *to) { struct intel_ring_buffer *ring = to->ring; - struct drm_i915_gem_object *from_obj = ring->last_context_obj; + struct i915_hw_context *from = ring->last_context; u32 hw_flags = 0; int ret; - BUG_ON(from_obj != NULL && from_obj->pin_count == 0); + BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0); - if (from_obj == to->obj) + if (from == to) return 0; ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false); @@ -382,7 +391,7 @@ static int do_switch(struct i915_hw_context *to) if (!to->is_initialized || is_default_context(to)) hw_flags |= MI_RESTORE_INHIBIT; - else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */ + else if (WARN_ON_ONCE(from == to)) /* not yet expected */ hw_flags |= MI_FORCE_RESTORE; ret = mi_set_context(ring, to, hw_flags); @@ -397,9 +406,9 @@ static int do_switch(struct i915_hw_context *to) * is a bit suboptimal because the retiring can occur simply after the * MI_SET_CONTEXT instead of when the next seqno has completed. */ - if (from_obj != NULL) { - from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; - i915_gem_object_move_to_active(from_obj, ring); + if (from != NULL) { + from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; + i915_gem_object_move_to_active(from->obj, 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 @@ -407,15 +416,26 @@ static int do_switch(struct i915_hw_context *to) * 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); - i915_gem_object_unpin(from_obj); + from->obj->dirty = 1; + BUG_ON(from->obj->ring != ring); - drm_gem_object_unreference(&from_obj->base); + ret = i915_add_request(ring, NULL, NULL); + if (ret) { + /* Too late, we've already scheduled a context switch. + * Try to undo the change so that the hw state is + * consistent with out tracking. In case of emergency, + * scream. + */ + WARN_ON(mi_set_context(ring, from, MI_RESTORE_INHIBIT)); + return ret; + } + + i915_gem_object_unpin(from->obj); + i915_gem_context_unreference(from); } - drm_gem_object_reference(&to->obj->base); - ring->last_context_obj = to->obj; + i915_gem_context_reference(to); + ring->last_context = to; to->is_initialized = true; return 0; @@ -444,6 +464,8 @@ int i915_switch_context(struct intel_ring_buffer *ring, if (dev_priv->hw_contexts_disabled) return 0; + WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + if (ring != &dev_priv->ring[RCS]) return 0; @@ -512,8 +534,8 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, return -ENOENT; } - do_destroy(ctx); - + idr_remove(&ctx->file_priv->context_idr, ctx->id); + i915_gem_context_unreference(ctx); mutex_unlock(&dev->struct_mutex); DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index bdb0d7717bc7..ddad13fa3156 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -28,8 +28,6 @@ #include "i915_trace.h" #include "intel_drv.h" -typedef uint32_t gen6_gtt_pte_t; - /* PPGTT stuff */ #define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0)) @@ -44,29 +42,22 @@ typedef uint32_t gen6_gtt_pte_t; #define GEN6_PTE_CACHE_LLC_MLC (3 << 1) #define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr) -static inline gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, - dma_addr_t addr, - enum i915_cache_level level) +static gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) { gen6_gtt_pte_t pte = GEN6_PTE_VALID; pte |= GEN6_PTE_ADDR_ENCODE(addr); switch (level) { case I915_CACHE_LLC_MLC: - /* Haswell doesn't set L3 this way */ - if (IS_HASWELL(dev)) - pte |= GEN6_PTE_CACHE_LLC; - else - pte |= GEN6_PTE_CACHE_LLC_MLC; + pte |= GEN6_PTE_CACHE_LLC_MLC; break; case I915_CACHE_LLC: pte |= GEN6_PTE_CACHE_LLC; break; case I915_CACHE_NONE: - if (IS_HASWELL(dev)) - pte |= HSW_PTE_UNCACHED; - else - pte |= GEN6_PTE_UNCACHED; + pte |= GEN6_PTE_UNCACHED; break; default: BUG(); @@ -75,16 +66,48 @@ static inline gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, return pte; } -static int gen6_ppgtt_enable(struct drm_device *dev) +#define BYT_PTE_WRITEABLE (1 << 1) +#define BYT_PTE_SNOOPED_BY_CPU_CACHES (1 << 2) + +static gen6_gtt_pte_t byt_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) { - drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t pd_offset; - struct intel_ring_buffer *ring; - struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + gen6_gtt_pte_t pte = GEN6_PTE_VALID; + pte |= GEN6_PTE_ADDR_ENCODE(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 (level != I915_CACHE_NONE) + pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES; + + return pte; +} + +static gen6_gtt_pte_t hsw_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) +{ + gen6_gtt_pte_t pte = GEN6_PTE_VALID; + pte |= GEN6_PTE_ADDR_ENCODE(addr); + + if (level != I915_CACHE_NONE) + pte |= GEN6_PTE_CACHE_LLC; + + return pte; +} + +static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt) +{ + struct drm_i915_private *dev_priv = ppgtt->dev->dev_private; gen6_gtt_pte_t __iomem *pd_addr; uint32_t pd_entry; int i; + WARN_ON(ppgtt->pd_offset & 0x3f); pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset / sizeof(gen6_gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { @@ -97,6 +120,19 @@ static int gen6_ppgtt_enable(struct drm_device *dev) writel(pd_entry, pd_addr + i); } readl(pd_addr); +} + +static int gen6_ppgtt_enable(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t pd_offset; + struct intel_ring_buffer *ring; + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + int i; + + BUG_ON(ppgtt->pd_offset & 0x3f); + + gen6_write_pdes(ppgtt); pd_offset = ppgtt->pd_offset; pd_offset /= 64; /* in cachelines, */ @@ -154,9 +190,9 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; unsigned last_pte, i; - scratch_pte = gen6_pte_encode(ppgtt->dev, - ppgtt->scratch_page_dma_addr, - I915_CACHE_LLC); + scratch_pte = ppgtt->pte_encode(ppgtt->dev, + ppgtt->scratch_page_dma_addr, + I915_CACHE_LLC); while (num_entries) { last_pte = first_pte + num_entries; @@ -191,8 +227,8 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt, dma_addr_t page_addr; page_addr = sg_page_iter_dma_address(&sg_iter); - pt_vaddr[act_pte] = gen6_pte_encode(ppgtt->dev, page_addr, - cache_level); + pt_vaddr[act_pte] = ppgtt->pte_encode(ppgtt->dev, page_addr, + cache_level); if (++act_pte == I915_PPGTT_PT_ENTRIES) { kunmap_atomic(pt_vaddr); act_pt++; @@ -233,8 +269,15 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024 * entries. For aliasing ppgtt support we just steal them at the end for * now. */ - first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt); + first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt); + if (IS_HASWELL(dev)) { + ppgtt->pte_encode = hsw_pte_encode; + } else if (IS_VALLEYVIEW(dev)) { + ppgtt->pte_encode = byt_pte_encode; + } else { + ppgtt->pte_encode = gen6_pte_encode; + } ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES; ppgtt->enable = gen6_ppgtt_enable; ppgtt->clear_range = gen6_ppgtt_clear_range; @@ -437,7 +480,8 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev, for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) { addr = sg_page_iter_dma_address(&sg_iter); - iowrite32(gen6_pte_encode(dev, addr, level), >t_entries[i]); + iowrite32(dev_priv->gtt.pte_encode(dev, addr, level), + >t_entries[i]); i++; } @@ -449,7 +493,7 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev, */ if (i != 0) WARN_ON(readl(>t_entries[i-1]) - != gen6_pte_encode(dev, addr, level)); + != dev_priv->gtt.pte_encode(dev, addr, level)); /* 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 @@ -474,8 +518,9 @@ static void gen6_ggtt_clear_range(struct drm_device *dev, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = gen6_pte_encode(dev, dev_priv->gtt.scratch_page_dma, - I915_CACHE_LLC); + scratch_pte = dev_priv->gtt.pte_encode(dev, + dev_priv->gtt.scratch_page_dma, + I915_CACHE_LLC); for (i = 0; i < num_entries; i++) iowrite32(scratch_pte, >t_base[i]); readl(gtt_base); @@ -809,6 +854,13 @@ int i915_gem_gtt_init(struct drm_device *dev) } else { dev_priv->gtt.gtt_probe = gen6_gmch_probe; dev_priv->gtt.gtt_remove = gen6_gmch_remove; + if (IS_HASWELL(dev)) { + dev_priv->gtt.pte_encode = hsw_pte_encode; + } else if (IS_VALLEYVIEW(dev)) { + dev_priv->gtt.pte_encode = byt_pte_encode; + } else { + dev_priv->gtt.pte_encode = gen6_pte_encode; + } } ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 130d1db27e28..89cbfab9570e 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -62,7 +62,10 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) * its value of TOLUD. */ base = 0; - if (INTEL_INFO(dev)->gen >= 6) { + if (IS_VALLEYVIEW(dev)) { + pci_read_config_dword(dev->pdev, 0x5c, &base); + base &= ~((1<<20) - 1); + } else if (INTEL_INFO(dev)->gen >= 6) { /* Read Base Data of Stolen Memory Register (BDSM) directly. * Note that there is also a MCHBAR miror at 0x1080c0 or * we could use device 2:0x5c instead. @@ -136,6 +139,7 @@ static int i915_setup_compression(struct drm_device *dev, int size) err_fb: drm_mm_put_block(compressed_fb); err: + 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; } @@ -182,6 +186,7 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + int bios_reserved = 0; dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); if (dev_priv->mm.stolen_base == 0) @@ -190,8 +195,12 @@ int i915_gem_init_stolen(struct drm_device *dev) DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n", dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base); + if (IS_VALLEYVIEW(dev)) + bios_reserved = 1024*1024; /* top 1M on VLV/BYT */ + /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size); + drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size - + bios_reserved); return 0; } @@ -330,7 +339,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, /* KISS and expect everything to be page-aligned */ BUG_ON(stolen_offset & 4095); - BUG_ON(gtt_offset & 4095); BUG_ON(size & 4095); if (WARN_ON(size == 0)) @@ -351,6 +359,10 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, return NULL; } + /* Some objects just need physical mem from stolen space */ + if (gtt_offset == -1) + return obj; + /* To simplify the initialisation sequence between KMS and GTT, * we allow construction of the stolen object prior to * setting up the GTT space. The actual reservation will occur diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0aa2ef0d2ae0..879c4ccb00db 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -112,6 +112,213 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) } } +static bool ivb_can_enable_err_int(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc; + enum pipe pipe; + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + if (crtc->cpu_fifo_underrun_disabled) + return false; + } + + return true; +} + +static bool cpt_can_enable_serr_int(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; + struct intel_crtc *crtc; + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + if (crtc->pch_fifo_underrun_disabled) + return false; + } + + return true; +} + +static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN : + DE_PIPEB_FIFO_UNDERRUN; + + if (enable) + ironlake_enable_display_irq(dev_priv, bit); + else + ironlake_disable_display_irq(dev_priv, bit); +} + +static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (enable) { + if (!ivb_can_enable_err_int(dev)) + return; + + I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A | + ERR_INT_FIFO_UNDERRUN_B | + ERR_INT_FIFO_UNDERRUN_C); + + ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + } else { + ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); + } +} + +static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc, + bool enable) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER : + SDE_TRANSB_FIFO_UNDER; + + if (enable) + I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit); + else + I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit); + + POSTING_READ(SDEIMR); +} + +static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (enable) { + if (!cpt_can_enable_serr_int(dev)) + return; + + I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN | + SERR_INT_TRANS_B_FIFO_UNDERRUN | + SERR_INT_TRANS_C_FIFO_UNDERRUN); + + I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT); + } else { + I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT); + } + + POSTING_READ(SDEIMR); +} + +/** + * intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages + * @dev: drm device + * @pipe: pipe + * @enable: true if we want to report FIFO underrun errors, false otherwise + * + * This function makes us disable or enable CPU fifo underruns for a specific + * pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun + * reporting for one pipe may also disable all the other CPU error interruts for + * the other pipes, due to the fact that there's just one interrupt mask/enable + * bit for all the pipes. + * + * Returns the previous state of underrun reporting. + */ +bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long flags; + bool ret; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + + ret = !intel_crtc->cpu_fifo_underrun_disabled; + + if (enable == ret) + goto done; + + intel_crtc->cpu_fifo_underrun_disabled = !enable; + + if (IS_GEN5(dev) || IS_GEN6(dev)) + ironlake_set_fifo_underrun_reporting(dev, pipe, enable); + else if (IS_GEN7(dev)) + ivybridge_set_fifo_underrun_reporting(dev, enable); + +done: + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + return ret; +} + +/** + * intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages + * @dev: drm device + * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older) + * @enable: true if we want to report FIFO underrun errors, false otherwise + * + * This function makes us disable or enable PCH fifo underruns for a specific + * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO + * underrun reporting for one transcoder may also disable all the other PCH + * error interruts for the other transcoders, due to the fact that there's just + * one interrupt mask/enable bit for all the transcoders. + * + * Returns the previous state of underrun reporting. + */ +bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe p; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + unsigned long flags; + bool ret; + + if (HAS_PCH_LPT(dev)) { + crtc = NULL; + for_each_pipe(p) { + struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p]; + if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) { + crtc = c; + break; + } + } + if (!crtc) { + DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n"); + return false; + } + } else { + crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder]; + } + intel_crtc = to_intel_crtc(crtc); + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + + ret = !intel_crtc->pch_fifo_underrun_disabled; + + if (enable == ret) + goto done; + + intel_crtc->pch_fifo_underrun_disabled = !enable; + + if (HAS_PCH_IBX(dev)) + ibx_set_fifo_underrun_reporting(intel_crtc, enable); + else + cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable); + +done: + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + return ret; +} + + void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) { @@ -142,28 +349,21 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) } /** - * intel_enable_asle - enable ASLE interrupt for OpRegion + * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion */ -void intel_enable_asle(struct drm_device *dev) +static void i915_enable_asle_pipestat(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; unsigned long irqflags; - /* FIXME: opregion/asle for VLV */ - if (IS_VALLEYVIEW(dev)) + if (!dev_priv->opregion.asle || !IS_MOBILE(dev)) return; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - if (HAS_PCH_SPLIT(dev)) - ironlake_enable_display_irq(dev_priv, DE_GSE); - else { - i915_enable_pipestat(dev_priv, 1, - PIPE_LEGACY_BLC_EVENT_ENABLE); - if (INTEL_INFO(dev)->gen >= 4) - i915_enable_pipestat(dev_priv, 0, - PIPE_LEGACY_BLC_EVENT_ENABLE); - } + i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); + if (INTEL_INFO(dev)->gen >= 4) + i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -184,6 +384,10 @@ i915_pipe_enabled(struct drm_device *dev, int pipe) enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); + if (!intel_display_power_enabled(dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) + return false; + return I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE; } @@ -334,6 +538,21 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, crtc); } +static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *connector) +{ + enum drm_connector_status old_status; + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + old_status = connector->status; + + connector->status = connector->funcs->detect(connector, false); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", + connector->base.id, + drm_get_connector_name(connector), + old_status, connector->status); + return (old_status != connector->status); +} + /* * Handle hotplug events outside the interrupt handler proper. */ @@ -350,6 +569,8 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_connector *connector; unsigned long irqflags; bool hpd_disabled = false; + bool changed = false; + u32 hpd_event_bits; /* HPD irq before everything is fully set up. */ if (!dev_priv->enable_hotplug_processing) @@ -359,6 +580,9 @@ static void i915_hotplug_work_func(struct work_struct *work) DRM_DEBUG_KMS("running encoder hotplug functions\n"); spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + + hpd_event_bits = dev_priv->hpd_event_bits; + dev_priv->hpd_event_bits = 0; list_for_each_entry(connector, &mode_config->connector_list, head) { intel_connector = to_intel_connector(connector); intel_encoder = intel_connector->encoder; @@ -373,6 +597,10 @@ static void i915_hotplug_work_func(struct work_struct *work) | DRM_CONNECTOR_POLL_DISCONNECT; hpd_disabled = true; } + if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) { + DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n", + drm_get_connector_name(connector), intel_encoder->hpd_pin); + } } /* if there were no outputs to poll, poll was disabled, * therefore make sure it's enabled when disabling HPD on @@ -385,14 +613,20 @@ static void i915_hotplug_work_func(struct work_struct *work) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) - if (intel_encoder->hot_plug) - intel_encoder->hot_plug(intel_encoder); - + list_for_each_entry(connector, &mode_config->connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector->encoder; + if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) { + if (intel_encoder->hot_plug) + intel_encoder->hot_plug(intel_encoder); + if (intel_hpd_irq_event(dev, connector)) + changed = true; + } + } mutex_unlock(&mode_config->mutex); - /* Just fire off a uevent and let userspace tell us what to do */ - drm_helper_hpd_irq_event(dev); + if (changed) + drm_kms_helper_hotplug_event(dev); } static void ironlake_handle_rps_change(struct drm_device *dev) @@ -482,7 +716,21 @@ static void gen6_pm_rps_work(struct work_struct *work) */ if (!(new_delay > dev_priv->rps.max_delay || new_delay < dev_priv->rps.min_delay)) { - gen6_set_rps(dev_priv->dev, new_delay); + if (IS_VALLEYVIEW(dev_priv->dev)) + valleyview_set_rps(dev_priv->dev, new_delay); + else + gen6_set_rps(dev_priv->dev, new_delay); + } + + if (IS_VALLEYVIEW(dev_priv->dev)) { + /* + * On VLV, when we enter RC6 we may not be at the minimum + * voltage level, so arm a timer to check. It should only + * fire when there's activity or once after we've entered + * RC6, and then won't be re-armed until the next RPS interrupt. + */ + mod_delayed_work(dev_priv->wq, &dev_priv->rps.vlv_work, + msecs_to_jiffies(100)); } mutex_unlock(&dev_priv->rps.hw_lock); @@ -636,6 +884,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev, dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED) continue; + dev_priv->hpd_event_bits |= (1 << i); 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))) { @@ -643,6 +892,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev, dev_priv->hpd_stats[i].hpd_cnt = 0; } else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) { dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED; + dev_priv->hpd_event_bits &= ~(1 << i); DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i); ret = true; } else { @@ -763,10 +1013,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } - if (pch_iir & SDE_AUDIO_POWER_MASK) + if (pch_iir & SDE_AUDIO_POWER_MASK) { + int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> + SDE_AUDIO_POWER_SHIFT); DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", - (pch_iir & SDE_AUDIO_POWER_MASK) >> - SDE_AUDIO_POWER_SHIFT); + port_name(port)); + } if (pch_iir & SDE_AUX_MASK) dp_aux_irq_handler(dev); @@ -795,10 +1047,64 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR)) DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n"); - if (pch_iir & SDE_TRANSB_FIFO_UNDER) - DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n"); if (pch_iir & SDE_TRANSA_FIFO_UNDER) - DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n"); + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, + false)) + DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n"); + + if (pch_iir & SDE_TRANSB_FIFO_UNDER) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B, + false)) + DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n"); +} + +static void ivb_err_int_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 err_int = I915_READ(GEN7_ERR_INT); + + if (err_int & ERR_INT_POISON) + DRM_ERROR("Poison interrupt\n"); + + if (err_int & ERR_INT_FIFO_UNDERRUN_A) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) + DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); + + if (err_int & ERR_INT_FIFO_UNDERRUN_B) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false)) + DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n"); + + if (err_int & ERR_INT_FIFO_UNDERRUN_C) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false)) + DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n"); + + I915_WRITE(GEN7_ERR_INT, err_int); +} + +static void cpt_serr_int_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 serr_int = I915_READ(SERR_INT); + + if (serr_int & SERR_INT_POISON) + DRM_ERROR("PCH poison interrupt\n"); + + if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, + false)) + DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n"); + + if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B, + false)) + DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n"); + + if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C, + false)) + DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n"); + + I915_WRITE(SERR_INT, serr_int); } static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) @@ -812,10 +1118,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } - if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) - DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", - (pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> - SDE_AUDIO_POWER_SHIFT_CPT); + if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { + int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> + SDE_AUDIO_POWER_SHIFT_CPT); + DRM_DEBUG_DRIVER("PCH audio power change on port %c\n", + port_name(port)); + } if (pch_iir & SDE_AUX_MASK_CPT) dp_aux_irq_handler(dev); @@ -834,6 +1142,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); + + if (pch_iir & SDE_ERROR_CPT) + cpt_serr_int_handler(dev); } static irqreturn_t ivybridge_irq_handler(int irq, void *arg) @@ -846,6 +1157,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) atomic_inc(&dev_priv->irq_received); + /* We get interrupts on unclaimed registers, so check for this before we + * do any I915_{READ,WRITE}. */ + if (IS_HASWELL(dev) && + (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { + DRM_ERROR("Unclaimed register before interrupt\n"); + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + } + /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); @@ -861,6 +1180,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) POSTING_READ(SDEIER); } + /* On Haswell, also mask ERR_INT because we don't want to risk + * generating "unclaimed register" interrupts from inside the interrupt + * handler. */ + if (IS_HASWELL(dev)) + ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); + gt_iir = I915_READ(GTIIR); if (gt_iir) { snb_gt_irq_handler(dev, dev_priv, gt_iir); @@ -870,11 +1195,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_ERR_INT_IVB) + ivb_err_int_handler(dev); + if (de_iir & DE_AUX_CHANNEL_A_IVB) dp_aux_irq_handler(dev); if (de_iir & DE_GSE_IVB) - intel_opregion_gse_intr(dev); + intel_opregion_asle_intr(dev); for (i = 0; i < 3; i++) { if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) @@ -907,6 +1235,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) ret = IRQ_HANDLED; } + if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev)) + ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); if (!HAS_PCH_NOP(dev)) { @@ -968,7 +1299,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) dp_aux_irq_handler(dev); if (de_iir & DE_GSE) - intel_opregion_gse_intr(dev); + intel_opregion_asle_intr(dev); if (de_iir & DE_PIPEA_VBLANK) drm_handle_vblank(dev, 0); @@ -976,6 +1307,17 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (de_iir & DE_PIPEB_VBLANK) drm_handle_vblank(dev, 1); + if (de_iir & DE_POISON) + DRM_ERROR("Poison interrupt\n"); + + if (de_iir & DE_PIPEA_FIFO_UNDERRUN) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) + DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); + + if (de_iir & DE_PIPEB_FIFO_UNDERRUN) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false)) + DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n"); + if (de_iir & DE_PLANEA_FLIP_DONE) { intel_prepare_page_flip(dev, 0); intel_finish_page_flip_plane(dev, 0); @@ -2201,10 +2543,14 @@ static void ibx_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 mask; - if (HAS_PCH_IBX(dev)) - mask = SDE_GMBUS | SDE_AUX_MASK; - else - mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; + if (HAS_PCH_IBX(dev)) { + mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER | + SDE_TRANSA_FIFO_UNDER | SDE_POISON; + } else { + mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT; + + I915_WRITE(SERR_INT, I915_READ(SERR_INT)); + } if (HAS_PCH_NOP(dev)) return; @@ -2219,7 +2565,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | - DE_AUX_CHANNEL_A; + DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN | + DE_PIPEA_FIFO_UNDERRUN | DE_POISON; u32 render_irqs; dev_priv->irq_mask = ~display_mask; @@ -2269,12 +2616,14 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | DE_PLANEA_FLIP_DONE_IVB | - DE_AUX_CHANNEL_A_IVB; + DE_AUX_CHANNEL_A_IVB | + DE_ERR_INT_IVB; u32 render_irqs; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ + I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT)); I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(DEIMR, dev_priv->irq_mask); I915_WRITE(DEIER, @@ -2305,7 +2654,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) u32 enable_mask; u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; - u16 msid; enable_mask = I915_DISPLAY_PORT_INTERRUPT; enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | @@ -2321,13 +2669,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - /* Hack for broken MSIs on VLV */ - pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000); - pci_read_config_word(dev->pdev, 0x98, &msid); - msid &= 0xff; /* mask out delivery bits */ - msid |= (1<<14); - pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); - I915_WRITE(PORT_HOTPLUG_EN, 0); POSTING_READ(PORT_HOTPLUG_EN); @@ -2402,6 +2743,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(DEIMR, 0xffffffff); I915_WRITE(DEIER, 0x0); I915_WRITE(DEIIR, I915_READ(DEIIR)); + if (IS_GEN7(dev)) + I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT)); I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); @@ -2413,6 +2756,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(SDEIMR, 0xffffffff); I915_WRITE(SDEIER, 0x0); I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev)) + I915_WRITE(SERR_INT, I915_READ(SERR_INT)); } static void i8xx_irq_preinstall(struct drm_device * dev) @@ -2626,7 +2971,7 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); - intel_opregion_enable_asle(dev); + i915_enable_asle_pipestat(dev); return 0; } @@ -2860,7 +3205,7 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); POSTING_READ(PORT_HOTPLUG_EN); - intel_opregion_enable_asle(dev); + i915_enable_asle_pipestat(dev); return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2d6b62e42daf..e4cf382f0b75 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -351,6 +351,8 @@ * 0x8100: fast clock controls * * DPIO is VLV only. + * + * Note: digital port B is DDI0, digital pot C is DDI1 */ #define DPIO_PKT (VLV_DISPLAY_BASE + 0x2100) #define DPIO_RID (0<<24) @@ -367,8 +369,20 @@ #define DPIO_SFR_BYPASS (1<<1) #define DPIO_RESET (1<<0) +#define _DPIO_TX3_SWING_CTL4_A 0x690 +#define _DPIO_TX3_SWING_CTL4_B 0x2a90 +#define DPIO_TX3_SWING_CTL4(pipe) _PIPE(pipe, _DPIO_TX_SWING_CTL4_A, \ + _DPIO_TX3_SWING_CTL4_B) + +/* + * Per pipe/PLL DPIO regs + */ #define _DPIO_DIV_A 0x800c #define DPIO_POST_DIV_SHIFT (28) /* 3 bits */ +#define DPIO_POST_DIV_DAC 0 +#define DPIO_POST_DIV_HDMIDP 1 /* DAC 225-400M rate */ +#define DPIO_POST_DIV_LVDS1 2 +#define DPIO_POST_DIV_LVDS2 3 #define DPIO_K_SHIFT (24) /* 4 bits */ #define DPIO_P1_SHIFT (21) /* 3 bits */ #define DPIO_P2_SHIFT (16) /* 5 bits */ @@ -394,14 +408,111 @@ #define _DPIO_CORE_CLK_B 0x803c #define DPIO_CORE_CLK(pipe) _PIPE(pipe, _DPIO_CORE_CLK_A, _DPIO_CORE_CLK_B) +#define _DPIO_IREF_CTL_A 0x8040 +#define _DPIO_IREF_CTL_B 0x8060 +#define DPIO_IREF_CTL(pipe) _PIPE(pipe, _DPIO_IREF_CTL_A, _DPIO_IREF_CTL_B) + +#define DPIO_IREF_BCAST 0xc044 +#define _DPIO_IREF_A 0x8044 +#define _DPIO_IREF_B 0x8064 +#define DPIO_IREF(pipe) _PIPE(pipe, _DPIO_IREF_A, _DPIO_IREF_B) + +#define _DPIO_PLL_CML_A 0x804c +#define _DPIO_PLL_CML_B 0x806c +#define DPIO_PLL_CML(pipe) _PIPE(pipe, _DPIO_PLL_CML_A, _DPIO_PLL_CML_B) + #define _DPIO_LFP_COEFF_A 0x8048 #define _DPIO_LFP_COEFF_B 0x8068 #define DPIO_LFP_COEFF(pipe) _PIPE(pipe, _DPIO_LFP_COEFF_A, _DPIO_LFP_COEFF_B) +#define DPIO_CALIBRATION 0x80ac + #define DPIO_FASTCLK_DISABLE 0x8100 -#define DPIO_DATA_CHANNEL1 0x8220 -#define DPIO_DATA_CHANNEL2 0x8420 +/* + * Per DDI channel DPIO regs + */ + +#define _DPIO_PCS_TX_0 0x8200 +#define _DPIO_PCS_TX_1 0x8400 +#define DPIO_PCS_TX_LANE2_RESET (1<<16) +#define DPIO_PCS_TX_LANE1_RESET (1<<7) +#define DPIO_PCS_TX(port) _PORT(port, _DPIO_PCS_TX_0, _DPIO_PCS_TX_1) + +#define _DPIO_PCS_CLK_0 0x8204 +#define _DPIO_PCS_CLK_1 0x8404 +#define DPIO_PCS_CLK_CRI_RXEB_EIOS_EN (1<<22) +#define DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN (1<<21) +#define DPIO_PCS_CLK_DATAWIDTH_SHIFT (6) +#define DPIO_PCS_CLK_SOFT_RESET (1<<5) +#define DPIO_PCS_CLK(port) _PORT(port, _DPIO_PCS_CLK_0, _DPIO_PCS_CLK_1) + +#define _DPIO_PCS_CTL_OVR1_A 0x8224 +#define _DPIO_PCS_CTL_OVR1_B 0x8424 +#define DPIO_PCS_CTL_OVER1(port) _PORT(port, _DPIO_PCS_CTL_OVR1_A, \ + _DPIO_PCS_CTL_OVR1_B) + +#define _DPIO_PCS_STAGGER0_A 0x822c +#define _DPIO_PCS_STAGGER0_B 0x842c +#define DPIO_PCS_STAGGER0(port) _PORT(port, _DPIO_PCS_STAGGER0_A, \ + _DPIO_PCS_STAGGER0_B) + +#define _DPIO_PCS_STAGGER1_A 0x8230 +#define _DPIO_PCS_STAGGER1_B 0x8430 +#define DPIO_PCS_STAGGER1(port) _PORT(port, _DPIO_PCS_STAGGER1_A, \ + _DPIO_PCS_STAGGER1_B) + +#define _DPIO_PCS_CLOCKBUF0_A 0x8238 +#define _DPIO_PCS_CLOCKBUF0_B 0x8438 +#define DPIO_PCS_CLOCKBUF0(port) _PORT(port, _DPIO_PCS_CLOCKBUF0_A, \ + _DPIO_PCS_CLOCKBUF0_B) + +#define _DPIO_PCS_CLOCKBUF8_A 0x825c +#define _DPIO_PCS_CLOCKBUF8_B 0x845c +#define DPIO_PCS_CLOCKBUF8(port) _PORT(port, _DPIO_PCS_CLOCKBUF8_A, \ + _DPIO_PCS_CLOCKBUF8_B) + +#define _DPIO_TX_SWING_CTL2_A 0x8288 +#define _DPIO_TX_SWING_CTL2_B 0x8488 +#define DPIO_TX_SWING_CTL2(port) _PORT(port, _DPIO_TX_SWING_CTL2_A, \ + _DPIO_TX_SWING_CTL2_B) + +#define _DPIO_TX_SWING_CTL3_A 0x828c +#define _DPIO_TX_SWING_CTL3_B 0x848c +#define DPIO_TX_SWING_CTL3(port) _PORT(port, _DPIO_TX_SWING_CTL3_A, \ + _DPIO_TX_SWING_CTL3_B) + +#define _DPIO_TX_SWING_CTL4_A 0x8290 +#define _DPIO_TX_SWING_CTL4_B 0x8490 +#define DPIO_TX_SWING_CTL4(port) _PORT(port, _DPIO_TX_SWING_CTL4_A, \ + _DPIO_TX_SWING_CTL4_B) + +#define _DPIO_TX_OCALINIT_0 0x8294 +#define _DPIO_TX_OCALINIT_1 0x8494 +#define DPIO_TX_OCALINIT_EN (1<<31) +#define DPIO_TX_OCALINIT(port) _PORT(port, _DPIO_TX_OCALINIT_0, \ + _DPIO_TX_OCALINIT_1) + +#define _DPIO_TX_CTL_0 0x82ac +#define _DPIO_TX_CTL_1 0x84ac +#define DPIO_TX_CTL(port) _PORT(port, _DPIO_TX_CTL_0, _DPIO_TX_CTL_1) + +#define _DPIO_TX_LANE_0 0x82b8 +#define _DPIO_TX_LANE_1 0x84b8 +#define DPIO_TX_LANE(port) _PORT(port, _DPIO_TX_LANE_0, _DPIO_TX_LANE_1) + +#define _DPIO_DATA_CHANNEL1 0x8220 +#define _DPIO_DATA_CHANNEL2 0x8420 +#define DPIO_DATA_CHANNEL(port) _PORT(port, _DPIO_DATA_CHANNEL1, _DPIO_DATA_CHANNEL2) + +#define _DPIO_PORT0_PCS0 0x0220 +#define _DPIO_PORT0_PCS1 0x0420 +#define _DPIO_PORT1_PCS2 0x2620 +#define _DPIO_PORT1_PCS3 0x2820 +#define DPIO_DATA_LANE_A(port) _PORT(port, _DPIO_PORT0_PCS0, _DPIO_PORT1_PCS2) +#define DPIO_DATA_LANE_B(port) _PORT(port, _DPIO_PORT0_PCS1, _DPIO_PORT1_PCS3) +#define DPIO_DATA_CHANNEL1 0x8220 +#define DPIO_DATA_CHANNEL2 0x8420 /* * Fence registers @@ -527,7 +638,11 @@ #define ERROR_GEN6 0x040a0 #define GEN7_ERR_INT 0x44040 -#define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define ERR_INT_POISON (1<<31) +#define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define ERR_INT_FIFO_UNDERRUN_C (1<<6) +#define ERR_INT_FIFO_UNDERRUN_B (1<<3) +#define ERR_INT_FIFO_UNDERRUN_A (1<<0) #define FPGA_DBG 0x42300 #define FPGA_DBG_RM_NOCLAIM (1<<31) @@ -583,6 +698,7 @@ #define VLV_IIR (VLV_DISPLAY_BASE + 0x20a4) #define VLV_IMR (VLV_DISPLAY_BASE + 0x20a8) #define VLV_ISR (VLV_DISPLAY_BASE + 0x20ac) +#define VLV_PCBR (VLV_DISPLAY_BASE + 0x2120) #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) #define I915_DISPLAY_PORT_INTERRUPT (1<<17) #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) @@ -807,7 +923,9 @@ #define DPFC_CTL_EN (1<<31) #define DPFC_CTL_PLANEA (0<<30) #define DPFC_CTL_PLANEB (1<<30) +#define IVB_DPFC_CTL_PLANE_SHIFT (29) #define DPFC_CTL_FENCE_EN (1<<29) +#define IVB_DPFC_CTL_FENCE_EN (1<<28) #define DPFC_CTL_PERSISTENT_MODE (1<<25) #define DPFC_SR_EN (1<<10) #define DPFC_CTL_LIMIT_1X (0<<6) @@ -840,6 +958,7 @@ #define ILK_DPFC_CHICKEN 0x43224 #define ILK_FBC_RT_BASE 0x2128 #define ILK_FBC_RT_VALID (1<<0) +#define SNB_FBC_FRONT_BUFFER (1<<1) #define ILK_DISPLAY_CHICKEN1 0x42000 #define ILK_FBCQ_DIS (1<<22) @@ -855,6 +974,19 @@ #define SNB_CPU_FENCE_ENABLE (1<<29) #define DPFC_CPU_FENCE_OFFSET 0x100104 +/* Framebuffer compression for Ivybridge */ +#define IVB_FBC_RT_BASE 0x7020 + + +#define _HSW_PIPE_SLICE_CHICKEN_1_A 0x420B0 +#define _HSW_PIPE_SLICE_CHICKEN_1_B 0x420B4 +#define HSW_BYPASS_FBC_QUEUE (1<<22) +#define HSW_PIPE_SLICE_CHICKEN_1(pipe) _PIPE(pipe, + \ + _HSW_PIPE_SLICE_CHICKEN_1_A, + \ + _HSW_PIPE_SLICE_CHICKEN_1_B) + +#define HSW_CLKGATE_DISABLE_PART_1 0x46500 +#define HSW_DPFC_GATING_DISABLE (1<<23) /* * GPIO regs @@ -963,7 +1095,10 @@ #define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ #define DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */ #define DPLL_LOCK_VLV (1<<15) +#define DPLL_INTEGRATED_CRI_CLK_VLV (1<<14) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) +#define DPLL_PORTC_READY_MASK (0xf << 4) +#define DPLL_PORTB_READY_MASK (0xf) #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 /* @@ -1967,6 +2102,10 @@ #define BLM_PIPE_A (0 << 29) #define BLM_PIPE_B (1 << 29) #define BLM_PIPE_C (2 << 29) /* ivb + */ +#define BLM_TRANSCODER_A BLM_PIPE_A /* hsw */ +#define BLM_TRANSCODER_B BLM_PIPE_B +#define BLM_TRANSCODER_C BLM_PIPE_C +#define BLM_TRANSCODER_EDP (3 << 29) #define BLM_PIPE(pipe) ((pipe) << 29) #define BLM_POLARITY_I965 (1 << 28) /* gen4 only */ #define BLM_PHASE_IN_INTERUPT_STATUS (1 << 26) @@ -2540,9 +2679,7 @@ #define DP_PRE_EMPHASIS_SHIFT 22 /* How many wires to use. I guess 3 was too hard */ -#define DP_PORT_WIDTH_1 (0 << 19) -#define DP_PORT_WIDTH_2 (1 << 19) -#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH(width) (((width) - 1) << 19) #define DP_PORT_WIDTH_MASK (7 << 19) /* Mystic DPCD version 1.1 special mode */ @@ -2646,18 +2783,20 @@ * which is after the LUTs, so we want the bytes for our color format. * For our current usage, this is always 3, one byte for R, G and B. */ -#define _PIPEA_GMCH_DATA_M 0x70050 -#define _PIPEB_GMCH_DATA_M 0x71050 +#define _PIPEA_DATA_M_G4X 0x70050 +#define _PIPEB_DATA_M_G4X 0x71050 /* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ #define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */ +#define TU_SIZE_SHIFT 25 #define TU_SIZE_MASK (0x3f << 25) #define DATA_LINK_M_N_MASK (0xffffff) #define DATA_LINK_N_MAX (0x800000) -#define _PIPEA_GMCH_DATA_N 0x70054 -#define _PIPEB_GMCH_DATA_N 0x71054 +#define _PIPEA_DATA_N_G4X 0x70054 +#define _PIPEB_DATA_N_G4X 0x71054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) /* * Computing Link M and N values for the Display Port link @@ -2670,16 +2809,18 @@ * Attributes and VB-ID. */ -#define _PIPEA_DP_LINK_M 0x70060 -#define _PIPEB_DP_LINK_M 0x71060 +#define _PIPEA_LINK_M_G4X 0x70060 +#define _PIPEB_LINK_M_G4X 0x71060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) -#define _PIPEA_DP_LINK_N 0x70064 -#define _PIPEB_DP_LINK_N 0x71064 +#define _PIPEA_LINK_N_G4X 0x70064 +#define _PIPEB_LINK_N_G4X 0x71064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) -#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M) -#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N) -#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) -#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N) +#define PIPE_DATA_M_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_M_G4X, _PIPEB_DATA_M_G4X) +#define PIPE_DATA_N_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_N_G4X, _PIPEB_DATA_N_G4X) +#define PIPE_LINK_M_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_M_G4X, _PIPEB_LINK_M_G4X) +#define PIPE_LINK_N_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_N_G4X, _PIPEB_LINK_N_G4X) /* Display & cursor control */ @@ -2715,6 +2856,7 @@ #define PIPECONF_INTERLACED_ILK (3 << 21) #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ +#define PIPECONF_INTERLACE_MODE_MASK (7 << 21) #define PIPECONF_CXSR_DOWNCLOCK (1<<16) #define PIPECONF_COLOR_RANGE_SELECT (1 << 13) #define PIPECONF_BPC_MASK (0x7 << 5) @@ -3502,7 +3644,7 @@ #define DE_PIPEA_FIFO_UNDERRUN (1 << 0) /* More Ivybridge lolz */ -#define DE_ERR_DEBUG_IVB (1<<30) +#define DE_ERR_INT_IVB (1<<30) #define DE_GSE_IVB (1<<29) #define DE_PCH_EVENT_IVB (1<<28) #define DE_DP_A_HOTPLUG_IVB (1<<27) @@ -3661,6 +3803,7 @@ SDE_PORTC_HOTPLUG_CPT | \ SDE_PORTB_HOTPLUG_CPT) #define SDE_GMBUS_CPT (1 << 17) +#define SDE_ERROR_CPT (1 << 16) #define SDE_AUDIO_CP_REQ_C_CPT (1 << 10) #define SDE_AUDIO_CP_CHG_C_CPT (1 << 9) #define SDE_FDI_RXC_CPT (1 << 8) @@ -3685,6 +3828,12 @@ #define SDEIIR 0xc4008 #define SDEIER 0xc400c +#define SERR_INT 0xc4040 +#define SERR_INT_POISON (1<<31) +#define SERR_INT_TRANS_C_FIFO_UNDERRUN (1<<6) +#define SERR_INT_TRANS_B_FIFO_UNDERRUN (1<<3) +#define SERR_INT_TRANS_A_FIFO_UNDERRUN (1<<0) + /* digital port hotplug */ #define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ #define PORTD_HOTPLUG_ENABLE (1 << 20) @@ -3794,34 +3943,34 @@ /* transcoder */ -#define _TRANS_HTOTAL_A 0xe0000 -#define TRANS_HTOTAL_SHIFT 16 -#define TRANS_HACTIVE_SHIFT 0 -#define _TRANS_HBLANK_A 0xe0004 -#define TRANS_HBLANK_END_SHIFT 16 -#define TRANS_HBLANK_START_SHIFT 0 -#define _TRANS_HSYNC_A 0xe0008 -#define TRANS_HSYNC_END_SHIFT 16 -#define TRANS_HSYNC_START_SHIFT 0 -#define _TRANS_VTOTAL_A 0xe000c -#define TRANS_VTOTAL_SHIFT 16 -#define TRANS_VACTIVE_SHIFT 0 -#define _TRANS_VBLANK_A 0xe0010 -#define TRANS_VBLANK_END_SHIFT 16 -#define TRANS_VBLANK_START_SHIFT 0 -#define _TRANS_VSYNC_A 0xe0014 -#define TRANS_VSYNC_END_SHIFT 16 -#define TRANS_VSYNC_START_SHIFT 0 -#define _TRANS_VSYNCSHIFT_A 0xe0028 +#define _PCH_TRANS_HTOTAL_A 0xe0000 +#define TRANS_HTOTAL_SHIFT 16 +#define TRANS_HACTIVE_SHIFT 0 +#define _PCH_TRANS_HBLANK_A 0xe0004 +#define TRANS_HBLANK_END_SHIFT 16 +#define TRANS_HBLANK_START_SHIFT 0 +#define _PCH_TRANS_HSYNC_A 0xe0008 +#define TRANS_HSYNC_END_SHIFT 16 +#define TRANS_HSYNC_START_SHIFT 0 +#define _PCH_TRANS_VTOTAL_A 0xe000c +#define TRANS_VTOTAL_SHIFT 16 +#define TRANS_VACTIVE_SHIFT 0 +#define _PCH_TRANS_VBLANK_A 0xe0010 +#define TRANS_VBLANK_END_SHIFT 16 +#define TRANS_VBLANK_START_SHIFT 0 +#define _PCH_TRANS_VSYNC_A 0xe0014 +#define TRANS_VSYNC_END_SHIFT 16 +#define TRANS_VSYNC_START_SHIFT 0 +#define _PCH_TRANS_VSYNCSHIFT_A 0xe0028 -#define _TRANSA_DATA_M1 0xe0030 -#define _TRANSA_DATA_N1 0xe0034 -#define _TRANSA_DATA_M2 0xe0038 -#define _TRANSA_DATA_N2 0xe003c -#define _TRANSA_DP_LINK_M1 0xe0040 -#define _TRANSA_DP_LINK_N1 0xe0044 -#define _TRANSA_DP_LINK_M2 0xe0048 -#define _TRANSA_DP_LINK_N2 0xe004c +#define _PCH_TRANSA_DATA_M1 0xe0030 +#define _PCH_TRANSA_DATA_N1 0xe0034 +#define _PCH_TRANSA_DATA_M2 0xe0038 +#define _PCH_TRANSA_DATA_N2 0xe003c +#define _PCH_TRANSA_LINK_M1 0xe0040 +#define _PCH_TRANSA_LINK_N1 0xe0044 +#define _PCH_TRANSA_LINK_M2 0xe0048 +#define _PCH_TRANSA_LINK_N2 0xe004c /* Per-transcoder DIP controls */ @@ -3890,44 +4039,45 @@ #define HSW_TVIDEO_DIP_VSC_DATA(trans) \ _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B) -#define _TRANS_HTOTAL_B 0xe1000 -#define _TRANS_HBLANK_B 0xe1004 -#define _TRANS_HSYNC_B 0xe1008 -#define _TRANS_VTOTAL_B 0xe100c -#define _TRANS_VBLANK_B 0xe1010 -#define _TRANS_VSYNC_B 0xe1014 -#define _TRANS_VSYNCSHIFT_B 0xe1028 +#define _PCH_TRANS_HTOTAL_B 0xe1000 +#define _PCH_TRANS_HBLANK_B 0xe1004 +#define _PCH_TRANS_HSYNC_B 0xe1008 +#define _PCH_TRANS_VTOTAL_B 0xe100c +#define _PCH_TRANS_VBLANK_B 0xe1010 +#define _PCH_TRANS_VSYNC_B 0xe1014 +#define _PCH_TRANS_VSYNCSHIFT_B 0xe1028 -#define TRANS_HTOTAL(pipe) _PIPE(pipe, _TRANS_HTOTAL_A, _TRANS_HTOTAL_B) -#define TRANS_HBLANK(pipe) _PIPE(pipe, _TRANS_HBLANK_A, _TRANS_HBLANK_B) -#define TRANS_HSYNC(pipe) _PIPE(pipe, _TRANS_HSYNC_A, _TRANS_HSYNC_B) -#define TRANS_VTOTAL(pipe) _PIPE(pipe, _TRANS_VTOTAL_A, _TRANS_VTOTAL_B) -#define TRANS_VBLANK(pipe) _PIPE(pipe, _TRANS_VBLANK_A, _TRANS_VBLANK_B) -#define TRANS_VSYNC(pipe) _PIPE(pipe, _TRANS_VSYNC_A, _TRANS_VSYNC_B) -#define TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _TRANS_VSYNCSHIFT_A, \ - _TRANS_VSYNCSHIFT_B) +#define PCH_TRANS_HTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_HTOTAL_A, _PCH_TRANS_HTOTAL_B) +#define PCH_TRANS_HBLANK(pipe) _PIPE(pipe, _PCH_TRANS_HBLANK_A, _PCH_TRANS_HBLANK_B) +#define PCH_TRANS_HSYNC(pipe) _PIPE(pipe, _PCH_TRANS_HSYNC_A, _PCH_TRANS_HSYNC_B) +#define PCH_TRANS_VTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_VTOTAL_A, _PCH_TRANS_VTOTAL_B) +#define PCH_TRANS_VBLANK(pipe) _PIPE(pipe, _PCH_TRANS_VBLANK_A, _PCH_TRANS_VBLANK_B) +#define PCH_TRANS_VSYNC(pipe) _PIPE(pipe, _PCH_TRANS_VSYNC_A, _PCH_TRANS_VSYNC_B) +#define PCH_TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _PCH_TRANS_VSYNCSHIFT_A, \ + _PCH_TRANS_VSYNCSHIFT_B) -#define _TRANSB_DATA_M1 0xe1030 -#define _TRANSB_DATA_N1 0xe1034 -#define _TRANSB_DATA_M2 0xe1038 -#define _TRANSB_DATA_N2 0xe103c -#define _TRANSB_DP_LINK_M1 0xe1040 -#define _TRANSB_DP_LINK_N1 0xe1044 -#define _TRANSB_DP_LINK_M2 0xe1048 -#define _TRANSB_DP_LINK_N2 0xe104c +#define _PCH_TRANSB_DATA_M1 0xe1030 +#define _PCH_TRANSB_DATA_N1 0xe1034 +#define _PCH_TRANSB_DATA_M2 0xe1038 +#define _PCH_TRANSB_DATA_N2 0xe103c +#define _PCH_TRANSB_LINK_M1 0xe1040 +#define _PCH_TRANSB_LINK_N1 0xe1044 +#define _PCH_TRANSB_LINK_M2 0xe1048 +#define _PCH_TRANSB_LINK_N2 0xe104c -#define TRANSDATA_M1(pipe) _PIPE(pipe, _TRANSA_DATA_M1, _TRANSB_DATA_M1) -#define TRANSDATA_N1(pipe) _PIPE(pipe, _TRANSA_DATA_N1, _TRANSB_DATA_N1) -#define TRANSDATA_M2(pipe) _PIPE(pipe, _TRANSA_DATA_M2, _TRANSB_DATA_M2) -#define TRANSDATA_N2(pipe) _PIPE(pipe, _TRANSA_DATA_N2, _TRANSB_DATA_N2) -#define TRANSDPLINK_M1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M1, _TRANSB_DP_LINK_M1) -#define TRANSDPLINK_N1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N1, _TRANSB_DP_LINK_N1) -#define TRANSDPLINK_M2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M2, _TRANSB_DP_LINK_M2) -#define TRANSDPLINK_N2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N2, _TRANSB_DP_LINK_N2) +#define PCH_TRANS_DATA_M1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M1, _PCH_TRANSB_DATA_M1) +#define PCH_TRANS_DATA_N1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N1, _PCH_TRANSB_DATA_N1) +#define PCH_TRANS_DATA_M2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M2, _PCH_TRANSB_DATA_M2) +#define PCH_TRANS_DATA_N2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N2, _PCH_TRANSB_DATA_N2) +#define PCH_TRANS_LINK_M1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M1, _PCH_TRANSB_LINK_M1) +#define PCH_TRANS_LINK_N1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N1, _PCH_TRANSB_LINK_N1) +#define PCH_TRANS_LINK_M2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M2, _PCH_TRANSB_LINK_M2) +#define PCH_TRANS_LINK_N2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N2, _PCH_TRANSB_LINK_N2) -#define _TRANSACONF 0xf0008 -#define _TRANSBCONF 0xf1008 -#define TRANSCONF(plane) _PIPE(plane, _TRANSACONF, _TRANSBCONF) +#define _PCH_TRANSACONF 0xf0008 +#define _PCH_TRANSBCONF 0xf1008 +#define PCH_TRANSCONF(pipe) _PIPE(pipe, _PCH_TRANSACONF, _PCH_TRANSBCONF) +#define LPT_TRANSCONF _PCH_TRANSACONF /* lpt has only one transcoder */ #define TRANS_DISABLE (0<<31) #define TRANS_ENABLE (1<<31) #define TRANS_STATE_MASK (1<<30) @@ -4011,10 +4161,9 @@ #define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) #define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) #define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f<<22) -#define FDI_DP_PORT_WIDTH_X1 (0<<19) -#define FDI_DP_PORT_WIDTH_X2 (1<<19) -#define FDI_DP_PORT_WIDTH_X3 (2<<19) -#define FDI_DP_PORT_WIDTH_X4 (3<<19) +#define FDI_DP_PORT_WIDTH_SHIFT 19 +#define FDI_DP_PORT_WIDTH_MASK (7 << FDI_DP_PORT_WIDTH_SHIFT) +#define FDI_DP_PORT_WIDTH(width) (((width) - 1) << FDI_DP_PORT_WIDTH_SHIFT) #define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18) /* Ironlake: hardwired to 1 */ #define FDI_TX_PLL_ENABLE (1<<14) @@ -4039,7 +4188,6 @@ /* train, dp width same as FDI_TX */ #define FDI_FS_ERRC_ENABLE (1<<27) #define FDI_FE_ERRC_ENABLE (1<<26) -#define FDI_DP_PORT_WIDTH_X8 (7<<19) #define FDI_RX_POLARITY_REVERSED_LPT (1<<16) #define FDI_8BPC (0<<16) #define FDI_10BPC (1<<16) @@ -4061,9 +4209,6 @@ #define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2<<8) #define FDI_LINK_TRAIN_NORMAL_CPT (3<<8) #define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3<<8) -/* LPT */ -#define FDI_PORT_WIDTH_2X_LPT (1<<19) -#define FDI_PORT_WIDTH_1X_LPT (0<<19) #define _FDI_RXA_MISC 0xf0010 #define _FDI_RXB_MISC 0xf1010 @@ -4309,6 +4454,7 @@ #define GEN6_RC_CTL_RC6_ENABLE (1<<18) #define GEN6_RC_CTL_RC1e_ENABLE (1<<20) #define GEN6_RC_CTL_RC7_ENABLE (1<<22) +#define GEN7_RC_CTL_TO_MODE (1<<28) #define GEN6_RC_CTL_EI_MODE(x) ((x)<<27) #define GEN6_RC_CTL_HW_ENABLE (1<<31) #define GEN6_RP_DOWN_TIMEOUT 0xA010 @@ -4400,12 +4546,32 @@ #define IOSF_BAR_SHIFT 1 #define IOSF_SB_BUSY (1<<0) #define IOSF_PORT_PUNIT 0x4 +#define IOSF_PORT_NC 0x11 #define VLV_IOSF_DATA 0x182104 #define VLV_IOSF_ADDR 0x182108 #define PUNIT_OPCODE_REG_READ 6 #define PUNIT_OPCODE_REG_WRITE 7 +#define PUNIT_REG_GPU_LFM 0xd3 +#define PUNIT_REG_GPU_FREQ_REQ 0xd4 +#define PUNIT_REG_GPU_FREQ_STS 0xd8 +#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc + +#define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ +#define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ + +#define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c +#define FB_GFX_MAX_FREQ_FUSE_SHIFT 3 +#define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT 11 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_MASK 0x0007f800 +#define IOSF_NC_FB_GFX_FMAX_FUSE_HI 0x34 +#define FB_FMAX_VMIN_FREQ_HI_MASK 0x00000007 +#define IOSF_NC_FB_GFX_FMAX_FUSE_LO 0x30 +#define FB_FMAX_VMIN_FREQ_LO_SHIFT 27 +#define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000 + #define GEN6_GT_CORE_STATUS 0x138060 #define GEN6_CORE_CPD_STATE_MASK (7<<4) #define GEN6_RCn_MASK 7 @@ -4602,9 +4768,6 @@ #define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12) #define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12) #define TRANS_DDI_BFI_ENABLE (1<<4) -#define TRANS_DDI_PORT_WIDTH_X1 (0<<1) -#define TRANS_DDI_PORT_WIDTH_X2 (1<<1) -#define TRANS_DDI_PORT_WIDTH_X4 (3<<1) /* DisplayPort Transport Control */ #define DP_TP_CTL_A 0x64040 @@ -4648,9 +4811,7 @@ #define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) #define DDI_A_4_LANES (1<<4) -#define DDI_PORT_WIDTH_X1 (0<<1) -#define DDI_PORT_WIDTH_X2 (1<<1) -#define DDI_PORT_WIDTH_X4 (3<<1) +#define DDI_PORT_WIDTH(width) (((width) - 1) << 1) #define DDI_INIT_DISPLAY_DETECTED (1<<0) /* DDI Buffer Translations */ @@ -4787,6 +4948,9 @@ #define _PIPE_A_CSC_COEFF_RV_GV 0x49020 #define _PIPE_A_CSC_COEFF_BV 0x49024 #define _PIPE_A_CSC_MODE 0x49028 +#define CSC_BLACK_SCREEN_OFFSET (1 << 2) +#define CSC_POSITION_BEFORE_GAMMA (1 << 1) +#define CSC_MODE_YUV_TO_RGB (1 << 0) #define _PIPE_A_CSC_PREOFF_HI 0x49030 #define _PIPE_A_CSC_PREOFF_ME 0x49034 #define _PIPE_A_CSC_PREOFF_LO 0x49038 @@ -4808,10 +4972,6 @@ #define _PIPE_B_CSC_POSTOFF_ME 0x49144 #define _PIPE_B_CSC_POSTOFF_LO 0x49148 -#define CSC_BLACK_SCREEN_OFFSET (1 << 2) -#define CSC_POSITION_BEFORE_GAMMA (1 << 1) -#define CSC_MODE_YUV_TO_RGB (1 << 0) - #define PIPE_CSC_COEFF_RY_GY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RY_GY, _PIPE_B_CSC_COEFF_RY_GY) #define PIPE_CSC_COEFF_BY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BY, _PIPE_B_CSC_COEFF_BY) #define PIPE_CSC_COEFF_RU_GU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RU_GU, _PIPE_B_CSC_COEFF_RU_GU) diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 41f0fdecfbdc..88b9a663944f 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -192,6 +192,7 @@ static void i915_restore_vga(struct drm_device *dev) static void i915_save_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; /* Display arbitration control */ if (INTEL_INFO(dev)->gen <= 4) @@ -202,6 +203,8 @@ static void i915_save_display(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_save_display_reg(dev); + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + /* LVDS state */ if (HAS_PCH_SPLIT(dev)) { dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL); @@ -222,6 +225,8 @@ static void i915_save_display(struct drm_device *dev) dev_priv->regfile.saveLVDS = I915_READ(LVDS); } + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + if (!IS_I830(dev) && !IS_845G(dev) && !HAS_PCH_SPLIT(dev)) dev_priv->regfile.savePFIT_CONTROL = I915_READ(PFIT_CONTROL); @@ -257,6 +262,7 @@ static void i915_restore_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 mask = 0xffffffff; + unsigned long flags; /* Display arbitration */ if (INTEL_INFO(dev)->gen <= 4) @@ -265,6 +271,8 @@ static void i915_restore_display(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_restore_display_reg(dev); + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + /* LVDS state */ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) I915_WRITE(BLC_PWM_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2); @@ -304,6 +312,8 @@ static void i915_restore_display(struct drm_device *dev) I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL); } + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + /* only restore FBC info on the platform that supports FBC*/ intel_disable_fbc(dev); if (I915_HAS_FBC(dev)) { diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index d5e1890678f9..c0d7875b475c 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -212,7 +212,13 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) { + u32 freq; + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq); + ret = vlv_gpu_freq(dev_priv->mem_freq, (freq >> 8) & 0xff); + } else { + ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; + } mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -226,7 +232,10 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) + ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.max_delay); + else + ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -246,16 +255,25 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, if (ret) return ret; - val /= GT_FREQUENCY_MULTIPLIER; - mutex_lock(&dev_priv->rps.hw_lock); - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; - non_oc_max = (rp_state_cap & 0xff); - hw_min = ((rp_state_cap & 0xff0000) >> 16); + if (IS_VALLEYVIEW(dev_priv->dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); - if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) { + hw_max = valleyview_rps_max_freq(dev_priv); + hw_min = valleyview_rps_min_freq(dev_priv); + non_oc_max = hw_max; + } else { + val /= GT_FREQUENCY_MULTIPLIER; + + rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + hw_max = dev_priv->rps.hw_max; + non_oc_max = (rp_state_cap & 0xff); + hw_min = ((rp_state_cap & 0xff0000) >> 16); + } + + if (val < hw_min || val > hw_max || + val < dev_priv->rps.min_delay) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } @@ -264,8 +282,12 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, DRM_DEBUG("User requested overclocking to %d\n", val * GT_FREQUENCY_MULTIPLIER); - if (dev_priv->rps.cur_delay > val) - gen6_set_rps(dev_priv->dev, val); + if (dev_priv->rps.cur_delay > val) { + if (IS_VALLEYVIEW(dev_priv->dev)) + valleyview_set_rps(dev_priv->dev, val); + else + gen6_set_rps(dev_priv->dev, val); + } dev_priv->rps.max_delay = val; @@ -282,7 +304,10 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) + ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.min_delay); + else + ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -302,21 +327,32 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, if (ret) return ret; - val /= GT_FREQUENCY_MULTIPLIER; - mutex_lock(&dev_priv->rps.hw_lock); - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; - hw_min = ((rp_state_cap & 0xff0000) >> 16); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + + hw_max = valleyview_rps_max_freq(dev_priv); + hw_min = valleyview_rps_min_freq(dev_priv); + } else { + val /= GT_FREQUENCY_MULTIPLIER; + + rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + hw_max = dev_priv->rps.hw_max; + hw_min = ((rp_state_cap & 0xff0000) >> 16); + } if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - if (dev_priv->rps.cur_delay < val) - gen6_set_rps(dev_priv->dev, val); + if (dev_priv->rps.cur_delay < val) { + if (IS_VALLEYVIEW(dev)) + valleyview_set_rps(dev, val); + else + gen6_set_rps(dev_priv->dev, val); + } dev_priv->rps.min_delay = val; diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c index 985a09716237..5ef30b2e6bc6 100644 --- a/drivers/gpu/drm/i915/i915_ums.c +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -148,13 +148,13 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ); dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS); - dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF); - dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A); - dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A); - dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A); - dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A); - dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A); - dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A); + dev_priv->regfile.saveTRANSACONF = I915_READ(_PCH_TRANSACONF); + dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_PCH_TRANS_HTOTAL_A); + dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_PCH_TRANS_HBLANK_A); + dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_PCH_TRANS_HSYNC_A); + dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_PCH_TRANS_VTOTAL_A); + dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_PCH_TRANS_VBLANK_A); + dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_PCH_TRANS_VSYNC_A); } dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR); @@ -205,13 +205,13 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ); dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS); - dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF); - dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B); - dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B); - dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B); - dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B); - dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B); - dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B); + dev_priv->regfile.saveTRANSBCONF = I915_READ(_PCH_TRANSBCONF); + dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_PCH_TRANS_HTOTAL_B); + dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_PCH_TRANS_HBLANK_B); + dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_PCH_TRANS_HSYNC_B); + dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_PCH_TRANS_VTOTAL_B); + dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_PCH_TRANS_VBLANK_B); + dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_PCH_TRANS_VSYNC_B); } dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR); @@ -259,14 +259,14 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.saveDP_B = I915_READ(DP_B); dev_priv->regfile.saveDP_C = I915_READ(DP_C); dev_priv->regfile.saveDP_D = I915_READ(DP_D); - dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M); - dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M); - dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N); - dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N); - dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M); - dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M); - dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N); - dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N); + dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_DATA_M_G4X); + dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_DATA_M_G4X); + dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_DATA_N_G4X); + dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_DATA_N_G4X); + dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_LINK_M_G4X); + dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_LINK_M_G4X); + dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_LINK_N_G4X); + dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_LINK_N_G4X); } /* FIXME: regfile.save TV & SDVO state */ @@ -282,14 +282,14 @@ void i915_restore_display_reg(struct drm_device *dev) /* Display port ratios (must be done before clock is set) */ if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M); - I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M); - I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N); - I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N); - I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M); - I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M); - I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N); - I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N); + I915_WRITE(_PIPEA_DATA_M_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_M); + I915_WRITE(_PIPEB_DATA_M_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_M); + I915_WRITE(_PIPEA_DATA_N_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_N); + I915_WRITE(_PIPEB_DATA_N_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_N); + I915_WRITE(_PIPEA_LINK_M_G4X, dev_priv->regfile.savePIPEA_DP_LINK_M); + I915_WRITE(_PIPEB_LINK_M_G4X, dev_priv->regfile.savePIPEB_DP_LINK_M); + I915_WRITE(_PIPEA_LINK_N_G4X, dev_priv->regfile.savePIPEA_DP_LINK_N); + I915_WRITE(_PIPEB_LINK_N_G4X, dev_priv->regfile.savePIPEB_DP_LINK_N); } /* Fences */ @@ -379,13 +379,13 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ); I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS); - I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF); - I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); - I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); - I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); - I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); - I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); - I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); + I915_WRITE(_PCH_TRANSACONF, dev_priv->regfile.saveTRANSACONF); + I915_WRITE(_PCH_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); + I915_WRITE(_PCH_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); + I915_WRITE(_PCH_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); + I915_WRITE(_PCH_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); + I915_WRITE(_PCH_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); + I915_WRITE(_PCH_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); } /* Restore plane info */ @@ -448,13 +448,13 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ); I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS); - I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); - I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); - I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); - I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); - I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); - I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); - I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); + I915_WRITE(_PCH_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); + I915_WRITE(_PCH_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); + I915_WRITE(_PCH_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); + I915_WRITE(_PCH_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); + I915_WRITE(_PCH_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); + I915_WRITE(_PCH_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); + I915_WRITE(_PCH_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); } /* Restore plane info */ diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 95070b2124c6..53f2bed8bc5f 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -212,7 +212,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, if (!lvds_options) return; - dev_priv->lvds_dither = lvds_options->pixel_dither; + dev_priv->vbt.lvds_dither = lvds_options->pixel_dither; if (lvds_options->panel_type == 0xff) return; @@ -226,7 +226,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, if (!lvds_lfp_data_ptrs) return; - dev_priv->lvds_vbt = 1; + dev_priv->vbt.lvds_vbt = 1; panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, lvds_lfp_data_ptrs, @@ -238,7 +238,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing); - dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; + dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); @@ -274,9 +274,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, /* check the resolution, just to be sure */ if (fp_timing->x_res == panel_fixed_mode->hdisplay && fp_timing->y_res == panel_fixed_mode->vdisplay) { - dev_priv->bios_lvds_val = fp_timing->lvds_reg_val; + dev_priv->vbt.bios_lvds_val = fp_timing->lvds_reg_val; DRM_DEBUG_KMS("VBT initial LVDS value %x\n", - dev_priv->bios_lvds_val); + dev_priv->vbt.bios_lvds_val); } } } @@ -316,7 +316,7 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv, fill_detail_timing_data(panel_fixed_mode, dvo_timing + index); - dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; + dev_priv->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found SDVO panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); @@ -345,20 +345,20 @@ parse_general_features(struct drm_i915_private *dev_priv, general = find_section(bdb, BDB_GENERAL_FEATURES); if (general) { - dev_priv->int_tv_support = general->int_tv_support; - dev_priv->int_crt_support = general->int_crt_support; - dev_priv->lvds_use_ssc = general->enable_ssc; - dev_priv->lvds_ssc_freq = + dev_priv->vbt.int_tv_support = general->int_tv_support; + dev_priv->vbt.int_crt_support = general->int_crt_support; + dev_priv->vbt.lvds_use_ssc = general->enable_ssc; + dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, general->ssc_freq); - dev_priv->display_clock_mode = general->display_clock_mode; - dev_priv->fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; + dev_priv->vbt.display_clock_mode = general->display_clock_mode; + dev_priv->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d fdi_rx_polarity_inverted %d\n", - dev_priv->int_tv_support, - dev_priv->int_crt_support, - dev_priv->lvds_use_ssc, - dev_priv->lvds_ssc_freq, - dev_priv->display_clock_mode, - dev_priv->fdi_rx_polarity_inverted); + dev_priv->vbt.int_tv_support, + dev_priv->vbt.int_crt_support, + dev_priv->vbt.lvds_use_ssc, + dev_priv->vbt.lvds_ssc_freq, + dev_priv->vbt.display_clock_mode, + dev_priv->vbt.fdi_rx_polarity_inverted); } } @@ -375,7 +375,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv, int bus_pin = general->crt_ddc_gmbus_pin; DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin); if (intel_gmbus_is_port_valid(bus_pin)) - dev_priv->crt_ddc_pin = bus_pin; + dev_priv->vbt.crt_ddc_pin = bus_pin; } else { DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n", block_size); @@ -486,7 +486,7 @@ parse_driver_features(struct drm_i915_private *dev_priv, if (SUPPORTS_EDP(dev) && driver->lvds_config == BDB_DRIVER_FEATURE_EDP) - dev_priv->edp.support = 1; + dev_priv->vbt.edp_support = 1; if (driver->dual_frequency) dev_priv->render_reclock_avail = true; @@ -501,20 +501,20 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) edp = find_section(bdb, BDB_EDP); if (!edp) { - if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) + if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->vbt.edp_support) DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n"); return; } switch ((edp->color_depth >> (panel_type * 2)) & 3) { case EDP_18BPP: - dev_priv->edp.bpp = 18; + dev_priv->vbt.edp_bpp = 18; break; case EDP_24BPP: - dev_priv->edp.bpp = 24; + dev_priv->vbt.edp_bpp = 24; break; case EDP_30BPP: - dev_priv->edp.bpp = 30; + dev_priv->vbt.edp_bpp = 30; break; } @@ -522,48 +522,48 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) edp_pps = &edp->power_seqs[panel_type]; edp_link_params = &edp->link_params[panel_type]; - dev_priv->edp.pps = *edp_pps; + dev_priv->vbt.edp_pps = *edp_pps; - dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : + dev_priv->vbt.edp_rate = edp_link_params->rate ? DP_LINK_BW_2_7 : DP_LINK_BW_1_62; switch (edp_link_params->lanes) { case 0: - dev_priv->edp.lanes = 1; + dev_priv->vbt.edp_lanes = 1; break; case 1: - dev_priv->edp.lanes = 2; + dev_priv->vbt.edp_lanes = 2; break; case 3: default: - dev_priv->edp.lanes = 4; + dev_priv->vbt.edp_lanes = 4; break; } switch (edp_link_params->preemphasis) { case 0: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; break; case 1: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; break; case 2: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; break; case 3: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; break; } switch (edp_link_params->vswing) { case 0: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; break; case 1: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; break; case 2: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; break; case 3: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; break; } } @@ -611,13 +611,13 @@ parse_device_mapping(struct drm_i915_private *dev_priv, DRM_DEBUG_KMS("no child dev is parsed from VBT\n"); return; } - dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL); - if (!dev_priv->child_dev) { + dev_priv->vbt.child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL); + if (!dev_priv->vbt.child_dev) { DRM_DEBUG_KMS("No memory space for child device\n"); return; } - dev_priv->child_dev_num = count; + dev_priv->vbt.child_dev_num = count; count = 0; for (i = 0; i < child_device_num; i++) { p_child = &(p_defs->devices[i]); @@ -625,7 +625,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, /* skip the device block if device type is invalid */ continue; } - child_dev_ptr = dev_priv->child_dev + count; + child_dev_ptr = dev_priv->vbt.child_dev + count; count++; memcpy((void *)child_dev_ptr, (void *)p_child, sizeof(*p_child)); @@ -638,23 +638,23 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - dev_priv->crt_ddc_pin = GMBUS_PORT_VGADDC; + dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC; /* LFP panel data */ - dev_priv->lvds_dither = 1; - dev_priv->lvds_vbt = 0; + dev_priv->vbt.lvds_dither = 1; + dev_priv->vbt.lvds_vbt = 0; /* SDVO panel data */ - dev_priv->sdvo_lvds_vbt_mode = NULL; + dev_priv->vbt.sdvo_lvds_vbt_mode = NULL; /* general features */ - dev_priv->int_tv_support = 1; - dev_priv->int_crt_support = 1; + dev_priv->vbt.int_tv_support = 1; + dev_priv->vbt.int_crt_support = 1; /* Default to using SSC */ - dev_priv->lvds_use_ssc = 1; - dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); - DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); + dev_priv->vbt.lvds_use_ssc = 1; + dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); + DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->vbt.lvds_ssc_freq); } static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 58b4a53715cd..66a0c6f0bb81 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -207,6 +207,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(dev)) pipe_config->has_pch_encoder = true; + /* LPT FDI RX only supports 8bpc. */ + if (HAS_PCH_LPT(dev)) + pipe_config->pipe_bpp = 24; + return true; } @@ -431,7 +435,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); - i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin); + i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); edid = intel_crt_get_edid(connector, i2c); if (edid) { @@ -637,7 +641,7 @@ static int intel_crt_get_modes(struct drm_connector *connector) int ret; struct i2c_adapter *i2c; - i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin); + i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); ret = intel_crt_ddc_get_modes(connector, i2c); if (ret || !IS_G4X(dev)) return ret; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index fb961bb81903..062de679f38f 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -174,6 +174,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) * mode set "sequence for CRT port" document: * - TP1 to TP2 time with the default value * - FDI delay to 90h + * + * WaFDIAutoLinkSetTimingOverrride:hsw */ I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2) | @@ -181,7 +183,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Enable the PCH Receiver FDI PLL */ rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | - FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19); + FDI_RX_PLL_ENABLE | + FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); udelay(220); @@ -209,7 +212,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) * port reversal bit */ I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | - ((intel_crtc->fdi_lanes - 1) << 1) | + ((intel_crtc->config.fdi_lanes - 1) << 1) | hsw_ddi_buf_ctl_values[i / 2]); POSTING_READ(DDI_BUF_CTL(PORT_E)); @@ -278,392 +281,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DRM_ERROR("FDI link training failed!\n"); } -/* WRPLL clock dividers */ -struct wrpll_tmds_clock { - u32 clock; - u16 p; /* Post divider */ - u16 n2; /* Feedback divider */ - u16 r2; /* Reference divider */ -}; - -/* Table of matching values for WRPLL clocks programming for each frequency. - * The code assumes this table is sorted. */ -static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { - {19750, 38, 25, 18}, - {20000, 48, 32, 18}, - {21000, 36, 21, 15}, - {21912, 42, 29, 17}, - {22000, 36, 22, 15}, - {23000, 36, 23, 15}, - {23500, 40, 40, 23}, - {23750, 26, 16, 14}, - {24000, 36, 24, 15}, - {25000, 36, 25, 15}, - {25175, 26, 40, 33}, - {25200, 30, 21, 15}, - {26000, 36, 26, 15}, - {27000, 30, 21, 14}, - {27027, 18, 100, 111}, - {27500, 30, 29, 19}, - {28000, 34, 30, 17}, - {28320, 26, 30, 22}, - {28322, 32, 42, 25}, - {28750, 24, 23, 18}, - {29000, 30, 29, 18}, - {29750, 32, 30, 17}, - {30000, 30, 25, 15}, - {30750, 30, 41, 24}, - {31000, 30, 31, 18}, - {31500, 30, 28, 16}, - {32000, 30, 32, 18}, - {32500, 28, 32, 19}, - {33000, 24, 22, 15}, - {34000, 28, 30, 17}, - {35000, 26, 32, 19}, - {35500, 24, 30, 19}, - {36000, 26, 26, 15}, - {36750, 26, 46, 26}, - {37000, 24, 23, 14}, - {37762, 22, 40, 26}, - {37800, 20, 21, 15}, - {38000, 24, 27, 16}, - {38250, 24, 34, 20}, - {39000, 24, 26, 15}, - {40000, 24, 32, 18}, - {40500, 20, 21, 14}, - {40541, 22, 147, 89}, - {40750, 18, 19, 14}, - {41000, 16, 17, 14}, - {41500, 22, 44, 26}, - {41540, 22, 44, 26}, - {42000, 18, 21, 15}, - {42500, 22, 45, 26}, - {43000, 20, 43, 27}, - {43163, 20, 24, 15}, - {44000, 18, 22, 15}, - {44900, 20, 108, 65}, - {45000, 20, 25, 15}, - {45250, 20, 52, 31}, - {46000, 18, 23, 15}, - {46750, 20, 45, 26}, - {47000, 20, 40, 23}, - {48000, 18, 24, 15}, - {49000, 18, 49, 30}, - {49500, 16, 22, 15}, - {50000, 18, 25, 15}, - {50500, 18, 32, 19}, - {51000, 18, 34, 20}, - {52000, 18, 26, 15}, - {52406, 14, 34, 25}, - {53000, 16, 22, 14}, - {54000, 16, 24, 15}, - {54054, 16, 173, 108}, - {54500, 14, 24, 17}, - {55000, 12, 22, 18}, - {56000, 14, 45, 31}, - {56250, 16, 25, 15}, - {56750, 14, 25, 17}, - {57000, 16, 27, 16}, - {58000, 16, 43, 25}, - {58250, 16, 38, 22}, - {58750, 16, 40, 23}, - {59000, 14, 26, 17}, - {59341, 14, 40, 26}, - {59400, 16, 44, 25}, - {60000, 16, 32, 18}, - {60500, 12, 39, 29}, - {61000, 14, 49, 31}, - {62000, 14, 37, 23}, - {62250, 14, 42, 26}, - {63000, 12, 21, 15}, - {63500, 14, 28, 17}, - {64000, 12, 27, 19}, - {65000, 14, 32, 19}, - {65250, 12, 29, 20}, - {65500, 12, 32, 22}, - {66000, 12, 22, 15}, - {66667, 14, 38, 22}, - {66750, 10, 21, 17}, - {67000, 14, 33, 19}, - {67750, 14, 58, 33}, - {68000, 14, 30, 17}, - {68179, 14, 46, 26}, - {68250, 14, 46, 26}, - {69000, 12, 23, 15}, - {70000, 12, 28, 18}, - {71000, 12, 30, 19}, - {72000, 12, 24, 15}, - {73000, 10, 23, 17}, - {74000, 12, 23, 14}, - {74176, 8, 100, 91}, - {74250, 10, 22, 16}, - {74481, 12, 43, 26}, - {74500, 10, 29, 21}, - {75000, 12, 25, 15}, - {75250, 10, 39, 28}, - {76000, 12, 27, 16}, - {77000, 12, 53, 31}, - {78000, 12, 26, 15}, - {78750, 12, 28, 16}, - {79000, 10, 38, 26}, - {79500, 10, 28, 19}, - {80000, 12, 32, 18}, - {81000, 10, 21, 14}, - {81081, 6, 100, 111}, - {81624, 8, 29, 24}, - {82000, 8, 17, 14}, - {83000, 10, 40, 26}, - {83950, 10, 28, 18}, - {84000, 10, 28, 18}, - {84750, 6, 16, 17}, - {85000, 6, 17, 18}, - {85250, 10, 30, 19}, - {85750, 10, 27, 17}, - {86000, 10, 43, 27}, - {87000, 10, 29, 18}, - {88000, 10, 44, 27}, - {88500, 10, 41, 25}, - {89000, 10, 28, 17}, - {89012, 6, 90, 91}, - {89100, 10, 33, 20}, - {90000, 10, 25, 15}, - {91000, 10, 32, 19}, - {92000, 10, 46, 27}, - {93000, 10, 31, 18}, - {94000, 10, 40, 23}, - {94500, 10, 28, 16}, - {95000, 10, 44, 25}, - {95654, 10, 39, 22}, - {95750, 10, 39, 22}, - {96000, 10, 32, 18}, - {97000, 8, 23, 16}, - {97750, 8, 42, 29}, - {98000, 8, 45, 31}, - {99000, 8, 22, 15}, - {99750, 8, 34, 23}, - {100000, 6, 20, 18}, - {100500, 6, 19, 17}, - {101000, 6, 37, 33}, - {101250, 8, 21, 14}, - {102000, 6, 17, 15}, - {102250, 6, 25, 22}, - {103000, 8, 29, 19}, - {104000, 8, 37, 24}, - {105000, 8, 28, 18}, - {106000, 8, 22, 14}, - {107000, 8, 46, 29}, - {107214, 8, 27, 17}, - {108000, 8, 24, 15}, - {108108, 8, 173, 108}, - {109000, 6, 23, 19}, - {110000, 6, 22, 18}, - {110013, 6, 22, 18}, - {110250, 8, 49, 30}, - {110500, 8, 36, 22}, - {111000, 8, 23, 14}, - {111264, 8, 150, 91}, - {111375, 8, 33, 20}, - {112000, 8, 63, 38}, - {112500, 8, 25, 15}, - {113100, 8, 57, 34}, - {113309, 8, 42, 25}, - {114000, 8, 27, 16}, - {115000, 6, 23, 18}, - {116000, 8, 43, 25}, - {117000, 8, 26, 15}, - {117500, 8, 40, 23}, - {118000, 6, 38, 29}, - {119000, 8, 30, 17}, - {119500, 8, 46, 26}, - {119651, 8, 39, 22}, - {120000, 8, 32, 18}, - {121000, 6, 39, 29}, - {121250, 6, 31, 23}, - {121750, 6, 23, 17}, - {122000, 6, 42, 31}, - {122614, 6, 30, 22}, - {123000, 6, 41, 30}, - {123379, 6, 37, 27}, - {124000, 6, 51, 37}, - {125000, 6, 25, 18}, - {125250, 4, 13, 14}, - {125750, 4, 27, 29}, - {126000, 6, 21, 15}, - {127000, 6, 24, 17}, - {127250, 6, 41, 29}, - {128000, 6, 27, 19}, - {129000, 6, 43, 30}, - {129859, 4, 25, 26}, - {130000, 6, 26, 18}, - {130250, 6, 42, 29}, - {131000, 6, 32, 22}, - {131500, 6, 38, 26}, - {131850, 6, 41, 28}, - {132000, 6, 22, 15}, - {132750, 6, 28, 19}, - {133000, 6, 34, 23}, - {133330, 6, 37, 25}, - {134000, 6, 61, 41}, - {135000, 6, 21, 14}, - {135250, 6, 167, 111}, - {136000, 6, 62, 41}, - {137000, 6, 35, 23}, - {138000, 6, 23, 15}, - {138500, 6, 40, 26}, - {138750, 6, 37, 24}, - {139000, 6, 34, 22}, - {139050, 6, 34, 22}, - {139054, 6, 34, 22}, - {140000, 6, 28, 18}, - {141000, 6, 36, 23}, - {141500, 6, 22, 14}, - {142000, 6, 30, 19}, - {143000, 6, 27, 17}, - {143472, 4, 17, 16}, - {144000, 6, 24, 15}, - {145000, 6, 29, 18}, - {146000, 6, 47, 29}, - {146250, 6, 26, 16}, - {147000, 6, 49, 30}, - {147891, 6, 23, 14}, - {148000, 6, 23, 14}, - {148250, 6, 28, 17}, - {148352, 4, 100, 91}, - {148500, 6, 33, 20}, - {149000, 6, 48, 29}, - {150000, 6, 25, 15}, - {151000, 4, 19, 17}, - {152000, 6, 27, 16}, - {152280, 6, 44, 26}, - {153000, 6, 34, 20}, - {154000, 6, 53, 31}, - {155000, 6, 31, 18}, - {155250, 6, 50, 29}, - {155750, 6, 45, 26}, - {156000, 6, 26, 15}, - {157000, 6, 61, 35}, - {157500, 6, 28, 16}, - {158000, 6, 65, 37}, - {158250, 6, 44, 25}, - {159000, 6, 53, 30}, - {159500, 6, 39, 22}, - {160000, 6, 32, 18}, - {161000, 4, 31, 26}, - {162000, 4, 18, 15}, - {162162, 4, 131, 109}, - {162500, 4, 53, 44}, - {163000, 4, 29, 24}, - {164000, 4, 17, 14}, - {165000, 4, 22, 18}, - {166000, 4, 32, 26}, - {167000, 4, 26, 21}, - {168000, 4, 46, 37}, - {169000, 4, 104, 83}, - {169128, 4, 64, 51}, - {169500, 4, 39, 31}, - {170000, 4, 34, 27}, - {171000, 4, 19, 15}, - {172000, 4, 51, 40}, - {172750, 4, 32, 25}, - {172800, 4, 32, 25}, - {173000, 4, 41, 32}, - {174000, 4, 49, 38}, - {174787, 4, 22, 17}, - {175000, 4, 35, 27}, - {176000, 4, 30, 23}, - {177000, 4, 38, 29}, - {178000, 4, 29, 22}, - {178500, 4, 37, 28}, - {179000, 4, 53, 40}, - {179500, 4, 73, 55}, - {180000, 4, 20, 15}, - {181000, 4, 55, 41}, - {182000, 4, 31, 23}, - {183000, 4, 42, 31}, - {184000, 4, 30, 22}, - {184750, 4, 26, 19}, - {185000, 4, 37, 27}, - {186000, 4, 51, 37}, - {187000, 4, 36, 26}, - {188000, 4, 32, 23}, - {189000, 4, 21, 15}, - {190000, 4, 38, 27}, - {190960, 4, 41, 29}, - {191000, 4, 41, 29}, - {192000, 4, 27, 19}, - {192250, 4, 37, 26}, - {193000, 4, 20, 14}, - {193250, 4, 53, 37}, - {194000, 4, 23, 16}, - {194208, 4, 23, 16}, - {195000, 4, 26, 18}, - {196000, 4, 45, 31}, - {197000, 4, 35, 24}, - {197750, 4, 41, 28}, - {198000, 4, 22, 15}, - {198500, 4, 25, 17}, - {199000, 4, 28, 19}, - {200000, 4, 37, 25}, - {201000, 4, 61, 41}, - {202000, 4, 112, 75}, - {202500, 4, 21, 14}, - {203000, 4, 146, 97}, - {204000, 4, 62, 41}, - {204750, 4, 44, 29}, - {205000, 4, 38, 25}, - {206000, 4, 29, 19}, - {207000, 4, 23, 15}, - {207500, 4, 40, 26}, - {208000, 4, 37, 24}, - {208900, 4, 48, 31}, - {209000, 4, 48, 31}, - {209250, 4, 31, 20}, - {210000, 4, 28, 18}, - {211000, 4, 25, 16}, - {212000, 4, 22, 14}, - {213000, 4, 30, 19}, - {213750, 4, 38, 24}, - {214000, 4, 46, 29}, - {214750, 4, 35, 22}, - {215000, 4, 43, 27}, - {216000, 4, 24, 15}, - {217000, 4, 37, 23}, - {218000, 4, 42, 26}, - {218250, 4, 42, 26}, - {218750, 4, 34, 21}, - {219000, 4, 47, 29}, - {220000, 4, 44, 27}, - {220640, 4, 49, 30}, - {220750, 4, 36, 22}, - {221000, 4, 36, 22}, - {222000, 4, 23, 14}, - {222525, 4, 28, 17}, - {222750, 4, 33, 20}, - {227000, 4, 37, 22}, - {230250, 4, 29, 17}, - {233500, 4, 38, 22}, - {235000, 4, 40, 23}, - {238000, 4, 30, 17}, - {241500, 2, 17, 19}, - {245250, 2, 20, 22}, - {247750, 2, 22, 24}, - {253250, 2, 15, 16}, - {256250, 2, 18, 19}, - {262500, 2, 31, 32}, - {267250, 2, 66, 67}, - {268500, 2, 94, 95}, - {270000, 2, 14, 14}, - {272500, 2, 77, 76}, - {273750, 2, 57, 56}, - {280750, 2, 24, 23}, - {281250, 2, 23, 22}, - {286000, 2, 17, 16}, - {291750, 2, 26, 24}, - {296703, 2, 56, 51}, - {297000, 2, 22, 20}, - {298000, 2, 21, 19}, -}; - static void intel_ddi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -675,7 +292,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, int pipe = intel_crtc->pipe; int type = intel_encoder->type; - DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", + DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); intel_crtc->eld_vld = false; @@ -686,22 +303,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, intel_dp->DP = intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; - switch (intel_dp->lane_count) { - case 1: - intel_dp->DP |= DDI_PORT_WIDTH_X1; - break; - case 2: - intel_dp->DP |= DDI_PORT_WIDTH_X2; - break; - case 4: - intel_dp->DP |= DDI_PORT_WIDTH_X4; - break; - default: - intel_dp->DP |= DDI_PORT_WIDTH_X4; - WARN(1, "Unexpected DP lane count %d\n", - intel_dp->lane_count); - break; - } + intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); if (intel_dp->has_audio) { DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n", @@ -748,8 +350,8 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) } if (num_encoders != 1) - WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders, - intel_crtc->pipe); + WARN(1, "%d encoders on crtc for pipe %c\n", num_encoders, + pipe_name(intel_crtc->pipe)); BUG_ON(ret == NULL); return ret; @@ -802,27 +404,224 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; } -static void intel_ddi_calculate_wrpll(int clock, int *p, int *n2, int *r2) +#define LC_FREQ 2700 +#define LC_FREQ_2K (LC_FREQ * 2000) + +#define P_MIN 2 +#define P_MAX 64 +#define P_INC 2 + +/* Constraints for PLL good behavior */ +#define REF_MIN 48 +#define REF_MAX 400 +#define VCO_MIN 2400 +#define VCO_MAX 4800 + +#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a)) + +struct wrpll_rnp { + unsigned p, n2, r2; +}; + +static unsigned wrpll_get_budget_for_freq(int clock) { - u32 i; + unsigned budget; - for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) - if (clock <= wrpll_tmds_clock_table[i].clock) - break; + switch (clock) { + case 25175000: + case 25200000: + case 27000000: + case 27027000: + case 37762500: + case 37800000: + case 40500000: + case 40541000: + case 54000000: + case 54054000: + case 59341000: + case 59400000: + case 72000000: + case 74176000: + case 74250000: + case 81000000: + case 81081000: + case 89012000: + case 89100000: + case 108000000: + case 108108000: + case 111264000: + case 111375000: + case 148352000: + case 148500000: + case 162000000: + case 162162000: + case 222525000: + case 222750000: + case 296703000: + case 297000000: + budget = 0; + break; + case 233500000: + case 245250000: + case 247750000: + case 253250000: + case 298000000: + budget = 1500; + break; + case 169128000: + case 169500000: + case 179500000: + case 202000000: + budget = 2000; + break; + case 256250000: + case 262500000: + case 270000000: + case 272500000: + case 273750000: + case 280750000: + case 281250000: + case 286000000: + case 291750000: + budget = 4000; + break; + case 267250000: + case 268500000: + budget = 5000; + break; + default: + budget = 1000; + break; + } - if (i == ARRAY_SIZE(wrpll_tmds_clock_table)) - i--; + return budget; +} - *p = wrpll_tmds_clock_table[i].p; - *n2 = wrpll_tmds_clock_table[i].n2; - *r2 = wrpll_tmds_clock_table[i].r2; +static void wrpll_update_rnp(uint64_t freq2k, unsigned budget, + unsigned r2, unsigned n2, unsigned p, + struct wrpll_rnp *best) +{ + uint64_t a, b, c, d, diff, diff_best; - if (wrpll_tmds_clock_table[i].clock != clock) - DRM_INFO("WRPLL: using settings for %dKHz on %dKHz mode\n", - wrpll_tmds_clock_table[i].clock, clock); + /* No best (r,n,p) yet */ + if (best->p == 0) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + return; + } - DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", - clock, *p, *n2, *r2); + /* + * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to + * freq2k. + * + * delta = 1e6 * + * abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) / + * freq2k; + * + * and we would like delta <= budget. + * + * If the discrepancy is above the PPM-based budget, always prefer to + * improve upon the previous solution. However, if you're within the + * budget, try to maximize Ref * VCO, that is N / (P * R^2). + */ + a = freq2k * budget * p * r2; + b = freq2k * budget * best->p * best->r2; + diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2)); + diff_best = ABS_DIFF((freq2k * best->p * best->r2), + (LC_FREQ_2K * best->n2)); + c = 1000000 * diff; + d = 1000000 * diff_best; + + if (a < c && b < d) { + /* If both are above the budget, pick the closer */ + if (best->p * best->r2 * diff < p * r2 * diff_best) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + } + } else if (a >= c && b < d) { + /* If A is below the threshold but B is above it? Update. */ + best->p = p; + best->n2 = n2; + best->r2 = r2; + } else if (a >= c && b >= d) { + /* Both are below the limit, so pick the higher n2/(r2*r2) */ + if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + } + } + /* Otherwise a < c && b >= d, do nothing */ +} + +static void +intel_ddi_calculate_wrpll(int clock /* in Hz */, + unsigned *r2_out, unsigned *n2_out, unsigned *p_out) +{ + uint64_t freq2k; + unsigned p, n2, r2; + struct wrpll_rnp best = { 0, 0, 0 }; + unsigned budget; + + freq2k = clock / 100; + + budget = wrpll_get_budget_for_freq(clock); + + /* Special case handling for 540 pixel clock: bypass WR PLL entirely + * and directly pass the LC PLL to it. */ + if (freq2k == 5400000) { + *n2_out = 2; + *p_out = 1; + *r2_out = 2; + return; + } + + /* + * Ref = LC_FREQ / R, where Ref is the actual reference input seen by + * the WR PLL. + * + * We want R so that REF_MIN <= Ref <= REF_MAX. + * Injecting R2 = 2 * R gives: + * REF_MAX * r2 > LC_FREQ * 2 and + * REF_MIN * r2 < LC_FREQ * 2 + * + * Which means the desired boundaries for r2 are: + * LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN + * + */ + for (r2 = LC_FREQ * 2 / REF_MAX + 1; + r2 <= LC_FREQ * 2 / REF_MIN; + r2++) { + + /* + * VCO = N * Ref, that is: VCO = N * LC_FREQ / R + * + * Once again we want VCO_MIN <= VCO <= VCO_MAX. + * Injecting R2 = 2 * R and N2 = 2 * N, we get: + * VCO_MAX * r2 > n2 * LC_FREQ and + * VCO_MIN * r2 < n2 * LC_FREQ) + * + * Which means the desired boundaries for n2 are: + * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ + */ + for (n2 = VCO_MIN * r2 / LC_FREQ + 1; + n2 <= VCO_MAX * r2 / LC_FREQ; + n2++) { + + for (p = P_MIN; p <= P_MAX; p += P_INC) + wrpll_update_rnp(freq2k, budget, + r2, n2, p, &best); + } + } + + *n2_out = best.n2; + *p_out = best.p; + *r2_out = best.r2; + + DRM_DEBUG_KMS("WRPLL: %dHz refresh rate with p=%d, n2=%d r2=%d\n", + clock, *p_out, *n2_out, *r2_out); } bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) @@ -863,7 +662,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) return true; } else if (type == INTEL_OUTPUT_HDMI) { - int p, n2, r2; + unsigned p, n2, r2; if (plls->wrpll1_refcount == 0) { DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n", @@ -885,7 +684,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) WARN(I915_READ(reg) & WRPLL_PLL_ENABLE, "WRPLL already enabled\n"); - intel_ddi_calculate_wrpll(clock, &p, &n2, &r2); + intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | @@ -995,7 +794,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) /* Can only use the always-on power well for eDP when * not using the panel fitter, and when not using motion * blur mitigation (which we don't support). */ - if (dev_priv->pch_pf_size) + if (intel_crtc->config.pch_pfit.size) temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; else temp |= TRANS_DDI_EDP_INPUT_A_ON; @@ -1022,7 +821,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) } else if (type == INTEL_OUTPUT_ANALOG) { temp |= TRANS_DDI_MODE_SELECT_FDI; - temp |= (intel_crtc->fdi_lanes - 1) << 1; + temp |= (intel_crtc->config.fdi_lanes - 1) << 1; } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { @@ -1030,25 +829,10 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) temp |= TRANS_DDI_MODE_SELECT_DP_SST; - switch (intel_dp->lane_count) { - case 1: - temp |= TRANS_DDI_PORT_WIDTH_X1; - break; - case 2: - temp |= TRANS_DDI_PORT_WIDTH_X2; - break; - case 4: - temp |= TRANS_DDI_PORT_WIDTH_X4; - break; - default: - temp |= TRANS_DDI_PORT_WIDTH_X4; - WARN(1, "Unsupported lane count %d\n", - intel_dp->lane_count); - } - + temp |= DDI_PORT_WIDTH(intel_dp->lane_count); } else { - WARN(1, "Invalid encoder type %d for pipe %d\n", - intel_encoder->type, pipe); + WARN(1, "Invalid encoder type %d for pipe %c\n", + intel_encoder->type, pipe_name(pipe)); } I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp); @@ -1148,7 +932,7 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, } } - DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port); + DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); return false; } @@ -1334,7 +1118,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) ironlake_edp_backlight_on(intel_dp); } - if (intel_crtc->eld_vld) { + if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) { tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); @@ -1352,9 +1136,12 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t tmp; - tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); - tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); - I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) { + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << + (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + } if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); @@ -1518,16 +1305,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port) return; } - if (port != PORT_A) { - hdmi_connector = kzalloc(sizeof(struct intel_connector), - GFP_KERNEL); - if (!hdmi_connector) { - kfree(dp_connector); - kfree(intel_dig_port); - return; - } - } - intel_encoder = &intel_dig_port->base; encoder = &intel_encoder->base; @@ -1545,8 +1322,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_dig_port->port = port; intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & DDI_BUF_PORT_REVERSAL; - if (hdmi_connector) - intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); intel_encoder->type = INTEL_OUTPUT_UNKNOWN; @@ -1554,7 +1329,16 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_ddi_hot_plug; - if (hdmi_connector) - intel_hdmi_init_connector(intel_dig_port, hdmi_connector); intel_dp_init_connector(intel_dig_port, dp_connector); + + if (intel_encoder->type != INTEL_OUTPUT_EDP) { + hdmi_connector = kzalloc(sizeof(struct intel_connector), + GFP_KERNEL); + if (!hdmi_connector) { + return; + } + + intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); + intel_hdmi_init_connector(intel_dig_port, hdmi_connector); + } } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ad1117bebd7e..e81eb20e2c88 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -45,18 +45,6 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type); static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); -typedef struct { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -} intel_clock_t; - typedef struct { int min, max; } intel_range_t; @@ -113,15 +101,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, intel_clock_t *best_clock); -static bool -intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock); -static bool -intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock); - static bool intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, @@ -254,20 +233,6 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { .find_pll = intel_g4x_find_best_PLL, }; -static const intel_limit_t intel_limits_g4x_display_port = { - .dot = { .min = 161670, .max = 227000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 97, .max = 108 }, - .m1 = { .min = 0x10, .max = 0x12 }, - .m2 = { .min = 0x05, .max = 0x06 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, - .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_g4x_dp, -}; - static const intel_limit_t intel_limits_pineview_sdvo = { .dot = { .min = 20000, .max = 400000}, .vco = { .min = 1700000, .max = 3500000 }, @@ -374,20 +339,6 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { .find_pll = intel_g4x_find_best_PLL, }; -static const intel_limit_t intel_limits_ironlake_display_port = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 81, .max = 90 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, - .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_ironlake_dp, -}; - static const intel_limit_t intel_limits_vlv_dac = { .dot = { .min = 25000, .max = 270000 }, .vco = { .min = 4000000, .max = 6000000 }, @@ -396,15 +347,15 @@ static const intel_limit_t intel_limits_vlv_dac = { .m1 = { .min = 2, .max = 3 }, .m2 = { .min = 11, .max = 156 }, .p = { .min = 10, .max = 30 }, - .p1 = { .min = 2, .max = 3 }, + .p1 = { .min = 1, .max = 3 }, .p2 = { .dot_limit = 270000, .p2_slow = 2, .p2_fast = 20 }, .find_pll = intel_vlv_find_best_pll, }; static const intel_limit_t intel_limits_vlv_hdmi = { - .dot = { .min = 20000, .max = 165000 }, - .vco = { .min = 4000000, .max = 5994000}, + .dot = { .min = 25000, .max = 270000 }, + .vco = { .min = 4000000, .max = 6000000 }, .n = { .min = 1, .max = 7 }, .m = { .min = 60, .max = 300 }, /* guess */ .m1 = { .min = 2, .max = 3 }, @@ -424,7 +375,7 @@ static const intel_limit_t intel_limits_vlv_dp = { .m1 = { .min = 2, .max = 3 }, .m2 = { .min = 11, .max = 156 }, .p = { .min = 10, .max = 30 }, - .p1 = { .min = 2, .max = 3 }, + .p1 = { .min = 1, .max = 3 }, .p2 = { .dot_limit = 270000, .p2_slow = 2, .p2_fast = 20 }, .find_pll = intel_vlv_find_best_pll, @@ -450,8 +401,7 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) return I915_READ(DPIO_DATA); } -static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, - u32 val) +void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); @@ -468,17 +418,6 @@ static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, DRM_ERROR("DPIO write wait timed out\n"); } -static void vlv_init_dpio(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - /* Reset the DPIO config */ - I915_WRITE(DPIO_CTL, 0); - POSTING_READ(DPIO_CTL); - I915_WRITE(DPIO_CTL, 1); - POSTING_READ(DPIO_CTL); -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { @@ -497,10 +436,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, else limit = &intel_limits_ironlake_single_lvds; } - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) - limit = &intel_limits_ironlake_display_port; - else + } else limit = &intel_limits_ironlake_dac; return limit; @@ -521,8 +457,6 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) limit = &intel_limits_g4x_hdmi; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { limit = &intel_limits_g4x_sdvo; - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { - limit = &intel_limits_g4x_display_port; } else /* The option is for other outputs */ limit = &intel_limits_i9xx_sdvo; @@ -573,13 +507,18 @@ static void pineview_clock(int refclk, intel_clock_t *clock) clock->dot = clock->vco / clock->p; } +static uint32_t i9xx_dpll_compute_m(struct dpll *dpll) +{ + return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); +} + static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock) { if (IS_PINEVIEW(dev)) { pineview_clock(refclk, clock); return; } - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->m = i9xx_dpll_compute_m(clock); clock->p = clock->p1 * clock->p2; clock->vco = refclk * clock->m / (clock->n + 2); clock->dot = clock->vco / clock->p; @@ -712,12 +651,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, found = false; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - int lvds_reg; - - if (HAS_PCH_SPLIT(dev)) - lvds_reg = PCH_LVDS; - else - lvds_reg = LVDS; if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else @@ -746,9 +679,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, if (!intel_PLL_is_valid(dev, limit, &clock)) continue; - if (match_clock && - clock.p != match_clock->p) - continue; this_err = abs(clock.dot - target); if (this_err < err_most) { @@ -764,59 +694,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, return found; } -static bool -intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - - if (target < 200000) { - clock.n = 1; - clock.p1 = 2; - clock.p2 = 10; - clock.m1 = 12; - clock.m2 = 9; - } else { - clock.n = 2; - clock.p1 = 1; - clock.p2 = 10; - clock.m1 = 14; - clock.m2 = 8; - } - intel_clock(dev, refclk, &clock); - memcpy(best_clock, &clock, sizeof(intel_clock_t)); - return true; -} - -/* DisplayPort has only two frequencies, 162MHz and 270MHz */ -static bool -intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - intel_clock_t clock; - if (target < 200000) { - clock.p1 = 2; - clock.p2 = 10; - clock.n = 2; - clock.m1 = 23; - clock.m2 = 8; - } else { - clock.p1 = 1; - clock.p2 = 10; - clock.n = 1; - clock.m1 = 14; - clock.m2 = 2; - } - clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); - clock.p = (clock.p1 * clock.p2); - clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; - clock.vco = 0; - memcpy(best_clock, &clock, sizeof(intel_clock_t)); - return true; -} static bool intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, @@ -1097,14 +974,14 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, pch_dpll = I915_READ(PCH_DPLL_SEL); cur_state = pll->pll_reg == _PCH_DPLL_B; if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state, - "PLL[%d] not attached to this transcoder %d: %08x\n", - cur_state, crtc->pipe, pch_dpll)) { + "PLL[%d] not attached to this transcoder %c: %08x\n", + cur_state, pipe_name(crtc->pipe), pch_dpll)) { cur_state = !!(val >> (4*crtc->pipe + 3)); WARN(cur_state != state, - "PLL[%d] not %s on this transcoder %d: %08x\n", + "PLL[%d] not %s on this transcoder %c: %08x\n", pll->pll_reg == _PCH_DPLL_B, state_string(state), - crtc->pipe, + pipe_name(crtc->pipe), val); } } @@ -1227,8 +1104,8 @@ void assert_pipe(struct drm_i915_private *dev_priv, if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) state = true; - if (!intel_using_power_well(dev_priv->dev) && - cpu_transcoder != TRANSCODER_EDP) { + if (!intel_display_power_enabled(dev_priv->dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) { cur_state = false; } else { reg = PIPECONF(cpu_transcoder); @@ -1302,8 +1179,8 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, reg = SPCNTR(pipe, i); val = I915_READ(reg); WARN((val & SP_ENABLE), - "sprite %d assertion failure, should be off on pipe %c but is still active\n", - pipe * 2 + i, pipe_name(pipe)); + "sprite %c assertion failure, should be off on pipe %c but is still active\n", + sprite_name(pipe, i), pipe_name(pipe)); } } @@ -1323,14 +1200,14 @@ static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) WARN(!enabled, "PCH refclk assertion failure, should be active but is disabled\n"); } -static void assert_transcoder_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, + enum pipe pipe) { int reg; u32 val; bool enabled; - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); enabled = !!(val & TRANS_ENABLE); WARN(enabled, @@ -1474,6 +1351,8 @@ static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) int reg; u32 val; + assert_pipe_disabled(dev_priv, pipe); + /* No really, not for ILK+ */ BUG_ON(!IS_VALLEYVIEW(dev_priv->dev) && dev_priv->info->gen >= 5); @@ -1586,6 +1465,20 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, return I915_READ(SBI_DATA); } +void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port) +{ + u32 port_mask; + + if (!port) + port_mask = DPLL_PORTB_READY_MASK; + else + port_mask = DPLL_PORTC_READY_MASK; + + if (wait_for((I915_READ(DPLL(0)) & port_mask) == 0, 1000)) + WARN(1, "timed out waiting for port %c ready: 0x%08x\n", + 'B' + port, I915_READ(DPLL(0))); +} + /** * ironlake_enable_pch_pll - enable PCH PLL * @dev_priv: i915 private structure @@ -1666,7 +1559,7 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); /* Make sure transcoder isn't still depending on us */ - assert_transcoder_disabled(dev_priv, intel_crtc->pipe); + assert_pch_transcoder_disabled(dev_priv, intel_crtc->pipe); reg = pll->pll_reg; val = I915_READ(reg); @@ -1706,7 +1599,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); } - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); pipeconf_val = I915_READ(PIPECONF(pipe)); @@ -1731,7 +1624,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val | TRANS_ENABLE); if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) - DRM_ERROR("failed to enable transcoder %d\n", pipe); + DRM_ERROR("failed to enable transcoder %c\n", pipe_name(pipe)); } static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, @@ -1760,8 +1653,8 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, else val |= TRANS_PROGRESSIVE; - I915_WRITE(TRANSCONF(TRANSCODER_A), val); - if (wait_for(I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE, 100)) + I915_WRITE(LPT_TRANSCONF, val); + if (wait_for(I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE, 100)) DRM_ERROR("Failed to enable PCH transcoder\n"); } @@ -1778,13 +1671,13 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv, /* Ports must be off as well */ assert_pch_ports_disabled(dev_priv, pipe); - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); val &= ~TRANS_ENABLE; I915_WRITE(reg, val); /* wait for PCH transcoder off, transcoder state */ if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) - DRM_ERROR("failed to disable transcoder %d\n", pipe); + DRM_ERROR("failed to disable transcoder %c\n", pipe_name(pipe)); if (!HAS_PCH_IBX(dev)) { /* Workaround: Clear the timing override chicken bit again. */ @@ -1799,11 +1692,11 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv) { u32 val; - val = I915_READ(_TRANSACONF); + val = I915_READ(LPT_TRANSCONF); val &= ~TRANS_ENABLE; - I915_WRITE(_TRANSACONF, val); + I915_WRITE(LPT_TRANSCONF, val); /* wait for PCH transcoder off, transcoder state */ - if (wait_for((I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE) == 0, 50)) + if (wait_for((I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE) == 0, 50)) DRM_ERROR("Failed to disable PCH transcoder\n"); /* Workaround: clear timing override bit. */ @@ -1835,6 +1728,9 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int reg; u32 val; + assert_planes_disabled(dev_priv, pipe); + assert_sprites_disabled(dev_priv, pipe); + if (HAS_PCH_LPT(dev_priv->dev)) pch_transcoder = TRANSCODER_A; else @@ -2096,7 +1992,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, case 1: break; default: - DRM_ERROR("Can't update plane %d in SAREA\n", plane); + DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane)); return -EINVAL; } @@ -2193,7 +2089,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc, case 2: break; default: - DRM_ERROR("Can't update plane %d in SAREA\n", plane); + DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane)); return -EINVAL; } @@ -2384,9 +2280,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, } if (intel_crtc->plane > INTEL_INFO(dev)->num_pipes) { - DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", - intel_crtc->plane, - INTEL_INFO(dev)->num_pipes); + DRM_ERROR("no plane for crtc: plane %c, num_pipes %d\n", + plane_name(intel_crtc->plane), + INTEL_INFO(dev)->num_pipes); return -EINVAL; } @@ -2467,6 +2363,11 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc) FDI_FE_ERRC_ENABLE); } +static bool pipe_has_enabled_pch(struct intel_crtc *intel_crtc) +{ + return intel_crtc->base.enabled && intel_crtc->config.has_pch_encoder; +} + static void ivb_modeset_global_resources(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2476,10 +2377,13 @@ static void ivb_modeset_global_resources(struct drm_device *dev) to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_C]); uint32_t temp; - /* When everything is off disable fdi C so that we could enable fdi B - * with all lanes. XXX: This misses the case where a pipe is not using - * any pch resources and so doesn't need any fdi lanes. */ - if (!pipe_B_crtc->base.enabled && !pipe_C_crtc->base.enabled) { + /* + * When everything is off disable fdi C so that we could enable fdi B + * with all lanes. Note that we don't care about enabled pipes without + * an enabled pch encoder. + */ + if (!pipe_has_enabled_pch(pipe_B_crtc) && + !pipe_has_enabled_pch(pipe_C_crtc)) { WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE); WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE); @@ -2517,8 +2421,8 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(reg, temp | FDI_TX_ENABLE); @@ -2615,8 +2519,8 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2750,8 +2654,8 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2852,8 +2756,8 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ reg = FDI_RX_CTL(pipe); temp = I915_READ(reg); - temp &= ~((0x7 << 19) | (0x7 << 16)); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); @@ -3085,6 +2989,30 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) mutex_unlock(&dev_priv->dpio_lock); } +static void ironlake_pch_transcoder_set_timings(struct intel_crtc *crtc, + enum pipe pch_transcoder) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + + I915_WRITE(PCH_TRANS_HTOTAL(pch_transcoder), + I915_READ(HTOTAL(cpu_transcoder))); + I915_WRITE(PCH_TRANS_HBLANK(pch_transcoder), + I915_READ(HBLANK(cpu_transcoder))); + I915_WRITE(PCH_TRANS_HSYNC(pch_transcoder), + I915_READ(HSYNC(cpu_transcoder))); + + I915_WRITE(PCH_TRANS_VTOTAL(pch_transcoder), + I915_READ(VTOTAL(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VBLANK(pch_transcoder), + I915_READ(VBLANK(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VSYNC(pch_transcoder), + I915_READ(VSYNC(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VSYNCSHIFT(pch_transcoder), + I915_READ(VSYNCSHIFT(cpu_transcoder))); +} + /* * Enable PCH resources required for PCH ports: * - PCH PLLs @@ -3101,7 +3029,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; u32 reg, temp; - assert_transcoder_disabled(dev_priv, pipe); + assert_pch_transcoder_disabled(dev_priv, pipe); /* Write the TU size bits before fdi link training, so that error * detection works. */ @@ -3148,14 +3076,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* set transcoder timing, panel must allow it */ assert_panel_unlocked(dev_priv, pipe); - I915_WRITE(TRANS_HTOTAL(pipe), I915_READ(HTOTAL(pipe))); - I915_WRITE(TRANS_HBLANK(pipe), I915_READ(HBLANK(pipe))); - I915_WRITE(TRANS_HSYNC(pipe), I915_READ(HSYNC(pipe))); - - I915_WRITE(TRANS_VTOTAL(pipe), I915_READ(VTOTAL(pipe))); - I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe))); - I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); - I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe))); + ironlake_pch_transcoder_set_timings(intel_crtc, pipe); intel_fdi_normal_train(crtc); @@ -3205,19 +3126,12 @@ static void lpt_pch_enable(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; - assert_transcoder_disabled(dev_priv, TRANSCODER_A); + assert_pch_transcoder_disabled(dev_priv, TRANSCODER_A); lpt_program_iclkip(crtc); /* Set transcoder timing. */ - I915_WRITE(_TRANS_HTOTAL_A, I915_READ(HTOTAL(cpu_transcoder))); - I915_WRITE(_TRANS_HBLANK_A, I915_READ(HBLANK(cpu_transcoder))); - I915_WRITE(_TRANS_HSYNC_A, I915_READ(HSYNC(cpu_transcoder))); - - I915_WRITE(_TRANS_VTOTAL_A, I915_READ(VTOTAL(cpu_transcoder))); - I915_WRITE(_TRANS_VBLANK_A, I915_READ(VBLANK(cpu_transcoder))); - I915_WRITE(_TRANS_VSYNC_A, I915_READ(VSYNC(cpu_transcoder))); - I915_WRITE(_TRANS_VSYNCSHIFT_A, I915_READ(VSYNCSHIFT(cpu_transcoder))); + ironlake_pch_transcoder_set_timings(intel_crtc, PIPE_A); lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } @@ -3294,7 +3208,7 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 found: intel_crtc->pch_pll = pll; pll->refcount++; - DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); + DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe)); prepare: /* separate function? */ DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); @@ -3309,7 +3223,7 @@ prepare: /* separate function? */ return pll; } -void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) +static void cpt_verify_modeset(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; int dslreg = PIPEDSL(pipe); @@ -3319,7 +3233,28 @@ void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) udelay(500); if (wait_for(I915_READ(dslreg) != temp, 5)) { if (wait_for(I915_READ(dslreg) != temp, 5)) - DRM_ERROR("mode set failed: pipe %d stuck\n", pipe); + DRM_ERROR("mode set failed: pipe %c stuck\n", pipe_name(pipe)); + } +} + +static void ironlake_pfit_enable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + if (crtc->config.pch_pfit.size) { + /* Force use of hard-coded filter coefficients + * as some pre-programmed values are broken, + * e.g. x201. + */ + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) + I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | + PF_PIPE_SEL_IVB(pipe)); + else + I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3); + I915_WRITE(PF_WIN_POS(pipe), crtc->config.pch_pfit.pos); + I915_WRITE(PF_WIN_SZ(pipe), crtc->config.pch_pfit.size); } } @@ -3339,6 +3274,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) return; intel_crtc->active = true; + + intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); + intel_set_pch_fifo_underrun_reporting(dev, pipe, true); + intel_update_watermarks(dev); if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { @@ -3363,21 +3302,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) encoder->pre_enable(encoder); /* Enable panel fitting for LVDS */ - if (dev_priv->pch_pf_size && - (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { - /* Force use of hard-coded filter coefficients - * as some pre-programmed values are broken, - * e.g. x201. - */ - if (IS_IVYBRIDGE(dev)) - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | - PF_PIPE_SEL_IVB(pipe)); - else - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3); - I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos); - I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size); - } + ironlake_pfit_enable(intel_crtc); /* * On ILK+ LUT must be loaded before the pipe is running but with @@ -3402,7 +3327,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) encoder->enable(encoder); if (HAS_PCH_CPT(dev)) - intel_cpt_verify_modeset(dev, intel_crtc->pipe); + cpt_verify_modeset(dev, intel_crtc->pipe); /* * There seems to be a race in PCH platform hw (at least on some @@ -3430,6 +3355,11 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) return; 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); + intel_update_watermarks(dev); if (intel_crtc->config.has_pch_encoder) @@ -3442,17 +3372,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_ddi_enable_pipe_clock(intel_crtc); /* Enable panel fitting for eDP */ - if (dev_priv->pch_pf_size && - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { - /* Force use of hard-coded filter coefficients - * as some pre-programmed values are broken, - * e.g. x201. - */ - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | - PF_PIPE_SEL_IVB(pipe)); - I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos); - I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size); - } + ironlake_pfit_enable(intel_crtc); /* * On ILK+ LUT must be loaded before the pipe is running but with @@ -3490,6 +3410,21 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_wait_for_vblank(dev, intel_crtc->pipe); } +static void ironlake_pfit_disable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + /* To avoid upsetting the power well on haswell only disable the pfit if + * it's in use. The hw state code will make sure we get this right. */ + if (crtc->config.pch_pfit.size) { + I915_WRITE(PF_CTL(pipe), 0); + I915_WRITE(PF_WIN_POS(pipe), 0); + I915_WRITE(PF_WIN_SZ(pipe), 0); + } +} + static void ironlake_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3516,11 +3451,10 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + intel_set_pch_fifo_underrun_reporting(dev, pipe, false); intel_disable_pipe(dev_priv, pipe); - /* Disable PF */ - I915_WRITE(PF_CTL(pipe), 0); - I915_WRITE(PF_WIN_SZ(pipe), 0); + ironlake_pfit_disable(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->post_disable) @@ -3529,6 +3463,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ironlake_fdi_disable(crtc); ironlake_disable_pch_transcoder(dev_priv, pipe); + intel_set_pch_fifo_underrun_reporting(dev, pipe, true); if (HAS_PCH_CPT(dev)) { /* disable TRANS_DP_CTL */ @@ -3590,22 +3525,19 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false); - intel_disable_plane(dev_priv, plane, pipe); - + /* FBC must be disabled before disabling the plane on HSW. */ if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + intel_disable_plane(dev_priv, plane, pipe); + + if (intel_crtc->config.has_pch_encoder) + intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false); intel_disable_pipe(dev_priv, pipe); intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder); - /* XXX: Once we have proper panel fitter state tracking implemented with - * hardware state read/check support we should switch to only disable - * the panel fitter when we know it's used. */ - if (intel_using_power_well(dev)) { - I915_WRITE(PF_CTL(pipe), 0); - I915_WRITE(PF_WIN_SZ(pipe), 0); - } + ironlake_pfit_disable(intel_crtc); intel_ddi_disable_pipe_clock(intel_crtc); @@ -3615,6 +3547,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) 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); } @@ -3685,6 +3618,85 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) } } +static void i9xx_pfit_enable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc_config *pipe_config = &crtc->config; + + if (!crtc->config.gmch_pfit.control) + return; + + WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE); + assert_pipe_disabled(dev_priv, crtc->pipe); + + /* + * Enable automatic panel scaling so that non-native modes + * fill the screen. The panel fitter should only be + * adjusted whilst the pipe is disabled, according to + * register description and PRM. + */ + DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", + pipe_config->gmch_pfit.control, + pipe_config->gmch_pfit.pgm_ratios); + + I915_WRITE(PFIT_PGM_RATIOS, pipe_config->gmch_pfit.pgm_ratios); + I915_WRITE(PFIT_CONTROL, pipe_config->gmch_pfit.control); + + /* Border color in case we don't scale up to the full screen. Black by + * default, change to something else for debugging. */ + I915_WRITE(BCLRPAT(crtc->pipe), 0); +} + +static void valleyview_crtc_enable(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 *encoder; + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + + WARN_ON(!crtc->enabled); + + if (intel_crtc->active) + return; + + intel_crtc->active = true; + intel_update_watermarks(dev); + + mutex_lock(&dev_priv->dpio_lock); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + + intel_enable_pll(dev_priv, pipe); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_enable) + encoder->pre_enable(encoder); + + /* VLV wants encoder enabling _before_ the pipe is up. */ + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->enable(encoder); + + /* Enable panel fitting for eDP */ + i9xx_pfit_enable(intel_crtc); + + intel_enable_pipe(dev_priv, pipe, false); + intel_enable_plane(dev_priv, plane, pipe); + + intel_crtc_load_lut(crtc); + intel_update_fbc(dev); + + /* Give the overlay scaler a chance to enable if it's on this pipe */ + intel_crtc_dpms_overlay(intel_crtc, true); + intel_crtc_update_cursor(crtc, true); + + mutex_unlock(&dev_priv->dpio_lock); +} + static void i9xx_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3708,6 +3720,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) if (encoder->pre_enable) encoder->pre_enable(encoder); + /* Enable panel fitting for LVDS */ + i9xx_pfit_enable(intel_crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); if (IS_G4X(dev)) @@ -3728,20 +3743,15 @@ static void i9xx_pfit_disable(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum pipe pipe; - uint32_t pctl = I915_READ(PFIT_CONTROL); + + if (!crtc->config.gmch_pfit.control) + return; assert_pipe_disabled(dev_priv, crtc->pipe); - if (INTEL_INFO(dev)->gen >= 4) - pipe = (pctl & PFIT_PIPE_MASK) >> PFIT_PIPE_SHIFT; - else - pipe = PIPE_B; - - if (pipe == crtc->pipe) { - DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n", pctl); - I915_WRITE(PFIT_CONTROL, 0); - } + DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n", + I915_READ(PFIT_CONTROL)); + I915_WRITE(PFIT_CONTROL, 0); } static void i9xx_crtc_disable(struct drm_crtc *crtc) @@ -3773,6 +3783,10 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) i9xx_pfit_disable(intel_crtc); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->post_disable) + encoder->post_disable(encoder); + intel_disable_pll(dev_priv, pipe); intel_crtc->active = false; @@ -3845,8 +3859,8 @@ static void intel_crtc_disable(struct drm_crtc *crtc) /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); - intel_crtc->eld_vld = false; dev_priv->display.crtc_disable(crtc); + intel_crtc->eld_vld = false; intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); @@ -3977,8 +3991,118 @@ bool intel_connector_get_hw_state(struct intel_connector *connector) return encoder->get_hw_state(encoder, &pipe); } -static bool intel_crtc_compute_config(struct drm_crtc *crtc, - struct intel_crtc_config *pipe_config) +static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, + struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *pipe_B_crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); + + DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n", + pipe_name(pipe), pipe_config->fdi_lanes); + if (pipe_config->fdi_lanes > 4) { + DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + + if (IS_HASWELL(dev)) { + if (pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("only 2 lanes on haswell, required: %i lanes\n", + pipe_config->fdi_lanes); + return false; + } else { + return true; + } + } + + if (INTEL_INFO(dev)->num_pipes == 2) + return true; + + /* Ivybridge 3 pipe is really complicated */ + switch (pipe) { + case PIPE_A: + return true; + case PIPE_B: + if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && + pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + return true; + case PIPE_C: + if (!pipe_has_enabled_pch(pipe_B_crtc) || + pipe_B_crtc->config.fdi_lanes <= 2) { + if (pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + } else { + DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n"); + return false; + } + return true; + default: + BUG(); + } +} + +#define RETRY 1 +static int ironlake_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + int target_clock, lane, link_bw; + bool setup_ok, needs_recompute = false; + +retry: + /* FDI is a binary signal running at ~2.7GHz, encoding + * each output octet as 10 bits. The actual frequency + * is stored as a divider into a 100MHz clock, and the + * mode pixel clock is stored in units of 1KHz. + * Hence the bw of each lane in terms of the mode signal + * is: + */ + link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; + + if (pipe_config->pixel_target_clock) + target_clock = pipe_config->pixel_target_clock; + else + target_clock = adjusted_mode->clock; + + lane = ironlake_get_lanes_required(target_clock, link_bw, + pipe_config->pipe_bpp); + + pipe_config->fdi_lanes = lane; + + if (pipe_config->pixel_multiplier > 1) + link_bw *= pipe_config->pixel_multiplier; + intel_link_compute_m_n(pipe_config->pipe_bpp, lane, target_clock, + link_bw, &pipe_config->fdi_m_n); + + setup_ok = ironlake_check_fdi_lanes(intel_crtc->base.dev, + intel_crtc->pipe, pipe_config); + if (!setup_ok && pipe_config->pipe_bpp > 6*3) { + pipe_config->pipe_bpp -= 2*3; + DRM_DEBUG_KMS("fdi link bw constraint, reducing pipe bpp to %i\n", + pipe_config->pipe_bpp); + needs_recompute = true; + pipe_config->bw_constrained = true; + + goto retry; + } + + if (needs_recompute) + return RETRY; + + return setup_ok ? 0 : -EINVAL; +} + +static int intel_crtc_compute_config(struct drm_crtc *crtc, + struct intel_crtc_config *pipe_config) { struct drm_device *dev = crtc->dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; @@ -3987,7 +4111,7 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, /* FDI link clock is fixed at 2.7G */ if (pipe_config->requested_mode.clock * 3 > IRONLAKE_FDI_FREQ * 4) - return false; + return -EINVAL; } /* All interlaced capable intel hw wants timings in frames. Note though @@ -3996,12 +4120,12 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, if (!pipe_config->timings_set) drm_mode_set_crtcinfo(adjusted_mode, 0); - /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes - * with a hsync front porch of 0. + /* Cantiga+ cannot handle modes with a hsync front porch of 0. + * WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw. */ if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && adjusted_mode->hsync_start == adjusted_mode->hdisplay) - return false; + return -EINVAL; if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && pipe_config->pipe_bpp > 10*3) { pipe_config->pipe_bpp = 10*3; /* 12bpc is gen5+ */ @@ -4011,7 +4135,10 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, pipe_config->pipe_bpp = 8*3; } - return true; + if (pipe_config->has_pch_encoder) + return ironlake_fdi_compute_config(to_intel_crtc(crtc), pipe_config); + + return 0; } static int valleyview_get_display_clock_speed(struct drm_device *dev) @@ -4120,7 +4247,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) { if (i915_panel_use_ssc >= 0) return i915_panel_use_ssc != 0; - return dev_priv->lvds_use_ssc + return dev_priv->vbt.lvds_use_ssc && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } @@ -4156,7 +4283,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) refclk = vlv_get_refclk(crtc); } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - refclk = dev_priv->lvds_ssc_freq * 1000; + refclk = dev_priv->vbt.lvds_ssc_freq * 1000; DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", refclk / 1000); } else if (!IS_GEN2(dev)) { @@ -4168,28 +4295,14 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) return refclk; } -static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc *crtc) +static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) { - unsigned dotclock = crtc->config.adjusted_mode.clock; - struct dpll *clock = &crtc->config.dpll; + return (1 << dpll->n) << 16 | dpll->m1 << 8 | dpll->m2; +} - /* SDVO TV has fixed PLL values depend on its clock range, - this mirrors vbios setting. */ - if (dotclock >= 100000 && dotclock < 140500) { - clock->p1 = 2; - clock->p2 = 10; - clock->n = 3; - clock->m1 = 16; - clock->m2 = 8; - } else if (dotclock >= 140500 && dotclock <= 200000) { - clock->p1 = 1; - clock->p2 = 10; - clock->n = 6; - clock->m1 = 12; - clock->m2 = 8; - } - - crtc->config.clock_set = true; +static uint32_t i9xx_dpll_compute_fp(struct dpll *dpll) +{ + return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; } static void i9xx_update_pll_dividers(struct intel_crtc *crtc, @@ -4199,18 +4312,15 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; int pipe = crtc->pipe; u32 fp, fp2 = 0; - struct dpll *clock = &crtc->config.dpll; if (IS_PINEVIEW(dev)) { - fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2; + fp = pnv_dpll_compute_fp(&crtc->config.dpll); if (reduced_clock) - fp2 = (1 << reduced_clock->n) << 16 | - reduced_clock->m1 << 8 | reduced_clock->m2; + fp2 = pnv_dpll_compute_fp(reduced_clock); } else { - fp = clock->n << 16 | clock->m1 << 8 | clock->m2; + fp = i9xx_dpll_compute_fp(&crtc->config.dpll); if (reduced_clock) - fp2 = reduced_clock->n << 16 | reduced_clock->m1 << 8 | - reduced_clock->m2; + fp2 = i9xx_dpll_compute_fp(reduced_clock); } I915_WRITE(FP0(pipe), fp); @@ -4225,6 +4335,68 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc, } } +static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv) +{ + u32 reg_val; + + /* + * PLLB opamp always calibrates to max value of 0x3f, force enable it + * and set it to a reasonable value instead. + */ + reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val &= 0xffffff00; + reg_val |= 0x00000030; + intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val &= 0x8cffffff; + reg_val = 0x8c000000; + intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val &= 0xffffff00; + intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val &= 0x00ffffff; + reg_val |= 0xb0000000; + intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); +} + +static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + I915_WRITE(PCH_TRANS_DATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PCH_TRANS_DATA_N1(pipe), m_n->gmch_n); + I915_WRITE(PCH_TRANS_LINK_M1(pipe), m_n->link_m); + I915_WRITE(PCH_TRANS_LINK_N1(pipe), m_n->link_n); +} + +static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + enum transcoder transcoder = crtc->config.cpu_transcoder; + + if (INTEL_INFO(dev)->gen >= 5) { + I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); + I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); + } else { + I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M_G4X(pipe), m_n->link_m); + I915_WRITE(PIPE_LINK_N_G4X(pipe), m_n->link_n); + } +} + static void intel_dp_set_m_n(struct intel_crtc *crtc) { if (crtc->config.has_pch_encoder) @@ -4237,24 +4409,18 @@ static void vlv_update_pll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *adjusted_mode = + &crtc->config.adjusted_mode; + struct intel_encoder *encoder; int pipe = crtc->pipe; - u32 dpll, mdiv, pdiv; + u32 dpll, mdiv; u32 bestn, bestm1, bestm2, bestp1, bestp2; - bool is_sdvo; - u32 temp; + bool is_hdmi; + u32 coreclk, reg_val, dpll_md; mutex_lock(&dev_priv->dpio_lock); - is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); - - dpll = DPLL_VGA_MODE_DIS; - dpll |= DPLL_EXT_BUFFER_ENABLE_VLV; - dpll |= DPLL_REFA_CLK_ENABLE_VLV; - dpll |= DPLL_INTEGRATED_CLOCK_VLV; - - I915_WRITE(DPLL(pipe), dpll); - POSTING_READ(DPLL(pipe)); + is_hdmi = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); bestn = crtc->config.dpll.n; bestm1 = crtc->config.dpll.m1; @@ -4262,72 +4428,106 @@ static void vlv_update_pll(struct intel_crtc *crtc) bestp1 = crtc->config.dpll.p1; bestp2 = crtc->config.dpll.p2; - /* - * In Valleyview PLL and program lane counter registers are exposed - * through DPIO interface - */ + /* See eDP HDMI DPIO driver vbios notes doc */ + + /* PLL B needs special handling */ + if (pipe) + vlv_pllb_recal_opamp(dev_priv); + + /* Set up Tx target for periodic Rcomp update */ + intel_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f); + + /* Disable target IRef on PLL */ + reg_val = intel_dpio_read(dev_priv, DPIO_IREF_CTL(pipe)); + reg_val &= 0x00ffffff; + intel_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val); + + /* Disable fast lock */ + intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610); + + /* Set idtafcrecal before PLL is enabled */ mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); mdiv |= ((bestn << DPIO_N_SHIFT)); - mdiv |= (1 << DPIO_POST_DIV_SHIFT); mdiv |= (1 << DPIO_K_SHIFT); + + /* + * Post divider depends on pixel clock rate, DAC vs digital (and LVDS, + * but we don't support that). + * Note: don't use the DAC post divider as it seems unstable. + */ + mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); + intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); + mdiv |= DPIO_ENABLE_CALIBRATION; intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); - intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), 0x01000000); + /* Set HBR and RBR LPF coefficients */ + if (adjusted_mode->clock == 162000 || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) + intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + 0x005f0021); + else + intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + 0x00d0000f); - pdiv = (1 << DPIO_REFSEL_OVERRIDE) | (5 << DPIO_PLL_MODESEL_SHIFT) | - (3 << DPIO_BIAS_CURRENT_CTL_SHIFT) | (1<<20) | - (7 << DPIO_PLL_REFCLK_SEL_SHIFT) | (8 << DPIO_DRIVER_CTL_SHIFT) | - (5 << DPIO_CLK_BIAS_CTL_SHIFT); - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), pdiv); + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) { + /* Use SSC source */ + if (!pipe) + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df40000); + else + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df70000); + } else { /* HDMI or VGA */ + /* Use bend source */ + if (!pipe) + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df70000); + else + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df40000); + } - intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x005f003b); + coreclk = intel_dpio_read(dev_priv, DPIO_CORE_CLK(pipe)); + coreclk = (coreclk & 0x0000ff00) | 0x01c00000; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) + coreclk |= 0x01000000; + intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), coreclk); + + intel_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000); + + for_each_encoder_on_crtc(dev, &crtc->base, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + + /* Enable DPIO clock input */ + dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | + DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; + if (pipe) + dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; dpll |= DPLL_VCO_ENABLE; I915_WRITE(DPLL(pipe), dpll); POSTING_READ(DPLL(pipe)); + udelay(150); + if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) DRM_ERROR("DPLL %d failed to lock\n", pipe); - intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x620); + dpll_md = 0; + if (crtc->config.pixel_multiplier > 1) { + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + } + I915_WRITE(DPLL_MD(pipe), dpll_md); + POSTING_READ(DPLL_MD(pipe)); if (crtc->config.has_dp_encoder) intel_dp_set_m_n(crtc); - I915_WRITE(DPLL(pipe), dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(DPLL(pipe)); - udelay(150); - - temp = 0; - if (is_sdvo) { - temp = 0; - if (crtc->config.pixel_multiplier > 1) { - temp = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } - } - I915_WRITE(DPLL_MD(pipe), temp); - POSTING_READ(DPLL_MD(pipe)); - - /* Now program lane control registers */ - if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) - || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) { - temp = 0x1000C4; - if(pipe == 1) - temp |= (1 << 21); - intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL1, temp); - } - - if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) { - temp = 0x1000C4; - if(pipe == 1) - temp |= (1 << 21); - intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp); - } - mutex_unlock(&dev_priv->dpio_lock); } @@ -4355,14 +4555,15 @@ static void i9xx_update_pll(struct intel_crtc *crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) { - if ((crtc->config.pixel_multiplier > 1) && - (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { - dpll |= (crtc->config.pixel_multiplier - 1) - << SDVO_MULTIPLIER_SHIFT_HIRES; - } - dpll |= DPLL_DVO_HIGH_SPEED; + if ((crtc->config.pixel_multiplier > 1) && + (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { + dpll |= (crtc->config.pixel_multiplier - 1) + << SDVO_MULTIPLIER_SHIFT_HIRES; } + + if (is_sdvo) + dpll |= DPLL_DVO_HIGH_SPEED; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) dpll |= DPLL_DVO_HIGH_SPEED; @@ -4391,12 +4592,8 @@ static void i9xx_update_pll(struct intel_crtc *crtc, if (INTEL_INFO(dev)->gen >= 4) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); - if (is_sdvo && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) + if (crtc->config.sdvo_tv_clock) dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) - /* XXX: just matching BIOS for now */ - /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; @@ -4422,15 +4619,12 @@ static void i9xx_update_pll(struct intel_crtc *crtc, udelay(150); if (INTEL_INFO(dev)->gen >= 4) { - u32 temp = 0; - if (is_sdvo) { - temp = 0; - if (crtc->config.pixel_multiplier > 1) { - temp = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } + u32 dpll_md = 0; + if (crtc->config.pixel_multiplier > 1) { + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; } - I915_WRITE(DPLL_MD(pipe), temp); + I915_WRITE(DPLL_MD(pipe), dpll_md); } else { /* The pixel multiplier can only be updated once the * DPLL is enabled and the clocks are stable. @@ -4505,12 +4699,17 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; - uint32_t vsyncshift; + uint32_t vsyncshift, crtc_vtotal, crtc_vblank_end; + + /* We need to be careful not to changed the adjusted mode, for otherwise + * the hw state checker will get angry at the mismatch. */ + crtc_vtotal = adjusted_mode->crtc_vtotal; + crtc_vblank_end = adjusted_mode->crtc_vblank_end; if (!IS_GEN2(dev) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { /* the chip adds 2 halflines automatically */ - adjusted_mode->crtc_vtotal -= 1; - adjusted_mode->crtc_vblank_end -= 1; + crtc_vtotal -= 1; + crtc_vblank_end -= 1; vsyncshift = adjusted_mode->crtc_hsync_start - adjusted_mode->crtc_htotal / 2; } else { @@ -4532,10 +4731,10 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, I915_WRITE(VTOTAL(cpu_transcoder), (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); + ((crtc_vtotal - 1) << 16)); I915_WRITE(VBLANK(cpu_transcoder), (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); + ((crtc_vblank_end - 1) << 16)); I915_WRITE(VSYNC(cpu_transcoder), (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); @@ -4555,6 +4754,45 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); } +static void intel_get_pipe_timings(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 transcoder cpu_transcoder = pipe_config->cpu_transcoder; + uint32_t tmp; + + tmp = I915_READ(HTOTAL(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hdisplay = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_htotal = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(HBLANK(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hblank_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_hblank_end = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(HSYNC(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hsync_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_hsync_end = ((tmp >> 16) & 0xffff) + 1; + + tmp = I915_READ(VTOTAL(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vdisplay = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vtotal = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(VBLANK(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vblank_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vblank_end = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(VSYNC(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vsync_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vsync_end = ((tmp >> 16) & 0xffff) + 1; + + if (I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_INTERLACE_MASK) { + pipe_config->adjusted_mode.flags |= DRM_MODE_FLAG_INTERLACE; + pipe_config->adjusted_mode.crtc_vtotal += 1; + pipe_config->adjusted_mode.crtc_vblank_end += 1; + } + + tmp = I915_READ(PIPESRC(crtc->pipe)); + pipe_config->requested_mode.vdisplay = (tmp & 0xffff) + 1; + pipe_config->requested_mode.hdisplay = ((tmp >> 16) & 0xffff) + 1; +} + static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) { struct drm_device *dev = intel_crtc->base.dev; @@ -4577,22 +4815,29 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) pipeconf &= ~PIPECONF_DOUBLE_WIDE; } - /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); - if (intel_crtc->config.has_dp_encoder) { - if (intel_crtc->config.dither) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_DITHER_EN | - PIPECONF_DITHER_TYPE_SP; - } - } + /* only g4x and later have fancy bpc/dither controls */ + if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) { + pipeconf &= ~(PIPECONF_BPC_MASK | + PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); - if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(&intel_crtc->base, - INTEL_OUTPUT_EDP)) { - if (intel_crtc->config.dither) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_ENABLE | - I965_PIPECONF_ACTIVE; + /* Bspec claims that we can't use dithering for 30bpp pipes. */ + if (intel_crtc->config.dither && intel_crtc->config.pipe_bpp != 30) + pipeconf |= PIPECONF_DITHER_EN | + PIPECONF_DITHER_TYPE_SP; + + switch (intel_crtc->config.pipe_bpp) { + case 18: + pipeconf |= PIPECONF_6BPC; + break; + case 24: + pipeconf |= PIPECONF_8BPC; + break; + case 30: + pipeconf |= PIPECONF_10BPC; + break; + default: + /* Case prevented by intel_choose_pipe_bpp_dither. */ + BUG(); } } @@ -4639,8 +4884,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, int refclk, num_connectors = 0; intel_clock_t clock, reduced_clock; u32 dspcntr; - bool ok, has_reduced_clock = false, is_sdvo = false; - bool is_lvds = false, is_tv = false; + bool ok, has_reduced_clock = false; + bool is_lvds = false; struct intel_encoder *encoder; const intel_limit_t *limit; int ret; @@ -4650,15 +4895,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_SDVO: - case INTEL_OUTPUT_HDMI: - is_sdvo = true; - if (encoder->needs_tv_clock) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; } num_connectors++; @@ -4704,9 +4940,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, intel_crtc->config.dpll.p2 = clock.p2; } - if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(intel_crtc); - if (IS_GEN2(dev)) i8xx_update_pll(intel_crtc, adjusted_mode, has_reduced_clock ? &reduced_clock : NULL, @@ -4716,7 +4949,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, else i9xx_update_pll(intel_crtc, has_reduced_clock ? &reduced_clock : NULL, - num_connectors); + num_connectors); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -4728,7 +4961,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPPLANE_SEL_PIPE_B; } - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); @@ -4743,10 +4976,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, i9xx_set_pipeconf(intel_crtc); - intel_enable_pipe(dev_priv, pipe, false); - - intel_wait_for_vblank(dev, pipe); - I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); @@ -4757,6 +4986,36 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static void i9xx_get_pfit_config(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; + uint32_t tmp; + + tmp = I915_READ(PFIT_CONTROL); + + if (INTEL_INFO(dev)->gen < 4) { + if (crtc->pipe != PIPE_B) + return; + + /* gen2/3 store dither state in pfit control, needs to match */ + pipe_config->gmch_pfit.control = tmp & PANEL_8TO6_DITHER_ENABLE; + } else { + if ((tmp & PFIT_PIPE_MASK) != (crtc->pipe << PFIT_PIPE_SHIFT)) + return; + } + + if (!(tmp & PFIT_ENABLE)) + return; + + pipe_config->gmch_pfit.control = I915_READ(PFIT_CONTROL); + pipe_config->gmch_pfit.pgm_ratios = I915_READ(PFIT_PGM_RATIOS); + if (INTEL_INFO(dev)->gen < 5) + pipe_config->gmch_pfit.lvds_border_bits = + I915_READ(LVDS) & LVDS_BORDER_ENABLE; +} + static bool i9xx_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -4768,6 +5027,10 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; + intel_get_pipe_timings(crtc, pipe_config); + + i9xx_get_pfit_config(crtc, pipe_config); + return true; } @@ -4779,7 +5042,6 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) u32 val, final; bool has_lvds = false; bool has_cpu_edp = false; - bool has_pch_edp = false; bool has_panel = false; bool has_ck505 = false; bool can_ssc = false; @@ -4794,25 +5056,22 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) break; case INTEL_OUTPUT_EDP: has_panel = true; - if (intel_encoder_is_pch_edp(&encoder->base)) - has_pch_edp = true; - else + if (enc_to_dig_port(&encoder->base)->port == PORT_A) has_cpu_edp = true; break; } } if (HAS_PCH_IBX(dev)) { - has_ck505 = dev_priv->display_clock_mode; + has_ck505 = dev_priv->vbt.display_clock_mode; can_ssc = has_ck505; } else { has_ck505 = false; can_ssc = true; } - DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n", - has_panel, has_lvds, has_pch_edp, has_cpu_edp, - has_ck505); + DRM_DEBUG_KMS("has_panel %d has_lvds %d has_ck505 %d\n", + has_panel, has_lvds, has_ck505); /* Ironlake: try to setup display ref clock before DPLL * enabling. This is only under driver's control after @@ -5102,7 +5361,6 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *encoder; - struct intel_encoder *edp_encoder = NULL; int num_connectors = 0; bool is_lvds = false; @@ -5111,25 +5369,20 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_EDP: - edp_encoder = encoder; - break; } num_connectors++; } if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", - dev_priv->lvds_ssc_freq); - return dev_priv->lvds_ssc_freq * 1000; + dev_priv->vbt.lvds_ssc_freq); + return dev_priv->vbt.lvds_ssc_freq * 1000; } return 120000; } -static void ironlake_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, - bool dither) +static void ironlake_set_pipeconf(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5158,11 +5411,11 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, } val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); - if (dither) + if (intel_crtc->config.dither) val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) val |= PIPECONF_INTERLACED_ILK; else val |= PIPECONF_PROGRESSIVE; @@ -5240,9 +5493,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc) } } -static void haswell_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, - bool dither) +static void haswell_set_pipeconf(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5252,11 +5503,11 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(cpu_transcoder)); val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); - if (dither) + if (intel_crtc->config.dither) val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK_HSW; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) val |= PIPECONF_INTERLACED_ILK; else val |= PIPECONF_PROGRESSIVE; @@ -5276,22 +5527,13 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, struct intel_encoder *intel_encoder; int refclk; const intel_limit_t *limit; - bool ret, is_sdvo = false, is_tv = false, is_lvds = false; + bool ret, is_lvds = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) { switch (intel_encoder->type) { case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_SDVO: - case INTEL_OUTPUT_HDMI: - is_sdvo = true; - if (intel_encoder->needs_tv_clock) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; } } @@ -5322,9 +5564,6 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, reduced_clock); } - if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(to_intel_crtc(crtc)); - return true; } @@ -5346,65 +5585,25 @@ static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev) POSTING_READ(SOUTH_CHICKEN1); } -static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) +static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc) { struct drm_device *dev = intel_crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *pipe_B_crtc = - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); - - DRM_DEBUG_KMS("checking fdi config on pipe %i, lanes %i\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); - if (intel_crtc->fdi_lanes > 4) { - DRM_DEBUG_KMS("invalid fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 4; - - return false; - } - - if (INTEL_INFO(dev)->num_pipes == 2) - return true; switch (intel_crtc->pipe) { case PIPE_A: - return true; + break; case PIPE_B: - if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && - intel_crtc->fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 2; - - return false; - } - - if (intel_crtc->fdi_lanes > 2) + if (intel_crtc->config.fdi_lanes > 2) WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT); else cpt_enable_fdi_bc_bifurcation(dev); - return true; + break; case PIPE_C: - if (!pipe_B_crtc->base.enabled || pipe_B_crtc->fdi_lanes <= 2) { - if (intel_crtc->fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 2; - - return false; - } - } else { - DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n"); - return false; - } - cpt_enable_fdi_bc_bifurcation(dev); - return true; + break; default: BUG(); } @@ -5421,78 +5620,13 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp) return bps / (link_bw * 8) + 1; } -void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) +static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) { - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - - I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n); - I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m); - I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n); -} - -void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - enum transcoder transcoder = crtc->config.cpu_transcoder; - - if (INTEL_INFO(dev)->gen >= 5) { - I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); - I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); - I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); - } else { - I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n); - I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m); - I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n); - } -} - -static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_display_mode *adjusted_mode = - &intel_crtc->config.adjusted_mode; - struct intel_link_m_n m_n = {0}; - int target_clock, lane, link_bw; - - /* FDI is a binary signal running at ~2.7GHz, encoding - * each output octet as 10 bits. The actual frequency - * is stored as a divider into a 100MHz clock, and the - * mode pixel clock is stored in units of 1KHz. - * Hence the bw of each lane in terms of the mode signal - * is: - */ - link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; - - if (intel_crtc->config.pixel_target_clock) - target_clock = intel_crtc->config.pixel_target_clock; - else - target_clock = adjusted_mode->clock; - - lane = ironlake_get_lanes_required(target_clock, link_bw, - intel_crtc->config.pipe_bpp); - - intel_crtc->fdi_lanes = lane; - - if (intel_crtc->config.pixel_multiplier > 1) - link_bw *= intel_crtc->config.pixel_multiplier; - intel_link_compute_m_n(intel_crtc->config.pipe_bpp, lane, target_clock, - link_bw, &m_n); - - intel_cpu_transcoder_set_m_n(intel_crtc, &m_n); + return i9xx_dpll_compute_m(dpll) < factor * dpll->n; } static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, - intel_clock_t *clock, u32 *fp, + u32 *fp, intel_clock_t *reduced_clock, u32 *fp2) { struct drm_crtc *crtc = &intel_crtc->base; @@ -5501,7 +5635,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, struct intel_encoder *intel_encoder; uint32_t dpll; int factor, num_connectors = 0; - bool is_lvds = false, is_sdvo = false, is_tv = false; + bool is_lvds = false, is_sdvo = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) { switch (intel_encoder->type) { @@ -5511,11 +5645,6 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, case INTEL_OUTPUT_SDVO: case INTEL_OUTPUT_HDMI: is_sdvo = true; - if (intel_encoder->needs_tv_clock) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; break; } @@ -5526,13 +5655,13 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, factor = 21; if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && - dev_priv->lvds_ssc_freq == 100) || + dev_priv->vbt.lvds_ssc_freq == 100) || (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev))) factor = 25; - } else if (is_sdvo && is_tv) + } else if (intel_crtc->config.sdvo_tv_clock) factor = 20; - if (clock->m < factor * clock->n) + if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor)) *fp |= FP_CB_TUNE; if (fp2 && (reduced_clock->m < factor * reduced_clock->n)) @@ -5544,23 +5673,23 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) { - if (intel_crtc->config.pixel_multiplier > 1) { - dpll |= (intel_crtc->config.pixel_multiplier - 1) - << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; - } - dpll |= DPLL_DVO_HIGH_SPEED; + + if (intel_crtc->config.pixel_multiplier > 1) { + dpll |= (intel_crtc->config.pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } - if (intel_crtc->config.has_dp_encoder && - intel_crtc->config.has_pch_encoder) + + if (is_sdvo) + dpll |= DPLL_DVO_HIGH_SPEED; + if (intel_crtc->config.has_dp_encoder) dpll |= DPLL_DVO_HIGH_SPEED; /* compute bitmask from p1 value */ - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; /* also FPA1 */ - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - switch (clock->p2) { + switch (intel_crtc->config.dpll.p2) { case 5: dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; break; @@ -5575,13 +5704,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, break; } - if (is_sdvo && is_tv) - dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (is_tv) - /* XXX: just matching BIOS for now */ - /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; - else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else dpll |= PLL_REF_INPUT_DREFCLK; @@ -5603,12 +5726,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, int plane = intel_crtc->plane; int num_connectors = 0; intel_clock_t clock, reduced_clock; - u32 dpll, fp = 0, fp2 = 0; + u32 dpll = 0, fp = 0, fp2 = 0; bool ok, has_reduced_clock = false; bool is_lvds = false; struct intel_encoder *encoder; int ret; - bool dither, fdi_config_ok; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { @@ -5643,30 +5765,25 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); - /* determine panel color depth */ - dither = intel_crtc->config.dither; - if (is_lvds && dev_priv->lvds_dither) - dither = true; - - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; - - dpll = ironlake_compute_dpll(intel_crtc, &clock, &fp, &reduced_clock, - has_reduced_clock ? &fp2 : NULL); - - DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ if (intel_crtc->config.has_pch_encoder) { struct intel_pch_pll *pll; + fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll); + if (has_reduced_clock) + fp2 = i9xx_dpll_compute_fp(&reduced_clock); + + dpll = ironlake_compute_dpll(intel_crtc, + &fp, &reduced_clock, + has_reduced_clock ? &fp2 : NULL); + pll = intel_get_pch_pll(intel_crtc, dpll, fp); if (pll == NULL) { - DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", - pipe); + DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", + pipe_name(pipe)); return -EINVAL; } } else @@ -5706,17 +5823,15 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); - /* Note, this also computes intel_crtc->fdi_lanes which is used below in - * ironlake_check_fdi_lanes. */ - intel_crtc->fdi_lanes = 0; - if (intel_crtc->config.has_pch_encoder) - ironlake_fdi_set_m_n(crtc); + if (intel_crtc->config.has_pch_encoder) { + intel_cpu_transcoder_set_m_n(intel_crtc, + &intel_crtc->config.fdi_m_n); + } - fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); + if (IS_IVYBRIDGE(dev)) + ivybridge_update_fdi_bc_bifurcation(intel_crtc); - ironlake_set_pipeconf(crtc, adjusted_mode, dither); - - intel_wait_for_vblank(dev, pipe); + ironlake_set_pipeconf(crtc); /* Set up the display plane register */ I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); @@ -5728,7 +5843,38 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - return fdi_config_ok ? ret : -EINVAL; + return ret; +} + +static void ironlake_get_fdi_m_n_config(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 transcoder transcoder = pipe_config->cpu_transcoder; + + pipe_config->fdi_m_n.link_m = I915_READ(PIPE_LINK_M1(transcoder)); + pipe_config->fdi_m_n.link_n = I915_READ(PIPE_LINK_N1(transcoder)); + pipe_config->fdi_m_n.gmch_m = I915_READ(PIPE_DATA_M1(transcoder)) + & ~TU_SIZE_MASK; + pipe_config->fdi_m_n.gmch_n = I915_READ(PIPE_DATA_N1(transcoder)); + pipe_config->fdi_m_n.tu = ((I915_READ(PIPE_DATA_M1(transcoder)) + & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1; +} + +static void ironlake_get_pfit_config(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; + uint32_t tmp; + + tmp = I915_READ(PF_CTL(crtc->pipe)); + + if (tmp & PF_ENABLE) { + pipe_config->pch_pfit.pos = I915_READ(PF_WIN_POS(crtc->pipe)); + pipe_config->pch_pfit.size = I915_READ(PF_WIN_SZ(crtc->pipe)); + } } static bool ironlake_get_pipe_config(struct intel_crtc *crtc, @@ -5742,15 +5888,25 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; - if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE) + if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) { pipe_config->has_pch_encoder = true; + tmp = I915_READ(FDI_RX_CTL(crtc->pipe)); + 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); + } + + intel_get_pipe_timings(crtc, pipe_config); + + ironlake_get_pfit_config(crtc, pipe_config); + return true; } static void haswell_modeset_global_resources(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; bool enable = false; struct intel_crtc *crtc; struct intel_encoder *encoder; @@ -5761,6 +5917,9 @@ static void haswell_modeset_global_resources(struct drm_device *dev) /* XXX: Should check for edp transcoder here, but thanks to init * sequence that's not yet available. Just in case desktop eDP * on PORT D is possible on haswell, too. */ + /* Even the eDP panel fitter is outside the always-on well. */ + if (crtc->config.pch_pfit.size && crtc->base.enabled) + enable = true; } list_for_each_entry(encoder, &dev->mode_config.encoder_list, @@ -5770,10 +5929,6 @@ static void haswell_modeset_global_resources(struct drm_device *dev) enable = true; } - /* Even the eDP panel fitter is outside the always-on well. */ - if (dev_priv->pch_pf_size) - enable = true; - intel_set_power_well(dev, enable); } @@ -5793,12 +5948,11 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, bool is_cpu_edp = false; struct intel_encoder *encoder; int ret; - bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { case INTEL_OUTPUT_EDP: - if (!intel_encoder_is_pch_edp(&encoder->base)) + if (enc_to_dig_port(&encoder->base)->port == PORT_A) is_cpu_edp = true; break; } @@ -5829,10 +5983,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); - /* determine panel color depth */ - dither = intel_crtc->config.dither; - - DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); if (intel_crtc->config.has_dp_encoder) @@ -5842,10 +5993,12 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); - if (intel_crtc->config.has_pch_encoder) - ironlake_fdi_set_m_n(crtc); + if (intel_crtc->config.has_pch_encoder) { + intel_cpu_transcoder_set_m_n(intel_crtc, + &intel_crtc->config.fdi_m_n); + } - haswell_set_pipeconf(crtc, adjusted_mode, dither); + haswell_set_pipeconf(crtc); intel_set_pipe_csc(crtc); @@ -5867,22 +6020,40 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum intel_display_power_domain pfit_domain; uint32_t tmp; - tmp = I915_READ(PIPECONF(crtc->config.cpu_transcoder)); + if (!intel_display_power_enabled(dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) + return false; + + tmp = I915_READ(PIPECONF(cpu_transcoder)); if (!(tmp & PIPECONF_ENABLE)) return false; /* - * aswell has only FDI/PCH transcoder A. It is which is connected to + * 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(crtc->pipe)); + tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && - I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) + 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); + } + + intel_get_pipe_timings(crtc, pipe_config); + + pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe); + if (intel_display_power_enabled(dev, pfit_domain)) + ironlake_get_pfit_config(crtc, pipe_config); return true; } @@ -6120,7 +6291,7 @@ static void ironlake_write_eld(struct drm_connector *connector, eldv |= IBX_ELD_VALIDB << 4; eldv |= IBX_ELD_VALIDB << 8; } else { - DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i); + DRM_DEBUG_DRIVER("ELD on port %c\n", port_name(i)); eldv = IBX_ELD_VALIDB << ((i - 1) * 4); } @@ -7591,6 +7762,13 @@ pipe_config_set_bpp(struct drm_crtc *crtc, bpp, connector->display_info.bpc*3); pipe_config->pipe_bpp = connector->display_info.bpc*3; } + + /* Clamp bpp to 8 on screens without EDID 1.4 */ + if (connector->display_info.bpc == 0 && bpp > 24) { + DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of 24\n", + bpp); + pipe_config->pipe_bpp = 24; + } } return bpp; @@ -7605,7 +7783,8 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, struct drm_encoder_helper_funcs *encoder_funcs; struct intel_encoder *encoder; struct intel_crtc_config *pipe_config; - int plane_bpp; + int plane_bpp, ret = -EINVAL; + bool retry = true; pipe_config = kzalloc(sizeof(*pipe_config), GFP_KERNEL); if (!pipe_config) @@ -7618,6 +7797,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, if (plane_bpp < 0) goto fail; +encoder_retry: /* Pass our mode to the connectors and the CRTC to give them a chance to * adjust it according to limitations or connector properties, and also * a chance to reject the mode entirely. @@ -7646,10 +7826,23 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, } } - if (!(intel_crtc_compute_config(crtc, pipe_config))) { + ret = intel_crtc_compute_config(crtc, pipe_config); + if (ret < 0) { DRM_DEBUG_KMS("CRTC fixup failed\n"); goto fail; } + + if (ret == RETRY) { + if (WARN(!retry, "loop in pipe configuration computation\n")) { + ret = -EINVAL; + goto fail; + } + + DRM_DEBUG_KMS("CRTC bw constrained, retrying\n"); + retry = false; + goto encoder_retry; + } + DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); pipe_config->dither = pipe_config->pipe_bpp != plane_bpp; @@ -7659,7 +7852,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, return pipe_config; fail: kfree(pipe_config); - return ERR_PTR(-EINVAL); + return ERR_PTR(ret); } /* Computes which crtcs are affected and sets the relevant bits in the mask. For @@ -7755,6 +7948,9 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, */ *modeset_pipes &= 1 << intel_crtc->pipe; *prepare_pipes &= 1 << intel_crtc->pipe; + + DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", + *modeset_pipes, *prepare_pipes, *disable_pipes); } static bool intel_crtc_in_use(struct drm_crtc *crtc) @@ -7821,20 +8017,70 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) list_for_each_entry((intel_crtc), \ &(dev)->mode_config.crtc_list, \ base.head) \ - if (mask & (1 <<(intel_crtc)->pipe)) \ + if (mask & (1 <<(intel_crtc)->pipe)) static bool -intel_pipe_config_compare(struct intel_crtc_config *current_config, +intel_pipe_config_compare(struct drm_device *dev, + struct intel_crtc_config *current_config, struct intel_crtc_config *pipe_config) { - if (current_config->has_pch_encoder != pipe_config->has_pch_encoder) { - DRM_ERROR("mismatch in has_pch_encoder " - "(expected %i, found %i)\n", - current_config->has_pch_encoder, - pipe_config->has_pch_encoder); - return false; +#define PIPE_CONF_CHECK_I(name) \ + if (current_config->name != pipe_config->name) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected %i, found %i)\n", \ + current_config->name, \ + pipe_config->name); \ + return false; \ } +#define PIPE_CONF_CHECK_FLAGS(name, mask) \ + if ((current_config->name ^ pipe_config->name) & (mask)) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected %i, found %i)\n", \ + current_config->name & (mask), \ + pipe_config->name & (mask)); \ + return false; \ + } + + PIPE_CONF_CHECK_I(has_pch_encoder); + PIPE_CONF_CHECK_I(fdi_lanes); + PIPE_CONF_CHECK_I(fdi_m_n.gmch_m); + PIPE_CONF_CHECK_I(fdi_m_n.gmch_n); + PIPE_CONF_CHECK_I(fdi_m_n.link_m); + PIPE_CONF_CHECK_I(fdi_m_n.link_n); + PIPE_CONF_CHECK_I(fdi_m_n.tu); + + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hdisplay); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_htotal); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_end); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_end); + + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vdisplay); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vtotal); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_end); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end); + + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_INTERLACE); + + PIPE_CONF_CHECK_I(requested_mode.hdisplay); + PIPE_CONF_CHECK_I(requested_mode.vdisplay); + + PIPE_CONF_CHECK_I(gmch_pfit.control); + /* pfit ratios are autocomputed by the hw on gen4+ */ + if (INTEL_INFO(dev)->gen < 4) + PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios); + PIPE_CONF_CHECK_I(gmch_pfit.lvds_border_bits); + PIPE_CONF_CHECK_I(pch_pfit.pos); + PIPE_CONF_CHECK_I(pch_pfit.size); + +#undef PIPE_CONF_CHECK_I +#undef PIPE_CONF_CHECK_FLAGS + return true; } @@ -7935,6 +8181,7 @@ intel_modeset_check_state(struct drm_device *dev) "(expected %i, found %i)\n", enabled, crtc->base.enabled); memset(&pipe_config, 0, sizeof(pipe_config)); + pipe_config.cpu_transcoder = crtc->config.cpu_transcoder; active = dev_priv->display.get_pipe_config(crtc, &pipe_config); WARN(crtc->active != active, @@ -7942,7 +8189,7 @@ intel_modeset_check_state(struct drm_device *dev) "(expected %i, found %i)\n", crtc->active, active); WARN(active && - !intel_pipe_config_compare(&crtc->config, &pipe_config), + !intel_pipe_config_compare(dev, &crtc->config, &pipe_config), "pipe state doesn't match!\n"); } } @@ -7985,9 +8232,6 @@ static int __intel_set_mode(struct drm_crtc *crtc, } } - DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", - modeset_pipes, prepare_pipes, disable_pipes); - for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); @@ -8593,10 +8837,8 @@ static void intel_setup_outputs(struct drm_device *dev) intel_hdmi_init(dev, GEN4_HDMIB, PORT_B); } - if (!found && SUPPORTS_INTEGRATED_DP(dev)) { - DRM_DEBUG_KMS("probing DP_B\n"); + if (!found && SUPPORTS_INTEGRATED_DP(dev)) intel_dp_init(dev, DP_B, PORT_B); - } } /* Before G4X SDVOC doesn't have its own detect register */ @@ -8612,17 +8854,13 @@ static void intel_setup_outputs(struct drm_device *dev) DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); intel_hdmi_init(dev, GEN4_HDMIC, PORT_C); } - if (SUPPORTS_INTEGRATED_DP(dev)) { - DRM_DEBUG_KMS("probing DP_C\n"); + if (SUPPORTS_INTEGRATED_DP(dev)) intel_dp_init(dev, DP_C, PORT_C); - } } if (SUPPORTS_INTEGRATED_DP(dev) && - (I915_READ(DP_D) & DP_DETECTED)) { - DRM_DEBUG_KMS("probing DP_D\n"); + (I915_READ(DP_D) & DP_DETECTED)) intel_dp_init(dev, DP_D, PORT_D); - } } else if (IS_GEN2(dev)) intel_dvo_init(dev); @@ -8791,6 +9029,13 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; + } else if (IS_VALLEYVIEW(dev)) { + dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.crtc_enable = valleyview_crtc_enable; + dev_priv->display.crtc_disable = i9xx_crtc_disable; + dev_priv->display.off = i9xx_crtc_off; + dev_priv->display.update_plane = i9xx_update_plane; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; @@ -9032,6 +9277,11 @@ void intel_modeset_init_hw(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); } +void intel_modeset_suspend_hw(struct drm_device *dev) +{ + intel_suspend_hw(dev); +} + void intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -9077,8 +9327,8 @@ void intel_modeset_init(struct drm_device *dev) for (j = 0; j < dev_priv->num_plane; j++) { ret = intel_plane_init(dev, i, j); if (ret) - DRM_DEBUG_KMS("pipe %d plane %d init failed: %d\n", - i, j, ret); + DRM_DEBUG_KMS("pipe %c sprite %c init failed: %d\n", + pipe_name(i), sprite_name(i, j), ret); } } @@ -9435,12 +9685,23 @@ void intel_modeset_cleanup(struct drm_device *dev) struct drm_crtc *crtc; struct intel_crtc *intel_crtc; + /* + * Interrupts and polling as the first thing to avoid creating havoc. + * Too much stuff here (turning of rps, connectors, ...) would + * experience fancy races otherwise. + */ + drm_irq_uninstall(dev); + cancel_work_sync(&dev_priv->hotplug_work); + /* + * 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. + */ drm_kms_helper_poll_fini(dev); + mutex_lock(&dev->struct_mutex); intel_unregister_dsm_handler(); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { /* Skip inactive CRTCs */ if (!crtc->fb) @@ -9456,17 +9717,8 @@ void intel_modeset_cleanup(struct drm_device *dev) ironlake_teardown_rc6(dev); - if (IS_VALLEYVIEW(dev)) - vlv_init_dpio(dev); - mutex_unlock(&dev->struct_mutex); - /* Disable the irq before mode object teardown, for the irq might - * enqueue unpin/hotplug work. */ - drm_irq_uninstall(dev); - cancel_work_sync(&dev_priv->hotplug_work); - cancel_work_sync(&dev_priv->rps.work); - /* flush any delayed tasks or pending work */ flush_scheduled_work(); @@ -9515,6 +9767,9 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state) #include struct intel_display_error_state { + + u32 power_well_driver; + struct intel_cursor_error_state { u32 control; u32 position; @@ -9523,6 +9778,7 @@ struct intel_display_error_state { } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { + enum transcoder cpu_transcoder; u32 conf; u32 source; @@ -9557,8 +9813,12 @@ intel_display_capture_error_state(struct drm_device *dev) if (error == NULL) return NULL; + if (HAS_POWER_WELL(dev)) + error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); + for_each_pipe(i) { cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i); + error->pipe[i].cpu_transcoder = cpu_transcoder; if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) { error->cursor[i].control = I915_READ(CURCNTR(i)); @@ -9593,6 +9853,13 @@ intel_display_capture_error_state(struct drm_device *dev) error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder)); } + /* In the code above we read the registers without checking if the power + * well was on, so here we have to clear the FPGA_DBG_RM_NOCLAIM bit to + * prevent the next I915_WRITE from detecting it and printing an error + * message. */ + if (HAS_POWER_WELL(dev)) + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + return error; } @@ -9604,8 +9871,13 @@ intel_display_print_error_state(struct seq_file *m, int i; seq_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); + if (HAS_POWER_WELL(dev)) + seq_printf(m, "PWR_WELL_CTL2: %08x\n", + error->power_well_driver); for_each_pipe(i) { seq_printf(m, "Pipe [%d]:\n", i); + seq_printf(m, " CPU transcoder: %c\n", + transcoder_name(error->pipe[i].cpu_transcoder)); seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); seq_printf(m, " SRC: %08x\n", error->pipe[i].source); seq_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 70789b1b5642..bfc8664be8dc 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -52,17 +52,11 @@ static bool is_edp(struct intel_dp *intel_dp) return intel_dig_port->base.type == INTEL_OUTPUT_EDP; } -/** - * is_pch_edp - is the port on the PCH and attached to an eDP panel? - * @intel_dp: DP struct - * - * Returns true if the given DP struct corresponds to a PCH DP port attached - * to an eDP panel, false otherwise. Helpful for determining whether we - * may need FDI resources for a given DP output or not. - */ -static bool is_pch_edp(struct intel_dp *intel_dp) +static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp) { - return intel_dp->is_pch_edp; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + + return intel_dig_port->base.base.dev; } /** @@ -73,14 +67,12 @@ static bool is_pch_edp(struct intel_dp *intel_dp) */ static bool is_cpu_edp(struct intel_dp *intel_dp) { - return is_edp(intel_dp) && !is_pch_edp(intel_dp); -} - -static struct drm_device *intel_dp_to_dev(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); + enum port port = intel_dig_port->port; - return intel_dig_port->base.base.dev; + return is_edp(intel_dp) && + (port == PORT_A || (port == PORT_C && IS_VALLEYVIEW(dev))); } static struct intel_dp *intel_attached_dp(struct drm_connector *connector) @@ -88,25 +80,6 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) return enc_to_intel_dp(&intel_attached_encoder(connector)->base); } -/** - * intel_encoder_is_pch_edp - is the given encoder a PCH attached eDP? - * @encoder: DRM encoder - * - * Return true if @encoder corresponds to a PCH attached eDP panel. Needed - * by intel_display.c. - */ -bool intel_encoder_is_pch_edp(struct drm_encoder *encoder) -{ - struct intel_dp *intel_dp; - - if (!encoder) - return false; - - intel_dp = enc_to_intel_dp(encoder); - - return is_pch_edp(intel_dp); -} - static void intel_dp_link_down(struct intel_dp *intel_dp); static int @@ -660,6 +633,49 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, return ret; } +static void +intel_dp_set_clock(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config, int link_bw) +{ + struct drm_device *dev = encoder->base.dev; + + if (IS_G4X(dev)) { + if (link_bw == DP_LINK_BW_1_62) { + pipe_config->dpll.p1 = 2; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.n = 2; + pipe_config->dpll.m1 = 23; + pipe_config->dpll.m2 = 8; + } else { + pipe_config->dpll.p1 = 1; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.n = 1; + pipe_config->dpll.m1 = 14; + pipe_config->dpll.m2 = 2; + } + pipe_config->clock_set = true; + } else if (IS_HASWELL(dev)) { + /* Haswell has special-purpose DP DDI clocks. */ + } else if (HAS_PCH_SPLIT(dev)) { + if (link_bw == DP_LINK_BW_1_62) { + pipe_config->dpll.n = 1; + pipe_config->dpll.p1 = 2; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.m1 = 12; + pipe_config->dpll.m2 = 9; + } else { + pipe_config->dpll.n = 2; + pipe_config->dpll.p1 = 1; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.m1 = 14; + pipe_config->dpll.m2 = 8; + } + pipe_config->clock_set = true; + } else if (IS_VALLEYVIEW(dev)) { + /* FIXME: Need to figure out optimized DP clocks for vlv. */ + } +} + bool intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) @@ -667,8 +683,8 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; - struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct intel_crtc *intel_crtc = encoder->new_crtc; struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); @@ -685,9 +701,12 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { intel_fixed_panel_mode(intel_connector->panel.fixed_mode, adjusted_mode); - intel_pch_panel_fitting(dev, - intel_connector->panel.fitting_mode, - mode, adjusted_mode); + if (!HAS_PCH_SPLIT(dev)) + intel_gmch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); + else + intel_pch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); } /* We need to take the panel's fixed mode into account. */ target_clock = adjusted_mode->clock; @@ -702,8 +721,8 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ bpp = min_t(int, 8*3, pipe_config->pipe_bpp); - if (is_edp(intel_dp) && dev_priv->edp.bpp) - bpp = min_t(int, bpp, dev_priv->edp.bpp); + if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp) + bpp = min_t(int, bpp, dev_priv->vbt.edp_bpp); for (; bpp >= 6*3; bpp -= 2*3) { mode_rate = intel_dp_link_required(target_clock, bpp); @@ -755,6 +774,8 @@ found: target_clock, adjusted_mode->clock, &pipe_config->dp_m_n); + intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); + return true; } @@ -833,18 +854,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, /* Handle DP bits in common between all three register formats */ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count); - switch (intel_dp->lane_count) { - case 1: - intel_dp->DP |= DP_PORT_WIDTH_1; - break; - case 2: - intel_dp->DP |= DP_PORT_WIDTH_2; - break; - case 4: - intel_dp->DP |= DP_PORT_WIDTH_4; - break; - } if (intel_dp->has_audio) { DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n", pipe_name(intel_crtc->pipe)); @@ -1381,15 +1392,77 @@ static void intel_enable_dp(struct intel_encoder *encoder) intel_dp_complete_link_train(intel_dp); intel_dp_stop_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = + enc_to_dig_port(&encoder->base); + int channel = vlv_dport_to_channel(dport); + + vlv_wait_port_ready(dev_priv, channel); + } } static void intel_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) ironlake_edp_pll_on(intel_dp); + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct intel_crtc *intel_crtc = + to_intel_crtc(encoder->base.crtc); + int port = vlv_dport_to_channel(dport); + int pipe = intel_crtc->pipe; + u32 val; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); + val = 0; + if (pipe) + val |= (1<<21); + else + val &= ~(1<<21); + val |= 0x001000c4; + intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); + + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), + 0x00760018); + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), + 0x00400888); + } +} + +static void intel_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; + int port = vlv_dport_to_channel(dport); + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Program Tx lane resets to default */ + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), + DPIO_PCS_TX_LANE2_RESET | + DPIO_PCS_TX_LANE1_RESET); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), + DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | + DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | + (1<dev_private; + struct intel_digital_port *dport = dp_to_dig_port(intel_dp); + unsigned long demph_reg_value, preemph_reg_value, + uniqtranscale_reg_value; + uint8_t train_set = intel_dp->train_set[0]; + int port = vlv_dport_to_channel(dport); + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPHASIS_0: + preemph_reg_value = 0x0004000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B405555; + uniqtranscale_reg_value = 0x552AB83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x5548B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_800: + demph_reg_value = 0x2B245555; + uniqtranscale_reg_value = 0x5560B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_1200: + demph_reg_value = 0x2B405555; + uniqtranscale_reg_value = 0x5598DA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_3_5: + preemph_reg_value = 0x0002000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x5552B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B404848; + uniqtranscale_reg_value = 0x5580B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_800: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_6: + preemph_reg_value = 0x0000000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B305555; + uniqtranscale_reg_value = 0x5570B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B2B4040; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_9_5: + preemph_reg_value = 0x0006000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x1B405555; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + default: + return 0; + } + + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x00000000); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), demph_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port), + uniqtranscale_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port), 0x0C782040); + intel_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000); + intel_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), preemph_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x80000000); + + return 0; +} + static void intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { @@ -1676,7 +1860,10 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) if (HAS_DDI(dev)) { signal_levels = intel_hsw_signal_levels(train_set); mask = DDI_BUF_EMP_MASK; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + } else if (IS_VALLEYVIEW(dev)) { + signal_levels = intel_vlv_signal_levels(intel_dp); + mask = 0; + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen7_edp_signal_levels(train_set); mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB; } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { @@ -2588,11 +2775,11 @@ bool intel_dpd_is_edp(struct drm_device *dev) struct child_device_config *p_child; int i; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return false; - for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + p_child = dev_priv->vbt.child_dev + i; if (p_child->dvo_port == PORT_IDPD && p_child->device_type == DEVICE_TYPE_eDP) @@ -2670,7 +2857,7 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); - vbt = dev_priv->edp.pps; + vbt = dev_priv->vbt.edp_pps; /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of * our hw here, which are all in 100usec. */ @@ -2792,28 +2979,39 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp->DP = I915_READ(intel_dp->output_reg); intel_dp->attached_connector = intel_connector; - if (HAS_PCH_SPLIT(dev) && port == PORT_D) - if (intel_dpd_is_edp(dev)) - intel_dp->is_pch_edp = true; - + type = DRM_MODE_CONNECTOR_DisplayPort; /* * FIXME : We need to initialize built-in panels before external panels. * For X0, DP_C is fixed as eDP. Revisit this as part of VLV eDP cleanup */ - if (IS_VALLEYVIEW(dev) && port == PORT_C) { + switch (port) { + case PORT_A: type = DRM_MODE_CONNECTOR_eDP; - intel_encoder->type = INTEL_OUTPUT_EDP; - } else if (port == PORT_A || is_pch_edp(intel_dp)) { - type = DRM_MODE_CONNECTOR_eDP; - intel_encoder->type = INTEL_OUTPUT_EDP; - } else { - /* The intel_encoder->type value may be INTEL_OUTPUT_UNKNOWN for - * DDI or INTEL_OUTPUT_DISPLAYPORT for the older gens, so don't - * rewrite it. - */ - type = DRM_MODE_CONNECTOR_DisplayPort; + break; + case PORT_C: + if (IS_VALLEYVIEW(dev)) + type = DRM_MODE_CONNECTOR_eDP; + break; + case PORT_D: + if (HAS_PCH_SPLIT(dev) && intel_dpd_is_edp(dev)) + type = DRM_MODE_CONNECTOR_eDP; + break; + default: /* silence GCC warning */ + break; } + /* + * For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but + * for DP the encoder type can be set by the caller to + * INTEL_OUTPUT_UNKNOWN for DDI, so don't rewrite it. + */ + if (type == DRM_MODE_CONNECTOR_eDP) + intel_encoder->type = INTEL_OUTPUT_EDP; + + DRM_DEBUG_KMS("Adding %s connector on port %c\n", + type == DRM_MODE_CONNECTOR_eDP ? "eDP" : "DP", + port_name(port)); + drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); @@ -2929,8 +3127,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } /* fallback to VBT if available for eDP */ - if (!fixed_mode && dev_priv->lfp_lvds_vbt_mode) { - fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (!fixed_mode && dev_priv->vbt.lfp_lvds_vbt_mode) { + fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); if (fixed_mode) fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; } @@ -2986,6 +3184,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->disable = intel_disable_dp; intel_encoder->post_disable = intel_post_disable_dp; intel_encoder->get_hw_state = intel_dp_get_hw_state; + if (IS_VALLEYVIEW(dev)) + intel_encoder->pre_pll_enable = intel_dp_pre_pll_enable; intel_dig_port->port = port; intel_dig_port->dp.output_reg = output_reg; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 624a9e6b8d71..9b0af7e27c82 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -120,7 +120,6 @@ struct intel_encoder { struct intel_crtc *new_crtc; int type; - bool needs_tv_clock; /* * Intel hw has only one MUX where encoders could be clone, hence a * simple flag is enough to compute the possible_clones mask. @@ -177,6 +176,18 @@ struct intel_connector { u8 polled; }; +typedef struct dpll { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +} intel_clock_t; + struct intel_crtc_config { struct drm_display_mode requested_mode; struct drm_display_mode adjusted_mode; @@ -201,18 +212,30 @@ struct intel_crtc_config { /* DP has a bunch of special case unfortunately, so mark the pipe * accordingly. */ bool has_dp_encoder; + + /* + * Enable dithering, used when the selected pipe bpp doesn't match the + * plane bpp. + */ bool dither; /* Controls for the clock computation, to override various stages. */ bool clock_set; + /* SDVO TV has a bunch of special case. To make multifunction encoders + * work correctly, we need to track this at runtime.*/ + bool sdvo_tv_clock; + + /* + * crtc bandwidth limit, don't increase pipe bpp or clock if not really + * required. This is set in the 2nd loop of calling encoder's + * ->compute_config if the first pick doesn't work out. + */ + bool bw_constrained; + /* Settings for the intel dpll used on pretty much everything but * haswell. */ - struct dpll { - unsigned n; - unsigned m1, m2; - unsigned p1, p2; - } dpll; + struct dpll dpll; int pipe_bpp; struct intel_link_m_n dp_m_n; @@ -224,6 +247,23 @@ struct intel_crtc_config { int pixel_target_clock; /* Used by SDVO (and if we ever fix it, HDMI). */ unsigned pixel_multiplier; + + /* Panel fitter controls for gen2-gen4 + VLV */ + struct { + u32 control; + u32 pgm_ratios; + u32 lvds_border_bits; + } gmch_pfit; + + /* Panel fitter placement and size for Ironlake+ */ + struct { + u32 pos; + u32 size; + } pch_pfit; + + /* FDI configuration, only valid if has_pch_encoder is set. */ + int fdi_lanes; + struct intel_link_m_n fdi_m_n; }; struct intel_crtc { @@ -242,7 +282,6 @@ struct intel_crtc { bool lowfreq_avail; struct intel_overlay *overlay; struct intel_unpin_work *unpin_work; - int fdi_lanes; atomic_t unpin_work_count; @@ -265,6 +304,10 @@ struct intel_crtc { /* reset counter value when the last flip was submitted */ unsigned int reset_counter; + + /* Access to these should be protected by dev_priv->irq_lock. */ + bool cpu_fifo_underrun_disabled; + bool pch_fifo_underrun_disabled; }; struct intel_plane { @@ -411,7 +454,6 @@ struct intel_dp { uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; - bool is_pch_edp; uint8_t train_set[4]; int panel_power_up_delay; int panel_power_down_delay; @@ -431,6 +473,19 @@ struct intel_digital_port { struct intel_hdmi hdmi; }; +static inline int +vlv_dport_to_channel(struct intel_digital_port *dport) +{ + switch (dport->port) { + case PORT_B: + return 0; + case PORT_C: + return 1; + default: + BUG(); + } +} + static inline struct drm_crtc * intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) { @@ -474,6 +529,7 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); extern void intel_attach_force_audio_property(struct drm_connector *connector); extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector); +extern bool intel_pipe_has_type(struct drm_crtc *crtc, int type); extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port); @@ -512,7 +568,6 @@ extern void ironlake_edp_panel_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_off(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); -extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder); extern int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); @@ -524,12 +579,14 @@ extern void intel_panel_fini(struct intel_panel *panel); extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); -extern void intel_pch_panel_fitting(struct drm_device *dev, - int fitting_mode, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -extern u32 intel_panel_get_max_backlight(struct drm_device *dev); -extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); +extern void intel_pch_panel_fitting(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode); +extern void intel_gmch_panel_fitting(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode); +extern void intel_panel_set_backlight(struct drm_device *dev, + u32 level, u32 max); extern int intel_panel_setup_backlight(struct drm_connector *connector); extern void intel_panel_enable_backlight(struct drm_device *dev, enum pipe pipe); @@ -565,19 +622,17 @@ static inline struct intel_encoder *intel_attached_encoder(struct drm_connector return to_intel_connector(connector)->encoder; } -static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) -{ - struct intel_digital_port *intel_dig_port = - container_of(encoder, struct intel_digital_port, base.base); - return &intel_dig_port->dp; -} - static inline struct intel_digital_port * enc_to_dig_port(struct drm_encoder *encoder) { return container_of(encoder, struct intel_digital_port, base.base); } +static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) +{ + return &enc_to_dig_port(encoder)->dp; +} + static inline struct intel_digital_port * dp_to_dig_port(struct intel_dp *intel_dp) { @@ -607,6 +662,7 @@ intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, extern void intel_wait_for_vblank(struct drm_device *dev, int pipe); extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); extern int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); +extern void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port); struct intel_load_detect_pipe { struct drm_framebuffer *release_fb; @@ -660,13 +716,9 @@ extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, #define assert_pipe_disabled(d, p) assert_pipe(d, p, false) extern void intel_init_clock_gating(struct drm_device *dev); +extern void intel_suspend_hw(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); -extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); -extern void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n); -extern void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n); extern void intel_prepare_ddi(struct drm_device *dev); extern void hsw_fdi_link_train(struct drm_crtc *crtc); extern void intel_ddi_init(struct drm_device *dev, enum port port); @@ -690,6 +742,8 @@ extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); extern u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg); +extern void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, + u32 val); /* Power-related functions, located in intel_pm.c */ extern void intel_init_pm(struct drm_device *dev); @@ -701,7 +755,8 @@ extern void intel_update_fbc(struct drm_device *dev); extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); -extern bool intel_using_power_well(struct drm_device *dev); +extern bool intel_display_power_enabled(struct drm_device *dev, + enum intel_display_power_domain domain); extern void intel_init_power_well(struct drm_device *dev); extern void intel_set_power_well(struct drm_device *dev, bool enable); extern void intel_enable_gt_powersave(struct drm_device *dev); @@ -728,5 +783,11 @@ intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); extern void intel_ddi_fdi_disable(struct drm_crtc *crtc); extern void intel_display_handle_reset(struct drm_device *dev); +extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, + bool enable); +extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index cc70b16d5d42..2c0be924e9a9 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -53,6 +53,13 @@ static const struct intel_dvo_device intel_dvo_devices[] = { .slave_addr = CH7xxx_ADDR, .dev_ops = &ch7xxx_ops, }, + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "ch7xxx", + .dvo_reg = DVOC, + .slave_addr = 0x75, /* For some ch7010 */ + .dev_ops = &ch7xxx_ops, + }, { .type = INTEL_DVO_CHIP_LVDS, .name = "ivch", diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a9057930f2b2..2b727f0d201f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -697,6 +697,14 @@ static void intel_enable_hdmi(struct intel_encoder *encoder) I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); } + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = + enc_to_dig_port(&encoder->base); + int channel = vlv_dport_to_channel(dport); + + vlv_wait_port_ready(dev_priv, channel); + } } static void intel_disable_hdmi(struct intel_encoder *encoder) @@ -775,6 +783,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct drm_device *dev = encoder->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2; + int desired_bpp; if (intel_hdmi->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ @@ -794,14 +804,31 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, /* * HDMI is either 12 or 8, so if the display lets 10bpc sneak * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi - * outputs. + * outputs. We also need to check that the higher clock still fits + * within limits. */ - if (pipe_config->pipe_bpp > 8*3 && HAS_PCH_SPLIT(dev)) { - DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); - pipe_config->pipe_bpp = 12*3; + if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000 + && HAS_PCH_SPLIT(dev)) { + DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); + desired_bpp = 12*3; + + /* Need to adjust the port link by 1.5x for 12bpc. */ + adjusted_mode->clock = clock_12bpc; + pipe_config->pixel_target_clock = + pipe_config->requested_mode.clock; } else { - DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); - pipe_config->pipe_bpp = 8*3; + DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n"); + desired_bpp = 8*3; + } + + if (!pipe_config->bw_constrained) { + DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp); + pipe_config->pipe_bpp = desired_bpp; + } + + if (adjusted_mode->clock > 225000) { + DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n"); + return false; } return true; @@ -955,6 +982,101 @@ done: return 0; } +static void intel_hdmi_pre_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); + int port = vlv_dport_to_channel(dport); + int pipe = intel_crtc->pipe; + u32 val; + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Enable clock channels for this port */ + val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); + val = 0; + if (pipe) + val |= (1<<21); + else + val &= ~(1<<21); + val |= 0x001000c4; + intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); + + /* HDMI 1.0V-2dB */ + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), + 0x2b245f5f); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port), + 0x5578b83a); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port), + 0x0c782040); + intel_dpio_write(dev_priv, DPIO_TX3_SWING_CTL4(port), + 0x2b247878); + intel_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000); + intel_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), + 0x00002000); + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), + DPIO_TX_OCALINIT_EN); + + /* Program lane clock */ + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), + 0x00760018); + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), + 0x00400888); +} + +static void intel_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; + int port = vlv_dport_to_channel(dport); + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Program Tx lane resets to default */ + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), + DPIO_PCS_TX_LANE2_RESET | + DPIO_PCS_TX_LANE1_RESET); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), + DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | + DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | + (1<base); + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + int port = vlv_dport_to_channel(dport); + + /* Reset lanes to avoid HDMI flicker (VLV w/a) */ + mutex_lock(&dev_priv->dpio_lock); + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060); + mutex_unlock(&dev_priv->dpio_lock); +} + static void intel_hdmi_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); @@ -1094,6 +1216,11 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->enable = intel_enable_hdmi; intel_encoder->disable = intel_disable_hdmi; intel_encoder->get_hw_state = intel_hdmi_get_hw_state; + if (IS_VALLEYVIEW(dev)) { + intel_encoder->pre_enable = intel_hdmi_pre_enable; + intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable; + intel_encoder->post_disable = intel_hdmi_post_disable; + } intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index f36f1baabd5a..36fe291172ee 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -49,8 +49,6 @@ struct intel_lvds_connector { struct intel_lvds_encoder { struct intel_encoder base; - u32 pfit_control; - u32 pfit_pgm_ratios; bool is_dual_link; u32 reg; @@ -118,7 +116,8 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) } /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; + temp &= ~LVDS_BORDER_ENABLE; + temp |= intel_crtc->config.gmch_pfit.lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to * set the DPLLs for dual-channel mode or not. */ @@ -136,7 +135,10 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) * special lvds dither control bit on pch-split platforms, dithering is * only controlled through the PIPECONF reg. */ if (INTEL_INFO(dev)->gen == 4) { - if (dev_priv->lvds_dither) + /* Bspec wording suggests that LVDS port dithering only exists + * for 18bpp panels. */ + if (intel_crtc->config.dither && + intel_crtc->config.pipe_bpp == 18) temp |= LVDS_ENABLE_DITHER; else temp &= ~LVDS_ENABLE_DITHER; @@ -150,29 +152,6 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) I915_WRITE(lvds_encoder->reg, temp); } -static void intel_pre_enable_lvds(struct intel_encoder *encoder) -{ - struct drm_device *dev = encoder->base.dev; - struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base); - struct drm_i915_private *dev_priv = dev->dev_private; - - if (HAS_PCH_SPLIT(dev) || !enc->pfit_control) - return; - - /* - * Enable automatic panel scaling so that non-native modes - * fill the screen. The panel fitter should only be - * adjusted whilst the pipe is disabled, according to - * register description and PRM. - */ - DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", - enc->pfit_control, - enc->pfit_pgm_ratios); - - I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios); - I915_WRITE(PFIT_CONTROL, enc->pfit_control); -} - /** * Sets the power state for the panel. */ @@ -241,62 +220,6 @@ static int intel_lvds_mode_valid(struct drm_connector *connector, return MODE_OK; } -static void -centre_horizontally(struct drm_display_mode *mode, - int width) -{ - u32 border, sync_pos, blank_width, sync_width; - - /* keep the hsync and hblank widths constant */ - sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; - blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; - sync_pos = (blank_width - sync_width + 1) / 2; - - border = (mode->hdisplay - width + 1) / 2; - border += border & 1; /* make the border even */ - - mode->crtc_hdisplay = width; - mode->crtc_hblank_start = width + border; - mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; - - mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; - mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; -} - -static void -centre_vertically(struct drm_display_mode *mode, - int height) -{ - u32 border, sync_pos, blank_width, sync_width; - - /* keep the vsync and vblank widths constant */ - sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; - blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; - sync_pos = (blank_width - sync_width + 1) / 2; - - border = (mode->vdisplay - height + 1) / 2; - - mode->crtc_vdisplay = height; - mode->crtc_vblank_start = height + border; - mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; - - mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; - mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; -} - -static inline u32 panel_fitter_scaling(u32 source, u32 target) -{ - /* - * Floating point operation is not supported. So the FACTOR - * is defined, which can avoid the floating point computation - * when calculating the panel ratio. - */ -#define ACCURACY 12 -#define FACTOR (1 << ACCURACY) - u32 ratio = source * FACTOR / target; - return (FACTOR * ratio + FACTOR/2) / FACTOR; -} - static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct intel_crtc_config *pipe_config) { @@ -307,11 +230,8 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct intel_connector *intel_connector = &lvds_encoder->attached_connector->base; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; - struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; - u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; unsigned int lvds_bpp; - int pipe; /* Should never happen!! */ if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { @@ -328,11 +248,12 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, else lvds_bpp = 6*3; - if (lvds_bpp != pipe_config->pipe_bpp) { + if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) { DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", pipe_config->pipe_bpp, lvds_bpp); pipe_config->pipe_bpp = lvds_bpp; } + /* * We have timings from the BIOS for the panel, put them in * to the adjusted mode. The CRTC will be set up for this mode, @@ -345,139 +266,17 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, if (HAS_PCH_SPLIT(dev)) { pipe_config->has_pch_encoder = true; - intel_pch_panel_fitting(dev, - intel_connector->panel.fitting_mode, - mode, adjusted_mode); + intel_pch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); return true; + } else { + intel_gmch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); } - /* Native modes don't need fitting */ - if (adjusted_mode->hdisplay == mode->hdisplay && - adjusted_mode->vdisplay == mode->vdisplay) - goto out; - - /* 965+ wants fuzzy fitting */ - if (INTEL_INFO(dev)->gen >= 4) - pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | - PFIT_FILTER_FUZZY); - - /* - * Enable automatic panel scaling for non-native modes so that they fill - * the screen. Should be enabled before the pipe is enabled, according - * to register description and PRM. - * Change the value here to see the borders for debugging - */ - for_each_pipe(pipe) - I915_WRITE(BCLRPAT(pipe), 0); - drm_mode_set_crtcinfo(adjusted_mode, 0); pipe_config->timings_set = true; - switch (intel_connector->panel.fitting_mode) { - case DRM_MODE_SCALE_CENTER: - /* - * For centered modes, we have to calculate border widths & - * heights and modify the values programmed into the CRTC. - */ - centre_horizontally(adjusted_mode, mode->hdisplay); - centre_vertically(adjusted_mode, mode->vdisplay); - border = LVDS_BORDER_ENABLE; - break; - - case DRM_MODE_SCALE_ASPECT: - /* Scale but preserve the aspect ratio */ - if (INTEL_INFO(dev)->gen >= 4) { - u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; - u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; - - /* 965+ is easy, it does everything in hw */ - if (scaled_width > scaled_height) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR; - else if (scaled_width < scaled_height) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER; - else if (adjusted_mode->hdisplay != mode->hdisplay) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; - } else { - u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; - u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; - /* - * For earlier chips we have to calculate the scaling - * ratio by hand and program it into the - * PFIT_PGM_RATIO register - */ - if (scaled_width > scaled_height) { /* pillar */ - centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay); - - border = LVDS_BORDER_ENABLE; - if (mode->vdisplay != adjusted_mode->vdisplay) { - u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); - pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | - bits << PFIT_VERT_SCALE_SHIFT); - pfit_control |= (PFIT_ENABLE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - } else if (scaled_width < scaled_height) { /* letter */ - centre_vertically(adjusted_mode, scaled_width / mode->hdisplay); - - border = LVDS_BORDER_ENABLE; - if (mode->hdisplay != adjusted_mode->hdisplay) { - u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); - pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | - bits << PFIT_VERT_SCALE_SHIFT); - pfit_control |= (PFIT_ENABLE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - } else - /* Aspects match, Let hw scale both directions */ - pfit_control |= (PFIT_ENABLE | - VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - break; - - case DRM_MODE_SCALE_FULLSCREEN: - /* - * Full scaling, even if it changes the aspect ratio. - * Fortunately this is all done for us in hw. - */ - if (mode->vdisplay != adjusted_mode->vdisplay || - mode->hdisplay != adjusted_mode->hdisplay) { - pfit_control |= PFIT_ENABLE; - if (INTEL_INFO(dev)->gen >= 4) - pfit_control |= PFIT_SCALING_AUTO; - else - pfit_control |= (VERT_AUTO_SCALE | - VERT_INTERP_BILINEAR | - HORIZ_AUTO_SCALE | - HORIZ_INTERP_BILINEAR); - } - break; - - default: - break; - } - -out: - /* If not enabling scaling, be consistent and always use 0. */ - if ((pfit_control & PFIT_ENABLE) == 0) { - pfit_control = 0; - pfit_pgm_ratios = 0; - } - - /* Make sure pre-965 set dither correctly */ - if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - - if (pfit_control != lvds_encoder->pfit_control || - pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) { - lvds_encoder->pfit_control = pfit_control; - lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios; - } - dev_priv->lvds_border_bits = border; - /* * XXX: It would be nice to support lower refresh rates on the * panels to reduce power consumption, and perhaps match the @@ -937,11 +736,11 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; int i; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return true; - for (i = 0; i < dev_priv->child_dev_num; i++) { - struct child_device_config *child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + struct child_device_config *child = dev_priv->vbt.child_dev + i; /* If the device type is not LFP, continue. * We have to check both the new identifiers as well as the @@ -1029,7 +828,7 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) */ val = I915_READ(lvds_encoder->reg); if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; + val = dev_priv->vbt.bios_lvds_val; return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; } @@ -1089,7 +888,7 @@ bool intel_lvds_init(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return false; - if (dev_priv->edp.support) { + if (dev_priv->vbt.edp_support) { DRM_DEBUG_KMS("disable LVDS for eDP support\n"); return false; } @@ -1107,10 +906,6 @@ bool intel_lvds_init(struct drm_device *dev) lvds_encoder->attached_connector = lvds_connector; - if (!HAS_PCH_SPLIT(dev)) { - lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL); - } - intel_encoder = &lvds_encoder->base; encoder = &intel_encoder->base; intel_connector = &lvds_connector->base; @@ -1122,7 +917,6 @@ bool intel_lvds_init(struct drm_device *dev) DRM_MODE_ENCODER_LVDS); intel_encoder->enable = intel_enable_lvds; - intel_encoder->pre_enable = intel_pre_enable_lvds; intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; intel_encoder->compute_config = intel_lvds_compute_config; intel_encoder->disable = intel_disable_lvds; @@ -1212,11 +1006,11 @@ bool intel_lvds_init(struct drm_device *dev) } /* Failed to get EDID, what about VBT? */ - if (dev_priv->lfp_lvds_vbt_mode) { + if (dev_priv->vbt.lfp_lvds_vbt_mode) { DRM_DEBUG_KMS("using mode from VBT: "); - drm_mode_debug_printmodeline(dev_priv->lfp_lvds_vbt_mode); + drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode); - fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); if (fixed_mode) { fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index a8117e614009..5c2d6939600e 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -110,6 +110,10 @@ struct opregion_asle { u8 rsvd[102]; } __attribute__((packed)); +/* Driver readiness indicator */ +#define ASLE_ARDY_READY (1 << 0) +#define ASLE_ARDY_NOT_READY (0 << 0) + /* ASLE irq request bits */ #define ASLE_SET_ALS_ILLUM (1 << 0) #define ASLE_SET_BACKLIGHT (1 << 1) @@ -123,6 +127,12 @@ struct opregion_asle { #define ASLE_PFIT_FAILED (1<<14) #define ASLE_PWM_FREQ_FAILED (1<<16) +/* Technology enabled indicator */ +#define ASLE_TCHE_ALS_EN (1 << 0) +#define ASLE_TCHE_BLC_EN (1 << 1) +#define ASLE_TCHE_PFIT_EN (1 << 2) +#define ASLE_TCHE_PFMB_EN (1 << 3) + /* ASLE backlight brightness to set */ #define ASLE_BCLP_VALID (1<<31) #define ASLE_BCLP_MSK (~(1<<31)) @@ -152,7 +162,6 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - u32 max; DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); @@ -163,8 +172,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - max = intel_panel_get_max_backlight(dev); - intel_panel_set_backlight(dev, bclp * max / 255); + intel_panel_set_backlight(dev, bclp, 255); iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv); return 0; @@ -174,29 +182,22 @@ static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi) { /* alsi is the current ALS reading in lux. 0 indicates below sensor range, 0xffff indicates above sensor range. 1-0xfffe are valid */ - return 0; + DRM_DEBUG_DRIVER("Illum is not supported\n"); + return ASLE_ALS_ILLUM_FAILED; } static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb) { - struct drm_i915_private *dev_priv = dev->dev_private; - if (pfmb & ASLE_PFMB_PWM_VALID) { - u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL); - u32 pwm = pfmb & ASLE_PFMB_PWM_MASK; - blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK; - pwm = pwm >> 9; - /* FIXME - what do we do with the PWM? */ - } - return 0; + DRM_DEBUG_DRIVER("PWM freq is not supported\n"); + return ASLE_PWM_FREQ_FAILED; } static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) { /* Panel fitting is currently controlled by the X code, so this is a noop until modesetting support works fully */ - if (!(pfit & ASLE_PFIT_VALID)) - return ASLE_PFIT_FAILED; - return 0; + DRM_DEBUG_DRIVER("Pfit is not supported\n"); + return ASLE_PFIT_FAILED; } void intel_opregion_asle_intr(struct drm_device *dev) @@ -231,64 +232,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) iowrite32(asle_stat, &asle->aslc); } -void intel_opregion_gse_intr(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - u32 asle_stat = 0; - u32 asle_req; - - if (!asle) - return; - - asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; - - if (!asle_req) { - DRM_DEBUG_DRIVER("non asle set request??\n"); - return; - } - - if (asle_req & ASLE_SET_ALS_ILLUM) { - DRM_DEBUG_DRIVER("Illum is not supported\n"); - asle_stat |= ASLE_ALS_ILLUM_FAILED; - } - - if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); - - if (asle_req & ASLE_SET_PFIT) { - DRM_DEBUG_DRIVER("Pfit is not supported\n"); - asle_stat |= ASLE_PFIT_FAILED; - } - - if (asle_req & ASLE_SET_PWM_FREQ) { - DRM_DEBUG_DRIVER("PWM freq is not supported\n"); - asle_stat |= ASLE_PWM_FREQ_FAILED; - } - - iowrite32(asle_stat, &asle->aslc); -} -#define ASLE_ALS_EN (1<<0) -#define ASLE_BLC_EN (1<<1) -#define ASLE_PFIT_EN (1<<2) -#define ASLE_PFMB_EN (1<<3) - -void intel_opregion_enable_asle(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - - if (asle) { - if (IS_MOBILE(dev)) - intel_enable_asle(dev); - - iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | - ASLE_PFMB_EN, - &asle->tche); - iowrite32(1, &asle->ardy); - } -} - #define ACPI_EV_DISPLAY_SWITCH (1<<0) #define ACPI_EV_LID (1<<1) #define ACPI_EV_DOCK (1<<2) @@ -472,8 +415,10 @@ void intel_opregion_init(struct drm_device *dev) register_acpi_notifier(&intel_opregion_notifier); } - if (opregion->asle) - intel_opregion_enable_asle(dev); + if (opregion->asle) { + iowrite32(ASLE_TCHE_BLC_EN, &opregion->asle->tche); + iowrite32(ASLE_ARDY_READY, &opregion->asle->ardy); + } } void intel_opregion_fini(struct drm_device *dev) @@ -484,6 +429,9 @@ void intel_opregion_fini(struct drm_device *dev) if (!opregion->header) return; + if (opregion->asle) + iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); + if (opregion->acpi) { iowrite32(0, &opregion->acpi->drdy); @@ -546,6 +494,8 @@ int intel_opregion_setup(struct drm_device *dev) if (mboxes & MBOX_ASLE) { DRM_DEBUG_DRIVER("ASLE supported\n"); opregion->asle = base + OPREGION_ASLE_OFFSET; + + iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); } return 0; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index eb5e6e95f3c7..56f17b2382fc 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -54,14 +54,16 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, /* adjusted_mode has been preset to be the panel's fixed mode */ void -intel_pch_panel_fitting(struct drm_device *dev, - int fitting_mode, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +intel_pch_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *mode, *adjusted_mode; int x, y, width, height; + mode = &pipe_config->requested_mode; + adjusted_mode = &pipe_config->adjusted_mode; + x = y = width = height = 0; /* Native modes don't need fitting */ @@ -104,17 +106,209 @@ intel_pch_panel_fitting(struct drm_device *dev, } break; - default: case DRM_MODE_SCALE_FULLSCREEN: x = y = 0; width = adjusted_mode->hdisplay; height = adjusted_mode->vdisplay; break; + + default: + WARN(1, "bad panel fit mode: %d\n", fitting_mode); + return; } done: - dev_priv->pch_pf_pos = (x << 16) | y; - dev_priv->pch_pf_size = (width << 16) | height; + pipe_config->pch_pfit.pos = (x << 16) | y; + pipe_config->pch_pfit.size = (width << 16) | height; +} + +static void +centre_horizontally(struct drm_display_mode *mode, + int width) +{ + u32 border, sync_pos, blank_width, sync_width; + + /* keep the hsync and hblank widths constant */ + sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; + blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; + sync_pos = (blank_width - sync_width + 1) / 2; + + border = (mode->hdisplay - width + 1) / 2; + border += border & 1; /* make the border even */ + + mode->crtc_hdisplay = width; + mode->crtc_hblank_start = width + border; + mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; + + mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; + mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; +} + +static void +centre_vertically(struct drm_display_mode *mode, + int height) +{ + u32 border, sync_pos, blank_width, sync_width; + + /* keep the vsync and vblank widths constant */ + sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; + blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; + sync_pos = (blank_width - sync_width + 1) / 2; + + border = (mode->vdisplay - height + 1) / 2; + + mode->crtc_vdisplay = height; + mode->crtc_vblank_start = height + border; + mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; + + mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; + mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; +} + +static inline u32 panel_fitter_scaling(u32 source, u32 target) +{ + /* + * Floating point operation is not supported. So the FACTOR + * is defined, which can avoid the floating point computation + * when calculating the panel ratio. + */ +#define ACCURACY 12 +#define FACTOR (1 << ACCURACY) + u32 ratio = source * FACTOR / target; + return (FACTOR * ratio + FACTOR/2) / FACTOR; +} + +void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode) +{ + struct drm_device *dev = intel_crtc->base.dev; + u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; + struct drm_display_mode *mode, *adjusted_mode; + + mode = &pipe_config->requested_mode; + adjusted_mode = &pipe_config->adjusted_mode; + + /* Native modes don't need fitting */ + if (adjusted_mode->hdisplay == mode->hdisplay && + adjusted_mode->vdisplay == mode->vdisplay) + goto out; + + switch (fitting_mode) { + case DRM_MODE_SCALE_CENTER: + /* + * For centered modes, we have to calculate border widths & + * heights and modify the values programmed into the CRTC. + */ + centre_horizontally(adjusted_mode, mode->hdisplay); + centre_vertically(adjusted_mode, mode->vdisplay); + border = LVDS_BORDER_ENABLE; + break; + case DRM_MODE_SCALE_ASPECT: + /* Scale but preserve the aspect ratio */ + if (INTEL_INFO(dev)->gen >= 4) { + u32 scaled_width = adjusted_mode->hdisplay * + mode->vdisplay; + u32 scaled_height = mode->hdisplay * + adjusted_mode->vdisplay; + + /* 965+ is easy, it does everything in hw */ + if (scaled_width > scaled_height) + pfit_control |= PFIT_ENABLE | + PFIT_SCALING_PILLAR; + else if (scaled_width < scaled_height) + pfit_control |= PFIT_ENABLE | + PFIT_SCALING_LETTER; + else if (adjusted_mode->hdisplay != mode->hdisplay) + pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; + } else { + u32 scaled_width = adjusted_mode->hdisplay * + mode->vdisplay; + u32 scaled_height = mode->hdisplay * + adjusted_mode->vdisplay; + /* + * For earlier chips we have to calculate the scaling + * ratio by hand and program it into the + * PFIT_PGM_RATIO register + */ + if (scaled_width > scaled_height) { /* pillar */ + centre_horizontally(adjusted_mode, + scaled_height / + mode->vdisplay); + + border = LVDS_BORDER_ENABLE; + if (mode->vdisplay != adjusted_mode->vdisplay) { + u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); + pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | + bits << PFIT_VERT_SCALE_SHIFT); + pfit_control |= (PFIT_ENABLE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } else if (scaled_width < scaled_height) { /* letter */ + centre_vertically(adjusted_mode, + scaled_width / + mode->hdisplay); + + border = LVDS_BORDER_ENABLE; + if (mode->hdisplay != adjusted_mode->hdisplay) { + u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); + pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | + bits << PFIT_VERT_SCALE_SHIFT); + pfit_control |= (PFIT_ENABLE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } else { + /* Aspects match, Let hw scale both directions */ + pfit_control |= (PFIT_ENABLE | + VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } + break; + case DRM_MODE_SCALE_FULLSCREEN: + /* + * Full scaling, even if it changes the aspect ratio. + * Fortunately this is all done for us in hw. + */ + if (mode->vdisplay != adjusted_mode->vdisplay || + mode->hdisplay != adjusted_mode->hdisplay) { + pfit_control |= PFIT_ENABLE; + if (INTEL_INFO(dev)->gen >= 4) + pfit_control |= PFIT_SCALING_AUTO; + else + pfit_control |= (VERT_AUTO_SCALE | + VERT_INTERP_BILINEAR | + HORIZ_AUTO_SCALE | + HORIZ_INTERP_BILINEAR); + } + break; + default: + WARN(1, "bad panel fit mode: %d\n", fitting_mode); + return; + } + + /* 965+ wants fuzzy fitting */ + /* FIXME: handle multiple panels by failing gracefully */ + if (INTEL_INFO(dev)->gen >= 4) + pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | + PFIT_FILTER_FUZZY); + +out: + if ((pfit_control & PFIT_ENABLE) == 0) { + pfit_control = 0; + pfit_pgm_ratios = 0; + } + + /* Make sure pre-965 set dither correctly for 18bpp panels. */ + if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + pipe_config->gmch_pfit.control = pfit_control; + pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; + pipe_config->gmch_pfit.lvds_border_bits = border; } static int is_backlight_combination_mode(struct drm_device *dev) @@ -130,11 +324,16 @@ static int is_backlight_combination_mode(struct drm_device *dev) return 0; } +/* XXX: query mode clock or hardware clock and program max PWM appropriately + * when it's 0. + */ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 val; + WARN_ON(!spin_is_locked(&dev_priv->backlight.lock)); + /* Restore the CTL value if it lost, e.g. GPU reset */ if (HAS_PCH_SPLIT(dev_priv->dev)) { @@ -164,7 +363,7 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) return val; } -static u32 _intel_panel_get_max_backlight(struct drm_device *dev) +static u32 intel_panel_get_max_backlight(struct drm_device *dev) { u32 max; @@ -182,23 +381,8 @@ static u32 _intel_panel_get_max_backlight(struct drm_device *dev) max *= 0xff; } - return max; -} - -u32 intel_panel_get_max_backlight(struct drm_device *dev) -{ - u32 max; - - max = _intel_panel_get_max_backlight(dev); - if (max == 0) { - /* XXX add code here to query mode clock or hardware clock - * and program max PWM appropriately. - */ - pr_warn_once("fixme: max PWM is zero\n"); - return 1; - } - DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); + return max; } @@ -217,8 +401,11 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val) return val; if (i915_panel_invert_brightness > 0 || - dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) - return intel_panel_get_max_backlight(dev) - val; + dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { + u32 max = intel_panel_get_max_backlight(dev); + if (max) + return max - val; + } return val; } @@ -227,6 +414,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); if (HAS_PCH_SPLIT(dev)) { val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; @@ -244,6 +434,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev) } val = intel_panel_compute_brightness(dev, val); + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); return val; } @@ -270,6 +463,10 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level u32 max = intel_panel_get_max_backlight(dev); u8 lbpc; + /* we're screwed, but keep behaviour backwards compatible */ + if (!max) + max = 1; + lbpc = level * 0xfe / max + 1; level /= lbpc; pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc); @@ -282,9 +479,23 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level I915_WRITE(BLC_PWM_CTL, tmp | level); } -void intel_panel_set_backlight(struct drm_device *dev, u32 level) +/* set backlight brightness to level in range [0..max] */ +void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 freq; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + + freq = intel_panel_get_max_backlight(dev); + if (!freq) { + /* we are screwed, bail out */ + goto out; + } + + /* scale to hardware */ + level = level * freq / max; dev_priv->backlight.level = level; if (dev_priv->backlight.device) @@ -292,11 +503,16 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level) if (dev_priv->backlight.enabled) intel_panel_actually_set_backlight(dev, level); +out: + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } void intel_panel_disable_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); dev_priv->backlight.enabled = false; intel_panel_actually_set_backlight(dev, 0); @@ -314,12 +530,19 @@ void intel_panel_disable_backlight(struct drm_device *dev) I915_WRITE(BLC_PWM_PCH_CTL1, tmp); } } + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } void intel_panel_enable_backlight(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = + intel_pipe_to_cpu_transcoder(dev_priv, pipe); + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); if (dev_priv->backlight.level == 0) { dev_priv->backlight.level = intel_panel_get_max_backlight(dev); @@ -347,7 +570,10 @@ void intel_panel_enable_backlight(struct drm_device *dev, else tmp &= ~BLM_PIPE_SELECT; - tmp |= BLM_PIPE(pipe); + if (cpu_transcoder == TRANSCODER_EDP) + tmp |= BLM_TRANSCODER_EDP; + else + tmp |= BLM_PIPE(cpu_transcoder); tmp &= ~BLM_PWM_ENABLE; I915_WRITE(reg, tmp); @@ -369,6 +595,8 @@ set_level: */ dev_priv->backlight.enabled = true; intel_panel_actually_set_backlight(dev, dev_priv->backlight.level); + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } static void intel_panel_init_backlight(struct drm_device *dev) @@ -405,7 +633,8 @@ intel_panel_detect(struct drm_device *dev) static int intel_panel_update_status(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); - intel_panel_set_backlight(dev, bd->props.brightness); + intel_panel_set_backlight(dev, bd->props.brightness, + bd->props.max_brightness); return 0; } @@ -425,6 +654,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct backlight_properties props; + unsigned long flags; intel_panel_init_backlight(dev); @@ -434,7 +664,11 @@ int intel_panel_setup_backlight(struct drm_connector *connector) memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.brightness = dev_priv->backlight.level; - props.max_brightness = _intel_panel_get_max_backlight(dev); + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + props.max_brightness = intel_panel_get_max_backlight(dev); + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + if (props.max_brightness == 0) { DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n"); return -ENODEV; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index aa01128ff192..e2255ed97894 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -113,8 +113,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) fbc_ctl |= obj->fence_reg; I915_WRITE(FBC_CONTROL, fbc_ctl); - DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ", - cfb_pitch, crtc->y, intel_crtc->plane); + DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c, ", + cfb_pitch, crtc->y, plane_name(intel_crtc->plane)); } static bool i8xx_fbc_enabled(struct drm_device *dev) @@ -148,7 +148,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) /* enable it... */ I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN); - DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); + DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); } static void g4x_disable_fbc(struct drm_device *dev) @@ -228,7 +228,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) sandybridge_blit_fbc_update(dev); } - DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); + DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); } static void ironlake_disable_fbc(struct drm_device *dev) @@ -242,6 +242,18 @@ static void ironlake_disable_fbc(struct drm_device *dev) dpfc_ctl &= ~DPFC_CTL_EN; I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); + if (IS_IVYBRIDGE(dev)) + /* WaFbcDisableDpfcClockGating:ivb */ + I915_WRITE(ILK_DSPCLK_GATE_D, + I915_READ(ILK_DSPCLK_GATE_D) & + ~ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + + if (IS_HASWELL(dev)) + /* WaFbcDisableDpfcClockGating:hsw */ + I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, + I915_READ(HSW_CLKGATE_DISABLE_PART_1) & + ~HSW_DPFC_GATING_DISABLE); + DRM_DEBUG_KMS("disabled FBC\n"); } } @@ -253,6 +265,47 @@ static bool ironlake_fbc_enabled(struct drm_device *dev) return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN; } +static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + I915_WRITE(IVB_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID); + + I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X | + IVB_DPFC_CTL_FENCE_EN | + intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); + + if (IS_IVYBRIDGE(dev)) { + /* WaFbcAsynchFlipDisableFbcQueue:ivb */ + I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); + /* WaFbcDisableDpfcClockGating:ivb */ + I915_WRITE(ILK_DSPCLK_GATE_D, + I915_READ(ILK_DSPCLK_GATE_D) | + ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + } else { + /* WaFbcAsynchFlipDisableFbcQueue:hsw */ + I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe), + HSW_BYPASS_FBC_QUEUE); + /* WaFbcDisableDpfcClockGating:hsw */ + I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, + I915_READ(HSW_CLKGATE_DISABLE_PART_1) | + HSW_DPFC_GATING_DISABLE); + } + + I915_WRITE(SNB_DPFC_CTL_SA, + SNB_CPU_FENCE_ENABLE | obj->fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); + + sandybridge_blit_fbc_update(dev); + + DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); +} + bool intel_fbc_enabled(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -439,7 +492,7 @@ void intel_update_fbc(struct drm_device *dev) if (enable_fbc < 0) { DRM_DEBUG_KMS("fbc set to per-chip default\n"); enable_fbc = 1; - if (INTEL_INFO(dev)->gen <= 6) + if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) enable_fbc = 0; } if (!enable_fbc) { @@ -460,7 +513,8 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; goto out_disable; } - if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) { + if ((IS_I915GM(dev) || IS_I945GM(dev) || IS_HASWELL(dev)) && + intel_crtc->plane != 0) { DRM_DEBUG_KMS("plane not 0, disabling compression\n"); dev_priv->no_fbc_reason = FBC_BAD_PLANE; goto out_disable; @@ -481,8 +535,6 @@ void intel_update_fbc(struct drm_device *dev) goto out_disable; if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { - DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); - DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; goto out_disable; @@ -1633,6 +1685,10 @@ static bool ironlake_check_srwm(struct drm_device *dev, int level, I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS); return false; + } else if (INTEL_INFO(dev)->gen >= 6) { + /* enable FBC WM (except on ILK, where it must remain off) */ + I915_WRITE(DISP_ARB_CTL, + I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS); } if (display_wm > display->max_wm) { @@ -2146,15 +2202,15 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, &sandybridge_display_wm_info, latency, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n", + pipe_name(pipe)); return; } val = I915_READ(reg); val &= ~WM0_PIPE_SPRITE_MASK; I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT)); - DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm); + DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm); ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, @@ -2163,8 +2219,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM1_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM1S_LP_ILK, sprite_wm); @@ -2179,8 +2235,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM2_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM2S_LP_IVB, sprite_wm); @@ -2191,8 +2247,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM3_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM3S_LP_IVB, sprite_wm); @@ -2481,6 +2537,52 @@ void gen6_set_rps(struct drm_device *dev, u8 val) trace_intel_gpu_freq_change(val * 50); } +void valleyview_set_rps(struct drm_device *dev, u8 val) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long timeout = jiffies + msecs_to_jiffies(10); + u32 limits = gen6_rps_limits(dev_priv, &val); + u32 pval; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + WARN_ON(val > dev_priv->rps.max_delay); + WARN_ON(val < dev_priv->rps.min_delay); + + DRM_DEBUG_DRIVER("gpu freq request from %d to %d\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.cur_delay), + vlv_gpu_freq(dev_priv->mem_freq, val)); + + if (val == dev_priv->rps.cur_delay) + return; + + valleyview_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); + + do { + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + if (time_after(jiffies, timeout)) { + DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); + break; + } + udelay(10); + } while (pval & 1); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + if ((pval >> 8) != val) + DRM_DEBUG_DRIVER("punit overrode freq: %d requested, but got %d\n", + val, pval >> 8); + + /* Make sure we continue to get interrupts + * until we hit the minimum or maximum frequencies. + */ + I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); + + dev_priv->rps.cur_delay = pval >> 8; + + trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val)); +} + + static void gen6_disable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2501,6 +2603,30 @@ static void gen6_disable_rps(struct drm_device *dev) I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); } +static void valleyview_disable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(GEN6_RC_CONTROL, 0); + I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); + I915_WRITE(GEN6_PMIER, 0); + /* Complete PM interrupt masking here doesn't race with the rps work + * item again unmasking PM interrupts because that is using a different + * register (PMIMR) to mask PM interrupts. The only risk is in leaving + * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ + + spin_lock_irq(&dev_priv->rps.lock); + dev_priv->rps.pm_iir = 0; + spin_unlock_irq(&dev_priv->rps.lock); + + I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); + + if (dev_priv->vlv_pctx) { + drm_gem_object_unreference(&dev_priv->vlv_pctx->base); + dev_priv->vlv_pctx = NULL; + } +} + int intel_enable_rc6(const struct drm_device *dev) { /* Respect the kernel parameter if it is set */ @@ -2742,6 +2868,202 @@ static void gen6_update_ring_freq(struct drm_device *dev) } } +int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rp0; + + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE, &val); + + rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT; + /* Clamp to max */ + rp0 = min_t(u32, rp0, 0xea); + + return rp0; +} + +static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rpe; + + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO, &val); + rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT; + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI, &val); + rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5; + + return rpe; +} + +int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) +{ + u32 val; + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + + return val & 0xff; +} + +static void vlv_rps_timer_work(struct work_struct *work) +{ + drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, + rps.vlv_work.work); + + /* + * Timer fired, we must be idle. Drop to min voltage state. + * Note: we use RPe here since it should match the + * Vmin we were shooting for. That should give us better + * perf when we come back out of RC6 than if we used the + * min freq available. + */ + mutex_lock(&dev_priv->rps.hw_lock); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); + mutex_unlock(&dev_priv->rps.hw_lock); +} + +static void valleyview_setup_pctx(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *pctx; + unsigned long pctx_paddr; + u32 pcbr; + int pctx_size = 24*1024; + + pcbr = I915_READ(VLV_PCBR); + if (pcbr) { + /* BIOS set it up already, grab the pre-alloc'd space */ + int pcbr_offset; + + pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; + pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev, + pcbr_offset, + -1, + pctx_size); + goto out; + } + + /* + * From the Gunit register HAS: + * The Gfx driver is expected to program this register and ensure + * proper allocation within Gfx stolen memory. For example, this + * register should be programmed such than the PCBR range does not + * overlap with other ranges, such as the frame buffer, protected + * memory, or any other relevant ranges. + */ + pctx = i915_gem_object_create_stolen(dev, pctx_size); + if (!pctx) { + DRM_DEBUG("not enough stolen space for PCTX, disabling\n"); + return; + } + + pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start; + I915_WRITE(VLV_PCBR, pctx_paddr); + +out: + dev_priv->vlv_pctx = pctx; +} + +static void valleyview_enable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; + u32 gtfifodbg, val, rpe; + int i; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + if ((gtfifodbg = I915_READ(GTFIFODBG))) { + DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg); + I915_WRITE(GTFIFODBG, gtfifodbg); + } + + valleyview_setup_pctx(dev); + + gen6_gt_force_wake_get(dev_priv); + + 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); + + I915_WRITE(GEN6_RP_CONTROL, + GEN6_RP_MEDIA_TURBO | + GEN6_RP_MEDIA_HW_NORMAL_MODE | + GEN6_RP_MEDIA_IS_GFX | + GEN6_RP_ENABLE | + GEN6_RP_UP_BUSY_AVG | + GEN6_RP_DOWN_IDLE_CONT); + + I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 0x00280000); + I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); + I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); + + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); + + I915_WRITE(GEN6_RC6_THRESHOLD, 0xc350); + + /* allows RC6 residency counter to work */ + I915_WRITE(0x138104, _MASKED_BIT_ENABLE(0x3)); + I915_WRITE(GEN6_RC_CONTROL, + GEN7_RC_CTL_TO_MODE); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); + switch ((val >> 6) & 3) { + case 0: + case 1: + dev_priv->mem_freq = 800; + break; + case 2: + dev_priv->mem_freq = 1066; + break; + case 3: + dev_priv->mem_freq = 1333; + break; + } + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); + + DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); + DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); + + DRM_DEBUG_DRIVER("current GPU freq: %d\n", + vlv_gpu_freq(dev_priv->mem_freq, (val >> 8) & 0xff)); + dev_priv->rps.cur_delay = (val >> 8) & 0xff; + + dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv); + dev_priv->rps.hw_max = dev_priv->rps.max_delay; + DRM_DEBUG_DRIVER("max GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.max_delay)); + + rpe = valleyview_rps_rpe_freq(dev_priv); + DRM_DEBUG_DRIVER("RPe GPU freq: %d\n", + vlv_gpu_freq(dev_priv->mem_freq, rpe)); + dev_priv->rps.rpe_delay = rpe; + + val = valleyview_rps_min_freq(dev_priv); + DRM_DEBUG_DRIVER("min GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, + val)); + dev_priv->rps.min_delay = val; + + DRM_DEBUG_DRIVER("setting GPU freq to %d\n", + vlv_gpu_freq(dev_priv->mem_freq, rpe)); + + INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work); + + valleyview_set_rps(dev_priv->dev, rpe); + + /* requires MSI enabled */ + I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS); + spin_lock_irq(&dev_priv->rps.lock); + WARN_ON(dev_priv->rps.pm_iir != 0); + I915_WRITE(GEN6_PMIMR, 0); + spin_unlock_irq(&dev_priv->rps.lock); + /* enable all PM interrupts */ + I915_WRITE(GEN6_PMINTRMSK, 0); + + gen6_gt_force_wake_put(dev_priv); +} + void ironlake_teardown_rc6(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3465,13 +3787,22 @@ 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); + if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); ironlake_disable_rc6(dev); - } else if (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(dev)) { + } else if (INTEL_INFO(dev)->gen >= 6) { cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); + cancel_work_sync(&dev_priv->rps.work); + if (IS_VALLEYVIEW(dev)) + cancel_delayed_work_sync(&dev_priv->rps.vlv_work); mutex_lock(&dev_priv->rps.hw_lock); - gen6_disable_rps(dev); + if (IS_VALLEYVIEW(dev)) + valleyview_disable_rps(dev); + else + gen6_disable_rps(dev); mutex_unlock(&dev_priv->rps.hw_lock); } } @@ -3484,8 +3815,13 @@ static void intel_gen6_powersave_work(struct work_struct *work) struct drm_device *dev = dev_priv->dev; mutex_lock(&dev_priv->rps.hw_lock); - gen6_enable_rps(dev); - gen6_update_ring_freq(dev); + + if (IS_VALLEYVIEW(dev)) { + valleyview_enable_rps(dev); + } else { + gen6_enable_rps(dev); + gen6_update_ring_freq(dev); + } mutex_unlock(&dev_priv->rps.hw_lock); } @@ -3497,7 +3833,7 @@ void intel_enable_gt_powersave(struct drm_device *dev) ironlake_enable_drps(dev); ironlake_enable_rc6(dev); intel_init_emon(dev); - } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) { + } else if (IS_GEN6(dev) || IS_GEN7(dev)) { /* * PCU communication is slow and this doesn't need to be * done at any specific time, so do this out of our fast path @@ -3579,7 +3915,7 @@ static void ironlake_init_clock_gating(struct drm_device *dev) _3D_CHICKEN2_WM_READ_PIPELINED << 16 | _3D_CHICKEN2_WM_READ_PIPELINED); - /* WaDisableRenderCachePipelinedFlush */ + /* WaDisableRenderCachePipelinedFlush:ilk */ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); @@ -3607,7 +3943,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) val = I915_READ(TRANS_CHICKEN2(pipe)); val |= TRANS_CHICKEN2_TIMING_OVERRIDE; val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - if (dev_priv->fdi_rx_polarity_inverted) + if (dev_priv->vbt.fdi_rx_polarity_inverted) val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; @@ -3646,11 +3982,11 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_READ(ILK_DISPLAY_CHICKEN2) | ILK_ELPIN_409_SELECT); - /* WaDisableHiZPlanesWhenMSAAEnabled */ + /* WaDisableHiZPlanesWhenMSAAEnabled:snb */ I915_WRITE(_3D_CHICKEN, _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB)); - /* WaSetupGtModeTdRowDispatch */ + /* WaSetupGtModeTdRowDispatch:snb */ if (IS_SNB_GT1(dev)) I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE)); @@ -3677,8 +4013,8 @@ static void gen6_init_clock_gating(struct drm_device *dev) * According to the spec, bit 11 (RCCUNIT) must also be set, * but we didn't debug actual testcases to find it out. * - * Also apply WaDisableVDSUnitClockGating and - * WaDisableRCPBUnitClockGating. + * Also apply WaDisableVDSUnitClockGating:snb and + * WaDisableRCPBUnitClockGating:snb. */ I915_WRITE(GEN6_UCGCTL2, GEN7_VDSUNIT_CLOCK_GATE_DISABLE | @@ -3709,7 +4045,7 @@ static void gen6_init_clock_gating(struct drm_device *dev) ILK_DPARBUNIT_CLOCK_GATE_ENABLE | ILK_DPFDUNIT_CLOCK_GATE_ENABLE); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:snb */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); @@ -3739,7 +4075,6 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) reg |= GEN7_FF_VS_SCHED_HW; reg |= GEN7_FF_DS_SCHED_HW; - /* WaVSRefCountFullforceMissDisable */ if (IS_HASWELL(dev_priv->dev)) reg &= ~GEN7_FF_VS_REF_CNT_FFME; @@ -3758,6 +4093,23 @@ static void lpt_init_clock_gating(struct drm_device *dev) I915_WRITE(SOUTH_DSPCLK_GATE_D, I915_READ(SOUTH_DSPCLK_GATE_D) | PCH_LP_PARTITION_LEVEL_DISABLE); + + /* WADPOClockGatingDisable:hsw */ + I915_WRITE(_TRANSA_CHICKEN1, + I915_READ(_TRANSA_CHICKEN1) | + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); +} + +static void lpt_suspend_hw(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { + uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D); + + val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; + I915_WRITE(SOUTH_DSPCLK_GATE_D, val); + } } static void haswell_init_clock_gating(struct drm_device *dev) @@ -3770,21 +4122,21 @@ static void haswell_init_clock_gating(struct drm_device *dev) I915_WRITE(WM1_LP_ILK, 0); /* According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:hsw workaround. */ I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:hsw workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:hsw */ I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:hsw */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); @@ -3796,17 +4148,18 @@ static void haswell_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } + /* WaVSRefCountFullforceMissDisable:hsw */ gen7_setup_fixed_func_scheduler(dev_priv); - /* WaDisable4x2SubspanOptimization */ + /* WaDisable4x2SubspanOptimization:hsw */ I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:hsw */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); - /* WaSwitchSolVfFArbitrationPriority */ + /* WaSwitchSolVfFArbitrationPriority:hsw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); /* XXX: This is a workaround for early silicon revisions and should be @@ -3833,16 +4186,16 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); - /* WaDisableEarlyCull */ + /* WaDisableEarlyCull:ivb */ I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix */ + /* WaDisableBackToBackFlipFix:ivb */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaDisablePSDDualDispatchEnable */ + /* WaDisablePSDDualDispatchEnable:ivb */ if (IS_IVB_GT1(dev)) I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); @@ -3850,11 +4203,11 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN7_HALF_SLICE_CHICKEN1_GT2, _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:ivb */ I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, @@ -3867,7 +4220,7 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:ivb */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); @@ -3882,13 +4235,13 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) * but we didn't debug actual testcases to find it out. * * According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:ivb workaround. */ I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE | GEN6_RCCUNIT_CLOCK_GATE_DISABLE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:ivb */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); @@ -3900,13 +4253,14 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:ivb */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); + /* WaVSRefCountFullforceMissDisable:ivb */ gen7_setup_fixed_func_scheduler(dev_priv); - /* WaDisable4x2SubspanOptimization */ + /* WaDisable4x2SubspanOptimization:ivb */ I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); @@ -3932,46 +4286,46 @@ static void valleyview_init_clock_gating(struct drm_device *dev) I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); - /* WaDisableEarlyCull */ + /* WaDisableEarlyCull:vlv */ I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix */ + /* WaDisableBackToBackFlipFix:vlv */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaDisablePSDDualDispatchEnable */ + /* WaDisablePSDDualDispatchEnable:vlv */ I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP | GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:vlv workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:vlv */ I915_WRITE(GEN7_L3CNTLREG1, I915_READ(GEN7_L3CNTLREG1) | GEN7_L3AGDIS); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:vlv */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* WaDisableDopClockGating */ + /* WaDisableDopClockGating:vlv */ I915_WRITE(GEN7_ROW_CHICKEN2, _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:vlv */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:vlv */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:vlv */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); @@ -3987,10 +4341,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev) * but we didn't debug actual testcases to find it out. * * According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:vlv workaround. * - * Also apply WaDisableVDSUnitClockGating and - * WaDisableRCPBUnitClockGating. + * Also apply WaDisableVDSUnitClockGating:vlv and + * WaDisableRCPBUnitClockGating:vlv. */ I915_WRITE(GEN6_UCGCTL2, GEN7_VDSUNIT_CLOCK_GATE_DISABLE | @@ -4012,7 +4366,7 @@ static void valleyview_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); /* - * WaDisableVLVClockGating_VBIIssue + * WaDisableVLVClockGating_VBIIssue:vlv * Disable clock gating on th GCFG unit to prevent a delay * in the reporting of vblank events. */ @@ -4110,20 +4464,42 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_clock_gating(dev); } +void intel_suspend_hw(struct drm_device *dev) +{ + if (HAS_PCH_LPT(dev)) + lpt_suspend_hw(dev); +} + /** * We should only use the power well if we explicitly asked the hardware to * enable it, so check if it's enabled and also check if we've requested it to * be enabled. */ -bool intel_using_power_well(struct drm_device *dev) +bool intel_display_power_enabled(struct drm_device *dev, + enum intel_display_power_domain domain) { struct drm_i915_private *dev_priv = dev->dev_private; - if (IS_HASWELL(dev)) + if (!HAS_POWER_WELL(dev)) + return true; + + switch (domain) { + case POWER_DOMAIN_PIPE_A: + case POWER_DOMAIN_TRANSCODER_EDP: + return true; + case POWER_DOMAIN_PIPE_B: + case POWER_DOMAIN_PIPE_C: + case POWER_DOMAIN_PIPE_A_PANEL_FITTER: + case POWER_DOMAIN_PIPE_B_PANEL_FITTER: + case POWER_DOMAIN_PIPE_C_PANEL_FITTER: + case POWER_DOMAIN_TRANSCODER_A: + case POWER_DOMAIN_TRANSCODER_B: + case POWER_DOMAIN_TRANSCODER_C: return I915_READ(HSW_PWR_WELL_DRIVER) == (HSW_PWR_WELL_ENABLE | HSW_PWR_WELL_STATE); - else - return true; + default: + BUG(); + } } void intel_set_power_well(struct drm_device *dev, bool enable) @@ -4190,7 +4566,12 @@ void intel_init_pm(struct drm_device *dev) if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { dev_priv->display.fbc_enabled = ironlake_fbc_enabled; - dev_priv->display.enable_fbc = ironlake_enable_fbc; + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) + dev_priv->display.enable_fbc = + gen7_enable_fbc; + else + dev_priv->display.enable_fbc = + ironlake_enable_fbc; dev_priv->display.disable_fbc = ironlake_disable_fbc; } else if (IS_GM45(dev)) { dev_priv->display.fbc_enabled = g4x_fbc_enabled; @@ -4340,6 +4721,7 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); + /* WaRsForcewakeWaitTC0:snb */ __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4371,6 +4753,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); + /* WaRsForcewakeWaitTC0:ivb,hsw */ __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4474,6 +4857,7 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for media to ack forcewake request.\n"); + /* WaRsForcewakeWaitTC0:vlv */ __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4568,14 +4952,13 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } -static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, +static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, u8 addr, u32 *val) { - u32 cmd, devfn, port, be, bar; + u32 cmd, devfn, be, bar; bar = 0; be = 0xf; - port = IOSF_PORT_PUNIT; devfn = PCI_DEVFN(2, 0); cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | @@ -4597,7 +4980,7 @@ static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, - 500)) { + 5)) { DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", addr); @@ -4613,10 +4996,74 @@ static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { - return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_READ, addr, val); + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_READ, + addr, val); } int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { - return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val); + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_WRITE, + addr, &val); } + +int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +{ + return vlv_punit_rw(dev_priv, IOSF_PORT_NC, PUNIT_OPCODE_REG_READ, + addr, val); +} + +int vlv_gpu_freq(int ddr_freq, int val) +{ + int mult, base; + + switch (ddr_freq) { + case 800: + mult = 20; + base = 120; + break; + case 1066: + mult = 22; + base = 133; + break; + case 1333: + mult = 21; + base = 125; + break; + default: + return -1; + } + + return ((val - 0xbd) * mult) + base; +} + +int vlv_freq_opcode(int ddr_freq, int val) +{ + int mult, base; + + switch (ddr_freq) { + case 800: + mult = 20; + base = 120; + break; + case 1066: + mult = 22; + base = 133; + break; + case 1333: + mult = 21; + base = 125; + break; + default: + return -1; + } + + val /= mult; + val -= base / mult; + val += 0xbd; + + if (val > 0xea) + val = 0xea; + + return val; +} + diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 1d5d613eb6be..3d2c236e15ab 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -515,6 +515,8 @@ static int init_render_ring(struct intel_ring_buffer *ring) /* We need to disable the AsyncFlip performance optimisations in order * to use MI_WAIT_FOR_EVENT within the CS. It should already be * programmed to '1' on all products. + * + * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv */ if (INTEL_INFO(dev)->gen >= 6) I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE)); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index d66208c2c48b..dac1614a1bca 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -135,7 +135,7 @@ struct intel_ring_buffer { */ bool itlb_before_ctx_switch; struct i915_hw_context *default_context; - struct drm_i915_gem_object *last_context_obj; + struct i915_hw_context *last_context; void *private; }; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index d15428404b9a..78f0631b1c43 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1041,6 +1041,32 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo, return true; } +static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_config *pipe_config) +{ + unsigned dotclock = pipe_config->adjusted_mode.clock; + struct dpll *clock = &pipe_config->dpll; + + /* SDVO TV has fixed PLL values depend on its clock range, + this mirrors vbios setting. */ + if (dotclock >= 100000 && dotclock < 140500) { + clock->p1 = 2; + clock->p2 = 10; + clock->n = 3; + clock->m1 = 16; + clock->m2 = 8; + } else if (dotclock >= 140500 && dotclock <= 200000) { + clock->p1 = 1; + clock->p2 = 10; + clock->n = 6; + clock->m1 = 12; + clock->m2 = 8; + } else { + WARN(1, "SDVO TV clock out of range: %i\n", dotclock); + } + + pipe_config->clock_set = true; +} + static bool intel_sdvo_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { @@ -1066,6 +1092,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, (void) intel_sdvo_get_preferred_input_mode(intel_sdvo, mode, adjusted_mode); + pipe_config->sdvo_tv_clock = true; } else if (intel_sdvo->is_lvds) { if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, intel_sdvo->sdvo_lvds_fixed_mode)) @@ -1097,6 +1124,10 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, if (intel_sdvo->color_range) pipe_config->limited_color_range = true; + /* Clock computation needs to happen after pixel multiplier. */ + if (intel_sdvo->is_tv) + i9xx_adjust_sdvo_tv_clock(pipe_config); + return true; } @@ -1495,7 +1526,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector) return drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, - dev_priv->crt_ddc_pin)); + dev_priv->vbt.crt_ddc_pin)); } static enum drm_connector_status @@ -1625,12 +1656,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if (ret == connector_status_connected) { intel_sdvo->is_tv = false; intel_sdvo->is_lvds = false; - intel_sdvo->base.needs_tv_clock = false; - if (response & SDVO_TV_MASK) { + if (response & SDVO_TV_MASK) intel_sdvo->is_tv = true; - intel_sdvo->base.needs_tv_clock = true; - } if (response & SDVO_LVDS_MASK) intel_sdvo->is_lvds = intel_sdvo->sdvo_lvds_fixed_mode != NULL; } @@ -1781,9 +1809,9 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) goto end; /* Fetch modes from VBT */ - if (dev_priv->sdvo_lvds_vbt_mode != NULL) { + if (dev_priv->vbt.sdvo_lvds_vbt_mode != NULL) { newmode = drm_mode_duplicate(connector->dev, - dev_priv->sdvo_lvds_vbt_mode); + dev_priv->vbt.sdvo_lvds_vbt_mode); if (newmode != NULL) { /* Guarantee the mode is preferred */ newmode->type = (DRM_MODE_TYPE_PREFERRED | @@ -2327,7 +2355,6 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) intel_sdvo_connector->output_flag = type; intel_sdvo->is_tv = true; - intel_sdvo->base.needs_tv_clock = true; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); @@ -2415,7 +2442,6 @@ static bool intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags) { intel_sdvo->is_tv = false; - intel_sdvo->base.needs_tv_clock = false; intel_sdvo->is_lvds = false; /* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c7d25c5dd4e6..19b9cb961b5a 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "intel_drv.h" #include #include "i915_drv.h" @@ -583,6 +584,20 @@ ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; } +static bool +format_is_yuv(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YVYU: + return true; + default: + return false; + } +} + static int intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -600,9 +615,29 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); int ret = 0; - int x = src_x >> 16, y = src_y >> 16; - int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay; bool disable_primary = false; + bool visible; + int hscale, vscale; + int max_scale, min_scale; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + struct drm_rect src = { + /* sample coordinates in 16.16 fixed point */ + .x1 = src_x, + .x2 = src_x + src_w, + .y1 = src_y, + .y2 = src_y + src_h, + }; + struct drm_rect dst = { + /* integer pixels */ + .x1 = crtc_x, + .x2 = crtc_x + crtc_w, + .y1 = crtc_y, + .y2 = crtc_y + crtc_h, + }; + const struct drm_rect clip = { + .x2 = crtc->mode.hdisplay, + .y2 = crtc->mode.vdisplay, + }; intel_fb = to_intel_framebuffer(fb); obj = intel_fb->obj; @@ -618,19 +653,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, intel_plane->src_w = src_w; intel_plane->src_h = src_h; - src_w = src_w >> 16; - src_h = src_h >> 16; - /* Pipe must be running... */ - if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) - return -EINVAL; - - if (crtc_x >= primary_w || crtc_y >= primary_h) + if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) { + DRM_DEBUG_KMS("Pipe disabled\n"); return -EINVAL; + } /* Don't modify another pipe's plane */ - if (intel_plane->pipe != intel_crtc->pipe) + if (intel_plane->pipe != intel_crtc->pipe) { + DRM_DEBUG_KMS("Wrong plane <-> crtc mapping\n"); return -EINVAL; + } + + /* FIXME check all gen limits */ + if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) { + DRM_DEBUG_KMS("Unsuitable framebuffer for plane\n"); + return -EINVAL; + } /* Sprite planes can be linear or x-tiled surfaces */ switch (obj->tiling_mode) { @@ -638,55 +677,123 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, case I915_TILING_X: break; default: + DRM_DEBUG_KMS("Unsupported tiling mode\n"); return -EINVAL; } /* - * Clamp the width & height into the visible area. Note we don't - * try to scale the source if part of the visible region is offscreen. - * The caller must handle that by adjusting source offset and size. + * FIXME the following code does a bunch of fuzzy adjustments to the + * coordinates and sizes. We probably need some way to decide whether + * more strict checking should be done instead. */ - if ((crtc_x < 0) && ((crtc_x + crtc_w) > 0)) { - crtc_w += crtc_x; - crtc_x = 0; + max_scale = intel_plane->max_downscale << 16; + min_scale = intel_plane->can_scale ? 1 : (1 << 16); + + hscale = drm_rect_calc_hscale_relaxed(&src, &dst, min_scale, max_scale); + BUG_ON(hscale < 0); + + vscale = drm_rect_calc_vscale_relaxed(&src, &dst, min_scale, max_scale); + BUG_ON(vscale < 0); + + visible = drm_rect_clip_scaled(&src, &dst, &clip, hscale, vscale); + + crtc_x = dst.x1; + crtc_y = dst.y1; + crtc_w = drm_rect_width(&dst); + crtc_h = drm_rect_height(&dst); + + if (visible) { + /* check again in case clipping clamped the results */ + hscale = drm_rect_calc_hscale(&src, &dst, min_scale, max_scale); + if (hscale < 0) { + DRM_DEBUG_KMS("Horizontal scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return hscale; + } + + vscale = drm_rect_calc_vscale(&src, &dst, min_scale, max_scale); + if (vscale < 0) { + DRM_DEBUG_KMS("Vertical scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return vscale; + } + + /* Make the source viewport size an exact multiple of the scaling factors. */ + drm_rect_adjust_size(&src, + drm_rect_width(&dst) * hscale - drm_rect_width(&src), + drm_rect_height(&dst) * vscale - drm_rect_height(&src)); + + /* sanity check to make sure the src viewport wasn't enlarged */ + WARN_ON(src.x1 < (int) src_x || + src.y1 < (int) src_y || + src.x2 > (int) (src_x + src_w) || + src.y2 > (int) (src_y + src_h)); + + /* + * Hardware doesn't handle subpixel coordinates. + * Adjust to (macro)pixel boundary, but be careful not to + * increase the source viewport size, because that could + * push the downscaling factor out of bounds. + */ + src_x = src.x1 >> 16; + src_w = drm_rect_width(&src) >> 16; + src_y = src.y1 >> 16; + src_h = drm_rect_height(&src) >> 16; + + if (format_is_yuv(fb->pixel_format)) { + src_x &= ~1; + src_w &= ~1; + + /* + * Must keep src and dst the + * same if we can't scale. + */ + if (!intel_plane->can_scale) + crtc_w &= ~1; + + if (crtc_w == 0) + visible = false; + } } - if ((crtc_x + crtc_w) <= 0) /* Nothing to display */ - goto out; - if ((crtc_x + crtc_w) > primary_w) - crtc_w = primary_w - crtc_x; - if ((crtc_y < 0) && ((crtc_y + crtc_h) > 0)) { - crtc_h += crtc_y; - crtc_y = 0; + /* Check size restrictions when scaling */ + if (visible && (src_w != crtc_w || src_h != crtc_h)) { + unsigned int width_bytes; + + WARN_ON(!intel_plane->can_scale); + + /* FIXME interlacing min height is 6 */ + + if (crtc_w < 3 || crtc_h < 3) + visible = false; + + if (src_w < 3 || src_h < 3) + visible = false; + + width_bytes = ((src_x * pixel_size) & 63) + src_w * pixel_size; + + if (src_w > 2048 || src_h > 2048 || + width_bytes > 4096 || fb->pitches[0] > 4096) { + DRM_DEBUG_KMS("Source dimensions exceed hardware limits\n"); + return -EINVAL; + } } - if ((crtc_y + crtc_h) <= 0) /* Nothing to display */ - goto out; - if (crtc_y + crtc_h > primary_h) - crtc_h = primary_h - crtc_y; - if (!crtc_w || !crtc_h) /* Again, nothing to display */ - goto out; - - /* - * We may not have a scaler, eg. HSW does not have it any more - */ - if (!intel_plane->can_scale && (crtc_w != src_w || crtc_h != src_h)) - return -EINVAL; - - /* - * We can take a larger source and scale it down, but - * only so much... 16x is the max on SNB. - */ - if (((src_w * src_h) / (crtc_w * crtc_h)) > intel_plane->max_downscale) - return -EINVAL; + dst.x1 = crtc_x; + dst.x2 = crtc_x + crtc_w; + dst.y1 = crtc_y; + dst.y2 = crtc_y + crtc_h; /* * If the sprite is completely covering the primary plane, * we can disable the primary and save power. */ - if ((crtc_x == 0) && (crtc_y == 0) && - (crtc_w == primary_w) && (crtc_h == primary_h)) - disable_primary = true; + disable_primary = drm_rect_equals(&dst, &clip); + WARN_ON(disable_primary && !visible); mutex_lock(&dev->struct_mutex); @@ -708,8 +815,12 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (!disable_primary) intel_enable_primary(crtc); - intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y, - crtc_w, crtc_h, x, y, src_w, src_h); + if (visible) + intel_plane->update_plane(plane, fb, obj, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + else + intel_plane->disable_plane(plane); if (disable_primary) intel_disable_primary(crtc); @@ -732,7 +843,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, out_unlock: mutex_unlock(&dev->struct_mutex); -out: return ret; } @@ -918,13 +1028,15 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) break; case 7: - if (IS_HASWELL(dev) || IS_VALLEYVIEW(dev)) - intel_plane->can_scale = false; - else + if (IS_IVYBRIDGE(dev)) { intel_plane->can_scale = true; + intel_plane->max_downscale = 2; + } else { + intel_plane->can_scale = false; + intel_plane->max_downscale = 1; + } if (IS_VALLEYVIEW(dev)) { - intel_plane->max_downscale = 1; intel_plane->update_plane = vlv_update_plane; intel_plane->disable_plane = vlv_disable_plane; intel_plane->update_colorkey = vlv_update_colorkey; @@ -933,7 +1045,6 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) plane_formats = vlv_plane_formats; num_plane_formats = ARRAY_SIZE(vlv_plane_formats); } else { - intel_plane->max_downscale = 2; intel_plane->update_plane = ivb_update_plane; intel_plane->disable_plane = ivb_disable_plane; intel_plane->update_colorkey = ivb_update_colorkey; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index b945bc54207a..7d11a5adc985 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1521,12 +1521,12 @@ static int tv_is_present_in_vbt(struct drm_device *dev) struct child_device_config *p_child; int i, ret; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return 1; ret = 0; - for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + p_child = dev_priv->vbt.child_dev + i; /* * If the device type is not TV, continue. */ @@ -1564,7 +1564,7 @@ intel_tv_init(struct drm_device *dev) return; } /* Even if we have an encoder we may not have a connector */ - if (!dev_priv->int_tv_support) + if (!dev_priv->vbt.int_tv_support) return; /* diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h new file mode 100644 index 000000000000..64fa265c6ffb --- /dev/null +++ b/include/drm/drm_rect.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2011-2013 Intel Corporation + * + * 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. + */ + +#ifndef DRM_RECT_H +#define DRM_RECT_H + +/** + * drm_rect - two dimensional rectangle + * @x1: horizontal starting coordinate (inclusive) + * @x2: horizontal ending coordinate (exclusive) + * @y1: vertical starting coordinate (inclusive) + * @y2: vertical ending coordinate (exclusive) + */ +struct drm_rect { + int x1, y1, x2, y2; +}; + +/** + * drm_rect_adjust_size - adjust the size of the rectangle + * @r: rectangle to be adjusted + * @dw: horizontal adjustment + * @dh: vertical adjustment + * + * Change the size of rectangle @r by @dw in the horizontal direction, + * and by @dh in the vertical direction, while keeping the center + * of @r stationary. + * + * Positive @dw and @dh increase the size, negative values decrease it. + */ +static inline void drm_rect_adjust_size(struct drm_rect *r, int dw, int dh) +{ + r->x1 -= dw >> 1; + r->y1 -= dh >> 1; + r->x2 += (dw + 1) >> 1; + r->y2 += (dh + 1) >> 1; +} + +/** + * drm_rect_translate - translate the rectangle + * @r: rectangle to be tranlated + * @dx: horizontal translation + * @dy: vertical translation + * + * Move rectangle @r by @dx in the horizontal direction, + * and by @dy in the vertical direction. + */ +static inline void drm_rect_translate(struct drm_rect *r, int dx, int dy) +{ + r->x1 += dx; + r->y1 += dy; + r->x2 += dx; + r->y2 += dy; +} + +/** + * drm_rect_downscale - downscale a rectangle + * @r: rectangle to be downscaled + * @horz: horizontal downscale factor + * @vert: vertical downscale factor + * + * Divide the coordinates of rectangle @r by @horz and @vert. + */ +static inline void drm_rect_downscale(struct drm_rect *r, int horz, int vert) +{ + r->x1 /= horz; + r->y1 /= vert; + r->x2 /= horz; + r->y2 /= vert; +} + +/** + * drm_rect_width - determine the rectangle width + * @r: rectangle whose width is returned + * + * RETURNS: + * The width of the rectangle. + */ +static inline int drm_rect_width(const struct drm_rect *r) +{ + return r->x2 - r->x1; +} + +/** + * drm_rect_height - determine the rectangle height + * @r: rectangle whose height is returned + * + * RETURNS: + * The height of the rectangle. + */ +static inline int drm_rect_height(const struct drm_rect *r) +{ + return r->y2 - r->y1; +} + +/** + * drm_rect_visible - determine if the the rectangle is visible + * @r: rectangle whose visibility is returned + * + * RETURNS: + * %true if the rectangle is visible, %false otherwise. + */ +static inline bool drm_rect_visible(const struct drm_rect *r) +{ + return drm_rect_width(r) > 0 && drm_rect_height(r) > 0; +} + +/** + * drm_rect_equals - determine if two rectangles are equal + * @r1: first rectangle + * @r2: second rectangle + * + * RETURNS: + * %true if the rectangles are equal, %false otherwise. + */ +static inline bool drm_rect_equals(const struct drm_rect *r1, + const struct drm_rect *r2) +{ + return r1->x1 == r2->x1 && r1->x2 == r2->x2 && + r1->y1 == r2->y1 && r1->y2 == r2->y2; +} + +bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); +bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, + const struct drm_rect *clip, + int hscale, int vscale); +int drm_rect_calc_hscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_hscale, int max_hscale); +int drm_rect_calc_vscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_vscale, int max_vscale); +int drm_rect_calc_hscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_hscale, int max_hscale); +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); + +#endif