xref: /openbmc/linux/drivers/gpu/drm/vkms/vkms_crtc.c (revision 5804c19b80bf625c6a9925317f845e497434d6d3)
17fd56e02SRodrigo Siqueira // SPDX-License-Identifier: GPL-2.0+
2854502faSRodrigo Siqueira 
3d71cbff1SDaniel Vetter #include <linux/dma-fence.h>
4d71cbff1SDaniel Vetter 
58b186587SDaniel Vetter #include <drm/drm_atomic.h>
6854502faSRodrigo Siqueira #include <drm/drm_atomic_helper.h>
7fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
8ce672a1bSSam Ravnborg #include <drm/drm_vblank.h>
9ce672a1bSSam Ravnborg 
10ce672a1bSSam Ravnborg #include "vkms_drv.h"
11854502faSRodrigo Siqueira 
vkms_vblank_simulate(struct hrtimer * timer)12ba420afaSShayenne Moura static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
136c234fe3SHaneen Mohammed {
14ba420afaSShayenne Moura 	struct vkms_output *output = container_of(timer, struct vkms_output,
15ba420afaSShayenne Moura 						  vblank_hrtimer);
166c234fe3SHaneen Mohammed 	struct drm_crtc *crtc = &output->crtc;
178b186587SDaniel Vetter 	struct vkms_crtc_state *state;
1809ef09b4SShayenne Moura 	u64 ret_overrun;
19*7908632fSMaíra Canal 	bool ret, fence_cookie;
20d71cbff1SDaniel Vetter 
21d71cbff1SDaniel Vetter 	fence_cookie = dma_fence_begin_signalling();
226c234fe3SHaneen Mohammed 
237355965dSDaniel Vetter 	ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
247355965dSDaniel Vetter 					  output->period_ns);
25b4142fc4SDmitry Vyukov 	if (ret_overrun != 1)
26b4142fc4SDmitry Vyukov 		pr_warn("%s: vblank timer overrun\n", __func__);
277355965dSDaniel Vetter 
28*7908632fSMaíra Canal 	spin_lock(&output->lock);
296c234fe3SHaneen Mohammed 	ret = drm_crtc_handle_vblank(crtc);
306c234fe3SHaneen Mohammed 	if (!ret)
316c234fe3SHaneen Mohammed 		DRM_ERROR("vkms failure on handling vblank");
326c234fe3SHaneen Mohammed 
33a4e7e98eSRodrigo Siqueira 	state = output->composer_state;
34*7908632fSMaíra Canal 	spin_unlock(&output->lock);
3588ad7f3aSDaniel Vetter 
36*7908632fSMaíra Canal 	if (state && output->composer_enabled) {
370ca33adbSHaneen Mohammed 		u64 frame = drm_crtc_accurate_vblank_count(crtc);
380ca33adbSHaneen Mohammed 
39a4e7e98eSRodrigo Siqueira 		/* update frame_start only if a queued vkms_composer_worker()
400ca33adbSHaneen Mohammed 		 * has read the data
410ca33adbSHaneen Mohammed 		 */
42a4e7e98eSRodrigo Siqueira 		spin_lock(&output->composer_lock);
4318d0952aSDaniel Vetter 		if (!state->crc_pending)
440ca33adbSHaneen Mohammed 			state->frame_start = frame;
4518d0952aSDaniel Vetter 		else
4618d0952aSDaniel Vetter 			DRM_DEBUG_DRIVER("crc worker falling behind, frame_start: %llu, frame_end: %llu\n",
4718d0952aSDaniel Vetter 					 state->frame_start, frame);
4818d0952aSDaniel Vetter 		state->frame_end = frame;
4918d0952aSDaniel Vetter 		state->crc_pending = true;
50a4e7e98eSRodrigo Siqueira 		spin_unlock(&output->composer_lock);
510ca33adbSHaneen Mohammed 
52a4e7e98eSRodrigo Siqueira 		ret = queue_work(output->composer_workq, &state->composer_work);
530ca33adbSHaneen Mohammed 		if (!ret)
54a4e7e98eSRodrigo Siqueira 			DRM_DEBUG_DRIVER("Composer worker already queued\n");
556c234fe3SHaneen Mohammed 	}
566c234fe3SHaneen Mohammed 
57d71cbff1SDaniel Vetter 	dma_fence_end_signalling(fence_cookie);
58d71cbff1SDaniel Vetter 
593a070992SRodrigo Siqueira 	return HRTIMER_RESTART;
603a070992SRodrigo Siqueira }
613a070992SRodrigo Siqueira 
vkms_enable_vblank(struct drm_crtc * crtc)623a070992SRodrigo Siqueira static int vkms_enable_vblank(struct drm_crtc *crtc)
633a070992SRodrigo Siqueira {
643a070992SRodrigo Siqueira 	struct drm_device *dev = crtc->dev;
653a070992SRodrigo Siqueira 	unsigned int pipe = drm_crtc_index(crtc);
663a070992SRodrigo Siqueira 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
673a070992SRodrigo Siqueira 	struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
683a070992SRodrigo Siqueira 
693a070992SRodrigo Siqueira 	drm_calc_timestamping_constants(crtc, &crtc->mode);
703a070992SRodrigo Siqueira 
713a070992SRodrigo Siqueira 	hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
723a070992SRodrigo Siqueira 	out->vblank_hrtimer.function = &vkms_vblank_simulate;
733a070992SRodrigo Siqueira 	out->period_ns = ktime_set(0, vblank->framedur_ns);
743a070992SRodrigo Siqueira 	hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
753a070992SRodrigo Siqueira 
763a070992SRodrigo Siqueira 	return 0;
773a070992SRodrigo Siqueira }
783a070992SRodrigo Siqueira 
vkms_disable_vblank(struct drm_crtc * crtc)793a070992SRodrigo Siqueira static void vkms_disable_vblank(struct drm_crtc *crtc)
803a070992SRodrigo Siqueira {
813a070992SRodrigo Siqueira 	struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
823a070992SRodrigo Siqueira 
833a070992SRodrigo Siqueira 	hrtimer_cancel(&out->vblank_hrtimer);
843a070992SRodrigo Siqueira }
853a070992SRodrigo Siqueira 
vkms_get_vblank_timestamp(struct drm_crtc * crtc,int * max_error,ktime_t * vblank_time,bool in_vblank_irq)86dc3260d0SThomas Zimmermann static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
873a070992SRodrigo Siqueira 				      int *max_error, ktime_t *vblank_time,
883a070992SRodrigo Siqueira 				      bool in_vblank_irq)
893a070992SRodrigo Siqueira {
90dc3260d0SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
91dc3260d0SThomas Zimmermann 	unsigned int pipe = crtc->index;
923a070992SRodrigo Siqueira 	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
933a070992SRodrigo Siqueira 	struct vkms_output *output = &vkmsdev->output;
947355965dSDaniel Vetter 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
953a070992SRodrigo Siqueira 
9605ca5302SSidong Yang 	if (!READ_ONCE(vblank->enabled)) {
9705ca5302SSidong Yang 		*vblank_time = ktime_get();
9805ca5302SSidong Yang 		return true;
9905ca5302SSidong Yang 	}
10005ca5302SSidong Yang 
10188ad7f3aSDaniel Vetter 	*vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
1023a070992SRodrigo Siqueira 
1037355965dSDaniel Vetter 	if (WARN_ON(*vblank_time == vblank->time))
1047355965dSDaniel Vetter 		return true;
1057355965dSDaniel Vetter 
1067355965dSDaniel Vetter 	/*
1077355965dSDaniel Vetter 	 * To prevent races we roll the hrtimer forward before we do any
1087355965dSDaniel Vetter 	 * interrupt processing - this is how real hw works (the interrupt is
1097355965dSDaniel Vetter 	 * only generated after all the vblank registers are updated) and what
1107355965dSDaniel Vetter 	 * the vblank core expects. Therefore we need to always correct the
1117355965dSDaniel Vetter 	 * timestampe by one frame.
1127355965dSDaniel Vetter 	 */
113def35e7cSShayenne Moura 	*vblank_time -= output->period_ns;
114def35e7cSShayenne Moura 
1153a070992SRodrigo Siqueira 	return true;
1163a070992SRodrigo Siqueira }
1173a070992SRodrigo Siqueira 
118dfb9f5caSHaneen Mohammed static struct drm_crtc_state *
vkms_atomic_crtc_duplicate_state(struct drm_crtc * crtc)119dfb9f5caSHaneen Mohammed vkms_atomic_crtc_duplicate_state(struct drm_crtc *crtc)
120dfb9f5caSHaneen Mohammed {
121dfb9f5caSHaneen Mohammed 	struct vkms_crtc_state *vkms_state;
122dfb9f5caSHaneen Mohammed 
123dfb9f5caSHaneen Mohammed 	if (WARN_ON(!crtc->state))
124dfb9f5caSHaneen Mohammed 		return NULL;
125dfb9f5caSHaneen Mohammed 
126dfb9f5caSHaneen Mohammed 	vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
127dfb9f5caSHaneen Mohammed 	if (!vkms_state)
128dfb9f5caSHaneen Mohammed 		return NULL;
129dfb9f5caSHaneen Mohammed 
130dfb9f5caSHaneen Mohammed 	__drm_atomic_helper_crtc_duplicate_state(crtc, &vkms_state->base);
131dfb9f5caSHaneen Mohammed 
132a4e7e98eSRodrigo Siqueira 	INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
1336c234fe3SHaneen Mohammed 
134dfb9f5caSHaneen Mohammed 	return &vkms_state->base;
135dfb9f5caSHaneen Mohammed }
136dfb9f5caSHaneen Mohammed 
vkms_atomic_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)137dfb9f5caSHaneen Mohammed static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc,
138dfb9f5caSHaneen Mohammed 					   struct drm_crtc_state *state)
139dfb9f5caSHaneen Mohammed {
1406c234fe3SHaneen Mohammed 	struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
141dfb9f5caSHaneen Mohammed 
142dfb9f5caSHaneen Mohammed 	__drm_atomic_helper_crtc_destroy_state(state);
1436c234fe3SHaneen Mohammed 
144a4e7e98eSRodrigo Siqueira 	WARN_ON(work_pending(&vkms_state->composer_work));
1458b186587SDaniel Vetter 	kfree(vkms_state->active_planes);
146dfb9f5caSHaneen Mohammed 	kfree(vkms_state);
147dfb9f5caSHaneen Mohammed }
148dfb9f5caSHaneen Mohammed 
vkms_atomic_crtc_reset(struct drm_crtc * crtc)1497a34d9c4SMaarten Lankhorst static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
1507a34d9c4SMaarten Lankhorst {
1517a34d9c4SMaarten Lankhorst 	struct vkms_crtc_state *vkms_state =
1527a34d9c4SMaarten Lankhorst 		kzalloc(sizeof(*vkms_state), GFP_KERNEL);
1537a34d9c4SMaarten Lankhorst 
1547a34d9c4SMaarten Lankhorst 	if (crtc->state)
1557a34d9c4SMaarten Lankhorst 		vkms_atomic_crtc_destroy_state(crtc, crtc->state);
1567a34d9c4SMaarten Lankhorst 
1577a34d9c4SMaarten Lankhorst 	__drm_atomic_helper_crtc_reset(crtc, &vkms_state->base);
1587a34d9c4SMaarten Lankhorst 	if (vkms_state)
159a4e7e98eSRodrigo Siqueira 		INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
1607a34d9c4SMaarten Lankhorst }
1617a34d9c4SMaarten Lankhorst 
162854502faSRodrigo Siqueira static const struct drm_crtc_funcs vkms_crtc_funcs = {
163854502faSRodrigo Siqueira 	.set_config             = drm_atomic_helper_set_config,
164854502faSRodrigo Siqueira 	.page_flip              = drm_atomic_helper_page_flip,
165dfb9f5caSHaneen Mohammed 	.reset                  = vkms_atomic_crtc_reset,
166dfb9f5caSHaneen Mohammed 	.atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
167dfb9f5caSHaneen Mohammed 	.atomic_destroy_state   = vkms_atomic_crtc_destroy_state,
1683a070992SRodrigo Siqueira 	.enable_vblank		= vkms_enable_vblank,
1693a070992SRodrigo Siqueira 	.disable_vblank		= vkms_disable_vblank,
170dc3260d0SThomas Zimmermann 	.get_vblank_timestamp	= vkms_get_vblank_timestamp,
171c936843fSOleg Vasilev 	.get_crc_sources	= vkms_get_crc_sources,
1726c234fe3SHaneen Mohammed 	.set_crc_source		= vkms_set_crc_source,
173af697933SMahesh Kumar 	.verify_crc_source	= vkms_verify_crc_source,
1743a070992SRodrigo Siqueira };
1753a070992SRodrigo Siqueira 
vkms_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)1768b186587SDaniel Vetter static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
17729b77ad7SMaxime Ripard 				  struct drm_atomic_state *state)
1788b186587SDaniel Vetter {
17929b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
18029b77ad7SMaxime Ripard 									  crtc);
18129b77ad7SMaxime Ripard 	struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(crtc_state);
1828b186587SDaniel Vetter 	struct drm_plane *plane;
1838b186587SDaniel Vetter 	struct drm_plane_state *plane_state;
1848b186587SDaniel Vetter 	int i = 0, ret;
1858b186587SDaniel Vetter 
1868b186587SDaniel Vetter 	if (vkms_state->active_planes)
1878b186587SDaniel Vetter 		return 0;
1888b186587SDaniel Vetter 
18929b77ad7SMaxime Ripard 	ret = drm_atomic_add_affected_planes(crtc_state->state, crtc);
1908b186587SDaniel Vetter 	if (ret < 0)
1918b186587SDaniel Vetter 		return ret;
1928b186587SDaniel Vetter 
19329b77ad7SMaxime Ripard 	drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
19429b77ad7SMaxime Ripard 		plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
1958b186587SDaniel Vetter 								  plane);
1968b186587SDaniel Vetter 		WARN_ON(!plane_state);
1978b186587SDaniel Vetter 
1988b186587SDaniel Vetter 		if (!plane_state->visible)
1998b186587SDaniel Vetter 			continue;
2008b186587SDaniel Vetter 
2018b186587SDaniel Vetter 		i++;
2028b186587SDaniel Vetter 	}
2038b186587SDaniel Vetter 
2048b186587SDaniel Vetter 	vkms_state->active_planes = kcalloc(i, sizeof(plane), GFP_KERNEL);
2058b186587SDaniel Vetter 	if (!vkms_state->active_planes)
2068b186587SDaniel Vetter 		return -ENOMEM;
2078b186587SDaniel Vetter 	vkms_state->num_active_planes = i;
2088b186587SDaniel Vetter 
2098b186587SDaniel Vetter 	i = 0;
21029b77ad7SMaxime Ripard 	drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
21129b77ad7SMaxime Ripard 		plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
2128b186587SDaniel Vetter 								  plane);
2138b186587SDaniel Vetter 
2148b186587SDaniel Vetter 		if (!plane_state->visible)
2158b186587SDaniel Vetter 			continue;
2168b186587SDaniel Vetter 
2178b186587SDaniel Vetter 		vkms_state->active_planes[i++] =
2188b186587SDaniel Vetter 			to_vkms_plane_state(plane_state);
2198b186587SDaniel Vetter 	}
2208b186587SDaniel Vetter 
2218b186587SDaniel Vetter 	return 0;
2228b186587SDaniel Vetter }
2238b186587SDaniel Vetter 
vkms_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)2243a070992SRodrigo Siqueira static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
225351f950dSMaxime Ripard 				    struct drm_atomic_state *state)
2263a070992SRodrigo Siqueira {
2273a070992SRodrigo Siqueira 	drm_crtc_vblank_on(crtc);
2283a070992SRodrigo Siqueira }
2293a070992SRodrigo Siqueira 
vkms_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)2303a070992SRodrigo Siqueira static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
231351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
2323a070992SRodrigo Siqueira {
2333a070992SRodrigo Siqueira 	drm_crtc_vblank_off(crtc);
2343a070992SRodrigo Siqueira }
2353a070992SRodrigo Siqueira 
vkms_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)2366c234fe3SHaneen Mohammed static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
237f6ebe9f9SMaxime Ripard 				   struct drm_atomic_state *state)
2386c234fe3SHaneen Mohammed {
2396c234fe3SHaneen Mohammed 	struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
2406c234fe3SHaneen Mohammed 
2416c234fe3SHaneen Mohammed 	/* This lock is held across the atomic commit to block vblank timer
242a4e7e98eSRodrigo Siqueira 	 * from scheduling vkms_composer_worker until the composer is updated
2436c234fe3SHaneen Mohammed 	 */
2446c234fe3SHaneen Mohammed 	spin_lock_irq(&vkms_output->lock);
2456c234fe3SHaneen Mohammed }
2466c234fe3SHaneen Mohammed 
vkms_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)2473a070992SRodrigo Siqueira static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
248f6ebe9f9SMaxime Ripard 				   struct drm_atomic_state *state)
2493a070992SRodrigo Siqueira {
2506c234fe3SHaneen Mohammed 	struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
2513a070992SRodrigo Siqueira 
2523a070992SRodrigo Siqueira 	if (crtc->state->event) {
2531c305e13SDaniel Vetter 		spin_lock(&crtc->dev->event_lock);
2543a070992SRodrigo Siqueira 
2553a070992SRodrigo Siqueira 		if (drm_crtc_vblank_get(crtc) != 0)
2563a070992SRodrigo Siqueira 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
2573a070992SRodrigo Siqueira 		else
2583a070992SRodrigo Siqueira 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
2593a070992SRodrigo Siqueira 
2601c305e13SDaniel Vetter 		spin_unlock(&crtc->dev->event_lock);
2613a070992SRodrigo Siqueira 
2623a070992SRodrigo Siqueira 		crtc->state->event = NULL;
2633a070992SRodrigo Siqueira 	}
2646c234fe3SHaneen Mohammed 
265a4e7e98eSRodrigo Siqueira 	vkms_output->composer_state = to_vkms_crtc_state(crtc->state);
2668b186587SDaniel Vetter 
2676c234fe3SHaneen Mohammed 	spin_unlock_irq(&vkms_output->lock);
2683a070992SRodrigo Siqueira }
2693a070992SRodrigo Siqueira 
2703a070992SRodrigo Siqueira static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
2718b186587SDaniel Vetter 	.atomic_check	= vkms_crtc_atomic_check,
2726c234fe3SHaneen Mohammed 	.atomic_begin	= vkms_crtc_atomic_begin,
2733a070992SRodrigo Siqueira 	.atomic_flush	= vkms_crtc_atomic_flush,
2743a070992SRodrigo Siqueira 	.atomic_enable	= vkms_crtc_atomic_enable,
2753a070992SRodrigo Siqueira 	.atomic_disable	= vkms_crtc_atomic_disable,
276854502faSRodrigo Siqueira };
277854502faSRodrigo Siqueira 
vkms_crtc_init(struct drm_device * dev,struct drm_crtc * crtc,struct drm_plane * primary,struct drm_plane * cursor)278854502faSRodrigo Siqueira int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
279854502faSRodrigo Siqueira 		   struct drm_plane *primary, struct drm_plane *cursor)
280854502faSRodrigo Siqueira {
2816c234fe3SHaneen Mohammed 	struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc);
282854502faSRodrigo Siqueira 	int ret;
283854502faSRodrigo Siqueira 
28499cc528eSMaíra Canal 	ret = drmm_crtc_init_with_planes(dev, crtc, primary, cursor,
285854502faSRodrigo Siqueira 					 &vkms_crtc_funcs, NULL);
286854502faSRodrigo Siqueira 	if (ret) {
287854502faSRodrigo Siqueira 		DRM_ERROR("Failed to init CRTC\n");
288854502faSRodrigo Siqueira 		return ret;
289854502faSRodrigo Siqueira 	}
290854502faSRodrigo Siqueira 
2913a070992SRodrigo Siqueira 	drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
2923a070992SRodrigo Siqueira 
293db1f254fSArthur Grillo 	drm_mode_crtc_set_gamma_size(crtc, VKMS_LUT_SIZE);
294db1f254fSArthur Grillo 	drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_LUT_SIZE);
295db1f254fSArthur Grillo 
2966c234fe3SHaneen Mohammed 	spin_lock_init(&vkms_out->lock);
297a4e7e98eSRodrigo Siqueira 	spin_lock_init(&vkms_out->composer_lock);
2986c234fe3SHaneen Mohammed 
299a4e7e98eSRodrigo Siqueira 	vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
300a4e7e98eSRodrigo Siqueira 	if (!vkms_out->composer_workq)
301208c6e8cSKangjie Lu 		return -ENOMEM;
3026c234fe3SHaneen Mohammed 
303854502faSRodrigo Siqueira 	return ret;
304854502faSRodrigo Siqueira }
305