For each physical crtc, Gvt-g has a kernel thread to get the HW vblank
event on time and inject the interrupt event to the vGPUs which the
display planes of the crtc are assigned to.

Signed-off-by: Tina Zhang <tina.zh...@intel.com>
Cc: Zhenyu Wang <zhen...@linux.intel.com>
Cc: Zhi Wang <zhi.a.w...@intel.com>
---
 drivers/gpu/drm/i915/gvt/display.c   |  30 ++++++----
 drivers/gpu/drm/i915/gvt/display.h   |   3 +-
 drivers/gpu/drm/i915/gvt/gvt.c       | 112 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/gvt/gvt.h       |   3 +
 drivers/gpu/drm/i915/gvt/handlers.c  |   4 +-
 drivers/gpu/drm/i915/gvt/interrupt.c |  54 +++++++++++++++++
 drivers/gpu/drm/i915/gvt/interrupt.h |   2 +
 7 files changed, 193 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/gvt/display.c 
b/drivers/gpu/drm/i915/gvt/display.c
index e06d8d6..eec8336 100644
--- a/drivers/gpu/drm/i915/gvt/display.c
+++ b/drivers/gpu/drm/i915/gvt/display.c
@@ -358,7 +358,7 @@ static int setup_virtual_dp_monitor(struct intel_vgpu 
*vgpu, int port_num,
  * enabled/disabled virtual pipes.
  *
  */
-void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt)
+void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt, bool 
use_hw_vblank)
 {
        struct intel_gvt_irq *irq = &gvt->irq;
        struct intel_vgpu *vgpu;
@@ -369,25 +369,29 @@ void intel_gvt_check_vblank_emulation(struct intel_gvt 
*gvt)
        for_each_active_vgpu(gvt, vgpu, id) {
                for (pipe = 0; pipe < I915_MAX_PIPES; pipe++) {
                        if (pipe_is_enabled(vgpu, pipe)) {
-                               found = true;
-                               break;
+                               if (use_hw_vblank) {
+                                       
wake_up_process(gvt->vblank_thread[pipe]);
+                               } else {
+                                       found = true;
+                                       break;
+                               }
                        }
                }
-               if (found)
-                       break;
        }
 
-       /* all the pipes are disabled */
-       if (!found)
-               hrtimer_cancel(&irq->vblank_timer.timer);
-       else
-               hrtimer_start(&irq->vblank_timer.timer,
-                       ktime_add_ns(ktime_get(), irq->vblank_timer.period),
-                       HRTIMER_MODE_ABS);
+       if (!use_hw_vblank) {
+               /* all the pipes are disabled */
+               if (!found)
+                       hrtimer_cancel(&irq->vblank_timer.timer);
+               else
+                       hrtimer_start(&irq->vblank_timer.timer,
+                                     ktime_add_ns(ktime_get(), 
irq->vblank_timer.period),
+                                     HRTIMER_MODE_ABS);
+       }
        mutex_unlock(&gvt->lock);
 }
 
-static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
+void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
 {
        struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
        struct intel_vgpu_irq *irq = &vgpu->irq;
diff --git a/drivers/gpu/drm/i915/gvt/display.h 
b/drivers/gpu/drm/i915/gvt/display.h
index 5d929bb..e0a85df 100644
--- a/drivers/gpu/drm/i915/gvt/display.h
+++ b/drivers/gpu/drm/i915/gvt/display.h
@@ -187,12 +187,13 @@ static inline char *vgpu_edid_str(enum intel_vgpu_edid id)
 }
 
 void intel_gvt_emulate_vblank(struct intel_gvt *gvt);
-void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt);
+void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt, bool 
use_hw_vblank);
 
 int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution);
 void intel_vgpu_reset_display(struct intel_vgpu *vgpu);
 void intel_vgpu_clean_display(struct intel_vgpu *vgpu);
 
+void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe);
 int pipe_is_enabled(struct intel_vgpu *vgpu, int pipe);
 
 
diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c
index 733a2a0..a017ac4 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.c
+++ b/drivers/gpu/drm/i915/gvt/gvt.c
@@ -250,6 +250,94 @@ static void init_device_info(struct intel_gvt *gvt)
        info->msi_cap_offset = pdev->msi_cap;
 }
 
+static int emulate_crtc_a_vblank_thread(void *data)
+{
+       struct intel_gvt *gvt = (struct intel_gvt *)data;
+       struct drm_device *dev = &gvt->dev_priv->drm;
+       struct intel_vgpu *vgpu;
+       struct completion vblank_done = gvt->vblank_done[PIPE_A];
+       int ret;
+       int id;
+
+       DRM_DEBUG_KMS("vblank service thread a start\n");
+
+       while (!kthread_should_stop()) {
+               init_completion(&vblank_done);
+retry:
+               ret = intel_gvt_register_HW_vblank_event(dev, &vblank_done, 
PIPE_A);
+               if (ret) {
+                       goto retry;
+               }
+
+               /* wait for the complete */
+               wait_for_completion_interruptible(&vblank_done);
+
+               /* inject the vblank to gvt */
+               for_each_active_vgpu(gvt, vgpu, id)
+                       emulate_vblank_on_pipe(vgpu, PIPE_A);
+       }
+
+       return 0;
+}
+
+static int emulate_crtc_b_vblank_thread(void *data)
+{
+       struct intel_gvt *gvt = (struct intel_gvt *)data;
+       struct drm_device *dev = &gvt->dev_priv->drm;
+       struct intel_vgpu *vgpu;
+       struct completion vblank_done = gvt->vblank_done[PIPE_B];
+       int ret;
+       int id;
+
+       DRM_DEBUG_KMS("vblank service thread b start\n");
+
+       while (!kthread_should_stop()) {
+               init_completion(&vblank_done);
+retry:
+               ret = intel_gvt_register_HW_vblank_event(dev, &vblank_done, 
PIPE_B);
+               if (ret)
+                       goto retry;
+
+               /* wait for the complete */
+               wait_for_completion_interruptible(&vblank_done);
+
+               /* inject the vblank to gvt */
+               for_each_active_vgpu(gvt, vgpu, id)
+                       emulate_vblank_on_pipe(vgpu, PIPE_B);
+       }
+
+       return 0;
+}
+
+static int emulate_crtc_c_vblank_thread(void *data)
+{
+       struct intel_gvt *gvt = (struct intel_gvt *)data;
+       struct drm_device *dev = &gvt->dev_priv->drm;
+       struct intel_vgpu *vgpu;
+       struct completion vblank_done = gvt->vblank_done[PIPE_C];
+       int ret;
+       int id;
+
+       DRM_DEBUG_KMS("vblank service thread c start\n");
+
+       while (!kthread_should_stop()) {
+               init_completion(&vblank_done);
+retry:
+               ret = intel_gvt_register_HW_vblank_event(dev, &vblank_done, 
PIPE_C);
+               if (ret)
+                       goto retry;
+
+               /* wait for the complete */
+               wait_for_completion_interruptible(&vblank_done);
+
+               /* inject the vblank to gvt */
+               for_each_active_vgpu(gvt, vgpu, id)
+                       emulate_vblank_on_pipe(vgpu, PIPE_C);
+       }
+
+       return 0;
+}
+
 static int gvt_service_thread(void *data)
 {
        struct intel_gvt *gvt = (struct intel_gvt *)data;
@@ -285,6 +373,9 @@ static int gvt_service_thread(void *data)
 static void clean_service_thread(struct intel_gvt *gvt)
 {
        kthread_stop(gvt->service_thread);
+       kthread_stop(gvt->vblank_thread[PIPE_A]);
+       kthread_stop(gvt->vblank_thread[PIPE_B]);
+       kthread_stop(gvt->vblank_thread[PIPE_C]);
 }
 
 static int init_service_thread(struct intel_gvt *gvt)
@@ -297,6 +388,27 @@ static int init_service_thread(struct intel_gvt *gvt)
                gvt_err("fail to start service thread.\n");
                return PTR_ERR(gvt->service_thread);
        }
