Commit | Line | Data |
---|---|---|
dc5698e8 DA |
1 | /* |
2 | * Copyright (C) 2015 Red Hat, Inc. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining | |
6 | * a copy of this software and associated documentation files (the | |
7 | * "Software"), to deal in the Software without restriction, including | |
8 | * without limitation the rights to use, copy, modify, merge, publish, | |
9 | * distribute, sublicense, and/or sell copies of the Software, and to | |
10 | * permit persons to whom the Software is furnished to do so, subject to | |
11 | * the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice (including the | |
14 | * next paragraph) shall be included in all copies or substantial | |
15 | * portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
24 | */ | |
25 | ||
dc5698e8 | 26 | #include <drm/drm_atomic_helper.h> |
a3d63977 SR |
27 | #include <drm/drm_fourcc.h> |
28 | #include <drm/drm_plane_helper.h> | |
29 | ||
30 | #include "virtgpu_drv.h" | |
dc5698e8 DA |
31 | |
32 | static const uint32_t virtio_gpu_formats[] = { | |
42fd9e6c | 33 | DRM_FORMAT_HOST_XRGB8888, |
dc5698e8 DA |
34 | }; |
35 | ||
bbbed888 | 36 | static const uint32_t virtio_gpu_cursor_formats[] = { |
42fd9e6c | 37 | DRM_FORMAT_HOST_ARGB8888, |
bbbed888 GH |
38 | }; |
39 | ||
d519cb76 GH |
40 | uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc) |
41 | { | |
42 | uint32_t format; | |
43 | ||
44 | switch (drm_fourcc) { | |
d519cb76 GH |
45 | case DRM_FORMAT_XRGB8888: |
46 | format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; | |
47 | break; | |
48 | case DRM_FORMAT_ARGB8888: | |
49 | format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; | |
50 | break; | |
51 | case DRM_FORMAT_BGRX8888: | |
52 | format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; | |
53 | break; | |
54 | case DRM_FORMAT_BGRA8888: | |
55 | format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; | |
56 | break; | |
d519cb76 GH |
57 | default: |
58 | /* | |
59 | * This should not happen, we handle everything listed | |
60 | * in virtio_gpu_formats[]. | |
61 | */ | |
62 | format = 0; | |
63 | break; | |
64 | } | |
65 | WARN_ON(format == 0); | |
66 | return format; | |
67 | } | |
68 | ||
dc5698e8 DA |
69 | static void virtio_gpu_plane_destroy(struct drm_plane *plane) |
70 | { | |
fb70046c | 71 | drm_plane_cleanup(plane); |
dc5698e8 DA |
72 | kfree(plane); |
73 | } | |
74 | ||
75 | static const struct drm_plane_funcs virtio_gpu_plane_funcs = { | |
76 | .update_plane = drm_atomic_helper_update_plane, | |
77 | .disable_plane = drm_atomic_helper_disable_plane, | |
78 | .destroy = virtio_gpu_plane_destroy, | |
79 | .reset = drm_atomic_helper_plane_reset, | |
80 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
81 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
82 | }; | |
83 | ||
84 | static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, | |
85 | struct drm_plane_state *state) | |
86 | { | |
a02c4c25 GH |
87 | bool is_cursor = plane->type == DRM_PLANE_TYPE_CURSOR; |
88 | struct drm_crtc_state *crtc_state; | |
89 | int ret; | |
90 | ||
91 | if (!state->fb || !state->crtc) | |
92 | return 0; | |
93 | ||
94 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | |
95 | if (IS_ERR(crtc_state)) | |
96 | return PTR_ERR(crtc_state); | |
97 | ||
98 | ret = drm_atomic_helper_check_plane_state(state, crtc_state, | |
99 | DRM_PLANE_HELPER_NO_SCALING, | |
100 | DRM_PLANE_HELPER_NO_SCALING, | |
101 | is_cursor, true); | |
102 | return ret; | |
dc5698e8 DA |
103 | } |
104 | ||
bbbed888 GH |
105 | static void virtio_gpu_primary_plane_update(struct drm_plane *plane, |
106 | struct drm_plane_state *old_state) | |
dc5698e8 DA |
107 | { |
108 | struct drm_device *dev = plane->dev; | |
109 | struct virtio_gpu_device *vgdev = dev->dev_private; | |
d3767d49 | 110 | struct virtio_gpu_output *output = NULL; |
dc5698e8 DA |
111 | struct virtio_gpu_framebuffer *vgfb; |
112 | struct virtio_gpu_object *bo; | |
113 | uint32_t handle; | |
114 | ||
d3767d49 GH |
115 | if (plane->state->crtc) |
116 | output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); | |
117 | if (old_state->crtc) | |
118 | output = drm_crtc_to_virtio_gpu_output(old_state->crtc); | |
b28c69dd HS |
119 | if (WARN_ON(!output)) |
120 | return; | |
d3767d49 | 121 | |
6c19787e | 122 | if (plane->state->fb && output->enabled) { |
11c94ace | 123 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); |
3823da3a | 124 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
dc5698e8 | 125 | handle = bo->hw_res_handle; |
4109e7f7 | 126 | if (bo->dumb) { |
3d3bdbc0 GH |
127 | struct virtio_gpu_object_array *objs; |
128 | ||
129 | objs = virtio_gpu_array_alloc(1); | |
130 | if (!objs) | |
131 | return; | |
132 | virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]); | |
4109e7f7 | 133 | virtio_gpu_cmd_transfer_to_host_2d |
3d3bdbc0 | 134 | (vgdev, 0, |
64f1cc99 GH |
135 | plane->state->src_w >> 16, |
136 | plane->state->src_h >> 16, | |
137 | plane->state->src_x >> 16, | |
138 | plane->state->src_y >> 16, | |
3d3bdbc0 | 139 | objs, NULL); |
4109e7f7 | 140 | } |
dc5698e8 DA |
141 | } else { |
142 | handle = 0; | |
143 | } | |
144 | ||
0062795e | 145 | DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d, src %dx%d+%d+%d\n", handle, |
dc5698e8 | 146 | plane->state->crtc_w, plane->state->crtc_h, |
0062795e GH |
147 | plane->state->crtc_x, plane->state->crtc_y, |
148 | plane->state->src_w >> 16, | |
149 | plane->state->src_h >> 16, | |
150 | plane->state->src_x >> 16, | |
151 | plane->state->src_y >> 16); | |
dc5698e8 | 152 | virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, |
0062795e GH |
153 | plane->state->src_w >> 16, |
154 | plane->state->src_h >> 16, | |
155 | plane->state->src_x >> 16, | |
156 | plane->state->src_y >> 16); | |
6a01d277 GH |
157 | if (handle) |
158 | virtio_gpu_cmd_resource_flush(vgdev, handle, | |
159 | plane->state->src_x >> 16, | |
160 | plane->state->src_y >> 16, | |
161 | plane->state->src_w >> 16, | |
162 | plane->state->src_h >> 16); | |
dc5698e8 DA |
163 | } |
164 | ||
9fdd90c0 RF |
165 | static int virtio_gpu_cursor_prepare_fb(struct drm_plane *plane, |
166 | struct drm_plane_state *new_state) | |
167 | { | |
168 | struct drm_device *dev = plane->dev; | |
169 | struct virtio_gpu_device *vgdev = dev->dev_private; | |
170 | struct virtio_gpu_framebuffer *vgfb; | |
171 | struct virtio_gpu_object *bo; | |
172 | ||
173 | if (!new_state->fb) | |
174 | return 0; | |
175 | ||
176 | vgfb = to_virtio_gpu_framebuffer(new_state->fb); | |
177 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); | |
178 | if (bo && bo->dumb && (plane->state->fb != new_state->fb)) { | |
179 | vgfb->fence = virtio_gpu_fence_alloc(vgdev); | |
180 | if (!vgfb->fence) | |
181 | return -ENOMEM; | |
182 | } | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | static void virtio_gpu_cursor_cleanup_fb(struct drm_plane *plane, | |
188 | struct drm_plane_state *old_state) | |
189 | { | |
190 | struct virtio_gpu_framebuffer *vgfb; | |
191 | ||
192 | if (!plane->state->fb) | |
193 | return; | |
194 | ||
195 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); | |
cb66c6da GH |
196 | if (vgfb->fence) { |
197 | dma_fence_put(&vgfb->fence->f); | |
198 | vgfb->fence = NULL; | |
199 | } | |
9fdd90c0 RF |
200 | } |
201 | ||
bbbed888 GH |
202 | static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, |
203 | struct drm_plane_state *old_state) | |
204 | { | |
205 | struct drm_device *dev = plane->dev; | |
206 | struct virtio_gpu_device *vgdev = dev->dev_private; | |
207 | struct virtio_gpu_output *output = NULL; | |
208 | struct virtio_gpu_framebuffer *vgfb; | |
bbbed888 GH |
209 | struct virtio_gpu_object *bo = NULL; |
210 | uint32_t handle; | |
dc5698e8 | 211 | |
bbbed888 GH |
212 | if (plane->state->crtc) |
213 | output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); | |
214 | if (old_state->crtc) | |
215 | output = drm_crtc_to_virtio_gpu_output(old_state->crtc); | |
b28c69dd HS |
216 | if (WARN_ON(!output)) |
217 | return; | |
bbbed888 GH |
218 | |
219 | if (plane->state->fb) { | |
220 | vgfb = to_virtio_gpu_framebuffer(plane->state->fb); | |
3823da3a | 221 | bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); |
bbbed888 GH |
222 | handle = bo->hw_res_handle; |
223 | } else { | |
224 | handle = 0; | |
225 | } | |
226 | ||
227 | if (bo && bo->dumb && (plane->state->fb != old_state->fb)) { | |
228 | /* new cursor -- update & wait */ | |
3d3bdbc0 GH |
229 | struct virtio_gpu_object_array *objs; |
230 | ||
231 | objs = virtio_gpu_array_alloc(1); | |
232 | if (!objs) | |
233 | return; | |
234 | virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]); | |
bbbed888 | 235 | virtio_gpu_cmd_transfer_to_host_2d |
3d3bdbc0 | 236 | (vgdev, 0, |
64f1cc99 GH |
237 | plane->state->crtc_w, |
238 | plane->state->crtc_h, | |
3d3bdbc0 | 239 | 0, 0, objs, vgfb->fence); |
620f9c5e GH |
240 | dma_fence_wait(&vgfb->fence->f, true); |
241 | dma_fence_put(&vgfb->fence->f); | |
242 | vgfb->fence = NULL; | |
bbbed888 GH |
243 | } |
244 | ||
245 | if (plane->state->fb != old_state->fb) { | |
86f752d2 | 246 | DRM_DEBUG("update, handle %d, pos +%d+%d, hot %d,%d\n", handle, |
bbbed888 | 247 | plane->state->crtc_x, |
86f752d2 GH |
248 | plane->state->crtc_y, |
249 | plane->state->fb ? plane->state->fb->hot_x : 0, | |
250 | plane->state->fb ? plane->state->fb->hot_y : 0); | |
bbbed888 GH |
251 | output->cursor.hdr.type = |
252 | cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); | |
253 | output->cursor.resource_id = cpu_to_le32(handle); | |
86f752d2 GH |
254 | if (plane->state->fb) { |
255 | output->cursor.hot_x = | |
256 | cpu_to_le32(plane->state->fb->hot_x); | |
257 | output->cursor.hot_y = | |
258 | cpu_to_le32(plane->state->fb->hot_y); | |
259 | } else { | |
260 | output->cursor.hot_x = cpu_to_le32(0); | |
261 | output->cursor.hot_y = cpu_to_le32(0); | |
262 | } | |
bbbed888 GH |
263 | } else { |
264 | DRM_DEBUG("move +%d+%d\n", | |
265 | plane->state->crtc_x, | |
266 | plane->state->crtc_y); | |
267 | output->cursor.hdr.type = | |
268 | cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR); | |
269 | } | |
270 | output->cursor.pos.x = cpu_to_le32(plane->state->crtc_x); | |
271 | output->cursor.pos.y = cpu_to_le32(plane->state->crtc_y); | |
272 | virtio_gpu_cursor_ping(vgdev, output); | |
273 | } | |
274 | ||
275 | static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = { | |
dc5698e8 | 276 | .atomic_check = virtio_gpu_plane_atomic_check, |
bbbed888 GH |
277 | .atomic_update = virtio_gpu_primary_plane_update, |
278 | }; | |
279 | ||
280 | static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = { | |
9fdd90c0 RF |
281 | .prepare_fb = virtio_gpu_cursor_prepare_fb, |
282 | .cleanup_fb = virtio_gpu_cursor_cleanup_fb, | |
bbbed888 GH |
283 | .atomic_check = virtio_gpu_plane_atomic_check, |
284 | .atomic_update = virtio_gpu_cursor_plane_update, | |
dc5698e8 DA |
285 | }; |
286 | ||
287 | struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, | |
bbbed888 | 288 | enum drm_plane_type type, |
dc5698e8 DA |
289 | int index) |
290 | { | |
291 | struct drm_device *dev = vgdev->ddev; | |
bbbed888 | 292 | const struct drm_plane_helper_funcs *funcs; |
dc5698e8 | 293 | struct drm_plane *plane; |
bbbed888 GH |
294 | const uint32_t *formats; |
295 | int ret, nformats; | |
dc5698e8 DA |
296 | |
297 | plane = kzalloc(sizeof(*plane), GFP_KERNEL); | |
298 | if (!plane) | |
299 | return ERR_PTR(-ENOMEM); | |
300 | ||
bbbed888 GH |
301 | if (type == DRM_PLANE_TYPE_CURSOR) { |
302 | formats = virtio_gpu_cursor_formats; | |
303 | nformats = ARRAY_SIZE(virtio_gpu_cursor_formats); | |
304 | funcs = &virtio_gpu_cursor_helper_funcs; | |
305 | } else { | |
306 | formats = virtio_gpu_formats; | |
307 | nformats = ARRAY_SIZE(virtio_gpu_formats); | |
308 | funcs = &virtio_gpu_primary_helper_funcs; | |
309 | } | |
dc5698e8 DA |
310 | ret = drm_universal_plane_init(dev, plane, 1 << index, |
311 | &virtio_gpu_plane_funcs, | |
bbbed888 | 312 | formats, nformats, |
e6fc3b68 | 313 | NULL, type, NULL); |
dc5698e8 DA |
314 | if (ret) |
315 | goto err_plane_init; | |
316 | ||
bbbed888 | 317 | drm_plane_helper_add(plane, funcs); |
dc5698e8 DA |
318 | return plane; |
319 | ||
320 | err_plane_init: | |
321 | kfree(plane); | |
322 | return ERR_PTR(ret); | |
323 | } |