linux/drivers/gpu/drm/vkms/vkms_crtc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3#include <drm/drm_atomic.h>
   4#include <drm/drm_atomic_helper.h>
   5#include <drm/drm_probe_helper.h>
   6#include <drm/drm_vblank.h>
   7
   8#include "vkms_drv.h"
   9
  10static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
  11{
  12        struct vkms_output *output = container_of(timer, struct vkms_output,
  13                                                  vblank_hrtimer);
  14        struct drm_crtc *crtc = &output->crtc;
  15        struct vkms_crtc_state *state;
  16        u64 ret_overrun;
  17        bool ret;
  18
  19        ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
  20                                          output->period_ns);
  21        WARN_ON(ret_overrun != 1);
  22
  23        spin_lock(&output->lock);
  24        ret = drm_crtc_handle_vblank(crtc);
  25        if (!ret)
  26                DRM_ERROR("vkms failure on handling vblank");
  27
  28        state = output->composer_state;
  29        spin_unlock(&output->lock);
  30
  31        if (state && output->composer_enabled) {
  32                u64 frame = drm_crtc_accurate_vblank_count(crtc);
  33
  34                /* update frame_start only if a queued vkms_composer_worker()
  35                 * has read the data
  36                 */
  37                spin_lock(&output->composer_lock);
  38                if (!state->crc_pending)
  39                        state->frame_start = frame;
  40                else
  41                        DRM_DEBUG_DRIVER("crc worker falling behind, frame_start: %llu, frame_end: %llu\n",
  42                                         state->frame_start, frame);
  43                state->frame_end = frame;
  44                state->crc_pending = true;
  45                spin_unlock(&output->composer_lock);
  46
  47                ret = queue_work(output->composer_workq, &state->composer_work);
  48                if (!ret)
  49                        DRM_DEBUG_DRIVER("Composer worker already queued\n");
  50        }
  51
  52        return HRTIMER_RESTART;
  53}
  54
  55static int vkms_enable_vblank(struct drm_crtc *crtc)
  56{
  57        struct drm_device *dev = crtc->dev;
  58        unsigned int pipe = drm_crtc_index(crtc);
  59        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
  60        struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
  61
  62        drm_calc_timestamping_constants(crtc, &crtc->mode);
  63
  64        hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  65        out->vblank_hrtimer.function = &vkms_vblank_simulate;
  66        out->period_ns = ktime_set(0, vblank->framedur_ns);
  67        hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
  68
  69        return 0;
  70}
  71
  72static void vkms_disable_vblank(struct drm_crtc *crtc)
  73{
  74        struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
  75
  76        hrtimer_cancel(&out->vblank_hrtimer);
  77}
  78
  79static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
  80                                      int *max_error, ktime_t *vblank_time,
  81                                      bool in_vblank_irq)
  82{
  83        struct drm_device *dev = crtc->dev;
  84        unsigned int pipe = crtc->index;
  85        struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
  86        struct vkms_output *output = &vkmsdev->output;
  87        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
  88
  89        *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
  90
  91        if (WARN_ON(*vblank_time == vblank->time))
  92                return true;
  93
  94        /*
  95         * To prevent races we roll the hrtimer forward before we do any
  96         * interrupt processing - this is how real hw works (the interrupt is
  97         * only generated after all the vblank registers are updated) and what
  98         * the vblank core expects. Therefore we need to always correct the
  99         * timestampe by one frame.
 100         */
 101        *vblank_time -= output->period_ns;
 102
 103        return true;
 104}
 105
 106static struct drm_crtc_state *
 107vkms_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->composer_work, vkms_composer_worker);
 121
 122        return &vkms_state->base;
 123}
 124
 125static 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        WARN_ON(work_pending(&vkms_state->composer_work));
 133        kfree(vkms_state->active_planes);
 134        kfree(vkms_state);
 135}
 136
 137static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
 138{
 139        struct vkms_crtc_state *vkms_state =
 140                kzalloc(sizeof(*vkms_state), GFP_KERNEL);
 141
 142        if (crtc->state)
 143                vkms_atomic_crtc_destroy_state(crtc, crtc->state);
 144
 145        __drm_atomic_helper_crtc_reset(crtc, &vkms_state->base);
 146        if (vkms_state)
 147                INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
 148}
 149
 150static const struct drm_crtc_funcs vkms_crtc_funcs = {
 151        .set_config             = drm_atomic_helper_set_config,
 152        .destroy                = drm_crtc_cleanup,
 153        .page_flip              = drm_atomic_helper_page_flip,
 154        .reset                  = vkms_atomic_crtc_reset,
 155        .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
 156        .atomic_destroy_state   = vkms_atomic_crtc_destroy_state,
 157        .enable_vblank          = vkms_enable_vblank,
 158        .disable_vblank         = vkms_disable_vblank,
 159        .get_vblank_timestamp   = vkms_get_vblank_timestamp,
 160        .get_crc_sources        = vkms_get_crc_sources,
 161        .set_crc_source         = vkms_set_crc_source,
 162        .verify_crc_source      = vkms_verify_crc_source,
 163};
 164
 165static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
 166                                  struct drm_crtc_state *state)
 167{
 168        struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
 169        struct drm_plane *plane;
 170        struct drm_plane_state *plane_state;
 171        int i = 0, ret;
 172
 173        if (vkms_state->active_planes)
 174                return 0;
 175
 176        ret = drm_atomic_add_affected_planes(state->state, crtc);
 177        if (ret < 0)
 178                return ret;
 179
 180        drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
 181                plane_state = drm_atomic_get_existing_plane_state(state->state,
 182                                                                  plane);
 183                WARN_ON(!plane_state);
 184
 185                if (!plane_state->visible)
 186                        continue;
 187
 188                i++;
 189        }
 190
 191        vkms_state->active_planes = kcalloc(i, sizeof(plane), GFP_KERNEL);
 192        if (!vkms_state->active_planes)
 193                return -ENOMEM;
 194        vkms_state->num_active_planes = i;
 195
 196        i = 0;
 197        drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
 198                plane_state = drm_atomic_get_existing_plane_state(state->state,
 199                                                                  plane);
 200
 201                if (!plane_state->visible)
 202                        continue;
 203
 204                vkms_state->active_planes[i++] =
 205                        to_vkms_plane_state(plane_state);
 206        }
 207
 208        return 0;
 209}
 210
 211static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
 212                                    struct drm_crtc_state *old_state)
 213{
 214        drm_crtc_vblank_on(crtc);
 215}
 216
 217static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
 218                                     struct drm_crtc_state *old_state)
 219{
 220        drm_crtc_vblank_off(crtc);
 221}
 222
 223static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
 224                                   struct drm_crtc_state *old_crtc_state)
 225{
 226        struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 227
 228        /* This lock is held across the atomic commit to block vblank timer
 229         * from scheduling vkms_composer_worker until the composer is updated
 230         */
 231        spin_lock_irq(&vkms_output->lock);
 232}
 233
 234static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
 235                                   struct drm_crtc_state *old_crtc_state)
 236{
 237        struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 238
 239        if (crtc->state->event) {
 240                spin_lock(&crtc->dev->event_lock);
 241
 242                if (drm_crtc_vblank_get(crtc) != 0)
 243                        drm_crtc_send_vblank_event(crtc, crtc->state->event);
 244                else
 245                        drm_crtc_arm_vblank_event(crtc, crtc->state->event);
 246
 247                spin_unlock(&crtc->dev->event_lock);
 248
 249                crtc->state->event = NULL;
 250        }
 251
 252        vkms_output->composer_state = to_vkms_crtc_state(crtc->state);
 253
 254        spin_unlock_irq(&vkms_output->lock);
 255}
 256
 257static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
 258        .atomic_check   = vkms_crtc_atomic_check,
 259        .atomic_begin   = vkms_crtc_atomic_begin,
 260        .atomic_flush   = vkms_crtc_atomic_flush,
 261        .atomic_enable  = vkms_crtc_atomic_enable,
 262        .atomic_disable = vkms_crtc_atomic_disable,
 263};
 264
 265int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 266                   struct drm_plane *primary, struct drm_plane *cursor)
 267{
 268        struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc);
 269        int ret;
 270
 271        ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
 272                                        &vkms_crtc_funcs, NULL);
 273        if (ret) {
 274                DRM_ERROR("Failed to init CRTC\n");
 275                return ret;
 276        }
 277
 278        drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
 279
 280        spin_lock_init(&vkms_out->lock);
 281        spin_lock_init(&vkms_out->composer_lock);
 282
 283        vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
 284        if (!vkms_out->composer_workq)
 285                return -ENOMEM;
 286
 287        return ret;
 288}
 289