]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/gpu/drm/xilinx/xilinx_drm_fb.c
drm: xilinx: crtc: Add crtc set config helper
[zynq/linux.git] / drivers / gpu / drm / xilinx / xilinx_drm_fb.c
1 /*
2  * Xilinx DRM KMS Framebuffer helper
3  *
4  *  Copyright (C) 2015 Xilinx, Inc.
5  *
6  *  Author: Hyun Woo Kwon <hyun.kwon@xilinx.com>
7  *
8  * Based on drm_fb_cma_helper.c
9  *
10  *  Copyright (C) 2012 Analog Device Inc.
11  *
12  * This software is licensed under the terms of the GNU General Public
13  * License version 2, as published by the Free Software Foundation, and
14  * may be copied, distributed, and modified under those terms.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 #include <drm/drmP.h>
23 #include <drm/drm_crtc.h>
24 #include <drm/drm_crtc_helper.h>
25 #include <drm/drm_fb_helper.h>
26 #include <drm/drm_gem_cma_helper.h>
27
28 #include "xilinx_drm_drv.h"
29 #include "xilinx_drm_fb.h"
30
31 struct xilinx_drm_fb {
32         struct drm_framebuffer          base;
33         struct drm_gem_cma_object       *obj[4];
34 };
35
36 struct xilinx_drm_fbdev {
37         struct drm_fb_helper    fb_helper;
38         struct xilinx_drm_fb    *fb;
39         unsigned int align;
40         unsigned int vres_mult;
41         struct drm_display_mode old_mode;
42         bool mode_backup;
43 };
44
45 static inline struct xilinx_drm_fbdev *to_fbdev(struct drm_fb_helper *fb_helper)
46 {
47         return container_of(fb_helper, struct xilinx_drm_fbdev, fb_helper);
48 }
49
50 static inline struct xilinx_drm_fb *to_fb(struct drm_framebuffer *base_fb)
51 {
52         return container_of(base_fb, struct xilinx_drm_fb, base);
53 }
54
55 static void xilinx_drm_fb_destroy(struct drm_framebuffer *base_fb)
56 {
57         struct xilinx_drm_fb *fb = to_fb(base_fb);
58         int i;
59
60         for (i = 0; i < 4; i++)
61                 if (fb->obj[i])
62                         drm_gem_object_unreference_unlocked(&fb->obj[i]->base);
63
64         drm_framebuffer_cleanup(base_fb);
65         kfree(fb);
66 }
67
68 static int xilinx_drm_fb_create_handle(struct drm_framebuffer *base_fb,
69                                        struct drm_file *file_priv,
70                                        unsigned int *handle)
71 {
72         struct xilinx_drm_fb *fb = to_fb(base_fb);
73
74         return drm_gem_handle_create(file_priv, &fb->obj[0]->base, handle);
75 }
76
77 static struct drm_framebuffer_funcs xilinx_drm_fb_funcs = {
78         .destroy        = xilinx_drm_fb_destroy,
79         .create_handle  = xilinx_drm_fb_create_handle,
80 };
81
82 /**
83  * xilinx_drm_fb_alloc - Allocate a xilinx_drm_fb
84  * @drm: DRM object
85  * @mode_cmd: drm_mode_fb_cmd2 struct
86  * @obj: pointers for returned drm_gem_cma_objects
87  * @num_planes: number of planes to be allocated
88  *
89  * This function is based on drm_fb_cma_alloc().
90  *
91  * Return: a xilinx_drm_fb object, or ERR_PTR.
92  */
93 static struct xilinx_drm_fb *
94 xilinx_drm_fb_alloc(struct drm_device *drm, struct drm_mode_fb_cmd2 *mode_cmd,
95                     struct drm_gem_cma_object **obj, unsigned int num_planes)
96 {
97         struct xilinx_drm_fb *fb;
98         int ret;
99         int i;
100
101         fb = kzalloc(sizeof(*fb), GFP_KERNEL);
102         if (!fb)
103                 return ERR_PTR(-ENOMEM);
104
105         drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
106
107         for (i = 0; i < num_planes; i++)
108                 fb->obj[i] = obj[i];
109
110         ret = drm_framebuffer_init(drm, &fb->base, &xilinx_drm_fb_funcs);
111         if (ret) {
112                 DRM_ERROR("Failed to initialize framebuffer: %d\n", ret);
113                 kfree(fb);
114                 return ERR_PTR(ret);
115         }
116
117         return fb;
118 }
119
120 /**
121  * xilinx_drm_fb_get_gem_obj - Get CMA GEM object for framebuffer
122  * @base_fb: the framebuffer
123  * @plane: which plane
124  *
125  * This function is based on drm_fb_cma_get_gem_obj().
126  *
127  * Return: a CMA GEM object for given framebuffer, or NULL if not available.
128  */
129 struct drm_gem_cma_object *
130 xilinx_drm_fb_get_gem_obj(struct drm_framebuffer *base_fb, unsigned int plane)
131 {
132         struct xilinx_drm_fb *fb = to_fb(base_fb);
133
134         if (plane >= 4)
135                 return NULL;
136
137         return fb->obj[plane];
138 }
139
140 int xilinx_drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
141                               struct fb_info *info)
142 {
143         struct drm_fb_helper *fb_helper = info->par;
144         struct drm_device *dev = fb_helper->dev;
145         struct drm_mode_set *modeset;
146         int ret = 0;
147         int i;
148
149         if (oops_in_progress)
150                 return -EBUSY;
151
152         drm_modeset_lock_all(dev);
153         for (i = 0; i < fb_helper->crtc_count; i++) {
154                 modeset = &fb_helper->crtc_info[i].mode_set;
155
156                 modeset->x = var->xoffset;
157                 modeset->y = var->yoffset;
158
159                 if (modeset->num_connectors) {
160                         ret = drm_mode_set_config_internal(modeset);
161                         if (!ret) {
162                                 info->var.xoffset = var->xoffset;
163                                 info->var.yoffset = var->yoffset;
164                         }
165                 }
166         }
167         drm_modeset_unlock_all(dev);
168         return ret;
169 }
170
171 /**
172  * xilinx_drm_fb_set_config - synchronize resolution changes with fbdev
173  * @fb_helper: fb helper structure
174  * @set: mode set configuration
175  */
176 void xilinx_drm_fb_set_config(struct drm_fb_helper *fb_helper,
177                                 struct drm_mode_set *set)
178 {
179         if (fb_helper && set) {
180                 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
181
182                 if (fbdev && fb_helper->crtc_info &&
183                     fb_helper->crtc_info[0].mode_set.mode) {
184                         if (!fbdev->mode_backup) {
185                                 fbdev->old_mode =
186                                         *fb_helper->crtc_info[0].mode_set.mode;
187                                 fbdev->mode_backup = true;
188                         }
189                         *fb_helper->crtc_info[0].mode_set.mode = *set->mode;
190                }
191         }
192 }
193
194 int
195 xilinx_drm_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
196 {
197         struct drm_fb_helper *fb_helper = info->par;
198         unsigned int i;
199         int ret = 0;
200
201         switch (cmd) {
202         case FBIO_WAITFORVSYNC:
203                 for (i = 0; i < fb_helper->crtc_count; i++) {
204                         struct drm_mode_set *mode_set;
205                         struct drm_crtc *crtc;
206
207                         mode_set = &fb_helper->crtc_info[i].mode_set;
208                         crtc = mode_set->crtc;
209                         ret = drm_crtc_vblank_get(crtc);
210                         if (!ret) {
211                                 drm_crtc_wait_one_vblank(crtc);
212                                 drm_crtc_vblank_put(crtc);
213                         }
214                 }
215                 return ret;
216         default:
217                 return -ENOTTY;
218         }
219
220         return 0;
221 }
222
223 static struct fb_ops xilinx_drm_fbdev_ops = {
224         .owner          = THIS_MODULE,
225         .fb_fillrect    = sys_fillrect,
226         .fb_copyarea    = sys_copyarea,
227         .fb_imageblit   = sys_imageblit,
228         .fb_check_var   = drm_fb_helper_check_var,
229         .fb_set_par     = drm_fb_helper_set_par,
230         .fb_blank       = drm_fb_helper_blank,
231         .fb_pan_display = xilinx_drm_fb_helper_pan_display,
232         .fb_setcmap     = drm_fb_helper_setcmap,
233         .fb_ioctl       = xilinx_drm_fb_ioctl,
234 };
235
236 /**
237  * xilinx_drm_fbdev_create - Create the fbdev with a framebuffer
238  * @fb_helper: fb helper structure
239  * @sizes: framebuffer size info
240  *
241  * This function is based on drm_fbdev_cma_create().
242  *
243  * Return: 0 if successful, or the error code.
244  */
245 static int xilinx_drm_fbdev_create(struct drm_fb_helper *fb_helper,
246                                    struct drm_fb_helper_surface_size *sizes)
247 {
248         struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
249         struct drm_mode_fb_cmd2 mode_cmd = { 0 };
250         struct drm_device *drm = fb_helper->dev;
251         struct drm_gem_cma_object *obj;
252         struct drm_framebuffer *base_fb;
253         unsigned int bytes_per_pixel;
254         unsigned long offset;
255         struct fb_info *fbi;
256         size_t size;
257         int ret;
258
259         DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
260                         sizes->surface_width, sizes->surface_height,
261                         sizes->surface_bpp);
262
263         bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
264
265         mode_cmd.width = sizes->surface_width;
266         mode_cmd.height = sizes->surface_height;
267         mode_cmd.pitches[0] = ALIGN(sizes->surface_width * bytes_per_pixel,
268                                     fbdev->align);
269         mode_cmd.pixel_format = xilinx_drm_get_format(drm);
270
271         mode_cmd.height *= fbdev->vres_mult;
272         size = mode_cmd.pitches[0] * mode_cmd.height;
273         obj = drm_gem_cma_create(drm, size);
274         if (IS_ERR(obj))
275                 return PTR_ERR(obj);
276
277         fbi = framebuffer_alloc(0, drm->dev);
278         if (!fbi) {
279                 DRM_ERROR("Failed to allocate framebuffer info.\n");
280                 ret = -ENOMEM;
281                 goto err_drm_gem_cma_free_object;
282         }
283
284         fbdev->fb = xilinx_drm_fb_alloc(drm, &mode_cmd, &obj, 1);
285         if (IS_ERR(fbdev->fb)) {
286                 DRM_ERROR("Failed to allocate DRM framebuffer.\n");
287                 ret = PTR_ERR(fbdev->fb);
288                 goto err_framebuffer_release;
289         }
290
291         base_fb = &fbdev->fb->base;
292         fb_helper->fb = base_fb;
293         fb_helper->fbdev = fbi;
294
295         fbi->par = fb_helper;
296         fbi->flags = FBINFO_FLAG_DEFAULT;
297         fbi->fbops = &xilinx_drm_fbdev_ops;
298
299         ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
300         if (ret) {
301                 DRM_ERROR("Failed to allocate color map.\n");
302                 goto err_xilinx_drm_fb_destroy;
303         }
304
305         drm_fb_helper_fill_fix(fbi, base_fb->pitches[0], base_fb->depth);
306         drm_fb_helper_fill_var(fbi, fb_helper, base_fb->width, base_fb->height);
307         fbi->var.yres = base_fb->height / fbdev->vres_mult;
308
309         offset = fbi->var.xoffset * bytes_per_pixel;
310         offset += fbi->var.yoffset * base_fb->pitches[0];
311
312         drm->mode_config.fb_base = (resource_size_t)obj->paddr;
313         fbi->screen_base = (char __iomem *)(obj->vaddr + offset);
314         fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
315         fbi->screen_size = size;
316         fbi->fix.smem_len = size;
317
318         return 0;
319
320 err_xilinx_drm_fb_destroy:
321         drm_framebuffer_unregister_private(base_fb);
322         xilinx_drm_fb_destroy(base_fb);
323 err_framebuffer_release:
324         framebuffer_release(fbi);
325 err_drm_gem_cma_free_object:
326         drm_gem_cma_free_object(&obj->base);
327         return ret;
328 }
329
330 static struct drm_fb_helper_funcs xilinx_drm_fb_helper_funcs = {
331         .fb_probe = xilinx_drm_fbdev_create,
332 };
333
334 /**
335  * xilinx_drm_fb_init - Allocate and initializes the Xilinx framebuffer
336  * @drm: DRM device
337  * @preferred_bpp: preferred bits per pixel for the device
338  * @num_crtc: number of CRTCs
339  * @max_conn_count: maximum number of connectors
340  * @align: alignment value for pitch
341  * @vres_mult: multiplier for virtual resolution
342  *
343  * This function is based on drm_fbdev_cma_init().
344  *
345  * Return: a newly allocated drm_fb_helper struct or a ERR_PTR.
346  */
347 struct drm_fb_helper *
348 xilinx_drm_fb_init(struct drm_device *drm, unsigned int preferred_bpp,
349                    unsigned int num_crtc, unsigned int max_conn_count,
350                    unsigned int align, unsigned int vres_mult)
351 {
352         struct xilinx_drm_fbdev *fbdev;
353         struct drm_fb_helper *fb_helper;
354         int ret;
355
356         fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
357         if (!fbdev) {
358                 DRM_ERROR("Failed to allocate drm fbdev.\n");
359                 return ERR_PTR(-ENOMEM);
360         }
361
362         fbdev->vres_mult = vres_mult;
363
364         fbdev->align = align;
365         fb_helper = &fbdev->fb_helper;
366         drm_fb_helper_prepare(drm, fb_helper, &xilinx_drm_fb_helper_funcs);
367
368         ret = drm_fb_helper_init(drm, fb_helper, num_crtc, max_conn_count);
369         if (ret < 0) {
370                 DRM_ERROR("Failed to initialize drm fb helper.\n");
371                 goto err_free;
372         }
373
374         ret = drm_fb_helper_single_add_all_connectors(fb_helper);
375         if (ret < 0) {
376                 DRM_ERROR("Failed to add connectors.\n");
377                 goto err_drm_fb_helper_fini;
378
379         }
380
381         drm_helper_disable_unused_functions(drm);
382
383         ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp);
384         if (ret < 0) {
385                 DRM_ERROR("Failed to set initial hw configuration.\n");
386                 goto err_drm_fb_helper_fini;
387         }
388
389         return fb_helper;
390
391 err_drm_fb_helper_fini:
392         drm_fb_helper_fini(fb_helper);
393 err_free:
394         kfree(fbdev);
395
396         return ERR_PTR(ret);
397 }
398
399 /**
400  * xilinx_drm_fbdev_fini - Free the Xilinx framebuffer
401  * @fb_helper: drm_fb_helper struct
402  *
403  * This function is based on drm_fbdev_cma_fini().
404  */
405 void xilinx_drm_fb_fini(struct drm_fb_helper *fb_helper)
406 {
407         struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
408
409         if (fbdev->fb_helper.fbdev) {
410                 struct fb_info *info;
411                 int ret;
412
413                 info = fbdev->fb_helper.fbdev;
414                 ret = unregister_framebuffer(info);
415                 if (ret < 0)
416                         DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
417
418                 if (info->cmap.len)
419                         fb_dealloc_cmap(&info->cmap);
420
421                 framebuffer_release(info);
422         }
423
424         if (fbdev->fb) {
425                 drm_framebuffer_unregister_private(&fbdev->fb->base);
426                 xilinx_drm_fb_destroy(&fbdev->fb->base);
427         }
428
429         drm_fb_helper_fini(&fbdev->fb_helper);
430         kfree(fbdev);
431 }
432
433 /**
434  * xilinx_drm_fb_restore_mode - Restores initial framebuffer mode
435  * @fb_helper: drm_fb_helper struct, may be NULL
436  *
437  * This function is based on drm_fbdev_cma_restore_mode() and usually called
438  * from the Xilinx DRM drivers lastclose callback.
439  */
440 void xilinx_drm_fb_restore_mode(struct drm_fb_helper *fb_helper)
441 {
442         struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
443
444         /* restore old display mode */
445         if (fb_helper && fbdev && fbdev->mode_backup &&
446             fb_helper->crtc_info &&
447             fb_helper->crtc_info[0].mode_set.mode) {
448                 *fb_helper->crtc_info[0].mode_set.mode = fbdev->old_mode;
449                 fbdev->mode_backup = false;
450         }
451
452         if (fb_helper)
453                 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
454 }
455
456 /**
457  * xilinx_drm_fb_create - (struct drm_mode_config_funcs *)->fb_create callback
458  * @drm: DRM device
459  * @file_priv: drm file private data
460  * @mode_cmd: mode command for fb creation
461  *
462  * This functions creates a drm_framebuffer for given mode @mode_cmd. This
463  * functions is intended to be used for the fb_create callback function of
464  * drm_mode_config_funcs.
465  *
466  * Return: a drm_framebuffer object if successful, or ERR_PTR.
467  */
468 struct drm_framebuffer *xilinx_drm_fb_create(struct drm_device *drm,
469                                              struct drm_file *file_priv,
470                                              struct drm_mode_fb_cmd2 *mode_cmd)
471 {
472         struct xilinx_drm_fb *fb;
473         struct drm_gem_cma_object *objs[4];
474         struct drm_gem_object *obj;
475         unsigned int hsub;
476         unsigned int vsub;
477         int ret;
478         int i;
479
480         if (!xilinx_drm_check_format(drm, mode_cmd->pixel_format)) {
481                 DRM_ERROR("unsupported pixel format %08x\n",
482                           mode_cmd->pixel_format);
483                 return ERR_PTR(-EINVAL);
484         }
485
486         hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
487         vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
488
489         for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
490                 unsigned int width = mode_cmd->width / (i ? hsub : 1);
491                 unsigned int height = mode_cmd->height / (i ? vsub : 1);
492                 unsigned int min_size;
493
494                 obj = drm_gem_object_lookup(drm, file_priv,
495                                             mode_cmd->handles[i]);
496                 if (!obj) {
497                         DRM_ERROR("Failed to lookup GEM object\n");
498                         ret = -ENXIO;
499                         goto err_gem_object_unreference;
500                 }
501
502                 min_size = (height - 1) * mode_cmd->pitches[i] + width *
503                            drm_format_plane_cpp(mode_cmd->pixel_format, i) +
504                            mode_cmd->offsets[i];
505
506                 if (obj->size < min_size) {
507                         drm_gem_object_unreference_unlocked(obj);
508                         ret = -EINVAL;
509                         goto err_gem_object_unreference;
510                 }
511                 objs[i] = to_drm_gem_cma_obj(obj);
512         }
513
514         fb = xilinx_drm_fb_alloc(drm, mode_cmd, objs, i);
515         if (IS_ERR(fb)) {
516                 ret = PTR_ERR(fb);
517                 goto err_gem_object_unreference;
518         }
519
520         drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->base.depth,
521                              &fb->base.bits_per_pixel);
522         if (!fb->base.bits_per_pixel)
523                 fb->base.bits_per_pixel =
524                         xilinx_drm_format_bpp(mode_cmd->pixel_format);
525
526         return &fb->base;
527
528 err_gem_object_unreference:
529         for (i--; i >= 0; i--)
530                 drm_gem_object_unreference_unlocked(&objs[i]->base);
531         return ERR_PTR(ret);
532 }
533
534
535 /**
536  * xilinx_drm_fb_hotplug_event - Poll for hotpulug events
537  * @fb_helper: drm_fb_helper struct, may be NULL
538  *
539  * This function is based on drm_fbdev_cma_hotplug_event() and usually called
540  * from the Xilinx DRM drivers output_poll_changed callback.
541  */
542 void xilinx_drm_fb_hotplug_event(struct drm_fb_helper *fb_helper)
543 {
544         if (fb_helper) {
545                 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
546
547                 if (fbdev)
548                         fbdev->mode_backup = false;
549         }
550
551         if (fb_helper)
552                 drm_fb_helper_hotplug_event(fb_helper);
553 }