drm/i915: fix HPD IRQ reenable work cancelation
Atm, the HPD IRQ reenable timer can get rearmed right after it's canceled. Also to access the HPD IRQ mask registers we need to wake up the HW. Solve both issues by converting the reenable timer to a delayed work and grabbing a runtime PM reference in the work. By this we can also forgo canceling the timer during runtime suspend, since the only important thing there is that the HW is awake when we write the registers and that's ensured by the RPM ref. So do the cancelation only during driver unload time; this is also a requirement for an upcoming patch where we want to cancel all HPD related works only during system suspend and driver unload time, but not during runtime suspend. Note that there is still a race between the HPD IRQ reenable work and drm_irq_uninstall() during driver unload, where the work can reenable the HPD IRQs disabled by drm_irq_uninstall(). This isn't a problem since the HPD IRQs will still be effectively masked by the first level interrupt mask. v2-3: - unchanged v4: - use proper API for changing the expiration time for an already pending delayed work (Jani) Signed-off-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> (v2) Cc: stable@vger.kernel.org (3.16+) Signed-off-by: Jani Nikula <jani.nikula@intel.com>
This commit is contained in:
parent
1c767b339b
commit
6323751d28
|
@ -1458,7 +1458,7 @@ struct drm_i915_private {
|
|||
} hpd_mark;
|
||||
} hpd_stats[HPD_NUM_PINS];
|
||||
u32 hpd_event_bits;
|
||||
struct timer_list hotplug_reenable_timer;
|
||||
struct delayed_work hotplug_reenable_work;
|
||||
|
||||
struct i915_fbc fbc;
|
||||
struct i915_drrs drrs;
|
||||
|
|
|
@ -1189,8 +1189,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
|||
* some connectors */
|
||||
if (hpd_disabled) {
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
mod_timer(&dev_priv->hotplug_reenable_timer,
|
||||
jiffies + msecs_to_jiffies(I915_REENABLE_HOTPLUG_DELAY));
|
||||
mod_delayed_work(system_wq, &dev_priv->hotplug_reenable_work,
|
||||
msecs_to_jiffies(I915_REENABLE_HOTPLUG_DELAY));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
|
@ -1213,11 +1213,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
|||
drm_kms_helper_hotplug_event(dev);
|
||||
}
|
||||
|
||||
static void intel_hpd_irq_uninstall(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
del_timer_sync(&dev_priv->hotplug_reenable_timer);
|
||||
}
|
||||
|
||||
static void ironlake_rps_change_irq_handler(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
@ -3892,8 +3887,6 @@ static void gen8_irq_uninstall(struct drm_device *dev)
|
|||
if (!dev_priv)
|
||||
return;
|
||||
|
||||
intel_hpd_irq_uninstall(dev_priv);
|
||||
|
||||
gen8_irq_reset(dev);
|
||||
}
|
||||
|
||||
|
@ -3908,8 +3901,6 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
|
|||
|
||||
I915_WRITE(VLV_MASTER_IER, 0);
|
||||
|
||||
intel_hpd_irq_uninstall(dev_priv);
|
||||
|
||||
for_each_pipe(pipe)
|
||||
I915_WRITE(PIPESTAT(pipe), 0xffff);
|
||||
|
||||
|
@ -3988,8 +3979,6 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
|
|||
if (!dev_priv)
|
||||
return;
|
||||
|
||||
intel_hpd_irq_uninstall(dev_priv);
|
||||
|
||||
ironlake_irq_reset(dev);
|
||||
}
|
||||
|
||||
|
@ -4360,8 +4349,6 @@ static void i915_irq_uninstall(struct drm_device * dev)
|
|||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int pipe;
|
||||
|
||||
intel_hpd_irq_uninstall(dev_priv);
|
||||
|
||||
if (I915_HAS_HOTPLUG(dev)) {
|
||||
I915_WRITE(PORT_HOTPLUG_EN, 0);
|
||||
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
||||
|
@ -4598,8 +4585,6 @@ static void i965_irq_uninstall(struct drm_device * dev)
|
|||
if (!dev_priv)
|
||||
return;
|
||||
|
||||
intel_hpd_irq_uninstall(dev_priv);
|
||||
|
||||
I915_WRITE(PORT_HOTPLUG_EN, 0);
|
||||
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
||||
|
||||
|
@ -4615,14 +4600,18 @@ static void i965_irq_uninstall(struct drm_device * dev)
|
|||
I915_WRITE(IIR, I915_READ(IIR));
|
||||
}
|
||||
|
||||
static void intel_hpd_irq_reenable(unsigned long data)
|
||||
static void intel_hpd_irq_reenable(struct work_struct *work)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = (struct drm_i915_private *)data;
|
||||
struct drm_i915_private *dev_priv =
|
||||
container_of(work, typeof(*dev_priv),
|
||||
hotplug_reenable_work.work);
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
unsigned long irqflags;
|
||||
int i;
|
||||
|
||||
intel_runtime_pm_get(dev_priv);
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
for (i = (HPD_NONE + 1); i < HPD_NUM_PINS; i++) {
|
||||
struct drm_connector *connector;
|
||||
|
@ -4648,6 +4637,8 @@ static void intel_hpd_irq_reenable(unsigned long data)
|
|||
if (dev_priv->display.hpd_irq_setup)
|
||||
dev_priv->display.hpd_irq_setup(dev);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
|
||||
intel_runtime_pm_put(dev_priv);
|
||||
}
|
||||
|
||||
void intel_irq_init(struct drm_device *dev)
|
||||
|
@ -4670,8 +4661,8 @@ void intel_irq_init(struct drm_device *dev)
|
|||
setup_timer(&dev_priv->gpu_error.hangcheck_timer,
|
||||
i915_hangcheck_elapsed,
|
||||
(unsigned long) dev);
|
||||
setup_timer(&dev_priv->hotplug_reenable_timer, intel_hpd_irq_reenable,
|
||||
(unsigned long) dev_priv);
|
||||
INIT_DELAYED_WORK(&dev_priv->hotplug_reenable_work,
|
||||
intel_hpd_irq_reenable);
|
||||
|
||||
pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
|
|
|
@ -13104,6 +13104,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
|
|||
*/
|
||||
drm_irq_uninstall(dev);
|
||||
cancel_work_sync(&dev_priv->hotplug_work);
|
||||
cancel_delayed_work_sync(&dev_priv->hotplug_reenable_work);
|
||||
dev_priv->pm._irqs_disabled = true;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue