]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/qxl/qxl_fb.c
Merge branch 'sched-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[karo-tx-linux.git] / drivers / gpu / drm / qxl / qxl_fb.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
495 int qxl_fbdev_init(struct qxl_device *qdev)
496 {
497         struct qxl_fbdev *qfbdev;
498         int bpp_sel = 32; /* TODO: parameter from somewhere? */
499         int ret;
500
501         qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
502         if (!qfbdev)
503                 return -ENOMEM;
504
505         qfbdev->qdev = qdev;
506         qdev->mode_info.qfbdev = qfbdev;
507         spin_lock_init(&qfbdev->delayed_ops_lock);
508         spin_lock_init(&qfbdev->dirty.lock);
509         INIT_LIST_HEAD(&qfbdev->delayed_ops);
510
511         drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
512                               &qxl_fb_helper_funcs);
513
514         ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
515                                  qxl_num_crtc /* num_crtc - QXL supports just 1 */,
516                                  QXLFB_CONN_LIMIT);
517         if (ret)
518                 goto free;
519
520         ret = drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
521         if (ret)
522                 goto fini;
523
524         ret = drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
525         if (ret)
526                 goto fini;
527
528         return 0;
529
530 fini:
531         drm_fb_helper_fini(&qfbdev->helper);
532 free:
533         kfree(qfbdev);
534         return ret;
535 }
536
537 void qxl_fbdev_fini(struct qxl_device *qdev)
538 {
539         if (!qdev->mode_info.qfbdev)
540                 return;
541
542         qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev);
543         kfree(qdev->mode_info.qfbdev);
544         qdev->mode_info.qfbdev = NULL;
545 }
546
547 void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
548 {
549         drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state);
550 }
551
552 bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj)
553 {
554         if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj))
555                 return true;
556         return false;
557 }