2006-01-02 16:14:23 +07:00
|
|
|
/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2006-01-02 16:14:23 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
|
|
|
|
* All Rights Reserved.
|
2005-06-23 19:46:46 +07:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the
|
|
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
|
|
* of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
|
|
|
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
|
|
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*
|
2006-01-02 16:14:23 +07:00
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-03-19 03:00:11 +07:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2013-10-16 00:55:29 +07:00
|
|
|
#include <linux/circ_buf.h>
|
2019-04-05 18:00:09 +07:00
|
|
|
#include <linux/cpuidle.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/sysrq.h>
|
|
|
|
|
2019-01-18 04:03:34 +07:00
|
|
|
#include <drm/drm_drv.h>
|
2019-04-05 18:00:09 +07:00
|
|
|
#include <drm/drm_irq.h>
|
2012-10-03 00:01:07 +07:00
|
|
|
#include <drm/i915_drm.h>
|
2019-04-05 18:00:09 +07:00
|
|
|
|
2019-06-13 15:44:16 +07:00
|
|
|
#include "display/intel_fifo_underrun.h"
|
|
|
|
#include "display/intel_hotplug.h"
|
|
|
|
#include "display/intel_lpe_audio.h"
|
|
|
|
#include "display/intel_psr.h"
|
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
#include "gt/intel_gt.h"
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "i915_drv.h"
|
2019-04-29 19:29:27 +07:00
|
|
|
#include "i915_irq.h"
|
2009-08-25 17:15:50 +07:00
|
|
|
#include "i915_trace.h"
|
DRM: i915: add mode setting support
This commit adds i915 driver support for the DRM mode setting APIs.
Currently, VGA, LVDS, SDVO DVI & VGA, TV and DVO LVDS outputs are
supported. HDMI, DisplayPort and additional SDVO output support will
follow.
Support for the mode setting code is controlled by the new 'modeset'
module option. A new config option, CONFIG_DRM_I915_KMS controls the
default behavior, and whether a PCI ID list is built into the module for
use by user level module utilities.
Note that if mode setting is enabled, user level drivers that access
display registers directly or that don't use the kernel graphics memory
manager will likely corrupt kernel graphics memory, disrupt output
configuration (possibly leading to hangs and/or blank displays), and
prevent panic/oops messages from appearing. So use caution when
enabling this code; be sure your user level code supports the new
interfaces.
A new SysRq key, 'g', provides emergency support for switching back to
the kernel's framebuffer console; which is useful for testing.
Co-authors: Dave Airlie <airlied@linux.ie>, Hong Liu <hong.liu@intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
2008-11-08 05:24:08 +07:00
|
|
|
#include "intel_drv.h"
|
2019-06-06 19:22:00 +07:00
|
|
|
#include "intel_pm.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* DOC: interrupt handling
|
|
|
|
*
|
|
|
|
* These functions provide the basic support for enabling and disabling the
|
|
|
|
* interrupt handling support. There's a lot more functionality in i915_irq.c
|
|
|
|
* and related files, but that will be described in separate chapters.
|
|
|
|
*/
|
|
|
|
|
2015-08-28 03:56:03 +07:00
|
|
|
static const u32 hpd_ilk[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_A] = DE_DP_A_HOTPLUG,
|
|
|
|
};
|
|
|
|
|
2015-08-28 03:56:04 +07:00
|
|
|
static const u32 hpd_ivb[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB,
|
|
|
|
};
|
|
|
|
|
2015-08-28 03:56:06 +07:00
|
|
|
static const u32 hpd_bdw[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_A] = GEN8_PORT_DP_A_HOTPLUG,
|
|
|
|
};
|
|
|
|
|
2015-01-09 19:21:12 +07:00
|
|
|
static const u32 hpd_ibx[HPD_NUM_PINS] = {
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_CRT] = SDE_CRT_HOTPLUG,
|
|
|
|
[HPD_SDVO_B] = SDE_SDVOB_HOTPLUG,
|
|
|
|
[HPD_PORT_B] = SDE_PORTB_HOTPLUG,
|
|
|
|
[HPD_PORT_C] = SDE_PORTC_HOTPLUG,
|
|
|
|
[HPD_PORT_D] = SDE_PORTD_HOTPLUG
|
|
|
|
};
|
|
|
|
|
2015-01-09 19:21:12 +07:00
|
|
|
static const u32 hpd_cpt[HPD_NUM_PINS] = {
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_CRT] = SDE_CRT_HOTPLUG_CPT,
|
2013-03-27 04:38:43 +07:00
|
|
|
[HPD_SDVO_B] = SDE_SDVOB_HOTPLUG_CPT,
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
|
|
|
|
[HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
|
|
|
|
[HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT
|
|
|
|
};
|
|
|
|
|
2015-08-17 14:55:50 +07:00
|
|
|
static const u32 hpd_spt[HPD_NUM_PINS] = {
|
2015-08-28 03:56:07 +07:00
|
|
|
[HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT,
|
2015-08-17 14:55:50 +07:00
|
|
|
[HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
|
|
|
|
[HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
|
|
|
|
[HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT,
|
|
|
|
[HPD_PORT_E] = SDE_PORTE_HOTPLUG_SPT
|
|
|
|
};
|
|
|
|
|
2015-01-09 19:21:12 +07:00
|
|
|
static const u32 hpd_mask_i915[HPD_NUM_PINS] = {
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_CRT] = CRT_HOTPLUG_INT_EN,
|
|
|
|
[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN,
|
|
|
|
[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN,
|
|
|
|
[HPD_PORT_B] = PORTB_HOTPLUG_INT_EN,
|
|
|
|
[HPD_PORT_C] = PORTC_HOTPLUG_INT_EN,
|
|
|
|
[HPD_PORT_D] = PORTD_HOTPLUG_INT_EN
|
|
|
|
};
|
|
|
|
|
2015-01-09 19:21:12 +07:00
|
|
|
static const u32 hpd_status_g4x[HPD_NUM_PINS] = {
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X,
|
|
|
|
[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X,
|
|
|
|
[HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
|
|
|
|
};
|
|
|
|
|
drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
Use HOTPLUG_INT_STATUS_G4X instead of HOTPLUG_INT_STATUS_I915 on VLV/CHV
so that we don't confuse the AUX status bits with SDVO status bits.
Avoid pointless log spam as below while handling AUX interrupts:
[drm:intel_hpd_irq_handler] hotplug event received, stat 0x00000040, dig 0x00000000
[drm:intel_hpd_irq_handler] hotplug event received, stat 0x00000040, dig 0x00000000
[drm:intel_hpd_irq_handler] hotplug event received, stat 0x00000040, dig 0x00000000
[drm:intel_hpd_irq_handler] hotplug event received, stat 0x00000040, dig 0x00000000
[drm:intel_hpd_irq_handler] hotplug event received, stat 0x00000040, dig 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Note that there's no functional issue, it's just that the sdvo bits
overlap with the dp aux bits. Hence every time we receive an aux
interrupt we also think there's an sdvo hpd interrupt, but due to lack
of any sdvo encoders nothing ever happens because of that.
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
[danvet: Add Ville's explanation why nothing functional really
changes.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-05-12 00:49:10 +07:00
|
|
|
static const u32 hpd_status_i915[HPD_NUM_PINS] = {
|
2013-02-28 16:17:12 +07:00
|
|
|
[HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915,
|
|
|
|
[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915,
|
|
|
|
[HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
|
|
|
|
[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
|
|
|
|
};
|
|
|
|
|
2015-03-27 19:54:14 +07:00
|
|
|
/* BXT hpd list */
|
|
|
|
static const u32 hpd_bxt[HPD_NUM_PINS] = {
|
2015-08-10 12:05:35 +07:00
|
|
|
[HPD_PORT_A] = BXT_DE_PORT_HP_DDIA,
|
2015-03-27 19:54:14 +07:00
|
|
|
[HPD_PORT_B] = BXT_DE_PORT_HP_DDIB,
|
|
|
|
[HPD_PORT_C] = BXT_DE_PORT_HP_DDIC
|
|
|
|
};
|
|
|
|
|
2018-06-16 07:05:30 +07:00
|
|
|
static const u32 hpd_gen11[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_C] = GEN11_TC1_HOTPLUG | GEN11_TBT1_HOTPLUG,
|
|
|
|
[HPD_PORT_D] = GEN11_TC2_HOTPLUG | GEN11_TBT2_HOTPLUG,
|
|
|
|
[HPD_PORT_E] = GEN11_TC3_HOTPLUG | GEN11_TBT3_HOTPLUG,
|
|
|
|
[HPD_PORT_F] = GEN11_TC4_HOTPLUG | GEN11_TBT4_HOTPLUG
|
2018-06-16 07:05:29 +07:00
|
|
|
};
|
|
|
|
|
2018-06-27 03:52:23 +07:00
|
|
|
static const u32 hpd_icp[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_A] = SDE_DDIA_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_B] = SDE_DDIB_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_C] = SDE_TC1_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_D] = SDE_TC2_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_E] = SDE_TC3_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_F] = SDE_TC4_HOTPLUG_ICP
|
|
|
|
};
|
|
|
|
|
2019-06-15 07:42:10 +07:00
|
|
|
static const u32 hpd_mcc[HPD_NUM_PINS] = {
|
|
|
|
[HPD_PORT_A] = SDE_DDIA_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_B] = SDE_DDIB_HOTPLUG_ICP,
|
|
|
|
[HPD_PORT_C] = SDE_TC1_HOTPLUG_ICP
|
|
|
|
};
|
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen3_irq_reset(struct intel_uncore *uncore, i915_reg_t imr,
|
2019-04-11 06:53:40 +07:00
|
|
|
i915_reg_t iir, i915_reg_t ier)
|
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write(uncore, imr, 0xffffffff);
|
|
|
|
intel_uncore_posting_read(uncore, imr);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write(uncore, ier, 0);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
|
|
|
/* IIR can theoretically queue up two events. Be paranoid. */
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write(uncore, iir, 0xffffffff);
|
|
|
|
intel_uncore_posting_read(uncore, iir);
|
|
|
|
intel_uncore_write(uncore, iir, 0xffffffff);
|
|
|
|
intel_uncore_posting_read(uncore, iir);
|
2019-04-11 06:53:40 +07:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen2_irq_reset(struct intel_uncore *uncore)
|
2019-04-11 06:53:40 +07:00
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write16(uncore, GEN2_IMR, 0xffff);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IMR);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write16(uncore, GEN2_IER, 0);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
|
|
|
/* IIR can theoretically queue up two events. Be paranoid. */
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write16(uncore, GEN2_IIR, 0xffff);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IIR);
|
|
|
|
intel_uncore_write16(uncore, GEN2_IIR, 0xffff);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IIR);
|
2019-04-11 06:53:40 +07:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN8_IRQ_RESET_NDX(uncore, type, which) \
|
2019-04-11 06:53:40 +07:00
|
|
|
({ \
|
|
|
|
unsigned int which_ = which; \
|
2019-04-11 06:53:44 +07:00
|
|
|
gen3_irq_reset((uncore), GEN8_##type##_IMR(which_), \
|
2019-04-11 06:53:40 +07:00
|
|
|
GEN8_##type##_IIR(which_), GEN8_##type##_IER(which_)); \
|
|
|
|
})
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN3_IRQ_RESET(uncore, type) \
|
|
|
|
gen3_irq_reset((uncore), type##IMR, type##IIR, type##IER)
|
2019-04-11 06:53:40 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN2_IRQ_RESET(uncore) \
|
|
|
|
gen2_irq_reset(uncore)
|
2017-08-19 01:36:54 +07:00
|
|
|
|
2014-04-02 01:37:16 +07:00
|
|
|
/*
|
|
|
|
* We should clear IMR at preinstall/uninstall, and just check at postinstall.
|
|
|
|
*/
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen3_assert_iir_is_zero(struct intel_uncore *uncore, i915_reg_t reg)
|
2015-09-19 00:03:41 +07:00
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
u32 val = intel_uncore_read(uncore, reg);
|
2015-09-19 00:03:41 +07:00
|
|
|
|
|
|
|
if (val == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n",
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
i915_mmio_reg_offset(reg), val);
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write(uncore, reg, 0xffffffff);
|
|
|
|
intel_uncore_posting_read(uncore, reg);
|
|
|
|
intel_uncore_write(uncore, reg, 0xffffffff);
|
|
|
|
intel_uncore_posting_read(uncore, reg);
|
2015-09-19 00:03:41 +07:00
|
|
|
}
|
2014-04-02 01:37:16 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen2_assert_iir_is_zero(struct intel_uncore *uncore)
|
2017-08-19 01:36:54 +07:00
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
u16 val = intel_uncore_read16(uncore, GEN2_IIR);
|
2017-08-19 01:36:54 +07:00
|
|
|
|
|
|
|
if (val == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n",
|
drm/i915: add GEN2_ prefix to the I{E, I, M, S}R registers
This discussion started because we use token pasting in the
GEN{2,3}_IRQ_INIT and GEN{2,3}_IRQ_RESET macros, so gen2-4 passes an
empty argument to those macros, making the code a little weird. The
original proposal was to just add a comment as the empty argument, but
Ville suggested we just add a prefix to the registers, and that indeed
sounds like a more elegant solution.
Now doing this is kinda against our rules for register naming since we
only add gens or platform names as register prefixes when the given
gen/platform changes a register that already existed before. On the
other hand, we have so many instances of IIR/IMR in comments that
adding a prefix would make the users of these register more easily
findable, in addition to make our token pasting macros actually
readable. So IMHO opening an exception here is worth it.
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410235344.31199-4-paulo.r.zanoni@intel.com
2019-04-11 06:53:42 +07:00
|
|
|
i915_mmio_reg_offset(GEN2_IIR), val);
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write16(uncore, GEN2_IIR, 0xffff);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IIR);
|
|
|
|
intel_uncore_write16(uncore, GEN2_IIR, 0xffff);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IIR);
|
2017-08-19 01:36:54 +07:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen3_irq_init(struct intel_uncore *uncore,
|
2019-04-11 06:53:40 +07:00
|
|
|
i915_reg_t imr, u32 imr_val,
|
|
|
|
i915_reg_t ier, u32 ier_val,
|
|
|
|
i915_reg_t iir)
|
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
gen3_assert_iir_is_zero(uncore, iir);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write(uncore, ier, ier_val);
|
|
|
|
intel_uncore_write(uncore, imr, imr_val);
|
|
|
|
intel_uncore_posting_read(uncore, imr);
|
2019-04-11 06:53:40 +07:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
static void gen2_irq_init(struct intel_uncore *uncore,
|
2019-04-11 06:53:41 +07:00
|
|
|
u32 imr_val, u32 ier_val)
|
2019-04-11 06:53:40 +07:00
|
|
|
{
|
2019-04-11 06:53:43 +07:00
|
|
|
gen2_assert_iir_is_zero(uncore);
|
2019-04-11 06:53:40 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
intel_uncore_write16(uncore, GEN2_IER, ier_val);
|
|
|
|
intel_uncore_write16(uncore, GEN2_IMR, imr_val);
|
|
|
|
intel_uncore_posting_read16(uncore, GEN2_IMR);
|
2019-04-11 06:53:40 +07:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN8_IRQ_INIT_NDX(uncore, type, which, imr_val, ier_val) \
|
2019-04-11 06:53:40 +07:00
|
|
|
({ \
|
|
|
|
unsigned int which_ = which; \
|
2019-04-11 06:53:44 +07:00
|
|
|
gen3_irq_init((uncore), \
|
2019-04-11 06:53:40 +07:00
|
|
|
GEN8_##type##_IMR(which_), imr_val, \
|
|
|
|
GEN8_##type##_IER(which_), ier_val, \
|
|
|
|
GEN8_##type##_IIR(which_)); \
|
|
|
|
})
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN3_IRQ_INIT(uncore, type, imr_val, ier_val) \
|
|
|
|
gen3_irq_init((uncore), \
|
2019-04-11 06:53:40 +07:00
|
|
|
type##IMR, imr_val, \
|
|
|
|
type##IER, ier_val, \
|
|
|
|
type##IIR)
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
#define GEN2_IRQ_INIT(uncore, imr_val, ier_val) \
|
|
|
|
gen2_irq_init((uncore), imr_val, ier_val)
|
2017-08-19 01:36:54 +07:00
|
|
|
|
2014-11-06 01:48:37 +07:00
|
|
|
static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
|
2019-07-13 17:00:10 +07:00
|
|
|
static void guc_irq_handler(struct intel_guc *guc, u16 guc_iir);
|
2014-11-06 01:48:37 +07:00
|
|
|
|
2015-09-23 21:15:27 +07:00
|
|
|
/* For display hotplug interrupt */
|
|
|
|
static inline void
|
|
|
|
i915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 mask,
|
|
|
|
u32 bits)
|
2015-09-23 21:15:27 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 val;
|
2015-09-23 21:15:27 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2015-09-23 21:15:27 +07:00
|
|
|
WARN_ON(bits & ~mask);
|
|
|
|
|
|
|
|
val = I915_READ(PORT_HOTPLUG_EN);
|
|
|
|
val &= ~mask;
|
|
|
|
val |= bits;
|
|
|
|
I915_WRITE(PORT_HOTPLUG_EN, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* i915_hotplug_interrupt_update - update hotplug interrupt enable
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @mask: bits to update
|
|
|
|
* @bits: bits to enable
|
|
|
|
* NOTE: the HPD enable bits are modified both inside and outside
|
|
|
|
* of an interrupt context. To avoid that read-modify-write cycles
|
|
|
|
* interfer, these bits are protected by a spinlock. Since this
|
|
|
|
* function is usually not called from a context where the lock is
|
|
|
|
* held already, this function acquires the lock itself. A non-locking
|
|
|
|
* version is also available.
|
|
|
|
*/
|
|
|
|
void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 mask,
|
|
|
|
u32 bits)
|
2015-09-23 21:15:27 +07:00
|
|
|
{
|
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
|
|
|
i915_hotplug_interrupt_update_locked(dev_priv, mask, bits);
|
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
}
|
|
|
|
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
static u32
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_engine_identity(struct intel_gt *gt,
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
const unsigned int bank, const unsigned int bit);
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
static bool gen11_reset_one_iir(struct intel_gt *gt,
|
2018-07-14 03:35:29 +07:00
|
|
|
const unsigned int bank,
|
|
|
|
const unsigned int bit)
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
{
|
2019-07-04 19:17:54 +07:00
|
|
|
void __iomem * const regs = gt->uncore->regs;
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
u32 dw;
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
|
|
|
dw = raw_reg_read(regs, GEN11_GT_INTR_DW(bank));
|
|
|
|
if (dw & BIT(bit)) {
|
|
|
|
/*
|
|
|
|
* According to the BSpec, DW_IIR bits cannot be cleared without
|
|
|
|
* first servicing the Selector & Shared IIR registers.
|
|
|
|
*/
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_engine_identity(gt, bank, bit);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We locked GT INT DW by reading it. If we want to (try
|
|
|
|
* to) recover from this succesfully, we need to clear
|
|
|
|
* our bit, otherwise we are locking the register for
|
|
|
|
* everybody.
|
|
|
|
*/
|
|
|
|
raw_reg_write(regs, GEN11_GT_INTR_DW(bank), BIT(bit));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-28 03:55:58 +07:00
|
|
|
/**
|
|
|
|
* ilk_update_display_irq - update DEIMR
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
2015-11-23 23:06:16 +07:00
|
|
|
void ilk_update_display_irq(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2009-06-08 13:40:19 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 new_val;
|
2015-08-28 03:55:58 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2013-06-27 18:44:58 +07:00
|
|
|
|
2015-08-28 03:55:58 +07:00
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
2014-06-20 23:29:20 +07:00
|
|
|
if (WARN_ON(!intel_irqs_enabled(dev_priv)))
|
2013-08-19 23:18:09 +07:00
|
|
|
return;
|
|
|
|
|
2015-08-28 03:55:58 +07:00
|
|
|
new_val = dev_priv->irq_mask;
|
|
|
|
new_val &= ~interrupt_mask;
|
|
|
|
new_val |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
|
|
|
|
if (new_val != dev_priv->irq_mask) {
|
|
|
|
dev_priv->irq_mask = new_val;
|
2010-12-04 18:30:53 +07:00
|
|
|
I915_WRITE(DEIMR, dev_priv->irq_mask);
|
2010-11-16 22:55:10 +07:00
|
|
|
POSTING_READ(DEIMR);
|
2009-06-08 13:40:19 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 04:57:12 +07:00
|
|
|
/**
|
|
|
|
* ilk_update_gt_irq - update GTIMR
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
|
|
|
static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2013-08-07 04:57:12 +07:00
|
|
|
{
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2013-08-07 04:57:12 +07:00
|
|
|
|
2014-12-08 22:30:00 +07:00
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
2014-06-20 23:29:20 +07:00
|
|
|
if (WARN_ON(!intel_irqs_enabled(dev_priv)))
|
2013-08-19 23:18:09 +07:00
|
|
|
return;
|
|
|
|
|
2013-08-07 04:57:12 +07:00
|
|
|
dev_priv->gt_irq_mask &= ~interrupt_mask;
|
|
|
|
dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
|
|
|
}
|
|
|
|
|
2019-01-16 16:15:24 +07:00
|
|
|
void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, u32 mask)
|
2013-08-07 04:57:12 +07:00
|
|
|
{
|
|
|
|
ilk_update_gt_irq(dev_priv, mask, mask);
|
2019-06-11 17:45:44 +07:00
|
|
|
intel_uncore_posting_read_fw(&dev_priv->uncore, GTIMR);
|
2013-08-07 04:57:12 +07:00
|
|
|
}
|
|
|
|
|
2019-01-16 16:15:24 +07:00
|
|
|
void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, u32 mask)
|
2013-08-07 04:57:12 +07:00
|
|
|
{
|
|
|
|
ilk_update_gt_irq(dev_priv, mask, 0);
|
|
|
|
}
|
|
|
|
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
static i915_reg_t gen6_pm_iir(struct drm_i915_private *dev_priv)
|
2014-11-06 01:48:48 +07:00
|
|
|
{
|
2018-04-05 21:00:50 +07:00
|
|
|
WARN_ON_ONCE(INTEL_GEN(dev_priv) >= 11);
|
|
|
|
|
2017-07-19 01:28:00 +07:00
|
|
|
return INTEL_GEN(dev_priv) >= 8 ? GEN8_GT_IIR(2) : GEN6_PMIIR;
|
2014-11-06 01:48:48 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void write_pm_imr(struct intel_gt *gt)
|
2014-11-06 01:48:31 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
u32 mask = gt->pm_imr;
|
2019-04-10 17:59:22 +07:00
|
|
|
i915_reg_t reg;
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
if (INTEL_GEN(i915) >= 11) {
|
2019-04-10 17:59:22 +07:00
|
|
|
reg = GEN11_GPM_WGBOXPERF_INTR_MASK;
|
|
|
|
/* pm is in upper half */
|
|
|
|
mask = mask << 16;
|
2019-07-04 19:17:56 +07:00
|
|
|
} else if (INTEL_GEN(i915) >= 8) {
|
2019-04-10 17:59:22 +07:00
|
|
|
reg = GEN8_GT_IMR(2);
|
|
|
|
} else {
|
|
|
|
reg = GEN6_PMIMR;
|
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
intel_uncore_write(uncore, reg, mask);
|
|
|
|
intel_uncore_posting_read(uncore, reg);
|
2014-11-06 01:48:31 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void write_pm_ier(struct intel_gt *gt)
|
2014-11-06 01:48:48 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
u32 mask = gt->pm_ier;
|
2019-04-10 17:59:22 +07:00
|
|
|
i915_reg_t reg;
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
if (INTEL_GEN(i915) >= 11) {
|
2019-04-10 17:59:22 +07:00
|
|
|
reg = GEN11_GPM_WGBOXPERF_INTR_ENABLE;
|
|
|
|
/* pm is in upper half */
|
|
|
|
mask = mask << 16;
|
2019-07-04 19:17:56 +07:00
|
|
|
} else if (INTEL_GEN(i915) >= 8) {
|
2019-04-10 17:59:22 +07:00
|
|
|
reg = GEN8_GT_IER(2);
|
|
|
|
} else {
|
|
|
|
reg = GEN6_PMIER;
|
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
intel_uncore_write(uncore, reg, mask);
|
2014-11-06 01:48:48 +07:00
|
|
|
}
|
|
|
|
|
2013-08-07 04:57:13 +07:00
|
|
|
/**
|
2015-11-25 21:21:30 +07:00
|
|
|
* snb_update_pm_irq - update GEN6_PMIMR
|
2019-07-04 19:17:56 +07:00
|
|
|
* @gt: gt for the interrupts
|
2015-11-25 21:21:30 +07:00
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
2019-07-04 19:17:56 +07:00
|
|
|
static void snb_update_pm_irq(struct intel_gt *gt,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2013-08-07 04:57:13 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 new_val;
|
2013-08-07 04:57:13 +07:00
|
|
|
|
2014-12-08 22:30:00 +07:00
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
2013-08-07 04:57:13 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
new_val = gt->pm_imr;
|
2013-08-07 04:57:14 +07:00
|
|
|
new_val &= ~interrupt_mask;
|
|
|
|
new_val |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
if (new_val != gt->pm_imr) {
|
|
|
|
gt->pm_imr = new_val;
|
|
|
|
write_pm_imr(gt);
|
2013-08-07 04:57:14 +07:00
|
|
|
}
|
2013-08-07 04:57:13 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
void gen6_unmask_pm_irq(struct intel_gt *gt, u32 mask)
|
2013-08-07 04:57:13 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
if (WARN_ON(!intel_irqs_enabled(gt->i915)))
|
2014-11-21 04:01:47 +07:00
|
|
|
return;
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
snb_update_pm_irq(gt, mask, mask);
|
2013-08-07 04:57:13 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void __gen6_mask_pm_irq(struct intel_gt *gt, u32 mask)
|
2013-08-07 04:57:13 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
snb_update_pm_irq(gt, mask, 0);
|
2013-08-07 04:57:13 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
void gen6_mask_pm_irq(struct intel_gt *gt, u32 mask)
|
2014-11-21 04:01:47 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
if (WARN_ON(!intel_irqs_enabled(gt->i915)))
|
2014-11-21 04:01:47 +07:00
|
|
|
return;
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
__gen6_mask_pm_irq(gt, mask);
|
2014-11-21 04:01:47 +07:00
|
|
|
}
|
|
|
|
|
2017-08-24 06:58:24 +07:00
|
|
|
static void gen6_reset_pm_iir(struct drm_i915_private *dev_priv, u32 reset_mask)
|
2014-11-19 20:30:03 +07:00
|
|
|
{
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
i915_reg_t reg = gen6_pm_iir(dev_priv);
|
2014-11-19 20:30:03 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2016-10-12 23:24:30 +07:00
|
|
|
|
|
|
|
I915_WRITE(reg, reset_mask);
|
|
|
|
I915_WRITE(reg, reset_mask);
|
2014-11-19 20:30:03 +07:00
|
|
|
POSTING_READ(reg);
|
2016-10-12 23:24:30 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void gen6_enable_pm_irq(struct intel_gt *gt, u32 enable_mask)
|
2016-10-12 23:24:30 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
2016-10-12 23:24:30 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
gt->pm_ier |= enable_mask;
|
|
|
|
write_pm_ier(gt);
|
|
|
|
gen6_unmask_pm_irq(gt, enable_mask);
|
2016-10-12 23:24:30 +07:00
|
|
|
/* unmask_pm_irq provides an implicit barrier (POSTING_READ) */
|
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void gen6_disable_pm_irq(struct intel_gt *gt, u32 disable_mask)
|
2016-10-12 23:24:30 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
2016-10-12 23:24:30 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
gt->pm_ier &= ~disable_mask;
|
|
|
|
__gen6_mask_pm_irq(gt, disable_mask);
|
|
|
|
write_pm_ier(gt);
|
2016-10-12 23:24:30 +07:00
|
|
|
/* though a barrier is missing here, but don't really need a one */
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:00:50 +07:00
|
|
|
void gen11_reset_rps_interrupts(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
while (gen11_reset_one_iir(&dev_priv->gt, 0, GEN11_GTPM))
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
;
|
2018-04-05 21:00:50 +07:00
|
|
|
|
|
|
|
dev_priv->gt_pm.rps.pm_iir = 0;
|
|
|
|
|
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
}
|
|
|
|
|
2016-10-12 23:24:30 +07:00
|
|
|
void gen6_reset_rps_interrupts(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2018-08-02 17:06:30 +07:00
|
|
|
gen6_reset_pm_iir(dev_priv, GEN6_PM_RPS_EVENTS);
|
2017-10-11 04:30:06 +07:00
|
|
|
dev_priv->gt_pm.rps.pm_iir = 0;
|
2014-11-19 20:30:03 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
void gen6_enable_rps_interrupts(struct drm_i915_private *dev_priv)
|
2014-11-06 01:48:48 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
struct intel_gt *gt = &dev_priv->gt;
|
2017-10-11 04:30:06 +07:00
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
|
|
|
|
|
|
|
if (READ_ONCE(rps->interrupts_enabled))
|
2016-09-21 20:51:06 +07:00
|
|
|
return;
|
|
|
|
|
2014-11-06 01:48:48 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-10-11 04:30:06 +07:00
|
|
|
WARN_ON_ONCE(rps->pm_iir);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
2018-04-05 21:00:50 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
2019-07-04 19:17:56 +07:00
|
|
|
WARN_ON_ONCE(gen11_reset_one_iir(gt, 0, GEN11_GTPM));
|
2018-04-05 21:00:50 +07:00
|
|
|
else
|
|
|
|
WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->interrupts_enabled = true;
|
2019-07-04 19:17:56 +07:00
|
|
|
gen6_enable_pm_irq(gt, dev_priv->pm_rps_events);
|
2014-12-15 23:59:27 +07:00
|
|
|
|
2014-11-06 01:48:48 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv)
|
2014-11-06 01:48:48 +07:00
|
|
|
{
|
2017-10-11 04:30:06 +07:00
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
|
|
|
|
|
|
|
if (!READ_ONCE(rps->interrupts_enabled))
|
2016-09-21 20:51:06 +07:00
|
|
|
return;
|
|
|
|
|
drm/i915: sanitize rps irq disabling
When disabling the RPS interrupts there is a tricky dependency between
the thread disabling the interrupts, the RPS interrupt handler and the
corresponding RPS work. The RPS work can reenable the interrupts, so
there is no straightforward order in the disabling thread to (1) make
sure that any RPS work is flushed and to (2) disable all RPS
interrupts. Currently this is solved by masking the interrupts using two
separate mask registers (first level display IMR and PM IMR) and doing
the disabling when all first level interrupts are disabled.
This works, but the requirement to run with all first level interrupts
disabled is unnecessary making the suspend / unload time ordering of RPS
disabling wrt. other unitialization steps difficult and error prone.
Removing this restriction allows us to disable RPS early during suspend
/ unload and forget about it for the rest of the sequence. By adding a
more explicit method for avoiding the above race, it also becomes easier
to prove its correctness. Finally currently we can hit the WARN in
snb_update_pm_irq(), when a final RPS work runs with the first level
interrupts already disabled. This won't lead to any problem (due to the
separate interrupt masks), but with the change in this and the next
patch we can get rid of the WARN, while leaving it in place for other
scenarios.
To address the above points, add a new RPS interrupts_enabled flag and
use this during RPS disabling to avoid requeuing the RPS work and
reenabling of the RPS interrupts. Since the interrupt disabling happens
now in intel_suspend_gt_powersave(), we will disable RPS interrupts
explicitly during suspend (and not just through the first level mask),
but there is no problem doing so, it's also more consistent and allows
us to unify more of the RPS disabling during suspend and unload time in
the next patch.
v2/v3:
- rebase on patch "drm/i915: move rps irq disable one level up" in the
patchset
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2014-11-19 20:30:04 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->interrupts_enabled = false;
|
2014-11-21 04:01:47 +07:00
|
|
|
|
2016-09-13 03:19:35 +07:00
|
|
|
I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0u));
|
2014-11-21 04:01:47 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
gen6_disable_pm_irq(&dev_priv->gt, GEN6_PM_RPS_EVENTS);
|
2015-03-24 00:11:34 +07:00
|
|
|
|
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2019-07-02 22:17:23 +07:00
|
|
|
intel_synchronize_irq(dev_priv);
|
2016-07-04 14:08:36 +07:00
|
|
|
|
|
|
|
/* Now that we will not be generating any more work, flush any
|
2017-08-24 06:58:24 +07:00
|
|
|
* outstanding tasks. As we are called on the RPS idle path,
|
2016-07-04 14:08:36 +07:00
|
|
|
* we will reset the GPU to minimum frequencies, so the current
|
|
|
|
* state of the worker can be discarded.
|
|
|
|
*/
|
2017-10-11 04:30:06 +07:00
|
|
|
cancel_work_sync(&rps->work);
|
2018-04-05 21:00:50 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
gen11_reset_rps_interrupts(dev_priv);
|
|
|
|
else
|
|
|
|
gen6_reset_rps_interrupts(dev_priv);
|
2014-11-06 01:48:48 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen9_reset_guc_interrupts(struct intel_guc *guc)
|
2016-10-12 23:24:31 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
assert_rpm_wakelock_held(&i915->runtime_pm);
|
2018-01-24 22:46:56 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_lock_irq(&i915->irq_lock);
|
|
|
|
gen6_reset_pm_iir(i915, gt->pm_guc_events);
|
|
|
|
spin_unlock_irq(&i915->irq_lock);
|
2016-10-12 23:24:31 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen9_enable_guc_interrupts(struct intel_guc *guc)
|
2016-10-12 23:24:31 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
assert_rpm_wakelock_held(&i915->runtime_pm);
|
2018-01-24 22:46:56 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_lock_irq(&i915->irq_lock);
|
2019-07-13 17:00:09 +07:00
|
|
|
if (!guc->interrupts.enabled) {
|
2019-07-13 17:00:15 +07:00
|
|
|
WARN_ON_ONCE(intel_uncore_read(gt->uncore, gen6_pm_iir(i915)) &
|
|
|
|
gt->pm_guc_events);
|
2019-07-13 17:00:09 +07:00
|
|
|
guc->interrupts.enabled = true;
|
2019-07-13 17:00:15 +07:00
|
|
|
gen6_enable_pm_irq(gt, gt->pm_guc_events);
|
2016-10-12 23:24:31 +07:00
|
|
|
}
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_unlock_irq(&i915->irq_lock);
|
2016-10-12 23:24:31 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen9_disable_guc_interrupts(struct intel_guc *guc)
|
2016-10-12 23:24:31 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
assert_rpm_wakelock_held(&i915->runtime_pm);
|
2018-01-24 22:46:56 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_lock_irq(&i915->irq_lock);
|
2019-07-13 17:00:09 +07:00
|
|
|
guc->interrupts.enabled = false;
|
2016-10-12 23:24:31 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
gen6_disable_pm_irq(gt, gt->pm_guc_events);
|
2016-10-12 23:24:31 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_unlock_irq(&i915->irq_lock);
|
|
|
|
intel_synchronize_irq(i915);
|
2016-10-12 23:24:31 +07:00
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
gen9_reset_guc_interrupts(guc);
|
2016-10-12 23:24:31 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen11_reset_guc_interrupts(struct intel_guc *guc)
|
2019-05-28 01:36:08 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-05-28 01:36:08 +07:00
|
|
|
spin_lock_irq(&i915->irq_lock);
|
2019-07-13 17:00:15 +07:00
|
|
|
gen11_reset_one_iir(gt, 0, GEN11_GUC);
|
2019-05-28 01:36:08 +07:00
|
|
|
spin_unlock_irq(&i915->irq_lock);
|
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen11_enable_guc_interrupts(struct intel_guc *guc)
|
2019-05-28 01:36:08 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_lock_irq(>->i915->irq_lock);
|
2019-07-13 17:00:09 +07:00
|
|
|
if (!guc->interrupts.enabled) {
|
2019-07-13 17:00:10 +07:00
|
|
|
u32 events = REG_FIELD_PREP(ENGINE1_MASK, GUC_INTR_GUC2HOST);
|
2019-05-28 01:36:08 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
WARN_ON_ONCE(gen11_reset_one_iir(gt, 0, GEN11_GUC));
|
|
|
|
intel_uncore_write(gt->uncore, GEN11_GUC_SG_INTR_ENABLE, events);
|
|
|
|
intel_uncore_write(gt->uncore, GEN11_GUC_SG_INTR_MASK, ~events);
|
2019-07-13 17:00:09 +07:00
|
|
|
guc->interrupts.enabled = true;
|
2019-05-28 01:36:08 +07:00
|
|
|
}
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_unlock_irq(>->i915->irq_lock);
|
2019-05-28 01:36:08 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
void gen11_disable_guc_interrupts(struct intel_guc *guc)
|
2019-05-28 01:36:08 +07:00
|
|
|
{
|
2019-07-13 17:00:15 +07:00
|
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-07-13 17:00:09 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_lock_irq(&i915->irq_lock);
|
2019-07-13 17:00:09 +07:00
|
|
|
guc->interrupts.enabled = false;
|
2019-05-28 01:36:08 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
intel_uncore_write(gt->uncore, GEN11_GUC_SG_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(gt->uncore, GEN11_GUC_SG_INTR_ENABLE, 0);
|
2019-05-28 01:36:08 +07:00
|
|
|
|
2019-07-13 17:00:15 +07:00
|
|
|
spin_unlock_irq(&i915->irq_lock);
|
|
|
|
intel_synchronize_irq(i915);
|
2019-05-28 01:36:08 +07:00
|
|
|
|
2019-07-13 17:00:09 +07:00
|
|
|
gen11_reset_guc_interrupts(guc);
|
2019-05-28 01:36:08 +07:00
|
|
|
}
|
|
|
|
|
2015-08-28 03:56:06 +07:00
|
|
|
/**
|
2015-11-25 21:21:30 +07:00
|
|
|
* bdw_update_port_irq - update DE port interrupt
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
2015-08-28 03:56:06 +07:00
|
|
|
static void bdw_update_port_irq(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2015-08-28 03:56:06 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 new_val;
|
|
|
|
u32 old_val;
|
2015-08-28 03:56:06 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2015-08-28 03:56:06 +07:00
|
|
|
|
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
|
|
|
if (WARN_ON(!intel_irqs_enabled(dev_priv)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
old_val = I915_READ(GEN8_DE_PORT_IMR);
|
|
|
|
|
|
|
|
new_val = old_val;
|
|
|
|
new_val &= ~interrupt_mask;
|
|
|
|
new_val |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
|
|
|
|
if (new_val != old_val) {
|
|
|
|
I915_WRITE(GEN8_DE_PORT_IMR, new_val);
|
|
|
|
POSTING_READ(GEN8_DE_PORT_IMR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-23 23:06:17 +07:00
|
|
|
/**
|
|
|
|
* bdw_update_pipe_irq - update DE pipe interrupt
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @pipe: pipe whose interrupt to update
|
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
|
|
|
void bdw_update_pipe_irq(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2015-11-23 23:06:17 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 new_val;
|
2015-11-23 23:06:17 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2015-11-23 23:06:17 +07:00
|
|
|
|
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
|
|
|
if (WARN_ON(!intel_irqs_enabled(dev_priv)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_val = dev_priv->de_irq_mask[pipe];
|
|
|
|
new_val &= ~interrupt_mask;
|
|
|
|
new_val |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
|
|
|
|
if (new_val != dev_priv->de_irq_mask[pipe]) {
|
|
|
|
dev_priv->de_irq_mask[pipe] = new_val;
|
|
|
|
I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
|
|
|
|
POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-05 04:35:21 +07:00
|
|
|
/**
|
|
|
|
* ibx_display_interrupt_update - update SDEIMR
|
|
|
|
* @dev_priv: driver private
|
|
|
|
* @interrupt_mask: mask of interrupt bits to update
|
|
|
|
* @enabled_irq_mask: mask of interrupt bits to enable
|
|
|
|
*/
|
2014-09-30 15:56:46 +07:00
|
|
|
void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 interrupt_mask,
|
|
|
|
u32 enabled_irq_mask)
|
2013-07-05 04:35:21 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 sdeimr = I915_READ(SDEIMR);
|
2013-07-05 04:35:21 +07:00
|
|
|
sdeimr &= ~interrupt_mask;
|
|
|
|
sdeimr |= (~enabled_irq_mask & interrupt_mask);
|
|
|
|
|
2014-12-08 22:30:00 +07:00
|
|
|
WARN_ON(enabled_irq_mask & ~interrupt_mask);
|
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2013-07-05 04:35:21 +07:00
|
|
|
|
2014-06-20 23:29:20 +07:00
|
|
|
if (WARN_ON(!intel_irqs_enabled(dev_priv)))
|
2013-08-19 23:18:09 +07:00
|
|
|
return;
|
|
|
|
|
2013-07-05 04:35:21 +07:00
|
|
|
I915_WRITE(SDEIMR, sdeimr);
|
|
|
|
POSTING_READ(SDEIMR);
|
|
|
|
}
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
u32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe)
|
2008-11-04 17:03:27 +07:00
|
|
|
{
|
2017-09-14 22:17:31 +07:00
|
|
|
u32 status_mask = dev_priv->pipestat_irq_mask[pipe];
|
|
|
|
u32 enable_mask = status_mask << 16;
|
2008-11-04 17:03:27 +07:00
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2008-11-04 17:03:27 +07:00
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
if (INTEL_GEN(dev_priv) < 5)
|
|
|
|
goto out;
|
2014-02-10 23:42:48 +07:00
|
|
|
|
|
|
|
/*
|
2014-04-09 17:28:48 +07:00
|
|
|
* On pipe A we don't support the PSR interrupt yet,
|
|
|
|
* on pipe B and C the same bit MBZ.
|
2014-02-10 23:42:48 +07:00
|
|
|
*/
|
|
|
|
if (WARN_ON_ONCE(status_mask & PIPE_A_PSR_STATUS_VLV))
|
|
|
|
return 0;
|
2014-04-09 17:28:48 +07:00
|
|
|
/*
|
|
|
|
* On pipe B and C we don't support the PSR interrupt yet, on pipe
|
|
|
|
* A the same bit is for perf counters which we don't use either.
|
|
|
|
*/
|
|
|
|
if (WARN_ON_ONCE(status_mask & PIPE_B_PSR_STATUS_VLV))
|
|
|
|
return 0;
|
2014-02-10 23:42:48 +07:00
|
|
|
|
|
|
|
enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS |
|
|
|
|
SPRITE0_FLIP_DONE_INT_EN_VLV |
|
|
|
|
SPRITE1_FLIP_DONE_INT_EN_VLV);
|
|
|
|
if (status_mask & SPRITE0_FLIP_DONE_INT_STATUS_VLV)
|
|
|
|
enable_mask |= SPRITE0_FLIP_DONE_INT_EN_VLV;
|
|
|
|
if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV)
|
|
|
|
enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV;
|
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
out:
|
|
|
|
WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
|
|
|
|
status_mask & ~PIPESTAT_INT_STATUS_MASK,
|
|
|
|
"pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
|
|
|
|
pipe_name(pipe), enable_mask, status_mask);
|
|
|
|
|
2014-02-10 23:42:48 +07:00
|
|
|
return enable_mask;
|
|
|
|
}
|
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
void i915_enable_pipestat(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe, u32 status_mask)
|
2014-02-10 23:42:47 +07:00
|
|
|
{
|
2017-09-14 22:17:31 +07:00
|
|
|
i915_reg_t reg = PIPESTAT(pipe);
|
2014-02-10 23:42:47 +07:00
|
|
|
u32 enable_mask;
|
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK,
|
|
|
|
"pipe %c: status_mask=0x%x\n",
|
|
|
|
pipe_name(pipe), status_mask);
|
|
|
|
|
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
|
|
|
WARN_ON(!intel_irqs_enabled(dev_priv));
|
|
|
|
|
|
|
|
if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == status_mask)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->pipestat_irq_mask[pipe] |= status_mask;
|
|
|
|
enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
|
|
|
|
|
|
|
|
I915_WRITE(reg, enable_mask | status_mask);
|
|
|
|
POSTING_READ(reg);
|
2014-02-10 23:42:47 +07:00
|
|
|
}
|
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
void i915_disable_pipestat(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe, u32 status_mask)
|
2014-02-10 23:42:47 +07:00
|
|
|
{
|
2017-09-14 22:17:31 +07:00
|
|
|
i915_reg_t reg = PIPESTAT(pipe);
|
2014-02-10 23:42:47 +07:00
|
|
|
u32 enable_mask;
|
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK,
|
|
|
|
"pipe %c: status_mask=0x%x\n",
|
|
|
|
pipe_name(pipe), status_mask);
|
|
|
|
|
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
|
|
|
WARN_ON(!intel_irqs_enabled(dev_priv));
|
|
|
|
|
|
|
|
if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->pipestat_irq_mask[pipe] &= ~status_mask;
|
|
|
|
enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
|
|
|
|
|
|
|
|
I915_WRITE(reg, enable_mask | status_mask);
|
|
|
|
POSTING_READ(reg);
|
2014-02-10 23:42:47 +07:00
|
|
|
}
|
|
|
|
|
2019-03-18 23:56:31 +07:00
|
|
|
static bool i915_has_asle(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (!dev_priv->opregion.asle)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return IS_PINEVIEW(dev_priv) || IS_MOBILE(dev_priv);
|
|
|
|
}
|
|
|
|
|
2009-10-28 12:10:00 +07:00
|
|
|
/**
|
2013-04-29 17:02:54 +07:00
|
|
|
* i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
|
2016-06-03 20:02:17 +07:00
|
|
|
* @dev_priv: i915 device private
|
2009-10-28 12:10:00 +07:00
|
|
|
*/
|
2016-05-06 20:48:28 +07:00
|
|
|
static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv)
|
2009-10-28 12:10:00 +07:00
|
|
|
{
|
2019-03-18 23:56:31 +07:00
|
|
|
if (!i915_has_asle(dev_priv))
|
2013-04-29 17:02:54 +07:00
|
|
|
return;
|
|
|
|
|
2014-09-15 19:55:29 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2009-10-28 12:10:00 +07:00
|
|
|
|
2014-02-10 23:42:47 +07:00
|
|
|
i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_STATUS);
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 4)
|
2013-10-21 23:04:35 +07:00
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A,
|
2014-02-10 23:42:47 +07:00
|
|
|
PIPE_LEGACY_BLC_EVENT_STATUS);
|
2010-12-04 18:30:53 +07:00
|
|
|
|
2014-09-15 19:55:29 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2009-10-28 12:10:00 +07:00
|
|
|
}
|
|
|
|
|
2014-05-16 00:20:36 +07:00
|
|
|
/*
|
|
|
|
* This timing diagram depicts the video signal in and
|
|
|
|
* around the vertical blanking period.
|
|
|
|
*
|
|
|
|
* Assumptions about the fictitious mode used in this example:
|
|
|
|
* vblank_start >= 3
|
|
|
|
* vsync_start = vblank_start + 1
|
|
|
|
* vsync_end = vblank_start + 2
|
|
|
|
* vtotal = vblank_start + 3
|
|
|
|
*
|
|
|
|
* start of vblank:
|
|
|
|
* latch double buffered registers
|
|
|
|
* increment frame counter (ctg+)
|
|
|
|
* generate start of vblank interrupt (gen4+)
|
|
|
|
* |
|
|
|
|
* | frame start:
|
|
|
|
* | generate frame start interrupt (aka. vblank interrupt) (gmch)
|
|
|
|
* | may be shifted forward 1-3 extra lines via PIPECONF
|
|
|
|
* | |
|
|
|
|
* | | start of vsync:
|
|
|
|
* | | generate vsync interrupt
|
|
|
|
* | | |
|
|
|
|
* ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx
|
|
|
|
* . \hs/ . \hs/ \hs/ \hs/ . \hs/
|
|
|
|
* ----va---> <-----------------vb--------------------> <--------va-------------
|
|
|
|
* | | <----vs-----> |
|
|
|
|
* -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2)
|
|
|
|
* -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+)
|
|
|
|
* -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi)
|
|
|
|
* | | |
|
|
|
|
* last visible pixel first visible pixel
|
|
|
|
* | increment frame counter (gen3/4)
|
|
|
|
* pixel counter = vblank_start * htotal pixel counter = 0 (gen3/4)
|
|
|
|
*
|
|
|
|
* x = horizontal active
|
|
|
|
* _ = horizontal blanking
|
|
|
|
* hs = horizontal sync
|
|
|
|
* va = vertical active
|
|
|
|
* vb = vertical blanking
|
|
|
|
* vs = vertical sync
|
|
|
|
* vbs = vblank_start (number)
|
|
|
|
*
|
|
|
|
* Summary:
|
|
|
|
* - most events happen at the start of horizontal sync
|
|
|
|
* - frame start happens at the start of horizontal blank, 1-4 lines
|
|
|
|
* (depending on PIPECONF settings) after the start of vblank
|
|
|
|
* - gen3/4 pixel and frame counter are synchronized with the start
|
|
|
|
* of horizontal active on the first line of vertical active
|
|
|
|
*/
|
|
|
|
|
2008-10-19 09:39:29 +07:00
|
|
|
/* Called from drm generic code, passed a 'crtc', which
|
|
|
|
* we use as a pipe index
|
|
|
|
*/
|
2019-06-20 00:08:40 +07:00
|
|
|
u32 i915_get_vblank_counter(struct drm_crtc *crtc)
|
2008-10-01 02:14:26 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
struct drm_vblank_crtc *vblank = &dev_priv->drm.vblank[drm_crtc_index(crtc)];
|
2018-11-28 03:05:50 +07:00
|
|
|
const struct drm_display_mode *mode = &vblank->hwmode;
|
2019-06-20 00:08:40 +07:00
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
i915_reg_t high_frame, low_frame;
|
2014-04-29 17:35:50 +07:00
|
|
|
u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
|
2017-03-09 22:44:30 +07:00
|
|
|
unsigned long irqflags;
|
2008-10-01 02:14:26 +07:00
|
|
|
|
2018-11-28 03:05:50 +07:00
|
|
|
/*
|
|
|
|
* On i965gm TV output the frame counter only works up to
|
|
|
|
* the point when we enable the TV encoder. After that the
|
|
|
|
* frame counter ceases to work and reads zero. We need a
|
|
|
|
* vblank wait before enabling the TV encoder and so we
|
|
|
|
* have to enable vblank interrupts while the frame counter
|
|
|
|
* is still in a working state. However the core vblank code
|
|
|
|
* does not like us returning non-zero frame counter values
|
|
|
|
* when we've told it that we don't have a working frame
|
|
|
|
* counter. Thus we must stop non-zero values leaking out.
|
|
|
|
*/
|
|
|
|
if (!vblank->max_vblank_count)
|
|
|
|
return 0;
|
|
|
|
|
2015-02-14 03:03:44 +07:00
|
|
|
htotal = mode->crtc_htotal;
|
|
|
|
hsync_start = mode->crtc_hsync_start;
|
|
|
|
vbl_start = mode->crtc_vblank_start;
|
|
|
|
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
|
|
vbl_start = DIV_ROUND_UP(vbl_start, 2);
|
2013-09-25 23:55:26 +07:00
|
|
|
|
2014-04-29 17:35:50 +07:00
|
|
|
/* Convert to pixel count */
|
|
|
|
vbl_start *= htotal;
|
|
|
|
|
|
|
|
/* Start of vblank event occurs at start of hsync */
|
|
|
|
vbl_start -= htotal - hsync_start;
|
|
|
|
|
2011-02-08 03:26:52 +07:00
|
|
|
high_frame = PIPEFRAME(pipe);
|
|
|
|
low_frame = PIPEFRAMEPIXEL(pipe);
|
2010-09-11 19:48:45 +07:00
|
|
|
|
2017-03-09 22:44:30 +07:00
|
|
|
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
|
|
|
|
2008-10-01 02:14:26 +07:00
|
|
|
/*
|
|
|
|
* High & low register fields aren't synchronized, so make sure
|
|
|
|
* we get a low value that's stable across two reads of the high
|
|
|
|
* register.
|
|
|
|
*/
|
|
|
|
do {
|
2017-03-09 22:44:30 +07:00
|
|
|
high1 = I915_READ_FW(high_frame) & PIPE_FRAME_HIGH_MASK;
|
|
|
|
low = I915_READ_FW(low_frame);
|
|
|
|
high2 = I915_READ_FW(high_frame) & PIPE_FRAME_HIGH_MASK;
|
2008-10-01 02:14:26 +07:00
|
|
|
} while (high1 != high2);
|
|
|
|
|
2017-03-09 22:44:30 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
|
|
|
|
2010-09-11 19:48:45 +07:00
|
|
|
high1 >>= PIPE_FRAME_HIGH_SHIFT;
|
2013-09-25 23:55:26 +07:00
|
|
|
pixel = low & PIPE_PIXEL_MASK;
|
2010-09-11 19:48:45 +07:00
|
|
|
low >>= PIPE_FRAME_LOW_SHIFT;
|
2013-09-25 23:55:26 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The frame counter increments at beginning of active.
|
|
|
|
* Cook up a vblank counter by also checking the pixel
|
|
|
|
* counter against vblank start.
|
|
|
|
*/
|
2013-11-06 22:56:27 +07:00
|
|
|
return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff;
|
2008-10-01 02:14:26 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
u32 g4x_get_vblank_counter(struct drm_crtc *crtc)
|
2009-02-07 01:22:41 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2009-02-07 01:22:41 +07:00
|
|
|
|
2015-09-22 23:50:01 +07:00
|
|
|
return I915_READ(PIPE_FRMCOUNT_G4X(pipe));
|
2009-02-07 01:22:41 +07:00
|
|
|
}
|
|
|
|
|
2017-09-25 20:56:01 +07:00
|
|
|
/*
|
|
|
|
* On certain encoders on certain platforms, pipe
|
|
|
|
* scanline register will not work to get the scanline,
|
|
|
|
* since the timings are driven from the PORT or issues
|
|
|
|
* with scanline register updates.
|
|
|
|
* This function will use Framestamp and current
|
|
|
|
* timestamp registers to calculate the scanline.
|
|
|
|
*/
|
|
|
|
static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
|
|
|
struct drm_vblank_crtc *vblank =
|
|
|
|
&crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
|
|
|
|
const struct drm_display_mode *mode = &vblank->hwmode;
|
|
|
|
u32 vblank_start = mode->crtc_vblank_start;
|
|
|
|
u32 vtotal = mode->crtc_vtotal;
|
|
|
|
u32 htotal = mode->crtc_htotal;
|
|
|
|
u32 clock = mode->crtc_clock;
|
|
|
|
u32 scanline, scan_prev_time, scan_curr_time, scan_post_time;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To avoid the race condition where we might cross into the
|
|
|
|
* next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR
|
|
|
|
* reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR
|
|
|
|
* during the same frame.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* This field provides read back of the display
|
|
|
|
* pipe frame time stamp. The time stamp value
|
|
|
|
* is sampled at every start of vertical blank.
|
|
|
|
*/
|
|
|
|
scan_prev_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The TIMESTAMP_CTR register has the current
|
|
|
|
* time stamp value.
|
|
|
|
*/
|
|
|
|
scan_curr_time = I915_READ_FW(IVB_TIMESTAMP_CTR);
|
|
|
|
|
|
|
|
scan_post_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe));
|
|
|
|
} while (scan_post_time != scan_prev_time);
|
|
|
|
|
|
|
|
scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time,
|
|
|
|
clock), 1000 * htotal);
|
|
|
|
scanline = min(scanline, vtotal - 1);
|
|
|
|
scanline = (scanline + vblank_start) % vtotal;
|
|
|
|
|
|
|
|
return scanline;
|
|
|
|
}
|
|
|
|
|
2015-10-22 19:34:56 +07:00
|
|
|
/* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */
|
2014-04-29 17:35:45 +07:00
|
|
|
static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = crtc->base.dev;
|
2016-07-04 17:34:36 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
2017-05-09 21:03:29 +07:00
|
|
|
const struct drm_display_mode *mode;
|
|
|
|
struct drm_vblank_crtc *vblank;
|
2014-04-29 17:35:45 +07:00
|
|
|
enum pipe pipe = crtc->pipe;
|
2014-05-16 00:23:23 +07:00
|
|
|
int position, vtotal;
|
2014-04-29 17:35:45 +07:00
|
|
|
|
2017-03-03 00:15:05 +07:00
|
|
|
if (!crtc->active)
|
|
|
|
return -1;
|
|
|
|
|
2017-05-09 21:03:29 +07:00
|
|
|
vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
|
|
|
|
mode = &vblank->hwmode;
|
|
|
|
|
2017-09-25 20:56:01 +07:00
|
|
|
if (mode->private_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP)
|
|
|
|
return __intel_get_crtc_scanline_from_timestamp(crtc);
|
|
|
|
|
2014-05-16 00:23:23 +07:00
|
|
|
vtotal = mode->crtc_vtotal;
|
2014-04-29 17:35:45 +07:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
|
|
|
vtotal /= 2;
|
|
|
|
|
drm/i915: replace IS_GEN<N> with IS_GEN(..., N)
Define IS_GEN() similarly to our IS_GEN_RANGE(). but use gen instead of
gen_mask to do the comparison. Now callers can pass then gen as a parameter,
so we don't require one macro for each gen.
The following spatch was used to convert the users of these macros:
@@
expression e;
@@
(
- IS_GEN2(e)
+ IS_GEN(e, 2)
|
- IS_GEN3(e)
+ IS_GEN(e, 3)
|
- IS_GEN4(e)
+ IS_GEN(e, 4)
|
- IS_GEN5(e)
+ IS_GEN(e, 5)
|
- IS_GEN6(e)
+ IS_GEN(e, 6)
|
- IS_GEN7(e)
+ IS_GEN(e, 7)
|
- IS_GEN8(e)
+ IS_GEN(e, 8)
|
- IS_GEN9(e)
+ IS_GEN(e, 9)
|
- IS_GEN10(e)
+ IS_GEN(e, 10)
|
- IS_GEN11(e)
+ IS_GEN(e, 11)
)
v2: use IS_GEN rather than GT_GEN and compare to info.gen rather than
using the bitmask
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181212181044.15886-2-lucas.demarchi@intel.com
2018-12-13 01:10:43 +07:00
|
|
|
if (IS_GEN(dev_priv, 2))
|
2015-10-22 19:34:56 +07:00
|
|
|
position = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
|
2014-04-29 17:35:45 +07:00
|
|
|
else
|
2015-10-22 19:34:56 +07:00
|
|
|
position = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
|
2014-04-29 17:35:45 +07:00
|
|
|
|
2015-09-23 02:15:54 +07:00
|
|
|
/*
|
|
|
|
* On HSW, the DSL reg (0x70000) appears to return 0 if we
|
|
|
|
* read it just before the start of vblank. So try it again
|
|
|
|
* so we don't accidentally end up spanning a vblank frame
|
|
|
|
* increment, causing the pipe_update_end() code to squak at us.
|
|
|
|
*
|
|
|
|
* The nature of this problem means we can't simply check the ISR
|
|
|
|
* bit and return the vblank start value; nor can we use the scanline
|
|
|
|
* debug register in the transcoder as it appears to have the same
|
|
|
|
* problem. We may need to extend this to include other platforms,
|
|
|
|
* but so far testing only shows the problem on HSW.
|
|
|
|
*/
|
2016-05-06 20:48:28 +07:00
|
|
|
if (HAS_DDI(dev_priv) && !position) {
|
2015-09-23 02:15:54 +07:00
|
|
|
int i, temp;
|
|
|
|
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
udelay(1);
|
2017-03-09 22:44:31 +07:00
|
|
|
temp = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
|
2015-09-23 02:15:54 +07:00
|
|
|
if (temp != position) {
|
|
|
|
position = temp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-29 17:35:45 +07:00
|
|
|
/*
|
2014-05-16 00:23:23 +07:00
|
|
|
* See update_scanline_offset() for the details on the
|
|
|
|
* scanline_offset adjustment.
|
2014-04-29 17:35:45 +07:00
|
|
|
*/
|
2014-05-16 00:23:23 +07:00
|
|
|
return (position + crtc->scanline_offset) % vtotal;
|
2014-04-29 17:35:45 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:42 +07:00
|
|
|
bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
|
|
|
bool in_vblank_irq, int *vpos, int *hpos,
|
|
|
|
ktime_t *stime, ktime_t *etime,
|
|
|
|
const struct drm_display_mode *mode)
|
2010-12-08 10:07:19 +07:00
|
|
|
{
|
2016-07-04 17:34:36 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
2016-11-01 03:37:10 +07:00
|
|
|
struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
|
|
|
|
pipe);
|
2013-10-11 23:10:32 +07:00
|
|
|
int position;
|
2014-04-29 17:35:44 +07:00
|
|
|
int vbl_start, vbl_end, hsync_start, htotal, vtotal;
|
2013-10-30 11:13:08 +07:00
|
|
|
unsigned long irqflags;
|
2019-01-26 01:19:31 +07:00
|
|
|
bool use_scanline_counter = INTEL_GEN(dev_priv) >= 5 ||
|
|
|
|
IS_G4X(dev_priv) || IS_GEN(dev_priv, 2) ||
|
|
|
|
mode->private_flags & I915_MODE_FLAG_USE_SCANLINE_COUNTER;
|
2010-12-08 10:07:19 +07:00
|
|
|
|
2015-06-01 17:50:07 +07:00
|
|
|
if (WARN_ON(!mode->crtc_clock)) {
|
2010-12-08 10:07:19 +07:00
|
|
|
DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
|
2011-02-08 03:26:52 +07:00
|
|
|
"pipe %c\n", pipe_name(pipe));
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 21:03:28 +07:00
|
|
|
return false;
|
2010-12-08 10:07:19 +07:00
|
|
|
}
|
|
|
|
|
2013-09-23 18:48:50 +07:00
|
|
|
htotal = mode->crtc_htotal;
|
2014-04-29 17:35:44 +07:00
|
|
|
hsync_start = mode->crtc_hsync_start;
|
2013-09-23 18:48:50 +07:00
|
|
|
vtotal = mode->crtc_vtotal;
|
|
|
|
vbl_start = mode->crtc_vblank_start;
|
|
|
|
vbl_end = mode->crtc_vblank_end;
|
2010-12-08 10:07:19 +07:00
|
|
|
|
2013-10-28 21:31:41 +07:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
|
|
|
vbl_start = DIV_ROUND_UP(vbl_start, 2);
|
|
|
|
vbl_end /= 2;
|
|
|
|
vtotal /= 2;
|
|
|
|
}
|
|
|
|
|
2013-10-30 11:13:08 +07:00
|
|
|
/*
|
|
|
|
* Lock uncore.lock, as we will do multiple timing critical raw
|
|
|
|
* register reads, potentially with preemption disabled, so the
|
|
|
|
* following code must not block on uncore.lock.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
2014-04-29 17:35:44 +07:00
|
|
|
|
2013-10-30 11:13:08 +07:00
|
|
|
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
|
|
|
|
|
|
|
|
/* Get optional system timestamp before query. */
|
|
|
|
if (stime)
|
|
|
|
*stime = ktime_get();
|
|
|
|
|
2019-01-26 01:19:31 +07:00
|
|
|
if (use_scanline_counter) {
|
2010-12-08 10:07:19 +07:00
|
|
|
/* No obvious pixelcount register. Only query vertical
|
|
|
|
* scanout position from Display scan line register.
|
|
|
|
*/
|
2014-04-29 17:35:45 +07:00
|
|
|
position = __intel_get_crtc_scanline(intel_crtc);
|
2010-12-08 10:07:19 +07:00
|
|
|
} else {
|
|
|
|
/* Have access to pixelcount since start of frame.
|
|
|
|
* We can split this into vertical and horizontal
|
|
|
|
* scanout position.
|
|
|
|
*/
|
2015-10-22 19:34:56 +07:00
|
|
|
position = (I915_READ_FW(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
|
2010-12-08 10:07:19 +07:00
|
|
|
|
2013-10-11 23:10:32 +07:00
|
|
|
/* convert to pixel counts */
|
|
|
|
vbl_start *= htotal;
|
|
|
|
vbl_end *= htotal;
|
|
|
|
vtotal *= htotal;
|
2014-04-29 17:35:44 +07:00
|
|
|
|
2014-04-29 17:35:49 +07:00
|
|
|
/*
|
|
|
|
* In interlaced modes, the pixel counter counts all pixels,
|
|
|
|
* so one field will have htotal more pixels. In order to avoid
|
|
|
|
* the reported position from jumping backwards when the pixel
|
|
|
|
* counter is beyond the length of the shorter field, just
|
|
|
|
* clamp the position the length of the shorter field. This
|
|
|
|
* matches how the scanline counter based position works since
|
|
|
|
* the scanline counter doesn't count the two half lines.
|
|
|
|
*/
|
|
|
|
if (position >= vtotal)
|
|
|
|
position = vtotal - 1;
|
|
|
|
|
2014-04-29 17:35:44 +07:00
|
|
|
/*
|
|
|
|
* Start of vblank interrupt is triggered at start of hsync,
|
|
|
|
* just prior to the first active line of vblank. However we
|
|
|
|
* consider lines to start at the leading edge of horizontal
|
|
|
|
* active. So, should we get here before we've crossed into
|
|
|
|
* the horizontal active of the first line in vblank, we would
|
|
|
|
* not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that,
|
|
|
|
* always add htotal-hsync_start to the current pixel position.
|
|
|
|
*/
|
|
|
|
position = (position + htotal - hsync_start) % vtotal;
|
2010-12-08 10:07:19 +07:00
|
|
|
}
|
|
|
|
|
2013-10-30 11:13:08 +07:00
|
|
|
/* Get optional system timestamp after query. */
|
|
|
|
if (etime)
|
|
|
|
*etime = ktime_get();
|
|
|
|
|
|
|
|
/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
|
|
|
|
2013-10-11 23:10:32 +07:00
|
|
|
/*
|
|
|
|
* While in vblank, position will be negative
|
|
|
|
* counting up towards 0 at vbl_end. And outside
|
|
|
|
* vblank, position will be positive counting
|
|
|
|
* up since vbl_end.
|
|
|
|
*/
|
|
|
|
if (position >= vbl_start)
|
|
|
|
position -= vbl_end;
|
|
|
|
else
|
|
|
|
position += vtotal - vbl_end;
|
2010-12-08 10:07:19 +07:00
|
|
|
|
2019-01-26 01:19:31 +07:00
|
|
|
if (use_scanline_counter) {
|
2013-10-11 23:10:32 +07:00
|
|
|
*vpos = position;
|
|
|
|
*hpos = 0;
|
|
|
|
} else {
|
|
|
|
*vpos = position / htotal;
|
|
|
|
*hpos = position - (*vpos * htotal);
|
|
|
|
}
|
2010-12-08 10:07:19 +07:00
|
|
|
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 21:03:28 +07:00
|
|
|
return true;
|
2010-12-08 10:07:19 +07:00
|
|
|
}
|
|
|
|
|
2014-04-29 17:35:45 +07:00
|
|
|
int intel_get_crtc_scanline(struct intel_crtc *crtc)
|
|
|
|
{
|
2016-07-04 17:34:36 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
2014-04-29 17:35:45 +07:00
|
|
|
unsigned long irqflags;
|
|
|
|
int position;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
|
|
|
position = __intel_get_crtc_scanline(crtc);
|
|
|
|
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
|
|
|
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
|
2010-01-30 02:27:07 +07:00
|
|
|
{
|
2019-06-11 17:45:48 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2010-02-03 01:30:47 +07:00
|
|
|
u32 busy_up, busy_down, max_avg, min_avg;
|
2012-08-09 21:46:01 +07:00
|
|
|
u8 new_delay;
|
|
|
|
|
2013-07-05 04:35:25 +07:00
|
|
|
spin_lock(&mchdev_lock);
|
2010-01-30 02:27:07 +07:00
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
intel_uncore_write16(uncore,
|
|
|
|
MEMINTRSTS,
|
|
|
|
intel_uncore_read(uncore, MEMINTRSTS));
|
2012-08-09 04:35:37 +07:00
|
|
|
|
2012-08-09 04:35:39 +07:00
|
|
|
new_delay = dev_priv->ips.cur_delay;
|
2012-08-09 21:46:01 +07:00
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
intel_uncore_write16(uncore, MEMINTRSTS, MEMINT_EVAL_CHG);
|
|
|
|
busy_up = intel_uncore_read(uncore, RCPREVBSYTUPAVG);
|
|
|
|
busy_down = intel_uncore_read(uncore, RCPREVBSYTDNAVG);
|
|
|
|
max_avg = intel_uncore_read(uncore, RCBMAXAVG);
|
|
|
|
min_avg = intel_uncore_read(uncore, RCBMINAVG);
|
2010-01-30 02:27:07 +07:00
|
|
|
|
|
|
|
/* Handle RCS change request from hw */
|
2010-02-03 01:30:47 +07:00
|
|
|
if (busy_up > max_avg) {
|
2012-08-09 04:35:39 +07:00
|
|
|
if (dev_priv->ips.cur_delay != dev_priv->ips.max_delay)
|
|
|
|
new_delay = dev_priv->ips.cur_delay - 1;
|
|
|
|
if (new_delay < dev_priv->ips.max_delay)
|
|
|
|
new_delay = dev_priv->ips.max_delay;
|
2010-02-03 01:30:47 +07:00
|
|
|
} else if (busy_down < min_avg) {
|
2012-08-09 04:35:39 +07:00
|
|
|
if (dev_priv->ips.cur_delay != dev_priv->ips.min_delay)
|
|
|
|
new_delay = dev_priv->ips.cur_delay + 1;
|
|
|
|
if (new_delay > dev_priv->ips.min_delay)
|
|
|
|
new_delay = dev_priv->ips.min_delay;
|
2010-01-30 02:27:07 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (ironlake_set_drps(dev_priv, new_delay))
|
2012-08-09 04:35:39 +07:00
|
|
|
dev_priv->ips.cur_delay = new_delay;
|
2010-01-30 02:27:07 +07:00
|
|
|
|
2013-07-05 04:35:25 +07:00
|
|
|
spin_unlock(&mchdev_lock);
|
2012-08-09 21:46:01 +07:00
|
|
|
|
2010-01-30 02:27:07 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-18 16:48:22 +07:00
|
|
|
static void vlv_c0_read(struct drm_i915_private *dev_priv,
|
|
|
|
struct intel_rps_ei *ei)
|
2014-07-04 04:33:01 +07:00
|
|
|
{
|
2017-03-15 22:43:03 +07:00
|
|
|
ei->ktime = ktime_get_raw();
|
2015-03-18 16:48:22 +07:00
|
|
|
ei->render_c0 = I915_READ(VLV_RENDER_C0_COUNT);
|
|
|
|
ei->media_c0 = I915_READ(VLV_MEDIA_C0_COUNT);
|
|
|
|
}
|
2014-07-04 04:33:01 +07:00
|
|
|
|
2015-03-18 16:48:22 +07:00
|
|
|
void gen6_rps_reset_ei(struct drm_i915_private *dev_priv)
|
2014-07-04 04:33:01 +07:00
|
|
|
{
|
2017-10-11 04:30:06 +07:00
|
|
|
memset(&dev_priv->gt_pm.rps.ei, 0, sizeof(dev_priv->gt_pm.rps.ei));
|
2015-03-18 16:48:22 +07:00
|
|
|
}
|
2014-07-04 04:33:01 +07:00
|
|
|
|
2015-03-18 16:48:22 +07:00
|
|
|
static u32 vlv_wa_c0_ei(struct drm_i915_private *dev_priv, u32 pm_iir)
|
|
|
|
{
|
2017-10-11 04:30:06 +07:00
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
|
|
|
const struct intel_rps_ei *prev = &rps->ei;
|
2015-03-18 16:48:22 +07:00
|
|
|
struct intel_rps_ei now;
|
|
|
|
u32 events = 0;
|
2014-07-04 04:33:01 +07:00
|
|
|
|
2017-03-10 04:12:30 +07:00
|
|
|
if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0)
|
2015-03-18 16:48:22 +07:00
|
|
|
return 0;
|
2014-07-04 04:33:01 +07:00
|
|
|
|
2015-03-18 16:48:22 +07:00
|
|
|
vlv_c0_read(dev_priv, &now);
|
2014-07-04 04:33:01 +07:00
|
|
|
|
2017-03-15 22:43:03 +07:00
|
|
|
if (prev->ktime) {
|
2017-03-10 04:12:30 +07:00
|
|
|
u64 time, c0;
|
2017-03-10 04:12:31 +07:00
|
|
|
u32 render, media;
|
2017-03-10 04:12:30 +07:00
|
|
|
|
2017-03-15 22:43:03 +07:00
|
|
|
time = ktime_us_delta(now.ktime, prev->ktime);
|
2017-03-14 00:06:17 +07:00
|
|
|
|
2017-03-10 04:12:30 +07:00
|
|
|
time *= dev_priv->czclk_freq;
|
|
|
|
|
|
|
|
/* Workload can be split between render + media,
|
|
|
|
* e.g. SwapBuffers being blitted in X after being rendered in
|
|
|
|
* mesa. To account for this we need to combine both engines
|
|
|
|
* into our activity counter.
|
|
|
|
*/
|
2017-03-10 04:12:31 +07:00
|
|
|
render = now.render_c0 - prev->render_c0;
|
|
|
|
media = now.media_c0 - prev->media_c0;
|
|
|
|
c0 = max(render, media);
|
2017-03-15 23:12:59 +07:00
|
|
|
c0 *= 1000 * 100 << 8; /* to usecs and scale to threshold% */
|
2017-03-10 04:12:30 +07:00
|
|
|
|
drm/i915: Interactive RPS mode
RPS provides a feedback loop where we use the load during the previous
evaluation interval to decide whether to up or down clock the GPU
frequency. Our responsiveness is split into 3 regimes, a high and low
plateau with the intent to keep the gpu clocked high to cover occasional
stalls under high load, and low despite occasional glitches under steady
low load, and inbetween. However, we run into situations like kodi where
we want to stay at low power (video decoding is done efficiently
inside the fixed function HW and doesn't need high clocks even for high
bitrate streams), but just occasionally the pipeline is more complex
than a video decode and we need a smidgen of extra GPU power to present
on time. In the high power regime, we sample at sub frame intervals with
a bias to upclocking, and conversely at low power we sample over a few
frames worth to provide what we consider to be the right levels of
responsiveness respectively. At low power, we more or less expect to be
kicked out to high power at the start of a busy sequence by waitboosting.
Prior to commit e9af4ea2b9e7 ("drm/i915: Avoid waitboosting on the active
request") whenever we missed the frame or stalled, we would immediate go
full throttle and upclock the GPU to max. But in commit e9af4ea2b9e7, we
relaxed the waitboosting to only apply if the pipeline was deep to avoid
over-committing resources for a near miss. Sadly though, a near miss is
still a miss, and perceptible as jitter in the frame delivery.
To try and prevent the near miss before having to resort to boosting
after the fact, we use the pageflip queue as an indication that we are
in an "interactive" regime and so should sample the load more frequently
to provide power before the frame misses it vblank. This will make us
more favorable to providing a small power increase (one or two bins) as
required rather than going all the way to maximum and then having to
work back down again. (We still keep the waitboosting mechanism around
just in case a dramatic change in system load requires urgent uplocking,
faster than we can provide in a few evaluation intervals.)
v2: Reduce rps_set_interactive to a boolean parameter to avoid the
confusion of what if they wanted a new power mode after pinning to a
different mode (which to choose?)
v3: Only reprogram RPS while the GT is awake, it will be set when we
wake the GT, and while off warns about being used outside of rpm.
v4: Fix deferred application of interactive mode
v5: s/state/interactive/
v6: Group the mutex with its principle in a substruct
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=107111
Fixes: e9af4ea2b9e7 ("drm/i915: Avoid waitboosting on the active request")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Radoslaw Szwichtenberg <radoslaw.szwichtenberg@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180731132629.3381-1-chris@chris-wilson.co.uk
2018-07-31 20:26:29 +07:00
|
|
|
if (c0 > time * rps->power.up_threshold)
|
2017-03-10 04:12:30 +07:00
|
|
|
events = GEN6_PM_RP_UP_THRESHOLD;
|
drm/i915: Interactive RPS mode
RPS provides a feedback loop where we use the load during the previous
evaluation interval to decide whether to up or down clock the GPU
frequency. Our responsiveness is split into 3 regimes, a high and low
plateau with the intent to keep the gpu clocked high to cover occasional
stalls under high load, and low despite occasional glitches under steady
low load, and inbetween. However, we run into situations like kodi where
we want to stay at low power (video decoding is done efficiently
inside the fixed function HW and doesn't need high clocks even for high
bitrate streams), but just occasionally the pipeline is more complex
than a video decode and we need a smidgen of extra GPU power to present
on time. In the high power regime, we sample at sub frame intervals with
a bias to upclocking, and conversely at low power we sample over a few
frames worth to provide what we consider to be the right levels of
responsiveness respectively. At low power, we more or less expect to be
kicked out to high power at the start of a busy sequence by waitboosting.
Prior to commit e9af4ea2b9e7 ("drm/i915: Avoid waitboosting on the active
request") whenever we missed the frame or stalled, we would immediate go
full throttle and upclock the GPU to max. But in commit e9af4ea2b9e7, we
relaxed the waitboosting to only apply if the pipeline was deep to avoid
over-committing resources for a near miss. Sadly though, a near miss is
still a miss, and perceptible as jitter in the frame delivery.
To try and prevent the near miss before having to resort to boosting
after the fact, we use the pageflip queue as an indication that we are
in an "interactive" regime and so should sample the load more frequently
to provide power before the frame misses it vblank. This will make us
more favorable to providing a small power increase (one or two bins) as
required rather than going all the way to maximum and then having to
work back down again. (We still keep the waitboosting mechanism around
just in case a dramatic change in system load requires urgent uplocking,
faster than we can provide in a few evaluation intervals.)
v2: Reduce rps_set_interactive to a boolean parameter to avoid the
confusion of what if they wanted a new power mode after pinning to a
different mode (which to choose?)
v3: Only reprogram RPS while the GT is awake, it will be set when we
wake the GT, and while off warns about being used outside of rpm.
v4: Fix deferred application of interactive mode
v5: s/state/interactive/
v6: Group the mutex with its principle in a substruct
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=107111
Fixes: e9af4ea2b9e7 ("drm/i915: Avoid waitboosting on the active request")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Radoslaw Szwichtenberg <radoslaw.szwichtenberg@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180731132629.3381-1-chris@chris-wilson.co.uk
2018-07-31 20:26:29 +07:00
|
|
|
else if (c0 < time * rps->power.down_threshold)
|
2017-03-10 04:12:30 +07:00
|
|
|
events = GEN6_PM_RP_DOWN_THRESHOLD;
|
2014-07-04 04:33:01 +07:00
|
|
|
}
|
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->ei = now;
|
2015-03-18 16:48:22 +07:00
|
|
|
return events;
|
2014-07-04 04:33:01 +07:00
|
|
|
}
|
|
|
|
|
2011-04-26 01:25:20 +07:00
|
|
|
static void gen6_pm_rps_work(struct work_struct *work)
|
2010-12-18 05:19:02 +07:00
|
|
|
{
|
2014-03-31 18:27:17 +07:00
|
|
|
struct drm_i915_private *dev_priv =
|
2017-10-11 04:30:06 +07:00
|
|
|
container_of(work, struct drm_i915_private, gt_pm.rps.work);
|
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
2017-03-10 04:12:32 +07:00
|
|
|
bool client_boost = false;
|
2015-05-22 03:01:47 +07:00
|
|
|
int new_delay, adj, min, max;
|
2017-03-10 04:12:32 +07:00
|
|
|
u32 pm_iir = 0;
|
2011-04-26 01:25:20 +07:00
|
|
|
|
2013-07-05 04:35:28 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-10-11 04:30:06 +07:00
|
|
|
if (rps->interrupts_enabled) {
|
|
|
|
pm_iir = fetch_and_zero(&rps->pm_iir);
|
|
|
|
client_boost = atomic_read(&rps->num_waiters);
|
drm/i915: sanitize rps irq disabling
When disabling the RPS interrupts there is a tricky dependency between
the thread disabling the interrupts, the RPS interrupt handler and the
corresponding RPS work. The RPS work can reenable the interrupts, so
there is no straightforward order in the disabling thread to (1) make
sure that any RPS work is flushed and to (2) disable all RPS
interrupts. Currently this is solved by masking the interrupts using two
separate mask registers (first level display IMR and PM IMR) and doing
the disabling when all first level interrupts are disabled.
This works, but the requirement to run with all first level interrupts
disabled is unnecessary making the suspend / unload time ordering of RPS
disabling wrt. other unitialization steps difficult and error prone.
Removing this restriction allows us to disable RPS early during suspend
/ unload and forget about it for the rest of the sequence. By adding a
more explicit method for avoiding the above race, it also becomes easier
to prove its correctness. Finally currently we can hit the WARN in
snb_update_pm_irq(), when a final RPS work runs with the first level
interrupts already disabled. This won't lead to any problem (due to the
separate interrupt masks), but with the change in this and the next
patch we can get rid of the WARN, while leaving it in place for other
scenarios.
To address the above points, add a new RPS interrupts_enabled flag and
use this during RPS disabling to avoid requeuing the RPS work and
reenabling of the RPS interrupts. Since the interrupt disabling happens
now in intel_suspend_gt_powersave(), we will disable RPS interrupts
explicitly during suspend (and not just through the first level mask),
but there is no problem doing so, it's also more consistent and allows
us to unify more of the RPS disabling during suspend and unload time in
the next patch.
v2/v3:
- rebase on patch "drm/i915: move rps irq disable one level up" in the
patchset
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2014-11-19 20:30:04 +07:00
|
|
|
}
|
2013-07-05 04:35:28 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2010-12-18 05:19:02 +07:00
|
|
|
|
2013-08-15 21:50:01 +07:00
|
|
|
/* Make sure we didn't queue anything we're not going to process. */
|
2014-03-15 21:53:22 +07:00
|
|
|
WARN_ON(pm_iir & ~dev_priv->pm_rps_events);
|
2015-05-22 03:01:47 +07:00
|
|
|
if ((pm_iir & dev_priv->pm_rps_events) == 0 && !client_boost)
|
2017-03-10 04:12:32 +07:00
|
|
|
goto out;
|
2010-12-18 05:19:02 +07:00
|
|
|
|
2019-04-26 15:17:21 +07:00
|
|
|
mutex_lock(&rps->lock);
|
2012-04-28 14:56:39 +07:00
|
|
|
|
2015-03-18 16:48:22 +07:00
|
|
|
pm_iir |= vlv_wa_c0_ei(dev_priv, pm_iir);
|
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
adj = rps->last_adj;
|
|
|
|
new_delay = rps->cur_freq;
|
|
|
|
min = rps->min_freq_softlimit;
|
|
|
|
max = rps->max_freq_softlimit;
|
2017-06-28 19:35:48 +07:00
|
|
|
if (client_boost)
|
2017-10-11 04:30:06 +07:00
|
|
|
max = rps->max_freq;
|
|
|
|
if (client_boost && new_delay < rps->boost_freq) {
|
|
|
|
new_delay = rps->boost_freq;
|
2015-05-22 03:01:47 +07:00
|
|
|
adj = 0;
|
|
|
|
} else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
|
drm/i915: Tweak RPS thresholds to more aggressively downclock
After applying wait-boost we often find ourselves stuck at higher clocks
than required. The current threshold value requires the GPU to be
continuously and completely idle for 313ms before it is dropped by one
bin. Conversely, we require the GPU to be busy for an average of 90% over
a 84ms period before we upclock. So the current thresholds almost never
downclock the GPU, and respond very slowly to sudden demands for more
power. It is easy to observe that we currently lock into the wrong bin
and both underperform in benchmarks and consume more power than optimal
(just by repeating the task and measuring the different results).
An alternative approach, as discussed in the bspec, is to use a
continuous threshold for upclocking, and an average value for downclocking.
This is good for quickly detecting and reacting to state changes within a
frame, however it fails with the common throttling method of waiting
upon the outstanding frame - at least it is difficult to choose a
threshold that works well at 15,000fps and at 60fps. So continue to use
average busy/idle loads to determine frequency change.
v2: Use 3 power zones to keep frequencies low in steady-state mostly
idle (e.g. scrolling, interactive 2D drawing), and frequencies high
for demanding games. In between those end-states, we use a
fast-reclocking algorithm to converge more quickly on the desired bin.
v3: Bug fixes - make sure we reset adj after switching power zones.
v4: Tune - drop the continuous busy thresholds as it prevents us from
choosing the right frequency for glxgears style swap benchmarks. Instead
the goal is to be able to find the right clocks irrespective of the
wait-boost.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Kenneth Graunke <kenneth@whitecape.org>
Cc: Stéphane Marchesin <stephane.marchesin@gmail.com>
Cc: Owen Taylor <otaylor@redhat.com>
Cc: "Meng, Mengmeng" <mengmeng.meng@intel.com>
Cc: "Zhuang, Lena" <lena.zhuang@intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-09-25 23:34:57 +07:00
|
|
|
if (adj > 0)
|
|
|
|
adj *= 2;
|
2015-04-07 22:20:29 +07:00
|
|
|
else /* CHV needs even encode values */
|
|
|
|
adj = IS_CHERRYVIEW(dev_priv) ? 2 : 1;
|
2017-01-20 10:48:24 +07:00
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
if (new_delay >= rps->max_freq_softlimit)
|
2017-01-20 10:48:24 +07:00
|
|
|
adj = 0;
|
2017-06-28 19:35:48 +07:00
|
|
|
} else if (client_boost) {
|
2015-04-27 19:41:23 +07:00
|
|
|
adj = 0;
|
drm/i915: Tweak RPS thresholds to more aggressively downclock
After applying wait-boost we often find ourselves stuck at higher clocks
than required. The current threshold value requires the GPU to be
continuously and completely idle for 313ms before it is dropped by one
bin. Conversely, we require the GPU to be busy for an average of 90% over
a 84ms period before we upclock. So the current thresholds almost never
downclock the GPU, and respond very slowly to sudden demands for more
power. It is easy to observe that we currently lock into the wrong bin
and both underperform in benchmarks and consume more power than optimal
(just by repeating the task and measuring the different results).
An alternative approach, as discussed in the bspec, is to use a
continuous threshold for upclocking, and an average value for downclocking.
This is good for quickly detecting and reacting to state changes within a
frame, however it fails with the common throttling method of waiting
upon the outstanding frame - at least it is difficult to choose a
threshold that works well at 15,000fps and at 60fps. So continue to use
average busy/idle loads to determine frequency change.
v2: Use 3 power zones to keep frequencies low in steady-state mostly
idle (e.g. scrolling, interactive 2D drawing), and frequencies high
for demanding games. In between those end-states, we use a
fast-reclocking algorithm to converge more quickly on the desired bin.
v3: Bug fixes - make sure we reset adj after switching power zones.
v4: Tune - drop the continuous busy thresholds as it prevents us from
choosing the right frequency for glxgears style swap benchmarks. Instead
the goal is to be able to find the right clocks irrespective of the
wait-boost.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Kenneth Graunke <kenneth@whitecape.org>
Cc: Stéphane Marchesin <stephane.marchesin@gmail.com>
Cc: Owen Taylor <otaylor@redhat.com>
Cc: "Meng, Mengmeng" <mengmeng.meng@intel.com>
Cc: "Zhuang, Lena" <lena.zhuang@intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-09-25 23:34:57 +07:00
|
|
|
} else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
|
2017-10-11 04:30:06 +07:00
|
|
|
if (rps->cur_freq > rps->efficient_freq)
|
|
|
|
new_delay = rps->efficient_freq;
|
|
|
|
else if (rps->cur_freq > rps->min_freq_softlimit)
|
|
|
|
new_delay = rps->min_freq_softlimit;
|
drm/i915: Tweak RPS thresholds to more aggressively downclock
After applying wait-boost we often find ourselves stuck at higher clocks
than required. The current threshold value requires the GPU to be
continuously and completely idle for 313ms before it is dropped by one
bin. Conversely, we require the GPU to be busy for an average of 90% over
a 84ms period before we upclock. So the current thresholds almost never
downclock the GPU, and respond very slowly to sudden demands for more
power. It is easy to observe that we currently lock into the wrong bin
and both underperform in benchmarks and consume more power than optimal
(just by repeating the task and measuring the different results).
An alternative approach, as discussed in the bspec, is to use a
continuous threshold for upclocking, and an average value for downclocking.
This is good for quickly detecting and reacting to state changes within a
frame, however it fails with the common throttling method of waiting
upon the outstanding frame - at least it is difficult to choose a
threshold that works well at 15,000fps and at 60fps. So continue to use
average busy/idle loads to determine frequency change.
v2: Use 3 power zones to keep frequencies low in steady-state mostly
idle (e.g. scrolling, interactive 2D drawing), and frequencies high
for demanding games. In between those end-states, we use a
fast-reclocking algorithm to converge more quickly on the desired bin.
v3: Bug fixes - make sure we reset adj after switching power zones.
v4: Tune - drop the continuous busy thresholds as it prevents us from
choosing the right frequency for glxgears style swap benchmarks. Instead
the goal is to be able to find the right clocks irrespective of the
wait-boost.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Kenneth Graunke <kenneth@whitecape.org>
Cc: Stéphane Marchesin <stephane.marchesin@gmail.com>
Cc: Owen Taylor <otaylor@redhat.com>
Cc: "Meng, Mengmeng" <mengmeng.meng@intel.com>
Cc: "Zhuang, Lena" <lena.zhuang@intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-09-25 23:34:57 +07:00
|
|
|
adj = 0;
|
|
|
|
} else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
|
|
|
|
if (adj < 0)
|
|
|
|
adj *= 2;
|
2015-04-07 22:20:29 +07:00
|
|
|
else /* CHV needs even encode values */
|
|
|
|
adj = IS_CHERRYVIEW(dev_priv) ? -2 : -1;
|
2017-01-20 10:48:24 +07:00
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
if (new_delay <= rps->min_freq_softlimit)
|
2017-01-20 10:48:24 +07:00
|
|
|
adj = 0;
|
drm/i915: Tweak RPS thresholds to more aggressively downclock
After applying wait-boost we often find ourselves stuck at higher clocks
than required. The current threshold value requires the GPU to be
continuously and completely idle for 313ms before it is dropped by one
bin. Conversely, we require the GPU to be busy for an average of 90% over
a 84ms period before we upclock. So the current thresholds almost never
downclock the GPU, and respond very slowly to sudden demands for more
power. It is easy to observe that we currently lock into the wrong bin
and both underperform in benchmarks and consume more power than optimal
(just by repeating the task and measuring the different results).
An alternative approach, as discussed in the bspec, is to use a
continuous threshold for upclocking, and an average value for downclocking.
This is good for quickly detecting and reacting to state changes within a
frame, however it fails with the common throttling method of waiting
upon the outstanding frame - at least it is difficult to choose a
threshold that works well at 15,000fps and at 60fps. So continue to use
average busy/idle loads to determine frequency change.
v2: Use 3 power zones to keep frequencies low in steady-state mostly
idle (e.g. scrolling, interactive 2D drawing), and frequencies high
for demanding games. In between those end-states, we use a
fast-reclocking algorithm to converge more quickly on the desired bin.
v3: Bug fixes - make sure we reset adj after switching power zones.
v4: Tune - drop the continuous busy thresholds as it prevents us from
choosing the right frequency for glxgears style swap benchmarks. Instead
the goal is to be able to find the right clocks irrespective of the
wait-boost.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Kenneth Graunke <kenneth@whitecape.org>
Cc: Stéphane Marchesin <stephane.marchesin@gmail.com>
Cc: Owen Taylor <otaylor@redhat.com>
Cc: "Meng, Mengmeng" <mengmeng.meng@intel.com>
Cc: "Zhuang, Lena" <lena.zhuang@intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-09-25 23:34:57 +07:00
|
|
|
} else { /* unknown event */
|
2015-04-07 22:20:29 +07:00
|
|
|
adj = 0;
|
drm/i915: Tweak RPS thresholds to more aggressively downclock
After applying wait-boost we often find ourselves stuck at higher clocks
than required. The current threshold value requires the GPU to be
continuously and completely idle for 313ms before it is dropped by one
bin. Conversely, we require the GPU to be busy for an average of 90% over
a 84ms period before we upclock. So the current thresholds almost never
downclock the GPU, and respond very slowly to sudden demands for more
power. It is easy to observe that we currently lock into the wrong bin
and both underperform in benchmarks and consume more power than optimal
(just by repeating the task and measuring the different results).
An alternative approach, as discussed in the bspec, is to use a
continuous threshold for upclocking, and an average value for downclocking.
This is good for quickly detecting and reacting to state changes within a
frame, however it fails with the common throttling method of waiting
upon the outstanding frame - at least it is difficult to choose a
threshold that works well at 15,000fps and at 60fps. So continue to use
average busy/idle loads to determine frequency change.
v2: Use 3 power zones to keep frequencies low in steady-state mostly
idle (e.g. scrolling, interactive 2D drawing), and frequencies high
for demanding games. In between those end-states, we use a
fast-reclocking algorithm to converge more quickly on the desired bin.
v3: Bug fixes - make sure we reset adj after switching power zones.
v4: Tune - drop the continuous busy thresholds as it prevents us from
choosing the right frequency for glxgears style swap benchmarks. Instead
the goal is to be able to find the right clocks irrespective of the
wait-boost.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Kenneth Graunke <kenneth@whitecape.org>
Cc: Stéphane Marchesin <stephane.marchesin@gmail.com>
Cc: Owen Taylor <otaylor@redhat.com>
Cc: "Meng, Mengmeng" <mengmeng.meng@intel.com>
Cc: "Zhuang, Lena" <lena.zhuang@intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-09-25 23:34:57 +07:00
|
|
|
}
|
2010-12-18 05:19:02 +07:00
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->last_adj = adj;
|
2015-04-07 22:20:29 +07:00
|
|
|
|
2019-02-19 19:22:03 +07:00
|
|
|
/*
|
|
|
|
* Limit deboosting and boosting to keep ourselves at the extremes
|
|
|
|
* when in the respective power modes (i.e. slowly decrease frequencies
|
|
|
|
* while in the HIGH_POWER zone and slowly increase frequencies while
|
|
|
|
* in the LOW_POWER zone). On idle, we will hit the timeout and drop
|
|
|
|
* to the next level quickly, and conversely if busy we expect to
|
|
|
|
* hit a waitboost and rapidly switch into max power.
|
|
|
|
*/
|
|
|
|
if ((adj < 0 && rps->power.mode == HIGH_POWER) ||
|
|
|
|
(adj > 0 && rps->power.mode == LOW_POWER))
|
|
|
|
rps->last_adj = 0;
|
|
|
|
|
2012-09-08 09:43:42 +07:00
|
|
|
/* sysfs frequency interfaces may have snuck in while servicing the
|
|
|
|
* interrupt
|
|
|
|
*/
|
2015-04-07 22:20:29 +07:00
|
|
|
new_delay += adj;
|
2015-05-22 03:01:47 +07:00
|
|
|
new_delay = clamp_t(int, new_delay, min, max);
|
2014-01-27 23:05:05 +07:00
|
|
|
|
2017-01-26 17:19:19 +07:00
|
|
|
if (intel_set_rps(dev_priv, new_delay)) {
|
|
|
|
DRM_DEBUG_DRIVER("Failed to set new GPU frequency\n");
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->last_adj = 0;
|
2017-01-26 17:19:19 +07:00
|
|
|
}
|
2010-12-18 05:19:02 +07:00
|
|
|
|
2019-04-26 15:17:21 +07:00
|
|
|
mutex_unlock(&rps->lock);
|
2017-03-10 04:12:32 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
/* Make sure not to corrupt PMIMR state used by ringbuffer on GEN6 */
|
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-10-11 04:30:06 +07:00
|
|
|
if (rps->interrupts_enabled)
|
2019-07-04 19:17:56 +07:00
|
|
|
gen6_unmask_pm_irq(&dev_priv->gt, dev_priv->pm_rps_events);
|
2017-03-10 04:12:32 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2010-12-18 05:19:02 +07:00
|
|
|
}
|
|
|
|
|
2012-05-26 06:56:22 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ivybridge_parity_work - Workqueue called when a parity error interrupt
|
|
|
|
* occurred.
|
|
|
|
* @work: workqueue struct
|
|
|
|
*
|
|
|
|
* Doesn't actually do anything except notify userspace. As a consequence of
|
|
|
|
* this event, userspace should try to remap the bad rows since statistically
|
|
|
|
* it is likely the same row is more likely to go bad again.
|
|
|
|
*/
|
|
|
|
static void ivybridge_parity_work(struct work_struct *work)
|
|
|
|
{
|
2014-03-31 18:27:17 +07:00
|
|
|
struct drm_i915_private *dev_priv =
|
2017-04-28 14:58:39 +07:00
|
|
|
container_of(work, typeof(*dev_priv), l3_parity.error_work);
|
2012-05-26 06:56:22 +07:00
|
|
|
u32 error_status, row, bank, subbank;
|
2013-09-20 01:13:41 +07:00
|
|
|
char *parity_event[6];
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 misccpctl;
|
|
|
|
u8 slice = 0;
|
2012-05-26 06:56:22 +07:00
|
|
|
|
|
|
|
/* We must turn off DOP level clock gating to access the L3 registers.
|
|
|
|
* In order to prevent a get/put style interface, acquire struct mutex
|
|
|
|
* any time we access those registers.
|
|
|
|
*/
|
2016-07-05 16:40:23 +07:00
|
|
|
mutex_lock(&dev_priv->drm.struct_mutex);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
/* If we've screwed up tracking, just let the interrupt fire again */
|
|
|
|
if (WARN_ON(!dev_priv->l3_parity.which_slice))
|
|
|
|
goto out;
|
|
|
|
|
2012-05-26 06:56:22 +07:00
|
|
|
misccpctl = I915_READ(GEN7_MISCCPCTL);
|
|
|
|
I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
|
|
|
|
POSTING_READ(GEN7_MISCCPCTL);
|
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
while ((slice = ffs(dev_priv->l3_parity.which_slice)) != 0) {
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
i915_reg_t reg;
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
slice--;
|
2016-04-07 15:08:05 +07:00
|
|
|
if (WARN_ON_ONCE(slice >= NUM_L3_SLICES(dev_priv)))
|
2013-09-20 01:13:41 +07:00
|
|
|
break;
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
dev_priv->l3_parity.which_slice &= ~(1<<slice);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2015-11-05 04:20:02 +07:00
|
|
|
reg = GEN7_L3CDERRST1(slice);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
error_status = I915_READ(reg);
|
|
|
|
row = GEN7_PARITY_ERROR_ROW(error_status);
|
|
|
|
bank = GEN7_PARITY_ERROR_BANK(error_status);
|
|
|
|
subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
|
|
|
|
|
|
|
|
I915_WRITE(reg, GEN7_PARITY_ERROR_VALID | GEN7_L3CDERRST1_ENABLE);
|
|
|
|
POSTING_READ(reg);
|
|
|
|
|
|
|
|
parity_event[0] = I915_L3_PARITY_UEVENT "=1";
|
|
|
|
parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
|
|
|
|
parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
|
|
|
|
parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
|
|
|
|
parity_event[4] = kasprintf(GFP_KERNEL, "SLICE=%d", slice);
|
|
|
|
parity_event[5] = NULL;
|
|
|
|
|
2016-07-05 16:40:23 +07:00
|
|
|
kobject_uevent_env(&dev_priv->drm.primary->kdev->kobj,
|
2013-09-20 01:13:41 +07:00
|
|
|
KOBJ_CHANGE, parity_event);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
DRM_DEBUG("Parity error: Slice = %d, Row = %d, Bank = %d, Sub bank = %d.\n",
|
|
|
|
slice, row, bank, subbank);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
kfree(parity_event[4]);
|
|
|
|
kfree(parity_event[3]);
|
|
|
|
kfree(parity_event[2]);
|
|
|
|
kfree(parity_event[1]);
|
|
|
|
}
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
I915_WRITE(GEN7_MISCCPCTL, misccpctl);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2013-09-20 01:13:41 +07:00
|
|
|
out:
|
|
|
|
WARN_ON(dev_priv->l3_parity.which_slice);
|
2014-09-15 19:55:26 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2016-04-07 15:08:05 +07:00
|
|
|
gen5_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
|
2014-09-15 19:55:26 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2013-09-20 01:13:41 +07:00
|
|
|
|
2016-07-05 16:40:23 +07:00
|
|
|
mutex_unlock(&dev_priv->drm.struct_mutex);
|
2012-05-26 06:56:22 +07:00
|
|
|
}
|
|
|
|
|
2016-04-14 01:19:57 +07:00
|
|
|
static void ivybridge_parity_error_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 iir)
|
2012-05-26 06:56:22 +07:00
|
|
|
{
|
2016-04-14 01:19:57 +07:00
|
|
|
if (!HAS_L3_DPF(dev_priv))
|
2012-05-26 06:56:22 +07:00
|
|
|
return;
|
|
|
|
|
2013-07-05 04:35:25 +07:00
|
|
|
spin_lock(&dev_priv->irq_lock);
|
2016-04-14 01:19:57 +07:00
|
|
|
gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
|
2013-07-05 04:35:25 +07:00
|
|
|
spin_unlock(&dev_priv->irq_lock);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2016-04-14 01:19:57 +07:00
|
|
|
iir &= GT_PARITY_ERROR(dev_priv);
|
2013-09-20 01:13:41 +07:00
|
|
|
if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
|
|
|
|
dev_priv->l3_parity.which_slice |= 1 << 1;
|
|
|
|
|
|
|
|
if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
|
|
|
|
dev_priv->l3_parity.which_slice |= 1 << 0;
|
|
|
|
|
2012-11-03 01:55:07 +07:00
|
|
|
queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
|
2012-05-26 06:56:22 +07:00
|
|
|
}
|
|
|
|
|
2016-04-14 01:19:57 +07:00
|
|
|
static void ilk_gt_irq_handler(struct drm_i915_private *dev_priv,
|
2013-07-13 05:56:30 +07:00
|
|
|
u32 gt_iir)
|
|
|
|
{
|
2016-07-01 23:23:21 +07:00
|
|
|
if (gt_iir & GT_RENDER_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[RCS0]);
|
2013-07-13 05:56:30 +07:00
|
|
|
if (gt_iir & ILK_BSD_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[VCS0]);
|
2013-07-13 05:56:30 +07:00
|
|
|
}
|
|
|
|
|
2016-04-14 01:19:57 +07:00
|
|
|
static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
|
2012-03-31 01:24:35 +07:00
|
|
|
u32 gt_iir)
|
|
|
|
{
|
2016-07-01 23:23:21 +07:00
|
|
|
if (gt_iir & GT_RENDER_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[RCS0]);
|
2013-05-29 09:22:29 +07:00
|
|
|
if (gt_iir & GT_BSD_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[VCS0]);
|
2013-05-29 09:22:29 +07:00
|
|
|
if (gt_iir & GT_BLT_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[BCS0]);
|
2012-03-31 01:24:35 +07:00
|
|
|
|
2013-05-29 09:22:29 +07:00
|
|
|
if (gt_iir & (GT_BLT_CS_ERROR_INTERRUPT |
|
|
|
|
GT_BSD_CS_ERROR_INTERRUPT |
|
2014-11-04 21:52:22 +07:00
|
|
|
GT_RENDER_CS_MASTER_ERROR_INTERRUPT))
|
|
|
|
DRM_DEBUG("Command parser error, gt_iir 0x%08x\n", gt_iir);
|
2012-05-26 06:56:22 +07:00
|
|
|
|
2016-04-14 01:19:57 +07:00
|
|
|
if (gt_iir & GT_PARITY_ERROR(dev_priv))
|
|
|
|
ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
|
2012-03-31 01:24:35 +07:00
|
|
|
}
|
|
|
|
|
2017-05-17 19:10:06 +07:00
|
|
|
static void
|
2018-03-09 08:08:08 +07:00
|
|
|
gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir)
|
2015-10-20 16:23:52 +07:00
|
|
|
{
|
2017-03-16 19:56:18 +07:00
|
|
|
bool tasklet = false;
|
2017-01-24 22:20:21 +07:00
|
|
|
|
2018-06-29 03:12:10 +07:00
|
|
|
if (iir & GT_CONTEXT_SWITCH_INTERRUPT)
|
|
|
|
tasklet = true;
|
2017-03-16 19:56:18 +07:00
|
|
|
|
2018-03-09 08:08:08 +07:00
|
|
|
if (iir & GT_RENDER_USER_INTERRUPT) {
|
drm/i915: Replace global breadcrumbs with per-context interrupt tracking
A few years ago, see commit 688e6c725816 ("drm/i915: Slaughter the
thundering i915_wait_request herd"), the issue of handling multiple
clients waiting in parallel was brought to our attention. The
requirement was that every client should be woken immediately upon its
request being signaled, without incurring any cpu overhead.
To handle certain fragility of our hw meant that we could not do a
simple check inside the irq handler (some generations required almost
unbounded delays before we could be sure of seqno coherency) and so
request completion checking required delegation.
Before commit 688e6c725816, the solution was simple. Every client
waiting on a request would be woken on every interrupt and each would do
a heavyweight check to see if their request was complete. Commit
688e6c725816 introduced an rbtree so that only the earliest waiter on
the global timeline would woken, and would wake the next and so on.
(Along with various complications to handle requests being reordered
along the global timeline, and also a requirement for kthread to provide
a delegate for fence signaling that had no process context.)
The global rbtree depends on knowing the execution timeline (and global
seqno). Without knowing that order, we must instead check all contexts
queued to the HW to see which may have advanced. We trim that list by
only checking queued contexts that are being waited on, but still we
keep a list of all active contexts and their active signalers that we
inspect from inside the irq handler. By moving the waiters onto the fence
signal list, we can combine the client wakeup with the dma_fence
signaling (a dramatic reduction in complexity, but does require the HW
being coherent, the seqno must be visible from the cpu before the
interrupt is raised - we keep a timer backup just in case).
Having previously fixed all the issues with irq-seqno serialisation (by
inserting delays onto the GPU after each request instead of random delays
on the CPU after each interrupt), we can rely on the seqno state to
perfom direct wakeups from the interrupt handler. This allows us to
preserve our single context switch behaviour of the current routine,
with the only downside that we lose the RT priority sorting of wakeups.
In general, direct wakeup latency of multiple clients is about the same
(about 10% better in most cases) with a reduction in total CPU time spent
in the waiter (about 20-50% depending on gen). Average herd behaviour is
improved, but at the cost of not delegating wakeups on task_prio.
v2: Capture fence signaling state for error state and add comments to
warm even the most cold of hearts.
v3: Check if the request is still active before busywaiting
v4: Reduce the amount of pointer misdirection with list_for_each_safe
and using a local i915_request variable inside the loops
v5: Add a missing pluralisation to a purely informative selftest message.
References: 688e6c725816 ("drm/i915: Slaughter the thundering i915_wait_request herd")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190129205230.19056-2-chris@chris-wilson.co.uk
2019-01-30 03:52:29 +07:00
|
|
|
intel_engine_breadcrumbs_irq(engine);
|
2019-03-29 22:49:12 +07:00
|
|
|
tasklet |= intel_engine_needs_breadcrumb_tasklet(engine);
|
2017-03-16 19:56:18 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tasklet)
|
2018-06-29 03:12:10 +07:00
|
|
|
tasklet_hi_schedule(&engine->execlists.tasklet);
|
2015-10-20 16:23:52 +07:00
|
|
|
}
|
|
|
|
|
2018-02-19 17:09:26 +07:00
|
|
|
static void gen8_gt_irq_ack(struct drm_i915_private *i915,
|
2018-02-02 22:34:48 +07:00
|
|
|
u32 master_ctl, u32 gt_iir[4])
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2019-03-20 01:35:40 +07:00
|
|
|
void __iomem * const regs = i915->uncore.regs;
|
2018-02-19 17:09:26 +07:00
|
|
|
|
2018-02-15 14:37:12 +07:00
|
|
|
#define GEN8_GT_IRQS (GEN8_GT_RCS_IRQ | \
|
|
|
|
GEN8_GT_BCS_IRQ | \
|
2019-03-06 01:03:30 +07:00
|
|
|
GEN8_GT_VCS0_IRQ | \
|
2018-02-15 14:37:12 +07:00
|
|
|
GEN8_GT_VCS1_IRQ | \
|
|
|
|
GEN8_GT_VECS_IRQ | \
|
|
|
|
GEN8_GT_PM_IRQ | \
|
|
|
|
GEN8_GT_GUC_IRQ)
|
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
|
2018-02-19 17:09:26 +07:00
|
|
|
gt_iir[0] = raw_reg_read(regs, GEN8_GT_IIR(0));
|
|
|
|
if (likely(gt_iir[0]))
|
|
|
|
raw_reg_write(regs, GEN8_GT_IIR(0), gt_iir[0]);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2019-03-06 01:03:30 +07:00
|
|
|
if (master_ctl & (GEN8_GT_VCS0_IRQ | GEN8_GT_VCS1_IRQ)) {
|
2018-02-19 17:09:26 +07:00
|
|
|
gt_iir[1] = raw_reg_read(regs, GEN8_GT_IIR(1));
|
|
|
|
if (likely(gt_iir[1]))
|
|
|
|
raw_reg_write(regs, GEN8_GT_IIR(1), gt_iir[1]);
|
2014-05-16 00:58:08 +07:00
|
|
|
}
|
|
|
|
|
2018-02-19 17:09:26 +07:00
|
|
|
if (master_ctl & (GEN8_GT_PM_IRQ | GEN8_GT_GUC_IRQ)) {
|
|
|
|
gt_iir[2] = raw_reg_read(regs, GEN8_GT_IIR(2));
|
2018-08-02 17:06:29 +07:00
|
|
|
if (likely(gt_iir[2]))
|
|
|
|
raw_reg_write(regs, GEN8_GT_IIR(2), gt_iir[2]);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2018-02-19 17:09:26 +07:00
|
|
|
if (master_ctl & GEN8_GT_VECS_IRQ) {
|
|
|
|
gt_iir[3] = raw_reg_read(regs, GEN8_GT_IIR(3));
|
|
|
|
if (likely(gt_iir[3]))
|
|
|
|
raw_reg_write(regs, GEN8_GT_IIR(3), gt_iir[3]);
|
2014-05-16 00:58:08 +07:00
|
|
|
}
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2018-02-19 17:09:26 +07:00
|
|
|
static void gen8_gt_irq_handler(struct drm_i915_private *i915,
|
2018-02-15 14:37:12 +07:00
|
|
|
u32 master_ctl, u32 gt_iir[4])
|
2016-04-14 01:19:58 +07:00
|
|
|
{
|
2018-02-15 14:37:12 +07:00
|
|
|
if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
|
2019-03-06 01:03:30 +07:00
|
|
|
gen8_cs_irq_handler(i915->engine[RCS0],
|
2018-03-09 08:08:08 +07:00
|
|
|
gt_iir[0] >> GEN8_RCS_IRQ_SHIFT);
|
2019-03-06 01:03:30 +07:00
|
|
|
gen8_cs_irq_handler(i915->engine[BCS0],
|
2018-03-09 08:08:08 +07:00
|
|
|
gt_iir[0] >> GEN8_BCS_IRQ_SHIFT);
|
2016-04-14 01:19:58 +07:00
|
|
|
}
|
|
|
|
|
2019-03-06 01:03:30 +07:00
|
|
|
if (master_ctl & (GEN8_GT_VCS0_IRQ | GEN8_GT_VCS1_IRQ)) {
|
|
|
|
gen8_cs_irq_handler(i915->engine[VCS0],
|
|
|
|
gt_iir[1] >> GEN8_VCS0_IRQ_SHIFT);
|
|
|
|
gen8_cs_irq_handler(i915->engine[VCS1],
|
2018-03-09 08:08:08 +07:00
|
|
|
gt_iir[1] >> GEN8_VCS1_IRQ_SHIFT);
|
2016-04-14 01:19:58 +07:00
|
|
|
}
|
|
|
|
|
2018-02-15 14:37:12 +07:00
|
|
|
if (master_ctl & GEN8_GT_VECS_IRQ) {
|
2019-03-06 01:03:30 +07:00
|
|
|
gen8_cs_irq_handler(i915->engine[VECS0],
|
2018-03-09 08:08:08 +07:00
|
|
|
gt_iir[3] >> GEN8_VECS_IRQ_SHIFT);
|
2018-02-15 14:37:12 +07:00
|
|
|
}
|
2016-04-14 01:19:58 +07:00
|
|
|
|
2018-02-15 14:37:12 +07:00
|
|
|
if (master_ctl & (GEN8_GT_PM_IRQ | GEN8_GT_GUC_IRQ)) {
|
2018-02-19 17:09:26 +07:00
|
|
|
gen6_rps_irq_handler(i915, gt_iir[2]);
|
2019-07-13 17:00:12 +07:00
|
|
|
guc_irq_handler(&i915->gt.uc.guc, gt_iir[2] >> 16);
|
2018-02-15 14:37:12 +07:00
|
|
|
}
|
2016-04-14 01:19:58 +07:00
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool gen11_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2018-06-16 07:05:29 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_C:
|
2018-06-16 07:05:29 +07:00
|
|
|
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC1);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_D:
|
2018-06-16 07:05:29 +07:00
|
|
|
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC2);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_E:
|
2018-06-16 07:05:29 +07:00
|
|
|
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC3);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_F:
|
2018-06-16 07:05:29 +07:00
|
|
|
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC4);
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool bxt_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2015-07-21 04:43:39 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_A:
|
2015-08-28 03:56:00 +07:00
|
|
|
return val & PORTA_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_B:
|
2015-07-21 04:43:39 +07:00
|
|
|
return val & PORTB_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_C:
|
2015-07-21 04:43:39 +07:00
|
|
|
return val & PORTC_HOTPLUG_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool icp_ddi_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2018-06-27 03:52:23 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_A:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_DDIA_HPD_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_B:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_DDIB_HPD_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool icp_tc_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2018-06-27 03:52:23 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_C:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC1);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_D:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC2);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_E:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC3);
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_F:
|
2018-06-27 03:52:23 +07:00
|
|
|
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC4);
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool spt_port_hotplug2_long_detect(enum hpd_pin pin, u32 val)
|
2015-08-28 03:56:02 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_E:
|
2015-08-28 03:56:02 +07:00
|
|
|
return val & PORTE_HOTPLUG_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool spt_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2015-08-28 03:56:07 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_A:
|
2015-08-28 03:56:07 +07:00
|
|
|
return val & PORTA_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_B:
|
2015-08-28 03:56:07 +07:00
|
|
|
return val & PORTB_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_C:
|
2015-08-28 03:56:07 +07:00
|
|
|
return val & PORTC_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_D:
|
2015-08-28 03:56:07 +07:00
|
|
|
return val & PORTD_HOTPLUG_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool ilk_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2015-08-28 03:56:03 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_A:
|
2015-08-28 03:56:03 +07:00
|
|
|
return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool pch_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2014-06-18 08:29:35 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_B:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTB_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_C:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTC_HOTPLUG_LONG_DETECT;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_D:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTD_HOTPLUG_LONG_DETECT;
|
|
|
|
default:
|
|
|
|
return false;
|
2014-06-18 08:29:35 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
static bool i9xx_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
|
2014-06-18 08:29:35 +07:00
|
|
|
{
|
2018-07-05 23:43:55 +07:00
|
|
|
switch (pin) {
|
|
|
|
case HPD_PORT_B:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTB_HOTPLUG_INT_LONG_PULSE;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_C:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTC_HOTPLUG_INT_LONG_PULSE;
|
2018-07-05 23:43:55 +07:00
|
|
|
case HPD_PORT_D:
|
2015-05-28 19:43:53 +07:00
|
|
|
return val & PORTD_HOTPLUG_INT_LONG_PULSE;
|
|
|
|
default:
|
|
|
|
return false;
|
2014-06-18 08:29:35 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-29 01:26:27 +07:00
|
|
|
/*
|
|
|
|
* Get a bit mask of pins that have triggered, and which ones may be long.
|
|
|
|
* This can be called multiple times with the same masks to accumulate
|
|
|
|
* hotplug detection results from several registers.
|
|
|
|
*
|
|
|
|
* Note that the caller is expected to zero out the masks initially.
|
|
|
|
*/
|
2018-01-30 06:22:21 +07:00
|
|
|
static void intel_get_hpd_pins(struct drm_i915_private *dev_priv,
|
|
|
|
u32 *pin_mask, u32 *long_mask,
|
|
|
|
u32 hotplug_trigger, u32 dig_hotplug_reg,
|
|
|
|
const u32 hpd[HPD_NUM_PINS],
|
2018-07-05 23:43:55 +07:00
|
|
|
bool long_pulse_detect(enum hpd_pin pin, u32 val))
|
2015-05-28 19:43:53 +07:00
|
|
|
{
|
2018-07-05 23:43:54 +07:00
|
|
|
enum hpd_pin pin;
|
2015-05-28 19:43:53 +07:00
|
|
|
|
2018-07-05 23:43:54 +07:00
|
|
|
for_each_hpd_pin(pin) {
|
|
|
|
if ((hpd[pin] & hotplug_trigger) == 0)
|
2015-06-18 17:06:17 +07:00
|
|
|
continue;
|
2015-05-28 19:43:53 +07:00
|
|
|
|
2018-07-05 23:43:54 +07:00
|
|
|
*pin_mask |= BIT(pin);
|
2015-06-18 17:06:17 +07:00
|
|
|
|
2018-07-05 23:43:55 +07:00
|
|
|
if (long_pulse_detect(pin, dig_hotplug_reg))
|
2018-07-05 23:43:54 +07:00
|
|
|
*long_mask |= BIT(pin);
|
2015-05-28 19:43:53 +07:00
|
|
|
}
|
|
|
|
|
2018-07-05 23:43:57 +07:00
|
|
|
DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x, dig 0x%08x, pins 0x%08x, long 0x%08x\n",
|
|
|
|
hotplug_trigger, dig_hotplug_reg, *pin_mask, *long_mask);
|
2015-05-28 19:43:53 +07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void gmbus_irq_handler(struct drm_i915_private *dev_priv)
|
2012-12-01 19:53:44 +07:00
|
|
|
{
|
2012-12-01 19:53:45 +07:00
|
|
|
wake_up_all(&dev_priv->gmbus_wait_queue);
|
2012-12-01 19:53:44 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void dp_aux_irq_handler(struct drm_i915_private *dev_priv)
|
2012-12-01 19:53:47 +07:00
|
|
|
{
|
drm/i915: irq-drive the dp aux communication
At least on the platforms that have a dp aux irq and also have it
enabled - vlvhsw should have one, too. But I don't have a machine to
test this on. Judging from docs there's no dp aux interrupt for gm45.
Also, I only have an ivb cpu edp machine, so the dp aux A code for
snb/ilk is untested.
For dpcd probing when nothing is connected it slashes about 5ms of cpu
time (cpu time is now negligible), which agrees with 3 * 5 400 usec
timeouts.
A previous version of this patch increases the time required to go
through the dp_detect cycle (which includes reading the edid) from
around 33 ms to around 40 ms. Experiments indicated that this is
purely due to the irq latency - the hw doesn't allow us to queue up
dp aux transactions and hence irq latency directly affects throughput.
gmbus is much better, there we have a 8 byte buffer, and we get the
irq once another 4 bytes can be queued up.
But by using the pm_qos interface to request the lowest possible cpu
wake-up latency this slowdown completely disappeared.
Since all our output detection logic is single-threaded with the
mode_config mutex right now anyway, I've decide not ot play fancy and
to just reuse the gmbus wait queue. But this would definitely prep the
way to run dp detection on different ports in parallel
v2: Add a timeout for dp aux transfers when using interrupts - the hw
_does_ prevent this with the hw-based 400 usec timeout, but if the
irq somehow doesn't arrive we're screwed. Lesson learned while
developing this ;-)
v3: While at it also convert the busy-loop to wait_for_atomic, so that
we don't run the risk of an infinite loop any more.
v4: Ensure we have the smallest possible irq latency by using the
pm_qos interface.
v5: Add a comment to the code to explain why we frob pm_qos. Suggested
by Chris Wilson.
v6: Disable dp irq for vlv, that's easier than trying to get at docs
and hw.
v7: Squash in a fix for Haswell that Paulo Zanoni tracked down - the
dp aux registers aren't at a fixed offset any more, but can be on the
PCH while the DP port is on the cpu die.
Reviewed-by: Imre Deak <imre.deak@intel.com> (v6)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2012-12-01 19:53:48 +07:00
|
|
|
wake_up_all(&dev_priv->gmbus_wait_queue);
|
2012-12-01 19:53:47 +07:00
|
|
|
}
|
|
|
|
|
2013-10-16 00:55:27 +07:00
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
2016-05-06 20:48:28 +07:00
|
|
|
static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 crc0, u32 crc1,
|
|
|
|
u32 crc2, u32 crc3,
|
|
|
|
u32 crc4)
|
2013-10-16 00:55:27 +07:00
|
|
|
{
|
|
|
|
struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
|
2017-01-10 20:43:04 +07:00
|
|
|
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
|
2019-02-07 03:49:07 +07:00
|
|
|
u32 crcs[5] = { crc0, crc1, crc2, crc3, crc4 };
|
|
|
|
|
|
|
|
trace_intel_pipe_crc(crtc, crcs);
|
2013-10-16 00:55:29 +07:00
|
|
|
|
2013-10-21 20:29:30 +07:00
|
|
|
spin_lock(&pipe_crc->lock);
|
2018-06-28 14:23:02 +07:00
|
|
|
/*
|
|
|
|
* For some not yet identified reason, the first CRC is
|
|
|
|
* bonkers. So let's just wait for the next vblank and read
|
|
|
|
* out the buggy result.
|
|
|
|
*
|
|
|
|
* On GEN8+ sometimes the second CRC is bonkers as well, so
|
|
|
|
* don't trust that one either.
|
|
|
|
*/
|
|
|
|
if (pipe_crc->skipped <= 0 ||
|
|
|
|
(INTEL_GEN(dev_priv) >= 8 && pipe_crc->skipped == 1)) {
|
|
|
|
pipe_crc->skipped++;
|
2017-01-10 20:43:04 +07:00
|
|
|
spin_unlock(&pipe_crc->lock);
|
2018-06-28 14:23:02 +07:00
|
|
|
return;
|
2017-01-10 20:43:04 +07:00
|
|
|
}
|
2018-06-28 14:23:02 +07:00
|
|
|
spin_unlock(&pipe_crc->lock);
|
|
|
|
|
|
|
|
drm_crtc_add_crc_entry(&crtc->base, true,
|
|
|
|
drm_crtc_accurate_vblank_count(&crtc->base),
|
|
|
|
crcs);
|
2013-10-16 00:55:27 +07:00
|
|
|
}
|
2013-10-18 21:37:07 +07:00
|
|
|
#else
|
|
|
|
static inline void
|
2016-05-06 20:48:28 +07:00
|
|
|
display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe,
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 crc0, u32 crc1,
|
|
|
|
u32 crc2, u32 crc3,
|
|
|
|
u32 crc4) {}
|
2013-10-18 21:37:07 +07:00
|
|
|
#endif
|
|
|
|
|
2013-10-17 03:55:46 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void hsw_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe)
|
2013-10-17 03:55:52 +07:00
|
|
|
{
|
2016-05-06 20:48:28 +07:00
|
|
|
display_pipe_crc_irq_handler(dev_priv, pipe,
|
2013-10-18 21:37:07 +07:00
|
|
|
I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
|
|
|
|
0, 0, 0, 0);
|
2013-10-17 03:55:52 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ivb_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe)
|
2013-10-17 03:55:46 +07:00
|
|
|
{
|
2016-05-06 20:48:28 +07:00
|
|
|
display_pipe_crc_irq_handler(dev_priv, pipe,
|
2013-10-18 21:37:07 +07:00
|
|
|
I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_2_IVB(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_3_IVB(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_4_IVB(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_5_IVB(pipe)));
|
2013-10-17 03:55:46 +07:00
|
|
|
}
|
2013-10-17 03:55:48 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void i9xx_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
enum pipe pipe)
|
2013-10-17 03:55:48 +07:00
|
|
|
{
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 res1, res2;
|
2013-10-17 03:55:53 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 3)
|
2013-10-17 03:55:53 +07:00
|
|
|
res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe));
|
|
|
|
else
|
|
|
|
res1 = 0;
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv))
|
2013-10-17 03:55:53 +07:00
|
|
|
res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe));
|
|
|
|
else
|
|
|
|
res2 = 0;
|
2013-10-17 03:55:48 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
display_pipe_crc_irq_handler(dev_priv, pipe,
|
2013-10-18 21:37:07 +07:00
|
|
|
I915_READ(PIPE_CRC_RES_RED(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_GREEN(pipe)),
|
|
|
|
I915_READ(PIPE_CRC_RES_BLUE(pipe)),
|
|
|
|
res1, res2);
|
2013-10-17 03:55:48 +07:00
|
|
|
}
|
2013-10-16 00:55:27 +07:00
|
|
|
|
2013-08-15 21:51:32 +07:00
|
|
|
/* 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. */
|
2019-07-04 19:17:56 +07:00
|
|
|
static void gen11_rps_irq_handler(struct intel_gt *gt, u32 pm_iir)
|
2019-04-10 20:21:23 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
2019-04-10 20:21:23 +07:00
|
|
|
struct intel_rps *rps = &i915->gt_pm.rps;
|
|
|
|
const u32 events = i915->pm_rps_events & pm_iir;
|
|
|
|
|
|
|
|
lockdep_assert_held(&i915->irq_lock);
|
|
|
|
|
|
|
|
if (unlikely(!events))
|
|
|
|
return;
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
gen6_mask_pm_irq(gt, events);
|
2019-04-10 20:21:23 +07:00
|
|
|
|
|
|
|
if (!rps->interrupts_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rps->pm_iir |= events;
|
|
|
|
schedule_work(&rps->work);
|
|
|
|
}
|
|
|
|
|
2013-08-15 21:51:32 +07:00
|
|
|
static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
|
2013-05-29 09:22:24 +07:00
|
|
|
{
|
2017-10-11 04:30:06 +07:00
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
|
|
|
|
2014-03-15 21:53:22 +07:00
|
|
|
if (pm_iir & dev_priv->pm_rps_events) {
|
2013-07-05 04:35:28 +07:00
|
|
|
spin_lock(&dev_priv->irq_lock);
|
2019-07-04 19:17:56 +07:00
|
|
|
gen6_mask_pm_irq(&dev_priv->gt,
|
|
|
|
pm_iir & dev_priv->pm_rps_events);
|
2017-10-11 04:30:06 +07:00
|
|
|
if (rps->interrupts_enabled) {
|
|
|
|
rps->pm_iir |= pm_iir & dev_priv->pm_rps_events;
|
|
|
|
schedule_work(&rps->work);
|
drm/i915: sanitize rps irq disabling
When disabling the RPS interrupts there is a tricky dependency between
the thread disabling the interrupts, the RPS interrupt handler and the
corresponding RPS work. The RPS work can reenable the interrupts, so
there is no straightforward order in the disabling thread to (1) make
sure that any RPS work is flushed and to (2) disable all RPS
interrupts. Currently this is solved by masking the interrupts using two
separate mask registers (first level display IMR and PM IMR) and doing
the disabling when all first level interrupts are disabled.
This works, but the requirement to run with all first level interrupts
disabled is unnecessary making the suspend / unload time ordering of RPS
disabling wrt. other unitialization steps difficult and error prone.
Removing this restriction allows us to disable RPS early during suspend
/ unload and forget about it for the rest of the sequence. By adding a
more explicit method for avoiding the above race, it also becomes easier
to prove its correctness. Finally currently we can hit the WARN in
snb_update_pm_irq(), when a final RPS work runs with the first level
interrupts already disabled. This won't lead to any problem (due to the
separate interrupt masks), but with the change in this and the next
patch we can get rid of the WARN, while leaving it in place for other
scenarios.
To address the above points, add a new RPS interrupts_enabled flag and
use this during RPS disabling to avoid requeuing the RPS work and
reenabling of the RPS interrupts. Since the interrupt disabling happens
now in intel_suspend_gt_powersave(), we will disable RPS interrupts
explicitly during suspend (and not just through the first level mask),
but there is no problem doing so, it's also more consistent and allows
us to unify more of the RPS disabling during suspend and unload time in
the next patch.
v2/v3:
- rebase on patch "drm/i915: move rps irq disable one level up" in the
patchset
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2014-11-19 20:30:04 +07:00
|
|
|
}
|
2013-07-05 04:35:28 +07:00
|
|
|
spin_unlock(&dev_priv->irq_lock);
|
2013-05-29 09:22:24 +07:00
|
|
|
}
|
|
|
|
|
2017-07-19 01:28:00 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 8)
|
2014-11-06 01:48:37 +07:00
|
|
|
return;
|
|
|
|
|
2019-03-05 22:09:13 +07:00
|
|
|
if (pm_iir & PM_VEBOX_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[VECS0]);
|
2013-05-29 09:22:31 +07:00
|
|
|
|
2019-03-05 22:09:13 +07:00
|
|
|
if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT)
|
|
|
|
DRM_DEBUG("Command parser error, pm_iir 0x%08x\n", pm_iir);
|
2013-05-29 09:22:24 +07:00
|
|
|
}
|
|
|
|
|
2019-07-13 17:00:10 +07:00
|
|
|
static void guc_irq_handler(struct intel_guc *guc, u16 iir)
|
2016-10-12 23:24:31 +07:00
|
|
|
{
|
2019-07-13 17:00:10 +07:00
|
|
|
if (iir & GUC_INTR_GUC2HOST)
|
|
|
|
intel_guc_to_host_event_handler(guc);
|
2019-05-28 01:36:08 +07:00
|
|
|
}
|
|
|
|
|
2017-08-19 01:36:51 +07:00
|
|
|
static void i9xx_pipestat_irq_reset(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
enum pipe pipe;
|
|
|
|
|
|
|
|
for_each_pipe(dev_priv, pipe) {
|
|
|
|
I915_WRITE(PIPESTAT(pipe),
|
|
|
|
PIPESTAT_INT_STATUS_MASK |
|
|
|
|
PIPE_FIFO_UNDERRUN_STATUS);
|
|
|
|
|
|
|
|
dev_priv->pipestat_irq_mask[pipe] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-19 01:36:59 +07:00
|
|
|
static void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv,
|
|
|
|
u32 iir, u32 pipe_stats[I915_MAX_PIPES])
|
2014-02-05 02:35:46 +07:00
|
|
|
{
|
|
|
|
int pipe;
|
|
|
|
|
2014-02-05 02:35:47 +07:00
|
|
|
spin_lock(&dev_priv->irq_lock);
|
2016-02-19 02:54:26 +07:00
|
|
|
|
|
|
|
if (!dev_priv->display_irqs_enabled) {
|
|
|
|
spin_unlock(&dev_priv->irq_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
drm/i915: Type safe register read/write
Make I915_READ and I915_WRITE more type safe by wrapping the register
offset in a struct. This should eliminate most of the fumbles we've had
with misplaced parens.
This only takes care of normal mmio registers. We could extend the idea
to other register types and define each with its own struct. That way
you wouldn't be able to accidentally pass the wrong thing to a specific
register access function.
The gpio_reg setup is probably the ugliest thing left. But I figure I'd
just leave it for now, and wait for some divine inspiration to strike
before making it nice.
As for the generated code, it's actually a bit better sometimes. Eg.
looking at i915_irq_handler(), we can see the following change:
lea 0x70024(%rdx,%rax,1),%r9d
mov $0x1,%edx
- movslq %r9d,%r9
- mov %r9,%rsi
- mov %r9,-0x58(%rbp)
- callq *0xd8(%rbx)
+ mov %r9d,%esi
+ mov %r9d,-0x48(%rbp)
callq *0xd8(%rbx)
So previously gcc thought the register offset might be signed and
decided to sign extend it, just in case. The rest appears to be
mostly just minor shuffling of instructions.
v2: i915_mmio_reg_{offset,equal,valid}() helpers added
s/_REG/_MMIO/ in the register defines
mo more switch statements left to worry about
ring_emit stuff got sorted in a prep patch
cmd parser, lrc context and w/a batch buildup also in prep patch
vgpu stuff cleaned up and moved to a prep patch
all other unrelated changes split out
v3: Rebased due to BXT DSI/BLC, MOCS, etc.
v4: Rebased due to churn, s/i915_mmio_reg_t/i915_reg_t/
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1447853606-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-11-18 20:33:26 +07:00
|
|
|
i915_reg_t reg;
|
2017-09-14 22:17:31 +07:00
|
|
|
u32 status_mask, enable_mask, iir_bit = 0;
|
2014-02-10 23:42:49 +07:00
|
|
|
|
2014-02-12 23:55:36 +07:00
|
|
|
/*
|
|
|
|
* PIPESTAT bits get signalled even when the interrupt is
|
|
|
|
* disabled with the mask bits, and some of the status bits do
|
|
|
|
* not generate interrupts at all (like the underrun bit). Hence
|
|
|
|
* we need to be careful that we only handle what we want to
|
|
|
|
* handle.
|
|
|
|
*/
|
2014-09-30 15:56:49 +07:00
|
|
|
|
|
|
|
/* fifo underruns are filterered in the underrun handler. */
|
2017-09-14 22:17:31 +07:00
|
|
|
status_mask = PIPE_FIFO_UNDERRUN_STATUS;
|
2014-02-12 23:55:36 +07:00
|
|
|
|
|
|
|
switch (pipe) {
|
|
|
|
case PIPE_A:
|
|
|
|
iir_bit = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
|
|
|
|
break;
|
|
|
|
case PIPE_B:
|
|
|
|
iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
|
|
|
|
break;
|
2014-04-09 17:28:49 +07:00
|
|
|
case PIPE_C:
|
|
|
|
iir_bit = I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
|
|
|
|
break;
|
2014-02-12 23:55:36 +07:00
|
|
|
}
|
|
|
|
if (iir & iir_bit)
|
2017-09-14 22:17:31 +07:00
|
|
|
status_mask |= dev_priv->pipestat_irq_mask[pipe];
|
2014-02-12 23:55:36 +07:00
|
|
|
|
2017-09-14 22:17:31 +07:00
|
|
|
if (!status_mask)
|
2014-02-10 23:42:49 +07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
reg = PIPESTAT(pipe);
|
2017-09-14 22:17:31 +07:00
|
|
|
pipe_stats[pipe] = I915_READ(reg) & status_mask;
|
|
|
|
enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
|
2014-02-05 02:35:46 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the PIPE*STAT regs before the IIR
|
2018-06-12 03:02:55 +07:00
|
|
|
*
|
|
|
|
* Toggle the enable bits to make sure we get an
|
|
|
|
* edge in the ISR pipe event bit if we don't clear
|
|
|
|
* all the enabled status bits. Otherwise the edge
|
|
|
|
* triggered IIR on i965/g4x wouldn't notice that
|
|
|
|
* an interrupt is still pending.
|
2014-02-05 02:35:46 +07:00
|
|
|
*/
|
2018-06-12 03:02:55 +07:00
|
|
|
if (pipe_stats[pipe]) {
|
|
|
|
I915_WRITE(reg, pipe_stats[pipe]);
|
|
|
|
I915_WRITE(reg, enable_mask);
|
|
|
|
}
|
2014-02-05 02:35:46 +07:00
|
|
|
}
|
2014-02-05 02:35:47 +07:00
|
|
|
spin_unlock(&dev_priv->irq_lock);
|
2016-04-14 01:19:55 +07:00
|
|
|
}
|
|
|
|
|
2017-08-19 01:36:59 +07:00
|
|
|
static void i8xx_pipestat_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u16 iir, u32 pipe_stats[I915_MAX_PIPES])
|
|
|
|
{
|
|
|
|
enum pipe pipe;
|
|
|
|
|
|
|
|
for_each_pipe(dev_priv, pipe) {
|
|
|
|
if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
|
i9xx_pipe_crc_irq_handler(dev_priv, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i915_pipestat_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 iir, u32 pipe_stats[I915_MAX_PIPES])
|
|
|
|
{
|
|
|
|
bool blc_event = false;
|
|
|
|
enum pipe pipe;
|
|
|
|
|
|
|
|
for_each_pipe(dev_priv, pipe) {
|
|
|
|
if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
|
|
|
|
blc_event = true;
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
|
i9xx_pipe_crc_irq_handler(dev_priv, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blc_event || (iir & I915_ASLE_INTERRUPT))
|
|
|
|
intel_opregion_asle_intr(dev_priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i965_pipestat_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 iir, u32 pipe_stats[I915_MAX_PIPES])
|
|
|
|
{
|
|
|
|
bool blc_event = false;
|
|
|
|
enum pipe pipe;
|
|
|
|
|
|
|
|
for_each_pipe(dev_priv, pipe) {
|
|
|
|
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
|
|
|
|
blc_event = true;
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
|
i9xx_pipe_crc_irq_handler(dev_priv, pipe);
|
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blc_event || (iir & I915_ASLE_INTERRUPT))
|
|
|
|
intel_opregion_asle_intr(dev_priv);
|
|
|
|
|
|
|
|
if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
|
|
|
|
gmbus_irq_handler(dev_priv);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv,
|
2016-04-14 01:19:55 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES])
|
|
|
|
{
|
|
|
|
enum pipe pipe;
|
2014-02-05 02:35:46 +07:00
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-07-21 00:57:51 +07:00
|
|
|
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
2014-02-05 02:35:46 +07:00
|
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
2016-05-06 20:48:28 +07:00
|
|
|
i9xx_pipe_crc_irq_handler(dev_priv, pipe);
|
2014-02-05 02:35:46 +07:00
|
|
|
|
2014-09-30 15:56:48 +07:00
|
|
|
if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
2014-02-05 02:35:46 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
|
2016-05-06 20:48:28 +07:00
|
|
|
gmbus_irq_handler(dev_priv);
|
2014-02-05 02:35:46 +07:00
|
|
|
}
|
|
|
|
|
2016-04-14 01:19:54 +07:00
|
|
|
static u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv)
|
2014-04-01 14:54:36 +07:00
|
|
|
{
|
2018-06-15 00:56:25 +07:00
|
|
|
u32 hotplug_status = 0, hotplug_status_mask;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (IS_G4X(dev_priv) ||
|
|
|
|
IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
|
|
|
|
hotplug_status_mask = HOTPLUG_INT_STATUS_G4X |
|
|
|
|
DP_AUX_CHANNEL_MASK_INT_STATUS_G4X;
|
|
|
|
else
|
|
|
|
hotplug_status_mask = HOTPLUG_INT_STATUS_I915;
|
2014-04-01 14:54:36 +07:00
|
|
|
|
2018-06-15 00:56:25 +07:00
|
|
|
/*
|
|
|
|
* We absolutely have to clear all the pending interrupt
|
|
|
|
* bits in PORT_HOTPLUG_STAT. Otherwise the ISR port
|
|
|
|
* interrupt bit won't have an edge, and the i965/g4x
|
|
|
|
* edge triggered IIR will not notice that an interrupt
|
|
|
|
* is still pending. We can't use PORT_HOTPLUG_EN to
|
|
|
|
* guarantee the edge as the act of toggling the enable
|
|
|
|
* bits can itself generate a new hotplug interrupt :(
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
u32 tmp = I915_READ(PORT_HOTPLUG_STAT) & hotplug_status_mask;
|
|
|
|
|
|
|
|
if (tmp == 0)
|
|
|
|
return hotplug_status;
|
|
|
|
|
|
|
|
hotplug_status |= tmp;
|
2016-04-14 01:19:54 +07:00
|
|
|
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
|
2018-06-15 00:56:25 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
WARN_ONCE(1,
|
|
|
|
"PORT_HOTPLUG_STAT did not clear (0x%08x)\n",
|
|
|
|
I915_READ(PORT_HOTPLUG_STAT));
|
2014-04-01 14:54:36 +07:00
|
|
|
|
2016-04-14 01:19:54 +07:00
|
|
|
return hotplug_status;
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void i9xx_hpd_irq_handler(struct drm_i915_private *dev_priv,
|
2016-04-14 01:19:54 +07:00
|
|
|
u32 hotplug_status)
|
|
|
|
{
|
|
|
|
u32 pin_mask = 0, long_mask = 0;
|
2014-04-01 14:54:36 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) ||
|
|
|
|
IS_CHERRYVIEW(dev_priv)) {
|
2015-05-27 19:03:39 +07:00
|
|
|
u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
|
2014-04-01 14:54:36 +07:00
|
|
|
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
if (hotplug_trigger) {
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
hotplug_trigger, hotplug_trigger,
|
|
|
|
hpd_status_g4x,
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
i9xx_port_hotplug_long_detect);
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
}
|
2015-05-27 19:03:40 +07:00
|
|
|
|
|
|
|
if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2015-05-27 19:03:39 +07:00
|
|
|
} else {
|
|
|
|
u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
|
2014-04-01 14:54:36 +07:00
|
|
|
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
if (hotplug_trigger) {
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
hotplug_trigger, hotplug_trigger,
|
|
|
|
hpd_status_i915,
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
i9xx_port_hotplug_long_detect);
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt
On GMCH plaforms we are now getting the following spew on aux
interrupts:
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064
Prevent it by not calling intel_get_hpd_pins() unless one of the HPD
interrupt bits are actually set.
I already fixed similar annoyance once with
4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV
but another source for it got added in
fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins
due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to
serve as the new unified piece of code. pch_get_hpd_pins() had the debug
print, and i9xx_get_hpd_pins() didn't.
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2015-08-29 02:59:08 +07:00
|
|
|
}
|
2014-06-16 22:10:58 +07:00
|
|
|
}
|
2014-04-01 14:54:36 +07:00
|
|
|
}
|
|
|
|
|
2012-10-02 20:10:55 +07:00
|
|
|
static irqreturn_t valleyview_irq_handler(int irq, void *arg)
|
2012-03-29 03:39:38 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2012-03-29 03:39:38 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2016-04-14 01:19:52 +07:00
|
|
|
do {
|
2016-04-14 01:19:53 +07:00
|
|
|
u32 iir, gt_iir, pm_iir;
|
2016-04-14 01:19:55 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES] = {};
|
2016-04-14 01:19:54 +07:00
|
|
|
u32 hotplug_status = 0;
|
2016-04-14 01:19:51 +07:00
|
|
|
u32 ier = 0;
|
2014-06-16 22:10:58 +07:00
|
|
|
|
2012-03-29 03:39:38 +07:00
|
|
|
gt_iir = I915_READ(GTIIR);
|
|
|
|
pm_iir = I915_READ(GEN6_PMIIR);
|
2014-06-16 22:10:58 +07:00
|
|
|
iir = I915_READ(VLV_IIR);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
|
|
|
if (gt_iir == 0 && pm_iir == 0 && iir == 0)
|
2016-04-14 01:19:52 +07:00
|
|
|
break;
|
2012-03-29 03:39:38 +07:00
|
|
|
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
|
2016-04-14 01:19:51 +07:00
|
|
|
/*
|
|
|
|
* Theory on interrupt generation, based on empirical evidence:
|
|
|
|
*
|
|
|
|
* x = ((VLV_IIR & VLV_IER) ||
|
|
|
|
* (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) &&
|
|
|
|
* (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE)));
|
|
|
|
*
|
|
|
|
* A CPU interrupt will only be raised when 'x' has a 0->1 edge.
|
|
|
|
* Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to
|
|
|
|
* guarantee the CPU interrupt will be raised again even if we
|
|
|
|
* don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR
|
|
|
|
* bits this time around.
|
|
|
|
*/
|
2016-04-14 01:19:50 +07:00
|
|
|
I915_WRITE(VLV_MASTER_IER, 0);
|
2016-04-14 01:19:51 +07:00
|
|
|
ier = I915_READ(VLV_IER);
|
|
|
|
I915_WRITE(VLV_IER, 0);
|
2016-04-14 01:19:50 +07:00
|
|
|
|
|
|
|
if (gt_iir)
|
|
|
|
I915_WRITE(GTIIR, gt_iir);
|
|
|
|
if (pm_iir)
|
|
|
|
I915_WRITE(GEN6_PMIIR, pm_iir);
|
|
|
|
|
2016-04-14 01:19:49 +07:00
|
|
|
if (iir & I915_DISPLAY_PORT_INTERRUPT)
|
2016-04-14 01:19:54 +07:00
|
|
|
hotplug_status = i9xx_hpd_irq_ack(dev_priv);
|
2016-04-14 01:19:49 +07:00
|
|
|
|
2014-06-16 22:10:58 +07:00
|
|
|
/* Call regardless, as some status bits might not be
|
|
|
|
* signalled in iir */
|
2017-08-19 01:36:59 +07:00
|
|
|
i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
|
2016-04-14 01:19:49 +07:00
|
|
|
|
2017-01-25 05:57:49 +07:00
|
|
|
if (iir & (I915_LPE_PIPE_A_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_B_INTERRUPT))
|
|
|
|
intel_lpe_audio_irq_handler(dev_priv);
|
|
|
|
|
2016-04-14 01:19:49 +07:00
|
|
|
/*
|
|
|
|
* VLV_IIR is single buffered, and reflects the level
|
|
|
|
* from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
|
|
|
|
*/
|
|
|
|
if (iir)
|
|
|
|
I915_WRITE(VLV_IIR, iir);
|
2016-04-14 01:19:50 +07:00
|
|
|
|
2016-04-14 01:19:51 +07:00
|
|
|
I915_WRITE(VLV_IER, ier);
|
2016-04-14 01:19:50 +07:00
|
|
|
I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
|
2016-04-14 01:19:54 +07:00
|
|
|
|
2016-04-14 01:19:56 +07:00
|
|
|
if (gt_iir)
|
2016-04-14 01:19:57 +07:00
|
|
|
snb_gt_irq_handler(dev_priv, gt_iir);
|
2016-04-14 01:19:56 +07:00
|
|
|
if (pm_iir)
|
|
|
|
gen6_rps_irq_handler(dev_priv, pm_iir);
|
|
|
|
|
2016-04-14 01:19:54 +07:00
|
|
|
if (hotplug_status)
|
2016-05-06 20:48:28 +07:00
|
|
|
i9xx_hpd_irq_handler(dev_priv, hotplug_status);
|
2016-04-14 01:19:55 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
valleyview_pipestat_irq_handler(dev_priv, pipe_stats);
|
2016-04-14 01:19:52 +07:00
|
|
|
} while (0);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2012-03-29 03:39:38 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-10 00:40:52 +07:00
|
|
|
static irqreturn_t cherryview_irq_handler(int irq, void *arg)
|
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2014-04-10 00:40:52 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2016-03-14 16:01:57 +07:00
|
|
|
do {
|
2016-04-14 01:19:53 +07:00
|
|
|
u32 master_ctl, iir;
|
2016-04-14 01:19:55 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES] = {};
|
2016-04-14 01:19:54 +07:00
|
|
|
u32 hotplug_status = 0;
|
2018-02-15 14:37:12 +07:00
|
|
|
u32 gt_iir[4];
|
2016-04-14 01:19:51 +07:00
|
|
|
u32 ier = 0;
|
|
|
|
|
2014-04-09 17:28:50 +07:00
|
|
|
master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
|
|
|
|
iir = I915_READ(VLV_IIR);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2014-04-09 17:28:50 +07:00
|
|
|
if (master_ctl == 0 && iir == 0)
|
|
|
|
break;
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2014-06-16 22:11:00 +07:00
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
|
2016-04-14 01:19:51 +07:00
|
|
|
/*
|
|
|
|
* Theory on interrupt generation, based on empirical evidence:
|
|
|
|
*
|
|
|
|
* x = ((VLV_IIR & VLV_IER) ||
|
|
|
|
* ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) &&
|
|
|
|
* (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL)));
|
|
|
|
*
|
|
|
|
* A CPU interrupt will only be raised when 'x' has a 0->1 edge.
|
|
|
|
* Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to
|
|
|
|
* guarantee the CPU interrupt will be raised again even if we
|
|
|
|
* don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL
|
|
|
|
* bits this time around.
|
|
|
|
*/
|
2014-04-09 17:28:50 +07:00
|
|
|
I915_WRITE(GEN8_MASTER_IRQ, 0);
|
2016-04-14 01:19:51 +07:00
|
|
|
ier = I915_READ(VLV_IER);
|
|
|
|
I915_WRITE(VLV_IER, 0);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2016-04-14 01:19:58 +07:00
|
|
|
gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2016-04-14 01:19:49 +07:00
|
|
|
if (iir & I915_DISPLAY_PORT_INTERRUPT)
|
2016-04-14 01:19:54 +07:00
|
|
|
hotplug_status = i9xx_hpd_irq_ack(dev_priv);
|
2016-04-14 01:19:49 +07:00
|
|
|
|
2014-06-16 22:11:00 +07:00
|
|
|
/* Call regardless, as some status bits might not be
|
|
|
|
* signalled in iir */
|
2017-08-19 01:36:59 +07:00
|
|
|
i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2017-01-25 05:57:49 +07:00
|
|
|
if (iir & (I915_LPE_PIPE_A_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_B_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_C_INTERRUPT))
|
|
|
|
intel_lpe_audio_irq_handler(dev_priv);
|
|
|
|
|
2016-04-14 01:19:49 +07:00
|
|
|
/*
|
|
|
|
* VLV_IIR is single buffered, and reflects the level
|
|
|
|
* from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
|
|
|
|
*/
|
|
|
|
if (iir)
|
|
|
|
I915_WRITE(VLV_IIR, iir);
|
|
|
|
|
2016-04-14 01:19:51 +07:00
|
|
|
I915_WRITE(VLV_IER, ier);
|
2016-04-14 01:19:47 +07:00
|
|
|
I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
|
2016-04-14 01:19:54 +07:00
|
|
|
|
2018-02-15 14:37:12 +07:00
|
|
|
gen8_gt_irq_handler(dev_priv, master_ctl, gt_iir);
|
2016-04-14 01:19:58 +07:00
|
|
|
|
2016-04-14 01:19:54 +07:00
|
|
|
if (hotplug_status)
|
2016-05-06 20:48:28 +07:00
|
|
|
i9xx_hpd_irq_handler(dev_priv, hotplug_status);
|
2016-04-14 01:19:55 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
valleyview_pipestat_irq_handler(dev_priv, pipe_stats);
|
2016-03-14 16:01:57 +07:00
|
|
|
} while (0);
|
2014-04-09 17:28:49 +07:00
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2014-04-10 00:40:52 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ibx_hpd_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
const u32 hpd[HPD_NUM_PINS])
|
|
|
|
{
|
|
|
|
u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
|
|
|
|
|
2015-11-25 21:47:22 +07:00
|
|
|
/*
|
|
|
|
* Somehow the PCH doesn't seem to really ack the interrupt to the CPU
|
|
|
|
* unless we touch the hotplug register, even if hotplug_trigger is
|
|
|
|
* zero. Not acking leads to "The master control interrupt lied (SDE)!"
|
|
|
|
* errors.
|
|
|
|
*/
|
2015-08-28 03:56:10 +07:00
|
|
|
dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
|
2015-11-25 21:47:22 +07:00
|
|
|
if (!hotplug_trigger) {
|
|
|
|
u32 mask = PORTA_HOTPLUG_STATUS_MASK |
|
|
|
|
PORTD_HOTPLUG_STATUS_MASK |
|
|
|
|
PORTC_HOTPLUG_STATUS_MASK |
|
|
|
|
PORTB_HOTPLUG_STATUS_MASK;
|
|
|
|
dig_hotplug_reg &= ~mask;
|
|
|
|
}
|
|
|
|
|
2015-08-28 03:56:10 +07:00
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
|
2015-11-25 21:47:22 +07:00
|
|
|
if (!hotplug_trigger)
|
|
|
|
return;
|
2015-08-28 03:56:10 +07:00
|
|
|
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
dig_hotplug_reg, hpd,
|
|
|
|
pch_port_hotplug_long_detect);
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
2015-08-28 03:56:10 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ibx_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
|
2011-01-05 06:09:39 +07:00
|
|
|
{
|
2011-02-08 03:26:52 +07:00
|
|
|
int pipe;
|
2013-04-16 18:36:54 +07:00
|
|
|
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
|
2014-06-18 08:29:35 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
ibx_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ibx);
|
2013-06-27 22:52:14 +07:00
|
|
|
|
2013-04-17 21:48:48 +07:00
|
|
|
if (pch_iir & SDE_AUDIO_POWER_MASK) {
|
|
|
|
int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
|
|
|
|
SDE_AUDIO_POWER_SHIFT);
|
2011-01-05 06:09:39 +07:00
|
|
|
DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
|
2013-04-17 21:48:48 +07:00
|
|
|
port_name(port));
|
|
|
|
}
|
2011-01-05 06:09:39 +07:00
|
|
|
|
2012-12-01 19:53:47 +07:00
|
|
|
if (pch_iir & SDE_AUX_MASK)
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2012-12-01 19:53:47 +07:00
|
|
|
|
2011-01-05 06:09:39 +07:00
|
|
|
if (pch_iir & SDE_GMBUS)
|
2016-05-06 20:48:28 +07:00
|
|
|
gmbus_irq_handler(dev_priv);
|
2011-01-05 06:09:39 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_AUDIO_HDCP_MASK)
|
|
|
|
DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & SDE_AUDIO_TRANS_MASK)
|
|
|
|
DRM_DEBUG_DRIVER("PCH transcoder audio interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & SDE_POISON)
|
|
|
|
DRM_ERROR("PCH poison interrupt\n");
|
|
|
|
|
2011-02-08 03:26:52 +07:00
|
|
|
if (pch_iir & SDE_FDI_MASK)
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
2011-02-08 03:26:52 +07:00
|
|
|
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
|
|
|
|
pipe_name(pipe),
|
|
|
|
I915_READ(FDI_RX_IIR(pipe)));
|
2011-01-05 06:09:39 +07:00
|
|
|
|
|
|
|
if (pch_iir & (SDE_TRANSB_CRC_DONE | SDE_TRANSA_CRC_DONE))
|
|
|
|
DRM_DEBUG_DRIVER("PCH transcoder CRC done interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
|
|
|
|
DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & SDE_TRANSA_FIFO_UNDER)
|
2017-07-18 01:14:03 +07:00
|
|
|
intel_pch_fifo_underrun_irq_handler(dev_priv, PIPE_A);
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_TRANSB_FIFO_UNDER)
|
2017-07-18 01:14:03 +07:00
|
|
|
intel_pch_fifo_underrun_irq_handler(dev_priv, PIPE_B);
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ivb_err_int_handler(struct drm_i915_private *dev_priv)
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
{
|
|
|
|
u32 err_int = I915_READ(GEN7_ERR_INT);
|
2013-10-17 03:55:52 +07:00
|
|
|
enum pipe pipe;
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
2013-04-13 03:57:58 +07:00
|
|
|
if (err_int & ERR_INT_POISON)
|
|
|
|
DRM_ERROR("Poison interrupt\n");
|
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2014-09-30 15:56:48 +07:00
|
|
|
if (err_int & ERR_INT_FIFO_UNDERRUN(pipe))
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
2013-10-16 00:55:27 +07:00
|
|
|
|
2013-10-17 03:55:52 +07:00
|
|
|
if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
|
2016-05-06 20:48:28 +07:00
|
|
|
if (IS_IVYBRIDGE(dev_priv))
|
|
|
|
ivb_pipe_crc_irq_handler(dev_priv, pipe);
|
2013-10-17 03:55:52 +07:00
|
|
|
else
|
2016-05-06 20:48:28 +07:00
|
|
|
hsw_pipe_crc_irq_handler(dev_priv, pipe);
|
2013-10-17 03:55:52 +07:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 00:55:27 +07:00
|
|
|
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
I915_WRITE(GEN7_ERR_INT, err_int);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void cpt_serr_int_handler(struct drm_i915_private *dev_priv)
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
{
|
|
|
|
u32 serr_int = I915_READ(SERR_INT);
|
2017-10-10 17:17:06 +07:00
|
|
|
enum pipe pipe;
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
2013-04-13 03:57:58 +07:00
|
|
|
if (serr_int & SERR_INT_POISON)
|
|
|
|
DRM_ERROR("PCH poison interrupt\n");
|
|
|
|
|
2017-10-10 17:17:06 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
|
|
|
if (serr_int & SERR_INT_TRANS_FIFO_UNDERRUN(pipe))
|
|
|
|
intel_pch_fifo_underrun_irq_handler(dev_priv, pipe);
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
|
|
|
I915_WRITE(SERR_INT, serr_int);
|
2011-01-05 06:09:39 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void cpt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
|
2012-06-07 02:45:44 +07:00
|
|
|
{
|
|
|
|
int pipe;
|
2015-08-28 03:56:02 +07:00
|
|
|
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
|
2014-06-18 08:29:35 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
ibx_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_cpt);
|
2013-06-27 22:52:14 +07:00
|
|
|
|
2013-04-17 21:48:48 +07:00
|
|
|
if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
|
|
|
|
int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
|
|
|
|
SDE_AUDIO_POWER_SHIFT_CPT);
|
|
|
|
DRM_DEBUG_DRIVER("PCH audio power change on port %c\n",
|
|
|
|
port_name(port));
|
|
|
|
}
|
2012-06-07 02:45:44 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_AUX_MASK_CPT)
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2012-06-07 02:45:44 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_GMBUS_CPT)
|
2016-05-06 20:48:28 +07:00
|
|
|
gmbus_irq_handler(dev_priv);
|
2012-06-07 02:45:44 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_AUDIO_CP_REQ_CPT)
|
|
|
|
DRM_DEBUG_DRIVER("Audio CP request interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & SDE_AUDIO_CP_CHG_CPT)
|
|
|
|
DRM_DEBUG_DRIVER("Audio CP change interrupt\n");
|
|
|
|
|
|
|
|
if (pch_iir & SDE_FDI_MASK_CPT)
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
2012-06-07 02:45:44 +07:00
|
|
|
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
|
|
|
|
pipe_name(pipe),
|
|
|
|
I915_READ(FDI_RX_IIR(pipe)));
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_ERROR_CPT)
|
2016-05-06 20:48:28 +07:00
|
|
|
cpt_serr_int_handler(dev_priv);
|
2012-06-07 02:45:44 +07:00
|
|
|
}
|
|
|
|
|
2019-06-15 07:42:10 +07:00
|
|
|
static void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir,
|
|
|
|
const u32 *pins)
|
2018-06-27 03:52:23 +07:00
|
|
|
{
|
|
|
|
u32 ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_ICP;
|
|
|
|
u32 tc_hotplug_trigger = pch_iir & SDE_TC_MASK_ICP;
|
|
|
|
u32 pin_mask = 0, long_mask = 0;
|
|
|
|
|
|
|
|
if (ddi_hotplug_trigger) {
|
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(SHOTPLUG_CTL_DDI);
|
|
|
|
I915_WRITE(SHOTPLUG_CTL_DDI, dig_hotplug_reg);
|
|
|
|
|
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
ddi_hotplug_trigger,
|
2019-06-15 07:42:10 +07:00
|
|
|
dig_hotplug_reg, pins,
|
2018-06-27 03:52:23 +07:00
|
|
|
icp_ddi_port_hotplug_long_detect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tc_hotplug_trigger) {
|
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(SHOTPLUG_CTL_TC);
|
|
|
|
I915_WRITE(SHOTPLUG_CTL_TC, dig_hotplug_reg);
|
|
|
|
|
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
tc_hotplug_trigger,
|
2019-06-15 07:42:10 +07:00
|
|
|
dig_hotplug_reg, pins,
|
2018-06-27 03:52:23 +07:00
|
|
|
icp_tc_port_hotplug_long_detect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pin_mask)
|
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
|
|
|
|
|
|
|
if (pch_iir & SDE_GMBUS_ICP)
|
|
|
|
gmbus_irq_handler(dev_priv);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void spt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
|
2015-08-28 03:56:02 +07:00
|
|
|
{
|
|
|
|
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT &
|
|
|
|
~SDE_PORTE_HOTPLUG_SPT;
|
|
|
|
u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT;
|
|
|
|
u32 pin_mask = 0, long_mask = 0;
|
|
|
|
|
|
|
|
if (hotplug_trigger) {
|
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
|
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
|
|
|
|
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
hotplug_trigger, dig_hotplug_reg, hpd_spt,
|
2015-08-28 03:56:07 +07:00
|
|
|
spt_port_hotplug_long_detect);
|
2015-08-28 03:56:02 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hotplug2_trigger) {
|
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2);
|
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG2, dig_hotplug_reg);
|
|
|
|
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
|
|
|
|
hotplug2_trigger, dig_hotplug_reg, hpd_spt,
|
2015-08-28 03:56:02 +07:00
|
|
|
spt_port_hotplug2_long_detect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pin_mask)
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
2015-08-28 03:56:02 +07:00
|
|
|
|
|
|
|
if (pch_iir & SDE_GMBUS_CPT)
|
2016-05-06 20:48:28 +07:00
|
|
|
gmbus_irq_handler(dev_priv);
|
2015-08-28 03:56:02 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ilk_hpd_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
const u32 hpd[HPD_NUM_PINS])
|
|
|
|
{
|
|
|
|
u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
|
|
|
|
I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg);
|
|
|
|
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
dig_hotplug_reg, hpd,
|
|
|
|
ilk_port_hotplug_long_detect);
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
2015-08-28 03:56:10 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ilk_display_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 de_iir)
|
2013-07-13 02:35:10 +07:00
|
|
|
{
|
2013-10-21 23:04:36 +07:00
|
|
|
enum pipe pipe;
|
2015-08-28 03:56:03 +07:00
|
|
|
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG;
|
|
|
|
|
2015-08-28 03:56:10 +07:00
|
|
|
if (hotplug_trigger)
|
2016-05-06 20:48:28 +07:00
|
|
|
ilk_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ilk);
|
2013-07-13 02:35:10 +07:00
|
|
|
|
|
|
|
if (de_iir & DE_AUX_CHANNEL_A)
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2013-07-13 02:35:10 +07:00
|
|
|
|
|
|
|
if (de_iir & DE_GSE)
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_opregion_asle_intr(dev_priv);
|
2013-07-13 02:35:10 +07:00
|
|
|
|
|
|
|
if (de_iir & DE_POISON)
|
|
|
|
DRM_ERROR("Poison interrupt\n");
|
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-07-21 00:57:51 +07:00
|
|
|
if (de_iir & DE_PIPE_VBLANK(pipe))
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
2013-10-17 03:55:48 +07:00
|
|
|
|
2013-10-21 23:04:36 +07:00
|
|
|
if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
|
2014-09-30 15:56:48 +07:00
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
2013-10-17 03:55:48 +07:00
|
|
|
|
2013-10-21 23:04:36 +07:00
|
|
|
if (de_iir & DE_PIPE_CRC_DONE(pipe))
|
2016-05-06 20:48:28 +07:00
|
|
|
i9xx_pipe_crc_irq_handler(dev_priv, pipe);
|
2013-07-13 02:35:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check event from PCH */
|
|
|
|
if (de_iir & DE_PCH_EVENT) {
|
|
|
|
u32 pch_iir = I915_READ(SDEIIR);
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (HAS_PCH_CPT(dev_priv))
|
|
|
|
cpt_irq_handler(dev_priv, pch_iir);
|
2013-07-13 02:35:10 +07:00
|
|
|
else
|
2016-05-06 20:48:28 +07:00
|
|
|
ibx_irq_handler(dev_priv, pch_iir);
|
2013-07-13 02:35:10 +07:00
|
|
|
|
|
|
|
/* should clear PCH hotplug event before clear CPU irq */
|
|
|
|
I915_WRITE(SDEIIR, pch_iir);
|
|
|
|
}
|
|
|
|
|
drm/i915: replace IS_GEN<N> with IS_GEN(..., N)
Define IS_GEN() similarly to our IS_GEN_RANGE(). but use gen instead of
gen_mask to do the comparison. Now callers can pass then gen as a parameter,
so we don't require one macro for each gen.
The following spatch was used to convert the users of these macros:
@@
expression e;
@@
(
- IS_GEN2(e)
+ IS_GEN(e, 2)
|
- IS_GEN3(e)
+ IS_GEN(e, 3)
|
- IS_GEN4(e)
+ IS_GEN(e, 4)
|
- IS_GEN5(e)
+ IS_GEN(e, 5)
|
- IS_GEN6(e)
+ IS_GEN(e, 6)
|
- IS_GEN7(e)
+ IS_GEN(e, 7)
|
- IS_GEN8(e)
+ IS_GEN(e, 8)
|
- IS_GEN9(e)
+ IS_GEN(e, 9)
|
- IS_GEN10(e)
+ IS_GEN(e, 10)
|
- IS_GEN11(e)
+ IS_GEN(e, 11)
)
v2: use IS_GEN rather than GT_GEN and compare to info.gen rather than
using the bitmask
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181212181044.15886-2-lucas.demarchi@intel.com
2018-12-13 01:10:43 +07:00
|
|
|
if (IS_GEN(dev_priv, 5) && de_iir & DE_PCU_EVENT)
|
2016-05-06 20:48:28 +07:00
|
|
|
ironlake_rps_change_irq_handler(dev_priv);
|
2013-07-13 02:35:10 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ivb_display_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 de_iir)
|
2013-07-13 02:35:11 +07:00
|
|
|
{
|
2014-03-04 00:31:46 +07:00
|
|
|
enum pipe pipe;
|
2015-08-28 03:56:04 +07:00
|
|
|
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB;
|
|
|
|
|
2015-08-28 03:56:10 +07:00
|
|
|
if (hotplug_trigger)
|
2016-05-06 20:48:28 +07:00
|
|
|
ilk_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ivb);
|
2013-07-13 02:35:11 +07:00
|
|
|
|
|
|
|
if (de_iir & DE_ERR_INT_IVB)
|
2016-05-06 20:48:28 +07:00
|
|
|
ivb_err_int_handler(dev_priv);
|
2013-07-13 02:35:11 +07:00
|
|
|
|
2018-04-05 08:37:17 +07:00
|
|
|
if (de_iir & DE_EDP_PSR_INT_HSW) {
|
|
|
|
u32 psr_iir = I915_READ(EDP_PSR_IIR);
|
|
|
|
|
|
|
|
intel_psr_irq_handler(dev_priv, psr_iir);
|
|
|
|
I915_WRITE(EDP_PSR_IIR, psr_iir);
|
|
|
|
}
|
2018-04-06 05:00:23 +07:00
|
|
|
|
2013-07-13 02:35:11 +07:00
|
|
|
if (de_iir & DE_AUX_CHANNEL_A_IVB)
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2013-07-13 02:35:11 +07:00
|
|
|
|
|
|
|
if (de_iir & DE_GSE_IVB)
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_opregion_asle_intr(dev_priv);
|
2013-07-13 02:35:11 +07:00
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-07-21 00:57:51 +07:00
|
|
|
if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
2013-07-13 02:35:11 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check event from PCH */
|
2016-05-06 20:48:28 +07:00
|
|
|
if (!HAS_PCH_NOP(dev_priv) && (de_iir & DE_PCH_EVENT_IVB)) {
|
2013-07-13 02:35:11 +07:00
|
|
|
u32 pch_iir = I915_READ(SDEIIR);
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
cpt_irq_handler(dev_priv, pch_iir);
|
2013-07-13 02:35:11 +07:00
|
|
|
|
|
|
|
/* clear PCH hotplug event before clear CPU irq */
|
|
|
|
I915_WRITE(SDEIIR, pch_iir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-16 22:10:57 +07:00
|
|
|
/*
|
|
|
|
* To handle irqs with the minimum potential races with fresh interrupts, we:
|
|
|
|
* 1 - Disable Master Interrupt Control.
|
|
|
|
* 2 - Find the source(s) of the interrupt.
|
|
|
|
* 3 - Clear the Interrupt Identity bits (IIR).
|
|
|
|
* 4 - Process the interrupt(s) that had bits set in the IIRs.
|
|
|
|
* 5 - Re-enable Master Interrupt Control.
|
|
|
|
*/
|
2013-07-13 05:56:30 +07:00
|
|
|
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
2011-04-07 02:13:38 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2013-07-13 05:56:30 +07:00
|
|
|
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
|
2012-05-10 03:45:44 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2011-04-07 02:13:38 +07:00
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2011-04-07 02:13:38 +07:00
|
|
|
/* disable master interrupt before clearing iir */
|
|
|
|
de_ier = I915_READ(DEIER);
|
|
|
|
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
|
|
|
|
|
drm/i915: also disable south interrupts when handling them
From the docs:
"IIR can queue up to two interrupt events. When the IIR is cleared,
it will set itself again after one clock if a second event was
stored."
"Only the rising edge of the PCH Display interrupt will cause the
North Display IIR (DEIIR) PCH Display Interrupt even bit to be set,
so all PCH Display Interrupts, including back to back interrupts,
must be cleared before a new PCH Display interrupt can cause DEIIR
to be set".
The current code works fine because we don't get many interrupts, but
if we enable the PCH FIFO underrun interrupts we'll start getting so
many interrupts that at some point new PCH interrupts won't cause
DEIIR to be set.
The initial implementation I tried was to turn the code that checks
SDEIIR into a loop, but we can still get interrupts even after the
loop is done (and before the irq handler finishes), so we have to
either disable the interrupts or mask them. In the end I concluded
that just disabling the PCH interrupts is enough, you don't even need
the loop, so this is what this patch implements. I've tested it and it
passes the 2 "PCH FIFO underrun interrupt storms" I can reproduce:
the "ironlake_crtc_disable" case and the "wrong watermarks" case.
In other words, here's how to reproduce the problem fixed by this
patch:
1 - Enable PCH FIFO underrun interrupts (SERR_INT on SNB+)
2 - Boot the machine
3 - While booting we'll get tons of PCH FIFO underrun interrupts
4 - Plug a new monitor
5 - Run xrandr, notice it won't detect the new monitor
6 - Read SDEIIR and notice it's not 0 while DEIIR is 0
Q: Can't we just clear DEIIR before SDEIIR?
A: It doesn't work. SDEIIR has to be completely cleared (including the
interrupts stored on its back queue) before it can flip DEIIR's bit to
1 again, and even while you're clearing it you'll be getting more and
more interrupts.
Q: Why does it work by just disabling+enabling the south interrupts?
A: Because when we re-enable them, if there's something on the SDEIIR
register (maybe an interrupt stored on the queue), the re-enabling
will make DEIIR's bit flip to 1, and since we'll already have
interrupts enabled we'll get another interrupt, then run our irq
handler again to process the "back" interrupts.
v2: Even bigger commit message, added code comments.
Note that this fixes missed dp aux irqs which have been reported for
3.9-rc1. This regression has been introduced by switching to
irq-driven dp aux transactions with
commit 9ee32fea5fe810ec06af3a15e4c65478de56d4f5
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Sat Dec 1 13:53:48 2012 +0100
drm/i915: irq-drive the dp aux communication
References: http://www.mail-archive.com/intel-gfx@lists.freedesktop.org/msg18588.html
References: https://lkml.org/lkml/2013/2/26/769
Tested-by: Imre Deak <imre.deak@intel.com>
Reported-by: Sedat Dilek <sedat.dilek@gmail.com>
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
[danvet: Pimp commit message with references for the dp aux irq
timeout regression this fixes.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-02-23 03:05:28 +07:00
|
|
|
/* Disable south interrupts. We'll only write to SDEIIR once, so further
|
|
|
|
* interrupts will will be stored on its back queue, and then we'll be
|
|
|
|
* able to process them after we restore SDEIER (as soon as we restore
|
|
|
|
* it, we'll get an interrupt if SDEIIR still has something to process
|
|
|
|
* due to its back queue). */
|
2016-05-06 20:48:28 +07:00
|
|
|
if (!HAS_PCH_NOP(dev_priv)) {
|
2013-04-06 03:12:41 +07:00
|
|
|
sde_ier = I915_READ(SDEIER);
|
|
|
|
I915_WRITE(SDEIER, 0);
|
|
|
|
}
|
drm/i915: also disable south interrupts when handling them
From the docs:
"IIR can queue up to two interrupt events. When the IIR is cleared,
it will set itself again after one clock if a second event was
stored."
"Only the rising edge of the PCH Display interrupt will cause the
North Display IIR (DEIIR) PCH Display Interrupt even bit to be set,
so all PCH Display Interrupts, including back to back interrupts,
must be cleared before a new PCH Display interrupt can cause DEIIR
to be set".
The current code works fine because we don't get many interrupts, but
if we enable the PCH FIFO underrun interrupts we'll start getting so
many interrupts that at some point new PCH interrupts won't cause
DEIIR to be set.
The initial implementation I tried was to turn the code that checks
SDEIIR into a loop, but we can still get interrupts even after the
loop is done (and before the irq handler finishes), so we have to
either disable the interrupts or mask them. In the end I concluded
that just disabling the PCH interrupts is enough, you don't even need
the loop, so this is what this patch implements. I've tested it and it
passes the 2 "PCH FIFO underrun interrupt storms" I can reproduce:
the "ironlake_crtc_disable" case and the "wrong watermarks" case.
In other words, here's how to reproduce the problem fixed by this
patch:
1 - Enable PCH FIFO underrun interrupts (SERR_INT on SNB+)
2 - Boot the machine
3 - While booting we'll get tons of PCH FIFO underrun interrupts
4 - Plug a new monitor
5 - Run xrandr, notice it won't detect the new monitor
6 - Read SDEIIR and notice it's not 0 while DEIIR is 0
Q: Can't we just clear DEIIR before SDEIIR?
A: It doesn't work. SDEIIR has to be completely cleared (including the
interrupts stored on its back queue) before it can flip DEIIR's bit to
1 again, and even while you're clearing it you'll be getting more and
more interrupts.
Q: Why does it work by just disabling+enabling the south interrupts?
A: Because when we re-enable them, if there's something on the SDEIIR
register (maybe an interrupt stored on the queue), the re-enabling
will make DEIIR's bit flip to 1, and since we'll already have
interrupts enabled we'll get another interrupt, then run our irq
handler again to process the "back" interrupts.
v2: Even bigger commit message, added code comments.
Note that this fixes missed dp aux irqs which have been reported for
3.9-rc1. This regression has been introduced by switching to
irq-driven dp aux transactions with
commit 9ee32fea5fe810ec06af3a15e4c65478de56d4f5
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Sat Dec 1 13:53:48 2012 +0100
drm/i915: irq-drive the dp aux communication
References: http://www.mail-archive.com/intel-gfx@lists.freedesktop.org/msg18588.html
References: https://lkml.org/lkml/2013/2/26/769
Tested-by: Imre Deak <imre.deak@intel.com>
Reported-by: Sedat Dilek <sedat.dilek@gmail.com>
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
[danvet: Pimp commit message with references for the dp aux irq
timeout regression this fixes.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-02-23 03:05:28 +07:00
|
|
|
|
2014-06-16 22:10:57 +07:00
|
|
|
/* Find, clear, then process each source of interrupt */
|
|
|
|
|
2011-04-07 02:13:38 +07:00
|
|
|
gt_iir = I915_READ(GTIIR);
|
2012-05-10 03:45:44 +07:00
|
|
|
if (gt_iir) {
|
2014-06-16 22:10:57 +07:00
|
|
|
I915_WRITE(GTIIR, gt_iir);
|
|
|
|
ret = IRQ_HANDLED;
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 6)
|
2016-04-14 01:19:57 +07:00
|
|
|
snb_gt_irq_handler(dev_priv, gt_iir);
|
2013-07-20 04:57:55 +07:00
|
|
|
else
|
2016-04-14 01:19:57 +07:00
|
|
|
ilk_gt_irq_handler(dev_priv, gt_iir);
|
2011-04-07 02:13:38 +07:00
|
|
|
}
|
|
|
|
|
2012-05-10 03:45:44 +07:00
|
|
|
de_iir = I915_READ(DEIIR);
|
|
|
|
if (de_iir) {
|
2014-06-16 22:10:57 +07:00
|
|
|
I915_WRITE(DEIIR, de_iir);
|
|
|
|
ret = IRQ_HANDLED;
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 7)
|
|
|
|
ivb_display_irq_handler(dev_priv, de_iir);
|
2013-07-13 05:56:30 +07:00
|
|
|
else
|
2016-05-06 20:48:28 +07:00
|
|
|
ilk_display_irq_handler(dev_priv, de_iir);
|
2011-04-07 02:13:38 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 6) {
|
2013-07-13 05:56:30 +07:00
|
|
|
u32 pm_iir = I915_READ(GEN6_PMIIR);
|
|
|
|
if (pm_iir) {
|
|
|
|
I915_WRITE(GEN6_PMIIR, pm_iir);
|
|
|
|
ret = IRQ_HANDLED;
|
2014-06-16 22:10:57 +07:00
|
|
|
gen6_rps_irq_handler(dev_priv, pm_iir);
|
2013-07-13 05:56:30 +07:00
|
|
|
}
|
2012-05-10 03:45:44 +07:00
|
|
|
}
|
2011-04-07 02:13:38 +07:00
|
|
|
|
|
|
|
I915_WRITE(DEIER, de_ier);
|
2018-06-29 03:12:03 +07:00
|
|
|
if (!HAS_PCH_NOP(dev_priv))
|
2013-04-06 03:12:41 +07:00
|
|
|
I915_WRITE(SDEIER, sde_ier);
|
2011-04-07 02:13:38 +07:00
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2011-04-07 02:13:38 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void bxt_hpd_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
const u32 hpd[HPD_NUM_PINS])
|
2014-08-22 19:10:41 +07:00
|
|
|
{
|
2015-08-28 03:56:09 +07:00
|
|
|
u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
|
2014-08-22 19:10:41 +07:00
|
|
|
|
2015-08-28 03:56:11 +07:00
|
|
|
dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
|
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
|
2014-08-22 19:10:41 +07:00
|
|
|
|
2018-01-30 06:22:21 +07:00
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger,
|
2015-08-28 03:56:10 +07:00
|
|
|
dig_hotplug_reg, hpd,
|
2015-08-28 03:56:09 +07:00
|
|
|
bxt_port_hotplug_long_detect);
|
2015-08-28 03:56:10 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
2014-08-22 19:10:41 +07:00
|
|
|
}
|
|
|
|
|
2018-06-16 07:05:29 +07:00
|
|
|
static void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir)
|
|
|
|
{
|
|
|
|
u32 pin_mask = 0, long_mask = 0;
|
2018-06-16 07:05:30 +07:00
|
|
|
u32 trigger_tc = iir & GEN11_DE_TC_HOTPLUG_MASK;
|
|
|
|
u32 trigger_tbt = iir & GEN11_DE_TBT_HOTPLUG_MASK;
|
2018-06-16 07:05:29 +07:00
|
|
|
|
|
|
|
if (trigger_tc) {
|
2018-06-16 07:05:30 +07:00
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
2018-06-16 07:05:29 +07:00
|
|
|
dig_hotplug_reg = I915_READ(GEN11_TC_HOTPLUG_CTL);
|
|
|
|
I915_WRITE(GEN11_TC_HOTPLUG_CTL, dig_hotplug_reg);
|
|
|
|
|
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, trigger_tc,
|
2018-06-16 07:05:30 +07:00
|
|
|
dig_hotplug_reg, hpd_gen11,
|
|
|
|
gen11_port_hotplug_long_detect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trigger_tbt) {
|
|
|
|
u32 dig_hotplug_reg;
|
|
|
|
|
|
|
|
dig_hotplug_reg = I915_READ(GEN11_TBT_HOTPLUG_CTL);
|
|
|
|
I915_WRITE(GEN11_TBT_HOTPLUG_CTL, dig_hotplug_reg);
|
|
|
|
|
|
|
|
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, trigger_tbt,
|
|
|
|
dig_hotplug_reg, hpd_gen11,
|
2018-06-16 07:05:29 +07:00
|
|
|
gen11_port_hotplug_long_detect);
|
2018-06-16 07:05:30 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pin_mask)
|
2018-06-16 07:05:29 +07:00
|
|
|
intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
|
2018-06-16 07:05:30 +07:00
|
|
|
else
|
2018-06-16 07:05:29 +07:00
|
|
|
DRM_ERROR("Unexpected DE HPD interrupt 0x%08x\n", iir);
|
|
|
|
}
|
|
|
|
|
2019-02-26 07:49:00 +07:00
|
|
|
static u32 gen8_de_port_aux_mask(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 mask = GEN8_AUX_CHANNEL_A;
|
|
|
|
|
|
|
|
if (INTEL_GEN(dev_priv) >= 9)
|
|
|
|
mask |= GEN9_AUX_CHANNEL_B |
|
|
|
|
GEN9_AUX_CHANNEL_C |
|
|
|
|
GEN9_AUX_CHANNEL_D;
|
|
|
|
|
|
|
|
if (IS_CNL_WITH_PORT_F(dev_priv))
|
|
|
|
mask |= CNL_AUX_CHANNEL_F;
|
|
|
|
|
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
mask |= ICL_AUX_CHANNEL_E |
|
|
|
|
CNL_AUX_CHANNEL_F;
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2019-06-27 01:03:40 +07:00
|
|
|
static u32 gen8_de_pipe_fault_mask(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (INTEL_GEN(dev_priv) >= 9)
|
|
|
|
return GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
|
|
|
|
else
|
|
|
|
return GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:04:07 +07:00
|
|
|
static irqreturn_t
|
|
|
|
gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2016-01-12 23:04:07 +07:00
|
|
|
u32 iir;
|
2013-11-07 17:05:40 +07:00
|
|
|
enum pipe pipe;
|
2014-11-14 00:51:48 +07:00
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
if (master_ctl & GEN8_DE_MISC_IRQ) {
|
2016-01-12 23:04:06 +07:00
|
|
|
iir = I915_READ(GEN8_DE_MISC_IIR);
|
|
|
|
if (iir) {
|
2018-04-04 04:24:18 +07:00
|
|
|
bool found = false;
|
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
I915_WRITE(GEN8_DE_MISC_IIR, iir);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
ret = IRQ_HANDLED;
|
2018-04-04 04:24:18 +07:00
|
|
|
|
|
|
|
if (iir & GEN8_DE_MISC_GSE) {
|
2016-05-06 20:48:28 +07:00
|
|
|
intel_opregion_asle_intr(dev_priv);
|
2018-04-04 04:24:18 +07:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iir & GEN8_DE_EDP_PSR) {
|
2018-04-05 08:37:17 +07:00
|
|
|
u32 psr_iir = I915_READ(EDP_PSR_IIR);
|
|
|
|
|
|
|
|
intel_psr_irq_handler(dev_priv, psr_iir);
|
|
|
|
I915_WRITE(EDP_PSR_IIR, psr_iir);
|
2018-04-04 04:24:18 +07:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
2014-06-16 22:10:59 +07:00
|
|
|
DRM_ERROR("Unexpected DE Misc interrupt\n");
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
2014-06-16 22:10:59 +07:00
|
|
|
else
|
|
|
|
DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2018-06-16 07:05:29 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 11 && (master_ctl & GEN11_DE_HPD_IRQ)) {
|
|
|
|
iir = I915_READ(GEN11_DE_HPD_IIR);
|
|
|
|
if (iir) {
|
|
|
|
I915_WRITE(GEN11_DE_HPD_IIR, iir);
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
gen11_hpd_irq_handler(dev_priv, iir);
|
|
|
|
} else {
|
|
|
|
DRM_ERROR("The master control interrupt lied, (DE HPD)!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-07 20:49:55 +07:00
|
|
|
if (master_ctl & GEN8_DE_PORT_IRQ) {
|
2016-01-12 23:04:06 +07:00
|
|
|
iir = I915_READ(GEN8_DE_PORT_IIR);
|
|
|
|
if (iir) {
|
|
|
|
u32 tmp_mask;
|
2014-08-22 19:10:41 +07:00
|
|
|
bool found = false;
|
2015-08-28 03:56:09 +07:00
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
I915_WRITE(GEN8_DE_PORT_IIR, iir);
|
2013-11-07 20:49:55 +07:00
|
|
|
ret = IRQ_HANDLED;
|
2014-11-14 00:51:48 +07:00
|
|
|
|
2019-02-26 07:49:00 +07:00
|
|
|
if (iir & gen8_de_port_aux_mask(dev_priv)) {
|
2016-05-06 20:48:28 +07:00
|
|
|
dp_aux_irq_handler(dev_priv);
|
2014-08-22 19:10:41 +07:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:23:49 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv)) {
|
2016-01-12 23:04:06 +07:00
|
|
|
tmp_mask = iir & BXT_DE_PORT_HOTPLUG_MASK;
|
|
|
|
if (tmp_mask) {
|
2016-05-06 20:48:28 +07:00
|
|
|
bxt_hpd_irq_handler(dev_priv, tmp_mask,
|
|
|
|
hpd_bxt);
|
2016-01-12 23:04:06 +07:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
} else if (IS_BROADWELL(dev_priv)) {
|
|
|
|
tmp_mask = iir & GEN8_PORT_DP_A_HOTPLUG;
|
|
|
|
if (tmp_mask) {
|
2016-05-06 20:48:28 +07:00
|
|
|
ilk_hpd_irq_handler(dev_priv,
|
|
|
|
tmp_mask, hpd_bdw);
|
2016-01-12 23:04:06 +07:00
|
|
|
found = true;
|
|
|
|
}
|
2014-08-22 19:10:41 +07:00
|
|
|
}
|
|
|
|
|
2016-12-02 15:23:49 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv) && (iir & BXT_DE_PORT_GMBUS)) {
|
2016-05-06 20:48:28 +07:00
|
|
|
gmbus_irq_handler(dev_priv);
|
2014-08-22 19:10:43 +07:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
2014-08-22 19:10:41 +07:00
|
|
|
if (!found)
|
2014-06-16 22:10:59 +07:00
|
|
|
DRM_ERROR("Unexpected DE Port interrupt\n");
|
2013-11-07 20:49:55 +07:00
|
|
|
}
|
2014-06-16 22:10:59 +07:00
|
|
|
else
|
|
|
|
DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
|
2013-11-07 20:49:55 +07:00
|
|
|
}
|
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
2017-07-21 00:57:51 +07:00
|
|
|
u32 fault_errors;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2013-11-07 17:05:40 +07:00
|
|
|
if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
|
|
|
|
continue;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
|
|
|
|
if (!iir) {
|
|
|
|
DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
|
|
|
|
continue;
|
|
|
|
}
|
2014-03-21 03:45:01 +07:00
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir);
|
2014-06-16 22:10:59 +07:00
|
|
|
|
2017-07-21 00:57:51 +07:00
|
|
|
if (iir & GEN8_PIPE_VBLANK)
|
|
|
|
drm_handle_vblank(&dev_priv->drm, pipe);
|
2014-06-16 22:10:59 +07:00
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
if (iir & GEN8_PIPE_CDCLK_CRC_DONE)
|
2016-05-06 20:48:28 +07:00
|
|
|
hsw_pipe_crc_irq_handler(dev_priv, pipe);
|
2014-06-16 22:10:59 +07:00
|
|
|
|
2016-01-12 23:04:06 +07:00
|
|
|
if (iir & GEN8_PIPE_FIFO_UNDERRUN)
|
|
|
|
intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
|
2014-03-21 03:45:01 +07:00
|
|
|
|
2019-06-27 01:03:40 +07:00
|
|
|
fault_errors = iir & gen8_de_pipe_fault_mask(dev_priv);
|
2016-01-12 23:04:06 +07:00
|
|
|
if (fault_errors)
|
2016-10-27 19:48:32 +07:00
|
|
|
DRM_ERROR("Fault errors on pipe %c: 0x%08x\n",
|
2016-01-12 23:04:06 +07:00
|
|
|
pipe_name(pipe),
|
|
|
|
fault_errors);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (HAS_PCH_SPLIT(dev_priv) && !HAS_PCH_NOP(dev_priv) &&
|
2014-08-22 19:10:42 +07:00
|
|
|
master_ctl & GEN8_DE_PCH_IRQ) {
|
2013-11-07 17:05:43 +07:00
|
|
|
/*
|
|
|
|
* FIXME(BDW): Assume for now that the new interrupt handling
|
|
|
|
* scheme also closed the SDE interrupt handling race we've seen
|
|
|
|
* on older pch-split platforms. But this needs testing.
|
|
|
|
*/
|
2016-01-12 23:04:06 +07:00
|
|
|
iir = I915_READ(SDEIIR);
|
|
|
|
if (iir) {
|
|
|
|
I915_WRITE(SDEIIR, iir);
|
2013-11-07 17:05:43 +07:00
|
|
|
ret = IRQ_HANDLED;
|
2015-08-28 03:56:02 +07:00
|
|
|
|
2019-06-15 07:42:10 +07:00
|
|
|
if (INTEL_PCH_TYPE(dev_priv) >= PCH_MCC)
|
|
|
|
icp_irq_handler(dev_priv, iir, hpd_mcc);
|
|
|
|
else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP)
|
|
|
|
icp_irq_handler(dev_priv, iir, hpd_icp);
|
2019-03-09 04:43:00 +07:00
|
|
|
else if (INTEL_PCH_TYPE(dev_priv) >= PCH_SPT)
|
2016-05-06 20:48:28 +07:00
|
|
|
spt_irq_handler(dev_priv, iir);
|
2015-08-28 03:56:02 +07:00
|
|
|
else
|
2016-05-06 20:48:28 +07:00
|
|
|
cpt_irq_handler(dev_priv, iir);
|
2016-01-07 15:29:10 +07:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Like on previous PCH there seems to be something
|
|
|
|
* fishy going on with forwarding PCH interrupts.
|
|
|
|
*/
|
|
|
|
DRM_DEBUG_DRIVER("The master control interrupt lied (SDE)!\n");
|
|
|
|
}
|
2013-11-07 17:05:43 +07:00
|
|
|
}
|
|
|
|
|
2016-01-12 23:04:07 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-15 21:14:38 +07:00
|
|
|
static inline u32 gen8_master_intr_disable(void __iomem * const regs)
|
|
|
|
{
|
|
|
|
raw_reg_write(regs, GEN8_MASTER_IRQ, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now with master disabled, get a sample of level indications
|
|
|
|
* for this interrupt. Indications will be cleared on related acks.
|
|
|
|
* New indications can and will light up during processing,
|
|
|
|
* and will generate new interrupt after enabling master.
|
|
|
|
*/
|
|
|
|
return raw_reg_read(regs, GEN8_MASTER_IRQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void gen8_master_intr_enable(void __iomem * const regs)
|
|
|
|
{
|
|
|
|
raw_reg_write(regs, GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:04:07 +07:00
|
|
|
static irqreturn_t gen8_irq_handler(int irq, void *arg)
|
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2019-03-20 01:35:40 +07:00
|
|
|
void __iomem * const regs = dev_priv->uncore.regs;
|
2016-01-12 23:04:07 +07:00
|
|
|
u32 master_ctl;
|
2018-02-15 14:37:12 +07:00
|
|
|
u32 gt_iir[4];
|
2016-01-12 23:04:07 +07:00
|
|
|
|
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2018-10-15 21:14:38 +07:00
|
|
|
master_ctl = gen8_master_intr_disable(regs);
|
|
|
|
if (!master_ctl) {
|
|
|
|
gen8_master_intr_enable(regs);
|
2016-01-12 23:04:07 +07:00
|
|
|
return IRQ_NONE;
|
2018-10-15 21:14:38 +07:00
|
|
|
}
|
2016-01-12 23:04:07 +07:00
|
|
|
|
|
|
|
/* Find, clear, then process each source of interrupt */
|
2018-02-02 22:34:48 +07:00
|
|
|
gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
|
2018-02-15 14:37:12 +07:00
|
|
|
|
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
|
|
|
if (master_ctl & ~GEN8_GT_IRQS) {
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2018-02-15 14:37:12 +07:00
|
|
|
gen8_de_irq_handler(dev_priv, master_ctl);
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2018-02-15 14:37:12 +07:00
|
|
|
}
|
2016-01-12 23:04:07 +07:00
|
|
|
|
2018-10-15 21:14:38 +07:00
|
|
|
gen8_master_intr_enable(regs);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2018-02-15 14:37:12 +07:00
|
|
|
gen8_gt_irq_handler(dev_priv, master_ctl, gt_iir);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2018-02-02 22:34:48 +07:00
|
|
|
return IRQ_HANDLED;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
static u32
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_engine_identity(struct intel_gt *gt,
|
2018-04-06 16:31:45 +07:00
|
|
|
const unsigned int bank, const unsigned int bit)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
2019-07-04 19:17:54 +07:00
|
|
|
void __iomem * const regs = gt->uncore->regs;
|
2018-02-28 17:11:53 +07:00
|
|
|
u32 timeout_ts;
|
|
|
|
u32 ident;
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
raw_reg_write(regs, GEN11_IIR_REG_SELECTOR(bank), BIT(bit));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NB: Specs do not specify how long to spin wait,
|
|
|
|
* so we do ~100us as an educated guess.
|
|
|
|
*/
|
|
|
|
timeout_ts = (local_clock() >> 10) + 100;
|
|
|
|
do {
|
|
|
|
ident = raw_reg_read(regs, GEN11_INTR_IDENTITY_REG(bank));
|
|
|
|
} while (!(ident & GEN11_INTR_DATA_VALID) &&
|
|
|
|
!time_after32(local_clock() >> 10, timeout_ts));
|
|
|
|
|
|
|
|
if (unlikely(!(ident & GEN11_INTR_DATA_VALID))) {
|
|
|
|
DRM_ERROR("INTR_IDENTITY_REG%u:%u 0x%08x not valid!\n",
|
|
|
|
bank, bit, ident);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
raw_reg_write(regs, GEN11_INTR_IDENTITY_REG(bank),
|
|
|
|
GEN11_INTR_DATA_VALID);
|
|
|
|
|
2018-04-06 16:31:45 +07:00
|
|
|
return ident;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_other_irq_handler(struct intel_gt *gt, const u8 instance,
|
|
|
|
const u16 iir)
|
2018-04-06 16:31:45 +07:00
|
|
|
{
|
2019-05-28 01:36:08 +07:00
|
|
|
if (instance == OTHER_GUC_INSTANCE)
|
2019-07-13 17:00:12 +07:00
|
|
|
return guc_irq_handler(>->uc.guc, iir);
|
2019-05-28 01:36:08 +07:00
|
|
|
|
2018-04-05 21:00:50 +07:00
|
|
|
if (instance == OTHER_GTPM_INSTANCE)
|
2019-07-04 19:17:56 +07:00
|
|
|
return gen11_rps_irq_handler(gt, iir);
|
2018-04-05 21:00:50 +07:00
|
|
|
|
2018-04-06 16:31:45 +07:00
|
|
|
WARN_ONCE(1, "unhandled other interrupt instance=0x%x, iir=0x%x\n",
|
|
|
|
instance, iir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_engine_irq_handler(struct intel_gt *gt, const u8 class,
|
|
|
|
const u8 instance, const u16 iir)
|
2018-04-06 16:31:45 +07:00
|
|
|
{
|
|
|
|
struct intel_engine_cs *engine;
|
|
|
|
|
|
|
|
if (instance <= MAX_ENGINE_INSTANCE)
|
2019-07-04 19:17:54 +07:00
|
|
|
engine = gt->i915->engine_class[class][instance];
|
2018-04-06 16:31:45 +07:00
|
|
|
else
|
|
|
|
engine = NULL;
|
|
|
|
|
|
|
|
if (likely(engine))
|
|
|
|
return gen8_cs_irq_handler(engine, iir);
|
|
|
|
|
|
|
|
WARN_ONCE(1, "unhandled engine interrupt class=0x%x, instance=0x%x\n",
|
|
|
|
class, instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_identity_handler(struct intel_gt *gt, const u32 identity)
|
2018-04-06 16:31:45 +07:00
|
|
|
{
|
|
|
|
const u8 class = GEN11_INTR_ENGINE_CLASS(identity);
|
|
|
|
const u8 instance = GEN11_INTR_ENGINE_INSTANCE(identity);
|
|
|
|
const u16 intr = GEN11_INTR_ENGINE_INTR(identity);
|
|
|
|
|
|
|
|
if (unlikely(!intr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (class <= COPY_ENGINE_CLASS)
|
2019-07-04 19:17:54 +07:00
|
|
|
return gen11_engine_irq_handler(gt, class, instance, intr);
|
2018-04-06 16:31:45 +07:00
|
|
|
|
|
|
|
if (class == OTHER_CLASS)
|
2019-07-04 19:17:54 +07:00
|
|
|
return gen11_other_irq_handler(gt, instance, intr);
|
2018-04-06 16:31:45 +07:00
|
|
|
|
|
|
|
WARN_ONCE(1, "unknown interrupt class=0x%x, instance=0x%x, intr=0x%x\n",
|
|
|
|
class, instance, intr);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_bank_handler(struct intel_gt *gt, const unsigned int bank)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
2019-07-04 19:17:54 +07:00
|
|
|
void __iomem * const regs = gt->uncore->regs;
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
unsigned long intr_dw;
|
|
|
|
unsigned int bit;
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
lockdep_assert_held(>->i915->irq_lock);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
intr_dw = raw_reg_read(regs, GEN11_GT_INTR_DW(bank));
|
2018-02-28 17:11:53 +07:00
|
|
|
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
for_each_set_bit(bit, &intr_dw, 32) {
|
2019-07-04 19:17:54 +07:00
|
|
|
const u32 ident = gen11_gt_engine_identity(gt, bank, bit);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_identity_handler(gt, ident);
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
}
|
2018-02-28 17:11:53 +07:00
|
|
|
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
/* Clear must be after shared has been served for engine */
|
|
|
|
raw_reg_write(regs, GEN11_GT_INTR_DW(bank), intr_dw);
|
|
|
|
}
|
2018-02-28 17:11:53 +07:00
|
|
|
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_irq_handler(struct intel_gt *gt, const u32 master_ctl)
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
{
|
2019-07-04 19:17:54 +07:00
|
|
|
struct drm_i915_private *i915 = gt->i915;
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
unsigned int bank;
|
|
|
|
|
|
|
|
spin_lock(&i915->irq_lock);
|
|
|
|
|
|
|
|
for (bank = 0; bank < 2; bank++) {
|
|
|
|
if (master_ctl & GEN11_GT_DW_IRQ(bank))
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_bank_handler(gt, bank);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
drm/i915/icl: Deal with GT INT DW correctly
BSpec says:
"Second level interrupt events are stored in the GT INT DW. GT INT DW is
a double buffered structure. A snapshot of events is taken when SW reads
GT INT DW. From the time of read to the time of SW completely clearing
GT INT DW (to indicate end of service), all incoming interrupts are logged
in a secondary storage structure. this guarantees that the record of
interrupts SW is servicing will not change while under service".
We read GT INT DW in several places now:
- The IRQ handler (banks 0 and 1) where, hopefully, it is completely
cleared (operation now covered with the irq lock).
- The 'reset' interrupts functions for RPS and GuC logs, where we clear
the bit we are interested in and leave the others for the normal
interrupt handler.
- The 'enable' interrupts functions for RPS and GuC logs, as a measure
of precaution. Here we could relax a bit and don't check GT INT DW
at all or, if we do, at least we should clear the offending bit
(which is what this patch does).
Note that, if every bit is cleared on reading GT INT DW, the register
won't be locked. Also note that, according to the BSpec, GT INT DW
cannot be cleared without first servicing the Selector & Shared IIR
registers.
v2:
- Remove some code duplication (Tvrtko)
- Make sure GT_INTR_DW are protected by the irq spinlock, since it's a
global resource (Tvrtko)
v3: Optimize the spinlock (Tvrtko)
v4: Rebase.
v5:
- take spinlock on outer scope to please sparse (Mika)
- use raw_reg accessors (Mika)
v6: omit the continue in looping banks (Michel)
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> (v4)
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180406093237.14548-1-mika.kuoppala@linux.intel.com
2018-04-06 16:32:37 +07:00
|
|
|
|
|
|
|
spin_unlock(&i915->irq_lock);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2018-09-26 17:47:18 +07:00
|
|
|
static u32
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gu_misc_irq_ack(struct intel_gt *gt, const u32 master_ctl)
|
2018-06-16 07:05:28 +07:00
|
|
|
{
|
2019-07-04 19:17:54 +07:00
|
|
|
void __iomem * const regs = gt->uncore->regs;
|
2018-09-26 17:47:18 +07:00
|
|
|
u32 iir;
|
2018-06-16 07:05:28 +07:00
|
|
|
|
|
|
|
if (!(master_ctl & GEN11_GU_MISC_IRQ))
|
2018-09-26 17:47:18 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
iir = raw_reg_read(regs, GEN11_GU_MISC_IIR);
|
|
|
|
if (likely(iir))
|
|
|
|
raw_reg_write(regs, GEN11_GU_MISC_IIR, iir);
|
2018-06-16 07:05:28 +07:00
|
|
|
|
2018-09-26 17:47:18 +07:00
|
|
|
return iir;
|
2018-06-16 07:05:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gu_misc_irq_handler(struct intel_gt *gt, const u32 iir)
|
2018-06-16 07:05:28 +07:00
|
|
|
{
|
|
|
|
if (iir & GEN11_GU_MISC_GSE)
|
2019-07-04 19:17:54 +07:00
|
|
|
intel_opregion_asle_intr(gt->i915);
|
2018-06-16 07:05:28 +07:00
|
|
|
}
|
|
|
|
|
2018-10-15 21:14:40 +07:00
|
|
|
static inline u32 gen11_master_intr_disable(void __iomem * const regs)
|
|
|
|
{
|
|
|
|
raw_reg_write(regs, GEN11_GFX_MSTR_IRQ, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now with master disabled, get a sample of level indications
|
|
|
|
* for this interrupt. Indications will be cleared on related acks.
|
|
|
|
* New indications can and will light up during processing,
|
|
|
|
* and will generate new interrupt after enabling master.
|
|
|
|
*/
|
|
|
|
return raw_reg_read(regs, GEN11_GFX_MSTR_IRQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void gen11_master_intr_enable(void __iomem * const regs)
|
|
|
|
{
|
|
|
|
raw_reg_write(regs, GEN11_GFX_MSTR_IRQ, GEN11_MASTER_IRQ);
|
|
|
|
}
|
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
static irqreturn_t gen11_irq_handler(int irq, void *arg)
|
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private * const i915 = arg;
|
2019-03-20 01:35:40 +07:00
|
|
|
void __iomem * const regs = i915->uncore.regs;
|
2019-07-04 19:17:54 +07:00
|
|
|
struct intel_gt *gt = &i915->gt;
|
2018-02-28 17:11:53 +07:00
|
|
|
u32 master_ctl;
|
2018-06-16 07:05:28 +07:00
|
|
|
u32 gu_misc_iir;
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
if (!intel_irqs_enabled(i915))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2018-10-15 21:14:40 +07:00
|
|
|
master_ctl = gen11_master_intr_disable(regs);
|
|
|
|
if (!master_ctl) {
|
|
|
|
gen11_master_intr_enable(regs);
|
2018-02-28 17:11:53 +07:00
|
|
|
return IRQ_NONE;
|
2018-10-15 21:14:40 +07:00
|
|
|
}
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
/* Find, clear, then process each source of interrupt. */
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_irq_handler(gt, master_ctl);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
|
|
|
if (master_ctl & GEN11_DISPLAY_IRQ) {
|
|
|
|
const u32 disp_ctl = raw_reg_read(regs, GEN11_DISPLAY_INT_CTL);
|
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&i915->runtime_pm);
|
2018-02-28 17:11:53 +07:00
|
|
|
/*
|
|
|
|
* GEN11_DISPLAY_INT_CTL has same format as GEN8_MASTER_IRQ
|
|
|
|
* for the display related bits.
|
|
|
|
*/
|
|
|
|
gen8_de_irq_handler(i915, disp_ctl);
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&i915->runtime_pm);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gu_misc_iir = gen11_gu_misc_irq_ack(gt, master_ctl);
|
2018-06-16 07:05:28 +07:00
|
|
|
|
2018-10-15 21:14:40 +07:00
|
|
|
gen11_master_intr_enable(regs);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gu_misc_irq_handler(gt, gu_misc_iir);
|
2018-06-16 07:05:28 +07:00
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2008-10-19 09:39:29 +07:00
|
|
|
/* Called from drm generic code, passed 'crtc' which
|
|
|
|
* we use as a pipe index
|
|
|
|
*/
|
2019-06-20 00:08:40 +07:00
|
|
|
int i8xx_enable_vblank(struct drm_crtc *crtc)
|
2008-10-01 02:14:26 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2008-10-17 01:31:38 +07:00
|
|
|
unsigned long irqflags;
|
2009-01-09 01:42:15 +07:00
|
|
|
|
2010-12-04 18:30:53 +07:00
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS);
|
2010-12-04 18:30:53 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
2011-02-05 17:08:21 +07:00
|
|
|
|
2008-10-01 02:14:26 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
int i945gm_enable_vblank(struct drm_crtc *crtc)
|
2019-03-23 01:08:03 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
2019-03-23 01:08:03 +07:00
|
|
|
|
|
|
|
if (dev_priv->i945gm_vblank.enabled++ == 0)
|
|
|
|
schedule_work(&dev_priv->i945gm_vblank.work);
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
return i8xx_enable_vblank(crtc);
|
2019-03-23 01:08:03 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
int i965_enable_vblank(struct drm_crtc *crtc)
|
2011-04-08 03:58:17 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2011-04-08 03:58:17 +07:00
|
|
|
unsigned long irqflags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
i915_enable_pipestat(dev_priv, pipe,
|
|
|
|
PIPE_START_VBLANK_INTERRUPT_STATUS);
|
2011-04-07 02:13:38 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
int ilk_enable_vblank(struct drm_crtc *crtc)
|
2012-03-29 03:39:38 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2012-03-29 03:39:38 +07:00
|
|
|
unsigned long irqflags;
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 bit = INTEL_GEN(dev_priv) >= 7 ?
|
2016-10-08 02:49:52 +07:00
|
|
|
DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
ilk_enable_display_irq(dev_priv, bit);
|
2012-03-29 03:39:38 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
2018-02-03 12:13:02 +07:00
|
|
|
/* Even though there is no DMC, frame counter can get stuck when
|
|
|
|
* PSR is active as no frames are generated.
|
|
|
|
*/
|
|
|
|
if (HAS_PSR(dev_priv))
|
2019-06-20 00:08:40 +07:00
|
|
|
drm_crtc_vblank_restore(crtc);
|
2018-02-03 12:13:02 +07:00
|
|
|
|
2012-03-29 03:39:38 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
int bdw_enable_vblank(struct drm_crtc *crtc)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
unsigned long irqflags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2015-11-23 23:06:17 +07:00
|
|
|
bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
2015-11-23 23:06:17 +07:00
|
|
|
|
2018-02-03 12:13:02 +07:00
|
|
|
/* Even if there is no DMC, frame counter can get stuck when
|
|
|
|
* PSR is active as no frames are generated, so check only for PSR.
|
|
|
|
*/
|
|
|
|
if (HAS_PSR(dev_priv))
|
2019-06-20 00:08:40 +07:00
|
|
|
drm_crtc_vblank_restore(crtc);
|
2018-02-03 12:13:02 +07:00
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-19 09:39:29 +07:00
|
|
|
/* Called from drm generic code, passed 'crtc' which
|
|
|
|
* we use as a pipe index
|
|
|
|
*/
|
2019-06-20 00:08:40 +07:00
|
|
|
void i8xx_disable_vblank(struct drm_crtc *crtc)
|
2008-10-01 02:14:26 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2008-10-17 01:31:38 +07:00
|
|
|
unsigned long irqflags;
|
2008-10-01 02:14:26 +07:00
|
|
|
|
2010-12-04 18:30:53 +07:00
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS);
|
2011-04-08 03:58:17 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
void i945gm_disable_vblank(struct drm_crtc *crtc)
|
2019-03-23 01:08:03 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
2019-03-23 01:08:03 +07:00
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
i8xx_disable_vblank(crtc);
|
2019-03-23 01:08:03 +07:00
|
|
|
|
|
|
|
if (--dev_priv->i945gm_vblank.enabled == 0)
|
|
|
|
schedule_work(&dev_priv->i945gm_vblank.work);
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
void i965_disable_vblank(struct drm_crtc *crtc)
|
2011-04-08 03:58:17 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2011-04-08 03:58:17 +07:00
|
|
|
unsigned long irqflags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
i915_disable_pipestat(dev_priv, pipe,
|
|
|
|
PIPE_START_VBLANK_INTERRUPT_STATUS);
|
2011-04-07 02:13:38 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
void ilk_disable_vblank(struct drm_crtc *crtc)
|
2012-03-29 03:39:38 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
2012-03-29 03:39:38 +07:00
|
|
|
unsigned long irqflags;
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 bit = INTEL_GEN(dev_priv) >= 7 ?
|
2016-10-08 02:49:52 +07:00
|
|
|
DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2016-10-08 02:49:52 +07:00
|
|
|
ilk_disable_display_irq(dev_priv, bit);
|
2012-03-29 03:39:38 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:08:40 +07:00
|
|
|
void bdw_disable_vblank(struct drm_crtc *crtc)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2019-06-20 00:08:40 +07:00
|
|
|
struct drm_i915_private *dev_priv = to_i915(crtc->dev);
|
|
|
|
enum pipe pipe = to_intel_crtc(crtc)->pipe;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
unsigned long irqflags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
2015-11-23 23:06:17 +07:00
|
|
|
bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
}
|
|
|
|
|
2019-06-27 05:42:12 +07:00
|
|
|
static void i945gm_vblank_work_func(struct work_struct *work)
|
2019-03-23 01:08:03 +07:00
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv =
|
|
|
|
container_of(work, struct drm_i915_private, i945gm_vblank.work);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Vblank interrupts fail to wake up the device from C3,
|
|
|
|
* hence we want to prevent C3 usage while vblank interrupts
|
|
|
|
* are enabled.
|
|
|
|
*/
|
|
|
|
pm_qos_update_request(&dev_priv->i945gm_vblank.pm_qos,
|
|
|
|
READ_ONCE(dev_priv->i945gm_vblank.enabled) ?
|
|
|
|
dev_priv->i945gm_vblank.c3_disable_latency :
|
|
|
|
PM_QOS_DEFAULT_VALUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cstate_disable_latency(const char *name)
|
|
|
|
{
|
|
|
|
const struct cpuidle_driver *drv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
drv = cpuidle_get_driver();
|
|
|
|
if (!drv)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < drv->state_count; i++) {
|
|
|
|
const struct cpuidle_state *state = &drv->states[i];
|
|
|
|
|
|
|
|
if (!strcmp(state->name, name))
|
|
|
|
return state->exit_latency ?
|
|
|
|
state->exit_latency - 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i945gm_vblank_work_init(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
INIT_WORK(&dev_priv->i945gm_vblank.work,
|
|
|
|
i945gm_vblank_work_func);
|
|
|
|
|
|
|
|
dev_priv->i945gm_vblank.c3_disable_latency =
|
|
|
|
cstate_disable_latency("C3");
|
|
|
|
pm_qos_add_request(&dev_priv->i945gm_vblank.pm_qos,
|
|
|
|
PM_QOS_CPU_DMA_LATENCY,
|
|
|
|
PM_QOS_DEFAULT_VALUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i945gm_vblank_work_fini(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
cancel_work_sync(&dev_priv->i945gm_vblank.work);
|
|
|
|
pm_qos_remove_request(&dev_priv->i945gm_vblank.pm_qos);
|
|
|
|
}
|
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
static void ibx_irq_reset(struct drm_i915_private *dev_priv)
|
2013-06-06 00:21:51 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_NOP(dev_priv))
|
2013-06-06 00:21:51 +07:00
|
|
|
return;
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, SDE);
|
2014-04-02 01:37:17 +07:00
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
|
2014-04-02 01:37:17 +07:00
|
|
|
I915_WRITE(SERR_INT, 0xffffffff);
|
2014-04-02 01:37:22 +07:00
|
|
|
}
|
2014-04-02 01:37:17 +07:00
|
|
|
|
2014-04-02 01:37:22 +07:00
|
|
|
/*
|
|
|
|
* SDEIER is also touched by the interrupt handler to work around missed PCH
|
|
|
|
* interrupts. Hence we can't update it after the interrupt handler is enabled -
|
|
|
|
* instead we unconditionally enable all PCH interrupt sources here, but then
|
|
|
|
* only unmask them as needed with SDEIMR.
|
|
|
|
*
|
|
|
|
* This function needs to be called before interrupts are enabled.
|
|
|
|
*/
|
2019-06-20 17:33:34 +07:00
|
|
|
static void ibx_irq_pre_postinstall(struct drm_i915_private *dev_priv)
|
2014-04-02 01:37:22 +07:00
|
|
|
{
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_NOP(dev_priv))
|
2014-04-02 01:37:22 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
WARN_ON(I915_READ(SDEIER) != 0);
|
2013-06-06 00:21:51 +07:00
|
|
|
I915_WRITE(SDEIER, 0xffffffff);
|
|
|
|
POSTING_READ(SDEIER);
|
|
|
|
}
|
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
static void gen5_gt_irq_reset(struct drm_i915_private *dev_priv)
|
2013-07-13 03:43:25 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
|
|
|
GEN3_IRQ_RESET(uncore, GT);
|
2016-11-16 15:55:38 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 6)
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN6_PM);
|
2013-07-13 03:43:25 +07:00
|
|
|
}
|
|
|
|
|
2014-10-31 00:42:58 +07:00
|
|
|
static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
2016-04-11 20:56:31 +07:00
|
|
|
if (IS_CHERRYVIEW(dev_priv))
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
|
2016-04-11 20:56:31 +07:00
|
|
|
else
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, DPINVGTT, DPINVGTT_STATUS_MASK);
|
2016-04-11 20:56:31 +07:00
|
|
|
|
2016-04-12 22:56:14 +07:00
|
|
|
i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0);
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
2014-10-31 00:42:58 +07:00
|
|
|
|
2017-08-19 01:36:51 +07:00
|
|
|
i9xx_pipestat_irq_reset(dev_priv);
|
2014-10-31 00:42:58 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, VLV_);
|
2017-11-30 19:52:53 +07:00
|
|
|
dev_priv->irq_mask = ~0u;
|
2014-10-31 00:42:58 +07:00
|
|
|
}
|
|
|
|
|
2016-04-12 22:56:44 +07:00
|
|
|
static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
2016-04-12 22:56:44 +07:00
|
|
|
u32 pipestat_mask;
|
2016-04-11 20:56:28 +07:00
|
|
|
u32 enable_mask;
|
2016-04-12 22:56:44 +07:00
|
|
|
enum pipe pipe;
|
|
|
|
|
2017-08-19 01:36:50 +07:00
|
|
|
pipestat_mask = PIPE_CRC_DONE_INTERRUPT_STATUS;
|
2016-04-12 22:56:44 +07:00
|
|
|
|
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
|
|
|
|
for_each_pipe(dev_priv, pipe)
|
|
|
|
i915_enable_pipestat(dev_priv, pipe, pipestat_mask);
|
|
|
|
|
2016-04-11 20:56:28 +07:00
|
|
|
enable_mask = I915_DISPLAY_PORT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
2017-04-27 23:02:22 +07:00
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_A_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_B_INTERRUPT;
|
|
|
|
|
2016-04-12 22:56:44 +07:00
|
|
|
if (IS_CHERRYVIEW(dev_priv))
|
2017-04-27 23:02:22 +07:00
|
|
|
enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
|
|
|
|
I915_LPE_PIPE_C_INTERRUPT;
|
2016-04-11 20:56:29 +07:00
|
|
|
|
2017-11-30 19:52:53 +07:00
|
|
|
WARN_ON(dev_priv->irq_mask != ~0u);
|
2016-04-11 20:56:29 +07:00
|
|
|
|
2016-04-11 20:56:28 +07:00
|
|
|
dev_priv->irq_mask = ~enable_mask;
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, VLV_, dev_priv->irq_mask, enable_mask);
|
2016-04-12 22:56:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* drm_dma.h hooks
|
|
|
|
*/
|
2019-06-20 17:33:34 +07:00
|
|
|
static void ironlake_irq_reset(struct drm_i915_private *dev_priv)
|
2016-04-12 22:56:44 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2016-04-12 22:56:44 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, DE);
|
drm/i915: replace IS_GEN<N> with IS_GEN(..., N)
Define IS_GEN() similarly to our IS_GEN_RANGE(). but use gen instead of
gen_mask to do the comparison. Now callers can pass then gen as a parameter,
so we don't require one macro for each gen.
The following spatch was used to convert the users of these macros:
@@
expression e;
@@
(
- IS_GEN2(e)
+ IS_GEN(e, 2)
|
- IS_GEN3(e)
+ IS_GEN(e, 3)
|
- IS_GEN4(e)
+ IS_GEN(e, 4)
|
- IS_GEN5(e)
+ IS_GEN(e, 5)
|
- IS_GEN6(e)
+ IS_GEN(e, 6)
|
- IS_GEN7(e)
+ IS_GEN(e, 7)
|
- IS_GEN8(e)
+ IS_GEN(e, 8)
|
- IS_GEN9(e)
+ IS_GEN(e, 9)
|
- IS_GEN10(e)
+ IS_GEN(e, 10)
|
- IS_GEN11(e)
+ IS_GEN(e, 11)
)
v2: use IS_GEN rather than GT_GEN and compare to info.gen rather than
using the bitmask
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181212181044.15886-2-lucas.demarchi@intel.com
2018-12-13 01:10:43 +07:00
|
|
|
if (IS_GEN(dev_priv, 7))
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN7_ERR_INT, 0xffffffff);
|
2016-04-12 22:56:44 +07:00
|
|
|
|
2018-04-06 05:00:23 +07:00
|
|
|
if (IS_HASWELL(dev_priv)) {
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff);
|
|
|
|
intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff);
|
2018-04-06 05:00:23 +07:00
|
|
|
}
|
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
gen5_gt_irq_reset(dev_priv);
|
2016-04-12 22:56:44 +07:00
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
ibx_irq_reset(dev_priv);
|
2016-04-12 22:56:44 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void valleyview_irq_reset(struct drm_i915_private *dev_priv)
|
2012-03-29 03:39:38 +07:00
|
|
|
{
|
2016-04-14 01:19:48 +07:00
|
|
|
I915_WRITE(VLV_MASTER_IER, 0);
|
|
|
|
POSTING_READ(VLV_MASTER_IER);
|
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
gen5_gt_irq_reset(dev_priv);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2016-04-11 20:56:25 +07:00
|
|
|
if (dev_priv->display_irqs_enabled)
|
|
|
|
vlv_display_irq_reset(dev_priv);
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2012-03-29 03:39:38 +07:00
|
|
|
}
|
|
|
|
|
2014-05-23 03:18:22 +07:00
|
|
|
static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
|
|
|
GEN8_IRQ_RESET_NDX(uncore, GT, 0);
|
|
|
|
GEN8_IRQ_RESET_NDX(uncore, GT, 1);
|
|
|
|
GEN8_IRQ_RESET_NDX(uncore, GT, 2);
|
|
|
|
GEN8_IRQ_RESET_NDX(uncore, GT, 3);
|
2014-05-23 03:18:22 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void gen8_irq_reset(struct drm_i915_private *dev_priv)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
int pipe;
|
|
|
|
|
2019-03-20 01:35:40 +07:00
|
|
|
gen8_master_intr_disable(dev_priv->uncore.regs);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2014-05-23 03:18:22 +07:00
|
|
|
gen8_gt_irq_reset(dev_priv);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff);
|
|
|
|
intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff);
|
2018-04-04 04:24:18 +07:00
|
|
|
|
2014-08-18 19:49:10 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
2014-09-30 15:56:39 +07:00
|
|
|
if (intel_display_power_is_enabled(dev_priv,
|
|
|
|
POWER_DOMAIN_PIPE(pipe)))
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_DE_PORT_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_DE_MISC_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_PCU_);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_SPLIT(dev_priv))
|
2016-11-16 15:55:38 +07:00
|
|
|
ibx_irq_reset(dev_priv);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
2014-01-11 04:13:09 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
static void gen11_gt_irq_reset(struct intel_gt *gt)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
2019-07-04 19:17:55 +07:00
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
2019-07-04 19:17:54 +07:00
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
/* Disable RCS, BCS, VCS and VECS class engines. */
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_RENDER_COPY_INTR_ENABLE, 0);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS_VECS_INTR_ENABLE, 0);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
/* Restore masks irqs on RCS, BCS, VCS and VECS engines. */
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_RCS0_RSVD_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(uncore, GEN11_BCS_RSVD_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS0_VCS1_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS2_VCS3_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(uncore, GEN11_VECS0_VECS1_INTR_MASK, ~0);
|
|
|
|
|
|
|
|
intel_uncore_write(uncore, GEN11_GPM_WGBOXPERF_INTR_ENABLE, 0);
|
|
|
|
intel_uncore_write(uncore, GEN11_GPM_WGBOXPERF_INTR_MASK, ~0);
|
|
|
|
intel_uncore_write(uncore, GEN11_GUC_SG_INTR_ENABLE, 0);
|
|
|
|
intel_uncore_write(uncore, GEN11_GUC_SG_INTR_MASK, ~0);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void gen11_irq_reset(struct drm_i915_private *dev_priv)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2018-02-28 17:11:53 +07:00
|
|
|
int pipe;
|
|
|
|
|
2019-03-20 01:35:40 +07:00
|
|
|
gen11_master_intr_disable(dev_priv->uncore.regs);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_irq_reset(&dev_priv->gt);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_DISPLAY_INT_CTL, 0);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff);
|
|
|
|
intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff);
|
2018-11-07 02:08:42 +07:00
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
for_each_pipe(dev_priv, pipe)
|
|
|
|
if (intel_display_power_is_enabled(dev_priv,
|
|
|
|
POWER_DOMAIN_PIPE(pipe)))
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_DE_PORT_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_DE_MISC_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN11_DE_HPD_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN11_GU_MISC_);
|
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_PCU_);
|
2018-06-27 03:52:23 +07:00
|
|
|
|
2019-03-14 04:43:07 +07:00
|
|
|
if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP)
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, SDE);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2015-03-07 01:50:48 +07:00
|
|
|
void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv,
|
2017-07-12 22:54:13 +07:00
|
|
|
u8 pipe_mask)
|
2014-07-04 21:50:31 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN;
|
2016-02-20 01:47:31 +07:00
|
|
|
enum pipe pipe;
|
2014-07-04 21:50:31 +07:00
|
|
|
|
2014-09-15 19:55:29 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-09-28 17:06:24 +07:00
|
|
|
|
|
|
|
if (!intel_irqs_enabled(dev_priv)) {
|
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-20 01:47:31 +07:00
|
|
|
for_each_pipe_masked(dev_priv, pipe, pipe_mask)
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_INIT_NDX(uncore, DE_PIPE, pipe,
|
2016-02-20 01:47:31 +07:00
|
|
|
dev_priv->de_irq_mask[pipe],
|
|
|
|
~dev_priv->de_irq_mask[pipe] | extra_ier);
|
2017-09-28 17:06:24 +07:00
|
|
|
|
2014-09-15 19:55:29 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2014-07-04 21:50:31 +07:00
|
|
|
}
|
|
|
|
|
2016-02-20 01:47:30 +07:00
|
|
|
void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv,
|
2017-07-12 22:54:13 +07:00
|
|
|
u8 pipe_mask)
|
2016-02-20 01:47:30 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2016-02-20 01:47:31 +07:00
|
|
|
enum pipe pipe;
|
|
|
|
|
2016-02-20 01:47:30 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2017-09-28 17:06:24 +07:00
|
|
|
|
|
|
|
if (!intel_irqs_enabled(dev_priv)) {
|
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-20 01:47:31 +07:00
|
|
|
for_each_pipe_masked(dev_priv, pipe, pipe_mask)
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe);
|
2017-09-28 17:06:24 +07:00
|
|
|
|
2016-02-20 01:47:30 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
|
|
|
|
/* make sure we're done processing display irqs */
|
2019-07-02 22:17:23 +07:00
|
|
|
intel_synchronize_irq(dev_priv);
|
2016-02-20 01:47:30 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void cherryview_irq_reset(struct drm_i915_private *dev_priv)
|
2014-04-10 00:40:52 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2014-04-10 00:40:52 +07:00
|
|
|
|
|
|
|
I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
|
POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
|
|
2014-05-23 03:18:22 +07:00
|
|
|
gen8_gt_irq_reset(dev_priv);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN8_PCU_);
|
2014-04-10 00:40:52 +07:00
|
|
|
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2016-04-11 20:56:25 +07:00
|
|
|
if (dev_priv->display_irqs_enabled)
|
|
|
|
vlv_display_irq_reset(dev_priv);
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2014-04-10 00:40:52 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static u32 intel_hpd_enabled_irqs(struct drm_i915_private *dev_priv,
|
2015-08-28 03:55:57 +07:00
|
|
|
const u32 hpd[HPD_NUM_PINS])
|
|
|
|
{
|
|
|
|
struct intel_encoder *encoder;
|
|
|
|
u32 enabled_irqs = 0;
|
|
|
|
|
2016-07-05 16:40:23 +07:00
|
|
|
for_each_intel_encoder(&dev_priv->drm, encoder)
|
2015-08-28 03:55:57 +07:00
|
|
|
if (dev_priv->hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED)
|
|
|
|
enabled_irqs |= hpd[encoder->hpd_pin];
|
|
|
|
|
|
|
|
return enabled_irqs;
|
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:21 +07:00
|
|
|
static void ibx_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
2011-09-20 03:31:02 +07:00
|
|
|
{
|
2017-01-27 16:39:21 +07:00
|
|
|
u32 hotplug;
|
2013-03-27 21:55:01 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable digital hotplug on the PCH, and configure the DP short pulse
|
2015-08-28 03:56:02 +07:00
|
|
|
* duration to 2ms (which is the minimum in the Display Port spec).
|
|
|
|
* The pulse duration bits are reserved on LPT+.
|
2013-03-27 21:55:01 +07:00
|
|
|
*/
|
2011-09-20 03:31:02 +07:00
|
|
|
hotplug = I915_READ(PCH_PORT_HOTPLUG);
|
2017-01-27 16:39:21 +07:00
|
|
|
hotplug &= ~(PORTB_PULSE_DURATION_MASK |
|
|
|
|
PORTC_PULSE_DURATION_MASK |
|
|
|
|
PORTD_PULSE_DURATION_MASK);
|
2011-09-20 03:31:02 +07:00
|
|
|
hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms;
|
2017-01-27 16:39:21 +07:00
|
|
|
hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms;
|
|
|
|
hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
|
2015-08-28 03:56:05 +07:00
|
|
|
/*
|
|
|
|
* When CPU and PCH are on the same package, port A
|
|
|
|
* HPD must be enabled in both north and south.
|
|
|
|
*/
|
2016-05-06 20:48:28 +07:00
|
|
|
if (HAS_PCH_LPT_LP(dev_priv))
|
2015-08-28 03:56:05 +07:00
|
|
|
hotplug |= PORTA_HOTPLUG_ENABLE;
|
2011-09-20 03:31:02 +07:00
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
|
2015-08-28 03:56:02 +07:00
|
|
|
}
|
2015-08-17 14:55:50 +07:00
|
|
|
|
2017-01-27 16:39:21 +07:00
|
|
|
static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
|
|
|
|
|
|
|
if (HAS_PCH_IBX(dev_priv)) {
|
|
|
|
hotplug_irqs = SDE_HOTPLUG_MASK;
|
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ibx);
|
|
|
|
} else {
|
|
|
|
hotplug_irqs = SDE_HOTPLUG_MASK_CPT;
|
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_cpt);
|
|
|
|
}
|
|
|
|
|
|
|
|
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
|
|
|
|
|
|
|
|
ibx_hpd_detection_setup(dev_priv);
|
|
|
|
}
|
|
|
|
|
2018-06-27 03:52:23 +07:00
|
|
|
static void icp_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug;
|
|
|
|
|
|
|
|
hotplug = I915_READ(SHOTPLUG_CTL_DDI);
|
|
|
|
hotplug |= ICP_DDIA_HPD_ENABLE |
|
|
|
|
ICP_DDIB_HPD_ENABLE;
|
|
|
|
I915_WRITE(SHOTPLUG_CTL_DDI, hotplug);
|
|
|
|
|
|
|
|
hotplug = I915_READ(SHOTPLUG_CTL_TC);
|
|
|
|
hotplug |= ICP_TC_HPD_ENABLE(PORT_TC1) |
|
|
|
|
ICP_TC_HPD_ENABLE(PORT_TC2) |
|
|
|
|
ICP_TC_HPD_ENABLE(PORT_TC3) |
|
|
|
|
ICP_TC_HPD_ENABLE(PORT_TC4);
|
|
|
|
I915_WRITE(SHOTPLUG_CTL_TC, hotplug);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void icp_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
|
|
|
|
|
|
|
hotplug_irqs = SDE_DDI_MASK_ICP | SDE_TC_MASK_ICP;
|
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_icp);
|
|
|
|
|
|
|
|
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
|
|
|
|
|
|
|
|
icp_hpd_detection_setup(dev_priv);
|
|
|
|
}
|
|
|
|
|
2018-06-16 07:05:29 +07:00
|
|
|
static void gen11_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug;
|
|
|
|
|
|
|
|
hotplug = I915_READ(GEN11_TC_HOTPLUG_CTL);
|
|
|
|
hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4);
|
|
|
|
I915_WRITE(GEN11_TC_HOTPLUG_CTL, hotplug);
|
2018-06-16 07:05:30 +07:00
|
|
|
|
|
|
|
hotplug = I915_READ(GEN11_TBT_HOTPLUG_CTL);
|
|
|
|
hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) |
|
|
|
|
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4);
|
|
|
|
I915_WRITE(GEN11_TBT_HOTPLUG_CTL, hotplug);
|
2018-06-16 07:05:29 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
|
|
|
u32 val;
|
|
|
|
|
2018-06-16 07:05:30 +07:00
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_gen11);
|
|
|
|
hotplug_irqs = GEN11_DE_TC_HOTPLUG_MASK | GEN11_DE_TBT_HOTPLUG_MASK;
|
2018-06-16 07:05:29 +07:00
|
|
|
|
|
|
|
val = I915_READ(GEN11_DE_HPD_IMR);
|
|
|
|
val &= ~hotplug_irqs;
|
|
|
|
I915_WRITE(GEN11_DE_HPD_IMR, val);
|
|
|
|
POSTING_READ(GEN11_DE_HPD_IMR);
|
|
|
|
|
|
|
|
gen11_hpd_detection_setup(dev_priv);
|
2018-06-27 03:52:23 +07:00
|
|
|
|
2019-03-14 04:43:07 +07:00
|
|
|
if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP)
|
2018-06-27 03:52:23 +07:00
|
|
|
icp_hpd_irq_setup(dev_priv);
|
2018-06-16 07:05:29 +07:00
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:18 +07:00
|
|
|
static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
2015-08-28 03:56:02 +07:00
|
|
|
{
|
2017-09-20 04:57:03 +07:00
|
|
|
u32 val, hotplug;
|
|
|
|
|
|
|
|
/* Display WA #1179 WaHardHangonHotPlug: cnp */
|
|
|
|
if (HAS_PCH_CNP(dev_priv)) {
|
|
|
|
val = I915_READ(SOUTH_CHICKEN1);
|
|
|
|
val &= ~CHASSIS_CLK_REQ_DURATION_MASK;
|
|
|
|
val |= CHASSIS_CLK_REQ_DURATION(0xf);
|
|
|
|
I915_WRITE(SOUTH_CHICKEN1, val);
|
|
|
|
}
|
2015-08-28 03:56:02 +07:00
|
|
|
|
|
|
|
/* Enable digital hotplug on the PCH */
|
|
|
|
hotplug = I915_READ(PCH_PORT_HOTPLUG);
|
2017-01-27 16:39:18 +07:00
|
|
|
hotplug |= PORTA_HOTPLUG_ENABLE |
|
|
|
|
PORTB_HOTPLUG_ENABLE |
|
|
|
|
PORTC_HOTPLUG_ENABLE |
|
|
|
|
PORTD_HOTPLUG_ENABLE;
|
2015-08-28 03:56:02 +07:00
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
|
|
|
|
|
|
|
|
hotplug = I915_READ(PCH_PORT_HOTPLUG2);
|
|
|
|
hotplug |= PORTE_HOTPLUG_ENABLE;
|
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG2, hotplug);
|
2011-09-20 03:31:02 +07:00
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:18 +07:00
|
|
|
static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
|
|
|
|
|
|
|
hotplug_irqs = SDE_HOTPLUG_MASK_SPT;
|
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_spt);
|
|
|
|
|
|
|
|
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
|
|
|
|
|
|
|
|
spt_hpd_detection_setup(dev_priv);
|
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:21 +07:00
|
|
|
static void ilk_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable digital hotplug on the CPU, and configure the DP short pulse
|
|
|
|
* duration to 2ms (which is the minimum in the Display Port spec)
|
|
|
|
* The pulse duration bits are reserved on HSW+.
|
|
|
|
*/
|
|
|
|
hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
|
|
|
|
hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK;
|
|
|
|
hotplug |= DIGITAL_PORTA_HOTPLUG_ENABLE |
|
|
|
|
DIGITAL_PORTA_PULSE_DURATION_2ms;
|
|
|
|
I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, hotplug);
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
2015-08-28 03:56:03 +07:00
|
|
|
{
|
2017-01-27 16:39:21 +07:00
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
2015-08-28 03:56:03 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 8) {
|
2015-08-28 03:56:06 +07:00
|
|
|
hotplug_irqs = GEN8_PORT_DP_A_HOTPLUG;
|
2016-05-06 20:48:28 +07:00
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bdw);
|
2015-08-28 03:56:06 +07:00
|
|
|
|
|
|
|
bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
|
2016-05-06 20:48:28 +07:00
|
|
|
} else if (INTEL_GEN(dev_priv) >= 7) {
|
2015-08-28 03:56:04 +07:00
|
|
|
hotplug_irqs = DE_DP_A_HOTPLUG_IVB;
|
2016-05-06 20:48:28 +07:00
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ivb);
|
2015-08-28 03:56:06 +07:00
|
|
|
|
|
|
|
ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
|
2015-08-28 03:56:04 +07:00
|
|
|
} else {
|
|
|
|
hotplug_irqs = DE_DP_A_HOTPLUG;
|
2016-05-06 20:48:28 +07:00
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ilk);
|
2015-08-28 03:56:03 +07:00
|
|
|
|
2015-08-28 03:56:06 +07:00
|
|
|
ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
|
|
|
|
}
|
2015-08-28 03:56:03 +07:00
|
|
|
|
2017-01-27 16:39:21 +07:00
|
|
|
ilk_hpd_detection_setup(dev_priv);
|
2015-08-28 03:56:03 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
ibx_hpd_irq_setup(dev_priv);
|
2015-08-28 03:56:03 +07:00
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:18 +07:00
|
|
|
static void __bxt_hpd_detection_setup(struct drm_i915_private *dev_priv,
|
|
|
|
u32 enabled_irqs)
|
2015-03-27 19:54:14 +07:00
|
|
|
{
|
2017-01-27 16:39:18 +07:00
|
|
|
u32 hotplug;
|
2015-03-27 19:54:14 +07:00
|
|
|
|
2015-08-28 03:56:11 +07:00
|
|
|
hotplug = I915_READ(PCH_PORT_HOTPLUG);
|
2017-01-27 16:39:18 +07:00
|
|
|
hotplug |= PORTA_HOTPLUG_ENABLE |
|
|
|
|
PORTB_HOTPLUG_ENABLE |
|
|
|
|
PORTC_HOTPLUG_ENABLE;
|
2016-03-31 17:41:47 +07:00
|
|
|
|
|
|
|
DRM_DEBUG_KMS("Invert bit setting: hp_ctl:%x hp_port:%x\n",
|
|
|
|
hotplug, enabled_irqs);
|
|
|
|
hotplug &= ~BXT_DDI_HPD_INVERT_MASK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For BXT invert bit has to be set based on AOB design
|
|
|
|
* for HPD detection logic, update it based on VBT fields.
|
|
|
|
*/
|
|
|
|
if ((enabled_irqs & BXT_DE_PORT_HP_DDIA) &&
|
|
|
|
intel_bios_is_port_hpd_inverted(dev_priv, PORT_A))
|
|
|
|
hotplug |= BXT_DDIA_HPD_INVERT;
|
|
|
|
if ((enabled_irqs & BXT_DE_PORT_HP_DDIB) &&
|
|
|
|
intel_bios_is_port_hpd_inverted(dev_priv, PORT_B))
|
|
|
|
hotplug |= BXT_DDIB_HPD_INVERT;
|
|
|
|
if ((enabled_irqs & BXT_DE_PORT_HP_DDIC) &&
|
|
|
|
intel_bios_is_port_hpd_inverted(dev_priv, PORT_C))
|
|
|
|
hotplug |= BXT_DDIC_HPD_INVERT;
|
|
|
|
|
2015-08-28 03:56:11 +07:00
|
|
|
I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
|
2015-03-27 19:54:14 +07:00
|
|
|
}
|
|
|
|
|
2017-01-27 16:39:18 +07:00
|
|
|
static void bxt_hpd_detection_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
__bxt_hpd_detection_setup(dev_priv, BXT_DE_PORT_HOTPLUG_MASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
u32 hotplug_irqs, enabled_irqs;
|
|
|
|
|
|
|
|
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bxt);
|
|
|
|
hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK;
|
|
|
|
|
|
|
|
bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
|
|
|
|
|
|
|
|
__bxt_hpd_detection_setup(dev_priv, enabled_irqs);
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void ibx_irq_postinstall(struct drm_i915_private *dev_priv)
|
2013-02-09 02:35:15 +07:00
|
|
|
{
|
2013-03-27 21:55:01 +07:00
|
|
|
u32 mask;
|
2013-02-28 16:17:12 +07:00
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_NOP(dev_priv))
|
2013-05-30 02:43:05 +07:00
|
|
|
return;
|
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_IBX(dev_priv))
|
2014-03-08 02:34:46 +07:00
|
|
|
mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
|
2017-09-09 07:42:55 +07:00
|
|
|
else if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
|
2014-03-08 02:34:46 +07:00
|
|
|
mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
|
2017-09-09 07:42:55 +07:00
|
|
|
else
|
|
|
|
mask = SDE_GMBUS_CPT;
|
drm/i915: report Gen5+ CPU and PCH FIFO underruns
In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
- after we receive one of these errors, we mask the interrupt, so
we won't get an "interrupt storm" and we also won't flood dmesg;
- at each mode set we enable the interrupts again, so we'll see each
message at most once per mode set;
- in the specific places where we need to ignore the errors, we
completely mask the interrupts.
The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).
V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-04-13 03:57:57 +07:00
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
gen3_assert_iir_is_zero(&dev_priv->uncore, SDEIIR);
|
2013-02-09 02:35:15 +07:00
|
|
|
I915_WRITE(SDEIMR, ~mask);
|
2017-01-27 16:39:18 +07:00
|
|
|
|
|
|
|
if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) ||
|
|
|
|
HAS_PCH_LPT(dev_priv))
|
2017-01-27 16:39:21 +07:00
|
|
|
ibx_hpd_detection_setup(dev_priv);
|
2017-01-27 16:39:18 +07:00
|
|
|
else
|
|
|
|
spt_hpd_detection_setup(dev_priv);
|
2013-02-09 02:35:15 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void gen5_gt_irq_postinstall(struct drm_i915_private *dev_priv)
|
2013-07-13 03:43:26 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2013-07-13 03:43:26 +07:00
|
|
|
u32 pm_irqs, gt_irqs;
|
|
|
|
|
|
|
|
pm_irqs = gt_irqs = 0;
|
|
|
|
|
|
|
|
dev_priv->gt_irq_mask = ~0;
|
2016-10-13 17:03:05 +07:00
|
|
|
if (HAS_L3_DPF(dev_priv)) {
|
2013-07-13 03:43:26 +07:00
|
|
|
/* L3 parity interrupt is always unmasked. */
|
2016-10-13 17:03:01 +07:00
|
|
|
dev_priv->gt_irq_mask = ~GT_PARITY_ERROR(dev_priv);
|
|
|
|
gt_irqs |= GT_PARITY_ERROR(dev_priv);
|
2013-07-13 03:43:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
gt_irqs |= GT_RENDER_USER_INTERRUPT;
|
drm/i915: replace IS_GEN<N> with IS_GEN(..., N)
Define IS_GEN() similarly to our IS_GEN_RANGE(). but use gen instead of
gen_mask to do the comparison. Now callers can pass then gen as a parameter,
so we don't require one macro for each gen.
The following spatch was used to convert the users of these macros:
@@
expression e;
@@
(
- IS_GEN2(e)
+ IS_GEN(e, 2)
|
- IS_GEN3(e)
+ IS_GEN(e, 3)
|
- IS_GEN4(e)
+ IS_GEN(e, 4)
|
- IS_GEN5(e)
+ IS_GEN(e, 5)
|
- IS_GEN6(e)
+ IS_GEN(e, 6)
|
- IS_GEN7(e)
+ IS_GEN(e, 7)
|
- IS_GEN8(e)
+ IS_GEN(e, 8)
|
- IS_GEN9(e)
+ IS_GEN(e, 9)
|
- IS_GEN10(e)
+ IS_GEN(e, 10)
|
- IS_GEN11(e)
+ IS_GEN(e, 11)
)
v2: use IS_GEN rather than GT_GEN and compare to info.gen rather than
using the bitmask
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181212181044.15886-2-lucas.demarchi@intel.com
2018-12-13 01:10:43 +07:00
|
|
|
if (IS_GEN(dev_priv, 5)) {
|
2016-07-01 23:23:21 +07:00
|
|
|
gt_irqs |= ILK_BSD_USER_INTERRUPT;
|
2013-07-13 03:43:26 +07:00
|
|
|
} else {
|
|
|
|
gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
|
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GT, dev_priv->gt_irq_mask, gt_irqs);
|
2013-07-13 03:43:26 +07:00
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 6) {
|
2014-12-15 23:59:27 +07:00
|
|
|
/*
|
|
|
|
* RPS interrupts will get enabled/disabled on demand when RPS
|
|
|
|
* itself is enabled/disabled.
|
|
|
|
*/
|
2019-03-06 01:03:30 +07:00
|
|
|
if (HAS_ENGINE(dev_priv, VECS0)) {
|
2013-07-13 03:43:26 +07:00
|
|
|
pm_irqs |= PM_VEBOX_USER_INTERRUPT;
|
2019-07-04 19:17:56 +07:00
|
|
|
dev_priv->gt.pm_ier |= PM_VEBOX_USER_INTERRUPT;
|
2016-10-12 23:24:30 +07:00
|
|
|
}
|
2013-07-13 03:43:26 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
dev_priv->gt.pm_imr = 0xffffffff;
|
|
|
|
GEN3_IRQ_INIT(uncore, GEN6_PM, dev_priv->gt.pm_imr, pm_irqs);
|
2013-07-13 03:43:26 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void ironlake_irq_postinstall(struct drm_i915_private *dev_priv)
|
2009-06-08 13:40:19 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2013-07-13 06:01:56 +07:00
|
|
|
u32 display_mask, extra_mask;
|
|
|
|
|
2016-11-16 15:55:38 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 7) {
|
2013-07-13 06:01:56 +07:00
|
|
|
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
|
2017-08-19 01:36:50 +07:00
|
|
|
DE_PCH_EVENT_IVB | DE_AUX_CHANNEL_A_IVB);
|
2013-07-13 06:01:56 +07:00
|
|
|
extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
|
2015-08-28 03:56:04 +07:00
|
|
|
DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB |
|
|
|
|
DE_DP_A_HOTPLUG_IVB);
|
2013-07-13 06:01:56 +07:00
|
|
|
} else {
|
|
|
|
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
|
2017-08-19 01:36:50 +07:00
|
|
|
DE_AUX_CHANNEL_A | DE_PIPEB_CRC_DONE |
|
|
|
|
DE_PIPEA_CRC_DONE | DE_POISON);
|
2015-08-28 03:56:03 +07:00
|
|
|
extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
|
|
|
|
DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
|
|
|
|
DE_DP_A_HOTPLUG);
|
2013-07-13 06:01:56 +07:00
|
|
|
}
|
2009-06-08 13:40:19 +07:00
|
|
|
|
2018-04-06 05:00:23 +07:00
|
|
|
if (IS_HASWELL(dev_priv)) {
|
2019-04-11 06:53:44 +07:00
|
|
|
gen3_assert_iir_is_zero(uncore, EDP_PSR_IIR);
|
2018-08-22 05:11:56 +07:00
|
|
|
intel_psr_irq_control(dev_priv, dev_priv->psr.debug);
|
2018-04-06 05:00:23 +07:00
|
|
|
display_mask |= DE_EDP_PSR_INT_HSW;
|
|
|
|
}
|
|
|
|
|
2010-12-04 18:30:53 +07:00
|
|
|
dev_priv->irq_mask = ~display_mask;
|
2009-06-08 13:40:19 +07:00
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
ibx_irq_pre_postinstall(dev_priv);
|
2014-04-02 01:37:22 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, DE, dev_priv->irq_mask,
|
|
|
|
display_mask | extra_mask);
|
2009-06-08 13:40:19 +07:00
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
gen5_gt_irq_postinstall(dev_priv);
|
2009-06-08 13:40:19 +07:00
|
|
|
|
2017-01-27 16:39:21 +07:00
|
|
|
ilk_hpd_detection_setup(dev_priv);
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
ibx_irq_postinstall(dev_priv);
|
2011-09-20 03:31:02 +07:00
|
|
|
|
2016-10-13 17:02:58 +07:00
|
|
|
if (IS_IRONLAKE_M(dev_priv)) {
|
2013-06-27 18:44:59 +07:00
|
|
|
/* Enable PCU event interrupts
|
|
|
|
*
|
|
|
|
* spinlocking not required here for correctness since interrupt
|
2013-06-27 18:44:58 +07:00
|
|
|
* setup is guaranteed to run in single-threaded context. But we
|
|
|
|
* need it to make the assert_spin_locked happy. */
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2015-11-23 23:06:16 +07:00
|
|
|
ilk_enable_display_irq(dev_priv, DE_PCU_EVENT);
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2010-01-30 02:27:07 +07:00
|
|
|
}
|
2009-06-08 13:40:19 +07:00
|
|
|
}
|
|
|
|
|
2014-03-05 00:23:07 +07:00
|
|
|
void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2014-03-05 00:23:07 +07:00
|
|
|
|
|
|
|
if (dev_priv->display_irqs_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->display_irqs_enabled = true;
|
|
|
|
|
2016-04-11 20:56:27 +07:00
|
|
|
if (intel_irqs_enabled(dev_priv)) {
|
|
|
|
vlv_display_irq_reset(dev_priv);
|
2016-04-12 22:56:14 +07:00
|
|
|
vlv_display_irq_postinstall(dev_priv);
|
2016-04-11 20:56:27 +07:00
|
|
|
}
|
2014-03-05 00:23:07 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2014-03-05 00:23:07 +07:00
|
|
|
|
|
|
|
if (!dev_priv->display_irqs_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->display_irqs_enabled = false;
|
|
|
|
|
2014-09-08 19:21:09 +07:00
|
|
|
if (intel_irqs_enabled(dev_priv))
|
2016-04-12 22:56:14 +07:00
|
|
|
vlv_display_irq_reset(dev_priv);
|
2014-03-05 00:23:07 +07:00
|
|
|
}
|
|
|
|
|
2014-10-31 00:43:00 +07:00
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void valleyview_irq_postinstall(struct drm_i915_private *dev_priv)
|
2014-10-31 00:43:00 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
gen5_gt_irq_postinstall(dev_priv);
|
2012-03-29 03:39:38 +07:00
|
|
|
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2016-04-11 20:56:25 +07:00
|
|
|
if (dev_priv->display_irqs_enabled)
|
|
|
|
vlv_display_irq_postinstall(dev_priv);
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
|
2012-03-29 03:39:38 +07:00
|
|
|
I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
|
2016-04-14 01:19:48 +07:00
|
|
|
POSTING_READ(VLV_MASTER_IER);
|
2012-12-11 20:05:07 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
static void gen8_gt_irq_postinstall(struct drm_i915_private *i915)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2019-07-04 19:17:56 +07:00
|
|
|
struct intel_gt *gt = &i915->gt;
|
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
2019-04-11 06:53:44 +07:00
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
/* These are interrupts we'll toggle with the ring mask register */
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 gt_interrupts[] = {
|
2019-03-06 01:03:30 +07:00
|
|
|
(GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
|
|
|
|
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
|
|
|
|
GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT |
|
|
|
|
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT),
|
|
|
|
|
|
|
|
(GT_RENDER_USER_INTERRUPT << GEN8_VCS0_IRQ_SHIFT |
|
|
|
|
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS0_IRQ_SHIFT |
|
|
|
|
GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
|
|
|
|
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT),
|
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
0,
|
2019-03-06 01:03:30 +07:00
|
|
|
|
|
|
|
(GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT |
|
|
|
|
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT)
|
|
|
|
};
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2019-07-04 19:17:56 +07:00
|
|
|
gt->pm_ier = 0x0;
|
|
|
|
gt->pm_imr = ~gt->pm_ier;
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_INIT_NDX(uncore, GT, 0, ~gt_interrupts[0], gt_interrupts[0]);
|
|
|
|
GEN8_IRQ_INIT_NDX(uncore, GT, 1, ~gt_interrupts[1], gt_interrupts[1]);
|
2014-12-15 23:59:27 +07:00
|
|
|
/*
|
|
|
|
* RPS interrupts will get enabled/disabled on demand when RPS itself
|
2016-10-12 23:24:31 +07:00
|
|
|
* is enabled/disabled. Same wil be the case for GuC interrupts.
|
2014-12-15 23:59:27 +07:00
|
|
|
*/
|
2019-07-04 19:17:56 +07:00
|
|
|
GEN8_IRQ_INIT_NDX(uncore, GT, 2, gt->pm_imr, gt->pm_ier);
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_INIT_NDX(uncore, GT, 3, ~gt_interrupts[3], gt_interrupts[3]);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
|
|
|
|
2019-01-16 16:15:24 +07:00
|
|
|
u32 de_pipe_masked = GEN8_PIPE_CDCLK_CRC_DONE;
|
|
|
|
u32 de_pipe_enables;
|
2015-08-28 03:56:06 +07:00
|
|
|
u32 de_port_masked = GEN8_AUX_CHANNEL_A;
|
|
|
|
u32 de_port_enables;
|
2018-06-16 07:05:28 +07:00
|
|
|
u32 de_misc_masked = GEN8_DE_EDP_PSR;
|
2015-08-28 03:56:06 +07:00
|
|
|
enum pipe pipe;
|
2014-03-21 03:45:01 +07:00
|
|
|
|
2018-06-16 07:05:28 +07:00
|
|
|
if (INTEL_GEN(dev_priv) <= 10)
|
|
|
|
de_misc_masked |= GEN8_DE_MISC_GSE;
|
|
|
|
|
2017-07-19 01:28:00 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 9) {
|
2017-08-19 01:36:50 +07:00
|
|
|
de_pipe_masked |= GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
|
2015-08-28 03:56:06 +07:00
|
|
|
de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
|
|
|
|
GEN9_AUX_CHANNEL_D;
|
2016-12-02 15:23:49 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv))
|
2015-08-28 03:56:06 +07:00
|
|
|
de_port_masked |= BXT_DE_PORT_GMBUS;
|
|
|
|
} else {
|
2017-08-19 01:36:50 +07:00
|
|
|
de_pipe_masked |= GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
|
2015-08-28 03:56:06 +07:00
|
|
|
}
|
2014-03-21 03:45:01 +07:00
|
|
|
|
2018-06-12 07:25:12 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
de_port_masked |= ICL_AUX_CHANNEL_E;
|
|
|
|
|
2018-05-22 07:25:35 +07:00
|
|
|
if (IS_CNL_WITH_PORT_F(dev_priv) || INTEL_GEN(dev_priv) >= 11)
|
2018-01-30 06:22:15 +07:00
|
|
|
de_port_masked |= CNL_AUX_CHANNEL_F;
|
|
|
|
|
2014-03-21 03:45:01 +07:00
|
|
|
de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
|
|
|
|
GEN8_PIPE_FIFO_UNDERRUN;
|
|
|
|
|
2015-08-28 03:56:06 +07:00
|
|
|
de_port_enables = de_port_masked;
|
2016-12-02 15:23:49 +07:00
|
|
|
if (IS_GEN9_LP(dev_priv))
|
2015-08-28 03:56:11 +07:00
|
|
|
de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK;
|
|
|
|
else if (IS_BROADWELL(dev_priv))
|
2015-08-28 03:56:06 +07:00
|
|
|
de_port_enables |= GEN8_PORT_DP_A_HOTPLUG;
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
gen3_assert_iir_is_zero(uncore, EDP_PSR_IIR);
|
2018-04-05 08:37:17 +07:00
|
|
|
intel_psr_irq_control(dev_priv, dev_priv->psr.debug);
|
2018-04-04 04:24:18 +07:00
|
|
|
|
2017-10-10 17:17:04 +07:00
|
|
|
for_each_pipe(dev_priv, pipe) {
|
|
|
|
dev_priv->de_irq_mask[pipe] = ~de_pipe_masked;
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2014-09-30 15:56:39 +07:00
|
|
|
if (intel_display_power_is_enabled(dev_priv,
|
2014-07-04 21:50:29 +07:00
|
|
|
POWER_DOMAIN_PIPE(pipe)))
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN8_IRQ_INIT_NDX(uncore, DE_PIPE, pipe,
|
2014-07-04 21:50:29 +07:00
|
|
|
dev_priv->de_irq_mask[pipe],
|
|
|
|
de_pipe_enables);
|
2017-10-10 17:17:04 +07:00
|
|
|
}
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GEN8_DE_PORT_, ~de_port_masked, de_port_enables);
|
|
|
|
GEN3_IRQ_INIT(uncore, GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked);
|
2017-01-27 16:39:18 +07:00
|
|
|
|
2018-06-16 07:05:29 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 11) {
|
|
|
|
u32 de_hpd_masked = 0;
|
2018-06-16 07:05:30 +07:00
|
|
|
u32 de_hpd_enables = GEN11_DE_TC_HOTPLUG_MASK |
|
|
|
|
GEN11_DE_TBT_HOTPLUG_MASK;
|
2018-06-16 07:05:29 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GEN11_DE_HPD_, ~de_hpd_masked,
|
|
|
|
de_hpd_enables);
|
2018-06-16 07:05:29 +07:00
|
|
|
gen11_hpd_detection_setup(dev_priv);
|
|
|
|
} else if (IS_GEN9_LP(dev_priv)) {
|
2017-01-27 16:39:18 +07:00
|
|
|
bxt_hpd_detection_setup(dev_priv);
|
2018-06-16 07:05:29 +07:00
|
|
|
} else if (IS_BROADWELL(dev_priv)) {
|
2017-01-27 16:39:21 +07:00
|
|
|
ilk_hpd_detection_setup(dev_priv);
|
2018-06-16 07:05:29 +07:00
|
|
|
}
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void gen8_irq_postinstall(struct drm_i915_private *dev_priv)
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
{
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_SPLIT(dev_priv))
|
2019-06-20 17:33:34 +07:00
|
|
|
ibx_irq_pre_postinstall(dev_priv);
|
2014-04-02 01:37:22 +07:00
|
|
|
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
gen8_gt_irq_postinstall(dev_priv);
|
|
|
|
gen8_de_irq_postinstall(dev_priv);
|
|
|
|
|
2016-10-13 17:02:53 +07:00
|
|
|
if (HAS_PCH_SPLIT(dev_priv))
|
2019-06-20 17:33:34 +07:00
|
|
|
ibx_irq_postinstall(dev_priv);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
|
2019-03-20 01:35:40 +07:00
|
|
|
gen8_master_intr_enable(dev_priv->uncore.regs);
|
drm/i915/bdw: Implement interrupt changes
The interrupt handling implementation remains the same as previous
generations with the 4 types of registers, status, identity, mask, and
enable. However the layout of where the bits go have changed entirely.
To address these changes, all of the interrupt vfuncs needed special
gen8 code.
The way it works is there is a top level status register now which
informs the interrupt service routine which unit caused the interrupt,
and therefore which interrupt registers to read to process the
interrupt. For display the division is quite logical, a set of interrupt
registers for each pipe, and in addition to those, a set each for "misc"
and port.
For GT the things get a bit hairy, as seen by the code. Each of the GT
units has it's own bits defined. They all look *very similar* and
resides in 16 bits of a GT register. As an example, RCS and BCS share
register 0. To compact the code a bit, at a slight expense to
complexity, this is exactly how the code works as well. 2 structures are
added to the ring buffer so that our ring buffer interrupt handling code
knows which ring shares the interrupt registers, and a shift value (ie.
the top or bottom 16 bits of the register).
The above allows us to kept the interrupt register caching scheme, the
per interrupt enables, and the code to mask and unmask interrupts
relatively clean (again at the cost of some more complexity).
Most of the GT units mentioned above are command streamers, and so the
symmetry should work quite well for even the yet to be implemented rings
which Broadwell adds.
v2: Fixes up a couple of bugs, and is more verbose about errors in the
Broadwell interrupt handler.
v3: fix DE_MISC IER offset
v4: Simplify interrupts:
I totally misread the docs the first time I implemented interrupts, and
so this should greatly simplify the mess. Unlike GEN6, we never touch
the regular mask registers in irq_get/put.
v5: Rebased on to of recent pch hotplug setup changes.
v6: Fixup on top of moving num_pipes to intel_info.
v7: Rebased on top of Egbert Eich's hpd irq handling rework. Also
wired up ibx_hpd_irq_setup for gen8.
v8: Rebase on top of Jani's asle handling rework.
v9: Rebase on top of Ben's VECS enabling for Haswell, where he
unfortunately went OCD on the gt irq #defines. Not that they're still
not yet fully consistent:
- Used the GT_RENDER_ #defines + bdw shifts.
- Dropped the shift from the L3_PARITY stuff, seemed clearer.
- s/irq_refcount/irq_refcount.gt/
v10: Squash in VECS enabling patches and the gen8_gt_irq_handler
refactoring from Zhao Yakui <yakui.zhao@intel.com>
v11: Rebase on top of the interrupt cleanups in upstream.
v12: Rebase on top of Ben's DPF changes in upstream.
v13: Drop bdw from the HAS_L3_DPF feature flag for now, it's unclear what
exactly needs to be done. Requested by Ben.
v14: Fix the patch.
- Drop the mask of reserved bits and assorted logic, it doesn't match
the spec.
- Do the posting read inconditionally instead of commenting it out.
- Add a GEN8_MASTER_IRQ_CONTROL definition and use it.
- Fix up the GEN8_PIPE interrupt defines and give the GEN8_ prefixes -
we actually will need to use them.
- Enclose macros in do {} while (0) (checkpatch).
- Clear DE_MISC interrupt bits only after having processed them.
- Fix whitespace fail (checkpatch).
- Fix overtly long lines where appropriate (checkpatch).
- Don't use typedef'ed private_t (maintainer-scripts).
- Align the function parameter list correctly.
Signed-off-by: Ben Widawsky <ben@bwidawsk.net> (v4)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
bikeshed
2013-11-03 11:07:09 +07:00
|
|
|
}
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
static void gen11_gt_irq_postinstall(struct intel_gt *gt)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
|
|
|
const u32 irqs = GT_RENDER_USER_INTERRUPT | GT_CONTEXT_SWITCH_INTERRUPT;
|
2019-07-04 19:17:55 +07:00
|
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
|
|
const u32 dmask = irqs << 16 | irqs;
|
|
|
|
const u32 smask = irqs << 16;
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
BUILD_BUG_ON(irqs & 0xffff0000);
|
|
|
|
|
|
|
|
/* Enable RCS, BCS, VCS and VECS class interrupts. */
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_RENDER_COPY_INTR_ENABLE, dmask);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS_VECS_INTR_ENABLE, dmask);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
|
|
|
/* Unmask irqs on RCS, BCS, VCS and VECS engines. */
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_RCS0_RSVD_INTR_MASK, ~smask);
|
|
|
|
intel_uncore_write(uncore, GEN11_BCS_RSVD_INTR_MASK, ~smask);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS0_VCS1_INTR_MASK, ~dmask);
|
|
|
|
intel_uncore_write(uncore, GEN11_VCS2_VCS3_INTR_MASK, ~dmask);
|
|
|
|
intel_uncore_write(uncore, GEN11_VECS0_VECS1_INTR_MASK, ~dmask);
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2018-04-05 21:00:50 +07:00
|
|
|
/*
|
|
|
|
* RPS interrupts will get enabled/disabled on demand when RPS itself
|
|
|
|
* is enabled/disabled.
|
|
|
|
*/
|
2019-07-04 19:17:56 +07:00
|
|
|
gt->pm_ier = 0x0;
|
|
|
|
gt->pm_imr = ~gt->pm_ier;
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_GPM_WGBOXPERF_INTR_ENABLE, 0);
|
|
|
|
intel_uncore_write(uncore, GEN11_GPM_WGBOXPERF_INTR_MASK, ~0);
|
2019-05-28 01:36:08 +07:00
|
|
|
|
|
|
|
/* Same thing for GuC interrupts */
|
2019-07-04 19:17:55 +07:00
|
|
|
intel_uncore_write(uncore, GEN11_GUC_SG_INTR_ENABLE, 0);
|
|
|
|
intel_uncore_write(uncore, GEN11_GUC_SG_INTR_MASK, ~0);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void icp_irq_postinstall(struct drm_i915_private *dev_priv)
|
2018-06-27 03:52:23 +07:00
|
|
|
{
|
|
|
|
u32 mask = SDE_GMBUS_ICP;
|
|
|
|
|
|
|
|
WARN_ON(I915_READ(SDEIER) != 0);
|
|
|
|
I915_WRITE(SDEIER, 0xffffffff);
|
|
|
|
POSTING_READ(SDEIER);
|
|
|
|
|
2019-04-11 06:53:43 +07:00
|
|
|
gen3_assert_iir_is_zero(&dev_priv->uncore, SDEIIR);
|
2018-06-27 03:52:23 +07:00
|
|
|
I915_WRITE(SDEIMR, ~mask);
|
|
|
|
|
|
|
|
icp_hpd_detection_setup(dev_priv);
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void gen11_irq_postinstall(struct drm_i915_private *dev_priv)
|
2018-02-28 17:11:53 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2018-06-16 07:05:28 +07:00
|
|
|
u32 gu_misc_masked = GEN11_GU_MISC_GSE;
|
2018-02-28 17:11:53 +07:00
|
|
|
|
2019-03-14 04:43:07 +07:00
|
|
|
if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP)
|
2019-06-20 17:33:34 +07:00
|
|
|
icp_irq_postinstall(dev_priv);
|
2018-06-27 03:52:23 +07:00
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_gt_irq_postinstall(&dev_priv->gt);
|
2018-02-28 17:11:53 +07:00
|
|
|
gen8_de_irq_postinstall(dev_priv);
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GEN11_GU_MISC_, ~gu_misc_masked, gu_misc_masked);
|
2018-06-16 07:05:28 +07:00
|
|
|
|
2018-02-28 17:11:53 +07:00
|
|
|
I915_WRITE(GEN11_DISPLAY_INT_CTL, GEN11_DISPLAY_IRQ_ENABLE);
|
|
|
|
|
2019-07-04 19:17:54 +07:00
|
|
|
gen11_master_intr_enable(uncore->regs);
|
2019-01-23 09:32:27 +07:00
|
|
|
POSTING_READ(GEN11_GFX_MSTR_IRQ);
|
2018-02-28 17:11:53 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void cherryview_irq_postinstall(struct drm_i915_private *dev_priv)
|
2014-04-10 00:40:52 +07:00
|
|
|
{
|
|
|
|
gen8_gt_irq_postinstall(dev_priv);
|
|
|
|
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2016-04-11 20:56:25 +07:00
|
|
|
if (dev_priv->display_irqs_enabled)
|
|
|
|
vlv_display_irq_postinstall(dev_priv);
|
2016-04-12 22:56:14 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
|
|
|
|
2016-04-14 01:19:47 +07:00
|
|
|
I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
|
2014-04-10 00:40:52 +07:00
|
|
|
POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i8xx_irq_reset(struct drm_i915_private *dev_priv)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2006-02-18 11:17:04 +07:00
|
|
|
|
2017-08-19 01:36:51 +07:00
|
|
|
i9xx_pipestat_irq_reset(dev_priv);
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN2_IRQ_RESET(uncore);
|
2012-04-23 03:13:57 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i8xx_irq_postinstall(struct drm_i915_private *dev_priv)
|
2012-04-23 03:13:57 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2017-08-19 01:36:54 +07:00
|
|
|
u16 enable_mask;
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
intel_uncore_write16(uncore,
|
|
|
|
EMR,
|
|
|
|
~(I915_ERROR_PAGE_TABLE |
|
|
|
|
I915_ERROR_MEMORY_REFRESH));
|
2012-04-23 03:13:57 +07:00
|
|
|
|
|
|
|
/* Unmask the interrupts that we always want on. */
|
|
|
|
dev_priv->irq_mask =
|
|
|
|
~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
2018-06-12 03:02:58 +07:00
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
|
|
|
I915_MASTER_ERROR_INTERRUPT);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2017-08-19 01:36:54 +07:00
|
|
|
enable_mask =
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
2018-06-12 03:02:58 +07:00
|
|
|
I915_MASTER_ERROR_INTERRUPT |
|
2017-08-19 01:36:54 +07:00
|
|
|
I915_USER_INTERRUPT;
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN2_IRQ_INIT(uncore, dev_priv->irq_mask, enable_mask);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2013-10-17 03:55:56 +07:00
|
|
|
/* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
|
* just to make the assert_spin_locked check happy. */
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2014-02-10 23:42:47 +07:00
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
|
|
|
i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2012-04-23 03:13:57 +07:00
|
|
|
}
|
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
static void i8xx_error_irq_ack(struct drm_i915_private *i915,
|
2018-06-12 03:02:57 +07:00
|
|
|
u16 *eir, u16 *eir_stuck)
|
|
|
|
{
|
2019-06-11 17:45:48 +07:00
|
|
|
struct intel_uncore *uncore = &i915->uncore;
|
2018-06-12 03:02:57 +07:00
|
|
|
u16 emr;
|
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
*eir = intel_uncore_read16(uncore, EIR);
|
2018-06-12 03:02:57 +07:00
|
|
|
|
|
|
|
if (*eir)
|
2019-06-11 17:45:48 +07:00
|
|
|
intel_uncore_write16(uncore, EIR, *eir);
|
2018-06-12 03:02:57 +07:00
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
*eir_stuck = intel_uncore_read16(uncore, EIR);
|
2018-06-12 03:02:57 +07:00
|
|
|
if (*eir_stuck == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Toggle all EMR bits to make sure we get an edge
|
|
|
|
* in the ISR master error bit if we don't clear
|
|
|
|
* all the EIR bits. Otherwise the edge triggered
|
|
|
|
* IIR on i965/g4x wouldn't notice that an interrupt
|
|
|
|
* is still pending. Also some EIR bits can't be
|
|
|
|
* cleared except by handling the underlying error
|
|
|
|
* (or by a GPU reset) so we mask any bit that
|
|
|
|
* remains set.
|
|
|
|
*/
|
2019-06-11 17:45:48 +07:00
|
|
|
emr = intel_uncore_read16(uncore, EMR);
|
|
|
|
intel_uncore_write16(uncore, EMR, 0xffff);
|
|
|
|
intel_uncore_write16(uncore, EMR, emr | *eir_stuck);
|
2018-06-12 03:02:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void i8xx_error_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u16 eir, u16 eir_stuck)
|
|
|
|
{
|
|
|
|
DRM_DEBUG("Master Error: EIR 0x%04x\n", eir);
|
|
|
|
|
|
|
|
if (eir_stuck)
|
|
|
|
DRM_DEBUG_DRIVER("EIR stuck: 0x%04x, masked\n", eir_stuck);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i9xx_error_irq_ack(struct drm_i915_private *dev_priv,
|
|
|
|
u32 *eir, u32 *eir_stuck)
|
|
|
|
{
|
|
|
|
u32 emr;
|
|
|
|
|
|
|
|
*eir = I915_READ(EIR);
|
|
|
|
|
|
|
|
I915_WRITE(EIR, *eir);
|
|
|
|
|
|
|
|
*eir_stuck = I915_READ(EIR);
|
|
|
|
if (*eir_stuck == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Toggle all EMR bits to make sure we get an edge
|
|
|
|
* in the ISR master error bit if we don't clear
|
|
|
|
* all the EIR bits. Otherwise the edge triggered
|
|
|
|
* IIR on i965/g4x wouldn't notice that an interrupt
|
|
|
|
* is still pending. Also some EIR bits can't be
|
|
|
|
* cleared except by handling the underlying error
|
|
|
|
* (or by a GPU reset) so we mask any bit that
|
|
|
|
* remains set.
|
|
|
|
*/
|
|
|
|
emr = I915_READ(EMR);
|
|
|
|
I915_WRITE(EMR, 0xffffffff);
|
|
|
|
I915_WRITE(EMR, emr | *eir_stuck);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i9xx_error_irq_handler(struct drm_i915_private *dev_priv,
|
|
|
|
u32 eir, u32 eir_stuck)
|
|
|
|
{
|
|
|
|
DRM_DEBUG("Master Error, EIR 0x%08x\n", eir);
|
|
|
|
|
|
|
|
if (eir_stuck)
|
|
|
|
DRM_DEBUG_DRIVER("EIR stuck: 0x%08x, masked\n", eir_stuck);
|
|
|
|
}
|
|
|
|
|
2012-10-02 20:10:55 +07:00
|
|
|
static irqreturn_t i8xx_irq_handler(int irq, void *arg)
|
2012-04-23 03:13:57 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2017-08-19 01:37:00 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
do {
|
2017-08-19 01:36:59 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES] = {};
|
2018-06-12 03:02:57 +07:00
|
|
|
u16 eir = 0, eir_stuck = 0;
|
2017-08-19 01:37:00 +07:00
|
|
|
u16 iir;
|
2017-08-19 01:36:59 +07:00
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
iir = intel_uncore_read16(&dev_priv->uncore, GEN2_IIR);
|
2017-08-19 01:37:00 +07:00
|
|
|
if (iir == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = IRQ_HANDLED;
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2017-08-19 01:36:59 +07:00
|
|
|
/* Call regardless, as some status bits might not be
|
|
|
|
* signalled in iir */
|
|
|
|
i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i8xx_error_irq_ack(dev_priv, &eir, &eir_stuck);
|
|
|
|
|
2019-06-11 17:45:48 +07:00
|
|
|
intel_uncore_write16(&dev_priv->uncore, GEN2_IIR, iir);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
|
|
|
if (iir & I915_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[RCS0]);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i8xx_error_irq_handler(dev_priv, eir, eir_stuck);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
i8xx_pipestat_irq_handler(dev_priv, iir, pipe_stats);
|
|
|
|
} while (0);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2012-04-23 03:13:57 +07:00
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
return ret;
|
2012-04-23 03:13:57 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i915_irq_reset(struct drm_i915_private *dev_priv)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2016-11-07 16:29:20 +07:00
|
|
|
if (I915_HAS_HOTPLUG(dev_priv)) {
|
2015-09-23 21:15:27 +07:00
|
|
|
i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
|
2012-04-25 04:59:44 +07:00
|
|
|
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
|
|
|
}
|
|
|
|
|
2017-08-19 01:36:51 +07:00
|
|
|
i9xx_pipestat_irq_reset(dev_priv);
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN2_);
|
2012-04-25 04:59:44 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i915_irq_postinstall(struct drm_i915_private *dev_priv)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2012-04-25 04:59:50 +07:00
|
|
|
u32 enable_mask;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2017-08-19 01:36:55 +07:00
|
|
|
I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE |
|
|
|
|
I915_ERROR_MEMORY_REFRESH));
|
2012-04-25 04:59:50 +07:00
|
|
|
|
|
|
|
/* Unmask the interrupts that we always want on. */
|
|
|
|
dev_priv->irq_mask =
|
|
|
|
~(I915_ASLE_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
2018-06-12 03:02:58 +07:00
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
|
|
|
I915_MASTER_ERROR_INTERRUPT);
|
2012-04-25 04:59:50 +07:00
|
|
|
|
|
|
|
enable_mask =
|
|
|
|
I915_ASLE_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
2018-06-12 03:02:58 +07:00
|
|
|
I915_MASTER_ERROR_INTERRUPT |
|
2012-04-25 04:59:50 +07:00
|
|
|
I915_USER_INTERRUPT;
|
|
|
|
|
2016-11-07 16:29:20 +07:00
|
|
|
if (I915_HAS_HOTPLUG(dev_priv)) {
|
2012-04-25 04:59:44 +07:00
|
|
|
/* Enable in IER... */
|
|
|
|
enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
|
|
|
|
/* and unmask in IMR */
|
|
|
|
dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT;
|
|
|
|
}
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GEN2_, dev_priv->irq_mask, enable_mask);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2013-10-17 03:55:56 +07:00
|
|
|
/* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
|
* just to make the assert_spin_locked check happy. */
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2014-02-10 23:42:47 +07:00
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
|
|
|
i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2013-10-17 03:55:56 +07:00
|
|
|
|
2017-08-19 01:36:57 +07:00
|
|
|
i915_enable_asle_pipestat(dev_priv);
|
2012-12-11 20:05:07 +07:00
|
|
|
}
|
|
|
|
|
2012-10-02 20:10:55 +07:00
|
|
|
static irqreturn_t i915_irq_handler(int irq, void *arg)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2017-08-19 01:37:00 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2012-04-25 04:59:50 +07:00
|
|
|
do {
|
2017-08-19 01:36:59 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES] = {};
|
2018-06-12 03:02:57 +07:00
|
|
|
u32 eir = 0, eir_stuck = 0;
|
2017-08-19 01:37:00 +07:00
|
|
|
u32 hotplug_status = 0;
|
|
|
|
u32 iir;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
drm/i915: add GEN2_ prefix to the I{E, I, M, S}R registers
This discussion started because we use token pasting in the
GEN{2,3}_IRQ_INIT and GEN{2,3}_IRQ_RESET macros, so gen2-4 passes an
empty argument to those macros, making the code a little weird. The
original proposal was to just add a comment as the empty argument, but
Ville suggested we just add a prefix to the registers, and that indeed
sounds like a more elegant solution.
Now doing this is kinda against our rules for register naming since we
only add gens or platform names as register prefixes when the given
gen/platform changes a register that already existed before. On the
other hand, we have so many instances of IIR/IMR in comments that
adding a prefix would make the users of these register more easily
findable, in addition to make our token pasting macros actually
readable. So IMHO opening an exception here is worth it.
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410235344.31199-4-paulo.r.zanoni@intel.com
2019-04-11 06:53:42 +07:00
|
|
|
iir = I915_READ(GEN2_IIR);
|
2017-08-19 01:37:00 +07:00
|
|
|
if (iir == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
|
|
|
|
if (I915_HAS_HOTPLUG(dev_priv) &&
|
|
|
|
iir & I915_DISPLAY_PORT_INTERRUPT)
|
|
|
|
hotplug_status = i9xx_hpd_irq_ack(dev_priv);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2017-08-19 01:36:59 +07:00
|
|
|
/* Call regardless, as some status bits might not be
|
|
|
|
* signalled in iir */
|
|
|
|
i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i9xx_error_irq_ack(dev_priv, &eir, &eir_stuck);
|
|
|
|
|
drm/i915: add GEN2_ prefix to the I{E, I, M, S}R registers
This discussion started because we use token pasting in the
GEN{2,3}_IRQ_INIT and GEN{2,3}_IRQ_RESET macros, so gen2-4 passes an
empty argument to those macros, making the code a little weird. The
original proposal was to just add a comment as the empty argument, but
Ville suggested we just add a prefix to the registers, and that indeed
sounds like a more elegant solution.
Now doing this is kinda against our rules for register naming since we
only add gens or platform names as register prefixes when the given
gen/platform changes a register that already existed before. On the
other hand, we have so many instances of IIR/IMR in comments that
adding a prefix would make the users of these register more easily
findable, in addition to make our token pasting macros actually
readable. So IMHO opening an exception here is worth it.
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410235344.31199-4-paulo.r.zanoni@intel.com
2019-04-11 06:53:42 +07:00
|
|
|
I915_WRITE(GEN2_IIR, iir);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
|
|
|
if (iir & I915_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[RCS0]);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i9xx_error_irq_handler(dev_priv, eir, eir_stuck);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
if (hotplug_status)
|
|
|
|
i9xx_hpd_irq_handler(dev_priv, hotplug_status);
|
|
|
|
|
|
|
|
i915_pipestat_irq_handler(dev_priv, iir, pipe_stats);
|
|
|
|
} while (0);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2012-04-25 04:59:44 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i965_irq_reset(struct drm_i915_private *dev_priv)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2015-09-23 21:15:27 +07:00
|
|
|
i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
|
2012-05-12 00:01:31 +07:00
|
|
|
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2017-08-19 01:36:51 +07:00
|
|
|
i9xx_pipestat_irq_reset(dev_priv);
|
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_RESET(uncore, GEN2_);
|
2012-04-25 04:59:44 +07:00
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static void i965_irq_postinstall(struct drm_i915_private *dev_priv)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-04-11 06:53:44 +07:00
|
|
|
struct intel_uncore *uncore = &dev_priv->uncore;
|
2012-04-25 04:59:51 +07:00
|
|
|
u32 enable_mask;
|
2012-04-25 04:59:44 +07:00
|
|
|
u32 error_mask;
|
|
|
|
|
2017-08-19 01:36:55 +07:00
|
|
|
/*
|
|
|
|
* Enable some error detection, note the instruction error mask
|
|
|
|
* bit is reserved, so we leave it masked.
|
|
|
|
*/
|
|
|
|
if (IS_G4X(dev_priv)) {
|
|
|
|
error_mask = ~(GM45_ERROR_PAGE_TABLE |
|
|
|
|
GM45_ERROR_MEM_PRIV |
|
|
|
|
GM45_ERROR_CP_PRIV |
|
|
|
|
I915_ERROR_MEMORY_REFRESH);
|
|
|
|
} else {
|
|
|
|
error_mask = ~(I915_ERROR_PAGE_TABLE |
|
|
|
|
I915_ERROR_MEMORY_REFRESH);
|
|
|
|
}
|
|
|
|
I915_WRITE(EMR, error_mask);
|
|
|
|
|
2012-04-25 04:59:44 +07:00
|
|
|
/* Unmask the interrupts that we always want on. */
|
2017-08-19 01:36:57 +07:00
|
|
|
dev_priv->irq_mask =
|
|
|
|
~(I915_ASLE_INTERRUPT |
|
|
|
|
I915_DISPLAY_PORT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
2018-06-12 03:02:57 +07:00
|
|
|
I915_MASTER_ERROR_INTERRUPT);
|
2012-04-25 04:59:51 +07:00
|
|
|
|
2017-08-19 01:36:57 +07:00
|
|
|
enable_mask =
|
|
|
|
I915_ASLE_INTERRUPT |
|
|
|
|
I915_DISPLAY_PORT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
|
|
|
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
2018-06-12 03:02:57 +07:00
|
|
|
I915_MASTER_ERROR_INTERRUPT |
|
2017-08-19 01:36:57 +07:00
|
|
|
I915_USER_INTERRUPT;
|
2012-04-25 04:59:51 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
if (IS_G4X(dev_priv))
|
2012-04-25 04:59:51 +07:00
|
|
|
enable_mask |= I915_BSD_USER_INTERRUPT;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2019-04-11 06:53:44 +07:00
|
|
|
GEN3_IRQ_INIT(uncore, GEN2_, dev_priv->irq_mask, enable_mask);
|
2017-08-19 01:36:57 +07:00
|
|
|
|
2013-06-27 22:52:10 +07:00
|
|
|
/* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
|
* just to make the assert_spin_locked check happy. */
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
2014-02-10 23:42:47 +07:00
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
|
|
|
|
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
|
|
|
i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
|
2014-09-15 19:55:27 +07:00
|
|
|
spin_unlock_irq(&dev_priv->irq_lock);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
i915_enable_asle_pipestat(dev_priv);
|
2012-12-11 20:05:07 +07:00
|
|
|
}
|
|
|
|
|
2016-05-06 20:48:28 +07:00
|
|
|
static void i915_hpd_irq_setup(struct drm_i915_private *dev_priv)
|
2012-12-11 20:05:07 +07:00
|
|
|
{
|
|
|
|
u32 hotplug_en;
|
|
|
|
|
2017-03-02 20:28:01 +07:00
|
|
|
lockdep_assert_held(&dev_priv->irq_lock);
|
2013-06-27 22:52:15 +07:00
|
|
|
|
2015-01-09 19:21:13 +07:00
|
|
|
/* Note HDMI and DP share hotplug bits */
|
|
|
|
/* enable bits are the same for all generations */
|
2016-05-06 20:48:28 +07:00
|
|
|
hotplug_en = intel_hpd_enabled_irqs(dev_priv, hpd_mask_i915);
|
2015-01-09 19:21:13 +07:00
|
|
|
/* Programming the CRT detection parameters tends
|
|
|
|
to generate a spurious hotplug event about three
|
|
|
|
seconds later. So just do it once.
|
|
|
|
*/
|
2016-05-06 20:48:28 +07:00
|
|
|
if (IS_G4X(dev_priv))
|
2015-01-09 19:21:13 +07:00
|
|
|
hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
|
|
|
|
hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
|
|
|
|
|
|
|
|
/* Ignore TV since it's buggy */
|
2015-09-23 21:15:27 +07:00
|
|
|
i915_hotplug_interrupt_update_locked(dev_priv,
|
2015-10-21 21:22:43 +07:00
|
|
|
HOTPLUG_INT_EN_MASK |
|
|
|
|
CRT_HOTPLUG_VOLTAGE_COMPARE_MASK |
|
|
|
|
CRT_HOTPLUG_ACTIVATION_PERIOD_64,
|
|
|
|
hotplug_en);
|
2012-04-25 04:59:44 +07:00
|
|
|
}
|
|
|
|
|
2012-10-02 20:10:55 +07:00
|
|
|
static irqreturn_t i965_irq_handler(int irq, void *arg)
|
2012-04-25 04:59:44 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
struct drm_i915_private *dev_priv = arg;
|
2017-08-19 01:37:00 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2015-02-24 16:14:30 +07:00
|
|
|
if (!intel_irqs_enabled(dev_priv))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2015-12-16 07:52:19 +07:00
|
|
|
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
|
2019-06-14 06:21:51 +07:00
|
|
|
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
do {
|
2017-08-19 01:36:59 +07:00
|
|
|
u32 pipe_stats[I915_MAX_PIPES] = {};
|
2018-06-12 03:02:57 +07:00
|
|
|
u32 eir = 0, eir_stuck = 0;
|
2017-08-19 01:37:00 +07:00
|
|
|
u32 hotplug_status = 0;
|
|
|
|
u32 iir;
|
2012-04-25 04:59:44 +07:00
|
|
|
|
drm/i915: add GEN2_ prefix to the I{E, I, M, S}R registers
This discussion started because we use token pasting in the
GEN{2,3}_IRQ_INIT and GEN{2,3}_IRQ_RESET macros, so gen2-4 passes an
empty argument to those macros, making the code a little weird. The
original proposal was to just add a comment as the empty argument, but
Ville suggested we just add a prefix to the registers, and that indeed
sounds like a more elegant solution.
Now doing this is kinda against our rules for register naming since we
only add gens or platform names as register prefixes when the given
gen/platform changes a register that already existed before. On the
other hand, we have so many instances of IIR/IMR in comments that
adding a prefix would make the users of these register more easily
findable, in addition to make our token pasting macros actually
readable. So IMHO opening an exception here is worth it.
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410235344.31199-4-paulo.r.zanoni@intel.com
2019-04-11 06:53:42 +07:00
|
|
|
iir = I915_READ(GEN2_IIR);
|
2017-08-19 01:37:00 +07:00
|
|
|
if (iir == 0)
|
2012-04-25 04:59:44 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
if (iir & I915_DISPLAY_PORT_INTERRUPT)
|
|
|
|
hotplug_status = i9xx_hpd_irq_ack(dev_priv);
|
|
|
|
|
|
|
|
/* Call regardless, as some status bits might not be
|
|
|
|
* signalled in iir */
|
|
|
|
i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i9xx_error_irq_ack(dev_priv, &eir, &eir_stuck);
|
|
|
|
|
drm/i915: add GEN2_ prefix to the I{E, I, M, S}R registers
This discussion started because we use token pasting in the
GEN{2,3}_IRQ_INIT and GEN{2,3}_IRQ_RESET macros, so gen2-4 passes an
empty argument to those macros, making the code a little weird. The
original proposal was to just add a comment as the empty argument, but
Ville suggested we just add a prefix to the registers, and that indeed
sounds like a more elegant solution.
Now doing this is kinda against our rules for register naming since we
only add gens or platform names as register prefixes when the given
gen/platform changes a register that already existed before. On the
other hand, we have so many instances of IIR/IMR in comments that
adding a prefix would make the users of these register more easily
findable, in addition to make our token pasting macros actually
readable. So IMHO opening an exception here is worth it.
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410235344.31199-4-paulo.r.zanoni@intel.com
2019-04-11 06:53:42 +07:00
|
|
|
I915_WRITE(GEN2_IIR, iir);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
|
|
|
if (iir & I915_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[RCS0]);
|
2017-08-19 01:37:00 +07:00
|
|
|
|
2012-04-25 04:59:44 +07:00
|
|
|
if (iir & I915_BSD_USER_INTERRUPT)
|
2019-03-06 01:03:30 +07:00
|
|
|
intel_engine_breadcrumbs_irq(dev_priv->engine[VCS0]);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2018-06-12 03:02:57 +07:00
|
|
|
if (iir & I915_MASTER_ERROR_INTERRUPT)
|
|
|
|
i9xx_error_irq_handler(dev_priv, eir, eir_stuck);
|
2012-12-01 19:53:44 +07:00
|
|
|
|
2017-08-19 01:37:00 +07:00
|
|
|
if (hotplug_status)
|
|
|
|
i9xx_hpd_irq_handler(dev_priv, hotplug_status);
|
|
|
|
|
|
|
|
i965_pipestat_irq_handler(dev_priv, iir, pipe_stats);
|
|
|
|
} while (0);
|
2012-04-25 04:59:44 +07:00
|
|
|
|
2019-06-14 06:21:51 +07:00
|
|
|
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
|
2015-12-16 07:52:19 +07:00
|
|
|
|
2012-04-25 04:59:44 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* intel_irq_init - initializes irq support
|
|
|
|
* @dev_priv: i915 device instance
|
|
|
|
*
|
|
|
|
* This function initializes all the irq support including work items, timers
|
|
|
|
* and all the vtables. It does not setup the interrupt itself though.
|
|
|
|
*/
|
2014-09-30 15:56:44 +07:00
|
|
|
void intel_irq_init(struct drm_i915_private *dev_priv)
|
2011-06-29 03:00:41 +07:00
|
|
|
{
|
2016-07-05 16:40:23 +07:00
|
|
|
struct drm_device *dev = &dev_priv->drm;
|
2017-10-11 04:30:06 +07:00
|
|
|
struct intel_rps *rps = &dev_priv->gt_pm.rps;
|
2017-04-28 14:58:39 +07:00
|
|
|
int i;
|
2012-04-25 04:59:41 +07:00
|
|
|
|
2019-03-23 01:08:03 +07:00
|
|
|
if (IS_I945GM(dev_priv))
|
|
|
|
i945gm_vblank_work_init(dev_priv);
|
|
|
|
|
2015-06-18 17:06:16 +07:00
|
|
|
intel_hpd_init_work(dev_priv);
|
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
INIT_WORK(&rps->work, gen6_pm_rps_work);
|
2017-04-28 14:58:39 +07:00
|
|
|
|
2012-11-03 01:55:07 +07:00
|
|
|
INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
|
2017-04-28 14:58:39 +07:00
|
|
|
for (i = 0; i < MAX_L3_SLICES; ++i)
|
|
|
|
dev_priv->l3_parity.remap_info[i] = NULL;
|
2012-04-25 04:59:41 +07:00
|
|
|
|
2019-07-13 17:00:10 +07:00
|
|
|
/* pre-gen11 the guc irqs bits are in the upper 16 bits of the pm reg */
|
2019-07-25 07:18:06 +07:00
|
|
|
if (HAS_GT_UC(dev_priv) && INTEL_GEN(dev_priv) < 11)
|
2019-07-13 17:00:15 +07:00
|
|
|
dev_priv->gt.pm_guc_events = GUC_INTR_GUC2HOST << 16;
|
2016-10-12 23:24:31 +07:00
|
|
|
|
2014-03-15 21:53:22 +07:00
|
|
|
/* Let's track the enabled rps events */
|
2015-12-10 03:29:35 +07:00
|
|
|
if (IS_VALLEYVIEW(dev_priv))
|
2014-08-29 18:14:07 +07:00
|
|
|
/* WaGsvRC0ResidencyMethod:vlv */
|
2017-03-10 04:12:30 +07:00
|
|
|
dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED;
|
2014-07-04 04:33:01 +07:00
|
|
|
else
|
2018-08-02 17:06:30 +07:00
|
|
|
dev_priv->pm_rps_events = (GEN6_PM_RP_UP_THRESHOLD |
|
|
|
|
GEN6_PM_RP_DOWN_THRESHOLD |
|
|
|
|
GEN6_PM_RP_DOWN_TIMEOUT);
|
2014-03-15 21:53:22 +07:00
|
|
|
|
2019-04-10 17:59:22 +07:00
|
|
|
/* We share the register with other engine */
|
|
|
|
if (INTEL_GEN(dev_priv) > 9)
|
|
|
|
GEM_WARN_ON(dev_priv->pm_rps_events & 0xffff0000);
|
|
|
|
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->pm_intrmsk_mbz = 0;
|
2016-05-31 15:28:27 +07:00
|
|
|
|
|
|
|
/*
|
2017-04-13 18:15:27 +07:00
|
|
|
* SNB,IVB,HSW can while VLV,CHV may hard hang on looping batchbuffer
|
2016-05-31 15:28:27 +07:00
|
|
|
* if GEN6_PM_UP_EI_EXPIRED is masked.
|
|
|
|
*
|
|
|
|
* TODO: verify if this can be reproduced on VLV,CHV.
|
|
|
|
*/
|
2017-07-19 01:28:00 +07:00
|
|
|
if (INTEL_GEN(dev_priv) <= 7)
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->pm_intrmsk_mbz |= GEN6_PM_RP_UP_EI_EXPIRED;
|
2016-05-31 15:28:27 +07:00
|
|
|
|
2017-07-19 01:28:00 +07:00
|
|
|
if (INTEL_GEN(dev_priv) >= 8)
|
2017-10-11 04:30:06 +07:00
|
|
|
rps->pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
|
2016-05-31 15:28:27 +07:00
|
|
|
|
2019-03-23 01:08:04 +07:00
|
|
|
dev->vblank_disable_immediate = true;
|
2014-08-06 18:49:55 +07:00
|
|
|
|
2017-02-15 20:15:47 +07:00
|
|
|
/* Most platforms treat the display irq block as an always-on
|
|
|
|
* power domain. vlv/chv can disable it at runtime and need
|
|
|
|
* special care to avoid writing any of the display block registers
|
|
|
|
* outside of the power domain. We defer setting up the display irqs
|
|
|
|
* in this case to the runtime pm.
|
|
|
|
*/
|
|
|
|
dev_priv->display_irqs_enabled = true;
|
|
|
|
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
|
|
|
|
dev_priv->display_irqs_enabled = false;
|
|
|
|
|
2017-02-04 09:18:25 +07:00
|
|
|
dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
|
drm/i915: Add short HPD IRQ storm detection for non-MST systems
Unfortunately, it seems that the HPD IRQ storm problem from the early
days of Intel GPUs was never entirely solved, only mostly. Within the
last couple of days, I got a bug report from one of our customers who
had been having issues with their machine suddenly booting up very
slowly after having updated. The amount of time it took to boot went
from around 30 seconds, to over 6 minutes consistently.
After some investigation, I discovered that i915 was reporting massive
amounts of short HPD IRQ spam on this system from the DisplayPort port,
despite there not being anything actually connected. The symptoms would
start with one "long" HPD IRQ being detected at boot:
[ 1.891398] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00440000, dig 0x00440000, pins 0x000000a0
[ 1.891436] [drm:intel_hpd_irq_handler [i915]] digital hpd port B - long
[ 1.891472] [drm:intel_hpd_irq_handler [i915]] Received HPD interrupt on PIN 5 - cnt: 0
[ 1.891508] [drm:intel_hpd_irq_handler [i915]] digital hpd port D - long
[ 1.891544] [drm:intel_hpd_irq_handler [i915]] Received HPD interrupt on PIN 7 - cnt: 0
[ 1.891592] [drm:intel_dp_hpd_pulse [i915]] got hpd irq on port B - long
[ 1.891628] [drm:intel_dp_hpd_pulse [i915]] got hpd irq on port D - long
…
followed by constant short IRQs afterwards:
[ 1.895091] [drm:intel_encoder_hotplug [i915]] [CONNECTOR:66:DP-1] status updated from unknown to disconnected
[ 1.895129] [drm:i915_hotplug_work_func [i915]] Connector DP-3 (pin 7) received hotplug event.
[ 1.895165] [drm:intel_dp_detect [i915]] [CONNECTOR:72:DP-3]
[ 1.895275] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00200000, dig 0x00200000, pins 0x00000080
[ 1.895312] [drm:intel_hpd_irq_handler [i915]] digital hpd port D - short
[ 1.895762] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00200000, dig 0x00200000, pins 0x00000080
[ 1.895799] [drm:intel_hpd_irq_handler [i915]] digital hpd port D - short
[ 1.896239] [drm:intel_dp_aux_xfer [i915]] dp_aux_ch timeout status 0x71450085
[ 1.896293] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00200000, dig 0x00200000, pins 0x00000080
[ 1.896330] [drm:intel_hpd_irq_handler [i915]] digital hpd port D - short
[ 1.896781] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00200000, dig 0x00200000, pins 0x00000080
[ 1.896817] [drm:intel_hpd_irq_handler [i915]] digital hpd port D - short
[ 1.897275] [drm:intel_get_hpd_pins [i915]] hotplug event received, stat 0x00200000, dig 0x00200000, pins 0x00000080
The customer's system in question has a GM45 GPU, which is apparently
well known for hotplugging storms.
So, workaround this impressively broken hardware by changing the default
HPD storm threshold from 5 to 50. Then, make long IRQs count for 10, and
short IRQs count for 1. This makes it so that 5 long IRQs will trigger
an HPD storm, and on systems with short HPD storm detection 50 short
IRQs will trigger an HPD storm. 50 short IRQs amounts to 100ms of
constant pulsing, which seems like a good middleground between being too
sensitive and not being sensitive enough (which would cause visible
stutters in userspace every time a storm occurs).
And just to be extra safe: we don't enable this by default on systems
with MST support. There's too high of a chance of MST support triggering
storm detection, and systems that are new enough to support MST are a
lot less likely to have issues with IRQ storms anyway.
As a note: this patch was tested using a ThinkPad T450s and a Chamelium
to simulate the short IRQ storms.
Changes since v1:
- Don't use two separate thresholds, just make long IRQs count for 10
each and short IRQs count for 1. This simplifies the code a bit
- Ville Syrjälä
Changes since v2:
- Document @long_hpd in intel_hpd_irq_storm_detect, no functional
changes
Changes since v4:
- Remove !! in long_hpd assignment - Ville Syrjälä
- queue_hp = true - Ville Syrjälä
Signed-off-by: Lyude Paul <lyude@redhat.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181106213017.14563-6-lyude@redhat.com
2018-11-07 04:30:16 +07:00
|
|
|
/* If we have MST support, we want to avoid doing short HPD IRQ storm
|
|
|
|
* detection, as short HPD storms will occur as a natural part of
|
|
|
|
* sideband messaging with MST.
|
|
|
|
* On older platforms however, IRQ storms can occur with both long and
|
|
|
|
* short pulses, as seen on some G4x systems.
|
|
|
|
*/
|
|
|
|
dev_priv->hotplug.hpd_short_storm_enabled = !HAS_DP_MST(dev_priv);
|
2017-02-04 09:18:25 +07:00
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
if (HAS_GMCH(dev_priv)) {
|
|
|
|
if (I915_HAS_HOTPLUG(dev_priv))
|
|
|
|
dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
|
|
|
|
} else {
|
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
dev_priv->display.hpd_irq_setup = gen11_hpd_irq_setup;
|
|
|
|
else if (IS_GEN9_LP(dev_priv))
|
2015-03-27 19:54:14 +07:00
|
|
|
dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
|
2019-03-09 04:43:00 +07:00
|
|
|
else if (INTEL_PCH_TYPE(dev_priv) >= PCH_SPT)
|
2015-08-28 03:56:02 +07:00
|
|
|
dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
|
|
|
|
else
|
2015-08-28 03:56:06 +07:00
|
|
|
dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
|
2011-06-29 03:00:41 +07:00
|
|
|
}
|
|
|
|
}
|
2012-12-11 20:05:07 +07:00
|
|
|
|
2017-04-28 14:58:39 +07:00
|
|
|
/**
|
|
|
|
* intel_irq_fini - deinitializes IRQ support
|
|
|
|
* @i915: i915 device instance
|
|
|
|
*
|
|
|
|
* This function deinitializes all the IRQ support.
|
|
|
|
*/
|
|
|
|
void intel_irq_fini(struct drm_i915_private *i915)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2019-03-23 01:08:03 +07:00
|
|
|
if (IS_I945GM(i915))
|
|
|
|
i945gm_vblank_work_fini(i915);
|
|
|
|
|
2017-04-28 14:58:39 +07:00
|
|
|
for (i = 0; i < MAX_L3_SLICES; ++i)
|
|
|
|
kfree(i915->l3_parity.remap_info[i]);
|
|
|
|
}
|
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
static irq_handler_t intel_irq_handler(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (HAS_GMCH(dev_priv)) {
|
|
|
|
if (IS_CHERRYVIEW(dev_priv))
|
|
|
|
return cherryview_irq_handler;
|
|
|
|
else if (IS_VALLEYVIEW(dev_priv))
|
|
|
|
return valleyview_irq_handler;
|
|
|
|
else if (IS_GEN(dev_priv, 4))
|
|
|
|
return i965_irq_handler;
|
|
|
|
else if (IS_GEN(dev_priv, 3))
|
|
|
|
return i915_irq_handler;
|
|
|
|
else
|
|
|
|
return i8xx_irq_handler;
|
|
|
|
} else {
|
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
return gen11_irq_handler;
|
|
|
|
else if (INTEL_GEN(dev_priv) >= 8)
|
|
|
|
return gen8_irq_handler;
|
|
|
|
else
|
|
|
|
return ironlake_irq_handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_irq_reset(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (HAS_GMCH(dev_priv)) {
|
|
|
|
if (IS_CHERRYVIEW(dev_priv))
|
|
|
|
cherryview_irq_reset(dev_priv);
|
|
|
|
else if (IS_VALLEYVIEW(dev_priv))
|
|
|
|
valleyview_irq_reset(dev_priv);
|
|
|
|
else if (IS_GEN(dev_priv, 4))
|
|
|
|
i965_irq_reset(dev_priv);
|
|
|
|
else if (IS_GEN(dev_priv, 3))
|
|
|
|
i915_irq_reset(dev_priv);
|
|
|
|
else
|
|
|
|
i8xx_irq_reset(dev_priv);
|
|
|
|
} else {
|
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
gen11_irq_reset(dev_priv);
|
|
|
|
else if (INTEL_GEN(dev_priv) >= 8)
|
|
|
|
gen8_irq_reset(dev_priv);
|
|
|
|
else
|
|
|
|
ironlake_irq_reset(dev_priv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intel_irq_postinstall(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (HAS_GMCH(dev_priv)) {
|
|
|
|
if (IS_CHERRYVIEW(dev_priv))
|
|
|
|
cherryview_irq_postinstall(dev_priv);
|
|
|
|
else if (IS_VALLEYVIEW(dev_priv))
|
|
|
|
valleyview_irq_postinstall(dev_priv);
|
|
|
|
else if (IS_GEN(dev_priv, 4))
|
|
|
|
i965_irq_postinstall(dev_priv);
|
|
|
|
else if (IS_GEN(dev_priv, 3))
|
|
|
|
i915_irq_postinstall(dev_priv);
|
|
|
|
else
|
|
|
|
i8xx_irq_postinstall(dev_priv);
|
|
|
|
} else {
|
|
|
|
if (INTEL_GEN(dev_priv) >= 11)
|
|
|
|
gen11_irq_postinstall(dev_priv);
|
|
|
|
else if (INTEL_GEN(dev_priv) >= 8)
|
|
|
|
gen8_irq_postinstall(dev_priv);
|
|
|
|
else
|
|
|
|
ironlake_irq_postinstall(dev_priv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* intel_irq_install - enables the hardware interrupt
|
|
|
|
* @dev_priv: i915 device instance
|
|
|
|
*
|
|
|
|
* This function enables the hardware interrupt handling, but leaves the hotplug
|
|
|
|
* handling still disabled. It is called after intel_irq_init().
|
|
|
|
*
|
|
|
|
* In the driver load and resume code we need working interrupts in a few places
|
|
|
|
* but don't want to deal with the hassle of concurrent probe and hotplug
|
|
|
|
* workers. Hence the split into this two-stage approach.
|
|
|
|
*/
|
2014-09-30 15:56:43 +07:00
|
|
|
int intel_irq_install(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
int irq = dev_priv->drm.pdev->irq;
|
|
|
|
int ret;
|
|
|
|
|
2014-09-30 15:56:43 +07:00
|
|
|
/*
|
|
|
|
* We enable some interrupt sources in our postinstall hooks, so mark
|
|
|
|
* interrupts as enabled _before_ actually enabling them to avoid
|
|
|
|
* special cases in our ordering checks.
|
|
|
|
*/
|
2017-10-11 04:30:04 +07:00
|
|
|
dev_priv->runtime_pm.irqs_enabled = true;
|
2014-09-30 15:56:43 +07:00
|
|
|
|
2019-06-20 17:33:34 +07:00
|
|
|
dev_priv->drm.irq_enabled = true;
|
|
|
|
|
|
|
|
intel_irq_reset(dev_priv);
|
|
|
|
|
|
|
|
ret = request_irq(irq, intel_irq_handler(dev_priv),
|
|
|
|
IRQF_SHARED, DRIVER_NAME, dev_priv);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_priv->drm.irq_enabled = false;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
intel_irq_postinstall(dev_priv);
|
|
|
|
|
|
|
|
return ret;
|
2014-09-30 15:56:43 +07:00
|
|
|
}
|
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* intel_irq_uninstall - finilizes all irq handling
|
|
|
|
* @dev_priv: i915 device instance
|
|
|
|
*
|
|
|
|
* This stops interrupt and hotplug handling and unregisters and frees all
|
|
|
|
* resources acquired in the init functions.
|
|
|
|
*/
|
2014-09-30 15:56:43 +07:00
|
|
|
void intel_irq_uninstall(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
int irq = dev_priv->drm.pdev->irq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME we can get called twice during driver load
|
|
|
|
* error handling due to intel_modeset_cleanup()
|
|
|
|
* calling us out of sequence. Would be nice if
|
|
|
|
* it didn't do that...
|
|
|
|
*/
|
|
|
|
if (!dev_priv->drm.irq_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->drm.irq_enabled = false;
|
|
|
|
|
|
|
|
intel_irq_reset(dev_priv);
|
|
|
|
|
|
|
|
free_irq(irq, dev_priv);
|
|
|
|
|
2014-09-30 15:56:43 +07:00
|
|
|
intel_hpd_cancel_work(dev_priv);
|
2017-10-11 04:30:04 +07:00
|
|
|
dev_priv->runtime_pm.irqs_enabled = false;
|
2014-09-30 15:56:43 +07:00
|
|
|
}
|
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* intel_runtime_pm_disable_interrupts - runtime interrupt disabling
|
|
|
|
* @dev_priv: i915 device instance
|
|
|
|
*
|
|
|
|
* This function is used to disable interrupts at runtime, both in the runtime
|
|
|
|
* pm and the system suspend/resume code.
|
|
|
|
*/
|
2014-09-30 15:56:44 +07:00
|
|
|
void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv)
|
2013-08-19 23:18:09 +07:00
|
|
|
{
|
2019-06-20 17:33:34 +07:00
|
|
|
intel_irq_reset(dev_priv);
|
2017-10-11 04:30:04 +07:00
|
|
|
dev_priv->runtime_pm.irqs_enabled = false;
|
2019-07-02 22:17:23 +07:00
|
|
|
intel_synchronize_irq(dev_priv);
|
2013-08-19 23:18:09 +07:00
|
|
|
}
|
|
|
|
|
2014-09-30 15:56:45 +07:00
|
|
|
/**
|
|
|
|
* intel_runtime_pm_enable_interrupts - runtime interrupt enabling
|
|
|
|
* @dev_priv: i915 device instance
|
|
|
|
*
|
|
|
|
* This function is used to enable interrupts at runtime, both in the runtime
|
|
|
|
* pm and the system suspend/resume code.
|
|
|
|
*/
|
2014-09-30 15:56:44 +07:00
|
|
|
void intel_runtime_pm_enable_interrupts(struct drm_i915_private *dev_priv)
|
2013-08-19 23:18:09 +07:00
|
|
|
{
|
2017-10-11 04:30:04 +07:00
|
|
|
dev_priv->runtime_pm.irqs_enabled = true;
|
2019-06-20 17:33:34 +07:00
|
|
|
intel_irq_reset(dev_priv);
|
|
|
|
intel_irq_postinstall(dev_priv);
|
2013-08-19 23:18:09 +07:00
|
|
|
}
|