2 * Xilinx DRM KMS Framebuffer helper
4 * Copyright (C) 2015 Xilinx, Inc.
6 * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com>
8 * Based on drm_fb_cma_helper.c
10 * Copyright (C) 2012 Analog Device Inc.
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.
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.
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>
28 #include "xilinx_drm_drv.h"
29 #include "xilinx_drm_fb.h"
31 struct xilinx_drm_fb {
32 struct drm_framebuffer base;
33 struct drm_gem_cma_object *obj[4];
36 struct xilinx_drm_fbdev {
37 struct drm_fb_helper fb_helper;
38 struct xilinx_drm_fb *fb;
40 unsigned int vres_mult;
41 struct drm_display_mode old_mode;
45 static inline struct xilinx_drm_fbdev *to_fbdev(struct drm_fb_helper *fb_helper)
47 return container_of(fb_helper, struct xilinx_drm_fbdev, fb_helper);
50 static inline struct xilinx_drm_fb *to_fb(struct drm_framebuffer *base_fb)
52 return container_of(base_fb, struct xilinx_drm_fb, base);
55 static void xilinx_drm_fb_destroy(struct drm_framebuffer *base_fb)
57 struct xilinx_drm_fb *fb = to_fb(base_fb);
60 for (i = 0; i < 4; i++)
62 drm_gem_object_unreference_unlocked(&fb->obj[i]->base);
64 drm_framebuffer_cleanup(base_fb);
68 static int xilinx_drm_fb_create_handle(struct drm_framebuffer *base_fb,
69 struct drm_file *file_priv,
72 struct xilinx_drm_fb *fb = to_fb(base_fb);
74 return drm_gem_handle_create(file_priv, &fb->obj[0]->base, handle);
77 static struct drm_framebuffer_funcs xilinx_drm_fb_funcs = {
78 .destroy = xilinx_drm_fb_destroy,
79 .create_handle = xilinx_drm_fb_create_handle,
83 * xilinx_drm_fb_alloc - Allocate a xilinx_drm_fb
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
89 * This function is based on drm_fb_cma_alloc().
91 * Return: a xilinx_drm_fb object, or ERR_PTR.
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)
97 struct xilinx_drm_fb *fb;
101 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
103 return ERR_PTR(-ENOMEM);
105 drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
107 for (i = 0; i < num_planes; i++)
110 ret = drm_framebuffer_init(drm, &fb->base, &xilinx_drm_fb_funcs);
112 DRM_ERROR("Failed to initialize framebuffer: %d\n", ret);
121 * xilinx_drm_fb_get_gem_obj - Get CMA GEM object for framebuffer
122 * @base_fb: the framebuffer
123 * @plane: which plane
125 * This function is based on drm_fb_cma_get_gem_obj().
127 * Return: a CMA GEM object for given framebuffer, or NULL if not available.
129 struct drm_gem_cma_object *
130 xilinx_drm_fb_get_gem_obj(struct drm_framebuffer *base_fb, unsigned int plane)
132 struct xilinx_drm_fb *fb = to_fb(base_fb);
137 return fb->obj[plane];
140 int xilinx_drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
141 struct fb_info *info)
143 struct drm_fb_helper *fb_helper = info->par;
144 struct drm_device *dev = fb_helper->dev;
145 struct drm_mode_set *modeset;
149 if (oops_in_progress)
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;
156 modeset->x = var->xoffset;
157 modeset->y = var->yoffset;
159 if (modeset->num_connectors) {
160 ret = drm_mode_set_config_internal(modeset);
162 info->var.xoffset = var->xoffset;
163 info->var.yoffset = var->yoffset;
167 drm_modeset_unlock_all(dev);
172 * xilinx_drm_fb_set_config - synchronize resolution changes with fbdev
173 * @fb_helper: fb helper structure
174 * @set: mode set configuration
176 void xilinx_drm_fb_set_config(struct drm_fb_helper *fb_helper,
177 struct drm_mode_set *set)
179 if (fb_helper && set) {
180 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
182 if (fbdev && fb_helper->crtc_info &&
183 fb_helper->crtc_info[0].mode_set.mode) {
184 if (!fbdev->mode_backup) {
186 *fb_helper->crtc_info[0].mode_set.mode;
187 fbdev->mode_backup = true;
189 *fb_helper->crtc_info[0].mode_set.mode = *set->mode;
195 xilinx_drm_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
197 struct drm_fb_helper *fb_helper = info->par;
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;
207 mode_set = &fb_helper->crtc_info[i].mode_set;
208 crtc = mode_set->crtc;
209 ret = drm_crtc_vblank_get(crtc);
211 drm_crtc_wait_one_vblank(crtc);
212 drm_crtc_vblank_put(crtc);
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,
237 * xilinx_drm_fbdev_create - Create the fbdev with a framebuffer
238 * @fb_helper: fb helper structure
239 * @sizes: framebuffer size info
241 * This function is based on drm_fbdev_cma_create().
243 * Return: 0 if successful, or the error code.
245 static int xilinx_drm_fbdev_create(struct drm_fb_helper *fb_helper,
246 struct drm_fb_helper_surface_size *sizes)
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;
259 DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
260 sizes->surface_width, sizes->surface_height,
263 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
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,
269 mode_cmd.pixel_format = xilinx_drm_get_format(drm);
271 mode_cmd.height *= fbdev->vres_mult;
272 size = mode_cmd.pitches[0] * mode_cmd.height;
273 obj = drm_gem_cma_create(drm, size);
277 fbi = framebuffer_alloc(0, drm->dev);
279 DRM_ERROR("Failed to allocate framebuffer info.\n");
281 goto err_drm_gem_cma_free_object;
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;
291 base_fb = &fbdev->fb->base;
292 fb_helper->fb = base_fb;
293 fb_helper->fbdev = fbi;
295 fbi->par = fb_helper;
296 fbi->flags = FBINFO_FLAG_DEFAULT;
297 fbi->fbops = &xilinx_drm_fbdev_ops;
299 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
301 DRM_ERROR("Failed to allocate color map.\n");
302 goto err_xilinx_drm_fb_destroy;
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;
309 offset = fbi->var.xoffset * bytes_per_pixel;
310 offset += fbi->var.yoffset * base_fb->pitches[0];
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;
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);
330 static struct drm_fb_helper_funcs xilinx_drm_fb_helper_funcs = {
331 .fb_probe = xilinx_drm_fbdev_create,
335 * xilinx_drm_fb_init - Allocate and initializes the Xilinx framebuffer
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
343 * This function is based on drm_fbdev_cma_init().
345 * Return: a newly allocated drm_fb_helper struct or a ERR_PTR.
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)
352 struct xilinx_drm_fbdev *fbdev;
353 struct drm_fb_helper *fb_helper;
356 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
358 DRM_ERROR("Failed to allocate drm fbdev.\n");
359 return ERR_PTR(-ENOMEM);
362 fbdev->vres_mult = vres_mult;
364 fbdev->align = align;
365 fb_helper = &fbdev->fb_helper;
366 drm_fb_helper_prepare(drm, fb_helper, &xilinx_drm_fb_helper_funcs);
368 ret = drm_fb_helper_init(drm, fb_helper, num_crtc, max_conn_count);
370 DRM_ERROR("Failed to initialize drm fb helper.\n");
374 ret = drm_fb_helper_single_add_all_connectors(fb_helper);
376 DRM_ERROR("Failed to add connectors.\n");
377 goto err_drm_fb_helper_fini;
381 drm_helper_disable_unused_functions(drm);
383 ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp);
385 DRM_ERROR("Failed to set initial hw configuration.\n");
386 goto err_drm_fb_helper_fini;
391 err_drm_fb_helper_fini:
392 drm_fb_helper_fini(fb_helper);
400 * xilinx_drm_fbdev_fini - Free the Xilinx framebuffer
401 * @fb_helper: drm_fb_helper struct
403 * This function is based on drm_fbdev_cma_fini().
405 void xilinx_drm_fb_fini(struct drm_fb_helper *fb_helper)
407 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
409 if (fbdev->fb_helper.fbdev) {
410 struct fb_info *info;
413 info = fbdev->fb_helper.fbdev;
414 ret = unregister_framebuffer(info);
416 DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
419 fb_dealloc_cmap(&info->cmap);
421 framebuffer_release(info);
425 drm_framebuffer_unregister_private(&fbdev->fb->base);
426 xilinx_drm_fb_destroy(&fbdev->fb->base);
429 drm_fb_helper_fini(&fbdev->fb_helper);
434 * xilinx_drm_fb_restore_mode - Restores initial framebuffer mode
435 * @fb_helper: drm_fb_helper struct, may be NULL
437 * This function is based on drm_fbdev_cma_restore_mode() and usually called
438 * from the Xilinx DRM drivers lastclose callback.
440 void xilinx_drm_fb_restore_mode(struct drm_fb_helper *fb_helper)
442 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
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;
453 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
457 * xilinx_drm_fb_create - (struct drm_mode_config_funcs *)->fb_create callback
459 * @file_priv: drm file private data
460 * @mode_cmd: mode command for fb creation
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.
466 * Return: a drm_framebuffer object if successful, or ERR_PTR.
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)
472 struct xilinx_drm_fb *fb;
473 struct drm_gem_cma_object *objs[4];
474 struct drm_gem_object *obj;
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);
486 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
487 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
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;
494 obj = drm_gem_object_lookup(drm, file_priv,
495 mode_cmd->handles[i]);
497 DRM_ERROR("Failed to lookup GEM object\n");
499 goto err_gem_object_unreference;
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];
506 if (obj->size < min_size) {
507 drm_gem_object_unreference_unlocked(obj);
509 goto err_gem_object_unreference;
511 objs[i] = to_drm_gem_cma_obj(obj);
514 fb = xilinx_drm_fb_alloc(drm, mode_cmd, objs, i);
517 goto err_gem_object_unreference;
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);
528 err_gem_object_unreference:
529 for (i--; i >= 0; i--)
530 drm_gem_object_unreference_unlocked(&objs[i]->base);
536 * xilinx_drm_fb_hotplug_event - Poll for hotpulug events
537 * @fb_helper: drm_fb_helper struct, may be NULL
539 * This function is based on drm_fbdev_cma_hotplug_event() and usually called
540 * from the Xilinx DRM drivers output_poll_changed callback.
542 void xilinx_drm_fb_hotplug_event(struct drm_fb_helper *fb_helper)
545 struct xilinx_drm_fbdev *fbdev = to_fbdev(fb_helper);
548 fbdev->mode_backup = false;
552 drm_fb_helper_hotplug_event(fb_helper);