hc
2023-11-22 9ca5fbcb63a8dcaee0527f96afb91dc4b4bd8fa9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-License-Identifier: GPL-2.0+
 
#include "vkms_drv.h"
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
 
static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
{
   struct vkms_output *output = container_of(timer, struct vkms_output,
                         vblank_hrtimer);
   struct drm_crtc *crtc = &output->crtc;
   int ret_overrun;
   bool ret;
 
   ret = drm_crtc_handle_vblank(crtc);
   if (!ret)
       DRM_ERROR("vkms failure on handling vblank");
 
   ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
                     output->period_ns);
 
   return HRTIMER_RESTART;
}
 
static int vkms_enable_vblank(struct drm_crtc *crtc)
{
   struct drm_device *dev = crtc->dev;
   unsigned int pipe = drm_crtc_index(crtc);
   struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
   struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
 
   drm_calc_timestamping_constants(crtc, &crtc->mode);
 
   hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
   out->vblank_hrtimer.function = &vkms_vblank_simulate;
   out->period_ns = ktime_set(0, vblank->framedur_ns);
   hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
 
   return 0;
}
 
static void vkms_disable_vblank(struct drm_crtc *crtc)
{
   struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
 
   hrtimer_cancel(&out->vblank_hrtimer);
}
 
bool vkms_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
                  int *max_error, ktime_t *vblank_time,
                  bool in_vblank_irq)
{
   struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
   struct vkms_output *output = &vkmsdev->output;
 
   *vblank_time = output->vblank_hrtimer.node.expires;
 
   if (!in_vblank_irq)
       *vblank_time -= output->period_ns;
 
   return true;
}
 
static const struct drm_crtc_funcs vkms_crtc_funcs = {
   .set_config             = drm_atomic_helper_set_config,
   .destroy                = drm_crtc_cleanup,
   .page_flip              = drm_atomic_helper_page_flip,
   .reset                  = drm_atomic_helper_crtc_reset,
   .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
   .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
   .enable_vblank        = vkms_enable_vblank,
   .disable_vblank        = vkms_disable_vblank,
};
 
static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
                   struct drm_crtc_state *old_state)
{
   drm_crtc_vblank_on(crtc);
}
 
static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
                    struct drm_crtc_state *old_state)
{
   drm_crtc_vblank_off(crtc);
}
 
static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
                  struct drm_crtc_state *old_crtc_state)
{
   unsigned long flags;
 
   if (crtc->state->event) {
       spin_lock_irqsave(&crtc->dev->event_lock, flags);
 
       if (drm_crtc_vblank_get(crtc) != 0)
           drm_crtc_send_vblank_event(crtc, crtc->state->event);
       else
           drm_crtc_arm_vblank_event(crtc, crtc->state->event);
 
       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 
       crtc->state->event = NULL;
   }
}
 
static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
   .atomic_flush    = vkms_crtc_atomic_flush,
   .atomic_enable    = vkms_crtc_atomic_enable,
   .atomic_disable    = vkms_crtc_atomic_disable,
};
 
int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
          struct drm_plane *primary, struct drm_plane *cursor)
{
   int ret;
 
   ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
                   &vkms_crtc_funcs, NULL);
   if (ret) {
       DRM_ERROR("Failed to init CRTC\n");
       return ret;
   }
 
   drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
 
   return ret;
}