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