2 * drm kms/fb cma (contiguous memory allocator) helper functions
4 * Copyright (C) 2012 Analog Device Inc.
5 * Author: Lars-Peter Clausen <lars@metafoo.de>
8 * Copyright (C) 2012 Red Hat
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
21 #include <drm/drm_fb_helper.h>
22 #include <drm/drm_framebuffer.h>
23 #include <drm/drm_gem_cma_helper.h>
24 #include <drm/drm_gem_framebuffer_helper.h>
25 #include <drm/drm_fb_cma_helper.h>
26 #include <drm/drm_print.h>
27 #include <linux/module.h>
29 #define DEFAULT_FBDEFIO_DELAY_MS 50
31 struct drm_fbdev_cma
{
32 struct drm_fb_helper fb_helper
;
33 const struct drm_framebuffer_funcs
*fb_funcs
;
37 * DOC: framebuffer cma helper functions
39 * Provides helper functions for creating a cma (contiguous memory allocator)
42 * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create
43 * callback function to create a cma backed framebuffer.
45 * An fbdev framebuffer backed by cma is also available by calling
46 * drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down.
47 * If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be
48 * set up automatically. &drm_framebuffer_funcs.dirty is called by
49 * drm_fb_helper_deferred_io() in process context (&struct delayed_work).
51 * Example fbdev deferred io code::
53 * static int driver_fb_dirty(struct drm_framebuffer *fb,
54 * struct drm_file *file_priv,
55 * unsigned flags, unsigned color,
56 * struct drm_clip_rect *clips,
59 * struct drm_gem_cma_object *cma = drm_fb_cma_get_gem_obj(fb, 0);
60 * ... push changes ...
64 * static struct drm_framebuffer_funcs driver_fb_funcs = {
65 * .destroy = drm_gem_fb_destroy,
66 * .create_handle = drm_gem_fb_create_handle,
67 * .dirty = driver_fb_dirty,
72 * fbdev = drm_fb_cma_fbdev_init_with_funcs(dev, 16,
73 * dev->mode_config.num_crtc,
74 * dev->mode_config.num_connector,
79 static inline struct drm_fbdev_cma
*to_fbdev_cma(struct drm_fb_helper
*helper
)
81 return container_of(helper
, struct drm_fbdev_cma
, fb_helper
);
85 * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
86 * @fb: The framebuffer
89 * Return the CMA GEM object for given framebuffer.
91 * This function will usually be called from the CRTC callback functions.
93 struct drm_gem_cma_object
*drm_fb_cma_get_gem_obj(struct drm_framebuffer
*fb
,
96 struct drm_gem_object
*gem
;
98 gem
= drm_gem_fb_get_obj(fb
, plane
);
102 return to_drm_gem_cma_obj(gem
);
104 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj
);
107 * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
108 * @fb: The framebuffer
109 * @state: Which state of drm plane
110 * @plane: Which plane
111 * Return the CMA GEM address for given framebuffer.
113 * This function will usually be called from the PLANE callback functions.
115 dma_addr_t
drm_fb_cma_get_gem_addr(struct drm_framebuffer
*fb
,
116 struct drm_plane_state
*state
,
119 struct drm_gem_cma_object
*obj
;
122 obj
= drm_fb_cma_get_gem_obj(fb
, plane
);
126 paddr
= obj
->paddr
+ fb
->offsets
[plane
];
127 paddr
+= fb
->format
->cpp
[plane
] * (state
->src_x
>> 16);
128 paddr
+= fb
->pitches
[plane
] * (state
->src_y
>> 16);
132 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr
);
134 static int drm_fb_cma_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
)
136 return dma_mmap_writecombine(info
->device
, vma
, info
->screen_base
,
137 info
->fix
.smem_start
, info
->fix
.smem_len
);
140 static struct fb_ops drm_fbdev_cma_ops
= {
141 .owner
= THIS_MODULE
,
142 DRM_FB_HELPER_DEFAULT_OPS
,
143 .fb_fillrect
= drm_fb_helper_sys_fillrect
,
144 .fb_copyarea
= drm_fb_helper_sys_copyarea
,
145 .fb_imageblit
= drm_fb_helper_sys_imageblit
,
146 .fb_mmap
= drm_fb_cma_mmap
,
149 static int drm_fbdev_cma_deferred_io_mmap(struct fb_info
*info
,
150 struct vm_area_struct
*vma
)
152 fb_deferred_io_mmap(info
, vma
);
153 vma
->vm_page_prot
= pgprot_writecombine(vma
->vm_page_prot
);
158 static int drm_fbdev_cma_defio_init(struct fb_info
*fbi
,
159 struct drm_gem_cma_object
*cma_obj
)
161 struct fb_deferred_io
*fbdefio
;
162 struct fb_ops
*fbops
;
165 * Per device structures are needed because:
166 * fbops: fb_deferred_io_cleanup() clears fbops.fb_mmap
167 * fbdefio: individual delays
169 fbdefio
= kzalloc(sizeof(*fbdefio
), GFP_KERNEL
);
170 fbops
= kzalloc(sizeof(*fbops
), GFP_KERNEL
);
171 if (!fbdefio
|| !fbops
) {
177 /* can't be offset from vaddr since dirty() uses cma_obj */
178 fbi
->screen_buffer
= cma_obj
->vaddr
;
179 /* fb_deferred_io_fault() needs a physical address */
180 fbi
->fix
.smem_start
= page_to_phys(virt_to_page(fbi
->screen_buffer
));
182 *fbops
= *fbi
->fbops
;
185 fbdefio
->delay
= msecs_to_jiffies(DEFAULT_FBDEFIO_DELAY_MS
);
186 fbdefio
->deferred_io
= drm_fb_helper_deferred_io
;
187 fbi
->fbdefio
= fbdefio
;
188 fb_deferred_io_init(fbi
);
189 fbi
->fbops
->fb_mmap
= drm_fbdev_cma_deferred_io_mmap
;
194 static void drm_fbdev_cma_defio_fini(struct fb_info
*fbi
)
199 fb_deferred_io_cleanup(fbi
);
205 drm_fbdev_cma_create(struct drm_fb_helper
*helper
,
206 struct drm_fb_helper_surface_size
*sizes
)
208 struct drm_fbdev_cma
*fbdev_cma
= to_fbdev_cma(helper
);
209 struct drm_device
*dev
= helper
->dev
;
210 struct drm_gem_cma_object
*obj
;
211 struct drm_framebuffer
*fb
;
212 unsigned int bytes_per_pixel
;
213 unsigned long offset
;
218 DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
219 sizes
->surface_width
, sizes
->surface_height
,
222 bytes_per_pixel
= DIV_ROUND_UP(sizes
->surface_bpp
, 8);
223 size
= sizes
->surface_width
* sizes
->surface_height
* bytes_per_pixel
;
224 obj
= drm_gem_cma_create(dev
, size
);
228 fbi
= drm_fb_helper_alloc_fbi(helper
);
231 goto err_gem_free_object
;
234 fb
= drm_gem_fbdev_fb_create(dev
, sizes
, 0, &obj
->base
,
235 fbdev_cma
->fb_funcs
);
237 dev_err(dev
->dev
, "Failed to allocate DRM framebuffer.\n");
239 goto err_fb_info_destroy
;
245 fbi
->flags
= FBINFO_FLAG_DEFAULT
;
246 fbi
->fbops
= &drm_fbdev_cma_ops
;
248 drm_fb_helper_fill_fix(fbi
, fb
->pitches
[0], fb
->format
->depth
);
249 drm_fb_helper_fill_var(fbi
, helper
, sizes
->fb_width
, sizes
->fb_height
);
251 offset
= fbi
->var
.xoffset
* bytes_per_pixel
;
252 offset
+= fbi
->var
.yoffset
* fb
->pitches
[0];
254 dev
->mode_config
.fb_base
= (resource_size_t
)obj
->paddr
;
255 fbi
->screen_base
= obj
->vaddr
+ offset
;
256 fbi
->fix
.smem_start
= (unsigned long)(obj
->paddr
+ offset
);
257 fbi
->screen_size
= size
;
258 fbi
->fix
.smem_len
= size
;
260 if (fb
->funcs
->dirty
) {
261 ret
= drm_fbdev_cma_defio_init(fbi
, obj
);
263 goto err_cma_destroy
;
269 drm_framebuffer_remove(fb
);
271 drm_fb_helper_fini(helper
);
273 drm_gem_object_put_unlocked(&obj
->base
);
277 static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs
= {
278 .fb_probe
= drm_fbdev_cma_create
,
282 * drm_fb_cma_fbdev_init_with_funcs() - Allocate and initialize fbdev emulation
284 * @preferred_bpp: Preferred bits per pixel for the device.
285 * @dev->mode_config.preferred_depth is used if this is zero.
286 * @max_conn_count: Maximum number of connectors.
287 * @dev->mode_config.num_connector is used if this is zero.
288 * @funcs: Framebuffer functions, in particular a custom dirty() callback.
292 * Zero on success or negative error code on failure.
294 int drm_fb_cma_fbdev_init_with_funcs(struct drm_device
*dev
,
295 unsigned int preferred_bpp
, unsigned int max_conn_count
,
296 const struct drm_framebuffer_funcs
*funcs
)
298 struct drm_fbdev_cma
*fbdev_cma
;
299 struct drm_fb_helper
*fb_helper
;
303 preferred_bpp
= dev
->mode_config
.preferred_depth
;
308 max_conn_count
= dev
->mode_config
.num_connector
;
310 fbdev_cma
= kzalloc(sizeof(*fbdev_cma
), GFP_KERNEL
);
314 fbdev_cma
->fb_funcs
= funcs
;
315 fb_helper
= &fbdev_cma
->fb_helper
;
317 drm_fb_helper_prepare(dev
, fb_helper
, &drm_fb_cma_helper_funcs
);
319 ret
= drm_fb_helper_init(dev
, fb_helper
, max_conn_count
);
321 DRM_DEV_ERROR(dev
->dev
, "Failed to initialize fbdev helper.\n");
325 ret
= drm_fb_helper_single_add_all_connectors(fb_helper
);
327 DRM_DEV_ERROR(dev
->dev
, "Failed to add connectors.\n");
328 goto err_drm_fb_helper_fini
;
331 ret
= drm_fb_helper_initial_config(fb_helper
, preferred_bpp
);
333 DRM_DEV_ERROR(dev
->dev
, "Failed to set fbdev configuration.\n");
334 goto err_drm_fb_helper_fini
;
339 err_drm_fb_helper_fini
:
340 drm_fb_helper_fini(fb_helper
);
346 EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs
);
349 * drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation
351 * @preferred_bpp: Preferred bits per pixel for the device.
352 * @dev->mode_config.preferred_depth is used if this is zero.
353 * @max_conn_count: Maximum number of connectors.
354 * @dev->mode_config.num_connector is used if this is zero.
357 * Zero on success or negative error code on failure.
359 int drm_fb_cma_fbdev_init(struct drm_device
*dev
, unsigned int preferred_bpp
,
360 unsigned int max_conn_count
)
362 return drm_fb_cma_fbdev_init_with_funcs(dev
, preferred_bpp
,
363 max_conn_count
, NULL
);
365 EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init
);
368 * drm_fb_cma_fbdev_fini() - Teardown fbdev emulation
371 void drm_fb_cma_fbdev_fini(struct drm_device
*dev
)
373 struct drm_fb_helper
*fb_helper
= dev
->fb_helper
;
378 /* Unregister if it hasn't been done already */
379 if (fb_helper
->fbdev
&& fb_helper
->fbdev
->dev
)
380 drm_fb_helper_unregister_fbi(fb_helper
);
382 if (fb_helper
->fbdev
)
383 drm_fbdev_cma_defio_fini(fb_helper
->fbdev
);
386 drm_framebuffer_remove(fb_helper
->fb
);
388 drm_fb_helper_fini(fb_helper
);
389 kfree(to_fbdev_cma(fb_helper
));
391 EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini
);
394 * drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct
396 * @preferred_bpp: Preferred bits per pixel for the device
397 * @max_conn_count: Maximum number of connectors
398 * @funcs: fb helper functions, in particular a custom dirty() callback
400 * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
402 struct drm_fbdev_cma
*drm_fbdev_cma_init_with_funcs(struct drm_device
*dev
,
403 unsigned int preferred_bpp
, unsigned int max_conn_count
,
404 const struct drm_framebuffer_funcs
*funcs
)
406 struct drm_fbdev_cma
*fbdev_cma
;
407 struct drm_fb_helper
*helper
;
410 fbdev_cma
= kzalloc(sizeof(*fbdev_cma
), GFP_KERNEL
);
412 dev_err(dev
->dev
, "Failed to allocate drm fbdev.\n");
413 return ERR_PTR(-ENOMEM
);
415 fbdev_cma
->fb_funcs
= funcs
;
417 helper
= &fbdev_cma
->fb_helper
;
419 drm_fb_helper_prepare(dev
, helper
, &drm_fb_cma_helper_funcs
);
421 ret
= drm_fb_helper_init(dev
, helper
, max_conn_count
);
423 dev_err(dev
->dev
, "Failed to initialize drm fb helper.\n");
427 ret
= drm_fb_helper_single_add_all_connectors(helper
);
429 dev_err(dev
->dev
, "Failed to add connectors.\n");
430 goto err_drm_fb_helper_fini
;
434 ret
= drm_fb_helper_initial_config(helper
, preferred_bpp
);
436 dev_err(dev
->dev
, "Failed to set initial hw configuration.\n");
437 goto err_drm_fb_helper_fini
;
442 err_drm_fb_helper_fini
:
443 drm_fb_helper_fini(helper
);
449 EXPORT_SYMBOL_GPL(drm_fbdev_cma_init_with_funcs
);
451 static const struct drm_framebuffer_funcs drm_fb_cma_funcs
= {
452 .destroy
= drm_gem_fb_destroy
,
453 .create_handle
= drm_gem_fb_create_handle
,
457 * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
459 * @preferred_bpp: Preferred bits per pixel for the device
460 * @max_conn_count: Maximum number of connectors
462 * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
464 struct drm_fbdev_cma
*drm_fbdev_cma_init(struct drm_device
*dev
,
465 unsigned int preferred_bpp
, unsigned int max_conn_count
)
467 return drm_fbdev_cma_init_with_funcs(dev
, preferred_bpp
,
471 EXPORT_SYMBOL_GPL(drm_fbdev_cma_init
);
474 * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
475 * @fbdev_cma: The drm_fbdev_cma struct
477 void drm_fbdev_cma_fini(struct drm_fbdev_cma
*fbdev_cma
)
479 drm_fb_helper_unregister_fbi(&fbdev_cma
->fb_helper
);
480 if (fbdev_cma
->fb_helper
.fbdev
)
481 drm_fbdev_cma_defio_fini(fbdev_cma
->fb_helper
.fbdev
);
483 if (fbdev_cma
->fb_helper
.fb
)
484 drm_framebuffer_remove(fbdev_cma
->fb_helper
.fb
);
486 drm_fb_helper_fini(&fbdev_cma
->fb_helper
);
489 EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini
);
492 * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
493 * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
495 * This function is usually called from the &drm_driver.lastclose callback.
497 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma
*fbdev_cma
)
500 drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma
->fb_helper
);
502 EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode
);
505 * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
506 * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
508 * This function is usually called from the &drm_mode_config.output_poll_changed
511 void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma
*fbdev_cma
)
514 drm_fb_helper_hotplug_event(&fbdev_cma
->fb_helper
);
516 EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event
);
519 * drm_fbdev_cma_set_suspend - wrapper around drm_fb_helper_set_suspend
520 * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
521 * @state: desired state, zero to resume, non-zero to suspend
523 * Calls drm_fb_helper_set_suspend, which is a wrapper around
524 * fb_set_suspend implemented by fbdev core.
526 void drm_fbdev_cma_set_suspend(struct drm_fbdev_cma
*fbdev_cma
, bool state
)
529 drm_fb_helper_set_suspend(&fbdev_cma
->fb_helper
, state
);
531 EXPORT_SYMBOL(drm_fbdev_cma_set_suspend
);
534 * drm_fbdev_cma_set_suspend_unlocked - wrapper around
535 * drm_fb_helper_set_suspend_unlocked
536 * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
537 * @state: desired state, zero to resume, non-zero to suspend
539 * Calls drm_fb_helper_set_suspend, which is a wrapper around
540 * fb_set_suspend implemented by fbdev core.
542 void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma
*fbdev_cma
,
546 drm_fb_helper_set_suspend_unlocked(&fbdev_cma
->fb_helper
,
549 EXPORT_SYMBOL(drm_fbdev_cma_set_suspend_unlocked
);