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        if (!READ_ONCE(vblank->enabled)) {
  90                *vblank_time = ktime_get();
  91                return true;
  92        }
  93
  94        *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
  95
  96        if (WARN_ON(*vblank_time == vblank->time))
  97                return true;
  98
  99        /*
 100         * To prevent races we roll the hrtimer forward before we do any
 101         * interrupt processing - this is how real hw works (the interrupt is
 102         * only generated after all the vblank registers are updated) and what
 103         * the vblank core expects. Therefore we need to always correct the
 104         * timestampe by one frame.
 105         */
 106        *vblank_time -= output->period_ns;
 107
 108        return true;
 109}
 110
 111static struct drm_crtc_state *
 112vkms_atomic_crtc_duplicate_state(struct drm_crtc *crtc)
 113{
 114        struct vkms_crtc_state *vkms_state;
 115
 116        if (WARN_ON(!crtc->state))
 117                return NULL;
 118
 119        vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
 120        if (!vkms_state)
 121                return NULL;
 122
 123        __drm_atomic_helper_crtc_duplicate_state(crtc, &vkms_state->base);
 124
 125        INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
 126
 127        return &vkms_state->base;
 128}
 129
 130static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc,
 131                                           struct drm_crtc_state *state)
 132{
 133        struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
 134
 135        __drm_atomic_helper_crtc_destroy_state(state);
 136
 137        WARN_ON(work_pending(&vkms_state->composer_work));
 138        kfree(vkms_state->active_planes);
 139        kfree(vkms_state);
 140}
 141
 142static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
 143{
 144        struct vkms_crtc_state *vkms_state =
 145                kzalloc(sizeof(*vkms_state), GFP_KERNEL);
 146
 147        if (crtc->state)
 148                vkms_atomic_crtc_destroy_state(crtc, crtc->state);
 149
 150        __drm_atomic_helper_crtc_reset(crtc, &vkms_state->base);
 151        if (vkms_state)
 152                INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
 153}
 154
 155static const struct drm_crtc_funcs vkms_crtc_funcs = {
 156        .set_config             = drm_atomic_helper_set_config,
 157        .destroy                = drm_crtc_cleanup,
 158        .page_flip              = drm_atomic_helper_page_flip,
 159        .reset                  = vkms_atomic_crtc_reset,
 160        .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
 161        .atomic_destroy_state   = vkms_atomic_crtc_destroy_state,
 162        .enable_vblank          = vkms_enable_vblank,
 163        .disable_vblank         = vkms_disable_vblank,
 164        .get_vblank_timestamp   = vkms_get_vblank_timestamp,
 165        .get_crc_sources        = vkms_get_crc_sources,
 166        .set_crc_source         = vkms_set_crc_source,
 167        .verify_crc_source      = vkms_verify_crc_source,
 168};
 169
 170static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
 171                                  struct drm_crtc_state *state)
 172{
 173        struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
 174        struct drm_plane *plane;
 175        struct drm_plane_state *plane_state;
 176        int i = 0, ret;
 177
 178        if (vkms_state->active_planes)
 179                return 0;
 180
 181        ret = drm_atomic_add_affected_planes(state->state, crtc);
 182        if (ret < 0)
 183                return ret;
 184
 185        drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
 186                plane_state = drm_atomic_get_existing_plane_state(state->state,
 187                                                                  plane);
 188                WARN_ON(!plane_state);
 189
 190                if (!plane_state->visible)
 191                        continue;
 192
 193                i++;
 194        }
 195
 196        vkms_state->active_planes = kcalloc(i, sizeof(plane), GFP_KERNEL);
 197        if (!vkms_state->active_planes)
 198                return -ENOMEM;
 199        vkms_state->num_active_planes = i;
 200
 201        i = 0;
 202        drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
 203                plane_state = drm_atomic_get_existing_plane_state(state->state,
 204                                                                  plane);
 205
 206                if (!plane_state->visible)
 207                        continue;
 208
 209                vkms_state->active_planes[i++] =
 210                        to_vkms_plane_state(plane_state);
 211        }
 212
 213        return 0;
 214}
 215
 216static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
 217                                    struct drm_crtc_state *old_state)
 218{
 219        drm_crtc_vblank_on(crtc);
 220}
 221
 222static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
 223                                     struct drm_crtc_state *old_state)
 224{
 225        drm_crtc_vblank_off(crtc);
 226}
 227
 228static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
 229                                   struct drm_crtc_state *old_crtc_state)
 230{
 231        struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 232
 233        /* This lock is held across the atomic commit to block vblank timer
 234         * from scheduling vkms_composer_worker until the composer is updated
 235         */
 236        spin_lock_irq(&vkms_output->lock);
 237}
 238
 239static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
 240                                   struct drm_crtc_state *old_crtc_state)
 241{
 242        struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 243
 244        if (crtc->state->event) {
 245                spin_lock(&crtc->dev->event_lock);
 246
 247                if (drm_crtc_vblank_get(crtc) != 0)
 248                        drm_crtc_send_vblank_event(crtc, crtc->state->event);
 249                else
 250                        drm_crtc_arm_vblank_event(crtc, crtc->state->event);
 251
 252                spin_unlock(&crtc->dev->event_lock);
 253
 254                crtc->state->event = NULL;
 255        }
 256
 257        vkms_output->composer_state = to_vkms_crtc_state(crtc->state);
 258
 259        spin_unlock_irq(&vkms_output->lock);
 260}
 261
 262static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
 263        .atomic_check   = vkms_crtc_atomic_check,
 264        .atomic_begin   = vkms_crtc_atomic_begin,
 265        .atomic_flush   = vkms_crtc_atomic_flush,
 266        .atomic_enable  = vkms_crtc_atomic_enable,
 267        .atomic_disable = vkms_crtc_atomic_disable,
 268};
 269
 270int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 271                   struct drm_plane *primary, struct drm_plane *cursor)
 272{
 273        struct vkms_output *vkms_out = drm_crtc_to_vkms_output(crtc);
 274        int ret;
 275
 276        ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
 277                                        &vkms_crtc_funcs, NULL);
 278        if (ret) {
 279                DRM_ERROR("Failed to init CRTC\n");
 280                return ret;
 281        }
 282
 283        drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
 284
 285        spin_lock_init(&vkms_out->lock);
 286        spin_lock_init(&vkms_out->composer_lock);
 287
 288        vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0);
 289        if (!vkms_out->composer_workq)
 290                return -ENOMEM;
 291
 292        return ret;
 293}
 294