From patchwork Thu Jul 5 03:48:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rodrigo Siqueira X-Patchwork-Id: 10507889 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id ADEB460116 for ; Thu, 5 Jul 2018 03:48:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8ECAB28BD5 for ; Thu, 5 Jul 2018 03:48:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 80FF028D9A; Thu, 5 Jul 2018 03:48:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM, MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 01BA128BD5 for ; Thu, 5 Jul 2018 03:48:51 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8DFD86EBEC; Thu, 5 Jul 2018 03:48:49 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-qt0-x244.google.com (mail-qt0-x244.google.com [IPv6:2607:f8b0:400d:c0d::244]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5CA9B6EBEC for ; Thu, 5 Jul 2018 03:48:48 +0000 (UTC) Received: by mail-qt0-x244.google.com with SMTP id a18-v6so6057579qtj.4 for ; Wed, 04 Jul 2018 20:48:48 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:user-agent; bh=u+ZVFARpzRXgUcmlGfrM1EnQfSST6w0HMWlryedtf5U=; b=eTVT9mmVBK2NiAcvrd+wgUcyriD750YwnNMb1OKcxD6MaCCrVcOqI8jqibYe9JdM5h RuGaiRV2nlAmUYSAJ6BIJ3Nk3yosmXjkbgi2enAbMX+14Eg6lrmpyKjsWSwWW0IAbqbf 3fNejVUkjiNmyZXg/TxZCjLl5ZUceQt/25JY0+ZgaliZ+gM/GAGp9DfICRH6fzHgFC/J MnbVeOLV5U/jOwL6fP3R83aHYSbwLwQWiFpHGnm+gHQVXYa7o8PsqGI7F617Y17Gi4wb Ee/xI7IFu5qKL+2YQt9210EAM5/DotG0idyhlcr8v8IxJwgHqwyKIOXCEW55L7kkvIbM xW8g== X-Gm-Message-State: APt69E2BZz8y2ffszpc3K7LH+v80v1f5SFCuWVqTq1n2dEq94YPG2Tt6 CzuWcQEERZMITzvECDr1RBU= X-Google-Smtp-Source: AAOMgpd55sNdTyIe/JILE3r9Uvt19irPPF9HGHzN7giPnIJ8GCbQobianrUi+13lES5dLiLwRd6bgA== X-Received: by 2002:a0c:b4a9:: with SMTP id c41-v6mr3724043qve.57.1530762527194; Wed, 04 Jul 2018 20:48:47 -0700 (PDT) Received: from smtp.gmail.com ([143.107.45.1]) by smtp.gmail.com with ESMTPSA id 32-v6sm3330319qtc.96.2018.07.04.20.48.45 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 04 Jul 2018 20:48:46 -0700 (PDT) Date: Thu, 5 Jul 2018 00:48:43 -0300 From: Rodrigo Siqueira To: Daniel Vetter , Gustavo Padovan , Sean Paul , Haneen Mohammed Subject: [PATCH V2] drm/vkms: Add vblank events simulated by hrtimers Message-ID: <20180705034843.wxa5krenjrkaersm@smtp.gmail.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: NeoMutt/20180622 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: dri-devel@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP This commit adds regular vblank events simulated through hrtimers, which is a feature required by VKMS to mimic real hardware. Notice that keeping the periodicity as close as possible to the one specified in user space represents a vital constraint. In this sense, the callback function implements an algorithm based on module operations that avoids the accumulation of errors. Assuming we begin operating at timestamp 0, every event should happen at timestamps that are multiples of the requested period, i.e., current_timestamp % period == 0. Since we do *not* generally start at timestamp 0, we calculate the modulus of this initial timestamp and try to make all following events happen when current_timestamp % period == initial_timestamp % period (variables “current_offset” and “base_offset”). We do this by measuring the difference between them at each iteration and adjusting the next waiting period accordingly. Signed-off-by: Rodrigo Siqueira --- drivers/gpu/drm/vkms/vkms_crtc.c | 99 +++++++++++++++++++++++++++++++- drivers/gpu/drm/vkms/vkms_drv.c | 9 +++ drivers/gpu/drm/vkms/vkms_drv.h | 20 +++++++ 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 84cc05506b09..35d39d25df34 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -10,6 +10,83 @@ #include #include +static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) +{ + struct vkms_output *output = container_of(timer, struct vkms_output, + vblank_hrtimer); + struct drm_crtc *crtc = &output->crtc; + ktime_t new_period, current_timestamp, diff_times, current_offset, + candidate_expires; + unsigned long flags; + int ret_overrun; + bool ret; + + current_timestamp = ktime_get(); + current_offset = current_timestamp % output->period_ns; + diff_times = ktime_sub(current_offset, output->base_offset); + candidate_expires = ktime_sub(current_timestamp, diff_times); + if (candidate_expires > output->expires) + output->expires = candidate_expires; + + ret = drm_crtc_handle_vblank(crtc); + if (!ret) + DRM_ERROR("vkms failure on handling vblank"); + + if (output->event) { + spin_lock_irqsave(&crtc->dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, output->event); + output->event = NULL; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + drm_crtc_vblank_put(crtc); + } + + current_timestamp = ktime_get(); + current_offset = current_timestamp % output->period_ns; + diff_times = ktime_sub(output->base_offset, current_offset); + new_period = ktime_add(output->period_ns, diff_times); + output->expires = ktime_add(current_timestamp, new_period); + ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, new_period); + + return HRTIMER_RESTART; +} + +static int vkms_enable_vblank(struct drm_crtc *crtc) +{ + struct vkms_output *out = drm_crtc_to_vkms_output(crtc); + unsigned long period = 1000 * crtc->mode.htotal * crtc->mode.vtotal / + crtc->mode.clock; + ktime_t current_timestamp; + + hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + out->vblank_hrtimer.function = &vkms_vblank_simulate; + out->period_ns = ktime_set(0, period * US_TO_NS); + current_timestamp = ktime_get(); + out->base_offset = current_timestamp % out->period_ns; + out->expires = ktime_add(current_timestamp, out->period_ns); + hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL); + + return 0; +} + +static void vkms_disable_vblank(struct drm_crtc *crtc) +{ + struct vkms_output *out = drm_crtc_to_vkms_output(crtc); + + hrtimer_cancel(&out->vblank_hrtimer); +} + +bool vkms_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe, + int *max_error, ktime_t *vblank_time, + bool in_vblank_irq) +{ + struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev); + struct vkms_output *output = &vkmsdev->output; + + *vblank_time = output->expires; + + return true; +} + static const struct drm_crtc_funcs vkms_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup, @@ -17,6 +94,8 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = vkms_enable_vblank, + .disable_vblank = vkms_disable_vblank, }; static int vkms_crtc_atomic_check(struct drm_crtc *crtc, @@ -28,11 +107,27 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc, static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { + drm_crtc_vblank_on(crtc); +} + +static void vkms_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_s) +{ + struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc); + + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + vkms_output->event = crtc->state->event; + crtc->state->event = NULL; + } } static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { - .atomic_check = vkms_crtc_atomic_check, - .atomic_enable = vkms_crtc_atomic_enable, + .atomic_check = vkms_crtc_atomic_check, + .atomic_enable = vkms_crtc_atomic_enable, + .atomic_begin = vkms_crtc_atomic_begin, }; int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index fe93f8c17997..6368048c6e54 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -55,6 +55,7 @@ static struct drm_driver vkms_driver = { .dumb_create = vkms_dumb_create, .dumb_map_offset = vkms_dumb_map, .gem_vm_ops = &vkms_gem_vm_ops, + .get_vblank_timestamp = vkms_get_vblank_timestamp, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -102,6 +103,14 @@ static int __init vkms_init(void) goto out_fini; } + vkms_device->drm.irq_enabled = true; + + ret = drm_vblank_init(&vkms_device->drm, 1); + if (ret) { + DRM_ERROR("Failed to vblank\n"); + goto out_fini; + } + ret = vkms_modeset_init(vkms_device); if (ret) goto out_unregister; diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 76f1720f81a5..b69a223bdfe6 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -5,6 +5,7 @@ #include #include #include +#include #define XRES_MIN 32 #define YRES_MIN 32 @@ -15,6 +16,9 @@ #define XRES_MAX 8192 #define YRES_MAX 8192 +#define DEFAULT_VBLANK_NS 16666666 +#define US_TO_NS 1000 + static const u32 vkms_formats[] = { DRM_FORMAT_XRGB8888, }; @@ -23,6 +27,11 @@ struct vkms_output { struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector connector; + struct hrtimer vblank_hrtimer; + ktime_t period_ns; + ktime_t base_offset; + ktime_t expires; + struct drm_pending_vblank_event *event; }; struct vkms_device { @@ -37,9 +46,20 @@ struct vkms_gem_object { struct page **pages; }; +#define drm_crtc_to_vkms_output(target) \ + container_of(target, struct vkms_output, crtc) + +#define drm_device_to_vkms_device(target) \ + container_of(target, struct vkms_device, drm) + +/* CRTC */ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, struct drm_plane *cursor); +bool vkms_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe, + int *max_error, ktime_t *vblank_time, + bool in_vblank_irq); + int vkms_output_init(struct vkms_device *vkmsdev); struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev);