VIGS: DRM VBLANK handling implemented
authorStanislav Vorobiov <s.vorobiov@samsung.com>
Mon, 24 Jun 2013 12:56:12 +0000 (16:56 +0400)
committerStanislav Vorobiov <s.vorobiov@samsung.com>
Mon, 24 Jun 2013 12:56:12 +0000 (16:56 +0400)
VIGS: DRM pageflip handling implemented

VIGS DRM driver now supports both VBLANK events and
pageflip events

drivers/gpu/drm/vigs/Makefile
drivers/gpu/drm/vigs/vigs_comm.c
drivers/gpu/drm/vigs/vigs_crtc.c
drivers/gpu/drm/vigs/vigs_device.c
drivers/gpu/drm/vigs/vigs_device.h
drivers/gpu/drm/vigs/vigs_driver.c
drivers/gpu/drm/vigs/vigs_irq.c [new file with mode: 0644]
drivers/gpu/drm/vigs/vigs_irq.h [new file with mode: 0644]
drivers/gpu/drm/vigs/vigs_protocol.h
drivers/gpu/drm/vigs/vigs_regs.h [new file with mode: 0644]

index 539de77ea0ec5683351f3fbba31fa1888792513d..f3d0fb9fb483703e9e8096bb9070ef5fe8110bb0 100644 (file)
@@ -14,6 +14,7 @@ vigs_drm-y := main.o \
               vigs_output.o \
               vigs_framebuffer.o \
               vigs_comm.o \
-              vigs_fbdev.o
+              vigs_fbdev.o \
+              vigs_irq.o
 
 obj-$(CONFIG_DRM_VIGS) += vigs_drm.o
index cf94c090ec1465c4dc07b956b1890b877250bbc1..967ac3d8f30f2b58cf4982d563c2085352bad6c6 100644 (file)
@@ -1,6 +1,7 @@
 #include "vigs_comm.h"
 #include "vigs_device.h"
 #include "vigs_execbuffer.h"
+#include "vigs_regs.h"
 #include <drm/vigs_drm.h>
 
 static int vigs_comm_prepare(struct vigs_comm *comm,
@@ -80,7 +81,7 @@ static int vigs_comm_prepare(struct vigs_comm *comm,
 static void vigs_comm_exec_locked(struct vigs_comm *comm,
                                   struct vigs_execbuffer *execbuffer)
 {
-    writel(vigs_gem_offset(&execbuffer->gem), comm->io_ptr);
+    writel(vigs_gem_offset(&execbuffer->gem), comm->io_ptr + VIGS_REG_EXEC);
 }
 
 static int vigs_comm_exec_internal(struct vigs_comm *comm)
index 7fcfbdfa0c2cf3691b9f679463724294f5ac8076..581f6870934d8deaa81aa7be4199e07dd25d6411 100644 (file)
@@ -15,33 +15,8 @@ static inline struct vigs_crtc *crtc_to_vigs_crtc(struct drm_crtc *crtc)
     return container_of(crtc, struct vigs_crtc, base);
 }
 
-static void vigs_crtc_destroy(struct drm_crtc *crtc)
-{
-    struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc);
-
-    DRM_DEBUG_KMS("enter");
-
-    drm_crtc_cleanup(crtc);
-
-    kfree(vigs_crtc);
-}
-
-static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-    DRM_DEBUG_KMS("enter: mode = %d\n", mode);
-}
-
-static bool vigs_crtc_mode_fixup(struct drm_crtc *crtc,
-                                 struct drm_display_mode *mode,
-                                 struct drm_display_mode *adjusted_mode)
-{
-    DRM_DEBUG_KMS("enter\n");
-
-    return true;
-}
-
-static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-                                   struct drm_framebuffer *old_fb)
+static int vigs_crtc_update(struct drm_crtc *crtc,
+                            struct drm_framebuffer *old_fb)
 {
     struct vigs_device *vigs_dev = crtc->dev->dev_private;
     struct vigs_framebuffer *vigs_fb;
@@ -52,8 +27,6 @@ static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
      * root surface has been updated.
      */
 
-    DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y);
-
     if (!crtc->fb) {
         DRM_ERROR("crtc->fb is NULL\n");
         return -EINVAL;
@@ -83,6 +56,39 @@ static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
     return 0;
 }
 
