Skip to content

Commit 64bf00b

Browse files
committed
drm: apple: Override drm_vblank's page flip event handling [HACK]
Since we don't init/uses drm's vblank support our page flip timestamps are CLOCK_MONOTONIC timestamps during the event generation. Since compositors use the timestamp to schedule their next kms commit this is timing sensitive sop move it under the drivers control. Take the timestamp directly in the swap_complete callback. Framebuffer swaps are unfortunately not fast with DCP. Measured time from swap_submit to swap_complete is ~1.5 ms for dcp and ~2.3 ms for dcpext. This warrants further investigation. Presentation timestamps might help if delay on dcp firmware side occurs after the actual swap. In the meantime doctor the time stamps and move the page flip completion up to 1 ms earler. This fixes half rate refresh on external displays displays using dcpext. Signed-off-by: Janne Grunau <[email protected]>
1 parent 1a32cda commit 64bf00b

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

drivers/gpu/drm/apple/dcp-internal.h

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ struct apple_dcp {
175175

176176
/* swap id of the last completed swap */
177177
u32 last_swap_id;
178+
ktime_t swap_start;
178179

179180
/* Current display mode */
180181
bool during_modeset;
@@ -253,6 +254,8 @@ struct apple_dcp {
253254
int hdmi_hpd_irq;
254255
};
255256

257+
void dcp_drm_crtc_page_flip(struct apple_dcp *dcp, ktime_t now);
258+
256259
int dcp_backlight_register(struct apple_dcp *dcp);
257260
int dcp_backlight_update(struct apple_dcp *dcp);
258261
bool dcp_has_panel(struct apple_dcp *dcp);

drivers/gpu/drm/apple/dcp.c

+87
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,76 @@ bool hdmi_audio;
5050
module_param(hdmi_audio, bool, 0644);
5151
MODULE_PARM_DESC(hdmi_audio, "Enable unstable HDMI audio support");
5252

53+
/* copied and simplified from drm_vblank.c */
54+
static void send_vblank_event(struct drm_device *dev,
55+
struct drm_pending_vblank_event *e,
56+
u64 seq, ktime_t now)
57+
{
58+
struct timespec64 tv;
59+
60+
if (e->event.base.type != DRM_EVENT_FLIP_COMPLETE)
61+
return;
62+
63+
tv = ktime_to_timespec64(now);
64+
e->event.vbl.sequence = seq;
65+
/*
66+
* e->event is a user space structure, with hardcoded unsigned
67+
* 32-bit seconds/microseconds. This is safe as we always use
68+
* monotonic timestamps since linux-4.15
69+
*/
70+
e->event.vbl.tv_sec = tv.tv_sec;
71+
e->event.vbl.tv_usec = tv.tv_nsec / 1000;
72+
73+
/*
74+
* Use the same timestamp for any associated fence signal to avoid
75+
* mismatch in timestamps for vsync & fence events triggered by the
76+
* same HW event. Frameworks like SurfaceFlinger in Android expects the
77+
* retire-fence timestamp to match exactly with HW vsync as it uses it
78+
* for its software vsync modeling.
79+
*/
80+
drm_send_event_timestamp_locked(dev, &e->base, now);
81+
}
82+
83+
/**
84+
* dcp_crtc_send_page_flip_event - helper to send vblank event after pageflip
85+
*
86+
* Compensate for unknown slack between page flip and arrival of the
87+
* swap_complete callback. Minimal observed duration on DCP with HDMI output
88+
* was around 2.3 ms. If the fb swap was submitted closer to the expected
89+
* swap_complete it gets a penalty of one frame duration. This is on the border
90+
* of unreasonable considering that Apple advertises support for 240 Hz (frame
91+
* duration of 4.167 ms).
92+
* It is unreasonable considering kwin's kms commit scheduling. Kwin commits
93+
* 1.5 ms + the mode's vblank time before the expected next page flip
94+
* completion. This results in presenting at half the display's rate for HDMI
95+
* outputs.
96+
* This might be a difference between dcp and dcpext.
97+
*/
98+
static void dcp_crtc_send_page_flip_event(struct apple_crtc *crtc,
99+
struct drm_pending_vblank_event *e,
100+
ktime_t now, ktime_t start)
101+
{
102+
struct drm_device *dev = crtc->base.dev;
103+
u64 seq;
104+
unsigned int pipe = drm_crtc_index(&crtc->base);
105+
ktime_t flip;
106+
107+
seq = 0;
108+
if (start != KTIME_MIN) {
109+
s64 delta = ktime_us_delta(now, start);
110+
if (delta <= 500)
111+
flip = now;
112+
else if (delta >= 2500)
113+
flip = ktime_sub_us(now, 1000);
114+
else
115+
flip = ktime_sub_us(now, (delta - 500) / 2);
116+
} else {
117+
flip = now;
118+
}
119+
e->pipe = pipe;
120+
send_vblank_event(dev, e, seq, flip);
121+
}
122+
53123
/* HACK: moved here to avoid circular dependency between apple_drv and dcp */
54124
void dcp_drm_crtc_vblank(struct apple_crtc *crtc)
55125
{
@@ -63,6 +133,23 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc)
63133
spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags);
64134
}
65135

136+
void dcp_drm_crtc_page_flip(struct apple_dcp *dcp, ktime_t now)
137+
{
138+
unsigned long flags;
139+
struct apple_crtc *crtc = dcp->crtc;
140+
141+
spin_lock_irqsave(&crtc->base.dev->event_lock, flags);
142+
if (crtc->event) {
143+
if (crtc->event->event.base.type == DRM_EVENT_FLIP_COMPLETE)
144+
dcp_crtc_send_page_flip_event(crtc, crtc->event, now, dcp->swap_start);
145+
else
146+
drm_crtc_send_vblank_event(&crtc->base, crtc->event);
147+
crtc->event = NULL;
148+
dcp->swap_start = KTIME_MIN;
149+
}
150+
spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags);
151+
}
152+
66153
void dcp_set_dimensions(struct apple_dcp *dcp)
67154
{
68155
int i;

drivers/gpu/drm/apple/iomfb_template.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@ static u32 dcpep_cb_zero(struct apple_dcp *dcp)
120120
static void dcpep_cb_swap_complete(struct apple_dcp *dcp,
121121
struct DCP_FW_NAME(dc_swap_complete_resp) *resp)
122122
{
123+
ktime_t now = ktime_get();
123124
trace_iomfb_swap_complete(dcp, resp->swap_id);
124125
dcp->last_swap_id = resp->swap_id;
125126

126-
dcp_drm_crtc_vblank(dcp->crtc);
127+
dcp_drm_crtc_page_flip(dcp, now);
127128
}
128129

129130
/* special */
@@ -1124,6 +1125,7 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie)
11241125
dcp_drm_crtc_vblank(dcp->crtc);
11251126
return;
11261127
}
1128+
dcp->swap_start = ktime_get();
11271129

11281130
while (!list_empty(&dcp->swapped_out_fbs)) {
11291131
struct dcp_fb_reference *entry;

0 commit comments

Comments
 (0)