1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include "vkms_drv.h" 4 #include <drm/drm_atomic_helper.h> 5 #include <drm/drm_crtc_helper.h> 6 7 static void _vblank_handle(struct vkms_output *output) 8 { 9 struct drm_crtc *crtc = &output->crtc; 10 struct vkms_crtc_state *state = to_vkms_crtc_state(crtc->state); 11 bool ret; 12 13 spin_lock(&output->lock); 14 ret = drm_crtc_handle_vblank(crtc); 15 if (!ret) 16 DRM_ERROR("vkms failure on handling vblank"); 17 18 if (state && output->crc_enabled) { 19 u64 frame = drm_crtc_accurate_vblank_count(crtc); 20 21 /* update frame_start only if a queued vkms_crc_work_handle() 22 * has read the data 23 */ 24 spin_lock(&output->state_lock); 25 if (!state->frame_start) 26 state->frame_start = frame; 27 spin_unlock(&output->state_lock); 28 29 ret = queue_work(output->crc_workq, &state->crc_work); 30 if (!ret) 31 DRM_WARN("failed to queue vkms_crc_work_handle"); 32 } 33 34 spin_unlock(&output->lock); 35 } 36 37 static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) 38 { 39 struct vkms_output *output = container_of(timer, struct vkms_output, 40 vblank_hrtimer); 41 int ret_overrun; 42 43 _vblank_handle(output); 44 45 ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, 46 output->period_ns); 47 48 return HRTIMER_RESTART; 49 } 50 51 static int vkms_enable_vblank(struct drm_crtc *crtc) 52 { 53 struct drm_device *dev = crtc->dev; 54 unsigned int pipe = drm_crtc_index(crtc); 55 struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; 56 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 57 58 drm_calc_timestamping_constants(crtc, &crtc->mode); 59 60 hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 61 out->vblank_hrtimer.function = &vkms_vblank_simulate; 62 out->period_ns = ktime_set(0, vblank->framedur_ns); 63 hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL); 64 65 return 0; 66 } 67 68 static void vkms_disable_vblank(struct drm_crtc *crtc) 69 { 70 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 71 72 hrtimer_cancel(&out->vblank_hrtimer); 73 } 74 75 bool vkms_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe, 76 int *max_error, ktime_t *vblank_time, 77 bool in_vblank_irq) 78 { 79 struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev); 80 struct vkms_output *output = &vkmsdev->output; 81 82 *vblank_time = output->vblank_hrtimer.node.expires; 83 84 return true; 85 } 86 87 static void vkms_atomic_crtc_reset(struct drm_crtc *crtc) 88 { 89 struct vkms_crtc_state *vkms_state = NULL; 90 91 if (crtc->state) { 92 vkms_state = to_vkms_crtc_state(crtc->state); 93 __drm_atomic_helper_crtc_destroy_state(crtc->state); 94 kfree(vkms_state); 95 crtc->state = NULL; 96 } 97 98 vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); 99 if (!vkms_state) 100 return; 101 102 crtc->state = &vkms_state->base; 103 crtc->state->crtc = crtc; 104 } 105 106 static struct drm_crtc_state * 107 vkms_atomic_crtc_duplicate_state(struct drm_crtc *crtc) 108 { 109 struct vkms_crtc_state *vkms_state; 110 111 if (WARN_ON(!crtc->state)) 112 return NULL; 113 114 vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); 115 if (!vkms_state) 116 return NULL; 117 118 __drm_atomic_helper_crtc_duplicate_state(crtc, &vkms_state->base); 119 120 INIT_WORK(&vkms_state->crc_work, vkms_crc_work_handle); 121 122 return &vkms_state->base; 123 } 124 125 static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc, 126 struct drm_crtc_state *state) 127 { 128 struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state); 129 130 __drm_atomic_helper_crtc_destroy_state(state); 131 132 if (vkms_state) { 133 flush_work(&vkms_state->crc_work); 134 kfree(vkms_state); 135 } 136 } 137 138 static const struct drm_crtc_funcs vkms_crtc_funcs = { 139 .set_config = drm_atomic_helper_set_config, 140 .destroy = drm_crtc_cleanup, 141 .page_flip = drm_atomic_helper_page_flip, 142 .reset = vkms_atomic_crtc_reset, 143 .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state, 144 .atomic_destroy_state = vkms_atomic_crtc_destroy_state, 145 .enable_vblank = vkms_enable_vblank, 146 .disable_vblank = vkms_disable_vblank, 147 .set_crc_source = vkms_set_crc_source, 148 .verify_crc_source = vkms_verify_crc_source, 149 }; 150 151 static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, 152 struct drm_crtc_state *old_state) 153 { 154 drm_crtc_vblank_on(crtc); 155 } 156 157 static void vkms_crtc_atomic_disable(struct drm_crtc *crtc, 158 struct drm_crtc_state *old_state) 159 { 160 drm_crtc_vblank_off(crtc); 161 } 162 163 static void vkms_crtc_atomic_begin(struct drm_crtc *crtc, 164 struct drm_crtc_state *old_crtc_state) 165 { 166 struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc); 167 168 /* This lock is held across the atomic commit to block vblank timer 169 * from scheduling vkms_crc_work_handle until the crc_data is updated 170 */ 171 spin_lock_irq(&vkms_output->lock); 172 } 173 174 static void vkms_crtc_atomic_flush(struct drm_crtc *crtc, 175 struct drm_crtc_state *old_crtc_state) 176 { 177 struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc); 178 unsigned long flags; 179 180 if (crtc->state->event) { 181 spin_lock_irqsave(&crtc->dev->event_lock, flags); 182 183 if (drm_crtc_vblank_get(crtc) != 0) 184 drm_crtc_send_vblank_event(crtc, crtc->state->event); 185 else 186 drm_crtc_arm_vblank_event(crtc, crtc->state->event); 187 188 spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 189 190 crtc->state->event = NULL; 191 } 192 193 spin_unlock_irq(&vkms_output->lock); 194 } 195 196 static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { 197 .atomic_begin = vkms_crtc_atomic_begin, 198 .atomic_flush = vkms_crtc_atomic_flush, 199 .atomic_enable = vkms_crtc_atomic_enable, 200 .atomic_disable = vkms_crtc_atomic_disable, 201 }; 202 203 int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 204 struct drm_plane *primary, struct drm_plane *cursor) 205 { 206 struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc); 207 int ret; 208 209 ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, 210 &vkms_crtc_funcs, NULL); 211 if (ret) { 212 DRM_ERROR("Failed to init CRTC\n"); 213 return ret; 214 } 215 216 drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs); 217 218 spin_lock_init(&vkms_out->lock); 219 spin_lock_init(&vkms_out->state_lock); 220 221 vkms_out->crc_workq = alloc_ordered_workqueue("vkms_crc_workq", 0); 222 223 return ret; 224 } 225