+static void vigs_crtc_destroy(struct drm_crtc *crtc)
+{
+    struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc);
+
+    DRM_DEBUG_KMS("enter");
+
+    drm_crtc_cleanup(crtc);
+
+    kfree(vigs_crtc);
+}
+
+static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+    DRM_DEBUG_KMS("enter: mode = %d\n", mode);
+}
+
+static bool vigs_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+    DRM_DEBUG_KMS("enter\n");
+
+    return true;
+}
+
+static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                   struct drm_framebuffer *old_fb)
+{
+    DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y);
+
+    return vigs_crtc_update(crtc, old_fb);
+}
+
 static int vigs_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -108,6 +114,46 @@ static void vigs_crtc_load_lut(struct drm_crtc *crtc)
 {
 }
 
+static int vigs_crtc_page_flip(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               struct drm_pending_vblank_event *event)
+{
+    struct vigs_device *vigs_dev = crtc->dev->dev_private;
+    struct drm_framebuffer *old_fb = crtc->fb;
+    int ret = -EINVAL;
+
+    mutex_lock(&vigs_dev->drm_dev->struct_mutex);
+
+    if (event) {
+        event->pipe = 0;
+
+        ret = drm_vblank_get(vigs_dev->drm_dev, 0);
+
+        if (ret != 0) {
+            DRM_ERROR("failed to acquire vblank counter\n");
+            list_del(&event->base.link);
+            goto out;
+        }
+
+        list_add_tail(&event->base.link,
+                      &vigs_dev->pageflip_event_list);
+
+        crtc->fb = fb;
+        ret = vigs_crtc_update(crtc, old_fb);
+        if (ret != 0) {
+            crtc->fb = old_fb;
+            drm_vblank_put(vigs_dev->drm_dev, 0);
+            list_del(&event->base.link);
+            goto out;
+        }
+    }
+
+out:
+    mutex_unlock(&vigs_dev->drm_dev->struct_mutex);
+
+    return ret;
+}
+
 static void vigs_crtc_disable(struct drm_crtc *crtc)
 {
     struct vigs_device *vigs_dev = crtc->dev->dev_private;
@@ -135,6 +181,7 @@ static void vigs_crtc_disable(struct drm_crtc *crtc)
 static const struct drm_crtc_funcs vigs_crtc_funcs =
 {
     .set_config = drm_crtc_helper_set_config,
+    .page_flip = vigs_crtc_page_flip,
     .destroy = vigs_crtc_destroy,
 };
 
index 5bc96867f66f51bcd35093e13f9738f48638787c..2db30afb46672a4405da0f9265f6e841e0b447b3 100644 (file)
@@ -245,6 +245,8 @@ int vigs_device_init(struct vigs_device *vigs_dev,
     vigs_dev->drm_dev = drm_dev;
     vigs_dev->pci_dev = pci_dev;
 
+    INIT_LIST_HEAD(&vigs_dev->pageflip_event_list);
+
     vigs_dev->vram_base = pci_resource_start(pci_dev, 0);
     vigs_dev->vram_size = pci_resource_len(pci_dev, 0);
 
@@ -311,14 +313,36 @@ int vigs_device_init(struct vigs_device *vigs_dev,
         goto fail4;
     }
 
-    ret = vigs_fbdev_create(vigs_dev, &vigs_dev->fbdev);
+    ret = drm_vblank_init(drm_dev, 1);
 
     if (ret != 0) {
         goto fail4;
     }
 
+    /*
+     * We allow VBLANK interrupt disabling right from the start. There's
+     * no point in "waiting until first modeset".
+     */
+    drm_dev->vblank_disable_allowed = 1;
+
+    ret = drm_irq_install(drm_dev);
+
+    if (ret != 0) {
+        goto fail5;
+    }
+
+    ret = vigs_fbdev_create(vigs_dev, &vigs_dev->fbdev);
+
+    if (ret != 0) {
+        goto fail6;
+    }
+
     return 0;
 
+fail6:
+    drm_irq_uninstall(drm_dev);
+fail5:
+    drm_vblank_cleanup(drm_dev);
 fail4:
     drm_mode_config_cleanup(vigs_dev->drm_dev);
     vigs_comm_destroy(vigs_dev->comm);
@@ -337,6 +361,8 @@ void vigs_device_cleanup(struct vigs_device *vigs_dev)
     DRM_DEBUG_DRIVER("enter\n");
 
     vigs_fbdev_destroy(vigs_dev->fbdev);
+    drm_irq_uninstall(vigs_dev->drm_dev);
+    drm_vblank_cleanup(vigs_dev->drm_dev);
     drm_mode_config_cleanup(vigs_dev->drm_dev);
     vigs_comm_destroy(vigs_dev->comm);
     vigs_mman_destroy(vigs_dev->mman);
index a3ea2fb119cd9cf9a8d7d5854652b3355ece1093..29f09c10431a0a618463dcbf90a55a18d0906452 100644 (file)
@@ -15,6 +15,8 @@ struct vigs_device
     struct drm_device *drm_dev;
     struct pci_dev *pci_dev;
 
+    struct list_head pageflip_event_list;
+
     resource_size_t vram_base;
     resource_size_t vram_size;
 
index f5d3fd746e9db65393cfc34549640e898651c57e..d14f4ae2b2736b6ca43435a02d0dc5c65c8efcd5 100644 (file)
@@ -5,6 +5,7 @@
 #include "vigs_comm.h"
 #include "vigs_surface.h"
 #include "vigs_execbuffer.h"
+#include "vigs_irq.h"
 #include "drmP.h"
 #include "drm.h"
 #include <linux/module.h>
@@ -60,6 +61,7 @@ static const struct file_operations vigs_drm_driver_fops =
     .poll = drm_poll,
     .fasync = drm_fasync,
     .mmap = vigs_device_mmap,
+    .read = drm_read
 };
 
 static int vigs_drm_load(struct drm_device *dev, unsigned long flags)
@@ -106,6 +108,30 @@ static int vigs_drm_unload(struct drm_device *dev)
     return 0;
 }
 
