VIGS: Walk fb notifier call chain on DPMS
authorStanislav Vorobiov <s.vorobiov@samsung.com>
Mon, 1 Jul 2013 11:52:59 +0000 (15:52 +0400)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Wed, 9 Apr 2014 05:42:24 +0000 (14:42 +0900)
Since currently maru_bl is used to control LCD power
we need a way to trigger it. fbdev does this
automatically on FB_BLANK events by walking fb notifier call
chain. DRM doesn't do this on DPMS, so we need to do this
ourselves

Change-Id: I9585b06c027b210b4b563c2332c092a3084663e2

drivers/gpu/drm/vigs/vigs_crtc.c
drivers/gpu/drm/vigs/vigs_crtc.h
drivers/gpu/drm/vigs/vigs_fbdev.c

index 8c985d0..2ae8c04 100644 (file)
@@ -3,18 +3,9 @@
 #include "vigs_framebuffer.h"
 #include "vigs_surface.h"
 #include "vigs_comm.h"
+#include "vigs_fbdev.h"
 #include "drm_crtc_helper.h"
 
-struct vigs_crtc
-{
-    struct drm_crtc base;
-};
-
-static inline struct vigs_crtc *crtc_to_vigs_crtc(struct drm_crtc *crtc)
-{
-    return container_of(crtc, struct vigs_crtc, base);
-}
-
 static int vigs_crtc_update(struct drm_crtc *crtc,
                             struct drm_framebuffer *old_fb)
 {
@@ -74,7 +65,41 @@ static void vigs_crtc_destroy(struct drm_crtc *crtc)
 
 static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
-    DRM_DEBUG_KMS("enter: mode = %d\n", mode);
+    struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc);
+    struct vigs_device *vigs_dev = crtc->dev->dev_private;
+    int blank;
+    struct fb_event event;
+
+    DRM_DEBUG_KMS("enter: fb_blank = %d, mode = %d\n",
+                  vigs_crtc->in_fb_blank,
+                  mode);
+
+    if (vigs_crtc->in_fb_blank) {
+        return;
+    }
+
+    switch (mode) {
+    case DRM_MODE_DPMS_ON:
+        blank = FB_BLANK_UNBLANK;
+        break;
+    case DRM_MODE_DPMS_STANDBY:
+        blank = FB_BLANK_NORMAL;
+        break;
+    case DRM_MODE_DPMS_SUSPEND:
+        blank = FB_BLANK_VSYNC_SUSPEND;
+        break;
+    case DRM_MODE_DPMS_OFF:
+        blank = FB_BLANK_POWERDOWN;
+        break;
+    default:
+        DRM_ERROR("unspecified mode %d\n", mode);
+        return;
+    }
+
+    event.info = vigs_dev->fbdev->base.fbdev;
+    event.data = &blank;
+
+    fb_notifier_call_chain(FB_EVENT_BLANK, &event);
 }
 
 static bool vigs_crtc_mode_fixup(struct drm_crtc *crtc,
index ad9b0ce..6f33feb 100644 (file)
@@ -5,6 +5,22 @@
 
 struct vigs_device;
 
+struct vigs_crtc
+{
+    struct drm_crtc base;
+
+    /*
+     * A hack to tell if DPMS callback is called from inside
+     * 'fb_blank' or not.
+     */
+    bool in_fb_blank;
+};
+
+static inline struct vigs_crtc *crtc_to_vigs_crtc(struct drm_crtc *crtc)
+{
+    return container_of(crtc, struct vigs_crtc, base);
+}
+
 int vigs_crtc_init(struct vigs_device *vigs_dev);
 
 #endif
index 3ea7129..3b2a523 100644 (file)
@@ -3,6 +3,7 @@
 #include "vigs_surface.h"
 #include "vigs_framebuffer.h"
 #include "vigs_output.h"
+#include "vigs_crtc.h"
 #include "drm_crtc_helper.h"
 #include <drm/vigs_drm.h>
 
@@ -130,13 +131,87 @@ static int vigs_fbdev_setcmap(struct fb_cmap *cmap, struct fb_info *fbi)
  * @}
  */
 
-int vigs_fbdev_set_par(struct fb_info *fbi)
+static int vigs_fbdev_set_par(struct fb_info *fbi)
 {
     DRM_DEBUG_KMS("enter\n");
 
     return drm_fb_helper_set_par(fbi);
 }
 
+/*
+ * This is 'drm_fb_helper_dpms' modified to set 'fbdev'
+ * flag inside 'mode_config.mutex'.
+ */
+static void vigs_fbdev_dpms(struct fb_info *fbi, int dpms_mode)
+{
+    struct drm_fb_helper *fb_helper = fbi->par;
+    struct drm_device *dev = fb_helper->dev;
+    struct drm_crtc *crtc;
+    struct vigs_crtc *vigs_crtc;
+    struct drm_connector *connector;
+    int i, j;
+
+    /*
+     * For each CRTC in this fb, turn the connectors on/off.
+     */
+    mutex_lock(&dev->mode_config.mutex);
+
+    for (i = 0; i < fb_helper->crtc_count; i++) {
+        crtc = fb_helper->crtc_info[i].mode_set.crtc;
+        vigs_crtc = crtc_to_vigs_crtc(crtc);
+
+        if (!crtc->enabled) {
+            continue;
+        }
+
+        vigs_crtc->in_fb_blank = true;
+
+        /* Walk the connectors & encoders on this fb turning them on/off */
+        for (j = 0; j < fb_helper->connector_count; j++) {
+            connector = fb_helper->connector_info[j]->connector;
+            drm_helper_connector_dpms(connector, dpms_mode);
+            drm_connector_property_set_value(connector,
+                dev->mode_config.dpms_property, dpms_mode);
+        }
+
+        vigs_crtc->in_fb_blank = false;
+    }
+
+    mutex_unlock(&dev->mode_config.mutex);
+}
+
+/*
+ * This is 'drm_fb_helper_blank' modified to use
+ * 'vigs_fbdev_dpms'.
+ */
+static int vigs_fbdev_blank(int blank, struct fb_info *fbi)
+{
+    switch (blank) {
+    /* Display: On; HSync: On, VSync: On */
+    case FB_BLANK_UNBLANK:
+        vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_ON);
+        break;
+    /* Display: Off; HSync: On, VSync: On */
+    case FB_BLANK_NORMAL:
+        vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_STANDBY);
+        break;
+    /* Display: Off; HSync: Off, VSync: On */
+    case FB_BLANK_HSYNC_SUSPEND:
+        vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_STANDBY);
+        break;
+    /* Display: Off; HSync: On, VSync: Off */
+    case FB_BLANK_VSYNC_SUSPEND:
+        vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_SUSPEND);
+        break;
+    /* Display: Off; HSync: Off, VSync: Off */
+    case FB_BLANK_POWERDOWN:
+        vigs_fbdev_dpms(fbi, DRM_MODE_DPMS_OFF);
+        break;
+    }
+
+    return 0;
+}
+
 static struct fb_ops vigs_fbdev_ops =
 {
     .owner = THIS_MODULE,
@@ -145,7 +220,7 @@ static struct fb_ops vigs_fbdev_ops =
     .fb_imageblit = cfb_imageblit,
     .fb_check_var = drm_fb_helper_check_var,
     .fb_set_par = vigs_fbdev_set_par,
-    .fb_blank = drm_fb_helper_blank,
+    .fb_blank = vigs_fbdev_blank,
     .fb_pan_display = drm_fb_helper_pan_display,
     .fb_setcmap = vigs_fbdev_setcmap,
     .fb_debug_enter = drm_fb_helper_debug_enter,