2 * Copyright (C) 2015 Red Hat, Inc.
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:
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.
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.
27 #include <drm/drm_fb_helper.h>
28 #include "virtgpu_drv.h"
30 #define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60)
32 struct virtio_gpu_fbdev
{
33 struct drm_fb_helper helper
;
34 struct virtio_gpu_framebuffer vgfb
;
35 struct list_head fbdev_list
;
36 struct virtio_gpu_device
*vgdev
;
37 struct delayed_work work
;
40 static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer
*fb
,
41 bool store
, int x
, int y
,
42 int width
, int height
)
44 struct drm_device
*dev
= fb
->base
.dev
;
45 struct virtio_gpu_device
*vgdev
= dev
->dev_private
;
46 bool store_for_later
= false;
47 int bpp
= fb
->base
.bits_per_pixel
/ 8;
50 struct virtio_gpu_object
*obj
= gem_to_virtio_gpu_obj(fb
->obj
);
53 (x
+ width
> fb
->base
.width
) ||
54 (y
+ height
> fb
->base
.height
)) {
55 DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n",
57 fb
->base
.width
, fb
->base
.height
);
62 * Can be called with pretty much any context (console output
63 * path). If we are in atomic just store the dirty rect info
64 * to send out the update later.
66 * Can't test inside spin lock.
68 if (in_atomic() || store
)
69 store_for_later
= true;
74 spin_lock_irqsave(&fb
->dirty_lock
, flags
);
85 if (store_for_later
) {
90 spin_unlock_irqrestore(&fb
->dirty_lock
, flags
);
94 fb
->x1
= fb
->y1
= INT_MAX
;
97 spin_unlock_irqrestore(&fb
->dirty_lock
, flags
);
101 uint32_t w
= x2
- x
+ 1;
102 uint32_t h
= y2
- y
+ 1;
104 offset
= (y
* fb
->base
.pitches
[0]) + x
* bpp
;
106 virtio_gpu_cmd_transfer_to_host_2d(vgdev
, obj
->hw_res_handle
,
115 virtio_gpu_cmd_resource_flush(vgdev
, obj
->hw_res_handle
,
116 x
, y
, x2
- x
+ 1, y2
- y
+ 1);
120 int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer
*vgfb
,
121 struct drm_clip_rect
*clips
,
124 struct virtio_gpu_device
*vgdev
= vgfb
->base
.dev
->dev_private
;
125 struct virtio_gpu_object
*obj
= gem_to_virtio_gpu_obj(vgfb
->obj
);
126 struct drm_clip_rect norect
;
127 struct drm_clip_rect
*clips_ptr
;
128 int left
, right
, top
, bottom
;
134 norect
.x1
= norect
.y1
= 0;
135 norect
.x2
= vgfb
->base
.width
;
136 norect
.y2
= vgfb
->base
.height
;
143 /* skip the first clip rect */
144 for (i
= 1, clips_ptr
= clips
+ inc
;
145 i
< num_clips
; i
++, clips_ptr
+= inc
) {
146 left
= min_t(int, left
, (int)clips_ptr
->x1
);
147 right
= max_t(int, right
, (int)clips_ptr
->x2
);
148 top
= min_t(int, top
, (int)clips_ptr
->y1
);
149 bottom
= max_t(int, bottom
, (int)clips_ptr
->y2
);
153 return virtio_gpu_dirty_update(vgfb
, false, left
, top
,
154 right
- left
, bottom
- top
);
156 virtio_gpu_cmd_resource_flush(vgdev
, obj
->hw_res_handle
,
157 left
, top
, right
- left
, bottom
- top
);
161 static void virtio_gpu_fb_dirty_work(struct work_struct
*work
)
163 struct delayed_work
*delayed_work
= to_delayed_work(work
);
164 struct virtio_gpu_fbdev
*vfbdev
=
165 container_of(delayed_work
, struct virtio_gpu_fbdev
, work
);
166 struct virtio_gpu_framebuffer
*vgfb
= &vfbdev
->vgfb
;
168 virtio_gpu_dirty_update(&vfbdev
->vgfb
, false, vgfb
->x1
, vgfb
->y1
,
169 vgfb
->x2
- vgfb
->x1
, vgfb
->y2
- vgfb
->y1
);
172 static void virtio_gpu_3d_fillrect(struct fb_info
*info
,
173 const struct fb_fillrect
*rect
)
175 struct virtio_gpu_fbdev
*vfbdev
= info
->par
;
176 drm_fb_helper_sys_fillrect(info
, rect
);
177 virtio_gpu_dirty_update(&vfbdev
->vgfb
, true, rect
->dx
, rect
->dy
,
178 rect
->width
, rect
->height
);
179 schedule_delayed_work(&vfbdev
->work
, VIRTIO_GPU_FBCON_POLL_PERIOD
);
182 static void virtio_gpu_3d_copyarea(struct fb_info
*info
,
183 const struct fb_copyarea
*area
)
185 struct virtio_gpu_fbdev
*vfbdev
= info
->par
;
186 drm_fb_helper_sys_copyarea(info
, area
);
187 virtio_gpu_dirty_update(&vfbdev
->vgfb
, true, area
->dx
, area
->dy
,
188 area
->width
, area
->height
);
189 schedule_delayed_work(&vfbdev
->work
, VIRTIO_GPU_FBCON_POLL_PERIOD
);
192 static void virtio_gpu_3d_imageblit(struct fb_info
*info
,
193 const struct fb_image
*image
)
195 struct virtio_gpu_fbdev
*vfbdev
= info
->par
;
196 drm_fb_helper_sys_imageblit(info
, image
);
197 virtio_gpu_dirty_update(&vfbdev
->vgfb
, true, image
->dx
, image
->dy
,
198 image
->width
, image
->height
);
199 schedule_delayed_work(&vfbdev
->work
, VIRTIO_GPU_FBCON_POLL_PERIOD
);
202 static struct fb_ops virtio_gpufb_ops
= {
203 .owner
= THIS_MODULE
,
204 .fb_check_var
= drm_fb_helper_check_var
,
205 .fb_set_par
= drm_fb_helper_set_par
, /* TODO: copy vmwgfx */
206 .fb_fillrect
= virtio_gpu_3d_fillrect
,
207 .fb_copyarea
= virtio_gpu_3d_copyarea
,
208 .fb_imageblit
= virtio_gpu_3d_imageblit
,
209 .fb_pan_display
= drm_fb_helper_pan_display
,
210 .fb_blank
= drm_fb_helper_blank
,
211 .fb_setcmap
= drm_fb_helper_setcmap
,
212 .fb_debug_enter
= drm_fb_helper_debug_enter
,
213 .fb_debug_leave
= drm_fb_helper_debug_leave
,
216 static int virtio_gpu_vmap_fb(struct virtio_gpu_device
*vgdev
,
217 struct virtio_gpu_object
*obj
)
219 return virtio_gpu_object_kmap(obj
, NULL
);
222 static int virtio_gpufb_create(struct drm_fb_helper
*helper
,
223 struct drm_fb_helper_surface_size
*sizes
)
225 struct virtio_gpu_fbdev
*vfbdev
=
226 container_of(helper
, struct virtio_gpu_fbdev
, helper
);
227 struct drm_device
*dev
= helper
->dev
;
228 struct virtio_gpu_device
*vgdev
= dev
->dev_private
;
229 struct fb_info
*info
;
230 struct drm_framebuffer
*fb
;
231 struct drm_mode_fb_cmd2 mode_cmd
= {};
232 struct virtio_gpu_object
*obj
;
233 uint32_t resid
, format
, size
;
236 mode_cmd
.width
= sizes
->surface_width
;
237 mode_cmd
.height
= sizes
->surface_height
;
238 mode_cmd
.pitches
[0] = mode_cmd
.width
* 4;
239 mode_cmd
.pixel_format
= drm_mode_legacy_fb_format(32, 24);
241 switch (mode_cmd
.pixel_format
) {
243 case DRM_FORMAT_XRGB8888
:
244 format
= VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM
;
246 case DRM_FORMAT_ARGB8888
:
247 format
= VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM
;
249 case DRM_FORMAT_BGRX8888
:
250 format
= VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM
;
252 case DRM_FORMAT_BGRA8888
:
253 format
= VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM
;
255 case DRM_FORMAT_RGBX8888
:
256 format
= VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM
;
258 case DRM_FORMAT_RGBA8888
:
259 format
= VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM
;
261 case DRM_FORMAT_XBGR8888
:
262 format
= VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM
;
264 case DRM_FORMAT_ABGR8888
:
265 format
= VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM
;
268 case DRM_FORMAT_XRGB8888
:
269 format
= VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM
;
271 case DRM_FORMAT_ARGB8888
:
272 format
= VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM
;
274 case DRM_FORMAT_BGRX8888
:
275 format
= VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM
;
277 case DRM_FORMAT_BGRA8888
:
278 format
= VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM
;
280 case DRM_FORMAT_RGBX8888
:
281 format
= VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM
;
283 case DRM_FORMAT_RGBA8888
:
284 format
= VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM
;
286 case DRM_FORMAT_XBGR8888
:
287 format
= VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM
;
289 case DRM_FORMAT_ABGR8888
:
290 format
= VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM
;
294 DRM_ERROR("failed to find virtio gpu format for %d\n",
295 mode_cmd
.pixel_format
);
299 size
= mode_cmd
.pitches
[0] * mode_cmd
.height
;
300 obj
= virtio_gpu_alloc_object(dev
, size
, false, true);
304 virtio_gpu_resource_id_get(vgdev
, &resid
);
305 virtio_gpu_cmd_create_resource(vgdev
, resid
, format
,
306 mode_cmd
.width
, mode_cmd
.height
);
308 ret
= virtio_gpu_vmap_fb(vgdev
, obj
);
310 DRM_ERROR("failed to vmap fb %d\n", ret
);
314 /* attach the object to the resource */
315 ret
= virtio_gpu_object_attach(vgdev
, obj
, resid
, NULL
);
319 info
= drm_fb_helper_alloc_fbi(helper
);
327 ret
= virtio_gpu_framebuffer_init(dev
, &vfbdev
->vgfb
,
328 &mode_cmd
, &obj
->gem_base
);
332 fb
= &vfbdev
->vgfb
.base
;
334 vfbdev
->helper
.fb
= fb
;
336 strcpy(info
->fix
.id
, "virtiodrmfb");
337 info
->flags
= FBINFO_DEFAULT
;
338 info
->fbops
= &virtio_gpufb_ops
;
339 info
->pixmap
.flags
= FB_PIXMAP_SYSTEM
;
341 info
->screen_base
= obj
->vmap
;
342 info
->screen_size
= obj
->gem_base
.size
;
343 drm_fb_helper_fill_fix(info
, fb
->pitches
[0], fb
->depth
);
344 drm_fb_helper_fill_var(info
, &vfbdev
->helper
,
345 sizes
->fb_width
, sizes
->fb_height
);
347 info
->fix
.mmio_start
= 0;
348 info
->fix
.mmio_len
= 0;
352 drm_fb_helper_release_fbi(helper
);
354 virtio_gpu_cmd_resource_inval_backing(vgdev
, resid
);
357 virtio_gpu_gem_free_object(&obj
->gem_base
);
361 static int virtio_gpu_fbdev_destroy(struct drm_device
*dev
,
362 struct virtio_gpu_fbdev
*vgfbdev
)
364 struct virtio_gpu_framebuffer
*vgfb
= &vgfbdev
->vgfb
;
366 drm_fb_helper_unregister_fbi(&vgfbdev
->helper
);
367 drm_fb_helper_release_fbi(&vgfbdev
->helper
);
371 drm_fb_helper_fini(&vgfbdev
->helper
);
372 drm_framebuffer_cleanup(&vgfb
->base
);
376 static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs
= {
377 .fb_probe
= virtio_gpufb_create
,
380 int virtio_gpu_fbdev_init(struct virtio_gpu_device
*vgdev
)
382 struct virtio_gpu_fbdev
*vgfbdev
;
383 int bpp_sel
= 32; /* TODO: parameter from somewhere? */
386 vgfbdev
= kzalloc(sizeof(struct virtio_gpu_fbdev
), GFP_KERNEL
);
390 vgfbdev
->vgdev
= vgdev
;
391 vgdev
->vgfbdev
= vgfbdev
;
392 INIT_DELAYED_WORK(&vgfbdev
->work
, virtio_gpu_fb_dirty_work
);
394 drm_fb_helper_prepare(vgdev
->ddev
, &vgfbdev
->helper
,
395 &virtio_gpu_fb_helper_funcs
);
396 ret
= drm_fb_helper_init(vgdev
->ddev
, &vgfbdev
->helper
,
398 VIRTIO_GPUFB_CONN_LIMIT
);
404 drm_fb_helper_single_add_all_connectors(&vgfbdev
->helper
);
405 drm_fb_helper_initial_config(&vgfbdev
->helper
, bpp_sel
);
409 void virtio_gpu_fbdev_fini(struct virtio_gpu_device
*vgdev
)
414 virtio_gpu_fbdev_destroy(vgdev
->ddev
, vgdev
->vgfbdev
);
415 kfree(vgdev
->vgfbdev
);
416 vgdev
->vgfbdev
= NULL
;