+
+static void vigs_drm_preclose(struct drm_device *dev,
+                              struct drm_file *file_priv)
+{
+    struct vigs_device *vigs_dev = dev->dev_private;
+    struct drm_pending_vblank_event *event, *tmp;
+    unsigned long flags;
+
+    DRM_DEBUG_DRIVER("enter\n");
+
+    spin_lock_irqsave(&dev->event_lock, flags);
+
+    list_for_each_entry_safe(event, tmp,
+                             &vigs_dev->pageflip_event_list,
+                             base.link) {
+        if (event->base.file_priv == file_priv) {
+            list_del(&event->base.link);
+            event->base.destroy(&event->base);
+        }
+    }
+
+    spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 static void vigs_drm_postclose(struct drm_device *dev,
                                struct drm_file *file_priv)
 {
@@ -127,11 +153,17 @@ static void vigs_drm_lastclose(struct drm_device *dev)
 
 static struct drm_driver vigs_drm_driver =
 {
-    .driver_features = DRIVER_GEM | DRIVER_MODESET,
+    .driver_features = DRIVER_GEM | DRIVER_MODESET |
+                       DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
     .load = vigs_drm_load,
     .unload = vigs_drm_unload,
+    .preclose = vigs_drm_preclose,
     .postclose = vigs_drm_postclose,
     .lastclose = vigs_drm_lastclose,
+    .get_vblank_counter = drm_vblank_count,
+    .enable_vblank = vigs_enable_vblank,
+    .disable_vblank = vigs_disable_vblank,
+    .irq_handler = vigs_irq_handler,
     .gem_init_object = vigs_gem_init_object,
     .gem_free_object = vigs_gem_free_object,
     .gem_open_object = vigs_gem_open_object,
diff --git a/drivers/gpu/drm/vigs/vigs_irq.c b/drivers/gpu/drm/vigs/vigs_irq.c
new file mode 100644 (file)
index 0000000..1305fc0
--- /dev/null
@@ -0,0 +1,116 @@
+#include "vigs_irq.h"
+#include "vigs_device.h"
+#include "vigs_regs.h"
+
+static void vigs_finish_pageflips(struct vigs_device *vigs_dev)
+{
+    struct drm_pending_vblank_event *event, *tmp;
+    struct timeval now;
+    unsigned long flags;
+    bool is_checked = false;
+
+    spin_lock_irqsave(&vigs_dev->drm_dev->event_lock, flags);
+
+    list_for_each_entry_safe(event, tmp,
+                             &vigs_dev->pageflip_event_list,
+                             base.link) {
+        if (event->pipe != 0) {
+            continue;
+        }
+
+        is_checked = true;
+
+        do_gettimeofday(&now);
+        event->event.sequence = 0;
+        event->event.tv_sec = now.tv_sec;
+        event->event.tv_usec = now.tv_usec;
+
+        list_move_tail(&event->base.link, &event->base.file_priv->event_list);
+        wake_up_interruptible(&event->base.file_priv->event_wait);
+    }
+
+    if (is_checked) {
+        /*
+         * Call 'drm_vblank_put' only in case that 'drm_vblank_get' was
+         * called.
+         */
+        if (atomic_read(&vigs_dev->drm_dev->vblank_refcount[0]) > 0) {
+            drm_vblank_put(vigs_dev->drm_dev, 0);
+        }
+    }
+
+    spin_unlock_irqrestore(&vigs_dev->drm_dev->event_lock, flags);
+}
+
+int vigs_enable_vblank(struct drm_device *drm_dev, int crtc)
+{
+    struct vigs_device *vigs_dev = drm_dev->dev_private;
+    u32 value;
+
+    DRM_DEBUG_KMS("enter: crtc = %d\n", crtc);
+
+    if (crtc != 0) {
+        DRM_ERROR("bad crtc = %d", crtc);
+        return -EINVAL;
+    }
+
+    value = readl(vigs_dev->io_map->handle + VIGS_REG_INT);
+
+    BUG_ON(value & VIGS_REG_INT_VBLANK_PENDING);
+
+    value |= VIGS_REG_INT_VBLANK_ENABLE;
+
+    writel(value, vigs_dev->io_map->handle + VIGS_REG_INT);
+
+    return 0;
+}
+
+void vigs_disable_vblank(struct drm_device *drm_dev, int crtc)
+{
+    struct vigs_device *vigs_dev = drm_dev->dev_private;
+    u32 value;
+
+    DRM_DEBUG_KMS("enter: crtc = %d\n", crtc);
+
+    if (crtc != 0) {
+        DRM_ERROR("bad crtc = %d", crtc);
+    }
+
+    value = readl(vigs_dev->io_map->handle + VIGS_REG_INT);
+
+    value &= ~VIGS_REG_INT_VBLANK_ENABLE;
+
+    writel(value, vigs_dev->io_map->handle + VIGS_REG_INT);
+}
+
+irqreturn_t vigs_irq_handler(DRM_IRQ_ARGS)
+{
+    struct drm_device *drm_dev = (struct drm_device*)arg;
+    struct vigs_device *vigs_dev = drm_dev->dev_private;
+    u32 value;
+
+    value = readl(vigs_dev->io_map->handle + VIGS_REG_INT);
+
+    if ((value & VIGS_REG_INT_VBLANK_PENDING) == 0) {
+        return IRQ_NONE;
+    }
+
+    /*
+     * Clear the interrupt first in order
+     * not to stall the hardware.
+     */
+
+    value &= ~VIGS_REG_INT_VBLANK_PENDING;
+
+    writel(value, vigs_dev->io_map->handle + VIGS_REG_INT);
+
+    /*
+     * Handle VBLANK.
+     */
+
+    drm_handle_vblank(drm_dev, 0);
+
+    vigs_finish_pageflips(vigs_dev);
+
+    return IRQ_HANDLED;
+}
diff --git a/drivers/gpu/drm/vigs/vigs_irq.h b/drivers/gpu/drm/vigs/vigs_irq.h
new file mode 100644 (file)
index 0000000..9bd9613
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _VIGS_IRQ_H_
+#define _VIGS_IRQ_H_
+
+#include "drmP.h"
+
+int vigs_enable_vblank(struct drm_device *drm_dev, int crtc);
+
+void vigs_disable_vblank(struct drm_device *drm_dev, int crtc);
+
+irqreturn_t vigs_irq_handler(DRM_IRQ_ARGS);
+
+#endif
index c63f17c9e99aa464e7744ae1d8740174c7d88d58..cd39cc9f6bf8bfda00e46a855116ba816f6a6a7a 100644 (file)
@@ -14,7 +14,7 @@
 /*
  * Bump this whenever protocol changes.
  */
-#define VIGS_PROTOCOL_VERSION 13
+#define VIGS_PROTOCOL_VERSION 14
 
 typedef signed char vigsp_s8;
 typedef signed short vigsp_s16;
diff --git a/drivers/gpu/drm/vigs/vigs_regs.h b/drivers/gpu/drm/vigs/vigs_regs.h
new file mode 100644 (file)
index 0000000..f3c08a6
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _VIGS_REGS_H_
+#define _VIGS_REGS_H_
+
+#define VIGS_REG_EXEC 0
+#define VIGS_REG_INT 8
+
+#define VIGS_REG_INT_VBLANK_ENABLE 1
+#define VIGS_REG_INT_VBLANK_PENDING 2
+
+#endif