Currently, we are working to make VKMS pass in the kms_flip test (IGT). As a result, we made a series of changes in the module with the goal to meet some of the necessary steps required by kms_flip. This patchset comprises all the modifications needed to make kms_flip partially pass.
Finally, this patchset is based on: - [PATCH V2 0/5] drm/vkms: Updates to meet basic kms_flip requirements (https://www.spinics.net/lists/dri-devel/msg180544.html) - [PATCH V3] drm/vkms: Add vblank events simulated by hrtimers (https://www.spinics.net/lists/dri-devel/msg182600.html)
Rodrigo Siqueira (4): drm/vkms: Add dumb operations drm/vkms: Add framebuffer and plane helpers drm/vkms: Add connectors helpers drm/vkms: Add vblank events simulated by hrtimers
drivers/gpu/drm/vkms/Makefile | 2 +- drivers/gpu/drm/vkms/vkms_crtc.c | 95 +++++++++++++++ drivers/gpu/drm/vkms/vkms_drv.c | 28 ++++- drivers/gpu/drm/vkms/vkms_drv.h | 47 ++++++++ drivers/gpu/drm/vkms/vkms_gem.c | 179 +++++++++++++++++++++++++++++ drivers/gpu/drm/vkms/vkms_output.c | 20 ++++ drivers/gpu/drm/vkms/vkms_plane.c | 11 ++ 7 files changed, 375 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/vkms/vkms_gem.c
VKMS currently does not handle dumb data, and as a consequence, it does not provide mechanisms for handling gem. This commit adds the necessary support for gem object/handler and the dumb functions.
Changes since V1: Daniel Vetter: - Add dumb buffer support to the same patchset Changes since V2: Haneen: - Add missing gem_free_object_unlocked callback to fix the warning "Memory manager not clean during takedown" Changes since V3: - None
Signed-off-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/Makefile | 2 +- drivers/gpu/drm/vkms/vkms_drv.c | 10 ++ drivers/gpu/drm/vkms/vkms_drv.h | 23 ++++ drivers/gpu/drm/vkms/vkms_gem.c | 179 ++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/vkms/vkms_gem.c
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 3f774a6a9c58..986297da51bf 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -1,3 +1,3 @@ -vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o +vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o vkms_gem.o
obj-$(CONFIG_DRM_VKMS) += vkms.o diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 740a4cbfed91..6ea2fd97bef9 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -37,6 +37,12 @@ static const struct file_operations vkms_driver_fops = { .release = drm_release, };
+static const struct vm_operations_struct vkms_gem_vm_ops = { + .fault = vkms_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + static void vkms_release(struct drm_device *dev) { struct vkms_device *vkms = container_of(dev, struct vkms_device, drm); @@ -50,6 +56,10 @@ static struct drm_driver vkms_driver = { .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, .release = vkms_release, .fops = &vkms_driver_fops, + .dumb_create = vkms_dumb_create, + .dumb_map_offset = vkms_dumb_map, + .gem_vm_ops = &vkms_gem_vm_ops, + .gem_free_object_unlocked = vkms_gem_free_object,
.name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index b0f9d2e61a42..cce4694cafb9 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -3,6 +3,7 @@
#include <drm/drmP.h> #include <drm/drm.h> +#include <drm/drm_gem.h> #include <drm/drm_encoder.h>
static const u32 vkms_formats[] = { @@ -21,6 +22,12 @@ struct vkms_device { struct vkms_output output; };
+struct vkms_gem_object { + struct drm_gem_object gem; + struct mutex pages_lock; /* Page lock used in page fault handler */ + struct page **pages; +}; + int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, struct drm_plane *cursor);
@@ -28,4 +35,20 @@ int vkms_output_init(struct vkms_device *vkmsdev);
struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev);
+/* Gem stuff */ +struct drm_gem_object *vkms_gem_create(struct drm_device *dev, + struct drm_file *file, + u32 *handle, + u64 size); + +int vkms_gem_fault(struct vm_fault *vmf); + +int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + +int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, + u32 handle, u64 *offset); + +void vkms_gem_free_object(struct drm_gem_object *obj); + #endif /* _VKMS_DRV_H_ */ diff --git a/drivers/gpu/drm/vkms/vkms_gem.c b/drivers/gpu/drm/vkms/vkms_gem.c new file mode 100644 index 000000000000..c7e38368602b --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_gem.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/shmem_fs.h> + +#include "vkms_drv.h" + +static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev, + u64 size) +{ + struct vkms_gem_object *obj; + int ret; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return ERR_PTR(-ENOMEM); + + size = roundup(size, PAGE_SIZE); + ret = drm_gem_object_init(dev, &obj->gem, size); + if (ret) { + kfree(obj); + return ERR_PTR(ret); + } + + mutex_init(&obj->pages_lock); + + return obj; +} + +void vkms_gem_free_object(struct drm_gem_object *obj) +{ + struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object, + gem); + + kvfree(gem->pages); + mutex_destroy(&gem->pages_lock); + drm_gem_object_release(obj); + kfree(gem); +} + +int vkms_gem_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct vkms_gem_object *obj = vma->vm_private_data; + unsigned long vaddr = vmf->address; + pgoff_t page_offset; + loff_t num_pages; + int ret; + + page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT; + num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE); + + if (page_offset > num_pages) + return VM_FAULT_SIGBUS; + + ret = -ENOENT; + mutex_lock(&obj->pages_lock); + if (obj->pages) { + get_page(obj->pages[page_offset]); + vmf->page = obj->pages[page_offset]; + ret = 0; + } + mutex_unlock(&obj->pages_lock); + if (ret) { + struct page *page; + struct address_space *mapping; + + mapping = file_inode(obj->gem.filp)->i_mapping; + page = shmem_read_mapping_page(mapping, page_offset); + + if (!IS_ERR(page)) { + vmf->page = page; + ret = 0; + } else { + switch (PTR_ERR(page)) { + case -ENOSPC: + case -ENOMEM: + ret = VM_FAULT_OOM; + break; + case -EBUSY: + ret = VM_FAULT_RETRY; + break; + case -EFAULT: + case -EINVAL: + ret = VM_FAULT_SIGBUS; + break; + default: + WARN_ON(PTR_ERR(page)); + ret = VM_FAULT_SIGBUS; + break; + } + } + } + return ret; +} + +struct drm_gem_object *vkms_gem_create(struct drm_device *dev, + struct drm_file *file, + u32 *handle, + u64 size) +{ + struct vkms_gem_object *obj; + int ret; + + if (!file || !dev || !handle) + return ERR_PTR(-EINVAL); + + obj = __vkms_gem_create(dev, size); + if (IS_ERR(obj)) + return ERR_CAST(obj); + + ret = drm_gem_handle_create(file, &obj->gem, handle); + drm_gem_object_put_unlocked(&obj->gem); + if (ret) { + drm_gem_object_release(&obj->gem); + kfree(obj); + return ERR_PTR(ret); + } + + return &obj->gem; +} + +int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct drm_gem_object *gem_obj; + u64 pitch, size; + + if (!args || !dev || !file) + return -EINVAL; + + pitch = args->width * DIV_ROUND_UP(args->bpp, 8); + size = pitch * args->height; + + if (!size) + return -EINVAL; + + gem_obj = vkms_gem_create(dev, file, &args->handle, size); + if (IS_ERR(gem_obj)) + return PTR_ERR(gem_obj); + + args->size = gem_obj->size; + args->pitch = pitch; + + DRM_DEBUG_DRIVER("Created object of size %lld\n", size); + + return 0; +} + +int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, + u32 handle, u64 *offset) +{ + struct drm_gem_object *obj; + int ret; + + obj = drm_gem_object_lookup(file, handle); + if (!obj) + return -ENOENT; + + if (!obj->filp) { + ret = -EINVAL; + goto unref; + } + + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto unref; + + *offset = drm_vma_node_offset_addr(&obj->vma_node); +unref: + drm_gem_object_put_unlocked(obj); + + return ret; +}
This patch appends the minimum helpers related to framebuffer and plane to make vkms minimally usable.
Changes since V1: - None Changes since V2: - Squash "Add plane helper struct" and "Add helper for framebuffer create" Changes since V3: Daniel Vetter: - Remove atomic_check from plane helper
Signed-off-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/vkms_drv.c | 3 +++ drivers/gpu/drm/vkms/vkms_plane.c | 11 +++++++++++ 2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 6ea2fd97bef9..b5d597a42f20 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -9,6 +9,8 @@ #include <drm/drm_gem.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_helper.h> #include "vkms_drv.h"
#define DRIVER_NAME "vkms" @@ -69,6 +71,7 @@ static struct drm_driver vkms_driver = { };
static const struct drm_mode_config_funcs vkms_mode_funcs = { + .fb_create = drm_gem_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 2c25b1d6ab5b..9f75b1e2c1c4 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -19,6 +19,15 @@ static const struct drm_plane_funcs vkms_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
+static void vkms_primary_plane_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + +static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = { + .atomic_update = vkms_primary_plane_update, +}; + struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) { struct drm_device *dev = &vkmsdev->drm; @@ -42,5 +51,7 @@ struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) return ERR_PTR(ret); }
+ drm_plane_helper_add(plane, &vkms_primary_helper_funcs); + return plane; }
On Thu, Jul 12, 2018 at 10:41:02AM -0300, Rodrigo Siqueira wrote:
This patch appends the minimum helpers related to framebuffer and plane to make vkms minimally usable.
Changes since V1:
- None
Changes since V2:
- Squash "Add plane helper struct" and "Add helper for framebuffer create"
Changes since V3: Daniel Vetter:
- Remove atomic_check from plane helper
Signed-off-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com
Ok I pulled this one in, plus the 3 other patches from v3. Since I pushed the v3 patches out already I couldn't pick the v4 ones here, hopefully that didn't create a chaos. Otherwise I guess we'd need a fixup. So please rebase onto latest drm-misc-next and check it all.
Thanks, Daniel
drivers/gpu/drm/vkms/vkms_drv.c | 3 +++ drivers/gpu/drm/vkms/vkms_plane.c | 11 +++++++++++ 2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 6ea2fd97bef9..b5d597a42f20 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -9,6 +9,8 @@ #include <drm/drm_gem.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_fb_helper.h> #include "vkms_drv.h"
#define DRIVER_NAME "vkms" @@ -69,6 +71,7 @@ static struct drm_driver vkms_driver = { };
static const struct drm_mode_config_funcs vkms_mode_funcs = {
- .fb_create = drm_gem_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit,
}; diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 2c25b1d6ab5b..9f75b1e2c1c4 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -19,6 +19,15 @@ static const struct drm_plane_funcs vkms_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
+static void vkms_primary_plane_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
+{ +}
+static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = {
- .atomic_update = vkms_primary_plane_update,
+};
struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) { struct drm_device *dev = &vkmsdev->drm; @@ -42,5 +51,7 @@ struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) return ERR_PTR(ret); }
- drm_plane_helper_add(plane, &vkms_primary_helper_funcs);
- return plane;
}
2.18.0
This patch adds the struct drm_connector_helper_funcs with some necessary hooks. Additionally, it also adds some missing hooks at drm_connector_funcs.
Changes since V1: - None Changes since V2: Daniel Vetter: - Remove vkms_conn_mode_valid Changes since V3: - None
Signed-off-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/vkms_drv.c | 6 ------ drivers/gpu/drm/vkms/vkms_drv.h | 9 +++++++++ drivers/gpu/drm/vkms/vkms_output.c | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index b5d597a42f20..0557d1d8aa5b 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -19,12 +19,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0
-#define XRES_MIN 32 -#define YRES_MIN 32 - -#define XRES_MAX 8192 -#define YRES_MAX 8192 - static struct vkms_device *vkms_device;
static const struct file_operations vkms_driver_fops = { diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index cce4694cafb9..57d7367fd814 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -6,6 +6,15 @@ #include <drm/drm_gem.h> #include <drm/drm_encoder.h>
+#define XRES_MIN 32 +#define YRES_MIN 32 + +#define XRES_DEF 1024 +#define YRES_DEF 768 + +#define XRES_MAX 8192 +#define YRES_MAX 8192 + static const u32 vkms_formats[] = { DRM_FORMAT_XRGB8888, }; diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c index 48143eac3c12..a7c0ec9b643f 100644 --- a/drivers/gpu/drm/vkms/vkms_output.c +++ b/drivers/gpu/drm/vkms/vkms_output.c @@ -8,6 +8,7 @@
#include "vkms_drv.h" #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h>
static void vkms_connector_destroy(struct drm_connector *connector) { @@ -18,12 +19,29 @@ static void vkms_connector_destroy(struct drm_connector *connector) static const struct drm_connector_funcs vkms_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = vkms_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
static const struct drm_encoder_funcs vkms_encoder_funcs = { .destroy = drm_encoder_cleanup, };
+static int vkms_conn_get_modes(struct drm_connector *connector) +{ + int count; + + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); + + return count; +} + +static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = { + .get_modes = vkms_conn_get_modes, +}; + int vkms_output_init(struct vkms_device *vkmsdev) { struct vkms_output *output = &vkmsdev->output; @@ -49,6 +67,8 @@ int vkms_output_init(struct vkms_device *vkmsdev) goto err_connector; }
+ drm_connector_helper_add(connector, &vkms_conn_helper_funcs); + ret = drm_connector_register(connector); if (ret) { DRM_ERROR("Failed to register connector\n");
This commit adds regular vblank events simulated through hrtimers, which is a feature required by VKMS to mimic real hardware. Additionally, all the vblank event send after pageflip is kept in the atomic_flush function.
Changes since V1: - Compute the vblank timer interval per interruption Ville Syrjälä and Daniel Vetter: - Removes hardcoded vblank interval to get it from user space
Changes since V2: Chris Wilson - Removes unnecessary algorithm to compute the next period Daniel Vetter: - Uses drm_calc_timestamping_constants to get the vblank interval instead of calculating it manually - Adds disable_vblank helper that turns of crtc - Simplifies implementation by using drm_crtc_arm_vblank_event - Replaces the code in atomic_begin to atomic_flush - Removes unnecessary field in vkms_output
Changes since V3: Daniel Vetter: - Squash "drm/vkms: Add atomic helpers functions" into the commit that handling vblank events simulated by hrtimers
Signed-off-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/vkms_crtc.c | 95 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/vkms/vkms_drv.c | 9 +++ drivers/gpu/drm/vkms/vkms_drv.h | 15 +++++ 3 files changed, 119 insertions(+)
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index bf76cd39ece7..875fca662ac0 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -10,6 +10,60 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h>
+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; + int ret_overrun; + bool ret; + + ret = drm_crtc_handle_vblank(crtc); + if (!ret) + DRM_ERROR("vkms failure on handling vblank"); + + ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, + output->period_ns); + + return HRTIMER_RESTART; +} + +static int vkms_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct vkms_output *out = drm_crtc_to_vkms_output(crtc); + + drm_calc_timestamping_constants(crtc, &crtc->mode); + + hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + out->vblank_hrtimer.function = &vkms_vblank_simulate; + out->period_ns = ktime_set(0, vblank->framedur_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->vblank_hrtimer.node.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 +71,45 @@ 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 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_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + drm_crtc_vblank_off(crtc); +} + +static void vkms_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + unsigned long flags; + + if (crtc->state->event) { + spin_lock_irqsave(&crtc->dev->event_lock, flags); + + if (drm_crtc_vblank_get(crtc) != 0) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + else + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + crtc->state->event = NULL; + } +} + +static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { + .atomic_flush = vkms_crtc_atomic_flush, + .atomic_enable = vkms_crtc_atomic_enable, + .atomic_disable = vkms_crtc_atomic_disable, };
int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, @@ -31,5 +124,7 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, return ret; }
+ drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs); + return ret; } diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 0557d1d8aa5b..37aa2ef33b21 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -56,6 +56,7 @@ static struct drm_driver vkms_driver = { .dumb_map_offset = vkms_dumb_map, .gem_vm_ops = &vkms_gem_vm_ops, .gem_free_object_unlocked = vkms_gem_free_object, + .get_vblank_timestamp = vkms_get_vblank_timestamp,
.name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -103,6 +104,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 57d7367fd814..07be29f2dc44 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -5,6 +5,7 @@ #include <drm/drm.h> #include <drm/drm_gem.h> #include <drm/drm_encoder.h> +#include <linux/hrtimer.h>
#define XRES_MIN 32 #define YRES_MIN 32 @@ -23,6 +24,9 @@ struct vkms_output { struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector connector; + struct hrtimer vblank_hrtimer; + ktime_t period_ns; + struct drm_pending_vblank_event *event; };
struct vkms_device { @@ -37,9 +41,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);
dri-devel@lists.freedesktop.org