[RFC PATCH 7/7] drm/i915/gvt: Introduce HW Vblank interrupt to vGPU
Tina Zhang
tina.zhang at intel.com
Mon Dec 3 07:35:22 UTC 2018
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.zhang at intel.com>
Cc: Zhenyu Wang <zhenyuw at linux.intel.com>
Cc: Zhi Wang <zhi.a.wang at 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
More information about the intel-gvt-dev
mailing list