Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 #include <linux/dma-fence.h>
0004 
0005 #include <drm/drm_atomic.h>
0006 #include <drm/drm_atomic_helper.h>
0007 #include <drm/drm_probe_helper.h>
0008 #include <drm/drm_vblank.h>
0009 
0010 #include "vkms_drv.h"
0011 
0012 static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
0013 {
0014     struct vkms_output *output = container_of(timer, struct vkms_output,
0015                           vblank_hrtimer);
0016     struct drm_crtc *crtc = &output->crtc;
0017     struct vkms_crtc_state *state;
0018     u64 ret_overrun;
0019     bool ret, fence_cookie;
0020 
0021     fence_cookie = dma_fence_begin_signalling();
0022 
0023     ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
0024                       output->period_ns);
0025     if (ret_overrun != 1)
0026         pr_warn("%s: vblank timer overrun\n", __func__);
0027 
0028     spin_lock(&output->lock);
0029     ret = drm_crtc_handle_vblank(crtc);
0030     if (!ret)
0031         DRM_ERROR("vkms failure on handling vblank");
0032 
0033     state = output->composer_state;
0034     spin_unlock(&output->lock);
0035 
0036     if (state && output->composer_enabled) {
0037         u64 frame = drm_crtc_accurate_vblank_count(crtc);
0038 
0039         /* update frame_start only if a queued vkms_composer_worker()
0040          * has read the data
0041          */
0042         spin_lock(&output->composer_lock);
0043         if (!state->crc_pending)
0044             state->frame_start = frame;
0045         else
0046             DRM_DEBUG_DRIVER("crc worker falling behind, frame_start: %llu, frame_end: %llu\n",
0047                      state->frame_start, frame);
0048         state->frame_end = frame;
0049         state->crc_pending = true;
0050         spin_unlock(&output->composer_lock);
0051 
0052         ret = queue_work(output->composer_workq, &state->composer_work);
0053         if (!ret)
0054             DRM_DEBUG_DRIVER("Composer worker already queued\n");
0055     }
0056 
0057     dma_fence_end_signalling(fence_cookie);
0058 
0059     return HRTIMER_RESTART;
0060 }
0061 
0062 static int vkms_enable_vblank(struct drm_crtc *crtc)
0063 {
0064     struct drm_device *dev = crtc->dev;
0065     unsigned int pipe = drm_crtc_index(crtc);
0066     struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
0067     struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0068 
0069     drm_calc_timestamping_constants(crtc, &crtc->mode);
0070 
0071     hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
0072     out->vblank_hrtimer.function = &vkms_vblank_simulate;
0073     out->period_ns = ktime_set(0, vblank->framedur_ns);
0074     hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
0075 
0076     return 0;
0077 }
0078 
0079 static void vkms_disable_vblank(struct drm_crtc *crtc)
0080 {
0081     struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0082 
0083     hrtimer_cancel(&out->vblank_hrtimer);
0084 }
0085 
0086 static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
0087                       int *max_error, ktime_t *vblank_time,
0088                       bool in_vblank_irq)
0089 {
0090     struct drm_device *dev = crtc->dev;
0091     unsigned int pipe = crtc->index;
0092     struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
0093     struct vkms_output *output = &vkmsdev->output;
0094     struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
0095 
0096     if (!READ_ONCE(vblank->enabled)) {
0097         *vblank_time = ktime_get();
0098         return true;
0099     }
0100 
0101     *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
0102 
0103     if (WARN_ON(*vblank_time == vblank->time))
0104         return true;
0105 
0106     /*
0107      * To prevent races we roll the hrtimer forward before we do any
0108      * interrupt processing - this is how real hw works (the interrupt is
0109      * only generated after all the vblank registers are updated) and what
0110      * the vblank core expects. Therefore we need to always correct the
0111      * timestampe by one frame.
0112      */
0113     *vblank_time -= output->period_ns;
0114 
0115     return true;
0116 }
0117 
0118 static struct drm_crtc_state *
0119 vkms_atomic_crtc_duplicate_state(struct drm_crtc *crtc)
0120 {
0121     struct vkms_crtc_state *vkms_state;
0122 
0123     if (WARN_ON(!crtc->state))
0124         return NULL;
0125 
0126     vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
0127     if (!vkms_state)
0128         return NULL;
0129 
0130     __drm_atomic_helper_crtc_duplicate_state(crtc, &vkms_state->base);
0131 
0132     INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
0133 
0134     return &vkms_state->base;
0135 }
0136 
0137 static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc,
0138                        struct drm_crtc_state *state)
0139 {
0140     struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
0141 
0142     __drm_atomic_helper_crtc_destroy_state(state);
0143 
0144     WARN_ON(work_pending(&vkms_state->composer_work));
0145     kfree(vkms_state->active_planes);
0146     kfree(vkms_state);
0147 }
0148 
0149 static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
0150 {
0151     struct vkms_crtc_state *vkms_state =
0152         kzalloc(sizeof(*vkms_state), GFP_KERNEL);
0153 
0154     if (crtc->state)
0155         vkms_atomic_crtc_destroy_state(crtc, crtc->state);
0156 
0157     __drm_atomic_helper_crtc_reset(crtc, &vkms_state->base);
0158     if (vkms_state)
0159         INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
0160 }
0161 
0162 static const struct drm_crtc_funcs vkms_crtc_funcs = {
0163     .set_config             = drm_atomic_helper_set_config,
0164     .destroy                = drm_crtc_cleanup,
0165     .page_flip              = drm_atomic_helper_page_flip,
0166     .reset                  = vkms_atomic_crtc_reset,
0167     .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
0168     .atomic_destroy_state   = vkms_atomic_crtc_destroy_state,
0169     .enable_vblank      = vkms_enable_vblank,
0170     .disable_vblank     = vkms_disable_vblank,
0171     .get_vblank_timestamp   = vkms_get_vblank_timestamp,
0172     .get_crc_sources    = vkms_get_crc_sources,
0173     .set_crc_source     = vkms_set_crc_source,
0174     .verify_crc_source  = vkms_verify_crc_source,
0175 };
0176 
0177 static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
0178                   struct drm_atomic_state *state)
0179 {
0180     struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
0181                                       crtc);
0182     struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(crtc_state);
0183     struct drm_plane *plane;
0184     struct drm_plane_state *plane_state;
0185     int i = 0, ret;
0186 
0187     if (vkms_state->active_planes)
0188         return 0;
0189 
0190     ret = drm_atomic_add_affected_planes(crtc_state->state, crtc);
0191     if (ret < 0)
0192         return ret;
0193 
0194     drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
0195         plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
0196                                   plane);
0197         WARN_ON(!plane_state);
0198 
0199         if (!plane_state->visible)
0200             continue;
0201 
0202         i++;
0203     }
0204 
0205     vkms_state->active_planes = kcalloc(i, sizeof(plane), GFP_KERNEL);
0206     if (!vkms_state->active_planes)
0207         return -ENOMEM;
0208     vkms_state->num_active_planes = i;
0209 
0210     i = 0;
0211     drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
0212         plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
0213                                   plane);
0214 
0215         if (!plane_state->visible)
0216             continue;
0217 
0218         vkms_state->active_planes[i++] =
0219             to_vkms_plane_state(plane_state);
0220     }
0221 
0222     return 0;
0223 }
0224 
0225 static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
0226                     struct drm_atomic_state *state)
0227 {
0228     drm_crtc_vblank_on(crtc);
0229 }
0230 
0231 static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
0232                      struct drm_atomic_state *state)
0233 {
0234     drm_crtc_vblank_off(crtc);
0235 }
0236 
0237 static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
0238                    struct drm_atomic_state *state)
0239 {
0240     struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
0241 
0242     /* This lock is held across the atomic commit to block vblank timer
0243      * from scheduling vkms_composer_worker until the composer is updated
0244      */
0245     spin_lock_irq(&vkms_output->lock);
0246 }
0247 
0248 static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
0249                    struct drm_atomic_state *state)
0250 {
0251     struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
0252 
0253     if (crtc->state->event) {
0254         spin_lock(&crtc->dev->event_lock);
0255 
0256         if (drm_crtc_vblank_get(crtc) != 0)
0257             drm_crtc_send_vblank_event(crtc, crtc->state->event);
0258         else
0259             drm_crtc_arm_vblank_event(crtc, crtc->state->event);
0260 
0261         spin_unlock(&crtc->dev->event_lock);
0262 
0263         crtc->state->event = NULL;
0264     }
0265 
0266     vkms_output->composer_state = to_vkms_crtc_state(crtc->state);
0267 
0268     spin_unlock_irq(&vkms_output->lock);
0269 }
0270 
0271 static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
0272     .atomic_check   = vkms_crtc_atomic_check,
0273     .atomic_begin   = vkms_crtc_atomic_begin,
0274     .atomic_flush   = vkms_crtc_atomic_flush,
0275     .atomic_enable  = vkms_crtc_atomic_enable,
0276     .atomic_disable = vkms_crtc_atomic_disable,
0277 };
0278 
0279 int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
0280            struct drm_plane *primary, struct drm_plane *cursor)
0281 {
0282     struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc);
0283     int ret;
0284 
0285     ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
0286                     &vkms_crtc_funcs, NULL);
0287     if (ret) {
0288         DRM_ERROR("Failed to init CRTC\n");
0289         return ret;
0290     }
0291 
0292     drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
0293 
0294     spin_lock_init(&vkms_out->lock);
0295     spin_lock_init(&vkms_out->composer_lock);
0296 
0297     vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
0298     if (!vkms_out->composer_workq)
0299         return -ENOMEM;
0300 
0301     return ret;
0302 }