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  
26  #include <drm/drm_atomic_helper.h>
27  #include <drm/drm_damage_helper.h>
28  #include <drm/drm_fourcc.h>
29  
30  #include "virtgpu_drv.h"
31  
32  static const uint32_t virtio_gpu_formats[] = {
33  	DRM_FORMAT_HOST_XRGB8888,
34  };
35  
36  static const uint32_t virtio_gpu_cursor_formats[] = {
37  	DRM_FORMAT_HOST_ARGB8888,
38  };
39  
virtio_gpu_translate_format(uint32_t drm_fourcc)40  uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc)
41  {
42  	uint32_t format;
43  
44  	switch (drm_fourcc) {
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;
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  
69  static const struct drm_plane_funcs virtio_gpu_plane_funcs = {
70  	.update_plane		= drm_atomic_helper_update_plane,
71  	.disable_plane		= drm_atomic_helper_disable_plane,
72  	.reset			= drm_atomic_helper_plane_reset,
73  	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
74  	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
75  };
76  
virtio_gpu_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)77  static int virtio_gpu_plane_atomic_check(struct drm_plane *plane,
78  					 struct drm_atomic_state *state)
79  {
80  	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
81  										 plane);
82  	bool is_cursor = plane->type == DRM_PLANE_TYPE_CURSOR;
83  	struct drm_crtc_state *crtc_state;
84  	int ret;
85  
86  	if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc))
87  		return 0;
88  
89  	crtc_state = drm_atomic_get_crtc_state(state,
90  					       new_plane_state->crtc);
91  	if (IS_ERR(crtc_state))
92                  return PTR_ERR(crtc_state);
93  
94  	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
95  						  DRM_PLANE_NO_SCALING,
96  						  DRM_PLANE_NO_SCALING,
97  						  is_cursor, true);
98  	return ret;
99  }
100  
virtio_gpu_update_dumb_bo(struct virtio_gpu_device * vgdev,struct drm_plane_state * state,struct drm_rect * rect)101  static void virtio_gpu_update_dumb_bo(struct virtio_gpu_device *vgdev,
102  				      struct drm_plane_state *state,
103  				      struct drm_rect *rect)
104  {
105  	struct virtio_gpu_object *bo =
106  		gem_to_virtio_gpu_obj(state->fb->obj[0]);
107  	struct virtio_gpu_object_array *objs;
108  	uint32_t w = rect->x2 - rect->x1;
109  	uint32_t h = rect->y2 - rect->y1;
110  	uint32_t x = rect->x1;
111  	uint32_t y = rect->y1;
112  	uint32_t off = x * state->fb->format->cpp[0] +
113  		y * state->fb->pitches[0];
114  
115  	objs = virtio_gpu_array_alloc(1);
116  	if (!objs)
117  		return;
118  	virtio_gpu_array_add_obj(objs, &bo->base.base);
119  
120  	virtio_gpu_cmd_transfer_to_host_2d(vgdev, off, w, h, x, y,
121  					   objs, NULL);
122  }
123  
virtio_gpu_resource_flush(struct drm_plane * plane,uint32_t x,uint32_t y,uint32_t width,uint32_t height)124  static void virtio_gpu_resource_flush(struct drm_plane *plane,
125  				      uint32_t x, uint32_t y,
126  				      uint32_t width, uint32_t height)
127  {
128  	struct drm_device *dev = plane->dev;
129  	struct virtio_gpu_device *vgdev = dev->dev_private;
130  	struct virtio_gpu_framebuffer *vgfb;
131  	struct virtio_gpu_object *bo;
132  
133  	vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
134  	bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
135  	if (vgfb->fence) {
136  		struct virtio_gpu_object_array *objs;
137  
138  		objs = virtio_gpu_array_alloc(1);
139  		if (!objs)
140  			return;
141  		virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
142  		virtio_gpu_array_lock_resv(objs);
143  		virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
144  					      width, height, objs, vgfb->fence);
145  		virtio_gpu_notify(vgdev);
146  
147  		dma_fence_wait_timeout(&vgfb->fence->f, true,
148  				       msecs_to_jiffies(50));
149  		dma_fence_put(&vgfb->fence->f);
150  		vgfb->fence = NULL;
151  	} else {
152  		virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
153  					      width, height, NULL, NULL);
154  		virtio_gpu_notify(vgdev);
155  	}
156  }
157  
virtio_gpu_primary_plane_update(struct drm_plane * plane,struct drm_atomic_state * state)158  static void virtio_gpu_primary_plane_update(struct drm_plane *plane,
159  					    struct drm_atomic_state *state)
160  {
161  	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
162  									   plane);
163  	struct drm_device *dev = plane->dev;
164  	struct virtio_gpu_device *vgdev = dev->dev_private;
165  	struct virtio_gpu_output *output = NULL;
166  	struct virtio_gpu_object *bo;
167  	struct drm_rect rect;
168  
169  	if (plane->state->crtc)
170  		output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
171  	if (old_state->crtc)
172  		output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
173  	if (WARN_ON(!output))
174  		return;
175  
176  	if (!plane->state->fb || !output->crtc.state->active) {
177  		DRM_DEBUG("nofb\n");
178  		virtio_gpu_cmd_set_scanout(vgdev, output->index, 0,
179  					   plane->state->src_w >> 16,
180  					   plane->state->src_h >> 16,
181  					   0, 0);
182  		virtio_gpu_notify(vgdev);
183  		return;
184  	}
185  
186  	if (!drm_atomic_helper_damage_merged(old_state, plane->state, &rect))
187  		return;
188  
189  	bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]);
190  	if (bo->dumb)
191  		virtio_gpu_update_dumb_bo(vgdev, plane->state, &rect);
192  
193  	if (plane->state->fb != old_state->fb ||
194  	    plane->state->src_w != old_state->src_w ||
195  	    plane->state->src_h != old_state->src_h ||
196  	    plane->state->src_x != old_state->src_x ||
197  	    plane->state->src_y != old_state->src_y ||
198  	    output->needs_modeset) {
199  		output->needs_modeset = false;
200  		DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d, src %dx%d+%d+%d\n",
201  			  bo->hw_res_handle,
202  			  plane->state->crtc_w, plane->state->crtc_h,
203  			  plane->state->crtc_x, plane->state->crtc_y,
204  			  plane->state->src_w >> 16,
205  			  plane->state->src_h >> 16,
206  			  plane->state->src_x >> 16,
207  			  plane->state->src_y >> 16);
208  
209  		if (bo->host3d_blob || bo->guest_blob) {
210  			virtio_gpu_cmd_set_scanout_blob
211  						(vgdev, output->index, bo,
212  						 plane->state->fb,
213  						 plane->state->src_w >> 16,
214  						 plane->state->src_h >> 16,
215  						 plane->state->src_x >> 16,
216  						 plane->state->src_y >> 16);
217  		} else {
218  			virtio_gpu_cmd_set_scanout(vgdev, output->index,
219  						   bo->hw_res_handle,
220  						   plane->state->src_w >> 16,
221  						   plane->state->src_h >> 16,
222  						   plane->state->src_x >> 16,
223  						   plane->state->src_y >> 16);
224  		}
225  	}
226  
227  	virtio_gpu_resource_flush(plane,
228  				  rect.x1,
229  				  rect.y1,
230  				  rect.x2 - rect.x1,
231  				  rect.y2 - rect.y1);
232  }
233  
virtio_gpu_plane_prepare_fb(struct drm_plane * plane,struct drm_plane_state * new_state)234  static int virtio_gpu_plane_prepare_fb(struct drm_plane *plane,
235  				       struct drm_plane_state *new_state)
236  {
237  	struct drm_device *dev = plane->dev;
238  	struct virtio_gpu_device *vgdev = dev->dev_private;
239  	struct virtio_gpu_framebuffer *vgfb;
240  	struct virtio_gpu_object *bo;
241  
242  	if (!new_state->fb)
243  		return 0;
244  
245  	vgfb = to_virtio_gpu_framebuffer(new_state->fb);
246  	bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
247  	if (!bo || (plane->type == DRM_PLANE_TYPE_PRIMARY && !bo->guest_blob))
248  		return 0;
249  
250  	if (bo->dumb && (plane->state->fb != new_state->fb)) {
251  		vgfb->fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
252  						     0);
253  		if (!vgfb->fence)
254  			return -ENOMEM;
255  	}
256  
257  	return 0;
258  }
259  
virtio_gpu_plane_cleanup_fb(struct drm_plane * plane,struct drm_plane_state * state)260  static void virtio_gpu_plane_cleanup_fb(struct drm_plane *plane,
261  					struct drm_plane_state *state)
262  {
263  	struct virtio_gpu_framebuffer *vgfb;
264  
265  	if (!state->fb)
266  		return;
267  
268  	vgfb = to_virtio_gpu_framebuffer(state->fb);
269  	if (vgfb->fence) {
270  		dma_fence_put(&vgfb->fence->f);
271  		vgfb->fence = NULL;
272  	}
273  }
274  
virtio_gpu_cursor_plane_update(struct drm_plane * plane,struct drm_atomic_state * state)275  static void virtio_gpu_cursor_plane_update(struct drm_plane *plane,
276  					   struct drm_atomic_state *state)
277  {
278  	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
279  									   plane);
280  	struct drm_device *dev = plane->dev;
281  	struct virtio_gpu_device *vgdev = dev->dev_private;
282  	struct virtio_gpu_output *output = NULL;
283  	struct virtio_gpu_framebuffer *vgfb;
284  	struct virtio_gpu_object *bo = NULL;
285  	uint32_t handle;
286  
287  	if (plane->state->crtc)
288  		output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
289  	if (old_state->crtc)
290  		output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
291  	if (WARN_ON(!output))
292  		return;
293  
294  	if (plane->state->fb) {
295  		vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
296  		bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
297  		handle = bo->hw_res_handle;
298  	} else {
299  		handle = 0;
300  	}
301  
302  	if (bo && bo->dumb && (plane->state->fb != old_state->fb)) {
303  		/* new cursor -- update & wait */
304  		struct virtio_gpu_object_array *objs;
305  
306  		objs = virtio_gpu_array_alloc(1);
307  		if (!objs)
308  			return;
309  		virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
310  		virtio_gpu_array_lock_resv(objs);
311  		virtio_gpu_cmd_transfer_to_host_2d
312  			(vgdev, 0,
313  			 plane->state->crtc_w,
314  			 plane->state->crtc_h,
315  			 0, 0, objs, vgfb->fence);
316  		virtio_gpu_notify(vgdev);
317  		dma_fence_wait(&vgfb->fence->f, true);
318  		dma_fence_put(&vgfb->fence->f);
319  		vgfb->fence = NULL;
320  	}
321  
322  	if (plane->state->fb != old_state->fb) {
323  		DRM_DEBUG("update, handle %d, pos +%d+%d, hot %d,%d\n", handle,
324  			  plane->state->crtc_x,
325  			  plane->state->crtc_y,
326  			  plane->state->fb ? plane->state->fb->hot_x : 0,
327  			  plane->state->fb ? plane->state->fb->hot_y : 0);
328  		output->cursor.hdr.type =
329  			cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
330  		output->cursor.resource_id = cpu_to_le32(handle);
331  		if (plane->state->fb) {
332  			output->cursor.hot_x =
333  				cpu_to_le32(plane->state->fb->hot_x);
334  			output->cursor.hot_y =
335  				cpu_to_le32(plane->state->fb->hot_y);
336  		} else {
337  			output->cursor.hot_x = cpu_to_le32(0);
338  			output->cursor.hot_y = cpu_to_le32(0);
339  		}
340  	} else {
341  		DRM_DEBUG("move +%d+%d\n",
342  			  plane->state->crtc_x,
343  			  plane->state->crtc_y);
344  		output->cursor.hdr.type =
345  			cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR);
346  	}
347  	output->cursor.pos.x = cpu_to_le32(plane->state->crtc_x);
348  	output->cursor.pos.y = cpu_to_le32(plane->state->crtc_y);
349  	virtio_gpu_cursor_ping(vgdev, output);
350  }
351  
352  static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = {
353  	.prepare_fb		= virtio_gpu_plane_prepare_fb,
354  	.cleanup_fb		= virtio_gpu_plane_cleanup_fb,
355  	.atomic_check		= virtio_gpu_plane_atomic_check,
356  	.atomic_update		= virtio_gpu_primary_plane_update,
357  };
358  
359  static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = {
360  	.prepare_fb		= virtio_gpu_plane_prepare_fb,
361  	.cleanup_fb		= virtio_gpu_plane_cleanup_fb,
362  	.atomic_check		= virtio_gpu_plane_atomic_check,
363  	.atomic_update		= virtio_gpu_cursor_plane_update,
364  };
365  
virtio_gpu_plane_init(struct virtio_gpu_device * vgdev,enum drm_plane_type type,int index)366  struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
367  					enum drm_plane_type type,
368  					int index)
369  {
370  	struct drm_device *dev = vgdev->ddev;
371  	const struct drm_plane_helper_funcs *funcs;
372  	struct drm_plane *plane;
373  	const uint32_t *formats;
374  	int nformats;
375  
376  	if (type == DRM_PLANE_TYPE_CURSOR) {
377  		formats = virtio_gpu_cursor_formats;
378  		nformats = ARRAY_SIZE(virtio_gpu_cursor_formats);
379  		funcs = &virtio_gpu_cursor_helper_funcs;
380  	} else {
381  		formats = virtio_gpu_formats;
382  		nformats = ARRAY_SIZE(virtio_gpu_formats);
383  		funcs = &virtio_gpu_primary_helper_funcs;
384  	}
385  
386  	plane = drmm_universal_plane_alloc(dev, struct drm_plane, dev,
387  					   1 << index, &virtio_gpu_plane_funcs,
388  					   formats, nformats, NULL, type, NULL);
389  	if (IS_ERR(plane))
390  		return plane;
391  
392  	drm_plane_helper_add(plane, funcs);
393  	return plane;
394  }
395