void
vivante_dri2_vblank(int fd, unsigned frame, unsigned tv_sec, unsigned tv_usec,
	void *event)
{
	struct vivante_dri_wait *wait = event;
	DrawablePtr draw;

	if (!wait->drawable_id)
		goto out;

	if (dixLookupDrawable(&draw, wait->drawable_id, serverClient, M_ANY,
			      DixWriteAccess) != Success)
		goto out;

	switch (wait->type) {
	case DRI2_FLIP:
		if (can_exchange(draw, wait->front, wait->back) &&
		    vivante_dri2_ScheduleFlip(draw, wait))
			return;
		/* FALLTHROUGH */

	case DRI2_SWAP:
		vivante_dri2_blit(wait->client, draw, wait->front, wait->back,
				  frame, tv_sec, tv_usec,
				  wait->client ? wait->event_func : NULL,
				  wait->event_data);
		break;

	case DRI2_WAITMSC:
		if (wait->client)
			DRI2WaitMSCComplete(wait->client, draw, frame, tv_sec, tv_usec);
		break;

	default:
		xf86DrvMsg(xf86ScreenToScrn(draw->pScreen)->scrnIndex,
			   X_WARNING, "%s: unknown vblank event received\n",
			   __FUNCTION__);
		break;
	}

 out:
	del_wait_info(wait);
}
static void
nouveau_dri2_vblank_handler(void *priv, uint64_t name, uint64_t ust, uint32_t frame)
{
	struct dri2_vblank *event = priv;
	struct nouveau_dri2_vblank_state *s = event->s;
	uint32_t tv_sec  = ust / 1000000;
	uint32_t tv_usec = ust % 1000000;
	DrawablePtr draw;
	int ret;

	ret = dixLookupDrawable(&draw, s->draw, serverClient,
				M_ANY, DixWriteAccess);
	if (ret) {
		free(s);
		return;
	}

	switch (s->action) {
	case SWAP:
		nouveau_dri2_finish_swap(draw, frame, tv_sec, tv_usec, s);
#if DRI2INFOREC_VERSION >= 6
		/* Restore real swap limit on drawable, now that it is safe. */
		ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen);
		DRI2SwapLimit(draw, NVPTR(scrn)->swap_limit);
#endif
		break;

	case WAIT:
		DRI2WaitMSCComplete(s->client, draw, frame, tv_sec, tv_usec);
		free(s);
		break;

	case BLIT:
		DRI2SwapComplete(s->client, draw, frame, tv_sec, tv_usec,
				 DRI2_BLIT_COMPLETE, s->func, s->data);
		free(s);
		break;
	}
}
static int
vivante_dri2_ScheduleWaitMSC(ClientPtr client, DrawablePtr draw,
	CARD64 target_msc, CARD64 divisor, CARD64 remainder)
{
	struct vivante *vivante = vivante_get_screen_priv(draw->pScreen);
	struct vivante_dri_wait *wait;
	drmVBlank vbl;
	int ret, crtc;
	CARD64 cur_msc;

	target_msc &= 0xffffffff;
	divisor &= 0xffffffff;
	remainder &= 0xffffffff;

	crtc = vivante_dri2_drawable_crtc(draw);

	/* Drawable not displayed, just complete */
	if (crtc < 0)
		goto out;

	wait = new_wait_info(client, draw, DRI2_WAITMSC);
	if (!wait)
		goto out;

	/* Get current count */
	ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__);
	if (ret)
		goto out_free;

	cur_msc = vbl.reply.sequence;

	/*
	 * If the divisor is zero, or cur_msc is smaller than target_msc, we
	 * just need to make sure target_msc passes before waking up the client.
	 */
	if (divisor == 0 || cur_msc < target_msc) {
		if (cur_msc >= target_msc)
			target_msc = cur_msc;

		vbl.request.sequence = target_msc;
	} else {
		/*
		 * If we get here, target_msc has already passed or we
		 * don't have one, so queue an event that will satisfy
		 * the divisor/remainder equation.
		 */
		vbl.request.sequence = cur_msc - (cur_msc % divisor) + remainder;

		/*
		 * If calculated remainder is larger than requested
		 * remainder, it means we've passed the point where
		 * seq % divisor == remainder, so we need to wait for
		 * the next time that will happen.
		 */
		if ((cur_msc & divisor) >= remainder)
			vbl.request.sequence += divisor;
	}

	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drm_req_crtc(crtc);
	vbl.request.signal = (unsigned long)wait;
	ret = drmWaitVBlank(vivante->drm_fd, &vbl);
	if (ret) {
		xf86DrvMsg(vivante->scrnIndex, X_WARNING,
				   "%s: get vblank counter failed: %s\n",
				   __FUNCTION__, strerror(errno));
		goto out_free;
	}

	wait->frame = vbl.reply.sequence;
	DRI2BlockClient(client, draw);
	return TRUE;

 out_free:
	del_wait_info(wait);
 out:
	DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
	return TRUE;
}