+
+       gvt->vblank_thread[PIPE_A] = 
kthread_create(emulate_crtc_a_vblank_thread,
+                       gvt, "emulate_crtc_a_vblank_thread");
+       if (IS_ERR(gvt->vblank_thread[PIPE_A])) {
+               gvt_err("fail to start service thread.\n");
+               return PTR_ERR(gvt->vblank_thread[PIPE_A]);
+       }
+
+       gvt->vblank_thread[PIPE_B] = 
kthread_create(emulate_crtc_b_vblank_thread,
+                       gvt, "emulate_crtc_b_vblank_thread");
+       if (IS_ERR(gvt->vblank_thread[PIPE_B])) {
+               gvt_err("fail to start service thread.\n");
+               return PTR_ERR(gvt->vblank_thread[PIPE_B]);
+       }
+
+       gvt->vblank_thread[PIPE_C] = 
kthread_create(emulate_crtc_c_vblank_thread,
+                       gvt, "emulate_crtc_c_vblank_thread");
+       if (IS_ERR(gvt->vblank_thread[PIPE_C])) {
+               gvt_err("fail to start service thread.\n");
+               return PTR_ERR(gvt->vblank_thread[PIPE_C]);
+       }
        return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index 0ab10b0..96903d2 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -356,6 +356,9 @@ struct intel_gvt {
 
        /* vGPU plane assignment */
        struct assigned_plane assigned_plane[I915_MAX_PIPES][I915_MAX_PLANES];
+
+       struct task_struct *vblank_thread[I915_MAX_PIPES];
+       struct completion vblank_done[I915_MAX_PIPES];
 };
 
 static inline struct intel_gvt *to_gvt(struct drm_i915_private *i915)
diff --git a/drivers/gpu/drm/i915/gvt/handlers.c 
b/drivers/gpu/drm/i915/gvt/handlers.c
index 89a22a5..4ad6611 100644
--- a/drivers/gpu/drm/i915/gvt/handlers.c
+++ b/drivers/gpu/drm/i915/gvt/handlers.c
@@ -449,10 +449,12 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, 
unsigned int offset,
                vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE;
        else
                vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE;
+
        /* vgpu_lock already hold by emulate mmio r/w */
        mutex_unlock(&vgpu->vgpu_lock);
-       intel_gvt_check_vblank_emulation(vgpu->gvt);
+       intel_gvt_check_vblank_emulation(vgpu->gvt, true);
        mutex_lock(&vgpu->vgpu_lock);
+
        return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c 
b/drivers/gpu/drm/i915/gvt/interrupt.c
index 5daa23a..cb92ce4 100644
--- a/drivers/gpu/drm/i915/gvt/interrupt.c
+++ b/drivers/gpu/drm/i915/gvt/interrupt.c
@@ -633,6 +633,60 @@ void intel_vgpu_trigger_virtual_event(struct intel_vgpu 
*vgpu,
        ops->check_pending_irq(vgpu);
 }
 
+static void release_vblank_event(struct completion *completion)
+{
+       ;
+}
+
+int intel_gvt_register_HW_vblank_event(struct drm_device *dev,
+                                    struct completion *vblank_done, int pipe)
+{
+       struct drm_pending_vblank_event *e;
+       struct drm_crtc *crtc;
+       unsigned long flags;
+       int ret;
+
+       DRM_DEBUG_KMS("%s begin with pipe %d, com %p, dev %p", __func__, pipe, 
vblank_done, dev);
+
+       crtc = drm_crtc_from_index(dev, pipe);
+       if (!crtc) {
+               DRM_ERROR("cannot get crtc %d\n", pipe);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = drm_crtc_vblank_get(crtc);
+       if (ret) {
+               DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", 
pipe, ret);
+               goto out;
+       }
+
+       e = kzalloc(sizeof *e, GFP_KERNEL);
+       if (!e) {
+               drm_crtc_vblank_put(crtc);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* create a vblank event */
+       e->event.base.type = DRM_EVENT_VBLANK;
+       e->event.base.length = sizeof(e->event);
+       e->pipe = pipe;
+       e->base.completion = vblank_done;
+       e->base.completion_release = release_vblank_event;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       e->sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
+
+       /* add the event to vblank list */
+       list_add_tail(&e->base.link, &dev->vblank_event_list);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       DRM_DEBUG_KMS("%s: success \n", __func__);
+out:
+       return ret;
+}
+
 static void init_events(
        struct intel_gvt_irq *irq)
 {
diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h 
b/drivers/gpu/drm/i915/gvt/interrupt.h
index 5313fb1..ed4936e 100644
--- a/drivers/gpu/drm/i915/gvt/interrupt.h
+++ b/drivers/gpu/drm/i915/gvt/interrupt.h
@@ -213,6 +213,8 @@ struct intel_gvt_irq {
 
 int intel_gvt_init_irq(struct intel_gvt *gvt);
 void intel_gvt_clean_irq(struct intel_gvt *gvt);
+int intel_gvt_register_HW_vblank_event(struct drm_device *dev,
+                                struct completion *vblank_done, int pipe);
 
 void intel_vgpu_trigger_virtual_event(struct intel_vgpu *vgpu,
        enum intel_gvt_event_type event);
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to