]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/qxl/qxl_fb.c
powerpc/dma: dma_set_coherent_mask() should not be GPL only
[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.y1 < y)
148                 y = qfbdev->dirty.y1;
149         if (qfbdev->dirty.y2 > y2)
150                 y2 = qfbdev->dirty.y2;
151         if (qfbdev->dirty.x1 < x)
152                 x = qfbdev->dirty.x1;
153         if (qfbdev->dirty.x2 > x2)
154                 x2 = qfbdev->dirty.x2;
155
156         qfbdev->dirty.x1 = x;
157         qfbdev->dirty.x2 = x2;
158         qfbdev->dirty.y1 = y;
159         qfbdev->dirty.y2 = y2;
160
161         spin_unlock_irqrestore(&qfbdev->dirty.lock, flags);
162
163         schedule_work(&qdev->fb_work);
164 }
165
166 static void qxl_deferred_io(struct fb_info *info,
167                             struct list_head *pagelist)
168 {
169         struct qxl_fbdev *qfbdev = info->par;
170         unsigned long start, end, min, max;
171         struct page *page;
172         int y1, y2;
173
174         min = ULONG_MAX;
175         max = 0;
176         list_for_each_entry(page, pagelist, lru) {
177                 start = page->index << PAGE_SHIFT;
178                 end = start + PAGE_SIZE - 1;
179                 min = min(min, start);
180                 max = max(max, end);
181         }
182
183         if (min < max) {
184                 y1 = min / info->fix.line_length;
185                 y2 = (max / info->fix.line_length) + 1;
186                 qxl_dirty_update(qfbdev, 0, y1, info->var.xres, y2 - y1);
187         }
188 };
189
190 static struct fb_deferred_io qxl_defio = {
191         .delay          = QXL_DIRTY_DELAY,
192         .deferred_io    = qxl_deferred_io,
193 };
194
195 static void qxl_fb_fillrect(struct fb_info *info,
196                             const struct fb_fillrect *rect)
197 {
198         struct qxl_fbdev *qfbdev = info->par;
199
200         drm_fb_helper_sys_fillrect(info, rect);
201         qxl_dirty_update(qfbdev, rect->dx, rect->dy, rect->width,
202                          rect->height);
203 }
204
205 static void qxl_fb_copyarea(struct fb_info *info,
206                             const struct fb_copyarea *area)
207 {
208         struct qxl_fbdev *qfbdev = info->par;
209
210         drm_fb_helper_sys_copyarea(info, area);
211         qxl_dirty_update(qfbdev, area->dx, area->dy, area->width,
212                          area->height);
213 }
214
215 static void qxl_fb_imageblit(struct fb_info *info,
216                              const struct fb_image *image)
217 {
218         struct qxl_fbdev *qfbdev = info->par;
219
220         drm_fb_helper_sys_imageblit(info, image);
221         qxl_dirty_update(qfbdev, image->dx, image->dy, image->width,
222                          image->height);
223 }
224
225 static void qxl_fb_work(struct work_struct *work)
226 {
227         struct qxl_device *qdev = container_of(work, struct qxl_device, fb_work);
228         struct qxl_fbdev *qfbdev = qdev->mode_info.qfbdev;
229
230         qxl_fb_dirty_flush(qfbdev->helper.fbdev);
231 }
232
233 int qxl_fb_init(struct qxl_device *qdev)
234 {
235         INIT_WORK(&qdev->fb_work, qxl_fb_work);
236         return 0;
237 }
238
239 static struct fb_ops qxlfb_ops = {
240         .owner = THIS_MODULE,
241         .fb_check_var = drm_fb_helper_check_var,
242         .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
243         .fb_fillrect = qxl_fb_fillrect,
244         .fb_copyarea = qxl_fb_copyarea,
245         .fb_imageblit = qxl_fb_imageblit,
246         .fb_pan_display = drm_fb_helper_pan_display,
247         .fb_blank = drm_fb_helper_blank,
248         .fb_setcmap = drm_fb_helper_setcmap,
249         .fb_debug_enter = drm_fb_helper_debug_enter,
250         .fb_debug_leave = drm_fb_helper_debug_leave,
251 };
252
253 static void qxlfb_destroy_pinned_object(struct drm_gem_object *gobj)
254 {
255         struct qxl_bo *qbo = gem_to_qxl_bo(gobj);
256         int ret;
257
258         ret = qxl_bo_reserve(qbo, false);
259         if (likely(ret == 0)) {
260                 qxl_bo_kunmap(qbo);
261                 qxl_bo_unpin(qbo);
262                 qxl_bo_unreserve(qbo);
263         }
264         drm_gem_object_unreference_unlocked(gobj);
265 }
266
267 int qxl_get_handle_for_primary_fb(struct qxl_device *qdev,
268                                   struct drm_file *file_priv,
269                                   uint32_t *handle)
270 {
271         int r;
272         struct drm_gem_object *gobj = qdev->fbdev_qfb->obj;
273
274         BUG_ON(!gobj);
275         /* drm_get_handle_create adds a reference - good */
276         r = drm_gem_handle_create(file_priv, gobj, handle);
277         if (r)
278                 return r;
279         return 0;
280 }
281
282 static int qxlfb_create_pinned_object(struct qxl_fbdev *qfbdev,
283                                       struct drm_mode_fb_cmd2 *mode_cmd,
284                                       struct drm_gem_object **gobj_p)
285 {
286         struct qxl_device *qdev = qfbdev->qdev;
287         struct drm_gem_object *gobj = NULL;
288         struct qxl_bo *qbo = NULL;
289         int ret;
290         int aligned_size, size;
291         int height = mode_cmd->height;
292         int bpp;
293         int depth;
294
295         drm_fb_get_bpp_depth(mode_cmd->pixel_format, &bpp, &depth);
296
297         size = mode_cmd->pitches[0] * height;
298         aligned_size = ALIGN(size, PAGE_SIZE);
299         /* TODO: unallocate and reallocate surface0 for real. Hack to just
300          * have a large enough surface0 for 1024x768 Xorg 32bpp mode */
301         ret = qxl_gem_object_create(qdev, aligned_size, 0,
302                                     QXL_GEM_DOMAIN_SURFACE,
303                                     false, /* is discardable */
304                                     false, /* is kernel (false means device) */
305                                     NULL,
306                                     &gobj);
307         if (ret) {
308                 pr_err("failed to allocate framebuffer (%d)\n",
309                        aligned_size);
310                 return -ENOMEM;
311         }
312         qbo = gem_to_qxl_bo(gobj);
313
314         qbo->surf.width = mode_cmd->width;
315         qbo->surf.height = mode_cmd->height;
316         qbo->surf.stride = mode_cmd->pitches[0];
317         qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB;
318         ret = qxl_bo_reserve(qbo, false);
319         if (unlikely(ret != 0))
320                 goto out_unref;
321         ret = qxl_bo_pin(qbo, QXL_GEM_DOMAIN_SURFACE, NULL);
322         if (ret) {
323                 qxl_bo_unreserve(qbo);
324                 goto out_unref;
325         }
326         ret = qxl_bo_kmap(qbo, NULL);
327         qxl_bo_unreserve(qbo); /* unreserve, will be mmaped */
328         if (ret)
329                 goto out_unref;
330
331         *gobj_p = gobj;
332         return 0;
333 out_unref:
334         qxlfb_destroy_pinned_object(gobj);
335         *gobj_p = NULL;
336         return ret;
337 }
338
339 static int qxlfb_create(struct qxl_fbdev *qfbdev,
340                         struct drm_fb_helper_surface_size *sizes)
341 {
342         struct qxl_device *qdev = qfbdev->qdev;
343         struct fb_info *info;
344         struct drm_framebuffer *fb = NULL;
345         struct drm_mode_fb_cmd2 mode_cmd;
346         struct drm_gem_object *gobj = NULL;
347         struct qxl_bo *qbo = NULL;
348         int ret;
349         int size;
350         int bpp = sizes->surface_bpp;
351         int depth = sizes->surface_depth;
352         void *shadow;
353
354         mode_cmd.width = sizes->surface_width;
355         mode_cmd.height = sizes->surface_height;
356
357         mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64);
358         mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
359
360         ret = qxlfb_create_pinned_object(qfbdev, &mode_cmd, &gobj);
361         qbo = gem_to_qxl_bo(gobj);
362         QXL_INFO(qdev, "%s: %dx%d %d\n", __func__, mode_cmd.width,
363                  mode_cmd.height, mode_cmd.pitches[0]);
364
365         shadow = vmalloc(mode_cmd.pitches[0] * mode_cmd.height);
366         /* TODO: what's the usual response to memory allocation errors? */
367         BUG_ON(!shadow);
368         QXL_INFO(qdev,
369         "surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n",
370                  qxl_bo_gpu_offset(qbo),
371                  qxl_bo_mmap_offset(qbo),
372                  qbo->kptr,
373                  shadow);
374         size = mode_cmd.pitches[0] * mode_cmd.height;
375
376         info = drm_fb_helper_alloc_fbi(&qfbdev->helper);
377         if (IS_ERR(info)) {
378                 ret = PTR_ERR(info);
379                 goto out_unref;
380         }
381
382         info->par = qfbdev;
383
384         qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj);
385
386         fb = &qfbdev->qfb.base;
387
388         /* setup helper with fb data */
389         qfbdev->helper.fb = fb;
390
391         qfbdev->shadow = shadow;
392         strcpy(info->fix.id, "qxldrmfb");
393
394         drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
395
396         info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
397         info->fbops = &qxlfb_ops;
398
399         /*
400          * TODO: using gobj->size in various places in this function. Not sure
401          * what the difference between the different sizes is.
402          */
403         info->fix.smem_start = qdev->vram_base; /* TODO - correct? */
404         info->fix.smem_len = gobj->size;
405         info->screen_base = qfbdev->shadow;
406         info->screen_size = gobj->size;
407
408         drm_fb_helper_fill_var(info, &qfbdev->helper, sizes->fb_width,
409                                sizes->fb_height);
410
411         /* setup aperture base/size for vesafb takeover */
412         info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base;
413         info->apertures->ranges[0].size = qdev->vram_size;
414
415         info->fix.mmio_start = 0;
416         info->fix.mmio_len = 0;
417
418         if (info->screen_base == NULL) {
419                 ret = -ENOSPC;
420                 goto out_destroy_fbi;
421         }
422
423         info->fbdefio = &qxl_defio;
424         fb_deferred_io_init(info);
425
426         qdev->fbdev_info = info;
427         qdev->fbdev_qfb = &qfbdev->qfb;
428         DRM_INFO("fb mappable at 0x%lX, size %lu\n",  info->fix.smem_start, (unsigned long)info->screen_size);
429         DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height);
430         return 0;
431
432 out_destroy_fbi:
433         drm_fb_helper_release_fbi(&qfbdev->helper);
434 out_unref:
435         if (qbo) {
436                 ret = qxl_bo_reserve(qbo, false);
437                 if (likely(ret == 0)) {
438                         qxl_bo_kunmap(qbo);
439                         qxl_bo_unpin(qbo);
440                         qxl_bo_unreserve(qbo);
441                 }
442         }
443         if (fb && ret) {
444                 drm_gem_object_unreference(gobj);
445                 drm_framebuffer_cleanup(fb);
446                 kfree(fb);
447         }
448         drm_gem_object_unreference(gobj);
449         return ret;
450 }
451
452 static int qxl_fb_find_or_create_single(
453                 struct drm_fb_helper *helper,
454                 struct drm_fb_helper_surface_size *sizes)
455 {
456         struct qxl_fbdev *qfbdev =
457                 container_of(helper, struct qxl_fbdev, helper);
458         int new_fb = 0;
459         int ret;
460
461         if (!helper->fb) {
462                 ret = qxlfb_create(qfbdev, sizes);
463                 if (ret)
464                         return ret;
465                 new_fb = 1;
466         }
467         return new_fb;
468 }
469
470 static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
471 {
472         struct qxl_framebuffer *qfb = &qfbdev->qfb;
473
474         drm_fb_helper_unregister_fbi(&qfbdev->helper);
475         drm_fb_helper_release_fbi(&qfbdev->helper);
476
477         if (qfb->obj) {
478                 qxlfb_destroy_pinned_object(qfb->obj);
479                 qfb->obj = NULL;
480         }
481         drm_fb_helper_fini(&qfbdev->helper);
482         vfree(qfbdev->shadow);
483         drm_framebuffer_cleanup(&qfb->base);
484
485         return 0;
486 }
487
488 static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
489         .fb_probe = qxl_fb_find_or_create_single,
490 };
491
492 int qxl_fbdev_init(struct qxl_device *qdev)
493 {
494         struct qxl_fbdev *qfbdev;
495         int bpp_sel = 32; /* TODO: parameter from somewhere? */
496         int ret;
497
498         qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
499         if (!qfbdev)
500                 return -ENOMEM;
501
502         qfbdev->qdev = qdev;
503         qdev->mode_info.qfbdev = qfbdev;
504         spin_lock_init(&qfbdev->delayed_ops_lock);
505         spin_lock_init(&qfbdev->dirty.lock);
506         INIT_LIST_HEAD(&qfbdev->delayed_ops);
507
508         drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
509                               &qxl_fb_helper_funcs);
510
511         ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
512                                  qxl_num_crtc /* num_crtc - QXL supports just 1 */,
513                                  QXLFB_CONN_LIMIT);
514         if (ret)
515                 goto free;
516
517         ret = drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
518         if (ret)
519                 goto fini;
520
521         ret = drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
522         if (ret)
523                 goto fini;
524
525         return 0;
526
527 fini:
528         drm_fb_helper_fini(&qfbdev->helper);
529 free:
530         kfree(qfbdev);
531         return ret;
532 }
533
534 void qxl_fbdev_fini(struct qxl_device *qdev)
535 {
536         if (!qdev->mode_info.qfbdev)
537                 return;
538
539         qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev);
540         kfree(qdev->mode_info.qfbdev);
541         qdev->mode_info.qfbdev = NULL;
542 }
543
544 void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
545 {
546         drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state);
547 }
548
549 bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj)
550 {
551         if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj))
552                 return true;
553         return false;
554 }