]> rtime.felk.cvut.cz Git - linux-imx.git/blob - drivers/gpu/drm/qxl/qxl_fb.c
Merge tag 'trace-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux...
[linux-imx.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         void *shadow;
47         int size;
48
49         /* dirty memory logging */
50         struct {
51                 spinlock_t lock;
52                 bool active;
53                 unsigned x1;
54                 unsigned y1;
55                 unsigned x2;
56                 unsigned y2;
57         } dirty;
58 };
59
60 static void qxl_fb_image_init(struct qxl_fb_image *qxl_fb_image,
61                               struct qxl_device *qdev, struct fb_info *info,
62                               const struct fb_image *image)
63 {
64         qxl_fb_image->qdev = qdev;
65         if (info) {
66                 qxl_fb_image->visual = info->fix.visual;
67                 if (qxl_fb_image->visual == FB_VISUAL_TRUECOLOR ||
68                     qxl_fb_image->visual == FB_VISUAL_DIRECTCOLOR)
69                         memcpy(&qxl_fb_image->pseudo_palette,
70                                info->pseudo_palette,
71                                sizeof(qxl_fb_image->pseudo_palette));
72         } else {
73                  /* fallback */
74                 if (image->depth == 1)
75                         qxl_fb_image->visual = FB_VISUAL_MONO10;
76                 else
77                         qxl_fb_image->visual = FB_VISUAL_DIRECTCOLOR;
78         }
79         if (image) {
80                 memcpy(&qxl_fb_image->fb_image, image,
81                        sizeof(qxl_fb_image->fb_image));
82         }
83 }
84
85 static void qxl_fb_dirty_flush(struct fb_info *info)
86 {
87         struct qxl_fbdev *qfbdev = info->par;
88         struct qxl_device *qdev = qfbdev->qdev;
89         struct qxl_fb_image qxl_fb_image;
90         struct fb_image *image = &qxl_fb_image.fb_image;
91         u32 x1, x2, y1, y2;
92
93         /* TODO: hard coding 32 bpp */
94         int stride = qfbdev->qfb.base.pitches[0] * 4;
95
96         x1 = qfbdev->dirty.x1;
97         x2 = qfbdev->dirty.x2;
98         y1 = qfbdev->dirty.y1;
99         y2 = qfbdev->dirty.y2;
100         /*
101          * we are using a shadow draw buffer, at qdev->surface0_shadow
102          */
103         qxl_io_log(qdev, "dirty x[%d, %d], y[%d, %d]", x1, x2, y1, y2);
104         image->dx = x1;
105         image->dy = y1;
106         image->width = x2 - x1;
107         image->height = y2 - y1;
108         image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized
109                                          warnings */
110         image->bg_color = 0;
111         image->depth = 32;           /* TODO: take from somewhere? */
112         image->cmap.start = 0;
113         image->cmap.len = 0;
114         image->cmap.red = NULL;
115         image->cmap.green = NULL;
116         image->cmap.blue = NULL;
117         image->cmap.transp = NULL;
118         image->data = qfbdev->shadow + (x1 * 4) + (stride * y1);
119
120         qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL);
121         qxl_draw_opaque_fb(&qxl_fb_image, stride);
122         qfbdev->dirty.x1 = 0;
123         qfbdev->dirty.x2 = 0;
124         qfbdev->dirty.y1 = 0;
125         qfbdev->dirty.y2 = 0;
126 }
127
128 static void qxl_deferred_io(struct fb_info *info,
129                             struct list_head *pagelist)
130 {
131         struct qxl_fbdev *qfbdev = info->par;
132         unsigned long start, end, min, max;
133         struct page *page;
134         int y1, y2;
135
136         min = ULONG_MAX;
137         max = 0;
138         list_for_each_entry(page, pagelist, lru) {
139                 start = page->index << PAGE_SHIFT;
140                 end = start + PAGE_SIZE - 1;
141                 min = min(min, start);
142                 max = max(max, end);
143         }
144
145         if (min < max) {
146                 y1 = min / info->fix.line_length;
147                 y2 = (max / info->fix.line_length) + 1;
148
149                 /* TODO: add spin lock? */
150                 /* spin_lock_irqsave(&qfbdev->dirty.lock, flags); */
151                 qfbdev->dirty.x1 = 0;
152                 qfbdev->dirty.y1 = y1;
153                 qfbdev->dirty.x2 = info->var.xres;
154                 qfbdev->dirty.y2 = y2;
155                 /* spin_unlock_irqrestore(&qfbdev->dirty.lock, flags); */
156         }
157
158         qxl_fb_dirty_flush(info);
159 };
160
161
162 static struct fb_deferred_io qxl_defio = {
163         .delay          = QXL_DIRTY_DELAY,
164         .deferred_io    = qxl_deferred_io,
165 };
166
167 static void qxl_fb_fillrect(struct fb_info *info,
168                             const struct fb_fillrect *fb_rect)
169 {
170         struct qxl_fbdev *qfbdev = info->par;
171         struct qxl_device *qdev = qfbdev->qdev;
172         struct qxl_rect rect;
173         uint32_t color;
174         int x = fb_rect->dx;
175         int y = fb_rect->dy;
176         int width = fb_rect->width;
177         int height = fb_rect->height;
178         uint16_t rop;
179         struct qxl_draw_fill qxl_draw_fill_rec;
180
181         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
182             info->fix.visual == FB_VISUAL_DIRECTCOLOR)
183                 color = ((u32 *) (info->pseudo_palette))[fb_rect->color];
184         else
185                 color = fb_rect->color;
186         rect.left = x;
187         rect.right = x + width;
188         rect.top = y;
189         rect.bottom = y + height;
190         switch (fb_rect->rop) {
191         case ROP_XOR:
192                 rop = SPICE_ROPD_OP_XOR;
193                 break;
194         case ROP_COPY:
195                 rop = SPICE_ROPD_OP_PUT;
196                 break;
197         default:
198                 pr_err("qxl_fb_fillrect(): unknown rop, "
199                        "defaulting to SPICE_ROPD_OP_PUT\n");
200                 rop = SPICE_ROPD_OP_PUT;
201         }
202         qxl_draw_fill_rec.qdev = qdev;
203         qxl_draw_fill_rec.rect = rect;
204         qxl_draw_fill_rec.color = color;
205         qxl_draw_fill_rec.rop = rop;
206         if (!drm_can_sleep()) {
207                 qxl_io_log(qdev,
208                         "%s: TODO use RCU, mysterious locks with spin_lock\n",
209                         __func__);
210                 return;
211         }
212         qxl_draw_fill(&qxl_draw_fill_rec);
213 }
214
215 static void qxl_fb_copyarea(struct fb_info *info,
216                             const struct fb_copyarea *region)
217 {
218         struct qxl_fbdev *qfbdev = info->par;
219
220         qxl_draw_copyarea(qfbdev->qdev,
221                           region->width, region->height,
222                           region->sx, region->sy,
223                           region->dx, region->dy);
224 }
225
226 static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image)
227 {
228         qxl_draw_opaque_fb(qxl_fb_image, 0);
229 }
230
231 static void qxl_fb_imageblit(struct fb_info *info,
232                              const struct fb_image *image)
233 {
234         struct qxl_fbdev *qfbdev = info->par;
235         struct qxl_device *qdev = qfbdev->qdev;
236         struct qxl_fb_image qxl_fb_image;
237
238         if (!drm_can_sleep()) {
239                 /* we cannot do any ttm_bo allocation since that will fail on
240                  * ioremap_wc..__get_vm_area_node, so queue the work item
241                  * instead This can happen from printk inside an interrupt
242                  * context, i.e.: smp_apic_timer_interrupt..check_cpu_stall */
243                 qxl_io_log(qdev,
244                         "%s: TODO use RCU, mysterious locks with spin_lock\n",
245                            __func__);
246                 return;
247         }
248
249         /* ensure proper order of rendering operations - TODO: must do this
250          * for everything. */
251         qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image);
252         qxl_fb_imageblit_safe(&qxl_fb_image);
253 }
254
255 int qxl_fb_init(struct qxl_device *qdev)
256 {
257         return 0;
258 }
259
260 static struct fb_ops qxlfb_ops = {
261         .owner = THIS_MODULE,
262         .fb_check_var = drm_fb_helper_check_var,
263         .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
264         .fb_fillrect = qxl_fb_fillrect,
265         .fb_copyarea = qxl_fb_copyarea,
266         .fb_imageblit = qxl_fb_imageblit,
267         .fb_pan_display = drm_fb_helper_pan_display,
268         .fb_blank = drm_fb_helper_blank,
269         .fb_setcmap = drm_fb_helper_setcmap,
270         .fb_debug_enter = drm_fb_helper_debug_enter,
271         .fb_debug_leave = drm_fb_helper_debug_leave,
272 };
273
274 static void qxlfb_destroy_pinned_object(struct drm_gem_object *gobj)
275 {
276         struct qxl_bo *qbo = gem_to_qxl_bo(gobj);
277         int ret;
278
279         ret = qxl_bo_reserve(qbo, false);
280         if (likely(ret == 0)) {
281                 qxl_bo_kunmap(qbo);
282                 qxl_bo_unpin(qbo);
283                 qxl_bo_unreserve(qbo);
284         }
285         drm_gem_object_unreference_unlocked(gobj);
286 }
287
288 int qxl_get_handle_for_primary_fb(struct qxl_device *qdev,
289                                   struct drm_file *file_priv,
290                                   uint32_t *handle)
291 {
292         int r;
293         struct drm_gem_object *gobj = qdev->fbdev_qfb->obj;
294
295         BUG_ON(!gobj);
296         /* drm_get_handle_create adds a reference - good */
297         r = drm_gem_handle_create(file_priv, gobj, handle);
298         if (r)
299                 return r;
300         return 0;
301 }
302
303 static int qxlfb_create_pinned_object(struct qxl_fbdev *qfbdev,
304                                       struct drm_mode_fb_cmd2 *mode_cmd,
305                                       struct drm_gem_object **gobj_p)
306 {
307         struct qxl_device *qdev = qfbdev->qdev;
308         struct drm_gem_object *gobj = NULL;
309         struct qxl_bo *qbo = NULL;
310         int ret;
311         int aligned_size, size;
312         int height = mode_cmd->height;
313         int bpp;
314         int depth;
315
316         drm_fb_get_bpp_depth(mode_cmd->pixel_format, &bpp, &depth);
317
318         size = mode_cmd->pitches[0] * height;
319         aligned_size = ALIGN(size, PAGE_SIZE);
320         /* TODO: unallocate and reallocate surface0 for real. Hack to just
321          * have a large enough surface0 for 1024x768 Xorg 32bpp mode */
322         ret = qxl_gem_object_create(qdev, aligned_size, 0,
323                                     QXL_GEM_DOMAIN_SURFACE,
324                                     false, /* is discardable */
325                                     false, /* is kernel (false means device) */
326                                     NULL,
327                                     &gobj);
328         if (ret) {
329                 pr_err("failed to allocate framebuffer (%d)\n",
330                        aligned_size);
331                 return -ENOMEM;
332         }
333         qbo = gem_to_qxl_bo(gobj);
334
335         qbo->surf.width = mode_cmd->width;
336         qbo->surf.height = mode_cmd->height;
337         qbo->surf.stride = mode_cmd->pitches[0];
338         qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB;
339         ret = qxl_bo_reserve(qbo, false);
340         if (unlikely(ret != 0))
341                 goto out_unref;
342         ret = qxl_bo_pin(qbo, QXL_GEM_DOMAIN_SURFACE, NULL);
343         if (ret) {
344                 qxl_bo_unreserve(qbo);
345                 goto out_unref;
346         }
347         ret = qxl_bo_kmap(qbo, NULL);
348         qxl_bo_unreserve(qbo); /* unreserve, will be mmaped */
349         if (ret)
350                 goto out_unref;
351
352         *gobj_p = gobj;
353         return 0;
354 out_unref:
355         qxlfb_destroy_pinned_object(gobj);
356         *gobj_p = NULL;
357         return ret;
358 }
359
360 static int qxlfb_create(struct qxl_fbdev *qfbdev,
361                         struct drm_fb_helper_surface_size *sizes)
362 {
363         struct qxl_device *qdev = qfbdev->qdev;
364         struct fb_info *info;
365         struct drm_framebuffer *fb = NULL;
366         struct drm_mode_fb_cmd2 mode_cmd;
367         struct drm_gem_object *gobj = NULL;
368         struct qxl_bo *qbo = NULL;
369         struct device *device = &qdev->pdev->dev;
370         int ret;
371         int size;
372         int bpp = sizes->surface_bpp;
373         int depth = sizes->surface_depth;
374         void *shadow;
375
376         mode_cmd.width = sizes->surface_width;
377         mode_cmd.height = sizes->surface_height;
378
379         mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64);
380         mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
381
382         ret = qxlfb_create_pinned_object(qfbdev, &mode_cmd, &gobj);
383         qbo = gem_to_qxl_bo(gobj);
384         QXL_INFO(qdev, "%s: %dx%d %d\n", __func__, mode_cmd.width,
385                  mode_cmd.height, mode_cmd.pitches[0]);
386
387         shadow = vmalloc(mode_cmd.pitches[0] * mode_cmd.height);
388         /* TODO: what's the usual response to memory allocation errors? */
389         BUG_ON(!shadow);
390         QXL_INFO(qdev,
391         "surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n",
392                  qxl_bo_gpu_offset(qbo),
393                  qxl_bo_mmap_offset(qbo),
394                  qbo->kptr,
395                  shadow);
396         size = mode_cmd.pitches[0] * mode_cmd.height;
397
398         info = framebuffer_alloc(0, device);
399         if (info == NULL) {
400                 ret = -ENOMEM;
401                 goto out_unref;
402         }
403
404         info->par = qfbdev;
405
406         qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj);
407
408         fb = &qfbdev->qfb.base;
409
410         /* setup helper with fb data */
411         qfbdev->helper.fb = fb;
412         qfbdev->helper.fbdev = info;
413         qfbdev->shadow = shadow;
414         strcpy(info->fix.id, "qxldrmfb");
415
416         drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
417
418         info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
419         info->fbops = &qxlfb_ops;
420
421         /*
422          * TODO: using gobj->size in various places in this function. Not sure
423          * what the difference between the different sizes is.
424          */
425         info->fix.smem_start = qdev->vram_base; /* TODO - correct? */
426         info->fix.smem_len = gobj->size;
427         info->screen_base = qfbdev->shadow;
428         info->screen_size = gobj->size;
429
430         drm_fb_helper_fill_var(info, &qfbdev->helper, sizes->fb_width,
431                                sizes->fb_height);
432
433         /* setup aperture base/size for vesafb takeover */
434         info->apertures = alloc_apertures(1);
435         if (!info->apertures) {
436                 ret = -ENOMEM;
437                 goto out_unref;
438         }
439         info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base;
440         info->apertures->ranges[0].size = qdev->vram_size;
441
442         info->fix.mmio_start = 0;
443         info->fix.mmio_len = 0;
444
445         if (info->screen_base == NULL) {
446                 ret = -ENOSPC;
447                 goto out_unref;
448         }
449
450         ret = fb_alloc_cmap(&info->cmap, 256, 0);
451         if (ret) {
452                 ret = -ENOMEM;
453                 goto out_unref;
454         }
455
456         info->fbdefio = &qxl_defio;
457         fb_deferred_io_init(info);
458
459         qdev->fbdev_info = info;
460         qdev->fbdev_qfb = &qfbdev->qfb;
461         DRM_INFO("fb mappable at 0x%lX, size %lu\n",  info->fix.smem_start, (unsigned long)info->screen_size);
462         DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height);
463         return 0;
464
465 out_unref:
466         if (qbo) {
467                 ret = qxl_bo_reserve(qbo, false);
468                 if (likely(ret == 0)) {
469                         qxl_bo_kunmap(qbo);
470                         qxl_bo_unpin(qbo);
471                         qxl_bo_unreserve(qbo);
472                 }
473         }
474         if (fb && ret) {
475                 drm_gem_object_unreference(gobj);
476                 drm_framebuffer_cleanup(fb);
477                 kfree(fb);
478         }
479         drm_gem_object_unreference(gobj);
480         return ret;
481 }
482
483 static int qxl_fb_find_or_create_single(
484                 struct drm_fb_helper *helper,
485                 struct drm_fb_helper_surface_size *sizes)
486 {
487         struct qxl_fbdev *qfbdev = (struct qxl_fbdev *)helper;
488         int new_fb = 0;
489         int ret;
490
491         if (!helper->fb) {
492                 ret = qxlfb_create(qfbdev, sizes);
493                 if (ret)
494                         return ret;
495                 new_fb = 1;
496         }
497         return new_fb;
498 }
499
500 static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
501 {
502         struct fb_info *info;
503         struct qxl_framebuffer *qfb = &qfbdev->qfb;
504
505         if (qfbdev->helper.fbdev) {
506                 info = qfbdev->helper.fbdev;
507
508                 unregister_framebuffer(info);
509                 framebuffer_release(info);
510         }
511         if (qfb->obj) {
512                 qxlfb_destroy_pinned_object(qfb->obj);
513                 qfb->obj = NULL;
514         }
515         drm_fb_helper_fini(&qfbdev->helper);
516         vfree(qfbdev->shadow);
517         drm_framebuffer_cleanup(&qfb->base);
518
519         return 0;
520 }
521
522 static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
523         .fb_probe = qxl_fb_find_or_create_single,
524 };
525
526 int qxl_fbdev_init(struct qxl_device *qdev)
527 {
528         struct qxl_fbdev *qfbdev;
529         int bpp_sel = 32; /* TODO: parameter from somewhere? */
530         int ret;
531
532         qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
533         if (!qfbdev)
534                 return -ENOMEM;
535
536         qfbdev->qdev = qdev;
537         qdev->mode_info.qfbdev = qfbdev;
538         qfbdev->helper.funcs = &qxl_fb_helper_funcs;
539
540         ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
541                                  qxl_num_crtc /* num_crtc - QXL supports just 1 */,
542                                  QXLFB_CONN_LIMIT);
543         if (ret) {
544                 kfree(qfbdev);
545                 return ret;
546         }
547
548         drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
549         drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
550         return 0;
551 }
552
553 void qxl_fbdev_fini(struct qxl_device *qdev)
554 {
555         if (!qdev->mode_info.qfbdev)
556                 return;
557
558         qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev);
559         kfree(qdev->mode_info.qfbdev);
560         qdev->mode_info.qfbdev = NULL;
561 }
562
563 void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
564 {
565         fb_set_suspend(qdev->mode_info.qfbdev->helper.fbdev, state);
566 }
567
568 bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj)
569 {
570         if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj))
571                 return true;
572         return false;
573 }