drm/i915: protect backlight registers and data with a spinlock
authorJani Nikula <jani.nikula@intel.com>
Fri, 12 Apr 2013 12:18:37 +0000 (15:18 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 25 Apr 2013 12:10:10 +0000 (14:10 +0200)
Backlight data and registers are fiddled through LVDS/eDP modeset
enable/disable hooks, backlight sysfs files, asle interrupts, and register
save/restore. Protect the backlight related registers and driver private
fields using a spinlock.

The locking in register save/restore covers a little more than is strictly
necessary, including non-modeset case, for simplicity.

v2: Cover register access, save/restore, i915_read_blc_pwm_ctl() and code
    paths leading there.

Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_panel.c

index 13a72e63b4e9558f8de97e7334b282a90d4bdb5e..a1648eb3a8b1bf5189b1f08257e6c8634b4b1a7e 100644 (file)
@@ -1633,6 +1633,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        spin_lock_init(&dev_priv->irq_lock);
        spin_lock_init(&dev_priv->gpu_error.lock);
        spin_lock_init(&dev_priv->rps.lock);
+       spin_lock_init(&dev_priv->backlight.lock);
        mutex_init(&dev_priv->dpio_lock);
 
        mutex_init(&dev_priv->rps.hw_lock);
index 95a3cc367d503a911f20b0afb0ccb064a5707444..f6f4839484d1c0b17c44a6197abe2a8daa6d4663 100644 (file)
@@ -960,6 +960,7 @@ typedef struct drm_i915_private {
        struct {
                int level;
                bool enabled;
+               spinlock_t lock; /* bl registers and the above bl fields */
                struct backlight_device *device;
        } backlight;
 
index 41f0fdecfbdc27c27f3132d39130e14ba19fd999..88b9a663944f0afce886e3070677753282cad516 100644 (file)
@@ -192,6 +192,7 @@ static void i915_restore_vga(struct drm_device *dev)
 static void i915_save_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
 
        /* Display arbitration control */
        if (INTEL_INFO(dev)->gen <= 4)
@@ -202,6 +203,8 @@ static void i915_save_display(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_save_display_reg(dev);
 
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+
        /* LVDS state */
        if (HAS_PCH_SPLIT(dev)) {
                dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
@@ -222,6 +225,8 @@ static void i915_save_display(struct drm_device *dev)
                        dev_priv->regfile.saveLVDS = I915_READ(LVDS);
        }
 
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        if (!IS_I830(dev) && !IS_845G(dev) && !HAS_PCH_SPLIT(dev))
                dev_priv->regfile.savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
 
@@ -257,6 +262,7 @@ static void i915_restore_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 mask = 0xffffffff;
+       unsigned long flags;
 
        /* Display arbitration */
        if (INTEL_INFO(dev)->gen <= 4)
@@ -265,6 +271,8 @@ static void i915_restore_display(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_restore_display_reg(dev);
 
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+
        /* LVDS state */
        if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
                I915_WRITE(BLC_PWM_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2);
@@ -304,6 +312,8 @@ static void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
        }
 
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        /* only restore FBC info on the platform that supports FBC*/
        intel_disable_fbc(dev);
        if (I915_HAS_FBC(dev)) {
index 63a7c36a76035bf30f774b07b9179a25f07afb71..5d3e9d7d51a7ab02d577df194246dd7739774b25 100644 (file)
@@ -138,6 +138,8 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
 
+       WARN_ON(!spin_is_locked(&dev_priv->backlight.lock));
+
        /* Restore the CTL value if it lost, e.g. GPU reset */
 
        if (HAS_PCH_SPLIT(dev_priv->dev)) {
@@ -218,6 +220,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (HAS_PCH_SPLIT(dev)) {
                val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
@@ -235,6 +240,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev)
        }
 
        val = intel_panel_compute_brightness(dev, val);
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
        return val;
 }
@@ -282,11 +290,14 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 freq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        freq = intel_panel_get_max_backlight(dev);
        if (!freq) {
                /* we are screwed, bail out */
-               return;
+               goto out;
        }
 
        /* scale to hardware */
@@ -298,11 +309,16 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
 
        if (dev_priv->backlight.enabled)
                intel_panel_actually_set_backlight(dev, level);
+out:
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 void intel_panel_disable_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        dev_priv->backlight.enabled = false;
        intel_panel_actually_set_backlight(dev, 0);
@@ -320,12 +336,17 @@ void intel_panel_disable_backlight(struct drm_device *dev)
                        I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
                }
        }
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 void intel_panel_enable_backlight(struct drm_device *dev,
                                  enum pipe pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (dev_priv->backlight.level == 0) {
                dev_priv->backlight.level = intel_panel_get_max_backlight(dev);
@@ -375,6 +396,8 @@ set_level:
         */
        dev_priv->backlight.enabled = true;
        intel_panel_actually_set_backlight(dev, dev_priv->backlight.level);
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 static void intel_panel_init_backlight(struct drm_device *dev)
@@ -432,6 +455,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        struct drm_device *dev = connector->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct backlight_properties props;
+       unsigned long flags;
 
        intel_panel_init_backlight(dev);
 
@@ -441,7 +465,11 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_RAW;
        props.brightness = dev_priv->backlight.level;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
        props.max_brightness = intel_panel_get_max_backlight(dev);
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        if (props.max_brightness == 0) {
                DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n");
                return -ENODEV;