Skip to content

Commit a19b9ee

Browse files
committed
drm: implement hrtimer-based vblank for proper frame pacing
1 parent eb36e4f commit a19b9ee

3 files changed

Lines changed: 66 additions & 76 deletions

File tree

module/evdi_drm_drv.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/version.h>
1818
#include <linux/mutex.h>
1919
#include <linux/device.h>
20+
#include <linux/hrtimer.h>
2021
#include <linux/i2c.h>
2122
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE || defined(EL8)
2223
#include <drm/drm_drv.h>
@@ -47,6 +48,7 @@ struct evdi_painter;
4748
struct evdi_device {
4849
struct drm_device *ddev;
4950
struct drm_connector *conn;
51+
struct drm_crtc *crtc;
5052
struct evdi_cursor *cursor;
5153
bool cursor_events_enabled;
5254

@@ -57,6 +59,9 @@ struct evdi_device {
5759
struct evdi_painter *painter;
5860
struct i2c_adapter *i2c_adapter;
5961

62+
struct hrtimer vblank_timer;
63+
ktime_t vblank_period;
64+
6065
int dev_index;
6166
};
6267

@@ -140,9 +145,6 @@ u8 *evdi_painter_get_edid_copy(struct evdi_device *evdi);
140145
int evdi_painter_get_num_dirts(struct evdi_painter *painter);
141146
void evdi_painter_mark_dirty(struct evdi_device *evdi,
142147
const struct drm_clip_rect *rect);
143-
void evdi_painter_set_vblank(struct evdi_painter *painter,
144-
struct drm_crtc *crtc,
145-
struct drm_pending_vblank_event *vblank);
146148
void evdi_painter_send_update_ready_if_needed(struct evdi_painter *painter);
147149
void evdi_painter_dpms_notify(struct evdi_painter *painter, int mode);
148150
void evdi_painter_mode_changed_notify(struct evdi_device *evdi,

module/evdi_modeset.c

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ static void evdi_crtc_disable(__always_unused struct drm_crtc *crtc)
4949

5050
static void evdi_crtc_destroy(struct drm_crtc *crtc)
5151
{
52+
struct evdi_device *evdi = crtc->dev->dev_private;
53+
5254
EVDI_CHECKPT();
55+
hrtimer_cancel(&evdi->vblank_timer);
56+
evdi->crtc = NULL;
5357
drm_crtc_cleanup(crtc);
5458
kfree(crtc);
5559
}
@@ -82,16 +86,31 @@ static void evdi_crtc_atomic_flush(
8286
(crtc_state->mode_changed || evdi_painter_needs_full_modeset(evdi->painter));
8387
bool notify_dpms = crtc_state->active_changed || evdi_painter_needs_full_modeset(evdi->painter);
8488

85-
if (notify_mode_changed)
86-
evdi_painter_mode_changed_notify(evdi, &crtc_state->adjusted_mode);
89+
if (notify_mode_changed) {
90+
struct drm_display_mode *m = &crtc_state->adjusted_mode;
91+
92+
if (m->htotal && m->vtotal && m->clock)
93+
evdi->vblank_period = ktime_set(0,
94+
(u64)m->vtotal * m->htotal *
95+
1000000ULL / m->clock);
96+
97+
evdi_painter_mode_changed_notify(evdi, m);
98+
}
8799

88100
if (notify_dpms)
89101
evdi_painter_dpms_notify(evdi->painter,
90102
crtc_state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
91103

92-
evdi_painter_set_vblank(evdi->painter, crtc, crtc_state->event);
104+
if (crtc_state->event) {
105+
spin_lock(&crtc->dev->event_lock);
106+
if (drm_crtc_vblank_get(crtc) != 0)
107+
drm_crtc_send_vblank_event(crtc, crtc_state->event);
108+
else
109+
drm_crtc_arm_vblank_event(crtc, crtc_state->event);
110+
spin_unlock(&crtc->dev->event_lock);
111+
crtc_state->event = NULL;
112+
}
93113
evdi_painter_send_update_ready_if_needed(evdi->painter);
94-
crtc_state->event = NULL;
95114
}
96115

97116
#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE || defined(EL8)
@@ -188,13 +207,32 @@ static struct drm_crtc_helper_funcs evdi_helper_funcs = {
188207
};
189208

190209
#if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(RPI) || defined(EL8)
191-
static int evdi_enable_vblank(__always_unused struct drm_crtc *crtc)
210+
static enum hrtimer_restart evdi_vblank_timer_fn(struct hrtimer *timer)
211+
{
212+
struct evdi_device *evdi =
213+
container_of(timer, struct evdi_device, vblank_timer);
214+
215+
if (evdi->crtc)
216+
drm_crtc_handle_vblank(evdi->crtc);
217+
218+
hrtimer_forward_now(timer, evdi->vblank_period);
219+
return HRTIMER_RESTART;
220+
}
221+
222+
static int evdi_enable_vblank(struct drm_crtc *crtc)
192223
{
193-
return 1;
224+
struct evdi_device *evdi = crtc->dev->dev_private;
225+
226+
hrtimer_start(&evdi->vblank_timer, evdi->vblank_period,
227+
HRTIMER_MODE_REL);
228+
return 0;
194229
}
195230

196-
static void evdi_disable_vblank(__always_unused struct drm_crtc *crtc)
231+
static void evdi_disable_vblank(struct drm_crtc *crtc)
197232
{
233+
struct evdi_device *evdi = crtc->dev->dev_private;
234+
235+
hrtimer_cancel(&evdi->vblank_timer);
198236
}
199237
#endif
200238

@@ -494,6 +532,22 @@ static int evdi_crtc_init(struct drm_device *dev)
494532
EVDI_DEBUG("drm_crtc_init: %d p%p\n", status, primary_plane);
495533
drm_crtc_helper_add(crtc, &evdi_helper_funcs);
496534

535+
{
536+
struct evdi_device *evdi = dev->dev_private;
537+
538+
evdi->crtc = crtc;
539+
evdi->vblank_period = ktime_set(0, 16666667);
540+
#if KERNEL_VERSION(6, 12, 0) <= LINUX_VERSION_CODE
541+
hrtimer_setup(&evdi->vblank_timer,
542+
evdi_vblank_timer_fn,
543+
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
544+
#else
545+
hrtimer_init(&evdi->vblank_timer, CLOCK_MONOTONIC,
546+
HRTIMER_MODE_REL);
547+
evdi->vblank_timer.function = evdi_vblank_timer_fn;
548+
#endif
549+
}
550+
497551
return 0;
498552
}
499553

module/evdi_painter.c

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,6 @@ struct evdi_painter {
106106

107107
bool was_update_requested;
108108
bool needs_full_modeset;
109-
struct drm_crtc *crtc;
110-
struct drm_pending_vblank_event *vblank;
111109

112110
struct list_head pending_events;
113111
struct delayed_work send_events_work;
@@ -685,53 +683,6 @@ void evdi_painter_mark_dirty(struct evdi_device *evdi,
685683
painter_unlock(painter);
686684
}
687685

688-
static void evdi_send_vblank(struct drm_crtc *crtc,
689-
struct drm_pending_vblank_event *vblank)
690-
{
691-
if (crtc && vblank) {
692-
unsigned long flags = 0;
693-
694-
spin_lock_irqsave(&crtc->dev->event_lock, flags);
695-
drm_crtc_send_vblank_event(crtc, vblank);
696-
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
697-
}
698-
}
699-
700-
static void evdi_painter_send_vblank(struct evdi_painter *painter)
701-
{
702-
EVDI_CHECKPT();
703-
704-
evdi_send_vblank(painter->crtc, painter->vblank);
705-
706-
painter->crtc = NULL;
707-
painter->vblank = NULL;
708-
}
709-
710-
void evdi_painter_set_vblank(
711-
struct evdi_painter *painter,
712-
struct drm_crtc *crtc,
713-
struct drm_pending_vblank_event *vblank)
714-
{
715-
EVDI_CHECKPT();
716-
717-
if (painter) {
718-
painter_lock(painter);
719-
720-
evdi_painter_send_vblank(painter);
721-
722-
if (painter->num_dirts > 0 && painter->is_connected) {
723-
painter->crtc = crtc;
724-
painter->vblank = vblank;
725-
} else {
726-
evdi_send_vblank(crtc, vblank);
727-
}
728-
729-
painter_unlock(painter);
730-
} else {
731-
evdi_send_vblank(crtc, vblank);
732-
}
733-
}
734-
735686
void evdi_painter_send_update_ready_if_needed(struct evdi_painter *painter)
736687
{
737688
EVDI_CHECKPT();
@@ -987,8 +938,6 @@ static int evdi_painter_disconnect(struct evdi_device *evdi,
987938
EVDI_INFO("(card%d) Disconnected from %s\n", evdi->dev_index, buf);
988939
evdi_painter_events_cleanup(painter);
989940

990-
evdi_painter_send_vblank(painter);
991-
992941
evdi_cursor_enable(evdi->cursor, false);
993942

994943
kfree(painter->ddcci_buffer);
@@ -1058,8 +1007,6 @@ int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data,
10581007
struct drm_evdi_grabpix *cmd = data;
10591008
struct evdi_framebuffer *efb = NULL;
10601009
struct drm_clip_rect dirty_rects[MAX_DIRTS];
1061-
struct drm_crtc *crtc = NULL;
1062-
struct drm_pending_vblank_event *vblank = NULL;
10631010
int err;
10641011
int ret;
10651012
struct dma_buf_attachment *import_attach;
@@ -1114,13 +1061,6 @@ int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data,
11141061

11151062
drm_framebuffer_get(&efb->base);
11161063

1117-
crtc = painter->crtc;
1118-
painter->crtc = NULL;
1119-
1120-
vblank = painter->vblank;
1121-
painter->vblank = NULL;
1122-
1123-
11241064
painter_unlock(painter);
11251065

11261066
if (!efb->obj->vmapping) {
@@ -1177,8 +1117,6 @@ int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data,
11771117
DMA_FROM_DEVICE);
11781118

11791119
err_fb:
1180-
evdi_send_vblank(crtc, vblank);
1181-
11821120
drm_framebuffer_put(&efb->base);
11831121

11841122
return err;
@@ -1321,8 +1259,6 @@ int evdi_painter_init(struct evdi_device *dev)
13211259
dev->painter->edid = NULL;
13221260
dev->painter->edid_length = 0;
13231261
dev->painter->needs_full_modeset = true;
1324-
dev->painter->crtc = NULL;
1325-
dev->painter->vblank = NULL;
13261262
dev->painter->drm_device = dev->ddev;
13271263
evdi_painter_register_to_vt(dev->painter);
13281264
#if KERNEL_VERSION(6, 7, 0) <= LINUX_VERSION_CODE
@@ -1358,8 +1294,6 @@ void evdi_painter_cleanup(struct evdi_painter *painter)
13581294
drm_framebuffer_put(&painter->scanout_fb->base);
13591295
painter->scanout_fb = NULL;
13601296

1361-
evdi_painter_send_vblank(painter);
1362-
13631297
evdi_painter_events_cleanup(painter);
13641298

13651299
painter->drm_device = NULL;

0 commit comments

Comments
 (0)