1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <linux/iosys-map.h> |
4 | |
5 | #include <drm/drm_atomic.h> |
6 | #include <drm/drm_atomic_helper.h> |
7 | #include <drm/drm_blend.h> |
8 | #include <drm/drm_fourcc.h> |
9 | #include <drm/drm_gem_atomic_helper.h> |
10 | #include <drm/drm_gem_framebuffer_helper.h> |
11 | |
12 | #include "vkms_drv.h" |
13 | #include "vkms_formats.h" |
14 | |
15 | static const u32 vkms_formats[] = { |
16 | DRM_FORMAT_ARGB8888, |
17 | DRM_FORMAT_XRGB8888, |
18 | DRM_FORMAT_XRGB16161616, |
19 | DRM_FORMAT_ARGB16161616, |
20 | DRM_FORMAT_RGB565 |
21 | }; |
22 | |
23 | static struct drm_plane_state * |
24 | vkms_plane_duplicate_state(struct drm_plane *plane) |
25 | { |
26 | struct vkms_plane_state *vkms_state; |
27 | struct vkms_frame_info *frame_info; |
28 | |
29 | vkms_state = kzalloc(size: sizeof(*vkms_state), GFP_KERNEL); |
30 | if (!vkms_state) |
31 | return NULL; |
32 | |
33 | frame_info = kzalloc(size: sizeof(*frame_info), GFP_KERNEL); |
34 | if (!frame_info) { |
35 | DRM_DEBUG_KMS("Couldn't allocate frame_info\n" ); |
36 | kfree(objp: vkms_state); |
37 | return NULL; |
38 | } |
39 | |
40 | vkms_state->frame_info = frame_info; |
41 | |
42 | __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state: &vkms_state->base); |
43 | |
44 | return &vkms_state->base.base; |
45 | } |
46 | |
47 | static void vkms_plane_destroy_state(struct drm_plane *plane, |
48 | struct drm_plane_state *old_state) |
49 | { |
50 | struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state); |
51 | struct drm_crtc *crtc = vkms_state->base.base.crtc; |
52 | |
53 | if (crtc && vkms_state->frame_info->fb) { |
54 | /* dropping the reference we acquired in |
55 | * vkms_primary_plane_update() |
56 | */ |
57 | if (drm_framebuffer_read_refcount(fb: vkms_state->frame_info->fb)) |
58 | drm_framebuffer_put(fb: vkms_state->frame_info->fb); |
59 | } |
60 | |
61 | kfree(objp: vkms_state->frame_info); |
62 | vkms_state->frame_info = NULL; |
63 | |
64 | __drm_gem_destroy_shadow_plane_state(shadow_plane_state: &vkms_state->base); |
65 | kfree(objp: vkms_state); |
66 | } |
67 | |
68 | static void vkms_plane_reset(struct drm_plane *plane) |
69 | { |
70 | struct vkms_plane_state *vkms_state; |
71 | |
72 | if (plane->state) { |
73 | vkms_plane_destroy_state(plane, old_state: plane->state); |
74 | plane->state = NULL; /* must be set to NULL here */ |
75 | } |
76 | |
77 | vkms_state = kzalloc(size: sizeof(*vkms_state), GFP_KERNEL); |
78 | if (!vkms_state) { |
79 | DRM_ERROR("Cannot allocate vkms_plane_state\n" ); |
80 | return; |
81 | } |
82 | |
83 | __drm_gem_reset_shadow_plane(plane, shadow_plane_state: &vkms_state->base); |
84 | } |
85 | |
86 | static const struct drm_plane_funcs vkms_plane_funcs = { |
87 | .update_plane = drm_atomic_helper_update_plane, |
88 | .disable_plane = drm_atomic_helper_disable_plane, |
89 | .reset = vkms_plane_reset, |
90 | .atomic_duplicate_state = vkms_plane_duplicate_state, |
91 | .atomic_destroy_state = vkms_plane_destroy_state, |
92 | }; |
93 | |
94 | static void vkms_plane_atomic_update(struct drm_plane *plane, |
95 | struct drm_atomic_state *state) |
96 | { |
97 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
98 | plane); |
99 | struct vkms_plane_state *vkms_plane_state; |
100 | struct drm_shadow_plane_state *shadow_plane_state; |
101 | struct drm_framebuffer *fb = new_state->fb; |
102 | struct vkms_frame_info *frame_info; |
103 | u32 fmt; |
104 | |
105 | if (!new_state->crtc || !fb) |
106 | return; |
107 | |
108 | fmt = fb->format->format; |
109 | vkms_plane_state = to_vkms_plane_state(new_state); |
110 | shadow_plane_state = &vkms_plane_state->base; |
111 | |
112 | frame_info = vkms_plane_state->frame_info; |
113 | memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect)); |
114 | memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect)); |
115 | memcpy(&frame_info->rotated, &new_state->dst, sizeof(struct drm_rect)); |
116 | frame_info->fb = fb; |
117 | memcpy(&frame_info->map, &shadow_plane_state->data, sizeof(frame_info->map)); |
118 | drm_framebuffer_get(fb: frame_info->fb); |
119 | frame_info->rotation = drm_rotation_simplify(rotation: new_state->rotation, DRM_MODE_ROTATE_0 | |
120 | DRM_MODE_ROTATE_90 | |
121 | DRM_MODE_ROTATE_270 | |
122 | DRM_MODE_REFLECT_X | |
123 | DRM_MODE_REFLECT_Y); |
124 | |
125 | drm_rect_rotate(r: &frame_info->rotated, width: drm_rect_width(r: &frame_info->rotated), |
126 | height: drm_rect_height(r: &frame_info->rotated), rotation: frame_info->rotation); |
127 | |
128 | frame_info->offset = fb->offsets[0]; |
129 | frame_info->pitch = fb->pitches[0]; |
130 | frame_info->cpp = fb->format->cpp[0]; |
131 | vkms_plane_state->pixel_read = get_pixel_conversion_function(format: fmt); |
132 | } |
133 | |
134 | static int vkms_plane_atomic_check(struct drm_plane *plane, |
135 | struct drm_atomic_state *state) |
136 | { |
137 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
138 | plane); |
139 | struct drm_crtc_state *crtc_state; |
140 | int ret; |
141 | |
142 | if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc)) |
143 | return 0; |
144 | |
145 | crtc_state = drm_atomic_get_crtc_state(state, |
146 | crtc: new_plane_state->crtc); |
147 | if (IS_ERR(ptr: crtc_state)) |
148 | return PTR_ERR(ptr: crtc_state); |
149 | |
150 | ret = drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state, |
151 | DRM_PLANE_NO_SCALING, |
152 | DRM_PLANE_NO_SCALING, |
153 | can_position: true, can_update_disabled: true); |
154 | if (ret != 0) |
155 | return ret; |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | static int vkms_prepare_fb(struct drm_plane *plane, |
161 | struct drm_plane_state *state) |
162 | { |
163 | struct drm_shadow_plane_state *shadow_plane_state; |
164 | struct drm_framebuffer *fb = state->fb; |
165 | int ret; |
166 | |
167 | if (!fb) |
168 | return 0; |
169 | |
170 | shadow_plane_state = to_drm_shadow_plane_state(state); |
171 | |
172 | ret = drm_gem_plane_helper_prepare_fb(plane, state); |
173 | if (ret) |
174 | return ret; |
175 | |
176 | return drm_gem_fb_vmap(fb, map: shadow_plane_state->map, data: shadow_plane_state->data); |
177 | } |
178 | |
179 | static void vkms_cleanup_fb(struct drm_plane *plane, |
180 | struct drm_plane_state *state) |
181 | { |
182 | struct drm_shadow_plane_state *shadow_plane_state; |
183 | struct drm_framebuffer *fb = state->fb; |
184 | |
185 | if (!fb) |
186 | return; |
187 | |
188 | shadow_plane_state = to_drm_shadow_plane_state(state); |
189 | |
190 | drm_gem_fb_vunmap(fb, map: shadow_plane_state->map); |
191 | } |
192 | |
193 | static const struct drm_plane_helper_funcs vkms_plane_helper_funcs = { |
194 | .atomic_update = vkms_plane_atomic_update, |
195 | .atomic_check = vkms_plane_atomic_check, |
196 | .prepare_fb = vkms_prepare_fb, |
197 | .cleanup_fb = vkms_cleanup_fb, |
198 | }; |
199 | |
200 | struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev, |
201 | enum drm_plane_type type, int index) |
202 | { |
203 | struct drm_device *dev = &vkmsdev->drm; |
204 | struct vkms_plane *plane; |
205 | |
206 | plane = drmm_universal_plane_alloc(dev, struct vkms_plane, base, 1 << index, |
207 | &vkms_plane_funcs, |
208 | vkms_formats, ARRAY_SIZE(vkms_formats), |
209 | NULL, type, NULL); |
210 | if (IS_ERR(ptr: plane)) |
211 | return plane; |
212 | |
213 | drm_plane_helper_add(plane: &plane->base, funcs: &vkms_plane_helper_funcs); |
214 | |
215 | drm_plane_create_rotation_property(plane: &plane->base, DRM_MODE_ROTATE_0, |
216 | DRM_MODE_ROTATE_MASK | DRM_MODE_REFLECT_MASK); |
217 | |
218 | return plane; |
219 | } |
220 | |