• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2013 Red Hat
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *     David Airlie
25  */
26 #include <linux/module.h>
27 #include <linux/fb.h>
28 
29 #include "drmP.h"
30 #include "drm/drm.h"
31 #include "drm/drm_crtc.h"
32 #include "drm/drm_crtc_helper.h"
33 #include "qxl_drv.h"
34 
35 #include "qxl_object.h"
36 #include "drm_fb_helper.h"
37 
38 #define QXL_DIRTY_DELAY (HZ / 30)
39 
40 struct qxl_fbdev {
41 	struct drm_fb_helper helper;
42 	struct qxl_framebuffer	qfb;
43 	struct list_head	fbdev_list;
44 	struct qxl_device	*qdev;
45 
46 	spinlock_t delayed_ops_lock;
47 	struct list_head delayed_ops;
48 	void *shadow;
49 	int size;
50 
51 	/* dirty memory logging */
52 	struct {
53 		spinlock_t lock;
54 		unsigned x1;
55 		unsigned y1;
56 		unsigned x2;
57 		unsigned y2;
58 	} dirty;
59 };
60 
qxl_fb_image_init(struct qxl_fb_image * qxl_fb_image,struct qxl_device * qdev,struct fb_info * info,const struct fb_image * image)61 static void qxl_fb_image_init(struct qxl_fb_image *qxl_fb_image,
62 			      struct qxl_device *qdev, struct fb_info *info,
63 			      const struct fb_image *image)
64 {
65 	qxl_fb_image->qdev = qdev;
66 	if (info) {
67 		qxl_fb_image->visual = info->fix.visual;
68 		if (qxl_fb_image->visual == FB_VISUAL_TRUECOLOR ||
69 		    qxl_fb_image->visual == FB_VISUAL_DIRECTCOLOR)
70 			memcpy(&qxl_fb_image->pseudo_palette,
71 			       info->pseudo_palette,
72 			       sizeof(qxl_fb_image->pseudo_palette));
73 	} else {
74 		 /* fallback */
75 		if (image->depth == 1)
76 			qxl_fb_image->visual = FB_VISUAL_MONO10;
77 		else
78 			qxl_fb_image->visual = FB_VISUAL_DIRECTCOLOR;
79 	}
80 	if (image) {
81 		memcpy(&qxl_fb_image->fb_image, image,
82 		       sizeof(qxl_fb_image->fb_image));
83 	}
84 }
85 
qxl_fb_dirty_flush(struct fb_info * info)86 static void qxl_fb_dirty_flush(struct fb_info *info)
87 {
88 	struct qxl_fbdev *qfbdev = info->par;
89 	struct qxl_device *qdev = qfbdev->qdev;
90 	struct qxl_fb_image qxl_fb_image;
91 	struct fb_image *image = &qxl_fb_image.fb_image;
92 	unsigned long flags;
93 	u32 x1, x2, y1, y2;
94 
95 	/* TODO: hard coding 32 bpp */
96 	int stride = qfbdev->qfb.base.pitches[0];
97 
98 	spin_lock_irqsave(&qfbdev->dirty.lock, flags);
99 
100 	x1 = qfbdev->dirty.x1;
101 	x2 = qfbdev->dirty.x2;
102 	y1 = qfbdev->dirty.y1;
103 	y2 = qfbdev->dirty.y2;
104 	qfbdev->dirty.x1 = 0;
105 	qfbdev->dirty.x2 = 0;
106 	qfbdev->dirty.y1 = 0;
107 	qfbdev->dirty.y2 = 0;
108 
109 	spin_unlock_irqrestore(&qfbdev->dirty.lock, flags);
110 
111 	/*
112 	 * we are using a shadow draw buffer, at qdev->surface0_shadow
113 	 */
114 	qxl_io_log(qdev, "dirty x[%d, %d], y[%d, %d]", x1, x2, y1, y2);
115 	image->dx = x1;
116 	image->dy = y1;
117 	image->width = x2 - x1 + 1;
118 	image->height = y2 - y1 + 1;
119 	image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized
120 					 warnings */
121 	image->bg_color = 0;
122 	image->depth = 32;	     /* TODO: take from somewhere? */
123 	image->cmap.start = 0;
124 	image->cmap.len = 0;
125 	image->cmap.red = NULL;
126 	image->cmap.green = NULL;
127 	image->cmap.blue = NULL;
128 	image->cmap.transp = NULL;
129 	image->data = qfbdev->shadow + (x1 * 4) + (stride * y1);
130 
131 	qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL);
132 	qxl_draw_opaque_fb(&qxl_fb_image, stride);
133 }
134 
qxl_dirty_update(struct qxl_fbdev * qfbdev,int x,int y,int width,int height)135 static void qxl_dirty_update(struct qxl_fbdev *qfbdev,
136 			     int x, int y, int width, int height)
137 {
138 	struct qxl_device *qdev = qfbdev->qdev;
139 	unsigned long flags;
140 	int x2, y2;
141 
142 	x2 = x + width - 1;
143 	y2 = y + height - 1;
144 
145 	spin_lock_irqsave(&qfbdev->dirty.lock, flags);
146 
147 	if ((qfbdev->dirty.y2 - qfbdev->dirty.y1) &&
148 	    (qfbdev->dirty.x2 - qfbdev->dirty.x1)) {
149 		if (qfbdev->dirty.y1 < y)
150 			y = qfbdev->dirty.y1;
151 		if (qfbdev->dirty.y2 > y2)
152 			y2 = qfbdev->dirty.y2;
153 		if (qfbdev->dirty.x1 < x)
154 			x = qfbdev->dirty.x1;
155 		if (qfbdev->dirty.x2 > x2)
156 			x2 = qfbdev->dirty.x2;
157 	}
158 
159 	qfbdev->dirty.x1 = x;
160 	qfbdev->dirty.x2 = x2;
161 	qfbdev->dirty.y1 = y;
162 	qfbdev->dirty.y2 = y2;
163 
164 	spin_unlock_irqrestore(&qfbdev->dirty.lock, flags);
165 
166 	schedule_work(&qdev->fb_work);
167 }
168 
qxl_deferred_io(struct fb_info * info,struct list_head * pagelist)169 static void qxl_deferred_io(struct fb_info *info,
170 			    struct list_head *pagelist)
171 {
172 	struct qxl_fbdev *qfbdev = info->par;
173 	unsigned long start, end, min, max;
174 	struct page *page;
175 	int y1, y2;
176 
177 	min = ULONG_MAX;
178 	max = 0;
179 	list_for_each_entry(page, pagelist, lru) {
180 		start = page->index << PAGE_SHIFT;
181 		end = start + PAGE_SIZE - 1;
182 		min = min(min, start);
183 		max = max(max, end);
184 	}
185 
186 	if (min < max) {
187 		y1 = min / info->fix.line_length;
188 		y2 = (max / info->fix.line_length) + 1;
189 		qxl_dirty_update(qfbdev, 0, y1, info->var.xres, y2 - y1);
190 	}
191 };
192 
193 static struct fb_deferred_io qxl_defio = {
194 	.delay		= QXL_DIRTY_DELAY,
195 	.deferred_io	= qxl_deferred_io,
196 };
197 
qxl_fb_fillrect(struct fb_info * info,const struct fb_fillrect * rect)198 static void qxl_fb_fillrect(struct fb_info *info,
199 			    const struct fb_fillrect *rect)
200 {
201 	struct qxl_fbdev *qfbdev = info->par;
202 
203 	drm_fb_helper_sys_fillrect(info, rect);
204 	qxl_dirty_update(qfbdev, rect->dx, rect->dy, rect->width,
205 			 rect->height);
206 }
207 
qxl_fb_copyarea(struct fb_info * info,const struct fb_copyarea * area)208 static void qxl_fb_copyarea(struct fb_info *info,
209 			    const struct fb_copyarea *area)
210 {
211 	struct qxl_fbdev *qfbdev = info->par;
212 
213 	drm_fb_helper_sys_copyarea(info, area);
214 	qxl_dirty_update(qfbdev, area->dx, area->dy, area->width,
215 			 area->height);
216 }
217 
qxl_fb_imageblit(struct fb_info * info,const struct fb_image * image)218 static void qxl_fb_imageblit(struct fb_info *info,
219 			     const struct fb_image *image)
220 {
221 	struct qxl_fbdev *qfbdev = info->par;
222 
223 	drm_fb_helper_sys_imageblit(info, image);
224 	qxl_dirty_update(qfbdev, image->dx, image->dy, image->width,
225 			 image->height);
226 }
227 
qxl_fb_work(struct work_struct * work)228 static void qxl_fb_work(struct work_struct *work)
229 {
230 	struct qxl_device *qdev = container_of(work, struct qxl_device, fb_work);
231 	struct qxl_fbdev *qfbdev = qdev->mode_info.qfbdev;
232 
233 	qxl_fb_dirty_flush(qfbdev->helper.fbdev);
234 }
235 
qxl_fb_init(struct qxl_device * qdev)236 int qxl_fb_init(struct qxl_device *qdev)
237 {
238 	INIT_WORK(&qdev->fb_work, qxl_fb_work);
239 	return 0;
240 }
241 
242 static struct fb_ops qxlfb_ops = {
243 	.owner = THIS_MODULE,
244 	.fb_check_var = drm_fb_helper_check_var,
245 	.fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
246 	.fb_fillrect = qxl_fb_fillrect,
247 	.fb_copyarea = qxl_fb_copyarea,
248 	.fb_imageblit = qxl_fb_imageblit,
249 	.fb_pan_display = drm_fb_helper_pan_display,
250 	.fb_blank = drm_fb_helper_blank,
251 	.fb_setcmap = drm_fb_helper_setcmap,
252 	.fb_debug_enter = drm_fb_helper_debug_enter,
253 	.fb_debug_leave = drm_fb_helper_debug_leave,
254 };
255 
qxlfb_destroy_pinned_object(struct drm_gem_object * gobj)256 static void qxlfb_destroy_pinned_object(struct drm_gem_object *gobj)
257 {
258 	struct qxl_bo *qbo = gem_to_qxl_bo(gobj);
259 	int ret;
260 
261 	ret = qxl_bo_reserve(qbo, false);
262 	if (likely(ret == 0)) {
263 		qxl_bo_kunmap(qbo);
264 		qxl_bo_unpin(qbo);
265 		qxl_bo_unreserve(qbo);
266 	}
267 	drm_gem_object_unreference_unlocked(gobj);
268 }
269 
qxl_get_handle_for_primary_fb(struct qxl_device * qdev,struct drm_file * file_priv,uint32_t * handle)270 int qxl_get_handle_for_primary_fb(struct qxl_device *qdev,
271 				  struct drm_file *file_priv,
272 				  uint32_t *handle)
273 {
274 	int r;
275 	struct drm_gem_object *gobj = qdev->fbdev_qfb->obj;
276 
277 	BUG_ON(!gobj);
278 	/* drm_get_handle_create adds a reference - good */
279 	r = drm_gem_handle_create(file_priv, gobj, handle);
280 	if (r)
281 		return r;
282 	return 0;
283 }
284 
qxlfb_create_pinned_object(struct qxl_fbdev * qfbdev,struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object ** gobj_p)285 static int qxlfb_create_pinned_object(struct qxl_fbdev *qfbdev,
286 				      struct drm_mode_fb_cmd2 *mode_cmd,
287 				      struct drm_gem_object **gobj_p)
288 {
289 	struct qxl_device *qdev = qfbdev->qdev;
290 	struct drm_gem_object *gobj = NULL;
291 	struct qxl_bo *qbo = NULL;
292 	int ret;
293 	int aligned_size, size;
294 	int height = mode_cmd->height;
295 	int bpp;
296 	int depth;
297 
298 	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &bpp, &depth);
299 
300 	size = mode_cmd->pitches[0] * height;
301 	aligned_size = ALIGN(size, PAGE_SIZE);
302 	/* TODO: unallocate and reallocate surface0 for real. Hack to just
303 	 * have a large enough surface0 for 1024x768 Xorg 32bpp mode */
304 	ret = qxl_gem_object_create(qdev, aligned_size, 0,
305 				    QXL_GEM_DOMAIN_SURFACE,
306 				    false, /* is discardable */
307 				    false, /* is kernel (false means device) */
308 				    NULL,
309 				    &gobj);
310 	if (ret) {
311 		pr_err("failed to allocate framebuffer (%d)\n",
312 		       aligned_size);
313 		return -ENOMEM;
314 	}
315 	qbo = gem_to_qxl_bo(gobj);
316 
317 	qbo->surf.width = mode_cmd->width;
318 	qbo->surf.height = mode_cmd->height;
319 	qbo->surf.stride = mode_cmd->pitches[0];
320 	qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB;
321 	ret = qxl_bo_reserve(qbo, false);
322 	if (unlikely(ret != 0))
323 		goto out_unref;
324 	ret = qxl_bo_pin(qbo, QXL_GEM_DOMAIN_SURFACE, NULL);
325 	if (ret) {
326 		qxl_bo_unreserve(qbo);
327 		goto out_unref;
328 	}
329 	ret = qxl_bo_kmap(qbo, NULL);
330 	qxl_bo_unreserve(qbo); /* unreserve, will be mmaped */
331 	if (ret)
332 		goto out_unref;
333 
334 	*gobj_p = gobj;
335 	return 0;
336 out_unref:
337 	qxlfb_destroy_pinned_object(gobj);
338 	*gobj_p = NULL;
339 	return ret;
340 }
341 
qxlfb_create(struct qxl_fbdev * qfbdev,struct drm_fb_helper_surface_size * sizes)342 static int qxlfb_create(struct qxl_fbdev *qfbdev,
343 			struct drm_fb_helper_surface_size *sizes)
344 {
345 	struct qxl_device *qdev = qfbdev->qdev;
346 	struct fb_info *info;
347 	struct drm_framebuffer *fb = NULL;
348 	struct drm_mode_fb_cmd2 mode_cmd;
349 	struct drm_gem_object *gobj = NULL;
350 	struct qxl_bo *qbo = NULL;
351 	int ret;
352 	int size;
353 	int bpp = sizes->surface_bpp;
354 	int depth = sizes->surface_depth;
355 	void *shadow;
356 
357 	mode_cmd.width = sizes->surface_width;
358 	mode_cmd.height = sizes->surface_height;
359 
360 	mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64);
361 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
362 
363 	ret = qxlfb_create_pinned_object(qfbdev, &mode_cmd, &gobj);
364 	qbo = gem_to_qxl_bo(gobj);
365 	QXL_INFO(qdev, "%s: %dx%d %d\n", __func__, mode_cmd.width,
366 		 mode_cmd.height, mode_cmd.pitches[0]);
367 
368 	shadow = vmalloc(mode_cmd.pitches[0] * mode_cmd.height);
369 	/* TODO: what's the usual response to memory allocation errors? */
370 	BUG_ON(!shadow);
371 	QXL_INFO(qdev,
372 	"surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n",
373 		 qxl_bo_gpu_offset(qbo),
374 		 qxl_bo_mmap_offset(qbo),
375 		 qbo->kptr,
376 		 shadow);
377 	size = mode_cmd.pitches[0] * mode_cmd.height;
378 
379 	info = drm_fb_helper_alloc_fbi(&qfbdev->helper);
380 	if (IS_ERR(info)) {
381 		ret = PTR_ERR(info);
382 		goto out_unref;
383 	}
384 
385 	info->par = qfbdev;
386 
387 	qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj);
388 
389 	fb = &qfbdev->qfb.base;
390 
391 	/* setup helper with fb data */
392 	qfbdev->helper.fb = fb;
393 
394 	qfbdev->shadow = shadow;
395 	strcpy(info->fix.id, "qxldrmfb");
396 
397 	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
398 
399 	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
400 	info->fbops = &qxlfb_ops;
401 
402 	/*
403 	 * TODO: using gobj->size in various places in this function. Not sure
404 	 * what the difference between the different sizes is.
405 	 */
406 	info->fix.smem_start = qdev->vram_base; /* TODO - correct? */
407 	info->fix.smem_len = gobj->size;
408 	info->screen_base = qfbdev->shadow;
409 	info->screen_size = gobj->size;
410 
411 	drm_fb_helper_fill_var(info, &qfbdev->helper, sizes->fb_width,
412 			       sizes->fb_height);
413 
414 	/* setup aperture base/size for vesafb takeover */
415 	info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base;
416 	info->apertures->ranges[0].size = qdev->vram_size;
417 
418 	info->fix.mmio_start = 0;
419 	info->fix.mmio_len = 0;
420 
421 	if (info->screen_base == NULL) {
422 		ret = -ENOSPC;
423 		goto out_destroy_fbi;
424 	}
425 
426 	info->fbdefio = &qxl_defio;
427 	fb_deferred_io_init(info);
428 
429 	qdev->fbdev_info = info;
430 	qdev->fbdev_qfb = &qfbdev->qfb;
431 	DRM_INFO("fb mappable at 0x%lX, size %lu\n",  info->fix.smem_start, (unsigned long)info->screen_size);
432 	DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height);
433 	return 0;
434 
435 out_destroy_fbi:
436 	drm_fb_helper_release_fbi(&qfbdev->helper);
437 out_unref:
438 	if (qbo) {
439 		ret = qxl_bo_reserve(qbo, false);
440 		if (likely(ret == 0)) {
441 			qxl_bo_kunmap(qbo);
442 			qxl_bo_unpin(qbo);
443 			qxl_bo_unreserve(qbo);
444 		}
445 	}
446 	if (fb && ret) {
447 		drm_gem_object_unreference(gobj);
448 		drm_framebuffer_cleanup(fb);
449 		kfree(fb);
450 	}
451 	drm_gem_object_unreference(gobj);
452 	return ret;
453 }
454 
qxl_fb_find_or_create_single(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)455 static int qxl_fb_find_or_create_single(
456 		struct drm_fb_helper *helper,
457 		struct drm_fb_helper_surface_size *sizes)
458 {
459 	struct qxl_fbdev *qfbdev =
460 		container_of(helper, struct qxl_fbdev, helper);
461 	int new_fb = 0;
462 	int ret;
463 
464 	if (!helper->fb) {
465 		ret = qxlfb_create(qfbdev, sizes);
466 		if (ret)
467 			return ret;
468 		new_fb = 1;
469 	}
470 	return new_fb;
471 }
472 
qxl_fbdev_destroy(struct drm_device * dev,struct qxl_fbdev * qfbdev)473 static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
474 {
475 	struct qxl_framebuffer *qfb = &qfbdev->qfb;
476 
477 	drm_fb_helper_unregister_fbi(&qfbdev->helper);
478 	drm_fb_helper_release_fbi(&qfbdev->helper);
479 
480 	if (qfb->obj) {
481 		qxlfb_destroy_pinned_object(qfb->obj);
482 		qfb->obj = NULL;
483 	}
484 	drm_fb_helper_fini(&qfbdev->helper);
485 	vfree(qfbdev->shadow);
486 	drm_framebuffer_cleanup(&qfb->base);
487 
488 	return 0;
489 }
490 
491 static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
492 	.fb_probe = qxl_fb_find_or_create_single,
493 };
494 
qxl_fbdev_init(struct qxl_device * qdev)495 int qxl_fbdev_init(struct qxl_device *qdev)
496 {
497 	int ret = 0;
498 
499 #ifdef CONFIG_DRM_FBDEV_EMULATION
500 	struct qxl_fbdev *qfbdev;
501 	int bpp_sel = 32; /* TODO: parameter from somewhere? */
502 
503 	qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
504 	if (!qfbdev)
505 		return -ENOMEM;
506 
507 	qfbdev->qdev = qdev;
508 	qdev->mode_info.qfbdev = qfbdev;
509 	spin_lock_init(&qfbdev->delayed_ops_lock);
510 	spin_lock_init(&qfbdev->dirty.lock);
511 	INIT_LIST_HEAD(&qfbdev->delayed_ops);
512 
513 	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
514 			      &qxl_fb_helper_funcs);
515 
516 	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
517 				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
518 				 QXLFB_CONN_LIMIT);
519 	if (ret)
520 		goto free;
521 
522 	ret = drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
523 	if (ret)
524 		goto fini;
525 
526 	ret = drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
527 	if (ret)
528 		goto fini;
529 
530 	return 0;
531 
532 fini:
533 	drm_fb_helper_fini(&qfbdev->helper);
534 free:
535 	kfree(qfbdev);
536 #endif
537 
538 	return ret;
539 }
540 
qxl_fbdev_fini(struct qxl_device * qdev)541 void qxl_fbdev_fini(struct qxl_device *qdev)
542 {
543 	if (!qdev->mode_info.qfbdev)
544 		return;
545 
546 	qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev);
547 	kfree(qdev->mode_info.qfbdev);
548 	qdev->mode_info.qfbdev = NULL;
549 }
550 
qxl_fbdev_set_suspend(struct qxl_device * qdev,int state)551 void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
552 {
553 	if (!qdev->mode_info.qfbdev)
554 		return;
555 
556 	drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state);
557 }
558 
qxl_fbdev_qobj_is_fb(struct qxl_device * qdev,struct qxl_bo * qobj)559 bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj)
560 {
561 	if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj))
562 		return true;
563 	return false;
564 }
565