Merge tag 'libnvdimm-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdim...
[linux-block.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
28 #include <drm/drmP.h>
29 #include <drm/drm.h>
30 #include <drm/drm_crtc.h>
31 #include <drm/drm_crtc_helper.h>
32 #include <drm/drm_fb_helper.h>
33 #include <drm/drm_gem_framebuffer_helper.h>
34
35 #include "qxl_drv.h"
36
37 #include "qxl_object.h"
38
39 static void qxl_fb_image_init(struct qxl_fb_image *qxl_fb_image,
40                               struct qxl_device *qdev, struct fb_info *info,
41                               const struct fb_image *image)
42 {
43         qxl_fb_image->qdev = qdev;
44         if (info) {
45                 qxl_fb_image->visual = info->fix.visual;
46                 if (qxl_fb_image->visual == FB_VISUAL_TRUECOLOR ||
47                     qxl_fb_image->visual == FB_VISUAL_DIRECTCOLOR)
48                         memcpy(&qxl_fb_image->pseudo_palette,
49                                info->pseudo_palette,
50                                sizeof(qxl_fb_image->pseudo_palette));
51         } else {
52                  /* fallback */
53                 if (image->depth == 1)
54                         qxl_fb_image->visual = FB_VISUAL_MONO10;
55                 else
56                         qxl_fb_image->visual = FB_VISUAL_DIRECTCOLOR;
57         }
58         if (image) {
59                 memcpy(&qxl_fb_image->fb_image, image,
60                        sizeof(qxl_fb_image->fb_image));
61         }
62 }
63
64 static struct fb_ops qxlfb_ops = {
65         .owner = THIS_MODULE,
66         DRM_FB_HELPER_DEFAULT_OPS,
67         .fb_fillrect = drm_fb_helper_sys_fillrect,
68         .fb_copyarea = drm_fb_helper_sys_copyarea,
69         .fb_imageblit = drm_fb_helper_sys_imageblit,
70 };
71
72 static void qxlfb_destroy_pinned_object(struct drm_gem_object *gobj)
73 {
74         struct qxl_bo *qbo = gem_to_qxl_bo(gobj);
75
76         qxl_bo_kunmap(qbo);
77         qxl_bo_unpin(qbo);
78
79         drm_gem_object_put_unlocked(gobj);
80 }
81
82 static int qxlfb_create_pinned_object(struct qxl_device *qdev,
83                                       const struct drm_mode_fb_cmd2 *mode_cmd,
84                                       struct drm_gem_object **gobj_p)
85 {
86         struct drm_gem_object *gobj = NULL;
87         struct qxl_bo *qbo = NULL;
88         int ret;
89         int aligned_size, size;
90         int height = mode_cmd->height;
91
92         size = mode_cmd->pitches[0] * height;
93         aligned_size = ALIGN(size, PAGE_SIZE);
94         /* TODO: unallocate and reallocate surface0 for real. Hack to just
95          * have a large enough surface0 for 1024x768 Xorg 32bpp mode */
96         ret = qxl_gem_object_create(qdev, aligned_size, 0,
97                                     QXL_GEM_DOMAIN_SURFACE,
98                                     false, /* is discardable */
99                                     false, /* is kernel (false means device) */
100                                     NULL,
101                                     &gobj);
102         if (ret) {
103                 pr_err("failed to allocate framebuffer (%d)\n",
104                        aligned_size);
105                 return -ENOMEM;
106         }
107         qbo = gem_to_qxl_bo(gobj);
108
109         qbo->surf.width = mode_cmd->width;
110         qbo->surf.height = mode_cmd->height;
111         qbo->surf.stride = mode_cmd->pitches[0];
112         qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB;
113
114         ret = qxl_bo_pin(qbo);
115         if (ret) {
116                 goto out_unref;
117         }
118         ret = qxl_bo_kmap(qbo, NULL);
119
120         if (ret)
121                 goto out_unref;
122
123         *gobj_p = gobj;
124         return 0;
125 out_unref:
126         qxlfb_destroy_pinned_object(gobj);
127         *gobj_p = NULL;
128         return ret;
129 }
130
131 /*
132  * FIXME
133  * It should not be necessary to have a special dirty() callback for fbdev.
134  */
135 static int qxlfb_framebuffer_dirty(struct drm_framebuffer *fb,
136                                    struct drm_file *file_priv,
137                                    unsigned int flags, unsigned int color,
138                                    struct drm_clip_rect *clips,
139                                    unsigned int num_clips)
140 {
141         struct qxl_device *qdev = fb->dev->dev_private;
142         struct fb_info *info = qdev->fb_helper.fbdev;
143         struct qxl_fb_image qxl_fb_image;
144         struct fb_image *image = &qxl_fb_image.fb_image;
145
146         /* TODO: hard coding 32 bpp */
147         int stride = fb->pitches[0];
148
149         /*
150          * we are using a shadow draw buffer, at qdev->surface0_shadow
151          */
152         image->dx = clips->x1;
153         image->dy = clips->y1;
154         image->width = clips->x2 - clips->x1;
155         image->height = clips->y2 - clips->y1;
156         image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized
157                                          warnings */
158         image->bg_color = 0;
159         image->depth = 32;           /* TODO: take from somewhere? */
160         image->cmap.start = 0;
161         image->cmap.len = 0;
162         image->cmap.red = NULL;
163         image->cmap.green = NULL;
164         image->cmap.blue = NULL;
165         image->cmap.transp = NULL;
166         image->data = info->screen_base + (clips->x1 * 4) + (stride * clips->y1);
167
168         qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL);
169         qxl_draw_opaque_fb(&qxl_fb_image, stride);
170
171         return 0;
172 }
173
174 static const struct drm_framebuffer_funcs qxlfb_fb_funcs = {
175         .destroy = drm_gem_fb_destroy,
176         .create_handle = drm_gem_fb_create_handle,
177         .dirty = qxlfb_framebuffer_dirty,
178 };
179
180 static int qxlfb_create(struct drm_fb_helper *helper,
181                         struct drm_fb_helper_surface_size *sizes)
182 {
183         struct qxl_device *qdev =
184                 container_of(helper, struct qxl_device, fb_helper);
185         struct fb_info *info;
186         struct drm_framebuffer *fb = NULL;
187         struct drm_mode_fb_cmd2 mode_cmd;
188         struct drm_gem_object *gobj = NULL;
189         struct qxl_bo *qbo = NULL;
190         int ret;
191         int bpp = sizes->surface_bpp;
192         int depth = sizes->surface_depth;
193         void *shadow;
194
195         mode_cmd.width = sizes->surface_width;
196         mode_cmd.height = sizes->surface_height;
197
198         mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 1) / 8), 64);
199         mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
200
201         ret = qxlfb_create_pinned_object(qdev, &mode_cmd, &gobj);
202         if (ret < 0)
203                 return ret;
204
205         qbo = gem_to_qxl_bo(gobj);
206         DRM_DEBUG_DRIVER("%dx%d %d\n", mode_cmd.width,
207                          mode_cmd.height, mode_cmd.pitches[0]);
208
209         shadow = vmalloc(array_size(mode_cmd.pitches[0], mode_cmd.height));
210         /* TODO: what's the usual response to memory allocation errors? */
211         BUG_ON(!shadow);
212         DRM_DEBUG_DRIVER("surface0 at gpu offset %lld, mmap_offset %lld (virt %p, shadow %p)\n",
213                          qxl_bo_gpu_offset(qbo), qxl_bo_mmap_offset(qbo),
214                          qbo->kptr, shadow);
215
216         info = drm_fb_helper_alloc_fbi(helper);
217         if (IS_ERR(info)) {
218                 ret = PTR_ERR(info);
219                 goto out_unref;
220         }
221
222         info->par = helper;
223
224         fb = drm_gem_fbdev_fb_create(&qdev->ddev, sizes, 64, gobj,
225                                      &qxlfb_fb_funcs);
226         if (IS_ERR(fb)) {
227                 DRM_ERROR("Failed to create framebuffer: %ld\n", PTR_ERR(fb));
228                 ret = PTR_ERR(fb);
229                 goto out_unref;
230         }
231
232         /* setup helper with fb data */
233         qdev->fb_helper.fb = fb;
234
235         strcpy(info->fix.id, "qxldrmfb");
236
237         drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
238
239         info->fbops = &qxlfb_ops;
240
241         /*
242          * TODO: using gobj->size in various places in this function. Not sure
243          * what the difference between the different sizes is.
244          */
245         info->fix.smem_start = qdev->vram_base; /* TODO - correct? */
246         info->fix.smem_len = gobj->size;
247         info->screen_base = shadow;
248         info->screen_size = gobj->size;
249
250         drm_fb_helper_fill_var(info, &qdev->fb_helper, sizes->fb_width,
251                                sizes->fb_height);
252
253         /* setup aperture base/size for vesafb takeover */
254         info->apertures->ranges[0].base = qdev->ddev.mode_config.fb_base;
255         info->apertures->ranges[0].size = qdev->vram_size;
256
257         info->fix.mmio_start = 0;
258         info->fix.mmio_len = 0;
259
260         if (info->screen_base == NULL) {
261                 ret = -ENOSPC;
262                 goto out_unref;
263         }
264
265         /* XXX error handling. */
266         drm_fb_helper_defio_init(helper);
267
268         DRM_INFO("fb mappable at 0x%lX, size %lu\n",  info->fix.smem_start, (unsigned long)info->screen_size);
269         DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n",
270                  fb->format->depth, fb->pitches[0], fb->width, fb->height);
271         return 0;
272
273 out_unref:
274         if (qbo) {
275                 qxl_bo_kunmap(qbo);
276                 qxl_bo_unpin(qbo);
277         }
278         drm_gem_object_put_unlocked(gobj);
279         return ret;
280 }
281
282 static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
283         .fb_probe = qxlfb_create,
284 };
285
286 int qxl_fbdev_init(struct qxl_device *qdev)
287 {
288         return drm_fb_helper_fbdev_setup(&qdev->ddev, &qdev->fb_helper,
289                                          &qxl_fb_helper_funcs, 32,
290                                          QXLFB_CONN_LIMIT);
291 }
292
293 void qxl_fbdev_fini(struct qxl_device *qdev)
294 {
295         struct fb_info *fbi = qdev->fb_helper.fbdev;
296         void *shadow = fbi ? fbi->screen_buffer : NULL;
297
298         drm_fb_helper_fbdev_teardown(&qdev->ddev);
299         vfree(shadow);
300 }