OSDN Git Service

drm/gma500: Move asle interrupt work into a work task
authorPatrik Jakobsson <patrik.r.jakobsson@gmail.com>
Tue, 11 Mar 2014 17:51:20 +0000 (18:51 +0100)
committerPatrik Jakobsson <patrik.r.jakobsson@gmail.com>
Mon, 17 Mar 2014 19:12:04 +0000 (20:12 +0100)
Previously the backlight code was called from IRQ context which isn't
allowed. This patch moves all the asle work into a work task which takes
care of the locking bug reported by users.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=64221
Signed-off-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
drivers/gpu/drm/gma500/opregion.c
drivers/gpu/drm/gma500/psb_drv.h

index 13ec628..ab696ca 100644 (file)
@@ -173,10 +173,13 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
        return 0;
 }
 
-void psb_intel_opregion_asle_intr(struct drm_device *dev)
+static void psb_intel_opregion_asle_work(struct work_struct *work)
 {
-       struct drm_psb_private *dev_priv = dev->dev_private;
-       struct opregion_asle *asle = dev_priv->opregion.asle;
+       struct psb_intel_opregion *opregion =
+               container_of(work, struct psb_intel_opregion, asle_work);
+       struct drm_psb_private *dev_priv =
+               container_of(opregion, struct drm_psb_private, opregion);
+       struct opregion_asle *asle = opregion->asle;
        u32 asle_stat = 0;
        u32 asle_req;
 
@@ -190,9 +193,18 @@ void psb_intel_opregion_asle_intr(struct drm_device *dev)
        }
 
        if (asle_req & ASLE_SET_BACKLIGHT)
-               asle_stat |= asle_set_backlight(dev, asle->bclp);
+               asle_stat |= asle_set_backlight(dev_priv->dev, asle->bclp);
 
        asle->aslc = asle_stat;
+
+}
+
+void psb_intel_opregion_asle_intr(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->opregion.asle)
+               schedule_work(&dev_priv->opregion.asle_work);
 }
 
 #define ASLE_ALS_EN    (1<<0)
@@ -282,6 +294,8 @@ void psb_intel_opregion_fini(struct drm_device *dev)
                unregister_acpi_notifier(&psb_intel_opregion_notifier);
        }
 
+       cancel_work_sync(&opregion->asle_work);
+
        /* just clear all opregion memory pointers now */
        iounmap(opregion->header);
        opregion->header = NULL;
@@ -304,6 +318,9 @@ int psb_intel_opregion_setup(struct drm_device *dev)
                DRM_DEBUG_DRIVER("ACPI Opregion not supported\n");
                return -ENOTSUPP;
        }
+
+       INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work);
+
        DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy);
        base = acpi_os_ioremap(opregion_phy, 8*1024);
        if (!base)
index 77bd7ab..d5421c0 100644 (file)
@@ -266,6 +266,7 @@ struct psb_intel_opregion {
        struct opregion_asle *asle;
        void *vbt;
        u32 __iomem *lid_state;
+       struct work_struct asle_work;
 };
 
 struct sdvo_device_mapping {