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
#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,
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)
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;
* 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;
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,
{
}
+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;
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,
};
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);
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);
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);
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;
#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>
.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)
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)
{
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,
--- /dev/null
+#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;
+}
--- /dev/null
+#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
/*
* 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;
--- /dev/null
+#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