From aee6db66f45d3b5be9f7d3c3e0ee11c972452cfd Mon Sep 17 00:00:00 2001 From: Stanislav Vorobiov Date: Mon, 24 Jun 2013 16:56:12 +0400 Subject: [PATCH] VIGS: DRM VBLANK handling implemented VIGS: DRM pageflip handling implemented VIGS DRM driver now supports both VBLANK events and pageflip events --- drivers/gpu/drm/vigs/Makefile | 3 +- drivers/gpu/drm/vigs/vigs_comm.c | 3 +- drivers/gpu/drm/vigs/vigs_crtc.c | 105 +++++++++++++++++------- drivers/gpu/drm/vigs/vigs_device.c | 28 ++++++- drivers/gpu/drm/vigs/vigs_device.h | 2 + drivers/gpu/drm/vigs/vigs_driver.c | 34 +++++++- drivers/gpu/drm/vigs/vigs_irq.c | 116 +++++++++++++++++++++++++++ drivers/gpu/drm/vigs/vigs_irq.h | 12 +++ drivers/gpu/drm/vigs/vigs_protocol.h | 2 +- drivers/gpu/drm/vigs/vigs_regs.h | 10 +++ 10 files changed, 281 insertions(+), 34 deletions(-) create mode 100644 drivers/gpu/drm/vigs/vigs_irq.c create mode 100644 drivers/gpu/drm/vigs/vigs_irq.h create mode 100644 drivers/gpu/drm/vigs/vigs_regs.h diff --git a/drivers/gpu/drm/vigs/Makefile b/drivers/gpu/drm/vigs/Makefile index 539de77ea0ec..f3d0fb9fb483 100644 --- a/drivers/gpu/drm/vigs/Makefile +++ b/drivers/gpu/drm/vigs/Makefile @@ -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 diff --git a/drivers/gpu/drm/vigs/vigs_comm.c b/drivers/gpu/drm/vigs/vigs_comm.c index cf94c090ec14..967ac3d8f30f 100644 --- a/drivers/gpu/drm/vigs/vigs_comm.c +++ b/drivers/gpu/drm/vigs/vigs_comm.c @@ -1,6 +1,7 @@ #include "vigs_comm.h" #include "vigs_device.h" #include "vigs_execbuffer.h" +#include "vigs_regs.h" #include 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) diff --git a/drivers/gpu/drm/vigs/vigs_crtc.c b/drivers/gpu/drm/vigs/vigs_crtc.c index 7fcfbdfa0c2c..581f6870934d 100644 --- a/drivers/gpu/drm/vigs/vigs_crtc.c +++ b/drivers/gpu/drm/vigs/vigs_crtc.c @@ -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, }; diff --git a/drivers/gpu/drm/vigs/vigs_device.c b/drivers/gpu/drm/vigs/vigs_device.c index 5bc96867f66f..2db30afb4667 100644 --- a/drivers/gpu/drm/vigs/vigs_device.c +++ b/drivers/gpu/drm/vigs/vigs_device.c @@ -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); diff --git a/drivers/gpu/drm/vigs/vigs_device.h b/drivers/gpu/drm/vigs/vigs_device.h index a3ea2fb119cd..29f09c10431a 100644 --- a/drivers/gpu/drm/vigs/vigs_device.h +++ b/drivers/gpu/drm/vigs/vigs_device.h @@ -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; diff --git a/drivers/gpu/drm/vigs/vigs_driver.c b/drivers/gpu/drm/vigs/vigs_driver.c index f5d3fd746e9d..d14f4ae2b273 100644 --- a/drivers/gpu/drm/vigs/vigs_driver.c +++ b/drivers/gpu/drm/vigs/vigs_driver.c @@ -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 @@ -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 index 000000000000..1305fc028354 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_irq.c @@ -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 index 000000000000..9bd961340b8e --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_irq.h @@ -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 diff --git a/drivers/gpu/drm/vigs/vigs_protocol.h b/drivers/gpu/drm/vigs/vigs_protocol.h index c63f17c9e99a..cd39cc9f6bf8 100644 --- a/drivers/gpu/drm/vigs/vigs_protocol.h +++ b/drivers/gpu/drm/vigs/vigs_protocol.h @@ -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 index 000000000000..f3c08a62aefe --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_regs.h @@ -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 -- 2.34.1