diff options
author | Dave Airlie <airlied@redhat.com> | 2014-09-16 10:02:09 +0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2014-09-16 10:02:09 +0400 |
commit | 40d201af0b9e6196a210b97d3b2493b1156564f6 (patch) | |
tree | 687b212fd0d91f648551967aba12ce7dc8de5560 /drivers/gpu/drm/i915 | |
parent | 29a7d1795a0376beee6c0f7515fae3789277e03e (diff) | |
parent | a12624959ad4e3bfa8c344ad71728ffc9a379158 (diff) | |
download | linux-40d201af0b9e6196a210b97d3b2493b1156564f6.tar.xz |
Merge tag 'drm-intel-next-2014-09-05' of git://anongit.freedesktop.org/drm-intel into drm-next
- final bits (again) for the rotation support (Sonika Jindal)
- support bl_power in the intel backlight (Jani)
- vdd handling improvements from Ville
- i830M fixes from Ville
- piles of prep work all over to make skl enabling just plug in (Damien, Sonika)
- rename DP training defines to reflect latest edp standards, this touches all
drm drivers supporting DP (Sonika Jindal)
- cache edids during single detect cycle to avoid re-reading it for e.g. audio,
from Chris
- move w/a for registers which are stored in the hw context to the context init
code (Arun&Damien)
- edp panel power sequencer fixes, helps chv a lot (Ville)
- piles of other chv fixes all over
- much more paranoid pageflip handling with stall detection and better recovery
from Chris
- small things all over, as usual
* tag 'drm-intel-next-2014-09-05' of git://anongit.freedesktop.org/drm-intel: (114 commits)
drm/i915: Update DRIVER_DATE to 20140905
drm/i915: Decouple the stuck pageflip on modeset
drm/i915: Check for a stalled page flip after each vblank
drm/i915: Introduce a for_each_plane() macro
drm/i915: Rewrite ABS_DIFF() in a safer manner
drm/i915: Add comments explaining the vdd on/off functions
drm/i915: Move DP port disable to post_disable for pch platforms
drm/i915: Enable DP port earlier
drm/i915: Turn on panel power before doing aux transfers
drm/i915: Be more careful when picking the initial power sequencer pipe
drm/i915: Reset power sequencer pipe tracking when disp2d is off
drm/i915: Track which port is using which pipe's power sequencer
drm/i915: Fix edp vdd locking
drm/i915: Reset the HEAD pointer for the ring after writing START
drm/i915: Fix unsafe vma iteration in i915_drop_caches
drm/i915: init sprites with univeral plane init function
drm/i915: Check of !HAS_PCH_SPLIT() in PCH transcoder funcs
drm/i915: Use HAS_GMCH_DISPLAY un underrun reporting code
drm/i915: Use IS_BROADWELL() instead of IS_GEN8() in forcewake code
drm/i915: Don't call gen8_fbc_sw_flush() on chv
...
Diffstat (limited to 'drivers/gpu/drm/i915')
33 files changed, 2886 insertions, 1463 deletions
diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index 74f2af7c2d3e..441630434d34 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -60,16 +60,297 @@ #define NS2501_REGC 0x0c +enum { + MODE_640x480, + MODE_800x600, + MODE_1024x768, +}; + +struct ns2501_reg { + uint8_t offset; + uint8_t value; +}; + +/* + * Magic values based on what the BIOS on + * Fujitsu-Siemens Lifebook S6010 programs (1024x768 panel). + */ +static const struct ns2501_reg regs_1024x768[][86] = { + [MODE_640x480] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x11, }, + [5] = { .offset = 0x1c, .value = 0x54, }, + [6] = { .offset = 0x1d, .value = 0x03, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0x90, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0x0f, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x16, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x02, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0xff, }, + [20] = { .offset = 0x81, .value = 0x07, }, + [21] = { .offset = 0x82, .value = 0x3d, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x0a, }, + [33] = { .offset = 0x9c, .value = 0x24, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x00, }, + [43] = { .offset = 0xb9, .value = 0xa0, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x00, }, + [47] = { .offset = 0x11, .value = 0xa0, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x10, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x04, }, + [67] = { .offset = 0xaa, .value = 0x70, }, + [68] = { .offset = 0xab, .value = 0x4f, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x84, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x05, }, + }, + [MODE_800x600] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x19, }, + [5] = { .offset = 0x1c, .value = 0x64, }, + [6] = { .offset = 0x1d, .value = 0x02, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0xd7, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0xf8, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x1a, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x73, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0x27, }, + [20] = { .offset = 0x81, .value = 0x03, }, + [21] = { .offset = 0x82, .value = 0x41, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x06, }, + [33] = { .offset = 0x9c, .value = 0x23, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x30, }, + [43] = { .offset = 0xb9, .value = 0xc8, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x20, }, + [47] = { .offset = 0x11, .value = 0xc8, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x04, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x83, }, + [67] = { .offset = 0xaa, .value = 0x40, }, + [68] = { .offset = 0xab, .value = 0x32, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x80, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x07, }, + }, + [MODE_1024x768] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x11, }, + [5] = { .offset = 0x1c, .value = 0x54, }, + [6] = { .offset = 0x1d, .value = 0x03, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0x90, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0x0f, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x16, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x02, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0xff, }, + [20] = { .offset = 0x81, .value = 0x07, }, + [21] = { .offset = 0x82, .value = 0x3d, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x0a, }, + [33] = { .offset = 0x9c, .value = 0x24, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x00, }, + [43] = { .offset = 0xb9, .value = 0xa0, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x00, }, + [47] = { .offset = 0x11, .value = 0xa0, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x10, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x04, }, + [67] = { .offset = 0xaa, .value = 0x70, }, + [68] = { .offset = 0xab, .value = 0x4f, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x84, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x34, }, /* 0x35 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x01, }, + }, +}; + +static const struct ns2501_reg regs_init[] = { + [0] = { .offset = 0x35, .value = 0xff, }, + [1] = { .offset = 0x34, .value = 0x00, }, + [2] = { .offset = 0x08, .value = 0x30, }, +}; + struct ns2501_priv { - //I2CDevRec d; bool quiet; - int reg_8_shadow; - int reg_8_set; - // Shadow registers for i915 - int dvoc; - int pll_a; - int srcdim; - int fw_blc; + const struct ns2501_reg *regs; }; #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr)) @@ -205,11 +486,9 @@ static bool ns2501_init(struct intel_dvo_device *dvo, goto out; } ns->quiet = false; - ns->reg_8_set = 0; - ns->reg_8_shadow = - NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN; DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n"); + return true; out: @@ -242,9 +521,9 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo, * of the panel in here so we could always accept it * by disabling the scaler. */ - if ((mode->hdisplay == 800 && mode->vdisplay == 600) || - (mode->hdisplay == 640 && mode->vdisplay == 480) || - (mode->hdisplay == 1024 && mode->vdisplay == 768)) { + if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) || + (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) || + (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) { return MODE_OK; } else { return MODE_ONE_SIZE; /* Is this a reasonable error? */ @@ -255,180 +534,30 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - bool ok; - int retries = 10; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + int mode_idx, i; DRM_DEBUG_KMS ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n", mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal); - /* - * Where do I find the native resolution for which scaling is not required??? - * - * First trigger the DVO on as otherwise the chip does not appear on the i2c - * bus. - */ - do { - ok = true; - - if (mode->hdisplay == 800 && mode->vdisplay == 600) { - /* mode 277 */ - ns->reg_8_shadow &= ~NS2501_8_BPAS; - DRM_DEBUG_KMS("switching to 800x600\n"); - - /* - * No, I do not know where this data comes from. - * It is just what the video bios left in the DVO, so - * I'm just copying it here over. - * This also means that I cannot support any other modes - * except the ones supported by the bios. - */ - ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works. - ok &= ns2501_writeb(dvo, 0x1b, 0x19); - ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer - ok &= ns2501_writeb(dvo, 0x1d, 0x02); - - ok &= ns2501_writeb(dvo, 0x34, 0x03); - ok &= ns2501_writeb(dvo, 0x35, 0xff); + if (mode->hdisplay == 640 && mode->vdisplay == 480) + mode_idx = MODE_640x480; + else if (mode->hdisplay == 800 && mode->vdisplay == 600) + mode_idx = MODE_800x600; + else if (mode->hdisplay == 1024 && mode->vdisplay == 768) + mode_idx = MODE_1024x768; + else + return; - ok &= ns2501_writeb(dvo, 0x80, 0x27); - ok &= ns2501_writeb(dvo, 0x81, 0x03); - ok &= ns2501_writeb(dvo, 0x82, 0x41); - ok &= ns2501_writeb(dvo, 0x83, 0x05); + /* Hopefully doing it every time won't hurt... */ + for (i = 0; i < ARRAY_SIZE(regs_init); i++) + ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value); - ok &= ns2501_writeb(dvo, 0x8d, 0x02); - ok &= ns2501_writeb(dvo, 0x8e, 0x04); - ok &= ns2501_writeb(dvo, 0x8f, 0x00); + ns->regs = regs_1024x768[mode_idx]; - ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */ - ok &= ns2501_writeb(dvo, 0x91, 0x07); - ok &= ns2501_writeb(dvo, 0x94, 0x00); - ok &= ns2501_writeb(dvo, 0x95, 0x00); - - ok &= ns2501_writeb(dvo, 0x96, 0x00); - - ok &= ns2501_writeb(dvo, 0x99, 0x00); - ok &= ns2501_writeb(dvo, 0x9a, 0x88); - - ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */ - ok &= ns2501_writeb(dvo, 0x9d, 0x00); - ok &= ns2501_writeb(dvo, 0x9e, 0x25); - ok &= ns2501_writeb(dvo, 0x9f, 0x03); - - ok &= ns2501_writeb(dvo, 0xa4, 0x80); - - ok &= ns2501_writeb(dvo, 0xb6, 0x00); - - ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ - - ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xc1, 0xd7); - - ok &= ns2501_writeb(dvo, 0xc2, 0x00); - ok &= ns2501_writeb(dvo, 0xc3, 0xf8); - - ok &= ns2501_writeb(dvo, 0xc4, 0x03); - ok &= ns2501_writeb(dvo, 0xc5, 0x1a); - - ok &= ns2501_writeb(dvo, 0xc6, 0x00); - ok &= ns2501_writeb(dvo, 0xc7, 0x73); - ok &= ns2501_writeb(dvo, 0xc8, 0x02); - - } else if (mode->hdisplay == 640 && mode->vdisplay == 480) { - /* mode 274 */ - DRM_DEBUG_KMS("switching to 640x480\n"); - /* - * No, I do not know where this data comes from. - * It is just what the video bios left in the DVO, so - * I'm just copying it here over. - * This also means that I cannot support any other modes - * except the ones supported by the bios. - */ - ns->reg_8_shadow &= ~NS2501_8_BPAS; - - ok &= ns2501_writeb(dvo, 0x11, 0xa0); - ok &= ns2501_writeb(dvo, 0x1b, 0x11); - ok &= ns2501_writeb(dvo, 0x1c, 0x54); - ok &= ns2501_writeb(dvo, 0x1d, 0x03); - - ok &= ns2501_writeb(dvo, 0x34, 0x03); - ok &= ns2501_writeb(dvo, 0x35, 0xff); - - ok &= ns2501_writeb(dvo, 0x80, 0xff); - ok &= ns2501_writeb(dvo, 0x81, 0x07); - ok &= ns2501_writeb(dvo, 0x82, 0x3d); - ok &= ns2501_writeb(dvo, 0x83, 0x05); - - ok &= ns2501_writeb(dvo, 0x8d, 0x02); - ok &= ns2501_writeb(dvo, 0x8e, 0x10); - ok &= ns2501_writeb(dvo, 0x8f, 0x00); - - ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */ - ok &= ns2501_writeb(dvo, 0x91, 0x07); - ok &= ns2501_writeb(dvo, 0x94, 0x00); - ok &= ns2501_writeb(dvo, 0x95, 0x00); - - ok &= ns2501_writeb(dvo, 0x96, 0x05); - - ok &= ns2501_writeb(dvo, 0x99, 0x00); - ok &= ns2501_writeb(dvo, 0x9a, 0x88); - - ok &= ns2501_writeb(dvo, 0x9c, 0x24); - ok &= ns2501_writeb(dvo, 0x9d, 0x00); - ok &= ns2501_writeb(dvo, 0x9e, 0x25); - ok &= ns2501_writeb(dvo, 0x9f, 0x03); - - ok &= ns2501_writeb(dvo, 0xa4, 0x84); - - ok &= ns2501_writeb(dvo, 0xb6, 0x09); - - ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ - - ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xc1, 0x90); - - ok &= ns2501_writeb(dvo, 0xc2, 0x00); - ok &= ns2501_writeb(dvo, 0xc3, 0x0f); - - ok &= ns2501_writeb(dvo, 0xc4, 0x03); - ok &= ns2501_writeb(dvo, 0xc5, 0x16); - - ok &= ns2501_writeb(dvo, 0xc6, 0x00); - ok &= ns2501_writeb(dvo, 0xc7, 0x02); - ok &= ns2501_writeb(dvo, 0xc8, 0x02); - - } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) { - /* mode 280 */ - DRM_DEBUG_KMS("switching to 1024x768\n"); - /* - * This might or might not work, actually. I'm silently - * assuming here that the native panel resolution is - * 1024x768. If not, then this leaves the scaler disabled - * generating a picture that is likely not the expected. - * - * Problem is that I do not know where to take the panel - * dimensions from. - * - * Enable the bypass, scaling not required. - * - * The scaler registers are irrelevant here.... - * - */ - ns->reg_8_shadow |= NS2501_8_BPAS; - ok &= ns2501_writeb(dvo, 0x37, 0x44); - } else { - /* - * Data not known. Bummer! - * Hopefully, the code should not go here - * as mode_OK delivered no other modes. - */ - ns->reg_8_shadow |= NS2501_8_BPAS; - } - ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow); - } while (!ok && retries--); + for (i = 0; i < 84; i++) + ns2501_writeb(dvo, ns->regs[i].offset, ns->regs[i].value); } /* set the NS2501 power state */ @@ -439,60 +568,46 @@ static bool ns2501_get_hw_state(struct intel_dvo_device *dvo) if (!ns2501_readb(dvo, NS2501_REG8, &ch)) return false; - if (ch & NS2501_8_PD) - return true; - else - return false; + return ch & NS2501_8_PD; } /* set the NS2501 power state */ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { - bool ok; - int retries = 10; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); - unsigned char ch; DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable); - ch = ns->reg_8_shadow; + if (enable) { + if (WARN_ON(ns->regs[83].offset != 0x08 || + ns->regs[84].offset != 0x41 || + ns->regs[85].offset != 0xc0)) + return; - if (enable) - ch |= NS2501_8_PD; - else - ch &= ~NS2501_8_PD; - - if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) { - ns->reg_8_set = 1; - ns->reg_8_shadow = ch; - - do { - ok = true; - ok &= ns2501_writeb(dvo, NS2501_REG8, ch); - ok &= - ns2501_writeb(dvo, 0x34, - enable ? 0x03 : 0x00); - ok &= - ns2501_writeb(dvo, 0x35, - enable ? 0xff : 0x00); - } while (!ok && retries--); - } -} + ns2501_writeb(dvo, 0xc0, ns->regs[85].value | 0x08); -static void ns2501_dump_regs(struct intel_dvo_device *dvo) -{ - uint8_t val; - - ns2501_readb(dvo, NS2501_FREQ_LO, &val); - DRM_DEBUG_KMS("NS2501_FREQ_LO: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_FREQ_HI, &val); - DRM_DEBUG_KMS("NS2501_FREQ_HI: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REG8, &val); - DRM_DEBUG_KMS("NS2501_REG8: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REG9, &val); - DRM_DEBUG_KMS("NS2501_REG9: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REGC, &val); - DRM_DEBUG_KMS("NS2501_REGC: 0x%02x\n", val); + ns2501_writeb(dvo, 0x41, ns->regs[84].value); + + ns2501_writeb(dvo, 0x34, 0x01); + msleep(15); + + ns2501_writeb(dvo, 0x08, 0x35); + if (!(ns->regs[83].value & NS2501_8_BPAS)) + ns2501_writeb(dvo, 0x08, 0x31); + msleep(200); + + ns2501_writeb(dvo, 0x34, 0x03); + + ns2501_writeb(dvo, 0xc0, ns->regs[85].value); + } else { + ns2501_writeb(dvo, 0x34, 0x01); + msleep(200); + + ns2501_writeb(dvo, 0x08, 0x34); + msleep(15); + + ns2501_writeb(dvo, 0x34, 0x00); + } } static void ns2501_destroy(struct intel_dvo_device *dvo) @@ -512,6 +627,5 @@ struct intel_dvo_dev_ops ns2501_ops = { .mode_set = ns2501_mode_set, .dpms = ns2501_dpms, .get_hw_state = ns2501_get_hw_state, - .dump_regs = ns2501_dump_regs, .destroy = ns2501_destroy, }; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 6c82bdaa0822..2cbc85f3b237 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -136,7 +136,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->last_read_seqno, obj->last_write_seqno, obj->last_fenced_seqno, - i915_cache_level_str(obj->cache_level), + i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level), obj->dirty ? " dirty" : "", obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) @@ -515,6 +515,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; unsigned long flags; struct intel_crtc *crtc; int ret; @@ -534,6 +535,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "No flip due on pipe %c (plane %c)\n", pipe, plane); } else { + u32 addr; + if (atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) { seq_printf(m, "Flip queued on pipe %c (plane %c)\n", pipe, plane); @@ -541,23 +544,35 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "Flip pending (waiting for vsync) on pipe %c (plane %c)\n", pipe, plane); } + if (work->flip_queued_ring) { + seq_printf(m, "Flip queued on %s at seqno %u, next seqno %u [current breadcrumb %u], completed? %d\n", + work->flip_queued_ring->name, + work->flip_queued_seqno, + dev_priv->next_seqno, + work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + work->flip_queued_seqno)); + } else + seq_printf(m, "Flip not associated with any ring\n"); + seq_printf(m, "Flip queued on frame %d, (was ready on frame %d), now %d\n", + work->flip_queued_vblank, + work->flip_ready_vblank, + drm_vblank_count(dev, crtc->pipe)); if (work->enable_stall_check) seq_puts(m, "Stall check enabled, "); else seq_puts(m, "Stall check waiting for page flip ioctl, "); seq_printf(m, "%d prepares\n", atomic_read(&work->pending)); - if (work->old_fb_obj) { - struct drm_i915_gem_object *obj = work->old_fb_obj; - if (obj) - seq_printf(m, "Old framebuffer gtt_offset 0x%08lx\n", - i915_gem_obj_ggtt_offset(obj)); - } + if (INTEL_INFO(dev)->gen >= 4) + addr = I915_HI_DISPBASE(I915_READ(DSPSURF(crtc->plane))); + else + addr = I915_READ(DSPADDR(crtc->plane)); + seq_printf(m, "Current scanout address 0x%08x\n", addr); + if (work->pending_flip_obj) { - struct drm_i915_gem_object *obj = work->pending_flip_obj; - if (obj) - seq_printf(m, "New framebuffer gtt_offset 0x%08lx\n", - i915_gem_obj_ggtt_offset(obj)); + seq_printf(m, "New framebuffer address 0x%08lx\n", (long)work->gtt_offset); + seq_printf(m, "MMIO update completed? %d\n", addr == work->gtt_offset); } } spin_unlock_irqrestore(&dev->event_lock, flags); @@ -650,7 +665,6 @@ static int i915_interrupt_info(struct seq_file *m, void *data) intel_runtime_pm_get(dev_priv); if (IS_CHERRYVIEW(dev)) { - int i; seq_printf(m, "Master Interrupt Control:\t%08x\n", I915_READ(GEN8_MASTER_IRQ)); @@ -662,7 +676,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(VLV_IIR_RW)); seq_printf(m, "Display IMR:\t%08x\n", I915_READ(VLV_IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat:\t%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -702,7 +716,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) i, I915_READ(GEN8_GT_IER(i))); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) { seq_printf(m, "Pipe %c power disabled\n", @@ -749,7 +763,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(VLV_IIR_RW)); seq_printf(m, "Display IMR:\t%08x\n", I915_READ(VLV_IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat:\t%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -785,7 +799,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(IIR)); seq_printf(m, "Interrupt mask: %08x\n", I915_READ(IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat: %08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -933,7 +947,7 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf, ssize_t ret_count = 0; int ret; - ret = i915_error_state_buf_init(&error_str, count, *pos); + ret = i915_error_state_buf_init(&error_str, to_i915(error_priv->dev), count, *pos); if (ret) return ret; @@ -1030,6 +1044,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) u32 rpstat, cagf, reqf; u32 rpupei, rpcurup, rpprevup; u32 rpdownei, rpcurdown, rpprevdown; + u32 pm_ier, pm_imr, pm_isr, pm_iir, pm_mask; int max_freq; /* RPSTAT1 is in the GT power well */ @@ -1067,12 +1082,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused) gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); mutex_unlock(&dev->struct_mutex); + if (IS_GEN6(dev) || IS_GEN7(dev)) { + pm_ier = I915_READ(GEN6_PMIER); + pm_imr = I915_READ(GEN6_PMIMR); + pm_isr = I915_READ(GEN6_PMISR); + pm_iir = I915_READ(GEN6_PMIIR); + pm_mask = I915_READ(GEN6_PMINTRMSK); + } else { + pm_ier = I915_READ(GEN8_GT_IER(2)); + pm_imr = I915_READ(GEN8_GT_IMR(2)); + pm_isr = I915_READ(GEN8_GT_ISR(2)); + pm_iir = I915_READ(GEN8_GT_IIR(2)); + pm_mask = I915_READ(GEN6_PMINTRMSK); + } seq_printf(m, "PM IER=0x%08x IMR=0x%08x ISR=0x%08x IIR=0x%08x, MASK=0x%08x\n", - I915_READ(GEN6_PMIER), - I915_READ(GEN6_PMIMR), - I915_READ(GEN6_PMISR), - I915_READ(GEN6_PMIIR), - I915_READ(GEN6_PMINTRMSK)); + pm_ier, pm_imr, pm_isr, pm_iir, pm_mask); seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status); seq_printf(m, "Render p-state ratio: %d\n", (gt_perf_status & 0xff00) >> 8); @@ -1371,7 +1395,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused) if (IS_VALLEYVIEW(dev)) return vlv_drpc_info(m); - else if (IS_GEN6(dev) || IS_GEN7(dev)) + else if (INTEL_INFO(dev)->gen >= 6) return gen6_drpc_info(m); else return ironlake_drpc_info(m); @@ -2618,6 +2642,40 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) return 0; } +static int i915_wa_registers(struct seq_file *m, void *unused) +{ + int i; + int ret; + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + intel_runtime_pm_get(dev_priv); + + seq_printf(m, "Workarounds applied: %d\n", dev_priv->num_wa_regs); + for (i = 0; i < dev_priv->num_wa_regs; ++i) { + u32 addr, mask; + + addr = dev_priv->intel_wa_regs[i].addr; + mask = dev_priv->intel_wa_regs[i].mask; + dev_priv->intel_wa_regs[i].value = I915_READ(addr) | mask; + if (dev_priv->intel_wa_regs[i].addr) + seq_printf(m, "0x%X: 0x%08X, mask: 0x%08X\n", + dev_priv->intel_wa_regs[i].addr, + dev_priv->intel_wa_regs[i].value, + dev_priv->intel_wa_regs[i].mask); + } + + intel_runtime_pm_put(dev_priv); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + struct pipe_crc_info { const char *name; struct drm_device *dev; @@ -3769,8 +3827,6 @@ i915_drop_caches_set(void *data, u64 val) struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; - struct i915_address_space *vm; - struct i915_vma *vma, *x; int ret; DRM_DEBUG("Dropping caches: 0x%08llx\n", val); @@ -3791,16 +3847,23 @@ i915_drop_caches_set(void *data, u64 val) i915_gem_retire_requests(dev); if (val & DROP_BOUND) { - list_for_each_entry(vm, &dev_priv->vm_list, global_link) { - list_for_each_entry_safe(vma, x, &vm->inactive_list, - mm_list) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.bound_list, + global_list) { + struct i915_vma *vma, *v; + + ret = 0; + drm_gem_object_reference(&obj->base); + list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link) { if (vma->pin_count) continue; ret = i915_vma_unbind(vma); if (ret) - goto unlock; + break; } + drm_gem_object_unreference(&obj->base); + if (ret) + goto unlock; } } @@ -4149,6 +4212,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_semaphore_status", i915_semaphore_status, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, {"i915_dp_mst_info", i915_dp_mst_info, 0}, + {"i915_wa_registers", i915_wa_registers, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) @@ -4178,7 +4242,7 @@ void intel_display_crc_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; pipe_crc->opened = false; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 272d3d16147c..1403b01e8216 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -28,6 +28,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/async.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> @@ -1381,7 +1382,7 @@ static int i915_load_modeset_init(struct drm_device *dev) * scanning against hotplug events. Hence do this first and ignore the * tiny window where we will loose hotplug notifactions. */ - intel_fbdev_initial_config(dev); + async_schedule(intel_fbdev_initial_config, dev_priv); drm_kms_helper_poll_init(dev); @@ -1534,10 +1535,10 @@ static void intel_device_info_runtime_init(struct drm_device *dev) info = (struct intel_device_info *)&dev_priv->info; if (IS_VALLEYVIEW(dev)) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) info->num_sprites[pipe] = 2; else - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) info->num_sprites[pipe] = 1; if (i915.disable_display) { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cdd95956811d..3870c7359a16 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -844,7 +844,13 @@ int i915_reset(struct drm_device *dev) !dev_priv->ums.mm_suspended) { dev_priv->ums.mm_suspended = 0; + /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */ + dev_priv->gpu_error.reload_in_reset = true; + ret = i915_gem_init_hw(dev); + + dev_priv->gpu_error.reload_in_reset = false; + mutex_unlock(&dev->struct_mutex); if (ret) { DRM_ERROR("Failed hw init on reset %d\n", ret); @@ -1456,13 +1462,29 @@ static int intel_runtime_suspend(struct device *device) dev_priv->pm.suspended = true; /* - * current versions of firmware which depend on this opregion - * notification have repurposed the D1 definition to mean - * "runtime suspended" vs. what you would normally expect (D3) - * to distinguish it from notifications that might be sent - * via the suspend path. + * FIXME: We really should find a document that references the arguments + * used below! */ - intel_opregion_notify_adapter(dev, PCI_D1); + if (IS_HASWELL(dev)) { + /* + * current versions of firmware which depend on this opregion + * notification have repurposed the D1 definition to mean + * "runtime suspended" vs. what you would normally expect (D3) + * to distinguish it from notifications that might be sent via + * the suspend path. + */ + intel_opregion_notify_adapter(dev, PCI_D1); + } else { + /* + * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop + * being detected, and the call we do at intel_runtime_resume() + * won't be able to restore them. Since PCI_D3hot matches the + * actual specification and appears to be working, use it. Let's + * assume the other non-Haswell platforms will stay the same as + * Broadwell. + */ + intel_opregion_notify_adapter(dev, PCI_D3hot); + } DRM_DEBUG_KMS("Device suspended\n"); return 0; @@ -1685,6 +1707,8 @@ static void __exit i915_exit(void) module_init(i915_init); module_exit(i915_exit); -MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_AUTHOR("Tungsten Graphics, Inc."); +MODULE_AUTHOR("Intel Corporation"); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 357913c4f2c6..88a8b72b553e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -37,6 +37,7 @@ #include "intel_ringbuffer.h" #include "intel_lrc.h" #include "i915_gem_gtt.h" +#include "i915_gem_render_state.h" #include <linux/io-mapping.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> @@ -51,11 +52,9 @@ /* General customization: */ -#define DRIVER_AUTHOR "Tungsten Graphics, Inc." - #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20140822" +#define DRIVER_DATE "20140905" enum pipe { INVALID_PIPE = -1, @@ -164,7 +163,10 @@ enum hpd_pin { I915_GEM_DOMAIN_INSTRUCTION | \ I915_GEM_DOMAIN_VERTEX) -#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++) +#define for_each_pipe(__dev_priv, __p) \ + for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++) +#define for_each_plane(pipe, p) \ + for ((p) = 0; (p) < INTEL_INFO(dev)->num_sprites[(pipe)] + 1; (p)++) #define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++) #define for_each_crtc(dev, crtc) \ @@ -639,6 +641,7 @@ struct intel_context { } legacy_hw_ctx; /* Execlists */ + bool rcs_initialized; struct { struct drm_i915_gem_object *state; struct intel_ringbuffer *ringbuf; @@ -712,6 +715,7 @@ enum intel_sbi_destination { #define QUIRK_LVDS_SSC_DISABLE (1<<1) #define QUIRK_INVERT_BRIGHTNESS (1<<2) #define QUIRK_BACKLIGHT_PRESENT (1<<3) +#define QUIRK_PIPEB_FORCE (1<<4) struct intel_fbdev; struct intel_fbc_work; @@ -941,6 +945,23 @@ struct intel_rps_ei { u32 media_c0; }; +struct intel_rps_bdw_cal { + u32 it_threshold_pct; /* interrupt, in percentage */ + u32 eval_interval; /* evaluation interval, in us */ + u32 last_ts; + u32 last_c0; + bool is_up; +}; + +struct intel_rps_bdw_turbo { + struct intel_rps_bdw_cal up; + struct intel_rps_bdw_cal down; + struct timer_list flip_timer; + u32 timeout; + atomic_t flip_received; + struct work_struct work_max_freq; +}; + struct intel_gen6_power_mgmt { /* work and pm_iir are protected by dev_priv->irq_lock */ struct work_struct work; @@ -974,6 +995,9 @@ struct intel_gen6_power_mgmt { bool enabled; struct delayed_work delayed_resume_work; + bool is_bdw_sw_turbo; /* Switch of BDW software turbo */ + struct intel_rps_bdw_turbo sw_turbo; /* Calculate RP interrupt timing */ + /* manual wa residency calculations */ struct intel_rps_ei up_ei, down_ei; @@ -1171,6 +1195,7 @@ struct i915_gem_mm { }; struct drm_i915_error_state_buf { + struct drm_i915_private *i915; unsigned bytes; unsigned size; int err; @@ -1243,6 +1268,9 @@ struct i915_gpu_error { /* For missed irq/seqno simulation. */ unsigned int test_irq_rings; + + /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */ + bool reload_in_reset; }; enum modeset_restore { @@ -1505,6 +1533,9 @@ struct drm_i915_private { /* LVDS info */ bool no_aux_handshake; + /* protects panel power sequencer state */ + struct mutex pps_mutex; + 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 */ @@ -1556,6 +1587,20 @@ struct drm_i915_private { struct intel_shared_dpll shared_dplls[I915_NUM_PLLS]; int dpio_phy_iosf_port[I915_NUM_PHYS_VLV]; + /* + * workarounds are currently applied at different places and + * changes are being done to consolidate them so exact count is + * not clear at this point, use a max value for now. + */ +#define I915_MAX_WA_REGS 16 + struct { + u32 addr; + u32 value; + /* bitmask representing WA bits */ + u32 mask; + } intel_wa_regs[I915_MAX_WA_REGS]; + u32 num_wa_regs; + /* Reclocking support */ bool render_reclock_avail; bool lvds_downclock_avail; @@ -1639,6 +1684,8 @@ struct drm_i915_private { */ struct workqueue_struct *dp_wq; + uint32_t bios_vgacntr; + /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct i915_dri1_state dri1; @@ -2596,8 +2643,6 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); -/* i915_gem_render_state.c */ -int i915_gem_render_state_init(struct intel_engine_cs *ring); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, @@ -2665,6 +2710,7 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...); int i915_error_state_to_str(struct drm_i915_error_state_buf *estr, const struct i915_error_state_file_priv *error); int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb, + struct drm_i915_private *i915, size_t count, loff_t pos); static inline void i915_error_state_buf_release( struct drm_i915_error_state_buf *eb) @@ -2679,7 +2725,7 @@ void i915_error_state_put(struct i915_error_state_file_priv *error_priv); void i915_destroy_error_state(struct drm_device *dev); void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone); -const char *i915_cache_level_str(int type); +const char *i915_cache_level_str(struct drm_i915_private *i915, int type); /* i915_cmd_parser.c */ int i915_cmd_parser_get_version(void); @@ -2771,10 +2817,13 @@ extern void intel_modeset_setup_hw_state(struct drm_device *dev, extern void i915_redisable_vga(struct drm_device *dev); extern void i915_redisable_vga_power_on(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); +extern void gen8_fbc_sw_flush(struct drm_device *dev, u32 value); 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 bdw_software_turbo(struct drm_device *dev); +extern void gen8_flip_interrupt(struct drm_device *dev); extern void valleyview_set_rps(struct drm_device *dev, u8 val); extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1133bb3b2766..4ca3a6dcf10b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1085,7 +1085,13 @@ i915_gem_check_wedge(struct i915_gpu_error *error, if (i915_terminally_wedged(error)) return -EIO; - return -EAGAIN; + /* + * Check if GPU Reset is in progress - we need intel_ring_begin + * to work properly to reinit the hw state while the gpu is + * still marked as reset-in-progress. Handle this with a flag. + */ + if (!error->reload_in_reset) + return -EAGAIN; } return 0; @@ -2982,9 +2988,11 @@ int i915_gpu_idle(struct drm_device *dev) /* Flush everything onto the inactive list. */ for_each_ring(ring, dev_priv, i) { - ret = i915_switch_context(ring, ring->default_context); - if (ret) - return ret; + if (!i915.enable_execlists) { + ret = i915_switch_context(ring, ring->default_context); + if (ret) + return ret; + } ret = intel_ring_idle(ring); if (ret) @@ -4658,11 +4666,46 @@ intel_enable_blt(struct drm_device *dev) return true; } +static void init_unused_ring(struct drm_device *dev, u32 base) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(RING_CTL(base), 0); + I915_WRITE(RING_HEAD(base), 0); + I915_WRITE(RING_TAIL(base), 0); + I915_WRITE(RING_START(base), 0); +} + +static void init_unused_rings(struct drm_device *dev) +{ + if (IS_I830(dev)) { + init_unused_ring(dev, PRB1_BASE); + init_unused_ring(dev, SRB0_BASE); + init_unused_ring(dev, SRB1_BASE); + init_unused_ring(dev, SRB2_BASE); + init_unused_ring(dev, SRB3_BASE); + } else if (IS_GEN2(dev)) { + init_unused_ring(dev, SRB0_BASE); + init_unused_ring(dev, SRB1_BASE); + } else if (IS_GEN3(dev)) { + init_unused_ring(dev, PRB1_BASE); + init_unused_ring(dev, PRB2_BASE); + } +} + int i915_gem_init_rings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; + /* + * At least 830 can leave some of the unused rings + * "active" (ie. head != tail) after resume which + * will prevent c3 entry. Makes sure all unused rings + * are totally idle. + */ + init_unused_rings(dev); + ret = intel_init_render_ring_buffer(dev); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 9683e62ec61a..a5221d8f1580 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -289,34 +289,23 @@ void i915_gem_context_reset(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int i; - /* Prevent the hardware from restoring the last context (which hung) on - * the next switch */ + /* In execlists mode we will unreference the context when the execlist + * queue is cleared and the requests destroyed. + */ + if (i915.enable_execlists) + return; + for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; - struct intel_context *dctx = ring->default_context; struct intel_context *lctx = ring->last_context; - /* Do a fake switch to the default context */ - if (lctx == dctx) - continue; + if (lctx) { + if (lctx->legacy_hw_ctx.rcs_state && i == RCS) + i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - if (!lctx) - continue; - - if (dctx->legacy_hw_ctx.rcs_state && i == RCS) { - WARN_ON(i915_gem_obj_ggtt_pin(dctx->legacy_hw_ctx.rcs_state, - get_context_alignment(dev), 0)); - /* Fake a finish/inactive */ - dctx->legacy_hw_ctx.rcs_state->base.write_domain = 0; - dctx->legacy_hw_ctx.rcs_state->active = 0; + i915_gem_context_unreference(lctx); + ring->last_context = NULL; } - - if (lctx->legacy_hw_ctx.rcs_state && i == RCS) - i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - - i915_gem_context_unreference(lctx); - i915_gem_context_reference(dctx); - ring->last_context = dctx; } } @@ -412,12 +401,11 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv) struct intel_engine_cs *ring; int ret, i; - /* FIXME: We should make this work, even in reset */ - if (i915_reset_in_progress(&dev_priv->gpu_error)) - return 0; - BUG_ON(!dev_priv->ring[RCS].default_context); + if (i915.enable_execlists) + return 0; + for_each_ring(ring, dev_priv, i) { ret = i915_switch_context(ring, ring->default_context); if (ret) @@ -479,6 +467,7 @@ mi_set_context(struct intel_engine_cs *ring, struct intel_context *new_context, u32 hw_flags) { + u32 flags = hw_flags | MI_MM_SPACE_GTT; int ret; /* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB @@ -492,6 +481,10 @@ mi_set_context(struct intel_engine_cs *ring, return ret; } + /* These flags are for resource streamer on HSW+ */ + if (!IS_HASWELL(ring->dev) && INTEL_INFO(ring->dev)->gen < 8) + flags |= (MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN); + ret = intel_ring_begin(ring, 6); if (ret) return ret; @@ -505,10 +498,7 @@ mi_set_context(struct intel_engine_cs *ring, intel_ring_emit(ring, MI_NOOP); intel_ring_emit(ring, MI_SET_CONTEXT); intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->legacy_hw_ctx.rcs_state) | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - hw_flags); + flags); /* * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP * WaMiSetContext_Hang:snb,ivb,vlv @@ -558,7 +548,7 @@ static int do_switch(struct intel_engine_cs *ring, from = ring->last_context; if (to->ppgtt) { - ret = to->ppgtt->switch_mm(to->ppgtt, ring, false); + ret = to->ppgtt->switch_mm(to->ppgtt, ring); if (ret) goto unpin_out; } @@ -638,6 +628,12 @@ done: ring->last_context = to; if (uninitialized) { + if (ring->init_context) { + ret = ring->init_context(ring); + if (ret) + DRM_ERROR("ring init context: %d\n", ret); + } + ret = i915_gem_render_state_init(ring); if (ret) DRM_ERROR("init render state: %d\n", ret); @@ -658,14 +654,19 @@ unpin_out: * * The context life cycle is simple. The context refcount is incremented and * decremented by 1 and create and destroy. If the context is in use by the GPU, - * it will have a refoucnt > 1. This allows us to destroy the context abstract + * it will have a refcount > 1. This allows us to destroy the context abstract * object while letting the normal object tracking destroy the backing BO. + * + * This function should not be used in execlists mode. Instead the context is + * switched by writing to the ELSP and requests keep a reference to their + * context. */ int i915_switch_context(struct intel_engine_cs *ring, struct intel_context *to) { struct drm_i915_private *dev_priv = ring->dev->dev_private; + WARN_ON(i915.enable_execlists); WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); if (to->legacy_hw_ctx.rcs_state == NULL) { /* We have the fake context */ diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 4db237065610..6f410cfb0510 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -204,19 +204,12 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr, /* Broadwell Page Directory Pointer Descriptors */ static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry, - uint64_t val, bool synchronous) + uint64_t val) { - struct drm_i915_private *dev_priv = ring->dev->dev_private; int ret; BUG_ON(entry >= 4); - if (synchronous) { - I915_WRITE(GEN8_RING_PDP_UDW(ring, entry), val >> 32); - I915_WRITE(GEN8_RING_PDP_LDW(ring, entry), (u32)val); - return 0; - } - ret = intel_ring_begin(ring, 6); if (ret) return ret; @@ -233,8 +226,7 @@ static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry, } static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { int i, ret; @@ -243,7 +235,7 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, for (i = used_pd - 1; i >= 0; i--) { dma_addr_t addr = ppgtt->pd_dma_addr[i]; - ret = gen8_write_pdp(ring, i, addr, synchronous); + ret = gen8_write_pdp(ring, i, addr); if (ret) return ret; } @@ -708,29 +700,10 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt) } static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { - struct drm_device *dev = ppgtt->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; - /* If we're in reset, we can assume the GPU is sufficiently idle to - * manually frob these bits. Ideally we could use the ring functions, - * except our error handling makes it quite difficult (can't use - * intel_ring_begin, ring->flush, or intel_ring_advance) - * - * FIXME: We should try not to special case reset - */ - if (synchronous || - i915_reset_in_progress(&dev_priv->gpu_error)) { - WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt); - I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); - I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); - POSTING_READ(RING_PP_DIR_BASE(ring)); - return 0; - } - /* NB: TLBs must be flushed and invalidated before a switch */ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); if (ret) @@ -752,29 +725,10 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, } static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { - struct drm_device *dev = ppgtt->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; - /* If we're in reset, we can assume the GPU is sufficiently idle to - * manually frob these bits. Ideally we could use the ring functions, - * except our error handling makes it quite difficult (can't use - * intel_ring_begin, ring->flush, or intel_ring_advance) - * - * FIXME: We should try not to special case reset - */ - if (synchronous || - i915_reset_in_progress(&dev_priv->gpu_error)) { - WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt); - I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); - I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); - POSTING_READ(RING_PP_DIR_BASE(ring)); - return 0; - } - /* NB: TLBs must be flushed and invalidated before a switch */ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); if (ret) @@ -803,14 +757,11 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, } static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { struct drm_device *dev = ppgtt->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (!synchronous) - return 0; I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); @@ -826,12 +777,6 @@ static void gen8_ppgtt_enable(struct drm_device *dev) struct intel_engine_cs *ring; int j; - /* In the case of execlists, PPGTT is enabled by the context descriptor - * and the PDPs are contained within the context itself. We don't - * need to do anything here. */ - if (i915.enable_execlists) - return; - for_each_ring(ring, dev_priv, j) { I915_WRITE(RING_MODE_GEN7(ring), _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); @@ -1175,6 +1120,12 @@ int i915_ppgtt_init_hw(struct drm_device *dev) struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; int i, ret = 0; + /* In the case of execlists, PPGTT is enabled by the context descriptor + * and the PDPs are contained within the context itself. We don't + * need to do anything here. */ + if (i915.enable_execlists) + return 0; + if (!USES_PPGTT(dev)) return 0; @@ -1189,7 +1140,7 @@ int i915_ppgtt_init_hw(struct drm_device *dev) if (ppgtt) { for_each_ring(ring, dev_priv, i) { - ret = ppgtt->switch_mm(ppgtt, ring, true); + ret = ppgtt->switch_mm(ppgtt, ring); if (ret != 0) return ret; } @@ -2190,8 +2141,10 @@ static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj, /* Keep GGTT vmas first to make debug easier */ if (i915_is_ggtt(vm)) list_add(&vma->vma_link, &obj->vma_list); - else + else { list_add_tail(&vma->vma_link, &obj->vma_list); + i915_ppgtt_get(i915_vm_to_ppgtt(vm)); + } return vma; } @@ -2206,8 +2159,5 @@ i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, if (!vma) vma = __i915_gem_vma_create(obj, vm); - if (!i915_is_ggtt(vm)) - i915_ppgtt_get(i915_vm_to_ppgtt(vm)); - return vma; } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 6280648d4805..d5c14af51e99 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -264,8 +264,7 @@ struct i915_hw_ppgtt { int (*enable)(struct i915_hw_ppgtt *ppgtt); int (*switch_mm)(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous); + struct intel_engine_cs *ring); void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m); }; diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index e60be3f552a6..a9a62d75aa57 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -28,13 +28,6 @@ #include "i915_drv.h" #include "intel_renderstate.h" -struct render_state { - const struct intel_renderstate_rodata *rodata; - struct drm_i915_gem_object *obj; - u64 ggtt_offset; - int gen; -}; - static const struct intel_renderstate_rodata * render_state_get_rodata(struct drm_device *dev, const int gen) { @@ -127,30 +120,47 @@ static int render_state_setup(struct render_state *so) return 0; } -static void render_state_fini(struct render_state *so) +void i915_gem_render_state_fini(struct render_state *so) { i915_gem_object_ggtt_unpin(so->obj); drm_gem_object_unreference(&so->obj->base); } -int i915_gem_render_state_init(struct intel_engine_cs *ring) +int i915_gem_render_state_prepare(struct intel_engine_cs *ring, + struct render_state *so) { - struct render_state so; int ret; if (WARN_ON(ring->id != RCS)) return -ENOENT; - ret = render_state_init(&so, ring->dev); + ret = render_state_init(so, ring->dev); if (ret) return ret; - if (so.rodata == NULL) + if (so->rodata == NULL) return 0; - ret = render_state_setup(&so); + ret = render_state_setup(so); + if (ret) { + i915_gem_render_state_fini(so); + return ret; + } + + return 0; +} + +int i915_gem_render_state_init(struct intel_engine_cs *ring) +{ + struct render_state so; + int ret; + + ret = i915_gem_render_state_prepare(ring, &so); if (ret) - goto out; + return ret; + + if (so.rodata == NULL) + return 0; ret = ring->dispatch_execbuffer(ring, so.ggtt_offset, @@ -164,6 +174,6 @@ int i915_gem_render_state_init(struct intel_engine_cs *ring) ret = __i915_add_request(ring, NULL, so.obj, NULL); /* __i915_add_request moves object to inactive if it fails */ out: - render_state_fini(&so); + i915_gem_render_state_fini(&so); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.h b/drivers/gpu/drm/i915/i915_gem_render_state.h new file mode 100644 index 000000000000..c44961ed3fad --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_render_state.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2014 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 _I915_GEM_RENDER_STATE_H_ +#define _I915_GEM_RENDER_STATE_H_ + +#include <linux/types.h> + +struct intel_renderstate_rodata { + const u32 *reloc; + const u32 *batch; + const u32 batch_items; +}; + +struct render_state { + const struct intel_renderstate_rodata *rodata; + struct drm_i915_gem_object *obj; + u64 ggtt_offset; + int gen; +}; + +int i915_gem_render_state_init(struct intel_engine_cs *ring); +void i915_gem_render_state_fini(struct render_state *so); +int i915_gem_render_state_prepare(struct intel_engine_cs *ring, + struct render_state *so); + +#endif /* _I915_GEM_RENDER_STATE_H_ */ diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 7e623bf097a1..2cefb597df6d 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -91,7 +91,14 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; - if (IS_VALLEYVIEW(dev)) { + if (INTEL_INFO(dev)->gen >= 8 || IS_VALLEYVIEW(dev)) { + /* + * On BDW+, swizzling is not used. We leave the CPU memory + * controller in charge of optimizing memory accesses without + * the extra address manipulation GPU side. + * + * VLV and CHV don't have GPU swizzling. + */ swizzle_x = I915_BIT_6_SWIZZLE_NONE; swizzle_y = I915_BIT_6_SWIZZLE_NONE; } else if (INTEL_INFO(dev)->gen >= 6) { diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 35e70d5d6282..2c87a797213f 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -208,7 +208,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_puts(m, err->userptr ? " userptr" : ""); err_puts(m, err->ring != -1 ? " " : ""); err_puts(m, ring_str(err->ring)); - err_puts(m, i915_cache_level_str(err->cache_level)); + err_puts(m, i915_cache_level_str(m->i915, err->cache_level)); if (err->name) err_printf(m, " (name: %d)", err->name); @@ -494,9 +494,11 @@ out: } int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf, + struct drm_i915_private *i915, size_t count, loff_t pos) { memset(ebuf, 0, sizeof(*ebuf)); + ebuf->i915 = i915; /* We need to have enough room to store any i915_error_state printf * so that we can move it to start position. @@ -558,24 +560,54 @@ static void i915_error_state_free(struct kref *error_ref) } static struct drm_i915_error_object * -i915_error_object_create_sized(struct drm_i915_private *dev_priv, - struct drm_i915_gem_object *src, - struct i915_address_space *vm, - const int num_pages) +i915_error_object_create(struct drm_i915_private *dev_priv, + struct drm_i915_gem_object *src, + struct i915_address_space *vm) { struct drm_i915_error_object *dst; - int i; + int num_pages; + bool use_ggtt; + int i = 0; u32 reloc_offset; if (src == NULL || src->pages == NULL) return NULL; + num_pages = src->base.size >> PAGE_SHIFT; + dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC); if (dst == NULL) return NULL; - reloc_offset = dst->gtt_offset = i915_gem_obj_offset(src, vm); - for (i = 0; i < num_pages; i++) { + if (i915_gem_obj_bound(src, vm)) + dst->gtt_offset = i915_gem_obj_offset(src, vm); + else + dst->gtt_offset = -1; + + reloc_offset = dst->gtt_offset; + use_ggtt = (src->cache_level == I915_CACHE_NONE && + i915_is_ggtt(vm) && + src->has_global_gtt_mapping && + reloc_offset + num_pages * PAGE_SIZE <= dev_priv->gtt.mappable_end); + + /* Cannot access stolen address directly, try to use the aperture */ + if (src->stolen) { + use_ggtt = true; + + if (!src->has_global_gtt_mapping) + goto unwind; + + reloc_offset = i915_gem_obj_ggtt_offset(src); + if (reloc_offset + num_pages * PAGE_SIZE > dev_priv->gtt.mappable_end) + goto unwind; + } + + /* Cannot access snooped pages through the aperture */ + if (use_ggtt && src->cache_level != I915_CACHE_NONE && !HAS_LLC(dev_priv->dev)) + goto unwind; + + dst->page_count = num_pages; + while (num_pages--) { unsigned long flags; void *d; @@ -584,10 +616,7 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (src->cache_level == I915_CACHE_NONE && - reloc_offset < dev_priv->gtt.mappable_end && - src->has_global_gtt_mapping && - i915_is_ggtt(vm)) { + if (use_ggtt) { void __iomem *s; /* Simply ignore tiling or any overlapping fence. @@ -599,14 +628,6 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); - } else if (src->stolen) { - unsigned long offset; - - offset = dev_priv->mm.stolen_base; - offset += src->stolen->start; - offset += i << PAGE_SHIFT; - - memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; @@ -623,11 +644,9 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, } local_irq_restore(flags); - dst->pages[i] = d; - + dst->pages[i++] = d; reloc_offset += PAGE_SIZE; } - dst->page_count = num_pages; return dst; @@ -637,13 +656,8 @@ unwind: kfree(dst); return NULL; } -#define i915_error_object_create(dev_priv, src, vm) \ - i915_error_object_create_sized((dev_priv), (src), (vm), \ - (src)->base.size>>PAGE_SHIFT) - #define i915_error_ggtt_object_create(dev_priv, src) \ - i915_error_object_create_sized((dev_priv), (src), &(dev_priv)->gtt.base, \ - (src)->base.size>>PAGE_SHIFT) + i915_error_object_create((dev_priv), (src), &(dev_priv)->gtt.base) static void capture_bo(struct drm_i915_error_buffer *err, struct i915_vma *vma) @@ -900,9 +914,6 @@ static void i915_record_ring_state(struct drm_device *dev, ering->hws = I915_READ(mmio); } - ering->cpu_ring_head = ring->buffer->head; - ering->cpu_ring_tail = ring->buffer->tail; - ering->hangcheck_score = ring->hangcheck.score; ering->hangcheck_action = ring->hangcheck.action; @@ -965,6 +976,7 @@ static void i915_gem_record_rings(struct drm_device *dev, for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; + struct intel_ringbuffer *rbuf; error->ring[i].pid = -1; @@ -992,8 +1004,7 @@ static void i915_gem_record_rings(struct drm_device *dev, request->batch_obj, vm); - if (HAS_BROKEN_CS_TLB(dev_priv->dev) && - ring->scratch.obj) + if (HAS_BROKEN_CS_TLB(dev_priv->dev)) error->ring[i].wa_batchbuffer = i915_error_ggtt_object_create(dev_priv, ring->scratch.obj); @@ -1012,12 +1023,27 @@ static void i915_gem_record_rings(struct drm_device *dev, } } + if (i915.enable_execlists) { + /* TODO: This is only a small fix to keep basic error + * capture working, but we need to add more information + * for it to be useful (e.g. dump the context being + * executed). + */ + if (request) + rbuf = request->ctx->engine[ring->id].ringbuf; + else + rbuf = ring->default_context->engine[ring->id].ringbuf; + } else + rbuf = ring->buffer; + + error->ring[i].cpu_ring_head = rbuf->head; + error->ring[i].cpu_ring_tail = rbuf->tail; + error->ring[i].ringbuffer = - i915_error_ggtt_object_create(dev_priv, ring->buffer->obj); + i915_error_ggtt_object_create(dev_priv, rbuf->obj); - if (ring->status_page.obj) - error->ring[i].hws_page = - i915_error_ggtt_object_create(dev_priv, ring->status_page.obj); + error->ring[i].hws_page = + i915_error_ggtt_object_create(dev_priv, ring->status_page.obj); i915_gem_record_active_context(ring, error, &error->ring[i]); @@ -1331,11 +1357,11 @@ void i915_destroy_error_state(struct drm_device *dev) kref_put(&error->ref, i915_error_state_free); } -const char *i915_cache_level_str(int type) +const char *i915_cache_level_str(struct drm_i915_private *i915, int type) { switch (type) { case I915_CACHE_NONE: return " uncached"; - case I915_CACHE_LLC: return " snooped or LLC"; + case I915_CACHE_LLC: return HAS_LLC(i915) ? " LLC" : " snooped"; case I915_CACHE_L3_LLC: return " L3+LLC"; case I915_CACHE_WT: return " WT"; default: return ""; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7391697c25e7..c96ddc953531 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -238,7 +238,7 @@ static bool ivb_can_enable_err_int(struct drm_device *dev) assert_spin_locked(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); if (crtc->cpu_fifo_underrun_disabled) @@ -296,7 +296,7 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev) assert_spin_locked(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); if (crtc->pch_fifo_underrun_disabled) @@ -497,7 +497,7 @@ static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, old = !intel_crtc->cpu_fifo_underrun_disabled; intel_crtc->cpu_fifo_underrun_disabled = !enable; - if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)) + if (HAS_GMCH_DISPLAY(dev)) i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old); else if (IS_GEN5(dev) || IS_GEN6(dev)) ironlake_set_fifo_underrun_reporting(dev, pipe, enable); @@ -1979,6 +1979,27 @@ static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe) res1, res2); } +void gen8_flip_interrupt(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!dev_priv->rps.is_bdw_sw_turbo) + return; + + if(atomic_read(&dev_priv->rps.sw_turbo.flip_received)) { + mod_timer(&dev_priv->rps.sw_turbo.flip_timer, + usecs_to_jiffies(dev_priv->rps.sw_turbo.timeout) + jiffies); + } + else { + dev_priv->rps.sw_turbo.flip_timer.expires = + usecs_to_jiffies(dev_priv->rps.sw_turbo.timeout) + jiffies; + add_timer(&dev_priv->rps.sw_turbo.flip_timer); + atomic_set(&dev_priv->rps.sw_turbo.flip_received, true); + } + + bdw_software_turbo(dev); +} + /* The RPS events need forcewake, so we add them to a work queue and mask their * IMR bits until the work is done. Other interrupts can be processed without * the work queue. */ @@ -2020,7 +2041,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) int pipe; spin_lock(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg; u32 mask, iir_bit = 0; @@ -2065,9 +2086,10 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) } spin_unlock(&dev_priv->irq_lock); - for_each_pipe(pipe) { - if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) { intel_prepare_page_flip(dev, pipe); @@ -2234,7 +2256,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_ERROR("PCH poison interrupt\n"); if (pch_iir & SDE_FDI_MASK) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); @@ -2265,7 +2287,7 @@ static void ivb_err_int_handler(struct drm_device *dev) if (err_int & ERR_INT_POISON) DRM_ERROR("Poison interrupt\n"); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) { if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false)) @@ -2342,7 +2364,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER("Audio CP change interrupt\n"); if (pch_iir & SDE_FDI_MASK_CPT) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); @@ -2365,9 +2387,10 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_POISON) DRM_ERROR("Poison interrupt\n"); - for_each_pipe(pipe) { - if (de_iir & DE_PIPE_VBLANK(pipe)) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (de_iir & DE_PIPE_VBLANK(pipe) && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe)) if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false)) @@ -2415,9 +2438,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_GSE_IVB) intel_opregion_asle_intr(dev); - for_each_pipe(pipe) { - if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)) && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); /* plane/pipes map 1:1 on ilk+ */ if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) { @@ -2562,7 +2586,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) DRM_ERROR("The master control interrupt lied (DE PORT)!\n"); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { uint32_t pipe_iir; if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe))) @@ -2572,8 +2596,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) if (pipe_iir) { ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir); - if (pipe_iir & GEN8_PIPE_VBLANK) - intel_pipe_handle_vblank(dev, pipe); + if (pipe_iir & GEN8_PIPE_VBLANK && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) { intel_prepare_page_flip(dev, pipe); @@ -2781,7 +2806,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) if (eir & I915_ERROR_MEMORY_REFRESH) { pr_err("memory refresh error:\n"); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) pr_err("pipe %c stat: 0x%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); /* pipestat has already been acked */ @@ -2878,52 +2903,6 @@ void i915_handle_error(struct drm_device *dev, bool wedged, schedule_work(&dev_priv->gpu_error.work); } -static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe) -{ - 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); - struct drm_i915_gem_object *obj; - struct intel_unpin_work *work; - unsigned long flags; - bool stall_detected; - - /* Ignore early vblank irqs */ - if (intel_crtc == NULL) - return; - - spin_lock_irqsave(&dev->event_lock, flags); - work = intel_crtc->unpin_work; - - if (work == NULL || - atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE || - !work->enable_stall_check) { - /* Either the pending flip IRQ arrived, or we're too early. Don't check */ - spin_unlock_irqrestore(&dev->event_lock, flags); - return; - } - - /* Potential stall - if we see that the flip has happened, assume a missed interrupt */ - obj = work->pending_flip_obj; - if (INTEL_INFO(dev)->gen >= 4) { - int dspsurf = DSPSURF(intel_crtc->plane); - stall_detected = I915_HI_DISPBASE(I915_READ(dspsurf)) == - i915_gem_obj_ggtt_offset(obj); - } else { - int dspaddr = DSPADDR(intel_crtc->plane); - stall_detected = I915_READ(dspaddr) == (i915_gem_obj_ggtt_offset(obj) + - crtc->y * crtc->primary->fb->pitches[0] + - crtc->x * crtc->primary->fb->bits_per_pixel/8); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); - - if (stall_detected) { - DRM_DEBUG_DRIVER("Pageflip stall detected\n"); - intel_prepare_page_flip(dev, intel_crtc->plane); - } -} - /* Called from drm generic code, passed 'crtc' which * we use as a pipe index */ @@ -3459,7 +3438,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IIR, 0xffffffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -3485,7 +3464,7 @@ static void gen8_irq_reset(struct drm_device *dev) gen8_gt_irq_reset(dev_priv); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); @@ -3528,7 +3507,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -3799,8 +3778,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) { - int i; - /* These are interrupts we'll toggle with the ring mask register */ uint32_t gt_interrupts[] = { GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | @@ -3817,15 +3794,15 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT }; - for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) - GEN8_IRQ_INIT_NDX(GT, i, ~gt_interrupts[i], gt_interrupts[i]); - dev_priv->pm_irq_mask = 0xffffffff; + GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]); + GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]); + GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_irq_mask, dev_priv->pm_rps_events); + GEN8_IRQ_INIT_NDX(GT, 3, ~gt_interrupts[3], gt_interrupts[3]); } static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) { - struct drm_device *dev = dev_priv->dev; uint32_t de_pipe_masked = GEN8_PIPE_PRIMARY_FLIP_DONE | GEN8_PIPE_CDCLK_CRC_DONE | GEN8_DE_PIPE_IRQ_FAULT_ERRORS; @@ -3836,7 +3813,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked; dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, @@ -3881,12 +3858,12 @@ static int cherryview_irq_postinstall(struct drm_device *dev) */ dev_priv->irq_mask = ~enable_mask; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); spin_lock_irqsave(&dev_priv->irq_lock, irqflags); i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) i915_enable_pipestat(dev_priv, pipe, pipestat_enable); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); @@ -3923,7 +3900,7 @@ static void valleyview_irq_uninstall(struct drm_device *dev) I915_WRITE(VLV_MASTER_IER, 0); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(HWSTAM, 0xffffffff); @@ -3985,7 +3962,7 @@ do { \ I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -4009,7 +3986,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE16(IMR, 0xffff); I915_WRITE16(IER, 0x0); @@ -4063,7 +4040,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev, return false; if ((iir & flip_pending) == 0) - return false; + goto check_page_flip; intel_prepare_page_flip(dev, plane); @@ -4074,11 +4051,14 @@ static bool i8xx_handle_vblank(struct drm_device *dev, * an interrupt per se, we watch for the change at vblank. */ if (I915_READ16(ISR) & flip_pending) - return false; + goto check_page_flip; intel_finish_page_flip(dev, pipe); - return true; + +check_page_flip: + intel_check_page_flip(dev, pipe); + return false; } static irqreturn_t i8xx_irq_handler(int irq, void *arg) @@ -4109,7 +4089,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4129,7 +4109,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int plane = pipe; if (HAS_FBC(dev)) plane = !plane; @@ -4157,7 +4137,7 @@ static void i8xx_irq_uninstall(struct drm_device * dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); @@ -4178,7 +4158,7 @@ static void i915_irq_preinstall(struct drm_device * dev) } I915_WRITE16(HWSTAM, 0xeffe); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); @@ -4248,7 +4228,7 @@ static bool i915_handle_vblank(struct drm_device *dev, return false; if ((iir & flip_pending) == 0) - return false; + goto check_page_flip; intel_prepare_page_flip(dev, plane); @@ -4259,11 +4239,14 @@ static bool i915_handle_vblank(struct drm_device *dev, * an interrupt per se, we watch for the change at vblank. */ if (I915_READ(ISR) & flip_pending) - return false; + goto check_page_flip; intel_finish_page_flip(dev, pipe); - return true; + +check_page_flip: + intel_check_page_flip(dev, pipe); + return false; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -4293,7 +4276,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4319,7 +4302,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int plane = pipe; if (HAS_FBC(dev)) plane = !plane; @@ -4377,7 +4360,7 @@ static void i915_irq_uninstall(struct drm_device * dev) } I915_WRITE16(HWSTAM, 0xffff); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); @@ -4397,7 +4380,7 @@ static void i965_irq_preinstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); I915_WRITE(HWSTAM, 0xeffe); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); @@ -4522,7 +4505,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4553,7 +4536,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && i915_handle_vblank(dev, pipe, pipe, iir)) flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe); @@ -4610,12 +4593,12 @@ static void i965_irq_uninstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); I915_WRITE(HWSTAM, 0xffffffff); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)) & 0x8000ffff); I915_WRITE(IIR, I915_READ(IIR)); @@ -4673,8 +4656,8 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); /* Let's track the enabled rps events */ - if (IS_VALLEYVIEW(dev)) - /* WaGsvRC0ResidenncyMethod:VLV */ + if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) + /* WaGsvRC0ResidencyMethod:vlv */ dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED; else dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1e4fb846d9a5..b65bdfc23ccb 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1030,6 +1030,13 @@ enum punit_power_well { #define PGTBL_ADDRESS_LO_MASK 0xfffff000 /* bits [31:12] */ #define PGTBL_ADDRESS_HI_MASK 0x000000f0 /* bits [35:32] (gen4) */ #define PGTBL_ER 0x02024 +#define PRB0_BASE (0x2030-0x30) +#define PRB1_BASE (0x2040-0x30) /* 830,gen3 */ +#define PRB2_BASE (0x2050-0x30) /* gen3 */ +#define SRB0_BASE (0x2100-0x30) /* gen2 */ +#define SRB1_BASE (0x2110-0x30) /* gen2 */ +#define SRB2_BASE (0x2120-0x30) /* 830 */ +#define SRB3_BASE (0x2130-0x30) /* 830 */ #define RENDER_RING_BASE 0x02000 #define BSD_RING_BASE 0x04000 #define GEN6_BSD_RING_BASE 0x12000 @@ -1276,6 +1283,10 @@ enum punit_power_well { #define INSTPM_TLB_INVALIDATE (1<<9) #define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 +#define MEM_MODE 0x020cc +#define MEM_DISPLAY_B_TRICKLE_FEED_DISABLE (1<<3) /* 830 only */ +#define MEM_DISPLAY_A_TRICKLE_FEED_DISABLE (1<<2) /* 830/845 only */ +#define MEM_DISPLAY_TRICKLE_FEED_DISABLE (1<<2) /* 85x only */ #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc #define FW_BLC_SELF 0x020e0 /* 915+ only */ @@ -4218,6 +4229,7 @@ enum punit_power_well { #define DISPPLANE_NO_LINE_DOUBLE 0 #define DISPPLANE_STEREO_POLARITY_FIRST 0 #define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +#define DISPPLANE_ROTATE_180 (1<<15) #define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */ #define DISPPLANE_TILED (1<<10) #define _DSPAADDR 0x70184 @@ -5356,8 +5368,7 @@ enum punit_power_well { #define PIPEA_PP_STATUS (VLV_DISPLAY_BASE + 0x61200) #define PIPEA_PP_CONTROL (VLV_DISPLAY_BASE + 0x61204) #define PIPEA_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61208) -#define PANEL_PORT_SELECT_DPB_VLV (1 << 30) -#define PANEL_PORT_SELECT_DPC_VLV (2 << 30) +#define PANEL_PORT_SELECT_VLV(port) ((port) << 30) #define PIPEA_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6120c) #define PIPEA_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61210) @@ -5566,6 +5577,10 @@ enum punit_power_well { #define GEN8_UCGCTL6 0x9430 #define GEN8_SDEUNIT_CLOCK_GATE_DISABLE (1<<14) +#define TIMESTAMP_CTR 0x44070 +#define FREQ_1_28_US(us) (((us) * 100) >> 7) +#define MCHBAR_PCU_C0 (MCHBAR_MIRROR_BASE_SNB + 0x5960) + #define GEN6_GFXPAUSE 0xA000 #define GEN6_RPNSWREQ 0xA008 #define GEN6_TURBO_DISABLE (1<<31) @@ -5654,12 +5669,6 @@ enum punit_power_well { GEN6_PM_RP_DOWN_THRESHOLD | \ GEN6_PM_RP_DOWN_TIMEOUT) -#define CHV_CZ_CLOCK_FREQ_MODE_200 200 -#define CHV_CZ_CLOCK_FREQ_MODE_267 267 -#define CHV_CZ_CLOCK_FREQ_MODE_320 320 -#define CHV_CZ_CLOCK_FREQ_MODE_333 333 -#define CHV_CZ_CLOCK_FREQ_MODE_400 400 - #define GEN7_GT_SCRATCH_BASE 0x4F100 #define GEN7_GT_SCRATCH_REG_NUM 8 @@ -5975,15 +5984,7 @@ enum punit_power_well { #define DDI_BUF_CTL_B 0x64100 #define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B) #define DDI_BUF_CTL_ENABLE (1<<31) -#define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */ -#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ -#define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */ -#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */ -#define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */ -#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */ -#define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */ -#define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ -#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ +#define DDI_BUF_TRANS_SELECT(n) ((n) << 24) #define DDI_BUF_EMP_MASK (0xf<<24) #define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index ae7fd8fc27f0..503847f18fdd 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -540,7 +540,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj, memset(&error_priv, 0, sizeof(error_priv)); - ret = i915_error_state_buf_init(&error_str, count, off); + ret = i915_error_state_buf_init(&error_str, to_i915(dev), count, off); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 32d22339c130..b3e579b4428e 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -627,16 +627,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) switch (edp_link_params->preemphasis) { case EDP_PREEMPHASIS_NONE: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; break; case EDP_PREEMPHASIS_3_5dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; break; case EDP_PREEMPHASIS_6dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; break; case EDP_PREEMPHASIS_9_5dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; break; default: DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n", @@ -646,16 +646,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) switch (edp_link_params->vswing) { case EDP_VSWING_0_4V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; break; case EDP_VSWING_0_6V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; break; case EDP_VSWING_0_8V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; break; case EDP_VSWING_1_2V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; break; default: DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n", diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 02d55843c78d..b63d4fa204a3 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -28,98 +28,103 @@ #include "i915_drv.h" #include "intel_drv.h" +struct ddi_buf_trans { + u32 trans1; /* balance leg enable, de-emph level */ + u32 trans2; /* vref sel, vswing */ +}; + /* HDMI/DVI modes ignore everything but the last 2 items. So we share * them for both DP and FDI transports, allowing those ports to * automatically adapt to HDMI connections as well */ -static const u32 hsw_ddi_translations_dp[] = { - 0x00FFFFFF, 0x0006000E, - 0x00D75FFF, 0x0005000A, - 0x00C30FFF, 0x00040006, - 0x80AAAFFF, 0x000B0000, - 0x00FFFFFF, 0x0005000A, - 0x00D75FFF, 0x000C0004, - 0x80C30FFF, 0x000B0000, - 0x00FFFFFF, 0x00040006, - 0x80D75FFF, 0x000B0000, +static const struct ddi_buf_trans hsw_ddi_translations_dp[] = { + { 0x00FFFFFF, 0x0006000E }, + { 0x00D75FFF, 0x0005000A }, + { 0x00C30FFF, 0x00040006 }, + { 0x80AAAFFF, 0x000B0000 }, + { 0x00FFFFFF, 0x0005000A }, + { 0x00D75FFF, 0x000C0004 }, + { 0x80C30FFF, 0x000B0000 }, + { 0x00FFFFFF, 0x00040006 }, + { 0x80D75FFF, 0x000B0000 }, }; -static const u32 hsw_ddi_translations_fdi[] = { - 0x00FFFFFF, 0x0007000E, - 0x00D75FFF, 0x000F000A, - 0x00C30FFF, 0x00060006, - 0x00AAAFFF, 0x001E0000, - 0x00FFFFFF, 0x000F000A, - 0x00D75FFF, 0x00160004, - 0x00C30FFF, 0x001E0000, - 0x00FFFFFF, 0x00060006, - 0x00D75FFF, 0x001E0000, +static const struct ddi_buf_trans hsw_ddi_translations_fdi[] = { + { 0x00FFFFFF, 0x0007000E }, + { 0x00D75FFF, 0x000F000A }, + { 0x00C30FFF, 0x00060006 }, + { 0x00AAAFFF, 0x001E0000 }, + { 0x00FFFFFF, 0x000F000A }, + { 0x00D75FFF, 0x00160004 }, + { 0x00C30FFF, 0x001E0000 }, + { 0x00FFFFFF, 0x00060006 }, + { 0x00D75FFF, 0x001E0000 }, }; -static const u32 hsw_ddi_translations_hdmi[] = { - /* Idx NT mV diff T mV diff db */ - 0x00FFFFFF, 0x0006000E, /* 0: 400 400 0 */ - 0x00E79FFF, 0x000E000C, /* 1: 400 500 2 */ - 0x00D75FFF, 0x0005000A, /* 2: 400 600 3.5 */ - 0x00FFFFFF, 0x0005000A, /* 3: 600 600 0 */ - 0x00E79FFF, 0x001D0007, /* 4: 600 750 2 */ - 0x00D75FFF, 0x000C0004, /* 5: 600 900 3.5 */ - 0x00FFFFFF, 0x00040006, /* 6: 800 800 0 */ - 0x80E79FFF, 0x00030002, /* 7: 800 1000 2 */ - 0x00FFFFFF, 0x00140005, /* 8: 850 850 0 */ - 0x00FFFFFF, 0x000C0004, /* 9: 900 900 0 */ - 0x00FFFFFF, 0x001C0003, /* 10: 950 950 0 */ - 0x80FFFFFF, 0x00030002, /* 11: 1000 1000 0 */ +static const struct ddi_buf_trans hsw_ddi_translations_hdmi[] = { + /* Idx NT mV d T mV d db */ + { 0x00FFFFFF, 0x0006000E }, /* 0: 400 400 0 */ + { 0x00E79FFF, 0x000E000C }, /* 1: 400 500 2 */ + { 0x00D75FFF, 0x0005000A }, /* 2: 400 600 3.5 */ + { 0x00FFFFFF, 0x0005000A }, /* 3: 600 600 0 */ + { 0x00E79FFF, 0x001D0007 }, /* 4: 600 750 2 */ + { 0x00D75FFF, 0x000C0004 }, /* 5: 600 900 3.5 */ + { 0x00FFFFFF, 0x00040006 }, /* 6: 800 800 0 */ + { 0x80E79FFF, 0x00030002 }, /* 7: 800 1000 2 */ + { 0x00FFFFFF, 0x00140005 }, /* 8: 850 850 0 */ + { 0x00FFFFFF, 0x000C0004 }, /* 9: 900 900 0 */ + { 0x00FFFFFF, 0x001C0003 }, /* 10: 950 950 0 */ + { 0x80FFFFFF, 0x00030002 }, /* 11: 1000 1000 0 */ }; -static const u32 bdw_ddi_translations_edp[] = { - 0x00FFFFFF, 0x00000012, - 0x00EBAFFF, 0x00020011, - 0x00C71FFF, 0x0006000F, - 0x00AAAFFF, 0x000E000A, - 0x00FFFFFF, 0x00020011, - 0x00DB6FFF, 0x0005000F, - 0x00BEEFFF, 0x000A000C, - 0x00FFFFFF, 0x0005000F, - 0x00DB6FFF, 0x000A000C, +static const struct ddi_buf_trans bdw_ddi_translations_edp[] = { + { 0x00FFFFFF, 0x00000012 }, + { 0x00EBAFFF, 0x00020011 }, + { 0x00C71FFF, 0x0006000F }, + { 0x00AAAFFF, 0x000E000A }, + { 0x00FFFFFF, 0x00020011 }, + { 0x00DB6FFF, 0x0005000F }, + { 0x00BEEFFF, 0x000A000C }, + { 0x00FFFFFF, 0x0005000F }, + { 0x00DB6FFF, 0x000A000C }, }; -static const u32 bdw_ddi_translations_dp[] = { - 0x00FFFFFF, 0x0007000E, - 0x00D75FFF, 0x000E000A, - 0x00BEFFFF, 0x00140006, - 0x80B2CFFF, 0x001B0002, - 0x00FFFFFF, 0x000E000A, - 0x00D75FFF, 0x00180004, - 0x80CB2FFF, 0x001B0002, - 0x00F7DFFF, 0x00180004, - 0x80D75FFF, 0x001B0002, +static const struct ddi_buf_trans bdw_ddi_translations_dp[] = { + { 0x00FFFFFF, 0x0007000E }, + { 0x00D75FFF, 0x000E000A }, + { 0x00BEFFFF, 0x00140006 }, + { 0x80B2CFFF, 0x001B0002 }, + { 0x00FFFFFF, 0x000E000A }, + { 0x00D75FFF, 0x00180004 }, + { 0x80CB2FFF, 0x001B0002 }, + { 0x00F7DFFF, 0x00180004 }, + { 0x80D75FFF, 0x001B0002 }, }; -static const u32 bdw_ddi_translations_fdi[] = { - 0x00FFFFFF, 0x0001000E, - 0x00D75FFF, 0x0004000A, - 0x00C30FFF, 0x00070006, - 0x00AAAFFF, 0x000C0000, - 0x00FFFFFF, 0x0004000A, - 0x00D75FFF, 0x00090004, - 0x00C30FFF, 0x000C0000, - 0x00FFFFFF, 0x00070006, - 0x00D75FFF, 0x000C0000, +static const struct ddi_buf_trans bdw_ddi_translations_fdi[] = { + { 0x00FFFFFF, 0x0001000E }, + { 0x00D75FFF, 0x0004000A }, + { 0x00C30FFF, 0x00070006 }, + { 0x00AAAFFF, 0x000C0000 }, + { 0x00FFFFFF, 0x0004000A }, + { 0x00D75FFF, 0x00090004 }, + { 0x00C30FFF, 0x000C0000 }, + { 0x00FFFFFF, 0x00070006 }, + { 0x00D75FFF, 0x000C0000 }, }; -static const u32 bdw_ddi_translations_hdmi[] = { - /* Idx NT mV diff T mV diff db */ - 0x00FFFFFF, 0x0007000E, /* 0: 400 400 0 */ - 0x00D75FFF, 0x000E000A, /* 1: 400 600 3.5 */ - 0x00BEFFFF, 0x00140006, /* 2: 400 800 6 */ - 0x00FFFFFF, 0x0009000D, /* 3: 450 450 0 */ - 0x00FFFFFF, 0x000E000A, /* 4: 600 600 0 */ - 0x00D7FFFF, 0x00140006, /* 5: 600 800 2.5 */ - 0x80CB2FFF, 0x001B0002, /* 6: 600 1000 4.5 */ - 0x00FFFFFF, 0x00140006, /* 7: 800 800 0 */ - 0x80E79FFF, 0x001B0002, /* 8: 800 1000 2 */ - 0x80FFFFFF, 0x001B0002, /* 9: 1000 1000 0 */ +static const struct ddi_buf_trans bdw_ddi_translations_hdmi[] = { + /* Idx NT mV d T mV df db */ + { 0x00FFFFFF, 0x0007000E }, /* 0: 400 400 0 */ + { 0x00D75FFF, 0x000E000A }, /* 1: 400 600 3.5 */ + { 0x00BEFFFF, 0x00140006 }, /* 2: 400 800 6 */ + { 0x00FFFFFF, 0x0009000D }, /* 3: 450 450 0 */ + { 0x00FFFFFF, 0x000E000A }, /* 4: 600 600 0 */ + { 0x00D7FFFF, 0x00140006 }, /* 5: 600 800 2.5 */ + { 0x80CB2FFF, 0x001B0002 }, /* 6: 600 1000 4.5 */ + { 0x00FFFFFF, 0x00140006 }, /* 7: 800 800 0 */ + { 0x80E79FFF, 0x001B0002 }, /* 8: 800 1000 2 */ + { 0x80FFFFFF, 0x001B0002 }, /* 9: 1000 1000 0 */ }; enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) @@ -158,25 +163,25 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) u32 reg; int i, n_hdmi_entries, hdmi_800mV_0dB; int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; - const u32 *ddi_translations_fdi; - const u32 *ddi_translations_dp; - const u32 *ddi_translations_edp; - const u32 *ddi_translations_hdmi; - const u32 *ddi_translations; + const struct ddi_buf_trans *ddi_translations_fdi; + const struct ddi_buf_trans *ddi_translations_dp; + const struct ddi_buf_trans *ddi_translations_edp; + const struct ddi_buf_trans *ddi_translations_hdmi; + const struct ddi_buf_trans *ddi_translations; if (IS_BROADWELL(dev)) { ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; ddi_translations_edp = bdw_ddi_translations_edp; ddi_translations_hdmi = bdw_ddi_translations_hdmi; - n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi) / 2; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); hdmi_800mV_0dB = 7; } else if (IS_HASWELL(dev)) { ddi_translations_fdi = hsw_ddi_translations_fdi; ddi_translations_dp = hsw_ddi_translations_dp; ddi_translations_edp = hsw_ddi_translations_dp; ddi_translations_hdmi = hsw_ddi_translations_hdmi; - n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi) / 2; + n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi); hdmi_800mV_0dB = 6; } else { WARN(1, "ddi translation table missing\n"); @@ -184,7 +189,7 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; ddi_translations_hdmi = bdw_ddi_translations_hdmi; - n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi) / 2; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); hdmi_800mV_0dB = 7; } @@ -211,7 +216,9 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) for (i = 0, reg = DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { - I915_WRITE(reg, ddi_translations[i]); + I915_WRITE(reg, ddi_translations[i].trans1); + reg += 4; + I915_WRITE(reg, ddi_translations[i].trans2); reg += 4; } @@ -221,10 +228,10 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) hdmi_level = hdmi_800mV_0dB; /* Entry 9 is for HDMI: */ - for (i = 0; i < 2; i++) { - I915_WRITE(reg, ddi_translations_hdmi[hdmi_level * 2 + i]); - reg += 4; - } + I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans1); + reg += 4; + I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans2); + reg += 4; } /* Program DDI buffers translations for DP. By default, program ports A-D in DP @@ -241,18 +248,6 @@ void intel_prepare_ddi(struct drm_device *dev) intel_prepare_ddi_buffers(dev, port); } -static const long hsw_ddi_buf_ctl_values[] = { - DDI_BUF_EMP_400MV_0DB_HSW, - DDI_BUF_EMP_400MV_3_5DB_HSW, - DDI_BUF_EMP_400MV_6DB_HSW, - DDI_BUF_EMP_400MV_9_5DB_HSW, - DDI_BUF_EMP_600MV_0DB_HSW, - DDI_BUF_EMP_600MV_3_5DB_HSW, - DDI_BUF_EMP_600MV_6DB_HSW, - DDI_BUF_EMP_800MV_0DB_HSW, - DDI_BUF_EMP_800MV_3_5DB_HSW -}; - static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, enum port port) { @@ -312,7 +307,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ - for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) { + for (i = 0; i < ARRAY_SIZE(hsw_ddi_translations_fdi) * 2; i++) { /* Configure DP_TP_CTL with auto-training */ I915_WRITE(DP_TP_CTL(PORT_E), DP_TP_CTL_FDI_AUTOTRAIN | @@ -327,7 +322,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | ((intel_crtc->config.fdi_lanes - 1) << 1) | - hsw_ddi_buf_ctl_values[i / 2]); + DDI_BUF_TRANS_SELECT(i / 2)); POSTING_READ(DDI_BUF_CTL(PORT_E)); udelay(600); @@ -402,7 +397,7 @@ void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder) enc_to_dig_port(&encoder->base); intel_dp->DP = intel_dig_port->saved_port_bits | - DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; + DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0); intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); } @@ -429,7 +424,7 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) } #define LC_FREQ 2700 -#define LC_FREQ_2K (LC_FREQ * 2000) +#define LC_FREQ_2K U64_C(LC_FREQ * 2000) #define P_MIN 2 #define P_MAX 64 @@ -441,7 +436,11 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) #define VCO_MIN 2400 #define VCO_MAX 4800 -#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a)) +#define abs_diff(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + (void) (&__a == &__b); \ + __a > __b ? (__a - __b) : (__b - __a); }) struct wrpll_rnp { unsigned p, n2, r2; @@ -551,9 +550,9 @@ static void wrpll_update_rnp(uint64_t freq2k, unsigned budget, */ 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)); + 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; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 32ede71fbe7f..1386086ec245 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -900,7 +900,8 @@ static void g4x_wait_for_vblank(struct drm_device *dev, int pipe) frame = I915_READ(frame_reg); if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50)) - WARN(1, "vblank wait timed out\n"); + WARN(1, "vblank wait on pipe %c timed out\n", + pipe_name(pipe)); } /** @@ -941,7 +942,8 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) if (wait_for(I915_READ(pipestat_reg) & PIPE_VBLANK_INTERRUPT_STATUS, 50)) - DRM_DEBUG_KMS("vblank wait timed out\n"); + DRM_DEBUG_KMS("vblank wait on pipe %c timed out\n", + pipe_name(pipe)); } static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) @@ -965,8 +967,7 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) /* * intel_wait_for_pipe_off - wait for pipe to turn off - * @dev: drm device - * @pipe: pipe to wait for + * @crtc: crtc whose pipe to wait for * * After disabling a pipe, we can't wait for vblank in the usual way, * spinning on the vblank interrupt status bit, since we won't actually @@ -980,11 +981,12 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) * ends up stopping at the start of the next frame). * */ -void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) +static void intel_wait_for_pipe_off(struct intel_crtc *crtc) { + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum pipe pipe = crtc->pipe; if (INTEL_INFO(dev)->gen >= 4) { int reg = PIPECONF(cpu_transcoder); @@ -1193,27 +1195,40 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv, static void assert_panel_unlocked(struct drm_i915_private *dev_priv, enum pipe pipe) { - int pp_reg, lvds_reg; + struct drm_device *dev = dev_priv->dev; + int pp_reg; u32 val; enum pipe panel_pipe = PIPE_A; bool locked = true; - if (HAS_PCH_SPLIT(dev_priv->dev)) { + if (WARN_ON(HAS_DDI(dev))) + return; + + if (HAS_PCH_SPLIT(dev)) { + u32 port_sel; + pp_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; + port_sel = I915_READ(PCH_PP_ON_DELAYS) & PANEL_PORT_SELECT_MASK; + + if (port_sel == PANEL_PORT_SELECT_LVDS && + I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) + panel_pipe = PIPE_B; + /* XXX: else fix for eDP */ + } else if (IS_VALLEYVIEW(dev)) { + /* presumably write lock depends on pipe, not port select */ + pp_reg = VLV_PIPE_PP_CONTROL(pipe); + panel_pipe = pipe; } else { pp_reg = PP_CONTROL; - lvds_reg = LVDS; + if (I915_READ(LVDS) & LVDS_PIPEB_SELECT) + panel_pipe = PIPE_B; } val = I915_READ(pp_reg); if (!(val & PANEL_POWER_ON) || - ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)) + ((val & PANEL_UNLOCK_MASK) == PANEL_UNLOCK_REGS)) locked = false; - if (I915_READ(lvds_reg) & LVDS_PIPEB_SELECT) - panel_pipe = PIPE_B; - WARN(panel_pipe == pipe && locked, "panel assertion failure, pipe %c regs locked\n", pipe_name(pipe)); @@ -1246,8 +1261,9 @@ void assert_pipe(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - /* if we need the pipe A quirk it must be always on */ - if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) + /* if we need the pipe quirk it must be always on */ + if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) state = true; if (!intel_display_power_enabled(dev_priv, @@ -1301,7 +1317,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, } /* Need to check both planes against the pipe */ - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { reg = DSPCNTR(i); val = I915_READ(reg); cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >> @@ -1533,7 +1549,7 @@ static void vlv_enable_pll(struct intel_crtc *crtc) BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); /* PLL is protected by panel, make sure we can write it */ - if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) + if (IS_MOBILE(dev_priv->dev)) assert_panel_unlocked(dev_priv, crtc->pipe); I915_WRITE(reg, dpll); @@ -1653,8 +1669,9 @@ static void i9xx_enable_pll(struct intel_crtc *crtc) */ static void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) + /* Don't disable pipe or pipe PLLs if needed */ + if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) return; /* Make sure the pipe isn't still relying on us */ @@ -1847,7 +1864,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, uint32_t reg, val, pipeconf_val; /* PCH only available on ILK+ */ - BUG_ON(INTEL_INFO(dev)->gen < 5); + BUG_ON(!HAS_PCH_SPLIT(dev)); /* Make sure PCH DPLL is enabled */ assert_shared_dpll_enabled(dev_priv, @@ -1900,7 +1917,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, u32 val, pipeconf_val; /* PCH only available on ILK+ */ - BUG_ON(INTEL_INFO(dev_priv->dev)->gen < 5); + BUG_ON(!HAS_PCH_SPLIT(dev_priv->dev)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); @@ -2022,8 +2039,8 @@ static void intel_enable_pipe(struct intel_crtc *crtc) reg = PIPECONF(cpu_transcoder); val = I915_READ(reg); if (val & PIPECONF_ENABLE) { - WARN_ON(!(pipe == PIPE_A && - dev_priv->quirks & QUIRK_PIPEA_FORCE)); + WARN_ON(!((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))); return; } @@ -2033,21 +2050,19 @@ static void intel_enable_pipe(struct intel_crtc *crtc) /** * intel_disable_pipe - disable a pipe, asserting requirements - * @dev_priv: i915 private structure - * @pipe: pipe to disable - * - * Disable @pipe, making sure that various hardware specific requirements - * are met, if applicable, e.g. plane disabled, panel fitter off, etc. + * @crtc: crtc whose pipes is to be disabled * - * @pipe should be %PIPE_A or %PIPE_B. + * Disable the pipe of @crtc, making sure that various hardware + * specific requirements are met, if applicable, e.g. plane + * disabled, panel fitter off, etc. * * Will wait until the pipe has shut down before returning. */ -static void intel_disable_pipe(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_disable_pipe(struct intel_crtc *crtc) { - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum pipe pipe = crtc->pipe; int reg; u32 val; @@ -2059,17 +2074,26 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv, assert_cursor_disabled(dev_priv, pipe); assert_sprites_disabled(dev_priv, pipe); - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) - return; - reg = PIPECONF(cpu_transcoder); val = I915_READ(reg); if ((val & PIPECONF_ENABLE) == 0) return; - I915_WRITE(reg, val & ~PIPECONF_ENABLE); - intel_wait_for_pipe_off(dev_priv->dev, pipe); + /* + * Double wide has implications for planes + * so best keep it disabled when not needed. + */ + if (crtc->config.double_wide) + val &= ~PIPECONF_DOUBLE_WIDE; + + /* Don't disable pipe or pipe PLLs if needed */ + if (!(pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) && + !(pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) + val &= ~PIPECONF_ENABLE; + + I915_WRITE(reg, val); + if ((val & PIPECONF_ENABLE) == 0) + intel_wait_for_pipe_off(crtc); } /* @@ -2390,11 +2414,12 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; u32 reg = DSPCNTR(plane); + int pixel_size; if (!intel_crtc->primary_enabled) { I915_WRITE(reg, 0); @@ -2406,6 +2431,12 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, return; } + obj = intel_fb_obj(fb); + if (WARN_ON(obj == NULL)) + return; + + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + dspcntr = DISPPLANE_GAMMA_ENABLE; dspcntr |= DISPLAY_PLANE_ENABLE; @@ -2461,20 +2492,33 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, if (IS_G4X(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); - - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; if (INTEL_INFO(dev)->gen >= 4) { intel_crtc->dspaddr_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, - fb->bits_per_pixel / 8, + pixel_size, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; } else { intel_crtc->dspaddr_offset = linear_offset; } + if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) { + dspcntr |= DISPPLANE_ROTATE_180; + + x += (intel_crtc->config.pipe_src_w - 1); + y += (intel_crtc->config.pipe_src_h - 1); + + /* Finding the last pixel of the last line of the display + data and adding to linear_offset*/ + linear_offset += + (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] + + (intel_crtc->config.pipe_src_w - 1) * pixel_size; + } + + I915_WRITE(reg, dspcntr); + DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", i915_gem_obj_ggtt_offset(obj), linear_offset, x, y, fb->pitches[0]); @@ -2496,11 +2540,12 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; u32 reg = DSPCNTR(plane); + int pixel_size; if (!intel_crtc->primary_enabled) { I915_WRITE(reg, 0); @@ -2509,6 +2554,12 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, return; } + obj = intel_fb_obj(fb); + if (WARN_ON(obj == NULL)) + return; + + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + dspcntr = DISPPLANE_GAMMA_ENABLE; dspcntr |= DISPLAY_PLANE_ENABLE; @@ -2549,14 +2600,28 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); - - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; intel_crtc->dspaddr_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, - fb->bits_per_pixel / 8, + pixel_size, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; + if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) { + dspcntr |= DISPPLANE_ROTATE_180; + + if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { + x += (intel_crtc->config.pipe_src_w - 1); + y += (intel_crtc->config.pipe_src_h - 1); + + /* Finding the last pixel of the last line of the display + data and adding to linear_offset*/ + linear_offset += + (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] + + (intel_crtc->config.pipe_src_w - 1) * pixel_size; + } + } + + I915_WRITE(reg, dspcntr); DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", i915_gem_obj_ggtt_offset(obj), linear_offset, x, y, @@ -3340,23 +3405,54 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev) return false; } +static void page_flip_completed(struct intel_crtc *intel_crtc) +{ + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + struct intel_unpin_work *work = intel_crtc->unpin_work; + + /* ensure that the unpin work is consistent wrt ->pending. */ + smp_rmb(); + intel_crtc->unpin_work = NULL; + + if (work->event) + drm_send_vblank_event(intel_crtc->base.dev, + intel_crtc->pipe, + work->event); + + drm_crtc_vblank_put(&intel_crtc->base); + + wake_up_all(&dev_priv->pending_flip_queue); + queue_work(dev_priv->wq, &work->work); + + trace_i915_flip_complete(intel_crtc->plane, + work->pending_flip_obj); +} + void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (crtc->primary->fb == NULL) - return; - WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + if (WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue, + !intel_crtc_has_pending_flip(crtc), + 60*HZ) == 0)) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long flags; - WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue, - !intel_crtc_has_pending_flip(crtc), - 60*HZ) == 0); + spin_lock_irqsave(&dev->event_lock, flags); + if (intel_crtc->unpin_work) { + WARN_ONCE(1, "Removing stuck page flip\n"); + page_flip_completed(intel_crtc); + } + spin_unlock_irqrestore(&dev->event_lock, flags); + } - mutex_lock(&dev->struct_mutex); - intel_finish_fb(crtc->primary->fb); - mutex_unlock(&dev->struct_mutex); + if (crtc->primary->fb) { + mutex_lock(&dev->struct_mutex); + intel_finish_fb(crtc->primary->fb); + mutex_unlock(&dev->struct_mutex); + } } /* Program iCLKIP clock to the desired frequency */ @@ -4178,7 +4274,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, pipe, false); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); + ironlake_pfit_disable(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) @@ -4226,7 +4323,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) 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; enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; if (!intel_crtc->active) @@ -4241,7 +4337,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); if (intel_crtc->config.dp_encoder_is_mst) intel_ddi_set_vc_payload_alloc(crtc, false); @@ -4831,7 +4927,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) */ intel_wait_for_vblank(dev, pipe); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); i9xx_pfit_disable(intel_crtc); @@ -6000,9 +6096,9 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) pipeconf = 0; - if (dev_priv->quirks & QUIRK_PIPEA_FORCE && - I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE) - pipeconf |= PIPECONF_ENABLE; + if ((intel_crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (intel_crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) + pipeconf |= I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE; if (intel_crtc->config.double_wide) pipeconf |= PIPECONF_DOUBLE_WIDE; @@ -8274,7 +8370,6 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, if (!obj) { DRM_DEBUG_KMS("cursor off\n"); addr = 0; - obj = NULL; mutex_lock(&dev->struct_mutex); goto finish; } @@ -8994,12 +9089,13 @@ static void intel_mark_fb_busy(struct drm_device *dev, unsigned frontbuffer_bits, struct intel_engine_cs *ring) { + struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; if (!i915.powersave) return; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (!(frontbuffer_bits & INTEL_FRONTBUFFER_ALL_MASK(pipe))) continue; @@ -9069,6 +9165,14 @@ void intel_frontbuffer_flush(struct drm_device *dev, intel_mark_fb_busy(dev, frontbuffer_bits, NULL); intel_edp_psr_flush(dev, frontbuffer_bits); + + /* + * FIXME: Unconditional fbc flushing here is a rather gross hack and + * needs to be reworked into a proper frontbuffer tracking scheme like + * psr employs. + */ + if (IS_BROADWELL(dev)) + gen8_fbc_sw_flush(dev, FBC_REND_CACHE_CLEAN); } /** @@ -9201,7 +9305,6 @@ static void intel_unpin_work_fn(struct work_struct *__work) static void do_intel_finish_page_flip(struct drm_device *dev, struct drm_crtc *crtc) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_unpin_work *work; unsigned long flags; @@ -9221,23 +9324,9 @@ static void do_intel_finish_page_flip(struct drm_device *dev, return; } - /* and that the unpin work is consistent wrt ->pending. */ - smp_rmb(); - - intel_crtc->unpin_work = NULL; - - if (work->event) - drm_send_vblank_event(dev, intel_crtc->pipe, work->event); - - drm_crtc_vblank_put(crtc); + page_flip_completed(intel_crtc); spin_unlock_irqrestore(&dev->event_lock, flags); - - wake_up_all(&dev_priv->pending_flip_queue); - - queue_work(dev_priv->wq, &work->work); - - trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); } void intel_finish_page_flip(struct drm_device *dev, int pipe) @@ -9717,6 +9806,65 @@ static int intel_default_queue_flip(struct drm_device *dev, return -ENODEV; } +static bool __intel_pageflip_stall_check(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_unpin_work *work = intel_crtc->unpin_work; + u32 addr; + + if (atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE) + return true; + + if (!work->enable_stall_check) + return false; + + if (work->flip_ready_vblank == 0) { + if (work->flip_queued_ring && + !i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + work->flip_queued_seqno)) + return false; + + work->flip_ready_vblank = drm_vblank_count(dev, intel_crtc->pipe); + } + + if (drm_vblank_count(dev, intel_crtc->pipe) - work->flip_ready_vblank < 3) + return false; + + /* Potential stall - if we see that the flip has happened, + * assume a missed interrupt. */ + if (INTEL_INFO(dev)->gen >= 4) + addr = I915_HI_DISPBASE(I915_READ(DSPSURF(intel_crtc->plane))); + else + addr = I915_READ(DSPADDR(intel_crtc->plane)); + + /* There is a potential issue here with a false positive after a flip + * to the same address. We could address this by checking for a + * non-incrementing frame counter. + */ + return addr == work->gtt_offset; +} + +void intel_check_page_flip(struct drm_device *dev, int pipe) +{ + 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; + + if (crtc == NULL) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + if (intel_crtc->unpin_work && __intel_pageflip_stall_check(dev, crtc)) { + WARN_ONCE(1, "Kicking stuck page flip: queued at %d, now %d\n", + intel_crtc->unpin_work->flip_queued_vblank, drm_vblank_count(dev, pipe)); + page_flip_completed(intel_crtc); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -9733,6 +9881,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, unsigned long flags; int ret; + //trigger software GT busyness calculation + gen8_flip_interrupt(dev); + /* * drm_mode_page_flip_ioctl() should already catch this, but double * check to be safe. In the future we may enable pageflipping from @@ -9773,12 +9924,20 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, /* We borrow the event spin lock for protecting unpin_work */ spin_lock_irqsave(&dev->event_lock, flags); if (intel_crtc->unpin_work) { - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(work); - drm_crtc_vblank_put(crtc); + /* Before declaring the flip queue wedged, check if + * the hardware completed the operation behind our backs. + */ + if (__intel_pageflip_stall_check(dev, crtc)) { + DRM_DEBUG_DRIVER("flip queue: previous flip completed, continuing\n"); + page_flip_completed(intel_crtc); + } else { + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + spin_unlock_irqrestore(&dev->event_lock, flags); - DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); - return -EBUSY; + drm_crtc_vblank_put(crtc); + kfree(work); + return -EBUSY; + } } intel_crtc->unpin_work = work; spin_unlock_irqrestore(&dev->event_lock, flags); @@ -9798,8 +9957,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->pending_flip_obj = obj; - work->enable_stall_check = true; - atomic_inc(&intel_crtc->unpin_work_count); intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); @@ -9828,14 +9985,26 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->gtt_offset = i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset; - if (use_mmio_flip(ring, obj)) + if (use_mmio_flip(ring, obj)) { ret = intel_queue_mmio_flip(dev, crtc, fb, obj, ring, page_flip_flags); - else + if (ret) + goto cleanup_unpin; + + work->flip_queued_seqno = obj->last_write_seqno; + work->flip_queued_ring = obj->ring; + } else { ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, - page_flip_flags); - if (ret) - goto cleanup_unpin; + page_flip_flags); + if (ret) + goto cleanup_unpin; + + work->flip_queued_seqno = intel_ring_get_seqno(ring); + work->flip_queued_ring = ring; + } + + work->flip_queued_vblank = drm_vblank_count(dev, intel_crtc->pipe); + work->enable_stall_check = true; i915_gem_track_fb(work->old_fb_obj, obj, INTEL_FRONTBUFFER_PRIMARY(pipe)); @@ -10723,8 +10892,9 @@ check_crtc_state(struct drm_device *dev) active = dev_priv->display.get_pipe_config(crtc, &pipe_config); - /* hw state is inconsistent with the pipe A quirk */ - if (crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) + /* hw state is inconsistent with the pipe quirk */ + if ((crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) active = crtc->active; for_each_intel_encoder(dev, encoder) { @@ -11596,6 +11766,7 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->fb); @@ -11618,6 +11789,21 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0, .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0, }; + const struct { + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + uint32_t src_x, src_y, src_w, src_h; + } orig = { + .crtc_x = crtc_x, + .crtc_y = crtc_y, + .crtc_w = crtc_w, + .crtc_h = crtc_h, + .src_x = src_x, + .src_y = src_y, + .src_w = src_w, + .src_h = src_h, + }; + struct intel_plane *intel_plane = to_intel_plane(plane); bool visible; int ret; @@ -11692,15 +11878,42 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); - return 0; - } + } else { + if (intel_crtc && intel_crtc->active && + intel_crtc->primary_enabled) { + /* + * FBC does not work on some platforms for rotated + * planes, so disable it when rotation is not 0 and + * update it when rotation is set back to 0. + * + * FIXME: This is redundant with the fbc update done in + * the primary plane enable function except that that + * one is done too late. We eventually need to unify + * this. + */ + if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && + dev_priv->fbc.plane == intel_crtc->plane && + intel_plane->rotation != BIT(DRM_ROTATE_0)) { + intel_disable_fbc(dev); + } + } + ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb); + if (ret) + return ret; - ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb); - if (ret) - return ret; + if (!intel_crtc->primary_enabled) + intel_enable_primary_hw_plane(plane, crtc); + } - if (!intel_crtc->primary_enabled) - intel_enable_primary_hw_plane(plane, crtc); + intel_plane->crtc_x = orig.crtc_x; + intel_plane->crtc_y = orig.crtc_y; + intel_plane->crtc_w = orig.crtc_w; + intel_plane->crtc_h = orig.crtc_h; + intel_plane->src_x = orig.src_x; + intel_plane->src_y = orig.src_y; + intel_plane->src_w = orig.src_w; + intel_plane->src_h = orig.src_h; + intel_plane->obj = obj; return 0; } @@ -11717,6 +11930,7 @@ static const struct drm_plane_funcs intel_primary_plane_funcs = { .update_plane = intel_primary_plane_setplane, .disable_plane = intel_primary_plane_disable, .destroy = intel_plane_destroy, + .set_property = intel_plane_set_property }; static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, @@ -11734,6 +11948,7 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, primary->max_downscale = 1; primary->pipe = pipe; primary->plane = pipe; + primary->rotation = BIT(DRM_ROTATE_0); if (HAS_FBC(dev) && INTEL_INFO(dev)->gen < 4) primary->plane = !pipe; @@ -11749,6 +11964,19 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, &intel_primary_plane_funcs, intel_primary_formats, num_formats, DRM_PLANE_TYPE_PRIMARY); + + if (INTEL_INFO(dev)->gen >= 4) { + if (!dev->mode_config.rotation_property) + dev->mode_config.rotation_property = + drm_mode_create_rotation_property(dev, + BIT(DRM_ROTATE_0) | + BIT(DRM_ROTATE_180)); + if (dev->mode_config.rotation_property) + drm_object_attach_property(&primary->base.base, + dev->mode_config.rotation_property, + primary->rotation); + } + return &primary->base; } @@ -12423,7 +12651,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.write_eld = ironlake_write_eld; dev_priv->display.modeset_global_resources = ivb_modeset_global_resources; - } else if (IS_HASWELL(dev) || IS_GEN8(dev)) { + } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; dev_priv->display.write_eld = haswell_write_eld; dev_priv->display.modeset_global_resources = @@ -12461,6 +12689,8 @@ static void intel_init_display(struct drm_device *dev) } intel_panel_init_backlight_funcs(dev); + + mutex_init(&dev_priv->pps_mutex); } /* @@ -12476,6 +12706,14 @@ static void quirk_pipea_force(struct drm_device *dev) DRM_INFO("applying pipe a force quirk\n"); } +static void quirk_pipeb_force(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->quirks |= QUIRK_PIPEB_FORCE; + DRM_INFO("applying pipe b force quirk\n"); +} + /* * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason */ @@ -12550,6 +12788,12 @@ static struct intel_quirk intel_quirks[] = { /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, + /* 830 needs to leave pipe A & dpll A up */ + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, + + /* 830 needs to leave pipe B & dpll B up */ + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipeb_force }, + /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, @@ -12623,7 +12867,11 @@ static void i915_disable_vga(struct drm_device *dev) vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); udelay(300); - I915_WRITE(vga_reg, VGA_DISP_DISABLE); + /* + * Fujitsu-Siemens Lifebook S6010 (830) has problems resuming + * from S3 without preserving (some of?) the other bits. + */ + I915_WRITE(vga_reg, dev_priv->bios_vgacntr | VGA_DISP_DISABLE); POSTING_READ(vga_reg); } @@ -12698,7 +12946,7 @@ void intel_modeset_init(struct drm_device *dev) INTEL_INFO(dev)->num_pipes, INTEL_INFO(dev)->num_pipes > 1 ? "s" : ""); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { intel_crtc_init(dev, pipe); for_each_sprite(pipe, sprite) { ret = intel_plane_init(dev, pipe, sprite); @@ -12712,6 +12960,8 @@ void intel_modeset_init(struct drm_device *dev) intel_shared_dpll_init(dev); + /* save the BIOS value before clobbering it */ + dev_priv->bios_vgacntr = I915_READ(i915_vgacntrl_reg(dev)); /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); @@ -12889,7 +13139,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) } } - if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) { + if (crtc->active || HAS_GMCH_DISPLAY(dev)) { /* * We start out with underrun reporting disabled to avoid races. * For correct bookkeeping mark this on active crtcs. @@ -13104,7 +13354,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, intel_sanitize_encoder(encoder); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); intel_sanitize_crtc(crtc); intel_dump_pipe_config(crtc, &crtc->config, "[setup_hw_state]"); @@ -13132,7 +13382,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, * We need to use raw interfaces for restoring state to avoid * checking (bogus) intermediate states. */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; @@ -13353,7 +13603,7 @@ intel_display_capture_error_state(struct drm_device *dev) if (IS_HASWELL(dev) || IS_BROADWELL(dev)) error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { error->pipe[i].power_domain_on = intel_display_power_enabled_unlocked(dev_priv, POWER_DOMAIN_PIPE(i)); @@ -13417,6 +13667,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, struct drm_device *dev, struct intel_display_error_state *error) { + struct drm_i915_private *dev_priv = dev->dev_private; int i; if (!error) @@ -13426,7 +13677,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, if (IS_HASWELL(dev) || IS_BROADWELL(dev)) err_printf(m, "PWR_WELL_CTL2: %08x\n", error->power_well_driver); - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { err_printf(m, "Pipe [%d]:\n", i); err_printf(m, " Power: %s\n", error->pipe[i].power_domain_on ? "on" : "off"); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a12a4d3363fd..2a26774ddb68 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -111,7 +111,7 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) } static void intel_dp_link_down(struct intel_dp *intel_dp); -static bool _edp_panel_vdd_on(struct intel_dp *intel_dp); +static bool edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); int @@ -290,32 +290,201 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, struct intel_dp *intel_dp, struct edp_power_seq *out); +static void pps_lock(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; + + /* + * See vlv_power_sequencer_reset() why we need + * a power domain reference here. + */ + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_get(dev_priv, power_domain); + + mutex_lock(&dev_priv->pps_mutex); +} + +static void pps_unlock(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; + + mutex_unlock(&dev_priv->pps_mutex); + + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_put(dev_priv, power_domain); +} + static enum pipe vlv_power_sequencer_pipe(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum port port = intel_dig_port->port; - enum pipe pipe; + struct intel_encoder *encoder; + unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); + struct edp_power_seq power_seq; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (intel_dp->pps_pipe != INVALID_PIPE) + return intel_dp->pps_pipe; + + /* + * We don't have power sequencer currently. + * Pick one that's not used by other ports. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + struct intel_dp *tmp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + tmp = enc_to_intel_dp(&encoder->base); + + if (tmp->pps_pipe != INVALID_PIPE) + pipes &= ~(1 << tmp->pps_pipe); + } + + /* + * Didn't find one. This should not happen since there + * are two power sequencers and up to two eDP ports. + */ + if (WARN_ON(pipes == 0)) + return PIPE_A; + + intel_dp->pps_pipe = ffs(pipes) - 1; + + DRM_DEBUG_KMS("picked pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pps_pipe), + port_name(intel_dig_port->port)); - /* modeset should have pipe */ - if (crtc) - return to_intel_crtc(crtc)->pipe; + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + + return intel_dp->pps_pipe; +} + +typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv, + enum pipe pipe); + +static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return I915_READ(VLV_PIPE_PP_STATUS(pipe)) & PP_ON; +} + +static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return I915_READ(VLV_PIPE_PP_CONTROL(pipe)) & EDP_FORCE_VDD; +} + +static bool vlv_pipe_any(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return true; +} + +static enum pipe +vlv_initial_pps_pipe(struct drm_i915_private *dev_priv, + enum port port, + vlv_pipe_check pipe_check) +{ + enum pipe pipe; - /* init time, try to find a pipe with this port selected */ for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) & PANEL_PORT_SELECT_MASK; - if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B) - return pipe; - if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C) - return pipe; + + if (port_sel != PANEL_PORT_SELECT_VLV(port)) + continue; + + if (!pipe_check(dev_priv, pipe)) + continue; + + return pipe; + } + + return INVALID_PIPE; +} + +static void +vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct edp_power_seq power_seq; + enum port port = intel_dig_port->port; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* try to find a pipe with this port selected */ + /* first pick one where the panel is on */ + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_pp_on); + /* didn't find one? pick one where vdd is on */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_vdd_on); + /* didn't find one? pick one with just the correct port */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_any); + + /* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */ + if (intel_dp->pps_pipe == INVALID_PIPE) { + DRM_DEBUG_KMS("no initial power sequencer for port %c\n", + port_name(port)); + return; } - /* shrug */ - return PIPE_A; + DRM_DEBUG_KMS("initial power sequencer for port %c: pipe %c\n", + port_name(port), pipe_name(intel_dp->pps_pipe)); + + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); +} + +void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + struct intel_encoder *encoder; + + if (WARN_ON(!IS_VALLEYVIEW(dev))) + return; + + /* + * We can't grab pps_mutex here due to deadlock with power_domain + * mutex when power_domain functions are called while holding pps_mutex. + * That also means that in order to use pps_pipe the code needs to + * hold both a power domain reference and pps_mutex, and the power domain + * reference get/put must be done while _not_ holding pps_mutex. + * pps_{lock,unlock}() do these steps in the correct order, so one + * should use them always. + */ + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + struct intel_dp *intel_dp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + intel_dp->pps_pipe = INVALID_PIPE; + } } static u32 _pp_ctrl_reg(struct intel_dp *intel_dp) @@ -349,12 +518,15 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code, struct drm_i915_private *dev_priv = dev->dev_private; u32 pp_div; u32 pp_ctrl_reg, pp_div_reg; - enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); if (!is_edp(intel_dp) || code != SYS_RESTART) return 0; + pps_lock(intel_dp); + if (IS_VALLEYVIEW(dev)) { + enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe); pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); pp_div = I915_READ(pp_div_reg); @@ -366,6 +538,8 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code, msleep(intel_dp->panel_power_cycle_delay); } + pps_unlock(intel_dp); + return 0; } @@ -374,6 +548,8 @@ static bool edp_have_panel_power(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + lockdep_assert_held(&dev_priv->pps_mutex); + return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0; } @@ -381,13 +557,10 @@ static bool edp_have_panel_vdd(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - enum intel_display_power_domain power_domain; - power_domain = intel_display_port_power_domain(intel_encoder); - return intel_display_power_enabled(dev_priv, power_domain) && - (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0; + lockdep_assert_held(&dev_priv->pps_mutex); + + return I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD; } static void @@ -535,7 +708,15 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, bool has_aux_irq = HAS_AUX_IRQ(dev); bool vdd; - vdd = _edp_panel_vdd_on(intel_dp); + pps_lock(intel_dp); + + /* + * We will be called with VDD already enabled for dpcd/edid/oui reads. + * In such cases we want to leave VDD enabled and it's up to upper layers + * to turn it off. But for eg. i2c-dev access we need to turn it on/off + * ourselves. + */ + vdd = edp_panel_vdd_on(intel_dp); /* dp aux is extremely sensitive to irq latency, hence request the * lowest possible wakeup latency and so prevent the cpu from going into @@ -644,6 +825,8 @@ out: if (vdd) edp_panel_vdd_off(intel_dp, false); + pps_unlock(intel_dp); + return ret; } @@ -1098,6 +1281,8 @@ static void wait_panel_status(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; u32 pp_stat_reg, pp_ctrl_reg; + lockdep_assert_held(&dev_priv->pps_mutex); + pp_stat_reg = _pp_stat_reg(intel_dp); pp_ctrl_reg = _pp_ctrl_reg(intel_dp); @@ -1161,13 +1346,20 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 control; + lockdep_assert_held(&dev_priv->pps_mutex); + control = I915_READ(_pp_ctrl_reg(intel_dp)); control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; return control; } -static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) +/* + * Must be paired with edp_panel_vdd_off(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_edp_panel_vdd_{on,off}() calls. + */ +static bool edp_panel_vdd_on(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); @@ -1178,6 +1370,8 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) u32 pp_stat_reg, pp_ctrl_reg; bool need_to_disable = !intel_dp->want_panel_vdd; + lockdep_assert_held(&dev_priv->pps_mutex); + if (!is_edp(intel_dp)) return false; @@ -1215,62 +1409,76 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) return need_to_disable; } +/* + * Must be paired with intel_edp_panel_vdd_off() or + * intel_edp_panel_off(). + * Nested calls to these functions are not allowed since + * we drop the lock. Caller must use some higher level + * locking to prevent nested calls from other threads. + */ void intel_edp_panel_vdd_on(struct intel_dp *intel_dp) { - if (is_edp(intel_dp)) { - bool vdd = _edp_panel_vdd_on(intel_dp); + bool vdd; - WARN(!vdd, "eDP VDD already requested on\n"); - } + if (!is_edp(intel_dp)) + return; + + pps_lock(intel_dp); + vdd = edp_panel_vdd_on(intel_dp); + pps_unlock(intel_dp); + + WARN(!vdd, "eDP VDD already requested on\n"); } static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = + dp_to_dig_port(intel_dp); + struct intel_encoder *intel_encoder = &intel_dig_port->base; + enum intel_display_power_domain power_domain; u32 pp; u32 pp_stat_reg, pp_ctrl_reg; - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + lockdep_assert_held(&dev_priv->pps_mutex); - if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) { - struct intel_digital_port *intel_dig_port = - dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - enum intel_display_power_domain power_domain; + WARN_ON(intel_dp->want_panel_vdd); - DRM_DEBUG_KMS("Turning eDP VDD off\n"); + if (!edp_have_panel_vdd(intel_dp)) + return; - pp = ironlake_get_pp_control(intel_dp); - pp &= ~EDP_FORCE_VDD; + DRM_DEBUG_KMS("Turning eDP VDD off\n"); - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - pp_stat_reg = _pp_stat_reg(intel_dp); + pp = ironlake_get_pp_control(intel_dp); + pp &= ~EDP_FORCE_VDD; - I915_WRITE(pp_ctrl_reg, pp); - POSTING_READ(pp_ctrl_reg); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + pp_stat_reg = _pp_stat_reg(intel_dp); - /* Make sure sequencer is idle before allowing subsequent activity */ - DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", - I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); - if ((pp & POWER_TARGET_ON) == 0) - intel_dp->last_power_cycle = jiffies; + /* Make sure sequencer is idle before allowing subsequent activity */ + DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_put(dev_priv, power_domain); - } + if ((pp & POWER_TARGET_ON) == 0) + intel_dp->last_power_cycle = jiffies; + + power_domain = intel_display_port_power_domain(intel_encoder); + intel_display_power_put(dev_priv, power_domain); } static void edp_panel_vdd_work(struct work_struct *__work) { struct intel_dp *intel_dp = container_of(to_delayed_work(__work), struct intel_dp, panel_vdd_work); - struct drm_device *dev = intel_dp_to_dev(intel_dp); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_lock(intel_dp); + if (!intel_dp->want_panel_vdd) + edp_panel_vdd_off_sync(intel_dp); + pps_unlock(intel_dp); } static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) @@ -1286,8 +1494,18 @@ static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) schedule_delayed_work(&intel_dp->panel_vdd_work, delay); } +/* + * Must be paired with edp_panel_vdd_on(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_edp_panel_vdd_{on,off}() calls. + */ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) { + struct drm_i915_private *dev_priv = + intel_dp_to_dev(intel_dp)->dev_private; + + lockdep_assert_held(&dev_priv->pps_mutex); + if (!is_edp(intel_dp)) return; @@ -1301,6 +1519,22 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) edp_panel_vdd_schedule_off(intel_dp); } +/* + * Must be paired with intel_edp_panel_vdd_on(). + * Nested calls to these functions are not allowed since + * we drop the lock. Caller must use some higher level + * locking to prevent nested calls from other threads. + */ +static void intel_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) +{ + if (!is_edp(intel_dp)) + return; + + pps_lock(intel_dp); + edp_panel_vdd_off(intel_dp, sync); + pps_unlock(intel_dp); +} + void intel_edp_panel_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); @@ -1313,9 +1547,11 @@ void intel_edp_panel_on(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power on\n"); + pps_lock(intel_dp); + if (edp_have_panel_power(intel_dp)) { DRM_DEBUG_KMS("eDP power already on\n"); - return; + goto out; } wait_panel_power_cycle(intel_dp); @@ -1344,6 +1580,9 @@ void intel_edp_panel_on(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); } + + out: + pps_unlock(intel_dp); } void intel_edp_panel_off(struct intel_dp *intel_dp) @@ -1361,6 +1600,8 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power off\n"); + pps_lock(intel_dp); + WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); pp = ironlake_get_pp_control(intel_dp); @@ -1382,9 +1623,12 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) /* We got a reference when we enabled the VDD. */ power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_put(dev_priv, power_domain); + + pps_unlock(intel_dp); } -void intel_edp_backlight_on(struct intel_dp *intel_dp) +/* Enable backlight in the panel power control. */ +static void _intel_edp_backlight_on(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; @@ -1392,13 +1636,6 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) u32 pp; u32 pp_ctrl_reg; - if (!is_edp(intel_dp)) - return; - - DRM_DEBUG_KMS("\n"); - - intel_panel_enable_backlight(intel_dp->attached_connector); - /* * If we enable the backlight right away following a panel power * on, we may see slight flicker as the panel syncs with the eDP @@ -1406,6 +1643,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) * allowing it to appear. */ wait_backlight_on(intel_dp); + + pps_lock(intel_dp); + pp = ironlake_get_pp_control(intel_dp); pp |= EDP_BLC_ENABLE; @@ -1413,9 +1653,24 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); + + pps_unlock(intel_dp); } -void intel_edp_backlight_off(struct intel_dp *intel_dp) +/* Enable backlight PWM and backlight PP control. */ +void intel_edp_backlight_on(struct intel_dp *intel_dp) +{ + if (!is_edp(intel_dp)) + return; + + DRM_DEBUG_KMS("\n"); + + intel_panel_enable_backlight(intel_dp->attached_connector); + _intel_edp_backlight_on(intel_dp); +} + +/* Disable backlight in the panel power control. */ +static void _intel_edp_backlight_off(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; @@ -1425,7 +1680,8 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return; - DRM_DEBUG_KMS("\n"); + pps_lock(intel_dp); + pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_BLC_ENABLE; @@ -1433,13 +1689,51 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); - intel_dp->last_backlight_off = jiffies; + pps_unlock(intel_dp); + + intel_dp->last_backlight_off = jiffies; edp_wait_backlight_off(intel_dp); +} +/* Disable backlight PP control and backlight PWM. */ +void intel_edp_backlight_off(struct intel_dp *intel_dp) +{ + if (!is_edp(intel_dp)) + return; + + DRM_DEBUG_KMS("\n"); + + _intel_edp_backlight_off(intel_dp); intel_panel_disable_backlight(intel_dp->attached_connector); } +/* + * Hook for controlling the panel power control backlight through the bl_power + * sysfs attribute. Take care to handle multiple calls. + */ +static void intel_edp_backlight_power(struct intel_connector *connector, + bool enable) +{ + struct intel_dp *intel_dp = intel_attached_dp(&connector->base); + bool is_enabled; + + pps_lock(intel_dp); + is_enabled = ironlake_get_pp_control(intel_dp) & EDP_BLC_ENABLE; + pps_unlock(intel_dp); + + if (is_enabled == enable) + return; + + DRM_DEBUG_KMS("panel power control backlight %s\n", + enable ? "enable" : "disable"); + + if (enable) + _intel_edp_backlight_on(intel_dp); + else + _intel_edp_backlight_off(intel_dp); +} + static void ironlake_edp_pll_on(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -1503,8 +1797,6 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) if (mode != DRM_MODE_DPMS_ON) { ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); - if (ret != 1) - DRM_DEBUG_DRIVER("failed to write sink power state\n"); } else { /* * When turning on, we need to retry for 1ms to give the sink @@ -1518,6 +1810,10 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) msleep(1); } } + + if (ret != 1) + DRM_DEBUG_KMS("failed to %s sink power state\n", + mode == DRM_MODE_DPMS_ON ? "enable" : "disable"); } static bool intel_dp_get_hw_state(struct intel_encoder *encoder, @@ -1564,7 +1860,7 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, return true; } - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { trans_dp = I915_READ(TRANS_DP_CTL(i)); if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) { *pipe = i; @@ -2020,7 +2316,6 @@ void intel_edp_psr_init(struct drm_device *dev) static void intel_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - enum port port = dp_to_dig_port(intel_dp)->port; struct drm_device *dev = encoder->base.dev; /* Make sure the panel is off before trying to change the mode. But also @@ -2030,21 +2325,19 @@ static void intel_disable_dp(struct intel_encoder *encoder) intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); intel_edp_panel_off(intel_dp); - /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */ - if (!(port == PORT_A || IS_VALLEYVIEW(dev))) + /* disable the port before the pipe on g4x */ + if (INTEL_INFO(dev)->gen < 5) intel_dp_link_down(intel_dp); } -static void g4x_post_disable_dp(struct intel_encoder *encoder) +static void ilk_post_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = dp_to_dig_port(intel_dp)->port; - if (port != PORT_A) - return; - intel_dp_link_down(intel_dp); - ironlake_edp_pll_off(intel_dp); + if (port == PORT_A) + ironlake_edp_pll_off(intel_dp); } static void vlv_post_disable_dp(struct intel_encoder *encoder) @@ -2090,6 +2383,104 @@ static void chv_post_disable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); } +static void +_intel_dp_set_link_train(struct intel_dp *intel_dp, + uint32_t *DP, + uint8_t dp_train_pat) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dig_port->port; + + if (HAS_DDI(dev)) { + uint32_t temp = I915_READ(DP_TP_CTL(port)); + + if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE) + temp |= DP_TP_CTL_SCRAMBLE_DISABLE; + else + temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE; + + temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + temp |= DP_TP_CTL_LINK_TRAIN_NORMAL; + + break; + case DP_TRAINING_PATTERN_1: + temp |= DP_TP_CTL_LINK_TRAIN_PAT1; + break; + case DP_TRAINING_PATTERN_2: + temp |= DP_TP_CTL_LINK_TRAIN_PAT2; + break; + case DP_TRAINING_PATTERN_3: + temp |= DP_TP_CTL_LINK_TRAIN_PAT3; + break; + } + I915_WRITE(DP_TP_CTL(port), temp); + + } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) { + *DP &= ~DP_LINK_TRAIN_MASK_CPT; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + *DP |= DP_LINK_TRAIN_OFF_CPT; + break; + case DP_TRAINING_PATTERN_1: + *DP |= DP_LINK_TRAIN_PAT_1_CPT; + break; + case DP_TRAINING_PATTERN_2: + *DP |= DP_LINK_TRAIN_PAT_2_CPT; + break; + case DP_TRAINING_PATTERN_3: + DRM_ERROR("DP training pattern 3 not supported\n"); + *DP |= DP_LINK_TRAIN_PAT_2_CPT; + break; + } + + } else { + if (IS_CHERRYVIEW(dev)) + *DP &= ~DP_LINK_TRAIN_MASK_CHV; + else + *DP &= ~DP_LINK_TRAIN_MASK; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + *DP |= DP_LINK_TRAIN_OFF; + break; + case DP_TRAINING_PATTERN_1: + *DP |= DP_LINK_TRAIN_PAT_1; + break; + case DP_TRAINING_PATTERN_2: + *DP |= DP_LINK_TRAIN_PAT_2; + break; + case DP_TRAINING_PATTERN_3: + if (IS_CHERRYVIEW(dev)) { + *DP |= DP_LINK_TRAIN_PAT_3_CHV; + } else { + DRM_ERROR("DP training pattern 3 not supported\n"); + *DP |= DP_LINK_TRAIN_PAT_2; + } + break; + } + } +} + +static void intel_dp_enable_port(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + + intel_dp->DP |= DP_PORT_EN; + + /* enable with pattern 1 (as per spec) */ + _intel_dp_set_link_train(intel_dp, &intel_dp->DP, + DP_TRAINING_PATTERN_1); + + I915_WRITE(intel_dp->output_reg, intel_dp->DP); + POSTING_READ(intel_dp->output_reg); +} + static void intel_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2100,11 +2491,12 @@ static void intel_enable_dp(struct intel_encoder *encoder) if (WARN_ON(dp_reg & DP_PORT_EN)) return; + intel_dp_enable_port(intel_dp); intel_edp_panel_vdd_on(intel_dp); + intel_edp_panel_on(intel_dp); + intel_edp_panel_vdd_off(intel_dp, true); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); - intel_edp_panel_on(intel_dp); - edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); intel_dp_stop_link_train(intel_dp); } @@ -2138,6 +2530,78 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder) } } +static void vlv_steal_power_sequencer(struct drm_device *dev, + enum pipe pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + + lockdep_assert_held(&dev_priv->pps_mutex); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + struct intel_dp *intel_dp; + enum port port; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + port = dp_to_dig_port(intel_dp)->port; + + if (intel_dp->pps_pipe != pipe) + continue; + + DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n", + pipe_name(pipe), port_name(port)); + + /* make sure vdd is off before we steal it */ + edp_panel_vdd_off_sync(intel_dp); + + intel_dp->pps_pipe = INVALID_PIPE; + } +} + +static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct edp_power_seq power_seq; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (intel_dp->pps_pipe == crtc->pipe) + return; + + /* + * If another power sequencer was being used on this + * port previously make sure to turn off vdd there while + * we still have control of it. + */ + if (intel_dp->pps_pipe != INVALID_PIPE) + edp_panel_vdd_off_sync(intel_dp); + + /* + * We may be stealing the power + * sequencer from another port. + */ + vlv_steal_power_sequencer(dev, crtc->pipe); + + /* now it's all ours */ + intel_dp->pps_pipe = crtc->pipe; + + DRM_DEBUG_KMS("initializing pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pps_pipe), port_name(intel_dig_port->port)); + + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); +} + static void vlv_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2147,7 +2611,6 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel port = vlv_dport_to_channel(dport); int pipe = intel_crtc->pipe; - struct edp_power_seq power_seq; u32 val; mutex_lock(&dev_priv->dpio_lock); @@ -2166,10 +2629,9 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); + pps_lock(intel_dp); + vlv_init_panel_power_sequencer(intel_dp); + pps_unlock(intel_dp); } intel_enable_dp(encoder); @@ -2213,7 +2675,6 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) struct intel_digital_port *dport = dp_to_dig_port(intel_dp); struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct edp_power_seq power_seq; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel ch = vlv_dport_to_channel(dport); @@ -2259,10 +2720,9 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); + pps_lock(intel_dp); + vlv_init_panel_power_sequencer(intel_dp); + pps_unlock(intel_dp); } intel_enable_dp(encoder); @@ -2381,13 +2841,13 @@ intel_dp_voltage_max(struct intel_dp *intel_dp) enum port port = dp_to_dig_port(intel_dp)->port; if (IS_VALLEYVIEW(dev)) - return DP_TRAIN_VOLTAGE_SWING_1200; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; else if (IS_GEN7(dev) && port == PORT_A) - return DP_TRAIN_VOLTAGE_SWING_800; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; else if (HAS_PCH_CPT(dev) && port != PORT_A) - return DP_TRAIN_VOLTAGE_SWING_1200; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; else - return DP_TRAIN_VOLTAGE_SWING_800; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; } static uint8_t @@ -2398,49 +2858,49 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_9_5; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_3; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else if (IS_VALLEYVIEW(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_9_5; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_3; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else if (IS_GEN7(dev) && port == PORT_A) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_600: - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } } @@ -2459,22 +2919,22 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) int pipe = intel_crtc->pipe; switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: preemph_reg_value = 0x0004000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B405555; uniqtranscale_reg_value = 0x552AB83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x5548B83A; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: demph_reg_value = 0x2B245555; uniqtranscale_reg_value = 0x5560B83A; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: demph_reg_value = 0x2B405555; uniqtranscale_reg_value = 0x5598DA3A; break; @@ -2482,18 +2942,18 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: preemph_reg_value = 0x0002000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x5552B83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B404848; uniqtranscale_reg_value = 0x5580B83A; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2501,14 +2961,14 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: preemph_reg_value = 0x0000000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B305555; uniqtranscale_reg_value = 0x5570B83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B2B4040; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2516,10 +2976,10 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: preemph_reg_value = 0x0006000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x1B405555; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2558,21 +3018,21 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) int i; switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 128; margin_reg_value = 52; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 128; margin_reg_value = 77; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: deemph_reg_value = 128; margin_reg_value = 102; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: deemph_reg_value = 128; margin_reg_value = 154; /* FIXME extra to set for 1200 */ @@ -2581,17 +3041,17 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 85; margin_reg_value = 78; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 85; margin_reg_value = 116; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: deemph_reg_value = 85; margin_reg_value = 154; break; @@ -2599,13 +3059,13 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 64; margin_reg_value = 104; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 64; margin_reg_value = 154; break; @@ -2613,9 +3073,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 43; margin_reg_value = 154; break; @@ -2662,9 +3122,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) } if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK) - == DP_TRAIN_PRE_EMPHASIS_0) && + == DP_TRAIN_PRE_EMPH_LEVEL_0) && ((train_set & DP_TRAIN_VOLTAGE_SWING_MASK) - == DP_TRAIN_VOLTAGE_SWING_1200)) { + == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)) { /* * The document said it needs to set bit 27 for ch0 and bit 26 @@ -2743,32 +3203,32 @@ intel_gen4_signal_levels(uint8_t train_set) uint32_t signal_levels = 0; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: default: signal_levels |= DP_VOLTAGE_0_4; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: signal_levels |= DP_VOLTAGE_0_6; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: signal_levels |= DP_VOLTAGE_0_8; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: signal_levels |= DP_VOLTAGE_1_2; break; } switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: default: signal_levels |= DP_PRE_EMPHASIS_0; break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: signal_levels |= DP_PRE_EMPHASIS_3_5; break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: signal_levels |= DP_PRE_EMPHASIS_6; break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: signal_levels |= DP_PRE_EMPHASIS_9_5; break; } @@ -2782,19 +3242,19 @@ intel_gen6_edp_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2: return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B; default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" @@ -2810,21 +3270,21 @@ intel_gen7_edp_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_400MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_400MV_3_5DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: return EDP_LINK_TRAIN_400MV_6DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_600MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_600MV_3_5DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_800MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_800MV_3_5DB_IVB; default: @@ -2841,30 +3301,30 @@ intel_hsw_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_400MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_400MV_3_5DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_400MV_6DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_9_5: - return DDI_BUF_EMP_400MV_9_5DB_HSW; - - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_600MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_600MV_3_5DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_600MV_6DB_HSW; - - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_800MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_800MV_3_5DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(0); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(1); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: + return DDI_BUF_TRANS_SELECT(2); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_3: + return DDI_BUF_TRANS_SELECT(3); + + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(4); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(5); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2: + return DDI_BUF_TRANS_SELECT(6); + + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(7); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(8); default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" "0x%x\n", signal_levels); - return DDI_BUF_EMP_400MV_0DB_HSW; + return DDI_BUF_TRANS_SELECT(0); } } @@ -2911,81 +3371,10 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum port port = intel_dig_port->port; uint8_t buf[sizeof(intel_dp->train_set) + 1]; int ret, len; - if (HAS_DDI(dev)) { - uint32_t temp = I915_READ(DP_TP_CTL(port)); - - if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE) - temp |= DP_TP_CTL_SCRAMBLE_DISABLE; - else - temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE; - - temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - temp |= DP_TP_CTL_LINK_TRAIN_NORMAL; - - break; - case DP_TRAINING_PATTERN_1: - temp |= DP_TP_CTL_LINK_TRAIN_PAT1; - break; - case DP_TRAINING_PATTERN_2: - temp |= DP_TP_CTL_LINK_TRAIN_PAT2; - break; - case DP_TRAINING_PATTERN_3: - temp |= DP_TP_CTL_LINK_TRAIN_PAT3; - break; - } - I915_WRITE(DP_TP_CTL(port), temp); - - } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) { - *DP &= ~DP_LINK_TRAIN_MASK_CPT; - - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - *DP |= DP_LINK_TRAIN_OFF_CPT; - break; - case DP_TRAINING_PATTERN_1: - *DP |= DP_LINK_TRAIN_PAT_1_CPT; - break; - case DP_TRAINING_PATTERN_2: - *DP |= DP_LINK_TRAIN_PAT_2_CPT; - break; - case DP_TRAINING_PATTERN_3: - DRM_ERROR("DP training pattern 3 not supported\n"); - *DP |= DP_LINK_TRAIN_PAT_2_CPT; - break; - } - - } else { - if (IS_CHERRYVIEW(dev)) - *DP &= ~DP_LINK_TRAIN_MASK_CHV; - else - *DP &= ~DP_LINK_TRAIN_MASK; - - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - *DP |= DP_LINK_TRAIN_OFF; - break; - case DP_TRAINING_PATTERN_1: - *DP |= DP_LINK_TRAIN_PAT_1; - break; - case DP_TRAINING_PATTERN_2: - *DP |= DP_LINK_TRAIN_PAT_2; - break; - case DP_TRAINING_PATTERN_3: - if (IS_CHERRYVIEW(dev)) { - *DP |= DP_LINK_TRAIN_PAT_3_CHV; - } else { - DRM_ERROR("DP training pattern 3 not supported\n"); - *DP |= DP_LINK_TRAIN_PAT_2; - } - break; - } - } + _intel_dp_set_link_train(intel_dp, DP, dp_train_pat); I915_WRITE(intel_dp->output_reg, *DP); POSTING_READ(intel_dp->output_reg); @@ -3318,15 +3707,11 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) struct drm_device *dev = dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; - if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) < 0) return false; /* aux transfer failed */ - hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), - 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); - DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); + DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), intel_dp->dpcd); if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */ @@ -3384,7 +3769,7 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); } static bool @@ -3398,7 +3783,7 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] < 0x12) return false; - _edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) { if (buf[0] & DP_MST_CAP) { DRM_DEBUG_KMS("Sink is MST capable\n"); @@ -3408,7 +3793,7 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) intel_dp->is_mst = false; } } - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); return intel_dp->is_mst; @@ -3640,20 +4025,24 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) } static enum drm_connector_status +edp_detect(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + enum drm_connector_status status; + + status = intel_panel_detect(dev); + if (status == connector_status_unknown) + status = connector_status_connected; + + return status; +} + +static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - enum drm_connector_status status; - - /* Can't disconnect eDP, but you can close the lid... */ - if (is_edp(intel_dp)) { - status = intel_panel_detect(dev); - if (status == connector_status_unknown) - status = connector_status_connected; - return status; - } if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) return connector_status_disconnected; @@ -3729,9 +4118,9 @@ g4x_dp_detect(struct intel_dp *intel_dp) } static struct edid * -intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) +intel_dp_get_edid(struct intel_dp *intel_dp) { - struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_connector *intel_connector = intel_dp->attached_connector; /* use cached edid if we have one */ if (intel_connector->edid) { @@ -3740,27 +4129,55 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) return NULL; return drm_edid_duplicate(intel_connector->edid); - } + } else + return drm_get_edid(&intel_connector->base, + &intel_dp->aux.ddc); +} + +static void +intel_dp_set_edid(struct intel_dp *intel_dp) +{ + struct intel_connector *intel_connector = intel_dp->attached_connector; + struct edid *edid; + + edid = intel_dp_get_edid(intel_dp); + intel_connector->detect_edid = edid; - return drm_get_edid(connector, adapter); + if (intel_dp->force_audio != HDMI_AUDIO_AUTO) + intel_dp->has_audio = intel_dp->force_audio == HDMI_AUDIO_ON; + else + intel_dp->has_audio = drm_detect_monitor_audio(edid); } -static int -intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter) +static void +intel_dp_unset_edid(struct intel_dp *intel_dp) { - struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_connector *intel_connector = intel_dp->attached_connector; - /* use cached edid if we have one */ - if (intel_connector->edid) { - /* invalid edid */ - if (IS_ERR(intel_connector->edid)) - return 0; + kfree(intel_connector->detect_edid); + intel_connector->detect_edid = NULL; - return intel_connector_update_modes(connector, - intel_connector->edid); - } + intel_dp->has_audio = false; +} + +static enum intel_display_power_domain +intel_dp_power_get(struct intel_dp *dp) +{ + struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; + enum intel_display_power_domain power_domain; + + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_get(to_i915(encoder->base.dev), power_domain); + + return power_domain; +} - return intel_ddc_get_modes(connector, adapter); +static void +intel_dp_power_put(struct intel_dp *dp, + enum intel_display_power_domain power_domain) +{ + struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; + intel_display_power_put(to_i915(encoder->base.dev), power_domain); } static enum drm_connector_status @@ -3770,33 +4187,30 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *intel_encoder = &intel_dig_port->base; struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; enum drm_connector_status status; enum intel_display_power_domain power_domain; - struct edid *edid = NULL; bool ret; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + intel_dp_unset_edid(intel_dp); if (intel_dp->is_mst) { /* MST devices are disconnected from a monitor POV */ if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; - status = connector_status_disconnected; - goto out; + return connector_status_disconnected; } - intel_dp->has_audio = false; + power_domain = intel_dp_power_get(intel_dp); - if (HAS_PCH_SPLIT(dev)) + /* Can't disconnect eDP, but you can close the lid... */ + if (is_edp(intel_dp)) + status = edp_detect(intel_dp); + else if (HAS_PCH_SPLIT(dev)) status = ironlake_dp_detect(intel_dp); else status = g4x_dp_detect(intel_dp); - if (status != connector_status_connected) goto out; @@ -3812,82 +4226,78 @@ intel_dp_detect(struct drm_connector *connector, bool force) goto out; } - if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { - intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); - } else { - edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); - if (edid) { - intel_dp->has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - } + intel_dp_set_edid(intel_dp); if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; status = connector_status_connected; out: - intel_display_power_put(dev_priv, power_domain); + intel_dp_power_put(intel_dp, power_domain); return status; } -static int intel_dp_get_modes(struct drm_connector *connector) +static void +intel_dp_force(struct drm_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; enum intel_display_power_domain power_domain; - int ret; - /* We should parse the EDID data and find out if it has an audio sink - */ + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + intel_dp_unset_edid(intel_dp); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); + if (connector->status != connector_status_connected) + return; - ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc); - intel_display_power_put(dev_priv, power_domain); - if (ret) - return ret; + power_domain = intel_dp_power_get(intel_dp); + + intel_dp_set_edid(intel_dp); + + intel_dp_power_put(intel_dp, power_domain); + + if (intel_encoder->type != INTEL_OUTPUT_EDP) + intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; +} + +static int intel_dp_get_modes(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct edid *edid; + + edid = intel_connector->detect_edid; + if (edid) { + int ret = intel_connector_update_modes(connector, edid); + if (ret) + return ret; + } /* if eDP has no EDID, fall back to fixed mode */ - if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { + if (is_edp(intel_attached_dp(connector)) && + intel_connector->panel.fixed_mode) { struct drm_display_mode *mode; - mode = drm_mode_duplicate(dev, + + mode = drm_mode_duplicate(connector->dev, intel_connector->panel.fixed_mode); if (mode) { drm_mode_probed_add(connector, mode); return 1; } } + return 0; } static bool intel_dp_detect_audio(struct drm_connector *connector) { - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - enum intel_display_power_domain power_domain; - struct edid *edid; bool has_audio = false; + struct edid *edid; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - - edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); - if (edid) { + edid = to_intel_connector(connector)->detect_edid; + if (edid) has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - - intel_display_power_put(dev_priv, power_domain); return has_audio; } @@ -3985,6 +4395,8 @@ intel_dp_connector_destroy(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); + intel_dp_unset_edid(intel_attached_dp(connector)); + if (!IS_ERR_OR_NULL(intel_connector->edid)) kfree(intel_connector->edid); @@ -4001,16 +4413,20 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) { struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); struct intel_dp *intel_dp = &intel_dig_port->dp; - struct drm_device *dev = intel_dp_to_dev(intel_dp); drm_dp_aux_unregister(&intel_dp->aux); intel_dp_mst_encoder_cleanup(intel_dig_port); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_unlock(intel_dp); + if (intel_dp->edp_notifier.notifier_call) { unregister_reboot_notifier(&intel_dp->edp_notifier); intel_dp->edp_notifier.notifier_call = NULL; @@ -4026,7 +4442,13 @@ static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) if (!is_edp(intel_dp)) return; + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); + pps_unlock(intel_dp); } static void intel_dp_encoder_reset(struct drm_encoder *encoder) @@ -4037,6 +4459,7 @@ static void intel_dp_encoder_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_dp_connector_funcs = { .dpms = intel_connector_dpms, .detect = intel_dp_detect, + .force = intel_dp_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, .destroy = intel_dp_connector_destroy, @@ -4213,6 +4636,8 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, u32 pp_on, pp_off, pp_div, pp; int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg; + lockdep_assert_held(&dev_priv->pps_mutex); + if (HAS_PCH_SPLIT(dev)) { pp_ctrl_reg = PCH_PP_CONTROL; pp_on_reg = PCH_PP_ON_DELAYS; @@ -4312,6 +4737,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, u32 pp_on, pp_off, pp_div, port_sel = 0; int div = HAS_PCH_SPLIT(dev) ? intel_pch_rawclk(dev) : intel_hrawclk(dev); int pp_on_reg, pp_off_reg, pp_div_reg; + enum port port = dp_to_dig_port(intel_dp)->port; + + lockdep_assert_held(&dev_priv->pps_mutex); if (HAS_PCH_SPLIT(dev)) { pp_on_reg = PCH_PP_ON_DELAYS; @@ -4346,12 +4774,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, /* Haswell doesn't have any port selection bits for the panel * power sequencer any more. */ if (IS_VALLEYVIEW(dev)) { - if (dp_to_dig_port(intel_dp)->port == PORT_B) - port_sel = PANEL_PORT_SELECT_DPB_VLV; - else - port_sel = PANEL_PORT_SELECT_DPC_VLV; + port_sel = PANEL_PORT_SELECT_VLV(port); } else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - if (dp_to_dig_port(intel_dp)->port == PORT_A) + if (port == PORT_A) port_sel = PANEL_PORT_SELECT_DPA; else port_sel = PANEL_PORT_SELECT_DPD; @@ -4509,8 +4934,11 @@ void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder) return; intel_dp = enc_to_intel_dp(&intel_encoder->base); + + pps_lock(intel_dp); + if (!edp_have_panel_vdd(intel_dp)) - return; + goto out; /* * The VDD bit needs a power domain reference, so if the bit is * already enabled when we boot or resume, grab this reference and @@ -4522,6 +4950,8 @@ void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder) intel_display_power_get(dev_priv, power_domain); edp_panel_vdd_schedule_off(intel_dp); + out: + pps_unlock(intel_dp); } static bool intel_edp_init_connector(struct intel_dp *intel_dp, @@ -4549,7 +4979,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* Cache DPCD and EDID for edp. */ intel_edp_panel_vdd_on(intel_dp); has_dpcd = intel_dp_get_dpcd(intel_dp); - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); if (has_dpcd) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) @@ -4563,7 +4993,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } /* We now know it's not a ghost, init power sequence regs. */ + pps_lock(intel_dp); intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq); + pps_unlock(intel_dp); mutex_lock(&dev->mode_config.mutex); edid = drm_get_edid(connector, &intel_dp->aux.ddc); @@ -4607,6 +5039,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); + intel_connector->panel.backlight_power = intel_edp_backlight_power; intel_panel_setup_backlight(connector); return true; @@ -4625,6 +5058,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct edp_power_seq power_seq = { 0 }; int type; + intel_dp->pps_pipe = INVALID_PIPE; + /* intel_dp vfuncs */ if (IS_VALLEYVIEW(dev)) intel_dp->get_aux_clock_divider = vlv_get_aux_clock_divider; @@ -4695,8 +5130,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } if (is_edp(intel_dp)) { - intel_dp_init_panel_power_timestamps(intel_dp); - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + pps_lock(intel_dp); + if (IS_VALLEYVIEW(dev)) { + vlv_initial_power_sequencer_setup(intel_dp); + } else { + intel_dp_init_panel_power_timestamps(intel_dp); + intel_dp_init_panel_power_sequencer(dev, intel_dp, + &power_seq); + } + pps_unlock(intel_dp); } intel_dp_aux_init(intel_dp, intel_connector); @@ -4704,7 +5146,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, /* init MST on ports that can support it */ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { if (port == PORT_B || port == PORT_C || port == PORT_D) { - intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id); + intel_dp_mst_encoder_init(intel_dig_port, + intel_connector->base.base.id); } } @@ -4712,9 +5155,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, drm_dp_aux_unregister(&intel_dp->aux); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_unlock(intel_dp); } drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -4778,7 +5225,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) } else { intel_encoder->pre_enable = g4x_pre_enable_dp; intel_encoder->enable = g4x_enable_dp; - intel_encoder->post_disable = g4x_post_disable_dp; + if (INTEL_INFO(dev)->gen >= 5) + intel_encoder->post_disable = ilk_post_disable_dp; } intel_dig_port->port = port; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d683a2090249..07ce04683c30 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -25,6 +25,7 @@ #ifndef __INTEL_DRV_H__ #define __INTEL_DRV_H__ +#include <linux/async.h> #include <linux/i2c.h> #include <linux/hdmi.h> #include <drm/i915_drm.h> @@ -179,6 +180,8 @@ struct intel_panel { bool active_low_pwm; struct backlight_device *device; } backlight; + + void (*backlight_power)(struct intel_connector *, bool enable); }; struct intel_connector { @@ -211,6 +214,7 @@ struct intel_connector { /* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */ struct edid *edid; + struct edid *detect_edid; /* since POLL and HPD connectors may use the same HPD line keep the native state of connector->polled in case hotplug storm detection changes it */ @@ -566,6 +570,12 @@ struct intel_dp { struct notifier_block edp_notifier; + /* + * Pipe whose power sequencer is currently locked into + * this port. Only relevant on VLV/CHV. + */ + enum pipe pps_pipe; + bool use_tps3; bool can_mst; /* this port supports mst */ bool is_mst; @@ -664,6 +674,10 @@ struct intel_unpin_work { #define INTEL_FLIP_COMPLETE 2 u32 flip_count; u32 gtt_offset; + struct intel_engine_cs *flip_queued_ring; + u32 flip_queued_seqno; + int flip_queued_vblank; + int flip_ready_vblank; bool enable_stall_check; }; @@ -828,7 +842,6 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, enum pipe pipe); void intel_wait_for_vblank(struct drm_device *dev, int pipe); -void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport); @@ -849,6 +862,7 @@ __intel_framebuffer_create(struct drm_device *dev, void intel_prepare_page_flip(struct drm_device *dev, int plane); void intel_finish_page_flip(struct drm_device *dev, int pipe); void intel_finish_page_flip_plane(struct drm_device *dev, int plane); +void intel_check_page_flip(struct drm_device *dev, int pipe); /* shared dpll functions */ struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); @@ -937,6 +951,7 @@ void intel_dp_mst_suspend(struct drm_device *dev); void intel_dp_mst_resume(struct drm_device *dev); int intel_dp_max_link_bw(struct intel_dp *intel_dp); void intel_dp_hot_plug(struct intel_encoder *intel_encoder); +void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv); /* intel_dp_mst.c */ int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port); @@ -951,7 +966,7 @@ void intel_dvo_init(struct drm_device *dev); /* legacy fbdev emulation in intel_fbdev.c */ #ifdef CONFIG_DRM_I915_FBDEV extern int intel_fbdev_init(struct drm_device *dev); -extern void intel_fbdev_initial_config(struct drm_device *dev); +extern void intel_fbdev_initial_config(void *data, async_cookie_t cookie); extern void intel_fbdev_fini(struct drm_device *dev); extern void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous); extern void intel_fbdev_output_poll_changed(struct drm_device *dev); @@ -962,7 +977,7 @@ static inline int intel_fbdev_init(struct drm_device *dev) return 0; } -static inline void intel_fbdev_initial_config(struct drm_device *dev) +static inline void intel_fbdev_initial_config(void *data, async_cookie_t cookie) { } @@ -1093,6 +1108,9 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); void intel_flush_primary_plane(struct drm_i915_private *dev_priv, enum plane plane); +int intel_plane_set_property(struct drm_plane *plane, + struct drm_property *prop, + uint64_t val); int intel_plane_restore(struct drm_plane *plane); void intel_plane_disable(struct drm_plane *plane); int intel_sprite_set_colorkey(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 56b47d2ffaf7..e40e3df33517 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -85,7 +85,7 @@ static const struct intel_dvo_device intel_dvo_devices[] = { { .type = INTEL_DVO_CHIP_TMDS, .name = "ns2501", - .dvo_reg = DVOC, + .dvo_reg = DVOB, .slave_addr = NS2501_ADDR, .dev_ops = &ns2501_ops, } @@ -185,12 +185,13 @@ static void intel_enable_dvo(struct intel_encoder *encoder) u32 dvo_reg = intel_dvo->dev.dvo_reg; u32 temp = I915_READ(dvo_reg); - I915_WRITE(dvo_reg, temp | DVO_ENABLE); - I915_READ(dvo_reg); intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, &crtc->config.requested_mode, &crtc->config.adjusted_mode); + I915_WRITE(dvo_reg, temp | DVO_ENABLE); + I915_READ(dvo_reg); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } @@ -226,10 +227,6 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); - intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, - &config->requested_mode, - &config->adjusted_mode); - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } else { intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index cf052a39558d..9b584f3fbb99 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -24,6 +24,7 @@ * David Airlie */ +#include <linux/async.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/console.h> @@ -332,24 +333,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, int num_connectors_enabled = 0; int num_connectors_detected = 0; - /* - * If the user specified any force options, just bail here - * and use that config. - */ - for (i = 0; i < fb_helper->connector_count; i++) { - struct drm_fb_helper_connector *fb_conn; - struct drm_connector *connector; - - fb_conn = fb_helper->connector_info[i]; - connector = fb_conn->connector; - - if (!enabled[i]) - continue; - - if (connector->force != DRM_FORCE_UNSPECIFIED) - return false; - } - save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), GFP_KERNEL); if (!save_enabled) @@ -375,8 +358,18 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, continue; } + if (connector->force == DRM_FORCE_OFF) { + DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", + connector->name); + enabled[i] = false; + continue; + } + encoder = connector->encoder; if (!encoder || WARN_ON(!encoder->crtc)) { + if (connector->force > DRM_FORCE_OFF) + goto bail; + DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", connector->name); enabled[i] = false; @@ -395,8 +388,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, for (j = 0; j < fb_helper->connector_count; j++) { if (crtcs[j] == new_crtc) { DRM_DEBUG_KMS("fallback: cloned configuration\n"); - fallback = true; - goto out; + goto bail; } } @@ -467,8 +459,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, fallback = true; } -out: if (fallback) { +bail: DRM_DEBUG_KMS("Not using firmware configuration\n"); memcpy(enabled, save_enabled, dev->mode_config.num_connector); kfree(save_enabled); @@ -679,9 +671,9 @@ int intel_fbdev_init(struct drm_device *dev) return 0; } -void intel_fbdev_initial_config(struct drm_device *dev) +void intel_fbdev_initial_config(void *data, async_cookie_t cookie) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = data; struct intel_fbdev *ifbdev = dev_priv->fbdev; /* Due to peculiar init order wrt to hpd handling this is separate. */ @@ -696,6 +688,7 @@ void intel_fbdev_fini(struct drm_device *dev) flush_work(&dev_priv->fbdev_suspend_work); + async_synchronize_full(); intel_fbdev_destroy(dev, dev_priv->fbdev); kfree(dev_priv->fbdev); dev_priv->fbdev = NULL; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 96957683032e..c5861736b4b0 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -971,104 +971,117 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, return true; } -static enum drm_connector_status -intel_hdmi_detect(struct drm_connector *connector, bool force) +static void +intel_hdmi_unset_edid(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct intel_digital_port *intel_dig_port = - hdmi_to_dig_port(intel_hdmi); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_i915_private *dev_priv = dev->dev_private; - struct edid *edid; - enum intel_display_power_domain power_domain; - enum drm_connector_status status = connector_status_disconnected; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); + intel_hdmi->has_hdmi_sink = false; + intel_hdmi->has_audio = false; + intel_hdmi->rgb_quant_range_selectable = false; + + kfree(to_intel_connector(connector)->detect_edid); + to_intel_connector(connector)->detect_edid = NULL; +} + +static bool +intel_hdmi_set_edid(struct drm_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->dev); + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + struct intel_encoder *intel_encoder = + &hdmi_to_dig_port(intel_hdmi)->base; + enum intel_display_power_domain power_domain; + struct edid *edid; + bool connected = false; power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); - intel_hdmi->has_hdmi_sink = false; - intel_hdmi->has_audio = false; - intel_hdmi->rgb_quant_range_selectable = false; edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); - if (edid) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) { - status = connector_status_connected; - if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) - intel_hdmi->has_hdmi_sink = - drm_detect_hdmi_monitor(edid); - intel_hdmi->has_audio = drm_detect_monitor_audio(edid); - intel_hdmi->rgb_quant_range_selectable = - drm_rgb_quant_range_selectable(edid); - } - kfree(edid); - } + intel_display_power_put(dev_priv, power_domain); + + to_intel_connector(connector)->detect_edid = edid; + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { + intel_hdmi->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); - if (status == connector_status_connected) { + intel_hdmi->has_audio = drm_detect_monitor_audio(edid); if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO) intel_hdmi->has_audio = - (intel_hdmi->force_audio == HDMI_AUDIO_ON); - intel_encoder->type = INTEL_OUTPUT_HDMI; + intel_hdmi->force_audio == HDMI_AUDIO_ON; + + if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) + intel_hdmi->has_hdmi_sink = + drm_detect_hdmi_monitor(edid); + + connected = true; } - intel_display_power_put(dev_priv, power_domain); + return connected; +} + +static enum drm_connector_status +intel_hdmi_detect(struct drm_connector *connector, bool force) +{ + enum drm_connector_status status; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + + intel_hdmi_unset_edid(connector); + + if (intel_hdmi_set_edid(connector)) { + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + + hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; + status = connector_status_connected; + } else + status = connector_status_disconnected; return status; } -static int intel_hdmi_get_modes(struct drm_connector *connector) +static void +intel_hdmi_force(struct drm_connector *connector) { - struct intel_encoder *intel_encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); - struct drm_i915_private *dev_priv = connector->dev->dev_private; - enum intel_display_power_domain power_domain; - int ret; + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - /* We should parse the EDID data and find out if it's an HDMI sink so - * we can send audio to it. - */ + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); + intel_hdmi_unset_edid(connector); - ret = intel_ddc_get_modes(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + if (connector->status != connector_status_connected) + return; - intel_display_power_put(dev_priv, power_domain); + intel_hdmi_set_edid(connector); + hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; +} - return ret; +static int intel_hdmi_get_modes(struct drm_connector *connector) +{ + struct edid *edid; + + edid = to_intel_connector(connector)->detect_edid; + if (edid == NULL) + return 0; + + return intel_connector_update_modes(connector, edid); } static bool intel_hdmi_detect_audio(struct drm_connector *connector) { - struct intel_encoder *intel_encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); - struct drm_i915_private *dev_priv = connector->dev->dev_private; - enum intel_display_power_domain power_domain; - struct edid *edid; bool has_audio = false; + struct edid *edid; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); - if (edid) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) - has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - - intel_display_power_put(dev_priv, power_domain); + edid = to_intel_connector(connector)->detect_edid; + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) + has_audio = drm_detect_monitor_audio(edid); return has_audio; } @@ -1488,6 +1501,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) static void intel_hdmi_destroy(struct drm_connector *connector) { + intel_hdmi_unset_edid(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -1495,6 +1509,7 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_connector_funcs intel_hdmi_connector_funcs = { .dpms = intel_connector_dpms, .detect = intel_hdmi_detect, + .force = intel_hdmi_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_hdmi_set_property, .destroy = intel_hdmi_destroy, diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index c096b9b7f22a..bd1b28d99920 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1217,8 +1217,6 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *ring) static int logical_ring_init(struct drm_device *dev, struct intel_engine_cs *ring) { int ret; - struct intel_context *dctx = ring->default_context; - struct drm_i915_gem_object *dctx_obj; /* Intentionally left blank. */ ring->buffer = NULL; @@ -1232,18 +1230,6 @@ static int logical_ring_init(struct drm_device *dev, struct intel_engine_cs *rin spin_lock_init(&ring->execlist_lock); ring->next_context_status_buffer = 0; - ret = intel_lr_context_deferred_create(dctx, ring); - if (ret) - return ret; - - /* The status page is offset 0 from the context object in LRCs. */ - dctx_obj = dctx->engine[ring->id].state; - ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(dctx_obj); - ring->status_page.page_addr = kmap(sg_page(dctx_obj->pages->sgl)); - if (ring->status_page.page_addr == NULL) - return -ENOMEM; - ring->status_page.obj = dctx_obj; - ret = i915_cmd_parser_init_ring(ring); if (ret) return ret; @@ -1254,7 +1240,9 @@ static int logical_ring_init(struct drm_device *dev, struct intel_engine_cs *rin return ret; } - return 0; + ret = intel_lr_context_deferred_create(ring->default_context, ring); + + return ret; } static int logical_render_ring_init(struct drm_device *dev) @@ -1448,16 +1436,53 @@ cleanup_render_ring: return ret; } +int intel_lr_context_render_state_init(struct intel_engine_cs *ring, + struct intel_context *ctx) +{ + struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf; + struct render_state so; + struct drm_i915_file_private *file_priv = ctx->file_priv; + struct drm_file *file = file_priv ? file_priv->file : NULL; + int ret; + + ret = i915_gem_render_state_prepare(ring, &so); + if (ret) + return ret; + + if (so.rodata == NULL) + return 0; + + ret = ring->emit_bb_start(ringbuf, + so.ggtt_offset, + I915_DISPATCH_SECURE); + if (ret) + goto out; + + i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), ring); + + ret = __i915_add_request(ring, file, so.obj, NULL); + /* intel_logical_ring_add_request moves object to inactive if it + * fails */ +out: + i915_gem_render_state_fini(&so); + return ret; +} + static int populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_obj, struct intel_engine_cs *ring, struct intel_ringbuffer *ringbuf) { + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *ring_obj = ringbuf->obj; struct i915_hw_ppgtt *ppgtt = ctx->ppgtt; struct page *page; uint32_t *reg_state; int ret; + if (!ppgtt) + ppgtt = dev_priv->mm.aliasing_ppgtt; + ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true); if (ret) { DRM_DEBUG_DRIVER("Could not set to CPU domain\n"); @@ -1687,6 +1712,29 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, ctx->engine[ring->id].ringbuf = ringbuf; ctx->engine[ring->id].state = ctx_obj; + if (ctx == ring->default_context) { + /* The status page is offset 0 from the default context object + * in LRC mode. */ + ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(ctx_obj); + ring->status_page.page_addr = + kmap(sg_page(ctx_obj->pages->sgl)); + if (ring->status_page.page_addr == NULL) + return -ENOMEM; + ring->status_page.obj = ctx_obj; + } + + if (ring->id == RCS && !ctx->rcs_initialized) { + ret = intel_lr_context_render_state_init(ring, ctx); + if (ret) { + DRM_ERROR("Init render state failed: %d\n", ret); + ctx->engine[ring->id].ringbuf = NULL; + ctx->engine[ring->id].state = NULL; + intel_destroy_ringbuffer_obj(ringbuf); + goto error; + } + ctx->rcs_initialized = true; + } + return 0; error: diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 991d4499fb03..33c3b4bf28c5 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -62,6 +62,8 @@ static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf, int intel_logical_ring_begin(struct intel_ringbuffer *ringbuf, int num_dwords); /* Logical Ring Contexts */ +int intel_lr_context_render_state_init(struct intel_engine_cs *ring, + struct intel_context *ctx); void intel_lr_context_free(struct intel_context *ctx); int intel_lr_context_deferred_create(struct intel_context *ctx, struct intel_engine_cs *ring); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 8e374449c6b5..18784470a760 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -751,6 +751,8 @@ void intel_panel_disable_backlight(struct intel_connector *connector) spin_lock_irqsave(&dev_priv->backlight_lock, flags); + if (panel->backlight.device) + panel->backlight.device->props.power = FB_BLANK_POWERDOWN; panel->backlight.enabled = false; dev_priv->display.disable_backlight(connector); @@ -957,6 +959,8 @@ void intel_panel_enable_backlight(struct intel_connector *connector) dev_priv->display.enable_backlight(connector); panel->backlight.enabled = true; + if (panel->backlight.device) + panel->backlight.device->props.power = FB_BLANK_UNBLANK; spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); } @@ -965,6 +969,7 @@ void intel_panel_enable_backlight(struct intel_connector *connector) static int intel_backlight_device_update_status(struct backlight_device *bd) { struct intel_connector *connector = bl_get_data(bd); + struct intel_panel *panel = &connector->panel; struct drm_device *dev = connector->base.dev; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); @@ -972,6 +977,23 @@ static int intel_backlight_device_update_status(struct backlight_device *bd) bd->props.brightness, bd->props.max_brightness); intel_panel_set_backlight(connector, bd->props.brightness, bd->props.max_brightness); + + /* + * Allow flipping bl_power as a sub-state of enabled. Sadly the + * backlight class device does not make it easy to to differentiate + * between callbacks for brightness and bl_power, so our backlight_power + * callback needs to take this into account. + */ + if (panel->backlight.enabled) { + if (panel->backlight_power) { + bool enable = bd->props.power == FB_BLANK_UNBLANK && + bd->props.brightness != 0; + panel->backlight_power(connector, enable); + } + } else { + bd->props.power = FB_BLANK_POWERDOWN; + } + drm_modeset_unlock(&dev->mode_config.connection_mutex); return 0; } @@ -1023,6 +1045,11 @@ static int intel_backlight_device_register(struct intel_connector *connector) panel->backlight.level, props.max_brightness); + if (panel->backlight.enabled) + props.power = FB_BLANK_UNBLANK; + else + props.power = FB_BLANK_POWERDOWN; + /* * Note: using the same name independent of the connector prevents * registration of multiple backlight devices in the driver. @@ -1203,7 +1230,7 @@ static int vlv_setup_backlight(struct intel_connector *connector) enum pipe pipe; u32 ctl, ctl2, val; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe)); /* Skip if the modulation freq is already set */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c8f744c418f0..45f71e6dc544 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -345,6 +345,16 @@ bool intel_fbc_enabled(struct drm_device *dev) return dev_priv->display.fbc_enabled(dev); } +void gen8_fbc_sw_flush(struct drm_device *dev, u32 value) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!IS_GEN8(dev)) + return; + + I915_WRITE(MSG_FBC_REND_STATE, value); +} + static void intel_fbc_work_fn(struct work_struct *__work) { struct intel_fbc_work *work = @@ -581,6 +591,12 @@ void intel_update_fbc(struct drm_device *dev) DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); goto out_disable; } + if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && + to_intel_plane(crtc->primary)->rotation != BIT(DRM_ROTATE_0)) { + if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE)) + DRM_DEBUG_KMS("Rotation unsupported, disabling\n"); + goto out_disable; + } /* If the kernel debugger is active, always disable compression */ if (in_dbg_master()) @@ -856,7 +872,7 @@ void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) * A value of 5us seems to be a good balance; safe for very low end * platforms but not overly aggressive on lower latency configs. */ -static const int latency_ns = 5000; +static const int pessimal_latency_ns = 5000; static int i9xx_get_fifo_size(struct drm_device *dev, int plane) { @@ -985,13 +1001,20 @@ static const struct intel_watermark_params i915_wm_info = { .guard_size = 2, .cacheline_size = I915_FIFO_LINE_SIZE, }; -static const struct intel_watermark_params i830_wm_info = { +static const struct intel_watermark_params i830_a_wm_info = { .fifo_size = I855GM_FIFO_SIZE, .max_wm = I915_MAX_WM, .default_wm = 1, .guard_size = 2, .cacheline_size = I830_FIFO_LINE_SIZE, }; +static const struct intel_watermark_params i830_bc_wm_info = { + .fifo_size = I855GM_FIFO_SIZE, + .max_wm = I915_MAX_WM/2, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I830_FIFO_LINE_SIZE, +}; static const struct intel_watermark_params i845_wm_info = { .fifo_size = I830_FIFO_SIZE, .max_wm = I915_MAX_WM, @@ -1364,14 +1387,14 @@ static void valleyview_update_wm(struct drm_crtc *crtc) vlv_update_drain_latency(crtc); if (g4x_compute_wm0(dev, PIPE_A, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planea_wm, &cursora_wm)) enabled |= 1 << PIPE_A; if (g4x_compute_wm0(dev, PIPE_B, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planeb_wm, &cursorb_wm)) enabled |= 1 << PIPE_B; @@ -1430,20 +1453,20 @@ static void cherryview_update_wm(struct drm_crtc *crtc) vlv_update_drain_latency(crtc); if (g4x_compute_wm0(dev, PIPE_A, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planea_wm, &cursora_wm)) enabled |= 1 << PIPE_A; if (g4x_compute_wm0(dev, PIPE_B, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planeb_wm, &cursorb_wm)) enabled |= 1 << PIPE_B; if (g4x_compute_wm0(dev, PIPE_C, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planec_wm, &cursorc_wm)) enabled |= 1 << PIPE_C; @@ -1536,14 +1559,14 @@ static void g4x_update_wm(struct drm_crtc *crtc) bool cxsr_enabled; if (g4x_compute_wm0(dev, PIPE_A, - &g4x_wm_info, latency_ns, - &g4x_cursor_wm_info, latency_ns, + &g4x_wm_info, pessimal_latency_ns, + &g4x_cursor_wm_info, pessimal_latency_ns, &planea_wm, &cursora_wm)) enabled |= 1 << PIPE_A; if (g4x_compute_wm0(dev, PIPE_B, - &g4x_wm_info, latency_ns, - &g4x_cursor_wm_info, latency_ns, + &g4x_wm_info, pessimal_latency_ns, + &g4x_cursor_wm_info, pessimal_latency_ns, &planeb_wm, &cursorb_wm)) enabled |= 1 << PIPE_B; @@ -1673,7 +1696,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) else if (!IS_GEN2(dev)) wm_info = &i915_wm_info; else - wm_info = &i830_wm_info; + wm_info = &i830_a_wm_info; fifo_size = dev_priv->display.get_fifo_size(dev, 0); crtc = intel_get_crtc_for_plane(dev, 0); @@ -1686,10 +1709,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, - latency_ns); + pessimal_latency_ns); enabled = crtc; - } else + } else { planea_wm = fifo_size - wm_info->guard_size; + if (planea_wm > (long)wm_info->max_wm) + planea_wm = wm_info->max_wm; + } + + if (IS_GEN2(dev)) + wm_info = &i830_bc_wm_info; fifo_size = dev_priv->display.get_fifo_size(dev, 1); crtc = intel_get_crtc_for_plane(dev, 1); @@ -1702,13 +1731,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, - latency_ns); + pessimal_latency_ns); if (enabled == NULL) enabled = crtc; else enabled = NULL; - } else + } else { planeb_wm = fifo_size - wm_info->guard_size; + if (planeb_wm > (long)wm_info->max_wm) + planeb_wm = wm_info->max_wm; + } DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); @@ -1795,7 +1827,7 @@ static void i845_update_wm(struct drm_crtc *unused_crtc) planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, &i845_wm_info, dev_priv->display.get_fifo_size(dev, 0), - 4, latency_ns); + 4, pessimal_latency_ns); fwater_lo = I915_READ(FW_BLC) & ~0xfff; fwater_lo |= (3<<8) | planea_wm; @@ -2242,7 +2274,6 @@ int ilk_wm_max_level(const struct drm_device *dev) else return 2; } - static void intel_print_wm_latency(struct drm_device *dev, const char *name, const uint16_t wm[5]) @@ -2648,7 +2679,7 @@ static struct intel_pipe_wm *ilk_find_best_result(struct drm_device *dev, #define WM_DIRTY_FBC (1 << 24) #define WM_DIRTY_DDB (1 << 25) -static unsigned int ilk_compute_wm_dirty(struct drm_device *dev, +static unsigned int ilk_compute_wm_dirty(struct drm_i915_private *dev_priv, const struct ilk_wm_values *old, const struct ilk_wm_values *new) { @@ -2656,7 +2687,7 @@ static unsigned int ilk_compute_wm_dirty(struct drm_device *dev, enum pipe pipe; int wm_lp; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (old->wm_linetime[pipe] != new->wm_linetime[pipe]) { dirty |= WM_DIRTY_LINETIME(pipe); /* Must disable LP1+ watermarks too */ @@ -2742,7 +2773,7 @@ static void ilk_write_wm_values(struct drm_i915_private *dev_priv, unsigned int dirty; uint32_t val; - dirty = ilk_compute_wm_dirty(dev, previous, results); + dirty = ilk_compute_wm_dirty(dev_priv, previous, results); if (!dirty) return; @@ -3211,6 +3242,9 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val) { int new_power; + if (dev_priv->rps.is_bdw_sw_turbo) + return; + new_power = dev_priv->rps.power; switch (dev_priv->rps.power) { case LOW_POWER: @@ -3418,8 +3452,11 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv) valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); else if (IS_VALLEYVIEW(dev)) vlv_set_rps_idle(dev_priv); - else + else if (!dev_priv->rps.is_bdw_sw_turbo + || atomic_read(&dev_priv->rps.sw_turbo.flip_received)){ gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); + } + dev_priv->rps.last_adj = 0; } mutex_unlock(&dev_priv->rps.hw_lock); @@ -3433,8 +3470,11 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv) if (dev_priv->rps.enabled) { if (IS_VALLEYVIEW(dev)) valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); - else + else if (!dev_priv->rps.is_bdw_sw_turbo + || atomic_read(&dev_priv->rps.sw_turbo.flip_received)){ gen6_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); + } + dev_priv->rps.last_adj = 0; } mutex_unlock(&dev_priv->rps.hw_lock); @@ -3453,6 +3493,10 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) dev_priv->rps.cur_freq, vlv_gpu_freq(dev_priv, val), val); + if (WARN_ONCE(IS_CHERRYVIEW(dev) && (val & 1), + "Odd GPU freq value\n")) + val &= ~1; + if (val != dev_priv->rps.cur_freq) vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); @@ -3465,21 +3509,26 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) static void gen8_disable_rps_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (IS_BROADWELL(dev) && dev_priv->rps.is_bdw_sw_turbo){ + if (atomic_read(&dev_priv->rps.sw_turbo.flip_received)) + del_timer(&dev_priv->rps.sw_turbo.flip_timer); + dev_priv-> rps.is_bdw_sw_turbo = false; + } else { + I915_WRITE(GEN6_PMINTRMSK, ~GEN8_PMINTR_REDIRECT_TO_NON_DISP); + I915_WRITE(GEN8_GT_IER(2), I915_READ(GEN8_GT_IER(2)) & + ~dev_priv->pm_rps_events); + /* Complete PM interrupt masking here doesn't race with the rps work + * item again unmasking PM interrupts because that is using a different + * register (GEN8_GT_IMR(2)) to mask PM interrupts. The only risk is in + * leaving stale bits in GEN8_GT_IIR(2) and GEN8_GT_IMR(2) which + * gen8_enable_rps will clean up. */ - I915_WRITE(GEN6_PMINTRMSK, ~GEN8_PMINTR_REDIRECT_TO_NON_DISP); - I915_WRITE(GEN8_GT_IER(2), I915_READ(GEN8_GT_IER(2)) & - ~dev_priv->pm_rps_events); - /* Complete PM interrupt masking here doesn't race with the rps work - * item again unmasking PM interrupts because that is using a different - * register (GEN8_GT_IMR(2)) to mask PM interrupts. The only risk is in - * leaving stale bits in GEN8_GT_IIR(2) and GEN8_GT_IMR(2) which - * gen8_enable_rps will clean up. */ - - spin_lock_irq(&dev_priv->irq_lock); - dev_priv->rps.pm_iir = 0; - spin_unlock_irq(&dev_priv->irq_lock); + spin_lock_irq(&dev_priv->irq_lock); + dev_priv->rps.pm_iir = 0; + spin_unlock_irq(&dev_priv->irq_lock); - I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events); + I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events); + } } static void gen6_disable_rps_interrupts(struct drm_device *dev) @@ -3527,8 +3576,14 @@ static void valleyview_disable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* we're doing forcewake before Disabling RC6, + * This what the BIOS expects when going into suspend */ + gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); + I915_WRITE(GEN6_RC_CONTROL, 0); + gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); + gen6_disable_rps_interrupts(dev); } @@ -3631,13 +3686,111 @@ static void parse_rp_state_cap(struct drm_i915_private *dev_priv, u32 rp_state_c dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; } +static void bdw_sw_calculate_freq(struct drm_device *dev, + struct intel_rps_bdw_cal *c, u32 *cur_time, u32 *c0) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u64 busy = 0; + u32 busyness_pct = 0; + u32 elapsed_time = 0; + u16 new_freq = 0; + + if (!c || !cur_time || !c0) + return; + + if (0 == c->last_c0) + goto out; + + /* Check Evaluation interval */ + elapsed_time = *cur_time - c->last_ts; + if (elapsed_time < c->eval_interval) + return; + + mutex_lock(&dev_priv->rps.hw_lock); + + /* + * c0 unit in 32*1.28 usec, elapsed_time unit in 1 usec. + * Whole busyness_pct calculation should be + * busy = ((u64)(*c0 - c->last_c0) << 5 << 7) / 100; + * busyness_pct = (u32)(busy * 100 / elapsed_time); + * The final formula is to simplify CPU calculation + */ + busy = (u64)(*c0 - c->last_c0) << 12; + do_div(busy, elapsed_time); + busyness_pct = (u32)busy; + + if (c->is_up && busyness_pct >= c->it_threshold_pct) + new_freq = (u16)dev_priv->rps.cur_freq + 3; + if (!c->is_up && busyness_pct <= c->it_threshold_pct) + new_freq = (u16)dev_priv->rps.cur_freq - 1; + + /* Adjust to new frequency busyness and compare with threshold */ + if (0 != new_freq) { + if (new_freq > dev_priv->rps.max_freq_softlimit) + new_freq = dev_priv->rps.max_freq_softlimit; + else if (new_freq < dev_priv->rps.min_freq_softlimit) + new_freq = dev_priv->rps.min_freq_softlimit; + + gen6_set_rps(dev, new_freq); + } + + mutex_unlock(&dev_priv->rps.hw_lock); + +out: + c->last_c0 = *c0; + c->last_ts = *cur_time; +} + +static void gen8_set_frequency_RP0(struct work_struct *work) +{ + struct intel_rps_bdw_turbo *p_bdw_turbo = + container_of(work, struct intel_rps_bdw_turbo, work_max_freq); + struct intel_gen6_power_mgmt *p_power_mgmt = + container_of(p_bdw_turbo, struct intel_gen6_power_mgmt, sw_turbo); + struct drm_i915_private *dev_priv = + container_of(p_power_mgmt, struct drm_i915_private, rps); + + mutex_lock(&dev_priv->rps.hw_lock); + gen6_set_rps(dev_priv->dev, dev_priv->rps.rp0_freq); + mutex_unlock(&dev_priv->rps.hw_lock); +} + +static void flip_active_timeout_handler(unsigned long var) +{ + struct drm_i915_private *dev_priv = (struct drm_i915_private *) var; + + del_timer(&dev_priv->rps.sw_turbo.flip_timer); + atomic_set(&dev_priv->rps.sw_turbo.flip_received, false); + + queue_work(dev_priv->wq, &dev_priv->rps.sw_turbo.work_max_freq); +} + +void bdw_software_turbo(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + u32 current_time = I915_READ(TIMESTAMP_CTR); /* unit in usec */ + u32 current_c0 = I915_READ(MCHBAR_PCU_C0); /* unit in 32*1.28 usec */ + + bdw_sw_calculate_freq(dev, &dev_priv->rps.sw_turbo.up, + ¤t_time, ¤t_c0); + bdw_sw_calculate_freq(dev, &dev_priv->rps.sw_turbo.down, + ¤t_time, ¤t_c0); +} + static void gen8_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; uint32_t rc6_mask = 0, rp_state_cap; + uint32_t threshold_up_pct, threshold_down_pct; + uint32_t ei_up, ei_down; /* up and down evaluation interval */ + u32 rp_ctl_flag; int unused; + /* Use software Turbo for BDW */ + dev_priv->rps.is_bdw_sw_turbo = IS_BROADWELL(dev); + /* 1a: Software RC state - RC0 */ I915_WRITE(GEN6_RC_STATE, 0); @@ -3681,35 +3834,74 @@ static void gen8_enable_rps(struct drm_device *dev) HSW_FREQUENCY(dev_priv->rps.rp1_freq)); I915_WRITE(GEN6_RC_VIDEO_FREQ, HSW_FREQUENCY(dev_priv->rps.rp1_freq)); - /* NB: Docs say 1s, and 1000000 - which aren't equivalent */ - I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 100000000 / 128); /* 1 second timeout */ + ei_up = 84480; /* 84.48ms */ + ei_down = 448000; + threshold_up_pct = 90; /* x percent busy */ + threshold_down_pct = 70; + + if (dev_priv->rps.is_bdw_sw_turbo) { + dev_priv->rps.sw_turbo.up.it_threshold_pct = threshold_up_pct; + dev_priv->rps.sw_turbo.up.eval_interval = ei_up; + dev_priv->rps.sw_turbo.up.is_up = true; + dev_priv->rps.sw_turbo.up.last_ts = 0; + dev_priv->rps.sw_turbo.up.last_c0 = 0; + + dev_priv->rps.sw_turbo.down.it_threshold_pct = threshold_down_pct; + dev_priv->rps.sw_turbo.down.eval_interval = ei_down; + dev_priv->rps.sw_turbo.down.is_up = false; + dev_priv->rps.sw_turbo.down.last_ts = 0; + dev_priv->rps.sw_turbo.down.last_c0 = 0; + + /* Start the timer to track if flip comes*/ + dev_priv->rps.sw_turbo.timeout = 200*1000; /* in us */ + + init_timer(&dev_priv->rps.sw_turbo.flip_timer); + dev_priv->rps.sw_turbo.flip_timer.function = flip_active_timeout_handler; + dev_priv->rps.sw_turbo.flip_timer.data = (unsigned long) dev_priv; + dev_priv->rps.sw_turbo.flip_timer.expires = + usecs_to_jiffies(dev_priv->rps.sw_turbo.timeout) + jiffies; + add_timer(&dev_priv->rps.sw_turbo.flip_timer); + INIT_WORK(&dev_priv->rps.sw_turbo.work_max_freq, gen8_set_frequency_RP0); + + atomic_set(&dev_priv->rps.sw_turbo.flip_received, true); + } else { + /* NB: Docs say 1s, and 1000000 - which aren't equivalent + * 1 second timeout*/ + I915_WRITE(GEN6_RP_DOWN_TIMEOUT, FREQ_1_28_US(1000000)); - /* Docs recommend 900MHz, and 300 MHz respectively */ - I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, - dev_priv->rps.max_freq_softlimit << 24 | - dev_priv->rps.min_freq_softlimit << 16); + /* Docs recommend 900MHz, and 300 MHz respectively */ + I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, + dev_priv->rps.max_freq_softlimit << 24 | + dev_priv->rps.min_freq_softlimit << 16); - I915_WRITE(GEN6_RP_UP_THRESHOLD, 7600000 / 128); /* 76ms busyness per EI, 90% */ - I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 31300000 / 128); /* 313ms busyness per EI, 70%*/ - I915_WRITE(GEN6_RP_UP_EI, 66000); /* 84.48ms, XXX: random? */ - I915_WRITE(GEN6_RP_DOWN_EI, 350000); /* 448ms, XXX: random? */ + I915_WRITE(GEN6_RP_UP_THRESHOLD, + FREQ_1_28_US(ei_up * threshold_up_pct / 100)); + I915_WRITE(GEN6_RP_DOWN_THRESHOLD, + FREQ_1_28_US(ei_down * threshold_down_pct / 100)); + I915_WRITE(GEN6_RP_UP_EI, + FREQ_1_28_US(ei_up)); + I915_WRITE(GEN6_RP_DOWN_EI, + FREQ_1_28_US(ei_down)); - I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + } /* 5: Enable RPS */ - 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_AVG); - - /* 6: Ring frequency + overclocking (our driver does this later */ - + rp_ctl_flag = GEN6_RP_MEDIA_TURBO | + GEN6_RP_MEDIA_HW_NORMAL_MODE | + GEN6_RP_MEDIA_IS_GFX | + GEN6_RP_UP_BUSY_AVG | + GEN6_RP_DOWN_IDLE_AVG; + if (!dev_priv->rps.is_bdw_sw_turbo) + rp_ctl_flag |= GEN6_RP_ENABLE; + + I915_WRITE(GEN6_RP_CONTROL, rp_ctl_flag); + + /* 6: Ring frequency + overclocking + * (our driver does this later */ gen6_set_rps(dev, (I915_READ(GEN6_GT_PERF_STATUS) & 0xff00) >> 8); - - gen8_enable_rps_interrupts(dev); + if (!dev_priv->rps.is_bdw_sw_turbo) + gen8_enable_rps_interrupts(dev); gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); } @@ -4084,11 +4276,27 @@ static void valleyview_cleanup_pctx(struct drm_device *dev) static void valleyview_init_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; valleyview_setup_pctx(dev); mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); + 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); + dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv); dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", @@ -4123,11 +4331,38 @@ static void valleyview_init_gt_powersave(struct drm_device *dev) static void cherryview_init_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; cherryview_setup_pctx(dev); mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, CCK_FUSE_REG); + switch ((val >> 2) & 0x7) { + case 0: + case 1: + dev_priv->rps.cz_freq = 200; + dev_priv->mem_freq = 1600; + break; + case 2: + dev_priv->rps.cz_freq = 267; + dev_priv->mem_freq = 1600; + break; + case 3: + dev_priv->rps.cz_freq = 333; + dev_priv->mem_freq = 2000; + break; + case 4: + dev_priv->rps.cz_freq = 320; + dev_priv->mem_freq = 1600; + break; + case 5: + dev_priv->rps.cz_freq = 400; + dev_priv->mem_freq = 1600; + break; + } + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); + dev_priv->rps.max_freq = cherryview_rps_max_freq(dev_priv); dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", @@ -4149,6 +4384,12 @@ static void cherryview_init_gt_powersave(struct drm_device *dev) vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), dev_priv->rps.min_freq); + WARN_ONCE((dev_priv->rps.max_freq | + dev_priv->rps.efficient_freq | + dev_priv->rps.rp1_freq | + dev_priv->rps.min_freq) & 1, + "Odd GPU freq values\n"); + /* Preserve min/max settings in case of re-init */ if (dev_priv->rps.max_freq_softlimit == 0) dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; @@ -5134,6 +5375,8 @@ static void intel_gen6_powersave_work(struct work_struct *work) rps.delayed_resume_work.work); struct drm_device *dev = dev_priv->dev; + dev_priv->rps.is_bdw_sw_turbo = false; + mutex_lock(&dev_priv->rps.hw_lock); if (IS_CHERRYVIEW(dev)) { @@ -5207,7 +5450,7 @@ static void g4x_disable_trickle_feed(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); @@ -5322,7 +5565,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) /* The below fixes the weird display corruption, a few pixels shifted * downward, on (only) LVDS of some HP laptops with IVY. */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { val = I915_READ(TRANS_CHICKEN2(pipe)); val |= TRANS_CHICKEN2_TIMING_OVERRIDE; val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; @@ -5334,7 +5577,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) I915_WRITE(TRANS_CHICKEN2(pipe), val); } /* WADP0ClockGatingDisable */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(TRANS_CHICKEN1(pipe), TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } @@ -5502,7 +5745,7 @@ static void lpt_suspend_hw(struct drm_device *dev) } } -static void gen8_init_clock_gating(struct drm_device *dev) +static void broadwell_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; @@ -5514,37 +5757,12 @@ static void gen8_init_clock_gating(struct drm_device *dev) /* FIXME(BDW): Check all the w/a, some might only apply to * pre-production hw. */ - /* WaDisablePartialInstShootdown:bdw */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); - - /* WaDisableThreadStallDopClockGating:bdw */ - /* FIXME: Unclear whether we really need this on production bdw. */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); - /* - * This GEN8_CENTROID_PIXEL_OPT_DIS W/A is only needed for - * pre-production hardware - */ - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS)); - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_BWGTLB_DISABLE)); I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2))); - I915_WRITE(COMMON_SLICE_CHICKEN2, - _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE)); - - I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, - _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE)); - - /* WaDisableDopClockGating:bdw May not be needed for production */ - I915_WRITE(GEN7_ROW_CHICKEN2, - _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); /* WaSwitchSolVfFArbitrationPriority:bdw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); @@ -5554,37 +5772,18 @@ static void gen8_init_clock_gating(struct drm_device *dev) I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD); /* WaPsrDPRSUnmaskVBlankInSRD:bdw */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(CHICKEN_PIPESL_1(pipe), I915_READ(CHICKEN_PIPESL_1(pipe)) | BDW_DPRS_MASK_VBLANK_SRD); } - /* Use Force Non-Coherent whenever executing a 3D context. This is a - * workaround for for a possible hang in the unlikely event a TLB - * invalidation occurs during a PSD flush. - */ - I915_WRITE(HDC_CHICKEN0, - I915_READ(HDC_CHICKEN0) | - _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT)); - /* WaVSRefCountFullforceMissDisable:bdw */ /* WaDSRefCountFullforceMissDisable:bdw */ I915_WRITE(GEN7_FF_THREAD_MODE, I915_READ(GEN7_FF_THREAD_MODE) & ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME)); - /* - * BSpec recommends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN7_GT_MODE, - GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4); - I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL, _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE)); @@ -5592,9 +5791,7 @@ static void gen8_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | GEN8_SDEUNIT_CLOCK_GATE_DISABLE); - /* Wa4x4STCOptimizationDisable:bdw */ - I915_WRITE(CACHE_MODE_1, - _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE)); + lpt_init_clock_gating(dev); } static void haswell_init_clock_gating(struct drm_device *dev) @@ -5750,24 +5947,6 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) static void valleyview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 val; - - mutex_lock(&dev_priv->rps.hw_lock); - val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); - mutex_unlock(&dev_priv->rps.hw_lock); - 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); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); @@ -5843,48 +6022,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev) static void cherryview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 val; - - mutex_lock(&dev_priv->rps.hw_lock); - val = vlv_punit_read(dev_priv, CCK_FUSE_REG); - mutex_unlock(&dev_priv->rps.hw_lock); - switch ((val >> 2) & 0x7) { - case 0: - case 1: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_200; - dev_priv->mem_freq = 1600; - break; - case 2: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_267; - dev_priv->mem_freq = 1600; - break; - case 3: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_333; - dev_priv->mem_freq = 2000; - break; - case 4: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_320; - dev_priv->mem_freq = 1600; - break; - case 5: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_400; - dev_priv->mem_freq = 1600; - break; - } - DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); - /* WaDisablePartialInstShootdown:chv */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); - - /* WaDisableThreadStallDopClockGating:chv */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); - /* WaVSRefCountFullforceMissDisable:chv */ /* WaDSRefCountFullforceMissDisable:chv */ I915_WRITE(GEN7_FF_THREAD_MODE, @@ -5903,10 +6045,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | GEN8_SDEUNIT_CLOCK_GATE_DISABLE); - /* WaDisableSamplerPowerBypass:chv (pre-production hw) */ - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); - /* WaDisableGunitClockGating:chv (pre-production hw) */ I915_WRITE(VLV_GUNIT_CLOCK_GATE, I915_READ(VLV_GUNIT_CLOCK_GATE) | GINT_DIS); @@ -5916,8 +6054,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(GEN8_FF_DOP_CLOCK_GATE_DISABLE)); /* WaDisableDopClockGating:chv (pre-production hw) */ - I915_WRITE(GEN7_ROW_CHICKEN2, - _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE); } @@ -6002,6 +6138,9 @@ static void gen3_init_clock_gating(struct drm_device *dev) /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); + + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); } static void i85x_init_clock_gating(struct drm_device *dev) @@ -6013,6 +6152,9 @@ static void i85x_init_clock_gating(struct drm_device *dev) /* interrupts should cause a wake up from C3 */ I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) | _MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE)); + + I915_WRITE(MEM_MODE, + _MASKED_BIT_ENABLE(MEM_DISPLAY_TRICKLE_FEED_DISABLE)); } static void i830_init_clock_gating(struct drm_device *dev) @@ -6020,6 +6162,10 @@ static void i830_init_clock_gating(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); + + I915_WRITE(MEM_MODE, + _MASKED_BIT_ENABLE(MEM_DISPLAY_A_TRICKLE_FEED_DISABLE) | + _MASKED_BIT_ENABLE(MEM_DISPLAY_B_TRICKLE_FEED_DISABLE)); } void intel_init_clock_gating(struct drm_device *dev) @@ -6322,6 +6468,8 @@ static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, spin_unlock_irq(&dev_priv->irq_lock); vlv_set_power_well(dev_priv, power_well, false); + + vlv_power_sequencer_reset(dev_priv); } static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, @@ -6357,12 +6505,11 @@ static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { - struct drm_device *dev = dev_priv->dev; enum pipe pipe; WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) assert_pll_disabled(dev_priv, pipe); /* Assert common reset */ @@ -7249,7 +7396,7 @@ void intel_init_pm(struct drm_device *dev) else if (IS_HASWELL(dev)) dev_priv->display.init_clock_gating = haswell_init_clock_gating; else if (INTEL_INFO(dev)->gen == 8) - dev_priv->display.init_clock_gating = gen8_init_clock_gating; + dev_priv->display.init_clock_gating = broadwell_init_clock_gating; } else if (IS_CHERRYVIEW(dev)) { dev_priv->display.update_wm = cherryview_update_wm; dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm; @@ -7443,6 +7590,7 @@ static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) return -1; } + /* CHV needs even values */ opcode = (DIV_ROUND_CLOSEST((val * 2 * mul), dev_priv->rps.cz_freq) * 2); return opcode; diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h index fd4f66231d30..6c792d3a9c9c 100644 --- a/drivers/gpu/drm/i915/intel_renderstate.h +++ b/drivers/gpu/drm/i915/intel_renderstate.h @@ -24,13 +24,7 @@ #ifndef _INTEL_RENDERSTATE_H #define _INTEL_RENDERSTATE_H -#include <linux/types.h> - -struct intel_renderstate_rodata { - const u32 *reloc; - const u32 *batch; - const u32 batch_items; -}; +#include "i915_drv.h" extern const struct intel_renderstate_rodata gen6_null_state; extern const struct intel_renderstate_rodata gen7_null_state; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 3c7283d8f160..109de2eeb9a8 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -444,7 +444,14 @@ gen8_render_ring_flush(struct intel_engine_cs *ring, return ret; } - return gen8_emit_pipe_control(ring, flags, scratch_addr); + ret = gen8_emit_pipe_control(ring, flags, scratch_addr); + if (ret) + return ret; + + if (!invalidate_domains && flush_domains) + return gen7_ring_fbc_flush(ring, FBC_REND_NUKE); + + return 0; } static void ring_write_tail(struct intel_engine_cs *ring, @@ -556,6 +563,14 @@ static int init_ring_common(struct intel_engine_cs *ring) * also enforces ordering), otherwise the hw might lose the new ring * register values. */ I915_WRITE_START(ring, i915_gem_obj_ggtt_offset(obj)); + + /* WaClearRingBufHeadRegAtInit:ctg,elk */ + if (I915_READ_HEAD(ring)) + DRM_DEBUG("%s initialization failed [head=%08x], fudging\n", + ring->name, I915_READ_HEAD(ring)); + I915_WRITE_HEAD(ring, 0); + (void)I915_READ_HEAD(ring); + I915_WRITE_CTL(ring, ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID); @@ -650,6 +665,146 @@ err: return ret; } +static inline void intel_ring_emit_wa(struct intel_engine_cs *ring, + u32 addr, u32 value) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (WARN_ON(dev_priv->num_wa_regs >= I915_MAX_WA_REGS)) + return; + + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit(ring, addr); + intel_ring_emit(ring, value); + + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].addr = addr; + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].mask = value & 0xFFFF; + /* value is updated with the status of remaining bits of this + * register when it is read from debugfs file + */ + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].value = value; + dev_priv->num_wa_regs++; + + return; +} + +static int bdw_init_workarounds(struct intel_engine_cs *ring) +{ + int ret; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* + * workarounds applied in this fn are part of register state context, + * they need to be re-initialized followed by gpu reset, suspend/resume, + * module reload. + */ + dev_priv->num_wa_regs = 0; + memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs)); + + /* + * update the number of dwords required based on the + * actual number of workarounds applied + */ + ret = intel_ring_begin(ring, 24); + if (ret) + return ret; + + /* WaDisablePartialInstShootdown:bdw */ + /* WaDisableThreadStallDopClockGating:bdw */ + /* FIXME: Unclear whether we really need this on production bdw. */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE + | STALL_DOP_GATING_DISABLE)); + + /* WaDisableDopClockGating:bdw May not be needed for production */ + intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2, + _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); + + /* + * This GEN8_CENTROID_PIXEL_OPT_DIS W/A is only needed for + * pre-production hardware + */ + intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3, + _MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS + | GEN8_SAMPLER_POWER_BYPASS_DIS)); + + intel_ring_emit_wa(ring, GEN7_HALF_SLICE_CHICKEN1, + _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE)); + + intel_ring_emit_wa(ring, COMMON_SLICE_CHICKEN2, + _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE)); + + /* Use Force Non-Coherent whenever executing a 3D context. This is a + * workaround for for a possible hang in the unlikely event a TLB + * invalidation occurs during a PSD flush. + */ + intel_ring_emit_wa(ring, HDC_CHICKEN0, + _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT)); + + /* Wa4x4STCOptimizationDisable:bdw */ + intel_ring_emit_wa(ring, CACHE_MODE_1, + _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE)); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + intel_ring_emit_wa(ring, GEN7_GT_MODE, + GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4); + + intel_ring_advance(ring); + + DRM_DEBUG_DRIVER("Number of Workarounds applied: %d\n", + dev_priv->num_wa_regs); + + return 0; +} + +static int chv_init_workarounds(struct intel_engine_cs *ring) +{ + int ret; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* + * workarounds applied in this fn are part of register state context, + * they need to be re-initialized followed by gpu reset, suspend/resume, + * module reload. + */ + dev_priv->num_wa_regs = 0; + memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs)); + + ret = intel_ring_begin(ring, 12); + if (ret) + return ret; + + /* WaDisablePartialInstShootdown:chv */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); + + /* WaDisableThreadStallDopClockGating:chv */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); + + /* WaDisableDopClockGating:chv (pre-production hw) */ + intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2, + _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); + + /* WaDisableSamplerPowerBypass:chv (pre-production hw) */ + intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3, + _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); + + intel_ring_advance(ring); + + return 0; +} + static int init_render_ring(struct intel_engine_cs *ring) { struct drm_device *dev = ring->dev; @@ -2148,6 +2303,10 @@ int intel_init_render_ring_buffer(struct drm_device *dev) dev_priv->semaphore_obj = obj; } } + if (IS_CHERRYVIEW(dev)) + ring->init_context = chv_init_workarounds; + else + ring->init_context = bdw_init_workarounds; ring->add_request = gen6_add_request; ring->flush = gen8_render_ring_flush; ring->irq_get = gen8_ring_get_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 9cbf7b0ebc99..96479c89f4bd 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -148,6 +148,8 @@ struct intel_engine_cs { int (*init)(struct intel_engine_cs *ring); + int (*init_context)(struct intel_engine_cs *ring); + void (*write_tail)(struct intel_engine_cs *ring, u32 value); int __must_check (*flush)(struct intel_engine_cs *ring, diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 0bdb00b7c59c..07a74ef589bd 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1218,9 +1218,9 @@ out_unlock: return ret; } -static int intel_plane_set_property(struct drm_plane *plane, - struct drm_property *prop, - uint64_t val) +int intel_plane_set_property(struct drm_plane *plane, + struct drm_property *prop, + uint64_t val) { struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); @@ -1232,6 +1232,9 @@ static int intel_plane_set_property(struct drm_plane *plane, if (hweight32(val & 0xf) != 1) return -EINVAL; + if (intel_plane->rotation == val) + return 0; + old_val = intel_plane->rotation; intel_plane->rotation = val; ret = intel_plane_restore(plane); @@ -1249,7 +1252,7 @@ int intel_plane_restore(struct drm_plane *plane) if (!plane->crtc || !plane->fb) return 0; - return intel_update_plane(plane, plane->crtc, plane->fb, + return plane->funcs->update_plane(plane, plane->crtc, plane->fb, intel_plane->crtc_x, intel_plane->crtc_y, intel_plane->crtc_w, intel_plane->crtc_h, intel_plane->src_x, intel_plane->src_y, @@ -1375,10 +1378,10 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) intel_plane->plane = plane; intel_plane->rotation = BIT(DRM_ROTATE_0); possible_crtcs = (1 << pipe); - ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs, - &intel_plane_funcs, - plane_formats, num_plane_formats, - false); + ret = drm_universal_plane_init(dev, &intel_plane->base, possible_crtcs, + &intel_plane_funcs, + plane_formats, num_plane_formats, + DRM_PLANE_TYPE_OVERLAY); if (ret) { kfree(intel_plane); goto out; diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index e81bc3bdc533..918b76163965 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -101,7 +101,7 @@ static void __gen7_gt_force_wake_mt_get(struct drm_i915_private *dev_priv, { u32 forcewake_ack; - if (IS_HASWELL(dev_priv->dev) || IS_GEN8(dev_priv->dev)) + if (IS_HASWELL(dev_priv->dev) || IS_BROADWELL(dev_priv->dev)) forcewake_ack = FORCEWAKE_ACK_HSW; else forcewake_ack = FORCEWAKE_MT_ACK; @@ -334,7 +334,7 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) else if (IS_GEN6(dev) || IS_GEN7(dev)) __gen6_gt_force_wake_reset(dev_priv); - if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_GEN8(dev)) + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) __gen7_gt_force_wake_mt_reset(dev_priv); if (restore) { /* If reset with a user forcewake, try to restore */ @@ -838,7 +838,7 @@ void intel_uncore_init(struct drm_device *dev) if (IS_VALLEYVIEW(dev)) { dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get; dev_priv->uncore.funcs.force_wake_put = __vlv_force_wake_put; - } else if (IS_HASWELL(dev) || IS_GEN8(dev)) { + } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { dev_priv->uncore.funcs.force_wake_get = __gen7_gt_force_wake_mt_get; dev_priv->uncore.funcs.force_wake_put = __gen7_gt_force_wake_mt_put; } else if (IS_IVYBRIDGE(dev)) { |